Quota admission and usage accounting for latency-critical services: fenced leases, local atomic counters, and a conserved usage ledger.
Rust
0
454 commits
updated Sep 29, 2026
Quota admission and usage accounting for latency-critical services, with no I/O on the request path.
A metered API has to answer "may this caller do this, and what does it cost?" on every request, and record the answer for billing. The usual answers put a database or a shared counter on the request path, which costs orders of magnitude more than the work it guards when that work takes nanoseconds or microseconds. Rate limiters remove the round trip but don't bill anything.
Tollgate splits the problem into two planes:
The core is domain-agnostic. It knows cost units, operations by index and permission bits, never plan names, product currencies or SQL.
Tollgate is built so that its claims are checked, not asserted:
deposited + overage_recorded == balance + active grants + settled usage + settlement loss + expired.sorry or axioms, model a request's charge
lifecycle, lease timing and fencing, idempotent ingest, sharded counters,
snapshot revocation, conservation and more. CI checks them on every pull
request.The library crates release together at one version. Depend on the ones you need, at the same version:
[dependencies]
tollgate-core = "0.30"
tollgate-admission = "0.30"
# The managed runtime: snapshot distribution, lease renewal, usage batching.
tollgate-client = "0.30"
| Crate | Role |
|---|---|
tollgate-core | Zero-I/O, clock-free types: cost units and tables, account snapshots, fenced leases, reservations |
tollgate-admission | The per-request admission pipeline |
tollgate-auth | Credential verification: the HMAC registry and session credentials |
tollgate-store | Backend traits, the in-memory reference store, wire types |
tollgate-store-postgres | The transactional PostgreSQL backend |
tollgate-client | InstanceRuntime, the HTTP store client, key and period management |
tollgate-server | The authenticated control-plane server |
begin looks the caller up and pins its snapshot, admit quotes the
operation and debits the lease, commit charges at execution start, and
dropping the committed guard records the billing event. In a service, the
snapshot and lease below arrive from the control plane.
use std::sync::{Arc, Mutex};
use jiff::Timestamp;
use tollgate_admission::{
AdmissionEngine, ArcSwapSnapshotMap, LeaseSlot, NoGate, Principal, SnapshotMap,
};
use tollgate_core::{
AccountId, AccountSnapshot, AccountStatus, CostTable, CostUnits, FencingToken, Generation,
LeaseGrant, LeaseId, LocalLease, OpIndex, PermissionBits, PublishableSnapshot, RequestId,
ResolvedLimits, UsageEvent, UsageSlot,
};
/// The one operation this API meters.
struct Price;
impl OpIndex for Price {
fn index(&self) -> usize {
0
}
}
/// Where committed charges go. In a service, a permit from the usage writer.
struct Billing(Arc<Mutex<Vec<UsageEvent>>>);
impl UsageSlot for Billing {
fn record(self, event: UsageEvent) {
self.0.lock().unwrap().push(event);
}
}
let now = Timestamp::from_second(1_755_600_000).unwrap();
let expires = Timestamp::from_second(1_755_600_060).unwrap();
// From the control plane: the account's compiled snapshot (1 unit per
// request plus 2 per priced item) and a 1,000-unit lease.
let costs = CostTable::builder(CostUnits(1), CostUnits(1))
.weight(&Price, CostUnits(2))
.build();
let snapshot = AccountSnapshot::builder(
AccountId(1),
Generation(1),
AccountStatus::Active,
expires,
PermissionBits::bit(0),
ResolvedLimits::new(64),
Arc::new(costs),
)
.build();
let lease = LocalLease::new(
LeaseGrant {
lease_id: LeaseId(1),
account_id: AccountId(1),
fencing_token: FencingToken(1),
units: CostUnits(1_000),
expires_at: expires,
},
CostUnits(100),
);
let engine = AdmissionEngine::new(ArcSwapSnapshotMap::new());
let slot = LeaseSlot::for_account(AccountId(1));
drop(slot.replace(Arc::new(lease)));
engine
.map()
.install_publishable(
Principal(42),
PublishableSnapshot::try_new(Arc::new(snapshot)).expect("valid snapshot"),
slot,
)
.expect("installed");
// The request path: no I/O from here on.
let billed = Arc::new(Mutex::new(Vec::new()));
let committed = engine
.begin(Principal(42), PermissionBits::bit(0), now)
.expect("known, active, permitted caller")
.admit(&[(Price, 3)], Billing(Arc::clone(&billed)), now)
.expect("within limits and funded")
.acquire_capacity(&NoGate)
.expect("no capacity gate configured")
.commit(RequestId(1), now)
.map_err(|(error, _released)| error)
.expect("inside the lease window");
// ... do the work ...
drop(committed);
assert_eq!(billed.lock().unwrap()[0].units, CostUnits(7)); // 1 + 2 × 3
Embedding Tollgate gives the supported request order, what you implement, what is sealed, and how to shut down without losing usage.
InstanceRuntime (in tollgate-client) supervises accounts
dynamically: it distributes snapshots, acquires and renews leases, batches
usage, reports continuous readiness, and shuts down within a bound.KeySource, with a KeyManager beside the runtime whose verifier bounds
cached evidence by feed freshness and each key's expiry. See
credential projection.PeriodRoller, which
funds schedules at startup and rolls due periods in bounded batches. See
its lifecycle example. With
HttpStore, the server does period maintenance instead.examples/pricing-api is a complete metered API with
HMAC-verified keys, admission, commit at execution start, and billing:
cargo run -p pricing-api --bin pricing-api
curl -s -H 'Authorization: Bearer demo-key-1' -H 'Content-Type: application/json' \
-d '{"contracts":[{"spot":100,"strike":105,"rate":0.05,"vol":0.2,"tte_years":0.25}]}' \
http://127.0.0.1:8081/v1/price
curl -s http://127.0.0.1:8081/metrics # what it admitted and refused, by reason
Set TOLLGATE_LOCAL_SHARDS=8 only after profiling sustained same-account
contention across cores; instance-local sharding
explains how to size it. The control-plane server starts with
TOLLGATE_SECURITY_CONFIG=/path/to/security.json cargo run -p tollgate-server
once identities and TLS are configured, and refuses remote plaintext and
anonymous calls.
Every binary answers --help and --version before reading any
configuration. pricing-api and tollgate-server take no positional
arguments, and exit with status 2 on any they are given.
The documentation site is at https://morphiq-labs.github.io/tollgate/, and the API reference is on docs.rs.
Tollgate is pre-1.0. A breaking change moves the minor version, so a caret
requirement ("0.30") never crosses one; the changelog marks
every break. The minimum supported Rust version is 1.89, and CI checks it on
every pull request.
See CONTRIBUTING.md for setup, the local gates and the pull-request process. AGENTS.md is the full engineering contract. Report security issues privately; see SECURITY.md. Participation is governed by the code of conduct.
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Tollgate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Tollgate is a product of MorphIQ Labs, a trade name of Prophetizo LLC.
cargo deny --locked check licenses gates every dependency against
deny.toml, which admits only permissive licenses.
Rust
93.6%
Lean
5.0%
Shell
1.3%
Quota admission and usage accounting for latency-critical services: fenced leases, local atomic counters, and a conserved usage ledger.
Rust
0
454 commits
updated Sep 29, 2026
Quota admission and usage accounting for latency-critical services, with no I/O on the request path.
A metered API has to answer "may this caller do this, and what does it cost?" on every request, and record the answer for billing. The usual answers put a database or a shared counter on the request path, which costs orders of magnitude more than the work it guards when that work takes nanoseconds or microseconds. Rate limiters remove the round trip but don't bill anything.
Tollgate splits the problem into two planes:
The core is domain-agnostic. It knows cost units, operations by index and permission bits, never plan names, product currencies or SQL.
Tollgate is built so that its claims are checked, not asserted:
deposited + overage_recorded == balance + active grants + settled usage + settlement loss + expired.sorry or axioms, model a request's charge
lifecycle, lease timing and fencing, idempotent ingest, sharded counters,
snapshot revocation, conservation and more. CI checks them on every pull
request.The library crates release together at one version. Depend on the ones you need, at the same version:
[dependencies]
tollgate-core = "0.30"
tollgate-admission = "0.30"
# The managed runtime: snapshot distribution, lease renewal, usage batching.
tollgate-client = "0.30"
| Crate | Role |
|---|---|
tollgate-core | Zero-I/O, clock-free types: cost units and tables, account snapshots, fenced leases, reservations |
tollgate-admission | The per-request admission pipeline |
tollgate-auth | Credential verification: the HMAC registry and session credentials |
tollgate-store | Backend traits, the in-memory reference store, wire types |
tollgate-store-postgres | The transactional PostgreSQL backend |
tollgate-client | InstanceRuntime, the HTTP store client, key and period management |
tollgate-server | The authenticated control-plane server |
begin looks the caller up and pins its snapshot, admit quotes the
operation and debits the lease, commit charges at execution start, and
dropping the committed guard records the billing event. In a service, the
snapshot and lease below arrive from the control plane.
use std::sync::{Arc, Mutex};
use jiff::Timestamp;
use tollgate_admission::{
AdmissionEngine, ArcSwapSnapshotMap, LeaseSlot, NoGate, Principal, SnapshotMap,
};
use tollgate_core::{
AccountId, AccountSnapshot, AccountStatus, CostTable, CostUnits, FencingToken, Generation,
LeaseGrant, LeaseId, LocalLease, OpIndex, PermissionBits, PublishableSnapshot, RequestId,
ResolvedLimits, UsageEvent, UsageSlot,
};
/// The one operation this API meters.
struct Price;
impl OpIndex for Price {
fn index(&self) -> usize {
0
}
}
/// Where committed charges go. In a service, a permit from the usage writer.
struct Billing(Arc<Mutex<Vec<UsageEvent>>>);
impl UsageSlot for Billing {
fn record(self, event: UsageEvent) {
self.0.lock().unwrap().push(event);
}
}
let now = Timestamp::from_second(1_755_600_000).unwrap();
let expires = Timestamp::from_second(1_755_600_060).unwrap();
// From the control plane: the account's compiled snapshot (1 unit per
// request plus 2 per priced item) and a 1,000-unit lease.
let costs = CostTable::builder(CostUnits(1), CostUnits(1))
.weight(&Price, CostUnits(2))
.build();
let snapshot = AccountSnapshot::builder(
AccountId(1),
Generation(1),
AccountStatus::Active,
expires,
PermissionBits::bit(0),
ResolvedLimits::new(64),
Arc::new(costs),
)
.build();
let lease = LocalLease::new(
LeaseGrant {
lease_id: LeaseId(1),
account_id: AccountId(1),
fencing_token: FencingToken(1),
units: CostUnits(1_000),
expires_at: expires,
},
CostUnits(100),
);
let engine = AdmissionEngine::new(ArcSwapSnapshotMap::new());
let slot = LeaseSlot::for_account(AccountId(1));
drop(slot.replace(Arc::new(lease)));
engine
.map()
.install_publishable(
Principal(42),
PublishableSnapshot::try_new(Arc::new(snapshot)).expect("valid snapshot"),
slot,
)
.expect("installed");
// The request path: no I/O from here on.
let billed = Arc::new(Mutex::new(Vec::new()));
let committed = engine
.begin(Principal(42), PermissionBits::bit(0), now)
.expect("known, active, permitted caller")
.admit(&[(Price, 3)], Billing(Arc::clone(&billed)), now)
.expect("within limits and funded")
.acquire_capacity(&NoGate)
.expect("no capacity gate configured")
.commit(RequestId(1), now)
.map_err(|(error, _released)| error)
.expect("inside the lease window");
// ... do the work ...
drop(committed);
assert_eq!(billed.lock().unwrap()[0].units, CostUnits(7)); // 1 + 2 × 3
Embedding Tollgate gives the supported request order, what you implement, what is sealed, and how to shut down without losing usage.
InstanceRuntime (in tollgate-client) supervises accounts
dynamically: it distributes snapshots, acquires and renews leases, batches
usage, reports continuous readiness, and shuts down within a bound.KeySource, with a KeyManager beside the runtime whose verifier bounds
cached evidence by feed freshness and each key's expiry. See
credential projection.PeriodRoller, which
funds schedules at startup and rolls due periods in bounded batches. See
its lifecycle example. With
HttpStore, the server does period maintenance instead.examples/pricing-api is a complete metered API with
HMAC-verified keys, admission, commit at execution start, and billing:
cargo run -p pricing-api --bin pricing-api
curl -s -H 'Authorization: Bearer demo-key-1' -H 'Content-Type: application/json' \
-d '{"contracts":[{"spot":100,"strike":105,"rate":0.05,"vol":0.2,"tte_years":0.25}]}' \
http://127.0.0.1:8081/v1/price
curl -s http://127.0.0.1:8081/metrics # what it admitted and refused, by reason
Set TOLLGATE_LOCAL_SHARDS=8 only after profiling sustained same-account
contention across cores; instance-local sharding
explains how to size it. The control-plane server starts with
TOLLGATE_SECURITY_CONFIG=/path/to/security.json cargo run -p tollgate-server
once identities and TLS are configured, and refuses remote plaintext and
anonymous calls.
Every binary answers --help and --version before reading any
configuration. pricing-api and tollgate-server take no positional
arguments, and exit with status 2 on any they are given.
The documentation site is at https://morphiq-labs.github.io/tollgate/, and the API reference is on docs.rs.
Tollgate is pre-1.0. A breaking change moves the minor version, so a caret
requirement ("0.30") never crosses one; the changelog marks
every break. The minimum supported Rust version is 1.89, and CI checks it on
every pull request.
See CONTRIBUTING.md for setup, the local gates and the pull-request process. AGENTS.md is the full engineering contract. Report security issues privately; see SECURITY.md. Participation is governed by the code of conduct.
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Tollgate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Tollgate is a product of MorphIQ Labs, a trade name of Prophetizo LLC.
cargo deny --locked check licenses gates every dependency against
deny.toml, which admits only permissive licenses.
Rust
93.6%
Lean
5.0%
Shell
1.3%