The receipt is the unit of trust.
Every audit log we have ever read is a story the system tells about itself. The receipt is something else. It is the system telling on itself — to a counterpart that can verify the claim without trusting the system at all.
That is the shift. Logs assume good faith. Receipts replace good faith with a signature.
We did not start there. The first version of Lumin had logs, like every other product. Tickets in, tickets out, JSON lines on disk, a dashboard that turned them into pretty charts. Our customers liked the dashboard. Their auditors did not. The auditors wanted the JSON. Then they wanted the JSON signed. Then they wanted the JSON signed by something that was not us. By the end of the first quarter, the receipt was the architecture and the log was the side effect.
What a receipt actually contains
A Lumin receipt is one file per agent action. It is JSON on the wire and PDF for the auditor. The shape is fixed: input, agent, models, tools, guard, output, cost. Each row is a fact, not a narrative.
The receipt covers the four things a regulator, a CFO, or a customer can ask about a single action — what came in, what the agent did, which system of record changed, what it cost. Nothing else needs to be in there, and nothing else is.
Here is a real one, lightly redacted. Invoice intake, picked off an AP inbox at half past two on a Tuesday afternoon. The agent parsed the PDF, matched the PO, posted to the ERP, sent the reply. Total wall-clock from inbound to receipt: under two seconds. The PDF version of this same record fits on one page.
What gets signed
The signature is over the bytes of the receipt, anchored to a per-tenant key. Four things land inside that signature.
- The request inputs — the verbatim email body, the API call, the document hash. Not a summary. The bytes.
- The agent identity — name, version, and the model bindings that produced the decision.
- The action — which tool ran, with what parameters, returning which response.
- The state hash — a fingerprint of the system-of-record row before and after the write.
Verification means re-hashing those four parts and checking the signature against the tenant key. A laptop and the public key are enough. The auditor never has to call us. If a receipt does not verify, one of three things has happened: the bytes were edited after signing, the wrong key was used, or the system-of-record state did not match what the receipt claims. All three are bugs, and all three are visible from the auditor's chair.
The per-tenant key is the part that surprises people the first time. Each customer holds the public half. We rotate the private half quarterly, and every rotation is itself a receipt — signed by the previous key, countersigned by the new one. The chain reaches back to the day the tenant was provisioned. There is no point where we could quietly swap a key without leaving evidence of the swap.
Why we chose SHA-256
Nothing fancy. SHA-256 is the same primitive that secures TLS, signs Git commits, and anchors most of the chain-of-custody work in the legal world. Auditors recognise it. Regulators have signed off on it. Tooling exists.
Each receipt also references the SHA-256 of the receipt that came before it for that tenant. That gives us a chain — pull any receipt, you can walk the chain back to the day the agent was deployed. Delete one in the middle and the chain breaks. That is the point.
The chain runs per tenant, per agent. Two reasons. First, it keeps the verification window small — an auditor checking March 2026 for the AP agent does not have to scan the rest of the company's traffic. Second, it makes a corrupted chain easy to isolate. If the chain breaks at receipt 17,394 on agent A, agent B is fine. Tenants are not coupled to each other; agents within a tenant are not coupled either.
What we chose not to do
We did not put the receipts on a blockchain. We did not invent a new signature scheme. We did not write a custom DSL for receipt queries. Those are interesting problems for someone else. Our problem is that the CFO needs to verify yesterday's payable run before standup, and she needs to do it in fifteen seconds.
Most of the time when a team says no to something obvious it is because they have not thought about it yet. We have. Blockchain anchoring adds latency and a dependency on a chain we do not control. Custom signature schemes look interesting until the first time an auditor asks which standard they conform to. A query DSL is the kind of thing that pays off in year three and slows you down in year one. We chose year one.
The receipt is the API
Because every action produces one, the receipt becomes the surface between Lumin and everything around it. Finance teams diff receipts month over month. Security teams stream them into a SIEM. Auditors pull a date range. Customers attach one to a dispute. The product team uses them to triage edge cases the same way developers use stack traces.
That last one matters more than it sounds. When a Northgate dispatcher flags a ticket as wrong, the engineer who picks it up does not start by asking the dispatcher to repeat what happened. The engineer pulls the receipt. The receipt has the input, the agent version, the models, the tool calls, the response codes, the cost, and the system-of-record diff. Reproducing the bug is a matter of replaying the receipt against a staging agent. We do this an order of magnitude faster than a team that has to dig through dashboards.
The same shape works for the people on the other side. A customer ops lead can hand a receipt to a vendor and say: this is what we sent, this is what your system returned, this is the timestamp. The vendor cannot say it did not happen. The receipt is the conversation.
What this forces in the architecture
Once a receipt is the unit, two things have to be true of the agent runtime. Every action must be addressable — every tool call, every model call, every guardrail, must have an identity that fits in a row. And every action must be wrapped in a transaction — the receipt and the system-of-record write either both happen or neither does. There is no halfway state where the ERP shows a new invoice and no receipt exists, or where a receipt is signed and the ERP never wrote.
Saying that out loud is the easy part. Making it true is the rest of the work. We had to rewrite our tool layer twice to get it right. The first version produced a receipt when the agent thought the write succeeded. That is not the same as the write actually succeeding. The second version waits for the system-of-record response, hashes the resulting state, and only then signs. If the write fails, we still produce a receipt — a receipt of the failure, with the error code and the rollback. Both kinds verify the same way.
Where's the receipt? That is the only question we let ourselves answer with.
A system without receipts asks you to take its word. A system with receipts hands you the evidence and walks away. That is what we are building toward. Not a smarter agent. A more honest one.