axol-rt Rust realtime core, the sole production hardware control backend for teleop and every other arm-motion command. It runs automatically as a step of provision; run it by hand to (re)build on demand:
axol-rt binary two narrowly scoped
capabilities: CAP_SYS_NICE, used by its two CAN threads to enter their
real-time scheduling class, and CAP_IPC_LOCK, which lets it lock its memory
(mlockall) past the unprivileged RLIMIT_MEMLOCK so a page reclaimed under
recording I/O pressure can never fault a CAN thread mid-tick. The step escalates on
its own: directly as root (the installer and the system service), otherwise
through passwordless or already-cached sudo, or one password prompt on a
terminal. It is skipped entirely when the binary already carries both
capabilities, which reading needs no privilege for. Only a run with none of
those fails, and it prints the exact sudo setcap command to run once. The control core refuses to arm if its requested real-time
scheduler cannot be established, so a normal scheduler pause can never occur
after the motors enable; a failed memory lock is only reported (the core runs
unlocked, as every session did before it locked at all).
Three concerns, each idempotent and self-gating:
- Toolchain — if no working
cargois available (PATH or~/.cargo/bin), a minimal stable Rust toolchain is installed via rustup (no sudo; lands in~/.cargo). Candidates are probed before use, so a rustup shim inherited from another user’s PATH does not block the bootstrap. - Sources — a dev checkout builds the in-repo crate (
rust/axol-rt). Tool installs have no sources on disk, so the crate is fetched into~/.almond/axol-rt-srcat the exact ref matching the installed package: the git commit foruv tool install git+...installs, or thev<version>release tag for PyPI installs. A tool install made from a local checkout (uv tool install /path/to/axol) builds that checkout’s crate. When none of that applies (a wheel file, a release that predates the crate), or guesses wrong, setAXOL_RT_SOURCEto a checkout path or to<git url>@<branch|tag|commit>— e.g.AXOL_RT_SOURCE=https://github.com/almond-bot/axol@main axol rt.install. An explicit override always rebuilds. - Binary — dev checkouts leave it in
rust/axol-rt/target/release/(where the rt link already looks, and where a manualcargo buildstays authoritative). Tool installs copy it intoUV_TOOL_BIN_DIRwhen that environment variable is set, or to~/.local/bin/axol-rtotherwise, and stamp the built ref so re-runningprovisionat the same version skips the build. The hosted installer setsUV_TOOL_BIN_DIR=/usr/local/bin, so its binary lands at/usr/local/bin/axol-rtalongside theaxollauncher.
AXOL_RT_BIN (env override), then the current checkout’s repo build, then axol-rt on PATH — so a manual checkout build or an explicit override wins over the provisioned copy.
The package and the binary must speak the same wire protocol. Python declares its generation in the core’s config (proto <n> — CONFIG_PROTO in almond_axol.rt.link, currently 3), and a binary built from a different layout refuses to configure — before any motor is touched — with a message naming the fix (config: client speaks proto …, this axol-rt speaks proto …); the session then fails with axol-rt … refused the configuration and exited. That is the signal to rebuild: axol rt.install, or cargo build --release in rust/axol-rt for a checkout whose target/release/ binary has gone stale.
A failed rustup download, source fetch, build, or capability setup is
warning-only inside plain
axol provision. Pass --require-rt to make that
failure exit non-zero; the hosted installer and self-updater use this strict
form. Running axol rt.install directly also surfaces the full error.