From 7c82786681409f246f2f77110d659fa422267959 Mon Sep 17 00:00:00 2001 From: bigbes Date: Wed, 22 Jul 2026 11:14:35 +0300 Subject: [PATCH] docs: align provenance section with the one-token decision The section still described per-space tokens with reader/proposer/writer roles, which the decisions table and the authorization section had already retracted in favour of one token plus mandatory provenance. Also records what implementation had to settle: the agent mailbox derives from our own origin rather than the bare cookie domain, X-Agent-Base is mandatory and must be an object name, identity travels as X-Agent and X-Agent-Session with the base coming only from If-Match, a non-owner human resolves to anonymous, and cookie expiry is not enforceable so network-key rotation is the only logout-everywhere lever. --- docs/DESIGN.md | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 12232faae5fd4f8fa13c19883501a805dbbd49ca..f55701d2d3d7d9d2705843aa7451e5a56772254d 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -543,12 +543,11 @@ who did what. ### Provenance -Per-space (or per-project) tokens with a role — `reader` / `proposer` / -`writer` — and a **required agent identity string**. Every commit records it in -a way that survives clone: +One agent token (no roles, no per-space scopes — see above) plus a **required +agent identity string**. Every commit records it in a way that survives clone: ``` -Author: claude-code/spec-writer (for bigbes) +Author: claude-code/spec-writer (for bigbes) Committer: bigbes Add storage model section @@ -560,6 +559,31 @@ Committer: bigbes Git trailers rather than a Postgres-only audit table, so provenance is visible in plain `git log` on any clone and cannot drift from the content it describes. +Details settled during implementation, recorded here because the sketch above +left them open: + +- **The agent mailbox is derived, not configured.** `agent@` — so `agent@spec.srht.bigb.es`. Earlier drafts wrote + `agent@srht.bigb.es` (the bare cookie domain) with no stated source. Deriving + it from our own origin adds no config key and cannot drift from it. +- **`X-Agent-Base` is mandatory**, validated as 7–64 lowercase hex. An empty base + is a provenance claim with nothing behind it, which would make the trailer + decorative rather than auditable — the thing the read-contract decision was + supposed to buy. It must be an object name, never a ref name. +- **Agents transmit identity over `X-Agent` / `X-Agent-Session` request + headers**, named after the trailers they become. Deliberately **no** + `X-Agent-Base` header: the base is `If-Match`, and a second spelling of one + value is exactly how REST and MCP drift apart. The MCP tools map arguments onto + the same three values. +- **A logged-in human who is not the owner resolves to anonymous**, with the + cookie name kept for logs only. Single-user makes this correct today; it is the + line to revisit if anyone else ever gets an account. +- **Cookie expiry is not enforceable and is not enforced.** The unified-login + cookie carries no service-side TTL, and core-go's own `cookieAuth` uses + `DecryptWithoutExpiration`. We match it, so "expired" collapses into "sealed + with a key we no longer hold" — i.e. **rotating `[sr.ht] network-key` is the + only logout-everywhere lever**, instance-wide. + MCP is a **first-class surface, not a wrapper** — it is how agents will actually consume this: `spec_search`, `spec_read`, `spec_propose`, `spec_comment`, `spec_status`. warren's `mcpsrv/` is the starting point.