Skip to content

CLI Reference

The softfig binary is the command surface over the five pillars. This page catalogs every subcommand and its flags as they ship in the 1.0 alpha. The other binaries (softfig-keeperd, softfig-growlightd, softfig-mcp, softfig-tui, growlight-gui) are listed under Other binaries.

Two flags recur across almost every command:

  • --garden <path> — the garden root. Omit it and the command uses the current directory (or the nearest ancestor containing a .softfig/).
  • --socket <path> — the keeperd Unix socket. Defaults to $XDG_RUNTIME_DIR/softfig-keeperd.sock. (softfig growlight commands that talk to the fleet daemon use $XDG_RUNTIME_DIR/softfig-growlightd.sock instead; noted per command.)

Commands that read or write history auto-detect a running daemon and route through it; see Daemon routing.

Command Purpose
softfig onboard [--garden-root P] [--state-root P] [--machine NAME] [--customize] [--yes] Scaffold a fresh garden from the embedded skeleton, init the vault, and write a born-in-FUSE genesis commit. Prompts for a passphrase and prints a 12-word recovery phrase once.

--garden-root defaults to ~/soft-fig_garden; --state-root to $XDG_DATA_HOME/softfig/<garden-dir>/; --machine to the system hostname. --customize interactively picks which concept dirs to include; --yes accepts the defaults and skips the dir prompts (a TTY is still required for the passphrase). Walk-through: the first-garden tutorial.

Key-lifecycle commands run in direct mode (no daemon). The Layer B commands (seal, unseal, list-sealed) and reveal need the daemon. The primitives behind these are in Crypto.

Command Purpose
softfig vault init [--garden P] Initialize a fresh vault under <garden>/.softfig/vault/.
softfig vault status [--garden P] [--identity] Print the declared recovery policy, the key wrappings actually on disk, any divergence between the two, and the master-key generations. Works on a locked vault with no passphrase; --identity additionally prompts for the passphrase and prints the identity fingerprint.
softfig vault recovery declare <policy> [--garden P] Record the intended recovery policy: passphrase-only, passphrase+phrase, passphrase+peers, or any +-joined mix of passphrase, phrase, peers. Records intent only; changes no keys. A mismatch shows up in vault status as a divergence.
softfig vault recovery add phrase [--garden P] Generate a fresh 12-word recovery phrase, wrap the vault key under it, and declare it. Shown once. Refuses if a phrase wrapping already exists.
softfig vault recovery drop <phrase|passphrase> [--garden P] Remove a wrapping and update the declared policy. Refuses to remove the last wrapping, one without a verified escrow copy of what remains, or the one the current session authenticated through.
softfig vault escrow export --out P [--force] [--garden P] Write every wrapping of the vault key plus its parameters to one inert file (about 1 KiB). Needs no passphrase and works on a vault you can’t unlock. --force replaces a differing existing bundle.
softfig vault escrow verify <path> [--integrity-only] [--no-record] [--garden P] Re-hash the bundle against its manifest, then prompt for the passphrase and actually unwrap the key from it. Never modifies the vault. --integrity-only skips the passphrase and the proof (bytes only, nothing recorded); --no-record skips writing the verification record.
softfig vault rewrap [--target-ms N] [--m-cost KiB] [--t-cost N] [--p-cost N] [--new-passphrase] [--allow-downgrade] [--garden P] Re-derive the passphrase-wrapped key under heavier Argon2 parameters without touching data, blobs, or commit hashes. With no cost flags it calibrates m_cost to --target-ms (default 1000). Won’t go below the baseline or the vault’s current cost unless --allow-downgrade. --new-passphrase also changes the passphrase.
softfig vault rotate-key [--garden P] Generate a new master-key generation and make it active.
softfig vault recover [--garden P] Unlock with the recovery phrase and re-wrap the vault key under a new passphrase.
softfig vault seal <pattern> [--socket P] Append a glob (**, *, ?, […], {a,b}) to the sealed-paths list; the daemon commits the change and encrypts every matching tracked file (Layer B).
softfig vault unseal <pattern> [--socket P] Remove a glob from the sealed set. Does not bulk-decrypt blobs already sealed.
softfig vault list-sealed [--socket P] Print the active globs and the tracked files that match them.
softfig reveal <path> [--id <region-id>]… [--socket P] Decrypt a sealed file to a 0600 temp file under $XDG_RUNTIME_DIR and print only the path, never the contents. --id reveals one inline <vault id="…"> region (repeatable; one temp file per id; charset [a-zA-Z0-9_-]+, max 64 bytes). Records an audit commit.

The day-to-day workflow is the Secrets guide. There is deliberately no MCP verb for any vault operation; see MCP Verbs.

Command Purpose
softfig init [--garden P] Write a genesis commit over an existing vault (onboard does this for you).
softfig commit --intent <name> [-m <msg>] [-f <path>]… [--kv k=v]… [--payload-json <json>] [--garden P] Snapshot the working tree as a commit. --intent is a closed enum (softfig commit --help lists it). -f attaches related paths, --kv free-form string metadata; --payload-json passes a raw JSON payload and overrides the other three.
softfig log [--limit N] [--garden P] Print history from the tip. --limit 0 (default) means no limit.
softfig show [<hex>] [--garden P] Show one commit and its root-tree entries. Defaults to the tip.
softfig fsck [--garden P] Verify object hashes, tree hashes, signatures, and reachability.

The per-device daemon owns all writes, runs the filesystem watcher, and serves the FUSE plaintext view. In normal use it runs as the softfig-keeperd systemd user unit rather than daemon start in a terminal.

Command Purpose
softfig daemon start [--garden P] [--socket P] Run the daemon in the foreground. Reads <garden>/.softfig/keeper.toml; with a state_root set it enters FUSE mode.
softfig daemon stop [--socket P] Send shutdown to a running daemon.
softfig daemon status [--socket P] Query daemon state (locked/unlocked, mount, …).
softfig daemon unlock [--socket P] Prompt for the vault passphrase and forward it to the daemon; mounts the FUSE view.
softfig daemon cycle [--socket P] [--timeout SECS] Arm a one-time relock token, stop the daemon, wait (default 30 s) for systemd to respawn it locked, then redeem the token, so a rebuilt binary comes up unlocked with no passphrase. Requires [growlight] allow_relock.
softfig daemon relock-arm [--socket P] Fallback: arm the token for an out-of-band restart; prints the token file path. Prefer cycle.
softfig daemon relock [--socket P] Fallback: redeem the token armed by relock-arm after the daemon restarted locked.
Command Purpose
softfig deploy [--garden-root P] [--cache-root P] [--dry-run] [--force] Materialize the garden’s config/deploy.toml source→target table onto the filesystem. Requires the garden unlocked.

--cache-root defaults to $XDG_DATA_HOME/softfig/deployed. --dry-run prints the plan and exits. --force backs a conflicting target up to <target>.softfig-bak and overwrites, instead of refusing. See the Deploy Dotfiles guide and config-files → deploy.toml.

Command Purpose
softfig migrate [--garden P] With no subcommand, print the current migration phase.
softfig migrate prepare [--garden P] [--state-root P] Copy .softfig/ to the XDG state dir and write keeper.toml. Deletes nothing; refuses if a daemon is reachable.
softfig migrate finalize [--socket P] Ask the running daemon to delete the orphan plaintext tree and legacy .softfig/, then remount FUSE.
softfig migrate split [--apply] [--socket P] Rewrite legacy notes.md / troubleshooting.md monoliths into numbered-note folders and archive the originals.
softfig migrate config [--apply] [--socket P] Lift post-unlock policy ([net]/[relay]/[replica]/[reveal]) into the in-garden config/keeper.toml.
softfig migrate reindex [--apply] [--socket P] Re-derive every daemon-managed softfig:index table from the notes it summarizes and report drifted rows. Re-runnable.

split, config, and reindex are dry-run unless --apply is passed.

Pairing, backup replication, and shared subtrees. All talk to the daemon, which must be unlocked and have networking configured; see config-files → keeper.toml.

Command Purpose
softfig pair [<fingerprint>] [--endpoint H:P] [--yes] [--socket P] Pair with another device (Noise XX handshake, then SAS confirmation). Omit the fingerprint to pick a LAN-discovered device by name. --endpoint dials host:port instead of mDNS. --yes skips the SAS prompt; use it only if you already compared the codes elsewhere.
softfig peers [--socket P] List paired devices and pending pairings.
softfig unpair <fingerprint> [--socket P] Remove a device from the ring.
softfig replica grant <fingerprint> [--socket P] Allow a paired peer to host this device’s encrypted chain backup (adds it to push_to). The host must also opt in with [replica] host.
softfig replica revoke <fingerprint> [--socket P] Stop pushing to that peer. Can’t un-send ciphertext it already holds.
softfig replica status [--socket P] Show who we push to, whether we host, and per-peer mirror stats.
softfig shared-subtree add <mount-path> [--id ID] [--socket P] Register a ring-shared subtree at a garden-relative path. Machine dirs and overlaps are rejected. --id defaults to the path’s last component.
softfig shared-subtree remove <id> [--socket P] Un-share a subtree (drops membership).
softfig shared-subtree enable <id> [--socket P] Re-enable a share on this device (local toggle).
softfig shared-subtree disable <id> [--socket P] Disable it on this device; the path falls back to the device chain.
softfig shared-subtree list [--socket P] List shares with per-device enabled state.
softfig shared-subtree accept <id> [--mount-path P] [--socket P] Accept a share offered by a peer, mounting it at a path you choose (default: the sharer’s recommendation).
softfig shared-subtree migrate-into-share <id> <from> [--socket P] Move existing content at a device path into an already-keyed share, re-encrypted under the share’s key.

Cross-device sync is an early surface in this alpha. See Status for what is exercised today.

softfig growlight … is the CLI for the work loop. Commands marked growlightd talk to the fleet daemon’s socket ($XDG_RUNTIME_DIR/softfig-growlightd.sock, override with --socket); the rest go to keeperd. See Growlight → Running.

Command Purpose
growlight init [--socket P] Scaffold the growlight/ area into an onboarded garden: protocol, session policy, backlog and baton-log skeleton, nav wiring. Idempotent; needs the garden unlocked.
growlight start [--backend claude|opencode] [--opencode] [--model SPEC] [--no-launch] [--garden-root P] [--config-dir P] [--socket P] Set up the loop runtime under $XDG_CONFIG_HOME/softfig/growlight/ and launch one interactive agent session in loop mode. --backend, --opencode (shorthand), and --model are mutually exclusive; with none, a TTY gets a picker. --model takes claude, reasoning, flash, auto, or provider/model; auto uses the model the active backlog item declares. --no-launch writes the runtime files and stops. Unattended work is driven by growlightd, not by this command.
growlight status [--socket P] growlightd. Fleet status: state, garden root, policy, admission gate, agents.
growlight watch [--socket P] growlightd. Tail the live event stream (agent activity, budgets, leases, bus messages) until Ctrl-C.
growlight stop --agent ID [--level after-slice|after-iteration|hard-kill] [--socket P] growlightd. Stop one agent. The first two record a boundary the loop honors at the next handoff (default after-slice); hard-kill interrupts immediately.
growlight pause [--socket P] growlightd. Close the admission gate: no new agents are admitted until resume. Idempotent.
growlight resume [<item>] [--queue Q] [--socket P] growlightd. With no item, reopen the admission gate. With an item, flip that blocked backlog item back to queued; --queue disambiguates an id present in several queues.
growlight say <body> [--to AGENT|@all|@human] [--kind KIND] [--socket P] Post to the coordination bus as the human. --to defaults to @all; --kind is info (default), coord-request, lease-request, question, alert, or restart-request.
growlight reports [--tag TAG]… [--all] [--socket P] List filed reports carrying every given tag (ns:* matches any value in a namespace). Hides closed reports unless --all.
growlight resources show [--socket P] growlightd. Show the per-agent build-resource caps.
growlight resources set [--build-jobs N] [--memory-high V] [--cpu-weight N] [--socket P] growlightd. Adjust the soft caps live. --memory-high and --cpu-weight hit running agents at once; --build-jobs applies from the next spawn. There is no hard cap flag.

The queue is the ordered list of work the fleet draws from. These are thin clients over the matching MCP verbs, all on keeperd, and every mutating form takes --dry-run (validate and render the would-be state, write nothing).

Command Purpose
growlight queue [show] [--socket P] Show the queue: one row per entry with id, kind, title, project, lane, status, after links.
growlight queue pick [--member ID] [--growlightd-socket P] growlightd, read-only. What the scheduler would assign a member now, and why each skipped entry was skipped.
growlight queue project add <name> <repo> [--main BRANCH] [--dry-run] Register a project: a name bound to a repo (local path or host:path) and its merge branch (default main).
growlight queue add <milestone|task|restack> [<slug>] --project P [--title T] [--mission M] [--criteria C] [--urgent] [--after IDS] [--front|--before ID|--behind ID] [--dry-run] Create an entry and its row. A milestone gets docs plus a slice list; a task a numbered doc; a restack is an urgent no-branch entry (project only; no slug). --mission/--criteria accept @file. --after takes comma-separated ids. Default placement: back of the normal lane.
growlight queue move <id> <front|back|before|behind> [<ref>] [--dry-run] Reorder within the entry’s lane.
growlight queue lane <id> <urgent|normal> [--dry-run] Change lane.
growlight queue after <id> [<ids>…] [--dry-run] Replace the entry’s after links; no ids clears them.
growlight queue status <id> <queued|blocked|deferred|done> [--dry-run] Set status (queued also un-blocks and un-defers; done archives the row).
growlight queue drop <id> --reason R [--dry-run] Archive the row as dropped. --reason accepts @file.
growlight slice insert <milestone> <slug> --body B [--title T] [<position> [<ref>]] [--dry-run] Append a numbered slice to a milestone. --body accepts @file; position is front/back/before/behind.
growlight slice move <milestone> <id> <front|back|before|behind> [<ref>] [--dry-run] Reorder a slice.
growlight slice status <milestone> <id> <queued|blocked|deferred|done> [--dry-run] Set a slice’s status.

active, paused, and split statuses are set by the daemon and are not accepted by the CLI.

commit, log, show, and fsck auto-detect a running daemon and route through the IPC socket, with no per-command passphrase prompt. They fall back to direct mode only when the socket is absent.

  • Direct mode, no daemon: vault init | status | recovery | escrow | rewrap | rotate-key | recover.
  • Daemon if present, else direct: commit | log | show | fsck.
  • Daemon required: vault seal | unseal | list-sealed, reveal, deploy, migrate finalize | split | config | reindex, daemon cycle | relock*, and every cross-device and growlight command.

Daemon-side errors (e.g. vault_locked) surface verbatim rather than falling back.

Binary Flags Role
softfig-keeperd --garden P (required), --socket P, --no-watcher The per-device daemon. Normally run by its systemd user unit; softfig daemon start execs it.
softfig-growlightd --socket P, --keeperd-socket P, --garden-root P, --max-concurrent-agents N The fleet control plane: the one process that runs growlight agent sessions. Fleet spawning is off until enabled in config/growlight.toml (config).
softfig-mcp none Stdio MCP server spawned by Claude Code; see MCP Verbs.
softfig-tui none Terminal UI over the daemon: browse the garden, read history, run write actions, unlock, and watch the growlight fleet.
growlight-gui none Experimental fleet console built on iced. Only built with --features gui and not part of the default build.

Related: MCP Verbs, Config Files, and Install.