1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
# 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#<commit>
```
The toolchain has no `git+ssh` support, so the manifest carries the read-only
HTTPS URL. Push to `[email protected]:~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).
## Programs
Related programs live in `cloud9/<name>/`, 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<name>` toggle (`zig build --help`
lists the steps). New related programs follow the same layout.
* [`web/`](docs/http.md) — the HTTP/WebSocket gateway `9web` and its
WebAssembly browser client (`web/main.zig`, `web/client.zig`, assets under
`web/static/`). Always built; steps `serve`, `web`, `http-test`, `e2e`.
* [`9proc/`](9proc/README.md) — a 9P debug/introspection server as
a library (module `9proc`, exported next to `cloud9`; freestanding
core, 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).
|