m0serve
A WSGI and ASGI server written in Mojo. A plain synchronous Django or Flask view can hold a Server-Sent Events stream or a WebSocket by setting two response headers, and one call publishes to every subscriber on every worker. It needs neither Channels nor Redis, and runs as one process. It installs as one wheel with no dependencies.
from m0serve import m0pub
def events(request): # an ordinary synchronous view
r = HttpResponse(": connected\n\n", content_type="text/event-stream")
r["M0-Hold"] = "stream" # m0serve holds the connection open
r["M0-Channel"] = "news" # ...subscribed to this channel
return r # Django's part in it ends here
def announce(request): # any view, command, or cron job
m0pub.publish("news", "deploy finished")
return HttpResponse("ok")
pip install m0serve
m0serve myproject.wsgi --realtime
For an agent: llms.txt is the operating contract and an
index of every page, and each page is Markdown at its URL with .md in
place of the trailing slash (/quickstart.md); /llms-full.txt is the
main pages in one file, and spec.json the
capability matrix as data. Two agent skills carry what a coding agent
looks for first: m0serve and
m0.
demo.m0serve.dev is this running: open it in two tabs and type in either. The page names the version serving it.
blobs.m0serve.dev is the other end of the same stack: an application written in Mojo on the HTTP server underneath m0serve, in an image with no Python in it. Click to drop a blob; every open tab sees it. The Mojo stack is how to write one.
The view runs first, with sessions and permissions in hand, so authorization stays where it is. Under gunicorn the two headers pass through unread and the same view returns a short plain response.
The name is m0serve with a zero, like the packages underneath it: m0-core,
m0-http, m0-wsgi.
Where next
- Quickstart: the application above, running, in five steps.
- Running m0serve: flags, execution modes, what to put in front of it, shutdown.
- Capabilities: what the server does, one row per capability with the test that proves it.
Beyond realtime
- WSGI and ASGI from one binary. The protocol is detected from the
application object. Django, Flask, FastAPI and FastHTML each have a smoke
test in CI. WSGI conformance is checked with
wsgiref, ASGI with a validator written from the specification. - Several applications in one process.
m0serve --mount /=shop.wsgi --mount /app=live.asgiruns sync Django on handler threads and async FastHTML on an asyncio executor, behind one listener with one shutdown. - A slow view does not stall the rest. Handler threads behind each event loop are the default for WSGI.
- Throughput. On a bare ASGI app it serves 1.63x the requests per second of uvicorn with uvloop at 16 connections, on 1.6 cores where uvicorn has one, and under mixed load its fast-request p99 is ahead of uvicorn's in every recorded run. On bare WSGI, one worker with one handler thread each, Granian to m0serve is 1.00x in requests per second, a tie. The benchmarks have every number, each rendered from a dated artifact that CI refuses once it is more than a minor version old.
Limits
- No TLS and no HTTP/2. Terminate at a proxy, as with gunicorn.
- The served contract is stable from 1.0: flags and environment variables,
the two hold headers, and
m0pub.publish(). The changelog records every change. - macOS arm64 and Linux x86_64 and aarch64. CPython 3.10 to 3.14, with free-threaded builds for WSGI only. Not supported: Intel Mac, Windows, musl.
Underneath
m0serve is one package of mojo-http, an HTTP/1.1 server and web framework for Mojo with routing, content negotiation, ETags, SSE, WebSockets, a Datastar adapter and SQLite bindings. The source is on GitHub under the MIT license.