Oath
A Linux distribution: the Linux kernel and its own userspace, PID 1 included. The machine is a catalog of typed objects at /oath, and one command, oath, reads and changes it. People and AI agents use it the same way, and every change can be undone.
Goals
An AI agent can run the machine. A new operating system has no forum answers and no training data, so the running machine has to explain itself. /oath/INDEX.md is generated from the live catalog: the verbs, the kinds of object on this machine and the safety rules. An agent that has never seen Oath reads it, or connects with ssh -T home@host oath mcp and gets each verb as a tool. Its changes go through sudo and into the same log as the owner's. The acceptance test gives a model nothing but that server and a task.
One catalog, one command. Everything you administer is an object with a typed schema: host:local, svc:sshd, pkg:curl. oath set writes what you want, oath diff shows how it differs from what is applied, and oath apply makes it so. There are no files in /etc to edit and no unit files. Unix is the substrate, not the admin interface.
Every change can be undone. oath apply checks every object before it touches the machine, and a refusal changes nothing. Then it snapshots the root filesystem to a btrfs generation and converges. oath undo puts back what the last apply changed. A change that could cut off SSH goes on probation: PID 1 reverts it after 180 seconds unless it is kept from a new session.
Independent, and built from source. Oath is not a remix of Debian, Arch, NixOS, Alpine or Ubuntu. PID 1, the catalog, oath, the boot loader and the installer are written for Oath, in Rust. Every other package, the kernel, GCC and glibc among them, is built from pinned source by an Oath plan or is the vendor's own release pinned by sha256. No package comes from another distribution, and rebuilds are bit-identical.
Open to inspection. Every package is published at store.oath.moonlight.pm with its licence, the plan that built it and the source that plan pins, so the origin alone can rebuild any of them. Sources are checked against their upstream signatures where upstream signs.
Small on purpose. One compiler, one C library, one GNU tool for each job. Two accounts, root and home, and no /usr. The machine belongs to one person.
The catalog
A machine is a set of objects. Each has an id, kind:name, a desired state that you write and an actual state that the machine reports. There are eight kinds. The counts are from one machine on 26 October 2026: a 2013 Mac Pro that runs Oath as a desktop.
| Kind | One object is | Count |
|---|---|---|
host | The machine itself: hostname, timezone, environment, power state, package origins.host:local | 1 |
svc | A process PID 1 supervises: its command, user, restart policy and the services it wants up first.svc:sshd · svc:time · svc:pipewire | 19 |
pkg | A package tree in the store, named by its hash and linked into /bin and /lib.pkg:curl · pkg:gcc · pkg:mesa | 149 |
plan | A build pinned by hash. oath build runs it in a sandbox with no network.plan:claude-code | 8 |
snap | A btrfs generation of the root filesystem, taken by each apply.snap:current · snap:162 | 333 |
net | The network link, by DHCP or a static address.net:net0 | 1 |
ssh | The keys that may log in as home.ssh:local | 1 |
dev | A device found in sysfs: a disk by its bus path, a network card, a serial port.dev:net0 · dev:disk-pci-0000.0e.00.0-ata-1 | 4 |
What works today
- PID 1
- Runs only applied
svcspecs, each in its own cgroup with its own log. Restarts a failing service with backoff, then marks itfailedwith a reason. Restarts a service when an apply moves the package its program comes from. Stops services in reverse order at shutdown, loads kernel modules by modalias, and survives its own panic. oathls schema get set new rm diff apply keep undo verify log logs fmt build store mcp, each with--json. A failure names the next step to take.- Agents
oath mcpover SSH: one tool per verb, the INDEX as instructions,--read-onlyfor an agent that only looks. The apply log records which agent made each change.- Generations
- A btrfs snapshot before every apply.
oath undorepeats to go further back. The boot menu keeps the last five boots. - Packages
- A store addressed by hash,
/binand/liblink farms, aneedsgraph, origins tried in order,oath storefor a tree built on the machine, andoath verifyat every boot. A package whose licence forbids redistribution ships as a recipe that the machine builds itself. - Plans
- Builds in a sandbox with no network and its own PID namespace, cached by plan and inputs. Everything in the image is a plan product: the kernel, the toolchain and Oath's own binaries.
- Toolchain
- GCC, binutils and glibc built from source, bootstrapped once by the build host and then rebuilt by themselves. bash and the GNU userland; zsh is the login shell.
- Kernel and boot
- Linux from kernel.org, pinned by sha256 and built as a plan.
oath-efiis the only boot loader, and the initrd runs Oath's PID 1. - Network and SSH
net0by DHCP or a static address, time from pool.ntp.org, OpenSSH for SSH ashomewith root login refused, the network daemons dropped to the unprivilegedvoid, anddig.- Install
cargo make installover SSH to a named disk, or to a USB stick; from a stick booted on the machine,oath installlists the disks and copies the system onto the one you name, with home in its own subvolume. Each machine updates itself:oath updatetakes the newest output its build host signed, without wiping it.- Testing
cargo make checkboots one QEMU machine per case and reboots for real.--targetruns the hardware cases on an installed machine and undoes each one.--agentgives a real model five administration tasks.- Desk
- Optional: a desktop. rivulet, a C port of the River compositor on wlroots, runs the shell, terminal, Projects, browser and Design of Sola, with Mesa's RADV for Vulkan and PipeWire for sound.
The manual covers each of these in detail.
A session
Recorded on the same Mac Pro. The first set is refused because sometimes is not a restart policy the kind allows, and nothing is written. The apply takes generation 162 before it changes the service; the undo returns to the generation before it, and the log records both.
$ sudo oath set svc:time restart=sometimessvc:time: unknown variant `sometimes`, expected one of `never`, `always`, `on-failure` (setting restart)hint: oath schema svc$ sudo oath set svc:time restart=on-failure$ oath diffsvc:time restart: "always" -> "on-failure"$ sudo oath applyCreate readonly snapshot of '/oath/run/fs/@' in '/oath/run/fs/@gen-162'applied generation 162 (svc:time)$ sudo oath undoundid to generation 135$ oath diffin-sync$ oath log -n 2{"actor":{"tty":"unknown","uid":0},"error":null,"generation":162,"ids":["svc:time"],"parent_generation":135,"result":"ok","time":"2026-10-20T03:34:37Z"}{"actor":{"tty":"unknown","uid":0},"error":null,"generation":135,"ids":["undo:162"],"parent_generation":162,"result":"ok","time":"2026-10-20T03:34:37Z"}
What an agent reads first
oath with no arguments prints /oath/INDEX.md, which is generated from the live catalog. This is the Mac Pro's.
# Oath You are on **Oath**. Do not assume Debian, Arch, NixOS, Alpine, or Ubuntu. Do **not** edit `/etc` or random files. Use `oath`. ## Verbs oath this INDEX (the file is /oath/INDEX.md) oath ls list objects (optional --kind) oath schema [kind] kinds, or one kind's schema + prose oath get <id> desired + actual oath set <id> k=v write desired only (checked against the kind's type) oath new <id> k=v create a svc or pkg (runs after apply) oath rm <id> remove a svc or pkg (after apply) oath diff [id] drift oath apply [id...] check, snapshot, then converge oath keep keep an apply on probation (from a new SSH session) oath undo put back the spec the last apply changed, and converge oath verify hash linked package trees again against their pins oath log apply log oath logs [id] output of svc:<name>, net:net0, a plan's last build (plan:<name>), or PID 1 (no id) oath fmt <path> canonical plan.plan oath build isolated plan build (no /bin link; log in /oath/log/plan/) oath store pkg:<name> <dir|tar> put a tree built elsewhere into the store and print its hash; `oath set pkg:<name> hash=…` pins it oath mcp these verbs as an MCP server on stdin/stdout (ssh -T home@<host> oath mcp) `--json` prints the same facts as JSON. `oath --help` for flags. ## Kinds here - `dev` — a hardware device (inventory) (`oath schema dev`)- `host` — this machine (hostname, power, env, timezone, mounts) (`oath schema host`)- `net` — a network link (net0, dhcp or static) (`oath schema net`)- `pkg` — a package (store + /bin links) (`oath schema pkg`)- `plan` — a build plan (hash-pinned file; oath build) (`oath schema plan`)- `snap` — btrfs generations (apply / undo) (`oath schema snap`)- `ssh` — owner SSH public keys (login as home) (`oath schema ssh`)- `svc` — a process PID 1 supervises (`oath schema svc`) ## Safety `oath apply` checks everything first (a refusal changes nothing), thentakes a filesystem snapshot. Halt, reboot, boot generation changes(other than `oath undo`) and changes that can cut off SSH need`--confirm`. Do **not** pass `--confirm` unless the owner asked for thatclass of change. A change that can cut off SSH then goes on probation:PID 1 undoes it after 180 s (`--probation <secs>`), or at the nextboot, unless `sudo oath keep` runs in an SSH session opened after theapply. Nothing else applies while it is open. ## Where you are Serial is **root** (break-glass). SSH is **home** (`HOME=/home`);`sudo` has no password. The catalog lives at `/oath`. `/home` is the`home` account's directory. There is no `/usr`; helpers are`/lib/oath`. `oath new` makes a `svc` or `pkg`; `oath rm` removes one. Both, like`set`, only change the spec: nothing happens until `oath apply`, and`oath undo` takes back the last apply. Changes that can cut off SSH(sshd, `ssh:local` keys, `net:net0`, a new `pkg:dropbear` hash) need`--confirm`. A service restarts when an apply moves the package itsprogram comes from.
Status
Oath is pre-alpha and not a daily driver. There are no releases or install images to download: you build it from source and install it over SSH, or from an Oath stick with oath install. As of 8 November 2026 the current build passes all 70 test cases in QEMU (75 with the desk), oath install its rehearsal, and updates from the releases machines run; the desk's hardware cases passed on the Mac Pro on 22 October, and the hardware cases that apply to a headless Intel machine on 27 October; on 30 October oath install put it on an AMD desktop with an NVIDIA card, from a stick. Not there yet:
- Releases. x86-64 with UEFI is the only platform.
- A signed package index. Packages are pinned by hash, their sources are checked against upstream signatures, and machines take only builds a key they trust signed (a release by the build host, a package by a key they trust for its name), but the index of the pack origin is not signed.
- More than one network interface. One is chosen at boot.
- Device node ownership as a catalog field.
- Desktop applications beyond Sola's terminal, Projects, browser and Design, and Steam: its 64-bit client runs on the Mac Pro and on two NVIDIA desks, one of which has played Terraria and Noita and connects a Bluetooth Xbox pad. Minecraft runs on that desk through Prism Launcher.
The work under way is in CURRENT.md, the decisions behind it in docs/decisions.md, and what each part can do in docs/capabilities.md.
Building
You need an x86-64 Linux build host with Rust, Nix (for the stage-0 compilers and QEMU), sudo and mkfs.btrfs. The first toolchain build takes an hour or more.
git clone https://github.com/moonlight-pm/oath
cd oath
cargo make bootstrap # the toolchain, once
cargo make pkgs # every package in pkgs/, from source
cargo make build # the base image
cargo make check # the QEMU test cases
cargo make run # boot it, with the serial console in this terminal
Installing wipes the target disk: from the build host over SSH, or with oath install from an Oath stick booted on the machine; the manual has the steps.