58 KiB
Conformance suite for the AIP-23 proof kinds
Every component beside this one ships its own verifier and its own way of saying no: the AIP-23 reference verifier prints four
levels, the payment modules return reasons, the market judge motive, the dataset module ok, the audit log and the agents'
ledger a refusal text, the identity verifier and the device verifier a list of labelled rows. This folder adds the one place that
says, in the same form for all of them, this artifact passes or does not, at level N, for reason M, and a set of vectors that any
implementation, in any language, can be run against.
It adds no verification rule. A verdict here is derived from the verdict of the published verifier of that artifact; the rule ids are the ones those verifiers name, read from their code.
The one verdict form
{ "v": 1, "kind": "aere-conformance-verdict", "artifact": "payment-receipt", "verdict": "INVALID",
"levels": [
{ "level": "integrity", "state": "FAILED", "rules": ["receipt.chain"], "reason": "the transaction on chain is not this payment ..." },
{ "level": "authenticity", "state": "NOT_MEASURED", "rules": [], "cause": "absent", "reason": "the envelope carries no signature" },
{ "level": "anchoring", "state": "NOT_MEASURED", "rules": [], "cause": "absent", "reason": "no notarization claimed and no --rpc given" },
{ "level": "finality", "state": "NOT_MEASURED", "rules": [], "cause": "absent", "reason": "no notarization claimed and no --rpc given" } ],
"failed": ["receipt.chain"] }
| level | what it says |
|---|---|
integrity |
the artifact is what it says: its hashes bind its content, and the rules it declares (a policy, deadlines, a challenge window, Merkle paths, a payout) hold when recomputed from the evidence supplied |
authenticity |
the signature the artifact carries verifies, with a key of the scheme declared, and the identity it names is the one derived from that key (an ML-DSA signature on an AIP-23 envelope, the ML-DSA-65 signature of an audit head, the ML-DSA-65 key and signatures of an agent's ledger; the hybrid Ed25519 + ML-DSA-65 signatures of a credential, of its presentation and of each delegation link, both halves required; the same hybrid signatures on a device's manufacture credential, firmware release, transfers, authorization and status records, and the ML-DSA-65 signature of the device on its attestation) |
anchoring |
its statementHash (or, for a trust chain, the head of its ledger) is recorded in the notary contract, under a state root that a post-quantum anchor covers |
finality |
that anchor's post-quantum certificate verifies. The finality is the one of the chain the artifact was notarized on |
Each level is PASSED, FAILED or NOT_MEASURED, with the reason; a level nobody judged says why (cause). The verdict is
INVALID when a level failed; PARTIAL when nothing failed but something the artifact claims was not measured (a notarization
claim not checked because the run was offline, a signature scheme the verifier cannot check, a timeline that is not terminal, a
presentation judged without the verifier's audience, nonce or trusted issuers, a credential whose status list was not read, a
device bundle with a link missing or a registry nobody read);
VALID otherwise, and only with integrity passed. A claim that was not checked is never VALID. failed is the sorted list of the
rule ids that refused; the suite compares it as an exact set.
A receipt or a market timeline that carries an AIP-23 envelope gets that envelope judged too, by the reference verifier, so one
verdict covers the artifact: the receipt recorded on testnet 28001 comes out with integrity, anchoring and finality PASSED in one
verdict (vectori/payment-receipt/company-p1-28001.valid.json).
What is enrolled
REGULI.json is derived from the code (node deriva-reguli.mjs --verifica): 240 rules of 15 verifiers, 21 artifact kinds (since
2026-10-18: 239 when the Travel Rule and the compliance verifiers were enrolled, 240 with identity.request later that day; 213 rules of 13 verifiers and 17 kinds after the SD-JWT
verifiers earlier that day; 181 rules of 11 verifiers, 15 kinds before). By level: 188 rules are reported at integrity, 49 at
authenticity, 3 at anchoring (one of them, envelope.finality, is reported at anchoring or at finality by the place that refused).
| artifact kind | judged by | rules |
|---|---|---|
envelope (every AIP-23 kind) |
the AIP-23 reference verifier, run as the program it is (AERE_VERIFY_PROOF=<verify-proof.mjs>, published at aere.network/tools) |
4: envelope.form, .integrity, .signature, .finality |
payment-risk (a payment presented as allowed under a declared policy) |
payments/risc.mjs |
6: risk.form, .asset, .counterparty, .rate, .limit, .window |
payment-receipt |
payments/chitanta.mjs |
5: receipt.binding, .amount, .policy, .risk, .chain |
market-timeline |
market-settlement/piata.mjs |
10: market.form, .complete, .posting, .deadline, .window, .arbiter, .payout, .result, .replay, .proof |
dataset-record, -answer, -consistency, -sample |
verifiable-data/baza.mjs |
10: dataset.form, .commitment, .field, .inclusion, .index, .range, .order, .completeness, .consistency, .sample |
ai-trust-bundle (the trust chain of an AI result) |
ai-trust/lant-de-incredere.mjs |
21: aitrust.form, .identity-mismatch, .identity-key, .version-attestation, .version-artifact-mismatch, .version-label-mismatch, .runtime-record, .runtime-artifact-mismatch, .runtime-after-output, .audit-log-broken, .runtime-not-in-audit-log, .audit-head, .witness-head, .policy-hash, .ledger-invalid, .output-not-in-ledger, .action-refused, .ledger-time-mismatch, .policy, .provenance-envelope, .provenance-finality |
audit-log (a hash-chained log, alone or against an attested head, signed or not) |
verify-layer/sidecar.mjs |
13: auditlog.entry, .seq, .chain, .hash, .head-form, .head-hash, .head-unsigned, .head-scheme, .head-key, .head-signature, .head-signer, .head-short, .head-rewritten |
audit-inclusion, audit-consistency (RFC 9162 proofs over that log) |
verify-layer/jurnal-arbore.mjs |
12: audittree.proof-form, .head-form, .head-hash, .inclusion-index, .inclusion-entry, .inclusion-envelope, .inclusion-leaf, .inclusion-path, .inclusion-head, .consistency-start, .consistency-end, .consistency |
agent-ledger (an agent's signed ledger under its policy) |
agents/agent-ledger.mjs |
16: agents.ledger-form, .policy, .other-policy, .session, .key, .identity, .anchor, .seq, .chain, .body, .signature, .hash, .time, .revocation, .approvals, .decision |
identity-presentation (a credential shown by its holder or by the last delegate of a chain, judged with what the verifier brings: its audience, its nonce, its clock, the issuers it trusts, the status lists and the revocations it was handed) |
identity/identity.mjs |
38: identity.form, .issuer-id, .holder-id, .credential-signature, .issuer-trusted, .credential-window, .status-list, .credential-revoked, .sd-distinct, .disclosure-readable, .disclosure-signed, .disclosure-once, .disclosure-plain, .claim-name, .binding-credential, .binding-disclosures, .binding-delegations, .presenter, .presentation-signature, .audience, .nonce, .freshness, .chain-length, .delegation-form, .delegation-ids, .delegation-from, .delegation-parent, .delegation-signature, .delegation-scope, .delegation-window, .delegation-depth, .delegation-narrows, .delegation-credential, .delegation-claims, .delegation-audience, .delegation-revoked, .unreadable, .request (since 2026-10-18: the presentation commits to the request the verifier received, OpenID4VP transaction_data) |
device-bundle (the chain of a device: the manufacturer's credential for the device key, a firmware release, the ownership transfers, the owner's authorization, the device's attestation and its AIP-23 envelope, judged with what the verifier brings: its clock, nonce and audience, the manufacturers it trusts, the recall lists, firmware policies and status records it read, its own reading of the notarized envelope) |
device-identity/device.mjs |
47: device.form, .manufacture-form, .manufacturer-id, .manufacture-holder, .manufacture-signature, .manufacturer-trusted, .manufacture-window, .manufacture-status-list, .manufacture-revoked, .device-key, .device-id, .firmware-form, .firmware-signer, .firmware-signature, .firmware-model, .firmware-retracted, .firmware-rollback, .firmware-software, .chain-length, .transfer-form, .transfer-device, .transfer-ids, .transfer-from, .transfer-prev, .transfer-signature, .transfer-time, .authorization-form, .authorization-device, .authorization-owner, .authorization-ownership, .authorization-signature, .authorization-window, .authorization-action, .status-retired, .status-suspended, .attestation-form, .attestation-key, .attestation-device, .attestation-signature, .audience, .nonce, .freshness, .attestation-firmware, .attestation-ownership, .witness-envelope, .witness-time, .unreadable |
sdjwt-presentation (an SD-JWT of IETF RFC 9901, as issued or with its Key Binding JWT, judged with what the verifier brings: the issuer key it chose, a public JWK, never one taken from the token; its clock; its audience and nonce; the issuer and the explicit type it expects; whether it requires key binding and exp) |
identity/sdjwt.mjs (enrolled 2026-10-18) |
23: sdjwt.form, .alg, .typ, .crit, .key, .signature, .issuer, .sd-alg, .disclosure-form, .disclosure-twice, .digest-twice, .disclosure-name, .claim-conflict, .unreferenced, .time, .kb-context, .kb-required, .kb-cnf, .kb-claims, .kb-audience, .kb-nonce, .kb-freshness, .kb-sd-hash |
sdjwt-vc-presentation (an SD-JWT VC of draft-ietf-oauth-sd-jwt-vc, judged as the SD-JWT above plus the profile: the credential type the verifier expects, and the Token Status List it fetched for the status the credential names) |
identity/sdjwt-vc.mjs (enrolled 2026-10-18) |
9 of its own, on top of the 23 of sdjwt: sdjwtvc.vct-expected, .vct, .vct-mismatch, .disclosed-protected, .form, .status-list, .status-index, .status-revoked, .status-unjudged |
travel-rule-binding, travel-rule-message, travel-rule-receipt (the Travel Rule between VASPs: the beneficiary's KEM key binding with its VASP presentation, judged by the originator with its id, its nonce, its clock, the registries it trusts and their status lists; the sealed IVMS101 message, judged by the beneficiary with its id, its own KEM key pair, which decrypts, its clock, its registries and lists and its replay store; the signed receipt, judged by the originator with the message it sent and the beneficiary it accepted) |
identity/travel-rule.mjs (enrolled 2026-10-18) |
23: travelrule.binding-form, .binding-id, .binding-signature, .binding-window, .vasp-presentation, .vasp-status, .vasp-identity, .vasp-claim, .message-form, .message-id, .message-signature, .message-recipient, .message-kem-keys, .message-freshness, .replay-store, .replay, .kem-alg, .kem-open, .ciphertext, .receipt-form, .receipt-from, .receipt-signature, .receipt-message |
compliance-presentation (a presentation of a credential judged against the verifier's compliance policy: the issuers it believes and the claims it requires, with the verifier's audience, nonce and clock, all required, and the status lists and revocations it holds) |
identity/conformitate.mjs (enrolled 2026-10-18) |
3: compliance.presentation, .status, .rule |
A module verifier exports the list of rules it can name (REGULI; its helper throws on any other) and marks its rules in one of two
ways, both read by the derivation: a marker on every check of a rule (nine modules), or the rule named at every call of the
verifier's one refusal helper (ai-trust). The derivation requires declared == marked == mapped here. A rule added to a verifier and
not mapped is a refusal of the derivation; a mapped rule without a vector is a refusal of the probe. For the reference verifier, a
published program this suite does not edit, the rules are its level names, and each of the six places where its finality level
can refuse is classified as anchoring or finality (a seventh place would be a refusal of the derivation).
The identity verifier does not stop at its first refusal: it makes every check it can reach and names every rule that refused, so a
vector may expect several. What it could not judge because the verifier did not bring it (no audience, no nonce, no trusted issuer,
the status list the credential names not handed or not current) leaves the verdict PARTIAL, never VALID; the cause is named
not-supplied (added to the verdict form on 2026-10-05).
The device verifier (enrolled on 2026-10-05) works the same way. A link that is not in the bundle, the verifier's nonce, audience
or trusted manufacturers not brought, a recall list, a firmware policy or the status registry not read, a recall list, a policy or
a status record that is signed by who may give it and cannot be read, a notarization the envelope claims and the verifier did not
read, a hardware root (which that verifier cannot judge): each leaves the verdict PARTIAL. The adapter refuses to run if the
causes the module declares as keeping its verdict from VALID are not exactly the ones mapped here, and if the verdict of the
suite is not the verdict of the module. The readings of a notarized envelope in these vectors are inputs (what a verifier would
bring), not readings of a chain: the module's own probe reads testnet 28001.
Four things the device verifier names without changing its verdict, and what a VALID then does and does not say: a status
record of a previous owner (it does not bind the owner of now: a seller could date a retirement before the sale after it;
the manufacturer's retirement is final, an owner's lasts as long as its ownership), a status record dated in the verifier's
future, a status record of a stranger, a credential that names no recall list. The one verdict form has no field for them: the
adapter writes them in the reason of the integrity level, and the vectors compare verdicts, rules and level states, not reasons.
So vectori/device-bundle/resold-after-retirement.valid.json is VALID here, and a verifier that drops the naming still passes it; that the
naming is there is measured by the device component's own probe, not by this suite.
The SD-JWT verifier and the SD-JWT VC profile (enrolled 2026-10-18) stop at their first refusal and name its rule: an integrity refusal
leaves the authenticity level NOT_MEASURED (not-reached), an authenticity refusal leaves integrity so. A VC presentation whose
status the verifier waived (requireStatus: false) and did not judge is PARTIAL (claim-not-checked), like a credential whose
status list was not read. The profile imports sdjwt.mjs from its real file, so a planted copy of sdjwt.mjs is reached only through
the artifact sdjwt-presentation: no sdjwt-vc-presentation vector expects a rule of sdjwt (they all exercise them).
The Travel Rule verifier and the compliance verifier (enrolled 2026-10-18, the last two of the identity component; the file counts
nothing as not enrolled since) run the identity verifier on the VASP or KYC presentation inside and name its whole verdict as one rule
(travelrule.vasp-presentation, compliance.presentation); the identity rules themselves are enrolled under identity-presentation.
Both make every check they can reach and name every rule that refused, like the identity verifier; the Travel Rule verifier does not open
a message whose header it refused for any reason, so with only its authenticity refused (the originator's signature or id, the VASP
presentation) the integrity of the message is NOT_MEASURED (not-reached), and a binding or a receipt that cannot be read at all
(binding-form, receipt-form, message-form before the signature) leaves authenticity so. Their verdicts are the verifier's: a
Travel Rule party brings its registries, its clock, the lists it holds and (the beneficiary) its replay store and its KEM keys; a
compliance verifier brings its policy, its audience, its nonce and its clock. A compliance judgement under a policy that waives the
credential status (requireStatus: false), with no list read, is PARTIAL (claim-not-checked); a Travel Rule verdict is never
PARTIAL, since a VASP status that was not judged is a refusal there (vasp-status). An invalid presentation establishes no claim, so
the compliance verifier reports every rule of its policy not met as well (the verifier's reasons list said so before the enrolment, and
the vectors expect both); a credential from an issuer or a registry whose status list the verifier does not hold is refused for its
issuer and for its status at once.
The vectors
vectori/ holds 626 vectors (617 offline, 9 that read testnet 28001; 22 MB, since every device, identity and Travel Rule vector carries its ML-DSA-65 keys and signatures), listed with their digests in vectori/INDEX.json:
envelope 67, payment-risk 11, payment-receipt 10, market-timeline 26, dataset 22, ai-trust-bundle 24, audit-log 19, audit-inclusion
10, audit-consistency 4, agent-ledger 18, identity-presentation 69, device-bundle 194, sdjwt-presentation 54, sdjwt-vc-presentation 32, travel-rule-binding 19, travel-rule-message 26, travel-rule-receipt 9, compliance-presentation 12. By expected verdict: 87 VALID, 70 PARTIAL, 469 INVALID. None is typed by hand
(node genereaza-vectori.mjs --verifica regenerates them and compares byte for byte):
- the 17 AIP-23 kinds come from their producers on fixed inputs (the same envelopes as the published AIP-23 kind vectors;
secret-access, the audit entry of the secrets service, since 2026-10-18); the audit log, its heads and its inclusion and consistency proofs come from the verify-layer producers on fixed inputs; - the receipt, the five market timelines, the notarized dataset commitment and one trust chain of an AI result are the evidence recorded on testnet 28001 on 2026-10-02 beside those components;
surse-proaspete.jsonholds what needed a key, randomness or the chain, made once by--proaspat(which only adds what is missing). Every key was generated and discarded in that call: a tree head signed with an ML-DSA-65 key and the same head "signed" with an Ed25519 key that declares itself ml-dsa-65; an audit head signed by an operator key, by another key, and by a classical key; the ledger of an agent (a tool call, a payment, a payment refused above the limit, a tool call) with the forged siblings that only the agent's key can make (an empty approvals list, a false decision, a revocation entry without a revocation) and one written with a classical key; three trust chains of a fresh agent (allowed, refused by the policy, dated apart); the presentations of one identity credential (an issuer, a holder, a phone, a session key, a stranger and an impostor issuer: six hybrid key pairs), its status lists, its delegations and revocations, with the forged siblings that only a key holder can make (a statement changed and signed again by the issuer, the holder or a delegate) and one credential from an issuer whose "ML-DSA-65" key is an Ed25519 key; the chain of one device (a manufacturer, two owners, a second identity the first owner sells to, a stranger, another manufacturer: six hybrid key pairs; the device key and a clone's key, ML-DSA-65), sold once, sold on, and bought back, its recall lists, firmware policies and status records, with the forged siblings that only a key holder can make and a device key that is an Ed25519 key (the firmware image and its Proof of Software attestation are the fixtures of the device component;--proaspat --refa devicemakes this one source anew when the producers of the device component change, and every other vector stays byte for byte); an envelope with random content that nobody notarized; a dataset of 16 records with random salts; and the transaction of the recorded receipt read from the chain (two read calls); since 2026-10-18 also an SD-JWT and an SD-JWT VC issued with AERE keys (an issuer, a holder, a stranger: three hybrid key pairs each, made and discarded), presented to one verifier, the VC's Status List Tokens (clean, revoked, suspended, 8 bits, for another uri, expired, by a stranger, typed JWT, withoutiat, too short, ML-DSA-65) and the forged siblings only a key holder can make (a key binding JWT by a stranger or without its nonce, a digest signed twice, a nested_sd_alg, a disclosure named_sd, a claim named__proto__, an algorithm confusion, astatusbehind a disclosure, and so on); since the same day also one Travel Rule exchange (a registry, another registry, the paying VASP, the receiving VASP, a third VASP and a stranger: six hybrid key pairs made and discarded; two hybrid KEM key pairs, X25519 + ML-KEM-768, of which the receiving VASP's is kept whole in the vectors, private half included, since the beneficiary's private KEM keys are what opens a message: it was made for these vectors and opens nothing else, and it is declared test material at the scene, in the form the secret gate of this repository reads, so that the gate reports it as seen and would still block a private key without that declaration), with the forged siblings only a key holder can make, each checked at generation to be refused for exactly the rule it was made for (a stranger's keys under a VASP's id, a binding that expired or whose ML-KEM key is an X25519 key, a header addressed to another VASP, dated 400 s earlier, with another encapsulation name, a truncated ML-KEM ciphertext, a flipped ciphertext byte or another amount, each signed again by the originator, a receipt by another VASP); and one compliance exchange (an issuer, a holder, another issuer: three hybrid key pairs made and discarded; a KYC credential with six disclosable claims, the issuer's status list, a policy of four rules, and the holder's presentations: enough claims, too few, claims that break every rule, a credential from the other issuer, a revoked one);- the SD-JWT vectors have two more sources that are fixed text: the example of RFC 9901 Section 5 with the key of its Appendix A.5
(
identity/fixturi-rfc9901.json), and the tokens of@sd-jwt/core0.23.0 and@sd-jwt/sd-jwt-vc0.23.0 with the status lists of@owf/token-status-list0.5.0 (identity/interop/fixturi-interop-*.json), which the identity component derived from those libraries' real output on 2026-10-18, with their public test keys.
An INVALID vector names its base and its mutation: one operation (set, remove or add at one JSON path) applied to the input
of a vector that is not invalid, and it must be refused for exactly the rules in expected.failed, not merely refused: 303 of the
469 are made this way. Where the rule sits under a hash or a signature, one place is not reachable (changing an amount under the
instruction hash trips the binding first; a false decision in a ledger must still be signed by the agent; a disclosure the issuer never signed must still be signed for by
the holder; an authorization by a previous owner must still be signed by that owner; a digest repeated in an SD-JWT must still be
signed by its issuer, a key binding JWT for another audience by its holder; a Travel Rule header addressed to another VASP, or with a
cut ML-KEM ciphertext, must still be signed by the originator; a presentation without a request must still be signed by its holder): the other 166 are made at
the producer, the commitments above the change recomputed with the published formulas, and every path that moved is declared
(mutation.changed). Twenty-four vectors expect more than one rule: rules that guard the same field cannot be separated (a looser policy shown
under a receipt also makes the risk verdict not reproduce; a Proof of AI envelope that is changed is also no longer the one the
ledger links; a delegation chain cut after it was signed for is also not presented by its last delegate; a manufacture credential
whose signature is changed is also no longer the link the first transfer names; a clone that claims the device's id with another
key breaks both the id derivation and the match with the manufacturer's credential; a revoked VASP credential, or one from a registry
whose list the verifier does not hold, fails the VASP presentation and the VASP status at once; a message opened by another VASP is
not addressed to it and its originator's presentation was not made for it; a compliance presentation that is invalid for any reason
also establishes no claim, so its policy's rules are not met either).
Run it
AERE_VERIFY_PROOF=<verify-proof.mjs from aere.network/tools, with verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it and npm install done>
node conformance.mjs --impl "node implementare-aere.mjs" # the published verifiers behind the one verdict form: 617 offline vectors
node conformance.mjs --impl "node implementare-aere.mjs" --online # and the 9 that read testnet 28001 (nothing is sent)
node conformance.mjs --impl "python3 implementare-integritate.py" # an independent implementation: 62 vectors, 557 reported NOT COVERED
node conformance.mjs --impl "<your command>" # yours
node implementare-aere.mjs <artifact.json> [--online] # one artifact { artifact, input }: its verdict
For the artifact envelope the input is { "envelope": <any JSON value> }, or { "raw": "<the text of the file>" } when the file is
not JSON at all: a file that is not an envelope is refused at the form level, by name, like any other. The other inputs are the ones
the vectors show ({ log, head?, signer? } for an audit log, { ledger, policy } for an agent's ledger, { bundle } for a trust
chain, { presentation, now, audience?, nonce?, trustedIssuers?, statusLists?, revocations?, maxAgeS? } for a presentation of a
credential, { bundle, now, nonce?, audience?, trustedManufacturers?, statusLists?, firmwarePolicies?, statusRecords?, softwareAttestation?, anchors?, maxAgeS? } for a device bundle, { sdJwt, issuerKey, issuerAlg?, now, audience?, nonce?, expectedIssuer?, expectedTyp?, requireKeyBinding?, requireExp?, maxAgeS? } for an SD-JWT presentation and the same plus
{ expectedVct, statusListJwt?, statusListKey?, statusListAlg?, requireStatus? } for an SD-JWT VC, { binding, presentation, registries, originatorId, nonce, now, statusLists?, maxAgeS? } for a Travel Rule binding (originatorId and nonce are the
originator's own; without them the identity verifier would leave the audience or the nonce of the VASP presentation unjudged, and the
Travel Rule verifier refuses instead of accepting on trust: a defect this suite's malformed-input row found on 2026-10-18, fixed the
same day), { message, me, kem, registries, now, statusLists?, seen?, maxAgeS? } for a Travel Rule message (kem is the beneficiary's key file, aere-travel-rule-kem-keys, with
its private half; seen is the list of message ids already received, the replay store; without it nothing is opened), { receipt, message, beneficiaryId } for a Travel Rule receipt, and { presentation, policy, audience, nonce, now, statusLists?, revocations? } for
a compliance presentation (audience and nonce required, as the module requires them): now, the verifier's clock, is required for
all of them but the receipt, since a verdict on the clock of the machine that runs a vector would not reproduce).
An implementation is a command that answers three calls with JSON on stdout: --capabilities ({ artifacts, rules, online, batch }:
what it claims to judge), <vector.json> [--online] (the verdict above for the file's { artifact, input }), and, if it says
batch, --batch <list.json> (one verdict per line). A vector is scored only if the implementation claims its artifact kind and
every rule the vector exercises; otherwise it is reported NOT COVERED, never passed. A scored vector passes when the verdict, the
exact set of failed rules and the state of each of the four levels are the expected ones. No output is BROKEN: a crash is not a
refusal. Without --online an implementation must not open a connection.
implementare-integritate.py is the second implementation: Python, standard library only, written from the rules and not from the
JavaScript. It claims 6 of the 240 rules (the form and the statement hash of an envelope; the form, the commitment, the salted field
commitments and the RFC 9162 inclusion of a dataset record) and passes all 65 vectors it claims (measured 2026-10-18). It cannot check an ML-DSA signature
or read a chain, says so in its capabilities, and the runner reports the rest as not covered: 561 vectors, among them every vector of
the trust chain, the audit log, the agents' ledger, the identity presentations, the device bundles, the SD-JWT presentations, the
Travel Rule artifacts and the compliance presentations. For those ten verifiers the suite has one implementation, ours (for SD-JWT,
the identity component measured our verifier against @sd-jwt/core and @sd-jwt/sd-jwt-vc outside this suite; those libraries are
not implementations of this suite's contract).
How the suite itself is checked
Measured on 2026-10-18 (UTC), in the evening, after the identity verifier gained the rule request (a presentation committed to the
request the verifier received; the adversarial review of the secrets service) and its four vectors were derived from a new fresh
source (identityRequest; every other fresh source kept, so no other vector's input moved; the 65 earlier identity vectors changed
only in exercises, which now lists identity.request), on Node.js 24.14.1, offline, with the served reference verifier and with the
one of the development repository: node proba-conformance.mjs gives 12 rows green, 0 red, 1 not measured (the 9 vectors that read
testnet 28001). 240 rules of 15 verifiers, 626 vectors (LA ZI), every rule has a vector (240 of 240), every INVALID vector (469) is
its base with one operation (303) or one declared producer change (166), the published verifiers pass 617 of 617 offline vectors,
the independent Python implementation passes the 65 it claims and reports the other 561 not covered, 101,833 malformed inputs each got
a verdict. node proba-conformance.mjs --control-negativ (offline) gives 319 rows green, 0 red, 3 not measured (the three rules whose
vectors need the testnet): 237 of 240 rules proven load-bearing, identity.request among them (planted out, exactly its 2 vectors
fail). Before the change, the same commands on the untouched tree: 12 green, 0 red, 1 not measured; 318 green, 0 red, 3 not
measured (236 of 239). Not run after the change: --online.
Run online later the same evening (21:06-21:25 UTC), with the reference verifier as served at aere.network/tools (fetched and installed
in an empty folder) and testnet 28001 read: node proba-conformance.mjs --online gives 12 rows green, 0 red; the published verifiers
pass 626 of 626 vectors, the independent Python implementation passes the 65 it claims and reports the other 561 not covered, 101,833
malformed inputs and 183 malformed envelope files each got a verdict. node proba-conformance.mjs --control-negativ --online gives 322
rows green, 0 red: 240 of 240 rules proven load-bearing through their vectors; the other 82 rows are the conditions of the device
verifier and the controls of the instruments.
Measured again on 2026-10-18 (UTC), later the same day, after the AIP-23 kind secret-access (the audit entry of the secrets service,
pq-secrets/) was added to proof-kinds and its three envelope vectors derived (Node.js 24.14.1, the reference verifier of the
development repository, offline): node proba-conformance.mjs gives 10 rows green, 2 red, 1 not measured. The vectors on disk are what
the generator derives (622, LA ZI); the 17 kind envelopes are the published kind vectors; every rule has a vector (239 of 239); every
artifact kind has a valid vector (21); every INVALID vector (467) is its base with one operation (302) or one declared producer change
(165); the published verifiers pass 613 of 613 offline vectors; the independent Python implementation passes all 65 it claims and the
other 557 are reported not covered; 100,825 malformed inputs and 183 malformed envelope files each got a verdict. The 9 vectors that
read testnet 28001 were not run (--online). The two red rows are the derivation of the aip23-envelope rules from the verifier's code
(its finality sites, "appears on 0 FAILED lines of the verifier") and REGULI.json, and they were red before this change, on the
untouched tree, in the same run form (measured first): the reference verifier was refactored earlier that day (judge() exported,
commit b2cf499c6) and the rule derivation has not followed it yet; the kind added here does not touch them.
Measured on 2026-10-18 (UTC), after the Travel Rule verifier and the compliance verifier were enrolled, the last two of the identity component (Node.js 24.14.1; the reference verifier of the development repository; testnet 28001 read, nothing sent; the run is the one on the final code, after the three fixes below):
node proba-conformance.mjs --online: 12 rows green, 0 red. The rules and the vectors are what the code derives now: 239 rules of 15 verifiers, 619 vectors (19 of the kindtravel-rule-binding, 26 oftravel-rule-message, 9 oftravel-rule-receipt, 12 ofcompliance-presentation). Every rule has a vector that expects it (239 of 239); every artifact kind has a valid vector (21); every invalid vector is its base with one operation (300) or one declared producer change (165); the published verifiers pass 619/619; the independent implementation passes all 62 it claims and the other 557 are reported not covered; 100,825 malformed inputs given to the module verifiers each got a verdict and none threw (4,766 still accepted, at 255 distinct paths; of the four new kinds onlytravel-rule-receiptaccepts damage, at 62 paths, all of them the originator's presentation and signature inside the message handed with the receipt, which the receipt names by the hash of the signed header and the ciphertext and does not read; the binding, the message and the compliance presentation accept none); 183 malformed envelope files given to the reference verifier each got a verdict.node proba-conformance.mjs --control-negativ --online: 321 rows green, 0 red: 239 of 239 rules (each of the 23 rules of the Travel Rule verifier and the 3 of the compliance verifier planted out in turn fails exactly the vectors that expect it), 76 of 76 conditions of the device verifier, six controls of the instruments (an implementation that calls everything valid fails 578 of the 610 offline vectors).- While the enrolment was being made, the measurement found three things, each fixed before this run: the malformed-input row showed
that
acceptBeneficiarycalled withoutoriginatorIdor withoutnonceaccepted the beneficiary (the identity verifier leaves the audience or the nonce of the VASP presentation unjudged, and the Travel Rule verifier did not require them judged: it refuses now; a test of the identity component, a planting of its negative control and two vectors here); and the control showed, with a*-formrule planted out, the vector whose artifact is the JSON valuenullbreaking instead of failing, twice in the adapter (its not-reached reason read the name of a stopping rule that was not there) and once inverifyReceipt(it readacceptedfrom a statement that was not there). A crash is not a refusal, and the control counted each as broken, not as a fail.
Measured on 2026-10-18 (UTC), when the SD-JWT verifier and the SD-JWT VC profile were enrolled (Node.js 24.14.1; the reference verifier of the development repository; testnet 28001 read, nothing sent):
node proba-conformance.mjs --online: 12 rows green, 0 red. The rules and the vectors are what the code derives now: 213 rules of 13 verifiers, 553 vectors (54 of the kindsdjwt-presentation, 32 ofsdjwt-vc-presentation). Every rule has a vector that expects it (213 of 213); every artifact kind has a valid vector (17); every invalid vector is its base with one operation (249) or one declared producer change (155); the published verifiers pass 553/553; the independent implementation passes all 62 it claims and the other 491 are reported not covered; 97,966 malformed inputs given to the module verifiers each got a verdict and none threw (4,398 still accepted, at 193 distinct paths, 4 of them ofsdjwt-presentationand 2 ofsdjwt-vc-presentation: the optional inputs of the verifier, such as the issuer it expects); 183 malformed envelope files given to the reference verifier each got a verdict.node proba-conformance.mjs --control-negativ --online: 295 rows green, 0 red: 213 of 213 rules (each of the 23 rules of the SD-JWT verifier and the 9 of the VC profile planted out in turn fails exactly the vectors that expect it), 76 of 76 conditions of the device verifier, six controls of the instruments (an implementation that calls everything valid fails 512 of the 544 offline vectors). While the enrolment was being made, the control found one thing and it was fixed before this measurement: withvct-expectedplanted out, the VC verifier threw on the vector that names no expected type instead of refusing it by the next rule, which the suite reported as a broken vector, not as a refusal.
Measured on 2026-10-14 (UTC), after the third review of the device verifier (Node.js 24.14.1; the AIP-23 reference verifier fetched
from aere.network/tools with its four sibling files and npm install done in an empty folder, as a stranger would; testnet 28001
read, nothing sent):
node proba-conformance.mjs --online: 12 rows green, 0 red (in this published copy the comparison of the 16 kind envelopes with the published AIP-23 kind vectors is printed as information; the twelfth row is green where their generator is). The rules and the vectors are what the code derives now: 181 rules of 11 verifiers, 467 vectors. Every rule has a vector that expects it (181 of 181); every artifact kind has a valid vector (15); every invalid vector is its base with one operation (221) or one declared producer change (116); the published verifiers pass 467/467; the independent implementation passes all 62 it claims and the other 405 are reported not covered; 96,757 malformed inputs given to the module verifiers each got a verdict and none threw (one vector,device-bundle/chain-of-64.partial, is larger than 300,000 characters and is not damaged this way); 183 malformed envelope files given to the reference verifier each got a verdict.node proba-conformance.mjs --control-negativ --online: 263 rows green, 0 red: 181 of 181 rules, 76 of 76 conditions of the device verifier (under them 96 distinct device vectors fail, each exactly under the conditions written for it), six controls of the instruments (an implementation that calls everything valid fails 426 of the 458 offline vectors, every one that is not plainly valid).
The finer counts in the paragraphs below (how many plantings of the device verifier no vector reaches, how many device vectors the
purpose separation breaks at once, and the --pe-loc measurement) are those of the measurement of 2026-10-05; they were not counted
again on 2026-10-14.
Measured on 2026-10-05 (Node.js 24.14.1, Python 3.14.4, testnet 28001 read):
node proba-conformance.mjs --online: every row green, 11 in this copy (a twelfth, the comparison of the 16 kind envelopes with the published AIP-23 kind vectors, runs where their generator is, in the development repository: 12/12 there, and here it is printed as information). The rules and the vectors are what the code derives now; every rule has a vector that expects it (181 of 181); every artifact kind has a valid vector (15); every invalid vector is its base with one operation (209) or one declared producer change (107); the published verifiers pass 422/422; the independent implementation passes all 62 it claims; and 70,358 malformed inputs (every path of every offline vector that is not invalid removed, or replaced by null, a string, a number, a list, an object) given to the module verifiers each got a verdict, none threw. That row covers the modules of nine verifiers; the trust chain verifier is not in it (each case would run the reference verifier several times). The reference verifier, a program, has a row of its own: 183 malformed envelope files (every path of a signed tree head and of a notarized envelope removed or replaced; files that hold null, a number, a string, true, a list, an empty object, text that is not JSON, nothing) are given to it, and each gets a verdict. That row contacts no public endpoint: the program goes to the chain whenever a file claims a notarization, so a file that still carries the claim is run against a closed local endpoint and its finality is not measured. Without--onlinethe nine chain vectors are reported unmeasured and the exit code is 2.node proba-conformance.mjs --control-negativ --online: 263 rows green: 181 of 181 rules, 76 of 76 conditions of the device verifier, six controls of the instruments. Each rule is planted out of its verifier, one at a time, in a copy (every check that carries its marker made true at run time; forai-trust, its refusal helper made to drop the refusals of that rule; for the reference verifier, the level made to pass); the suite is run against the planted verifier; the vectors that expect the rule, and only they, must fail, and none may break. A rule taken out whole says nothing about each condition of a check made of several: every vector of the rule fails, whichever condition it was written for. So, for the device verifier, the conditions are then planted out one at a time: 76 of 76. The plantings are those of that verifier's own negative control, read from itsplantari-device.mjsand not typed again here (a credential that has not begun, each condition of the authorization window, the position of a transfer, the id of who gives one, the sequence and the digest the device attests, the model, the validity and the version of a policy, a date ahead of the verifier's clock, a signature in another base64 text, whose status record counts, and so on); under each, exactly the vectors written for that condition must fail (82 vectors in all). Of the 88 plantings that control makes in the verifier, 11 are reached by no vector and are not in this list: four are of the producers and two of the key files, which a vector does not exercise; four are of what the verifier names without changing its verdict, which the verdict form does not carry; and one empties the list of causes that keep a verdict fromVALID, under which the adapter refuses to run at all (every device vector breaks, none passes). One more, the purpose separation of signatures, fails 123 of the 149 device vectors at once and is not a condition of one check. The other verifiers have no such list: for them the control shows that each rule is load-bearing, and--pe-locbelow measures their checks. Then six controls of the instruments: an implementation that calls everything valid fails every vector that is not plainly valid (381 of the 413 offline vectors); the coverage gate goes red when one rule's vector is taken away; a rule added to a verifier and not mapped is refused by the derivation; a judge that throws again turns the malformed-input row red; a reference verifier that throws again turns the malformed-envelope row red; no planted copy is left behind.node proba-conformance.mjs --pe-locis a measurement, not a gate: each of the 209 marked checks of the module verifiers planted out alone (the trust chain verifier has no per-check markers and is not in this count). For 174 of them a vector then fails (all 40 checks of the identity verifier and all 47 of the device verifier among them). The others are checks whose vector trips a second check of the same rule at the same time, so removing one alone changes nothing (the three checks ofmarket.replay, the three ofagents.anchor, the run and neighbour checks ofdataset.completeness), or checks no vector reaches (a dispute timed out before the arbitration timeout; five of the six checks ofagents.time). The control above shows that each rule is load-bearing, not each of its checks.
The suite found defects in the verifiers it enrols, on 2026-10-03, while they were being enrolled; all are fixed and each is now a vector or a probe row:
- three verifiers accepted a classical key under a post-quantum name. The AIP-23 reference verifier reported a signature that
declared
ml-dsa-65and was made with an Ed25519, Ed448, RSA or P-256 key assignature PASSED: ml-dsa-65: verifies; the audit log verifier accepted a head "signed" the same way as a head signed with ML-DSA-65; the agents' ledger verifier did not require the agent key to be an ML-DSA-65 key, so the ledger of a "post-quantum identity" could be written and signed with an Ed25519 key and verify. In each, the key was never required to be a key of the scheme declared. Each is now a refusal by name (envelope/signed-head.signature-classical-key,audit-log/signed-head.head-scheme-classical-key,agent-ledger/four-entries.key-classical). The audit head has a second vector since 2026-10-05,audit-log/signed-head-any-signer.head-scheme-classical-key, the same head with no signer expected: with the scheme rule taken out of the verifier, the first is still refused, for its signer, and this one is accepted; - the reference verifier exited with an exception on a file holding the JSON value
null, let a statement that is a list pass its form level, and reported a malformed ML-DSA signature (no key, a key that cannot be parsed) as unmeasured "needs OpenSSL 3.5" on a runtime that has it; each is now a refusal by name, and a file that is not JSON is refused at the form level instead of ending the run; - a malformed payment made the risk module throw instead of refusing by name; malformed dataset answers made the dataset verifier throw;
- the market judge accepted a timeline from which the submission event had been withheld, which skipped the challenge-window rule
(the judge now requires every event the job state implies:
market.complete); - the market judge took the time an execution envelope was notarized from the envelope's own claim, so a claim written earlier than
the truth passed. The time now comes from the reference verifier's reading under the anchor, and the claim is only compared with
it (
market-timeline/s1-28001.proof-claim-early).
On 2026-10-05, while the identity verifier was being enrolled, one more, fixed the same day:
- a fault of the issuer's keys was also blamed on the holder. When the issuer keys in a credential could not be read (for
example an Ed25519 key in the field of the ML-DSA-65 key), the identity verifier refused the credential, as it must, and also
reported that the holder id is not the id of its keys: the two ids were derived in one step, and the first failure left the
second underived. The verdict was
INVALIDeither way; the list of rules named one that had not been broken. Each id is now derived on its own, and the vectoridentity-presentation/direct.issuer-classical-keyexpects exactly the two rules of the issuer. That vector also measures that the identity verifier never accepted a classical key in the ML-DSA-65 field: its key type was checked before this suite.
What it is not, and what is not measured
- Not a certification, not an audit, not a mark. Passing says that an implementation agrees with the published verifiers on these 626 inputs. It does not say either of them is correct, and no third party has reviewed the verifiers, the vectors or this suite.
- The vectors and the verifiers have the same authors. The expected verdicts were written by the people who wrote the verifiers. The second implementation is independent in code and language, not in authorship, and it covers 6 rules of 240; the other 234 rules, among them every rule of the trust chain, the audit log, the agents' ledger, the identity verifier, the device verifier, the SD-JWT verifiers, the Travel Rule verifier and the compliance verifier, are judged here by one implementation.
- Finality is the chain's. The level says that the post-quantum certificate of the covering anchor verified on the chain where the artifact was notarized. Every notarized vector here is on chain 28001, a testnet; nothing in this suite ran on mainnet.
- Five of the six finality refusals have no vector. The reference verifier can refuse at its finality level in six places; the vectors reach one (a notarization claimed that the chain does not hold). The others need an endpoint that lies or an anchor that does not verify: they are exercised by that verifier's own probe, through a local proxy that lies to it, not by these vectors.
- The chain vectors depend on the testnet's state. The notary contract is open: anyone can notarize the fresh envelope's hash on
28001, and
envelope/fresh-28001.*then change verdict. The runner would show it;--proaspatmakes a new one. - Integrity is over the evidence supplied. A market timeline and the transaction facts of a receipt are the caller's reading of the chain; this suite does not certify that reading under an anchor. The market judge now refuses a timeline that lacks an event the job state implies; it does not prove that the job state shown is the chain's.
- Offline, a notarization time is only a claim. With the chain,
market.prooftakes the time an execution envelope was first seen from the reference verifier's reading under the anchor and refuses a claim that differs from it. Offline there is no such reading: the claim is all there is, the verdict isPARTIAL, and acertifiedfield that an artifact brings itself is dropped. - Authenticity is what each verifier checks. The reference verifier checks ML-DSA signatures on envelopes, the audit log verifier the
ML-DSA-65 signature of a head, the ledger verifier the agent's ML-DSA-65 key and signatures, each with a key that must be of the
scheme declared; the identity verifier the hybrid Ed25519 + ML-DSA-65 signatures of a credential, a presentation and each
delegation link (both halves, each with a key of its type). A hybrid signature on an AIP-23 envelope is still reported
unmeasured by the reference verifier, and the verdict is
PARTIAL. - Most times in a trust chain and in a ledger are claims. A trust chain without a witness is
PARTIAL: every time in it is the statement of whoever holds the keys. Three trust-chain vectors read a ledger head notarized on testnet 28001. No vector gives an agent's ledger an anchor that verifies:agents.anchoris reached only through an anchor that is refused, and five of the six checks ofagents.timehave no vector. - An identity verdict is the verifier's. A presentation is judged on the clock, the audience, the nonce, the trusted issuers, the
status lists and the revocations in the vector input: what a verifier brings decides the verdict.
VALIDfor a delegated presentation says that no revocation handed to the verifier has taken effect, not that none exists; a revocation that cannot be attributed to the giver of the link or to the holder is ignored; a credential that names no status list cannot be revoked by its issuer and is stillVALID. Every time in these vectors is the statement of a key holder: no vector notarizes a credential or a delegation. Nothing here says who an issuer is in the world. - A device verdict is the verifier's too, and the device key is a software key. A bundle is judged on the clock, the nonce, the
audience, the trusted manufacturers, the recall lists, the firmware policies, the status records and the readings in the vector
input.
VALIDsays that every link was brought and that what was read revokes nothing, not that nothing was withheld. No vector, and nothing in the device verifier, checks a hardware root: a device key is a key in software, and an attestation says what the holder of that key claims to run. A Proof of Software attestation handed with a bundle is checked for being the one the release names and for listing the image digest; its own signature is not verified there. An owner's retirement of a device does not survive a sale: after a transfer it is named and not applied (the manufacturer's is final). A retirement by an owner that holds across a sale needs a witness of when it and the transfer were signed, which is not built; every time in a transfer, an authorization and a status record of these vectors is the statement of the key holder that signed it. - What a damaged artifact can still pass. Of the 100,825 malformed inputs (measured 2026-10-18), 4,766 were still accepted, at 255 distinct paths (76 of them in device bundles, 62 in Travel Rule receipts: the originator's presentation and signature inside the message handed with a receipt, which the receipt does not read; on 2026-10-14 the count was 96,757 inputs, 4,280 accepted at 187 paths; the kinds that follow were listed on 2026-10-05, when the count was 81 and the device verifier had not had its third review: a status record that decided nothing, such as a suspension already lifted, a stranger's record, a record not in effect yet or one of a previous owner; a recall list or a firmware policy that could not be read and stood beside a clean one, which damaged is no longer signed by the manufacturer and counts like one that was not handed; the envelope and its notarization claim, which are optional; and the signature of a Proof of Software attestation, which the device verifier does not read): parts a
verifier treats as optional (the transaction facts of a receipt, the envelope of a timeline, the head of an audit log), a policy
field removed that falls back to its default, a log or a ledger without its last entry (still a valid shorter one), a revocation that was not in effect (damaged, it is
ignored as before), and fields no rule reads
(
node proba-conformance.mjs --acceptatelists them). A policy that cannot be defined makes the risk module throw, by design. Of the 183 malformed envelope files given to the reference verifier (the probe's own row, measured at every run), 16 are still calledVALID: an envelope with its signature or its notarization claim taken off (an unsigned, unnotarized envelope), and an envelope whose outervorkindwas damaged. Those two fields are outside the statement hash and the reference verifier does not read them; the statement carries its ownkind, which is under the hash. (This file used to quote 526 malformed envelopes, a figure of 2026-10-03 from a run whose script was not kept; the row above replaces it.) - Not part of AIP-23. The rule ids, the four levels and the verdict form are this suite's reading of the published verifiers; AIP-23 defines the envelope. The device verifier is enrolled, and so are all five identity verifiers published beside these: the presentation verifier, the SD-JWT verifier and the SD-JWT VC profile, the Travel Rule verifier and the compliance verifier (the last two since 2026-10-18).
- A Travel Rule verdict is the receiving party's, and the vectors carry one test decryption key. A binding, a message or a receipt
is judged with what the originator or the beneficiary brings: the registries it trusts, the status lists it holds, its clock, its
nonce, its replay store, its KEM keys.
VALIDfor a message says that it opened for this beneficiary, with these keys, on this clock, that it was not in the replay store handed, and that the originator's VASP status was judged on the lists handed; it does not say who a registry is in the world, nor that the IVMS101 inside is true or complete (the module does not validate the schema). The beneficiary's KEM key pair in the message vectors is test material made at generation, declared so at the scene, and opens only the messages of these vectors; nothing else in this suite holds a private key. The record of an exchange (an AIP-23complianceenvelope without personal data) is judged as the artifactenvelope, not here. - A compliance verdict is the verifier's policy applied to one presentation. It says that the presentation was valid for that verifier and that the claims it established satisfy the policy's rules, with the status judged on the lists handed when the policy requires it. It is not a zero-knowledge proof (a shown claim is shown whole), not an AML or sanctions screening, and not legal compliance with anything; a policy that cannot be defined is the verifier's error (the module throws, by design), not a verdict.
- The SD-JWT vectors say what two libraries agree on, on these inputs. The tokens of
@sd-jwt/coreand@sd-jwt/sd-jwt-vcin the SD-JWT vectors are the fixtures the identity component derived from those libraries' real output on 2026-10-18 (their versions are in the vector sources); the expected verdicts are those of our verifiers on them, which the identity component measured against the libraries' own verdicts in both directions. A verifier that resolves the issuer key fromissorx5c, fetches the status list or the type metadata, or takes an untyped token by default, is judged here on inputs where all of that is already brought. - The Python implementation writes numbers the way JavaScript does only for integers and plain decimals, and sorts keys by code point (JavaScript's order for keys in the Basic Multilingual Plane).