# Filesystem Every native session serves 9P2000 on a Unix socket. Pane shells receive `PARDES_PID` (the editor's process id), `PARDES_9P` (socket path) and `PARDES_PANE` (pane serial). The socket is `$XDG_RUNTIME_DIR/pardes-9p-.sock`, or lives under `~/.local/state/pardes` when XDG_RUNTIME_DIR is unset. Detached sessions use their session name; `--9p=` overrides it. A `pardes ` launched from a pane forwards Look to that pane over 9P. `PARDES_PID` alone says the shell is inside pardes; `PARDES_9P` and `PARDES_PANE` say how to reach it, and a launch that has the first without the other two refuses rather than opening a second editor. `--nested` opens a separate editor and withholds `PARDES_PID` from its pane shells, so a pardes started in one of them runs a session of its own; its 9P service stays available. Look resolves the OS filesystem first, then the editor's virtual filesystem. 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 | `/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`. At runtime, use `Mount peer dial` and `Unmount peer`. There are eight named mounts; `os` and `self` are reserved. Unmount refuses mounts still used by a pane, its working directory, or a pending Save. Mounts are saved in dumps. Save uses the file's original mount. `pardes --9p-tcp='tcp!127.0.0.1!5640'` adds a TCP listener alongside the Unix 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; `/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. It carries 9P2000 on one bidirectional stream with ALPN `pardes-9p`. Unix and TCP connections share four slots served by cloud9's `std.Io` runner; QUIC has four of its own on the editor's poll loop. 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, and `9ns` mounts the tree in a private namespace: ```sh 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"' ``` A `9ns --unix` mount lives in the private namespace of the command it runs, and nothing outside that command sees it. The mount everyone on the machine shares is the registry one, `9ns --mntgen` (default `/mnt/9p`): every running editor posts itself there, so `/mnt/9p/pardes//` is that editor's tree for any process. A new pane made through `pane/new` is a scratch named `/+New` until it is given a name, and closing a column's last pane leaves such a `+New` in its place (`Delcol` closes the column). 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 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 served tree ``` /README this guide, also src/fs-help.txt /index one line per pane: serial, kind (text|term|pdf|image), dirty flag, name /status pid, version and pane count /look write a line: a right click on it at the active pane; read: the serials the last look, exec or ctl write touched (made, else acted at) /exec write a line: a middle click; read the same serials /log recent events, one a line: new|del|rename|save , msg , dump|restore , err : ; write follow to that open to wait for more /screen rendered screen JSON; frozen per open handle /listeners the session's dial addresses /focus the serial of the pane with the keyboard; write a serial to give it the keyboard /ctl the settings, one a line as a write takes them; write a setting or a session builtin /commands every builtin: word, `arg` if it takes one, and `root` or `pane`, the ctl that takes it /pane/new open it to make a pane; the read answers that pane's serial /pane// name body tag ctl addr dot limit data xdata sel dirty mark scroll errors event look exec, plus pty/{ctl,status,data} on terminals /os/ the host filesystem /src/ the editor's embedded sources, only when built with -Dembed-sources=true ``` Control messages are split by what they act on, as acme keeps window verbs on a window's ctl and webfs and upas/fs keep session settings on a root ctl. Each builtin declares its scope in src/builtins.zig (`scope = .session`; every setting is one, the rest act on a pane). `/ctl` takes the session's builtins, one a line, at whichever pane has the keyboard as each runs -- `Newcol`, `Dump`, `Mount name dial`, `Theme ink`, `Verbose off`; `Exit`, which quits the editor as acme's does (it refuses once, naming each pane with unsaved text, `: Modified (Exit again to discard)`, and a second `Exit` with nothing edited since quits, throwing that text away; a scratch under 100 bytes is not asked about; `Restore`, which replaces every pane, asks the same first -- `Dump` writes `pardes--