CLI reference

Every hostssh command the agent binary dispatches — purpose, key flags, exit codes, and what each does — grounded in agent/internal/cli/cli.go.

CLI reference

The hostssh agent is the CLI: one static Go binary whose first argument selects a command. This page enumerates every command the dispatcher actually implements, grouped by area, with the flags each parses. It is a code-grounded reference — the source of truth is agent/internal/cli/cli.go, where Run() switches on args[0] and each cmd* function declares its own flag.FlagSet.

For the four-surface philosophy (CLI ⇄ REST ⇄ MCP ⇄ dashboard parity) see API & CLI; for the wire the agent daemon speaks see Agent protocol.

How dispatch works

Run(args) (cli.go) is the entry point:

  1. With no args it prints usage and exits 0.
  2. It calls enforceLicense(args[0]) — see Licensing.
  3. It switches on args[0] to the matching cmd* handler.
  4. Unknown commands print usage to stderr and exit 2.

Add --json to the read commands (status, doctor, fleet, audit, drill, and transfer --new-key) for machine-readable output. Not every command takes --json — only the ones noted below.

Honesty note. clone, migrate, and provision are advertised in the binary's own usage text but are not implemented in this build. Invoking any of them prints `hostssh <cmd>` is on the roadmap and not in this build yet. and exits 3 (see the case "clone", "migrate", "provision" arm in Run()). They are documented here as roadmap so the usage text and this page agree.

Licensing & exit codes

enforceLicense (cli.go) gates a fixed set of licensed featuresgatedCommands in the source: deploy, db, mcp, clone, migrate, transfer, provision, ssh. Recovery (capture, restore) and all read/admin commands are deliberately not gated — your data is never held hostage.

StatusBehavior
Activecommand runs
Grace (offline)command runs; prints ⚠ HostSSH offline grace: ~N day(s) left
Blockedprints how to recover + activate, exits 12

Other exit codes seen across handlers: 0 success · 1 command failed · 2 usage/flag error · 3 advertised-but-unimplemented (clone/migrate/provision) · 12 license-denied. doctor exits 1 if any check is fail, else 0.


Lifecycle — agent, status, doctor, version, help, license

CommandPurposeKey flags
hostssh agentRun the long-lived daemon (the systemd service). Starts the heartbeat, job-drain, Web-SSH poll, and daily reaper goroutines and serves /healthz, /events, /logs on cfg.Listen.--config PATH
hostssh statusHealth + identity: version, host, state, control plane, listen, channel.--json, --config PATH
hostssh doctorCheck local prerequisites and env wiring — config readable, control-plane HTTPS, license token, mTLS cert/key, docker, hostpack, BuildKit (BUILDKIT_HOST), reachability (public vs private/CGNAT), cloudflared (named tunnel + systemd), engine scripts, cleanup policy.--json, --config PATH
hostssh version (-v, --version)Print hostssh <Version>.
hostssh help (-h, --help)Print the usage block.
hostssh license activateActivate a key against the control plane; writes the client cert/key + config and starts the offline-grace clock. Only activate is implemented (no status/transfer subcommand in this build).--key KEY (required), --api URL (default https://api.hostssh.com), --config PATH

doctor is the fastest triage: it never mutates anything, and a fail row (e.g. missing engine scripts or an unreachable BUILDKIT_HOST) is what exits 1. Warnings (e.g. cloudflared absent, private/CGNAT-only addresses) are non-fatal — a Node that never exposes a port doesn't need a tunnel. The agent is pull-only. See tunnel-first join.


Deploy — deploy, db, proxy, logs

CommandPurposeKey flags
hostssh deployBuild → run → route an app through the agent's own pipeline (HostPack/Dockerfile build, Docker run, Traefik route).--name (required), --source (default .), --ref, --domain, --port (default 3000), --readiness-path, --network (default hostssh), --builder (hostpack|dockerfile|image, default hostpack), --image (alias for --source when builder=image), --image-limit-mb, --gpu (all|<count>|<ids e.g. 0,1> — reserve GPUs, needs a GPU node; see GPU support), --migrate, --verify, --command, --slot, --slot-size (s|m|l|xl), --env-file, --env K=V (repeatable), --volume name:/path, plus a trailing -- CMD ARGS argv run verbatim after the image entrypoint
hostssh db createProvision a managed database container on this Node. Postgres provisions pgvector by default; the DATABASE_URL is stored in the on-host secret store.--engine (default postgres; valid set from database.Engines()), --name (required), --network (default hostssh), --version, --force (destroy + recreate — data loss)
hostssh proxy up|down|statusManage the Node's edge proxy (Traefik + automatic Let's Encrypt). up is idempotent.--network (default hostssh), --acme-email (or HOSTSSH_ACME_EMAIL; enables TLS), --staging (LE staging CA), --force (recreate to apply changed config)
hostssh logs <app>Stream a container's logs via the Docker runtime.--follow, --tail N (default 200, 0 = all)

Notes grounded in cli.go:

  • --builder resolves in deploy.go: image runs a prebuilt image from --source; dockerfile builds from a Dockerfile in the source (also auto-detected when a Dockerfile is present); otherwise HostPack builds from source.
  • --slot-size resolves CPU/memory/pids caps via slotCaps(), which mirrors the control-plane slotCaps resolver (base 0.5 vCPU / 512 MB / 256 pids per unit; s=1, m=2, l=4, xl=8). --slot alone only sets the hostssh.slot label; caps stay unbounded without --slot-size.
  • --env-file loads KEY=VALUE lines (blank/# lines skipped, export + quotes stripped, only the first = separates) as the base env; --env flags override it. Parsing is strict — a line that isn't a valid KEY=VALUE fails the deploy with the file and line number rather than being skipped, and the deploy prints how many vars it loaded so a partial env is visible. It works with or without a trailing newline; note that appending to a file that lacks one glues your new var onto the previous value (LAST=valNEW_VAR=x) — check the printed count against what you expect.
  • --readiness-path is probed from the host before the deploy is accepted, and becomes the Traefik active healthcheck path for the route (G24). Give it a cheap endpoint (/api/version), not the SSR homepage. Without it the healthcheck stays /.
  • proxy parses its subcommand before the flags — proxy up --acme-email X works because the handler pulls up off first (a Go flag quirk noted in the source).

For how env and secrets actually reach the container, see Environment variables & secrets.


Backup — capture, restore, drill, retention, prune, audit

CommandPurposeKey flags
hostssh captureCapture this server to an encrypted .hsi image via the capture engine, then (best-effort) register the snapshot in the control-plane ledger. Never license-gated.--tag clone-<name> (normalized), --note NOTE, plus positional [box-name [note]]
hostssh restoreRestore this server from an image via the restore engine. Never license-gated.(passed through to engine.Restore)
hostssh drillRun a real restore into an isolated scratch directory, verify that files materialized, clean the scratch data, and persist the evidence for telemetry. Use --mode check only for a repository-integrity diagnostic; it does not prove restore.--mode restore|check (default restore), --tag, --include, --scratch, --restic BIN, --sample PCT (check only), --json
hostssh retention get|set|preview|applyShow / configure / preview / enforce the image-retention policy. Defaults to keep-all; set with no keep flags stays keep-all (never silently configures unbounded deletion).--tag, --keep-all, --keep-last N, --keep-daily N, --keep-weekly N, --keep-monthly N, --restic BIN
hostssh pruneReclaim Docker disk: keep last-N images per app, drop dangling images, bound the build cache. Volumes are never pruned.--keep-last, --builder-gb, --builder-until-hours, --dry-run (preview only)
hostssh audit (audit verify)Show or verify the tamper-evident action log. verify checks internal chain consistency (local check only).--json

Notes:

  • capture builds an engine arg plan (capturePlan) from the tag/box/note, runs engine.Capture, then registerCapturedImage POSTs the snapshot to the ledger. Registration failure is non-fatal — the capture already succeeded.
  • prune defaults come from env (HOSTSSH_KEEP_LAST, HOSTSSH_BUILDER_KEEP_GB, HOSTSSH_BUILDER_UNTIL_HOURS) via reaper.PolicyFromEnv(); the flags override per run. The same reaper runs daily inside hostssh agent and is available as the prune job kind.
  • audit is powered by audit.Verify(); most write commands call auditLog to append a best-effort entry (a write failure warns but never aborts the action).

Access — access, ssh, transfer

CommandPurposeKey flags
hostssh ssh <node-fingerprint>Open an interactive shell on a fleet Node through the control plane's Web-SSH relay — no inbound port. Mints a session, dials the relay, raw-mode bridges the local terminal ⇄ the node's PTY. License-gated.--config PATH, --relay wss://… (override)
hostssh access connectRun one outbound Web-SSH session directly (dial relay, open host PTY, bridge). The daemon normally drives this from its poll; the CLI form is for testing/manual use.--relay, --sid, --token, --shell
hostssh transfer --new-keyMint a one-time, scope-locked peer transfer key (Mode 2 source side); persists its hash to the keyring for exactly-once redemption. License-gated.--scope (server|<account>), --endpoint host:port (repeatable), --ttl (default 15m), --relay, --json, --config PATH
hostssh transfer --serveRun the source peer endpoint (HTTPS) that redeems presented keys once and streams the scoped image from --capture-cmd. Prints its SPKI pin for the target to pin.--tls-cert, --tls-key (required), --capture-cmd (required), --listen (default :8443), --capture-timeout (default 2h), --resumable, --stage-dir
hostssh transfer --pull <key>Redeem a key on the target: dial the source (pinned via --source-pin/$HOSTSSH_TRANSFER_PINS), stream the scoped image into --restore-cmd.--from host:port, --restore-cmd (required), --source-pin, --resume, --attempts (default 5)

transfer enforces exactly one mode (--new-key, --serve, or --pull); passing more than one exits 2. The three modes together are the peer-to-peer wedge — see Agent protocol §Control RPCs.


Fleet — fleet, firewall, and roadmap verbs

CommandPurposeKey flags
hostssh fleet servers|backupsList the license's fleet — reporting servers (state, version, last seen) or backup health (last capture, drill pass) — from the control plane.--server (filter backups to one host), --json, --config PATH
hostssh firewall up|statusOne-click host hardening (deny-in, allow-out, SSH always open) or show status.--ssh-port (default 22), --tcp (default 80,443), --udp (e.g. 51820), --reset
hostssh cloneRoadmap — not in this build. Advertised in usage; exits 3.
hostssh migrate --new-ipRoadmap — not in this build. Advertised in usage; exits 3.
hostssh provisionRoadmap — not in this build. Advertised in usage; exits 3.

clone/migrate/provision are the managed-relocation verbs described in API & CLI and Agent protocol; they are license-gated in gatedCommands and reserved in the dispatcher, but the concrete implementation is not present in this build (the case prints the roadmap message and returns 3).


MCP — mcp

CommandPurposeKey flags
hostssh mcpRun the MCP server over stdio so an AI agent drives the platform. Keeps its serverInfo version in lockstep with the binary. License-gated.— (reads stdin, writes stdout)

See API & CLI §The MCP server for the tool catalog and guardrails.


Relationship to the deploy queue

hostssh deploy, db create, prune, firewall, and restore also run as deploy-queue jobs the control plane enqueues and the daemon claims — the same engine, different trigger. hostssh agent is the loop that claims and executes them. For the enqueued-side contract — kinds, JobSpec fields, secret delivery, and the executeJob switch — see Job kinds.


See also: API & CLI · Agent protocol · Job kinds · Environment variables & secrets