Quickstart
Resolve targets
DNS queries return all IPs that are found:
astu resolve -T something.com
104.21.59.206
172.67.183.168
Ports are also supported and preserved:
astu resolve -T localhost:22
127.0.0.1:22
Ping targets
astu ping -T localhost:22
CLI
At startup, astu will source information about its environment:
- The full cmdline of the current process
- Number of Tokio threads
- Ulimits
- Status of whether stdin/stdout/stderr are TTYs
$ASTU_DATA_DIR
├── astu.db
└── logs/
├── 019cacd7-f900-7e20-af4b-ff53ef7f8a9f.log
└── latest.log -> 019cacd7-f900-7e20-af4b-ff53ef7f8a9f.log
The database will be created upon first use. Each run will ensure the database
schema is migrated. The latest action-like run will be persisted in the database
as the latest job ID (this may not always equal latest.log as such).
Each invocation will generate a unique run ID - this will be used as both the
job ID as well as the name of the debug log file. IDs are UUIDv7 strings so they
are time-sortable. Debug logs (via tracing-appender) will be written to this
file, and a symlink latest.log will always point to the latest run debug log.
Interactive progress will be displayed with tracing-indicatif if stderr is a
TTY. Outputs such as tables and JSON will be printed to stdout. Plans and
prompts for confirmation will be printed on stderr.
Subcommand Groups
Astu subcommands are broken down into a few groups:
- Action
- Result
- Other
Options
Flags are grouped based on scope and shared usage. Generally, each one will have a hierarchy like this:
- Subcommand flags
- Subcommand group flags
- Global flags
Global Options
Astu has a few global flags that are shared by all subcommands.
--data-dir
Env: ASTU_DATA_DIR
Default: $XDG_DATA_HOME/astu (if XDG_DATA_HOME set); dirs crate default
per platform (otherwise)
Astu database path.
--log-level
Env: ASTU_LOG
Default: debug
Filter directive for log file. Follows the RUST_LOG format.
-o/--output
Default: text
Possible values: text, json
Output format.
-h/--help
Prints help.
Action
Actions resolve targets and perform operations on that target set.
The general sequence goes like this:
- Resolve input target queries into into a set of unique targets
- Present an action plan and require either interactive approval or
--confirm=<targets>with the exact number of targets that the action will affect - Perform the sequence of actions defined by the subcommand for each target in
concurrently, bounded by
--concurrency, displaying progress usingindicatifas tasks complete - Display freq info for errors only (ie, automatically run
astu freq error) and suggestions for the command to run next, ieastu freqorastu output.
If astu receives a ctrl-c interrupt during a run: currently running tasks
will wait for completion, while not-yet-started tasks will be persisted as
canceled. Canceled jobs may be resumed with astu resume. A second ctrl-c
will forcefully kill the run without waiting for running tasks to complete.
Options
Action Options
-T/--target
Target URI or short form.
If not passed, will default to the local: target. Can be passed multiple
times.
-f/--target-file
Path to a file to read target URIs from.
Can use - to read from stdin. If this is set, then --stdin is assumed to be
target. Can be passed multiple times.
--stdin
Default: auto
Possible values: auto, param, target, pipe
How to interpret stdin.
auto sets the mode based on this chain of priority:
- If
{param}is used in the command template ->param - If
--target-fileis-or/dev/stdin->target - Else ->
pipe
param splits incoming stdin into tokens based on whitespace.
target allows --target-file to read from stdin (must still be passed on its
own).
pipe multiplexes stdin to each of the tasks by writing to a spool file where
each task has a cursor, guaranteeing delivery.
--timeout
Default: 30s
Per-task timeout value in humantime. 0 indicates no timeout.
--confirm
Auto-accept the plan if passed target count is correct.
Required if running non-interactively to proceed with action. Skips prompt for confirmation if running interactively.
astu lookup
Alias: l, resolve
Resolves targets.
Expands a set of input targets into a set of actionable targets. Does not display a plan, and no actual actions are performed on targets.
Examples
Resolve the default target
astu lookup
Output
local:
Resolve a single target
astu lookup -T ssh://user@host
Output
ssh://user@host
Resolve multiple targets
astu lookup -T cidr://user@[::1]:22/127
Output
ip://user@[::]:22
ip://user@[::1]:22
Resolve targets from files, stdin, and flags
cat targets.txt \
| astu lookup \
-f targets_a.txt \
-f targets_b.txt \
-f - \
-T ssh://user@host
Output
dns://foo@host-from-a
dns://bar@host-from-b
dns://baz@host-from-stdin
dns://quux@host-from-stdin
ssh://user@host
astu ping
Alias: p
Pings targets.
Performs this sequence of actions on each target in the set:
- Connect
- Ping
Persists the output of ping (if it exists) as stdout, as well as the timing of each phase. Exitcode and stderr will never exist.
Examples
Ping a target with no errors
astu ping -T ssh://user@host
Output
error-freq
(no rows)
Ping targets with some errors
astu ping -f targets-total-10.txt
Output
error-freq
| value | count | pct |
|-----------|-------|-----|
| foo error | 3 | 30% |
| bar error | 2 | 20% |
| baz error | 1 | 10% |
Use `astu output` or `astu freq` for result analysis
astu run
Alias: r, exec
Runs a command on targets.
Performs this sequence of actions on each target in the set:
- Connect
- Auth (if required) (until authenticated)
- Exec
Persists stdout/stderr/exitcode/error, as well as the timing of each phase.
Templates
Template strings that will be substituted with per-task context. Can also be
reverse substituted with --dedupe to reduce output cardinality. Not all
templates are guaranteed to exist; if a template cannot be substituted, the job
will fail fast.
{param}: Split param from--stdin=parammode{host}: Target hostname{user}: Target login username{ip}: Target IP address
Options
Arguments
<COMMAND>
Command template.
Options
--live
Stream task stdout and stderr the terminal.
Useful for things produce live tails of information such as bpftrace. Not to be used with fullscreen programs like Vim. Output will still be captured to the database.
--dedupe
Default: param, host, user, ip
Possible values: See TEMPLATES.
Deduplicators for line normalization.
These values will be substituted for their template tokens when seen, so that
aggregations like astu freq can more usefully display values that differ
predictably. Also helps with db size.
Examples
Execute a command on an SSH target
astu run -T ssh://user@host 'whoami'
Output
TODO
Result
Display results/output from prior runs.
Options
Result Options
-j/--job
Default: Last run job ID
Job ID to display results for.
If not set, will use the last action job ID persisted in the DB.
astu output
Alias: o, out
Displays tables of captured stdout/stderr/exitcode/error per task in a job.
Examples
Display all fields for all tasks in the last job
astu output
Output
TODO
Display all fields for an explicit target in the last job
astu output -T ssh://user@host
Output
TODO
Display all fields for all tasks where that field contains a string in the last job
astu output --contains=needle
Output
TODO
Display only stdout for all tasks in an explicit job
astu output stdout --job=746677e7-b6f9-458b-857e-aa6a8638e101
Output
TODO
astu freq
Alias: f
Displays tables of captured stdout/stderr/exitcode/error aggregated by count of appearance in a job.
Empty strings in stdout/stderr will be displayed as such. Exitcodes that do not exist (such as the task erroring before a real exitcode is received) will be displayed as -1. Tasks that did not error at all will not be displayed in the error table; thus it could potentially not sum to 100%. All other tables will sum to 100%.
Examples
Display all fields aggregated in the last job
astu freq
Output
stdout
| value | count | pct |
|----------|-------|-----|
| foo | 6 | 60% |
| bar | 3 | 30% |
| baz | 1 | 10% |
stderr
(no rows)
exitcode
| value | count | pct |
|-------|-------|------|
| 0 | 2 | 100% |
error-freq
| value | count | pct |
|-----------|-------|-----|
| foo error | 3 | 30% |
| bar error | 2 | 20% |
| baz error | 1 | 10% |
Display all fields aggregated where that field contains a string in the last job
astu freq --contains=foo
Output
stdout
| value | count | pct |
|----------|-------|-----|
| foo | 6 | 60% |
stderr
(no rows)
exitcode
(no rows)
error-freq
| value | count | pct |
|-----------|-------|-----|
| foo error | 3 | 30% |
Display only stdout aggregated for all tasks in an explicit job
astu freq stdout --job=746677e7-b6f9-458b-857e-aa6a8638e101
Output
stdout
| value | count | pct |
|----------|-------|-----|
| foo | 6 | 60% |
| bar | 3 | 30% |
| baz | 1 | 10% |
astu trace
Displays a diagnostic trace of timings for the sequence of actions and observed errors for tasks in a job.
Examples
Trace an SSH target
astu trace -T ssh://user@host
Output
TODO
Other
Other subcommands that do not fit into the action or result groups.
astu jobs
Alias: j, job
Displays a table of jobs and their metadata.
Examples
Display all jobs
astu jobs
Output
| job_id | started_at | command | task_count |
|--------------------------------------|----------------------------|-----------------------------------|------------|
| 019ca7da-534f-7fa0-874a-7af651acbd65 | 2026-03-01 05:23:49.199082 | /usr/bin/printf 'x=%s\n' '{param} | 2 |
astu tasks
Alias: t, task
Displays a table of tasks and their metadata within a job. The last job will automatically be used if job is not explicitly passed.
Options
Options
-j/--job
Default: Last run job ID
Job ID to display results for.
If not set, will use the last action job ID persisted in the DB.
Examples
Display all tasks in the last job
astu tasks
Output
| task_id | target | status |
|--------------------------------------|-------------------|----------|
| 019ca7da-534f-7fa0-874a-7b17b297d9e1 | local://localhost | complete |
| 019ca7da-534f-7fa0-874a-7b067d6636f7 | local://localhost | failed |
astu gc
Cleans the database of jobs and their associated data.
Examples
Delete data that was collected 30 days ago and older
astu gc --before=30d
Output
TODO
Architecture
Targets
A target is the basic unit of operation in Astu - it represents an object on which an action will be performed.
Targets are usually parsed from URIs, but they also support convenient short forms for common types. Not all target types support short forms; those that do will state so.
Here is an example long form for an IP target:
ip://127.0.0.1
While here is the equivalent short form for the same target:
127.0.0.1
Targets can be dynamically expanded and aggregated into other targets using resolvers. The core Astu workflow revolves around dynamic target discovery using resolvers. Generally, one provides at least one seed target which is iteratively expanded using resolver chains.
Clients are drivers that perform actions on targets.
Target Graph
A target graph is special target-centric data structure representing targets as a directed graph. Resolvers generally support resolving directly into a caller-provided target graph - this is useful for building a topological action plan.
Here's a simple example of the target graph for a DNS target that resolves to multiple different IP targets:
digraph {
rankdir=LR;
0 [ label="dns://something.com"]
1 [ label="ip://104.21.59.206"]
2 [ label="ip://172.67.183.168"]
0 -> 1 [ ]
0 -> 2 [ ]
}
While here's a more complex example where targets have multiple parents. In this example, both the CIDR target 10.0.0.0/31 and the DNS target myrouter.lan point to the IP target 10.0.0.1.
digraph {
rankdir=LR;
0 [ label="cidr://10.0.0.0/31"]
1 [ label="ip://10.0.0.0"]
2 [ label="ip://10.0.0.1"]
3 [ label="dns://myrouter.lan"]
0 -> 1 [ ]
0 -> 2 [ ]
3 -> 2 [ ]
}
Target Types
For each target type, the URI and short forms will be given along with some examples.
IP
Internet Protocol (IP) address.
- URI form:
ip://[user@]<ip>[:port]ip://127.0.0.1ip://root@127.0.0.1:22ip://[::1]ip://root@[::1]:22
- Short form:
<ip>[:port]127.0.0.1127.0.0.1:22::1[::1]:22
TCP
Essentially an alias for IP.
CIDR
Classless Inter-Domain Routing (CIDR) block.
- URI form:
cidr://[user@]<ip>[:port]/<prefix>cidr://127.0.0.0/32cidr://root@127.0.0.0:22/32cidr://[::1]/128cidr://root@[::1]:22/128
- Short form:
<ip>/<prefix>127.0.0.0/24::1/128
DNS
Domain Name System (DNS) record.
- URI form:
dns://[user@]<name>[:port]dns://localhostdns://root@localhost:22
- Short form: n/a
SSH
Secure Shell (SSH) address.
- URI form:
ssh://[user@]<host>[:port]ssh://127.0.0.1ssh://localhostssh://root@localhost:2222
- Short form: n/a
File
Local file.
- URI form:
file:[//]<path>file:///absolute/file.txtfile://relative/file.txtfile:relative/file.txt
- Short form:
<path>(if path exists locally)/absolute/file.txtrelative/file.txt
Kubernetes
Kubernetes pod.
- URI form:
k8s:[//][user@][cluster][/namespace]/<name>[#container][?kind]k8s:coredns-ff8999cc5-x56jwk8s:kube-system/coredns#coredns?deploymentk8s://user@cluster/kube-system/coredns#coredns?deployment
- Short form: n/a
Developing
Project Structure
CLI
The astu command line interface. Parses flags and loads configuration.
Library
Core logic: types and drivers for target resolution and action execution.
Coding Style
Commit Messages
Uses pre-commit for enforcing Conventional Commits. This is used for automating the release process.
Initialize pre-commit's Git hooks in this repo after first clone:
pre-commit install
Release
Inspired by this post. Uses conventional commits for automatically bumping versions.
TODOs
- Eliminate all usages of
dynin favor ofenum_dispatch - Eliminate all usages of
async_traitfor more readable documentation - Investigate if
internmentis really necessary for the target graph - Investigate why release PRs are being made even though no Rust changes have been made