OPEN SPECIFICATION
GoVTrace Receipt Format v1.
A portable, third-party-verifiable artifact attesting that an AI action was evaluated by a named policy and produced a named verdict at a named time. Signed with Ed25519. Verifiable by anyone holding the public key, without ever contacting the issuing system.
ON THIS PAGE
1. Design goals
- Portable. A receipt can be detached from the issuing system and verified anywhere by anyone with the public key.
- Third-party verifiable. Verification does not require the issuer to be online, in business, or cooperative.
- Tamper-evident. Any modification to any signed field invalidates the signature.
- Compact. Receipts fit in a single JSON object suitable for embedding in logs, contracts, eDiscovery exports, and audit reports.
- Stable. v1 is frozen. Forward-compatible additions arrive as v1.x. Breaking changes arrive as v2.
2. Receipt object
A v1 receipt is a JSON object with the following top-level fields. Additional issuer-defined keys are allowed and MUST NOT be a reason to reject the receipt.
| Field | Type | Req | Description |
|---|---|---|---|
| receipt_id | string | yes | Unique identifier. Format is issuer-defined. |
| signed_at | string (RFC 3339 UTC) | yes | Time the receipt was signed. |
| signature_algo | string | yes | MUST be "Ed25519" in v1. |
| signature | string (base64url) | yes | Unpadded base64url Ed25519 signature. |
| public_key_id | string | yes | Stable identifier for the issuing public key. |
| signed_fields | array of strings | yes | Ordered list of keys present in signed_fields_data. |
| signed_fields_data | object | yes | The actual key/value mapping that was canonicalized and signed. |
| canonical_digest | string (hex) | yes | Lowercase SHA-256 hex of the canonical bytes. |
| pdf_url | string | no | Issuer-served URL to a human-readable PDF rendition. |
| verify_url | string | no | Issuer-served URL to walk the chain (if any). |
2.1 Required fields in signed_fields_data
A v1-compliant receipt MUST include at least these six keys. Issuer-defined keys MAY be added and are covered by the same signature.
| Field | Type | Req | Description |
|---|---|---|---|
| run_id | string | yes | Issuer-defined identifier for the AI action being attested. |
| verdict | string | yes | The decision: STOP, NEEDS_REVIEW, SAFE, or issuer-defined. |
| record_hash | string | yes | SHA-256 hex of the canonical record (DoCR) the verdict was made on. |
| policy_digest | string | yes | SHA-256 hex of the policy bundle that produced the verdict. |
| input_hash | string | yes | SHA-256 hex of the AI action's input bytes. |
| timestamp | string | yes | RFC 3339 UTC timestamp of the verdict. |
3. Canonicalization
The canonical byte string of signed_fields_data is JSON with sorted keys at every level, compact separators ("," and ":"), no whitespace, UTF-8.
# Python reference
import json
canonical = json.dumps(
signed_fields_data,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")// Node.js reference (no deps)
function canonicalize(value) {
if (value === null || typeof value !== 'object') return JSON.stringify(value)
if (Array.isArray(value)) return '[' + value.map(canonicalize).join(',') + ']'
const keys = Object.keys(value).sort()
return '{' + keys.map(k =>
JSON.stringify(k) + ':' + canonicalize(value[k])
).join(',') + '}'
}The canonical_digest field MUST equal sha256(canonical_bytes) rendered as lowercase hex. A verifier MAY recompute it and reject the receipt on mismatch.
4. Signing
The signature covers the raw 32 bytes of the SHA-256 digest of the canonical bytes. Not the hex string of the digest. Not the canonical bytes themselves. This produces a fixed-length signing input independent of receipt size.
digest_bytes = sha256(canonical_bytes) # 32 bytes signature = ed25519.sign(private_key, digest_bytes) signature_b64url = base64url_unpadded(signature)
The signature is an Ed25519 signature as defined by RFC 8032.
5. Public key publication
Issuers MUST publish their public key at a stable HTTPS URL. The recommended location is:
https://<issuer-domain>/.well-known/govtrace-pubkey.json
The document MUST be a JSON object with this shape:
{
"key_id": "govtrace-signing-v1",
"algorithm": "Ed25519",
"public_key_b64url": "OwOdZ-d9QHwtuRCreEn6SIZ9iTuWxTpJPfeN5pGL-ns",
"public_key_pem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n",
"signs": "GoVTrace Receipt v1",
"signature_encoding": "base64url (unpadded)"
}The key_id in the published document MUST equal the public_key_id field in any receipt the holder of the corresponding private key issues. The reference engine publishes its key at https://govtrace-api.vercel.app/.well-known/govtrace-pubkey.json.
6. Verification procedure
Given a receipt object R:
- Validate
R.signature_algo == "Ed25519". Otherwise reject. - Resolve the public key for
R.public_key_id. Pin it, fetch from/.well-known/govtrace-pubkey.json, or accept it out of band. - Recompute the canonical bytes of
R.signed_fields_dataper section 3. - Compute
digest_bytes = sha256(canonical_bytes). - (Recommended.) Verify
R.canonical_digest == hex(digest_bytes). - Verify the Ed25519 signature against
digest_bytesusing the resolved key.
If step 6 succeeds, the receipt is VALID. The verifier SHOULD echo back signed_fields_data so the user sees exactly what was attested. If step 6 fails, the receipt is INVALID and the verifier SHOULD NOT present signed_fields_data as trustworthy.
7. JSON Schema
A machine-readable JSON Schema for the receipt object is published at:
https://gobotsai.com/spec/receipt.schema.json
The schema is normative for field names and types. This document is normative for canonicalization and signing.
8. Reference implementations
- Issuer (Python):
govtrace-api/signing.pyin aparankussam/govtrace-ai. - Verifier (Node CLI): @gobotsai/govtrace on npm.
npm i -g @gobotsai/govtracethengovtrace verify ./receipt.json. - Web verifier: gobotsai.com/verify. Paste any receipt. Stateless.
9. Versioning and stability
- v1 is frozen as of the publication date of this document.
- Additive, backward-compatible changes to issuer-defined keys in
signed_fields_dataare allowed at any time. - Changes that affect signing or canonicalization rules require a major version bump (v2). v1 verifiers SHOULD reject receipts carrying an unrecognized
spec_version.
10. Security considerations
- A verifier MUST resolve the public key from a source the verifier itself trusts. Never accept a public key embedded in the same receipt being verified.
- A receipt does not attest that the AI action signed actually occurred in the real world. It attests only that the issuer evaluated a given input under a given policy and reached the recorded verdict at the recorded time. Combine receipts with execution-time evidence to reconstruct what happened.
- Receipts are not anonymized. Avoid placing direct identifiers in
signed_fields_data. Use hashes (record_hash,input_hash) instead.
FULL TEXT
This page is a rendering of the canonical specification. The authoritative source is the Markdown file in the GoVTraceAI repository, published under CC BY 4.0.