# cloud9 A Zig 0.16 library for base **9P2000** clients, servers, and transports. The protocol core uses caller-owned buffers and bounded request tables. It has no heap allocation, OS calls, threads, or filesystem policy. TCP and Unix stream adapters support `std.Io`; nonblocking POSIX adapters support existing event loops. HTTP/WebSocket transport supports native HTTPS and caller-owned byte streams. Optional QUIC transport takes caller-provided OpenSSL bindings and an ALPN name. Mounting, namespace discovery, exported trees, permissions, and application lifecycle stay with the application. There are no 9P2000.u or 9P2000.L messages. Run the HTTP gateway and its WebAssembly file browser: ```sh zig build serve -Doptimize=ReleaseSafe -- --upstream tcp:127.0.0.1:564 # Open http://127.0.0.1:8080; files load automatically ``` This requires a running 9P server at `127.0.0.1:564`; the gateway does not start one. Set `--upstream unix:/actual/path/to/9p.sock` to use a running Pardes instance. If port 8080 is occupied, add `--listen 127.0.0.1:0` and open the URL printed at startup. Browser attach defaults can be set with `--user NAME --tree NAME`; there is no connection form in the page. File links use ordinary paths such as `/self/listeners`; gateway assets and endpoints live under `/_cloud9/`. It also accepts Unix sockets and configured serial devices. The HTTP transport is public library code; [HTTP/HTTPS usage and the browser tests](docs/http.md) explain how to embed it and where HTTP/3 support belongs. ```sh zig build test zig build transport-test zig build http-test zig build e2e -Doptimize=ReleaseSafe # Chromium + native HTTPS + Unix + serial zig build quic-test -Dquic=true # system OpenSSL 3.6+ zig build fuzz -Doptimize=ReleaseSafe -- 4200 1000000 zig build differential -Doptimize=ReleaseSafe -- --seed 4200 --rounds 1000 zig build differential -Doptimize=ReleaseSafe -- --seed 4201 --rounds 100 --chunk 1 ``` The differential harness requires Go, downloads pinned test-only dependencies, and saves a corpus, configuration, and JSON report under `test/differential/results`. It compares valid messages with 9fans and go9p and runs the cloud9 client against go9p's filesystem server over local pipes. Malformed-input probes run only against cloud9. No remote targets are contacted. Consume the published package by pinning a commit in `build.zig.zon` — this is what makes a build reproducible from the manifest alone: ```sh zig fetch --save=cloud9 git+https://git.sr.ht/~gbrls/cloud9# ``` The toolchain has no `git+ssh` support, so the manifest carries the read-only HTTPS URL. Push to `git@git.sr.ht:~gbrls/cloud9`. For work on both packages at once, substitute `.cloud9 = .{ .path = "../cloud9" }` while editing, then re-pin. Either way, import the module the same way: ```zig const cloud9 = b.dependency("cloud9", .{ .target = target, .optimize = optimize, }).module("cloud9"); app.root_module.addImport("cloud9", cloud9); ``` Start a client with `Client.init(.{ .in = input_buffer, .out = output_buffer })`. Submit `.version` first. Drain `output()` through your transport and report the number sent with `wrote()`. Feed received bytes using `push()` and collect tagged results with `take()`. All base requests are available through `Client.Request`. For a server, call `Server.receive()` after `push()`. A request borrows the input until `release()`. After releasing backend fids and canceling outstanding work, answer Tversion with `negotiate()`. Answer other requests with `reply()`. The backend implements its filesystem and fid lifecycle; cloud9 checks reply types, tags, counts, negotiated frame sizes, and flush completion. `fs.Server(Backend, options)` is a file server on top of that session: it keeps the fids, permissions, directory cursors, flush and hangup, and turns each 9P request into backend operations (`fs.Req`: lookup, getattr, setattr, open, read, write, release, readdir) that the backend answers by tag with `fs.Reply`, at once or later; a read or write may park with `Status.again` and is retried. Table sizes are comptime `fs.Options`; nothing allocates. See [the engine's contract](docs/design.md#file-server-engine). On a hosted target, `serve.Runner(Backend, options, limits)` runs that engine on `std.Io`: `listen()` on Unix or TCP addresses, a bounded connection table, two tasks per connection, a handler the backend answers through `Conn.reply()` now or later, `Conn.wake()` to retry parked reads when the backend has news, and `stop()` to close and join. Freestanding targets keep the push/step loop. See [the Io runner](docs/design.md#io-runner). ## Programs Related programs live in `cloud9//`, one directory per program beside `src/`, and carry `9`-prefixed names (`9web`, `9proc`, `9ns`). Each has its own sources, tests, README and a `build.zig` fragment that the root `build.zig` imports and enables with a `-D` toggle (`zig build --help` lists the steps). New related programs follow the same layout. * [`web/`](docs/http.md) — the HTTP/WebSocket gateway `9web`, a 9P multiplexer and its WebAssembly browser client (`web/main.zig`, `web/mux.zig`, `web/httpfs.zig`, `web/probe.zig`, `web/client.zig`, assets under `web/static/`). It holds one upstream 9P connection and fans it out to many downstreams: the browser over a WebSocket, plain 9P clients over an optional `--serve` TCP/Unix listener (a mux like 9pserve), and an HTTP view of the tree under `/fs/` (`GET`/`PUT`/`DELETE`, directory JSON, `Range`, and `?follow=1` server-sent events). `--probe` embeds 9proc for self-introspection. The page adds a tree view, create/rename/delete, upload, a stat panel and live follow. Always built; steps `serve`, `web`, `http-test`, `mux-test`, `http-fs-test`, `e2e`. * [`9proc/`](9proc/README.md) — a 9P debug/introspection server as a library (module `9proc`, exported next to `cloud9`; freestanding core that is a backend of `fs.Server`, Linux debug layer) and its demo binary `9proc-demo`. `-D9proc=[bool]`; steps `9proc`, `9proc-test`, `9proc-check-freestanding`, `9proc-debug-test`, `9proc-debug-itest`, `9proc-adv`. * [`9ns/`](9ns/README.md) — mount a 9P tree into a fresh user+mount namespace via FUSE and run a program in it, without root (Linux, no libc). `-D9ns=[bool]`; steps `9ns`, `9ns-test`, `9ns-itest`, `9ns-adv`. The `9proc` and `9ns` toggles default to on for Linux targets and off elsewhere; enabled programs are installed by the plain `zig build` next to `9web` and `cloud9-probe`, and `programs-test` / `programs-itest` run every enabled program's unit and integration steps. A dependent package gets both modules from the one dependency: ```zig const cloud9_dep = b.dependency("cloud9", .{ .target = target, .optimize = optimize }); app.root_module.addImport("cloud9", cloud9_dep.module("cloud9")); app.root_module.addImport("9proc", cloud9_dep.module("9proc")); ``` See [design and ownership contracts](docs/design.md), [specification references](docs/spec.md), and [validation](docs/validation.md).