← Back to writing

TerranoxOS — Everything I Learned, Applied

91 syscalls, capability tokens, an ELF loader, and the lessons from six previous kernels. Also: what it's like building an OS kernel with Claude as a pair programmer.

TerranoxOS is the kernel that knows what it’s doing. Not because I’m a better programmer than when I wrote KairosOS — though I am — but because every decision in TerranoxOS was informed by a specific failure in a previous kernel.

It’s also the first kernel I’ve built with AI assistance. Claude handles the boilerplate — syscall table scaffolding, header file generation, documentation — while I make the architecture decisions. The division of labor is surprisingly clean in kernel development, where the hard parts are design decisions (what should this syscall do?) not implementation details (how do you declare a function pointer array in C?).

The 91-Syscall ABI

KairosOS had maybe 5 syscalls, added ad-hoc as I needed them. TerranoxOS has 91, designed as a coherent ABI before implementation started.

The syscalls are organized by category:

RangeCategoryExamples
0-9Processfork, exec, exit, wait, getpid
10-19Memorymmap, munmap, brk, mprotect
20-29File I/Oopen, close, read, write, seek, stat
30-39Directoryopendir, readdir, mkdir, rmdir, chdir
40-49Networksocket, bind, listen, accept, connect
50-59IPCpipe, shmget, semget, msgget
60-69Signalkill, sigaction, sigprocmask
70-79Timegettimeofday, nanosleep, clock_gettime
80-89Systemmount, umount, reboot, sysinfo
90Capabilitycap_grant, cap_revoke, cap_check

Why 91? Because I listed every operation a libc needs to wrap for a POSIX-like environment, organized them into categories, and numbered them. The range-based numbering isn’t required by the CPU — syscall dispatch is just an array index — but it makes the ABI readable. You can look at a syscall number and know its category without checking a table.

Designing the ABI before implementing it was the lesson from KairosOS. When syscalls are added ad-hoc, the numbering is arbitrary, the argument conventions vary, and the error handling is inconsistent. When the ABI is designed up front, the implementation follows the specification — and a specification is something you can review, discuss, and get right before writing code.

Capability Tokens

The most architecturally distinctive feature. Instead of UNIX-style permission bits (owner/group/other, rwx), TerranoxOS uses 128-bit capability tokens for permission management.

A capability token is an unforgeable reference to a resource with associated permissions. To read a file, you don’t check “does this user have read permission on this file.” You check “does this process hold a capability token that grants read access to this file.”

typedef struct {
    uint64_t resource_id;   // what resource this grants access to
    uint64_t permissions;   // bitmask of allowed operations
    uint64_t issuer;        // who created this token
    uint64_t expiry;        // when it expires (0 = never)
} capability_t;

The advantage: capabilities are transferable, revocable, and fine-grained. A process can grant a subset of its own capabilities to a child process. Revocation is a tree operation — revoking a capability revokes all capabilities derived from it.

The idea came from the D exokernel experiment. Exokernels expose hardware resources directly, and capabilities are the natural access control mechanism. TerranoxOS isn’t an exokernel — it has a traditional syscall interface — but the capability system gives processes fine-grained control over what their children can do.

The ELF Loader

The assembly experiments started with writing ELF headers by hand. TerranoxOS closes the loop by parsing them in the kernel.

When exec is called, the kernel:

  1. Reads the ELF header from the file
  2. Validates the magic bytes (\x7fELF), architecture (x86_64), and type (executable)
  3. Iterates through the program header table
  4. For each PT_LOAD segment, allocates pages and copies the segment data
  5. Sets up the user stack with argc, argv, and envp
  6. Transfers control to the ELF entry point

This is what the Linux kernel does when you run ./program. I understood it abstractly after writing ELF headers by hand in the assembly era. I understood it concretely after implementing the parser in TerranoxOS.

What Carried Forward from Each Kernel

Previous KernelLesson Applied
Assembly experimentsELF loader — I know the format because I wrote it by hand
KairosOSDesign the syscall ABI before implementing it
KairosOS v2Build system as first-class infrastructure
VoletaNaked toolchain, sysroot structure, libc as independent component
D exokernelCapability-based access control
IsmenaOSDon’t depend on external language runtimes
AstraeaOSUnderstand every byte of the boot sequence

TerranoxOS isn’t better because I’m smarter. It’s better because it benefits from 6 previous attempts, each of which failed in a specific, instructive way. The architecture is the distillation of those failures.

Building with Claude

The AI pair-programming angle is worth discussing honestly. Claude is useful for kernel development in specific ways:

Good: Syscall table scaffolding, header file boilerplate, documentation strings, test case generation, explaining obscure CPU manual sections, generating linker script templates.

Limited: Architecture decisions (what should the capability revocation model look like?), debugging kernel panics from register dumps, understanding hardware-specific behavior (why does this PCI device not respond to BAR configuration?).

The split is roughly: Claude handles what (the syntax, the boilerplate, the rote implementation) while I handle why (the design, the tradeoffs, the decisions that make TerranoxOS different from “yet another hobby OS”). This division works well because kernel development has a high ratio of design-to-code — the hard parts are deciding what to build, not typing the implementation.

What’s Next

TerranoxOS is an active project. The 91-syscall ABI is specified; maybe 30 syscalls are implemented. The capability system works for basic access control; the revocation tree is incomplete. The ELF loader handles static binaries; dynamic linking is future work.

It will probably never run a desktop environment. But it will have a complete syscall ABI, a working capability system, and a well-tested ELF loader — the three things I’ve been building toward across six kernels and five years.


This is Part 7 of 8 in the OS Kernel Museum series.