Throughput
We did not have this number. This is the first time it has been measured, and nothing was tuned to make it look better.
What was measured
Two paths. The decision path is an intent through the real gate, with the gate context rebuilt from live store state exactly as the settler rebuilds it, and the settler stubbed out. The full kernel path is the same decision plus the signed audit chain, the stored intent, a settle over a deterministic in-process fake rail, and the receipt read back. The stack is the gateway composition root over the same sandbox rail the eval package grades correctness on. No real network was contacted at any point.
- Machine
- AMD Ryzen Threadripper PRO 5975WX, 32 cores, 64 logical cores, 256 GiB RAM, Windows, x64.
- Runtime
- bun 1.3.7.
- Rail
- Fake in-process rail, no network. Injected clock, simulated, advancing 8 seconds per payment.
- Gate config
- Kernel defaults except the velocity count, raised to 100,000,000. At the default of five per hour every payment after the fifth correctly routes to
confirm, and the run would be measuring the confirm path while calling it the allow path. - Warm-up
- 2,000 operations discarded before each measured window.
Decision path, settler stubbed
concurrency ops/sec p50 ms p95 ms p99 ms max ms
1 127,507 0.0068 0.0102 0.0157 2.9839
2 121,403 0.0146 0.0214 0.0274 2.0557
8 128,721 0.0561 0.0681 0.0849 1.8363
32 127,850 0.2307 0.2645 1.4089 2.9941
128 129,832 0.9165 1.1868 2.4950 2.8692Peak observed: 129,832 decisions per second in a single process. Every row was 50,000 allows, no confirms and no denies.
Concurrency here is workers interleaving at await points in one single-threaded process, not parallelism. The decision itself is synchronous work, so raising concurrency cannot add decision capacity. The sweep is there to show what it actually does, which is add overhead and push out the tail.
Decision plus settlement
concurrency ops/sec p50 ms p95 ms p99 ms max ms
1 196.5 4.9271 9.562 11.4 15.3
2 197.5 9.8443 18.9 20.8 24.8
8 200.4 38.8 76.3 81.8 83.8
32 199.9 158.4 300.3 308.8 311.3
128 210.6 604.6 1,143 1,194 1,206The gap against the decision path is not the cost of a rail. The fake rail mints an object and returns. It is the cost of everything the kernel does around the decision so that the decision can be proved later: three signed, hash-linked audit appends, the stored intent, the receipt, and the read-back.
The settle path has no steady-state rate
settled payments in run ops/sec p50 ms p99 ms
250 1,200 0.8319 1.5381
500 689.3 1.3859 3.8359
1,000 371.6 2.6362 6.809
2,000 193.7 5.1458 11.4
4,000 94.5 10.3 23Throughput falls as the run gets longer, and this is not noise. The reference in-memory store derives period spend, known payees and trust by scanning stored intents, and the settler scans the audit chain for multi-step risk chains, so per-payment cost grows with the payments already settled in the period.
Batching
batch decisions/sec vs batch=1 p50 ms p99 ms
1 96,741 1x 0.0082 0.0265
2 105,292 1.09x 0.0182 0.0375
4 194,469 2.01x 0.0199 0.0306
8 206,531 2.13x 0.0367 0.0555
16 224,637 2.32x 0.0698 0.0873
32 219,559 2.27x 0.141 0.1911
64 237,010 2.45x 0.2686 0.3035
256 235,442 2.43x 1.0535 3.2655
1024 240,109 2.48x 4.216 4.5419
4096 240,116 2.48x 16.9 17.8Best observed: batch size 4,096 at 240,116 decisions per second. Read the multipliers as approximate. The batch-of-one row at 96,741 and the decision path at concurrency one at 127,507 do the same work by different routes, and they differ by 24% in this run. That difference is the harness's own run-to-run variance on this machine, and it is not smaller than some of the steps in the batching curve. It is left in rather than smoothed out.
What batching amortises is the gate-context snapshot, the store reads, not the decision. The curve flattens once the snapshot cost is spread thin enough to stop mattering, after which the per-decision arithmetic is all that is left and a larger batch buys nothing.
What this does not measure
Stated plainly, because these are the claims the numbers do not support.
- One process on one machine. It is not a distributed system result.
- It says nothing about what a hosted deployment would sustain. There is no network, no HTTP server in the loop, no authentication, no durable store, no replication, no contention between instances and no real rail. Every one of those subtracts.
- It is not a latency-under-load result. The driver is closed-loop: it reports service time per operation and cannot report the queueing delay an open-loop arrival process would add.
- The fake rail's settle cost is zero by construction, so nothing here estimates the cost of real settlement. That was deliberate: settlement cost belongs to the network, and measuring it against a real one would be measuring the network.
- Nothing here is extrapolated to per-day or per-year volume. A rate observed for a few seconds on an idle machine does not multiply.
Reproducing it
The benchmark ships with the platform and rewrites its own results with what your machine did. A regression guard reads the recorded JSON and fails only on a large drop, so ordinary machine variance does not turn into a red build.
bun run packages/throughput/src/run.ts