AGENTS.md: carry the spec's three retractions into the binding file
AGENTS.md's own rule is "Where it disagrees with the design spec, the spec wins and this file is wrong — fix it in the same commit." The revision in da52b09 changed the spec and left this file behind, so the binding file has been asserting three things the spec explicitly retracts. A Fable review of an unrelated design question found it. The one rule said "never stores your address". Spec section 0 records that absolute as false at source: a magic link must survive a restart between send and click, so an address is at rest somewhere — sealed, enumerated and short-lived per section 5.1, not absent. Now "never stores a readable address", matching the spec word for word. The falsifiable form said "some unidentified person". Section 3.3 retracts that too, because one known membership identifies the row and reveals the rest of that person's memberships. Now "some person". Assertion expiry said "seconds". Section 5.3 says 120 seconds and explains why the difference matters: the exchange request is what wakes a hibernating instance, and process start plus decrypt plus migrate-check can exceed a few seconds, so a tighter window makes the first sign-in to any cold instance fail and loop. The reason travels with the number here, along with the line that stops someone tightening it again — single-use jti is what stops replay, not brevity. Also brings gate 2's description in line with section 12, which added "plus rekey" and "WAL included" — the parts most likely to be dropped when someone implements the test. Both retracted phrases are quoted in a short note saying not to restore them, because each reads like an improvement to anyone who has not read the spec's reasoning. Docs only; no behaviour change. All four .amadan/ci.d gates green. 🤖 Generated with Claude Opus 5, at the direction of Paul Campbell.
Reviewed-By: Fable (dispatched; found the divergence) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPUHaNUtTvE7f1VpFWZUHY
1 file changed,
+20
−8
AGENTS.md+20 −8
diff --git a/AGENTS.md b/AGENTS.md| index 1fb6bf9..9bce75c 100644 |
| --- a/AGENTS.md |
| +++ b/AGENTS.md |
| @@ -17,18 +17,26 @@ alternative beside each. |
| ## The one rule |
| > **The home is glue: thin, blind, replaceable. It knows that you exist and |
| -> where your teams are. It never stores your address, and it never learns |
| -> what is in anything.** |
| +> where your teams are. It never stores a readable address, and it never |
| +> learns what is in anything.** |
| The falsifiable form: **publish this database in full and the worst anyone |
| learns is how many people exist, what their teams are called, and that some |
| -unidentified person belongs to a particular set of them.** No address, no |
| -document, no cell. |
| +person belongs to a particular set of them.** No document. No cell. No |
| +readable address. |
| + |
| +**"Readable" is load-bearing, and so is "some person".** The first draft |
| +said "never stores your address", which spec §0 records as false: a magic |
| +link must survive a restart between send and click, so an address is at |
| +rest *somewhere* — sealed, enumerated and short-lived (§5.1), not absent. |
| +It said "some unidentified person"; §3.3 retracts that too, because one |
| +known membership identifies the row and reveals the rest. Do not restore |
| +either absolute — each is a claim the schema cannot keep. |
| Two tests enforce it from the first commit: a plaintext grep over the raw |
| SQLite file and WAL, and a no-address-at-rest test asserting the schema has |
| -no address column and that a full sign-in plus directory upsert leaves no |
| -readable address anywhere. |
| +no address column and that a full sign-in plus directory upsert **plus |
| +rekey** leaves no readable address anywhere, **WAL included**. |
| --- |
| @@ -100,8 +108,12 @@ quietly ignored). |
| authorization**. A claim may ride an assertion only if an instance would |
| behave identically given a hostile value. A wrong theme is ugly; a wrong |
| role is a breach. |
| -- Assertion expiry is **seconds**. The long-lived thing is the session the |
| - instance mints. |
| +- Assertion expiry is **120 seconds, not "seconds"** — sized to worst-case |
| + cold wake plus clock skew, because the exchange request *is* what wakes a |
| + hibernating instance and a tighter window makes the first sign-in to any |
| + cold instance fail and loop. Widening it costs nothing: **single-use `jti` |
| + is what stops replay, not brevity.** The long-lived thing is the session |
| + the instance mints. |
| - Every assertion names the **app and instance host** it is for. Without |
| audience binding, team A's assertion replays against team B, and a |
| deployment has thousands of siblings to replay against. |