Appearance
CLI
datum is the command-line client for a datum-agent daemon. It speaks DCP (the Datum Control Protocol) and gives you the job control surface — list, inspect, start, drain, stop, restart — plus live lifecycle-event and stream-metric feeds. It is the same typed DCP client the TUI and cluster tooling use, wrapped in a clap CLI.
sh
cargo install datum-cli # installs the `datum` binaryEverything the CLI does goes through a running datum-agent; start one first (see the datum-agent guide). The CLI connects to the loopback TCP dev listener by default.
Connecting
There are two transports, matching the two DCP listeners:
- Loopback TCP (local dev) — the default. No flags needed; the CLI connects to
127.0.0.1:9555. Point it elsewhere with--addr host:port. The agent refuses to bind a plaintext listener to a non-loopback address, so this stays a local-only convenience. - QUIC + mTLS (remote) — pass all three of
--tls-ca,--tls-cert,--tls-key(PEM or DER). This is the default for anything non-local.
sh
# Local dev — loopback TCP.
datum ps
# A non-default local port.
datum --addr 127.0.0.1:9600 ps
# Remote agent over QUIC + mTLS.
datum --addr agent.internal:9555 \
--tls-ca ca.pem \
--tls-cert client.pem \
--tls-key client-key.pem \
psPassing some-but-not-all of the TLS flags is a usage error: "QUIC+mTLS mode needs --tls-ca, --tls-cert, and --tls-key; pass all three or omit all three for loopback TCP."
Global options
These apply to every subcommand:
| Flag | Meaning |
|---|---|
--addr <ADDR> | DCP address. Default 127.0.0.1:9555. |
--tls-ca <PATH> | CA certificate for QUIC+mTLS mode. |
--tls-cert <PATH> | Client certificate for QUIC+mTLS mode. |
--tls-key <PATH> | Client private key for QUIC+mTLS mode. |
--json | Emit machine-readable JSON instead of human tables. |
$ datum --help
Manage Datum jobs through the Datum Control Protocol
Usage: datum [OPTIONS] <COMMAND>
Commands:
ps List jobs
nodes List cluster nodes
status Show one job
start Start a registered factory
submit Submit a job through cluster placement
drain Gracefully drain a job
stop Stop a job immediately
restart Restart a job
events Tail lifecycle events
metrics Show stream metrics for one job
help Print this message or the help of the given subcommand(s)nodes and submit, and the --cluster flag on ps/status/drain/stop, apply when the agent is a cluster node — see Cluster subcommands.
Subcommands
datum ps — list jobs
$ datum ps
JOB ID STATE DESIRED GEN STARTS RESTARTS ACTIVE
------------ -- ------- ------- --- ------ -------- ------
ingest 1 Running Running 2 1 1 4
daily-rollup 2 Drained Stopped 1 1 0 -With no jobs, datum ps prints no jobs. ACTIVE is the count of active stream instances (a dash when the job reports none).
datum status <job> — inspect one job
$ datum status ingest
FIELD VALUE
-------------------- -----------------------
job ingest
id 1
state Running
desired Running
generation 2
starts 1
restarts 1
last_start_ms 1751622013422
last_exit_ms 1751621948217
last_exit_reason restart policy: backoff
backoff_remaining_ms -
drain_remaining_ms -
drain_supported true
active_streams 4datum start <factory> [--name <name>] [--param k=v ...]
Jobs start from registered factories, not arbitrary blueprints — the daemon registers named JobSpec factories at startup and you start instances by factory name (closures don't cross the wire; see the datum-agent guide). --name gives the instance a name (defaults to the factory name); repeat --param key=value to pass parameters to the factory.
$ datum start ticker --name ingest --param shard=0
started 'ingest' from factory 'ticker' — state Running, generation 1A malformed --param fails before connecting, with exit code 2:
$ datum start ticker --param bad-param
invalid --param 'bad-param'; use --param key=value.datum drain <job> — graceful drain
Drain flips the job's kill switch: intake stops, in-flight elements finish, then the job settles. The CLI polls until the job reaches a terminal drain state:
$ datum drain ingest
drained 'ingest' — in-flight streams completedIf the job hasn't settled within the poll window, the CLI reports the current state and points you at datum status <job> to keep watching. Jobs whose factory doesn't wire in a kill switch report drain_supported = false and must be stopped instead.
datum stop <job> — stop immediately
$ datum stop ingest
stopped 'ingest' — state Stoppeddatum restart <job> — restart
Rematerializes the whole graph and advances the generation:
$ datum restart ingest
restarted 'ingest' — state Running, generation 2datum events [--follow] — lifecycle feed
Without --follow, the CLI collects the events available now and prints a table (or no events observed — run \datum events --follow` to keep waiting`):
$ datum events
SEQ TIME_MS JOB ID GEN KIND DETAIL
--- ------------- ------ -- --- --------- -----------------------
1 1751622013001 ingest 1 1 Submitted -
2 1751622013010 ingest 1 1 Started -
3 1751622048217 ingest 1 2 Restarted restart policy: backoffWith --follow, it streams events one per line until interrupted:
$ datum events --follow
1 Submitted 'ingest' generation 1
2 Started 'ingest' generation 1
3 Restarted 'ingest' generation 2Events carry monotonic sequence numbers, so a client can detect a gap and resync with ps/status.
datum metrics <job> [--follow] — stream metrics
Instrumentation samples for one job's streams (opt-in per-job counters from the factory). Element counts are grouped for readability:
$ datum metrics ingest
TIME_MS STREAM ID STATE ELEMENTS RESTARTS UPTIME_MS
------------- -------- -- ------- --------- -------- ---------
1751622061044 ingest:2 7 Running 1,204,338 1 47622--follow renders a fresh table per metric tick. If the job has no metrics yet, the CLI prints no metrics for '<job>' yet.
Cluster subcommands
When the agent you connect to is a cluster node (a ClusterAgent), these commands read and drive the whole cluster over DCP. They fan out to peer node sessions with bounded partial-result semantics: an unreachable node degrades to a row in a trailing NODE / ERROR table instead of hanging the command.
datum nodes — membership & session view
$ datum nodes
NODE MEMBER UNREACHABLE SESSION AGENT_ADDR ROLES
------ ------ ----------- --------- --------------- -------------
node-0 Up false connected 127.0.0.1:9555 agent,orders
node-1 Up false connected 127.0.0.1:9556 agent,workerMEMBER is the member state; UNREACHABLE is the orthogonal reachability flag; SESSION is this node's DCP session state to that peer (connected, connecting, backing_off:…, closed). With no nodes it prints no cluster nodes.
datum ps --cluster — jobs across the cluster
$ datum ps --cluster
NODE ADDRESS JOB ID STATE DESIRED GEN STARTS RESTARTS CLUSTER PLACED_ON PGEN
------ -------------- ---------- -- ------- ------- --- ------ -------- ------- --------- ----
node-0 127.0.0.1:9555 placed-job 3 Running Running 1 1 0 yes node-1 1
node-1 127.0.0.1:9556 remote-job 1 Running Running 1 1 0 - - -CLUSTER marks jobs the coordinator placed; PLACED_ON is the assigned node and PGEN the placement generation. With no jobs anywhere it prints no cluster jobs.
datum submit --cluster — place a job through the coordinator
Cluster jobs use the same registered-factory model as start (closures don't cross the wire). submit hands the factory + parameters to the placement coordinator, which chooses a node:
$ datum submit --cluster --factory ingest --name ingest-eu --param shard=0
submitted cluster job 'ingest-eu' from factory 'ingest' to node 'node-1' — state Running, placement generation 1Placement flags:
| Flag | Effect |
|---|---|
| (none) | LeastJobs — the eligible node with the fewest cluster jobs, tie-broken by node id |
--role <r> | constrain eligibility to members advertising role r |
--node <id> | pin the job to a specific node |
datum submit requires --cluster; without it the CLI fails with a usage error (exit 2) and points you at datum start for local jobs.
--cluster on status / drain / stop
status, drain, and stop accept --cluster <job> to target a cluster-placed job by name, routing to whichever node currently runs it rather than the connected node's local registry:
$ datum status --cluster ingest-eu
$ datum drain --cluster ingest-eu
$ datum stop --cluster ingest-euSee the datum-cluster guide for placement strategies and re-placement semantics.
--json for scripting
Add --json (globally) for machine-readable output. ps/status/start/ stop/drain/restart print a single pretty JSON document; the --follow feeds print one compact JSON object per line (newline-delimited JSON).
$ datum --json status ingest
{
"job": {
"name": "ingest",
"job_id": 1,
"state": "Running",
"desired_state": "Running",
"generation": 2,
"starts_total": 1,
"restarts_total": 1,
"last_start_at_ms": 1751622013422,
"last_exit_at_ms": 1751621948217,
"last_exit_reason": "restart policy: backoff",
"backoff_remaining_ms": null,
"drain_remaining_ms": null,
"drain_supported": true,
"active_streams": 4
}
}ps wraps the array in {"jobs":[…]} ({"jobs":[]} when empty); action commands wrap in {"action","message","job"}. Follow lines look like:
$ datum --json events --follow
{"event":{"sequence":2,"timestamp_ms":1751622013010,"name":"ingest","job_id":1,"generation":1,"kind":"Started","detail":""}}Fields map straight onto the DCP wire types, so jq and friends work as expected — datum --json ps | jq '.jobs[] | select(.state=="Running").name'.
Exit codes
| Code | Meaning | Examples |
|---|---|---|
0 | Success. | any command that completed |
1 | The agent rejected the request. | unknown job/factory, name conflict, unauthorized, protocol mismatch |
2 | Usage error or could not connect. | bad flags/args, malformed --param, agent unreachable |
A rejected request (exit 1) prints the agent's reason plus a targeted fix:
$ datum status missing
datum-agent rejected the request: job "missing" not found
Check the job or factory name with `datum ps`.A connection failure (exit 2) tells you how to reach an agent:
$ datum ps
could not connect to datum-agent at 127.0.0.1:9555: connection refused
start datum-agent or pass --addr host:port; loopback TCP defaults to 127.0.0.1:9555These codes are stable enough to branch on in scripts: 0 = done, 1 = the agent said no (don't retry blindly), 2 = fix your invocation or start/point-to an agent.
See also
- Agent — the daemon the CLI talks to: jobs, drain, restart, DCP.
- Membership — membership, downing, and job placement behind
nodes,ps --cluster, andsubmit --cluster.