# Command reference
`openlatch --help` is always current. This page adds what `--help` cannot: what each command guarantees, and how to read what it prints.
## Commands
| `openlatch init` (alias `setup`) | Complete, idempotent reinstall. Reclaims the ports first — identifying the holder **by the port**, not by `daemon.pid` — then proves the daemon it started is the one serving (PID, version, `/health`). |
| `openlatch status` | Dashboard plus the twelve-section verdict, and the License line — state, hosts, fair use, contract end; `events are queued` while the platform is refusing on licensing grounds |
| `openlatch start [--foreground] [--port N] [--model-relay-port N]` | Refuses when a daemon already runs (`OL-1501`, exit 5) |
| `openlatch stop` / `restart` | `stop` also removes the agent's model relay wiring, and says so |
| `openlatch doctor` | Full diagnosis with causes and remedies |
| `openlatch doctor --fix` | Heals what it can, backs up to `.bak` siblings + `~/.openlatch/fix-journal.json`. Never re-enables what you switched off. |
| `openlatch doctor --restore` | Reverses the last `--fix`. Surgical merge preserves your non-OpenLatch hooks. |
| `openlatch doctor --rescue [--since 24h] [--yes]` | Redacted diagnostic ZIP for support |
| `openlatch system <subsystem>` | The per-subsystem verbs, grouped: `auth`, `hooks`, `model-relay`, `proxy`, `supervision`, `telemetry`, `inventory`. Root stays the whole-host commands; `doctor` names the exact `system` command in every remedy. |
| `openlatch system model-relay status` | Full classification, not a port probe |
| `openlatch system model-relay {enable,disable} [-y] [--no-restart]` | Writes `[model_relay] enabled`, then offers the restart that applies it |
| `openlatch system hooks status` | The Hooks section in detail |
| `openlatch system supervision {install,uninstall,enable,disable,status}` | OS-native supervisor (launchd / systemd --user / Task Scheduler), user-scope only |
| `openlatch system inventory {list,log,rescan,status,inspect,projects,ack}` | Configuration-plane monitoring |
| `openlatch system proxy {status,discover,set,clear,test}` | The egress route. See [egress.md](egress.md#commands). |
| `openlatch system auth {login,logout,status}` · `openlatch system telemetry {…}` · `openlatch update` · `openlatch logs` · `openlatch uninstall [--purge]` · `openlatch docs` | |
Noun-verb aliases: `system hooks install` = `init`, `daemon start` = `start`, and so on — identical arguments, pinned by test.
### Internal commands (driven by the platform)
One `system` leaf is spoken by another process, not typed by a person, and is hidden from `openlatch system --help` for that reason.
| `openlatch system evaluate [--describe]` | Pure NDJSON evaluation from supplied inputs; no daemon or configuration required. See [the evaluation protocol](evaluate-protocol.md) for frames, state ownership and its command-specific exit codes. |
`openlatch evaluate` at the root is a deprecated alias of `system evaluate` — identical arguments and exit codes, pinned by test — kept for one release so a platform pinned to the previous client keeps working while it moves its argv. It is removed in the release after this one.
Global flags: `--json`, `--verbose`/`-v`, `--debug`, `--quiet`/`-q`, `--no-color`. Every diagnostic's JSON carries the same `sections[]` the human rendering shows.
### `init` flags
`init` additionally takes the egress flags — highest precedence, applied per key:
| `--proxy <url>` | The proxy to use. **Never put a credential in it**: command-line arguments are readable by every process on the host (`/proc/<pid>/cmdline`, Win32 `CommandLine`), so a URL carrying `user:pass@` is rejected with `OL-1226`. Use `OPENLATCH_PROXY` or the prompt — both store the password in the OS credential store. |
| `--no-proxy <list>` · `--ca-bundle <path>` · `--proxy-mode <auto\|manual\|direct>` · `--proxy-auth <auto\|none\|basic\|negotiate>` · `--proxy-spn <spn>` | Persisted to `[proxy]`. |
| `--yes` | Never prompt, even on a terminal. An install that would have to ask a question fails instead, naming what it needed — the only way to script an install from an interactive shell. |
| `--dry-run` | Report what would happen to the egress route and exit. Writes no file and creates no directory, not even `~/.openlatch`. With `--json`: `proxy: { action: "keep"\|"discover"\|"persist", source? }`. |
### `init` output
`init` speaks in six stages and ends with one result card. Engineering detail — ports,
PIDs, paths, hook event names — goes to `~/.openlatch/logs/init-YYYY-MM-DD.log` and is
shown only with `--verbose`.
```
┌ Setting up this machine
│
◇ Checked this machine · found Claude Code
│
◇ Signed in · Acme Corp
│
◇ Usage data · sharing anonymous usage data
│
◇ Connected Claude Code · actions now go through OpenLatch
│
◇ Started OpenLatch · running · starts at login
│
◇ Final checks · all checks passed
│
└ Done.
╭──────────────────────────────────────────────────────────────────────╮
│ │
│ ✓ OpenLatch is live on this machine │
│ │
│ Organization Acme Corp │
│ Agent Claude Code │
│ actions controlled · model calls observed │
│ Policies in sync │
│ Runs in the background · starts at login │
│ │
│ Next Keep using Claude Code as usual. │
│ See what they do at https://app.openlatch.ai │
│ │
╰──────────────────────────────────────────────────────────────────────╯
Check anytime openlatch status · Full diagnostics openlatch doctor
```
The card is the customer verdict, and the exit status follows it:
| ✓ **OpenLatch is live on this machine** | nothing failed and nothing needs you. A known coverage limit (an agent whose model calls are not relayed) is a coverage line, not a task; a first policy bundle still on its way is waited for up to 10 s, then shown as `first sync in progress · applies on arrival` | 0 |
| ! **OpenLatch is running — one thing needs you** | a warning you have to act on, with its remedy | 7 |
| ! **No AI agent to connect on this machine yet** | no supported agent found. Not an error: OpenLatch is installed, install an agent and run `init` again | 3 |
| ✗ **OpenLatch couldn't finish setting up** | a stage failed, or a check failed at the end. The log path is printed for support | 1, or the error's own code |
`openlatch doctor` is unchanged: the same host can be `DEGRADED` (exit 7) under `doctor`
and live (exit 0) under `init` when the only warnings are known limits.
**No terminal** (MDM, CI, a pipe): no animation and no prompt. One line per finished
stage, the card without its border, and a last line for scripts to grep:
```
Setting up this machine
[1/6] Checked this machine - found Claude Code
[2/6] Signed in - Acme Corp
[3/6] Usage data - off (no terminal to ask)
[4/6] Connected Claude Code - actions now go through OpenLatch
[5/6] Started OpenLatch - running - starts at login
[6/6] Final checks - all checks passed
Done.
…
result=live org="Acme Corp" agents=claude-code exit=0
```
`result=` is `live`, `attention` or `not_live` — the same vocabulary as the `verdict` key
of `init --json`. No agent is `result=attention reason=no-agent exit=3`; a failure adds
`code=` and `log=`. `--json` output is otherwise unchanged; its `exit_code` is the
process's own exit status. `--quiet` prints nothing but errors.
`--foreground` closes the rail with `└ Running in the foreground · Ctrl+C to stop` and
hands the terminal to the daemon; there is no card. When the install script has put
`openlatch` somewhere the calling shell will not find until it restarts, it sets
`OPENLATCH_INSTALL_PATH_HINT` and the card ends with *Open a new terminal to use the
openlatch command*.
## Reading a health report
Every command that reports health — `status`, `doctor`, `system model-relay status`, `system hooks status`, `system proxy test` — renders the same twelve sections from one model, so a state means the same thing wherever you see it.
### States
| `Ok` | `✓` | Enabled **and proven working**. The only green. |
| `Off` | `⚠` | Switched off deliberately. Always carries a code and a remedy. |
| `Degraded` / `Pending` | `⚠` | Working, but not at full capability, or still verifying |
| `Unknown` | `⚠` | Blocked by another section, which it names. One outage, one cross. |
| `Failed` | `✗` | Enabled and not working |
| `NotApplicable` | `·` | Absent from the build, an exercised privacy opt-out, or persistence inside a sandbox |
### Sections
Twelve sections, always all reported, under three groups — the questions a reader asks in the order they ask them:
| **Runtime** | Environment · Daemon · Persistence · Update | is it running, and will it keep running? |
| **Coverage** | Hooks · Model relay · Policy · Inventory · Integrity | is it seeing everything, and enforcing? |
| **Platform** | Connection · Cloud · Telemetry | is any of it leaving this machine? |
`Policy` sits under Coverage, not Platform: the bundle arrives from the cloud but the evaluation is local and authoritative — it is about what this host enforces, not about what leaves it.
`Connection` sits before `Cloud` because it is the hop `Cloud` travels over. Splitting them is what lets the report say *which* hop broke: a dead proxy and a dead platform are two different problems, and one section could only ever render both as the same failure.
A green section is one line. Anything else lists what needs attention, with its cause and its remedy. `-v` unfolds every check.
```
Runtime
Environment ✓ 3 passed, 1 not applicable
Daemon ✓ 2 checks passed
Persistence ⚠ Disabled at your request (user_opt_out) [OL-1513]
The daemon will not restart after a logout, a reboot, or a crash.
found in : [supervision] disabled_reason = "user_opt_out"
to fix : Run `openlatch system supervision enable` to re-arm it.
Update ✓ Daemon is serving the installed version (0.1.18)
Platform
Connection ✓ via HTTP proxy proxy.corp:8080 (set by: env)
Cloud ✓ Connected: http://localhost:5173 (0 event(s) forwarded)
Telemetry · Disabled by user (opt-out honoured)
```
### More than one agent
On a host running a single agent the report above is the whole story. On a host
running several, "Hooks is failing" stops being an answer — hooks can be
enforcing for Claude Code and dead for Codex CLI, and the section rollup is
worst-wins, so it reads `✗` either way.
So every row that belongs to one agent names it, and `doctor` and `status` both
close with a matrix: one column per detected agent, one row per section that has
anything to say per agent.
```
Coverage
Hooks ✓ claude-code Enforced
✗ codex-cli Monitored — installed and capturing, enforcing nothing [OL-1461]
Model Relay ✓ claude-code Agent wired to http://127.0.0.1:7600
Agents
claude-code codex-cli
Environment ✓ ✓
Hooks ✓ ✗
Model Relay ✓ —
```
A cell is the worst state among that agent's checks in that section. `—` means
that agent files no check there at all — absence, not a state, and deliberately
not `NotApplicable`. Host-wide findings — the relay listener, the daemon — carry
no agent and stay in the section list where they were.
The section rollup is unchanged and stays worst-wins: if one agent is broken,
something *is* broken. The matrix answers *which*.
`doctor --json` carries the same thing as a top-level `agents` object, keyed by
agent, each entry carrying that agent's per-section rollup under `sections` —
one object per agent, so everything `doctor` has to say about one agent is in
one place. A section an agent files nothing in is absent from its list rather
than null, and a host with no agent at all emits `{}` rather than dropping the
key.
### Exit codes
| 0 | Healthy |
| 1 | Something enabled is not working |
| 2 | Usage error |
| 3 | Resource not found — for `init`, no supported agent to connect |
| 4 | Permission denied |
| 5 | Conflict — `OL-1501` only, because systemd's `RestartPreventExitStatus=5` keys off it |
| **7** | **Degraded** — everything runs, but something is switched off or not at full capability |
| 130 | Interrupted |
`7` matters: `openlatch doctor && deploy` must not succeed on a host where nothing is captured or enforced.
`init` exits on its customer verdict rather than on doctor's: `7` only when a warning
needs you, never for a known coverage limit or a check still settling — see
[`init` output](#init-output).