Attested AI-Assisted Work
Draft Specification · v0.3
Status: Open for Comment
A receipt format for verifiable AI-assisted work. Model-provider neutral.
Context
Modern LLM providers operate the inference and hold the signing keys. Server-side integrity has established attestation paths in confidential-inference deployments (TEEs, Apple Private Cloud Compute), but the standard text-completion APIs ship without any per-completion provenance surface returned to the caller. What's missing is the inverse surface: a receipt the server returns to the client proving this prompt produced this output on this model at this time. This spec proposes that surface in a form any model provider can implement.
Primitive — the signed receipt
Receipt {
receipt_id : uuid // unique per completion
model_id : string // canonical model identifier (provider-namespaced)
prompt_hash : sha256 // hash of canonicalized request body
output_hash : sha256 // hash of full response body
issued_at : iso8601
nonce : base64url(16) // 128-bit random; required, prevents replay
weight_hash : sha256 // OPTIONAL; weights snapshot for TEE-attested issuers
key_id : string // public-key reference for verification
signature : ed25519 // signature over all preceding fields
}
Prompt and output are hashed, not stored — content stays private; verifiability is preserved.
Field encodings (normative)
prompt_hash,output_hash,weight_hash— lowercase hex ofsha256(content), where content is the canonical request body, the full response body, and (optionally) a weights snapshot respectively. No algorithm prefix.nonce—base64urlof 16 random bytes, unpadded (RFC 4648 §5, no=).issued_at— RFC 3339 / ISO 8601 in UTC at second precision (e.g.2026-04-12T14:32:00Z).signature— standardbase64(RFC 4648 §4) of the 64-byte ed25519 signature over the canonical bytes defined below.
Canonicalization (normative)
The signature is computed over a canonical serialization of the receipt that issuer and verifier MUST reproduce byte-for-byte. This spec adopts RFC 8785 JSON Canonicalization Scheme (JCS) over the receipt object, with two rules:
- The
signaturefield is omitted (it cannot sign itself). - The OPTIONAL
weight_hashfield is omitted when absent (it is included only when an issuer populates it).
Because every receipt field is a JSON string, JCS reduces to a small, auditable construction: the object's members are emitted in lexicographically-sorted key order, with no insignificant whitespace, and string values escaped per JCS (only ", \, and the C0 control characters are escaped — <, >, &, and non-ASCII pass through literally as UTF-8). The signature is ed25519(privkey, utf8_bytes(canonical_json)).
Worked example (line-wrapped here for readability; the real value has no whitespace):
{"issued_at":"2026-04-12T14:32:00Z","key_id":"gitjob-demo-2026q2",
"model_id":"gitjob.io/claude-sonnet-4-6","nonce":"6tmw7Mc3Ylw3molRAzstIg",
"output_hash":"8c6c…7cb8","prompt_hash":"26ac…612a","receipt_id":"demo-valid-001"}
Rationale. JCS is the defensible, interoperable choice: it is a published RFC with implementations in every major language, so the standalone
@gitjob/attestverifier and any third-party reimplementation converge on the same bytes. The all-string receipt sidesteps JCS's only genuinely tricky area (canonical number formatting), so a from-scratch implementation is a few dozen lines. Reference:internal/attest/canonical.go.
API surface (proposed)
The spec is transport-agnostic; the shape below is a representative HTTP mapping. Providers may bind it onto their existing completion endpoints.
POST {completion_endpoint}— response includes an optional top-levelreceiptfield when the client opts in viaX-Receipt-Request: trueGET /v1/receipts/{receipt_id}/verify— public endpoint; looks up a receipt the issuer persisted and returns{ status, key_id, issued_at }. The issuer holds the receipt, so this checks signature + key status but cannot re-derive content hashes by itself.POST /v1/receipts/verify— public endpoint; accepts a full receipt JSON body as{ "receipt": {…}, "prompt"?: string, "output"?: string }and returns{ status, key_id, issued_at }. Whenpromptandoutputare supplied, the issuer recomputesprompt_hash/output_hash— sotamperedis genuinely detectable for an arbitrary receipt presented alongside the content a third party was shown. This is the offline-verification surface for receipts the issuer never persisted.GET /v1/receipts/keys— public key set with rotation/revocation history (see below).
Key set (normative)
GET /v1/receipts/keys returns the issuer's published public keys:
{
"keys": [
{ "key_id": "gitjob-demo-2026q2",
"public_key": "<base64 of the 32-byte ed25519 public key>",
"status": "active",
"created_at": "2026-04-01T00:00:00Z",
"rotated_at": null },
{ "key_id": "gitjob-demo-2025q4",
"public_key": "<base64…>",
"status": "revoked",
"created_at": "2025-09-01T00:00:00Z",
"rotated_at": "2025-11-01T00:00:00Z" }
]
}
Only public keys appear here; the signing private key is never served. The set is cacheable — a verifier need not call the issuer at verify time. Revoked keys remain listed (with rotated_at) so receipts they signed can still be evaluated against the revocation timeline.
Verification flow
A verifier resolves exactly one status, in this precedence order:
unknown_key—key_idis absent from the published key set.revoked— the named key is markedrevokedas ofissued_at(i.e.issued_at >= rotated_at). A receipt signed before its key's revocation is not revoked — it continues to the signature check.tampered— the ed25519 signature does not verify over the canonical bytes, or (when content is supplied) a recomputedprompt_hash/output_hashdoes not match.valid— key known, not revoked-as-of-issuance, signature and any supplied hashes match.
Anyone can verify; nobody can forge. The reference implementation is internal/attest (Go, MIT); conformance vectors live at internal/attest/testdata/conformance/.
Reference implementation — gitjob.io commits to
- Open-source receipt verifier library (
@gitjob/attest, MIT) - Public spec hosted at
gitjob.io/spec, versioned, with change log - Conformance test suite for receipt issuers and verifiers
- First production consumer: gitjob.io's credentialing engine, with a public verifier dashboard
Posture: spec, verifier, and test suite published under MIT. Receipt issuance remains the model provider's; verification is public. Anyone can verify, nobody can forge.
Open questions — for implementing providers
- Multi-turn semantics: per-turn receipts vs. session-scoped bundle
- Tool-use scope: does the receipt cover tool inputs and outputs, or only the model's own tokens?
- Key rotation cadence and revocation surface
- Privacy posture: opt-in default, opt-out, or per-deployment policy
Related work
This spec is one set of design choices in a growing space. The choices are scoped to a specific use case — work provenance, where a third party verifies that a specific human prompt produced a specific model output — and a specific consumer model: artifact-scoped receipts that outlive the API call and are verified offline.
-
draft-sharif-ai-model-lifecycle-attestation-00(IETF individual draft, submitted 2026-03-31, no working-group adoption). The closest live proposal: per-inference signed payload withoutput_hash,model_id,weight_hash,timestamp,nonce, ECDSA P-256, returned in HTTP response headers. This spec diverges in three load-bearing ways. (1)prompt_hashis required because the receipt must bind the request, not only the response — without it, the receipt proves the model emitted some text, not that this prompt produced it. (2) ed25519 in place of ECDSA P-256: deterministic, smaller, no nonce-reuse failure mode; the FIPS/HSM install base that motivates P-256 in standards-track work is not a constraint here. (3) The receipt is a body field, not response headers, because the artifact is meant to be passed around, stored, and verified long after the original API call returns. -
AEX — Non-Intrusive Multi-Hop Attestation and Provenance for LLM APIs (arXiv 2603.14283). Academic prototype with an OpenAI-compatible profile and a TypeScript reference implementation. Adjacent rather than overlapping: AEX targets multi-hop provenance across chains of LLM intermediaries, not the single-completion receipt this spec defines. The two compose — an AEX hop can carry receipts in this format as its per-call provenance unit.
-
C2PA Content Credentials (production at Adobe, Google, Microsoft, OpenAI, Meta, BBC). Signed metadata manifest embedded in media files. Different scope (AI-generated and human-edited media, not text completions) and different consumer model (creator-side embed, not provider-issued receipt). The closest C2PA gets to this surface is
synthid-textwatermarking, which proves AI authorship but not the request-output binding this spec requires. -
TEE-attested inference (Phala, Apple Private Cloud Compute, Venice.ai, Chutes, and the broader confidential-compute landscape). Hardware-rooted attestation that signs outputs with a key bound to the loaded code's measurement. Stronger weight-binding than this spec offers on its own. The OPTIONAL
weight_hashfield in the receipt schema is reserved exactly so TEE-attested issuers can populate it without forking the format. The threat model is confidentiality of prompts and weights — orthogonal to and composable with the external-verifiability goal here.
Coordination on a single receipt envelope across providers, regulators, and verifiers is more usefully tracked at IETF SCITT than against any individual draft.
Changelog
- v0.3 — First version shipped with a working reference implementation (
internal/attest, Go/MIT). Added the normative Canonicalization section (RFC 8785 JCS,signatureomitted,weight_hashomitted when absent) and normative field encodings. Specified the key set wire format and the revocation semantics (revokedis evaluated as ofissued_at). Added thePOST /v1/receipts/verifyoffline-verification endpoint. The four demo receipt ids (demo-valid-001,demo-tampered-002,demo-unknown-key-003,demo-revoked-004) are now real signed receipts, not fixtures, and seed the conformance suite. - v0.2 — Added required
nonceand OPTIONALweight_hashto the receipt; added the Related Work section (IETFdraft-sharif-…, AEX, C2PA, TEE-attested inference) and the ed25519-over-P-256 justification. - v0.1 — Initial primitive: signed receipt binding
prompt_hash/output_hash/model_id/issued_at, the four statuses, and the proposed verify surface.
Justin Higgins · justin.c.higgins@gmail.com · gitjob.io