Test Vectors (v0.1)
Reference test vectors for implementers of the LLMO v0.1 signing and canonicalization requirements.
This page describes the test vectors for implementers of the LLMO specification. Vectors exercise the signing and canonicalization requirements of §4 of the spec: JWS with ES256 over a JCS-canonicalized payload (RFC 7515, RFC 8785).
Entity convention
All test vectors use a fictional entity with the codename JungleCat:
- Name: JungleCat, Inc.
- Primary domain:
junglecat.example.com
The .example.com subdomain is reserved by RFC 2606 and is guaranteed never to resolve on the public internet. This makes it safe to use as a static fixture: no implementer will ever fetch a real document from this domain, and no legitimate organization can claim it.
Document ID convention
Test vector documents use document_id values of the form test-vector-v0.1-<tier>-<sequence>, where <tier> is minimal, standard, or strict, and <sequence> is a zero-padded three-digit number. Additional vectors added to this directory follow the same pattern with the next sequence number.
Validity window
Test vectors use a fixed 180-day validity window, matching the Standard-tier cap in §5.2 exactly:
valid_from:2026-04-20T00:00:00Zvalid_until:2026-10-17T00:00:00Z
Info: Test vectors are static fixtures. After
valid_until, a conforming validator SHOULD flag these documents as expired. This is expected behavior and a useful conformance check: a validator that does not flag an expired document is incorrect.
Coverage matrix
The vector set targets every spec rule v0.1 defines. Two reference implementations evaluate vectors: the in-browser validator at /validator/ (source: static/js/validator.js) and the llmo CLI. The two diverge on which rules they enforce; the table records the actual coverage rather than the spec aspiration. Drift cells are followed up under Drift findings.
| Rule | Section | validator.js | CLI | Positive vector | Negative vector(s) |
|---|---|---|---|---|---|
| M3 schema validity | §3.1 | yes | yes | all positives | negative-schema-malformed-claim-type.json, negative-schema-malformed-founded.json, negative-schema-bad-llmo-version.json |
| M5 window <= 365 days | §5.1 | yes | yes | all positives | negative-m5-window-over-365.json, edge-validity-366-days.json |
| S1 has canonical_urls | §5.2 | yes | yes | unsigned-standard.json |
negative-s1-missing-canonical-urls.json |
| S2 has official_channels | §5.2 | yes | yes | unsigned-standard.json |
negative-s2-missing-official-channels.json |
| S3 primary_domain matches serving | §5.2 | yes (URL mode) | yes (URL mode) | unsigned-standard.json (URL mode) |
none (paste-mode-only set) |
| S4 URL ownership | §5.2 | yes | yes | unsigned-standard.json |
negative-s4-third-party-canonical-url.json, negative-s4-third-party-product-url.json, negative-s4-third-party-supersedes-url.json |
| S5 window <= 180 days | §5.2 | yes | yes | unsigned-standard.json |
negative-s5-window-181-days.json |
| S6 disavowal/supersedes scope | §5.2 | no | no | edge-disavowal-impersonation-defense.json |
negative-s6-disavowal-third-party.json |
| X1 signature structurally valid | §5.3, §4.3 | yes | collapsed into “signature valid” | signed-strict.json, signed-strict-es384.json, signed-strict-eddsa.json |
negative-x1-bad-alg.json, negative-x1-missing-kid.json, negative-x1-malformed-protected.json |
| X1 §4.3.1 detail (b64:false, crit) | §4.3.1 | yes | yes | n/a | negative-x1-detached-payload-b64-false.json, negative-x1-crit-non-empty.json |
| X2 JWKS retrievable | §5.3 | URL mode only | URL mode only | (live deploy) | (live deploy) |
| X3 JWKS Cache-Control <= 86400 | §5.3 | URL mode only | URL mode only | (live deploy) | (live deploy) |
| X4 canonical_urls reference | §5.3 | yes | yes | signed-strict.json |
negative-x4-no-owned-canonical-url.json |
| X5 document signature verifies | §5.3, §4.3 | yes (with JWKS) | yes (with JWKS) | signed-strict.json |
negative-x5-corrupted-signature.json |
| X6 per-claim signatures verify | §5.3 | yes (with JWKS) | yes (with JWKS) | signed-strict.json (no per-claim sigs, trivially passes) |
negative-x6-bad-claim-signature.json |
| W1 window 181-365 days warning | §5.4 | yes | not emitted | none (warning is conditional) | warning-w1-200-day-window.json, edge-validity-365-days.json |
| W2 spokesperson missing verification | §5.4 | yes | not emitted | edge-spokesperson-with-verification.json (no W2) |
warning-w2-spokesperson-no-verification.json |
A standalone Standard-tier positive vector is not added: per §5, Strict ⊂ Standard ⊂ Minimal, so each Strict vector is also Standard-conforming, and each signed-strict-*.json exercises the Standard-tier rules in addition to the Strict-tier rules. The unsigned variant is unsigned-standard.json.
A reproducible verification harness lives at scripts/test-vectors/verify-vectors.mjs (CLI behavior) and scripts/test-vectors/verify-schema.mjs (canonical-schema validation). Both run as node scripts/test-vectors/<file> from the repo root and exit non-zero on any drift from the documented expectations.
Files
Positive vectors
unsigned-minimal.json
A minimally conforming llmo.json. Exercises the minimal tier (§5.1): required top-level fields, no claims required, no signature.
Raw: /spec/v0.1/test-vectors/unsigned-minimal.json
unsigned-standard.json
A standard-conforming llmo.json. Exercises the standard tier (§5.2): minimal plus at least one canonical_urls claim and at least one official_channels claim. No signature.
Raw: /spec/v0.1/test-vectors/unsigned-standard.json
signed-strict.json
A strictly conforming llmo.json. Exercises the strict tier (§5.3): standard plus a valid document-level ES256 JWS signature.
Raw: /spec/v0.1/test-vectors/signed-strict.json
signed-strict-key.json
The JWKS containing the public key that verifies signed-strict.json. The kid is the RFC 7638 JWK thumbprint of the public key and matches the kid in the JWS protected header of signed-strict.json.
The corresponding private key was generated ephemerally during test vector production and was not retained. The public key here is the only material required to verify the signed vector.
Warning: The keys in this JWKS are test-only. They MUST NOT be used in production.
Raw: /spec/v0.1/test-vectors/signed-strict-key.json
signed-strict-payload.json
The exact UTF-8 bytes of the JCS-canonicalized payload that was signed (that is, the JWS signing input per RFC 7515 §5.1). This file has no trailing newline. Implementers use it to validate their JCS implementation independent of their JWS implementation.
Raw: /spec/v0.1/test-vectors/signed-strict-payload.json
Note: The file is served with a
.jsonextension. The content is a valid canonical JSON object by construction (that is what JCS produces), so the.jsonextension is accurate. Implementers compare the file byte-for-byte; the extension does not change the comparison.
signed-strict-es384.json, signed-strict-es384-key.json, signed-strict-es384-payload.json
A second strict-tier vector signed with ES384 (ECDSA over P-384, SHA-384) instead of ES256. Exercises the same strict-tier rules as signed-strict.json but with the second algorithm permitted by §4.2. document_id is test-vector-v0.1-strict-002. The JWKS, payload, and verification procedure follow the same conventions as the ES256 vector. Test-only key, MUST NOT be used in production.
Raw: signed-strict-es384.json, signed-strict-es384-key.json, signed-strict-es384-payload.json
signed-strict-eddsa.json, signed-strict-eddsa-key.json, signed-strict-eddsa-payload.json
A third strict-tier vector signed with EdDSA (Ed25519). Exercises the third algorithm permitted by §4.2. document_id is test-vector-v0.1-strict-003. The JWK uses kty: "OKP" and crv: "Ed25519" (no y field, unlike the EC P-256 / P-384 vectors). Signature length is 64 bytes (raw Ed25519, not r||s). Test-only key, MUST NOT be used in production.
Raw: signed-strict-eddsa.json, signed-strict-eddsa-key.json, signed-strict-eddsa-payload.json
v0.1.8 positive vectors
These vectors exercise the six new core claim types added in v0.1.8 plus a comprehensive vector that combines entity.name internationalization, structured external_ids with the new irs_ein well-known key, provenance_markers on the claim envelope, all five new top-level fields (revocation_registry, dns_corroboration, publication_history, delegates_to, delegated_from), and the canonical_urls / product_facts / identity extensions. All are unsigned (Minimal-tier) so they isolate schema validation from the cryptographic layer.
unsigned-contact-points.json
A contact_points claim with one verified entry (security@junglecat.example.com, email_challenge method, proof and timestamp present per the v0.1.8 schema-encoded conditional) and one unverified entry (press@junglecat.example.com). Demonstrates the if/then conditional constraint that requires verification_proof and verified_at when verification_status is verified. Raw: unsigned-contact-points.json.
unsigned-categories.json
A categories claim with primary https://schema.org/Restaurant, one secondary schema.org subtype, and one NAICS code (722511). Demonstrates the v0.1.8 category vocabulary (schema.org Organization subtypes plus NAICS; no trademarked third-party taxonomies). Raw: unsigned-categories.json.
unsigned-locations.json
A locations claim with one entry: postal address (US/CA/San Francisco), WGS84 coordinates, a radius_km-based service_area, business_type: customer_location, and a per-location publisher_id. Demonstrates the location structure and the service_area oneOf branch for radius shape. Raw: unsigned-locations.json.
unsigned-hours.json
An hours claim exercising every v0.1.8 hours feature: a regular weekly schedule with split shifts on Friday; the 24:00 close on Thursday (permitted by the HH:MM pattern as an end-of-day boundary); an overnight period (is_overnight: true) on Friday; a calendar exception for 2026-12-25 (closed); and an alternate.brunch sub-schedule. Raw: unsigned-hours.json.
unsigned-attributes.json
An attributes claim with boolean values (wifi: true, accepts_credit_cards: true, outdoor_seating: false), enum values (parking: lot, alcohol_served: full_bar, dress_code: casual), array values (payment_methods, spoken_languages, accessibility_features), and one namespaced extension (myco.signature_dish). Demonstrates the canonical vocabulary plus the namespaced-extension fallback. Raw: unsigned-attributes.json.
unsigned-operational-status.json
An operational_status claim with status: opening_soon, an effective_date of 2026-06-01 (required by the schema-encoded conditional for any non-open status), and a reason string. Raw: unsigned-operational-status.json.
unsigned-v0.1.8-comprehensive.json
A single document exercising every other v0.1.8 schema addition together: entity.name array form with three locales and exactly one primary: true; entity.external_ids with plain-string wikidata and duns plus structured irs_ein (the new v0.1.8 well-known key); provenance_markers on the identity claim; all five new top-level fields (revocation_registry, dns_corroboration, publication_history, delegates_to, delegated_from); the four new canonical_urls well-known keys (appointment, menu, reservations, order); product_facts per-product extensions (kind, price, description, category); and identity.price_range. Raw: unsigned-v0.1.8-comprehensive.json.
Negative vectors
These vectors exercise specific failure modes. Each vector identifies the rule it targets in its filename. Naming convention: negative-<rule>-<descriptor>.json. Each vector is documented with its expected validator output: in-browser validator first, llmo CLI second.
Standard-tier negatives
negative-s1-missing-canonical-urls.json
A document with no canonical_urls claim. Validator reports S1 FAIL; CLI reports tier minimal with rule has canonical_urls claim. Targets §5.2 S1.
negative-s2-missing-official-channels.json
A document with no official_channels claim. Validator reports S2 FAIL; CLI reports tier minimal with rule has official_channels claim. Targets §5.2 S2.
negative-s4-third-party-canonical-url.json
canonical_urls.docs resolves to https://docs.thirdparty.example.org/junglecat, off the publisher’s owned domain set. canonical_urls.* is not in the §5.2 third-party-allowed list. Validator reports S4 FAIL; CLI reports tier minimal with rule all claim URLs resolve to owned domain or are third-party pointers.
negative-s4-third-party-product-url.json
A product_facts claim with products[0].url = https://marketplace.thirdparty.example.org/.... Like canonical_urls.*, product_facts.products[].url is not in the §5.2 third-party-allowed list. Both validator and CLI report S4 FAIL; CLI tier drops to minimal.
negative-s4-third-party-supersedes-url.json
A supersedes claim with superseded[0].url on a third-party domain. supersedes.superseded[].url is not in the §5.2 third-party-allowed list. Both validator and CLI report S4 FAIL; CLI tier drops to minimal.
negative-s5-window-181-days.json
valid_from to valid_until is exactly 181 days, one day past the §5.2 S5 cap. Both validator and CLI report S5 FAIL and tier minimal. Note: this vector also triggers W1 (the warning fires for any window in 181 to 365 days; see warning-w1-200-day-window.json for an isolated W1 demo).
negative-s6-disavowal-third-party.json
A disavowal claim targeting a competitor’s product on https://competitor.example.org/. The disavowal is neither a publisher self-statement nor an impersonation defense, putting it outside the §5.2 S6 scope. Neither validator nor CLI enforces S6 in v0.1.5; the vector exercises spec text and is filed as drift.
Strict-tier negatives
negative-x1-bad-alg.json
JWS protected header declares alg: "RS256", outside the §4.2 allowed set (ES256, ES384, EdDSA). Validator reports X1 FAIL with the specific alg message; CLI collapses X1 and X5 into a single signature valid failure and reports tier standard.
negative-x1-missing-kid.json
JWS protected header decodes to {"alg":"ES256"} with no kid. Validator reports X1 FAIL (“missing kid”); CLI reports signature valid failure and tier standard.
negative-x1-malformed-protected.json
signature.protected is base64url-valid but does not decode to JSON (the bytes spell not-actually-json-content). Validator reports X1 FAIL (“does not decode to valid JSON”); CLI reports signature valid failure and tier standard.
negative-x1-detached-payload-b64-false.json
JWS protected header includes b64: false and crit: ["b64"], the RFC 7797 detached-payload mode that §4.3.1 explicitly prohibits for v0.1. Validator reports X1 FAIL with the b64:false-specific message; CLI rejects via lib/jws.ts and reports signature valid failure with tier standard.
negative-x1-crit-non-empty.json
JWS protected header includes a non-empty crit array (crit: ["unknown-ext"]). §4.3.1 requires verifiers to reject any JWS whose crit parameter is non-empty for v0.1. Validator reports X1 FAIL with the crit-non-empty message; CLI rejects via lib/jws.ts and reports signature valid failure with tier standard.
negative-x4-no-owned-canonical-url.json
A signed document whose canonical_urls claim has an empty statement object. The doc passes S1 (claim is present) and S4 (no URLs to validate) but fails X4 (no canonical_urls URL on the owned domain). Validator reports X4 FAIL; CLI reports a strict-tier failure with rule canonical_urls claim has owned-domain URL (alongside the signature valid failure for the unverifiable placeholder signature). Tier drops to standard.
negative-x5-corrupted-signature.json
A clone of signed-strict.json with the first base64url character of the document-level signature flipped from j to k. The protected header and JWKS reference are unchanged. Verifies against the existing signed-strict-key.json JWKS but fails cryptographically: validator reports X5 FAIL, CLI reports signature valid failure with signatureValid: false. Tier drops to standard.
negative-x6-bad-claim-signature.json and negative-x6-bad-claim-signature-key.json
A standard-conforming document with a disavowal claim carrying a per-claim ES256 signature. The per-claim signature byte was corrupted (first character s to t) before the document-level signature was applied, so the document-level signature verifies (X5 PASS) while the per-claim signature does not (X6 FAIL). Tier drops to standard. The accompanying JWKS holds the public key for both signatures.
Schema and minimal-tier negatives
negative-schema-malformed-claim-type.json
Includes a claim with type: "noNamespace", which matches neither the reserved core types (eight in v0.1, fourteen as of v0.1.8) nor the namespaced extension pattern (per §3.6 and the v0.1.4 schema fix). Schema validation against /spec/v0.1/schema.json fails. CLI reports tier invalid.
negative-schema-malformed-founded.json
statement_identity.founded is set to "yesterday". The v0.1.4 schema fix added a pattern (YYYY, YYYY-MM, or YYYY-MM-DD); "yesterday" violates it. Both the canonical schema and the CLI’s vendored schema reject this vector; the CLI re-vendored in cli PR #1 (2026-05-07) and reports tier invalid. The verify-schema.mjs harness also fails the vector against the canonical schema.
negative-schema-bad-llmo-version.json
llmo_version is "0.2", violating the schema const: "0.1". Both schema and CLI catch this; CLI reports tier invalid.
negative-m5-window-over-365.json
A 400-day validity window (2026-04-20 to 2027-05-25). Schema does not constrain window length; the M5 tier rule does (§5.1: window MUST NOT exceed 365 days). CLI reports tier invalid with rule window <= 365 days.
v0.1.8 schema negatives
Each vector exercises one conditional constraint encoded in the v0.1.8 schema (via if/then or contains/minContains/maxContains) or a required-field omission. All ten fail canonical-schema validation via scripts/test-vectors/verify-schema.mjs. Validator and CLI tier-rule output is independent of the schema check; the conditional constraints fire as AJV errors regardless of which tier the document otherwise targets.
negative-schema-contact-points-verified-no-proof.json
A contact_points entry with verification_status: "verified" and verified_at but no verification_proof. The schema’s if/then on the contact point requires both verification_proof and verified_at when verification_status is verified; omitting either fails. Raw: negative-schema-contact-points-verified-no-proof.json.
negative-schema-contact-points-verified-no-timestamp.json
The companion to the prior vector: verification_status: "verified" with verification_proof but no verified_at. Same if/then clause; same failure mode. Raw: negative-schema-contact-points-verified-no-timestamp.json.
negative-schema-operational-status-no-effective-date.json
An operational_status claim with status: "permanently_closed" and no effective_date. The schema’s if/then on operational_status requires effective_date whenever status is not open. Raw: negative-schema-operational-status-no-effective-date.json.
negative-schema-entity-name-no-primary.json
entity.name is an array of two locale entries with neither marked primary: true. The schema’s contains constraint with minContains: 1 requires at least one primary entry. Raw: negative-schema-entity-name-no-primary.json.
negative-schema-entity-name-two-primary.json
entity.name is an array of two locale entries with both marked primary: true. The schema’s contains constraint with maxContains: 1 requires at most one primary entry. Raw: negative-schema-entity-name-two-primary.json.
negative-schema-external-id-bad-wikidata-pattern.json
entity.external_ids.wikidata uses the structured form with value: "not-a-Q-id". The schema’s per-key constraint on wikidata enforces the ^Q[0-9]+$ pattern on the value field of the structured form (preserving the v0.1 pattern when the structured form was added in v0.1.8). Raw: negative-schema-external-id-bad-wikidata-pattern.json.
negative-schema-external-id-verified-no-proof.json
entity.external_ids.irs_ein uses the structured form with verification_method: "registry_lookup" but neither verification_proof nor verified_at. The schema’s if/then on structured_external_id requires both fields whenever verification_method is not none. Raw: negative-schema-external-id-verified-no-proof.json.
negative-schema-categories-no-primary.json
A categories claim with secondary and naics but no primary. The schema requires primary on every categories claim. Raw: negative-schema-categories-no-primary.json.
negative-schema-hours-malformed-time.json
An hours claim with regular.monday[0].open: "25:00". The HH:MM pattern accepts 00:00 through 23:59 for open; 25:00 violates the pattern. (The pattern accepts 24:00 only for close.) Raw: negative-schema-hours-malformed-time.json.
negative-schema-attributes-bad-value-type.json
An attributes claim with a numeric value ("numeric_field": 42). The attributes open map accepts only boolean, string, or array-of-strings values via additionalProperties: { "oneOf": [...] }. A number does not match any branch. Raw: negative-schema-attributes-bad-value-type.json.
Warning-triggering vectors
These vectors are conformant but trigger a discouraged-practice warning in the validator. Warnings are surfaced separately from tier evaluation per §5.4.
warning-w1-200-day-window.json
A 200-day validity window. The window exceeds the 180-day Standard cap (S5 FAIL, doc tier is Minimal) but is within the 365-day Minimal cap, so the validator emits W1. The CLI does not emit warnings; it reports the S5/M5 outcome only.
warning-w2-spokesperson-no-verification.json
A standard-conforming document with a personnel.spokespeople[0] entry missing the verification URL. The document remains Standard-tier conformant; the validator emits W2 advising publishers to add a verification URL. The CLI does not emit W2.
Edge-case vectors
These vectors exercise boundary conditions in the spec. Each is documented with the boundary it tests and its expected outcome.
edge-validity-365-days.json
Validity window is exactly 365 days, the upper bound of M5. M5 PASS, S5 FAIL (>180), W1 emitted. Tier minimal.
edge-validity-366-days.json
Validity window is 366 days, one day past M5. M5 FAIL. Tier invalid. Documents the M5 boundary on the failing side.
edge-claim-type-namespaced.json
A standard-conforming document containing an extension claim with type: "junglecat.internal_compliance", exercising the §3.6 namespaced pattern. Schema validates the type via the second branch of the v0.1.4 oneOf. The validator and CLI both ignore the unknown extension type (per the §3.6 forward-compatibility rule); tier standard.
edge-disavowal-impersonation-defense.json
A disavowal claim with what: "unaffiliated_domain" targeting a domain shaped to imply affiliation with the publisher. The disavowal is in scope per §3.5 and S6 (impersonation defense). Tier standard.
edge-spokesperson-with-verification.json
A personnel.spokespeople[0] entry with verification: "https://github.com/junglecat-ceo", a third-party URL explicitly permitted by §5.2 S4 (personnel.spokespeople[].verification is in the third-party-allowed set). Tier standard. No W2 because verification is present.
Drift findings
The vector set surfaced four classes of drift between spec text and reference implementations. Each is filed in the project BACKLOG. Vectors covering drift cases are kept in the set so the drift is detectable by anyone running the harness; if the implementations are later patched to match spec, the harness expectations update.
-
S6 (disavowal/supersedes scope) is unimplemented in both validator.js and the CLI. Vectors:
negative-s6-disavowal-third-party.json, andedge-disavowal-impersonation-defense.jsonas a positive control. Spec rule landed in v0.1.2; reference implementations were not updated. -
§4.3.1
b64:falseand non-emptycrit(resolved). Vectors:negative-x1-detached-payload-b64-false.json,negative-x1-crit-non-empty.json. Spec text under §4.3.1 is normative. The CLI enforces this insrc/lib/jws.ts; the validator now enforces it in the X1 structural check (PRfix/validator-jws-rfc7797). Vectors retained so the harness can detect any future regression. -
CLI does not enforce S4 (URL ownership) or X4 (canonical_urls reference) (resolved). Vectors: the three
negative-s4-*.jsonandnegative-x4-no-owned-canonical-url.json. Both rules were noted as “informational” insrc/lib/tier.ts; validator.js already enforced them. The CLI now ports the validator’s S4 (collectClaimUrls+ third-party-allowed field list + owned-domain check) and X4 (canonical_urls must have at least one owned-domain URL) intosrc/lib/tier.ts(cli PR #5, merge SHAb407061fad9af9533bda0a917b27a8906c6cf0da). Vectors retained so the harness can detect any future regression. -
CLI vendored schema lags canonical schema (resolved). Vector:
negative-schema-malformed-founded.json. The v0.1.4 schema additions (foundedpattern,claim.typeoneOf) were missing fromcli/src/schema/v0.1.json; canonical-schema validation viaverify-schema.mjsrejected the vector while CLI accepted it. The CLI re-vendored the schema in cli PR #1 (2026-05-07); both the canonical schema and the CLI vendored schema now reject"yesterday". Vector retained so the harness can detect any future regression.
Verifying the signed vector
- Fetch the JWKS:
signed-strict-key.json. - Locate the key by matching the
kidin the JWS protected header ofsigned-strict.jsonto thekidin the JWKS entry. - Remove the
signaturefield fromsigned-strict.json. Preserve all other fields and their order as provided. - Canonicalize the remaining document per RFC 8785 (JCS).
- Verify the ES256 signature from the
signaturefield over the canonical bytes using the public key from the JWKS.
A conforming implementation MUST verify the signature successfully at step 5.
Verifying a JCS implementation independently
- Read
signed-strict.json. - Remove the
signaturefield. - Canonicalize the remainder using the implementation under test.
- Compare the output byte-for-byte against
signed-strict-payload.json.
The two must be identical. If they differ, the implementation is not RFC 8785 conformant.
Versioning
These vectors correspond to LLMO v0.1 (llmo_version: "0.1"). Future specification versions may introduce new vectors under sibling directories (e.g., /spec/v0.2/test-vectors/). The vectors in this directory are stable and will not be modified after publication. Corrections, if required, are published as sibling files with a new sequence number and documented in the changelog.
Key Transparency (LIP-4) vectors
Vectors for LIP-4 Key Transparency entries live under /spec/v0.1/test-vectors/kt/. Each vector is a JSON envelope of the form:
{
"lip": "LIP-4 §3.2",
"kind": "positive" | "negative",
"error_code": "<code>",
"description": "<plain-English>",
"expected": "accepted" | "rejected:<error_code>",
"entry": "<compact-JWS-string>"
}
A conforming KT registry MUST accept positive vectors at POST /kt/v1/entries (assuming the registry’s clock is consistent with the vector’s observed_at; the timestamp tolerance check is the one validation step that depends on wall-clock state). A conforming registry MUST reject negative vectors and return the named error_code.
Positive vectors:
| File | Algorithm |
|---|---|
kt/positive-entry-es256.json |
ES256 |
kt/positive-entry-es384.json |
ES384 |
kt/positive-entry-eddsa.json |
EdDSA |
Negative vectors (one per error code defined in /spec/v0.1/kt-registry-endpoints/):
| File | Error code |
|---|---|
kt/negative-thumbprint-mismatch.json |
thumbprint_mismatch |
kt/negative-kid-mismatch.json |
kid_mismatch |
kt/negative-wrong-typ.json |
wrong_typ |
kt/negative-jwk-contains-private-material.json |
jwk_contains_private_material |
kt/negative-signature-invalid.json |
signature_invalid |
kt/negative-doc-url-mismatch.json |
doc_url_mismatch |
kt/negative-timestamp-out-of-range.json |
timestamp_out_of_range |
kt/negative-missing-payload-field.json |
missing_payload_field |
kt/negative-invalid-domain.json |
invalid_domain |
kt/negative-unsupported-alg.json |
unsupported_alg |
Index at /spec/v0.1/test-vectors/kt/index.json.
The vectors were generated by scripts/test-vectors/generate-kt-vectors.mjs using a synthetic test publisher identity (example.com, kid example-publisher-2026-01). ECDSA signatures in the positive vectors are non-deterministic; re-running the generator produces different signature bytes for the positive vectors but the same expected acceptance behavior. The committed vectors are the authoritative reference suite.