Worked examples · 05 of 07
Metered API
A compute provider sells a metered API to thousands of customers at per-second cadence, batching usage into signed reports and generating each monthly invoice as a projection of the receipt chain.
- SIZE
- fleet-scale
- PROFILE
- mk-se-b2b
- STRESSES
- Machine-speed cadence with batch windows, template/instance at fleet scale, and the invoice as a projection of the receipt chain rather than a document judged against it.
§3.3 claims the same primitives that judge a monthly kommunal invoice can gate machine-to-machine commerce at per-second cadence. This example cashes that claim: a compute provider sells a metered API to thousands of customers - each customer a template instance, usage flowing at hundreds of calls per second, settled monthly with an invoice that is GENERATED from the receipt chain instead of checked against it. One design question falls out immediately: nobody sane receipts every API call. Also covers the queued per-second scenario candidate from the scenarios doc.
Profile: mk-se-b2b (or any b2b profile - nothing here is Swedish except the example’s currency).
The inversion worth noticing
Every other example judges documents a human sent. Here the flow reverses: the receipt chain is the source of the invoice, not its judge. The monthly invoice is a projection of the log - fold(usage receipts) × tier table = invoice lines - so a billing dispute is not “check the invoice against the meter” but “replay the chain”; the invoice cannot disagree with the meter because it IS the meter, rendered. Telecom has run this shape for forty years (CDR-based settlement: call detail records aggregated into invoices); we add the part telecom never had - the tariff signed as a contract both machines can evaluate.
The contract (template; each customer = an instance)
mechanical-contract v0 TEMPLATE
id: tpl-compute-metered
parties:
provider: org 559333-4455 "Compute AB" sign eIDAS-seal
customer: from parameters sign eIDAS-seal | api-key-ceremony
parameters:
tier-binding lookupOf<usage-band, money> # which price card this customer got
monthly-cap money # spend ceiling: the takvolym, tiny
money: currency SEK rounding total half-up 0.01
state:
pool spend.month capacity {monthly-cap} resets per period # periodic
register usage shape { batch: id, calls: quantity<count>, # pool reset is
window: period, meter-hash: sha256 } # not in the spec
artifacts:
tier-table from parameters pin at genesis # price card: governed updates,
# at-most 1 per quarter, notice 30d
ports:
in usage.report from provider.meter payload signed-usage-batch # metered port
in usage.dispute from customer payload signed-claim
in payment.confirm from customer payload iso20022.pain.002
out invoice.issue to customer monthly, projected from usage register
out receipt.emit to submitter always
cadence:
usage.report at-most 1 per minute # BATCHES at minute cadence, calls at
# per-second cadence INSIDE the batch
usage.dispute within 30 days of invoice.issue
due payment within 20 days of invoice.issue
breach: suspend service per prose; dröjsmålsränta per räntelagen
rule on usage.report:
if payload.window overlaps accepted batch -> reject "window overlap" # gapless,
if payload.window not contiguous with last -> reject "window gap" # non-double-
spend := Σ payload.calls × tier-table rate # counting by
if spend.month consumed + spend > monthly-cap -> reject "cap breach" # construction
draw spend.month amount spend
record usage { ... }
accept # ~1 receipt per minute per customer:
# 43_200/month, a boring log
rule on invoice.issue (monthly): # the projection: no judgment, no
lines := fold(usage where period = month) grouped by band # human input -
emit invoice (peppol.bis-billing-3, lines, ref usage batch hashes) # the invoice
# cites the receipts it folds
Where the per-second story actually lives
The contract never sees individual calls - and must not. The per-second gate is pre-validation at the edge (§3.3): the provider’s gateway holds (contract file, current state hash) and evaluates would this call breach the cap? locally, statelessly, in microseconds, throttling before the call runs. The signed batch then makes the minute’s truth contractual. Cap enforcement is thus split honestly: advisory at wire speed, binding at batch cadence - the cap can be overshot by at most one batch window, and the contract’s overshoot rule (reject vs accept-and-carry) is a declared term. Same split as pre-validation everywhere else in the design: cheap optimistic checks anywhere, authoritative judgment at the log.
What this example stresses
| Edge | Spec section |
|---|---|
| Cadence far below human rhythm; batch windows | §3.2-3.3 |
| Template/instance at fleet scale, per-customer tiny logs | §6.3 |
| Invoice as projection OF the chain (referee both directions) | §2, §3.3 |
| Governed price-card updates under notice | §5.2 |
| The meter as executor-party whose reports are guarded, never trusted | §3.6, §5.5 |
Questions this example forced
- Batching is a trust decision, and it needs a §-home. A receipt per call at 500 calls/s is 1.3 billion receipts/month - not a log, a landfill. Batching is unavoidable, but the batch boundary is where trust concentrates: whoever aggregates (here: the provider’s meter) is an executor-party whose signed batches the customer can dispute but not independently reproduce. Proposed: name the pattern in the spec - metered ports declare (window size, gapless/contiguity rule, meter identity, dispute window, optional customer-side counter-meter reconciliation à la §3.6’s camt.053 sweep). The contiguity guards above make silent gaps/double-counts structurally rejectable, which is most of the honesty; the residual “meter lies within a window” risk is exactly telecom’s CDR-audit problem, solved there by counter-metering, named here as the profile’s option.
- Periodic pools.
spend.monthmust reset monthly; pools today only fill at genesis and drain by draws. Cadence buckets (§3.2) already reset implicitly - a spend cap is the same token bucket wearing money units. Proposed: allowresets per <period>on a pool, defined as event-timestamp arithmetic (a draw in a new period folds against a fresh window), i.e. specified BY REDUCTION to cadence semantics, no new machinery. Cheap; touches §3.4’s pool grammar. - Out-ports that originate documents.
invoice.issuehere is not a judgment on a submission - the contract composes a document from its own state on a schedule. §2’s worked example deliberately CLOSED invoice-out; this example needs it open, in gateway posture. The machinery exists (out-port + due + projection is a pure function of the log), but the spec never says a rule may EMIT a composed standards-payload rather than a verdict. One paragraph in §3.4 or §7.5 territory; flagged so it gets written deliberately rather than assumed.
What the example proves: §3.3 was honest. Nothing in the machine class changed between the kommunal ramavtal and the per-second meter - only the numbers, the batch window, and where pre-validation runs. The three creaks are additive vocabulary (metered ports, periodic pools, composed emissions), not cracks in determinism.