Skip to content
NanoFlags
Start free

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

326ms

p95

378ms

p99

1,418

samples

latency_summary · last 24 h · newest sample 2026-11-12 10:45 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.

Last 24 hours · 1,418
Probe locationSamplesp50p95p99Worst
KIX Osaka283188280323473
MEL Melbourne284194282329475
LHR London284229318366520
ATL Atlanta284253341386532
DEN Denver282254342385535
All locations1,418222326378535

ms · 2026-11-12 10:45 UTC

Last 30 days · 2,443
Probe locationSamplesp50p95p99Worst
KIX Osaka494175268314473
MEL Melbourne493195284322475
LHR London482235330364520
DEN Denver492237334375535
ATL Atlanta480243337379532
All locations2,443217320371535

ms · 2026-11-12 10:45 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.

  1. Publish

    Postgres commit · t₀

    The revision row is written in the publish transaction and pushed to the publisher at once.

  2. Sign

    Ed25519 · kid

    The publisher signs the server view and the browser view of the bundle.

  3. Fan out

    Durable Object

    One object per environment broadcasts to every socket, then advances the edge KV pointer.

  4. Push

    WebSocket · hibernating

    The message carries the signed bundle itself. Idle sockets cost nothing while they wait.

  5. SDK

    verify → swap

    The SDK verifies the signature, swaps the bundle in atomically and keeps evaluating locally.

When a socket can't open. The SDK falls back to a server-sent-events notification plus a conditional fetch from edge KV, and polls as a safety net. These fallback transports are slower and are not part of the measurement.
  • 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 the SDKs (JavaScript 0.4.0+, Python and Go 0.1.0+) verify 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.

flags.ts
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.

See the same data on /status

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.