From 1c2695d21d678e7d392d4c4de18f1c7f127384d6 Mon Sep 17 00:00:00 2001 From: "K. Isom" Date: Thu, 24 Sep 2026 01:54:50 -0700 Subject: [PATCH] Apply P16: approvals are !approve/!deny replies in the approval's thread; no reactions Co-Authored-By: Claude Opus 5.5 (1M context) --- CLAUDE.md | 2 +- docs/decisions.md | 5 ++--- docs/design.md | 5 +++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 75cbe75..3b3ad1c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,7 +72,7 @@ Separate binaries in one Cargo workspace. Each role holds as little authority as them), runs each approved tool call in a fresh rootless container, and writes a hash-chained JSONL audit log. - `gatewayd` is the Mattermost channel. Outbound only, no listening port; approvals arrive as - replies or reactions over the WebSocket. + `!approve`/`!deny` replies over the WebSocket. - `inferproxy` is a ~100-line byte forwarder from a Unix socket to the shared `llama-server` router on the host. It is required, because `loopd` has no network. - `proto` holds shared types. `toolkit` holds tool container entrypoints. diff --git a/docs/decisions.md b/docs/decisions.md index e57f61e..a077234 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -92,9 +92,8 @@ Later changes to the brief: | 2026-09-18 | P14, State: the list gained `broker/sessions/.json`, each session's taint and untrusted flag, written only by `brokerd`. | The M3a spec keeps this state in files the list did not name. It can be rebuilt from the audit log's `result` records, so the audit log stays the record of truth. Owner: a plain file also makes debugging easier. | | 2026-09-22 | P13, Authority contract 4: tool containers run from one image built from source by Nix and named by digest; nothing is pulled at call time. | Approved with the M3 design (2026-09-18); applied when the M3b spec came to rely on it. A pull at call time would be unlisted egress. | | 2026-09-23 | P15, State, the secrets line: three `SecretStore` backends chosen per secret (a systemd credential by default, an environment variable, an owner-only file), the last two in plaintext and for portability, a file secret warning at startup. | Owner's decision and approval, 2026-09-23 (M4a). The brief's "no plaintext secrets on disk" did not allow the file backend. | +| 2026-09-24 | P16, Network isolation 3: approvals are commands (`!approve`, `!deny`) replied in the approval's thread; emoji reactions are not used. | Owner's decision and approval, 2026-09-24; M4b spec `docs/specs/2026-09-24-m4b-approvals.md`. | ## Proposed changes to the design brief -| # | Change | Reason | -|---|---|---| -| P16 | Network isolation 3: "v0 approvals use replies or emoji reactions" becomes "approvals are commands (`!approve`, `!deny`) replied in the approval's thread". The rest of the item stands: they arrive over the Mattermost WebSocket, so no callback URL is needed. | Owner's decision of 2026-09-24: reactions are not used. | +None pending. diff --git a/docs/design.md b/docs/design.md index 02487d4..f1b60f9 100644 --- a/docs/design.md +++ b/docs/design.md @@ -64,8 +64,9 @@ ordinary outbound networking can reach the whole tailnet and the internet as the 2. `inferproxy` is a forwarder of about 100 lines: one Unix socket in, one fixed upstream out. The upstream is the shared router's TCP listener on the host. It is required: `loopd` has no network, and the router stays on TCP for its other clients. -3. `gatewayd` has outbound network only. It opens no listening TCP port. v0 approvals use replies - or emoji reactions, which arrive over the Mattermost WebSocket, so no callback URL is needed. +3. `gatewayd` has outbound network only. It opens no listening TCP port. Approvals are commands + (`!approve`, `!deny`) replied in the approval's thread, which arrive over the Mattermost + WebSocket, so no callback URL is needed. 4. Tool containers get no network unless the grant names hosts. Granted network access goes through an allowlisting egress proxy that `brokerd` configures per call. 5. Tailnet ACLs (kept in `deploy/`) limit the host's node to `llama-server` and Mattermost, plus