diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 398a52004..046d91217 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -197,7 +197,7 @@ The browser registry (Comet, Chrome, Arc, Brave, Edge) is hardcoded. Database pa Every enumerated gstack-initiated off-machine sink writes a hash-chained, tamper-evident receipt to `~/.gstack/security/egress.jsonl` BEFORE the send — `writeReceipt` in `lib/egress-receipt.ts` for TypeScript callers, `_receipted_curl` / `_receipted_git` from `bin/gstack-egress-lib.sh` for shell scripts. Receipts record a sha256 of the exact bytes sent when the caller owns them (subprocess-owned sends like git pushes record `sha256: null`); they never store the body. -Failure polarity is per-class and pinned by tests. Sensitive sinks are fail-closed: brain-sync pushes, memory-ingest, gbrain-sync, telemetry, ngrok tunnel starts, mcp-verify, and supabase-provision refuse to send if the receipt can't be written (each refusal prints problem + cause + fix). User-facing sinks fail open with a stderr warning — the design binary's OpenAI calls, update-check, the read-only dashboards, and git-class receipts proceed even when the receipt write failed, so a fail-open send can go unrecorded (warned, by design). The new-sink scanner in `test/egress-receipt-wiring.test.ts` fails CI when an off-machine sink ships unwired; its only exemptions are enumerated with reasons (user-directed page fetches, reachability probes, install-doc strings, skill prose). +Failure polarity is per-class and pinned by tests. Sensitive sinks are fail-closed: brain-sync pushes, memory-ingest, gbrain-sync, telemetry, ngrok tunnel starts, mcp-verify, supabase-provision, and the Memorable bridge's per-prompt `memorable-recall` hand-off (a prompt handed to a local vendor binary; see [docs/memorable-workflow-memory.md](docs/memorable-workflow-memory.md)) refuse to send if the receipt can't be written (each refusal prints problem + cause + fix). User-facing sinks fail open with a stderr warning — the design binary's OpenAI calls, update-check, the read-only dashboards, and git-class receipts proceed even when the receipt write failed, so a fail-open send can go unrecorded (warned, by design). The new-sink scanner in `test/egress-receipt-wiring.test.ts` fails CI when an off-machine sink ships unwired; its only exemptions are enumerated with reasons (user-directed page fetches, reachability probes, install-doc strings, skill prose). Inspect the ledger with `bin/gstack-egress`: `list` (what gstack attempted to send), `verify` (recompute the chain, exit 3 on tamper), `grants` (the standing consent settings and how to revoke each). `verify` detects in-place edits, reordering, and mid-chain deletion; it does NOT detect tail-truncation, whole-file re-fabrication, or deletion of the ledger itself — guarding against the same-machine, same-user actor who owns the file is out of scope for a forensic log. Threat model: the ledger is forensic observability of ATTEMPTED egress — it records what gstack tried to send so accidents are auditable; it is not an exfiltration control. diff --git a/CLAUDE.md b/CLAUDE.md index c23c19ab4..6f976c12d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -251,7 +251,8 @@ send off the machine MUST write a hash-chained receipt to `writeReceipt` from `lib/egress-receipt.ts`; shell scripts source `bin/gstack-egress-lib.sh` and use `_receipted_curl` / `_receipted_git`. Failure polarity is per-class: fail-closed for sensitive sinks (brain-sync, memory-ingest, -gbrain-sync, telemetry, ngrok tunnels, mcp-verify, supabase-provision), fail-open +gbrain-sync, telemetry, ngrok tunnels, mcp-verify, supabase-provision, and the +Memorable bridge's per-prompt memorable-recall hand-off), fail-open + stderr warning for user-facing ones (design OpenAI calls, update-check, dashboards, git-class ops). The new-sink scanner in `test/egress-receipt-wiring.test.ts` fails CI on an unreceipted `curl` /