mjl > inferno > getting started

About this document

This document intends to help getting started with Inferno. The topics listed below are covered. The sections do not depend on each other, so can be read in a different order.

Keep in mind this is only an introduction and thus limited in scope. If something is not described clearly or just plain wrong (even the smallest typo), please let me know at mechiel@ueber.net.

About Inferno

Inferno is an operating system for distributed systems. It was created at Bell Labs and Vita Nuova now owns the rights to it, and maintains the code. The source code is available under a combination of liberal open source licenses. Inferno shares many ideas (and code!) with Plan 9 from Bell Labs. Some applications have been converted from Plan 9 to run on Inferno. This is why the Plan 9 and Inferno communities overlap, and why documentation for Plan 9 is often useful to Inferno users as well.

Many existing resources explain what sets Inferno apart from other operation systems. Vita Nuova's page on Inferno describes it best. (others: wikipedia, cat-v) Here is a summary:


Hosted Inferno

In this section we'll compile hosted Inferno, i.e. Inferno that runs on top of your operating system.

Fetching Inferno

Vita Nuova publishes the latest sources of Inferno in a bitbucket Mercurial repository, inferno-os. It contains all of Inferno except some of the fonts (due to license restrictions). Snapshots of the repository are available from the inferno-os google code download page, and from Vita Nuova's download page. The snapshot from the Vita Nuova page is highly recommended since it does include all the fonts. Download the "snapshot of the complete source tree [...] including the non-free fonts". This is the easiest and recommended way to install Inferno.

The snapshot contains the directory inferno-os. Once exctracted, you can fetch updates using Mercurial by running (from the inferno directory): hg pull and then hg update.

The snapshot contains emu binaries (the name of the program for hosted Inferno) for some platforms. This is especially useful on Windows machines that don't have a C compiler.

File tree

Before compiling Inferno, we'll take a quick look at the layout of the files & directories in inferno-os. This should give you a better understanding of the system and desired result of the compilation. That in turn will help solve problems you might encounter. If you are in a hurry, skip to the compiling section right away (and come back here while compiling).

Note that the inferno directory serves a dual purpose:

  1. It contains all source files and binaries. C code & binaries for building hosted Inferno as application on your OS. Limbo source code & dis files for a running Inferno.
  2. It is the root of the file system for a running Inferno instance

binaries, for hosted/native

                        Emu is the Inferno-as-application binary
                        you will be executing.  The goal of compiling
                        Inferno is to create this file.  SYSHOST is
                        e.g.: Linux, Nt, Plan 9, OpenBSD, FreeBSD
                        (or one of the other values).  OBJTYPE is the
                        architecture your SYSHOST is running under,
                        e.g. 386 for Intel 386, or arm for ARM,
                        include/* holds the SYSHOST-specific includes
                        for compiling emu.  bin/* and lib/* contain
                        compiled binaries and libraries.

c source, for hosted/native

                        These mkfiles specify the tools (compilers,
                        linkers) and flags used for compiling.
                        Each SYSHOST and OBJTYPE have their own files.
                        You don't have to do anything with these.
                        C programs used when building emu.  utils/mk/
                        is needed first, to get the compilation
                        going.  Another tool that will be build is util/iyacc/
                        ("i" in iyacc is for Inferno, to prevent
                        name clash with the yacc on your system).

                        The lib*/ directories contain C library
                        code that is used in (linked into) emu.
                        Common header files for C code.  Such as
                        the lib*/ code, and emu/* and os/* code too
                        (described below).
                        C code for emu specific to the SYSHOST.  This
                        typically contains only little C code, and a
                        tiny amount of assembler.
                        C code for emu common to all OS'es.

                        os/* contains all the C code for native
                        Inferno (running on bare hardware).  For
                        brevity, it isn't described here in detail.
                        The layout should be obvious though.

                        The C version of the limbo compiler.  The
                        Limbo version is in appl/cmd/limbo/ (described
                        below).  This is compiled first and then
                        used during compilation.

compilation configuration file

                        This configuration file is included by all
                        mkfiles used when working with Inferno.  It
                        contains variables for the SYSHOST, OBJTYPE,
                        and the full path of the inferno directory.
                        This has to be configured correctly!

limbo code

                        The limbo source for most limbo programs
                        in the system.  Some programs (such as
                        the limbo compiler in limbo/) have their own
                        subdirectory.  The resulting programs are
                        installed in /dis/ (described below).
                        Limbo source too, but used as libraries.  The
                        split between appl/cmd/ and appl/lib/ is
                        just organisatory.  There is no difference
                        in the type/style of limbo code, or compilation
                        Limbo source for the graphical (wm) programs.
                        Again, just split for cleaner directory
                        Some programs or "subsystems" have their
                        own directories.  Once more, they just contain
                        the same type of limbo code as the other

                        Contains limbo .m files, the equivalent of
                        C's .h files.  Only limbo libraries need a
                        separate module interface description.
                        Thus, there is usually a direct mapping
                        between files in module/ and files in

dis executables

                        The source compiled in the appl/* directories
                        are installed to /dis.  The shell will look
                        here when it is asked to execute a program.
                        It's just like /bin (and the various other
                        bin directories) on unix.

remainder of root file system for hosted/native

                        The default mount point for local programs,
                        like factotum on /mnt/factotum.
                        The default mount point for (remote) services.
                        A special program is often run here that
                        synthesizes directories on demand, when you
                        chdir to them.  That is why this directory
                        is empty.
                        Place holder for binding kernel devices.
                        Some kernel devices are bound by emu's
                        initialization code.  /net will hold the kernel
                        network stack, /env the environment variables,
                        and /dev most other kernel devices.
                        Database files, such as dictionaries.
                        Network configuration files.

                        Fonts, used by the graphical programs.
                        Icons, used by graphical programs.
                        Mostly log files, generated by services (such as httpd).
                        Mostly time zone files.  The file locale/timezone
                        is used by Inferno.  Copy the appropriate
                        time zone file to locale/timezone to configure
                        the time zone.


                        Documentation, original source and pdf &
                        postscript versions.  These are the same
                        as published on Vita Nuova's web site.
                        Manual pages.  They are very good, definitely
                        worth reading.

other files & directories

                        The home directory of user (usr) "inferno".
                        Some external/additional C code that might
                        be used with Inferno.  Not really interesting
                        for now.


Unless you are using pre-compiled binaries, you are now ready to compile hosted Inferno for your operating system.

  1. Edit mkconfig, set ROOT correctly (e.g. to $HOME/inferno), SYSHOST (e.g. Linux), OBJTYPE (e.g. 386 for intel machines). Don't forget to remove/comment the line containing OBJTYPE=$objtype.
  2. Prepend the full path to $SYSHOST/$OBJTYPE/bin to your $PATH, so binaries compiled during the build can be found. E.g. for Linux/386: PATH=$HOME/inferno/Linux/386/bin:$PATH
  3. Compile Inferno: ./makemk.sh && mk -s nuke mkdirs install.


To start hosted Inferno, execute emu. You'll have to provide it with the location of the root file system. Either on the command-line or through an environment variable. Additionally, the flags -c0 and -c1 are often used (to disable/enable the just-in-time compiler for dis programs). Emu also accepts arguments: a program to start instead of the default shell. Wm/wm executes the file $home/lib/wmsetup when starting. For example:

# from unix shell, start emu (Inferno)
$SYSHOST/$OBJTYPE/bin/emu -r$HOME/inferno -c0 -g1024x768

# (wait for inferno shell, with semicolon as prompt)

# from inferno shell, start the window manager

The mechanisms described above can be used to make it easier to start Inferno:

.profile of a unix shell

$EMU is used by emu to find additional command-line arguments. Add this to your .profile:

EMU="-r$HOME/inferno -c0 -g1024x768"

Create the shell script infwm (or a file name of your choice) somewhere in your PATH, with the contents below. Emu will use the EMU environment variable set in the .profile. It binds the SYSHOST directory /home on Inferno's /usr, then launches the window manager wm/wm with automatic login as the user you started emu as. This makes all user directories from the host operating system available on /usr, and your home directory on /usr/$USER. Wm/logon sets $home and a few other variables.

exec emu $* /dis/sh.dis -c "bind '#U*/home' /usr; wm/wm wm/logon -u $USER"

When wm/wm starts, it executes commands in $HOME/lib/wmsetup. Put the following lines in that file to start network services and factotum (the authentication agent):


Running programs

You are now ready to try out some programs that come with Inferno. Some will be familiar, others will require more effort to understand. The recommended way to run programs is from the window manager, wm/wm. At startup it loads a start menu from which (graphical Tk) programs can be started. Wm/sh ("Shell" in the menu) is a window in which a shell is run (a bit like an xterm but simpler), wm/man is the manual page viewer. Try editing files with acme, start the browser charon (and no, unfortunately it doesn't support css, nor many other modern browser features), and start some other programs from the start menu or shell.

If you know the standard unix tools, you'll quickly find your way. These programs work mostly the same, only flags & features are sometimes different, or absent: ar, basename, cal, cat, cd, chgrp, chmod, cmp, cp, date, dd, diff, du, echo, env, fortune, fmt, freq, grep, gzip, kill, ls, m4, man, mkdir, mv, ps, pwd, rm, sed, sleep, sort, strings, tail, tee, telnet, time, touch, tr, uniq, wc.

Before describing new commands specific to namespaces & styx server usage, we continue with a section on where to find answers to your questions and where to go for further reading.


Since Inferno rid itself of most of its (ancient) unix ancestry, many of its concepts and programs will be unfamiliar and require learning. This includes the programming language Limbo. Various sources & methods are available to help. Different people have different ways of learning, so its best to mix and match them to your liking:

Vita Nuova has a list of papers on Inferno. The papers range from introductory to very specific for one program. To get started, the papers under "Getting Started" are recommended, as is "A Descent into Limbo" (for a gentle introduction to Limbo). "The Limbo Programming Language" is good too, but it is much more detailed, and more a Limbo reference than a tutorial or introduction. If you want to do all development under Inferno (editing & compiling inside Inferno instead of on host operating system), be sure to read "Program Development under Inferno" (and probably the Plan 9 paper about acme too). Recall that all papers are on your Inferno installation too, in /doc/.
Manual pages
Inferno's manual pages contain valuable information. You'll be happily surprised if you come from a Linux system. Wm/man can be used from inside Inferno. Vita Nuova also has all manual pages online. That page also describes which topics each section covers.
Source code
Be sure to read existing limbo code, to learn common idiom. Many of the programs in appl/cmd/ on your Inferno installation are relatively short and can be understood with little effort. If you find yourself in a place without a copy of Inferno, bitbuckets web front-end to the mercurial inferno-os repository will help you out.
Mailing list
Vita Nuova maintains a public mailing list, read the instructions on how to subscribe.
At the time of writing, a few dozen people are present in channel #inferno, on irc network irc.freenode.net. If you're in for some real-time talk about Inferno, feel encouraged to join. There are channels about Plan 9 too, e.g. #plan9.
A book on Limbo is commonly available in book-selling web shops: "Inferno Programming with Limbo", by Phillip Stanley-Marbell. It is a bit dated though, no longer accurate on some Limbo constructs (it describes "old-style" exceptions, and predates polymorphism). Still serves as a nice introduction, with plenty of examples. The author has a low-resolution pre-release version online, at the books homepage. Reports of people finding cheap copies of the book on various sites (not amazon) have been heard.
Plan 9 resources
As mentioned, Inferno has concepts, programs and actual code in common with Plan 9. Therefor many Plan 9 resources apply in various degress to Inferno as well. For example the papers on acme, the plumber and factotum.

Namespaces & styx servers

Since namespaces & styx file servers are both new and so powerful, they deserve to be explained in their own section. First namespaces and the idea of styx servers are described, then examples of styx servers are given.

This next section introduces the concepts of namespaces. In case you already know about namespaces (perhaps you're a Plan 9 user), you can safely skip to some examples of Inferno styx servers.


A namespace is a collection of mount points and binds, much like mount points on unix systems. In Inferno, you mount a server speaking the styx protocol over a file descriptor (such as a network connection, or a pipe to a program). Thus, a mount introduces a new file tree in the namespace. A bind on the other hand just makes a part of the namespace appear in another place in the namespace, as an alias.

The namespace actually consists of two parts: regular paths starting with a /, and a "special" set of paths that start with an "#" followed by a single character. These special paths are kernel devices, file trees served by the kernel, often giving access to hardware such as a disk, or to kernel data structures such as processes. Only the regular paths of the namespace can be changed by binds and mounts. This is done by the commands bind, mount & unmount (which use system calls of the same name). Ns prints the current namespace. When executed from the shell, it's the shells namespace that ns prints (ns prints its own namespace, which it inherited from the shell). To illustrate, this is the namespace from a freshly started emu, without window manager:

; ns
bind / /
bind -ac '#U' /
bind /dev /dev
bind -b '#^' /dev
bind /chan /chan
bind -b '#^' /chan
bind -b '#m' /dev
bind -b '#c' /dev
bind '#p' /prog
bind '#d' /fd
bind /net /net
bind -a '#I' /net
bind -a /dev /dev
bind -a /net /net
bind /net.alt /net.alt
bind -a /net.alt /net.alt
bind -c '#e' /env
cd /

As you can see most lines bind a kernel device on a location in the regular file system. Such as #U (the contents of the Inferno root directory) on /, and #m (the mouse) on /dev, and #I (the network stack) on /net. Options -a and -b make the contents of the first path appear after or before the original contents of the second path (the second path will then contain the union of the first path and itself). If option -c is given, file creation at the target path will be allowed. The namespace shown above was set up by the Inferno initialization code, as a way to bootstrap the system and give a reasonable default namespace (e.g. the network stack isn't strictly necessary for a freshly started Inferno, but it is convenient to have in place).

Mount introduces an external file tree into the namespace, just as it does on unix systems. The file tree is accessed by talking the styx protocol over a file descriptor. The kernel takes care of the styx-part by translating open/read/write/stat/etc system calls into styx messages (the messages are described in section 5 of the manual pages), and the response messages into return values. The mount system call expects a file descriptor to talk styx over. The mount program has convenient syntax for mounting three types of styx servers:

Note that mount performs Inferno authentication when mounting. Plan 9 has a different type of authentication, that occurs at a different point in connection set up. While you are trying out the commands, you can turn off authentication with option -A, this will make it easier to get started. Do remember to turn on authentication for your services later!

Styx servers

Finally we can test a few styx servers. Beside the obvious on-disk storage of "physical" files and directories (see kfs), many other resources can be represented as file servers. The examples below should give an idea of what you can do with styx servers.


An existing namespace can be exported with styxlisten. Use it as in the following example:

styxlisten 'tcp!*!styx' export /

This will listen for tcp connections on the port "styx" (which is defined in /lib/ndb/common to be 6666). All connections will be served by a single invocation of export / (styxlisten expects the program to serve the styx protocol on its file descriptor 0). The program "export" simply exports the namespace starting at the parameter it is passed, the root of the file system in this case. These simple programs (they are just over 300 lines combined!) give a lot of power. Just as with mount, option -A disables authentication on the connection.


Mntgen is a simple program that makes (fakes actually) a directory when you cd to it. This is used on /n, where all kinds of network services are mounted. Since the services and network machines are numerous, it is impractical to create directories for all those services. With mntgen, you can have an empty /n and still mount services on any subdirectory. The following examples should illustrate the use of mntgen. First, a reconnaissance of /n without mntgen:

% cd /n
% ls -l

% ls -ld test
ls: stat test: 'test' file does not exist

% cd test
cd: test: 'test' file does not exist

% bind '#m' /n/mouse
bind: /n/mouse: '/n/mouse' does not exist

As you see, /n is empty. Now the same, but with the mntgen:

% mount {mntgen} /n

% cd /n/

% ls -ld test
d-r-xr-xr-x M 2 me me 0 Jan 01  1970 test

% ls -ld another
d-r-xr-xr-x M 2 me me 0 Jan 01  1970 another

% cd more
% pwd

% bind '#m' /n/mouse
% ls -l /n/mouse
---w--w--w- m 0 mjl mjl 0 Apr 15 20:37 /n/mouse/cursor
--rw-rw-rw- m 0 mjl mjl 0 Apr 15 20:37 /n/mouse/pointer


Ftpfs is an ftp client. It simply presents the files of an ftp server as a file system. The example should be self-explanatory:

% ftpfs -a none@example.org -m /n/ftp ftp.openbsd.org
220 openbsd.srv.ualberta.ca FTP server ready.
331 Guest login ok, send your email address as password.
% cd /n/ftp
% cd pub/OpenBSD
% ls -l
d-rwxr-xr-x M 6 0     0       1024 Sep 28  2008 4.2
d-rwxr-xr-x M 6 0     0       1024 Sep 28  2008 4.3
d-rwxr-xr-x M 6 0     0       1024 Sep 04  2008 4.4
d-rwx------ M 6 0     0        512 Mar 09 17:04 4.5
d-rwxr-xr-x M 6 318   0        512 Feb 19 23:24 OpenBGPD
d-rwxr-xr-x M 6 318   0        512 Jan 16 06:05 OpenNTPD
d-rwxrwxr-x M 6 0     122     4608 Feb 22 17:34 OpenSSH
--rw-r--r-- M 6 0     121      238 Feb 07  1997 README
d-rwxr-xr-x M 6 20001 20001 454144 Apr 17 01:03 distfiles
d-rwxr-xr-x M 6 308   0       2048 Jan 12 04:20 doc
--rw-r--r-- M 6 0     0       8741 Sep 03  2008 ftplist
d-rwxr-xr-x M 6 0     100     1024 Apr 07 03:59 patches
d-rwxr-xr-x M 6 0     122      512 Aug 31  2008 snapshots
d-rwxr-xr-x M 6 0     100     1024 Apr 07 21:07 songs
d-rwxr-xr-x M 6 0     121      512 Jun 28  2008 tmp
d-rwxr-xr-x M 6 0     100      512 Jan 07  2005 tools


Tarfs mounts a tar file (read-only). Tar is a simple file format, but a traditional "file system" with on-disk storage is really just the same type of program, just with a different file format (plus they usually implement writes).

% tarfs gps.tar /n/tar
% cd /n/tar
% ls -l
d-rwxrwxr-x M 16 0 0 0 Jul 07  2006 gps
% cd gps
% ls -l
--rw-rw-r-- M 16 0 0   287 Mar 04  2006 dat.h
--rw-rw-r-- M 16 0 0  4501 Mar 04  2006 gpsevermore.c
--rw-rw-r-- M 16 0 0 18924 Apr 09  2006 gpsfs.c
--rw-rw-r-- M 16 0 0   219 Mar 04  2006 mkfile
--rw-rw-r-- M 16 0 0  2795 Mar 04  2006 util.c
% wc gpsfs.c
   1010    2713   18920 gpsfs.c
% cd
% unmount /n/tar


Memfs is a file system that stores files in memory. Ideal for mounting on /tmp:

% ls -l /tmp/test
ls: stat /tmp/test: '/tmp/test' does not exist

% mount -bc {memfs -s} /tmp

% echo test >/tmp/test
% ls -l /tmp/test
--rw-r--r-- M 20 mjl memfs 5 Apr 17 22:43 /tmp/test

% unmount /tmp
% ls -l /tmp/test
ls: stat /tmp/test: '/tmp/test' does not exist

/net, or #I

The network stack is provided by #I. It has a directory per protocol, e.g. tcp. Since #I is mounted on /net by default, the tcp stack is available on /net/tcp. Library functions that deal with network connections (such as dial and announce, for connecting and serving) are implemented with normal open/read/write calls on files in /net/tcp. To show the ease with which /net/tcp can be used, consider the following shell script that performs a HTTP request:

<>[3]/net/tcp/clone {
        dir=/net/tcp/^`{cat <[0=3]}
        echo connect!80 >$dir/ctl &&
                echo 'GET /search?q=inferno-os&btnI=I''m+Feeling+Lucky HTTP/1.1' &&
                echo 'connection: close' &&
                echo 'host: www.google.com' &&
                echo ''
        cat $dir/data

And the output:

HTTP/1.1 302 Found
Location: http://www.vitanuova.com/inferno/
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Date: Sat, 18 Apr 2009 19:57:52 GMT
Server: gws
Content-Length: 230
Connection: close

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.vitanuova.com/inferno/">here</A>.

That's the power of a well-designed abstraction, and powerful tools like the Inferno shell. In a similar fashion, dns requests can be performed using /net/dns, and more general network address translation can be done using /net/cs.

Hello world

To give you a taste of Limbo programming, here is an implementation of "hello world", and instructions on how to compile and run it.


# this file implements the module interface "Helloworld"
implement Helloworld;

# include the files sys.m & draw.m (from /module)
include "sys.m";
include "draw.m";

# "Helloworld" is a module, it has the function ("fn") "init" with two parameters.
# both parameters are unnamed ("nil") because they are not used.
Helloworld: module {
	init:	fn(nil: ref Draw->Context, nil: list of string);

# implementation of the function "init", as described in the
# module interface "Helloworld" above.  "init" in limbo is like "main" in C.
init(nil: ref Draw->Context, nil: list of string)
	# declare "sys" by assigning it the result of loading the Sys module.
	sys := load Sys Sys->PATH;

	# call function "print" from the loaded module "sys"
	sys->print("hello world!\n");

compile & run

; limbo -gw helloworld.b
; ./helloworld
hello world!

Quick links

man pages index section 2 index
hg commits browse source issues
misc inferno programmers notebook
papers limbo programming language all papers


A list of updates to this document:

Fixed broken links.
Small improvements, remove broken links.
Small improvements, mention lib/emptydirs, fix links to mercurial files in inferno-os repository.
Updated section on fetching Inferno. Subversion has been replaced by Mercurial.

This document is in the public domain.