updates

ober

14c901db52a43e73c04d9485dc0bd72b3030d74a

diff --git a/SECURITY.md b/SECURITY.md
index 65421bd..178c96f 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -7,6 +7,8 @@ library or developer tooling work.
 
 Production claims require the gates in [`docs/release-security.md`](docs/release-security.md)
 and the cross-repository tracker at `~/mine/jerboa-production-readiness.md`.
+The security roadmap and prioritized implementation handoff lives in
+[`docs/kimi3-security-recommmendations.md`](docs/kimi3-security-recommmendations.md).
 
 ## Supported Status
 
diff --git a/docs/index.md b/docs/index.md
index f9defa4..5059367 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -66,8 +66,9 @@ Start with [quickstart.md](quickstart.md), then use
 - [harden-usage.md](harden-usage.md) - antidebug, seccomp, integrity, and related APIs
 - [hide.md](hide.md) - reducing information leakage from binaries
 - [unification.md](unification.md) - security-first system architecture
-- [ai-threat.md](ai-threat.md) - AI-assisted adversary assessment
-- [uhoh.md](uhoh.md) - cryptographic failure lessons
+ - [ai-threat.md](ai-threat.md) - AI-assisted adversary assessment
+ - [kimi3-security-recommmendations.md](kimi3-security-recommmendations.md) - K3-resistant security roadmap and implementation handoff
+ - [uhoh.md](uhoh.md) - cryptographic failure lessons
 
 ## Build, Packaging, And Release
 
diff --git a/docs/kimi3-security-recommmendations.md b/docs/kimi3-security-recommmendations.md
new file mode 100644
index 0000000..a53e867
--- /dev/null
+++ b/docs/kimi3-security-recommmendations.md
@@ -0,0 +1,974 @@
+# K3-Resistant Security Recommendations — Handoff for Implementing Models
+
+Status: living handoff document. Created 2026-07-27 from a full review of
+`docs/*`, `SECURITY.md`, `lib/std/security/`, `jerboa-native-rs/`, `tests/`,
+the `Makefile`, and the `.jerboa-system` manifest. Tone per
+[`Philosophy.md`](Philosophy.md): concrete, no hype, no stubs. Every claim
+about code was verified in the tree on the date above.
+
+**Audience:** the next model (or human) asked to implement security work in
+this repo. Each work item has an ID, the threat it closes, exact files to
+touch, and acceptance criteria. Implement top-down (P0 → P3). Do not skip
+P0-01 — the build is currently broken.
+
+---
+
+## Table of Contents
+
+1. [Why this document exists](#1-why-this-document-exists)
+2. [The threat: the 23-minute zero-day era](#2-the-threat-the-23-minute-zero-day-era)
+3. [Defense doctrine](#3-defense-doctrine)
+4. [Current posture inventory (verified)](#4-current-posture-inventory-verified)
+5. [Gap analysis](#5-gap-analysis)
+6. [P0 — Containment foundation (do first)](#6-p0--containment-foundation-do-first)
+7. [P1 — Harden the boundaries](#7-p1--harden-the-boundaries)
+8. [P2 — Supply chain, ecosystem, process](#8-p2--supply-chain-ecosystem-process)
+9. [P3 — Research and frontier](#9-p3--research-and-frontier)
+10. [Standing practices](#10-standing-practices)
+11. [Application containment checklist (for Jerboa users)](#11-application-containment-checklist-for-jerboa-users)
+12. [Metrics — how we know the posture improved](#12-metrics--how-we-know-the-posture-improved)
+13. [Appendix A: Verified commands](#appendix-a-verified-commands)
+14. [Appendix B: Documentation inventory](#appendix-b-documentation-inventory)
+15. [Appendix C: Honest limitation ledger](#appendix-c-honest-limitation-ledger)
+
+---
+
+## 1. Why this document exists
+
+The motivating scenario: a frontier model ("K3-class" — named after the
+kimi-k3 incident this document is filed under) was pointed at a mature,
+heavily-fuzzed, memory-unsafe C codebase (redis) and produced a working
+zero-day in **23 minutes**. The user's requirement is not "make Jerboa
+bug-free" (impossible) but:
+
+> **Have the best possible security posture so that zero-days don't fully
+> take over the applications written in this language.**
+
+That is a *blast-radius containment* requirement. It decomposes into five
+engineering goals, which organize every recommendation below:
+
+| # | Goal | Meaning |
+|---|------|---------|
+| G1 | **Shrink the target** | Eliminate vulnerability *classes* so there is less to find. |
+| G2 | **Cap the blast radius** | When a bug is found, the exploit hits a wall: no ambient authority, kernel-enforced confinement, bounded egress. |
+| G3 | **Find it first** | Fuzz, scan, and AI-red-team our own tree continuously, faster than adversaries do. |
+| G4 | **Fail closed** | Every degraded security control is a hard error, never a silent fallback. |
+| G5 | **Recover fast** | Reproducible, signed, auditable releases so a fix ships in hours, not days. |
+
+This document maps the existing posture onto G1–G5, identifies the gaps,
+and turns each gap into an implementable work item.
+
+### Rules for the implementing model
+
+1. **Read [`AGENTS.md`](../AGENTS.md) first.** The `.ss`/`.sls` editing rules
+   are mandatory (balanced-edit tools only, `jerboa_check_balance` after every
+   change, no hand paren-counting).
+2. **One work item per commit.** Each item below has acceptance criteria;
+   do not mark done without running them.
+3. **Never stub or weaken a security control to make a build pass** — the
+   repo's standing rule (Philosophy.md, "What we explicitly reject"). A
+   fail-closed refusal is always acceptable; a fake control never is.
+4. **Update the docs in the same session.** If you change behavior described
+   in `security-reference.md`, `safety-guide.md`, `ai-threat.md`,
+   `release-security.md`, or this file, fix the doc with the code.
+5. **Save discoveries.** Non-trivial patterns → `jerboa_howto_add`; mistakes
+   → `data/anti-patterns.sexp`; error→fix → `jerboa_error_fix_add`.
+6. **Pre-commit gate:** `make binary` on macOS/FreeBSD, `make docker-build`
+   on Linux, per AGENTS.md. Do not commit with a failing build.
+
+---
+
+## 2. The threat: the 23-minute zero-day era
+
+### 2.1 What changes
+
+| Dimension | Pre-AI era | K3-class era |
+|---|---|---|
+| Time to working exploit for a fresh bug | days–weeks | minutes–hours |
+| Cost of auditing 100k LOC for vuln patterns | $50k+ human review | ~free, continuous |
+| Fuzzer authoring | specialist weeks | model writes harness in minutes |
+| Exploit chaining (bug → RCE → lateral) | rare expertise | on demand |
+| Patch window defender can rely on | weeks | **none** |
+
+Consequences:
+
+- **"Find and fix every bug" stops working as the primary strategy.** The
+  adversary re-finds bugs faster than you patch them. Bug-finding speed is
+  now symmetric; only *structural* asymmetries favor the defender.
+- **Heuristic defenses depreciate fastest.** Anything that works by
+  "recognizing a known-bad pattern" (blocklists, injection regexes,
+  signature AV) is exactly what a generative adversary is best at
+  circumventing. [`ai-threat.md`](ai-threat.md) already flags the SQL
+  injection heuristic this way.
+- **Known-unknowns become knowns.** Every "we should audit this someday"
+  corner of the tree (the 91 `foreign-procedure` files, the 225 exported
+  Rust FFI symbols, the raw `read` paths) will be systematically enumerated.
+  There is no security through obscurity left.
+- **Supply-chain and tooling surfaces get attacked at machine speed.**
+  Install scripts, code generators, MCP tools, LSP servers, and CI scripts
+  are all "applications written in this language" too.
+
+### 2.2 What does not change
+
+The mechanisms that survive a K3-class adversary are the ones that were
+never pattern-based:
+
+- **Memory safety of the managed core.** Pure-Scheme Jerboa code has no
+  buffer overflows, no UAF, no ROP. The redis scenario (memory-corruption
+  zero-day in C) does not transfer to code that stays off the FFI boundary.
+  This is Jerboa's single biggest structural asset ([`ai-threat.md`](ai-threat.md) §"What Actually Protects You").
+- **Allowlist (not blocklist) confinement.** `(std security restrict)`'s
+  `(only (chezscheme) ,@safe-bindings)` environment is *provably closed*:
+  bindings that aren't listed don't exist, and no prompt engineering or
+  gadget chain can summon `system` from an empty binding. Verified at
+  `lib/std/security/restrict.ss:128`.
+- **Kernel enforcement.** seccomp BPF and Landlock are evaluated by the
+  kernel, not by software the attacker can reason around. Irreversible once
+  installed.
+- **Unforgeable capabilities.** Attenuation-only tokens with CSPRNG nonces.
+  An AI can *misuse* a capability it was handed (confused deputy) but
+  cannot mint one.
+- **Cryptographic provenance.** Ed25519 signatures, content addressing,
+  reproducible builds. Math, not heuristics.
+
+### 2.3 The uncomfortable truth
+
+- **Jerboa cannot fix logic bugs.** Authorization bypass, TOCTOU in
+  application code, and state-machine errors are language-agnostic
+  ([`ai-threat.md`](ai-threat.md) §"Where AI Adversaries Will Actually
+  Win"). What the language *can* do is make the safe pattern the default
+  and the unsafe pattern unrepresentable — that's most of P0/P1 below.
+- **The FFI boundary is our `unsafe`.** 91 files under `lib/std/` mention
+  `foreign-procedure`; `jerboa-native-rs/src` exports ~225 `#[no_mangle]`
+  C-ABI symbols and contains 432 matches for `unsafe`. That boundary is
+  where a K3-class adversary will concentrate, because it is the only place
+  memory-corruption patterns *exist* in a Jerboa application.
+  [`Philosophy.md`](Philosophy.md) Principle 4 and open tension #5 both
+  name this.
+- **The process-isolation boundary is currently missing from the tree.**
+  Every doc says "put adversarial work in a bounded, separately exec'd
+  worker." No such worker launcher exists in-tree today:
+  `sandbox-launch` returns status 126 `pre-exec-refused`,
+  `supervise-available?` returns `#f`, and the raw-fork launchers all fail
+  closed by design ([`limits.md`](limits.md), [`security-reference.md`](security-reference.md) §5–6).
+  **This is the single most important gap.** P0-02 builds it.
+
+---
+
+## 3. Defense doctrine
+
+Five principles, mapped to the goals G1–G5. Every work item below cites the
+principles it serves.
+
+**D1 — Structural beats heuristic (G1).** Prefer defenses that remove a
+class (memory safety, allowlists, parameterized queries, no-install-code)
+over defenses that recognize an instance (injection regexes, blocklists).
+Where a heuristic exists, it must be documented as a speed bump and layered
+under a structural defense.
+
+**D2 — Least authority by default (G2).** Ambient authority is the bug
+([`Philosophy.md`](Philosophy.md) Principle 1). Every process that touches
+untrusted input runs with: the safe prelude, an explicit capability set, a
+kernel sandbox (Landlock/seccomp/Seatbelt/Capsicum as available), a
+declared egress policy, and OS resource limits. Defaults must fail closed.
+
+**D3 — Process boundaries for hostile input (G2, G4).** In-process
+restriction (`run-safe-eval`) is for *semi-trusted* expressions only.
+Anything adversarial gets a fresh exec'd worker with irreversible controls
+installed post-exec. Never run Scheme between fork and exec (the reason
+the raw-fork launchers were retired).
+
+**D4 — Adversarial self-testing (G3).** The same model class that attacks
+us defends us: continuous fuzzing with seed corpora and crash regression,
+a standing AI-red-team job against our own tree, and a scanner rule base
+that grows with every finding.
+
+**D5 — Auditable everything (G4, G5).** Reproducible builds, signed
+artifacts, SBOM, TCB accounting, append-only audit logs. When (not if) a
+zero-day lands, the questions "are we affected, which binaries, shipped
+when" must be answerable from `dist/release-evidence/` in minutes.
+
+---
+
+## 4. Current posture inventory (verified)
+
+### 4.1 Defense layers that exist and are real
+
+| Layer | Module(s) | Verified state | Doc |
+|---|---|---|---|
+| Safe-by-default prelude (no FFI, no `fork-thread`, no `eval`) | `(jerboa prelude safe)`, `(std safe)` | exists; has a known cosmetic import-conflict warning | [safety-guide.md](safety-guide.md) §1 |
+| Allowlist sandbox (closed env, bounded `jerboa-read`) | `(std security restrict)` | exists; `safe-bindings` at `restrict.ss:24` | [security-reference.md](security-reference.md) §2 |
+| In-process bounded eval (engine timeout, result cap, fails closed on process controls) | `(std security sandbox)` `run-safe-eval` | exists; no memory limit, no FFI preemption (documented) | security-reference §6 |
+| Capability tokens (sealed, CSPRNG nonce, monotone attenuation, revocation) | `(std security capability)` | exists; path check is prefix-string based (limitation documented) | [capability.md](capability.md) |
+| Typed capability declarations | `(std security capability-typed)` | exists | security-reference §3 |
+| Taint tracking + safe sinks | `(std security taint)` | exists; opt-in only (limitation documented) | security-reference §4 |
+| Kernel FS confinement (Linux 5.13+, ABI v1–v3) | `(std security landlock)`, `(std os landlock-native)` | real syscalls | security-reference §5 |
+| Kernel syscall filter (BPF, x86_64 + aarch64 numbers) | `(std security seccomp)`, `(std os seccomp)` | real BPF | security-reference §5 |
+| macOS / FreeBSD confinement | `(std security seatbelt)`, `(std security capsicum)` | exist | lib listing |
+| Self-confinement (pledge/unveil-style `cage!`) | `(std security cage)` | exists; **absent from security-reference.md** — doc gap | `lib/std/security/cage.ss` |
+| Privilege-separation pipe channels (no launcher) | `(std security privsep)` | channels only, by design | security-reference §5 |
+| Launch policy planner + egress policy objects (no launcher) | `(std os limits sandbox)` | passive policy only, `pre-exec-refused` | [limits.md](limits.md) |
+| Native async exec launcher (collect-safe) | `(std os aproc)` | exists — the primitive P0-02 should build on | [aproc.md](aproc.md) |
+| Parser hardening (depth/size/backtrack budgets: reader, JSON, XML, YAML, DNS, HTTP/2, WS, zlib, base64, hex, CSV, pregexp, format) | various | phases 1–4 done, 42 tests; **phase 5 (FFI audit) not started** | security-reference §7 |
+| Safe deserialization (tagged-JSON envelope, no native FASL on untrusted paths) | `(std safe-fasl)`, `(std fasl)` (trusted-only) | exists; raw-FASL callers still need triage (P0-04) | [safety-guide.md](safety-guide.md) §10 |
+| Crypto (Rust `ring`: AEAD, HMAC, PBKDF2, scrypt, ChaCha20, digests; CSPRNG from `/dev/urandom`; timing-safe compare) | `(std crypto native-rust)` et al. | exists; OpenSSL legacy paths still present; no Argon2id | security-reference §10 |
+| Secure memory (mlock, guard pages, DONTDUMP, DONTFORK, explicit_bzero) | `(std crypto secure-mem)` | exists; low-level pointer API only | security-reference §9 |
+| Lifetime-scoped secrets with auto-wipe | `(std security secret)` | exists; **absent from security-reference.md** — doc gap | `lib/std/security/secret.ss` |
+| Env/secret broker for child processes (allow/deny patterns, redaction) | `(std security env)` | exists; **absent from security-reference.md** — doc gap | `lib/std/security/env.ss` |
+| Sanitization (HTML/attr/URL/SQL/path/header) | `(std security sanitize)` | exists | security-reference §12 |
+| Error classification (opaque client refs) | `(std security errors)` | exists | security-reference §12 |
+| Audit log (append-only hash-chained JSONL) | `(std security audit)`, `(std security audit-log)` | exists | security-reference §12 |
+| Info-flow lattice (public<internal<secret<top-secret) | `(std security flow)` | exists | security-reference §12 |
+| I/O interception (effect-mediated FS/net/proc) | `(std security io-intercept)` | exists | security-reference §12 |
+| Build-time import audit (detects direct `(chezscheme)` bypass) | `(std security import-audit)` | exists | security-reference §3 |
+| Repo security profiles for scanners | `(std security profile)` | exists | `lib/std/security/profile.ss` |
+| Binary hardening (anti-debug, integrity, Ed25519 self-verify) | `(std os antidebug)`, `(std os integrity)` | exists; encrypted-boot and full pipeline are **design-only** ([harden.md](harden.md) §7/§12) | [harden-usage.md](harden-usage.md) |
+| Chez runtime hardening (`--enable-harden`: stack protector, FORTIFY, RELRO, CET/BTI/PAC compile-side; SHSTK opt-in at runtime) | vendored Chez fork + launcher hooks | phases 1–2 done, 3.3 done for generated binaries; call/cc-aware SHSTK deferred | [chez-hardening.md](chez-hardening.md) |
+| Structured concurrency, timeouts, resource RAII, finalizer net | `(std safe)`, `(std concur deadlock)` | exists | safety-guide §3–6 |
+| Lint rules incl. `sql-interpolation`, `unsafe-import` | `(std lint)` | 14 rules | safety-guide §11 |
+| WASM sandbox for hostile parsers (wasmi) | `wasm_sm.rs`, `tests/test-wasm-sandbox.ss` | exists, opt-in | [wasm.md](wasm.md), [slang.md](slang.md) |
+
+### 4.2 Release and CI machinery that exists
+
+- `make audit` = `security-audit audit-native data-check security-production unification-check` (PR gate).
+- `make verify` — one-command local production gate.
+- `make sbom`, `make reproducibility-report`, `make reproducibility-compare`,
+  `make signing-evidence`, `make release-evidence`, `make security-hardware-smoke`.
+- Unification toolchain in `tools/`: `check-capability-use.ss`,
+  `check-native-boundary.ss`, `check-install-policy.ss`, `tcb-report.ss`,
+  `capability-plan.ss`, `check-audit-events.ss`, `workspace-health.ss`,
+  `system-report.ss`, `system-provenance.ss`, `system-diff.ss`,
+  `check-generated-policy.ss`.
+- 13 fuzz harnesses in `tests/fuzz/harness/` + `make fuzz` / `fuzz-smoke` /
+  `fuzz-deep` and 13 per-parser Makefile targets.
+- 21 security-related test files (see Appendix A).
+- jpkg: content-addressed reproducible artifacts, lockfile-as-boundary, TUF
+  signed metadata, Ed25519 package signatures, SLSA/in-toto provenance,
+  transparency log, `jpkg audit` (OSV), declared-capability gating
+  ([jpkg-guide.md](jpkg-guide.md)).
+
+### 4.3 Verified numbers
+
+| Metric | Value (2026-07-27) | Command |
+|---|---|---|
+| Files under `lib/std/` mentioning `foreign-procedure` | **91** | `grep -rl foreign-procedure lib/std \| wc -l` |
+| `#[no_mangle]` C-ABI exports in `jerboa-native-rs/src` | **225** | `grep -c no_mangle jerboa-native-rs/src/*.rs` |
+| `unsafe` matches in `jerboa-native-rs/src` | **432** | `grep -c unsafe jerboa-native-rs/src/*.rs` |
+| Fuzz harnesses | 13 | `ls tests/fuzz/harness` |
+| Checked-in fuzz seed corpora | **0** | `tests/fuzz/` contains only `harness/` |
+| Security test files | 21 | Appendix A |
+| Vendored C amalgamation in tree | `vendor/jsqlite` | `ls vendor/` |
+
+### 4.4 Confirmed problems found during this review
+
+1. **`lib/std/compiler/pattern.ss` is unbalanced *right now*** (confirmed
+   with `jerboa_check_balance`: unclosed `(` at line 6). This is a build
+   blocker and violates the pre-commit gate. `git status` shows it clean,
+   meaning the breakage is committed. **Fix first** (P0-01). The same
+   breakage was reported for `lib/std/pipeline.ss`,
+   `tests/test-pipeline.ss`, `tests/test-security2-parsers.ss` in the
+   checklist appended to `security-reference.md` — re-verify all four.
+2. **No exec-based confined worker exists** (see §2.3). All confinement
+   docs point at a boundary that isn't built.
+3. **No fuzz seed corpora and no crash-regression corpus** — fuzzing is
+   harness-only; nothing preserves found crashes as tests.
+4. **`docs/ai-threat.md` comparative table is stale**: it rates supply
+   chain "Behind (cargo audit)" but jpkg now has TUF/signatures/transparency
+   /OSV audit. Refresh the table (P2-06).
+5. **Three security modules are undocumented** in `security-reference.md`:
+   `(std security cage)`, `(std security secret)`, `(std security env)`.
+6. **Cross-repo findings** recorded in `security-reference.md`'s appended
+   checklist remain open in sibling repos (out of scope here, tracked in
+   §8 P2-04 note): `jerboa-code` external-LLM launcher passes
+   `--dangerously-skip-permissions`/`--yolo` with an allow-by-default
+   sandbox profile; `jerboa-wafter` has a shell-injection via `system`;
+   `jerboa-inotify` has an unchecked FFI buffer cast; sibling cross-build
+   scripts run `git checkout --` against `$jerboa-home/lib`.
+
+---
+
+## 5. Gap analysis
+
+| Goal | Where we stand | The gap |
+|---|---|---|
+| G1 shrink target | Managed core is memory-safe; parsers budgeted; safe prelude exists | FFI surface (91 files / 225 symbols / 432 `unsafe`) never systematically audited (phase 5); `vendor/jsqlite` is C in the TCB; safe prelude not the default entry; import conflict undermines "safe symbol wins" confidence |
+| G2 cap blast radius | Capabilities, taint, kernel sandbox, egress policy objects all exist | **No worker launcher to assemble them at runtime**; taint opt-in; TOCTOU on paths; distributed actors unauthenticated; no memory limit story; `define-syntax` in sandbox |
+| G3 find it first | 13 harnesses, scanner w/ rule DB, lint | No corpora, no crash regression, no scheduled fuzzing, no standing AI-red-team, no exploit-shaped regression suite |
+| G4 fail closed | Raw-fork launchers retired correctly; `allow-degraded?` explicit | New controls must keep the invariant; degraded-mode warnings must be test-locked |
+| G5 recover fast | SBOM/repro/signing gates exist | TCB accounting manual; doc drift (stale tables, undocumented modules); independent-builder reproducibility not yet routine |
+
+Priority order follows directly: fix the build (it blocks everything),
+build the worker (G2's missing keystone), close the highest-value
+structural holes (FFI audit, raw-read triage, actor auth, safe-by-default),
+then industrialize find-first and recover-fast.
+
+---
+
+## 6. P0 — Containment foundation (do first)
+
+### K3-P0-01 — Repair the build; add a balance gate
+**Serves:** G4. **Effort:** hours.
+
+A committed unbalanced file means the safety guarantees in the docs are not
+the code that ships. This violates the repo's own pre-commit rule.
+
+- **Where:** `lib/std/compiler/pattern.ss` (confirmed unclosed `(` at line 6);
+  re-verify `lib/std/pipeline.ss`, `tests/test-pipeline.ss`,
+  `tests/test-security2-parsers.ss`.
+- **Do:** Recover per AGENTS.md rule 3: `git log -p --follow -- <file>` to
+  find the breaking commit; prefer fixing forward with
+  `jerboa_repair_balance` (dry-run, then `apply: true`) — **not** raw
+  `git checkout --`, which would discard the committed feature work. Then
+  add `tools/` wiring so a balance check over all tracked `.ss`/`.sls`
+  files runs inside `make audit` (a `jerboa_project_health_check`-style
+  pass), failing the gate on any unbalanced file.
+- **Accept:** `jerboa_check_balance` clean on all four files; `make binary`
+  succeeds; `make audit` fails when a synthetic unbalanced file is added.
+
+### K3-P0-02 — Build the exec-based confined worker (`(std security worker)`)
+**Serves:** G2, G3, G4. **Effort:** 1–2 weeks. **This is the keystone.**
+
+Every security doc routes adversarial work to "a bounded, separately exec'd
+worker" that does not exist. Build it as the assembly point for controls
+that already exist individually.
+
+- **Where:** new `lib/std/security/worker.ss`; build on `(std os aproc)`
+  (collect-safe argv exec, no shell) for launch; reuse policy records from
+  `(std os limits)`, `(std os limits sandbox)`, `(std security env)`,
+  `(std security landlock)`, `(std security seccomp)`, `(std security seatbelt)`,
+  `(std security capsicum)`, `(std security audit-log)`. Optionally a tiny
+  pre-exec helper in `jerboa-native-rs` (Rust) for rlimit/namespace setup
+  that must happen before Scheme boots.
+- **Design (fail closed at every step):**
+  - Worker = fresh `jerboa run worker-main.ss` process started via argv
+    (never shell), with `env-pure:` from an `(std security env)` policy
+    (allowlist + deny patterns like `*TOKEN*`).
+  - Supervisor owns: wall-clock deadline, process-group kill
+    (`kill(neg pid, SIGKILL)` after grace), bounded stdin/stdout/stderr
+    pipes (byte caps, close-on-limit), and structured result decoding.
+  - Worker installs, *before* reading any untrusted byte: rlimits
+    (AS/CPU/NOFILE/NO CORE), Landlock ruleset, seccomp filter (or
+    Seatbelt/Capsicum equivalent), then enters `(jerboa prelude safe)` and
+    signals readiness. Any setup failure = nonzero exit, no evaluation.
+  - Supervisor treats missing readiness, pipe overflow, or deadline as
+    kill-and-report; results carry `launched?`, refusal axis, and audit
+    record — the same shapes `(std os limits sandbox)` already defines, so
+    `sandbox-launch` can be re-pointed at the real backend while keeping
+    its fail-closed diagnostics contract.
+  - Wire `make-egress-policy` enforcement via `(std net allow-proxy)` as
+    the worker's only network route when a deny-default policy is set.
+- **Do NOT:** resurrect any Scheme-callback-after-fork API. The retirement
+  of `run-safe` thunks was correct ([security-reference.md](security-reference.md) §6); the worker
+  boundary is `exec`.
+- **Accept:** new `tests/test-worker.ss` demonstrating: (a) `system`/`eval`/
+  FFI unreachable inside worker even if smuggled into input; (b) CPU-spin
+  killed at deadline with process-group reaped (no orphans — regression
+  for the `pipeline-timeout` leak noted in security-reference.md's
+  checklist); (c) memory bomb killed by rlimit AS; (d) FS escape blocked by
+  Landlock on Linux; (e) oversized stdout truncated at cap; (f) requested
+  control that can't install ⇒ structured refusal, never silent
+  degradation; (g) audit-log records start/end. Docs: new section in
+  `security-reference.md` §5/§6 and rewrite of the "use an exec worker"
+  passages in `safety-guide.md` §2 and `ai-threat.md` to point at the real
+  API.
+
+### K3-P0-03 — Prove the restricted-environment allowlist closed
+**Serves:** G1, G4. **Effort:** 2–3 days.
+
+The allowlist is Jerboa's crown jewel; it currently rests on manual curation.
+
+- **Where:** new `tools/check-restrict-closure.ss`; `lib/std/security/restrict.ss`.
+- **Do:** (a) Mechanically verify every symbol in `safe-bindings` exists in
+  the pinned Chez version (catches typos that silently *widen* to
+  nothing or, worse, drift after a Chez upgrade). (b) Forbid-list sweep:
+  assert none of `system process open-*-file foreign-* load eval compile*
+  getenv putenv fork-thread import library call/cc call-with-current-continuation
+  gensym read read-eval` appears. (c) Reachability audit: expand each allowed
+  binding's definition in the vendored Chez source and flag any that can
+  reach a forbidden primitive (e.g. a seemingly-safe builtin that calls
+  `system` internally). Start with a documented manual audit table; automate
+  what's automatable. (d) Publish the resulting per-binding justification
+  into `security-reference.md` §2.
+- **Accept:** `make audit` runs the checker; removing a binding fails CI if
+  any doc/test references it; adding a binding requires editing the
+  justification table. A unit test evaluates escape attempts
+  (`(eval ...)`, `(system ...)`, `((foreign-procedure ...))`, macro-based
+  smuggling) inside `restricted-eval` and asserts `&assertion`/`unbound`
+  failures.
+
+### K3-P0-04 — Triage and close raw `read` / native FASL paths on untrusted input
+**Serves:** G1. **Effort:** 3–5 days.
+
+`security-reference.md`'s checklist already names exemplars:
+`jerbuild.ss:308`, `lib/std/actor/distributed.ss:303`, `lib/std/fasl.ss:27`,
+`lib/std/net/grpc.ss:162`. There are more.
+
+- **Do:** (a) Enumerate every call site of bare `read`, `fasl-read`, and
+  Chez `load` in `lib/`, `src/`, `tools/`, `mcp/`, `lsp/` (the MCP scanner
+  already flags these — run it repo-wide, `changed_only: #f`). (b) Classify
+  each site: *trusted build-time input* (comment + lint suppression with
+  justification) or *possibly adversarial* (convert to bounded
+  `jerboa-read` / `safe-fasl` envelope / reject). (c) For `(std fasl)`,
+  add a loud docstring/contract: trusted provenance only, never network/
+  workspace/IPC bytes. (d) Add an `unsafe-deserialize` lint rule so new
+  raw-`read` sites fail `make audit`.
+- **Accept:** scanner reports zero unclassified raw-read/FASL sites; each
+  remaining site has an inline provenance justification; `jerbuild.ss`,
+  `distributed.ss`, `grpc.ss` either bounded or documented-trusted.
+
+### K3-P0-05 — Authenticate distributed actor messages
+**Serves:** G2. **Effort:** 3–4 days.
+
+`security-reference.md` §13: "No message authentication for distributed
+actors — messages are still plaintext with no HMAC." A K3-class adversary
+that finds any deserialization-shape bug gets a network-reachable
+eval-shaped channel; authentication shrinks who can even reach the parser.
+
+- **Where:** `lib/std/actor/distributed.ss`; reuse `rust-hmac-sha256` /
+  `rust-timing-safe-equal?` from `(std crypto native-rust)`.
+- **Do:** HMAC-SHA256 over the canonical serialized frame with a per-peer
+  key; include timestamp + monotonic sequence for replay rejection; key
+  distribution out-of-band (document) or via X25519 handshake
+  (`x25519.rs` exists in the native backend). Fail closed: unauthenticated
+  or replayed frames dropped before `deserialize-message` runs.
+- **Accept:** tests: tampered frame rejected, replay rejected, wrong key
+  rejected, valid frame accepted; fuzz harness extended to mutate MAC'd
+  frames; `security-reference.md` §13 entry removed.
+
+### K3-P0-06 — Make the safe prelude the default surface
+**Serves:** G1, G2. **Effort:** 1 week.
+
+Today "always use `(jerboa prelude safe)`" is advice in docs. Advice is
+what a 23-minute adversary counts on users skipping.
+
+- **Do:** (a) `jerboa run file.ss` and the script path default to resolving
+  `(jerboa prelude)` as `(jerboa prelude safe)`; opting into the raw
+  prelude requires an explicit `(jerboa prelude unsafe)` (rename with
+  alias kept) or `--unsafe-prelude` flag, which prints a one-line warning
+  to stderr. (b) Fix the safe prelude import conflict (the "multiple
+  definitions" warning) so symbol resolution order is deterministic and
+  tested — a wrong-symbol resolution in a security wrapper is a real bug,
+  not cosmetics. (c) Extend `(std security import-audit)` so direct
+  `(chezscheme)` imports in application code fail `make audit` outside an
+  allowlisted set of stdlib-internal files. (d) Update `quickstart.md`,
+  `safety-guide.md`, `JERBOA-LANG.md`, `model-coding-guidance.md` and the
+  MCP cookbook to teach safe-prelude-first.
+- **Accept:** `tests/test-safe-prelude.ss` extended: a fresh script using
+  `(jerboa prelude)` cannot reach `foreign-procedure`, `system`, `eval`,
+  or `fork-thread`; the escape hatch works and warns; no import-conflict
+  warning remains at build.
+
+### K3-P0-07 — Security regression suite v1 (exploit-shaped tests)
+**Serves:** G3, G4. **Effort:** 3–4 days.
+
+Current tests verify features work; almost none verify *attacks fail*.
+
+- **Where:** new `tests/security/` tree (keeps attack tests separate from
+  feature tests), wired into `make test-security`.
+- **Do:** One test per historical finding so regressions are impossible:
+  sandbox default-env closure (#1 from the AI-attack table), unbounded
+  reader budgets (#2/#10), capability intersection attenuation (#3),
+  empty-host default-deny (#4), symlink canonicalization (#5), bounded
+  actor deserialization (#6), taint sink enforcement (#8), `read`/`gensym`
+  absence (#9), import-audit detection (#11), URL/attribute sanitization
+  (#12), raw-fork fail-closed (#13/#14), `run-safe-eval` degradation
+  refusal without `allow-degraded?`, plus new P0-02 worker escapes. Each
+  test names the finding it locks.
+- **Accept:** `make test-security` runs the suite; deliberately reverting
+  any one fix makes exactly its test fail (spot-check three).
+
+### K3-P0-08 — Fuzzing becomes continuous, seeded, and regressive
+**Serves:** G3. **Effort:** 1 week.
+
+Harnesses without corpora find a bug once and forget it.
+
+- **Do:** (a) Add `tests/fuzz/corpus/<parser>/` seed corpora (start from
+  valid samples mutated minimally; include every historical crash input).
+  (b) Add `tests/fuzz/regression/` — every crash ever found becomes a
+  checked-in test input executed by `make test` (not just `make fuzz`).
+  (c) CI: `make fuzz-smoke` on every PR; `make fuzz-deep` nightly with
+  corpus minimization and new-crash upload as artifacts. (d) Add missing
+  harnesses: YAML parser, safe-fasl envelope decoder, HMAC'd actor frames
+  (P0-05), URI/URL sanitizer, `jerboa-read` against the *full* reader
+  (including block comments/heredocs), `jpkg` archive validator
+  (path-traversal shaped inputs), MCP/LSP/repl-protocol message parsers
+  (they are network-adjacent input surfaces for the dev environment —
+  [`unification.md`](unification.md) §"Developer Environment as a Security
+  Boundary").
+- **Accept:** `make fuzz-smoke` green in CI; a seeded known-bad input is
+  caught by regression tests; corpus directories documented in
+  `testing-and-infrastructure.md`.
+
+---
+
+## 7. P1 — Harden the boundaries
+
+### K3-P1-01 — FFI boundary audit (parser-hardening phase 5)
+**Serves:** G1. **Effort:** 3–4 weeks (the big one).
+
+The 91 `foreign-procedure` files and 225 native exports are where a
+K3-class adversary will shop. `security-reference.md` §13: "FFI audit … is
+not started."
+
+- **Do:** (a) Inventory: generate the authoritative list of every
+  `foreign-procedure` binding and every `#[no_mangle]` export, with arity,
+  pointer arguments, and ownership contract (the `.jerboa-system` native
+  boundary + `tools/check-native-boundary.ss` are the starting point;
+  `tools/tcb-report.ss` consumes it). (b) Per binding, verify: null-return
+  checks, length/bounds derivation (no caller-supplied length trusted),
+  `__collect_safe` on blocking calls, `EINTR` retry (the scanner already
+  has this C rule — extend to Scheme FFI call sites), integer width
+  confusion (`unsigned-64` vs `size_t` on all targets), and GC-safety of
+  bytevector pointers across calls that may collect. (c) Rust side:
+  `cargo geiger`-style `unsafe` census; each `unsafe` block gets an
+  invariant comment; deny-by-default `#![deny(unsafe_op_in_unsafe_fn)]`;
+  panic-free C ABI (unwind across FFI is UB — check `panic.rs` covers all
+  exports). (d) Shrink: unexport any `#[no_mangle]` symbol without a live
+  Scheme caller. (e) Generate bindings where possible
+  ([`ffi.md`](ffi.md)'s `define-ffi-library` + `define-foreign/check`
+  already encode ownership/destructors — prefer them over hand-written
+  `foreign-procedure`). (f) Decide `vendor/jsqlite`'s fate: it is a C
+  amalgamation in the TCB; either move consumers to the Rust sqlite path
+  (`sqlite.rs`) or document accepted risk + add its CVE feed to release
+  gates.
+- **Accept:** audit report committed (`docs/ffi-audit.md`) with
+  per-binding verdict; scanner gains null-check/width/bounds rules; zero
+  un-annotated `unsafe`; export count reduced and justified; findings
+  fixed or tracked.
+
+### K3-P1-02 — TOCTOU-safe filesystem capability checks
+**Serves:** G2. **Effort:** 1 week.
+
+`security-reference.md` §13: path checks use `realpath(3)` *before* access,
+not `O_NOFOLLOW` + fd-relative verification after open. A symlink race
+defeats the capability.
+
+- **Where:** `(std security capability)` path enforcement; Rust
+  `secure_fs.rs` already exists — extend it.
+- **Do:** open with `O_NOFOLLOW`/`openat2(RESOLVE_NO_SYMLINKS)` where
+  available, verify the opened fd via `/proc/self/fd/N` against the
+  canonicalized allowed set, then operate on the fd (not the path). macOS/
+  BSD: `open(..., O_NOFOLLOW | O_CLOEXEC)` + `fstat` checks; document the
+  residual race where the platform can't fully close it.
+- **Accept:** race PoC test (symlink swapped between check and open) fails
+  pre-patch, passes post-patch; `capability.md` limitation removed.
+
+### K3-P1-03 — Taint enforcement on by default in the safe prelude
+**Serves:** G2. **Effort:** 1 week.
+
+Taint is opt-in; native sinks don't check it. In the safe prelude, the
+*default* file/shell/delete operations should be the taint-aware wrappers.
+
+- **Do:** (a) In `(jerboa prelude safe)`, rebind `open-input-file`,
+  `open-output-file`, `delete-file`, and any process-spawn to the
+  `safe-*` taint-checking versions (with the raw versions available only
+  via explicit `(std unsafe)` import). (b) Taint sources by default: HTTP
+  request fields, env vars, file contents read in safe mode, network
+  frames — wrapped at the boundary. (c) Propagation coverage: extend the
+  tainted-string operation set (currently four functions) to the full
+  common string/bytevector API so taint doesn't silently launder away.
+  (d) Performance: measure; if overhead matters, document
+  `*taint-enforce* #f` as an explicit, warned de-opt — never silent.
+- **Accept:** tests: tainted value reaching a sink in safe-prelude code
+  raises `&taint-violation`; untainted flows unchanged; propagation tests
+  for each new operation; `safety-guide.md` gains a taint section.
+
+### K3-P1-04 — Decide `define-syntax` in the sandbox allowlist
+**Serves:** G1. **Effort:** 2–3 days.
+
+Macros in sandboxed code can smuggle references to bindings the allowlist
+excluded, depending on expansion environment semantics.
+
+- **Do:** (a) Write the adversarial test battery first: `syntax-rules`
+  emitting references to `system`/`eval`/`foreign-procedure`, use of
+  `datum->syntax` with forged contexts, macro-defined macros. (b) If any
+  escapes: remove `define-syntax`/`syntax-rules` from `safe-bindings`, or
+  fence expansion in the restricted environment. (c) If provably closed:
+  document the proof sketch in `security-reference.md` §13 and lock with
+  tests.
+- **Accept:** battery passes (or feature removed); §13 entry resolved.
+
+### K3-P1-05 — Crypto modernization: Argon2id, retire OpenSSL legacy paths
+**Serves:** G1, G5. **Effort:** 1 week.
+
+- **Do:** (a) Add Argon2id via the Rust `argon2` crate in
+  `jerboa-native-rs` (password hashing is the documented gap in
+  `security-reference.md` §13); make it the recommended `password-hash`
+  backend, keep PBKDF2 for compatibility. (b) Deprecate the OpenSSL EVP
+  legacy paths (`(std crypto hmac)`/`aead`/`kdf` legacy): emit a
+  deprecation warning, route defaults to `ring`, and set a removal
+  milestone — one fewer C crypto dependency in the TCB. (c) Enforce
+  [`uhoh.md`](uhoh.md)'s rule table in review: no new pure-Scheme crypto
+  where secrets flow; digests-only in Scheme.
+- **Accept:** Argon2id vectors (RFC 9106) pass; OpenSSL-free build works
+  (`make native` with openssl disabled); docs updated.
+
+### K3-P1-06 — Typed secure-memory interface
+**Serves:** G2. **Effort:** 3–4 days.
+
+`secure-mem` requires manual `foreign-ref`/`foreign-set!` pointer
+arithmetic — itself an FFI footgun (`security-reference.md` §13).
+
+- **Do:** Add a high-level API: `secure-bytevector` view type with bounds-
+  checked get/set, `with-secure-bytevector` scoped form, and integration
+  with `(std security secret)` so `with-secret` can back secrets by
+  mlock'd memory. No raw pointer arithmetic exposed.
+- **Accept:** tests exercise bounds violations (expect exceptions, not
+  corruption); guard-page trip test (SIGSEGV on out-of-bounds at the page
+  level) verified on Linux; §13 entry removed.
+
+### K3-P1-07 — Memory-limit story for untrusted evaluation
+**Serves:** G2. **Effort:** 1 week.
+
+Chez cannot heap-cap a thread; `run-safe-eval` rightly refuses
+`max-memory-size`. The worker (P0-02) is the answer — make it ergonomic.
+
+- **Do:** (a) Ship `worker-run-eval` convenience: one call that execs a
+  confined worker with rlimit AS + CPU, runs the restricted eval, returns
+  bounded output. (b) Document the hierarchy: in-process `run-safe-eval`
+  (semi-trusted) → confined worker eval (untrusted) → container/VM
+  (hostile). (c) Investigate Chez `collect-trip` / maximum-heap parameters
+  as an in-process tripwire (defense-in-depth, not a boundary) — document
+  findings either way in `chez-limits.md`.
+- **Accept:** eval-bomb test killed by worker rlimit; docs updated;
+  `run-safe-eval` docs cross-link the worker API.
+
+### K3-P1-08 — Platform sandbox parity
+**Serves:** G2. **Effort:** 2 weeks.
+
+- **Do:** (a) seccomp: extend the BPF generator's syscall tables beyond
+  x86_64/aarch64 only as far as testing supports; keep the architecture
+  validation that prevents syscall-number confusion. (b) Ship default
+  profiles for the common server shapes (`network-server-filter` exists —
+  add `http-server`, `dns-server`, `worker-eval` presets mapped to the
+  P0-02 worker). (c) Seatbelt (macOS) and Capsicum (FreeBSD): bring test
+  coverage to parity with Landlock/seccomp so `make verify` exercises all
+  three on their native platforms. (d) A `sandbox-report` API that
+  enumerates which confinements are active in the current process (for
+  audit logs and the release-evidence bundle).
+- **Accept:** per-platform CI smoke for each mechanism; presets used by
+  P0-02 worker tests.
+
+### K3-P1-09 — Confused-deputy defenses: capability plans enforced at runtime
+**Serves:** G2. **Effort:** 1 week.
+
+`Philosophy.md` open tension #7: ocap prevents forgery, not misuse.
+`tools/capability-plan.ss` and `check-capability-use.ss` already exist —
+they are build-time. Close the runtime loop.
+
+- **Do:** (a) A `.jerboa/capabilities.sexp` per application declaring
+  intended grants; at startup, the app builds exactly those capabilities
+  from the root and the runtime *refuses* undeclared grants (fail closed).
+  (b) `check-capability!/audit` everywhere a capability crosses a module
+  boundary (audit chain already exists). (c) Lint rule: passing a wider
+  capability where a narrower derivable one suffices (detect
+  `attenuate-*` opportunities). (d) Doc: capability-grant style guide in
+  `capability.md` (grant at the latest moment, narrowest scope, log every
+  grant).
+- **Accept:** undeclared-grant refusal test; audit-chain verification
+  test; lint rule fires on a synthetic over-grant.
+
+### K3-P1-10 — Information-leak sweep: errors, logs, and protocol surfaces
+**Serves:** G2, G4. **Effort:** 1 week.
+
+- **Do:** (a) Adopt `(std security errors)` classification in the HTTP
+  server, router, gRPC, MCP, LSP, and repl-protocol paths: client-visible
+  errors carry opaque refs; internal detail only in server logs. (b) Add a
+  scanner rule: `display`/`format` of raw condition messages into HTTP
+  responses. (c) Ensure `(std security env)` redaction is wired into
+  `(std security audit-log)` sinks so registered secret values cannot be
+  logged (the broker already supports value redaction — enforce its use).
+  (d) Verify release binaries strip build paths (`-ffile-prefix-map`,
+  `(generate-inspector-information #f)` per `harden.md` §8) — test the
+  shipped artifact for `$HOME` path leakage.
+- **Accept:** tests: crafted failing request yields opaque ref + logged
+  detail; scanner catches a synthetic leak; artifact grep clean.
+
+### K3-P1-11 — Parameterized-only SQL in the safe surface
+**Serves:** G1. **Effort:** 2–3 days.
+
+The injection heuristic is a documented speed bump; the structural fix is
+making string-built SQL unrepresentable in safe code.
+
+- **Do:** (a) In `(jerboa prelude safe)`, expose `sqlite-query`/`exec`
+  *only* in parameterized form (SQL literal + params); the raw-string
+  variants move behind `(std unsafe)`. (b) Keep the heuristic as
+  defense-in-depth for the unsafe surface. (c) Strengthen
+  `sql-interpolation` lint to error in safe-prelude files.
+- **Accept:** safe-prelude script cannot call the string-SQL API; lint
+  errors on interpolation; `safety-guide.md` §4 updated.
+
+### K3-P1-12 — Eliminate the safe-prelude import conflict
+**Serves:** G1, G4. **Effort:** 2 days. (Folded into P0-06 if done there.)
+
+Tracked separately because "which `sort`/`hash` did I get?" ambiguity in
+security wrappers is a latent vulnerability. Make exports disjoint, add a
+test asserting no duplicate-export warnings at build, and remove the
+"cosmetic" language from `ai-threat.md` once fixed.
+
+---
+
+## 8. P2 — Supply chain, ecosystem, process
+
+### K3-P2-01 — jpkg secure-by-default templates
+**Serves:** G5, G1. **Effort:** 3 days.
+
+The jpkg hardening exists but is opt-in (`JPKG_REQUIRE_SIGNATURES`,
+`JPKG_REQUIRE_PROVENANCE`). Generate new projects with these set, the
+lockfile committed, and `jpkg audit` in their CI template. Update
+`jpkg-guide.md` quickstart to the secure default path.
+
+### K3-P2-02 — Dependency-audit parity and freshness
+**Serves:** G5. **Effort:** 3 days.
+
+`ai-threat.md` rates Jerboa behind cargo/npm audit. Close it: wire
+`jpkg audit` (OSV) into `make audit` for this repo's own `jpkg.lock`;
+keep `cargo audit -D warnings`; add a scheduled weekly job that fails on
+new advisories affecting locked versions; document the OSV DB snapshot
+provenance.
+
+### K3-P2-03 — Automated TCB accounting
+**Serves:** G5. **Effort:** 1 week.
+
+`tools/tcb-report.ss` exists — promote it to a release artifact: the
+written, versioned list of everything trusted (Chez kernel C, Jerboa C
+shims, each Rust crate + its `unsafe` count, vendored C like
+`vendor/jsqlite`, the kernel, the container base image digest from
+`support/container-dependencies.lock`). Gate: TCB size growth requires a
+changelog note. `Philosophy.md` open tension #6 asks for exactly this.
+
+### K3-P2-04 — Standing AI red-team practice
+**Serves:** G3. **Effort:** ongoing; 2 days to bootstrap.
+
+Institutionalize "we find our own CVEs first" (Philosophy P5): a
+documented, repeatable red-team protocol — fresh model instance, repo +
+threat model as prompt, findings filed as `data/anti-patterns.sexp`
+entries and exploit-shaped tests (P0-07). Quarterly cadence + after every
+P1 landing. Also drive the open sibling-repo findings (§4.4 item 6) to
+resolution in their own repos — do not patch them from here (repository
+boundary rule), but track them in the cross-repo readiness file.
+
+### K3-P2-05 — Scanner rule growth and pre-commit wiring
+**Serves:** G3. **Effort:** continuous; 2 days to wire.
+
+`data/security-rules.sexp` is the rule DB behind `jerboa_security_scan`.
+(a) Add rules for every finding from P1-01 (FFI null/width/bounds), raw
+`read`, native FASL, `system` with string concatenation, missing
+`with-resource` on `sqlite-open`/`tcp-connect`, hardcoded secrets, unsafe
+randomness (`(random N)` for tokens), disabled contract checks in
+production (`*safe-mode* 'release` outside tests). (b) Wire
+changed-lines-only scanning into a pre-commit hook template under
+`support/`; document opt-in in `devex.md`. (c) Every future security
+fix must add its scanner rule in the same commit (write it into
+`AGENTS.md`-adjacent contributing docs).
+
+### K3-P2-06 — Documentation consistency pass
+**Serves:** G5. **Effort:** 2 days.
+
+- Refresh `ai-threat.md`'s comparative table (supply chain now:
+  TUF + signatures + transparency + OSV; update the "Behind" cells and the
+  "Known Vulnerabilities" table statuses).
+- Document `(std security cage)`, `(std security secret)`,
+  `(std security env)` in `security-reference.md`.
+- Add this file to `docs/index.md` (done at creation) and cross-link from
+  `SECURITY.md`.
+- Move the appended "Security Patterns & Threat-Model Checklist" findings
+  out of `security-reference.md` into a dated `docs/reviews/` file —
+  reference docs shouldn't carry stale review state.
+
+### K3-P2-07 — Secret-handling standard adoption
+**Serves:** G2. **Effort:** 1 week.
+
+Standardize on `(std security secret)` (auto-wipe lifetimes) +
+`(std security env)` (brokered injection, never parent-env inheritance) +
+`(std crypto secure-mem)` (storage). Sweep `lib/`, `mcp/`, `lsp/`,
+`tools/` for: secrets in env passed to children unfiltered, secrets in
+command-line argv (`ps`-visible — Philosophy rejects this), secrets in
+error messages/logs. Scanner rules from P2-05 enforce going forward.
+
+### K3-P2-08 — SHSTK default decision for daemon binaries
+**Serves:** G1. **Effort:** 2 days.
+
+`JERBOA_ENABLE_SHSTK=try` exists for generated binaries
+(`chez-hardening.md` §6.3). For binaries that provably never invoke
+`call/cc` (parsers, DNS, static-binary daemons), flip the default to
+`try` with a `JERBOA_SECURITY_REPORT=1` attestation line in release
+evidence; keep opt-in for REPL/tooling. Record the decision and the
+call/cc hazard in `release-security.md`.
+
+---
+
+## 9. P3 — Research and frontier
+
+Tracked so they're not invented twice (per `chez-hardening.md` §11's
+maintenance rule). Do not start before P0/P1 land.
+
+| ID | Item | Source | Notes |
+|---|---|---|---|
+| K3-P3-01 | ARM MTE integration in Chez GC | [chez-hardening.md](chez-hardening.md) §7.1 | hardware UAF/overflow detection; needs GC tagging work |
+| K3-P3-02 | call/cc-aware shadow stack (SHSTK/GCS) | chez-hardening §6.4 | kernel continuation patch; weeks; needs CET CI runner |
+| K3-P3-03 | Intel MPK for JIT page W^X narrowing | chez-hardening §7.3 | most impactful x86_64 item past phase 1 |
+| K3-P3-04 | WASM-sandboxed parsers by default for image/media/compression formats | [slang.md](slang.md), Philosophy tension #4 | wasmi fuel sandbox exists; decide throughput trade-off per format |
+| K3-P3-05 | Adopt the `slang` secure static subset as the default profile for shipped binaries | [slang.md](slang.md) | attack-surface elimination at the language-subset level; pair with P0-06 |
+| K3-P3-06 | Machine-checked proofs of the two smallest, highest-value kernels: the restricted-env allowlist closure (P0-03) and capability attenuation monotonicity | security-reference §13 ("no formal verification") | both are small enough for ACL2/Coq or a model-checking pass; even a property-based test harness over attenuation algebra is progress |
+| K3-P3-07 | Covert/side-channel posture | security-reference §13 | GC timing channel documented; add a constant-time coding guide for crypto-adjacent Scheme and a scanner rule for data-dependent branching on secrets |
+| K3-P3-08 | Encrypted boot files + full signed-binary pipeline | [harden.md](harden.md) §7/§12 (design-only) | needs the C main entry in the consuming project; coordinates with jerboa-shell |
+
+---
+
+## 10. Standing practices
+
+These become the permanent operating rhythm once the work items land.
+
+| Practice | Gate / cadence | Mechanism |
+|---|---|---|
+| Balance + build health | every commit | `make audit` incl. all-file balance check (P0-01) |
+| Scanner with growing rule DB | every commit (changed lines), every night (full tree) | `jerboa_security_scan`, pre-commit template (P2-05) |
+| Fuzz smoke / deep | PR / nightly | `make fuzz-smoke` / `fuzz-deep` with corpora (P0-08) |
+| Exploit-shaped regression suite | every `make test` | `tests/security/` (P0-07) |
+| Dependency advisories | weekly | `jpkg audit` + `cargo audit` (P2-02) |
+| AI red-team | quarterly + after P1 landings | protocol in P2-04 |
+| TCB report drift | every release | `tools/tcb-report.ss` in release evidence (P2-03) |
+| Release gates | every release | `make verify`, `make release-evidence`, `make signing-evidence` with `JERBOA_REQUIRE_RELEASE_SIGNATURES=1` ([release-security.md](release-security.md)) |
+| Doc drift check | every security commit | the four security docs + this file updated in-session; `make audit` fails on undocumented new `(std security *)` exports |
+
+---
+
+## 11. Application containment checklist (for Jerboa users)
+
+This is the answer to "a zero-day was just found in my Jerboa service —
+why isn't it game over?" Ship this as a template (consider
+`jerboa_project_template` MCP scaffolding) so new services start here.
+
+**Baseline (every app):**
+1. `(import (jerboa prelude safe))` — no raw FFI, no `eval`, no raw threads. (P0-06 makes this the default.)
+2. Parameterized queries only; heuristic is a seatbelt, not a wall. (P1-11)
+3. `with-resource` on every handle; `with-timeout` on every I/O; socket-level timeouts to cover FFI blocking.
+4. Structured conditions + `(std security errors)` for client-facing errors — never leak internals.
+5. Secrets via `(std security secret)`/`(std security env)`; never argv, never unfiltered env to children, never logs. (P2-07)
+
+**Any code path touching untrusted bytes:**
+6. Bounded parsers only (they are by default); `jerboa-read` for S-expressions; `safe-fasl` envelope for data-at-rest; native FASL for trusted build artifacts only. (P0-04)
+7. Taint sources at the boundary; sinks reject tainted. (P1-03)
+
+**Anything adversarial (user expressions, uploaded content, plugin code):**
+8. Run it in the confined worker: fresh exec, rlimits, Landlock/seccomp (Seatbelt/Capsicum), `env-pure` allowlist, deny-default egress via the allowlist proxy, bounded pipes, supervisor deadline + process-group kill, audit-log start/end. (P0-02, P1-08)
+9. Grant the narrowest capabilities, declared in the capability plan, intersected per request. (P1-09)
+
+**Servers:**
+10. Drop privileges after startup: `cage!`/Landlock + seccomp profile, then serve. (`safety-guide.md` §"Server That Drops Privileges"; `(std security cage)`).
+11. Admission limits (global/per-source) from `(std net thread-httpd)`; deadlines on header/body.
+12. Append-only audit log with hash chain; alert on verify failure.
+
+**Release:**
+13. Signed artifacts, SBOM, reproducibility report, pinned `jpkg.lock`, `jpkg audit` clean. When the CVE drops, `dist/release-evidence/` answers "affected? which build? shipped when?" in minutes. (G5)
+
+---
+
+## 12. Metrics — how we know the posture improved
+
+Track these in `docs/status.md` per release:
+
+| Metric | Baseline (2026-07-27) | Target |
+|---|---|---|
+| Build balance clean | **failing** (pattern.ss) | always clean |
+| Confined worker exists | no | yes, tested (P0-02) |
+| Unclassified raw `read`/FASL sites | unknown (4+ named) | 0 |
+| FFI bindings audited | 0 / 91 files | 100% with verdicts |
+| Un-annotated Rust `unsafe` blocks | unknown / 432 matches | 0 |
+| Fuzz corpora / crash regressions | 0 / 0 | per-parser corpora + every crash a test |
+| Exploit-shaped regression tests | ~0 | ≥ 1 per historical finding |
+| Sandboxed-by-default app entry | no | yes (P0-06) |
+| Supply-chain gates in `make audit` | partial | jpkg audit + cargo audit + signatures required |
+| Security-doc drift items | ≥ 5 (§4.4) | 0 |
+| Time from advisory → release evidence answering "affected?" | unmeasured | < 1 hour drill, quarterly |
+
+---
+
+## Appendix A: Verified commands
+
+```bash
+# Posture facts used in this document (re-run to refresh):
+grep -rl 'foreign-procedure' lib/std | wc -l        # 91
+grep -c 'no_mangle' jerboa-native-rs/src/*.rs | awk -F: '{s+=$2} END {print s}'   # 225
+grep -c 'unsafe' jerboa-native-rs/src/*.rs | awk -F: '{s+=$2} END {print s}'      # 432
+ls tests/fuzz/harness                                # 13 harnesses
+ls vendor/                                           # ChezScheme, ChezScheme-patches, jsqlite
+
+# Security test files (21):
+# test-capability test-fasl-safety test-phase3-security test-phase4-safety
+# test-region-safety test-restrict-hardened test-safe-prelude
+# test-sandbox-native-timeout test-sandbox-script-failclosed
+# test-sandbox-thread-failclosed test-sandbox test-secure-fs
+# test-secure-link-argv test-security-capability test-security-profile
+# test-security-taint test-security2-parsers test-taint test-typed-fuzz
+# test-wasm-sandbox (+ tests/fuzz/)
+
+# Gates:
+make audit            # PR gate: security-audit audit-native data-check security-production unification-check
+make test-security    # security feature tests (extend with P0-07)
+make fuzz-smoke       # CI fuzz pass (extend with corpora, P0-08)
+make verify           # local production gate
+make sbom reproducibility-report signing-evidence release-evidence
+```
+
+## Appendix B: Documentation inventory
+
+Security-relevant docs as of 2026-07-27, with their role in this plan:
+
+| Doc | Role |
+|---|---|
+| [`Philosophy.md`](Philosophy.md) | The *why*: authority-centric doctrine; principles every item above cites |
+| [`ai-threat.md`](ai-threat.md) | Existing AI-adversary threat model; needs table refresh (P2-06) |
+| [`security-reference.md`](security-reference.md) | Feature reference + limitation ledger (§13); add worker/cage/secret/env sections |
+| [`safety-guide.md`](safety-guide.md) | User-facing secure-app guide; update §2 to the real worker API |
+| [`release-security.md`](release-security.md) | Release gates and threat model for the distribution |
+| [`capability.md`](capability.md) | ocap implementation + documented limitations (TOCTOU fixed by P1-02) |
+| [`chez-hardening.md`](chez-hardening.md) | CPU/runtime mitigations; P2-08 and P3-01..03 source |
+| [`harden.md`](harden.md), [`harden-usage.md`](harden-usage.md) | Binary hardening; §7/§12 design-only → P3-08 |
+| [`limits.md`](limits.md) | Fail-closed launcher policy the worker (P0-02) must satisfy |
+| [`aproc.md`](aproc.md) | The exec primitive the worker builds on |
+| [`slang.md`](slang.md) | Secure static subset; P3-05 |
+| [`unification.md`](unification.md) | Security invariants for the whole stack; §"AI code is untrusted code" |
+| [`ffi.md`](ffi.md) | FFI DSL with ownership/destructors; P1-01 prefers generated bindings |
+| [`jpkg-guide.md`](jpkg-guide.md) | Supply-chain security model; P2-01/02 |
+| [`uhoh.md`](uhoh.md) | Crypto discipline rule table; enforced by P1-05 |
+| [`finalizer-safety-net.md`](finalizer-safety-net.md) | Resource-leak net proposal; verify guardian coverage when adopting |
+| [`chez-limits.md`](chez-limits.md) | Target for P1-07 heap-cap findings |
+| [`testing-and-infrastructure.md`](testing-and-infrastructure.md) | Update with corpus/regression layout (P0-08) |
+| [`SECURITY.md`](../SECURITY.md) | Policy root; cross-link this file |
+
+## Appendix C: Honest limitation ledger
+
+Carried forward from `security-reference.md` §13, updated, plus additions
+from this review. Keep this list current — a stale limitation list is how
+fake confidence happens.
+
+- No formal verification of security modules (P3-06 targets the two
+  smallest kernels).
+- No FIPS 140-3 / Common Criteria evaluation.
+- No covert-channel analysis; Chez GC is a timing side channel (P3-07).
+- Seccomp tables cover x86_64/aarch64 only; Landlock needs Linux 5.13+;
+  Seatbelt/Capsicum have thinner test coverage (P1-08).
+- Taint is opt-in until P1-03; native Chez sinks never check it.
+- No message authentication for distributed actors until P0-05.
+- Path checks are TOCTOU-raceable until P1-02.
+- `define-syntax` remains in the sandbox allowlist pending P1-04's proof.
+- `run-safe-eval` output cap is on the formatted result only; no
+  stdout/stderr capture; engine timeouts can't preempt blocked FFI
+  (by design — the worker, P0-02, is the boundary).
+- No Argon2id until P1-05; OpenSSL legacy paths present until then.
+- Secure memory API is pointer-level until P1-06.
+- FFI audit (phase 5) unstarted until P1-01; `vendor/jsqlite` is C in the
+  TCB pending its decision.
+- No independent red-team evaluation yet (P2-04 starts the practice).
+- The confined exec worker does not exist yet (P0-02); until it lands,
+  every doc sentence saying "use an exec worker" describes an aspiration,
+  not an API.
+- `lib/std/compiler/pattern.ss` is unbalanced as of this writing (P0-01).
+
+---
+
+*Maintenance rule (per `chez-hardening.md` §11): drift between this
+document and the tree is itself a smell — fix the tree or fix the doc, in
+the same commit.*