openlatch-provider 0.2.1

Self-service onboarding CLI + runtime daemon for OpenLatch Editors and Providers
# Runtime mode

`openlatch-provider listen` is the long-lived daemon that receives
HMAC-signed Standard Webhooks v1 from `openlatch-platform`, verifies
them, proxies the events to your localhost-hosted detection tool, and
returns HMAC-signed verdicts.

## Lifecycle per event

```
1. POST /v1/event arrives           (axum + tower-http RequestBodyLimit 1 MB)
2. Read raw body bytes              (Bytes — no parse yet)
3. Verify webhook-signature header  (HMAC-SHA256 over <id>.<ts>.<body>)
4. Skew check on webhook-timestamp  (±5 min)
5. Replay-cache lookup on webhook-id (LRU 1000, 5 min TTL)
   └── hit -> return cached signed verdict (idempotent)
6. Resolve X-OpenLatch-Binding-Id -> LocalRoute  (HashMap snapshot)
7. Parse body as JSON               (only after auth verified)
8. POST event to localhost tool     (deadline-bounded, no redirects)
9. Validate verdict size            (≤ 250 KB cap)
10. HMAC-sign outbound headers      (reusing the same whsec_live_… secret)
11. Insert into replay cache        (so retries are idempotent)
12. Append audit-log line           (mpsc channel + bg writer task)
13. Return signed response          (200 + the 3 webhook-* headers)
```

Failure modes short-circuit early; a malformed signature MUST stop
before `serde_json::from_slice`.

## Listen flags

| Flag | Purpose |
|---|---|
| `--port 8443` | HTTPS port. Use `0` for OS-assigned in tests. |
| `--bind-addr 0.0.0.0` | Bind address. Default 0.0.0.0; use `127.0.0.1` behind a reverse proxy. |
| `--manifest <path>` | Override the active profile's manifest. |
| `--cert <pem> --key <pem>` | Direct TLS termination (not yet wired in v0.1; see `--no-tls`). |
| `--no-tls` | Plain TCP. Use behind a TLS-terminating reverse proxy. |
| `--no-watch` | Disable cross-platform manifest file-watcher. |
| `--admin-port <p>` | Bind the bearer-auth admin endpoint on `127.0.0.1:<p>`. |
| `--log-retention-days <n>` | Audit log rotation (default 14). |

## Reload triggers

The daemon's route table can be refreshed without restart:

- **SIGHUP** (Unix only) — `kill -HUP $PID` or `systemctl reload`.
- **Manifest file change** — cross-platform via the `notify` watcher.
- **`POST /v1/admin/reload`** — bearer-auth admin endpoint, gated on
  `--admin-port`.

Each path runs the same internal `reload_into(...)` function: refresh
live bindings from the platform (best-effort), build a new
`RouteTable`, swap atomically.

## Auto-update

The runtime daemon includes the auto-update worker (P3.T2c). Default
behaviour:

- Spawns at startup unless a kill-switch fires (`OPENLATCH_NO_AUTO_UPDATE=1`,
  `OPENLATCH_PROVIDER_AUTO_UPDATE=false`, CI env, or `cargo install`).
- Polls the npm registry every 6 h (1 h on critical, 5 min on defer-recheck).
- Activity-aware: 60 s quiet window default + `events_in_flight == 0`,
  24 h hard cap, `severity=critical` bypasses deferral.
- Apply pipeline: download -> SRI verify -> tar extract (hardened) ->
  multi-key minisign verify -> sanity-check `--version` ->
  `self_replace::self_replace` -> sentinel write -> drain -> re-exec.

Manual control:

```bash
openlatch-provider update --check    # JSON status: current/latest/severity/min_supported
openlatch-provider update --apply --yes
openlatch-provider update --apply --yes --force-cargo  # bypass cargo-install gate
```

## Telemetry

Two separate consent subsystems:

- **PostHog usage telemetry**: opt-in. Disabled by default; toggle via
  `openlatch-provider config set telemetry true`. CI environments
  auto-disable. `DO_NOT_TRACK=1` is the cross-tool kill switch.
- **Sentry crash reports**: default-on. Captures Rust panic
  backtraces only, scrubbed of stack-locals + env-var values + tokens.
  Disable with `SENTRY_DISABLED=1` or `[crashreport] enabled = false`
  in `~/.openlatch/provider/config.toml`.

## Deploy patterns

- **systemd**: `docs/deploy/systemd.example` (hardened unit file).
- **Docker**: `docs/deploy/docker.md` (multi-arch image at
  `ghcr.io/openlatch/provider`).
- **Caddy reverse proxy**: `docs/deploy/caddy-tls-termination.md`.

## Health

`GET /v1/health` returns:

```json
{
  "status": "ok",
  "uptime_secs": 1234,
  "events_processed": 5678,
  "events_failed": 2,
  "binding_count": 3
}
```

No auth required. Use as your liveness + readiness probe.