From 0122e94fb37422085325f2dc78ca015051ce5f91 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 21 Sep 2026 17:24:37 -0300 Subject: Advertise the editor in the posted-9P registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pardes spoke 9P and could be mounted, but only by naming its socket: $XDG_RUNTIME_DIR/pardes-9p-.sock sits one directory above the registry and nothing could find it. Now a listening editor advertises itself at $XDG_RUNTIME_DIR/9p/pardes/, a symlink to the socket it already binds. One directory for the program, one entry per editor — the layout zmx posts its sessions under — so several editors group rather than crowd the registry root. The socket does not move: adopting the registry only advertises. Only the runtime-directory socket posts, so an instance on the ~/.local/state fallback stays out of the user's registry, the way a private ZMX_DIR does for zmx. Stopping unposts, and only while the entry is still ours, so a name another editor has since claimed is never unlinked. The cloud9 pin moves to 9c4d668c for cloud9.post's path helpers. Serving is the whole of it. Consuming the registry is 9ns's job: it mounts the lot at /mnt/9p and an interactive fish already self-wraps in one, so a pardes started from a terminal reads /mnt/9p/harness/... with the same code that reads any other path. Two drafts that taught `resolve` to dial the registry itself were reverted — one duplicated 9ns for no gain, the other reinterpreted relative dials, which are a feature. `resolve` is byte-identical to what it was, and no dial that worked changes meaning. What pardes still does not do, and why, is in docs/cloud9.md: it binds its own socket rather than posting through cloud9.post, because post claims flat names only — legalName rejects '/', and claimName derives its lock directory by stripping "/9p" — so a name inside a subdirectory cannot go through it. zmx hand-rolls the same symlink for the same reason. Unifying them means teaching post a group, which is a change to adversarially-hardened code rather than a rename. docs/divergences.md records what this bookmark move leaves beside it: the editor line rruwvuzm (~1300 lines, forked at 01104e7c, still on the old cloud9 pin), the other bookmarks, and two failures that are not this change — fs-test's syntax-highlighting assertion, which fails identically on a clean main, and pardes not starting headless, which is why this is covered by 9p-io-test rather than by running the editor. Tests: 11/11 9p-io-test, including a listener that posts on start and unposts on stop; 31/31 unit-test. --- docs/cloud9.md | 53 ++++++++++++++++++++++++++++++++++++++++ docs/divergences.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 docs/divergences.md (limited to 'docs') diff --git a/docs/cloud9.md b/docs/cloud9.md index d5acc37c..79fda717 100644 --- a/docs/cloud9.md +++ b/docs/cloud9.md @@ -34,3 +34,56 @@ known test-environment limits. Invalid framing now terminates a server connection. Cloud9 also checks reply counts and reserves tags until flush completion. Client metadata is slightly larger to track those reservations, and its bounds tests reflect that fixed cost. + +## The posted-9P registry + +`$XDG_RUNTIME_DIR/9p` is this machine's `/srv`: a server posts itself in it +under a name, and clients dial names rather than paths. cloud9 owns both +sides (`cloud9.post`), and `9ns --mntgen` mounts the whole registry at +`/mnt/9p` for programs that want it as a filesystem. pardes's only part in +it is to put itself there. + +**Serving.** A listening editor advertises itself at +`$XDG_RUNTIME_DIR/9p/pardes/`, a symlink to the socket it already +binds. One directory for the program, one entry per editor, so several +editors group instead of crowding the registry root — the layout zmx posts +its sessions under. The socket itself does not move: adopting the registry +only advertises. Only the runtime-directory socket posts; an instance that +fell back to `~/.local/state/pardes` stays out of the user's registry, the +way a private `ZMX_DIR` does for zmx. Stopping unposts, and only while the +entry is still ours, so a name another editor has since claimed is never +unlinked. + +**Consuming.** Nothing. `9ns --mntgen` mounts the whole registry at +`/mnt/9p`, and an interactive fish already self-wraps in one, so a pardes +started from a terminal sees every posted service as ordinary files — +`/mnt/9p/harness/active/...` is read with the same code that reads any other +path. Teaching pardes to dial the registry itself would put discovery in a +second place for no gain: mounting is the client's job and 9ns is the +client. `--mount==` keeps meaning exactly what it always did, +and a dial keeps resolving exactly as it always did — a bare name is another +pardes session, and anything with a slash is a path, relative ones included. + +The one case that is not free: a pardes started outside a mntgen mount has +no `/mnt/9p`. That is 9ns's problem to solve — by being in the namespace — +not a reason for pardes to carry its own registry client. + +### What this diverges from, deliberately + +* **pardes binds its own socket; it does not post through `cloud9.post`.** + `post` would give us its hardened claim protocol (temp-bind plus atomic + rename under a lock) instead of the stale-socket retry in `listen`, but it + claims *flat* names only: `legalName` rejects `/`, and `claimName` derives + its lock directory by stripping `/9p` from the registry path, so a name + inside a subdirectory cannot go through it. zmx hand-rolls the same + symlink for the same reason. Unifying them means teaching `post` a group — + passing the lock directory in rather than deriving it — and that is a + change to adversarially-hardened code, not a rename. +* **The registry entry is a symlink, not the socket.** A reader that expects + every registry entry to be a socket must `stat` following symlinks. + `9ns --mntgen` and `cloud9.post.dial` both do. +* **Dialing is untouched.** An earlier draft taught `resolve` to fall back + to the registry for a bare name and to read `/` as a + subdirectory entry. Both were reverted: the second reinterpreted relative + dials, which are a feature, and the first duplicated what 9ns already + does. `src/9p_io.zig`'s `resolve` is byte-identical to what it was. diff --git a/docs/divergences.md b/docs/divergences.md new file mode 100644 index 00000000..fada830c --- /dev/null +++ b/docs/divergences.md @@ -0,0 +1,70 @@ +# Divergences + +What is not on `main`, and what on `main` is known to be wrong. Written so +that moving a bookmark does not quietly orphan work or hide a failure. + +## Two lines, forked at `01104e7c` + +`main` is not the only living line, and the other one is not behind it — +they are siblings: + +``` +◆ rruwvuzm 09-17 editor work: syntax, panes, modal, gui, fs, output +│ ◆ xqxpolmw 2b547e15 main 09-20 "Serve Unix and TCP 9P through cloud9.serve" +├─╯ +◆ lsnxpxtq 01104e7c 09-16 "Add macOS backdrop blur and preserve PDF ink opacity" +``` + +* **`main`** carries the 9P work: the `cloud9.serve` runner, and now the + posted-9P registry (`docs/cloud9.md`). +* **`rruwvuzm`** carries editor work — roughly 1300 lines across + `src/syntax.zig`, `src/panes.zig`, `src/modal.zig`, `src/gui/gui.zig`, + `src/fs.zig`, `src/pardes.zig`, `test/output.zig`, `docs/fs.md`. Reach it + with `jj edit rruwvuzm`. + +The fork matters for one concrete reason: **the cloud9 pin lives on `main` +only**. `rruwvuzm` still pins `ae310a20` (2026-09-14), `main` now pins +`9c4d668c`. Rebasing or merging the editor line will want the newer pin, or +`zig build` there fetches a cloud9 that predates `fs.Server`'s current shape. + +## Other bookmarks + +| bookmark | | | +|---|---|---| +| `reload-perf-wip` | 09-11 | unfinished: Reload presentation transport regression | +| `reload` | 09-10 | reload core code with shell-owned allocators | +| `reload-start` | 09-10 | names the Core/Shell seam, deferred Reload request | +| `ninep` | 08-27 | a 9P design note and a design registry to argue it in | +| `macos-fix` | 07-23 | | +| `full-prototype`, `term`, `tty-colors-mouse`, `vibes-ghostty`, `mouse` | 06-xx | older prototypes, also on the `vps` remote | + +None of these are published to the FreeBSD mirror: only `main` is pushed +there, deliberately, because that box serves a public site. + +## Known-failing on `main`, not caused by the 9P work + +* **`zig build fs-test`** fails on a syntax-highlighting assertion — the + word `fn` is expected bold and comes back unstyled: + + ``` + AssertionError: ('fn', [{'fg': {'rgb': [201, 176, 228]}, ..., 'bold': False}, ...]) + ``` + + Verified by restoring `main`'s own `src/9p_io.zig`, `build.zig` and + `build.zig.zon` and re-running: it fails identically. The editor line + (`rruwvuzm`) has substantial `src/syntax.zig` changes and may well be the + fix in progress. + +* **pardes does not start headless.** `pardes --9p=` with no terminal + exits 1 from the argument-forwarding path; the installed build fails + earlier still, with `error.NoDevice` opening a terminal device. So the + registry posting above is covered by unit tests + (`zig build 9p-io-test`) rather than by running the editor. + +## Upstream + +`build.zig.zon` pins cloud9 `9c4d668c`. That commit exists because pinning +cloud9 `534c084f` here failed: cloud9's `build.zig` `@import`s each program's +build fragment, and `9harness` was missing from its `.paths`, so the +published package built from a checkout and not from a tarball. The pardes +build was the first consumer to notice. -- cgit v1.3