summaryrefslogtreecommitdiff
path: root/docs/fs.md
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-20 00:36:50 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commitc1990b3e6e196ad41379aa432bf3ccca8a65d9f0 (patch)
treed7ad5afe559f7f56464b14c3122242fa3f16399b /docs/fs.md
parent717afaf3177a2e0925b18ae3445119efe808b129 (diff)
downloadpardes-c1990b3e6e196ad41379aa432bf3ccca8a65d9f0.tar.gz
pardes-c1990b3e6e196ad41379aa432bf3ccca8a65d9f0.zip
Flatten the 9P control tree and move it out of fs.zig
The served tree loses the self/ level: /index /ctl /new /log /screen /listeners /pane/<n>/... /os, with /src only in -Dembed-sources=true builds (default off, on for esp32p4). ctl speaks the editor's own language with two lowercase verbs, look TEXT and exec TEXT, plus acme's addr verbs; the new/ factory directory becomes one clone file; cons is gone (exec Msg); name and sel are files; stats report real lengths, modes and mtimes; /log streams pane new/del/rename/save events. The tree code lives in src/ninep/ (tree, pane, ctl, addr, pty, events, screen, sources); fs.zig keeps host access, mounts, resolution and find/grep. Same engine and transports. README (fs-help.txt) and docs rewritten; tests updated and extended. Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'docs/fs.md')
-rw-r--r--docs/fs.md118
1 files changed, 87 insertions, 31 deletions
diff --git a/docs/fs.md b/docs/fs.md
index 942a26a3..c16f810b 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -16,9 +16,13 @@ Explicit paths bypass that search:
| Editor path | Meaning | 9P server path |
|---|---|---|
| `/n/os/proc/self` | OS filesystem | `/os/proc/self` |
-| `/n/self/pane/2/body` | pane 2's text | `/self/pane/2/body` |
-| `/virtual/src/pardes.zig` | source embedded in this build | `/self/src/pardes.zig` |
-| `/n/peer/self/pane/2/body` | another session's text | peer's `/self/pane/2/body` |
+| `/n/self/pane/2/body` | pane 2's text | `/pane/2/body` |
+| `/virtual/pane/2/body` | the same, in the editor's own spelling | `/pane/2/body` |
+| `/virtual/src/pardes.zig` | source embedded in this build | `/src/pardes.zig` |
+| `/n/peer/pane/2/body` | another session's text | peer's `/pane/2/body` |
+
+The mount name `self` is reserved and maps to the server root, so `/n/self/X`
+and `/virtual/X` both name the served `/X`.
`--mount=peer=work` mounts the named session `work`; the dial can also be an
absolute socket path, `unix!/path`, `tcp!IP!port`, or `quic!IP!port`.
@@ -31,7 +35,7 @@ pending Save. Mounts are saved in dumps. Save uses the file's original mount.
socket. Build with `-Dquic=true` and system OpenSSL 3.6+ to enable QUIC;
`--9p-quic='quic!127.0.0.1!5641'` adds its listener. Both accept numeric
IPv4/IPv6 addresses, not DNS names. Listener port zero chooses a free port;
-`/self/listeners` reports all active dial addresses.
+`/listeners` reports all active dial addresses.
All connections have session access, including `os`. TCP is unencrypted.
QUIC uses an ephemeral TLS identity without peer verification or login.
@@ -40,56 +44,108 @@ The four application connection slots are shared across transports;
OpenSSL's internal buffers are separate, dynamically allocated memory.
[Plan9port's client](https://9fans.github.io/plan9port/man/man1/9p.html) can
-drive Unix or TCP without a kernel mount:
+drive Unix or TCP without a kernel mount, and `9ns` mounts the tree in a
+private namespace:
```sh
-9p -n -a "unix!$PARDES_9P" read self/index
-9p -n -a 'tcp!127.0.0.1!5640' read self/index
+9p -n -a "unix!$PARDES_9P" read index
+9p -n -a 'tcp!127.0.0.1!5640' ls pane/1
+9ns --unix "$PARDES_9P" -- sh -c 'cat "$NINE_MOUNT/index"'
```
For [Linux v9fs](https://www.kernel.org/doc/html/latest/filesystems/9p.html),
use `version=9p2000,cache=none,access=any` and `trans=unix`, or `trans=tcp`
with `port=5640`. Set `uname`, `dfltuid`, and `dfltgid` for the local user.
-Leave `aname` empty: the mount root contains `os` and `self`. The opt-in
+Leave `aname` empty. The opt-in
[Linux v9fs experiment](v9fs.md) tests a kernel mount in a separate subprocess
namespace (`zig build v9fs-test`, requiring explicit mount authorization).
Neither 9P2000.u nor 9P2000.L is implemented.
Existing Plan9port/v9fs clients need a userspace bridge for QUIC.
-The server root contains `os` and `self`. Under `self`, `index` lists panes,
-`README` explains the interface. `new/` lists its creation endpoints and another
-`README`; listing, walking and statting entries do not create panes. Opening
-`new/ctl` creates a pane and returns its serial, and `pane/<serial>` contains
-`body`, `tag`, `ctl`, `addr`, `data`, `event`, and selection files. Terminal
-panes additionally have `pty/{ctl,status,data}`.
+## The served tree
+
+```
+/README this guide, also src/fs-help.txt
+/index one line per pane: serial, kind (text|term|pdf|image), dirty flag, name
+/ctl write: one command per line; read: the serials the last command made or touched
+/new reading it creates one empty pane and answers "<serial>\n" (the /net/tcp/clone idiom)
+/log one line per editor event: new|del|rename|save <serial> <name>; reads park
+/screen rendered screen JSON; frozen per open handle
+/listeners the session's dial addresses
+/pane/<n>/ name body tag ctl addr data xdata sel errors event, plus pty/{ctl,status,data}
+/os/ the host filesystem
+/src/ the editor's embedded sources, only when built with -Dembed-sources=true
+```
+
+Nothing in the tree is created by list, stat, walk or read, except that
+reading `/new` makes a pane; that is its whole purpose, so a recursive read
+of the tree makes one pane per open of it.
-`EffectCode <effect>` lists the current backend's embedded implementation
-files under `/virtual`; Look opens their full source.
+`/ctl` and `/pane/<n>/ctl` take the editor's own command language, two verbs:
-`self/screen` returns JSON with `cols`, `rows`, `cursor`, a `styles` table,
-and row-major `cells` of `[grapheme, style_index]`. Each open freezes one
-frame until close, including across multiple reads. The independent client
-can inspect it directly with `Client(socket_path).screen()`.
+- `look TEXT` is a right click on `TEXT`: a path opens a file, `file:12` jumps
+ to a line, a directory opens a shell there, a URL opens in the browser. From
+ `/ctl` the look happens at the active pane; from a pane's `ctl` at that pane.
+- `exec TEXT` is a middle click: a command word from `src/builtins.zig`
+ (`Save`, `Del`, `New`, `Newcol`, `Mount NAME DIAL`, `Unmount NAME`, `Dump`,
+ `Restore`, `Msg TEXT`, `Find`, `Grep`, `Tty`, ...), or anything else, which
+ runs in the pane's terminal.
-A terminal `body` freezes its history on the first read of each open handle;
-later reads use the same bytes while output continues. Close and reopen for
-newer history, or read `pty/data` for the live stream. Screen and terminal-body
-snapshots share 32 handle slots, released on close or disconnect.
+Both verbs are lowercase; the words after `exec` are the editor's capitalized
+commands. A pane's `ctl` also keeps acme's addr verbs (`addr=dot`, `dot=addr`,
+`limit=addr`, `clean`, `dirty`, `cleartag`, `get`, `mark`, `nomark`,
+`noscroll`, `scroll`, `show`). Every line of a write is checked before any
+line runs; a malformed line fails the write with EINVAL. A command that fails
+inside the editor is reported on the message row, not as a write error.
-Writing `body` appends; opening it with truncation replaces its contents.
-`addr` selects a range and `data` replaces that range. `ctl` accepts `name`,
-`look <word>`, `get`, `put`, `del`, and `delete`. Look resolves from that pane
-without editing its body or tag. Holding `event` open redirects the pane's Look
+After a write to either `ctl`, reading `/ctl` answers the serials of the panes
+the command created, or, when it created none, the pane a look focused or the
+pane an exec acted on (even one it closed), one per line. Before any command it answers `pid`, `version` and `panes` lines.
+
+`/pane/<n>/name` reads the pane's file name (a terminal's directory) and
+writing it renames the buffer; a relative name resolves against the pane's
+directory. `sel` reads the editor selection and writing it replaces the
+selection. `body` appends on write and replaces on truncating open. `addr`
+selects a range and `data` or `xdata` read or replace it. `errors` appends to
+the directory's `+Errors` pane. Holding `event` open redirects the pane's Look
and Exec clicks to that client; writing a record back performs the action.
+Stats report real lengths for `index`, `ctl`, `name`, `body`, `tag` and `sel`,
+modes 0644/0666 (0444 for read-only files, 0222 for write-only), the pane's
+last edit time or the process start as mtime, and stable qids. Directory
+entries carry no sizes; stat the entry.
+
+`/log` records `new`, `del`, `rename` and `save` while at least one client
+holds it open; a read parks until a record arrives. `/screen` returns JSON with
+`cols`, `rows`, `cursor`, a `styles` table, and row-major `cells` of
+`[grapheme, style_index]`. Each open freezes one frame until close. A
+terminal `body` freezes its history on the first read of each open handle;
+`pty/data` streams live output. Screen and terminal-body snapshots share 32
+handle slots, released on close or disconnect.
+
+`-Dembed-sources=true` embeds the editor's sources and serves them under
+`/src` (and `/shaders` on GUI builds). `EffectCode <effect>` lists the current
+backend's implementation files under `/virtual`, which Look opens; without the
+option the command reports the sources as unavailable. The esp32p4 build
+enables the option by default, so the device can serve its own source.
+
This is a control filesystem, not a complete POSIX export. Native filenames
may contain up to 255 bytes. Existing regular OS files support read, write,
and truncation to zero; protocol create, remove, rename, and other metadata
-changes are refused. Ownership, permissions, and timestamps are synthetic.
+changes are refused. Ownership and permissions under `/os` are synthetic.
Zero-length truncation accepts the accompanying `mtime` hint sent by Linux
v9fs; the hint is not stored. Standalone timestamp changes remain refused.
+The tree lives in `src/ninep/`: `tree.zig` (nodes, lookup, readdir, dispatch,
+the backend contract), `pane.zig` (pane files), `ctl.zig`, `addr.zig`,
+`pty.zig`, `events.zig` (event and log streams), `screen.zig` and
+`sources.zig`. The protocol engine is `src/9p.zig`, the transports
+`src/9p_io.zig`; `src/fs.zig` keeps host access, mounts, resolution, find and
+grep.
+
`zig build fs-test` drives real sessions using the independent Python client
-in `test/ninep.py`. `zig build 9p-test` checks the freestanding wire protocol;
-`zig build fs-bench` measures filesystem transactions in the core.
+in `test/ninep.py`; `zig build fs-discovery-test` checks that browsing creates
+nothing and that `new`, `ctl`, `name`, `sel` and `log` behave. `zig build
+9p-test` checks the freestanding wire protocol; `zig build fs-bench` measures
+filesystem transactions in the core.
`zig build fs-test quic-test -Dquic=true` also exercises QUIC mounts and I/O.