Public: anyone can read this; the crew can change it.
Each row of docs/THREATS.md names the test or gate that enforces it, and a test fails on a row that names none #11874
Check · Review · Land · Live done — Landed and live in production.
Every protection in `docs/THREATS.md` now ends in an `Enforced by:` line in place of the old `Test:` line. In the internal-calls table, the last column holds the enforcer. An enforcer is one of three things: - a test file under `test/` - a gate under `scripts/` - `none yet`, with an optional `(#N)` for the open issue that owns the gap
shipped production is serving it deploy 11ca711
Every protection in docs/THREATS.md now ends in an Enforced by: line in place of the old Test: line. In the internal-calls table, the last column holds the enforcer. An enforcer is one of three things:
- a test file under
test/ - a gate under
scripts/ none yet, with an optional(#N)for the open issue that owns the gap
No enforcer was added or changed: every row keeps the test file it already cited.
What moved where
src/core/threats.ts:parseThreatsreadsEnforced by:. The newenforcement(md, exists)checks the doc against the rule and returns plain data: the problems, thenone yetcount, and the cited paths. Bullets under a(later)heading are preconditions, not protections, so it skips them. TheProtection.testfield keeps its name but may now hold ascripts/gate; such a row shows "untested" on the Resilience page, because no gate result ever names a script file.test/threats-doc.test.ts→test/threats-enforced.test.ts: the test fails when a bullet has noEnforced by:line, a call row's last cell names nothing, a named path does not exist, an enforcer is neither a test, a gate nornone yet, or the oldTest:form is still there. Each of these cases has its own fixture. It also checks that every row the Resilience page lists was judged, and printsthreats: 5 rows enforced by none yet.test/resilience.test.tsandtest/checks-resilience.test.ts: these two tests changed. Their fixtures and line counts used theTest:form the issue retires, so they now useEnforced by:. Nothing else in them changed.scripts/gate.tspicks up everytest/*.test.tsby itself, so nothing needed adding there.
Follow-ups: the none yet rows (none filed by this pull)
- TLS at the edge: certificates are issued only for names the forge vouches for (the
on_demand_tlsask). - Traffic from the edge to the forge, and from the forge to a cell, is not encrypted.
- Revoking every token of one account at once, and telling its owner when a token is used from somewhere new.
- Reviewing lockfile (
bun.lock) changes before they reach an app's cell. - Distributed attacks across many addresses: only the whole-forge caps stand there.
The issues API has no search, so I couldn't find any existing open issues for these and cited no #N rather than guess one.
Checks run here
- Passed:
bun scripts/gate.ts,bun x tsc --noEmit, andbun testontest/threats-enforced.test.ts,test/resilience.test.ts,test/checks-resilience.test.ts,test/resilience-fit.test.tsandtest/tui-screens.test.ts. - Skipped: the phone-layout check in
resilience-fitskipped itself because no Chromium starts in this sandbox. - Not run:
bun scripts/fast.tsnamed 1641 test files, becausethreats.tsis reached from the server, so I ran the tests above instead of all of them.
🤖 Generated with Claude Code
Approved. Every row now ends in an Enforced by: line, and enforcement() in src/core/threats.ts is small, plain data in and out, and tested for each failure case. The old threats-doc test is folded into the new one, and the resilience fixtures were updated with it. No UI or dependency change, and no README addition. Next two things: (1) give the 5 none-yet rows an (#N) each, filed as separate follow-ups, and tighten NONE_YET to require it once they exist; (2) show scripts/ gate rows as enforced rather than untested on the Resilience page.
Left #11865 open: 1 unticked item remain (Each threat in docs/THREATS.md carries one line Enforced by: <path> naming a test file or a gate in scripts/, or Enforced by: none yet (#N) with an open issue.).
Every protection in
docs/THREATS.mdnow ends in anEnforced by:line in place of the oldTest:line. In the internal-calls table, the last column holds the enforcer. An enforcer is one of three things:test/scripts/none yet, with an optional(#N)for the open issue that owns the gapNo enforcer was added or changed: every row keeps the test file it already cited.
What moved where
src/core/threats.ts:parseThreatsreadsEnforced by:. The newenforcement(md, exists)checks the doc against the rule and returns plain data: the problems, thenone yetcount, and the cited paths. Bullets under a(later)heading are preconditions, not protections, so it skips them. TheProtection.testfield keeps its name but may now hold ascripts/gate; such a row shows "untested" on the Resilience page, because no gate result ever names a script file.test/threats-doc.test.ts→test/threats-enforced.test.ts: the test fails when a bullet has noEnforced by:line, a call row's last cell names nothing, a named path does not exist, an enforcer is neither a test, a gate nornone yet, or the oldTest:form is still there. Each of these cases has its own fixture. It also checks that every row the Resilience page lists was judged, and printsthreats: 5 rows enforced by none yet.test/resilience.test.tsandtest/checks-resilience.test.ts: these two tests changed. Their fixtures and line counts used theTest:form the issue retires, so they now useEnforced by:. Nothing else in them changed.scripts/gate.tspicks up everytest/*.test.tsby itself, so nothing needed adding there.Follow-ups: the
none yetrows (none filed by this pull)on_demand_tlsask).bun.lock) changes before they reach an app's cell.The issues API has no search, so I couldn't find any existing open issues for these and cited no
#Nrather than guess one.Checks run here
bun scripts/gate.ts,bun x tsc --noEmit, andbun testontest/threats-enforced.test.ts,test/resilience.test.ts,test/checks-resilience.test.ts,test/resilience-fit.test.tsandtest/tui-screens.test.ts.resilience-fitskipped itself because no Chromium starts in this sandbox.bun scripts/fast.tsnamed 1641 test files, becausethreats.tsis reached from the server, so I ran the tests above instead of all of them.🤖 Generated with Claude Code
Closes #11865