ryoku is the front door to the whole system. It updates the machine, rolls it
back, reports its state, reloads the desktop, and repairs stateful drift, all
from one Go program (ryoku/cli/). It does not reimplement package management
or snapshots; it orchestrates pacman, yay, and snapper and adds the safety
rails around them. Run ryoku with no arguments for the built-in usage.
Two worlds: which install is this?
Every command behaves with respect to one of the two ways Ryoku can live on a machine. Most confusion about the CLI comes from mixing them up.- Packaged install (normal)
- Dev checkout (maintainers)
The desktop is installed from the
[ryoku] pacman repo. ryoku-desktop
ships the base config under /usr/share/ryoku/config; there is no git
checkout and no build toolchain. This is what you have after installing from
the ISO or converting an existing Arch box.ryoku update auto-detects which world it is in, so the same command is correct
on both. A recorded checkout means the git path; otherwise it takes the pacman
path. A few commands belong to only one world; that is called out below.
At a glance
Everyday commands
These are user-facing and work on any install.ryoku update
The full, safe update. It is wrapped in a snapper pre/post snapshot pair
(best-effort: an unconfigured snapper never blocks the update, but a failed step
aborts before anything else changes). Throughout the run it publishes progress to
$XDG_RUNTIME_DIR/ryoku-update.json, so the shell’s update island shows the wave
while the update runs. What it actually runs depends on the world:
- Packaged install
- Dev checkout
1
Snapshot
Take a snapper pre-snapshot so the run is reversible.
2
System packages
sudo pacman -Syu.3
AUR packages
yay -Sua, if yay is present.4
Materialize config
ryoku materialize lays the refreshed base config into ~/.config.5
Reload
Restart the shell and reload Hyprland so the new config is live.
ryoku update also runs ryoku doctor itself, so self-healing happens on every
update. See Updates for the snapshot model and channel details.
ryoku status [--json]
A read-only report; it changes nothing. It always prints the active config base.
On a checkout it shows the channel, the deployed commit (installed), and how
many commits behind the channel you are. On a packaged install it shows the
installed ryoku-desktop version, what the [ryoku] repo offers, and the count
of pending package updates (via checkupdates from pacman-contrib). It ends
with the snapshot count.
--json is the data seam the Hub and the update island read; it is not meant for
humans.
ryoku rollback [id]
Restore a snapper snapshot after a bad change.
sudo snapper rollback <id>. The same snapshots are selectable from the Limine
boot menu, so a machine that will not boot back into Hyprland can still be
recovered from the bootloader. See Troubleshoot for the
recovery playbook.
ryoku snapshots
List the snapper snapshots (sudo snapper list). Requires snapper.
ryoku reload
Restart the shell and reload Hyprland by handing off to ryoku-shell reload. Use
it after changing config that is already in place, for example a hand-edit to
~/.config/hypr/user.lua.
Keep it healthy: ryoku doctor
ryoku doctor runs the convergent reconcilers: idempotent checks (and, where it
is safe, fixes) for stateful drift the package and config layers cannot express.
Each reconciler reports ok when the machine matches the desired state,
otherwise it converges, proposes the exact fix, or flags it for a human. A
healthy machine is quiet.
ryoku update runs ryoku doctor for you, so a finding never aborts an
update. Run it by hand any time you suspect drift.ryoku/cli/doctor.go covers:
- Disk and snapshots: swap kept out of snapshots, snapper configuration, btrfs device health.
- Boot: Limine boot-menu layout, stale install crypt mapper.
- Packages: stale pacman database lock, the ryoku package channel and
keyring, pending
.pacnewconfig, orphaned packages. - Session and desktop: desktop session components, Hyprland config
integrity (it revalidates and repairs the generated
monitors.lua/gpu.luadrop-ins so a corrupt one cannot strand the desktop in emergency mode), the ryoku shell daemon, failed services, cursor theme, SDDM greeter theme. - Display and hardware: display backlight, display resolution, NVIDIA boot reliability.
When doctor cannot fix it: --report
When doctor cannot fix something (or finds an unknown problem) it writes a single
shareable text report and points you to it. Generate one any time:
/proc/swaps, failed units, recent journal errors, pacman state, the ryoku
channel state, session env, and hardware: backlight devices, GPU drivers, kernel
cmdline, recent display-driver log) into one .txt the maintainers can read. It
contains no passwords or keys.
When you want it to reason: --explain
ryoku doctor --explain sends that same report to a cloud model and prints its
root-cause analysis and fix steps, reaching the long tail the reconcilers cannot
pre-encode.
Any OpenAI-compatible endpoint works. Defaults target Groq (free, fast):
One layout everywhere: ryoku keyboard
A keyboard layout has to land in four places, and only one of them is the
desktop: Hyprland’s config, the X11 file the SDDM greeter reads,
/etc/vconsole.conf for the TTYs, and a copy of that file baked into the boot
image, which is what the disk passphrase prompt uses. The last one is the trap:
mkinitcpio copies the keymap in at build time, so editing the file later, even
through localectl, never reaches the passphrase prompt.
With no layout argument,
apply pushes the desktop’s own layout out. The
Displays-page “Apply system-wide” button runs the same command, and doctor
reports the drift with the exact fix.
Config lay-down: ryoku materialize
ryoku materialize lays Ryoku’s base configs into ~/.config, declaratively and
override-safe. It copies every file the package ships under the base config
dir (clobbering the previous Ryoku copy), prunes files a past release shipped but
this one dropped (tracked by a manifest at ~/.local/state/ryoku/materialized),
and never touches files the package never shipped.
Your own overrides survive every
materialize: hypr/user.lua,
hypr/monitors_user.lua, kitty/user.conf, fish/user.fish, and anything
else you added are left alone. The per-machine generated drop-ins
(hypr/monitors.lua from ryoku-monitor, hypr/gpu.lua from ryoku-gpu) are
seeded only when absent and never clobbered or pruned, so an update refreshes
the shipped config without ever resetting your display layout, GPU pin, or any
setting. See Customize./usr/share/ryoku/config, shipped by ryoku-desktop. This is
the packaged-install replacement for a checkout’s config copy: it is run for you
by ryoku update and by the installer’s deploy step, not usually by hand.
Emergency: ryoku recovery
The last resort when the desktop is too broken to update normally (a corrupt
local config, a broken local build). It resets the checkout to main and
redeploys, overwriting configs.
bin/ryoku-recovery, preferring the copy in a local checkout and
otherwise fetching the canonical script from the repo over the network, so it
still works when the local build is broken. See
Troubleshoot for when to reach for it.
Developer command: ryoku deploy
The dev loop, never used on production installs. It builds the Go binaries and
the QML plugin and lays the repo into ~/.config from a checkout by running
ryoku/shell/deploy.sh. It requires RYOKU_REPO to point at the checkout and
errors otherwise. The deploy also records the checkout root and the deployed
commit under ~/.local/state/ryoku/, which is what lets status and update
track the git channel afterwards.
Environment and state
Overrides, mostly fordeploy and tests:
AI reasoning for
doctor --explain (opt-in; the report is sent only when a key
is set). Any OpenAI-compatible endpoint works:
State the CLI keeps under
$XDG_STATE_HOME/ryoku (default ~/.local/state/ryoku):
At runtime,
$XDG_RUNTIME_DIR/ryoku-update.json is the update island’s progress
file, written by update.
See also
Updates
The snapshot model, the channel, and what a safe update guarantees.
Troubleshoot
Logs,
ryoku doctor, rollback, and recovery when something breaks.Customize
The override files
materialize never touches.
