On a headless or real board nothing captures serial, so the boot log — the whole diagnostic stream — is lost at power-off. This retains it in the kernel and copies it to the boot USB volume as /mnt/usb/DANOS.LOG, the on-disk equivalent of QEMU's `-serial file:`. Pull the stick, read DANOS.LOG on another machine. How it fits together: - Kernel RAM sink (log.zig): a fixed 256 KiB in-image buffer registered as a log sink in kmain, right after serial. Because userspace debug_write funnels through log.write, it captures the entire stream — kernel lines and every service's output — from the first line. Fills linearly and stops when full (earliest boot output, the most valuable, is kept); no allocation, so it is panic-safe. - klog_read syscall (32): copies that buffer out to a user buffer, the mirror of debug_write — same overflow-safe user-half bounds check, kernel -> user copy, under the kernel lock so the snapshot can't grow mid-copy. Wrapped by runtime.system.klogRead. - log-flush (new one-shot, in the initial-ramdisk): waits for the fat server to mount /mnt/usb, then copies the whole log to /mnt/usb/DANOS.LOG. init spawns it once the boot services are up (fire-and-forget; it polls the mount itself). If no volume is mounted — no stick, or the no-VFS ramdisk sweep — it exits silently. - init shutdown flush: init repeats the copy inline at the top of shutDown(), BEFORE it tears down the storage services (the fat server is stopped first), so a clean poweroff captures the fullest log while /mnt/usb is still writable. - unistd.writeAll: loops write() past the 224-byte VFS payload cap; both flush paths use it. The filename is 8.3 (DANOS.LOG) at the mount root — the FAT short-name rule, and there is no mkdir on the FAT path yet. Extend-only writes mean the two same-session flushes never leave stale bytes (the shutdown log is a superset of the boot log); a shorter log on a later boot of the same stick can leave a stale tail — a noted, cosmetic limitation, not worth pulling O_TRUNC into the FAT write path for now. Verified end to end under QEMU: a new `log-flush` case (reusing the orderly-shutdown build) asserts both markers then S5, and DANOS.LOG is read back out of the image afterwards (11804 bytes, containing the kernel init line, the FAT mount line, and the boot flush marker). Regression stays green: zig build, zig build test, zig build check-fat-image, and a sequential QEMU sweep — smoke, init, initial-ramdisk (log-flush silent in the bare sweep), orderly-shutdown, fat-mount, usb-storage, usb-hid, vfs, input, device-manager, process, signals, dma, fault-pf.
DanOS
Codename: Shodan Version: 1
A small resilient operating system, written from scratch in Zig.
Zen of DanOS:
- Resilient Micro-Kernel Architecture.
- Every process run in an isolated user space not kernel space.
- Processes cannot take down the entire OS with it when they die or is killed
- Stable public runtime library, private OS ABI.
- Keeps a stable runtime for user space processes between OS versions (great for backwards compatibility)
- Allows the underlying OS to be changed without effecting applications
- Provides a boundary to enable compatibility between OS's e.g. POSIX, MUSL etc
- Drivers are just isolated processes in user space.
- Thin binaries that can be restarted like applications.
- Useful during driver development.
- Drivers can claim MMIO / ports
- Driver resources (e.g. IRQ/Port/MMIO) claims are automatically cleaned up if the driver dies or is killed
- Drivers can also hook into the process lifecyle to clean up or reset hardware
- No legacy to deal with
- Zig code uses a clean coding style (Zen of Zig)
- Favor reading code over writing code.
- No magic numbers.
- No shortend names unless its for ABI compatibility or acronyms
- Inter-Process Communication (IPC)
- Publish and subscribe to Asynchronous Messages
- Talk to services and processes synchronously
Prerequisites
- Zig 0.16.x — the build is pinned to this line (
.zig-version); other minor versions are rejected, because Zig makes breaking changes between releases pre-1.0. A toolchain manager such as zvm orzigupwill pick up.zig-versionautomatically. - QEMU (
qemu-system-x86_64) — to run and test the kernel. On macOS,brew install qemualso bundles the OVMF firmware below. - OVMF UEFI firmware — the
edk2-ovmfpackage (Arch),ovmf(Debian/Ubuntu), oredk2-ovmf(Fedora); on macOS it ships inside the Homebrewqemuformula. Both the build and the test harness probe the known Arch/Debian/Fedora/macOS layouts and use the first that exists, so no configuration is normally needed. Override with-Dovmf-code=/-Dovmf-vars=(build) if yours lives elsewhere. - Python 3 — for the QEMU integration test harness.
Build
zig build
Produces a FHS-shaped zig-out/ that is the danos filesystem and the boot volume:
the UEFI bootloader at zig-out/EFI/BOOT/BOOTX64.efi, the kernel at
zig-out/system/kernel, init at zig-out/system/services/init, drivers under
zig-out/system/drivers/, and the initial-ramdisk at zig-out/boot/.
Run
Boot it in QEMU with OVMF (opens a display window):
zig build run-x86-64
# distro with OVMF elsewhere:
zig build run-x86-64 -Dovmf-code=/path/OVMF_CODE.fd -Dovmf-vars=/path/OVMF_VARS.fd
Test
zig build test # host unit tests (the platform-independent shared code)
python3 test/qemu_test.py # QEMU integration tests: boots the kernel and asserts
# on its serial output (see docs/testing.md)
The integration harness builds and boots the kernel once per test case, checking memory, the frame allocator, paging (incl. NX and the null guard), the heap, interrupts, and exception handling. It exits non-zero on any failure, so it drops straight into CI.
Documentation
Design notes explaining why behind the code live in
docs/ — start with docs/README.md.
For the hardware needed to run DanOS — minimum specs plus a plain-language guide
matching Intel/AMD CPU generations by name — see
docs/system-requirements.md.
Logo
San Serif Text "Dan OS" with a black karate belt around it.