# API and ownership The public entry point is `src/root.zig`. `Msg` covers every base request and response, excluding the reserved, nonexistent Terror. Decode returns borrowed strings/data. Encode checks complete output size before writing; input payloads may already occupy their exact final position in the output frame, but arbitrary input/output overlap is not supported. Stat uses the protocol's inner length; Rstat and Twstat add and validate the outer length separately. `Client` supports 16 ordinary outstanding requests and one additional flush. Replies can arrive out of order. Tags remain reserved while flushes are pending, even when the original request has already completed. Multiple flushes and flushing a flush are accounted for. A completed result's slices remain valid until the next `take()` or `hangup()`. `push()` only appends and never compacts. Submit copies request payloads into the output buffer. An iounit returned by an open/create is per fid; the caller must apply it when selecting atomic I/O sizes. `maxRead()` and `maxWrite()` describe frame limits, not that per-fid guarantee. Renegotiation requires an empty client request window. `Server` supports 64 ordinary outstanding requests and one additional flush. Receive errors terminate the connection. A full request table produces NoTags; output backpressure instead returns null without consuming the next request. `receive()` yields at most one frame. `release()` ends its borrow and advances input. A backend retaining any request data must copy it before release. A successful reply copies output and frees the request tag. An output-capacity error leaves that tag pending so the backend can drain and retry. The server validates transaction structure, not filesystem policy. Backends own fid maps, authentication state, file handles, access modes, complete directory records, atomic wstat, and cancellations. They must retire canceled work before reusing its tag; a tag alone cannot distinguish a stale backend completion from a new request. A version event requires backend cancellation and fid cleanup before `negotiate()`. A partially sent response drains before a new version is accepted. An Rflush promises no later reply to oldtag. Replying Rerror to a flush is refused. Caller-provided input/output buffers must be disjoint and remain stable for the session. No protocol API takes an allocator. Server and client request tables are fixed-size. Copies and work per frame are bounded by buffer size and fixed table capacities; transports and application backends can have their own allocations. # Transports `transport.readFrame` and `writeFrame` adapt `std.Io.Reader` and `std.Io.Writer`. The writer remains buffered until its owner flushes it. Reader errors leave the stream unsuitable for further frame processing; close the connection. `transport.connect` and `listen` use `std.Io.net` for TCP and Unix streams. The POSIX `connectFd`, `listenFd`, `acceptFd`, `read`, `write`, and `wait` APIs fit caller-owned poll loops. They configure nonblocking descriptors and close-on-exec; read/write return null for retry, and read returns zero for EOF. `wait` takes an absolute monotonic deadline. The application closes descriptors, limits its connections, selects addresses and deadlines, and manages Unix path permissions and removal. No mounting or namespace policy is performed. `http.WebSocket` adapts established HTTP streams without owning their sockets or buffers. `http.Client` negotiates HTTP/1.1 upgrades using `std.http.Client`, including its certificate-verified HTTPS support. `http.Bridge` relays between an accepted WebSocket and arbitrary standard readers/writers, including UART adapters. The runnable application's web UI, device leases, deadlines, and origin policy remain in `web/`. See [HTTP ownership and usage](http.md). `Quic(OpenSSL, alpn)` is an optional OpenSSL 3.6+ adapter with a single ordered bidirectional stream per connection. It preserves the previous Pardes transport: ephemeral self-signed certificates, no peer authentication. Applications needing identity verification must provide that policy before using it across a trust boundary. OpenSSL allocations and handshake costs are outside the allocation-free protocol core. Accepted connections must close before their shared listener. # File server engine `fs.Server(Backend, Options)` is a file server built on `Server`: it owns the fid table, permission checks, the directory-read cursor, flush and hangup, and asks a backend only for filesystem operations. The backend contract is `fs.Req` (`Op`: lookup, getattr, setattr, open, read, write, release, readdir; each names a node, and open/read/write/readdir/release carry the handle open returned) answered by `fs.Reply` (`Status`, an errno from `fs.E`, `fs.Attr`, the open handle, the written count) with a read's or readdir's bytes passed beside the reply. `fs.ReplyWith(Payload)` is the same reply carrying an application-defined locator for those bytes, which the engine ignores; a backend declares `Req` and `Reply` as those types. A readdir answers records of `node:u64le dir:u8 len:u8 name`, which the engine encodes into whole Stat entries directly in the output frame. Every 9P request is a job: attach, walk, open, read, readdir, write, clunk, remove, stat and wstat become one or more backend requests issued in order (a walk asks one lookup per element; an open with OTRUNC asks setattr then open; a clunk of an open fid asks release). `next()` yields the next backend request and null while one is outstanding, the output is full, or no whole frame has arrived; the backend answers by the request's tag through `reply()`, at once or later. Tags are unique per connection and never zero. A stale tag (flushed, hung up) is ignored; the backend must retire canceled work before answering. A read, readdir or write may answer `Status.again`: the job parks in a slot, the engine moves on, and `retry()` re-issues each parked request once per round, oldest first, until it completes; a parked write keeps a copy of its data up to `park_data_max` bytes. Any other operation answering `again`, or a park with no free slot, fails with EAGAIN. Tflush answers a parked request's tag with EINTR before its Rflush and drops the slot; a flush of an unknown tag is just Rflush. `hangup()` marks every open fid orphaned and `next()` then yields the release each one owes before anything else, so a dropped connection still pays the backend; a Tversion does the same before negotiating. Every table is sized at comptime by `fs.Options`: `fid_capacity` (256), `slot_capacity` (32), `park_data_max` (128), `name_capacity` (28, at most 255) and `username_capacity` (28); the defaults are the editor's, except the name capacity, which is the board's. `msize_min` (217) is the smallest negotiable msize, one full Rwalk. Create, remove, auth, attaching a named tree and any wstat other than a zero-length truncation are refused. Rerror strings are the ones Linux v9fs maps back to errnos (`fs.errString`). The engine allocates nothing and makes no OS calls; a session is `push()`, `retry()`, `next()`, `reply()`, `output()`, `wrote()`. # Related programs Programs built on the library ship from this repository as `cloud9//`, sibling directories of `src/` with `9`-prefixed names (`web/` builds `9web`; `9proc/` and `9ns/` build `9proc-demo` and `9ns`), never inside it: each has its own `src/`, `test/`, `docs/`, README and a `build.zig` fragment (`pub fn add(b, ctx)`) that the root `build.zig` imports, passes the resolved target, optimize mode and the `cloud9` module to, and enables with a `-D` toggle. Fragments register namespaced steps (``, `-test`, ...), never call `standardTargetOptions`, and use root-relative `b.path("/...")`. The library keeps its contract: mounting, namespaces, threads, allocation and process policy stay in the program. `9proc` is also exported as a module next to `cloud9` for dependents. New related programs follow this layout. # Pardes integration Pardes consumes the sibling package through build.zig.zon. Its control tree (`src/ninep/`) is an `fs.Server` backend using the contract types above; its `src/9p.zig` names the editor's and the board's `fs.Options`. `src/9p_io.zig` retains mounting, discovery, the editor's event-loop scheduling, connection limits, and error presentation. Protocol bytes, session validation, the file-server engine, and TCP/Unix/QUIC transport implementation come from cloud9. Pardes selects the existing `pardes-9p` ALPN for compatibility. The separate `05-zig-p4` firmware build adds cloud9 to the GPIO application's module map. UART hardware access remains in the board firmware. No hardware was flashed by this change.