Plan 9: Everything Is a File (And We Weren't Ready)


Ruby says: Everything is an object.

42.times { puts "Everything is an object" }
"string".class  # => String
nil.class       # => NilClass

This was revolutionary for programming languages. Objects everywhere. Consistency.

Thirty years earlier, Bell Labs said: Everything is a file.

And they meant it literally.

Plan 9 from Bell Labs:

Plan 9 was designed as Unix’s successor by the same people who created Unix: Ken Thompson, Dennis Ritchie, Rob Pike, and others at Bell Labs.

Unix had the philosophy “everything is a file” — but Unix lied. Processes were not files. Network connections were not files. Graphics were not files. The window system was not files.

Plan 9 did not lie.

Everything. Is. A. File.

ResourceUnixPlan 9
Disk filesFilesFiles
Processes/proc (limited)/proc (complete)
NetworkSockets (different API)/net (files)
GraphicsX11 (separate protocol)/dev/draw (files)
Window systemX server (client-server)/mnt/wsys (files)
Environment variablesSpecial handling/env (files)
Other machinesNFS/SSH (special)/mnt/remote (files)

In Plan 9, to make a network connection:

# Open a TCP connection to google.com:80
echo "connect google.com!80" > /net/tcp/clone
# Read and write to the connection via file descriptors

No socket(). No connect(). No special API. Just files.

9P: The Protocol:

Plan 9’s secret weapon is 9P — a protocol for serving file trees over networks.

Every resource is a file. Files can be served over the network. Therefore, every resource can be accessed from any machine.

Machine A: Has a webcam at /dev/camera
Machine B: Wants to use the webcam

# On Machine B:
import A /dev/camera /mnt/camera
cat /mnt/camera > photo.jpg

The camera on Machine A appears as a file on Machine B. No drivers needed on B. No special protocol. Just 9P serving a file tree.

This is not NFS. This is not CIFS. This is not FUSE. This is resources as files, served transparently.

Namespaces:

In Plan 9, every process has its own namespace — its own view of the filesystem:

# Bind a new directory over an existing one
bind /mnt/alternative/bin /bin

# Now this process sees different /bin
# Other processes unchanged

No containers. No Docker. No namespaces bolted on later. Process-private filesystem views from the beginning.

# Union mounts (before Linux had them)
bind -a /mnt/extra/lib /lib
# /lib now shows files from both directories

Linux added namespaces in 2002 (inspired by Plan 9). Docker uses them. Kubernetes uses them.

Plan 9 had them in 1992.

The /proc Filesystem:

Unix’s /proc is limited — some process info, exposed inconsistently.

Plan 9’s /proc exposes everything:

/proc/123/
├── ctl      # Write commands: "stop", "start", "kill"
├── status   # Process status
├── mem      # Process memory (readable/writable)
├── text     # Executable
├── fd/      # Open file descriptors as files
├── ns       # Process namespace
└── notepg   # Note group (signals)

To debug a process:

echo "stop" > /proc/123/ctl      # Stop the process
cat /proc/123/mem > dump.bin     # Dump memory
echo "start" > /proc/123/ctl     # Resume

No ptrace. No special syscalls. Just files.

Distributed Computing:

Plan 9 was designed for distributed systems from day one:

# Import file tree from remote machine
import graphicsserver /dev/draw /dev/draw

# Now local graphics go to remote display
# No X11 forwarding. Just files.
# Export your namespace to another machine
exportfs -r /home/user &

# Other machines can import your files
# Authentication via factotum

A cluster of Plan 9 machines behaves as one. Resources flow between machines via 9P. The network is transparent.

Factotum: Authentication Agent:

Factotum holds your authentication credentials:

/mnt/factotum/
├── ctl       # Control
├── proto     # Supported protocols
├── needkey   # Requests for keys
└── rpc       # Authentication RPC

Programs do not handle authentication. Programs ask factotum. Factotum proves identity. Credentials never exist in application memory.

SSH agent does this for SSH keys. Factotum does this for everything: SSH, passwords, certificates, any authentication protocol.

One agent. All credentials. Applications cannot leak what they do not have.

Rio: The Window System:

Rio is Plan 9’s window system. Each window is a file tree:

/mnt/wsys/
├── cons      # Console I/O
├── mouse     # Mouse events
├── draw      # Graphics
├── kbd       # Keyboard
└── window/   # Sub-windows

To take a screenshot:

cat /dev/draw > screenshot.img

To read mouse input:

cat /dev/mouse

X11 has a complex protocol with authentication, extensions, and libraries. Rio has files.

Why Plan 9 Failed:

Plan 9 was too advanced. Plan 9 was too different. Plan 9 was not Unix-compatible.

Timeline:
1992: Plan 9 released
1991: Linux released (Unix compatible)
1992: 386BSD released (Unix compatible)

The world had Unix programs. Unix programs expected Unix APIs. Plan 9 had better APIs, but different APIs.

Linux offered: “Run your existing programs.” Plan 9 offered: “Rewrite everything better.”

The world chose convenience over correctness.

UTF-8: Plan 9’s Gift to All Computing

Unicode existed — a universal character set assigning numbers to every character. But how do you encode those numbers as bytes?

Early encodings like UCS-2 and UTF-16 broke ASCII compatibility. Existing tools would choke. Existing files would need conversion. The Unix world would fracture.

In September 1992, IBM came to Bell Labs proposing an encoding for Plan 9. Thompson and Pike thought it was terrible. They went to dinner at a New Jersey diner, designed a better encoding on a placemat, and Thompson implemented it overnight.

They needed an encoding for Plan 9 that could:

  • Represent all Unicode characters
  • Remain compatible with ASCII
  • Not break C string handling (no null bytes mid-string)
  • Work with existing Unix tools

They designed UTF-8 on a placemat in a New Jersey diner. Thompson implemented it that night.

UTF-8 encoding:
0xxxxxxx                            → ASCII (0-127)
110xxxxx 10xxxxxx                   → 2 bytes (128-2047)
1110xxxx 10xxxxxx 10xxxxxx          → 3 bytes (2048-65535)
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx → 4 bytes (65536+)

The genius: ASCII is valid UTF-8. Every ASCII file ever written is automatically a valid UTF-8 file. No conversion needed. No tools broken.

UTF-8 did not replace ASCII. UTF-8 absorbed ASCII. A retcon so elegant that the world adopted it without realizing.

UTF-8 is now:

  • The dominant encoding on the web (98%+ of websites)
  • The default encoding in Linux, macOS, and modern Windows
  • Required by JSON specification
  • The encoding you’re reading this in

Plan 9 was the first operating system fully designed around UTF-8. File names, text processing, the shell — everything assumed UTF-8 from day one.

The world took 20 years to catch up. Now UTF-8 is everywhere, and few remember it came from Plan 9.

Thompson and Pike gave the world a universal text encoding. They did it over dinner. They implemented it overnight.

What Linux Took:

Linux eventually adopted Plan 9 ideas:

  • /proc filesystem — from Plan 9
  • Namespaces — from Plan 9
  • 9P protocol — QEMU uses 9P for host-guest file sharing
  • FUSE — user-space filesystems, inspired by Plan 9
  • Containers — namespace isolation, Plan 9 had it first

Linux is slowly becoming Plan 9, one feature at a time, badly reimplemented.

Plan 9 Today:

Plan 9 is not dead:

ProjectStatus
9frontActive development, Plan 9 fork
Harvey OSPlan 9 kernel with Linux compatibility
InfernoPlan 9 successor, portable
plan9portPlan 9 tools ported to Unix

9front continues development. The community is small but committed. The ideas remain valid.

The Ruby Connection:

Matz said Ruby was influenced by Smalltalk’s “everything is an object” consistency.

Pike and Thompson said Plan 9 would have “everything is a file” consistency.

Both sought the same goal: uniform interfaces reducing complexity.

In Ruby, you do not ask “is this an object?” — everything is. In Plan 9, you do not ask “is this a file?” — everything is.

Consistency enables composition. Composition enables power.

Ruby won in its domain. Plan 9 lost in its domain. But Plan 9’s ideas won eventually — they just wear Linux costumes now.

The Lesson:

Bell Labs built the true successor to Unix. Clean design. Consistent interfaces. Everything is a file — truly.

The world was not ready. The world wanted compatibility. The world chose Linux.

Now Linux slowly absorbs Plan 9’s innovations: namespaces, /proc, 9P, FUSE. Docker exists because namespaces exist. Namespaces exist because Plan 9 existed.

Plan 9 was right. Plan 9 was early. Being right and early is indistinguishable from being wrong.

But the ideas survive. The code is open. 9front continues.

Ruby: Everything is an object. Plan 9: Everything is a file.

Both were right. Only one was ready for the world.

Perhaps one day, we will deserve Plan 9.

— Kim Jong Rails, Supreme Leader of the Republic of Derails