Open Standard · Working Draft
mekaniskt·kontrakt

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.month must 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: allow resets 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.issue here 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.