Open-Sourcing Tollgate: Bounded Spend Without a Network Hop per Request
Tollgate is an open-source Rust library for quota admission and usage accounting. Each instance spends a fenced lease locally, so a metered request is admitted, charged and billed without synchronous I/O, and the spend stays bounded by what the account was allocated.
By MorphIQ Labs · Platform
Today we are open-sourcing Tollgate, a Rust library for quota admission and usage accounting in latency-critical services. It is MIT or Apache-2.0, its crates are on crates.io, and it ships with the evidence we use to check that it does what it says.
The problem
A metered API answers two questions on every request. May this caller do this, and what does it cost? Then it records the answer, because that record is what the customer is billed for.
The usual answers put something slow on the request path. A database row per account, or a shared counter in a cache, means a network round trip before any work starts. That is acceptable when the work takes tens of milliseconds. When the work itself takes nanoseconds, the check costs orders of magnitude more than the thing it guards.
Removing the round trip usually costs correctness instead. A local counter cannot see what other instances spent. Counting asynchronously and reconciling later lets an account overspend before the reconciliation catches up, and a crash between work and record loses the charge.
Tollgate is built for services that need both: no synchronous coordination per request, and spend that stays bounded by what the account was allocated, with every committed unit reaching the billing record.
Two planes
Tollgate splits the work into two planes.
The request path runs on local state. Each account is compiled into an immutable snapshot: its status, permissions, limits and a direct-indexed cost table. Funding comes from a lease, a block of units the control plane debited from the account's central balance and handed to this instance, which spends it with atomic counters. Admitting a request is a snapshot lookup, a permission check, a quote, a rate token and a lease debit. It performs no I/O, takes no blocking locks, and reads no clock for a policy decision: the caller passes the time in.
The control plane does everything slow in the background. It distributes snapshots and revocations, keeps each instance's lease topped up, and ingests usage events in idempotent batches. Those events are the billing record.
A request moves through typed stages:
- It reserves a slot in the usage queue before its body is read, so an overloaded accounting pipeline sheds load before any work is done.
- Admission debits the lease and opens a pending charge.
- The charge commits when execution actually starts.
- Dropping the committed guard records the billing event, on every exit path Rust controls, including a panic that unwinds.
Cancelling before execution charges nothing. Anything unknown, stale, exhausted or backpressured is refused, with zero units charged. There is no slower fallback, because a fallback that reached the database would put I/O back on the request path.
On an Apple M1 Pro, the full admission path measured about 134 ns in a Criterion benchmark under the release profile. That is a microbenchmark on one controlled host, not an end-to-end latency figure; the repository records the workload, revision and conditions alongside the gates that check it.
Why leases
Leases are how many instances enforce one balance without coordinating per request. Units leave the central balance when a lease is granted, so the instances together cannot spend more than was allocated to them.
Each lease carries a fencing token. The store accepts usage and releases only against the exact capability it issued, so one lease's holder cannot settle against another. A lease expires: its holder stops spending before the store's reclaim cutoff, and a lease abandoned by a crash is forfeited as settlement loss, so work that may have run is never credited back to the account.
For accounts that should keep working through a funding gap, an elastic mode admits past an unfunded lease up to a per-instance cap, and records every such unit as overage. Every backend asserts one ledger equation per account in its test suite:
deposited + overage_recorded
== balance + active grants + settled usage + settlement loss + expired
How the claims are checked
Correctness is the product here, so each claim is tied to something that checks it.
- Invariants. The contract is 40 numbered invariants. Each one names how it is enforced (by types, by the component that owns the state, or as a tested convention) and the tests that witness it. CI fails if a cited test or theorem stops existing.
- An executable specification. The in-memory store is the reference implementation. The PostgreSQL backend passes the same scenario suite, test by test, and a check fails if a mirrored test drives a different contract on each side.
- Mutation testing. Code a pull request changes is mutated, and a mutant that no test catches fails the build.
- Machine-checked proofs. 26 of the 40 invariants cite Lean 4 theorems:
24 modules and 346 theorems covering lease fencing, the ledger equation, a
request's charge lifecycle, idempotent ingest, snapshot revocation, sharded
counters and more. None use
sorryor axioms. - Mutation-tested proofs. A proof is only as strong as what it states, so the models are mutated too: a tool changes one operator at a time in each model's transitions and requires a theorem to fail. That keeps the proofs precise about exact behaviour, such as a limit accepting exactly what fits as well as refusing what exceeds it. It runs on every pull request.
The scope matters as much as the evidence. The Lean models prove properties of the design; property tests, backend parity and mutation testing are the argument that the Rust implements it. There is no machine-checked link between the two. Performance numbers are host-specific. And if the process is killed, usage it had not flushed is lost, while its leases are forfeited so the account is never credited for work that may have run. The documentation states each of these boundaries.
Open, with the techniques as prior art
Tollgate is MIT or Apache-2.0. The techniques it composes are published in the repository as prior art, with no patent claims.
Try it
[dependencies]
tollgate-core = "0.30.1"
tollgate-admission = "0.30.1"
tollgate-client = "0.30.1"
The Getting started guide builds a metered service's admission layer in one process. It provisions an account, starts the runtime, serves a request, shows refusals, shuts down and checks that the ledger balances. Its code is a test in the repository, so it cannot drift from the API. There is also a complete example HTTP service to run and call.
- Documentation: morphiq-labs.github.io/tollgate
- Repository: github.com/MorphIQ-Labs/tollgate
- API reference: docs.rs/tollgate-core
Tollgate is pre-1.0. A breaking change moves the minor version, and the changelog marks every one. Issues and pull requests are welcome, and the contributing guide explains the gates. If you find something that lets work run uncharged or bills it twice, please report it privately through the repository's security advisories.
More from Platform
July 6, 2026 · 2 min
Deterministic Replay for Trading Systems
The most expensive production bugs in trading systems are often not the ones that crash. They are the ones that cannot be reproduced. A signal changed, an order decision differed, a timeout fired in a different order,…
May 19, 2026 · 4 min
Determinism as a Correctness Contract
Most trading systems are tested the way most software is tested: feed an input, assert an output, hope the parts you did not assert on behaved. That works until the part you did not assert on is wall-clock time, or it…