What it is, in plain terms
ITISYOU OS is a small operating system kernel, written from scratch in Rust, that I build and test entirely inside an emulator. It is pre-alpha: version 0.8.0 as I write this, at the eighth milestone of a project that has been running for a matter of days rather than months. It is not released. There is nothing to download, nothing to install, and it must never be run on a real computer — every boot happens inside QEMU, an emulator, on a throwaway disk image the project generates for that run and discards afterwards.
If you have never thought about what an operating system actually is, the short version is: it is the software that starts first when a computer powers on, and it decides what every other piece of software is allowed to do. It manages memory, decides which program runs when, talks to storage and the screen and the keyboard, and — this is the part I care most about — decides what one running program is and is not permitted to see or touch. ITISYOU OS is my attempt to build that decision-making layer from the ground up, with the boundaries between programs made explicit and checked, rather than assumed.
I keep a public engineering record of this project at os.itisyou.app, which mirrors the same evidence this page is built from — architecture notes, the roadmap, and the current verified state. It is a record, not a product page: there is no download button there either.
Why I built it
I wanted to find out whether a personal operating system could be designed today around a small set of properties that usually get bolted on afterwards, if at all: explicit privilege boundaries between programs, a kernel and user-space split that is actually enforced rather than nominal, changes to system state that can be rolled back rather than trusted blindly, and a build process that is reproducible rather than hand-tuned. I also wanted the design to leave room, structurally, for a future where an AI system can reason about the operating system — suggest things, explain things — without ever being handed the authority to simply do them. That last idea does not exist in the kernel yet. But the capability model I have built is shaped so that if I add such a layer later, it would have to ask, the same as anything else.
None of that is a claim about what this kernel currently achieves. It is the question the project is trying to answer, stated honestly, and the reason a from-scratch kernel felt like the right way to find out rather than modifying an existing one.
The problem underneath
Most of the operating systems in daily use trace their lineage back decades, to a time when a single user on a single machine with no network connection was the normal case, and security and isolation were retrofitted onto that foundation as threats appeared. That history is not a criticism — it produced software that works extremely well — but it does mean that some of the hardest boundaries a modern system needs, like a program only being able to do exactly what it was explicitly granted, sit on top of a design that was not built with that boundary in mind from day one.
The underlying question I keep returning to is simple to state and hard to answer honestly: if a program on your computer wants to do something, on what basis does the system decide whether it may? In a lot of software that basis is implicit — a program runs as you, so it can do what you can do, full stop. I wanted to see what a kernel looks like if that answer is never implicit. Every syscall a process makes is checked against what it was explicitly given, at the point where it asks, not at the point where you install it and hope.
How it works, without the jargon
Every time I run the kernel, an emulator called QEMU pretends to be a computer and boots it from a disk image the build process just generated. The kernel starts by figuring out how much memory it has, sets up the machinery that keeps one program's memory separate from another's, and installs handlers for the hardware events — a key press, a timer tick, a disk finishing a read — that keep the system responsive rather than frozen waiting for one thing at a time.
From there it can start user programs. Each one runs in what is called Ring 3 — the least privileged mode the processor offers — with its own private view of memory, so one program genuinely cannot read another's data even if it tries. A scheduler switches between them dozens of times a second, forcibly if one refuses to yield the processor, so a program that loops forever cannot freeze everything else. Programs that need to talk to each other do it through a small, bounded messaging system rather than by directly touching each other's memory.
Storage works the same way underneath: the kernel talks to a virtual NVMe drive, and a filesystem I built for this project commits changes to its bookkeeping structures in a way that is designed to survive a crash midway through a write, because a filesystem that can be silently corrupted by bad timing is not one I am willing to trust with anything. There is a graphical desktop too — the kernel itself draws windows onto a framebuffer and composites them together, keeping one program's window contents inaccessible to another, driven by real keyboard and mouse input rather than anything faked or drawn by the host machine.
None of this happens on your computer, and none of it is meant to yet. Every one of these things happens inside the emulator, verified by an automated test harness that watches the serial output of each run and decides, mechanically, whether what it saw counts as a pass.
What building it taught me
The thing that surprised me most is how often the real bug was not in the feature I was building but in the assumption underneath it. Early on, the kernel triple-faulted — the processor's way of saying something has gone catastrophically wrong before there is even a handler installed to report it — because a large piece of memory-management state was being built on the small stack the boot process starts with, before being moved to its permanent home. It overflowed that stack and the whole machine died silently. The fix was small once found: build the structure directly where it will live, never on the boot stack at all. But finding it meant not trusting the assumption that a working piece of code that had run fine in isolation would behave the same once it moved.
A similar lesson came from the very first networking milestone. Adding a network card meant, for the first time, letting the kernel process input chosen by somebody else rather than only bytes this same machine had produced. One line of code that read perfectly well — look something up, and if it is not found, send a request for it — turned out to deadlock the whole kernel, because the lookup held a lock for the entire duration of that decision, and the fallback path needed the same lock. It is the kind of bug that a code review would plausibly wave through, because nothing about the line itself looks wrong. It only shows up when you actually run it under the exact conditions that trigger the second branch.
I have also learned to be suspicious of tests that pass by construction. When I added package signing, there was one failure mode I knew a self-consistent test suite could never catch: a single mistyped constant produces a working implementation of a different mathematical curve, which signs and verifies its own output perfectly and rejects every genuine signature. So every constant is derived from small numbers rather than typed in, and the implementation is checked against the standard's own published test vectors, not only against itself. For anything cryptographic I now treat that as non-negotiable.
And more than once, a feature that looked done in code was not actually shown to work until the test harness demanded a positive signal rather than accepting silence. A background service that paces its own work by counting how often it gets a turn on the processor looked, on paper, like a reasonable design — until a real run showed that most of its output was noise, because how often a process gets scheduled measures how busy the machine is, not how much time has passed. Replacing that with an actual clock changed the behaviour from meaningless to correct, and the only reason I caught it was that I made myself read the actual output rather than trust that the code looked right.
Where it stands today
As of 5 September 2026, the kernel has passed through eight milestones — labelled v0.1 through v0.8 — built between 2 and 5 September. In that time it went from an empty repository to a kernel that boots on two firmware paths, runs isolated user processes under preemptive scheduling, persists data through a crash-consistent filesystem, draws a real graphical desktop, talks to USB and audio hardware, enforces a default-deny capability model, checks and rolls back package updates, keeps a tamper-evident audit trail, and speaks basic IPv4 networking — all inside QEMU.
The current evidence is 278 automated tests that run directly on the development machine, plus a 24-leg matrix that boots the kernel inside QEMU under a range of conditions and checks its behaviour, both passing in continuous integration on 5 September 2026. That is a meaningful amount of coverage for a project this young, and it is also exactly the scope it sounds like: one kernel, tested by its own author, inside one emulator, over the course of a few days. Nobody outside the project has reviewed any of it.
One thing worth being precise about: the project's next-generation capability mechanism — a move from simple permission bits to unforgeable handles that can be revoked individually — is built and tested on the host machine, in isolation, but has not yet been wired into the kernel's actual boundary between a process and the system call it is making. Until it has run there, inside QEMU, and been exercised by the same adversarial test harness everything else goes through, I am not willing to describe it as verified. It is real code that passes its own tests; it has not yet been shown to hold at the place that will matter.
The package trust root deserves the same honesty. Packages are checked today against a key that the build process generates and includes in the source tree. It is described everywhere in the project's own records as a development key — published on purpose, because a key that looked secret while sitting in the source tree would be worse than an honestly labelled placeholder, not better. It authenticates against something anyone with the source can reproduce. That is fine for a research kernel with no users. It would not be fine for anything real, and moving to an actual signing key that never enters the source tree is explicit future work, not something already done and merely unmentioned.
Where it may go
- Next: take the capability-handle model from host-tested to genuinely wired into the kernel's own syscall boundary, and verify it under the same QEMU harness as everything else.
- Next: extend the network stack with TCP, DHCP and IPv6, none of which exist today — the current stack is IPv4 datagram traffic only.
- Then: route hardware interrupts through the more modern APIC controller instead of the legacy interrupt controller the kernel still relies on.
- Then: replace the development package-signing key with one provisioned properly, outside the source tree.
- Later, if the underlying design continues to hold up under its own tests: start exploring what a local-first AI layer that can reason about the system, without ever holding authority over it, would actually need from the kernel underneath it.
I am also, separately, exploring what a Chromium-based browser built along similar honesty-about-status lines would look like — that project is described on its own page, ITISYOU Browser, and the two are unrelated codebases exploring related instincts about what software owes the people who might eventually use it.
For developers: how the isolation and verification actually hold together
Process isolation rests on per-process top-level page tables. Each process gets its own private address-space root, with kernel mappings shared read-only from a common boot table and a private window reserved exclusively for that process's own memory. Pages are marked either writable or executable, never both, which closes off a whole class of exploitation technique before it can start. User-supplied pointers passed into a system call are validated against the calling process's own active page table rather than trusted at face value, which matters because a naive implementation that checks against the kernel's own view of memory will silently accept a pointer that is wrong for the process actually making the call — a bug the project hit and fixed early on.
The capability model that governs what a process may do is enforced at the system-call dispatch boundary itself, as a default-deny check, rather than as a convention that well-behaved code happens to follow. Spawning a child process is defined so that it can never grant the child more authority than the parent already holds — amplification is impossible by construction, not merely discouraged — and every denial is written into the audit trail whether or not the caller notices it was refused.
Networking is the one subsystem so far that has to trust bytes this machine did not produce, and the test harness treats that seriously: it brings its own independent, byte-level implementation of the network peer rather than reusing the kernel's own networking code to test itself, because two ends built from the same checksum routine proving they agree with each other proves nothing about whether either one is correct. That independent peer both behaves like a normal host — answering ARP and ICMP requests — and deliberately sends malformed traffic: a corrupted checksum, a misdirected packet, an unsolicited response to an unbound port, a VLAN tag, and an ARP packet whose header fields contradict each other. The kernel is required to refuse all of it and answer none of it, and that refusal, specifically the absence of any response, is checked for directly rather than assumed from the lack of a crash.
Cryptographic code is held to a higher bar than the kernel's other components: it is checked against an externally published, independent set of test vectors, not only against its own round-trip behaviour, precisely because a self-consistent but incorrectly implemented scheme will pass every test it is asked to write for itself. Hardware memory-protection features are verified in a configuration where the emulator actually models them, rather than left nominally enabled against hardware that would silently ignore the setting — and where the desired outcome is that an operation is refused, the test harness is built to check for the complete absence of a success marker, since a refusal by its nature produces no output of its own.