@openlatch/client 0.1.18 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +207 -237
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/OpenLatch/openlatch-client/main/assets/banner.jpg" alt="OpenLatch — runtime enforcement node for the AI Operations Platform" width="600" />
2
+ <img src="https://raw.githubusercontent.com/OpenLatch/openlatch-client/main/assets/banner.jpg" alt="OpenLatch — the execution control layer for enterprise AI agents" width="600" />
3
3
  </p>
4
4
 
5
- <h3 align="center">See what your agent does. Block what it shouldn't.</h3>
5
+ <h3 align="center">Greater control should enable greater autonomy.<br /><em>Dare to Let Go.</em></h3>
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://github.com/OpenLatch/openlatch-client/actions/workflows/pr-checks.yml"><img src="https://github.com/OpenLatch/openlatch-client/actions/workflows/pr-checks.yml/badge.svg" alt="PR Checks" /></a>
@@ -15,332 +15,303 @@
15
15
 
16
16
  ---
17
17
 
18
- **OpenLatch** is the open-source **runtime enforcement node** for the [OpenLatch AI Operations Platform](https://openlatch.ai). It plugs into AI agents via their **native lifecycle hooks** — capturing every action inside the agent's own lifecycle and positioned to enforce policy deterministically before an action executes. One command installs it across all your agents — no code changes, no configuration.
18
+ **[OpenLatch](https://openlatch.ai) is the agent-agnostic execution control layer for enterprise AI agents.** This repository is its open-source **capture-and-enforce adapter**: the node that runs inside a coding agent's own execution path, evaluates every covered action against that agent's Autonomy Zone before it runs, and is what gives an agent the control level **Enforced**.
19
19
 
20
- ```bash
21
- npx @openlatch/client init
22
- ```
23
-
24
- That's it. `init` auto-detects your installed agents, writes the hook configs, and starts the daemon that captures agent activity and forwards it to the OpenLatch control plane.
20
+ It is **Mode 1 — the native hook** — the first of three adapters onto one policy brain. It plugs into agents through their native lifecycle hooks, decides locally, and forwards what it sees to the control plane.
25
21
 
26
- <!-- TODO: Replace with VHS terminal recording once available -->
27
- <!-- <p align="center">
28
- <img src="https://raw.githubusercontent.com/OpenLatch/openlatch-client/main/assets/demo.gif" alt="OpenLatch demo" width="700" />
29
- </p> -->
22
+ **This README is for people working on the client.** If you want to *use* OpenLatch, start at [docs.openlatch.ai](https://docs.openlatch.ai) — `npx @openlatch/client init` and you are done.
30
23
 
31
- ## Why OpenLatch?
24
+ ---
32
25
 
33
- AI agents execute shell commands, read files, call APIs, and install tools — all with your permissions. Most users have **zero visibility** into what their agents actually do.
26
+ ## Where this node sits
34
27
 
35
- OpenLatch gives you:
28
+ OpenLatch runs a five-step control loop around each agent's **Agent Autonomy Zone** — the operating envelope of what an agent may do, authored on the platform as intent policies across five dimensions (Quality, Economics, Security, Safety, Compliance) and compiled to deterministic rules. This client touches four of the five steps. None of the authoring happens here.
36
29
 
37
- - **Visibility** — audit logs of every tool call, shell command, and file access across all your agents
38
- - **Control** — actions run through the agent's own lifecycle, the point where OpenLatch is positioned to enforce policy deterministically before they execute
39
- - **Zero friction** — deploys from inside your agent in one command, auto-detects platforms, no code changes
30
+ | Step | What this node does | What it does not do |
31
+ | ---- | ------------------- | ------------------- |
32
+ | **1 · Identify** | Stamps every event with agent type, session, host and identity, so the platform's record of a sanctioned agent stays live rather than quarterly | Discovery. An agent arrives here by being installed onto, never by being found |
33
+ | **2 · Define** | Nothing. It receives the compiled zone as a signed policy bundle and holds it resident | Author policy. Intent policies are written and compiled on the platform |
34
+ | **3 · Detect** | Captures the actions the execution graph is built from — tool calls, commands, file changes, model calls, cost — and matches them against the resident bundle | Detection of its own. No models, no scanning, no cross-host correlation |
35
+ | **4 · Act** | Returns the verdict at the hook boundary, before the action runs, from an in-process evaluation | Ask the network for it |
36
+ | **5 · Improve** | Writes the local audit log and forwards every event, verdict and mode asynchronously | Report, alert, or render a dashboard |
40
37
 
41
- ## Supported Agents
38
+ Four properties shape the code more than any feature does:
42
39
 
43
- OpenLatch attaches in three flavors, one per class of agent:
40
+ | Property | What it means here |
41
+ | -------- | ------------------ |
42
+ | **Inside the agent, not a proxy in front of it** | The decision point is the agent's own hook lifecycle, so it sees the *action* rather than the traffic. It is not a sandbox — the threat model is a **misbehaving** agent, not an operator working to defeat it |
43
+ | **Local-authoritative** | The resident bundle is the authority, not a cache of one. Evaluation reads an in-memory handle and returns, so the node keeps deciding with the network unplugged |
44
+ | **Sanctioned agents only** | Depth over dragnet. This node makes the agents an enterprise has already blessed enforceable; it does not go looking for the ones it has not |
45
+ | **Coverage is a deployment outcome** | An adapter in the execution path is what makes an agent **Enforced**. Without one an agent can still be **Monitored** (visible through an integration) or **Known** (identified and classified). The record always states which level applies |
44
46
 
45
- | Flavor | Agents | How it attaches | Status |
46
- | ------ | ------ | --------------- | ------ |
47
- | Coding agents | Claude Code | Native lifecycle hooks | **Shipping** |
48
- | Coding agents | Cursor, Windsurf, GitHub Copilot, Codex CLI, Gemini CLI, Cline, OpenClaw | Native lifecycle hooks | Planned |
49
- | Cloud agent platforms | Azure AI Foundry, AWS Bedrock | SDK + tool/MCP gate | Planned |
50
- | Agentic Python frameworks | LangChain / LangGraph-class | Framework middleware | Planned |
47
+ ### The three adapters
51
48
 
52
- Claude Code is the only integration that attaches today, across **macOS, Windows, and Linux**. It is the reference implementation the remaining coding agents will follow.
49
+ | Mode | Surface | This repo |
50
+ | ---- | ------- | --------- |
51
+ | **Mode 1 — native hook** | Coding agents, including hosted ones | **Yes.** Ships first |
52
+ | **Mode 2 — framework middleware** | LangChain / LangGraph | No. Ships second |
53
+ | **Mode 3 — SDK + tool / MCP gate** | Bedrock, Azure AI Foundry, custom agents | No. Ships third, or on demand |
53
54
 
54
- | Coding Agent | macOS | Windows | Linux | Hook Events |
55
- | ------------ | :---: | :-----: | :---: | ----------: |
56
- | Claude Code | :white_check_mark: | :white_check_mark: | :white_check_mark: | 12 |
55
+ ### Verdicts, and which ones this node enforces today
57
56
 
58
- Nine runtime lifecycle events — `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Notification`, `Stop`, `SubagentStop`, `PreCompact`, `SessionStart`, `SessionEnd` — plus three configuration-plane events, `ConfigChange`, `InstructionsLoaded` and `FileChanged`, which the daemon dedupes against its own filesystem watcher.
57
+ The verdict vocabulary is **Allow, Ask, Block or Optimize**. What this binary can express is narrower than the vocabulary, and this is the honest map of it. A verdict is deterministic because an explicit policy produced it.
59
58
 
60
- The other coding agents are recognized as envelope `agent_type` values, so events forwarded on their behalf are stored and attributed correctly — but `openlatch init` cannot yet install their hooks, and `openlatch doctor` reports them as `not_supported_in_v1`. Adding one means a new `AgentBinding` in `src/hooks/bindings/` plus a verdict translator in `src/hook_output/`; without the translator a `deny` is discarded before the developer sees it.
59
+ | Verdict | On the wire / in the bundle | What this node does today |
60
+ | ------- | --------------------------- | ------------------------- |
61
+ | **Allow** | `allow`, `approve` | The action proceeds. `approve` is a user-confirmed allow |
62
+ | **Ask** | No wire variant — an `allow` / `approve` carrying `context` | Rendered to Claude Code as `permissionDecision: "ask"`, the alert as its reason. It **allows and flags**; it does not hold the agent waiting on a human. It prevents recurrence, not the first occurrence |
63
+ | **Block** | `deny` — the only action a `kind=command` rule may take | Reaches the agent as a native deny at `pre_tool_use`, naming the rule that produced it |
64
+ | **Optimize** | `kind=request` rules: `prefix_reorder`, `history_trim`, `prompt_edit` | **Measurement only.** `history_trim` and `prompt_edit` are rewritten to `observe` at bundle load whatever the config says; `prefix_reorder` may act only behind `boundary.transforms_act`, which ships `false`. Out of the box the boundary observes, reports what a transform *would* have saved, and forwards the original bytes |
61
65
 
62
- ## Behavioral Coverage
66
+ Staging is per rule: `observe` records the match and allows the action, `enforce` applies the verdict — Monitor and Enforce in the product. Most restrictive wins across matching rules, and every rule is coerced to `observe` when the bundle's `enforcement_enabled` is false.
63
67
 
64
- OpenLatch captures the agent behaviors that matter — the raw signal the platform's modules act on:
68
+ Deny enforcement is event-scoped. Only pre-action events have somewhere to put a deny; on a post-action event it degrades to `{}` and survives in the record alone.
65
69
 
66
- | Category | Behaviors captured |
67
- | -------- | ------------------ |
68
- | **Tool Poisoning** | Malicious MCP servers, tampered tool definitions, supply chain attacks on agent tooling |
69
- | **PII & Credential Leakage** | API keys, tokens, passwords, and personal data leaving your machine via agent actions |
70
- | **Prompt Injection** | Injected instructions hidden in files, tool outputs, or web content that hijack agent behavior |
71
- | **Shell Command Guardrails** | Destructive commands (`rm -rf /`), privilege escalation, crypto miners, reverse shells |
72
- | **Supply Chain** | Compromised dependencies, typosquatted packages, unsigned binaries |
73
- | **Attack Path Analysis** | Multi-step attack chains that combine individually benign actions into a malicious sequence |
74
-
75
- ## Quick Start
70
+ ---
76
71
 
77
- ### Install via npm (recommended)
72
+ ## Develop
78
73
 
79
74
  ```bash
80
- npx @openlatch/client init
75
+ git clone https://github.com/OpenLatch/openlatch-client && cd openlatch-client
76
+ cargo build
77
+ cargo test
81
78
  ```
82
79
 
83
- ### Install via Cargo
84
-
85
- ```bash
86
- cargo install openlatch-client
87
- openlatch init
88
- ```
80
+ Rust 1.88+, no other prerequisites. `cargo build` produces both binaries in `target/debug/`.
89
81
 
90
- ### Download binary
82
+ ### Work in a sandbox, not on your own machine
91
83
 
92
- Pre-built binaries for macOS (ARM, Intel), Linux (x86_64, ARM), and Windows are available on the [Releases](https://github.com/OpenLatch/openlatch-client/releases) page. After the binary is on your `PATH`, run `openlatch init` to auto-detect agents and install hooks.
84
+ `openlatch init` installs into `~/.openlatch`, writes `~/.claude/settings.json`, and points every live Claude Code session on the host at its boundary listener. You do not want your work-in-progress build doing that.
93
85
 
94
- ## Usage
86
+ `olbox` gives you a complete install that cannot touch your own — its own state directory, its own agent config, its own ports:
95
87
 
96
88
  ```bash
97
- # Auto-detect agents, install hooks, and start the daemon
98
- openlatch init
89
+ cargo install --path tools/sandbox --locked # once
99
90
 
100
- # Point the client at a self-hosted platform (persisted to config.toml).
101
- # Use the console origin, not the API port: it proxies /api/v1 AND serves
102
- # the /cli-auth page that browser login opens.
103
- openlatch init --api-url http://127.0.0.1:5183
104
-
105
- # Check daemon status, uptime, and event counts
106
- openlatch status
107
-
108
- # View the 20 most recent security events
109
- openlatch logs
110
-
111
- # Tail events in real time
112
- openlatch logs -f
91
+ olbox enter dev # a shell inside the sandbox
92
+ openlatch init # installs into the sandbox and nothing else
93
+ claude # this session — and only this session — is routed
94
+ exit # and you are back on your own machine
95
+ ```
113
96
 
114
- # Show events from the last hour
115
- openlatch logs --since 1h
97
+ It lives in [`tools/sandbox`](tools/sandbox/README.md) rather than in the client, because sandboxing is what *we* need and the client is what customers install. `olbox list`, `olbox exec dev -- <cmd>`, `olbox destroy dev`, and `eval "$(olbox env dev)"` if you would rather not use a subshell — the README covers all of it.
116
98
 
117
- # Start/stop/restart the daemon
118
- openlatch start
119
- openlatch stop
120
- openlatch restart
99
+ Under the hood it is five environment variables derived from the name, which are supported client seams you can also set by hand. Getting one wrong is not a failed test — it writes your real `~/.claude/settings.json` — which is why the tool exists.
121
100
 
122
- # Run daemon in foreground (useful for debugging)
123
- openlatch start --foreground
101
+ | Variable | Role |
102
+ | -------- | ---- |
103
+ | `OPENLATCH_DIR` | State: config, token, logs, outbox |
104
+ | `CLAUDE_CONFIG_DIR` | Agent config. **Claude Code's own variable**, so the daemon and the session it captures cannot end up pointed at different directories |
105
+ | `OPENLATCH_PORT` | Daemon port (sandboxes: 17400–17499) |
106
+ | `OPENLATCH_BOUNDARY_PORT` | Model-boundary port (sandboxes: 17600–17699) |
107
+ | `OPENLATCH_BOUNDARY_OWN_WIRING` | Lets a non-default-port instance write *its own* agent config. Never inferred — see [`.claude/rules/isolated-instances.md`](.claude/rules/isolated-instances.md) |
124
108
 
125
- # Diagnose configuration and connectivity issues
126
- openlatch doctor
109
+ `openlatch uninstall --purge` works inside a sandbox and removes the install; `olbox destroy` removes the sandbox itself.
127
110
 
128
- # Remove hooks and stop daemon
129
- openlatch uninstall
111
+ A sandbox is never supervised: the OS unit is machine-global and carries no environment, so one installed from a sandbox shell would supervise your *real* install from your dev binary. `supervision enable` refuses there, `init` skips it, and `doctor` reports Persistence as not applicable. Use `openlatch start` — a sandbox is a session of work, not something that should survive a reboot.
130
112
 
131
- # Remove hooks, stop daemon, and delete all data (~/.openlatch/)
132
- openlatch uninstall --purge
113
+ ### Verify before you push
133
114
 
134
- # Open documentation in browser
135
- openlatch docs
115
+ ```bash
116
+ cargo fmt --all -- --check
117
+ cargo clippy --all-targets --all-features -- -D warnings
118
+ cargo test --all-features
119
+ cargo test --lib --no-default-features # proves openlatch-hook links no `core`
136
120
  ```
137
121
 
138
- ### Command Reference
139
-
140
- | Command | Description |
141
- | ------- | ----------- |
142
- | `openlatch init` | Auto-detect agents, install hooks, start daemon. Safe to re-run. |
143
- | `openlatch status` | Show daemon status, uptime, and event counts |
144
- | `openlatch start [--foreground]` | Start the daemon (or run in foreground) |
145
- | `openlatch stop` | Stop the daemon |
146
- | `openlatch restart` | Restart the daemon |
147
- | `openlatch logs [-f] [-n N] [--since T]` | View event logs. `-f` follows, `-n` limits count, `--since` filters by time |
148
- | `openlatch doctor` | Diagnose configuration and connectivity issues |
149
- | `openlatch doctor --fix` | Auto-heal common issues (config, hooks, daemon, binary). Creates `.bak` siblings and a journal so changes can be reverted with `--restore`. |
150
- | `openlatch doctor --restore` | Reverse the most recent `--fix` run. Surgical merge for `~/.claude/settings.json` preserves user edits to non-OpenLatch hooks. |
151
- | `openlatch doctor --rescue [--since 24h] [--output PATH] [--yes]` | Bundle a redacted diagnostic ZIP for sharing with support. Privacy filter runs on every text file; tokens are hard-redacted. |
152
- | `openlatch uninstall [--purge] [--yes]` | Remove hooks and stop daemon. `--purge` deletes all data |
153
- | `openlatch supervision {install,uninstall,enable,disable,status}` | Manage the OS-native supervisor that keeps the daemon alive after reboot (launchd / systemd --user / Task Scheduler). Installed by default during `openlatch init`; opt out with `openlatch init --no-persistence`. |
154
- | `openlatch inventory {list,log,rescan,status,inspect,projects,ack}` | Inspect configuration monitoring: list observed config sources (MCP servers, skills, commands, hooks, rules), tail `ai.openlatch.config.*` events from the audit log, force a rescan, report monitor health, drill down on a single source (`inspect`), enumerate observed project roots (`projects`), and acknowledge pending alerts (`ack`). |
155
- | `openlatch docs` | Open documentation in browser |
122
+ `tools/e2e` holds the end-to-end harness; it drives a real daemon, so run it under `olbox exec <name> --` or in its Docker mode — never straight against your own install. It enforces that itself and will refuse to start on a machine whose isolation seams are unset, printing the commands to fix it.
156
123
 
157
- Noun-verb aliases are also available: `openlatch hooks install` = `init`, `openlatch hooks uninstall` = `uninstall`, `openlatch daemon start` = `start`, etc.
124
+ ---
158
125
 
159
- #### Persistence
126
+ ## The output contract
160
127
 
161
- `openlatch init` registers an OS-native supervisor by default so the daemon auto-starts when you log in or reboot — persistence is a security property, not a convenience. All three backends (macOS launchd, Linux systemd --user, Windows Task Scheduler) are user-scope only; no admin prompt, no sudo. Opt out with `openlatch init --no-persistence`, or toggle later with `openlatch supervision disable` / `enable`. `openlatch uninstall` always tears the supervisor down before stopping the daemon so it cannot auto-restart underneath you.
128
+ Every command that reports health renders from one model (`src/cli/report.rs`) over the same eleven sections. Three rules decide what you see, and they are enforced by tests rather than by review:
162
129
 
163
- ### Troubleshooting
130
+ | State | Mark | Means |
131
+ | ----- | :--: | ----- |
132
+ | `Ok` | `✓` | Enabled **and proven working**. The only green. |
133
+ | `Off` | `⚠` | Switched off deliberately. Always carries a code and a remedy. |
134
+ | `Degraded` / `Pending` | `⚠` | Working, but not at full capability, or still verifying |
135
+ | `Unknown` | `⚠` | Blocked by another section, which it names. One outage, one cross. |
136
+ | `Failed` | `✗` | Enabled and not working |
137
+ | `NotApplicable` | `·` | Absent from the build, or an exercised privacy opt-out |
164
138
 
165
- When something goes wrong, prefer this order:
139
+ Eleven sections, always all reported, under three groups — the questions a reader asks in the order they ask them:
166
140
 
167
- 1. **Diagnose** — `openlatch doctor` lists every check that's failing (8 today: agent detection, config file, crash-report consent, auth token, daemon liveness, cloud reachability, hook installation, and recent fallback-log activity).
168
- 2. **Heal** — `openlatch doctor --fix` rewrites broken state files (config, token, hooks) and restarts the daemon. Every mutation is backed up to a `.bak` sibling and a journal at `~/.openlatch/fix-journal.json`. Exit code is 0 when post-fix checks all pass and 1 otherwise.
169
- 3. **Roll back** — `openlatch doctor --restore` reads the journal and reverses each reversible action. `~/.claude/settings.json` is restored via a surgical merge — non-OpenLatch hook entries you've added since `--fix` are preserved.
170
- 4. **Bundle for support** — `openlatch doctor --rescue --yes` produces `openlatch-rescue-<ts>-<machid>.zip` in `$PWD`. The bundle includes redacted state files, daemon `/health` + `/metrics` snapshots, binary metadata (SHA256 only — never bytes), and a per-pattern privacy-filter hit summary in `MANIFEST.json`. The bearer token in `daemon.token` is hard-redacted; absolute paths are anonymised to `~`.
141
+ | Group | Sections | Question |
142
+ | ----- | -------- | -------- |
143
+ | **Runtime** | Environment · Daemon · Persistence · Update | is it running, and will it keep running? |
144
+ | **Coverage** | Hooks · Boundary · Policy · Inventory · Integrity | is it seeing everything, and enforcing? |
145
+ | **Platform** | Cloud · Telemetry | is any of it leaving this machine? |
171
146
 
172
- Combined `openlatch doctor --fix --rescue` runs the rescue first to snapshot the pre-fix state, then heals.
147
+ A green section is one line. Anything else lists what needs attention, with its cause and its remedy. `-v` unfolds every check.
173
148
 
174
- ### Global Flags
149
+ ```
150
+ Runtime
151
+ Environment ✓ 3 passed, 1 not applicable
152
+ Daemon ✓ 2 checks passed
153
+ Persistence ⚠ Disabled at your request (user_opt_out) [OL-1513]
154
+ The daemon will not restart after a logout, a reboot, or a crash.
155
+ source : [supervision] disabled_reason = "user_opt_out"
156
+ remedy : Run `openlatch supervision enable` to re-arm it.
157
+ Update ✓ Daemon is serving the installed version (0.1.18)
158
+
159
+ Platform
160
+ Cloud ✓ Connected: http://localhost:5173 (0 event(s) forwarded)
161
+ Telemetry · Disabled by user (opt-out honoured)
162
+ ```
175
163
 
176
- | Flag | Description |
177
- | ---- | ----------- |
178
- | `--json` | Machine-readable JSON output (parseable by `jq`) |
179
- | `--verbose` / `-v` | Show extra operational detail |
180
- | `--debug` | Show internal state and timings (implies `--verbose`) |
181
- | `--quiet` / `-q` | Suppress all output except errors |
182
- | `--no-color` | Disable colored output |
164
+ `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.
183
165
 
184
- ### Exit Codes
166
+ ### Exit codes
185
167
 
186
168
  | Code | Meaning |
187
169
  | ---- | ------- |
188
- | 0 | Success |
189
- | 1 | Security findings detected |
190
- | 2 | Usage error (invalid arguments) |
170
+ | 0 | Healthy |
171
+ | 1 | Something enabled is not working |
172
+ | 2 | Usage error (clap) |
191
173
  | 3 | Resource not found |
192
174
  | 4 | Permission denied |
193
- | 5 | Conflict |
194
- | 130 | Interrupted (Ctrl+C) |
175
+ | 5 | Conflict — `OL-1501` only, because systemd's `RestartPreventExitStatus=5` keys off it |
176
+ | **7** | **Degraded** — everything runs, but something is switched off or not at full capability |
177
+ | 130 | Interrupted |
195
178
 
196
- ### Binaries
179
+ `7` matters: `openlatch doctor && deploy` must not succeed on a host where nothing is captured or enforced.
180
+
181
+ ---
197
182
 
198
- OpenLatch ships two binaries:
183
+ ## Commands
184
+
185
+ | Command | Notes |
186
+ | ------- | ----- |
187
+ | `openlatch init` | 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`). |
188
+ | `openlatch status` | Dashboard plus the eleven-section verdict |
189
+ | `openlatch start [--foreground] [--port N] [--boundary-port N]` | Refuses when a daemon already runs (`OL-1501`, exit 5) |
190
+ | `openlatch stop` / `restart` | `stop` also removes the agent's boundary wiring, and says so |
191
+ | `openlatch doctor` | Full diagnosis with causes and remedies |
192
+ | `openlatch doctor --fix` | Heals what it can, backs up to `.bak` siblings + `~/.openlatch/fix-journal.json`. Never re-enables what you switched off. |
193
+ | `openlatch doctor --restore` | Reverses the last `--fix`. Surgical merge preserves your non-OpenLatch hooks. |
194
+ | `openlatch doctor --rescue [--since 24h] [--yes]` | Redacted diagnostic ZIP for support |
195
+ | `openlatch boundary status` | Full classification, not a port probe |
196
+ | `openlatch boundary {enable,disable} [-y] [--no-restart]` | Writes `[boundary] enabled`, then offers the restart that applies it |
197
+ | `openlatch hooks status` | The Hooks section in detail |
198
+ | `openlatch supervision {install,uninstall,enable,disable,status}` | OS-native supervisor (launchd / systemd --user / Task Scheduler), user-scope only |
199
+ | `openlatch inventory {list,log,rescan,status,inspect,projects,ack}` | Configuration-plane monitoring |
200
+ | `openlatch auth {login,logout,status}` · `openlatch telemetry {…}` · `openlatch update` · `openlatch logs` · `openlatch uninstall [--purge]` · `openlatch docs` | |
201
+
202
+ Noun-verb aliases: `hooks install` = `init`, `daemon start` = `start`, and so on — identical arguments, pinned by test.
203
+
204
+ Global flags: `--json`, `--verbose`/`-v`, `--debug`, `--quiet`/`-q`, `--no-color`. Every diagnostic's JSON carries the same `sections[]` the human rendering shows.
199
205
 
200
- - **`openlatch`** (<30MB) — full CLI for setup, daemon management, and log viewing.
201
- - **`openlatch-hook`** (<20MB) — minimal hook handler spawned by agent hooks. Reads event JSON from stdin, forwards to the daemon on `localhost:7443`, and writes the verdict to stdout. If the daemon is unreachable, it fails open (returns allow) and logs to `~/.openlatch/logs/fallback.jsonl`. Not invoked directly by users.
206
+ ---
202
207
 
203
- ## How It Works
208
+ ## Architecture
204
209
 
205
- OpenLatch uses a **three-layer architecture** — hooks live inside your agent, the client decides locally and forwards events, and the cloud authors the rules and runs analysis:
210
+ Hooks live inside the agent, the client decides locally, the platform authors rules and runs analysis.
206
211
 
207
212
  ```
208
213
  ┌─────────────────────────────────────────────────────────────────┐
209
214
  │ Your Machine │
210
215
  │ │
211
216
  │ ┌──────────┐ hook event ┌──────────────────────────────┐ │
212
- │ │ Claude │─────────────▶│ OpenLatch Client │ │
213
- │ │ Code │ │ (localhost:7443) │ │
214
- │ │ │ │ │ │
215
- │ │ │ │ 1. Wrap in envelope │ │
216
- │ │ │ │ 2. Evaluate resident │ │
217
- │ │ │ │ policy in-process │ │
218
- │ │ │ ◀─ verdict ─│ 3. Redact credentials │ │
219
- │ └──────────┘ (allow/deny) │ 4. Write local audit log │ │
220
- │ │ 5. Batch → forward │────┼──▶ OpenLatch Cloud
221
- │ │ ◀── policy bundle │◀───┼── (rules + analysis)
222
- │ └──────────────────────────────┘ │
217
+ │ │ Claude │──────────────▶│ OpenLatch Client │ │
218
+ │ │ Code │ │ (localhost:7443) │ │
219
+ │ │ │ │ │ │
220
+ │ │ │ │ 1. Wrap in envelope │ │
221
+ │ │ │ │ 2. Evaluate resident │ │
222
+ │ │ │ │ policy in-process │ │
223
+ │ │ │ ◀─ verdict ──│ 3. Redact credentials │ │
224
+ │ └──────────┘ │ 4. Write local audit log │ │
225
+ │ │ 5. Batch → forward │────┼──▶ OpenLatch platform
226
+ │ │ ◀── policy bundle │◀───┼── (the policy brain:
227
+ │ └──────────────────────────────┘ │ zone, rules, analysis)
223
228
  └─────────────────────────────────────────────────────────────────┘
224
229
  ```
225
230
 
226
- **Key design decisions:**
227
-
228
- - **Hook-native** — OpenLatch plugs into the agent's official hook/extension system. The enforcement path runs inside the agent runtime, never as a network proxy; events cannot be bypassed. (Model-call economics capture additionally uses a loopback listener on `127.0.0.1` — on by default, see `[boundary]`.)
229
- - **Local-authoritative verdicts** — the daemon evaluates a resident policy bundle in-process, so the verdict path never touches the network. The cloud authors and serves the rules; it does not decide. Correlation, cross-host analysis, and scanning stay cloud-side. Policy evaluation ships **on** (secure-by-default); `[policy] enabled = false` opts out — a complete off switch, not observe mode.
230
- - **Privacy-first** — credentials and secrets are redacted locally via regex-based filtering before anything leaves your machine.
231
- - **Fail-static, not fail-open** — a resident bundle keeps enforcing offline forever, and a failed refresh keeps the last-known-good bundle. Genuine fail-open is limited to two cases: no bundle was ever fetched, and the hook binary being unable to reach the daemon. Event forwarding is separately durable — events spool to `outbox.jsonl` and sync later.
232
-
233
- ## Updating openlatch
231
+ - **Hook-native, not a proxy** — the decision point runs inside the agent's own runtime, so it evaluates the action rather than the traffic. Model-call economics additionally use a loopback listener on `127.0.0.1`.
232
+ - **Local-authoritative verdicts** — the daemon evaluates a resident policy bundle in-process; the verdict path never touches the network. The cloud serves rules, it does not decide.
233
+ - **The daemon owns `ANTHROPIC_BASE_URL`** — written after its bind succeeds and a synthetic round trip proves the listener can reach the provider, removed on teardown. Nothing else writes it, so the agent config can never name a listener that does not exist.
234
+ - **Fail-static, not fail-open** — a resident bundle keeps enforcing offline forever. Genuine fail-open is limited to "no bundle was ever fetched" and "the hook binary cannot reach the daemon".
235
+ - **Privacy-first** — credentials are redacted locally before anything leaves the machine.
234
236
 
235
- `openlatch` auto-updates itself silently in the background. By default the running daemon polls the npm registry every six hours, downloads + verifies + applies new releases during quiet moments (no agent activity for sixty seconds AND no hook in flight), and re-execs into the new binary. The `min_supported_client` and `cargo install` gates from the manual path apply equally to the worker.
236
-
237
- ```bash
238
- # Disable the worker (env or config):
239
- export OPENLATCH_AUTO_UPDATE=false
240
- # or, persistently:
241
- echo -e "[update]\nauto_update = false" >> ~/.openlatch/config.toml
242
- ```
237
+ Two binaries ship: **`openlatch`** (<30 MB, the CLI and daemon) and **`openlatch-hook`** (<20 MB, spawned per hook event, fails open to `~/.openlatch/logs/fallback.jsonl`).
243
238
 
244
- If the supervisor restarts the daemon at least three times within sixty seconds after a fresh update (sentinel + `.bak` siblings both present), the next start auto-rolls-back from the `.bak` files — running before logging is initialised, with output through stderr only. CI environments and `cargo install`-installed binaries are auto-detected and skip the worker entirely.
245
-
246
- `openlatch update` is the manual escape hatch and works whether or not the worker is enabled:
247
-
248
- ```bash
249
- # Probe the registry; never modifies anything.
250
- openlatch update --check --json
251
-
252
- # Apply the update if one is available.
253
- openlatch update --apply --yes
254
- ```
239
+ The product perimeter — what the client does, and what it deliberately does not — is **[AGENTS.md](AGENTS.md)**. Subsystem detail (envelope format, policy engine, subsystem supervision, the boundary's wiring invariants, releasing) lives in [`.claude/rules/`](.claude/rules/), one file per subsystem, scoped to the paths it governs.
255
240
 
256
- When a daemon is running, the CLI routes the apply through the daemon's authenticated `POST /admin/update` RPC; the daemon downloads the tarball, verifies the SHA-512 integrity hash and minisign signatures against the trusted key baked into the binary, performs an atomic swap, drains in-flight HTTP traffic, and re-execs into the new binary. With no daemon running, the CLI performs the same swap in-process.
241
+ ### Supported agents
257
242
 
258
- The pipeline refuses to apply when:
243
+ Claude Code, on macOS, Windows and Linux, across 12 hook events. It is the reference implementation, and the only agent this node can currently make **Enforced**. Other coding agents are recognised as envelope `agent_type` values — `cursor`, `windsurf`, `github-copilot`, `codex-cli`, `gemini-cli`, `cline`, `openclaw` — so their events store and attribute correctly, but `init` cannot install their hooks yet.
259
244
 
260
- - The binary was installed via `cargo install` (run `cargo install --force --locked openlatch-client` instead — exit 5, OL-1505).
261
- - The new release's `min_supported_client` is greater than the running version (run `npm install -g @openlatch/client@latest` to resync).
262
- - A signature does not verify against the trusted key bundled into the running binary (exit 1, OL-1504).
245
+ Adding one means an `AgentBinding` in `src/hooks/bindings/` **and** a verdict translator in `src/hook_output/` — without the translator a `deny` is discarded before the developer ever sees it, which is worse than not enforcing. The translator comes first.
263
246
 
264
- The supported install paths are `npm install -g @openlatch/client` and `npx @openlatch/client …`. Direct curl-installs work but disable the npm-vs-actual drift detection in `openlatch status`. Releases marked `Severity: critical` (in the release commit footer) bypass the activity-aware deferral and apply on the next poll regardless of in-flight hook traffic.
247
+ ---
265
248
 
266
249
  ## Configuration
267
250
 
268
- OpenLatch works with zero configuration. For advanced use cases:
269
-
270
- ```bash
271
- # Configuration precedence (highest to lowest):
272
- # 1. CLI flags
273
- # 2. Environment variables (OPENLATCH_*)
274
- # 3. User config (~/.openlatch/config.toml)
275
- # 4. Defaults
276
- ```
277
-
278
- | Environment Variable | Description | Default |
279
- | -------------------- | ----------- | ------- |
280
- | `OPENLATCH_API_KEY` | Your OpenLatch API key | — |
281
- | `OPENLATCH_API_URL` | Cloud API base URL. Callers append `/api/v1/...` path segments. | `https://app.openlatch.ai` |
282
- | `OPENLATCH_APP_URL` | Web app URL used for browser auth. Falls back to `OPENLATCH_API_URL` (with a trailing `/api` stripped, for back-compat with overrides). | `https://app.openlatch.ai` |
283
- | `OPENLATCH_LOG` | Logging verbosity (`error` / `warn` / `info` / `debug` / `trace`) | `info` |
284
- | `OPENLATCH_DIR` | State directory — config, logs, outbox, daemon port/token. Override to run a second isolated instance. | `~/.openlatch` |
285
- | `OPENLATCH_PORT` | Local daemon port. `init` binds this exact port and fails with OL-1500 if it is taken; unset, it probes 7443-7543 for the first free one. | `7443` |
286
- | `OPENLATCH_SKIP_KEYRING` | Treat the OS keychain as unavailable, so `OPENLATCH_API_KEY` is used instead. The credential chain is keychain → env var → encrypted file, so a stale keychain entry otherwise wins over the env var. | unset |
287
- | `OPENLATCH_CLOUD_ENABLED` | Set to `false` to stop forwarding events to the cloud | `true` |
288
- | `OPENLATCH_CLOUD_BATCH_MAX_EVENTS` | Events per ingest batch. Clamped to `1..=100`; `1` restores one POST per event. | `50` |
251
+ Precedence: CLI flags → environment (`OPENLATCH_*`) → `~/.openlatch/config.toml` → defaults.
252
+
253
+ | Variable | Description | Default |
254
+ | -------- | ----------- | ------- |
255
+ | `OPENLATCH_DIR` | State directory | `~/.openlatch` |
256
+ | `OPENLATCH_PORT` | Daemon port. `init` binds this exact port; unset, it probes 7443–7543. | `7443` |
257
+ | `OPENLATCH_API_URL` | Cloud API base URL | `https://app.openlatch.ai` |
258
+ | `OPENLATCH_API_KEY` | API key (credential chain: environment → keychain → encrypted file) | — |
259
+ | `OPENLATCH_SKIP_KEYRING` | Treat the OS keychain as unavailable, so the env var wins | unset |
260
+ | `OPENLATCH_LOG` | `error` / `warn` / `info` / `debug` / `trace` | `info` |
261
+ | `OPENLATCH_BOUNDARY_ENABLED` | Model-boundary listener. While on, the daemon points the agent at it — but only once a synthetic request proves the listener can reach the provider. Being wired disables Claude Code Remote Control. | `true` |
262
+ | `OPENLATCH_BOUNDARY_PORT` | Boundary port. A non-default port makes the instance isolated. | `7600` |
263
+ | `OPENLATCH_BOUNDARY_OWN_WIRING` | Let an isolated instance write its own agent config | derived from the port |
264
+ | `OPENLATCH_BOUNDARY_UPSTREAM` | Where the boundary forwards. Every model call and provider credential goes here — change it only for a local harness. | `https://api.anthropic.com` |
265
+ | `OPENLATCH_BOUNDARY_TRANSFORMS_ACT` | Whether an acting `prefix_reorder` rule may rewrite the forwarded request body. Off, the boundary measures and forwards the original bytes. See [Optimize](#verdicts-and-which-ones-this-node-enforces-today) for what it does and does not gate. | `false` |
266
+ | `OPENLATCH_POLICY_ENABLED` | Local policy evaluation. `false` is a complete off switch, not observe mode. | `true` |
267
+ | `OPENLATCH_INVENTORY_ENABLED` | Configuration-plane monitoring | `true` |
268
+ | `OPENLATCH_CLOUD_BATCH_MAX_EVENTS` | Events per ingest batch, clamped to `1..=100` | `50` |
289
269
  | `OPENLATCH_CLOUD_BATCH_MAX_WAIT_MS` | Flush deadline, anchored to the first buffered event | `5000` |
290
- | `OPENLATCH_POLICY_ENABLED` | Local policy evaluation. Set to `false` to opt out — a complete off switch, not observe mode. | `true` |
291
- | `OPENLATCH_BOUNDARY_ENABLED` | Model-boundary listener (the loopback proxy that captures model-call economics + attribution). Set to `false` to stop binding the pinned loopback port. While enabled, the daemon points the agent at the listener via `ANTHROPIC_BASE_URL` — but only once a synthetic request has proven the listener can actually reach the provider, and it removes the pointer again if that stops being true. Being wired disables Claude Code Remote Control. | `true` |
292
- | `OPENLATCH_BOUNDARY_UPSTREAM` | Where the boundary forwards. Every model call and every provider credential on this host goes here, so change it only for a local harness or a deliberate gateway. | `https://api.anthropic.com` |
293
- | `OPENLATCH_INVENTORY_ENABLED` | Configuration-plane monitoring (MCP servers, skills, rules, hooks) | `true` |
294
- | `OPENLATCH_TELEMETRY_DISABLED` | Set to `1` to opt out of anonymous usage telemetry | unset |
295
- | `OPENLATCH_NPM_REGISTRY` | npm registry origin used by `openlatch update` (override for testing) | `https://registry.npmjs.org` |
296
- | `OPENLATCH_UPDATE_CHECK` | Set to `false` to disable the daemon's startup version check | `true` |
297
- | `OPENLATCH_AUTO_UPDATE` | Set to `false` to disable the background auto-update worker | `true` |
298
- | `OPENLATCH_UPDATE_CHECK_INTERVAL_SECS` | Worker poll cadence (E2E override; production keeps the default) | `21600` (6h) |
299
- | `OPENLATCH_UPDATE_QUIET_WINDOW_SECS` | Seconds of hook silence required before a non-critical apply | `60` |
300
- | `OPENLATCH_UPDATE_MAX_DEFER_SECS` | Hard cap on cumulative deferral before applying anyway | `86400` (24h) |
301
- | `OPENLATCH_CLOUD_OUTBOX_MAX_BYTES` | Cap on `~/.openlatch/outbox.jsonl` size before drop-oldest eviction (0 disables) | `104857600` (100 MB) |
302
- | `OPENLATCH_CLOUD_FALLBACK_MAX_BYTES` | Cap on `~/.openlatch/logs/fallback.jsonl` size before drop-oldest via offset advance (0 disables) | `52428800` (50 MB) |
270
+ | `OPENLATCH_CLOUD_OUTBOX_MAX_BYTES` | Outbox cap before drop-oldest (0 disables) | `104857600` |
271
+ | `OPENLATCH_CLOUD_FALLBACK_MAX_BYTES` | Fallback-log cap before drop-oldest (0 disables) | `52428800` |
272
+ | `OPENLATCH_AUTO_UPDATE` | Background auto-update worker | `true` |
273
+ | `OPENLATCH_UPDATE_CHECK` | Startup version check | `true` |
274
+ | `OPENLATCH_NPM_REGISTRY` | Registry origin for `openlatch update` | `https://registry.npmjs.org` |
275
+ | `OPENLATCH_TELEMETRY_DISABLED` | `1` opts out of anonymous usage telemetry | unset |
303
276
 
304
- ## Security
277
+ **Cloud forwarding is not optional.** `[cloud] enabled` and `OPENLATCH_CLOUD_ENABLED` are still parsed so existing configs keep loading, but they are ignored — a client that captures events and sends them nowhere is a log rotator. Point `[cloud] api_url` elsewhere to change *where* events go.
305
278
 
306
- OpenLatch is the on-host runtime enforcement node of the AI Operations Platform — and we hold our own code to the same standard the platform is built to enforce for your agents:
279
+ Auto-update, its quiet-window deferral and its rollback sentinel are documented in the module rustdoc of [`src/core/update.rs`](src/core/update.rs); `openlatch update --check --json` is the manual probe.
307
280
 
308
- - **Responsible disclosure** — report vulnerabilities to **security@openlatch.ai**
309
- - **Private vulnerability reporting** enabled on this repository
310
- - All releases are built with **SLSA provenance** via GitHub Actions
311
- - See [SECURITY.md](SECURITY.md) for our full security policy
281
+ ---
312
282
 
313
283
  ## Releases
314
284
 
315
- This repository publishes two artifacts on **independent version lines**:
285
+ Two artifacts on independent version lines, both cut by [release-please](https://github.com/googleapis/release-please) from Conventional Commits:
316
286
 
317
287
  | Artifact | Tag | Where |
318
288
  | -------- | --- | ----- |
319
- | The client itself | `v1.2.3` | GitHub Releases, [`openlatch`](https://www.npmjs.com/package/openlatch) (npm), [`openlatch-client`](https://crates.io/crates/openlatch-client) (crates.io) |
320
- | The wire-format JSON Schemas | `schemas-v1.2.3` | [`@openlatch/client-schemas`](https://www.npmjs.com/package/@openlatch/client-schemas) (npm), [`openlatch-client-schemas`](https://pypi.org/project/openlatch-client-schemas/) (PyPI) |
289
+ | The client | `v1.2.3` | GitHub Releases, npm, crates.io |
290
+ | The wire-format JSON Schemas | `schemas-v1.2.3` | npm, PyPI |
321
291
 
322
- The schema packages are versioned **only** by changes under `schemas/`, so a
323
- major bump means a breaking change to the events exchanged between the client
324
- and the platform — and nothing else. Pin them independently of the client
325
- version; they carry no dependency on it.
292
+ The schema packages are versioned **only** by changes under `schemas/`, so a major bump means a breaking change to the events exchanged with the platform and nothing else. Pin them independently. Details in [`.claude/rules/release-lines.md`](.claude/rules/release-lines.md).
326
293
 
327
- Both lines are cut by [release-please](https://github.com/googleapis/release-please)
328
- from Conventional Commits. Contributor-facing details are in
329
- [AGENT.md](AGENT.md#releasing--two-independent-version-lines).
294
+ ## Why it is open source
330
295
 
331
- ## Contributing
296
+ The platform is closed; this node is Apache-2.0, for three enterprise reasons.
332
297
 
333
- We welcome contributions! Whether it's bug fixes, new agent integrations, or documentation improvements.
298
+ | Reason | Why it matters |
299
+ | ------ | -------------- |
300
+ | **Transparency** | A node that sits in the execution path of every agent action cannot be a black box. Source access is a hard procurement gate at regulated enterprises. |
301
+ | **Auditability** | Security reviews, internal AppSec and third-party testers all need to read the code that enforces policy on production agents. |
302
+ | **Distribution** | The developer inside the account is who runs `npx @openlatch/client init` first. Open source removes the friction from that. |
334
303
 
335
- - Read our [Contributing Guide](.github/CONTRIBUTING.md) to get started
336
- - Check [good first issues](https://github.com/OpenLatch/openlatch-client/labels/good%20first%20issue) for entry points
337
- - Join our [Slack](https://openlatch.slack.com) to connect with the team
304
+ ## Security
338
305
 
339
- ## License
306
+ - Report vulnerabilities to **security@openlatch.ai**; private vulnerability reporting is enabled on this repository
307
+ - Releases are built with SLSA provenance via GitHub Actions
308
+ - Full policy: [SECURITY.md](SECURITY.md)
309
+
310
+ ## Contributing
340
311
 
341
- Licensed under the [Apache License 2.0](LICENSE).
312
+ Read the [Contributing Guide](.github/CONTRIBUTING.md), pick a [good first issue](https://github.com/OpenLatch/openlatch-client/labels/good%20first%20issue), and join us on [Slack](https://openlatch.slack.com).
342
313
 
343
- ## Links
314
+ Licensed under [Apache-2.0](LICENSE).
344
315
 
345
316
  | Resource | URL |
346
317
  | -------- | --- |
@@ -348,4 +319,3 @@ Licensed under the [Apache License 2.0](LICENSE).
348
319
  | Documentation | [docs.openlatch.ai](https://docs.openlatch.ai) |
349
320
  | Slack | [openlatch.slack.com](https://openlatch.slack.com) |
350
321
  | Twitter / X | [@openlatch](https://x.com/openlatch) |
351
- | GitHub | [github.com/OpenLatch](https://github.com/OpenLatch) |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openlatch/client",
3
- "version": "0.1.18",
4
- "description": "OpenLatch runtime enforcement node — the capture-and-enforce client for the AI Operations Platform",
3
+ "version": "0.2.2",
4
+ "description": "OpenLatch runtime enforcement node — the capture-and-enforce adapter that evaluates every covered action against a coding agent's Autonomy Zone before it runs",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -20,10 +20,10 @@
20
20
  "postinstall": "node lib/postinstall.js"
21
21
  },
22
22
  "optionalDependencies": {
23
- "@openlatch/client-darwin-arm64": "0.1.18",
24
- "@openlatch/client-darwin-x64": "0.1.18",
25
- "@openlatch/client-linux-x64": "0.1.18",
26
- "@openlatch/client-linux-arm64": "0.1.18",
27
- "@openlatch/client-win32-x64": "0.1.18"
23
+ "@openlatch/client-darwin-arm64": "0.2.2",
24
+ "@openlatch/client-darwin-x64": "0.2.2",
25
+ "@openlatch/client-linux-x64": "0.2.2",
26
+ "@openlatch/client-linux-arm64": "0.2.2",
27
+ "@openlatch/client-win32-x64": "0.2.2"
28
28
  }
29
29
  }