Skip to content

CLI reference

The kneo-dash console script (installed by pip install kneo-dash) has three subcommands: serve (the default), recover — a safety-critical break-glass flow for recovering an instance after a database restore — and prune — operator-safe retention for the growth tables (0.8.0). This page documents every subcommand, flag, and exit code; the recovery model itself is ADR-012 §7 and the operator runbook is Backup & recovery.

kneo-dash [serve]                         # run the BFF (default)
kneo-dash recover [--reason TEXT]         # ENTER recovery (arm the gate)
kneo-dash recover --status                # is this instance in recovery? (deployment gate)
kneo-dash recover --reconcile --access-map FILE (--connections FILE | --keep-connections) [--reason TEXT]
kneo-dash prune --older-than-days N --export-dir DIR --by OPERATOR [--table T] [--vacuum] [--dry-run]

serve (default)

kneo-dash with no subcommand (or kneo-dash serve) starts the BFF. It is a convenience launcher for local/dev use — it binds 127.0.0.1:8090 with autoreload, so it is not how you run a real deployment.

In production, run uvicorn directly (this is what the shipped container does):

uvicorn kneo_dash.app:app --host 0.0.0.0 --port 8090

The container's CMD is exactly that; put it behind the TLS-terminating reverse proxy from the Deployment guide. See also the post-deploy checklist.

recover — restore recovery (break-glass)

When the instance detects a database restore (an older backup landed, per the restore sentinel), it arms recovery: get_client fails closed and readyz returns 503 until an operator reconciles it. recover is how you inspect and clear that state. It is out-of-band — run it against the same DB (KNEO_DASH_DB_URL), not through the running server.

recover (no flags) — enter recovery

Manually arm the gate (e.g. before a maintenance restore). --reason TEXT sets the marker reason (default post-restore recovery). Normally you don't need this — a detected restore arms it automatically; SQLite is the certified topology (on Postgres, which has no local sidecar anchor, arming is manual — see Backup & recovery).

recover --status — the deployment gate

Reports whether the instance is in recovery, via the exit code — designed to gate a deploy/orchestrator step:

Exit Meaning
0 not in recovery — safe to serve (not in recovery — safe to serve)
3 in recovery — do not serve; reconcile first (message to stderr)
kneo-dash recover --status || echo "blocked: instance is in recovery"

recover --reconcile — the only exit from recovery

Clears recovery after you supply the post-restore decisions. It is the single supported way out (there is no --clear):

  • --access-map FILErequired. A JSON {role: [capabilities]} Access map to apply (validated, incl. the no-lockout guard — admin must retain settings.write).
  • exactly one connection decision (required):
  • --connections FILE — a JSON {env: {profile: …}} that replaces the restored connections, or
  • --keep-connections — explicitly keep the restored connections as-is.
  • --reason TEXT — optional marker reason.
kneo-dash recover --reconcile --access-map access.json --connections connections.json

Failure is safe: if reconcile fails (bad file, invalid map, …) it exits 1 and recovery is NOT cleared — the instance stays fenced until a successful reconcile. Requiring an explicit connection decision prevents silently serving a restored instance against the wrong (possibly stale/foreign) platform credentials.

prune — operator-safe retention (0.8.0)

Prunes the two growth tables — the audit log and launch history — with export-before-delete and a self-audit record (design: state-store design §Prune & export):

kneo-dash prune --older-than-days 90 --export-dir /backups/prune --by ops@example.com
# scope / preview / reclaim:
#   --table audit_log|launches|all   (default all)
#   --dry-run                        report only; changes nothing
#   --vacuum                         SQLite: rebuild the file so space returns to the OS
  • Export first, always: candidates are written to DIR/<table>-pruned-<utc>.jsonl plus a .sha256 sidecar; the file's first line is a {"_schema": "kneo-dash-prune-export/1", …} header recording the table, store schema version, cutoff, and row count (explicitly versioned). Every target table is exported before any row of any table is deleted, so an export failure aborts with nothing deleted. Deletes target exactly the exported id set — rows appended mid-prune are untouchable.
  • Self-auditing: a store.prune audit row records the --by operator, cutoff, and per-table {rows, export filename, sha256} — so the log documents its own pruning and each row can be matched to its exact export artifact.
  • A completion receipt lands on disk first: after the deletes and before the audit append, DIR/prune-receipt-<utc>.json records the full per-table outcome — so even if the audit append itself fails, the deletion is never unrepresented.
  • If the delete or audit phase fails mid-way (deletes run as batched transactions): the remaining old rows simply stay put — nothing unexported is ever deleted — a best-effort store.prune.partial audit row records the progress, and re-running the same prune converges (the exports, and the receipt if deletes finished, are already on disk).
  • Back up first (a real precondition, not advice), and watch the kneo_dash_store_rows{table=…} gauges to decide when — see Observability for the watch levels.
  • Postgres (best-effort lane): --vacuum is a no-op (autovacuum owns reclamation).

Exit codes (summary)

Command 0 1 3
recover --status not in recovery in recovery (gate)
recover --reconcile reconciled + cleared reconcile failed (still fenced)
  • Backup & recovery — the full restore → reconcile runbook.
  • ADR-012 §7 — the recovery/restore-detection design.
  • Deployment — running the BFF in production (uvicorn + proxy).
  • State store — the recovery marker + restore sentinel.