A ledger you can't quietly rewrite
Every capital flow, every approval, every mode change lands in an audit log with a hash chain. Why this matters in trading software, and how to verify integrity in under a minute.
There’s one class of bug in financial software that’s worse than the rest: not a crash, a quiet break. The algorithm keeps trading, the dashboard keeps drawing the curve, and in the background something — a bug, a race, a serialisation error — rewrote a past row in place. You can’t catch a break like that a week later: you don’t know which numbers were real and which were the rewrite.
The audit ledger in inite.fund is built to rule out exactly this failure mode. Not to flag it after the fact, but to make rewriting the past structurally impossible.
Append-only by construction
Every audit table — audit_log, capital_flows, mode_history,
hil_decisions — runs as append-only. UPDATEs are blocked by database
triggers. DELETE is allowed only on the most recent row, and only
while it’s marked pending. Anything more than a day old can’t be
modified by anyone, admin included.
It sounds like a restriction; it’s actually a freedom. Once a row is in the ledger, it’s there forever. No “we patched yesterday’s value because we spotted a typo.” The typo stays next to the correction, and both are visible.
The hash chain
Every ledger row carries two extra fields alongside its data:
prev_hash and row_hash. row_hash is the SHA-256 of all the
significant fields concatenated with prev_hash of the previous row.
Each row signs not only itself but everything that came before in the
same ledger.
The arithmetic from there is simple. Change one row in the middle and
its row_hash breaks. To hide that, you’d have to recompute every
row_hash after it. To recompute, you’d have to disable the database
triggers. To disable them, you’d need postgres superuser. And every
such elevation lands in postgres’s own audit, which we also keep.
The chain can be verified by any external tool in a single pass. Take
the first row, recompute row_hash by hand, compare to what’s
written. Take the next, recompute with prev_hash, compare. Repeat to
the end. If even one disagrees, the ledger was touched. The script
fits in twenty lines; we run it inside every nightly backup.
Idempotency via audit_ref
Every capital operation — move_to_long, move_to_trading,
approve_trade, set_mode — requires an audit_ref. It’s a unique
string the caller generates before the request. The database checks
that no row with that ref exists yet, and only then applies the op.
Why this matters. A network blip mid-call is routine. Client sends a
request, the database applies it, the response is dropped, the client
retries. Without audit_ref, the second call goes through and the
same rebalance happens twice. With audit_ref, the second call comes
back “already applied” with the same row_hash as the first, and the
client sees there’s nothing new to do.
In practice this means capital operations in inite.fund are safe to retry at any layer. You can retry from the network stack, from the MCP client, from a manual operator script — the ledger stays consistent.
What it gets the operator
Three things. First, a complete history of capital movement broken out by cause and initiator. For any figure on the dashboard you can walk back to the specific audit row that produced it — when, by whom, with what reasoning.
Second, external verifiability. If we ever need to show a regulator, an auditor, or a partner that the data wasn’t fudged after the fact, we have a script and an export format for it. This isn’t “trust us, we’re the good guys.” It’s a mathematical property of the ledger that can be checked independently.
Third, and to us the most important — psychological relief. When you know that every operation left a trace and the trace can’t be forged, a whole class of anxiety disappears. You don’t have to wonder whether the system quietly decided for you and hid the decision. If the ledger is empty, the action didn’t happen. If the ledger has a row, it’s exactly the row that was written at the moment.
What it costs
Each ledger write is one extra INSERT, one SHA-256, one
uniqueness check on audit_ref. On our measurements that’s 40-60
microseconds per operation. For a trading engine ticking every 200
milliseconds, it’s noise. For a high-traffic broker API it would be
critical, but we’re not a broker.
Storage grows by roughly 4-6 KB per trade. A year of live trading strategy is 20-40 MB of ledger. The portfolio side is an order of magnitude smaller because the rebalance runs once a week. Free on any modern-disk scale.
Net: the operational cost is near zero, and the payoff is the inability to quietly rewrite the past. It’s the rare architecture choice where you don’t have to trade one for the other.
— inite team
- 2026-04-19Per-strategy ownership: your kitchen, your keys
We dropped the global trading-user model. Each strategy now reads venue keys from its owner's vault.
- 2026-04-22The non-linear trading balance
Why your trading NAV is allowed to be greater than your allocation — and why we keep them in different DBs.
- 2026-05-09When the engine stops
The algorithm runs itself. But in three cases it asks the operator, and in one more it simply lies down. A walk-through of how the HIL queue, strategy modes, and the kill switch fit together — and where the line between them is.