Imported from Lfan-ke/mooncat (
AGENTS.md). Install upstream withnpx skills add Lfan-ke/mooncat. Copyright stays with the author.
mooncat is a native ASGI 3.0 server for MoonBit, in the shape of uvicorn: it accepts connections, turns each request into a Scope + Receive + Send, and drives a moonasgi app. HTTP/1.1 and WebSocket ride moonbitlang/async's transport; TLS 1.3, QUIC and HTTP/3 are written here from the packet up.
Working here
moon fmtbefore anything else. CI runsmoon fmt && git diff --exit-code, so an unformatted file fails the build on its own.moon check --target native --deny-warnandmoon build --target nativeare the gate, and warnings are errors. Native is what CI runs, because the server half cannot compile anywhere else; the protocol code below it is portable and its tests do run on the other three backends, somoon test --target allis still worth running after a change down there.moon inforegeneratespkg.generated.mbti. If that file does not change, your edit is not visible to anyone depending on this package, which usually means the refactor was safe. If it does change, read the diff before committing — that is the public interface moving. The examples regenerate their own, which is why those are gitignored.- CI installs the latest moon on every run, so a toolchain that is behind will disagree with it. Upgrade locally rather than pinning.
Layout
One flat package, named by protocol layer. server.native.mbt, http1.native.mbt and process_model.native.mbt are the server itself; config.mbt and lifespan.mbt are the uvicorn-shaped configuration and the ASGI lifespan protocol; websocket*.mbt the upgrade path. Below them sits everything the transport needs and the async library does not provide: tls13_*.mbt and x509.mbt, quic_*.mbt, http3_*.mbt and qpack_*.mbt, and the primitives they rest on (aes.mbt, gcm.mbt, sha256.mbt, hmac.mbt, hkdf.mbt, x25519.mbt, ecdsa.mbt, asn1.mbt). Tests sit beside their subject as *_wbtest.mbt; examples/NN-topic/ are runnable one-file demos; certs/ holds a self-signed pair for the TLS tests, regenerated by scripts/gen_test_cert.sh.
Things worth knowing
- A file named
*.native.mbtis one the other backends cannot compile. The split is what keepsmoon checkhonest about which code is portable; a new file that touches a socket, the filesystem or the async HTTP types belongs on that side of the line. moon.pkgcarrieswarnings = "-alert_internal-29"and both halves are load-bearing: the async TLS-server constructor is#internaland is the only server-side TLS entry point the library exposes, andmoonbitlang/async/fsis only used from the native-only reload watcher, so it reads as unused on the targets that never compile it. Do not widen that suppression to hide a real warning.- Shutdown stops the acceptor by cancelling its task, never by closing the listening socket from another task. Closing a socket that a task is parked in
acceptorreadon does not wake it — it wedges it, past the reach of cancellation.serve_gracefulruns the whole sequence underprotect_from_cancelso a second signal cannot leave the port half-closed. - The graceful path has its own accept loop because
@http.Server::run_forevergives no hook to stop accepting and drain. It builds an@http.ServerConnectionby hand, which is also what lets WebSocket upgrades work there — the 101 handshake needs exactly that connection. - The protocol code is spec-driven and the tests cite their RFC sections. Keep that when adding a case; it is how the coverage gets audited against the spec rather than against itself.
docs/index.htmlis built byscripts/gen_docs.pyfrom///comments. A new top-level file needs an entry in itsSECTIONSlist or it will not appear.