rowan.id.au— Piers Rowan

HomePiROS › README.md

PiROS — a provable-runtime x86 OS (proof of concept)

A from-scratch x86_64 operating system whose organizing principle is: a program runs only if it can prove it is the exact, unmodified thing the package authority signed. Every hardware interaction goes through a lowest-common-denominator abstraction, so any backend can be swapped for another that meets the same API and the OS is unaffected.

It has grown from a kernel that boots and runs echo into a small multi-user system: ring-3 isolated programs loaded from a signed /bin, per-user encrypted persistent storage, cross-user publishing, a full-screen editor, shell scripting, and runtime program installation — all still gated by the same rule (a program runs only if it verifies, A + B = C). See GUIDE.md to use it and ARCHITECTURE.md for the design.

What works today

A multi-user system you log into on the serial console, where every command is an authorized, integrity-verified, ring-3 tool invocation:

login: bob bobpw
bob@pros> echo bob secret notes > files/notes   # written through the encryption vault
bob@pros> grep secret files/notes               # grep is a separate loaded ELF
bob secret notes
bob@pros> iamgood iambad                         # bob holds the iamgood grant
Yes bob, you are good!
bob@pros> logout
login: mary marypw
mary@pros> ls files                              # Mary cannot see Bob's /data
(empty)
mary@pros> iamgood iambad                         # Mary wasn't granted iamgood
[DENIED] no grant for service 'iamgood'

Every tool is a real ELF loaded from the ramdisk, verified, and run unprivileged. Tamper a program's ELF and the loader refuses it (image does not match signed hash); run a privileged instruction and the process is killed while the shell survives.

Proven end-to-end:

(today a 16550 UART on COM1).

separately-compiled freestanding ELF, packed into a ramdisk the bootloader hands the kernel. The kernel loads it into an RWX window, verifies its ELF against the signed manifest, and runs it via a syscall table. The kernel binary is byte-identical when a tool changes — OS and toolchain are decoupled.

an int 0x80 syscall trap. A tool that runs a privileged instruction or touches memory it doesn't own is killed, and the shell survives — not a kernel crash.

(ed25519 signature + SHA-256 of the ELF). Tamper the ELF in the ramdisk → denied.

core is the ambient namespace (its tools run bare); a service is a namespace, not a macro (text_tools cat … uses the cluster grant). iambad is in neither core nor text_tools, so it is denied bare and reachable only via iamgood. Deny-by-default.

password-hash auth. Each user has an isolated /data/$user; ownership is enforced at the syscall trap, so a tool can only ever touch its owner's data. Grants are per-user.

(write-through), auto-mounted at boot, so files survive reboots. Every file is sealed with ChaCha20 + HMAC-SHA256 (per-user key) before storage — the raw disk holds ciphertext (verified: file contents don't appear in the image).

^X exit). Edit a file, reboot, and it's still there.

fetches it if you're in the audience. The kernel decrypts with the owner's key and enforces access — Mary can publish to Bob, and a non-audience user is denied.

and ↑/↓ recall recent commands. Author a script with prano, run it, and it persists on the encrypted disk.

program can be installed into a writable disk overlay and it persists across reboots, no OS rebuild. Safe because the kernel verifies signatures at load — a tampered install is refused. (This is how you add a compiled program, e.g. a browser.)

small PHP/bash-style scripting language ($vars, if/while, string interpolation, file I/O). Author a .psh script and run it with psh <file>.

files/; programs author logs//tmp/, readable but not user-writable (so logs are provenance-preserving).

Beyond the base — networking, multitasking, services

HTTP, with RTT/RTO estimation and Reno-style congestion control (slow start + congestion avoidance) on the TCP send side. ping, nslookup/host, curl (prints the raw response), wget <url> [dest] (saves the body into files/), and a text-mode web reader all hit the real internet from ring-3 programs via server/client socket syscalls.

a client that verifies GitHub's host key, and a server you can log into with a password or an authorized_keys pubkey (real OpenSSH clients interoperate). The server spawns one worker task per accepted connection, so several users hold sessions concurrently — each with its own identity and privileges (bounded by the task table, ~3–5 at once; a full table refuses a new connection with a one-line hint). ssh <box> '<cmd>' runs a single command non-interactively (output + exit status), so a monitoring tool can probe the box like a Linux server.

(fopen/fread/fwrite/fflush/fclose/fsize): a ring-3 program opens a file in its own store into a plaintext cache, reads and writes it by offset (random access), then flushes/closes it back through the encryption vault — turning the whole-file vfs into random-access, write-back descriptors. The same cache stages OS upgrades.

by a per-box machine key (the offline authority key never touches the machine). admin user create / admin grant / admin service create.

by all users (e.g. mnt/authorized_keys for provisioning SSH keys).

a cooperative scheduler (kernel-stack context switch), and spawn — so a program runs as a sandboxed, isolated, scheduled process.

the background while the console stays usable: sshd (kernel-hosted), tintin (HTTP), nemo (FTP), queequeg (SQL) — the last three as sandboxed ring-3 processes running as their own no-login service user. ps lists them; syslog records events + resource snapshots (the run-up to a crash).

Users: root/rootpw, bob/bobpw, mary/marypw (root+bob get all services; mary gets core). Tools (in /bin): echo, cat, ls, grep, cp, rm, prano, publish, argo, curl, wget, asterix, psh, list_build/list_get/list_save, iambad, hello. publish=produce, argo=consume what another user published (argo owner:name [dest], argo --list owner; no network); wget/curl=HTTP. Services: core (echo, cat, ls, grep, cp, rm, prano, publish, wget, argo, asterix, hello, psh), text_tools, iamgood, and admin (list_build, list_get, list_save — root only).

New here? See GUIDE.md for the full command reference, the publish/fetch workflow, and how to write, sign, and install your own programs. UAT.md has step-by-step acceptance tests (incl. the admin list workflow).

Build & run

Prerequisites (installed automatically if you used the setup in this repo): a Rust nightly toolchain with rust-src + llvm-tools-preview, and qemu-system-x86_64.

./build.sh          # compile tools, sign them, pack /bin, build the kernel, boot in QEMU
./build.sh --build  # build the bootable image only (dist/pros-bios.img)

QEMU emulates x86 on non-x86 hosts, so the kernel takes a few seconds to reach the pros> prompt. Type quit to power the machine off.

Rebuild only the tools (kernel untouched) to see the decoupling:

(cd programs && cargo build --release)              # tools only
cargo test --manifest-path crates/cert/Cargo.toml   # host-side SHA-256 tests

Layout

PathRole
kernel/The no_std microkernel (/os): HAL/IO, auth/verify, program loader, builtins. Holds only the authority PUBLIC key.
crates/abi/The program ABI: entry signature + syscall table both sides compile against.
crates/cert/The manifest/grant schema + verifier (/manifest/schema). Shared by signer and kernel.
crates/rt/Tool runtime: entry glue, panic handler, syscall wrapper. Every tool links it.
programs/<name>/A tool — a freestanding binary crate (echo, cat, ls, grep, cp, rm, prano, publish, wget, asterix, …), built independently of the kernel.
tools/manifest/The authority (/manifest/bin). Holds the PRIVATE key: signs programs, grants, users; never runs them.
tools/ramdisk/Packs the signed programs into the baked /bin ramdisk.
tools/install/Installs a signed program into the persistent /bin overlay on data.img (no OS rebuild).
tools/bootbuild/Joins the kernel ELF + ramdisk into a bootable BIOS image.

See ARCHITECTURE.md for the design, the certificate model, and the roadmap toward the full runtime directory tree (/os, /bin, /manifest, /data/$user).

License

PiROS is GPL-2.0 — see LICENSE.

The TCP send-side control logic (kernel/src/net/tcp_input.rs, and the parts of kernel/src/net/tcp.rs that drive it) is derived from the Linux kernel: net/ipv4/tcp_input.c, net/ipv4/tcp_cong.c, and include/net/tcp.h. Because tcp_input.c is GPL-2.0 only (not "or later"), the combined work is GPL-2.0 only. Every other dependency is MIT or MIT/Apache-2.0 dual-licensed, so all are GPL-2.0-compatible under their MIT option.

Distributing a pros-rN.img therefore means offering the corresponding source under the GPL.

Developing across more than one machine (or more than one agent)? See CONTRIBUTING.md for the branch/merge workflow, the Ubuntu + KVM + bare-metal setup, and the component-ownership map that keeps parallel work from colliding — plus TASKS.md, the live board of who owns what.