How grading works
Every check result is deterministic: given the same directory and response headers, the checker always produces the same verdict and grade.
3-state verdict
The verdict has exactly three possible values:
- VALID - the directory exists, every Ed25519 key has a 32-byte
x, and no private key material (d) is present. The verdict does not depend onkid: akidthat is not the key thumbprint raises an advisory warning but never makes a directory invalid. - INVALID - a directory was found at the well-known path, but at least one critical check failed: private
dpresent, a malformed Ed25519 key (xnot 32 bytes), or the Ed25519 signature does not verify. A key of another type or curve does NOT make the directory invalid: the drafts permit other registered algorithms, so such keys only raise advisory warnings. - NOT FOUND - no usable directory at the well-known path: non-2xx HTTP response, response body is not JSON, the key set is empty, or the host is unreachable (including DNS failure).
Letter grade
The letter grade (A through F) is only shown when a directory exists (verdict is VALID or INVALID). There is no letter grade for NOT FOUND.
The grade is derived from a weighted score that starts at 100 and subtracts points for each failed or warned check.
Scoring weights
| Tier | Fail deduction | Warn deduction |
|---|---|---|
| JWKS directory | -25 per fail | -6 per warn |
| RFC 9421 signature | -30 per fail | -8 per warn |
| Agent Card (advisory) | -6 per fail | -2 per warn |
Letter thresholds
| Letter | Minimum score |
|---|---|
| A | 90 or above |
| B | 75 to 89 |
| C | 60 to 74 |
| D | 40 to 59 |
| F | below 40, or capped (see below) |
Grade capping rules
Certain critical failures impose a hard ceiling on the grade regardless of the raw score:
- Private key material present in the directory - grade is capped to F.
- Ed25519 signature fails to verify - grade is capped to a maximum of D.
- Directory not served over HTTPS - grade is capped to a maximum of D.
Agent Card checks are advisory
Checks in the Agent Card tier nudge the score and can raise warnings, but they never change the verdict and never trigger a grade cap. A missing or incomplete Agent Card will lower the score slightly; it will not cause a VALID directory to become INVALID.
What is checked
JWKS directory checks
- Directory is served over HTTPS
- Directory URL returns a 2xx status (a 403 bot-protection challenge gets its own message)
- Directory is served directly with HTTP 200, without a redirect (advisory: verifiers do not follow redirects and accept only 200, so a redirect or another 2xx status warns)
- Response media type. The web-bot-auth type (
application/http-message-signatures-directory+json) passes. Another valid JWKS type such asapplication/jwk-set+jsonor a genericapplication/jsonis accepted but raises a warning, because web-bot-auth asks for its own media type. A non-JSON type such astext/html, or a missing media type, is a failure. - Response body is valid JSON
- Key set has the expected shape (
keysarray) - Key set is non-empty
- Key set size is within reasonable bounds
- Caching headers are present
- Directory response self-signature: the draft recommends the response carries one signature per key (tag
http-message-signatures-directory), covering"@authority";reqandcontent-digest. Absent warns, valid withoutcontent-digestwarns, valid with a matching digest passes, present-but-broken fails. Advisory either way: never a cap, never a verdict change. - Per-key:
kty=OKP(advisory: another type warns, because the drafts permit other registered algorithms while Ed25519 remains the deployed baseline) - Per-key:
crv=Ed25519on OKP keys (advisory: another curve warns) - Per-key:
xis 32 bytes (base64url-encoded), evaluated on Ed25519 keys - Per-key: the RFC 7638 thumbprint is computed and shown. This is the value a signature’s
keyidmust equal. At the well-known path akidis optional, but when present the protocol draft requires it to be the thumbprint, so akidwith any other value warns (advisory). Keys withoutkidpass. - Per-key: no private key material (
dnot present) - Per-key: not a published test key, such as the RFC 9421 example keys (advisory: a match warns)
- No duplicate
kidvalues in the set - Validity window (if present) is plausible
RFC 9421 signature checks
Signature-Agentheader present and well-formed: a missing or malformed header, or a value that is not an https URI, fails; the legacy string form, adirectorymember that is not an origin, and an unknowntypewarn (the legacy form warns rather than fails because some deployed verifiers accept only that form)Signature-Inputparses correctly- Covered components include
@authorityor@target-uri, plussignature-agentwhenever the header is sent (a missing header fails the Signature-Agent check instead) - Required signature parameters present
tag=web-bot-authcreatedandexpireswithin allowed clock skew- Validity window within the draft-recommended 24 hours (advisory)
keyidresolves to a key in the directory- Ed25519 signature verifies against the resolved key (a signature made with another registered algorithm warns as “not verifiable by this checker yet” instead of failing)
- Replay note: informational, passes with or without a
nonce(the protocol draft adds no nonce requirement)
Agent Card checks (advisory)
Both card shapes are read: registry-03, with the bot-specific fields inside a web_bot_auth object, and the older flat registry-02 shape.
client_namepresentclient_uripresentclient_id, when present, equals the URL the card was fetched fromcontactspresent- Key material: an https
jwks_urior inlinejwks(both together fail, still without a cap or verdict change) purposepresent- RFC 9309 robots.txt compliance declaration
- Rate expectation declared