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):
| Cost | Value | Source |
|---|---|---|
| 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-month | Cloudflare (egress is free) |
| Neon at-rest | $0.35/GB-month, ×1.5 physical-bytes pad | Neon |
| OpenRouter credit fee | 5.5% of list-rate spend | OpenRouter |
| Stripe credit fee | 2.9% of top-up spend (fixed $0.30 covered by top-up minimum) | Stripe |
Mechanisms#
Three settlement mechanisms, chosen by op shape:
- Escrow (per-op). High-value or unbounded-duration ops place a hold that
bounds the spend before running, then capture the actual cost on completion
(
placeHold→captureHold/releaseHold). A crash releases the hold via the periodic sweeper. Used by AI completions and file writes (upload/copy) — the write charge is small, but the hold is what gates uploads on solvency and the app cap. - Atomic direct charge (per request). DB operations, file egress, and file link/unlink settle one integer-micro-USD charge per payer per HTTP request. App-schema mutations and the wallet/budget/ledger updates share one Postgres transaction, so unpaid work rolls back rather than committing ahead of its charge. Fractional DB estimates are summed per payer and rounded once for the request; a positive request therefore has a minimum durable charge of 1 µUSD.
- At-rest sweep (recurring). Bytes sitting in R2 and Neon cost money every
month. The sweep (
packages/gateway/src/at-rest.ts, piggybacked on the cron/interval sweeper) creates a durable byte/cost snapshot for one fixed elapsed window, then settles each payer with a deterministic idempotency key. Interrupted windows resume from their uncharged items, and the completed checkpoint advances only after every item is durable. Sub-µUSD dust rounds to zero rather than being ceiled up.
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):
| Surface | Op | Payer | Charge (default) | Mechanism |
|---|---|---|---|---|
| AI | chat completion | caller | OpenRouter list rate × 1.055 (their credit fee) × 1.029 (Stripe) × margin | escrow (ai_completion) |
| Database | read / write | private → user; resource-owned → resource payer policy; creator-owned → actor | request-total compute estimates (see query.ts PRICE) × 1.029 × margin, rounded once per payer/request | atomic direct charge (db_usage) |
| Database | bytes at rest | row owner; resource-owned rows → resource owner | $0.35/GB-month × 1.5 pad × 1.029 × margin | at-rest sweep (db_at_rest) |
| Storage | upload / copy | private → uploader; resource bucket with payer:"owner" → resource owner; else uploader | flat ~7 µUSD (Class A + request, × 1.029 × margin) | escrow (storage_write) |
| Storage | bytes at rest | same payer as the write | $0.015/GB-month × 1.029 × margin | at-rest sweep (storage_at_rest) |
| Storage | download | the fetching session | flat ~1 µUSD (Class B + request, × 1.029 × margin); per-byte defaults to 0 — R2 egress is free | atomic direct charge (storage_egress) |
| Realtime | publish (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 delivered | post-hoc direct charge (realtime_publish) |
| Functions | invoke | caller (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 above | escrow (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 busy scope amortizes. Messages arriving closer than ~11s apart keep the object resident, and each pays only its storage writes — matching the fact that they genuinely add no residency.
- A quiet scope pays the floor, which is also what it actually costs. One message an hour costs the same object-time as fifty in one second.
- Fan-out size is not a billing dimension. Delivering to 2 sockets and to
200 costs the same, because it does. The per-recipient rate exists
(
TALLPOND_COST_REALTIME_PER_RECIPIENT_MICRO) and defaults to zero, the same way R2 egress does.
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#
- Single chokepoint — no resource is reachable except through the metered gateway, or metering leaks.
- Escrow before use — every escrowed op holds funds before running; capture
cannot exceed the reserved estimate. The one exception is
realtime_publish, which settles after delivery because the write that caused it was already gated; see above. - Work and charge together — discretionary DB/file metadata mutations and their direct charge commit in one transaction or both roll back.
- Scoped budgets — production and each environment settle into distinct budget scopes; synthetic test actors never become wallet payers.
- Retry-safe recurring billing — at-rest snapshots and deterministic keys make interrupted settlement resumable without duplicate debits.
- Every byte at rest belongs to a wallet that is either paying or on a clock
toward deletion — a negative balance starts the
grace → frozen → reclaimeddelinquency lifecycle rather than accruing unbounded debt; seeapi/billing.md. - Stripe transacts only at top-up — usage never calls Stripe or touches a
card; it settles against the prepaid balance. Stripe's fee is still priced
into every charge (as
STRIPE_FEE_FACTOR, a cost) because top-up dollars and usage charges can't be attributed to each other. - Cost and margin never mix — a rate constant is either a measured cost (provider invoice or payment-rail fee) or the one global margin; no surface hides revenue in a padded "cost".