The v0.9 draft is written in `spec/DateKeys_Protocol_Specification_v0.9.md` (title "Borrador normativo v0.9", date 29 septiembre 2026) and in `spec/datekeys.cddl`. Nothing is committed. `spec/README.md`, testdata, Go and TS are untouched. The two Go tests that read the spec still pass, because they read the v0.8.2 file. The existing §76 text is byte-identical to v0.8.2. I rechecked the design's numbers and the critics' claims before writing: - **Padding:** every value recomputes exactly, and the stated properties hold for L from 0 to 300 000 and for 200 000 random L up to L_MAX. - **Fixture lengths:** all 5 fixtures match the length formulas. - **JS arithmetic:** 32-bit operators first give a wrong P at L = 2³² + 1, and in Node `Math.log2(2^49 − 1)` returns 49 instead of 48. - **Recipients:** with a recipient whose bit 255 is set, both `filippo.io/age` 1.3.2 and `age-encryption` 0.3.1 encrypt a stanza that the identity cannot open. With the zero point (low order), both refuse to encrypt. ## Sections changed - **Top of the document:** title, date, §1, §4 (goal 8), §5 (new non-goal), §15. - **Format and header:** §22 (table mapping each format to its control version), §23, §24, §26, §27, §28.1. - **Padding:** §29, new §29.1 «Relleno del payload», §30, §30.1. - **Control and slots:** §31 (keys 6 and 7, the 103-byte control), §33, §36, §36.1, §37, §38, §39 (rewritten, defines «credencial» and «señuelo»), §40. - **Privacy and limits:** §55.1, new §55.2 «Consideraciones de privacidad», §56, §57, §58, §58.1. - **Writing:** §61, §62, new §62.1 «Reglas del escritor». - **Reading and tests:** §63 steps 2, 4, 12, 13, 14, 16, 17 and 18, §64, §67, §68. - **Precedence and compatibility:** §69.1 (layers 1 to 3, the cross-object paragraph, Alcance, 7 new examples), §70, §72. - **Closing sections:** §73, §74 (with a new «Trabajo futuro» part), §75, new «Cambios normativos de la v0.9» at the end of §76 (13 numbered items, each with Cambio / Motivo / Caso / Tests), §77. - **Unchanged:** §65, §66 and §69. - **CDDL:** new header; `control = control-v1 / control-v2`; `payload-length`, `padding-scheme` and `max-payload-length`; the rule linking control version to format, and the 8-byte reading rule. ## Where I followed a critic over the design 1. **Relabel tests (both critics):** the `time_and_key` relabel cases offer the identity, not the `.dkk`. A `.dkk` carries a `capsule_digest`, and editing the VERSION byte breaks it, so those cases would fail earlier (step 9.a, `ERR_ACCESS_INVALID`). I qualified §26 and §70 the same way, added a §69.1 example for the `.dkk` case, and planned `spec:false` companion cases. 2. **Downgrade claim scoped (both):** §70 now says only a capsule whose control was not resealed never outputs its padding. Resealing by someone who knows `I_PAYLOAD` is described as a rewrite, not a downgrade. 3. **Format table, not "control version equals VERSION" (consistency):** §22 has the table and says a new padding code needs a new format. I dropped the clause «salvo que su registro diga otra cosa» from §72. 4. **Padding arithmetic (security):** rows above 2³² added to §29.1; a MUST NOT on 32-bit operations and `Math.clz32`; the float example is now 2⁴⁹ − 1, which is inside the range; a planned accept vector `48 0000000100000001`. 5. **Dummies (security):** - A dummy's private key is stated to be a full credential while it exists, with a note on how to generate and wipe it. - The permutation and which slots are dummies are MUST NOT stored or delivered. - The shuffle rule binds the written header order, unbiased (Fisher–Yates). - The indistinguishability argument now rests on age's recipient anonymity under the Diffie–Hellman assumption, not on the distribution of keys. 6. **Readers (both):** L and P MUST NOT drive memory or disk reservation; a MAY check of the `PAYLOAD_AGE` length at step 17, with the same code; §56 gets a MUST NOT for format 2. 7. **`I_PAYLOAD` reason (security):** the stated reason is now confidentiality (shared, content-derived or master-derived keys), not only the binding. 8. **§55.2 additions (security):** - a subsection on what format 2 makes impossible to check; - the quantum caveat; - whether a portable `.dkk` exists; - the fixed 256-byte bucket of `bloque256`; - what VERSION 1 and the choice of code reveal. 9. **Writers (consistency):** a writer MAY spool the source to learn L and MUST NOT guess it; "abortar" became "report the error, and the receiver MUST discard the output". The self-check also counts P and checks the `PAYLOAD_AGE` length (security). 10. **Smaller corrections:** - §62.1 fixture note (C = 127 for `time_only_extensions`); - «como mucho 256»; - format 1 «contiene (MUST, §33)»; - one definition of «credencial»; - §69.1 cross-object list now includes the control version; - §74 no longer contradicts itself (the CONTROL_CBOR schema stays open, L stays fixed-width); - the precedence case "padding 3 + unknown critical extension" is a step test, not a `cbor.json` vector; - forward references are marked "(por generar)" / "(por implementar)". ## Rules I added that were not in D1–D7 (please confirm) - L_MAX = 2⁵³ − 2⁴⁶, with a reader rejection (`ERR_NON_CANONICAL_CBOR`). - The MUST for a uniform, unbiased stanza order. - The MAY to reject twist points (the Legendre check in §37). The security critic offered SHOULD or "the list is not exhaustive"; I wrote the latter plus a MAY. - The official SDK SHOULD show the effective round time, and MAY compare it with the latest published round when online (§62.1 rule 2). - The MAY check of the `PAYLOAD_AGE` length at step 17. - The MUST NOT on presenting content before step 17 ends (§56). ## Open questions for the author 1. Is `bstr .size 8` for L acceptable, instead of a CBOR `uint` (which would leak L's width through the control length)? 2. Should readers report the format as SHOULD (as written) or MAY? 3. Keep goal 8 in §4 and the new non-goal in §5? 4. Any new format-1 test objects need a test-only builder, since conforming writers MUST NOT write format 1. Is that acceptable? 5. The `format2_` prefix for the planned fixture names. 6. Optional change, not written: a dummy could be a random valid public key (a canonical u that is not low order), so no private key ever exists. D1 fixes the mechanism, so this needs your approval. 7. Twist points: keep MAY, or make it SHOULD? ## Notes - **README:** the design also proposed edits to `spec/README.md`; I left them out because the task forbids touching that file. It will need the v0.9 entry once `SpecVersion` becomes "0.9". - **Effects on the TS phase 3 plan** (from the consistency critic, not written into the spec): - `R_ACCESS` must move from last place to the shuffle. - The "1 024 recipients" test becomes a rejection above 16. - `I_PAYLOAD` should be wiped after the new self-check, not right after sealing.