Receipts and verification
Every hosted model call from a Decosa tool comes with a receipt: a record of which model served the request and
hashes of the request and of the output, signed with an Ed25519 key that Decosa publishes. Anyone can check a receipt at
/receipts/<id> on this site or with GET /receipts/{id} on the API. Receipts hold hashes and signatures, never the text.
Three kinds of language-model receipt
| Where the call ran | Who signs | What the signature covers |
|---|---|---|
| Hosted, on an outside inference provider (the hosted API at launch; who serves it) | Decosa's API, with its published key | Hashes of exactly what Decosa sent and received, the token counts the provider reported, and who served the call |
| Self-hosted (your own hardware) | Your box, with a key it makes on first start | The same hashes and counts, signed by the box that ran the model |
| Hosted on Decosa's own GPUs (not at launch; it returns when they serve again) | Decosa's gateway, after checking the serving machine's proof | The hashes, the model's weights fingerprint and a proof over one matrix product of the request |
A hosted receipt (outside provider)
GET /receipts/{id} returns the signed record, the checks it passed and a plain note that says who served it. The
fields that matter:
| Field | Meaning |
|---|---|
route |
upstream: an outside provider ran the model under Decosa's account |
served_by |
Who served it: the backend, a label (e.g. the provider's name), the provider's model id and request id, and which provider the router picked |
request_sha256, response_sha256 |
Hashes of exactly what Decosa sent and what came back (tool calls included) |
prompt_tokens, completion_tokens, cached_tokens, usage_source |
The provider's reported usage (estimated if it reported none) |
model_root |
null: Decosa can't attest which weights ran on someone else's hardware |
checks |
Each check, whether it passed, and a short explanation |
It proves: Decosa signed this record with its published key, nothing in it changed after signing, and these are the hashes of what Decosa sent and received. It does not prove which weights ran, or that the provider kept no copy: the computation is the provider's. The provider is named on the receipt and on Where your data goes.
A gateway receipt (Decosa's own GPUs; not at launch)
When hosted calls run on Decosa's own GPUs, the receipt adds a second signer and a proof:
| Field | Meaning |
|---|---|
model |
The model that served the request, e.g. qwen3.8-27b |
weights_root |
The published fingerprint of that model's weights |
request_hash, output_hash |
Hashes of what was asked and what was returned. The text itself is not stored. |
provider |
The serving machine's id, public key and signature (the id field keeps its older name, miner_id) |
gateway |
The gateway's public key and countersignature |
proof |
The proof format and whether it verified |
checks |
Each check, whether it passed, and a short explanation |
The checks:
- Weights bound to the published root. The serving machine's proof is tied to the model it claims to serve.
- Real matrix work on this request. A native proof over one matrix product of this request's own activations is verified.
- Serving machine's signature. The machine signs its claim over the job, model, output hash and token counts.
- Gateway countersignature. Decosa's gateway binds the charge to that claim with an Ed25519 signature. Its public key is published.
It proves:
- Decosa's gateway signed this receipt with its published key, and the receipt has not been altered since;
- the serving machine computed a matrix product with weights under the committed roots, on this request's activations;
- the proof belongs to this request: its
request_hash, machine id and model manifest match.
It does not prove:
- that the whole forward pass was computed. The proof covers one matrix multiplication per request (a first-layer prefill product);
- a cryptographic link between the proof and the published model root at the proof level. The model is bound through the model manifest hash, which the gateway vouches for;
- that the gateway is honest. Decosa runs the gateway: a receipt shows what Decosa's gateway vouched for, not an independent check that the inference happened.
Re-running a sample of outputs against the reference model is designed but not running today.
Generated media (Decosa Studio) is not re-checked by anyone today. A render's receipt is our signed statement of the model, the seed, the weights and the output's hash. Re-rendering sampled steps from the recorded seed is designed but not implemented, so this is weaker than the text receipts.
Patient data
Receipts carry hashes, not text. A self-hosted box signs its own receipts with an Ed25519 key it generates on
first start (status attested), so a clinic can show which model produced a note without sending the note anywhere.
Its public key is at /attest/signing-key on that box.
Attested receipts, speech receipts and session records
Some receipts are signed by the decosa-api server that ran the work, not by the gateway:
- Self-hosted model calls. Model name, weights root, request and output hashes, token counts, signed by the box.
- Speech recognition. In the tamper-evident record, each live caption and each speaker-attributed line gets a receipt with the sha256 of the audio it came from and of the text.
- Session records. Every caption, transcript line, summary sentence, model receipt and 5-second audio hash goes
into one hash chain with a Merkle root and signed checkpoints. The server signs the record when the session ends.
Anyone can re-check it in the browser at the "Verify a record" panel of the record console, or with
POST /record/verify. Changing one word breaks the check and names the entry.
These are attestations by whoever runs that server. They prove nothing was changed after signing and which key signed. They do not prove the model actually ran, that speech was recognised correctly, or that the operator is honest: there is no second signer and no proof of computation. Gateway receipts add both.
Model-call receipts (speech, voice, embeddings and other non-LLM models)
Language models are not the only models in a Decosa app. Speech recognition, text-to-speech, embedders, rerankers,
translation and page parsers get a model-call receipt (decosa.model-call.v1), signed by the decosa-api server
that ran the call with the same key as its other receipts (/attest/signing-key). Today it covers Voxtral speech
recognition in consented dubbing, every Kokoro line in the audio drama studio, and the MiniLM lyric embeddings in sample
clearance. More models move to it as they are wired.
A receipt holds hashes and settings, never the audio or text:
| Field | Meaning |
|---|---|
kind, use_case |
What sort of call (asr, tts, embed, rerank, mt, ocr...) and which app made it |
model, weights |
The model id, its Hugging Face repo and revision, and a hash of the weights that were loaded |
runtime, device |
The inference runtime and version, and the device class (CPU or GPU model) |
inputs, outputs |
Each part's name, type, size and sha256, plus one digest over each list |
params, units |
The settings (voice, speed, pooling...) and the work done (audio milliseconds, characters, items) |
signer, sig |
The server's Ed25519 public key and signature over the canonical JSON |
An example (hashes shortened):
{"v": "decosa.model-call.v1", "id": "mc-b5f6c9330a0ae432df5c", "kind": "tts", "use_case": "audio-drama-studio",
"model": "kokoro-82m", "weights": {"repo": "hexgrad/Kokoro-82M", "revision": "f3ff3571…", "sha256": "496dba11…", "scheme": "file-sha256"},
"runtime": {"name": "kokoro", "version": "0.9.4 (torch cpu)"}, "device": "cpu",
"inputs": [{"name": "text", "mime": "text/plain;charset=utf-8", "bytes": 24, "sha256": "…"}], "inputs_sha256": "…",
"outputs": [{"name": "audio", "mime": "audio/wav", "bytes": 84044, "sha256": "…"}], "outputs_sha256": "…",
"params": {"voice": "ef_dora", "speed": "0.95", "sample_rate": 24000}, "units": {"characters": 20, "audio_ms": 1830},
"started_ms": 1790500000000, "finished_ms": 1790500000410, "signer": "6112e9a8…", "sig": "…"}
A full sample, signed with a test key and countersigned by a test gateway, is at /samples/model-call-receipt.json.
Check one:
- open
/receipts/<id>on this site: the server's checks are listed, and the signature and part digests are re-checked in your browser; POST /receipts/verifywith the receipt (no key needed);- offline, with the server's public key:
python -m decosa_api.model_call verify receipt.json --key-url https://<api>/attest/signing-key --output audio=line.wav. Passing the file re-hashes it against the receipt; - inside a session record, the "Verify a record" panel and
POST /record/verifyre-check each embedded receipt.
What it proves: the hashes, model revision, settings and work units were not changed after signing, and which key signed them. What it does not: that the model ran or that its output is right. Like the speech receipts above, it is an attestation by whoever runs the server. When the gateway has the model-call route enabled, the server also reports each receipt there: the gateway checks the signatures, logs the call in its model-call ledger and countersigns the hashes. It never sees the audio or text on that path, so this is a timestamped witness, not a proof, and nothing is charged or paid for it yet.