summaryrefslogtreecommitdiff
path: root/docs/design.md
blob: 2013b3e2240c9addaef085458895d1d06dd207a7 (plain) (blame)
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
# 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.

# Related programs

Programs built on the library ship from this repository as `cloud9/<name>/`,
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<name>` toggle. Fragments register namespaced steps (`<name>`,
`<name>-test`, ...), never call `standardTargetOptions`, and use root-relative
`b.path("<name>/...")`. 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 `src/9p.zig` now
adapts filesystem requests and retains editor/GPIO capacities and permissions.
`src/9p_io.zig` retains mounting, discovery, the editor's event-loop scheduling,
connection limits, and error presentation. Protocol bytes, session validation,
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.