Cost Dashboard
SPENT — real, this window
measured account meters
PROJECTED — month end
same usage, run-rated
🔌 Connection — Worker URL & admin token

💰 What the account actually spent

measured meters · /admin/usage

No measured data yet — open 🔌 Connection above, or press ⬇️ Load real usage in the top bar.

Cost breakdown — measured meters

How this window is priced

How this basis is computed

Meter units / month included status rate $
Variable meters — 8 lines above
Base — Workers Paid, flat
💰 REAL COST
+ AE preview meters, if Cloudflare ever bills them

🔮 End of billing period, at the current rate

Meterso farper dayat period end % of tier$
Variable meters — 0 lines above
Base — Workers Paid, flat
💰 PROJECTED BILL AT PERIOD END

Previous billing period

📊 Where the requests go, and how close each meter is to charging

measured · /admin/usage + /admin/stats

No measured data yet — press ⬇️ Load real usage in the top bar.

📊 Requests → cache → delivery

L2 · edge cache

L3 · billed requests — what they bought
The three buckets are exclusive and sum to the billed total — that is why they get a composition bar

Free-tier headroom

Spend by meter

Meterunitsfree tier$ / month · share of spend

Preview meters (not billed)

🛡️ What the targeting gate would cost

left column measured · right column modelled

The comparison below needs a load — press ⬇️ Load real usage in the top bar. The modelled ladder underneath it works without one.

🛡️ EdgeGate — now vs gated

Meterunits / monthfree tiercost / month
now 📡 measuredwith EdgeGate ⚠ modellednow 📡with EdgeGate Δ
💰 TOTAL
Hypothetical (if the AE preview meters — writes + SQL reads — get billed; free today)

This Δ is CPU + R2 only; it cannot see the /geo request the gate removes nor the 300 s dedupe its no-store 403 destroys

🛡️ Modelled delta by volume ⚠ modelled

Worker req / monthCPU-ms nowCPU-ms with EdgeGate$ now$ with EdgeGateΔ
The bill is identical at every volume below — all of it CPU

Ladder assumptions: 90% cache hit · 0.20 ms/req today · +0.01 ms warm · refills 2× miss at 0.30 ms

📐 What a given volume would cost

derived — no $ here is a bill
Every cost in this tab is DERIVED; two of its assumptions can be replaced with measurements

Every COST in this tab is DERIVED: a published Cloudflare rate multiplied by a volume you choose below. No $ figure here is a bill. The rates are the frozen constants in the first card — read from cost-model.js, the single source of truth for the whole panel; the volumes are the editable assumptions, each shown with its documented default. Two of those assumptions — edge cache hit-rate and CPU-ms / requestcan be replaced with values measured in the 💰 Spend / 📊 Meters tabs; when you do that, the field says MEASURED and names the endpoint that answered. Everything else stays an assumption and says so. Where a measured value appears here for context it carries a 📡 or 📊 prefix and names the endpoint that answered. Anything without one of those prefixes is model output.

💵 Rates — fixed inputs

Requests per pageview
  • wrapper1 Worker req (KV only on a cache miss, 1 AE write)
  • engineR2: $0 Worker, free egress
  • ad configR2: $0 Worker (rotation mode only, cfg.analyytics.com, edge-cached; single-URL mode = no cfg req)
  • /geo1 Worker req (rotation mode only), {cc,vpn} cached 12 h in localStorage
Rates (Workers Paid, / month)
estimate
💰 Monthly cost
hypothetical
If AE preview meters get billed
Per 1,000 pageviews
average over the month
pageviews / day
1K/d10K100K1M10M100M/d
⚙️ Assumptions
default: rotation — /geo once per 12 h (live)
default 5 pageviews
default 99% — measurable in 📊 Meters
default 0.1 ms
default 1 (log every request)
default 50 / day
default 30 variants

Cost breakdown — USD / month

Meter units / month included status rate $
💰 MONTHLY COST
Hypothetical (if the AE preview meters — writes + SQL reads — get billed; free today)
What this wrapper mode implies

👤 What one visitor costs

billed today
💰 Per 1,000 visitors
hypothetical
If the AE preview meters get billed
all-in
Incl. proxycheck.io
What you owe today, and what is owed regardless of traffic

⚙️ Inputs — MEASURED vs ASSUMPTION

🔗 One cold visitor, event by event

#EventHostMeterrateqty$ / visitor$ / 1,000
💰 BILLED TODAY — one cold visitor
AE preview (what it would cost — $0 owed) · proxycheck.io (external)

What the chain adds up to, and why rows 5-8 are weighted

🧭 The cases

Casebasis$ / unit$ / 1,000$ / 1M+ AE / 1M (preview)+ external / 1M
A refused visitor re-bills on EVERY pageview; a delivered one bills once per 300 s — and case S prices a whole visitor, not a pageview

📊 Cost per 1,000 visitors

pageviews / visitorbilled /w/ / visitorWorker req / 1,000 visitors$ / 1,000 visitors (today)$ / 1,000 (if AE bills)$ / 1,000 pageviews
How the billed request count is derived

🛡️ EdgeGate — per OUT-OF-TARGET visitor

Metertoday — gate offarmed — enforceΔ / 1M refused
💰 NET — Cloudflare metersbreak-even: pageviews inside one 300 s window · pageviews if all are >300 s apart
External quota (proxycheck.io)
Both break-evens, and which one you are in

💥 Abuse cost — /w/ id flood

Meter (per month)Without rate-limitWith /w/ rate-limit
Total / month (today)
The free rate-limit rule blocks the 11th request in 10 s from one IP — it never sees a distributed flood

⚖️ Does the model match the meters?

both sides on the same footing — a full calendar month, preview meters excluded

Needs both sides: a volume in 📐 Model, and a successful load.

Needs both sides: a volume in the 📐 Model tab, and a successful load.

🛠️ Kill switches & reference

destructive actions, and how the money is wired
🛑 Kill switches — stop billed traffic (Worker domain) · stop the engine (R2)

🛑 Kill switch

🛑 Engine kill switch (R2)

🔀 Full flow — wrapper · /geo · engine · pop
analyytics.comWorker + KV — /w/<id>.js, /geo, /admin/*
cfg.analyytics.comR2 — ad config /cfg/<id>.js (rotation, $0, edge-cached)
cloudfroze.comR2 — engine (static, free egress)
panel.…Pages — config.html builder (operator)
RUNTIME (a real visitor) publisher page <head> ── 1-line loader ──┐ ▼ (1) GET analyytics.com/w/<id>.js ► WORKER 1 Worker req · 1 AE write caches.default ? (billed on EVERY request) ├─ HIT (≤300s) → wrapper bytes 0 KV └─ MISS → KV.get("w:<id>") 1 KV read → cache.put 300s ├ found → bytes (max-age 300) └ missing → 404 (max-age 60, negative cache) │ (2) wrapper runs → needs country ▼ [rotation mode only] GET analyytics.com/geo ► WORKER request.cf.country → "XX" └ subrequest → proxycheck.io (VPN flag; $0 on CF — not a billed request, await ≠ CPU, no egress — billed by proxycheck on a flat external plan) cached 12h in localStorage __cc_<CFG_ID> +1 Worker req / cache window · 0 KV (IP-precise; not spoofable. Empty cc is never cached → that visitor re-requests /geo on every pageview. /geo FAILS → the wrapper does NOT boot: no engine, no pop.) │ (3) load engine ▼ GET cloudfroze.com/<engine>.js ► R2 $0 Worker · free egress edge-cached, immutable → BetterJsPop runs (engine is NOT on the Worker) │ (4) AD TARGET branch (POP_CONFIG.window.url = the switch) ▼ ├ plain landing url → open it directly, NO cfg load single-URL mode · $0 └ cfg.analyytics.com/cfg/<id>.js → GET ► R2 rotation mode · $0 Worker · edge-cached <script src> → sets window.__ADCFG DIFFERENT domain than engine (config decoupled from the blockable engine domain — R2-direct, CORS-exempt) │ (5) user CLICK → pickAdUrl() picks ▼ [rotation mode only] · browser · $0 window.__ADCFG → country(XX) + device → weighted / round-robin → +subid (config absent/empty/error, or single-URL mode → the AD TARGET url as-is; the pop never breaks) │ (6) POP opens ───────────────────────► ad URL AUTHORING (operator, rare) config.html ──PUT /admin/wrapper {id,js}─► WORKER ──► KV w:<id> config.html ──PUT /admin/adconfig {id,cfg}► WORKER ──► R2 cfg/<id>.js ($0 · edge-purged) (bearer ADMIN_TOKEN; cfg validated server-side; instant fleet-wide via purge)
Cache: wrapper 300s (WRAPPER_TTL), 404 60s (NEGATIVE_TTL), per data-center; …

Cache: wrapper 300s (WRAPPER_TTL), 404 60s (NEGATIVE_TTL), per data-center; re-publish live ≤300s (instant with purge). Cost key: the Worker runs on every request (even a cache hit) → 1 Worker req each; the cache only saves the KV read. The engine (R2) and the click-time resolver never touch the Worker → $0.

Ad rotation = $0: the config is loaded R2-direct from …

Ad rotation = $0: the config is loaded R2-direct from cfg.analyytics.com/cfg/<id>.js (a <script>, $0 Worker, edge-cached) and the rotating url is picked client-side ($0) — the only per-pageview metered item stays the 1 wrapper Worker request. Config sits on a different domain than the engine (decoupled from the blockable engine domain). The cfg request only happens in rotation mode (AD TARGET = a cfg.analyytics.com/….js url); in single-URL mode (AD TARGET = a plain landing url) there is no cfg request at all — the pop opens that url directly. $0 either way.

Note: /geo and the click-time rotation resolver are live, not planned — the …

Note: /geo and the click-time rotation resolver are live, not planned — the Worker answers /geo with {cc,vpn} (country from request.cf.country, VPN flag from a proxycheck.io subrequest, fail-closed) and the wrapper calls it unconditionally once cfg loads. They exist only on a wrapper deployed in rotation mode; a single-URL wrapper has CFG_ID baked to '', so its resolver returns on the first line and it makes neither the cfg GET nor the /geo request. That is the Wrapper mode assumption in 📐 Model, and it is what the model's second billed Worker request is priced on.

🔧 Enabling Real usage & Kill switch — one-time: redeploy with CF_ACCOUNT_ID + MGMT_API_TOKEN
  • Redeploy the Worker (it has the routes /admin/usage, /admin/domain, /admin/r2domain) with CF_ACCOUNT_ID + the secret MGMT_API_TOKEN.
  • MGMT_API_TOKEN = a User API Token with Account: Analytics Read + Workers Scripts Edit + Workers R2 Storage Edit + Zone: Workers Routes Edit + DNS Edit + Zone Read (+ Cache Purge). Put it in deploy.env → run deploy.command.
  • The browser only sends ADMIN_TOKEN (from the 🔌 Connection bar) to the Worker; the powerful token stays a Worker secret → no CORS, no secret in the browser.