Installing HORUS
Most platforms have a prebuilt, checksum-verified binary, so the usual install is one command and under a minute. This guide covers that, the source build for the platforms without one, upgrading, and uninstalling.
Platform Support
HORUS has native cross-platform support:
| Platform | Status | Shared Memory Path | Notes |
|---|---|---|---|
| Ubuntu 20.04+ | Supported | /dev/shm/horus_<namespace>/ | Recommended for production |
| Ubuntu 22.04+ | Supported | /dev/shm/horus_<namespace>/ | Best performance |
| Debian 11+ | Supported | /dev/shm/horus_<namespace>/ | Tested and working |
| Fedora 36+ | Supported | /dev/shm/horus_<namespace>/ | Use dnf for packages |
| Arch Linux | Supported | /dev/shm/horus_<namespace>/ | Community maintained |
| Raspberry Pi | Supported | /dev/shm/horus_<namespace>/ | 64-bit and 32-bit Pi OS both have a prebuilt binary |
| macOS | Supported | /tmp/horus_<namespace>/ | tmpfs-backed, full support |
| Windows | Supported | %TEMP%\horus_<namespace>\ | Native Windows support |
| WSL 2 | Supported | /dev/shm/horus_<namespace>/ | Linux mode in WSL |
The namespace defaults to the literal default — so on Linux the real directory is /dev/shm/horus_default/. Set HORUS_NAMESPACE to isolate robots from each other.
Prebuilt Binaries
Each release publishes exactly these six assets, plus a SHA256SUMS file the
installer verifies every download against. The list is the release workflow's
matrix, and the release refuses to publish at all if one of them is missing —
a hole in the set is a 404 for that platform, followed by a silent fall-through
to an hours-long source build.
| Platform | Release asset | Notes |
|---|---|---|
| Linux x86_64 | horus-linux-amd64.tar.gz | glibc 2.28 or newer |
| Linux aarch64 (64-bit Pi OS, Jetson) | horus-linux-arm64.tar.gz | glibc 2.28 or newer |
| Linux armv7 (32-bit Pi OS) | horus-linux-armv7.tar.gz | glibc 2.28 or newer |
| macOS Apple Silicon | horus-macos-arm64.tar.gz | |
| macOS Intel | horus-macos-amd64.tar.gz | |
| Windows x86_64 | horus-windows-amd64.zip | see Windows |
The Linux binaries are dynamically linked against glibc, not static, and they are cross-compiled against a glibc 2.28 floor — Debian 10, RHEL 8, Ubuntu 18.04, the same floor the Python wheels use. The release workflow reads the version-needs back out of each ELF and fails the release rather than publishing an asset above the floor.
Releases cut before that floor was enforced — v0.4.0 and earlier — were
built natively on the CI runner and carry a GLIBC_2.39 version-need, so they
will not start on Raspberry Pi OS, JetPack, Ubuntu 22.04, Debian 12 or RHEL 9.
On those, build from source until a newer release is available.
Anything at or above the floor runs the binaries. Anything older, or a musl distro such as Alpine, needs a source build:
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | HORUS_BUILD_FROM_SOURCE=1 bash
horus: /lib/.../libc.so.6: version 'GLIBC_2.xx' not found means exactly this
and nothing else. The installer runs horus --version before it declares
success, so it catches the case and tells you to build from source rather than
reporting a working install you cannot run. If you see the message from a
binary you copied onto a robot by hand, that check never ran.
Any platform with no asset — a BSD, a musl distro, an architecture not in the
table — is not an error path. The installer prints
No pre-built binary for <os>-<arch> and compiles the same tag it would have
downloaded.
Prerequisites
For any install:
- Operating System: Linux, macOS, or Windows
- Linux: glibc 2.28 or newer for a prebuilt binary; Ubuntu 20.04+ recommended
- macOS: Native support using
/tmp/horus_<namespace>/ - Windows: Native support using
%TEMP%\horus_<namespace>\
- curl and git — the installer fetches the matching source tree with git, and keeps it
- Internet connection: to reach GitHub Releases
Only for a source build (a platform with no release asset, HORUS_BUILD_FROM_SOURCE=1, or a glibc below the floor):
- Rust 1.90+: the workspace MSRV. We'll install this in Step 1
- Build Tools & System Libraries: Build tools, pkg-config, and all required development libraries
# Ubuntu/Debian/Raspberry Pi OS - COMPLETE dependencies (copy-paste this!) sudo apt update sudo apt install -y build-essential pkg-config \ libssl-dev libudev-dev libasound2-dev \ libx11-dev libxrandr-dev libxi-dev libxcursor-dev libxinerama-dev \ libwayland-dev wayland-protocols libxkbcommon-dev \ libvulkan-dev libfontconfig-dev libfreetype-dev \ libv4l-dev # Fedora/RHEL/CentOS sudo dnf groupinstall "Development Tools" sudo dnf install -y pkg-config openssl-devel systemd-devel alsa-lib-devel \ libX11-devel libXrandr-devel libXi-devel libXcursor-devel libXinerama-devel \ wayland-devel wayland-protocols-devel libxkbcommon-devel \ vulkan-devel fontconfig-devel freetype-devel \ libv4l-devel # Arch Linux sudo pacman -S base-devel pkg-config openssl systemd alsa-lib \ libx11 libxrandr libxi libxcursor libxinerama \ wayland wayland-protocols libxkbcommon \ vulkan-icd-loader fontconfig freetype2 \ v4l-utils - 10-15 minutes: For a first-time source build
What these packages do:
- Core:
libssl-dev(networking),libudev-dev(device detection),libasound2-dev(audio) - Graphics/GUI: X11, Wayland libraries (required for monitor)
- Optional:
libv4l-dev(camera support), fontconfig (improved text rendering)
Not required:
- Rust, on any platform with a prebuilt binary — see Prebuilt Binaries
- A clone of the repository; the installer fetches its own source
- Systems programming experience
Quick Install (Recommended)
One line, no clone, no Rust toolchain needed first:
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | bash
horus --help
If the help command displays available commands, installation is complete. Skip to Next Steps.
What the installer does:
- Resolves the latest release tag — call it
T - Downloads the prebuilt binary for
Tand verifies it against that release'sSHA256SUMS, refusing to install if the file is missing, has no line for your asset, or the digest does not match - Clones the source at the same
Tinto~/.horus/cache/horus@<version>/, and compileshorus_managerfrom it if no binary matched your platform (~3-5 min) - Installs the
horusbinary to~/.cargo/bin/(or~/.local/bin/), and adds that directory to your PATH - Runs
horus --versionand refuses to report success unless it works - Records what it installed in
~/.horus/installed_versionand~/.horus/install_manifest.toml
horus run and horus build generate a Cargo.toml that depends on horus by
path, pointing at ~/.horus/cache/horus@<version>/. Deleting that directory
leaves a CLI that cannot compile a single Rust project — see
HORUS source not found.
That is also why the binary and the source come from one resolved tag rather
than two refs. When they did not, both trees called themselves 0.4.0 while
their shared-memory headers disagreed, and the symptom arrived hours later as
horus topic list reporting 0 messages on a live topic.
Installer Options
Every knob is an environment variable. The installer reads them from its own
environment, so with a pipe they go on the right-hand side — VAR=x curl ... | bash
puts the variable in curl's environment, where nothing reads it:
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | HORUS_VERSION=v0.4.0 bash
| Variable | Effect |
|---|---|
HORUS_VERSION=v0.4.0 | Install exactly that release — binary and source. The leading v is optional |
HORUS_BUILD_FROM_SOURCE=1 | Skip the prebuilt binary; compile the source at the resolved tag |
HORUS_LOCAL_SOURCE=/path | Air-gapped: build a source tree already on disk. No clone, nothing fetched for the source |
HORUS_PREFIX=/opt/horus | Install root override: $HORUS_PREFIX/bin for the binary, $HORUS_PREFIX in place of ~/.horus for state and cache. Also how a root install says where it means to go. horus run searches $HORUS_PREFIX/cache first, then ~/.cache/horus and ~/.horus/cache — so keep HORUS_PREFIX exported in the environment of whoever runs horus, or set HORUS_SOURCE=$HORUS_PREFIX/cache/horus@<version> instead |
HORUS_INSTALL_BRANCH=dev | Developer escape hatch: build from a branch. Forces a source build |
HORUS_NO_SHELL_INTEGRATION=1 | Leave shell rc files alone — skips horus env --init, which shadows cargo, pip, cmake, conan and vcpkg inside HORUS projects |
Full descriptions are in the environment variable reference.
Pinning a branch used to change only which source was cloned, while the binary
still came from releases/latest — so the one documented way to "pin" an
install was the surest way to end up with a CLI and libraries from different
commits. It now implies HORUS_BUILD_FROM_SOURCE=1: a branch has no matching
release asset, so the whole install is compiled from that branch or not made
at all.
To pin a release, use HORUS_VERSION. That is the one that moves both
halves together.
Installing as root, or for another user
curl ... | sudo bash is how a provisioning runbook drives a shell installer,
and $HOME is /root there. The installer resolves $SUDO_USER and installs
into that account's home instead. With no SUDO_USER — the normal case under
Docker, systemd and Ansible's become — it stops and asks you to say where,
rather than putting the binary, the cache and the state under /root and
printing "Installation complete!":
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | HORUS_PREFIX=/opt/horus bash
Shell integration and completions are skipped for a root-on-behalf-of install,
because both write to $HOME and under sudo that is root's. Run
horus env --init as the target user afterwards if you want the tool proxies.
Installing from a clone
If you already have the repository — or you are working on HORUS itself:
git clone https://github.com/softmata/horus.git
cd horus
./install.sh
This is the same script, so it still resolves and installs a release tag. To install the checkout you are standing in, point it at the tree:
HORUS_LOCAL_SOURCE="$PWD" ./install.sh
Step-by-Step Installation
The one-liner above needs only curl and git. This longer route installs the
prerequisites explicitly, which is what you want when the fast path fell through
to a source build — a platform with no release asset, HORUS_BUILD_FROM_SOURCE=1,
or a glibc older than the floor.
Step 1: Install Rust
HORUS is built with Rust. No prior Rust experience is required; HORUS also supports Python for application development.
A source build needs Rust 1.90 or newer — the workspace MSRV. The installer reads that floor out of the workspace manifest and refuses before it starts compiling, rather than failing several minutes in with a cargo error about a package you have never heard of.
On Linux (or WSL/Docker):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Follow the prompts (just press Enter for defaults).
Restart your terminal or run:
source $HOME/.cargo/env
Verify Rust is installed:
rustc --version
cargo --version
You should see version numbers like rustc 1.90.0 or higher.
Step 2: Install Git (If You Don't Have It)
Check if you have Git:
git --version
If not, install it:
# Ubuntu/Debian
sudo apt install git
# Fedora
sudo dnf install git
# Arch Linux
sudo pacman -S git
Step 3: Fetch the Installer
You do not need a clone — the installer fetches its own source. Either pipe it:
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | bash
or clone first, if you want to read the script before running it:
git clone https://github.com/softmata/horus.git
cd horus
Step 4: Run the Installer
./install.sh
This script:
- Resolves one release tag, and uses it for both the binary and the source
- Downloads the prebuilt
horusbinary for that tag and verifies it against that release'sSHA256SUMS, or compileshorus_managerfrom the cached source if no asset matches your platform (~3-5 min) - Caches the source at
~/.horus/cache/horus@<version>/ - Installs the
horusbinary to~/.cargo/bin/(or~/.local/bin/) - Adds the install directory to your PATH and runs
horus env --init - Verifies with
horus --version, and compares the binary's version against the cached source before declaring success - Writes
~/.horus/installed_versionand~/.horus/install_manifest.toml
The output displays progress with colored indicators. Green checkmarks indicate successful steps.
It also installs shell completions for bash, zsh or fish, and a man page
(man horus). Both are best-effort: a failure there is reported but never
fails the install.
Step 5: Verify Installation
Test that the horus command works:
horus --help
You should see a list of available commands like new, run, monitor, etc.
Python Support
The Python bindings are a separate distribution: install.sh installs the CLI
and the source cache, never the Python package. The supported way to get them is
pip, with a version floor:
pip install "horus-robotics>=0.4.0"
The newest horus-robotics on PyPI is 0.1.9. It imports fine and runs fine,
and it speaks an older shared-memory format than a 0.4.x CLI — so a bare
pip install horus-robotics gets you a package whose nodes publish topics your
CLI cannot see, with no error anywhere. horus topic list simply reports
nothing.
Keeping the >=0.4.0 floor turns that into a resolver error you can read
instead of a silence you have to debug. Until 0.4.x is published, build the
bindings from the tree the installer already cached — same tag as your CLI, by
construction:
pip install ~/.horus/cache/horus@0.4.0/horus_py
Substitute your own version; horus --version prints it.
Building the bindings by hand (working on HORUS itself, or on a platform with no wheel):
# 1. Install maturin (Python build tool)
# Option A: Via Cargo (recommended for Ubuntu 24.04+)
cargo install maturin
# Option B: Via pip (if not blocked by PEP 668)
# pip install maturin
# 2. Navigate to the Python bindings in the cached source
cd ~/.horus/cache/horus@0.4.0/horus_py
# 3. Build and install into the active interpreter (takes ~3 minutes)
maturin develop --release
To verify Python bindings work:
python3 -c "import horus; print('Python bindings installed successfully')"
Note: The PyPI package name is
horus-robotics, but you import it asimport horusin Python code.
See Python Bindings for complete API documentation and examples.
Rust Crate Support
There is no crates.io channel. cargo add horus and cargo install horus
resolve to an unrelated crate that owns the name on crates.io, and the first
use horus::prelude::*; fails to compile against it. HORUS's own crates carry
git and path dependencies that cargo package rejects, so none of them are
published.
horus new and horus run point Cargo at ~/.horus/cache/horus@<version>/
as a path dependency instead — which is why the installer keeps that tree, and
why a horus line in Cargo.toml is neither needed nor correct.
Platform-Specific Notes
Linux (Ubuntu/Debian)
Linux installation is straightforward. If build errors occur, install all required packages:
sudo apt update
sudo apt install -y build-essential pkg-config \
libssl-dev libudev-dev libasound2-dev \
libx11-dev libxrandr-dev libxi-dev libxcursor-dev libxinerama-dev \
libwayland-dev wayland-protocols libxkbcommon-dev \
libvulkan-dev libfontconfig-dev libfreetype-dev \
libv4l-dev
Linux (Fedora/RHEL)
sudo dnf groupinstall "Development Tools"
sudo dnf install -y pkg-config openssl-devel systemd-devel alsa-lib-devel \
libX11-devel libXrandr-devel libXi-devel libXcursor-devel libXinerama-devel \
wayland-devel wayland-protocols-devel libxkbcommon-devel \
vulkan-devel fontconfig-devel freetype-devel \
libv4l-devel
Raspberry Pi
HORUS runs on Raspberry Pi. Install Ubuntu Server or Raspberry Pi OS, then:
# Install all dependencies
sudo apt update
sudo apt install -y build-essential pkg-config \
libssl-dev libudev-dev libasound2-dev \
libx11-dev libxrandr-dev libxi-dev libxcursor-dev libxinerama-dev \
libwayland-dev wayland-protocols libxkbcommon-dev \
libvulkan-dev libfontconfig-dev libfreetype-dev \
libv4l-dev
# Raspberry Pi specific packages (GPIO, I2C, SPI support)
sudo apt install -y libraspberrypi-dev i2c-tools python3-smbus
# Enable I2C and SPI (required for sensors)
sudo raspi-config
# Navigate to: Interface Options → I2C → Enable
# Navigate to: Interface Options → SPI → Enable
Performance tips:
- Use 64-bit OS for better performance
- Compile with
--releaseflag (much faster than debug) - Allocate more RAM to GPU if using camera nodes (in
/boot/config.txt)
NVIDIA Jetson Nano
HORUS supports Jetson Nano with GPU acceleration for vision tasks:
# Install all dependencies
sudo apt update
sudo apt install -y build-essential pkg-config \
libssl-dev libudev-dev libasound2-dev \
libx11-dev libxrandr-dev libxi-dev libxcursor-dev libxinerama-dev \
libwayland-dev wayland-protocols libxkbcommon-dev \
libvulkan-dev libfontconfig-dev libfreetype-dev \
libv4l-dev
# Jetson specific packages (GPU acceleration)
sudo apt install -y nvidia-jetpack
# Verify CUDA is installed
nvcc --version
GPU acceleration:
- Install JetPack for CUDA support; HORUS itself ships no CUDA feature
--enable <cap>forwards<cap>as a cargo feature to your crate, sohorus run --enable cudaworks only once your project declares acudafeature under[rust.features]in itshorus.toml(in a workspace, in the member'shorus.toml— a virtual root cannot carry[features]) and your own node code gates the CUDA path on it
macOS (Native)
HORUS has native macOS support using /tmp/horus_<namespace>/ for shared memory:
# 1. Install Xcode Command Line Tools (if not already installed) — this is
# where git comes from on a clean macOS
xcode-select --install
# 2. Install HORUS. Both Apple Silicon and Intel have a prebuilt binary, so
# this needs no Rust toolchain.
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | bash
# 3. Verify installation
horus --help
macOS shared memory:
- Uses
/tmp/horus_<namespace>/, i.e./tmp/horus_default/by default (tmpfs-backed, fast) - Automatically created on first run
- Same API as Linux - no code changes needed
Alternative options (if native doesn't work for your use case):
- Docker - Run HORUS in a Linux container; see Docker below
- VMware/Parallels - Full Linux VM
- Cloud Linux - Remote development on AWS/DigitalOcean
Windows (Native)
HORUS has native Windows support using %TEMP%\horus_<namespace>\ for shared memory.
Windows has its own installer, install.ps1. Run it from PowerShell:
irm https://github.com/softmata/horus/raw/main/install.ps1 | iex
horus --help
That downloads horus-windows-amd64.zip for the resolved release tag, verifies
it against that release's SHA256SUMS, clones the matching source into
~\.horus\cache\horus@<version>, installs horus.exe into ~\.cargo\bin, and
adds that directory to your user PATH. It needs git
(winget install --id Git.Git); it needs a Rust toolchain only if you ask it
to build from source.
install.sh verifies the checksum, prints Checksum verified, and then dies at
unzip: command not found (exit 127) — unzip is not in Git for Windows'
bundled MSYS2 set, and the tar that is there is GNU tar, which cannot read a
zip. The script now falls back to Windows' own tar.exe when it can find it and
tells you what to do when it cannot, but install.ps1 is the supported path and
does not have the problem at all.
Pinning a version, or any other option. A piped script cannot take
parameters, so the knobs are the same environment variables install.sh uses.
PowerShell has no VAR=value command prefix form, so set them as their own
statement first:
$env:HORUS_VERSION = 'v0.4.0'
irm https://github.com/softmata/horus/raw/main/install.ps1 | iex
Saved to a file instead, the same knobs are real parameters:
irm https://github.com/softmata/horus/raw/main/install.ps1 -OutFile install.ps1
.\install.ps1 -Version v0.4.0
-Version, -BuildFromSource, -InstallBranch, -LocalSource, -Prefix and
-NoShellIntegration correspond one-for-one to the variables in Installer
Options.
The installer turns TLS 1.2 on for you. Without that, an unpatched Windows PowerShell 5.1 fails against github.com with "The request was aborted: Could not create SSL/TLS secure channel", which reads like a proxy problem and is not one. PowerShell 7 negotiates on its own.
Windows shared memory:
- Uses
%TEMP%\horus_<namespace>\(typicallyC:\Users\<user>\AppData\Local\Temp\horus_default\) - Automatically created on first run
- Cross-process communication works natively
Alternative: WSL 2 (if you prefer Linux environment)
- Open PowerShell as Administrator
- Run:
wsl --install - Restart your computer
- Follow Linux installation steps inside WSL
Hardware Driver Support (Optional)
HORUS integrates hardware drivers distributed as registry packages; none are bundled with the core install. These are completely optional - the default installation does NOT require any hardware packages or drivers.
Key points:
- The default install works without any hardware packages
- Nodes that declare no
[hardware]need no driver packages at all;horus run --simswaps declared hardware for stubs - Hardware drivers are installed from the registry only when you ask for them
- You can install HORUS on your laptop and deploy to hardware later
When you need hardware packages:
- Only when you install a hardware driver from the registry (
horus install <driver>) and configure it under[hardware]inhorus.toml - Only for production deployment on actual robots
- Never needed for development, testing, or simulation
Hardware Drivers Available as Registry Packages
None of these ship with the core install. Find and install them with the horus CLI (horus search <hardware>, then horus install <package>), and wire them up under [hardware] in horus.toml. The "System Packages" column lists the host libraries that must already be present for the driver to work.
| Driver | Hardware | System Packages | Availability |
|---|---|---|---|
| SocketCAN | CAN bus devices | can-utils (optional) | Registry package |
| spidev | SPI devices | None (kernel interface) | Registry package |
| i2cdev | I2C devices | i2c-tools (Pi only) | Registry package |
| Serial | Serial devices | None (kernel interface) | Registry package |
| GPIO | GPIO pins | libraspberrypi-dev (Pi only) | Registry package |
| PWM | PWM outputs | libraspberrypi-dev (Pi only) | Registry package |
| RealSense | Intel depth cameras | librealsense2-dev | Registry package |
| Modbus TCP | Force-torque sensors | None (pure Rust) | Planned |
Installing the System Packages Drivers Need
These are the host libraries and tools from the table above. The drivers themselves come from the registry (horus install <package>).
For Raspberry Pi GPIO/I2C/SPI/PWM:
sudo apt install -y libraspberrypi-dev i2c-tools python3-smbus
# Enable hardware interfaces
sudo raspi-config
# Navigate to: Interface Options → I2C → Enable
# Navigate to: Interface Options → SPI → Enable
# Navigate to: Interface Options → Serial Port → Enable
For CAN bus debugging tools:
sudo apt install -y can-utils
# Setup virtual CAN for testing
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0
# Test CAN tools
cansend vcan0 123#DEADBEEF
candump vcan0
For Intel RealSense depth cameras:
# Add Intel RealSense repository
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key F6E65AC044F831AC80A06380C8B3A55A6F3EFCDE
sudo add-apt-repository "deb https://librealsense.intel.com/Debian/apt-repo $(lsb_release -cs) main" -u
# Install RealSense SDK
sudo apt install -y librealsense2-dev librealsense2-utils
# Test camera
realsense-viewer
Workflow: Development to Hardware Deployment
Development (on your laptop):
# 1. Install HORUS normally (no hardware packages needed)
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | bash
# 2. Create and run your project
horus new my_robot
cd my_robot
horus run # Builds and runs your nodes. A new project declares no
# [hardware], so nothing here touches a device.
A bare horus run is not simulation mode — it instantiates whatever real drivers
[hardware] declares. Once you add hardware entries and want to develop against stubs
on your laptop, use --sim:
horus install horus-sim3d # one-time: the simulator plugin `--sim` launches
horus run --sim # swaps every [hardware] entry marked `sim = true`
# for a stub node
Note:
--simauto-launches the simulator plugin (sim3dby default, or[robot].simulator). If that plugin is not installed,horus run --simrefuses to start rather than fall through to the real hardware.
Deployment (on the robot):
# 1. Install system packages on the robot
sudo apt install libraspberrypi-dev i2c-tools can-utils
# 2. Run against the real hardware
horus run --release
Hardware is declared in horus.toml, not on the command line:
[hardware]
imu = { use = "bno055", bus = "/dev/i2c-1" }
Capabilities such as GPU acceleration or the editor are command-line flags. Each
capability name is forwarded to cargo as a feature of your own crate, so declare it
under [rust.features] in horus.toml first — otherwise the build stops with
none of the selected packages contains these features: cuda:
# horus.toml
[rust.features]
cuda = []
editor = []
horus run --enable cuda --release
Or enable them once in horus.toml (top level, above any [section] header) instead of
passing the flag every time:
# horus.toml
enable = ["cuda", "editor"]
Capability names recognised by --enable (each is forwarded as a cargo feature of your crate unless noted; you must declare it under [rust.features] yourself):
cuda(aliasgpu) → featurecudaeditor→ featureeditorheadless→ featureheadlessvisual→ featurevisualpython(aliaspy) → featurepythonopencv→ featureopencv-backendio-uring→ featureio-uring-netultra-low-latency→ featureultra-low-latencyfull→ featurefullnet(aliasnetwork) — not forwarded to your crate; enables thenetfeature on thehorusdependency insteadsim(aliassimulation) — turns on no build features by itself
Note: An unrecognised name is passed through verbatim as a cargo feature. If your crate does not declare it, the build fails with
none of the selected packages contains these features: <name>.
Hardware buses are not --enable capabilities. Configure them under [hardware] in horus.toml, as shown above.
Cargo feature flags (for advanced users building with cargo directly):
macros- Procedural macros (node!,topics!) — enabled by defaultnet- Transparent LAN topic replication across machines (opt-in; also reachable ashorus run --net)telemetry- Telemetry export for live monitoring — enabled by defaultblackbox- Flight recorder for post-mortem analysis — enabled by default
Note: Most users should use
horus run --enable <capability>or setenable = ["cuda", "editor"]inhorus.tomlrather than passingcargo --featuresby hand. The four flags above are features of thehoruscrate itself, not of your project.
How simulation works:
Entries marked sim = true under [hardware] in horus.toml are swapped for stub nodes
when you run horus run --sim; the log then shows
hardware.<name>: simulation mode — using stub.
That is the whole mechanism. HORUS does not detect a missing device and silently fall
back to simulation — if your driver opens /dev/spidev0.0 and it is not there, that is
your driver's error to handle. Write the fallback yourself if you want one, or use --sim
to develop on a laptop.
Hardware in Python
Once a driver is installed and declared under [hardware] in horus.toml, you can interact with hardware from Python nodes using HORUS Topics and standard message types:
import horus
from horus import Imu, CmdVel
def controller_tick(node):
imu_data = node.recv("imu/data")
if imu_data:
node.send("cmd_vel", CmdVel(0.5, 0.0))
# A dict works too, as long as its keys are the message's field names:
# node.send("cmd_vel", {"linear": 0.5, "angular": 0.0})
node = horus.Node(
name="Controller",
subs={"imu/data": {"type": Imu}},
pubs={"cmd_vel": {"type": CmdVel}},
tick=controller_tick,
rate=50
)
horus.run(node)
Declaring a topic with {"type": CmdVel} makes it a typed topic. node.send() accepts
either an instance of the message class or a dict, converting a dict with
CmdVel(**data).
That conversion is why the field names matter: CmdVel's fields are linear and
angular, so {"linear_x": ..., "angular_z": ...} fails to convert. The failure is
silent at the conversion step — the code falls through and hands the raw dict to the
typed path, which rejects it:
TypeError: 'dict' object is not an instance of 'CmdVel'
The message names the type, not the bad key, so check your field names against the message when you see it.
Additional hardware drivers (CAN bus, SPI, I2C, Dynamixel servos, stepper motors, etc.) are available as registry packages. Use horus search to find drivers for your hardware.
Understanding Shared Memory
HORUS uses platform-specific shared memory for ultra-fast communication between components:
| Platform | Shared Memory Path | Notes |
|---|---|---|
| Linux | /dev/shm/horus_<namespace>/ | Native POSIX shm, fastest |
| macOS | /tmp/horus_<namespace>/ | tmpfs-backed |
| Windows | %TEMP%\horus_<namespace>\ | Temp directory |
The namespace is default unless you set HORUS_NAMESPACE, so the directory you will normally see is /dev/shm/horus_default/ (Linux), /tmp/horus_default/ (macOS) or %TEMP%\horus_default\ (Windows).
Check available space:
# Linux
df -h /dev/shm
# macOS
df -h /tmp
# Windows
Get-PSDrive -Name $env:TEMP.Substring(0,1)
You should have at least 256MB. Most systems have 1-2GB.
If you need more space (Linux only):
# Temporarily increase to 2GB
sudo mount -o remount,size=2G /dev/shm
# Make permanent: edit /etc/fstab (requires sudo)
# Add line: tmpfs /dev/shm tmpfs defaults,size=2G 0 0
Upgrading HORUS
horus self update # move the CLI and the cached source to the latest release
horus self update --check # report current vs latest, change nothing
horus self update resolves the newest published release from GitHub, fetches
that tag's source, downloads the platform binary for the same tag and
verifies it against that release's SHA256SUMS, replaces the running binary in
place, and refreshes ~/.horus/cache/horus@<version>.
Both halves move together, for the reason in Quick Install: a new CLI compiling your projects against the previous release's headers is the version skew, not a smaller version of it. On a platform with no published asset it compiles that same tag instead of downloading it.
--check prints the current and latest versions and installs nothing. It exits
0 whether or not an update is available; a non-zero exit means the check
failed — GitHub was unreachable, rate-limited, or answered something unparseable.
Those are errors now, not a silent "up to date".
Installing a specific version
self update only ever moves forward to the newest release. To install a
particular one — including going back to an older one — re-run the installer
with the tag pinned:
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | HORUS_VERSION=v0.4.0 bash
$env:HORUS_VERSION = 'v0.4.0'
irm https://github.com/softmata/horus/raw/main/install.ps1 | iex
Pin the same tag on every robot in a fleet and every machine is running one
known tree, binary and source alike. That is what HORUS_VERSION is for, and
what HORUS_INSTALL_BRANCH is not — a branch forces a source build precisely so
it can never be paired with a release binary.
Telling what is installed
horus --version # the CLI binary
cat ~/.horus/installed_version # what the last install recorded
cat ~/.horus/install_manifest.toml # the full record
install_manifest.toml is written by both install.sh and horus self update:
version = "0.4.0"
tag = "v0.4.0"
commit = "…40 hex…"
topic_version = 4
source_dir = "/home/you/.horus/cache/horus@0.4.0"
binary = "/home/you/.cargo/bin/horus"
install_method = "release-binary"
installed_at = "2026-08-31T09:14:07Z"
commit is the field worth quoting in a bug report: a tag identifies a tree only
until someone moves it. topic_version is the shared-memory header version, and
it is the number that actually decides whether the CLI can attach to what your
nodes publish — two trees can share a version string and disagree about it.
An install made before these files existed has neither. Nothing treats their absence as an error.
The version mismatch warning
If the CLI and the recorded install disagree, every horus new and every
dependency-resolving horus run prints a warning naming both versions, both
topic ABIs when they are known, and the command that reconciles them. It is a
warning: the command carries on.
Set HORUS_STRICT_VERSION=1 to make it a hard failure instead — which is what
you want in CI, and only there. The default is deliberately loud rather than
fatal, because the earlier hard failure printed a remedy that could not work and
left a whole release cohort unable to run horus new at all.
See Version mismatch warning for how to read one and clear it.
Docker
Two images, answering different questions. Each release tag publishes both to GitHub Container Registry:
| Image | Contents | Size |
|---|---|---|
ghcr.io/softmata/horus:dev | Rust, Python and CMake — builds and runs projects | 3.98 GB |
ghcr.io/softmata/horus:latest | The CLI alone, no language toolchains | 284 MB |
Both also carry an immutable per-release tag: ghcr.io/softmata/horus:0.4.0 and
ghcr.io/softmata/horus:0.4.0-dev. Pin those on a robot — latest and dev
move with every release.
# Build and run projects — this is the one you want
docker run --rm -it --shm-size=1g -v "$PWD:/work" -w /work ghcr.io/softmata/horus:dev run
To build them yourself instead — from a fork, from an unreleased commit, or on an architecture the published images do not cover:
git clone https://github.com/softmata/horus.git
cd horus
docker build --target dev -t horus:dev .
docker run --rm -it --shm-size=1g -v "$PWD:/work" -w /work horus:dev run
The default build target — and the latest tag that follows it — is the
CLI-only image: about 284 MB, with no language toolchains. It can inspect a
system, validate a manifest, generate message types and read logs. It cannot
build or run a project: horus run there stops at
horus hint [preflight] H060
Rust toolchain not installed.
The error is correct; the image is just the wrong one. Use :dev, or
--target dev when building — it carries Rust, Python and CMake, and costs
3.98 GB for them.
# CLI-only image: inspect, validate, generate
docker run --rm -v "$PWD:/work" -w /work ghcr.io/softmata/horus:latest doctor
docker run --rm -v "$PWD:/work" -w /work ghcr.io/softmata/horus:latest check
# The same, built locally
docker build -t horus:cli .
docker run --rm -v "$PWD:/work" -w /work horus:cli doctor
Shared memory. Nodes talk through /dev/shm, so a container that runs them
needs a real one — Docker's 64 MB default is small for image or point-cloud
topics, hence --shm-size=1g. For IPC between containers, share the namespace
too:
docker run --rm --ipc=host --shm-size=1g -v "$PWD:/work" -w /work horus:dev run
Real-time scheduling needs a capability. Without it the container cannot set
SCHED_FIFO and HORUS degrades — correctly and loudly, but it degrades, and a
benchmark run that way measures the container rather than HORUS:
docker run --rm --cap-add=SYS_NICE --shm-size=1g -v "$PWD:/work" -w /work horus:dev run
File ownership. These images run as root, so anything horus new or
horus build writes into a bind mount is root-owned on the host. Pass your own
uid and a writable CARGO_HOME to keep it:
docker run --rm --user "$(id -u):$(id -g)" -e CARGO_HOME=/work/.cargo \
-v "$PWD:/work" -w /work horus:dev build
Uninstalling
To completely remove HORUS — no clone needed:
curl -fsSL https://github.com/softmata/horus/raw/main/uninstall.sh | bash
Or ./uninstall.sh from a checkout. It prints an inventory first and asks before
deleting anything, on your terminal even when the script itself arrived through
the pipe. --dry-run prints that inventory and removes nothing; --yes runs
unattended and keeps ~/.horus/config.toml, your credentials and the Cargo
registry cache. It answers every other prompt with that prompt's default, so
items 7 and 8 below — which default to yes — happen without being asked:
curl -fsSL https://github.com/softmata/horus/raw/main/uninstall.sh | bash -s -- --yes
With no terminal and no --yes — a CI job, a provisioning script — it stops and
says so rather than guessing.
The uninstaller will:
- Remove the
horusCLI binary from~/.cargo/bin/and~/.local/bin/ - Remove
~/.horus/cache/— the cached HORUS source and its pre-compiled deps - Remove the rest of
~/.horus/(shell env files, install state), prompting first only if it holdsconfig.tomlor credentials — answer "no" there and your config survives whilecache/still goes - Remove
~/.config/horus/(auth.json, workspace registry) on Linux, or~/Library/Caches/horus+~/Library/Application Support/horuson macOS, or%APPDATA%\horus+%LOCALAPPDATA%\horuson Windows - Clean up shared memory files (platform-specific paths)
- Leave project-local
.horus/directories untouched - Offer to
pip uninstall horus-robotics, if that wheel is installed — the prompt defaults to yes - On Linux and WSL, if
/etc/security/limits.d/99-horus-realtime.confexists, offer to remove the RT scheduling configuration — that file,/etc/sysctl.d/99-horus-realtime.confand thehorus-performance-governorsystemd unit — via sudo, again defaulting to yes. That check predateshorus setup-rt, which writes99-horus-rt.conf; a limits file from a currentsetup-rtrun does not match it and survives, so undo that separately withhorus setup-rt --undo
The uninstaller does not touch ~/.cache/horus/ — the XDG package cache used by
horus pkg — or ~/.local/share/horus/, which holds recordings, blackbox records and
your signing keys. Blackbox recordings from a run inside a project are written to that
project's own .horus/blackbox/, which item 6 leaves alone. Remove them by hand if you
want them gone.
Manual uninstall (if needed):
Linux/macOS:
# Remove CLI tool
rm -f ~/.cargo/bin/horus ~/.local/bin/horus
# Remove caches, config and credentials — these are separate directories
rm -rf ~/.horus/ # cached source + pre-compiled deps, install state, env.sh / env.fish
rm -rf ~/.cache/horus/ # XDG package cache (horus pkg) — uninstall.sh does not remove this
rm -rf ~/.config/horus/ # auth.json — your registry credentials
rm -rf ~/.local/share/horus/ # recordings, blackbox, signing keys — uninstall.sh does not remove this
# macOS: ~/Library/Caches/horus, ~/Library/Application Support/horus
# Remove source code
rm -rf ~/horus/ # or wherever you cloned it
# Clean up shared memory (optional - HORUS auto-cleans sessions)
# Linux:
rm -rf /dev/shm/horus_*
# macOS:
rm -rf /tmp/horus_*
Windows (PowerShell):
HORUS ships a native PowerShell uninstaller — prefer it over deleting by hand:
.\uninstall.ps1
.\uninstall.ps1 -DryRun # list what would be removed, remove nothing
.\uninstall.ps1 -Yes # unattended
Piping to iex runs the script in a child scope where -Yes never arrives, so
for the unattended form fetch it into a scriptblock instead:
& ([scriptblock]::Create((irm https://github.com/softmata/horus/raw/main/uninstall.ps1))) -Yes
To do it manually instead:
# Remove CLI tool
Remove-Item -Force "$env:USERPROFILE\.cargo\bin\horus.exe"
# Remove caches, config and credentials
Remove-Item -Recurse -Force "$env:USERPROFILE\.horus"
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\horus"
Remove-Item -Recurse -Force "$env:APPDATA\horus" # auth.json
# Remove source code
Remove-Item -Recurse -Force "$env:USERPROFILE\horus" # or wherever you cloned it
# Clean up shared memory (optional - HORUS auto-cleans sessions)
Remove-Item -Recurse -Force "$env:TEMP\horus_*"
Troubleshooting
Having installation issues? Clear the caches and reinstall:
rm -rf ~/.horus/cache # installer's source cache (horus@<version>) and pre-compiled deps
rm -rf ~/.cache/horus # XDG cache root — searched first by `horus run`
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | bash
If that reinstalls the same broken thing, the problem is the release, not the cache. Pin a known-good one instead of repeating the step:
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | HORUS_VERSION=v0.4.0 bash
or compile the same tag locally, which also side-steps a glibc floor your distribution does not meet:
curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | HORUS_BUILD_FROM_SOURCE=1 bash
Common issues:
- Missing packages: Install all required system dependencies (see above)
GLIBC_2.xx' not found: the release binary needs a newer glibc than this machine has — useHORUS_BUILD_FROM_SOURCE=1- Raspberry Pi: Ensure GPIO/I2C packages are installed
- Jetson Nano: Ensure CUDA/JetPack packages are installed
See the Troubleshooting & Maintenance Guide for:
- Common installation errors and detailed fixes
- System dependency issues
- Platform-specific problems
- 15+ solved issues with step-by-step solutions
Next Steps
With HORUS installed, proceed to one of the following:
- Quick Start Tutorial - Build your first HORUS application
- CLI Reference - Complete command documentation
- Examples - Sample HORUS applications
Recommended: Start with the Quick Start Tutorial.