CLI reference
The client is a set of one-shot verbs: dial the daemon, perform one
request, print (human or --json), and exit. Every verb also supports a global
--json flag for machine-readable output that mirrors the daemon RPC contract.
Common flags on every call:
--socket PATH— daemon socket (defaults to the daemon's default path).--config PATH—harness.tomlpath (defaults to~/.config/harness/harness.toml).--json— machine-readable output.
Lifecycle verbs
harness start <name> # start a harness (or a project harness)
harness stop <name> # stop a harness
harness restart <name> # restart a harness
harness start --all # start/stop/restart every harness at once
<name> resolves to <project>/<name> inside a project (see
Projects); --all keeps its daemon-wide meaning. --all runs
render live per-harness progress with a Bubble Tea animation so a large fleet
start/stop is visible as it converges.
The client warns when its own build is older or newer than the daemon's (client/daemon skew) — after upgrading, restart the daemon so both sides speak the same protocol version.
Listing & inspection
harness list # table of every harness: name, state, enabled, restarts, PID
harness ps # inside a project: only that project's harnesses
harness describe <name> # one harness in detail (state, harness kind, backend, flapping, ...)
harness daemon status # daemon version, proto, PID, uptime, socket, active profile
list and describe also surface schedule metadata for scheduled
one-shots (see Scheduled jobs under Configuration). In the
listing, a scheduled harness is marked inline rather than by extra columns: its
state glyph becomes a clock (⏱, in the same colour, so the state still reads at
a glance) and its next firing is appended to the description as a relative time
— sweeps the fleet · in 4h3m. describe shows the full picture: the cron
spec verbatim plus the absolute time of the next firing.
The cron spec itself is config, not status, so it stays off the listing
surface; reach for describe or --json when you need it.
describe additionally lists the harness's live attach sessions — who is
attached right now, and whether each session is read-only.
--json on any of these emits the same data as structured JSON.
Scheduled jobs
Scheduled one-shots fire daemon-side on a cron schedule — no verb to remember,
just configure schedule on a prompt harness (see
Configuration → Scheduled one-shots).
harness list gives every harness a SCHEDULE column (the cadence, e.g.
daily 10:00 UTC, or the raw expression when it cannot be paraphrased) and a
NEXT column (the countdown, e.g. in 2h), both derived from the config and
the running scheduler — never from the description text. A job waiting for its
next firing reads ⏱ armed rather than stopped, because it is loaded and
will fire on its own; harness describe reports armed instead of an
enabled that is false for every scheduled harness by construction.
harness jobs # every scheduled harness: next run, last run, consecutive failures
harness runs <name> # its run history, newest first (--limit N, default 20)
harness trigger <name> # run it now — on_overlap applies, as for a firing
harness trigger <name> --wait # …stream the run's log and exit with its exit code
harness logs <name> --run 3 # what run 3 did (--raw for its own log)
trigger --wait exits with the run's own exit code, 124 when the run timed
out, and 75 when it was skipped because a run was already in flight — so a job
scripts like the command it wraps. The verb is trigger because harness run
starts a throwaway scratchpad.
--wait polls the run history rather than consuming events, because history is
authoritative even when an event is dropped. When the trigger was queued behind
a run already in flight, --wait attaches to the oldest manual, non-skipped run
newer than the moment it was issued — so if two manual triggers fire
concurrently, either may pick up the other's run and both stream the same log.
Scheduled firings never collide this way.
Logs
harness logs <name> # tail (default 200 lines)
harness logs <name> --lines 50 # a specific number of trailing lines
harness logs <name> --follow # stream new output as it arrives
harness logs <name> --run 3 # one run of a scheduled harness (see harness runs)
When a log rotates or truncates, --follow reprints the current tail so you
never silently lose context.
Profiles
harness profiles # list profiles and which is active (*)
harness use-profile <name> # switch the active profile
Profiles (a "configuration of harnesses") switch whole sets at once. Only one
is active at a time; harness list flags it with *. See
Configuration.
Reload & diagnostics
harness reload # re-read config, reconcile running harnesses
harness doctor # health check battery (config, daemon, versions, remote SSH, harnesses)
reload picks up config changes without restarting the daemon. describe
shows a config — changed — restart to apply row when the running harness is
out of date with the config file.
Attach
harness attach <name> # attach to a harness as a live terminal
harness attach <name> --ro # read-only: attach but ignore keystrokes
attach reuses the same full-window terminal the dashboard uses, with the
1-line status bar and tmux-style detach chords. See Cockpit TUI.
Project verbs
harness up # bring the enclosing project up (detached)
harness down [PROJECT] # stop & deregister a project's harnesses
harness ps # project-scoped listing
See Projects for the full story, discovery rules, and the project file schema.
Daemon subcommands
harness daemon is its own subcommand group:
harness daemon # run the supervisor in the foreground (== daemon run)
harness daemon stop # ask the running daemon to shut down (SIGTERM)
harness daemon status # one-shot: daemon info
harness daemon --detach # fork into the background (dev convenience)
Daemon flags: --config, --socket, --scrollback N (per-harness ring depth),
--ssh, --ssh-listen, --log-level, --log-file, --detach.
Exit codes & error handling
Every error is classified and rendered as a styled error box with an actionable
hint. A --json error still prints a structured object. Exit code is 0 on
success, non-zero on failure (doctor returns non-zero when any check fails;
trigger --wait returns the run's exit code, as described under
Scheduled jobs).