Register a signed statement, get a receipt and a permanent page
AI Source of Truth runs as a SCITT Transparency Service. Send a COSE_Sign1 signed statement over SCRAPI and get back a COSE receipt that any SCITT verifier can check offline with our log key. Each registered statement also gets the same permanent page every record gets. The receipt proves the statement is in the log. The page says what it is.
Base https://aisourceoftruth.com
| ROUTE | METHOD | WHAT IT DOES |
|---|---|---|
| /entries | POST | Register a signed statement (Content-Type application/cose) with a Tier-2 token. 201 with the receipt and Location, or 202 with Location and Retry-After. |
| /entries/{entry_id} | GET | The COSE receipt (200), 204 with Retry-After 5 while no checkpoint covers it yet, or 404. The entry id is the sha256 of the statement bytes. |
| /.well-known/scitt-keys | GET | The COSE Key Set receipts verify with (application/cbor). |
| /.well-known/scitt-keys/{kid} | GET | One COSE key by its base64url kid, or 404 "No such key". |
| /.well-known/transparency-configuration | GET | Discovery: registration path, receipt format, keys and payload rules. CBOR, or JSON on Accept: application/json. |
| /r/{entry_id} | GET | The permanent record page every registered statement gets, with errata, lineage and sealing on top. |
Configuration: https://aisourceoftruth.com/.well-known/transparency-configuration. Spec: RFC 9943 (architecture), draft-ietf-scitt-scrapi-11 (SCRAPI), RFC 9942 (COSE receipts), RFC 9995 (hash envelope).
The payload is the hash of your content, never the content
- Register a publisher and its key (POST /v1/publishers). The COSE kid is that key id, <publisher>#<version>.
- Build a tagged COSE_Sign1 with alg, kid and CWT claims (15) iss and sub in the protected header, and an empty unprotected header.
- Put the hash of your content in the payload, never the content: a hash envelope, or a hashes-only JSON agent receipt.
- POST it to /entries with a Tier-2 Bearer token from /oauth/token.
curl -X POST https://aisourceoftruth.com/entries -H 'content-type: application/cose' -H 'authorization: Bearer <tier-2 token>' --data-binary @statement.cose -o receipt.cose
- 201 Created: the body is the COSE receipt; Location is /entries/{entry_id}.
- 202 Accepted: no checkpoint covered it within the wait; poll Location after Retry-After (5 s).
- 4xx: Concise Problem Details (application/concise-problem-details+cbor) with the SCRAPI title, for example Malformed request or Bad Signature Algorithm.
- Anonymous submit is refused: there is no private or unsigned ingest path.
| PROFILE | HEADER | ACCEPTS | MAX BYTES |
|---|---|---|---|
| Hash envelope (RFC 9995) | 258 payload_hash_alg, optional 259 preimage content type and 260 location | payload is the artifact digest: sha-256 (-16) | 65,536 |
| JSON agent receipt | 3 content type | application/json, application/agent-receipt+json, application/vnd.aisot.agent-receipt+json; hashes, identifiers, signatures and URLs only | 16,384 |
Anything else in the payload is content and is refused.
RFC 9942, RFC9162_SHA256
{ 1: -8 (EdDSA), 4: kid, 15: { 1: issuer, 2: statement sub }, 395: 1 }{ 396: { -1: [ bstr .cbor [tree_size, leaf_index, [audit path]] ] } }A RECEIPT PROVES
- this exact signed statement is in the log at that index
- the log key signed the tree head that contains it
A RECEIPT DOES NOT PROVE
- that the statement is true, or that the agent behaved well
- anything about content the registry never saw: it stores the hash
npm install @aisourceoftruth/sdk
| HELPER | WHAT IT DOES |
|---|---|
| buildHashEnvelopeStatement({ artifactDigest, keyId, privateJwk, iss, sub }) | a COSE_Sign1 over the artifact's SHA-256 (RFC 9995 hash envelope), signed locally |
| client.registerStatement(bytes) | POST /entries; the receipt on 201, null on 202 |
| client.receipt(entryId) | GET /entries/{id}; the receipt, or null while pending |
curl -s https://aisourceoftruth.com/entries/<entry_id> -o receipt.cose curl -s https://aisourceoftruth.com/.well-known/scitt-keys -o scitt-keys.cbor curl -s https://aisourceoftruth.com/v1/export/verify.mjs -o verify.mjs node verify.mjs --receipt receipt.cose --statement statement.cose --keys scitt-keys.cbor
POST /v1/export bundles carry each statement's receipt and the key set; node verify.mjs bundle.json checks the issuer signature and the receipt.
LIVE A provenance record can carry a Sigstore bundle or an OpenSSF Model Signing signature as evidence about the artifact, and every record downloads as a Sigstore bundle. The Sigstore signer is shown apart from the publisher who signed the record.
buildSigstoreProvenance({ bundle, creator, model }) in @aisourceoftruth/sdkGET https://aisourceoftruth.com/r/{digest}/sigstore-bundle · application/vnd.dev.sigstore.bundle.v0.3+json · log entry kind aisot-record 0.0.1. Any record that is not a SCITT statement, as a bundle: the publisher's key as the verification material, the publisher's signature as a message signature over the record body, and one log entry for the registry log with its inclusion proof and the signed checkpoint. Computed on read; free.VERIFIED AT INGEST
- the DSSE or message signature over the artifact digest
- each Rekor v1 or v2 log entry: its body, the signed entry timestamp, the inclusion proof and the signed checkpoint, against the pinned Sigstore public-good trusted root
- each RFC 3161 timestamp over the signature, against the trusted root's TSA chains
- a Fulcio certificate: the code signing use, the chain and validity at the verified signing time
- that the record's artifact digest is one the bundle signs
NOT VERIFIED
- the certificate's embedded SCT
- the TSA certificate's revocation status
- a bare key bundle has no anchor: the key is the signer's own claim or the one the publisher supplied
- a certificate from a private CA is refused, not checked