Measured on real Cloudflare — native Durable Objects + R2 + edge

Performance & scalability

An honest, end-to-end measurement of the Hypha-Mesh platform as deployed: the real shipped client over WebSockets and the artifact HTTP plane, against a dedicated Cloudflare deployment with real Durable Objects and R2 — so every latency folds in the true edge-to-DO round-trip and the real durable-commit cost. No mocks; bad numbers reported too. Each number below is paired with what it means for what you can build.

What the platform delivers

Real Cloudflare deployment (§2.2 of the report) — p50 unless noted, from one host over one ISP uplink to a Cloudflare-assigned DO region. Each card: the measured number, and what it lets you build.

3–35msCall to a co-located objectvs 450–692 ms when the object is on the far side of the worldWhere you put the object is the biggest single effect measured here — bigger than everything else on this page combined. Put it near its users.
4,727/sAggregate appends · 32 Log DOs≈25× a single object's rate · measured, not inferredHorizontal scale is real — adding objects adds capacity, with no shared bottleneck between them.
1,022Concurrent WS on one DOstill serving traffic at the peak · refused at a configured capMany clients per object — and the limit is a setting you control, not resource exhaustion.
+28msDurable write, above the networkpaired measurement · 0/200 negative pairsPersist authoritative state in about one round-trip — durable storage co-located with the object.
+3msRouted RPC, above the networka call into a peer client, no durable writeRouting through the mesh is nearly free — agents and UIs feel instant.
0err10 minutes sustainedp50 36.4 → 37.1 ms across 12,000 mixed opsIt stays that way — no drift, no leak, no eviction cliff under steady load.
0%Conflicts, atomic counter16 writers, one key · final exact · vs 85% for CASCheap counters & ledgers that stay exact under contention — cost scales with useful work, not retries.
50kEntries replayed after a dropgap-free in 17.3 s · 100 entries in 15 msA client that disconnects loses nothing — and you know what a deep catch-up costs before you design for it.
~0.5sCold DO first touchone-time wake; every call after is warmFirst hit on a sleeping object pays a wake — mask it with a "waking…" affordance.
~0Standing cost at restidle objects hibernateCheap to operate — you pay for active work, not for objects sitting idle.

On latency numbers: this benchmark runs from a single host on one uplink, and round-trip-dominated latencies varied 67–123% between repetitions of identical code. So the figures above are the ones that survive that: throughput, the paired server-side cost of an operation, and structural limits. Raw latency medians are in the full report, with their spread beside them.

How it was measured

A repeatable, bounded, self-cleaning harness — production-representative, zero risk to live users.

Real client, real path

The harness drives the shipped client-ts over WebSockets plus the artifact HTTP plane. Every number is an end-to-end client → edge → DO (→ R2) round-trip — no mocks.

Isolated, torn down

A dedicated hypha-mesh-bench worker with its own DOs, R2 bucket and throwaway JWT secret — same code, classes and migrations as prod. Teardown deletes every artifact it created. Real workspaces are never touched.

Repeated, so you can see the noise

The core scenarios ran six times back to back. They disagreed by up to 123% on round-trip-dominated latency and by only 13–20% on throughput. Every figure in the full report carries that spread, because a single sample from a path like this is not a specification.

Paired, not subtracted

To separate network from server work, each operation is timed interleaved with a Worker-only request, and the statistic is the median of per-pair differences. Subtracting two separately-measured medians — what earlier editions did — produced a wider spread than the raw number, and sometimes a negative one.

Latency & throughput

Real Cloudflare (§2.2). All latencies in milliseconds.

Geography — routed-call p50 (ms), by client region × object region

client ↓ / object →N. America WN. America EEurope WEurope NAsia-PacificOceania
N. America W (San Jose)14159291348224330
N. America E (Miami)1805226286350451
Europe W (Paris)299221974449665
Europe N (Stockholm)3522847413501680
Asia-Pacific (Osaka)2303464625023264
Oceania (Sydney)30742560868121730

What it means: a call to a co-located object costs 3–35 ms; the same call to an object on the far side of the world costs 450–692 ms. Where you put the object is the largest single effect in this entire benchmark — bigger than the durable-write cost (+28 ms), the saturation knee, and fan-out combined. Because every workspace, log and doc is its own object, that placement is a per-tenant decision, and Cloudflare makes it wherever the object is first touched — so it is worth being deliberate about. Halve these figures for one-way distance: a routed call crosses to the object and back twice.

Measured by running the shipped client from a probe pinned to each region against an object placed in each region — all 9×9 combinations (6 shown; the full matrix and the two regions that collapsed onto one datacentre are in the report). Placement is not assumed: it is validated by requiring the diagonal to be the minimum of its column, which held for every distinct location. Both ends are inside Cloudflare, so this is region-to-region cost, not an end-user's last mile.

Horizontal scale — the same load against N independent objects at once

objectsLog appends/s (aggregate)per objectDoc commits/s (aggregate)per object
1187187208208
4631158731183
81,2681591,309164
162,4921562,562160
324,7271484,478140

What it means: per-object throughput stays nearly flat as objects are added, so 32 objects do ~25× the work of one. This is the sharding claim measured rather than argued — it was previously printed as "∞" with no data behind it. It is not perfectly linear: about 21% of per-object efficiency is lost by 32 objects. A client-side event-loop-lag control ran throughout (~2 ms), confirming the benchmark client was never the limit.

Cost of an operation — above the network, measured by pairing

operationmedian opmedian floorserver's sharenegative pairs
routed RPC call (no durable write)14.811.2+3.234/200
durable Log append39.611.2+28.30/200

What it means: routing a call through the mesh into a peer costs ~3 ms of server work; a durable commit costs ~28 ms. The "negative pairs" column is the honesty check — a DO-backed call cannot really be faster than a Worker-only one, so that fraction bounds the jitter still inside each pair. Zero of them for the durable write means that figure is cleanly resolved.

RPC round-trip — a call routed into a peer client (no durable write)

scenariomedianrange across 6 runsthroughput
call noop (serial)18.012.0 – 34.1
call echo-1KB (serial)17.614.1 – 33.7
call noop (concurrent, 32 in-flight)51.542.9 – 62.0589 calls/s

What it means: a routed call is ≈ one network round-trip plus ~3 ms of server work. The wide ranges are this host's uplink, not the platform — which is exactly why the paired table above is the figure to design against.

Concurrency — where one socket saturates

in flight18163264128256
throughput63/s317/s369/s460/s726/s711/s934/s
p50 latency14.824.438.165.382.6176.0268.7

What it means: throughput climbs steeply to about 64 in flight, then flattens while latency keeps rising roughly linearly. Past the knee you are buying queue depth, not throughput — add sockets or objects instead.

Log — one Log DO; append = a durable SQLite commit

scenarionp50p95p99throughput
append (serial)median of 640.0range 37.9 – 44.5
append (concurrent, 32)median of 6range 452 – 517/s488 appends/s
live-tail delivery (append→deliver)median of 639.1range 37.1 – 44.9
read pagination (100/page, 1000)10 pages124.7343343591 entries/s
subscribe replay (recent 200)20019.5 ms total10,264 entries/s

What it means: ~488 durable appends/s per log carries a busy chat or event stream, and sharding across logs multiplies it (see the scale table). Subscribe-replay re-sends a recent window in ~20 ms so a reconnecting client catches up gap-free.

Replay depth & the cold tier — what a client that was away a long time pays

entries to replay1001,00010,00050,000
time15 ms213 ms2.75 s17.3 s
completed gap-free

What it means: every depth replayed completely — a client away for 50,000 entries still loses nothing, and now you know it takes ~17 s, so you can show progress instead of appearing to hang. Entries older than the hot threshold live in R2 cold segments, and reading across that boundary was no slower than reading the hot tail (104 vs 125 ms/page) — the tier split is not a latency cliff.

Doc — one Doc DO; durable conditional + atomic writes

scenariocommitsp50p95throughputintegrity
CAS serial (uncontended conditional write)median of 638.5range 32.5–42.6
CAS contended (16 conns racing on one key)80021 commits/s85% conflicts · final exact (800)
atomic incr (16 conns racing on one counter)800283 commits/s0% conflicts · 1.0× amplification · final exact

What it means: the single-writer DO never loses an update; a server-side atomic incr commits a counter/ledger in one round-trip with no retry storm — so cheap, exact counters and balances whose cost scales with useful commits, not with contention.

Connections, presence & cold start

metricp50p95p99 / maxnote
WS connect (warm DO)63.8range 51.6 – 86.0 across 6 runsflat while holding ~1,000 connections
Max concurrent WS on one DO1,022 held · 1,019 still alive · 20/20 routed calls servedrefused with close 1013 at a configured cap of 1,024
Cold Log DO (first-ever dial)557681681instantiation + schema init + handshake
Cold workspace DO (fresh ws)508627627one-time wake; every call after is warm

What it means: one object holds 1,022 live connections and still serves traffic, with connect latency flat the whole way up — and the wall it hits is DEFAULT_MAX_CONNS_PER_WORKSPACE, a deliberate DoS control you can raise, not resource exhaustion. The previous "≥200, ceiling not reached" was the harness's own configured target, not a limit.

Presence fan-out — propagation vs number of subscribers

subscribers131050200
p50 propagation59.448.861.978.4129.8

What it means: presence reaches a handful of subscribers in about one round-trip, but fan-out is not free at width — 200 subscribers takes ~2.7× as long as 10. An earlier edition of this page said there was no fan-out cost; that was generalised from a 3-subscriber sample, a width at which the cost cannot show up.

Payload limits — how big a routed call can be

payload1 KB16 KB64 KB256 KB512 KB1 MiB
p5016.220.933.844.364.9refused

What it means: cost is flat to ~16 KB then tracks bytes; 512 KB is the largest usable payload. At 1 MiB the server rejects the frame before parsing it (close 1009) — a deliberate control, since an unbounded frame is cheap memory amplification against a single-threaded shard. Bulk bytes belong on the artifact plane below.

Artifacts — bulk bytes to R2

pathresultnote
direct-to-R2 multipart (64 MB, 8×8 MB)completes · ✓ byte-verified≈10.5 MB/s aggregate · 0 errors · 0 dangling MPUs
binding plane upload (8 MB, through the Worker)≈9.8 MB/s from one hostclient-uplink-bound; fine for small/medium blobs
binding plane download (8 MB)≈22.3 MB/s

What it means: large files upload direct edge↔R2 (bytes never touch billable DO time) and round-trip byte-for-byte; small/medium blobs stream simply through the Worker.

Scalability analysis

What the numbers say about the architecture (§3).

Placement beats optimization

A call to a co-located object is 3–35 ms; to a misplaced one, 450–692 ms. Nothing else measured here is within an order of magnitude of that. The DO-per-object model is what makes placement a per-tenant decision — a workspace can live near its users — and the object lands wherever it is first touched.

DO-per-object sharding → measured scale

Each workspace, Log and Doc is its own single-threaded DO — nothing is shared (no global bus, no shared SQL). Driving 32 objects at once delivers ~25× a single object's throughput, with per-object efficiency down only ~21%. Capacity ≈ per-object × objects, with an efficiency factor. The one risk is a single hot object.

Hot SQLite, cold R2

Control + small ordered data live in co-located hot DO-SQLite (a durable commit costs ~28 ms beyond the bare round-trip). Bulk bytes go to R2 — through the Worker for small blobs, or direct-to-R2 multipart to keep bytes off billable DO time. Reads crossing the hot→cold boundary were no slower than hot reads.

Atomic ops stay exact under contention

The single-writer DO never loses an update, and a server-side atomic incr commits a hot counter in one round-trip — 0% conflicts against 85% for optimistic CAS on the same key. Contention becomes cost that scales with useful commits, never a latency cliff or a lost write.

No global bus — but fan-out has a price

Presence and subscribe-streams are served from the owning DO's SQLite + connection set and pushed to subscribers — no Redis, no fan-out tier, so the whole class of shared-bus incidents is gone by construction. Fan-out itself is not free: ~130 ms to 200 subscribers on one object, against ~62 ms to 10.

Cost — cheap to operate, ~zero at rest

Cloudflare bills DOs on requests + active duration, and R2 on class-A/B ops + storage (§5).

Requests scale with useful work

Each call/append/commit ≈ one DO request; concurrency doesn't reduce the count. Atomic ops keep a contended counter at one request per commit (no retry storm), so the bill tracks useful work, not contention.

Duration, and the byte path

Streaming and through-Worker artifact bytes accrue billable wall-time; the direct-to-R2 path keeps bytes off the DO. Idle DOs hibernate to ~0 — cold wake costs ~0.5 s once, not standing cost.

~0 at rest

Cold workspaces/logs/docs left behind hibernate to ~zero — per-object sharding is also per-object cost isolation, with no shared always-on infrastructure to pay for.

Measured 2026-09-16 against a dedicated Cloudflare deployment running the shipped code. Numbers from docs/benchmark-report.md — captured from one host over one uplink, so round-trip-dominated latencies vary run to run and are reported with their spread; a multi-region client matrix is the largest missing measurement. The full methodology, raw per-op samples, limitations, and the 2026-06 run this replaces are in the complete report.

Read the full report