Red Echo

July 9, 2015

I’ve had a concept for an operating system bouncing around my head for a decade and a half or so. With the exception of a general affinity for exokernels, the structure I’m thinking about now bears no resemblance to anything I considered back in the ’90s, but on the basis of arbitrary convenience I’m going to say that this Ship of Theseus is, in fact, still the same boat. The current incarnation lives in a series of C header files named “trindle.h”, “trindle2.h”, “trindle3.h”, und so weiter, documenting the kernel API, which is the only part that actually exists.

I have at various times written all of the individual components necessary for an operating system, though if one were to imagine them all glommed together it would be form one unholy mongrel with no particular reason to exist. The Trindle concept is rather an attempt to answer the same sorts of questions I was exploring with the Radian language. Observing that all of the interesting problems in computing currently have to do with asynchrony and distributed processing, immutability has become a prominent and valuable tool: but “immutability” is really just a way of describing the way objects look when your tools require you to be explicit about the whens and hows of the changes you are making to observable state.

Trindle is therefore not a Unix: it is a single-user, single-address-space, capability-based, filesystem-driven architecture which may well end up offering a POSIX API but only as a secondary concern should it happen to be practical. It does, however, retain all the familiar notions of independent processes, protected memory, virtual memory, and the stdin/stdout/argv/envp conglomeration necessary for operating C programs.

The capability system works by attaching a list of inodes to each process. A process may read from those objects and no others; it doesn’t matter what sort of path-mangling shenanigans you get up to or what other subprocesses you launch, there is simply no way for a process to refer to any file not granted by its upstream launching process.

To be more precise, permission to open a read stream from an inode is a capability attached to some other stream. A stream is an interface to one end of a pipe connecting two processes; the upstream process can send data through the pipe, and can also attach permission to access some object it knows about.

A process may generate a new pipe either by forking or by loading an executable image. This pipe is itself a new file, which can be opened and read, or can be sent down an output stream so that some downstream process can read from it.

The only difference between an executable and a file is that the executable does not yet have an input stream, while the file does. To be solved: memoization and lazy computation of file contents.

Since processes cannot alter existing files, merely read from them, how do you actually get any work done? I’m imagining that the shell would be a process which reads from various processes representing user-interface devices and then pipes the filesystem root through various programs as the user requests. Changes would be made by generating a new directory tree as appropriate.

But that seems like a lot of copying and replacement. I think this system needs some sort of “log” object, preferably one which can merge writes from multiple inputs. A directory could thus be represented by a series of mutations, so that inserting a new file or deleting an old one just involves appending a log entry recording the fact, with periodic writes of a new summary of the current state.

The equivalent of a user’s home directory would then be something like an activity log, recording the various files the user has created, with an index of their names. The user can pass these files through various programs in order to generate new files, which can either live along side the originals under new names, or which can replace the originals by redefining their names.

Since the only way to gain access to a file is to be given it by the upstream process, the user is therefore in complete control over which programs get to see which files. If you don’t want a program to have access to your contact list, you simply don’t give it a pointer to your contact list, and that’s that – there is no mechanism by which it can name that file, ask for access to it, or raise its privilege level in order to read it. Nor can any program alter your files for you; programs merely generate files, and it is up to you, through the shell, to put the results where you want them.

I’m not sure whether I will ever actually build this thing, but it’s been an interesting concept to chew on while riding the bus or laying in bed unable to sleep.