Metering & Rate Card

Every metered surface flows through the gateway (the single chokepoint) and settles against the caller's prepaid wallet. This page is the shared vocabulary the surfaces were missing: for each one, who pays, at what rate, and by which mechanism.

Cost model: (provider costs + payment-rail fee) × one margin#

Rates are not hand-tuned per surface. Every constant in packages/gateway/src/pricing.ts is a measured cost — either a number on a Cloudflare/Neon/OpenRouter invoice, or Stripe's percentage cut on top-up (TALLPOND_STRIPE_FEE_FACTOR, default 1.029/2.9%, folded in because there's no per-op way to attribute which top-up dollar funded which charge) — and one global TALLPOND_MARGIN (default 1.05/5%) is applied on top, at settlement, everywhere. The margin is the only revenue knob; it's kept thin on purpose and covers only what isn't individually measurable — fixed overhead (Workers base fee, Ory, domains) and cost-estimation error. Stripe's fixed $0.30/transaction isn't in the factor; it's covered by the $10 top-up minimum instead.

Provider costs (env-overridable; verified 2026-07):

CostValueSource
Workers request$0.30/M (0.3 µUSD)Cloudflare
R2 Class A op (write)$4.50/M (4.5 µUSD)Cloudflare
R2 Class B op (read)$0.36/M (0.36 µUSD)Cloudflare
R2 at-rest$0.015/GB-monthCloudflare (egress is free)
Neon at-rest$0.35/GB-month, ×1.5 physical-bytes padNeon
OpenRouter credit fee5.5% of list-rate spendOpenRouter
Stripe credit fee2.9% of top-up spend (fixed $0.30 covered by top-up minimum)Stripe

Mechanisms#

Three settlement mechanisms, chosen by op shape:

Every settlement carries a budget deployment scope: '' for production, or the pinned environment deployment ID for test-session traffic. A test session's real developer payer overrides synthetic actor/resource ownership. Recurring at-rest cost is already incurred and therefore cannot be rejected: it counts against the same scoped budget and may push balance/spend over their limits; subsequent discretionary operations are then rejected until funds/cap recover.

Rate card#

Charges below are cost × Stripe fee factor × margin; at the defaults (1.029 × 1.05 ≈ 1.08 combined):

SurfaceOpPayerCharge (default)Mechanism
AIchat completioncallerOpenRouter list rate × 1.055 (their credit fee) × 1.029 (Stripe) × marginescrow (ai_completion)
Databaseread / writeprivate → user; resource-owned → resource payer policy; creator-owned → actorrequest-total compute estimates (see query.ts PRICE) × 1.029 × margin, rounded once per payer/requestatomic direct charge (db_usage)
Databasebytes at restrow owner; resource-owned rows → resource owner$0.35/GB-month × 1.5 pad × 1.029 × marginat-rest sweep (db_at_rest)
Storageupload / copyprivate → uploader; resource bucket with payer:"owner" → resource owner; else uploaderflat ~7 µUSD (Class A + request, × 1.029 × margin)escrow (storage_write)
Storagebytes at restsame payer as the write$0.015/GB-month × 1.029 × marginat-rest sweep (storage_at_rest)
Storagedownloadthe fetching sessionflat ~1 µUSD (Class B + request, × 1.029 × margin); per-byte defaults to 0 — R2 egress is freeatomic direct charge (storage_egress)
Realtimepublish (fan-out to one scope)same payer as the write that caused it~21 µUSD when the write woke the coordinator, ~3 µUSD when it was already resident; 0 when nothing was deliveredpost-hoc direct charge (realtime_publish)
Functionsinvokecaller (functions always run as the invoking user)25 µUSD base + 15 µUSD/s wall clock (see routes/functions.ts PRICE), × 1.029 × margin; every ctx.* op inside the function bills through its own row aboveescrow (function_invoke)

The AI model catalog (/v1/ai/models) reports per-token rates already scaled to charge rates, so what an app sees is exactly what it is held against and settled at. The escrow hold additionally carries a 1.25× estimation-safety multiplier — that is slack for tokenizer variance, not margin.

Realtime is priced per wake, not per message#

The unusual line in the table above, and the reasoning is worth stating because the obvious rate card is wrong.

A publish's real cost is almost entirely Durable Object residency. Measured (experiments/idle-socket-cost, 2026-07-29), an object stays resident ~10–12 seconds after its last activity before hibernating, so any activity is billed at least that much. Meanwhile the two components that look like they should dominate are ~zero: Cloudflare does not bill outbound WebSocket messages at all, and the CPU to fan out to N sockets is inside the wake that was already paid for.

So a publish is charged one residency floor plus two coordinator storage writes, and only the first of those when the coordinator was actually asleep. Three consequences:

A publish nobody received is free. The coordinator still records the change — the ring buffer is what lets a disconnected client resume — but charging for that would tax every write on the platform for a feature the scope is not using. The platform absorbs it; this is the same principle that leaves connection-seconds unbilled.

This is the one direct charge settled after the fact rather than escrowed, which is a deliberate exception to "escrow before use" below. A hold exists to refuse an operation the payer cannot afford — but a write is the publish, and the write's own charge already passed the wallet check and the app cap inside its transaction. A second gate would cost two extra ledger transactions on every write in order to charge nothing in the common case. The publish therefore settles in unavoidable mode and can push a wallet a few µUSD past its balance or cap, which the existing grace lifecycle already handles. Refusing after delivery would not un-deliver anything.

DB per-op estimates are the fuzzy spot#

Neon bills awake compute time, not queries, so one query's marginal cost depends on tenancy utilization. The PRICE table in db/query.ts is a calibrated estimate; recalibrate it periodically against (Neon bill ÷ ledger op volume). The byte dimension per-row pricing can't see is handled by the at-rest sweep, so drift here is bounded to compute.

Invariants#