OpenAI compatible API · Attested · Public status

Signed Inference Receipts

Opt into a signed, offline-verifiable receipt on any inference call: exact request and response hashes, the routed model and provider, the upstream verification tier, and a signing key committed into hardware attestation.

Verify gateway
Onebase URL to migrate
100sof models and routes
0prompt or output logs. Always.
Offline-verifiable proof of inference

Every response can carry a signed receipt.

Add one header to any chat or responses call and TrustedRouter returns a signed receipt: a compact proof that this exact request produced this exact response, which model and provider served it, and how the upstream was verified. The signing key is minted inside the attested enclave and committed into its hardware attestation, so a receipt chains to the same Confidential Space evidence as the live gateway.

Receipts are opt-in per request and never stored server-side. Managed prepaid inference with a signed receipt uses a 12% total TrustedRouter service fee instead of the standard 5.5%. BYOK token pricing is unchanged. Verification is offline: signature, hashes, and the attestation chain all check without calling TrustedRouter again.

A receipt is not a confidentiality proof. It proves integrity and origin of the bytes; session privacy remains the job of the attested TLS channel documented on the trust page.

Non-streaming

Compact JWS in a header

The response carries x-inference-receipt: a three-part JWS over the exact request and response body bytes. The attestation document is pinned by hash and fetched separately.

Streaming

Self-contained final chunk

The last data event before [DONE] is a chat.completion.chunk with an inference_receipt object — a flattened JWS with the full attestation embedded in its protected header. Truncated streams never get a receipt.

Quickstart

Send the header with true, or with a fresh nonce of 1–88 characters from A–Z a–z 0–9 _ -. The nonce is echoed inside the signed claims, which is what makes a receipt provably fresh to you.

curl https://api.trustedrouter.com/v1/chat/completions \
  -H "Authorization: Bearer $TRUSTEDROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-inference-receipt: $(openssl rand -hex 16)" \
  -d '{"model":"trustedrouter/auto","messages":[{"role":"user","content":"Hello"}]}' \
  -D - -o response.json

The x-inference-receipt response header holds the compact receipt. Requests without the header keep standard 5.5% pricing and are byte-identical to today. Receipt-enabled managed prepaid requests use the 12% total service fee. Malformed opt-in values are rejected with 400.

Verify a receipt

All six SDKs verify receipts offline, and the reference verifier ships in the open quill-cloud-proxy repository as tools/verify-attestation.py --verify-receipt.

Python

Streaming, fully self-contained

from trustedrouter.receipts import verify_receipt

claims = verify_receipt(
    receipt,                      # the inference_receipt object
    request_body=body_bytes,      # exact bytes you sent
    response_stream=wire_bytes,   # exact SSE bytes you received
    expected_nonce=my_nonce,
    max_age_seconds=300,
)
print(claims.model.selected, claims.upstream.tier)
JavaScript

Compact, with the pinned attestation

import { verifyReceipt } from "@lore-hex/trusted-router/receipts";

const claims = await verifyReceipt(compactReceipt, {
  requestBody, responseBody,
  expectedNonce: myNonce,
  maxAgeSeconds: 300,
  attestation,   // bytes whose sha256 equals the att_sha256 claim
});

Go, Rust, Swift, and Java expose the same verification with the same checks and the same frozen cross-SDK test vectors. Verification always covers: the Ed25519 signature; rv, iat freshness, and your nonce; the request and response hashes over exact bytes; the receipt event's position in a stream; and the attestation chain with the signing key's commitment checked by set membership.

What the claims say

The payload is inference-receipt/1. Hashes are unpadded base64url SHA-256.

ClaimMeaning
rvReceipt version, 1
issThe issuing plane's canonical https origin
iatSigning time, Unix seconds; verifiers allow at most 60 s of future skew
jtiThe response id (chatcmpl-… or resp_…)
genGeneration id — non-streaming only, since streams settle after [DONE]
nonceYour opt-in value, echoed verbatim when it was not true
routechat.completions or responses
reqsha256 over the exact request body bytes
respsha256 over exact response bytes: of = body, or the stream domains sse-data-v1 / sse-events-v1 with an events count
modelrequested, selected, provider, endpoint — the router's actual selection
upstreamThe verification tier below
att_sha256Compact receipts only: SHA-256 of the exact key-binding attestation document

Upstream tiers name a mechanism, never a privacy property

The tier records how the upstream that served this request was verified. It makes no claim about what the upstream retains.

TierMeaningExtra claims
tee-verifiedThe upstream's own TEE attestation was verified before your bytes were sent, under a named policy such as chutes-tdx-nvidia-e2e-v1 or tinfoil-snp-dual-source-v1policy, verified_at, verification_expires_at; verifiers require verified_at ≤ iat < verification_expires_at
tls-webpkiThe upstream was reached over ordinary WebPKI TLS

A failed attested candidate never lends its tier to the fallback that actually serves the response — the tier always describes the connection your bytes used.

The attestation chain

Each enclave instance mints a fresh Ed25519 keypair at boot and commits SHA-256("inference-receipt-key-v1" ‖ 0x00 ‖ pubkey) into its hardware attestation. Verifiers check that commitment by set membership among the attestation's committed values — never by position.

EndpointServes
GET /receipt-attestationThis instance's raw key-binding attestation document
GET /receipt-keyThis instance's {kid, jwk, att, att_kind} envelope — match a compact receipt's kid directly
GET /.well-known/inference-receipt-keysThe append-only key log: every observed signing key with its attestation, so compact receipts stay verifiable after the instance that signed them is gone

Streaming receipts embed the attestation, so nothing extra is fetched and they verify forever from the captured bytes alone. For compact receipts, keys are per-instance and routing is DNS-based, so fetch deterministically: resolve the gateway hostname's A records and ask each instance directly (pin Host/SNI to the hostname while connecting by IP) for its /receipt-key until the kid matches — polling the hostname is a lottery that can miss instances behind connection reuse. The key log covers keys after their instance is gone.

Archive the attestation with the receipt. If a receipt matters to you — an audit trail, a dispute, an eval run — store the matching attestation document (and for streams, the captured wire bytes) next to it at verification time. That bundle verifies offline forever with no dependency on TrustedRouter, the signing instance, or the key log being reachable.

What a receipt proves — and deliberately does not

Proves

Integrity and origin

The exact request produced the exact response; the named model, provider, and endpoint served it; the signing key lives in a measured, debug-disabled Confidential Space workload; with your nonce, that the receipt was minted for your request and is not a replay.

Does not prove

Confidentiality or identity

A relay that forwarded your request holds a valid receipt and read everything — session privacy is the attested-TLS flow on the trust page, not the receipt. Receipts carry no requester identity, so sharing one cannot deanonymize you.

Workspace access

Sign in

Choose a sign in method to access your TrustedRouter workspace.

By signing in you agree to the terms of service and privacy policy.