<feed xmlns='http://www.w3.org/2005/Atom'>
<title>cloud9.git/9proc, branch main</title>
<subtitle>9p for zig</subtitle>
<id>https://git.0x4200.cafe/cloud9.git/atom?h=main</id>
<link rel='self' href='https://git.0x4200.cafe/cloud9.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/cloud9.git/'/>
<updated>2026-09-21T18:20:27Z</updated>
<entry>
<title>9ns --mntgen: registry subdirectories are mount points too</title>
<updated>2026-09-21T18:20:27Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-21T17:23:27Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/cloud9.git/commit/?id=0d7e295efee1fca0935cf4a8bee9629c007dd2b6'/>
<id>urn:sha1:0d7e295efee1fca0935cf4a8bee9629c007dd2b6</id>
<content type='text'>
A registry entry that is a directory is now served the way the root is:
a synthetic directory listing the real one, dialing the sockets inside
it on walk and recursing into further directories, to max_synth_depth
(8) levels across max_synth_dirs (64) synthetic nodes. That is the
plan9port mntgen shape and the layout zmx now posts under, so a live
session reads at /mnt/9p/zmx/&lt;name&gt;. Before this a directory in the
registry was dialed like a socket and answered EIO for good.

post gains the two entry points the traversal needs: postedDir (the
registry scan, against any directory) and dialPath (a dial by composed
path, no name validation).

Hardening, each from an attack that broke the code:

- BATCH_FORGET carries entries for many owners and puts 0 in the header
  nodeid, so routing it by the header dropped all of them: 32 of 64
  synthetic slots leaked in one close burst and the subdirectories that
  held them answered EIO forever. distributeForgets unpacks the body and
  hands each entry to its owner.
- probe() and connectBlocking() copied a caller's path into the kernel
  address with no bound: a path past sun_path overran the 110-byte stack
  sockaddr (a panic in Debug, silent corruption in ReleaseFast). Both
  refuse it now, probe as `.live` so a claim never deletes what it could
  not inspect.
- That bound then caught 9proc's own listener, which handed probe() the
  whole 108-byte sun_path array instead of the path inside it. The probe
  reads `.live` for anything it cannot ask about, so every stale socket
  became AlreadyListening and no server could ever take a dead
  predecessor's name back. It passes the path now.

Suites: 87/87 root (+7 post/serve attack regressions), 48/48 9ns,
51+88 9ns integration (+4 traversal and slot-recycling checks), 213/0
9ns adversarial, 60/60 9proc plus its adversarial suites with a new
stale-socket takeover check, freestanding green.
</content>
</entry>
<entry>
<title>post registry + 9ns --mntgen: the /srv translation</title>
<updated>2026-09-21T17:13:43Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-21T17:13:43Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/cloud9.git/commit/?id=3a23f6a29e47ace901bd4d82b9db4055fcc12bb9'/>
<id>urn:sha1:3a23f6a29e47ace901bd4d82b9db4055fcc12bb9</id>
<content type='text'>
cloud9.post: servers post their socket under a name in
$XDG_RUNTIME_DIR/9p (post/unpost, posted, dial, Watch) and
serve.Runner.listenPosted posts a server by name, unposting on stop.
Names are budget-checked against the 108-byte socket path; a claim
binds+listens at a private temp path and takes the name with atomic
renames under flock (RENAME_NOREPLACE for free names, RENAME_EXCHANGE
grab-verify-commit for stale ones): the registry path is never unlinked
by a claim, live names refuse with AlreadyPosted, foreign files with
NotSocket, and unpost removes only the caller's inode-matched entry.
Watch surfaces inotify overflow and a replaced registry dir.

9ns --mntgen [--mount DIR] -- PROGRAM: one FUSE mount at /mnt/9p whose
synthetic root lists the posted registry (no connection made); a walk
into an unmounted name dials it and runs the existing bridge dispatch
in a per-server worker thread, routed by mount index in the node id's
top bits (ordinals never reused, cap 4096); a dead server answers EIO
on its subtree and is re-dialed on the next walk. The dial watches
stop_fd through Tversion (connectWatched). All existing 9ns forms are
unchanged.

9proc's unix listener no longer blind-unlinks its path: a foreign
non-socket is refused (Occupied), a live server is refused
(AlreadyListening), only a refused socket is cleared, and stop()
unlinks only the listener's own inode-matched socket.

Hardened by adversarial review (GLM 5.3 x2 + DeepSeek V4.1 Flash, all
high-thinking): double-bind races on one name (0 in 180k rounds),
foreign-file TOCTOU deletions (0 in 4M flips), a 255-byte-name listing
panic, inotify queue overflow silently dropped, listenPosted silently
overwriting, dial-time Tversion hangs wedging the dispatcher, --debug
silently ignored in mntgen, and xattr/statx probes answering EPERM on
the synthetic root (broke `ls -l /mnt/9p`).

Tests: root 80/80, 9ns 47/47, 9proc 60/60, integration 88/88 +
mntgen 37/37, adversarial 213/0, freestanding riscv32 gate green.
</content>
</entry>
<entry>
<title>9proc core becomes a backend of cloud9.fs; engine gains optional features</title>
<updated>2026-09-20T04:47:29Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-20T04:47:28Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/cloud9.git/commit/?id=66f2e492c348677ab3050f5e378b9eb4c04c98ce'/>
<id>urn:sha1:66f2e492c348677ab3050f5e378b9eb4c04c98ce</id>
<content type='text'>
9proc's own fid table, walk loop and dir-read engine are replaced by
cloud9.fs.Server; the tree (static, vars, providers) is served through the
engine's Req/Reply contract with node ids that keep the old qid scheme.
Providers may answer later by returning error.Again (parked in the engine,
retried each step, Tflush -&gt; EINTR); no new files are exposed.
Engine (backward compatible, all opt-in via Backend.features / Options):
create, remove, wstat, reference accounting for backends that count
handles, a salted fid index, name_capacity 0 (names from getattr),
Reply.ename for backend-chosen error text, Attr.path/version/atime.
Engine-level error strings and the 217-byte msize floor now apply to 9proc;
tests updated accordingly.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>9ns: --name and /mnt/9p/&lt;name&gt; mounts, qid.path as inode number, interrupts as Tflush</title>
<updated>2026-09-20T02:55:47Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-20T02:55:47Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/cloud9.git/commit/?id=3e9f8805f293f622bb885cf849b5ce47dc062ad1'/>
<id>urn:sha1:3e9f8805f293f622bb885cf849b5ce47dc062ad1</id>
<content type='text'>
- --name NAME (default derived from the transport: socket basename, tcp-IP-PORT,
  spawned command, fdN) mounts at /mnt/9p/&lt;name&gt;; --mount still overrides.
  ensureMountpoint walks down and creates missing components, shadowing the
  deepest unwritable ancestor. NINE_MOUNT is the only exported variable.
- The inode number reported to the kernel is the 9P qid.path for every node,
  root included; a server handing qid.path 1 to a file (Pardes /self) no
  longer collides with the root.
- FUSE_INTERRUPT for the request in flight becomes Tflush; a blocked read
  returns EINTR when the server answers the flush, chunked transfers return
  short counts, other requests arriving meanwhile are stashed and served
  next. Servers ignoring Tflush still block until they answer.
- 9ns-test now covers nine/bridge/fuse; new adv_bridge_interrupt suite (28);
  9ns-itest grows to 88 checks.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Rename programs: 9player -&gt; 9ns, introspect -&gt; 9proc, app -&gt; web (9web)</title>
<updated>2026-09-20T02:28:22Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-20T02:28:22Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/cloud9.git/commit/?id=ba996acfcad1698adbf4a1834fe50e73b1c6cab9'/>
<id>urn:sha1:ba996acfcad1698adbf4a1834fe50e73b1c6cab9</id>
<content type='text'>
Directories, binaries, build options (-D9ns, -D9proc), step names, module
name (9proc), thread and fs names, env var NINEPLAYER_MOUNT -&gt; NINE_MOUNT,
docs and test scripts. Browser assets move to web/static.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
