| Filename | Latest commit message | Latest commit date |
|---|---|---|
SPEC §3.1's check 5 now has two parts. Revocation runs at every sink that holds a key log, for every capability a pin applies to: a set whose digest is in the revoked_modules of the head the sink reaches from any anchor it holds is refused with prior_revoked. It needs no grant, status or clock, so it no longer waits on the relying party holding the capability to jwp-receipts §7.9, and it is reported as prior_revoked whichever check of §7.9 a grant fails first, offline or with no grant included. Authorisation is still the relying party's choice per capability. SPEC §3.1 and §4, jwp-receipts §7.9 and the CHANGELOG say so. An envelope that pins no set no longer escapes check 5. The neural registry, given a key log, checks the set an unpinned decode reads under each digest a pin over it could name (the file's SHA-256 for a one-file set, and each listing beside it), and dispatch falls back when it is refused. PinAuthority gains check_unpinned for this. A family receipt now needs energy/<class> for each class its figures carry, as Item::from_family already derived: the §7.5 row, the corpus README, signer_verify.py and the corpus reader now agree. Item::for_group and Item::for_family are crate-internal, the corpus's builders, and the docs say that from_family takes signer_id and time from its caller. signer-conformance gains vectors for a revoked set whose grant fails at check 4, a revoked set with no grant, a set with no grant, and a family receipt with two classes, regenerated by wai_signer_vectors gen; both verifiers reproduce every verdict, and every earlier vector keeps its declared outcome. The CI library tests cover the revocation part at a sink that does not hold the capability to §7.9, and the neural key-log gate covers the unpinned decode. Docs say that a key log naming the subject's origin holds for one envelope. ModelRegistry's key_log field is private, which the CHANGELOG records. The pin corpus README says its cases model a sink with no key log. The parameter-set entry's refusal list names check 5's two reasons, and the did:web label test is described by the files it reads. Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
| .. | ||
| .gitattributes | ||
| README.md | ||
| signer_verify.py | ||
| vectors.json | ||
signer-conformance
Conformance for key discovery, signing authority and module authorisation
(§7): the forms of a signer_id, an origin's key log, anchoring, the confirmation a
verifier reports beside a receipt's or claim's verdict, and the checks a sink runs
before it runs a decoder or reconstruct module.
Each vector declares one verdict, set from the text in the generator and never copied from a verifier. A verifier that reaches another verdict — another level, step, reason, head or anchor path — does not conform.
Files
| file | what it is |
|---|---|
vectors.json |
The corpus. Regenerate with wai_signer_vectors gen .; wai_signer_vectors verify . checks the canonical fixture, a byte-identical regeneration and every verdict. |
signer_verify.py |
A second verifier, written from §7, that shares no code with wai-rs. It takes from ../claim-conformance/claim_verify.py only what §7 reuses of §3.1: the JSON reading, JCS, BLAKE3, the strict Ed25519 rule, the RFC 3339 syntax and claim verification. |
Procedures
confirm (§7.4) runs on an item and reports confirmed with the origin, key-id,
head revision_hash and anchor path — and, where they apply, moved_to,
retired_until, untimed=true, expected= and also_delivers= — or unconfirmed or
refused at a §7.4 step (1–7, or 6a) with a reason of §7.7. The item is either
receipt, a claim's wire form, which is first verified under §3.1 (a claim that fails
is reject step=<n> reason=<label>, and is never confirmed), or context.item: the
kind (group or family), signer_pubkey, signer_id, classes (for a group,
the acquisition classes its objects' labelled leaves and its labelled total carry; for a
family receipt, the acquisition classes its figures' labels carry) and its
signed time (or null). The authorities an item needs are derived from it by §7.5's
table. The context also holds revisions (revision texts the verifier holds, in any
order; one that fails rule 1 is not held), anchors (origin, revision, path,
acquired_at, and pinned_revision for a pinned record, which a forward move keeps)
and, optionally, expected_origin (origin and source, relying-party or
subject).
anchor (§7.3) runs on a key-log document (log) retrieved for origin by path
(pinned or fetched, with acquired_at), and optionally current: an anchor already
held, with the revisions held with it. It reports anchored revision=<hash> anchor=<path> — with pinned_revision=<hash> where a pinned record moved forward —
not_anchored reason=<label> or replaced reason=key_log_replaced.
module (§7.9) runs on a module's bytes (module, hex), whether they are a module
or a pinned parameter set (object), a grant, the capability being decoded, the
sink's module_origins (each an origin and source), the revisions, anchors and
statuses the sink holds, its trusted clock (now, or null for none) and whether
the envelope leaves a fallback. It reports run with the granting key's origin,
key-id, head, anchor path, where its module origin came from (expected=) and
kill_latency_s, or fallback or inert at a §7.9 check with its reason; a digest
another anchored origin revokes adds revoked_by=<origin>. A parameter set that is not
held adds the refusal SPEC §3.1's check 5 reports: refusal=prior_revoked where its
digest is revoked, refusal=prior_not_authorised for any other check. A parameter set
whose digest the head of an anchor the sink holds revokes is reported at check 5 with
revoked_by=<that origin> and prior_revoked whichever check its grant failed first,
unless the grant reached check 5 itself. For a parameter set grant may be null, a
sink that holds no grant: the set is then refused as revoked where its digest is, and
otherwise reported as no_grant refusal=prior_not_authorised.
Keys, origins and times
Everything is synthetic. Keys are Ed25519 from published one-byte seeds and attest
nothing: 0x61 the root in use, 0x62 the key it commits to, 0x68 the key that one commits
to, 0x63 and 0x66 delivery keys (0x66 also meters), 0x64 and 0x69 metering keys, 0x65
and 0x6e lawful-basis keys, 0x6b and 0x6c module-granting keys, 0x60 a key added later,
0x67, 0x6a and 0x6f keys an attacker holds, 0x70, 0x71 and 0x72 the root, committed key
and module-granting key of a second origin, 0x73 and 0x74 a measurement key and an
operating-point key, and 0x75 a key that signs family receipts with a ModelBased figure. jwp-receipts §7.8 says such a key must
never be listed in a real key log: every anchored verdict here carries
note=published_test_key, which is what a verifier reports on finding one. Origins are
reserved example names (publisher.example, with and without port 8443,
service.example, other.example). Times are in September 2026 and invented.
signer_verify.py needs Python 3 with the cryptography package. CI runs it on the
linux runner (independent-verifiers in wai-conformance.yml), and requires its
verdicts output to equal wai_signer_vectors verdicts line for line. By hand:
python3 signer_verify.py # every vector, and the fixture against wai-rs
python3 signer_verify.py fixture # the fixture's bytes, signature and hash
python3 signer_verify.py verdicts vectors.json