Two phases, and one of them is 74% of it
The same page on our prerendered host does no server work at all. This one does 1,005ms of it — down from 3,220ms before the waterfall was collapsed — and until the trace shipped nobody could see which part.
A real run, timed
a person opens a tenant page
Three phases off a live production response. Two carry a measured number, the first carries a tilde because no instrument reaches it, and the line underneath is a command you can run against this host right now.
No instrument reaches the edge boot, so that step carries a tilde and a range instead of a number, and it adds nothing to the total. Every other chip says prod: read off a live production response.
Four database round trips, one after another, before the first line of page code runs: the domain row, the authority walk, the descendants, the billing record. That is the tenant tax, and it is the next thing to cut.
On this entry path, render still has the data reads folded into it — so it cannot be read as "rendering is slow". The split shipped for /u/[slug] and not for a custom-domain root, and until it does, that number is two things at once.
The timed phases sum to 1,002.0ms and the header printed 1,005.0ms. The 3.0ms difference is overhead between the phases that the timers cannot see. It is left in rather than reconciled away.
That is the same header, on this host. This page is prerendered, so there is no server work to time and it answers zero. The run above is the expensive case; this line is the cheap one, and both are real.
Five steps, and a page never writes the header itself.
Step 1 of 5
Cloudflare Workers, at the nearest edge location. No cold start to pay.
Step 2 of 5
A middleware wrapper starts the request timer and attaches the header on the way out.
src/lib/perf-middleware.ts
Step 3 of 5
Domain row, session, workspace. The auth phase closes the instant the walk hands off.
src/middleware.ts
Step 4 of 5
From a closed set of names. A page never builds the header itself.
Astro.locals.perf?.mark('data')
Step 5 of 5
On every response, in a standard format your browser already parses.
Server-Timing: auth;dur=…, render;dur=…, det;dur=…
The timing you just watched is six files, and the runs behind it are four more.
src/lib/perf-middleware.ts starts the clock and attaches the header. src/components/speed/speed-run.ts holds the recorded runs — every step in it carries the command it was read from and the machine it was read on.
src/components/speed/SpeedLine.tsx
| Prop | Type | Default | Notes |
|---|---|---|---|
| journey | 'page' | 'chat' | 'agent' | 'chat' | Which recorded run this instance replays. The fold above is set to page. src/components/speed/SpeedLine.tsx:105 |
| proof | boolean | true | Renders the falsify-it-yourself line — the curl and what it answers. src/components/speed/SpeedLine.tsx:112 |
| still | boolean | false | Start paused on the final frame. Reduced motion already does this on its own. src/components/speed/SpeedLine.tsx:103 |
| speed | number | 1 | Multiplier on every dwell. src/components/speed/SpeedLine.tsx:107 |
The component at the top of this page is the same one on /speed-metrics, with one prop changed. There is no screenshot of it anywhere on this page.
A phase with no measurement gets no millisecond. That rule is why one of these is a gap rather than a win.
0.0ms
server work behind the prerendered host
curl -sI https://one.ie/ | grep -i server-timing
1,005.0ms
server work behind one tenant page, in production
Server-Timing det;dur on mover.chat, ten hits, 2026-08-20 — text/speed-results.md
0 of 30
steps in the recorded machine run that woke a model
text/speed-lifecycle-run.md — RECORD in src/components/speed/speed-run.ts
3.0ms
gap between the printed total and the steps that make it up
untimedGapOf(page) — printed header minus summed phases, left in
From an empty folder to a provisioned workspace, on the same runtime the run above was measured on. These are the commands the CLI prints, and that middle line is what it prints back.
$ npx oneie create my-nodeNode created — free plan, no credit card.$ cd my-node$ bun install$ one setupThree of these rows go against us. They are the ones worth reading first.
| Feature | ONE | Typical chatbot platform | In-house build |
|---|---|---|---|
| Every response carries a phase-by-phase timing header | you build it | ||
| No cold starts | varies | varies | |
| Streaming inference from the edge | varies | you build it | |
| Ships in one command, no build pipeline to configure | varies | you build it | |
| A dated performance baseline in the repo you can re-run | rarely published | rarely published | |
| Sub-second first byte on a tenant custom domainConceded. The run at the top of this page is real, and it spends 1,005.0ms in the worker before a byte leaves. Our prerendered host spends none. A tenant page on a custom domain is not there yet, and the auth phase is why. | varies | varies | |
| Model response timeConceded. Inference is measured in seconds and it runs on the provider’s clock. Fallback chains route around a slow provider; nothing routes around slow. | |||
| Total control of infrastructure and costDedicated in-house infrastructure can match these deploy times. It costs meaningfully more to build and to run. |
Every plan gets the same edge and the same runtime.
Same edge, same runtime, every plan.
Priority routing · faster reports
Dedicated edge region
Every number here names where it was measured, and the one step nobody has timed says so out loud.
No credit card · every number names its source