Revision 9: a link token is stored hashed (ruling 16) open
On branch worktree-worktree-token-at-rest
Branch discussion — the review record for worktree-worktree-token-at-rest (one commit, 75063e6).
What changed
Adds ruling 16: a link token is stored hashed (SHA-256), never in plaintext. Shown to its maker once, at creation; the app cannot show it again.
Four edits: the rule itself beside ruling 15's ladder, a consequence item (13) in §9's list, an A.2 note recording that Docs broke it and must adopt it, and the status block (revision 8 → 9).
Why this is doctrine and not a Docs bug
Sheets already had it right, in its own spec §8.3, with the sentence worth keeping: "stored plaintext, the replicated database becomes a bundle of live read capabilities." The suite never lifted that rule, so Docs built link sharing this morning with a plaintext token column and a share page that re-displayed the URL on every visit.
That is §2's failure mode with the roles reversed from revision 3's. Revision 3 restated a sibling's rule and let the copy drift. This one is a rule that lived correctly in one app and was never lifted, so the next app could not inherit it and re-decided it worse, in ignorance. Both argue for the same thing.
Found by reading Sheets' schema while looking for something else — not by review, and not by any test Docs had, because a plaintext token is not a behaviour a test can observe. That is the argument for it being a rule rather than a gate.
The part most likely to be argued with
It binds apps whose content is not sealed. The tempting exception: a leaked database of a plaintext app already yields the documents, so the token adds nothing. The ruling says it adds two things — a token grants ongoing access to a live system including every future revision, which a stolen snapshot does not; and an app plaintext today is one migration from sealed tomorrow, at which point the plaintext token is the single row that defeats the sealing. Docs is exactly that app (its spec §2 has titles due to be sealed).
If that reasoning is wrong, the ruling should be scoped to sealed apps, and Docs should keep its plaintext token and its more convenient share page. I do not think it is wrong, but it is the load-bearing claim.
SHA-256, not a password hash. Deliberate, following Sheets: a token is 128+ bits from crypto/rand, so guessing is not the threat model, and key-stretching would make every public page load do work for nothing. The property that matters is that the stored form does not yield the token.
The accepted cost is stated rather than hidden. An owner who loses the URL cannot ask the app for it, only replace it.
Consequences for work already in flight
- oficina/docs branch
sharing(discussion 38, in review) stores the token plaintext and must change before it merges: hash the column, show the URL once after publishing, and offer replace/stop rather than re-display. I am making that change. - oficina/memoria's vaults work is live in another session and has not shipped a token yet. This is the reason for filing now rather than after the Docs review returns — so it inherits the rule instead of repeating the mistake.
Requesting the whole-branch review AGENTS.md asks for before this merges.