summaryrefslogtreecommitdiff
path: root/docs
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
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')
-rw-r--r--docs/config.md3
-rw-r--r--docs/fs.md118
-rw-r--r--docs/v9fs.md12
3 files changed, 95 insertions, 38 deletions
diff --git a/docs/config.md b/docs/config.md
index 972f9403..a55aef1c 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -496,7 +496,8 @@ palette; Ripple and Glitch primarily perturb sample coordinates.
`EffectCode PanelAscii` or `EffectCode Crt` lists the current backend's
build-embedded source paths under `/virtual`. Look opens each full file;
-no checkout is needed. TTY exposes grid transitions, native GUI builds also
+no checkout is needed, but the build must carry them (`-Dembed-sources=true`,
+the default only for esp32p4); otherwise the command reports them unavailable. TTY exposes grid transitions, native GUI builds also
expose scene shaders, and web has neither. Shared implementations share paths.
SDL reports whether GLSL was compiled during this build or came from the
`-Dprebuilt-shaders` snapshot paired with the committed SPIR-V.
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.
diff --git a/docs/v9fs.md b/docs/v9fs.md
index 51b35a46..6cb4e998 100644
--- a/docs/v9fs.md
+++ b/docs/v9fs.md
@@ -8,11 +8,11 @@ your configured shell as your normal user, with your account's supplementary
groups. The shell receives `PARDES_MOUNT`, the absolute mountpoint:
```sh
-ls "$PARDES_MOUNT/self/pane"
-cat "$PARDES_MOUNT/self/index"
-cat "$PARDES_MOUNT/self/README"
-ls -l "$PARDES_MOUNT/self/new"
-cat "$PARDES_MOUNT/self/pane/$PARDES_PANE/body"
+ls "$PARDES_MOUNT/pane"
+cat "$PARDES_MOUNT/index"
+cat "$PARDES_MOUNT/README"
+cat "$PARDES_MOUNT/pane/$PARDES_PANE/body"
+echo 'exec Msg hello' > "$PARDES_MOUNT/ctl"
```
The mount belongs to that pane's subprocess tree. Other panes and the editor
@@ -96,7 +96,7 @@ unprivileged and uses `sudo -n`, retaining the calling terminal's authorization.
Missing authorization or kernel support fails the test instead of skipping it.
It checks mount isolation, privilege dropping, inherited access, directory
refresh, body reads and truncation, independent wire updates, addressed edits,
-shell redirection to ctl, Exec dispatch, rendered screen JSON, and OS-file reads.
+shell redirection to name, Exec dispatch, rendered screen JSON, and OS-file reads.
Linux follows `O_TRUNC` with a `Twstat` carrying zero length and an `mtime` hint.
Pardes accepts this truncation without storing caller-selected timestamps;