Skip to content
autonym.dev
ContentsHide contents

Core

A5. Disclosure and the compiler

spec 1.0-draft-1

Status: normative. Document version 1.0-draft-1.

A5.1 Levels

Level Maximum permitted detail
L0 The key's closed l0_category code only; no value
L1 Identifier-free values, including non-identifying structure and broad age bands
L2 L1 plus first names and only the additional structure the key explicitly supports
L3 The full permitted value

Names, exact dates of birth, detailed addresses, phone/email, account/loyalty numbers, employer and institution names MUST NOT appear at L1. Free text at L1 or L2 requires the person's identifier-safe approved rendering; omit it if absent. Automatic redaction is not approval. Structure by itself does not imply a higher level: safe roles and age bands may be structured at L1. L0 is reduced detail, not anonymity or ambient permission.

A5.2 One compiler and transaction

The context request has one optional max_level, default L3, applied independently to each requested domain. Omitted declared_only is false, min_confidence is zero. The request cannot widen the stored grant. Explicitly ungranted domains fail with domain_not_granted, without revealing whether they contain entries. Keys are the intersection of request, grant and vocabulary; unknown or ungranted keys do not reveal existence.

For each requested domain, select the highest vocabulary-supported level at or below both ceilings. If none exists, omit the entry. Effective declared_only is grant OR request; minimum confidence is the maximum of both. A declared-only read includes only exactly confirmed declared entries in v1. Restrict provenance evidence separately. For this filter, an exactly confirmed declaration has effective confidence 1; other eligible provenance uses its recorded confidence, with null treated as zero. This is a filtering convention, not proof that a statement is true.

Within one transaction: authenticate the client, validate token audience/sender proof, lock and recheck the grant and counters, capture trusted time, select eligible baselines/overlays, enforce dependencies, render, create and sign the envelope, append its receipt, increment the counter, commit, then return. A revoked, expired or exhausted grant fails grant_revoked, grant_expired or grant_exhausted. Signing failure, receipt failure or commit failure returns internal_error and no disclosure. Concurrent reads cannot overspend a limit. All surfaces call this same transaction.

Safety-critical entries sort first, then declared, observed and derived; ties sort by domain, key and recipient alias. No recipient envelope carries hidden-entry counts, omission reasons, internal IDs or full evidence. A domain list exposes only granted keys that could currently be disclosed, and has no counts.

since is a UTC change filter, not permission to reuse indefinitely. It includes entries whose selected value or temporal eligibility changed since the instant, including baseline restoration. Always include currently eligible safety-critical entries even when older. Implementations MAY conservatively include otherwise eligible entries when their selection group or dependency group changed; they MUST NOT omit restored eligibility because the baseline's stored value was unchanged. Deletion and explicit selection changes that are no longer represented in current entries require trusted change evidence from storage. A client must obtain a full fresh read after a previous envelope expires; a partial response does not renew missing entries.

A5.3 Signed disclosure

disclosure.json is the exact response. Entries are a flat array with domain/key and client-bound rent_ identifiers. The signature binds provider (the issuer URL discovery publishes, A3.3), protected-resource audience, client, handle, grant ID/revision, purpose, read ID, issue time and absolute valid_until. Example bytes in protocol.json are synthetic and intentionally not a verifiable signature.

valid_until is the earliest of grant expiry, any disclosed entry/dependency expiry, the next eligible overlay/selection time boundary affecting the result, and five minutes after issue. ttl_seconds is the nonnegative whole-second interval to that deadline, rounded down. Even an empty response expires at the next relevant selection boundary. Use Cache-Control:no-store; recipients may retain transient context only until this deadline unless a separate persistence right exists.

Persistence authorises retention, not continued use of an expired temporary fact as current context. A recipient must stop treating an entry as current at the signed deadline and refresh through a new authorised read. persist_until is null without explicit permission and never exceeds grant expiry. No onward-sharing right exists in v1.

A5.4 Signature representation

Sign UTF-8 RFC 8785 canonical JSON of the complete object without proof. Use ordinary base64url payload mode with RFC 7515 Appendix F detached compact JWS: protected..signature, each part in unpadded base64url and in no other spelling of the same bytes. The protected header has exactly three members: alg:ES256, kid and typ, which is autonym-disclosure+jws for a disclosure and the value the signing section names for any other object. Unencoded-payload mode is not used. Receipts (A9), purge acknowledgements (A10) and exports (A11) are signed the same way. Each signed object has one signed time: issued_at for a disclosure, at for a receipt or purge acknowledgement, exported_at for an export, and iat for the purge instruction token (A10). A signature is not an identifier: ECDSA admits more than one valid signature over the same bytes, so signed objects are compared without proof.

Key IDs. A kid is the RFC 7638 JWK thumbprint of the public key: SHA-256 over its required members crv, kty, x and y, in canonical unpadded base64url: 43 characters, the last of which carries two unused bits that are zero. A key ID therefore names exactly one key and cannot be reused for a different key.

Key sets. A provider publishes its verification keys at the discovery jwks_uri as an RFC 7517 JWK set following key-set.json. Each key has kty:EC, crv:P-256, x and y (each 32 bytes in canonical unpadded base64url, so one key has one key ID), kid, alg:ES256 and use:sig, and no private member. A provider's signing key belongs to that provider alone and MUST NOT appear in another provider's key set, so that a valid signature identifies one provider whatever the signed object names (KEY-01). A retired key also has exp, the JWK parameter registered by OpenID Federation 1.0 section 8.7.2: a whole number of seconds since 1970-01-01T00:00:00Z, the first instant at which the key signs nothing. It stays in the set, with the same exp, so that what it signed earlier still verifies (A9). No other key lifetime member is used: a key that carries nbf, iat or revoked is not used to verify anything. The keys a recipient registers to sign purge acknowledgements follow the same rules.

Canonical instants. Every instant a provider writes into an object it signs or hashes has one spelling: UTC with Z, seconds always present, and fractional seconds only when they are not zero, without trailing zeros, for example 2026-10-02T19:00:00Z or 2026-10-02T19:00:00.25Z. Precision is never reduced. Equal instants therefore give equal bytes, and a deadline's spelling reveals nothing beyond the instant itself. Instants compare by the time they denote; a verifier does not reject an instant for its spelling.

Verification. Parse the received text strictly before canonicalisation: reject duplicate members, with names compared after escapes are decoded, and anything that is not I-JSON, including a lone surrogate or noncharacter and a number written with more magnitude or precision than an IEEE 754 binary64 value provides: one whose decimal value differs from that of the shortest decimal identifying the binary64 value it parses to, such as 1E400 or 3.141592653589793238462643383279, while 0.1 and 1e23 are accepted. A verifier bounds the size and nesting of what it parses (A15.3), and may refuse a member named __proto__, which no object here defines and which common JSON libraries handle inconsistently. Verify the signature over the canonical form of the parsed object without proof. Accept only ES256 and a key from the trusted, configured key set of the issuer: the configured provider for a disclosure, receipt, export or purge instruction, and the recipient's registered keys for an acknowledgement. Once an export verifies against the configured key set of the provider it came from, its public_keys are the trusted key set, and its origin_host the trusted issuer, for the receipts it carries (A11), which is how a receipt signed before a key rotation verifies from the export alone (A9). A carried receipt that names another provider is verified only against that provider's own configured key set, or reported as unverified history. A trusted issuer is never taken from the object being verified. Select the one key whose kid equals the protected header's, never a key or key URL that the object or its header supplies, and recompute its thumbprint. Reject:

  • any protected header member other than alg, kid and typ, including crit, jwk, jku, x5u and b64, and any duplicate header member;
  • alg:none and every algorithm other than ES256;
  • a typ that differs from the expected value, compared exactly;
  • proof metadata whose alg or kid disagrees with the protected header;
  • a key ID that is not in the trusted set, appears in it more than once, or is not the thumbprint of its key;
  • a matching key that does not follow key-set.json, such as one with a private member, key_ops or a lifetime member other than exp;
  • a signed object whose signed time is at or after the instant the matching key's exp denotes (A9, KEY-01);
  • a disclosure or receipt whose provider, or an export whose origin_host, is not the trusted issuer, compared as exact strings;
  • a receipt whose hash is not the A9 hash of its own content;
  • a disclosure whose valid_until is not after issued_at, is more than five minutes after it, or disagrees with ttl_seconds; one whose issued_at is more than 60 seconds after the verifier's trusted current time, the most the two clocks may differ; and one whose valid_until is not after the verifier's trusted current time.

Check every other signed binding as well, such as the client, audience, handle and grant of a disclosure. A valid signature alone does not establish current grant authority.

SD-JWT credential presentations are reserved for a later version and cannot be claimed by Core implementations.

Checks: DISC-01, SIGN-01, KEY-01, CORE-01, LOG-01, LOG-02, TEMP-02, TEMP-03, CONTRIB-03.

References to other standards and projects describe intended technical alignment only and do not imply endorsement or partnership.