@delegus/conformance
v0.4.0
Published
Delegus v0.2 conformance vectors (spec §15 step 2) and runner: 40 portable /verify cases with expected checks and byte-exact receipts, plus the htu, JCS and jti normalization sets. Runs against @delegus/core or any implementation. Also ships the shared MC
Readme
@delegus/conformance
The Delegus v0.2 conformance vectors (spec §15 step 2) and a runner. Thirty-six
portable /verify cases — three ALLOW paths and one DENY vector for every
reason code in §5.2 and §5.3, plus vectors that pin implicit readings — each
carrying the Grant, Proof, Action, relying-party DID, clock, Delegus test
keys, profile and trust-configuration bytes, the service state the three
seams observe, the expected checks, and the exact receipt the reference
engine produces. Three normalization sets cover htu (§4.3), RFC 8785 JCS
of Actions, and the jti format (§4.2).
Fixed test keys (Ed25519 seeds 0x01…0x07), a fixed clock
(2026-09-10T12:00:00.000Z) and non-production identifiers only
(did:web:acme.example, did:web:test.delegus.example). Every artifact is
reproducible byte for byte on any platform.
Run against the reference engine
npx @delegus/conformanceExit 0 when all 40 vectors pass with byte-identical receipts, every committed
receipt re-verifies offline, and the three sets pass. --json prints the
full report; --only <id,id> runs a subset; --dir <path> points at another
cases/ + sets/ directory.
Run against your implementation
Export an Implementation (see src/implementation.ts): evaluate(input)
returns the decision, reason, layer results and per-check outcomes computed
from the vector's input alone (no I/O — the DID document, status-list
credential and replay state are in input.state). Supply normalizeHtu,
canonicalize + sha256, and isJti to run the sets; omitted parts are
reported as skipped. Return a receipt only if you sign receipts with the
test key (seed 0x06, kid from input.service.receipt_keys); it is then
compared byte for byte.
npx @delegus/conformance --impl ./my-implementation.jsProgrammatic: runConformance({ implementation, only, dir }) → Report.
Run against an HTTP endpoint (any language)
An implementation in any language can be run through one HTTPS endpoint that
speaks the wire contract (src/http.ts, delegus-conformance-http v1). Every
request is a JSON POST carrying "v": 1 and "vectors" (the vector-set hash):
| op | request | answer |
|---|---|---|
| describe | — | {"ops": ["evaluate", …]}, or {"unsupported": true} if you do not implement it (a 404 fails) |
| evaluate | "input": <vector input> | the Evaluation (decision, reason, protocol_result, trust_result, protocol_checks, trust_checks, optionally receipt) |
| normalizeHtu | "inputs": [string, …] | {"results": [string \| null, …]} |
| canonicalize | "inputs": [JSON text, …] | {"results": [string, …]} |
| sha256 | "inputs": [string, …] | {"results": ["sha256:<hex>", …]} |
| isJti | "inputs": [string, …] | {"results": [boolean, …]} |
It fails closed. Every op, describe included, must answer HTTP 200; an op
you do not implement answers {"unsupported": true}. Only that, or an op
missing from a describe answer, skips a set. A 404, any other non-200, a timeout, a body
that is not JSON, the wrong number of results or a result of the wrong type
fails that set, and evaluate is never optional. canonicalize receives the
JSON text so your own parser handles the numbers. The committed-receipt
check always runs in the runner against @delegus/core, never on your
endpoint. A full run is about forty requests.
Test your endpoint locally, then have Delegus run it:
npx @delegus/conformance --endpoint https://verifier.example/conformancePOST https://api.delegus.ai/conformance/runs {"endpoint": "https://verifier.example/conformance"}
GET https://api.delegus.ai/conformance/runs/{run_id}The hosted run needs any live Delegus API key. It sends at most 50 requests
over at most 2 minutes, one run at a time per account, only to that one
public https URL. The report is pinned to the vector-set hash, which you can
recompute with vectorSetHash(). Reports are unsigned. If your engine is
JavaScript or TypeScript, answer(implementation, body) serves the contract
for you.
The v0.3 set (release candidate, not published)
src/vectors-v3/ holds the v0.3 set for the delegus-base-v3 profile (spec §15.1): 42 case vectors and the commitment framing set (sets/commitment.json). The v0.2 set above is unchanged, byte for byte, and keeps its own vector-set hash; a v0.3 implementation must pass both.
npx delegus-conformance --v3 # against @delegus/core
npx delegus-conformance --v3 --impl ./my-impl.js # exports an ImplementationV3What the 42 cases cover:
- Seller-side actions:
commerce:quote,commerce:acceptandcommerce:fulfil, allowed and denied; a v0.3 action type under the v0.2 profile isGRANT_MALFORMED. - Action fields: each malformed
relies_onform, and atransactionmember (a handle is an output only), isACTION_MALFORMED. - Dependencies: one case per dependency check, the revoke worked example, several entries at once, the relying party's
requires, the order against the actor's own reasons, and a resubmission after a dependency denial (PROOF_REPLAYED). - Budgets: a spend, an exhausted budget, the last headroom lost to a concurrent request, the spend store down, a missing snapshot, and no spend on any denial.
- Size: 8 dependencies, and a worst case with every variable-length field at its cap, both within the 4,096-byte signer limit.
Reason codes new in v0.3, all in receipts: AUTHORITY_EXHAUSTED (budget, P21 or T7) and the seven dependency reasons DEPENDENCY_NOT_DECLARED, DEPENDENCY_COMMITMENT_MISMATCH, DEPENDENCY_RECEIPT_INVALID, DEPENDENCY_NOT_ALLOW, DEPENDENCY_TRANSACTION_MISMATCH, DEPENDENCY_AUTHORITY_EXPIRED and DEPENDENCY_AUTHORITY_REVOKED (D1 to D7). Receipts are delegus-receipt-v2: the matched authority is pinned by a commitment, opened by the authority_opening returned beside the receipt.
An ImplementationV3 provides evaluateV3(input), returning the decision, reason, checks, the per-dependency results it returns beside the receipt (relies_on) and how many times it called its spend (consume_calls). It can also provide commit(tag, nonce, body), decodeOpening and isTransactionHandle to run the framing set. Each committed receipt is checked for its signature, the signer limit, relies_on_hash, and a transaction handle on ALLOW only. --endpoint does not run the v0.3 set yet.
Vector format
See src/format.ts. state describes what the three service seams observe
(issuer after P1, status_list before P6, replay after P1–P14) as they
were before the request. A third-party implementation reproduces
expected from input; an implementation of the reference receipt format
reproduces receipt exactly.
The canonical copies live in packages/core/test/vectors; this package
ships a verified copy (npm run vectors:sync, checked by the test suite).
