Measured live · refreshed every minute
Fast is a number.Here is ours, live.
Every 5 minutes a probe publishes a real change through the same path as the dashboard and times its arrival at listeners on Cloudflare's network. Every figure on this page comes from those samples, including the slow ones.
222ms
p50, all probe locations
327ms
p95
378ms
p99
1,418
samples
latency_summary · last 24 h · newest sample 2026-11-12 10:35 UTC
By probe location
Commit to listener, region by region.
The bar is the median, the line runs to the 95th percentile. Locations are where the probe's listener actually ran on Cloudflare's network, sorted by p50.
| Probe location | Samples | p50 | p95 | p99 | Worst |
|---|---|---|---|---|---|
| KIX Osaka | 283 | 189 | 280 | 323 | 473 |
| MEL Melbourne | 284 | 194 | 282 | 329 | 475 |
| LHR London | 284 | 229 | 318 | 366 | 520 |
| ATL Atlanta | 284 | 253 | 341 | 386 | 532 |
| DEN Denver | 282 | 255 | 342 | 385 | 535 |
| All locations | 1,418 | 222 | 327 | 378 | 535 |
ms · 2026-11-12 10:35 UTC
| Probe location | Samples | p50 | p95 | p99 | Worst |
|---|---|---|---|---|---|
| KIX Osaka | 492 | 175 | 268 | 315 | 473 |
| MEL Melbourne | 491 | 195 | 285 | 323 | 475 |
| LHR London | 480 | 236 | 330 | 364 | 520 |
| DEN Denver | 490 | 237 | 334 | 375 | 535 |
| ATL Atlanta | 478 | 243 | 337 | 379 | 532 |
| All locations | 2,433 | 217 | 320 | 371 | 535 |
ms · 2026-11-12 10:35 UTC
The totals include 1 samples from a probe that could not report its Cloudflare location.
The delivery path
One hop from commit to SDK.
The publish transaction pushes the new revision straight to the publisher. It signs it and hands it to one Durable Object per environment, which sends the signed bundle down every open WebSocket. The SDK applies it without a second request.
Publish
Postgres commit · t₀
The revision row is written in the publish transaction and pushed to the publisher at once.
Sign
Ed25519 · kid
The publisher signs the server view and the browser view of the bundle.
Fan out
Durable Object
One object per environment broadcasts to every socket, then advances the edge KV pointer.
Push
WebSocket · hibernating
The message carries the signed bundle itself. Idle sockets cost nothing while they wait.
SDK
verify → swap
The SDK verifies the signature, swaps the bundle in atomically and keeps evaluating locally.
Push, not poll
A connected SDK hears about a revision when it is published, not on its next poll. While the socket is healthy, polling drops to every 5 minutes.
Signed at the source
The bundle is signed once, before distribution. The edge serves the signed bytes unchanged, and SDK 0.4.0+ verifies them by default on the official edge.
Evaluated locally
Reading a flag is an in-memory function call. There is no network request on the hot path, so propagation time never adds to your request time.
Fail-safe behaviour
When the network is the problem, your app isn't.
Propagation is how fast a change arrives. Fail-safe is what happens when it can't. The SDK is built so that a lost connection costs you updates, never answers.
import { createClient } from "@nanoflags/sdk";
const flags = createClient({
sdkKey: process.env.NANOFLAGS_SDK_KEY!,
baseUrl: "https://edge.nanoflags.io",
bootstrap: bundleFromServer, // answers before the network does
});
await flags.ready(); // never rejects
flags.status; // "ready" | "stale" | "not-ready"
flags.on("update", (u) => {
// u.transport: "live" | "stream" | "poll"
// u.latencyMs: database commit → this process (pushed revisions)
console.log(u.revision, u.transport, u.latencyMs);
});
// Local and synchronous. Offline, it keeps answering from the last verified bundle.
const on = flags.boolean("checkout-v2", false, { key: user.id });- Last good configstatus: stale
- If the connection drops or the edge errors, the SDK keeps evaluating the last bundle it verified, reports status "stale" and retries with backoff. It never discards a working bundle because of an error, and never moves to an older revision than one it was pushed.
- Bootstrapbootstrap: Bundle
- Pass a bundle at start-up (for example the one you evaluated with on the server) and the SDK answers from it before the network has said anything. Server and client render the same result.
- ready() never rejectsawait flags.ready()
- Start-up waits for a usable bundle from the network, a store or bootstrap, or for the first attempt to finish without one. It never throws; check status instead. Until a bundle exists, every accessor returns the fallback you passed.
- Edge fallbackX-NanoFlags-Stale: true
- If the database is unreachable, the edge keeps serving the last signed revision it holds and marks it stale. Publishing stops; reading flags does not.
- Revoked key≈ 1 min at the edge
- A revoked key is normally refused at the edge within about a minute. An SDK that already holds a configuration keeps evaluating it, as stale, until it restarts.
Methodology
How the numbers are made.
Every 5 minutes a probe publishes a small change to a dedicated probe environment through the same path as the dashboard: a draft edit, then a publish. Listeners running on Cloudflare in the locations above each keep one live WebSocket connection to the edge with a server key of that environment.
A sample is the time from the database commit timestamp of the published revision to the moment the signed bundle was received over the live connection. Postgres takes the commit timestamp at the start of the publishing transaction, slightly before the commit itself, so samples include the time the publish transaction takes.
Clocks: the commit time comes from the database server's clock, the arrival time from the Cloudflare Workers clock. Both are synchronised independently, so an offset of a few milliseconds between them is part of every sample.
What is not measured
- Your network, your devices and browsers, and the last mile to your application.
- SDK processing after the bundle is received.
- The fallback transports (server-sent events and polling) used when a live connection is not available.
- Revisions a listener did not receive within 15 seconds: they produce no sample here and are counted separately by the probe.
- Samples below 0 ms or above 60 seconds, which are discarded as invalid.
The probes are few and fixed in location. Percentiles over a short window rest on few samples. This is measured propagation, not a guarantee.
Measure it in your own app.
The SDK's update event reports latencyMs for every pushed revision: the time from the database commit to your process.