| 7 | Threat model | creator model in `internal/testkit.Build`, `RewriteAge`; third-party edits in the mutation corpus | `agewrap.TestTimeIdentityStrictness`, `TestPayloadIdentityStrictness`, `TestAccessIdentityStrictness`, `capsule.TestMutationCorpus` |
| 30 | PAYLOAD_AGE is a complete age file | `filippo.io/age` public API only | `capsule.TestInteropAgeOpensPayload` (`-tags interop`, official `age` CLI) |
Where the specification does not name the error of a failure, the reference
implementation uses the following mapping. Each entry is a reproducible case
under the change policy of §76.
| Failure | Error |
|---|---|
| Bytes that are not the deterministic encoding of a valid schema instance: malformed CBOR, non-canonical encoding, unknown key, missing key, wrong type, wrong type tag (key 0), wrong field length, undefined `access_policy`, empty optional array or map, extension rules | `ERR_NON_CANONICAL_CBOR` |
| Schema version (key 1) other than 1 | `ERR_UNSUPPORTED_VERSION` |
| Truncated framing, length fields beyond the §57 limits, data after BODY_CBOR, malformed or unauthenticated age data, truncated or modified STREAM, trailing data after PAYLOAD_AGE, a PAYLOAD_AGE that I_PAYLOAD cannot open | `ERR_INTEGRITY` |
| Stanza count or type violations in OUTER_TIME_AGE, PAYLOAD_AGE or INNER_ACCESS_AGE, including two stanzas for one recipient | `ERR_POLICY_STRUCTURE_MISMATCH` |
| tlock stanza round argument not exactly the canonical decimal DateKey round | `ERR_ROUND_MISMATCH` |
| tlock stanza chain hash not exactly the lowercase hex chain hash of the pinned profile; profile whose parameters do not hash to its chain hash | `ERR_PROFILE_MISMATCH` |
| Instant before the profile genesis or after 9999-12-31T23:59:59Z; round outside the profile range | `ERR_DATEKEY_INVALID` |
| Unknown `access_type`, wrong material length, `.dkk` for another `capsule_id`, `capsule_digest` mismatch, no supplied identity is a recipient | `ERR_ACCESS_INVALID` |
| Round time not reached yet (no request is made), no source delivered the release | `ERR_RELEASE_UNAVAILABLE` |
## Implementation decisions to confirm in the specification
These are choices the reference implementation had to make where v0.8.1 is
silent or provisional (§74). None changes the protocol semantics; each is a
candidate clarification under §76.
1.**Pre-genesis instants.** §15 defines the candidate formula relative to
`genesis_time`; instants before it are rejected with `ERR_DATEKEY_INVALID`
instead of resolving to round 1.
2.**Round bounds.**`dk1_` accepts rounds in 1..2^53−1 so that JSON parsers
based on IEEE 754 doubles read the same integer; a profile further limits
rounds to round times up to 9999-12-31T23:59:59Z (Quicknet: 83 903 165 811).
3.**`profile_id` alphabet.** `[a-z0-9][a-z0-9:._-]{0,127}`, which keeps the
canonical `dk1_` JSON free of escapes and makes its re-emission trivial.
4.**Number spellings in `dk1_`.** JSON numbers are compared by value, so
`1e3` or `1000.0` for 1000 are `ERR_DATEKEY_NON_CANONICAL`, while
non-integers, negatives and out-of-range values are `ERR_DATEKEY_INVALID`.
Padded or standard-alphabet Base64 and non-zero trailing bits are
non-canonical.
5.**Closed maps.** Unknown keys in core maps are rejected; applications use
extensions (§1, §54).
6.**Extension data.** Key 2 is optional and omitted when absent; data must be
deterministic CBOR without tags. `extension_id` is 1 to 256 bytes of UTF-8.
No V1 schema allows repeating an `extension_id`.
7.**One stanza per recipient.** Enforced as far as a recipient can observe it:
no repeated X25519 ephemeral share, and no identity that unwraps more than
one stanza.
8.**Strict tlock stanza arguments.** Exact string comparison, as the tlock
library itself does for the chain hash; a round with leading zeros is a
mismatch.
9.**`capsule_digest`.** Written by `Encrypt` for every portable key and
checked before any request when the capsule reader is seekable; it remains
a UX shortcut (§43).
10.**Creation in the past.**`Encrypt` requires the unlock time to be strictly
after the injected clock.
11.**Clock injection.** No library package reads the wall clock; `Encrypt` and
`Open` require a `Now` function, and `Open` never requests a release for a