AetherOS and AquOS
Two operating systems I wrote from scratch in C and assembly, run in QEMU: first a windowed desktop, then a Unix-style shell with real processes.

I had already built VOS, which stands for "virtual OS" and was not really an operating system at all: it was an assistant made of several AI helpers working together. It left me with a nagging thought. It would be properly cool to build a real one, completely from scratch with nothing underneath it, and to find out how far you can get starting from the most basic languages. So I tried, twice.
An operating system is the program that sits underneath all the others. It decides whose turn it is on the processor, hands out memory, and deals with the screen and the keyboard so that every other program doesn't have to. I wrote two small ones over a few days in early April 2026. Both have only ever run inside virtual machines. QEMU, the one I used, is a program that pretends to be a computer, so I never put either of them on real hardware and nothing touches my actual machine.
The first, AetherOS, is the half of an operating system you can see. It boots to a desktop with a starfield background, a mouse pointer, a taskbar and four small windows: system info, a file browser, a text editor and a terminal. Type ls in the terminal and it lists the files of a little made-up filesystem that lives in memory.
Four windows seem to run at once, but the processor only does one thing at a time. The kernel, the core of the operating system, gives each task a turn of 20 milliseconds and then swaps to the next, fast enough that you never notice. Think of a kitchen with one stove and a manager who keeps saying "your turn" to the cooks. The catch is that these four apps are not separate programs. They are built into the kernel itself, so a bug in one of them can take the whole thing down.
The second one, AquOS, drops the windows and goes after the other half: running real programs. It has a text-only command prompt called tide (the shell), and 17 small tools beside it, such as one that counts words, one that searches text and one that lists running programs. They can be chained, so echo foo | wc, which counts the words in foo, hands the output of one to the next, like passing a note between two people. It also has one idea of its own, called Ripple: one program says something, and every program waiting to hear it wakes up.
AetherOS boots, draws its four windows, follows the mouse and takes typing. Apart from the blinking text cursor, and the mouse pointer now and then, the screen holds still. AquOS boots, formats a fresh disk in memory, installs its 18 programs and shows a prompt under a banner that says "C O V E, where the water is still".
One bug took a while. When the shell starts a program it first copies itself, and at first the copy shared its memory bookkeeping with the original, so changing one changed the other. I worked around it, and I would expect more bugs of that kind. Some limits are plain: nothing is saved between boots, the kernel trusts programs more than it should, and I have only checked that the shell starts, not what it does with commands.
What's next. AquOS has no windows and AetherOS cannot run separate programs, and I have not put the two together. That is the obvious gap. Both are prototypes, and so far they have only ever lived in a virtual machine.
Why I built it, and what it is
I had built VOS before this. Despite the name it was never an operating system, just a "virtual OS" in the sense of an assistant made of cooperating AI agents (it has its own page). I kept wanting to build a completely from-scratch one instead, and to see how far I could take it starting from C and assembly. That is where these two come from.
They are two separate attempts at an x86-64 kernel, in C and NASM assembly, written between 1 and 4 April 2026. Both have only ever run in virtual machines (QEMU), never on real hardware. AetherOS goes after the visible half of an operating system, a windowed desktop. AquOS drops the GUI and goes after the Unix half: user-mode processes, fork and exec, pipes, a shell. AquOS is a fresh source tree, not a continuation of the AetherOS code.
How AetherOS works
It boots from a GRUB ISO (Multiboot2), switches to 64-bit mode and identity-maps the first 4 GB with 2 MB pages, which is what makes the framebuffer at 0xfd000000 reachable. The kernel then brings up an interrupt table, a bitmap page allocator, four-level paging, a heap, PS/2 keyboard and mouse, PCI enumeration and a 100 Hz timer. About 4,900 lines of C and 420 of assembly, headers not counted.
What does identity-mapped mean?
Paging lets the CPU translate the addresses a program uses into real memory addresses. Identity-mapped means the translation changes nothing: address X maps to physical address X. It is the simplest setup, and it lets the kernel touch any physical address, the video memory included, directly. The price is that nothing separates one piece of code from another.
Multitasking is a round-robin scheduler. On every second timer tick (20 ms) it saves the interrupt frame of the running task, picks the next ready one and overwrites the frame, so the return from the interrupt lands in the other task. The four apps are such tasks: functions linked into the kernel, running in ring 0, not separate programs. Each one owns a window with a pixel buffer and a draw callback. The window manager itself runs in the kernel's main loop, which is the scheduler's idle task, and that is where the draw callbacks get called.
How can overwriting a frame switch tasks?
When the timer interrupt fires, the CPU and the entry code push the running task's registers onto its stack, and the handler normally pops them again on the way out. If the scheduler copies another task's saved registers over that frame first, the same pop restores the other task's registers and its instruction pointer, and execution simply continues over there.
What are ring 0 and ring 3?
x86 processors have privilege levels. Ring 0 may do anything: touch any memory, run privileged instructions, talk to hardware. Ring 3 is restricted, and a program there has to ask the kernel for anything sensitive through a system call. AetherOS apps run in ring 0, so there is no wall between them and the kernel. AquOS programs run in ring 3.
The window manager draws a pre-rendered starfield, composites windows in z-order into a full-screen back buffer, draws the taskbar, copies the result to video memory and draws the mouse cursor on top. The four apps are system info, file browser, text editor and a terminal. The terminal is a built-in command interpreter (ls, cd, cat, echo, mkdir, rm, pwd, clear, help), not a shell that launches programs. The filesystem, AetherFS, lives in memory: 128 inodes, 32 entries per directory.
How AquOS works
AquOS (its boot banner says Cove) boots the same way, then does something different with its files. The Makefile compiles every userspace program to an ELF, embeds them in the kernel with incbin, and at each boot the kernel formats a blank 8 MB RAM disk (AquaFS, 4 KB blocks, 512 inodes) and writes the programs into /bin. The first kernel thread then execs /bin/tide.
User programs run in ring 3 and enter the kernel through SYSCALL and SYSRET. Fork copies the program's mapped pages (from the load address up to 16 MB past it, or to the heap end if that is higher) and the used part of the stack, from the current stack pointer up, into a new address space; there is no copy-on-write. Exec loads the ELF64 segments into a fresh address space. The scheduler has three priority queues and a 10-tick slice, 100 ms at 100 Hz. Pipes are 4096-byte kernel ring buffers (4095 usable) that block the reader when empty and the writer when full.
What is copy-on-write?
Fork has to give the child a copy of the parent's memory. The simple way, which AquOS uses, is to copy every page right away. Copy-on-write shares the pages instead, marked read-only, and makes a real copy of a page only when one side tries to write to it. It is much cheaper, particularly when the child calls exec straight after, as a shell's children do.
The shell, tide, has code for pipes, redirects, background jobs, variables, if/for/while, functions, history and tab completion, but only its boot has been checked. A small libc supports it, and 17 programs sit beside it: cat, grep, wc, ps, top, a line editor and others. AquOS also has one feature of its own, Ripple events: emit name data wakes every process blocked in listen name. About 7,500 lines of C and 900 of assembly, headers not counted. Output is a text console drawn on the framebuffer.
The best way to see how the pieces fit is a pipeline. Step through echo foo | wc below, or click a box in the diagram to jump to the first step that uses it. Both stages are forked children, even builtins.
What it did when I ran it
| Check | Result |
|---|---|
| Boot steps printing OK in the serial log | 12 |
| Windows created | 4 |
| PCI devices found | 6 |
| Ten captures about 1 s apart: neighbouring pairs differing by 128 px | 7 of 9 |
| Neighbouring pairs differing by 186 px | 2 of 9 |
| Change between the first screenshot and the one after clicking the Terminal taskbar button and typing ls (includes the focus change, not only the ls text) | 25,757 px |
The 128-pixel change is an 8 by 16 box where the terminal cursor sits. The two 186-pixel cases are that same box plus 58 pixels at the mouse pointer, which is missing from one capture, so the pointer does blink out briefly. Apart from the cursor and that pointer, the windows look steady rather than flickering. I only compared pixels, so a flicker that falls between two captures would not show. Here are the ten captures, cropped to those two spots.


Here are the lines AquOS prints while booting: a freshly formatted filesystem, the 18 programs written to /bin, and the shell prompt. Selected lines:
[PMM] Total: 12543 MB, Free: 246 MB [AquaFS] No filesystem found - formatting [AquaFS] Formatted: 2039 data blocks, 512 inodes [INIT] wrote /bin/tide (34432 bytes) [INIT] wrote /bin/cat (21112 bytes) [INIT] Filesystem populated [AquOS] init process created [AquOS] Starting scheduler
After that, the shell draws its banner, a ring of tildes around C O V E and the line "where the water is still", then a prompt. I wrote scripts that type commands such as echo foo | wc into QEMU, and a scripting test inside the image, but I have only seen the shell get as far as its prompt. Both kernels have only ever run in QEMU, never on real hardware.
What didn't work
One bug I hit was address spaces. A new space starts as a copy of the boot page table, so a child shared page-table pages with its parent and mapping into one changed the other. The workarounds are in the code: the ELF loader copies argv into kernel memory before it maps anything, and fork gives the child its own tables for the user ranges. I would expect more bugs of this kind.
The memory line in the log is misleading. "Total: 12543 MB" comes from adding up every entry in the boot memory map, reserved ranges included, so it is not the RAM size.
There is no isolation to speak of. The kernel trusts user pointers directly, because everything is identity-mapped, and the AetherOS apps run in ring 0, so a bug in one can take the kernel down. The AquOS disk is a RAM disk reformatted on every boot, so nothing persists.
Ripple events have no queue. If nothing is blocked in listen when an event is emitted, the event is dropped.
My pipe test types echo foo | wc -c, but this wc does not parse flags. Given any argument it treats every one as a file name, so wc -c looks for a file called -c and prints "not found" without reading the pipe. Plain wc reads standard input.
The AetherOS test script clicks where I assumed the System Info window was (bottom right). It is actually at 100,100 under the other windows, so the third capture is pixel-identical to the second, and that window never gets exercised.
The names are inconsistent: the shell's source is still ripple.c but installs as tide, and the kernel strings say AquOS while the banner and ISO say Cove.
What is unfinished
AquOS has no GUI, AetherOS cannot run separate programs, and the two have not been merged. Also missing: a check of what the shell does beyond booting, and copy-on-write fork. Both are prototypes, and booting them on a real machine is something I have not tried.
- Stack
- C, NASM, GRUB ISO (Multiboot2), QEMU x86-64. AquOS is built with native gcc under WSL; the AetherOS Makefile expects an x86_64-elf-gcc cross-compiler. Both use nasm and grub-mkrescue. Tests are Python and PowerShell scripts that drive the QEMU monitor.
- Runs on
- Virtual machines only (QEMU), never real hardware
- Written
- 1 to 4 April 2026
- Status
- Prototype
- Code
- Not public