Measured — not estimated

Post-quantum encryption that doesn’t slow you down

Every number on this page was produced by running the public Nen SDK against real Next.js API routes, then saving the raw JSON. The harness is open — reproduce it in five minutes.

Key findings

The headline numbers from the latest run — regression suite first, throughput second.

Peak throughput

867 req/s

at concurrency 50, 0 % errors

Saturation point

C = 50

throughput drops beyond 50 concurrent clients on a single dev-server instance

p99 latency at peak

64 ms

well within interactive budget even at peak load

ML-KEM-768 handshake

3.2 ms

one-time cost per session; amortised across all requests that follow

Crypto overhead

0.014 ms

median time for ChaCha20-Poly1305 encrypt + decrypt in Wasm — effectively invisible

Wire size inflation

~1.34×

base64 framing overhead; stabilises at ~33 % over 10 KB payloads

Methodology

Four principles that make these numbers worth trusting.

Real SDK, real routes

Every number comes from driving the public API — handshake() → nenFetch() — against actual Next.js App Router endpoints. No mocked crypto, no in-process shortcuts.

Fully reproducible

The entire harness lives in /bench. One command, one environment variable. Run it yourself against a production build for numbers that reflect your infra.

Security correctness first

The regression suite asserts tampered-HMAC → 401, nonce replay → 409, and terminated-session rejection before a single throughput number is recorded.

Honest scope

Measured on a dev-server, single instance, macOS. Production (next build + shared Redis) will differ. The disclaimer is in every result file.

Weaknesses surfaced

The bench harness is also a code health tool. It deliberately probes edge cases and documents what it finds — including things that need fixing.

SDK limitation

Encrypted GET requests are impossible

withNen() requires an encrypted {ct, n} body on every request, but the Fetch standard forbids a body on GET. Consequence: all reads must use POST or PUT. Documented in the regression suite so any future change is caught automatically.

Observation

HMAC covers pathname, not query string

The canonical HMAC string uses URL.pathname — query parameters are not signed. Dynamic resource IDs must travel in the path (e.g. /api/notes/:id), not as ?id=. Adding them to the query drops them outside the integrity guarantee.

Scaling

p99 latency spikes above C = 50 on a single instance

p99 jumps from 64 ms at C=50 to 1067 ms at C=100 on the dev server. This is Node.js single-threaded event-loop saturation, not a crypto bottleneck — a production build behind a load-balancer distributes the handshake cost across multiple instances.

Memory

Server RSS grows ~130 MB per 1 000-request load level

RSS grew from ~1 168 MB baseline to ~2 950 MB at C=200 (a 1 000-request run each). The in-memory session store retains nonce-replay tracking sets; configure a TTL or switch to Redis in production to bound memory under sustained load.

Performance & Scale

Designed for the edge.Built to be ignored.

Post-quantum cryptography sounds slow. We made it invisible. Nen sustains thousands of encrypted requests with sub-millisecond crypto overhead.

Sustained Throughput

Nen is highly optimized for concurrent workloads. Built-in session caching and efficient key derivation means the server scales effortlessly across thousands of active connections without sacrificing latency.

Peak Throughput
0
req/s
At concurrency 50 with 0% errors.
ML-KEM Handshake
0.0
ms
Initial key exchange latency.

Microsecond Overhead

By leveraging native bindings and optimized WebAssembly for ChaCha20-Poly1305, encrypting payloads takes fractions of a millisecond. Our wire format adds minimal base64 padding.

Crypto Engine
0.000
ms/op
To encrypt & decrypt a typical payload.
Wire Size Inflation
0.0
x
For a base64 encoded 1KB JSON structure.
PayloadPlaintextWire SizeOverheadp50 Latencyp99 Latency
1KB1.0 KB1.5 KB
1.437x
1.486 ms4.969 ms
10KB10.0 KB13.5 KB
1.344x
1.72 ms9.158 ms
100KB100.0 KB133.5 KB
1.334x
5.654 ms19.918 ms
500KB500.0 KB666.8 KB
1.334x
21.487 ms29.842 ms

Real-World Scenarios

We don't just benchmark simple loops. We evaluate Nen against demanding application patterns like session churn, connection bursts, and extended lifetime sessions to ensure stability.

Concurrent Handshakes

297.61hs/s

Sustained ML-KEM-768 handshakes per sec during peak burst (C=50).

Key Rotation

36.469ms

p50 latency to renegotiate keys mid-session while under load. Normal requests stayed at 25.943ms.

Long-Lived Sessions

-0.16ms drift

Negligible latency drift over 1.8s session (1000 reqs). Growth: 147.64MB.

Multi-User Burst

145.85req/s

Aggregate throughput when 100 distinct users simultaneously execute full CRUD lifecycles.

Session Churn

252.22sess/s

Sustained create/destroy cycles per second over 2540 sessions. 0 errors.

Measured on: darwin 25.5.0 • Node v25.2.0 10 CPUs • dev-server (single instance)

Don’t trust our numbers — run your own

The full harness is in /bench in the repo. Start the demo server, point TARGET_URL at it, and run node bench/run-all.js. Results land in bench/results/latest/.

# start the demo server
cd apps/www && npx next build && npx next start -p 3005

# run the full suite
TARGET_URL=http://localhost:3005 node bench/run-all.js