Picked Peregrine back up today, twenty-one months after I left it halfway through a TLS handshake. The last commit before the gap was Write certificateVerify and Finished, on a blocking prototype that could never have carried HTTP/2.
So most of it got rewritten. Every protocol layer is now a state machine that does no I/O at all: bytes in, bytes out, never a syscall. The event loops own the sockets. That's what lets one parser run on epoll, kqueue and a new io_uring backend, and what will let QUIC reuse the TLS handshake later. It also makes every layer testable a byte at a time: one test feeds sixteen pipelined requests in 199 random splits and demands identical output, and it caught a real bug.
HTTP/1.1 and WebSockets are done. The WebSockets used to share one thread of their own; now each stays on the worker that accepted it. Late this afternoon the TLS 1.3 server landed, checked against the RFC 8448 trace and against Zig's own TLS client for every cipher suite and key exchange, including the post-quantum X25519MLKEM768.
Six cores serving and six loading it, over loopback in Docker on an M2 Pro: about a million keep-alive requests a second where NGINX manages 0.8–0.9 million, and 8–9 million pipelined where it manages 1.2–1.5. Same machine for both, so relative rather than absolute. io_uring works and is no faster than epoll yet, which I didn't expect. HTTP/2 is next.