← the Atlas

Padi — Typing-Echo Latency Baseline

Reference·seedling·implemented·

The W1 baseline owed by padi W1 (PR 1652) before W2.2 starts — keystroke→echo latency measured through the real stack on current master, both clock points on kolu-server's websocket so the padi hop W2.2 adds is measured honestly. Method, box spec, numbers, and how to re-run for the sub-5ms added-p99 budget.

The number W2.2 has to beat. padi W2.2 slips a whole new process — padi — between kolu-server and kaval, so every keystroke gains a hop. The plan budgets that hop at < 5ms added p99 and says the ceiling is measured, not guessed: W1 (#1652) owes a baseline of today’s echo latency, and W2.2’s done-criterion (e) compares against it. This note is that baseline — what we measure, where the clock starts and stops, the box it ran on, the numbers, and the one command that re-runs it identically.

client probe Node · /rpc/ws kolu-server padiSurface kaval → PTY line-discipline echo sendInput terminalAttach delta t0 clock starts t1 clock stops W2.2 inserts padi here both clock points sit on the near (client) side of this hop
The measured path. The probe writes a keystroke (t0) via padiSurface.lifecycle.sendInput over kolu-server's /rpc/ws; the PTY line discipline echoes it; the echoed byte returns as a padiSurface.terminalAttach delta (t1). Both clock points sit on the client side of kolu-server's websocket — which is exactly where padi's future hop lands, so W2.2 re-running this probe measures the added cost and nothing else.

What the baseline measures

One keystroke’s full round-trip through the real stack, timed by a headless client that speaks the same wire the browser does:

latency = t1 − t0, both read from one process’s monotonic clock (process.hrtime.bigint()), so absolute clock skew is irrelevant — only the delta matters.

The measured baseline

Current master (#1652 at merge — the padi domain served in-process, no separate padi process yet), a nix-built kolu (.#default), on a quiet ephemeral box with nothing else running. 2400 warm keystrokes (8 terminals × 300 measured, 50 warm-up discarded per terminal):

percentile latency
p50 2.14 ms
p90 3.16 ms
p95 3.42 ms
p99 4.36 ms
min / mean / max 0.16 / 2.36 / 20.9 ms

A second independent run agreed within noise (p50 2.04, p95 3.45, p99 4.21 ms), so the percentiles are stable, not a lucky draw.Percentiles use the nearest-rank method on the ascending-sorted samples (the value at rank ⌈p/100·n⌉) — no interpolation, so the computation itself is deterministic and W2.2 compares like for like. The lone ~21 ms max is a single GC/scheduler outlier well outside p99.

The box — recorded in the result JSON, so a re-run on a different box is never mistaken for the same conditions: pu ephemeral Incus container (NixOS), Intel Core i9-14900K, 32 logical CPUs, 125 GiB RAM, Linux 6.12.85 x86_64; kolu’s bundled Node v22.22.1; kolu built from master via nix build .#default.

How the probe drives the stack

The measurement is a small, reusable script — not a one-off — so W2.2 re-runs the identical method (bench + this note shipped in #1660):

Both live under packages/server/ because the probe imports @kolu/surface and ws, which that package already depends on — no new dependency, no lockfile churn.

Re-running it

From a checkout, on a quiet box:

just bench-typing-echo

That builds .#default, boots the private server, runs the probe, prints the percentiles, and cleans up. Knobs (all optional) are environment variables: KOLU_BENCH_SAMPLES (measured keystrokes per terminal), KOLU_BENCH_TERMINALS, KOLU_BENCH_WARMUP, KOLU_BENCH_INTERKEY_MS, KOLU_BENCH_OUT (write the full JSON, raw samples included), and KOLU_BENCH_BIN (point at a pre-built bin/kolu to skip the nix build).