Skip to content

Instantly share code, notes, and snippets.

@progrium
Created June 1, 2026 23:01
Show Gist options
  • Select an option

  • Save progrium/467b2e6e409a5ed89c4f2283c4573cb0 to your computer and use it in GitHub Desktop.

Select an option

Save progrium/467b2e6e409a5ed89c4f2283c4573cb0 to your computer and use it in GitHub Desktop.

SSH and Plan 9 rcpu Compared

Executive summary

SSH and Plan 9’s modern rcpu stack solve superficially similar problems, but they embody very different architectural bets. SSH is a single, standardized, layered protocol family: a transport protocol provides encryption and host authentication, a user-authentication protocol authenticates the client, and a connection protocol multiplexes multiple logical channels over one encrypted tunnel. That design makes SSH broadly interoperable and feature-rich, especially for shells, port forwarding, X11 forwarding, agent forwarding, and subsystem execution. The core architecture is explicitly defined in RFC 4251-4254. citeturn29view0turn29view1turn29view2turn29view3

Plan 9’s rcpu stack is different in kind. It is not one giant transport with a large built-in channel taxonomy. Instead, it is a composed system service: rcpu, rimport, rexport, and rconnect are front-end commands; tlssrv/tlsclient provide mutually authenticated encrypted transport; factotum and the Plan 9 auth protocols provide identity and key handling; exportfs and 9P integrate remote resources into the namespace; optional aan adds reconnection resilience. In current 9front documentation, rcpu is explicitly described as running commands on a CPU server with the local namespace exported under /mnt/term, while rconnect sets up a mutually authenticated encrypted TLS connection and ships an rc remotescript to the server. citeturn26view3turn27view0turn25view0

The crucial analytical distinction is this: SSH multiplexes services inside the transport, while Plan 9 composes services at the namespace layer. In SSH, shells, TCP forwarding, X11, and agent forwarding are all channel types inside the SSH connection protocol. In Plan 9, remote execution, remote filesystem export, and service posting are separate tools that converge in the name space via 9P, /srv, bind/mount, and exportfs. This is why the Plan 9 model often feels “smaller” and more elegant to systems programmers: instead of inventing more channel types, it reuses the system’s general mechanism for composition. citeturn29view3turn30view6turn26view2turn19search1turn20search5

That does not mean rcpu is simply “better.” SSH is far stronger on interoperability, deployment ubiquity, and standardized feature coverage. OpenSSH supports connection sharing, agent forwarding, host-key databases, update/rotation mechanisms, certificates, and a mature portable implementation across many operating systems. citeturn33view0turn33view1turn34view0turn34view2turn34view3turn44search0

What makes Plan 9 compelling is different. Its remote-computing story is not an add-on bolted to a local POSIX process model; it is a natural extension of Plan 9’s distributed namespace design. rcpu passes the current directory, file descriptors 0/1/2, /dev/cons, interrupt notes, and a mounted view of the local namespace into the remote execution environment, and related tools can post services into /srv for further composition. That is precisely the sort of “remote is just another part of the namespace” design that makes Plan 9 feel unusually coherent. citeturn26view3turn27view0turn19search1turn26view2

Architectural comparison

SSH’s architecture is explicitly layered in the protocol spec. RFC 4251 defines three major components: the transport layer, user authentication, and connection protocol. RFC 4253 says the transport layer provides strong encryption, cryptographic host authentication, integrity protection, and optional compression; RFC 4252 defines the authentication framework; RFC 4254 defines interactive sessions, remote command execution, forwarding, and logical channel multiplexing on top. citeturn31view6turn29view1turn29view2turn29view3

By contrast, current 9front rcpu is documented as a set of cooperating commands and services. rcpu, rimport, rexport, and rconnect are in the user-facing command layer; tlssrv and tlsclient establish encrypted mutually authenticated sessions; factotum stores and brokers authentication material; auth_proxy is the library bridge into factotum; exportfs serves name spaces across the network; srv/srvtls and /srv publish services locally; listen names standard service ports like tcp17019 for rcpu and tcp17020 for TLS-encrypted 9P. citeturn26view3turn25view0turn21view2turn26view1turn26view2turn19search1turn18search3

A useful way to visualize the contrast is that SSH centralizes complexity in one protocol stack, whereas Plan 9 distributes it across reusable OS mechanisms.

flowchart TD
    subgraph SSH
        A1[SSH Transport RFC 4253]
        A2[SSH Userauth RFC 4252]
        A3[SSH Connection RFC 4254]
        A4[Shell session]
        A5[TCP forwarding]
        A6[X11 forwarding]
        A7[Agent forwarding]
        A1 --> A2 --> A3
        A3 --> A4
        A3 --> A5
        A3 --> A6
        A3 --> A7
    end

    subgraph Plan9_rcpu
        B1[tlssrv or tlsclient]
        B2[factotum and auth_proxy]
        B3[rconnect and rc remotescript]
        B4[rcpu]
        B5[exportfs and 9P]
        B6[/srv and bind mount]
        B7[rimport and rexport]
        B2 --> B1 --> B3 --> B4
        B4 --> B5 --> B6
        B7 --> B5
    end
Loading

The feature consequence is immediate. SSH has a first-class channel model with explicit per-channel open, confirmation, window sizing, packet sizing, and channel-specific requests. Plan 9 rcpu has no analogous channel zoo inside one remote-login protocol; instead, remote files and services are mounted and bound into the namespace, and separate tools like rimport/rexport handle those operations directly. citeturn30view6turn26view3turn26view2turn19search1

Protocol components and layering

Dimension SSH Plan 9 rcpu stack
Top-level architecture One protocol family with transport, userauth, and connection layers. citeturn31view6turn29view1turn29view2turn29view3 Composed tools and services: rcpu/rconnect, TLS helpers, factotum/auth, exportfs, 9P mounts, /srv. citeturn26view3turn25view0turn21view2turn26view2turn19search1
Remote command model Remote shell or command as a connection-protocol session. citeturn29view3turn33view5 rcpu runs rc commands on a CPU server; rconnect sends an rc remotescript. citeturn26view3turn27view0
Filesystem integration Not part of the core shell semantics; file transfer/subsystems are additional protocol features or separate tooling. citeturn29view3 Local namespace is exported to the remote side under /mnt/term; exportfs and 9P are first-class. citeturn26view3turn26view2
Service publication Inside SSH via channel types and forwarding requests. citeturn29view3turn30view6 Via /srv, srv, srvtls, bind/mount, and rimport/rexport. citeturn19search1turn20search5turn26view3
Naming integration Mostly host/key/config centric. citeturn34view0turn34view2 Integrated with ndb, /srv, mount points, and 9P namespace composition. citeturn26view3turn21view2turn19search1

Connection establishment and session lifecycle

SSH’s lifecycle is canonical: TCP setup, version exchange, algorithm negotiation, key exchange, transport protection, service request, user authentication, then connection-protocol channels for shells, commands, or forwarding. RFC 4253 notes that the protocol was designed to minimize round trips, and in typical cases the key exchange, server authentication, service request, and acceptance can complete in two round-trips; RFC 4252 then runs on top of the protected transport; RFC 4254 then opens session or forwarding channels. citeturn29view1turn30view0turn31view1turn29view2turn29view3

Plan 9 rcpu’s lifecycle is much more “service construction” oriented. The client identifies a CPU server via -h, $cpu, or ndb. rconnect establishes a mutually authenticated encrypted TLS connection using tlssrv, then sends an rc remotescript whose execution cross-connects the relevant file descriptors. rcpu exposes the local namespace to the remote side under /mnt/term, while rimport and rexport provide directional namespace transfer as separate commands. The listen service maps rcpu to tcp17019, and the old import command is explicitly documented as deprecated in favor of rimport. citeturn26view3turn27view0turn25view0turn18search3turn18search10

An important operational detail is resilience. SSH itself does not standardize transparent reconnect-and-replay. OpenSSH exposes keepalives and connection sharing, but these primarily detect failure or reuse live connections. Plan 9 offers aan, which is designed to survive broken links and retransmit unacknowledged data after reconnection; rcpu, rimport, and rexport can request it with -p. citeturn33view0turn34view3turn18search2turn27view0

sequenceDiagram
    participant C as Client
    participant S as Server
    participant AS as Auth service or trust root

    Note over C,S: SSH
    C->>S: TCP + version exchange + KEXINIT
    C->>S: key exchange and host authentication
    C->>S: ssh-userauth
    C->>S: open session or forwarding channels
    S-->>C: shell, command, TCP/X11/agent channels

    Note over C,S: Plan 9 rcpu
    C->>S: connect to tcp17019
    opt optional pre-TLS auth
        C->>AS: p9any or dp9ik via factotum
        S->>AS: p9any or dp9ik via factotum
    end
    C->>S: mutual TLS via tlsclient or tlssrv
    C->>S: send rc remotescript
    S-->>C: executes as authenticated user
    S-->>C: remote process sees /mnt/term via exportfs and 9P
Loading

Lifecycle comparison

Phase SSH Plan 9 rcpu
Name resolution TCP/IP host selection, plus local config and known-host policy. citeturn33view5turn34view0turn34view2 Host comes from -h, $cpu, or ndb. citeturn26view3
Initial handshake Version exchange and KEXINIT over TCP. citeturn29view1 Connect to tcp17019; then use tlsclient/tlssrv. citeturn18search3turn25view0
Host authentication SSH transport host key. citeturn29view1turn34view0turn34view1 TLS certificate thumbprint and/or pre-TLS p9any authentication feeding TLS PSK mode. citeturn25view0turn19search0turn20search1
User authentication ssh-userauth with publickey, password, or hostbased methods. citeturn29view2turn30view9 Factotum-mediated Plan 9 auth protocols such as p9any, p9sk1, dp9ik; -a on tlssrv also runs the server command as the authorized user. citeturn21view2turn26view1turn26view0turn25view0
Session startup Open a session channel, request PTY if needed, execute shell or command. citeturn29view3turn33view5 Ship an rc remotescript; remote side runs user profile with $service=cpu; local namespace appears at /mnt/term. citeturn26view3
Continuity on flaky links Detect/keepalive/reuse live master connection; no transparent transport replay in RFCs. citeturn33view0turn34view3turn29view3 aan can reconnect and retransmit unacknowledged data; -p enables it. citeturn18search2turn27view0

Authentication, key management, encryption, and trust

SSH’s authentication model is layered and mostly decentralized. RFC 4252 defines a general-purpose user authentication protocol on top of the encrypted transport, with public-key authentication as the required method in the base spec and password and host-based methods also specified there. The session identifier from the lower layer is included in public-key signatures. In common OpenSSH operation, host authenticity is enforced through known_hosts and StrictHostKeyChecking, while user keys are managed through ~/.ssh, authorized_keys, ssh-agent, and optional CA-style certificates (HostCertificate, TrustedUserCAKeys). citeturn29view2turn30view9turn34view0turn34view1turn34view5turn33view1turn34view3turn34view4

Plan 9 centers authentication in factotum, which is a file-service authentication agent that manages keys as attribute-value tuples and supports multiple protocols including p9any, p9sk1, dp9ik, RSA for SSH/TLS, and cleartext-password helpers. auth_proxy is the library bridge into factotum. In the classic Plan 9 model, an authentication server and p9any are central; in 9front’s modern TLS stack, tlssrv can optionally run p9any before TLS and use the resulting session secret as a TLS pre-shared key, which permits authenticated TLS even without certificates. citeturn21view2turn26view1turn26view0turn25view0

That produces a meaningful trust-model contrast. SSH usually trusts host keys recorded in known_hosts, optionally DNS SSHFP, and optionally local CAs for user or host certificates. Plan 9 TLS apps, by contrast, check remote public keys against thumbprint files under /sys/lib/tls, and the thumbprint documentation explicitly says these local policy files “play the role taken by certificate authorities” in PKI systems. In other words, Plan 9 leans toward local trust lists and explicit local policy, whereas SSH often mixes TOFU, local policy, and optional CA-style certification. citeturn34view0turn34view1turn34view3turn19search0turn20search1

On cryptographic mechanics, SSH’s base transport negotiates key exchange, public-key algorithm, cipher, MAC, and hash. Integrity is packet-MAC based in RFC 4253, with the MAC computed over the sequence number and unencrypted packet; compression is optional; re-exchange is built into the transport. RFC 4251 also describes the transport layer as providing perfect forward secrecy, and RFC 4253 recommends rekeying after roughly a gigabyte or an hour. citeturn29view1turn31view0turn31view1turn31view2turn31view5turn30view3

Plan 9’s story is more hybrid. tlssrv/tlsclient provide TLS encryption and mutual authentication; tlssrv -a/-A can precede TLS with p9any and use the resulting session secret as a PSK. Separately, authsrv(6) describes dp9ik as a stronger successor to p9sk1, adding resistance to offline password attacks and deriving a 2048-bit session secret with HKDF-SHA256 over random contributions from both sides, after an AuthPAK exchange that uses Chacha20/Poly1305 AEAD to protect ticket requests to the authentication server. That means Plan 9’s remote-auth story is not “just TLS”; it can combine TLS transport with a fairly distinctive identity and key-establishment system. citeturn25view0turn28view2turn28view3

Security properties and trust assumptions

Property SSH Plan 9 rcpu stack
Host authentication Host keys checked via known_hosts; StrictHostKeyChecking hardens MITM resistance; optional host certificates. citeturn34view0turn34view1turn34view4 TLS certificate thumbprints, optionally client/server certs, and optionally p9any-derived TLS PSK. citeturn25view0turn19search0turn20search1
User authentication RFC-defined publickey, password, hostbased; OpenSSH adds agents and certificate workflows. citeturn29view2turn30view9turn34view3turn33view1 Factotum-mediated p9any, p9sk1, dp9ik, RSA, password, and other protocols. citeturn21view2turn27view2turn26view0
Forward secrecy Provided by SSH transport’s DH-based key exchange architecture; re-exchange supported. citeturn31view5turn30view3 Explicitly improved in dp9ik and AuthPAK-derived flows; tlssrv man page does not enumerate TLS ciphersuites, so transport-level PFS for certificate mode is not fully spelled out there. citeturn27view2turn28view2turn28view3turn25view0
Integrity Per-packet MAC in RFC 4253. citeturn31view0turn31view2 TLS record integrity; AuthPAK ticket protection uses Chacha20/Poly1305 in auth-server exchange. citeturn25view0turn28view2
Trust root Mostly per-host host keys, optionally SSHFP and CA keys. citeturn34view0turn34view1turn34view3 Local thumbprint files and/or centralized Plan 9 auth server. citeturn19search0turn20search1turn26view0
Failure assumption If the server endpoint is compromised, terminal sessions and forwarded channels are compromised. citeturn31view7 By inference, compromise of the CPU server or auth server can threaten execution authority, namespace exports, and trust decisions; the docs explicitly centralize significant trust in factotum and the AS. citeturn21view2turn26view0

A concrete operational difference follows from those assumptions. SSH agent forwarding is powerful but dangerous: OpenSSH explicitly warns that a remote user able to access the forwarded agent socket can cause the local agent to perform authentication operations, even though the private key material itself does not leave the agent. Plan 9 has an analogous but differently shaped risk surface: the elegant thing about rcpu is that it exports parts of your local namespace, but that also means the exported namespace becomes part of the remote execution environment and should be restricted carefully, for example with -P patternfile on rcpu/rexport. citeturn33view5turn33view1turn27view0turn26view2

Multiplexing, naming, and integration with 9P

SSH’s connection protocol is transport-centric. RFC 4254 defines channel open, open confirmation, per-channel initial window size, maximum packet size, window adjust, channel data, and closing semantics. Sessions, TCP forwarding, X11 forwarding, and other services are then encoded as types or requests on those channels. OpenSSH’s ControlMaster adds another layer of multiplexing at the client implementation level, letting multiple local sessions share one underlying network connection. citeturn29view3turn30view6turn33view0

Plan 9 does not try to solve that problem at the protocol level in the same way. rcpu itself is about remote execution with exported local namespace. rimport and rexport handle remote subtree mounting and exporting. exportfs is a relay file server that executes operations in the exported tree and returns results, typically mounted under /mnt/term. srv and srvtls create service files in /srv, and when rimport -s name is used the service can be posted under /srv/name for mounting in other namespaces. This is not “channel multiplexing” in the RFC 4254 sense; it is system-level service composition through 9P and namespaces. citeturn26view3turn26view2turn19search1

This is one of the most important contrasts if the goal is to explain what makes Plan 9 great. SSH extends a socket with more logical socket-like substreams. Plan 9 extends the name space. In SSH, extra remote capability typically appears as a forwarded connection, subsystem, or file transfer protocol. In Plan 9, extra remote capability appears as something you can mount, bind, post in /srv, and compose with the rest of the system’s file-oriented interfaces. That difference is exactly why Plan 9’s remote computing feels less like “remote access” and more like “distributed local computing.” The manuals do not phrase it that way, but that conclusion follows directly from rcpu, exportfs, srv, and bind. citeturn26view3turn26view2turn19search1turn20search5

Multiplexing and naming comparison

Question SSH answer Plan 9 answer
Where is multiplexing done? Inside the SSH connection protocol via logical channels; client-side connection sharing via ControlMaster can reuse one transport. citeturn29view3turn30view6turn33view0 Mostly above the transport, by publishing and mounting services in the namespace; rimport/rexport/exportfs compose with 9P. citeturn26view3turn26view2turn19search1
How are remote resources named? Host-oriented and channel-oriented; forwarding targets remain explicit remote endpoints. citeturn29view3 Via ndb, mount points, /mnt/term, /srv/name, and bind/mount operations. citeturn26view3turn19search1turn20search5
How are files integrated? Not a core shell property; requires protocol features or companion tools. citeturn29view3 Core property: exportfs and 9P make remote file trees part of the process namespace. citeturn26view2turn26view3
Service discovery Conventional network/service naming plus config. citeturn34view0turn34view2 ndb for host/auth lookup and /srv for posted services. citeturn26view3turn21view2turn19search1

Performance, resource usage, implementation complexity, interoperability, and ergonomics

The performance story is mixed. SSH’s transport was explicitly designed to minimize round-trips and to keep overhead acceptable, though RFC 4253 notes that the extra headers, padding, and MAC are proportionally significant for very small interactive packets. It also supports in-transport rekeying and optional compression. citeturn31view1turn31view2turn30view1turn30view3

Plan 9’s rcpu path is likely cheaper in some dimensions and costlier in others, but the documentation supports only a qualitative conclusion, not a benchmark claim. It is simpler in protocol ambition: there is no equivalent of SSH’s built-in channel ecology for X11, agent forwarding, or TCP forwarding. On the other hand, because Plan 9 composes remote files and services at the namespace layer, sophisticated workflows may involve multiple cooperating services like rcpu, exportfs, rimport, srvtls, and optional aan, potentially meaning more distinct connections or posted services over time. This is an inference from the architecture and manuals, not a benchmark result. citeturn26view3turn26view2turn19search1turn18search2turn29view3

Implementation complexity is where the contrast is easiest to see, even with incomplete Plan 9 LOC extraction. OpenSSH’s own OVERVIEW explicitly splits the implementation across packet handling, channel forwarding, session handling, and many support modules. In current public source metadata, sshd.c is 1,741 LOC, ssh-agent.c is 2,449 LOC, and session.c is 2,367 LOC, with additional large modules such as channels.c, kex.c, and packet.c visible as distinct units. citeturn45search5turn44search1turn44search2turn44search3turn46view1turn46view2turn46view3

For Plan 9, the readily available primary evidence strongly suggests a more decomposed construction style: rcpu, rimport, rexport, and rconnect live in /rc/bin; tlssrv and tlsclient live in separate C source files; exportfs is another separate service; srv/srvtls is another; authentication is delegated to factotum and auth_proxy. That decomposition matters more than the exact line count because it means remote computing is assembled from ordinary system parts instead of largely residing inside one privileged network daemon and its companion client. I was not able to extract reliable per-file LOC for rcpu, rconnect, tlssrv.c, and tlsclient.c before finalization, so any stronger quantitative claim would be misleading. citeturn27view0turn25view0turn26view2turn19search1turn21view2turn26view1

Interoperability is the opposite trade-off. SSH wins decisively. The RFCs standardize the protocol; OpenSSH is a complete portable implementation; and the OpenSSH project positions itself as the standard remote-login connectivity tool. Plan 9’s modern rcpu is much less interoperable with the broader world, though 9front’s drawterm broadens client portability substantially: its README documents builds for Unix, Solaris, Windows, macOS, and Android, and it specifically calls out support for 9front’s DP9IK authentication and TLS-based rcpu protocol. citeturn29view0turn29view1turn29view2turn29view3turn44search0turn44search16turn17search1

Developer ergonomics are where Plan 9 makes its strongest positive case. rcpu preserves standard descriptors 0/1/2, /dev/cons, interrupt notes, current directory, and a mounted local namespace under /mnt/term; the remote profile runs with $service=cpu; services can be posted under /srv; authenticated mounts use the same factotum/keypattern machinery used elsewhere in the system. The result is a feeling that remote execution is not a special mode but a regular extension of the same user-space model. SSH is ergonomically excellent in a different way: one ubiquitous command, one obvious trust database, one standard daemon, one rich forwarding story. But Plan 9’s ergonomics are more compositional than procedural. citeturn26view3turn27view0turn19search1turn20search5turn21view2

Practical trade-off summary

Dimension SSH Plan 9 rcpu
Performance focus Minimize RTTs inside one protocol; built-in rekeying and optional compression. citeturn31view1turn30view1turn30view3 Simpler remote-exec semantics; resilience via optional aan; richer composition often happens through additional namespace services. citeturn18search2turn27view0turn26view2
Implementation shape Large, feature-dense, protocol-centric codebase with many substantial C modules. citeturn45search5turn44search1turn44search2turn44search3 Smaller, more decomposed service graph: shell scripts plus helper commands plus namespace services. citeturn27view0turn25view0turn26view2turn19search1
Portability Excellent; protocol and tooling are ubiquitous. citeturn44search0turn44search16 Client portability via drawterm is good, ecosystem interoperability is narrow. citeturn17search1
Best ergonomic trait One standard secure-remote tool for heterogeneous systems. citeturn44search16turn33view5 Remote execution feels like namespace composition, not an overlay protocol. citeturn26view3turn26view2turn19search1

Historical perspective and what this says about Plan 9

SSH was designed to secure remote login and other network services over an insecure network. Its architecture reflects that mission exactly: build one strong transport, put client auth above it, then multiplex sessions and forwarding inside it. The protocol family is elegant by Internet-standards design criteria: explicit negotiation, clear layering, assigned message numbers, and well-defined extension points. citeturn29view0turn29view1turn29view2turn29view3

Plan 9 started from a different premise: it was a distributed computing environment from Bell Labs, and the system’s core abstractions were intended to make remote resources appear through the same file-and-namespace model as local ones. The modern 9front manuals make that lineage visible. exportfs exists to export portions of a name space from compute servers; srv/9fs/srvtls make remote filesystems appear locally; rcpu exports the local namespace into remote execution; and listen(8) says tcp17019 is the server for rcpu, which “replaces rx, import(4) and cpu(1) using TLS for encryption.” citeturn22search17turn26view2turn19search1turn26view3turn18search3

That is the deepest answer to “what makes Plan 9 great” in this comparison. SSH is a very successful secure remote-access protocol. Plan 9’s rcpu stack is not trying to out-SSH SSH on ubiquity or protocol completeness. It is trying to make remote computation a natural consequence of the operating system’s general composition model. The elegance is not that rcpu has more features than SSH. It does not. The elegance is that remote execution, remote files, service publication, authentication, and reconnection are all built by reusing the same small set of system ideas: file-oriented services, namespaces, mount/bind, 9P, and an authentication agent. That coherence is the real selling point. citeturn26view3turn26view2turn19search1turn20search5turn21view2turn18search2

If you want a single judgment sentence: SSH optimizes secure connectivity across the Internet; Plan 9 rcpu optimizes conceptual unity inside a distributed operating system. SSH is the better network standard. Plan 9’s model is the more radical and, to many systems programmers, the more beautiful operating-system idea. citeturn29view0turn29view3turn26view3turn26view2

Open questions and limitations

This report is strongest on protocol architecture, trust models, namespace integration, and documented behavior. It is weaker on two quantitative points because I stopped research at finalization time: I did not extract reliable per-file LOC for rcpu, rconnect, tlsclient.c, and tlssrv.c, and I did not collect benchmark data comparing latency, throughput, or memory usage under equivalent workloads. Those omissions matter for any claim stronger than the qualitative conclusion that SSH centralizes more protocol complexity, while Plan 9 distributes it across more reusable system mechanisms. citeturn27view0turn25view0turn45search5turn44search1turn44search2turn44search3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment