openlatch-client 0.1.18

OpenLatch runtime enforcement node — the capture-and-enforce client for the AI Operations Platform
<p align="center">
  <img src="./assets/banner.jpg" alt="OpenLatch — runtime enforcement node for the AI Operations Platform" width="600" />
</p>

<h3 align="center">See what your agent does. Block what it shouldn't.</h3>

<p align="center">
  <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>
  <a href="https://codecov.io/gh/OpenLatch/openlatch-client"><img src="https://codecov.io/gh/OpenLatch/openlatch-client/graph/badge.svg" alt="codecov" /></a>
  <a href="https://crates.io/crates/openlatch-client"><img src="https://img.shields.io/crates/v/openlatch-client.svg" alt="crates.io" /></a>
  <a href="https://www.npmjs.com/package/@openlatch/client"><img src="https://img.shields.io/npm/v/@openlatch/client.svg" alt="npm" /></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue.svg" alt="License" /></a>
  <a href="https://openlatch.slack.com"><img src="https://img.shields.io/badge/Slack-join%20us-4A154B?logo=slack&logoColor=white" alt="Slack" /></a>
</p>

---

**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.

```bash
npx @openlatch/client init
```

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.

<!-- TODO: Replace with VHS terminal recording once available -->
<!-- <p align="center">
  <img src="https://raw.githubusercontent.com/OpenLatch/openlatch-client/main/assets/demo.gif" alt="OpenLatch demo" width="700" />
</p> -->

## Why OpenLatch?

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.

OpenLatch gives you:

- **Visibility** — audit logs of every tool call, shell command, and file access across all your agents
- **Control** — actions run through the agent's own lifecycle, the point where OpenLatch is positioned to enforce policy deterministically before they execute
- **Zero friction** — deploys from inside your agent in one command, auto-detects platforms, no code changes

## Supported Agents

OpenLatch attaches in three flavors, one per class of agent:

| Flavor | Agents | How it attaches | Status |
| ------ | ------ | --------------- | ------ |
| Coding agents | Claude Code | Native lifecycle hooks | **Shipping** |
| Coding agents | Cursor, Windsurf, GitHub Copilot, Codex CLI, Gemini CLI, Cline, OpenClaw | Native lifecycle hooks | Planned |
| Cloud agent platforms | Azure AI Foundry, AWS Bedrock | SDK + tool/MCP gate | Planned |
| Agentic Python frameworks | LangChain / LangGraph-class | Framework middleware | Planned |

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.

| Coding Agent | macOS | Windows | Linux | Hook Events |
| ------------ | :---: | :-----: | :---: | ----------: |
| Claude Code | :white_check_mark: | :white_check_mark: | :white_check_mark: | 12 |

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.

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.

## Behavioral Coverage

OpenLatch captures the agent behaviors that matter — the raw signal the platform's modules act on:

| Category | Behaviors captured |
| -------- | ------------------ |
| **Tool Poisoning** | Malicious MCP servers, tampered tool definitions, supply chain attacks on agent tooling |
| **PII & Credential Leakage** | API keys, tokens, passwords, and personal data leaving your machine via agent actions |
| **Prompt Injection** | Injected instructions hidden in files, tool outputs, or web content that hijack agent behavior |
| **Shell Command Guardrails** | Destructive commands (`rm -rf /`), privilege escalation, crypto miners, reverse shells |
| **Supply Chain** | Compromised dependencies, typosquatted packages, unsigned binaries |
| **Attack Path Analysis** | Multi-step attack chains that combine individually benign actions into a malicious sequence |

## Quick Start

### Install via npm (recommended)

```bash
npx @openlatch/client init
```

### Install via Cargo

```bash
cargo install openlatch-client
openlatch init
```

### Download binary

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.

## Usage

```bash
# Auto-detect agents, install hooks, and start the daemon
openlatch init

# Point the client at a self-hosted platform (persisted to config.toml).
# Use the console origin, not the API port: it proxies /api/v1 AND serves
# the /cli-auth page that browser login opens.
openlatch init --api-url http://127.0.0.1:5183

# Check daemon status, uptime, and event counts
openlatch status

# View the 20 most recent security events
openlatch logs

# Tail events in real time
openlatch logs -f

# Show events from the last hour
openlatch logs --since 1h

# Start/stop/restart the daemon
openlatch start
openlatch stop
openlatch restart

# Run daemon in foreground (useful for debugging)
openlatch start --foreground

# Diagnose configuration and connectivity issues
openlatch doctor

# Remove hooks and stop daemon
openlatch uninstall

# Remove hooks, stop daemon, and delete all data (~/.openlatch/)
openlatch uninstall --purge

# Open documentation in browser
openlatch docs
```

### Command Reference

| Command | Description |
| ------- | ----------- |
| `openlatch init` | Auto-detect agents, install hooks, start daemon. Safe to re-run. |
| `openlatch status` | Show daemon status, uptime, and event counts |
| `openlatch start [--foreground]` | Start the daemon (or run in foreground) |
| `openlatch stop` | Stop the daemon |
| `openlatch restart` | Restart the daemon |
| `openlatch logs [-f] [-n N] [--since T]` | View event logs. `-f` follows, `-n` limits count, `--since` filters by time |
| `openlatch doctor` | Diagnose configuration and connectivity issues |
| `openlatch doctor --fix` | Auto-heal common issues (config, hooks, daemon, binary). Creates `.bak` siblings and a journal so changes can be reverted with `--restore`. |
| `openlatch doctor --restore` | Reverse the most recent `--fix` run. Surgical merge for `~/.claude/settings.json` preserves user edits to non-OpenLatch hooks. |
| `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. |
| `openlatch uninstall [--purge] [--yes]` | Remove hooks and stop daemon. `--purge` deletes all data |
| `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`. |
| `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`). |
| `openlatch docs` | Open documentation in browser |

Noun-verb aliases are also available: `openlatch hooks install` = `init`, `openlatch hooks uninstall` = `uninstall`, `openlatch daemon start` = `start`, etc.

#### Persistence

`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.

### Troubleshooting

When something goes wrong, prefer this order:

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).
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.
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.
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 `~`.

Combined `openlatch doctor --fix --rescue` runs the rescue first to snapshot the pre-fix state, then heals.

### Global Flags

| Flag | Description |
| ---- | ----------- |
| `--json` | Machine-readable JSON output (parseable by `jq`) |
| `--verbose` / `-v` | Show extra operational detail |
| `--debug` | Show internal state and timings (implies `--verbose`) |
| `--quiet` / `-q` | Suppress all output except errors |
| `--no-color` | Disable colored output |

### Exit Codes

| Code | Meaning |
| ---- | ------- |
| 0 | Success |
| 1 | Security findings detected |
| 2 | Usage error (invalid arguments) |
| 3 | Resource not found |
| 4 | Permission denied |
| 5 | Conflict |
| 130 | Interrupted (Ctrl+C) |

### Binaries

OpenLatch ships two binaries:

- **`openlatch`** (<30MB) — full CLI for setup, daemon management, and log viewing.
- **`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.

## How It Works

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:

```
┌─────────────────────────────────────────────────────────────────┐
│  Your Machine                                                   │
│                                                                 │
│  ┌──────────┐  hook event   ┌──────────────────────────────┐    │
│  │  Claude   │─────────────▶│   OpenLatch Client           │    │
│  │  Code     │              │   (localhost:7443)           │    │
│  │          │              │                              │    │
│  │          │              │   1. Wrap in envelope        │    │
│  │          │              │   2. Evaluate resident       │    │
│  │          │              │      policy in-process       │    │
│  │          │  ◀─ verdict ─│   3. Redact credentials      │    │
│  └──────────┘  (allow/deny) │   4. Write local audit log  │    │
│                             │   5. Batch → forward        │────┼──▶  OpenLatch Cloud
│                             │      ◀── policy bundle      │◀───┼──   (rules + analysis)
│                             └──────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘
```

**Key design decisions:**

- **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]`.)
- **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.
- **Privacy-first** — credentials and secrets are redacted locally via regex-based filtering before anything leaves your machine.
- **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.

## Updating openlatch

`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.

```bash
# Disable the worker (env or config):
export OPENLATCH_AUTO_UPDATE=false
# or, persistently:
echo -e "[update]\nauto_update = false" >> ~/.openlatch/config.toml
```

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.

`openlatch update` is the manual escape hatch and works whether or not the worker is enabled:

```bash
# Probe the registry; never modifies anything.
openlatch update --check --json

# Apply the update if one is available.
openlatch update --apply --yes
```

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.

The pipeline refuses to apply when:

- The binary was installed via `cargo install` (run `cargo install --force --locked openlatch-client` instead — exit 5, OL-1505).
- The new release's `min_supported_client` is greater than the running version (run `npm install -g @openlatch/client@latest` to resync).
- A signature does not verify against the trusted key bundled into the running binary (exit 1, OL-1504).

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.

## Configuration

OpenLatch works with zero configuration. For advanced use cases:

```bash
# Configuration precedence (highest to lowest):
# 1. CLI flags
# 2. Environment variables (OPENLATCH_*)
# 3. User config (~/.openlatch/config.toml)
# 4. Defaults
```

| Environment Variable | Description | Default |
| -------------------- | ----------- | ------- |
| `OPENLATCH_API_KEY` | Your OpenLatch API key | — |
| `OPENLATCH_API_URL` | Cloud API base URL. Callers append `/api/v1/...` path segments. | `https://app.openlatch.ai` |
| `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` |
| `OPENLATCH_LOG` | Logging verbosity (`error` / `warn` / `info` / `debug` / `trace`) | `info` |
| `OPENLATCH_DIR` | State directory — config, logs, outbox, daemon port/token. Override to run a second isolated instance. | `~/.openlatch` |
| `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` |
| `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 |
| `OPENLATCH_CLOUD_ENABLED` | Set to `false` to stop forwarding events to the cloud | `true` |
| `OPENLATCH_CLOUD_BATCH_MAX_EVENTS` | Events per ingest batch. Clamped to `1..=100`; `1` restores one POST per event. | `50` |
| `OPENLATCH_CLOUD_BATCH_MAX_WAIT_MS` | Flush deadline, anchored to the first buffered event | `5000` |
| `OPENLATCH_POLICY_ENABLED` | Local policy evaluation. Set to `false` to opt out — a complete off switch, not observe mode. | `true` |
| `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` |
| `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` |
| `OPENLATCH_INVENTORY_ENABLED` | Configuration-plane monitoring (MCP servers, skills, rules, hooks) | `true` |
| `OPENLATCH_TELEMETRY_DISABLED` | Set to `1` to opt out of anonymous usage telemetry | unset |
| `OPENLATCH_NPM_REGISTRY` | npm registry origin used by `openlatch update` (override for testing) | `https://registry.npmjs.org` |
| `OPENLATCH_UPDATE_CHECK` | Set to `false` to disable the daemon's startup version check | `true` |
| `OPENLATCH_AUTO_UPDATE` | Set to `false` to disable the background auto-update worker | `true` |
| `OPENLATCH_UPDATE_CHECK_INTERVAL_SECS` | Worker poll cadence (E2E override; production keeps the default) | `21600` (6h) |
| `OPENLATCH_UPDATE_QUIET_WINDOW_SECS` | Seconds of hook silence required before a non-critical apply | `60` |
| `OPENLATCH_UPDATE_MAX_DEFER_SECS` | Hard cap on cumulative deferral before applying anyway | `86400` (24h) |
| `OPENLATCH_CLOUD_OUTBOX_MAX_BYTES` | Cap on `~/.openlatch/outbox.jsonl` size before drop-oldest eviction (0 disables) | `104857600` (100 MB) |
| `OPENLATCH_CLOUD_FALLBACK_MAX_BYTES` | Cap on `~/.openlatch/logs/fallback.jsonl` size before drop-oldest via offset advance (0 disables) | `52428800` (50 MB) |

## Security

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:

- **Responsible disclosure** — report vulnerabilities to **security@openlatch.ai**
- **Private vulnerability reporting** enabled on this repository
- All releases are built with **SLSA provenance** via GitHub Actions
- See [SECURITY.md](SECURITY.md) for our full security policy

## Releases

This repository publishes two artifacts on **independent version lines**:

| Artifact | Tag | Where |
| -------- | --- | ----- |
| 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) |
| 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) |

The schema packages are versioned **only** by changes under `schemas/`, so a
major bump means a breaking change to the events exchanged between the client
and the platform — and nothing else. Pin them independently of the client
version; they carry no dependency on it.

Both lines are cut by [release-please](https://github.com/googleapis/release-please)
from Conventional Commits. Contributor-facing details are in
[AGENT.md](AGENT.md#releasing--two-independent-version-lines).

## Contributing

We welcome contributions! Whether it's bug fixes, new agent integrations, or documentation improvements.

- Read our [Contributing Guide](.github/CONTRIBUTING.md) to get started
- Check [good first issues](https://github.com/OpenLatch/openlatch-client/labels/good%20first%20issue) for entry points
- Join our [Slack](https://openlatch.slack.com) to connect with the team

## License

Licensed under the [Apache License 2.0](LICENSE).

## Links

| Resource | URL |
| -------- | --- |
| Website | [openlatch.ai](https://openlatch.ai) |
| Documentation | [docs.openlatch.ai](https://docs.openlatch.ai) |
| Slack | [openlatch.slack.com](https://openlatch.slack.com) |
| Twitter / X | [@openlatch](https://x.com/openlatch) |
| GitHub | [github.com/OpenLatch](https://github.com/OpenLatch) |