docs: fold the Codex accuracy review of the release docs

Six findings, all verified against source before fixing:

1. 'Every send writes a receipt' overclaimed — fail-open sinks proceed with
   a stderr warning when the receipt write fails, so a fail-open send can go
   unrecorded (lib/egress-receipt.ts:8-14). Descriptive prose now says so;
   the receipted framing keeps 'attempted'.
2. 'Receipts hash the request body' is wrong for subprocess-owned sends —
   git pushes record sha256: null (lib/egress-receipt.ts:71).
3. 'grants shows every consent in force' overclaimed — it reports the four
   standing config settings (bin/gstack-egress:139-181). Reworded in
   README, ARCHITECTURE, and the CHANGELOG entry.
4. 'Zero-exception scanner' vs reality: the new-sink scanner carries a
   reasoned SCANNER_EXEMPT list (user-directed fetches, probes, instruction
   strings, skill prose). CLAUDE.md now names it.
5. Error-catalog cause/fix for the receipt refusal: the writer mkdirs the
   ledger dir itself, so 'missing' isn't a cause and bare chmod fails when
   it is absent — cause reworded, fix is mkdir -p && chmod.
6. gbrain-sync first-run steps described the retired binary's behavior:
   default repo is gstack-artifacts-$USER, and init PRINTS the gbrain
   hookup command (never auto-executes; bin/gstack-artifacts-init:384-419).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-13 18:40:43 -07:00
co-authored by Claude Fable 5
parent 1ac4683b7b
commit eb066a4373
7 changed files with 24 additions and 21 deletions
+3 -3
View File
@@ -149,11 +149,11 @@ The browser registry (Comet, Chrome, Arc, Brave, Edge) is hardcoded. Database pa
### Egress receipt ledger (v1.63.0.0) ### Egress receipt ledger (v1.63.0.0)
Every gstack-initiated off-machine send 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 hash the request body; they never store it. 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. The zero-exception scanner in `test/egress-receipt-wiring.test.ts` fails CI when any new off-machine sink ships unwired. 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).
Inspect the ledger with `bin/gstack-egress`: `list` (what gstack attempted to send), `verify` (recompute the chain, exit 3 on tamper), `grants` (every consent in force). 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. 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). 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.
### Unicode sanitization at server egress (v1.38.0.0) ### Unicode sanitization at server egress (v1.38.0.0)
+1 -1
View File
@@ -53,7 +53,7 @@ bug fix and the port shortlist were selected and hardened for upstream.
#### Added #### Added
- `gstack-egress` — read the hash-chained egress receipt ledger: `list` (what - `gstack-egress` — read the hash-chained egress receipt ledger: `list` (what
gstack attempted to send off-machine), `verify` (recompute the chain, exit 3 on gstack attempted to send off-machine), `verify` (recompute the chain, exit 3 on
tamper), `grants` (every consent grant in force and how to revoke it). tamper), `grants` (the standing consent settings and how to revoke each).
- `gstack-context-bill` — offline token bill-of-materials for a skills tree: - `gstack-context-bill` — offline token bill-of-materials for a skills tree:
always-on discovery cost vs per-invocation cost, `--diff` between two trees, always-on discovery cost vs per-invocation cost, `--diff` between two trees,
`--budget`, and `--exact` (opt-in, measures against the real tokenizer). `--budget`, and `--exact` (opt-in, measures against the real tokenizer).
+5 -3
View File
@@ -348,9 +348,11 @@ send off the machine MUST write a hash-chained receipt to
polarity is per-class: fail-closed for sensitive sinks (brain-sync, memory-ingest, 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), fail-open
+ stderr warning for user-facing ones (design OpenAI calls, update-check, + stderr warning for user-facing ones (design OpenAI calls, update-check,
dashboards, git-class ops). The zero-exception scanner in dashboards, git-class ops). The new-sink scanner in
`test/egress-receipt-wiring.test.ts` fails CI on any unreceipted `curl` / `test/egress-receipt-wiring.test.ts` fails CI on an unreceipted `curl` /
`git push` / `fetch` to a non-loopback host — if you add a new off-machine sink, `git push` / `fetch` to a non-loopback host unless the file carries a reasoned
entry in its `SCANNER_EXEMPT` list (user-directed page fetches, reachability
probes, instruction strings, skill prose) — if you add a new off-machine sink,
wire it through the helpers and add it to the enumerated sink list. Inspect with wire it through the helpers and add it to the enumerated sink list. Inspect with
`bin/gstack-egress` (`list` | `verify`, exit 3 on tamper | `grants`). Threat `bin/gstack-egress` (`list` | `verify`, exit 3 on tamper | `grants`). Threat
model: forensic observability of ATTEMPTED egress, not an exfiltration control. model: forensic observability of ATTEMPTED egress, not an exfiltration control.
+2 -2
View File
@@ -243,7 +243,7 @@ Beyond the slash-command skills, gstack ships standalone CLIs for workflows that
|---------|-------------| |---------|-------------|
| `gstack-model-benchmark` | **Cross-model benchmark** — run the same prompt through Claude, GPT (via Codex CLI), and Gemini; compare latency, tokens, cost, and (optionally) LLM-judge quality score. Auth detected per provider, unavailable providers skip cleanly. Output as table, JSON, or markdown. `--dry-run` validates flags + auth without spending API calls. | | `gstack-model-benchmark` | **Cross-model benchmark** — run the same prompt through Claude, GPT (via Codex CLI), and Gemini; compare latency, tokens, cost, and (optionally) LLM-judge quality score. Auth detected per provider, unavailable providers skip cleanly. Output as table, JSON, or markdown. `--dry-run` validates flags + auth without spending API calls. |
| `gstack-taste-update` | **Design taste learning** — writes approvals and rejections from `/design-shotgun` into a persistent per-project taste profile. Decays 5%/week. Feeds back into future variant generation so the system learns what you actually pick. | | `gstack-taste-update` | **Design taste learning** — writes approvals and rejections from `/design-shotgun` into a persistent per-project taste profile. Decays 5%/week. Feeds back into future variant generation so the system learns what you actually pick. |
| `gstack-egress` | **Egress receipt auditor** — every gstack-initiated off-machine send writes a tamper-evident, hash-chained receipt to `~/.gstack/security/egress.jsonl` before the send. `list` shows what gstack attempted to send and to which host, `grants` shows every consent grant in force plus the exact command that revokes it, `verify` recomputes the hash chain and exits 3 on tamper. | | `gstack-egress` | **Egress receipt auditor** — every gstack-initiated off-machine send writes a tamper-evident, hash-chained receipt to `~/.gstack/security/egress.jsonl` before the send. `list` shows what gstack attempted to send and to which host, `grants` shows the standing consent settings plus the exact command that revokes each, `verify` recomputes the hash chain and exits 3 on tamper. |
| `gstack-context-bill` | **Token bill-of-materials** — read-only, offline audit of what an installed skills tree costs in tokens: always-on frontmatter every session pays vs per-invocation SKILL.md + forced references. `--diff` compares two trees, `--budget` enforces a ceiling, `--exact` opts into Anthropic `count_tokens` (sends file text off-machine; writes an egress receipt first, degrades to the offline estimate if the receipt can't be written). | | `gstack-context-bill` | **Token bill-of-materials** — read-only, offline audit of what an installed skills tree costs in tokens: always-on frontmatter every session pays vs per-invocation SKILL.md + forced references. `--diff` compares two trees, `--budget` enforces a ceiling, `--exact` opts into Anthropic `count_tokens` (sends file text off-machine; writes an egress receipt first, degrades to the offline estimate if the receipt can't be written). |
| `gstack-ios-qa-daemon` | **iOS QA daemon** — Mac-side broker between an agent and a connected iPhone over USB CoreDevice. Loopback by default; `--tailnet` opens a Tailscale-facing listener with identity-gated capability tiers. Single-instance via flock on `~/.gstack/ios-qa-daemon.pid`. See [docs/howto-ios-testing-with-gstack.md](docs/howto-ios-testing-with-gstack.md). | | `gstack-ios-qa-daemon` | **iOS QA daemon** — Mac-side broker between an agent and a connected iPhone over USB CoreDevice. Loopback by default; `--tailnet` opens a Tailscale-facing listener with identity-gated capability tiers. Single-instance via flock on `~/.gstack/ios-qa-daemon.pid`. See [docs/howto-ios-testing-with-gstack.md](docs/howto-ios-testing-with-gstack.md). |
| `gstack-ios-qa-mint` | **iOS allowlist manager** — owner-grant CLI for the tailnet allowlist. `grant`/`revoke`/`list` against `~/.gstack/ios-qa-allowlist.json` (mode 0600). Remote agents never auto-allowlist; this is the explicit-intent path. | | `gstack-ios-qa-mint` | **iOS allowlist manager** — owner-grant CLI for the tailnet allowlist. `grant`/`revoke`/`list` against `~/.gstack/ios-qa-allowlist.json` (mode 0600). Remote agents never auto-allowlist; this is the explicit-intent path. |
@@ -452,7 +452,7 @@ gstack includes **opt-in** usage telemetry to help improve the project. Here's e
- **What's sent (if you opt in):** skill name, duration, success/fail, gstack version, OS. That's it. - **What's sent (if you opt in):** skill name, duration, success/fail, gstack version, OS. That's it.
- **What's never sent:** code, file paths, repo names, branch names, prompts, or any user-generated content. - **What's never sent:** code, file paths, repo names, branch names, prompts, or any user-generated content.
- **Change anytime:** `gstack-config set telemetry off` disables everything instantly. - **Change anytime:** `gstack-config set telemetry off` disables everything instantly.
- **Every off-machine send is receipted.** Any gstack-initiated network send — telemetry included — writes a hash-chained, tamper-evident receipt to `~/.gstack/security/egress.jsonl` before the send. Audit with `gstack-egress list`, verify the chain with `gstack-egress verify` (exit 3 on tamper), see every consent in force with `gstack-egress grants`. The ledger records attempted sends so accidents are auditable — it's an audit trail, not a network firewall. - **Every off-machine send is receipted.** Any gstack-initiated network send — telemetry included — writes a hash-chained, tamper-evident receipt to `~/.gstack/security/egress.jsonl` before the send; sensitive sinks refuse to send at all if the receipt can't be written. Audit with `gstack-egress list`, verify the chain with `gstack-egress verify` (exit 3 on tamper), see the standing consent settings with `gstack-egress grants`. The ledger records attempted sends so accidents are auditable — it's an audit trail, not a network firewall.
Data is stored in [Supabase](https://supabase.com) (open source Firebase alternative). The schema is in [`supabase/migrations/`](supabase/migrations/) — you can verify exactly what's collected. The Supabase publishable key in the repo is a public key (like a Firebase API key) — row-level security policies deny all direct access. Telemetry flows through validated edge functions that enforce schema checks, event type allowlists, and field length limits. Data is stored in [Supabase](https://supabase.com) (open source Firebase alternative). The schema is in [`supabase/migrations/`](supabase/migrations/) — you can verify exactly what's collected. The Supabase publishable key in the repo is a public key (like a Firebase API key) — row-level security policies deny all direct access. Telemetry flows through validated edge functions that enforce schema checks, event type allowlists, and field length limits.
+1 -1
View File
@@ -132,7 +132,7 @@ Storage: `~/.gstack/gbrain-repo-policy.json`, mode 0600, schema-versioned so fut
The skill runs three stages — code, memory, brain-sync — independently. A failure in one doesn't block the others. State persists to `~/.gstack/.gbrain-sync-state.json` so re-running picks up cleanly. The skill runs three stages — code, memory, brain-sync — independently. A failure in one doesn't block the others. State persists to `~/.gstack/.gbrain-sync-state.json` so re-running picks up cleanly.
Stages that can send data off-machine (code sync into a possibly-remote gbrain DB, memory ingest, the brain-sync push) each write a tamper-evident receipt to the egress ledger (`~/.gstack/security/egress.jsonl`) before sending, fail-closed: if the receipt can't be written, the stage refuses with `EGRESS_RECEIPT_FAILED` instead of syncing unrecorded. Fix is usually `chmod -R u+w ~/.gstack/security`, then re-run. Inspect receipts with `gstack-egress list`. Stages that can send data off-machine (code sync into a possibly-remote gbrain DB, memory ingest, the brain-sync push) each write a tamper-evident receipt to the egress ledger (`~/.gstack/security/egress.jsonl`) before sending, fail-closed: if the receipt can't be written, the stage refuses with `EGRESS_RECEIPT_FAILED` instead of syncing unrecorded. Fix is usually `mkdir -p ~/.gstack/security && chmod -R u+w ~/.gstack/security`, then re-run. Inspect receipts with `gstack-egress list`.
**What it does on a fresh worktree:** **What it does on a fresh worktree:**
+4 -3
View File
@@ -103,12 +103,13 @@ made, and the queue is preserved — the next run retries the whole drain.
`gstack-brain-sync --status` shows `EGRESS_RECEIPT_FAILED` as the failure `gstack-brain-sync --status` shows `EGRESS_RECEIPT_FAILED` as the failure
detail. detail.
**Cause.** `~/.gstack/security/` is missing or unwritable, the disk is **Cause.** `~/.gstack/security/` is not writable (the receipt writer creates
full, or `GSTACK_HOME` points at a read-only location. it when missing, so absence alone is not the cause), the disk is full, or
`GSTACK_HOME` points at a read-only location.
**Fix.** **Fix.**
```bash ```bash
chmod -R u+w ~/.gstack/security mkdir -p ~/.gstack/security && chmod -R u+w ~/.gstack/security
``` ```
Then run any skill (or `gstack-brain-sync --once`) to retry. Inspect the Then run any skill (or `gstack-brain-sync --once`) to retry. Inspect the
ledger with `gstack-egress list`; verify its hash chain with ledger with `gstack-egress list`; verify its hash chain with
+8 -8
View File
@@ -38,18 +38,18 @@ The command:
1. Turns `~/.gstack/` into a git repo. 1. Turns `~/.gstack/` into a git repo.
2. Asks for a remote URL (default: `gh repo create --private 2. Asks for a remote URL (default: `gh repo create --private
gstack-brain-$USER`). Any git remote works — GitHub, GitLab, Gitea, gstack-artifacts-$USER`). Any git remote works — GitHub, GitLab, Gitea,
self-hosted. self-hosted.
3. Pushes an initial commit with just the config. 3. Pushes an initial commit with just the config.
4. Writes `~/.gstack-artifacts-remote.txt` (URL-only, no secrets — 4. Writes `~/.gstack-artifacts-remote.txt` (URL-only, no secrets —
safe to copy to another machine). safe to copy to another machine).
5. Wires the gstack-brain repo into your local gbrain as a federated 5. Prints the `gbrain sources add` hookup command for the brain host
source (via `gbrain sources add` + `git worktree`) so `gbrain search` (never auto-executed — run it yourself, or on your own machine
can index your synced learnings, plans, and designs. Implementation `bin/gstack-gbrain-source-wireup` does the same wiring) so
lives in `bin/gstack-gbrain-source-wireup`. The old `gbrain search` can index your synced learnings, plans, and designs.
`gstack-brain-reader add --ingest-url ...` HTTP path was removed in The old `gstack-brain-reader add --ingest-url ...` HTTP path was
v1.15.1.0 — it depended on a `/ingest-repo` endpoint gbrain never removed in v1.15.1.0 — it depended on a `/ingest-repo` endpoint gbrain
shipped. never shipped.
After init, the **next skill you run** will ask you ONE question about After init, the **next skill you run** will ask you ONE question about
privacy mode: privacy mode: