← All projects

Project · operating system

Rime OS

A Linux distribution I build as one signed image, with its own desktop, Rime Shell, built into it. It began as APEX-OS in July 2026 and became Rime in September, while machines were running it. Its own site is rimeos.com; these are the notes on how it is built.

rimeos.com ↗Why RimeDownload

  • Fedora bootc 43
  • Rust · rimed · rime
  • Quickshell · QML
  • cosign · SBOM
  • CachyOS kernel build
  • Android · Kotlin
Rime Shell in its default dark theme, the Dashboard open out of the centre notch over a photo of an office tower
Rime Shell, recorded from the real desktop at 3840 × 2400: the Dashboard grown out of the centre notch.

A package manager updates a running system in place, so a failed upgrade can leave the machine between two versions. Rime is built the other way round. The whole operating system is one OCI container image on Fedora bootc, built and signed in CI and published to GitHub's registry. Everything a fresh install has is in it: the desktop, the tools, the kernel, the apps.

One image, staged

sudo rime update checks the next image, stages it beside the one that is running, rebuilds any extra packages against it, and updates Flatpaks and firmware. The machine moves to the new image at the next reboot, and sudo rime rollback goes back to the previous one. Updates never start on their own.

The Shell is vendored into the image at build time, not installed on top of it. A release that changes the desktop and the system moves both, and a rollback takes both back together. Every image records the exact Shell revision it carries in its labels, so a release is always one pair of revisions.

Making an update small

The first images rebuilt everything on every change, so every update was the whole 5.3 GB. The build is now split into tiers: a core that holds the kernel, drivers and large apps, a base, and a thin top layer. The layer an ordinary update changes went from 358 MiB to 84.5 MiB, measured. A core rebuild still costs about 5 GB, so the core only rebuilds when something in it changes, such as Fedora's base image.

Checked before it deploys

Every image is signed with cosign in keyless mode, so the signature is tied to the GitHub Actions workflow that built it rather than to a key someone holds, and it carries a signed SPDX bill of materials recorded in Rekor. rime update refuses an image whose signature does not verify against a Fulcio root pinned inside the image. rime trust --gate runs the same checks and says whether the next update would be refused, and why, before anything changes.

Coding agents that never get root

Rime runs coding agents such as Claude Code, Codex, OpenCode, Gemini and Kimi as sessions the system tracks. rime-agentd owns each session's terminal, so it knows which agents are working, which are waiting for me, and which need a permission. The Dashboard's Agent Center lists them with those first.

  • Each session starts in a bubblewrap sandbox: a read-only system and a home directory that holds only the project. Inside, 4 processes are visible against 408 outside, with no camera and no sound devices.
  • No process in a session can gain privileges, so sudo does not work. An agent that needs a package asks from a closed list of eight requests, and I approve it with my password at the machine. There is no request that runs a command.
  • rime-secretd holds credentials and performs the operation itself: a git push, a Cloudflare deploy. It has no verb that returns a credential, so there is nothing for an agent to leak.

The sandbox is built for a cooperating but fallible agent, one that makes a mistake. It is not a defence against a determined attacker with a kernel exploit, and the documentation says so.

The Agent Center: a privilege request waiting for a decision above Claude and Codex sessions that need attention
The Agent Center, recorded from the real Shell: a privilege request waiting for a decision, then the sessions that need me.

Rime Remote

Rime Remote is the Android app for those sessions. It pairs with a computer through a one-time code, then talks to it end to end with Noise (X25519 and ChaCha20-Poly1305), directly on the local network or through a relay on Cloudflare Workers that carries ciphertext it cannot read. From the phone I can watch a session's terminal, reply to it, pause or stop it, and start new ones. It cannot approve anything that needs root, by design. The first release, 0.1.0, went out on 29 September 2026 as a signed APK.

Renaming a running OS

APEX-OS became Rime while two machines were running it, and a rename touches the command, the daemons, the image name, the signing identity and files in every user's home. It went out as two releases. The first taught every client to accept both signing identities and published every build under both image names, so a machine could move from apex-os to rime-os on its next update. The second did the rename itself, with the old names kept working as aliases and a migration for each account.

Before the second release went out, a virtual machine went from APEX to Rime, back to APEX and forward again. Going back found three bugs: the secret store's state directory broke the old service, a session migration rewrote user files to names the old image did not have, and a keybinding file that required itself made every shortcut register 63 times. Each was fixed and re-run before any real machine moved.

Games

Rime builds its own kernel from CachyOS's sources, with the BORE and sched-ext patches, and Gaming Mode asks for the LAVD latency scheduler. On my desktop, Gaming Mode froze under the packaged LAVD 1.1.3: five stalls in sixteen minutes. The cause was an upstream bug with a fix not yet released, so the image builds LAVD with that fix applied. The first real session after it ran 24 minutes of Terraria with no stalls.

Everything else is on rimeos.com ↗AgentsThe rename, in full