Content Attestation
The Problem
Section titled “The Problem”At negotiation time, the Exchange describes content by metadata: title, tokens, URI, pricing. At delivery time, the agent receives bytes from the CDN. Nothing cryptographically ties the negotiated metadata to the delivered content — unless someone pre-committed verifiable claims about those bytes.
The Exchange does not ingest or deliver content. It holds catalog metadata only. The CDN delivers directly to the agent. Content attestation solves this by having a trusted party sign claims about the content before negotiation.
Three Verification Levels
Section titled “Three Verification Levels”| Level | Attester | Trust Model | Auto-Disputable Claims |
|---|---|---|---|
| 0 — None | Nobody | No attestation | CDN delivery failure only (4xx/5xx) |
| 1 — Self-attested | Provider | Provider signs own claims (Ed25519) | Delivery failure + content hash mismatch |
| 2 — Third-party | Verification vendor | Independent measurement | Delivery failure + token count discrepancy (with CDN corroboration) |
Content identifiers (DOI, ISBN, IPTC GUID) are orthogonal to attestation level. A Level 0 article may carry a DOI — that identifies what content it is but does not verify that the delivered bytes match.
ResourceAttestation Message
Section titled “ResourceAttestation Message”message ResourceAttestation { string verifier = 1; string keyid = 2; google.protobuf.Timestamp attested_at = 3; string uri = 4; google.protobuf.Struct claims = 5; string signature = 6;}| Field | Description |
|---|---|
verifier | Domain of the attesting party. Its attestation-signing key lives in the verifier’s WBA directory (the JWK Set at https://{verifier}/.well-known/http-message-signatures-directory); role=ROLE_PUBLISHER when self-attesting, ROLE_EXCHANGE for a standalone vendor |
keyid | RFC 7638 thumbprint of the verifier’s attestation-signing key in its WBA directory. Enables key rotation |
attested_at | When the attester measured the content. Agents use this for freshness checks |
uri | Content URI this attestation covers. Must match the Offer URI |
claims | JSON claims about the content (max 4KB). See Claims Vocabulary |
signature | Ed25519 over JCS-canonicalized (RFC 8785) representation of all other fields |
Example: Provider Self-Attestation (Level 1)
Section titled “Example: Provider Self-Attestation (Level 1)”{ "verifier": "nytimes.com", "keyid": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k", "attested_at": "2026-03-18T09:00:00Z", "uri": "https://nytimes.com/2026/03/18/tech/ai-content-licensing.html", "claims": { "content_hash": "sha256:a1b2c3d4e5f6...", "hash_method": "sha256", "estimated_quantity": 3200, "word_count": 2424, "language": "en" }, "signature": "base64-encoded-ed25519-signature"}Example: Third-Party Vendor Attestation (Level 2)
Section titled “Example: Third-Party Vendor Attestation (Level 2)”{ "verifier": "doubleverify.com", "keyid": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k", "attested_at": "2026-03-18T08:45:00Z", "uri": "https://example-news.com/article/breaking-story", "claims": { "estimated_quantity": 2450, "language": "en", "iab_categories": ["News", "Technology"], "content_hash": "doubleverify-v1:x9y8z7...", "hash_method": "doubleverify-v1" }, "signature": "base64-encoded-ed25519-signature"}Verifier Infrastructure
Section titled “Verifier Infrastructure”Attestation signing keys do not live in fora.json. Every party that signs attestations publishes its keys in its WBA directory — the JWK Set served at:
https://{verifier-domain}/.well-known/http-message-signatures-directoryThis is the same WebBotAuth directory endpoint FORA uses for request-signing keys. An attestation’s keyid is the RFC 7638 thumbprint of the key in this directory; a verifier rotates keys by adding new entries to the directory, and the thumbprint in keyid selects which one signed a given attestation. The fora.json manifest still carries role and routing metadata, and the claims schema (what claim fields the verifier attests to) lives in ext["fora.attestation.claims_schema"] — but the keys themselves are in the WBA directory.
FORA has no dedicated verifier role: a verifier publishes under its operator’s role — ROLE_PUBLISHER when a publisher self-attests, ROLE_EXCHANGE for a standalone verification vendor.
WBA Directory (verifier keys)
Section titled “WBA Directory (verifier keys)”A self-attesting publisher’s attestation-signing key, served from its WBA directory. The attestation’s keyid is this key’s RFC 7638 thumbprint (kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k):
// GET https://nytimes.com/.well-known/http-message-signatures-directory// Content-Type: application/jwk-set+json{ "keys": [ { "kty": "OKP", "crv": "Ed25519", "use": "sig", "alg": "EdDSA", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", "not_before": "2026-01-01T00:00:00Z", "not_after": "2026-12-31T23:59:59Z" } ], "revocation_url": "https://nytimes.com/.well-known/fora-invalidations.json"}Manifest Format (verifier)
Section titled “Manifest Format (verifier)”A self-attesting publisher’s /.well-known/fora.json (role=ROLE_PUBLISHER) carries role, domain, and the claims schema — but no keys:
{ "ver": "1.0", "role": "ROLE_PUBLISHER", "domain": "nytimes.com", "ext": { "fora.attestation.claims_schema": { "content_hash": { "type": "string", "description": "SHA-256 hash of delivered content bytes" }, "estimated_quantity": { "type": "integer", "description": "Estimated consumption quantity computed by NYT CMS" }, "language": { "type": "string", "description": "ISO 639-1 language code" } } }}Key rotation follows the JWKS pattern: new keys are added to the WBA directory with overlapping not_before/not_after windows (half-open [not_before, not_after)); old keys are retained for verification until all signed attestations using them have expired from catalogs.
Claims Vocabulary
Section titled “Claims Vocabulary”| Claim | Type | Auto-Disputable? | Description |
|---|---|---|---|
estimated_quantity | integer | Yes (with CDN corroboration) | Estimated consumption quantity (tokens, pages, seconds, etc.) |
word_count | integer | No | Word count (~estimated_quantity * 0.76 for text) |
language | string | No | ISO 639-1 language code |
iab_categories | string[] | No | IAB Content Taxonomy 3.1 codes |
content_hash | string | Yes (Level 1 only) | Hash in method:hexdigest format |
hash_method | string | No | Algorithm for content_hash |
title | string | No | Article headline |
Only estimated_quantity and content_hash are auto-disputable — they are objectively measurable and independently verifiable. Vendors may add proprietary claims (e.g., brand_safety, sentiment) documented in their manifest’s ext["fora.attestation.claims_schema"].
Signature Computation
Section titled “Signature Computation”- Build a JSON object with the five signed fields:
verifier,keyid,attested_at,uri,claims - Apply JCS canonicalization (RFC 8785) — lexicographic key sorting, no whitespace, ECMAScript number format
- Sign the canonical bytes with Ed25519
canonical_bytes = utf8_encode(jcs_output)signature = ed25519_sign(canonical_bytes, private_key)attestation.signature = base64_encode(signature)Multiple Attestations
Section titled “Multiple Attestations”An offer may carry multiple attestations from different parties:
| Agent Trust Policy | Behavior |
|---|---|
| Trust provider only | Verify provider attestation (Level 1), ignore vendor |
| Trust vendor only | Use vendor attestation (Level 2), ignore provider |
| Trust both, prefer vendor | Vendor’s estimated_quantity for unit cost calculation, provider’s content_hash for delivery verification |
| Require both | Only transact if both are present. Highest assurance |
Freshness
Section titled “Freshness”The protocol does not mandate a maximum attestation age. Suggested thresholds:
| Content Type | Max Attestation Age |
|---|---|
| Breaking news | 1—4 hours |
| Daily news | 24 hours |
| Evergreen articles | 7—30 days |
| Academic papers | 90+ days |
Next Steps
Section titled “Next Steps”- Dispute Resolution — what happens when attestation claims don’t match delivery
- Provider Onboarding — how to publish signing keys and push attestations
- Proto: FORA v1 — full
ResourceAttestationmessage reference