capture #49f: central relay + I2 security-baseline ledger entry (non-library harnesses) #55

Closed
opened 2026-06-22 20:28:06 +00:00 by mathias · 3 comments
Owner

Parent: #49 · Spec: specs/capture-bdd-spec.md §2 (I2), §4.3 · Depends on: #53 (REST adapter), #54 (audit path) · The last sub-issue — heaviest security review

Scope

The central relay that lets harnesses which can't run the use-case library in-process (claude.ai Chat/Cowork/Design, Crush, Pi, LLM Council) reach capture — plus the mandatory I2 security-baseline ledger entry that must land before the relay ships. Split from the audit path (#54) deliberately so direct-REST capture (CLI/Agentsquad) works end-to-end first; this issue takes on the relay's concentration-risk review separately.

Why this is gated, not free (the I2 tension)

Per brain/wiki/homelab/decisions/no-centralized-cross-harness-observer-2026-06-17, a standing node with cross-harness reach is exactly the high-degree concentration Zero Trust / sovereign-containment is suspicious of. The relay is admissible only as: thin, no standing visibility, retains nothing beyond the I5 audit log, each call carrying the caller's own principal — AND only once its cross-harness reach is entered in the acceptance ledger. The ledger entry is a precondition of shipping, not documentation-after.

What to build

  1. The I2 ledger entry FIRST — add the relay's cross-harness reach to infra/docs/security-baseline.md with: what's accepted, why, the containment properties (thin/stateless/audit-only/per-principal), and a concrete "Revisit if" trigger. This lands and is reviewed before any relay code shers.
  2. The relay — a thin authenticated forwarder: accepts an authenticated capture request from a non-library harness, attaches/propagates the caller's own principal (never a shared service identity that would erase per-caller attribution), forwards to CaptureService. No standing state; no retention beyond the I5 audit path (#54).
  3. GitOps (I3): relay manifest under infra/k3s/apps/**, Flux-reconciled. No untracked runtime.
  4. Per-harness OAuth2 token provisioning (the bounded per-harness quirk from #49): how claude.ai / Crush / Pi / LLM Council each obtain a credential to call the relay. May spawn its own follow-up per harness.

Acceptance criteria

  • infra/docs/security-baseline.md ledger entry merged and reviewed BEFORE relay code ships (Why-accepted / containment props / Revisit-if)
  • Relay forwards capture requests, propagating the caller's own principal (no attribution-erasing shared identity)
  • Relay holds no standing visibility; retains nothing beyond the I5 audit log
  • Relay manifest under infra/k3s/apps/** (I3), Flux-reconciled
  • OAuth2 token path defined for at least one non-library harness (claude.ai) end-to-end
  • Tests: relay forwards + preserves principal; relay adds no retention; unauth request rejected
  • task check green; secret-handling respected

Notes

This closes the loop on "uniform across ALL harnesses" — without it, capture works from CLI/Agentsquad but not from claude.ai chat. It's last because it carries the most security weight (the I2 concentration acceptance) and depends on the audit path being solid first. Do NOT ship the relay before its ledger entry is merged.

**Parent:** #49 · **Spec:** `specs/capture-bdd-spec.md` §2 (I2), §4.3 · **Depends on:** #53 (REST adapter), #54 (audit path) · **The last sub-issue** — heaviest security review ## Scope The central relay that lets harnesses which **can't run the use-case library in-process** (claude.ai Chat/Cowork/Design, Crush, Pi, LLM Council) reach capture — plus the mandatory I2 security-baseline ledger entry that must land **before** the relay ships. Split from the audit path (#54) deliberately so direct-REST capture (CLI/Agentsquad) works end-to-end first; this issue takes on the relay's concentration-risk review separately. ## Why this is gated, not free (the I2 tension) Per `brain/wiki/homelab/decisions/no-centralized-cross-harness-observer-2026-06-17`, a standing node with cross-harness reach is exactly the high-degree concentration Zero Trust / sovereign-containment is suspicious of. The relay is admissible **only** as: thin, no standing visibility, retains nothing beyond the I5 audit log, each call carrying the caller's own principal — AND only once its cross-harness reach is entered in the acceptance ledger. The ledger entry is a **precondition of shipping**, not documentation-after. ## What to build 1. **The I2 ledger entry FIRST** — add the relay's cross-harness reach to `infra/docs/security-baseline.md` with: what's accepted, why, the containment properties (thin/stateless/audit-only/per-principal), and a concrete "Revisit if" trigger. This lands and is reviewed **before** any relay code shers. 2. **The relay** — a thin authenticated forwarder: accepts an authenticated capture request from a non-library harness, attaches/propagates the caller's own principal (never a shared service identity that would erase per-caller attribution), forwards to `CaptureService`. No standing state; no retention beyond the I5 audit path (#54). 3. **GitOps (I3):** relay manifest under `infra/k3s/apps/**`, Flux-reconciled. No untracked runtime. 4. **Per-harness OAuth2 token provisioning** (the bounded per-harness quirk from #49): how claude.ai / Crush / Pi / LLM Council each obtain a credential to call the relay. May spawn its own follow-up per harness. ## Acceptance criteria - [ ] **`infra/docs/security-baseline.md` ledger entry merged and reviewed BEFORE relay code ships** (Why-accepted / containment props / Revisit-if) - [ ] Relay forwards capture requests, propagating the **caller's own principal** (no attribution-erasing shared identity) - [ ] Relay holds no standing visibility; retains nothing beyond the I5 audit log - [ ] Relay manifest under `infra/k3s/apps/**` (I3), Flux-reconciled - [ ] OAuth2 token path defined for at least one non-library harness (claude.ai) end-to-end - [ ] Tests: relay forwards + preserves principal; relay adds no retention; unauth request rejected - [ ] `task check` green; secret-handling respected ## Notes This closes the loop on "uniform across ALL harnesses" — without it, capture works from CLI/Agentsquad but not from claude.ai chat. It's last because it carries the most security weight (the I2 concentration acceptance) and depends on the audit path being solid first. Do NOT ship the relay before its ledger entry is merged.
Author
Owner

I2 ledger entry drafted — infra PR #151 (the shipping gate)

PR: mathias/infra#151mathias/infra docs/security-baseline.md.

Per this issue's gating rule, the relay's cross-harness reach is entered in the acceptance ledger before any relay code. The entry records it as a bounded doorway (not the standing observer the 2026-06-17 decision rejected), with six containment properties — all must hold or the acceptance voids:

  1. thin/stateless · 2. no standing visibility · 3. audit-only retention (the #54 record, nothing more) · 4. per-principal attribution · 5. no trust-zone upgrade · 6. GitOps-reconciled.

Held for review — not auto-merged. Relay code (steps 2–4 of this issue) does not start until #151 merges.

Build constraints #151 pins on the relay

  • Per-principal (prop 4): forwarder propagates the caller's own JWT/principal — never a shared service identity — so the I1 gate + I5 audit see the real caller.
  • No zone upgrade (prop 5): a us-nexus caller via the relay stays us-nexus → I1 confidential refusal still fires.
  • #54 carry-over: the audit buffer (<brain>/.audit-buffer/) needs a persistent volume in the manifest, else durable-across-restart breaks on pod reschedule.
## I2 ledger entry drafted — infra PR #151 (the shipping gate) **PR:** https://git.d-ma.be/mathias/infra/pulls/151 → `mathias/infra` `docs/security-baseline.md`. Per this issue's gating rule, the relay's cross-harness reach is entered in the acceptance ledger **before any relay code**. The entry records it as a bounded *doorway* (not the standing observer the 2026-06-17 decision rejected), with six containment properties — all must hold or the acceptance voids: 1. thin/stateless · 2. no standing visibility · 3. audit-only retention (the #54 record, nothing more) · 4. per-principal attribution · 5. no trust-zone upgrade · 6. GitOps-reconciled. **Held for review — not auto-merged.** Relay code (steps 2–4 of this issue) does **not** start until #151 merges. ### Build constraints #151 pins on the relay - **Per-principal (prop 4):** forwarder propagates the caller's own JWT/principal — never a shared service identity — so the I1 gate + I5 audit see the real caller. - **No zone upgrade (prop 5):** a us-nexus caller via the relay stays us-nexus → I1 confidential refusal still fires. - **#54 carry-over:** the audit buffer (`<brain>/.audit-buffer/`) needs a **persistent volume** in the manifest, else durable-across-restart breaks on pod reschedule.
Author
Owner

Relay implemented — two PRs open (neither auto-merged)

Half Repo PR
Relay code (MCP capture tool) hyperguild #61
I3 deploy (gitea token + loki env) infra #152
I2 ledger (gate) infra #151 merged

Relay = MCP capture tool (decided shape): claude.ai/Crush/Pi/LLM Council reach it via the existing /mcp OAuth connector. Thin forwarder to the same CaptureService; no state, audit-only retention — the six I2 containment properties hold by construction.

  • Per-principal (prop 4): ServeHTTP re-derives the caller's principal from the Bearer header (chassis middleware gates but discards it) → tool resolves origin from it.
  • No zone upgrade (prop 5): body-asserted origin ignored; us-nexus JWT (claude.ai) → confidential refused; sovereign-allowlisted/static → allowed. Tested.

OAuth path for claude.ai (AC): the existing Dex/oauth/token connector — the tool rides it, no new code. claude.ai's subject → us-nexus (not in BRAIN_CAPTURE_SOVEREIGN_PRINCIPALS).

I3 manifest (#152): new gitea-api-token ExternalSecret + BRAIN_GITEA_TOKEN/BRAIN_LOKI_URL env. Audit buffer needs no PV — it's on the brain hostPath, nodeSelector-pinned to koala, so it survives restart (corrects my #54 note).

Gating

  • #152 activates the privileged cross-harness write path in prod → held for your review (and needs the 1P item DMABE_GITEA_API_TOKEN).
  • #61 is the code; merge it, then #152, then Flux reconciles and the capture tool goes live on the claude.ai connector.

After both merge: capture is uniform across all harnesses — epic #49 complete.

## Relay implemented — two PRs open (neither auto-merged) | Half | Repo | PR | |---|---|---| | Relay code (MCP `capture` tool) | hyperguild | **#61** | | I3 deploy (gitea token + loki env) | infra | **#152** | | I2 ledger (gate) | infra | #151 ✅ merged | **Relay = MCP `capture` tool** (decided shape): claude.ai/Crush/Pi/LLM Council reach it via the existing `/mcp` OAuth connector. Thin forwarder to the same `CaptureService`; no state, audit-only retention — the six I2 containment properties hold by construction. - **Per-principal (prop 4):** `ServeHTTP` re-derives the caller's principal from the Bearer header (chassis middleware gates but discards it) → tool resolves origin from it. - **No zone upgrade (prop 5):** body-asserted origin ignored; us-nexus JWT (claude.ai) → confidential refused; sovereign-allowlisted/static → allowed. Tested. **OAuth path for claude.ai (AC):** the existing Dex/`oauth/token` connector — the tool rides it, no new code. claude.ai's subject → us-nexus (not in `BRAIN_CAPTURE_SOVEREIGN_PRINCIPALS`). **I3 manifest (#152):** new `gitea-api-token` ExternalSecret + `BRAIN_GITEA_TOKEN`/`BRAIN_LOKI_URL` env. **Audit buffer needs no PV** — it's on the brain hostPath, nodeSelector-pinned to koala, so it survives restart (corrects my #54 note). ### Gating - #152 activates the privileged cross-harness write path in prod → **held for your review** (and needs the 1P item `DMABE_GITEA_API_TOKEN`). - #61 is the code; merge it, then #152, then Flux reconciles and the `capture` tool goes live on the claude.ai connector. After both merge: **capture is uniform across all harnesses** — epic #49 complete.
Author
Owner

Shipped — closing

Both halves merged; full GitOps chain landed:

  • #151 (I2 ledger) → #61 (relay tool) → #152 (env + gitea-token ExternalSecret) → CD cc223ec (image bump to the #61 SHA).
  • hyperguild CI run 272 ; Flux reconciles infra main → ingestion rolls to 76514215 with the capture env. The capture MCP tool goes live on the claude.ai connector once the pod rolls.

Tagged v0.11.0 marking the capability complete.

Closing as done — deployment converges via Flux (no manual reconcile needed).

## Shipped — closing Both halves merged; full GitOps chain landed: - #151 (I2 ledger) → #61 (relay tool) → #152 (env + gitea-token ExternalSecret) → CD `cc223ec` (image bump to the #61 SHA). - hyperguild CI run 272 ✅; Flux reconciles infra `main` → ingestion rolls to `76514215` with the capture env. The `capture` MCP tool goes live on the claude.ai connector once the pod rolls. Tagged **v0.11.0** marking the capability complete. Closing as done — deployment converges via Flux (no manual reconcile needed).
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/hyperguild#55