oficina / home Public

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
Paul Campbell pushed by paul@keymail.dev dd6006f3957623452356c7957029c0781d75ec00 parent 462df84
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.