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.

Pre-alphax86-64, UEFINo releases yetManualSource

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.

KindOne object isCount
hostThe machine itself: hostname, timezone, environment, power state, package origins.host:local1
svcA process PID 1 supervises: its command, user, restart policy and the services it wants up first.svc:sshd · svc:time · svc:pipewire19
pkgA package tree in the store, named by its hash and linked into /bin and /lib.pkg:curl · pkg:gcc · pkg:mesa149
planA build pinned by hash. oath build runs it in a sandbox with no network.plan:claude-code8
snapA btrfs generation of the root filesystem, taken by each apply.snap:current · snap:162333
netThe network link, by DHCP or a static address.net:net01
sshThe keys that may log in as home.ssh:local1
devA 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-14

What works today

PID 1
Runs only applied svc specs, each in its own cgroup with its own log. Restarts a failing service with backoff, then marks it failed with 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.
oath
ls 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 mcp over SSH: one tool per verb, the INDEX as instructions, --read-only for an agent that only looks. The apply log records which agent made each change.
Generations
A btrfs snapshot before every apply. oath undo repeats to go further back. The boot menu keeps the last five boots.
Packages
A store addressed by hash, /bin and /lib link farms, a needs graph, origins tried in order, oath store for a tree built on the machine, and oath verify at 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-efi is the only boot loader, and the initrd runs Oath's PID 1.
Network and SSH
net0 by DHCP or a static address, time from pool.ntp.org, OpenSSH for SSH as home with root login refused, the network daemons dropped to the unprivileged void, and dig.
Install
cargo make install over SSH to a named disk, or to a USB stick; from a stick booted on the machine, oath install lists the disks and copies the system onto the one you name, with home in its own subvolume. Each machine updates itself: oath update takes the newest output its build host signed, without wiping it.
Testing
cargo make check boots one QEMU machine per case and reboots for real. --target runs the hardware cases on an installed machine and undoes each one. --agent gives 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:

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.