One row per HTTP, WebSocket, WSGI, ASGI, security and deployment capability, each naming the test or CI step that proves it and the cadence it runs on. Also available as spec.json.
What this server implements, and what proves it. One row per capability, and
every row carries its evidence — a named CI gate, a source file, a roadmap
heading, or a reason for the refusal.
The badges are half of what makes a verified row mean anything. The claim is
a composition: CI is green for this commit AND every verified row names
a gate CI actually runs. poe check-docs enforces the second half and fails
the build if a row names a gate that does not exist or does not run; the badges
are the only visible evidence of the first. Read them together, and read them
knowing a badge reports the newest run on main rather than the commit you are
looking at.
158 capabilities: 134 verified, 0 implemented, 0 planned, 24 out of scope. Of the 134 verified, 130 are gated on every pull request, 2 weekly, and 2 before a release. Every pull-request-gated row's coverage is declared IN its gate (covers: in the cited test, or a recorder coverage call in what the cited step runs), and the checker requires the declaration and the citation to agree; the weekly and pre-release rows keep declared-static citations, their runs being absent from PR CI.
How to read this page
Each row carries a permanent id (A7, K3). Ids are assigned once, never
renumbered, and never reused — a deleted row's id is retired rather than
recycled, so an id in an old commit, an issue or a conversation still means
what it meant. Refer to a row by its id, not its wording: the wording is meant
to be edited as understanding improves, and it has been.
status
means
verified
a named CI gate exercises this, on the cadence the evidence states
implemented
it is in the tree, and no gate is dedicated to it — this is the work queue, not a claim of correctness
planned
intended and not built; the evidence names the roadmap heading
out of scope
a deliberate refusal; the evidence is the reason
verified means a gate runs, not that the capability is correct. No
checker can read a test's meaning. smoke-sendfile's RSS ceiling is 16 MB for
three 64 MB passes — generous on purpose so CI is not flaky — and a regression
that buffered 8 MB would pass it. The word is a statement about evidence, not
a warranty.
Cadence matters and is stated per row.(every PR) is a step in
.github/workflows/test.yml. (weekly) is py-canary.yml or
nightly-canary.yml on a cron — the free-threading rows live here, because
CI pins GIL-enabled 3.13 and every --threads phase skips on it.
(pre-release) is a gate docs/RELEASING.md requires and CI deliberately
does not run, because shared runners cannot reproduce the timing.
One conformance suite runs on a cadence: Autobahn|Testsuite, at
pre-release (poe autobahn, I13), compared both directions against a
pinned baseline. No h2spec and no PortSwigger desync harness is wired to
any gate (B8 and B9 refuse them, each with its reason). The WebSocket and
smuggling rows below are otherwise pinned by hand-written probes and unit
tests against the RFC text — real evidence that is not a conformance run,
and the rows say which they are. What Autobahn measured when first run by
hand (2026-08-30) is in ROADMAP's conformance-suite tier, including the two
live defects it found and the reason it could not have caught the bug that
motivated running it: its client always closes first, so the app-initiated
close path (L15) is a region no external suite can reach.
This page is checked by poe check-docs: a verified row whose gate does not
exist or does not run fails the build, as does a CI gate no row accounts for.
See scripts/spec_sheet.py.
How the rows were checked, and what that leaves. The machine proves a gate
exists and runs; only reading proves it tests the row's claim. Every row has
been read against its gate once, 2026-08-30, and roughly a fifth were wrong —
claims whose cited test asserted something narrower (a predicate rather than
the behaviour it guards), claims covering two things while citing one test,
and four rows citing a gate that did not touch the capability at all. Those are
now split, re-pointed, or demoted to implemented.
The two halves of that pass were not equally thorough, and the weaker one is
worth knowing about: every unit-test citation was checked by reading the test
body, while single-claim smoke steps were checked by a relevance probe with
the flagged ones read in full. A smoke step whose name matches its row but
whose body drifted away from it is the case most likely to have survived.
A. HTTP/1.1 framing and connection lifecycle
id
capability
status
evidence
A1
Persistent connections (keep-alive)
verified
Smoke test pipelined requests (every PR)
A2
The example Mojo server starts and answers /health
verified
Smoke test the hello server (every PR)
A3
Keep-alive request cap
verified
Smoke test the keep-alive request cap (every PR) — closes on the cap request, and a stream or WebSocket upgrade landing there survives it; the probe's third phase refuses to pass on a build whose cap never fires. No flag or env var exposes the limit
A4
Idle connection timeout, --idle-timeout
verified
Smoke test the idle connection timeout (every PR) — an answered keep-alive connection left quiet is closed at the deadline and no earlier, and one kept busy across it is not
A5
Header read timeout (slowloris defence)
verified
Smoke test the header read timeout (every PR)
A6
Request pipelining, answered in order (RFC 9112 §9.3)
verified
Smoke test pipelined requests (every PR)
A7
Chunked request bodies, decoded incrementally across reads
Trailer fields consumed and discarded, not surfaced to the application
verified
test_parsing.mojo:test_a_trailer_section_is_consumed_whole (every PR) — with seven more beside it: the framing fields a trailer must not honour, the pipelined tail surviving it, the section bounded by the abuse ratio, and test_without_consume_trailer_the_body_ends_at_the_zero_chunk as the other half. poe sabotage-trailers reverts each of the six rules and requires a failure for every one
A11
Expect: 100-continue, honoured case-insensitively and withheld from HTTP/1.0
verified
Expect 100-continue, honoured and withheld (every PR) — both directions: a server answering it unconditionally would pass the invitation half while discarding the refusal-before-upload the mechanism exists for
A12
Half-close answered rather than dropped
verified
Smoke test a half-closed client (every PR)
A13
Request headers larger than one socket read
verified
Smoke test a request larger than one read (every PR)
follows from having no HTTP/2 (A18), the same reason C7 gives
B9
PortSwigger-style desync scanning
out of scope
the scanner probes a proxy/server PAIR for disagreement about framing; this server has no proxy in front of it in any gate, so there is no second parser to disagree with. The shapes it looks for are unit-tested directly above (B1-B7), and fuzzing the decoder itself is G13
Smoke test hot reload (every PR) — --reload, --reload-dir
D9
A request whose body is still arriving at SIGTERM is read on and answered, not left to the deadline
verified
Smoke test the drain with an upload in flight (every PR) — scripts/drain_upload_probe.py, two-sided: answered whole AND exited inside 3 s, on the ASGI executor and on a WSGI pool thread
D6
SO_REUSEPORT off by default: a second bind fails loudly instead of silently sharing the port
verified
Smoke test the serve CLI (every PR) — five attempts a second apart, so a restart racing the previous process's drain still succeeds; the second server must exit 1 naming the address, print no ready banner, and leave the first answering. The opt-in ListenConfig.reuse_port remains for a deliberate handoff between two processes that both mean to listen; no shipped path enables it, because workers and threads all accept from ONE listener bound before the fork
D7
Binary/hot upgrade (USR2-style overlap)
out of scope
needs socket handoff this server does not have; run two behind a proxy
D8
SIGHUP reload
out of scope
--reload covers development; production reload is a new process behind a proxy
--access-log emits one JSON record per response, and nothing without it
verified
Smoke test the serve CLI (every PR) — --access-log
F3
--metrics turns /__metrics from the application's 404 into a 200
verified
Smoke test the serve CLI (every PR) — --metrics
F4
Prometheus exposition 0.0.4: 8 counter and gauge families, each with HELP, TYPE and a sample
verified
Smoke test the serve CLI (every PR) — --metrics
F5
Latency histograms on /__metrics
verified
Smoke test the serve CLI (every PR) — six log-spaced le bounds (100µs–1s, +Inf), integer-only and O(1) on the loop thread; scripts/histogram_check.py (selftested in the same phase) asserts the documented bounds, non-decreasing cumulative counts, le="+Inf" equal to _count, and a _count covering the phase's own requests; boundary math pinned by test_metrics.mojo
F12
Coverage declared by the gate rather than cited by this page
verified
Check machine-sourced doc facts (every PR) — every pull-request-gated row declares its coverage in the gate itself (a covers: docstring line in the cited test, or a recorder coverage call in what the cited step runs), and the checker requires the declaration to agree with the citation, which is what makes the audit's mis-citation class structurally impossible. Weekly and pre-release rows keep declared-static citations, their runs being absent from PR CI. Four sabotages in poe sabotage-spec revert the rules
F6
--health-path answers 200
verified
Smoke test the Django realtime example (every PR) — --health-path
Configuration report that exits as the server would
verified
Smoke test --doctor against the server's own exit codes (every PR) — --doctor
F9
OpenTelemetry tracing
out of scope
no tracing context crosses the Mojo/Python seam today; a wrapper in the application is the supported route
F10
CI measurements recorded, rendered per run and kept as an artifact
verified
Check machine-sourced doc facts (every PR)
F11
The measurement recorder itself
verified
Self-test the measurement recorder (every PR)
F14
The site's deploy image: the wheel in python:3.12-slim with the built site, served from a container as it deploys
verified
Smoke test the documentation site's deploy image (every PR) — deploy/site/Dockerfile built from the tree's own wheel; through a published port, llms.txt and the sitemap answer at the root, a page answers with the Markdown twin it advertises, the slash redirect and the HTML 404 come from the application, m0serve is PID 1 by /proc/1/cmdline, and docker stop is the drain's exit 0 inside its grace
F13
The documentation site: the tree's own pages rendered and served by the server, llms.txt at the root, a Markdown twin beside every page
verified
Smoke test the documentation site (every PR) — built by scripts/docsite.py, served through --static with apps/site behind it; every sitemap URL answers as HTML with the twin it advertises, the root text files name the site's pages by absolute URL, the sitemap goes out as XML, and the slash redirect and HTML 404 come from the application
G. Security hardening
id
capability
status
evidence
G1
An injected status reason phrase is emptied, not transmitted
X-Forwarded-* / Forwarded parsing with a trusted-proxy allowlist
out of scope
the server never consults them — REMOTE_ADDR is the socket peer and wsgi.url_scheme is configuration, so there is nothing to spoof
G12
PROXY protocol v1/v2
out of scope
same reason: the peer address is taken from the socket
G13
Parser fuzzing in CI
verified
Fuzz the request decoder (every PR) — 20k mutations of a seed corpus against parse_request_headers and the chunked decoder, replayable from the seed it prints; asserts determinism, that an INVALID request cannot become valid by appending, that a parsed one is unchanged by bytes after it, and that the decoder's counts index its buffer. poe fuzz-request-long is the release sweep (8 seeds x 250k). The run refuses to pass on thin coverage, and Sabotage the decoder invariants the fuzzer checks breaks each invariant so "no findings" cannot mean "checks nothing"
H. TLS
id
capability
status
evidence
H1
TLS 1.2 / 1.3 termination
out of scope
terminate at a proxy — gunicorn's answer, and the same one applies here
Cross-worker WebSocket fan-out over the broadcast bus
verified
Smoke test the WebSocket chat demo (every PR)
I9
Server-Sent Events, with heartbeats and disconnect cleanup
verified
Smoke test the Datastar counter (every PR)
I10
Last-Event-ID replay from a bounded journal
verified
Smoke test the Datastar todo demo (every PR)
I11
A synchronous view gating a held SSE connection, with cross-worker publish
verified
Smoke test the Django realtime example (every PR) — --realtime
I12
A synchronous view gating a held WebSocket it never speaks
verified
Smoke test the Django realtime example over WebSockets (every PR)
I18
N holds taken AT ONCE from a pool, and a publish reaching all of them
verified
Concurrent --realtime holds from a pool (every PR) — the other realtime rows subscribe sequentially, so this is the only one where more than one hold is in flight; the probe's own self-test gates it, because the result it reports is a negative
I19
Inbound WebSocket data larger than one socket read
verified
Inbound WebSocket messages survive a stalled client (every PR) — the WebSocket read path took one recv per event with no re-arm, A13's defect in the one path nothing had sent a large inbound burst to; invisible on kqueue's level trigger, and on epoll only once the client STOPS sending
I13
Autobahn|Testsuite conformance run, wired to a cadence
verified
autobahn (pre-release) — sections driven separately against the runner's pure-echo ASGI app, image version-pinned so the per-section case counts are asserted exactly; compared both directions against the pinned baseline (240/247, every failure I17's cap): a new failure is red, and an I17 case unexpectedly passing is red too. The comparator's selftest runs first. What the suite cannot see stays L15's territory — its client always closes first (ROADMAP: A conformance-suite tier)
I14
permessage-deflate
out of scope
follows from having no response compression
I15
WebSocket over HTTP/2 (RFC 8441)
out of scope
follows from having no HTTP/2
I16
A Close frame's code is VALIDATED, not just echoed
verified
test_websocket.mojo:test_reserved_close_codes_are_refused_1002 (every PR) — with test_legal_close_codes_are_still_echoed as the other half, so a refusal that refuses everything cannot pass
I17
A message at or above the outbox cap ends the connection
verified
Smoke test the outbox cap ending a connection (every PR) — the marker sent after the oversized message must never arrive, which is what separates ending the connection from dropping a frame the peer cannot know it missed; the under-cap half stops a server that ended every large-message connection from passing. Deliberate, and what Autobahn scores as 7 failures plus all of its performance section. poe sabotage-outbox-cap (pre-release) reverts each of the four rules
I20
A synchronous Flask view gating a held SSE stream and a WebSocket, with cross-worker publish
verified
Smoke test the Flask realtime views (every PR) — the headline names Flask, and K10 only proves plain WSGI. The file under test is extracted from QUICKSTART.md's own fenced block, so it is the one a reader types; the Django rows' RFC 6455 probe drives it unchanged (its gate phase off: the quickstart's views take no token), one stream and one socket pinned per worker under --workers 2, so a message sent on one worker's socket reaches a Flask view and comes back on the other worker's. The quickstart runs the same file from the wheel with curl alone (M10)
J. Static file serving
id
capability
status
evidence
J1
Zero-copy sendfile, body never entering the process
verified
Smoke test zero-copy static file serving (every PR) — --static
Smoke test the serve CLI (every PR) — --static-cache-control
J8
Response compression (gzip, brotli, zstd)
out of scope
recorded in ROADMAP as deliberate: no dynamic compression; a proxy compresses
J9
Precompressed sidecar files (.br, .gz)
out of scope
follows from the row above
K. WSGI (PEP 3333)
id
capability
status
evidence
K1
application(environ, start_response) against a bare callable
verified
Conformance test the WSGI bridge (every PR)
K2
wsgiref.validate pass, with an engagement canary
verified
Conformance test the WSGI bridge (every PR)
K3
The write() callable reaches the client, in production order
verified
Conformance test the WSGI bridge (every PR)
K4
A second start_response, with and without exc_info
verified
Conformance test the WSGI bridge (every PR)
K5
wsgi.input read, readline, iteration, and read past EOF
verified
Conformance test the WSGI bridge (every PR)
K6
QUERY_STRING raw while PATH_INFO is decoded
verified
Conformance test the WSGI bridge (every PR)
K7
close() called on the response iterable
verified
Conformance test the WSGI bridge (every PR)
K8
Correct wsgi.multithread / wsgi.multiprocess for the real topology
verified
Smoke test the Django WSGI example (every PR)
K9
Unsized iterables streamed from a pool thread, sized bodies buffered
verified
Smoke test streamed WSGI bodies (every PR)
K10
Framework-neutral: one contract, two frameworks
verified
Run the WSGI framework contract against Flask (every PR)
K11
The hold headers degrade under another WSGI server: the same view answers a short plain response
verified
Execute the quickstart (every PR) — the quickstart's Django file under gunicorn: /events answers 200 with the view's body and closes inside curl's deadline (held, it would not), the upgrade request answers 200 rather than 101, and publish() reports 0 workers without raising. The README's "degrades, not breaks" sentence, executed
L. ASGI 3.0
id
capability
status
evidence
L1
Single app(scope, receive, send), protocol detected from the object
verified
Conformance test the ASGI bridge (every PR) — --protocol
L2
http scope shape, validated against the spec
verified
Conformance test the ASGI bridge (every PR)
L3
websocket scope: connect, accept, receive, send, close
verified
Conformance test the ASGI bridge (every PR)
L4
lifespan startup and shutdown, degrading if unsupported
verified
Conformance test the ASGI bridge (every PR)
L5
lifespan.state shallow-copied into each request scope
verified
Conformance test the ASGI bridge (every PR)
L6
Streaming responses stream, credit-gated per stream and in total
verified
Conformance test the ASGI bridge (every PR)
L7
Slot ownership across recycled connections, sabotage-proven
verified
Run unit tests (every PR) — poe test-shim drives the extracted shim through real socketpairs and reverts each rule
L8
The event loop running inside asyncio (M0_INVERTED)
verified
Smoke test the ASGI executor under the loop inversion (every PR)
L9
Slot ownership under CPU contention, on the streamed AND WebSocket paths, in both loop modes
verified
stress-asgi (pre-release) — each round runs chunked_keepalive.py then ws_probe.py, so the handshake lands on the slot the streamed connection just released; run under CPU hogs on the pump and again under M0_INVERTED=1
L10
Django's own ASGI handler through the executor
verified
Serve a Django ASGI project through the executor (every PR)
L11
Starlette-family app (FastHTML) through the executor
verified
Serve a FastHTML app through the ASGI bridge (every PR)
L12
Cross-worker pub/sub as scope["state"]["m0"]
verified
ASGI cross-worker fan-out over the BroadcastBus (every PR)
L13
http.response.pathsend
out of scope
--static serves files in Mojo ahead of the application, which is the same saving without the extension
L14
http.response.zerocopysend, early_hint, trailers
out of scope
no application has asked; the extensions are additive and can be taken later
L15
An app-initiated close ends in a FIN, not an RST (RFC 6455 §5.5.1's order)
verified
Conformance test the ASGI bridge (every PR) — ws_probe.py runs 64 concurrent app-initiated closes and requires every one to end in a clean FIN; concurrency is what widens the window, so one close at a time would pass on the broken server
L16
...and the wait for the peer's reply is BOUNDED, so a peer that never answers does not hold its slot
verified
Smoke test the idle connection timeout (every PR) — the linger used to re-arm on every loop pass, which held the slot for the life of the process; L15 alone passes on that server
L18
ASGI on a free-threaded CPython build is refused with exit 78, naming modular/modular#5726
verified
py-canary (weekly) — smoke-django-realtime phase 6 on 3.14t: the executor's Python type cannot be built there (the stdlib lays PyObject out for the GIL build), so the mixed server exits 78 alone, through --doctor, and under --workers 2 without a respawn; on the pinned GIL interpreter the same phase runs the full mixed server
L17
Inbound websocket.receive is flow-controlled: a stalled client is throttled, never silently dropped
verified
Inbound WebSocket messages survive a stalled client (every PR) — the loop suspends the read rather than discarding what the executor cannot take; a concurrently-reading client loses nothing even on the broken build, so the gate stalls first and only then reads
M. Deployment and operations
id
capability
status
evidence
M1
Several applications in one process, routed by prefix
verified
Serve two mounted applications from one process (every PR) — --mount
M2
Flags over environment over defaults
verified
Smoke test the serve CLI (every PR) — --host, --port, --workers, --threads, --blocking-threads
M3
Application discovery from a bare module name
verified
Conformance test the ASGI bridge (every PR)
M4
--app-dir prepended to sys.path, shadowing an installed package
Smoke test --doctor against the server's own exit codes (every PR)
M6
--help names every documented flag; --version matches the release
verified
Smoke test the serve CLI (every PR) — --help, --version
M7
Installable wheel with no toolchain and no dependencies
verified
Build and smoke test the installable wheel (every PR)
M8
The aarch64 wheel built and served on arm64 hardware
verified
Build and smoke test the aarch64 wheel (every PR)
M9
C-ABI shared library loadable by dlopen/ctypes
verified
Smoke test the C-ABI shared library (every PR)
M10
The documented quickstart is executed, not asserted
verified
Execute the quickstart (every PR)
M11
Correct signal handling as PID 1 in a container
verified
Smoke test SIGTERM delivered to PID 1 in a container (every PR) — the wheel exec'd as PID 1 in python:3.12-slim (checked via /proc/1/cmdline, not trusted); docker stop must be the drain's exit 0 well inside the grace, never SIGKILL at the deadline, alone and as a supervisor reaping two workers whose exits must be clean rather than by the propagated signal
M12
Configuration from a TOML file
out of scope
flags and M0_* environment variables cover it; a third source is a third precedence rule
M13
systemd socket activation (LISTEN_FDS)
out of scope
no request for it. The old reason said SO_REUSEPORT covered the restart case, which is not true for anyone running m0serve: no flag or variable enables it (D6). What a restart here does get is the supervisor's graceful drain, so in-flight work finishes; a listener that outlives the process is a different property and nothing has asked for it
M14
An HTTP client in Mojo, for server-to-server calls
verified
Smoke test the HTTP client (every PR)
M15
Windows, musl
out of scope
no Mojo toolchain for either — see the platform table in README.md
M16
No second process: the running server is one process tree of m0serve binaries, and the wheel requires nothing
verified
Execute the quickstart (every PR) — under --workers 2, pgrep -x m0serve counts exactly a supervisor and two workers (the console script execves, so no Python parent survives), and pip show m0serve lists an empty Requires:. The exact count is the self-test: a check that only looks for a broker it does not expect passes on an empty machine
M17
The live demo: the quickstart's shape as a public page with per-visitor channels and rate and size limits, served from its deploy image
verified
Smoke test the live demo's deploy image (every PR) — deploy/demo/Dockerfile built from the tree's own wheel and probed through a published port by scripts/demo_probe.py: a first visitor is handed a token cookie and the page names the served version; without the cookie the hold views answer 403 and a foreign-Origin upgrade is refused; one publish reaches a second stream on the visitor's channel and a stranger's stream hears nothing for a measured silence; a WebSocket frame comes back to the socket and to the streams; a message over the cap is 413 and a burst meets 429 with Retry-After after the first LIMIT attempts and within workers×LIMIT+1; m0serve is PID 1 by /proc/1/cmdline and docker stop drains to exit 0 with held connections open. The same probe, pointed at a URL instead of the image, verifies the live deploy