Skip to content

Verifier libraries

Two libraries, one set of checks, held to one set of test vectors:

Package Runtime Dependencies
TypeScript @masterdb/verifier Node 24 or later none (node:crypto)
Python masterdb-verifier Python 3.10 or later cryptography

The only network call either makes is fetching the key set (GET /.well-known/keys); with a supplied key set everything is offline. Both carry both signature algorithms — the classical one and ML-DSA-65 — and require both for MasterDB’s own long-lived artefacts.

Artefact TypeScript / Python Checks
Key set KeySet.fetch, KeySet.fromSignedKeySet / KeySet.fetch, KeySet.from_signed_key_set every key certificate chains to a pinned anchor (roots certify roots and issuance keys; issuance keys certify working keys); the set is signed by an issuance key valid at issued_at; compromised_from marks are applied
Certificate verifyCertificate / verify_certificate signed by an issuance key valid at issued_at; sandbox only under the sandbox anchors; returns the cert_id, the subject, the status and since when and, for a withdrawn certificate, its status_reason
Key events keyAddedLeaf, checkKeyAdded / key_added_leaf, check_key_added; keyEvents and keyEventProofs on verifySeal (key_events, key_event_proofs on verify_seal) that the seal’s key was added to the business’s register in public: every business key is a key_event leaf in the transparency log over {v: 1, kind: "key_added", business_uuid, key_id, valid_from}, recomputed from the certificate (or the published key list) and the key’s valid_from; pass the leaf’s proof from GET /v1/log/proof?leaf=. warn (the default) keeps the seal and reports checks.key_event: "missing"; require refuses it (key_event_missing). The check proves the leaf is in the log; it never compares a leaf’s position with a seal’s
Key custody verifyKeyCustody, verifyKeyCustodyStatements, keyCustodyInForce / verify_key_custody, verify_key_custody_statements, key_custody_in_force who holds each business key, hosted (MasterDB, for the business) or self (Key custody): key-custody.v1, exact members, signed by both halves of an issuance key valid at issued_at; the statement in force for a key at an instant
Seal verifySeal / verify_seal the signature by a key of the business’s register (a passkey’s WebAuthn assertion — ES256, or RS256 from Windows Hello — Ed25519, or ES256); the hash over the exact bytes, or a Path A inclusion proof against the sealed root; the record’s own schema known and matching; the key valid at sealed_at; with the context supplied, the certificate, AI policy version and scope in force at sealed_at; seal format 2 (seal.v2, ai_policy_version) and format 1 (seal.v1, terms_version) both verify (a certificate MasterDB withdrew before sealed_at is refused as certificate_withdrawn, not as a compromise)
Row verifyServedRow / verify_served_row the projection version from adl_proj (unknown: refused); the serve-time members removed; RFC 8785; Ed25519 by a projection key
Projection re-run verifyProjection / verify_projection the row as above; its adl_origin is the fetched record’s hash; the record’s signed sidecar; then the published projection (spec v1, v2 or v3) re-run over the record and the sidecar and compared with the row byte for byte — nothing added, dropped or changed between the record and the index (projection_mismatch). The Python library is an independent port of the projection, held to the same vectors
Ad pool item verifyAdItem, verifyAdImage / verify_ad_item, verify_ad_image the item’s signed row (an ads row); adl_origin is the hash of record_base64; the seal inside the row verifies over those bytes (with the business’s register or published keys); every text and link in creative, and the row’s own, is the sealed one (ad_mismatch); the business’s AI policy on the item, its seal and the row’s ai_policy_bits; an image against adl_creative
Receipt verifyReceipt / verify_receipt formats 3, 2 and 1; signed by a receipt key of that region valid and not compromised at served_at; each served row matches its entry
AI policy bits verifyAiPolicyBits / verify_ai_policy_bits a row’s ai_policy_bits against the exact bytes of the sealed AI policy record a fetch returns (ai_policy.record): every mask derived from the named booleans, the version the fetch names, the purchase bit alone allowed to be withheld; returns the blocked contexts by code
Mandate verifyMandate, mandateCovers / verify_mandate, mandate_covers sealed by a business passkey valid at valid_from; covers a key, type, countries and instant
Merkle verifyInclusion / verify_inclusion RFC 9162 inclusion
Log verifyCheckpoint, verifyLogInclusion, verifyLogConsistency / verify_checkpoint, verify_log_inclusion, verify_log_consistency the log’s signed checkpoints and proofs
A fetch response recordBytes / record_bytes not a check: the exact bytes of the record member as served, which is what a seal covers

Every refusal is a VerificationError with a stable reason, the same in both languages: envelope_malformed, payload_type_mismatch, payload_malformed, version_unknown, key_unknown, signature_invalid, key_mismatch, hash_mismatch, record_malformed, record_type_mismatch, key_not_valid, key_compromised, certificate_unknown, certificate_invalid, certificate_not_in_force, certificate_withdrawn, ai_policy_version_mismatch, ai_policy_bits_mismatch, out_of_scope, mandate_invalid, inclusion_invalid, trust_chain_broken, post_quantum_required, projection_unknown, row_signature_invalid, receipt_row_mismatch, projection_mismatch, ad_mismatch, key_event_missing.

Both libraries are Apache-2.0; each package carries the LICENSE.

Unknown versions are refused, loudly. Every artefact carries its own version (v, schema, ai_policy_schema, payloadType); the libraries refuse one they do not know rather than guess, so MasterDB can evolve every format without a flag day and a verifier written today never silently accepts something it does not understand.

PRODUCTION_ANCHORS and SANDBOX_ANCHORS are pinned in the libraries, from MasterDB’s root key ceremonies; you can also pass anchors explicitly. Anchors are a set, so a root rotation is a published successor plus a library update, never a flag day.

Long-lived artefacts — certificates, key custody statements, key certificates, the key set — carry an Ed25519 or P-256 signature and an ML-DSA-65 signature. The libraries require both signatures, each valid and by a key that chains to the pinned anchors; a certificate or key set carrying only one is refused with post_quantum_required, and there is no option to relax it. Rows and receipts are Ed25519 only: they are verified within days, and can be re-signed by re-projection if ever needed.

The good cases and the broken-record corpus both libraries are held to are described in Test vectors.