ContextFile
— credits
Docs · versioned with the code · HTML, markdown and llms.txt

Built for agents to read first.

Verify it yourself

Every proof ContextFile issues uses a public format, so you can check it with tools we did not write. You need curl, jq and the tool named in each step. You never need ContextFile code, an account, or to trust our servers beyond fetching the files.

1. Find the keys

Each purpose has its own Ed25519 key, published in a key set. A rotated key stays listed with status: "retired" and its valid_to date, so what it signed before rotation still checks.

Key setSigns
https://contextfile.ai/.well-known/report-keysreports and their attestation bundles
https://contextfile.ai/.well-known/receipt-keys.jsonreceipts
https://contextfile.ai/.well-known/receipt-anchor-keys.jsondaily receipt tree heads (Rekor statement and witness checkpoint)
https://contextfile.ai/.well-known/did.jsonclaim credentials and status lists (did:web:contextfile.ai, Multikey)

Each entry has key_id, public_key (32 raw bytes, base64url) and, for report keys, pem, which is what cosign reads.

2. Check a report with cosign

Each report is an in-toto Statement v1 whose subject is the SHA-256 of the artifact file and whose predicate type is https://contextfile.ai/verification-report/v1. The statement sits in a DSSE envelope signed with the report key, delivered as a Sigstore bundle at /reports/<digest>/attestation.sigstore.json. The report page shows these commands with the digest and key id filled in.

curl -sSo attestation.sigstore.json 'https://contextfile.ai/reports/<digest>/attestation.sigstore.json'
KEY_ID=$(jq -r .verificationMaterial.publicKey.hint attestation.sigstore.json)
curl -sS https://contextfile.ai/.well-known/report-keys \
  | jq -r --arg k "$KEY_ID" '.keys[] | select(.key_id == $k) | .pem' > report-key.pem
cosign verify-blob-attestation \
  --new-bundle-format --bundle attestation.sigstore.json \
  --key report-key.pem \
  --type https://contextfile.ai/verification-report/v1 \
  ./artifact-file

cosign checks the signature against the key and that the file's SHA-256 is the statement subject. A report whose owner put it on the registry is also logged in the public Rekor instance, and its bundle carries the log entry. A bundle without one (the report is not public on the registry, or not logged yet) needs --insecure-ignore-tlog=true. Change one byte of the payload and the check fails.

To read what the report checked and what it does not cover:

jq -r .dsseEnvelope.payload attestation.sigstore.json | base64 -d | jq .predicate.body.scope

The predicate holds reportDigest, reportUri, the report key's own signature (reportSignature) and the full signed body. Recompute the digest yourself: SHA-256 of the body as JSON with object keys sorted at every level. The JSON Schema is at /schemas/verification-report-v1.json.

3. Check an OCI image

Images built by the platform are signed keyless by the GitHub Actions workflow that built them, and carry SLSA provenance, an SPDX SBOM and the report attestation, all attached with cosign.

slsa-verifier verify-image '<image>@sha256:<digest>' --source-uri github.com/<org>/<repo>
cosign verify-attestation --key report-key.pem \
  --type https://contextfile.ai/verification-report/v1 '<image>@sha256:<digest>'
cosign verify-attestation --type spdxjson \
  --certificate-identity-regexp '^https://github.com/<org>/<repo>/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com '<image>@sha256:<digest>'

slsa-verifier prints the source commit the image was built from.

4. Check a receipt against an outside log

Once a day ContextFile builds a Merkle tree (RFC 6962) over the receipts it issued the day before. The tree head is posted to AI Source of Truth, which belongs to the same company, and anchored outside it:

  • Rekor first. The tree head, as an in-toto statement with predicate type https://contextfile.ai/receipt-tree-head/v1 signed with the receipt anchor key, is logged in the public Rekor instance.
  • A witness when Rekor will not take it. The day's checkpoint becomes one leaf of the contextfile.ai/receipt-days log, whose C2SP checkpoint the anchor key signs and an independent witness co-signs, after checking it only grew since the size it last co-signed.

GET /receipts/<id>/proof?hash=<receipt_hash> returns outside_anchor once the day is logged. To check it:

  1. Walk outside_anchor.receipt_proof.audit_path from the receipt's leaf hash (SHA-256 of 0x00 and the 32 bytes of receipt_hash) to a root. It must equal outside_anchor.tree_head.root_hash.
  2. Rekor path: check the DSSE envelope against the anchor key set, and that its statement's subject digest is that root. Then look up the entry and check it is the same envelope:

``sh rekor-cli get --log-index <outside_anchor.rekor.logIndex> --format json ``

  1. Witness path: check that day_record_hash is in the receipt-days log (receipt_days.audit_path to receipt_days.root_hash), that receipt_days.note names that size and root and carries a signature by the anchor key's note_vkey, and a cosignature by the witness key you trust.

The receipt page shows where each receipt's day is anchored.

5. Check a claim as a Verifiable Credential

Any verified claim of a public report exports as a W3C Verifiable Credential 2.0:

curl -sSo claim.vc.json 'https://contextfile.ai/reports/<digest>/claims/<claim-id>/credential'

The issuer is did:web:contextfile.ai, which resolves to https://contextfile.ai/.well-known/did.json. The proof is a DataIntegrityProof with cryptosuite eddsa-rdfc-2022, so any VC library that supports W3C Data Integrity checks it, for example Digital Bazaar's @digitalbazaar/vc with @digitalbazaar/eddsa-rdfc-2022-cryptosuite. The subject holds claim_id, text_hash (SHA-256 of the claim text, not the text itself), source_locator, entailment and report_digest; its terms are defined in the credential's own context, so a verifier fetches nothing but the W3C context and the DID document.

6. Check whether a claim was recalled

Each credential has a credentialStatus entry of type BitstringStatusListEntry pointing at a W3C Bitstring Status List: statusListCredential is the list URL (/status-lists/<YYYY-MM>, one list per month of export) and statusListIndex is the claim's bit. When errata correct a claim, its bit is set. The list is itself a credential signed by the same DID, so check its proof first, then read the bit:

curl -sSo list.vc.json 'https://contextfile.ai/status-lists/<YYYY-MM>'
node -e 'const l = require("./list.vc.json").credentialSubject.encodedList;
  const bits = require("zlib").gunzipSync(Buffer.from(l.slice(1), "base64url"));
  const i = Number(process.argv[1]);
  console.log((bits[i >> 3] >> (7 - (i & 7))) & 1 ? "revoked" : "not revoked");' <statusListIndex>

@digitalbazaar/vc-bitstring-status-list does both steps. Lists can be cached for the ttl they carry. The errata ledger links every list.

7. Check Content Credentials on an image or PDF

Images and PDFs ContextFile delivers carry a C2PA manifest with an ai.contextfile.report assertion: report_digest, report_url, key_id (the report key that signed the report) and, for a listing, license_url. Open the file in any Content Credentials viewer (contentcredentials.org/verify) or run c2patool <file>, then check the report itself with section 2. PDFs also carry ContextFileReportDigest and ContextFileReportURL in their document information, so the digest is there even where a viewer does not read C2PA in PDFs yet.

8. Read the license terms

Each listing page embeds its terms in the RSL format (application/rsl+xml) and links the same file at /listings/<id>/license.xml. The terms combine the licenses of every source the report cites with the listing's own license; the most restrictive wins, so one non-commercial source makes the whole listing non-commercial.

For agents: sign your requests

Report, status list, listing and download paths accept Web Bot Auth: HTTP Message Signatures (RFC 9421) with tag="web-bot-auth", an Ed25519 key from your signature directory (Signature-Agent), and @authority covered. A signed request from an allowed directory is served without a bot challenge and carries x-contextfile-signed-agent: verified in the response. Unsigned requests are served as before, and agents signed in with a token are never challenged.

Older reports

Reports signed before attestations shipped carry only the original signature: an Ed25519 signature by the report key over the report digest string. Check it with the key's public_key and any Ed25519 library, after recomputing the digest from the body as above. These reports have no scope block. A worker job builds bundles for them, so most will also have an attestation bundle.

Version 1, updated 2026-11-01.

This page as markdown · llms.txt