Traces
The receipt a payee can read without an account.
A trace is one row written for every state change, hashed over its own contents, and written before a single unit of value moves.
{
"warrant_id": "wrt_01J8Z3M4YQ7K2V9X",
"from": "verifying",
"to": "released",
"amount": "8400000",
"asset": "USDC",
"released_bps": null,
"reason_codes": [],
"actor": { "kind": "system", "id": null }
}The record comes first
Inside the settlement transaction, the trace row is inserted before the ledger entries. Not after, and not alongside.
The ordering is the guarantee. If the process dies mid-settlement, the transaction rolls back and neither exists; there is no window in which money has moved and nothing says why. A payments system whose audit trail is written last is one crash away from an unexplained balance.
Every transition, not just the ending
A warrant that is opened, verified, escalated, then partially released leaves four trace rows. The lifecycle is reconstructable in full from them, including the paths that did not pay out.
Traces are append-only. Nothing rewrites one, because a record that can be edited after the fact is not a record.
The payload hash
payload_hash is a SHA-256 over the serialised payload. It lets anyone holding
a copy of a trace confirm it is the one that was written, without needing access
to the database that wrote it.
Anchoring
chain_ref is null until the chain confirms, then carries a reference such as
rialo:testnet#19189427.
Anchoring happens after the transaction commits, deliberately. Network I/O inside a settlement transaction would hold a database lock open for the duration of a round trip to a chain, and a slow chain would become a slow settlement. A warrant that settled but has not yet anchored is a normal, temporary state.
Where a warrant is not anchored, the console says so plainly rather than showing an empty field. "Not anchored" and "anchored, reference unknown" are different facts and the interface does not blur them.
The public receipt
A settled warrant gets a permalink at /t/:id that needs no account. Send it to
the counterparty and they can read why they were or were not paid.
It is unauthenticated, so it is deliberately narrow:
- Only settled warrants resolve. A held or verifying warrant is live operational state, and publishing it would leak what an organisation is doing right now to anyone who can guess an id. Ids are ULIDs, so guessing is not the defence — the state check is.
- No organisation and no person is named. The payee already knows who they are dealing with; a receipt is not a directory.
actor.idis redacted. The internal record names who approved a settlement, because an audit trail must. A public receipt naming them would let anyone holding two links work out who approves what.
payload_hash still covers the full unredacted record, and the response states
which fields were removed. The hash therefore will not match the redacted body
you can see — that is the intended behaviour, and saying which fields went
missing is what makes it checkable rather than mysterious.
Amounts on a trace
As everywhere: integer strings of the asset's smallest unit, matching
^[0-9]+$.
"8400000" 8.40 USDC
8400000 rejected — a JSON number
"8.40" rejected — not the smallest unitOn a partial release, amount is the warrant's full amount and released_bps
carries the share actually paid. The paid figure is a product of the two rather
than a third number stored separately, so the two can never disagree.