# JWS policy One policy for every signed artifact in the demo: request envelope, quote, mandate, receipt, key attestation, ledger signature, checkpoint, checkpoint receipt, scorecard, record `sig`. It is the one the seller enforces (`src/jws.mjs`, agent-bench 76baafc) and the one every other implementation is held to. ## Rules | | Rule | On violation | |---|---|---| | `alg` | ES256 (ECDSA P-256, SHA-256) only. Fixed by the verifier **per artifact type**, never read from the header and honoured. | `header_forbidden` | | `kid` | Mandatory, and equal to the RFC 7638 thumbprint of the verifying key. Keys are resolved from the key set discovered by domain and nothing else. A key set that names a key by any other label is refused even though the signature would verify. One exception, stated 2026-09-10: the **x5c form of the key attestation** carries no `kid`, because its signer is named by the certificate chain and verified against the CA in the bundle, not by a key set entry (the seller decodes it with `requireKid: false`); the JWT-SVID form of the same attestation keeps `kid` = thumbprint. | artifact's own invalid code (e.g. `authorization_invalid`) | | `jku`, `x5u`, `jwk`, `x5t`, `x5t#S256` | Rejected, not ignored: key material carried inside a token is what a rogue supplies to make its own signature verify. | `header_forbidden` | | `x5c` | Rejected everywhere except the buyer's **key attestation**, where it carries the X509-SVID chain and must chain to the CA in the buyer's published bundle. | `header_forbidden` | | `crit` | Rejected. A verifier that does not implement an extension must refuse a token that marks it critical; refusing all of them is the same rule with nothing to forget. | `header_forbidden` | | `typ` | Fixed per artifact: `request+jwt`, `quote+jwt`, `mandate+jwt`, `receipt+jwt`, `key-attestation+jwt`, `key-revocation+jwt` (signed like an attestation, the second place x5c is allowed), `checkpoint+jwt`, `checkpoint-receipt+jwt`, `scorecard+jwt`. The self-issued authorization and the JWT-SVID carry `JWT`. An artifact of one kind cannot be presented as another. | `typ_mismatch` | | signature | Compact serialization; R‖S, 64 bytes. Tried only under the key the `kid` names: with a mandatory `kid`, a signature that verifies under some other key is a misnamed key, a fault, not a success. | `signature_invalid` | Key sets: RFC 7517 JWK Set, public members only (`kty`, `crv`, `x`, `y`, `kid`, `alg`, `use`), served over HTTPS on a DNSSEC + DANE anchored name. The seller's are at `/.well-known/jwks.json` (authorizations), `/.well-known/spiffe-bundle.json` (SVID issuer, `use: jwt-svid`), `https://dns-aid./.well-known/dns-aid-jwks.json` (record signature). The authority's is `https:///.well-known/jwks.json` (mandates and its ledger); the verifier's is `https:///.well-known/jwks.json` (scorecard and, today, the witness receipts). Stated 2026-09-11, decided 2026-09-12: the seller fetches a mandate issuer's key set over HTTPS without requiring DNSSEC or DANE on the issuer's name (`src/authority.mjs`); its trust in the issuer comes from the `MANDATE_ISSUERS` allow-list, and the anchor of the issuer's name is proved by the verifier, not by the seller. The exception is listed below. ## Exceptions, stated - **The mandate issuer's key set** (checks 4 and 10): fetched over HTTPS, trusted by the `MANDATE_ISSUERS` allow-list, a merchant policy, as a shop chooses which card networks it accepts; the CA is what the fetch relies on. The seller does not prove the anchor of the issuer's name because it cannot: its runtime (a Cloudflare Worker) never sees the certificate a peer presents, so it could not compare a TLSA association even if it resolved one, and the running merchant resolves no DNS by design (it is the resource discovered, not a party that discovers). A resolver on the path of every checkout, for a check the seller could only half do, would add a failure the buyer pays for: on 2026-09-11 the public resolver answered SERVFAIL for the authority's name for minutes after its DS was published, which check 4 would have turned into `authority_unavailable` for nothing. The anchor is the verifier's to prove, on every run: `authority_dane` (a `3 1 1` association from an authenticated answer against the SPKI the authority serves) and `authority_ledger` (the authority's chain signed by a key in that DANE-fetched set); both PROVED since the run of 2026-09-11 23:09Z, which joined a purchase under a mandate issued after the DS (`5dddbf88…`, mandate `c3629784…`). - **Rented authorization server** (the human-connector path, not the demo path): it names its keys as it likes and may sign RS256, so `kid` = thumbprint and ES256-only do not apply there. The forbidden-header rule does. - **Record `sig`** (DNS-AID `key65405`): verified by the buyer with the reference implementation, which accepts ES256 only and matches on `kid`. The seller's current `kid` is `dnsaid-2026-09`, not a thumbprint; renaming it is a production identity change and is scheduled together with the ANS re-registration decision. ## Vectors `vectors/negative/R-21.json` (`alg: none`), `R-22.json` (HS256 with the public key as the secret), `R-23.json` (`jku` pointing at a rogue key set): each carries the token, the key set it was presented against and the expected reason code. `vectors/positive/` carries one signed example per artifact the seller issues today (quote, receipt, ledger entry), and the two mandates the authority issued (`mandate-authority*.json`), each with the verifying key, so a second implementation can verify them before it verifies anything live.