Ruling A is superseded: rastrillo already has a merged assertion design
Asked whether rastrillo should own this. It already does, and further along than this plan assumed. rastrillo discussion #1, "An instance needs to verify a signed identity assertion", is open and was filed jointly by the Sheets and Docs design sessions, with Paul ruling on the shape for both. The design is written and MERGED — PR #128, 2026-08-31. What is missing is the code: no Ed25519 and no assertion package anywhere in rastrillo's non-test Go. It contradicts this plan, and rastrillo is right. This plan recommended a hand-rolled compact JWS. rastrillo's design says the wire format should not be a JWT even in shape, and composes crypto's fixed primitive instead — one curve, one hash, one encoding, a pinned domain-separating context. Its argument: crypto has no algorithm agility, so there is no field in which to say "none" and no second algorithm to downgrade to, and algorithm confusion is designed out rather than defended against. That beats this plan's answer on exactly the failure mode the codex second opinion listed first. Which means the recommendation as it stood would have made home the THIRD app to hand-roll assertion verification differently — the precise outcome discussion #1 exists to prevent. Superseded rather than deleted, because the reasoning is why the conflict is visible at all. Two conflicts surfaced for Paul, both between specs rather than inside one: home §5.3 says Ed25519 where rastrillo says ECDSA P-256, and home publishes verification keys where rastrillo's §9 recommends pinning them. Home's side carries no stated reasoning on either; rastrillo's does. The unblock is not a new issue. It is Paul answering rastrillo's §9 — especially Q1, which that design says sizes everything else. Tasks 4, 5 and 6 here are independent of the signature format and remain workable meanwhile.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+57
−1
docs/superpowers/plans/2026-09-07-home-assertion.md+57 −1
CI passed — run details
diff --git a/docs/superpowers/plans/2026-09-07-home-assertion.md b/docs/superpowers/plans/2026-09-07-home-assertion.md| index a120d90..05e3dc7 100644 |
| --- a/docs/superpowers/plans/2026-09-07-home-assertion.md |
| +++ b/docs/superpowers/plans/2026-09-07-home-assertion.md |
| @@ -88,7 +88,63 @@ executor that has decided something it should not. |
| ## Three things to settle before Task 2 — not the executor's to decide |
| -### Ruling A: how the assertion is signed and encoded |
| +### Ruling A: SUPERSEDED — rastrillo already has a merged design |
| + |
| +**Read this before anything below it.** Checked 2026-09-07: |
| +`rastrillo/rastrillo` discussion **#1**, "An instance needs to verify a |
| +signed identity assertion", is open and was filed jointly by the Sheets |
| +and Docs design sessions, with Paul ruling on the shape for both. The |
| +design is **written and merged** — PR #128, 2026-08-31, |
| +`docs/superpowers/specs/2026-08-31-identity-assertions-design.md` in |
| +rastrillo. What does not exist is the code: there is no Ed25519 and no |
| +assertion package anywhere in rastrillo's non-test Go. |
| + |
| +**It contradicts this plan, and on the evidence rastrillo is right.** |
| +This section below recommends a hand-rolled compact JWS. rastrillo's |
| +design says explicitly that the wire format **should not be a JWT even |
| +in shape**, and builds on `crypto`'s fixed primitive instead — one curve |
| +(ECDSA P-256), one hash, one encoding, and a domain-separating context |
| +string pinned to `rastrillo/assertion/v1`. Its reasoning is that |
| +`crypto` has no algorithm agility at all, so there is no field in which |
| +to say "none" and no second algorithm to downgrade to: **algorithm |
| +confusion is designed out rather than defended against.** That is a |
| +better answer than this plan's on precisely the failure mode the second |
| +opinion below listed first. |
| + |
| +Its wire format is `base64url(payload) "." base64url(signature)`, with |
| +`kid` INSIDE the signed payload and a strict order — decode, check `v`, |
| +select key by `kid`, verify the signature, and only then read any other |
| +claim; nothing before the signature check may have a side effect. |
| + |
| +**So home must not hand-roll this.** Doing so would make it the third |
| +app to hand-roll assertion verification differently, which is the exact |
| +outcome discussion #1 exists to prevent. |
| + |
| +**Two conflicts for Paul, both between specs rather than within one:** |
| + |
| +1. Home §5.3 says "Signature: Ed25519". rastrillo's design says ECDSA |
| + P-256 via `crypto`. Home's choice carries no stated reasoning; |
| + rastrillo's carries the argument above. One of the two specs has to |
| + change. |
| +2. Home §5.3 publishes verification keys on the credential origin |
| + (fetched). rastrillo's §9 Q2 recommends **pinned** keys on the |
| + never-speak doctrine, and says the rotation counter-argument "is not |
| + weak". Home is the signer, so this one is home's problem more than |
| + any consumer's. |
| + |
| +**What actually unblocks this step** is not a new issue — it is Paul |
| +answering rastrillo's §9, especially Q1 ("both halves, or verify only?", |
| +which that design says "is the first thing to rule on, because it sizes |
| +everything else") and Q2. Then rastrillo implements, and home consumes |
| +it. |
| + |
| +**Meanwhile Tasks 4, 5 and 6 below are independent of the signature |
| +format** — the redirect protocol, the `jti` store and gate 3 do not care |
| +how the bytes are signed. That is where to work while #1 is unblocked. |
| + |
| +--- |
| + |
| +### Ruling A (superseded — kept for the reasoning): how the assertion is signed and encoded |
| **§5.3 says Ed25519. rastrillo cannot supply it.** Verified at the |
| v0.22.0 pin: `rastrillo/crypto` is **ECDSA P-256**, signing over |