CLI Reference
The horus command gives you everything you need to build, run, and manage your applications.
Quick Reference
# Project Management
horus init # Initialize workspace in current directory
horus new <name> # Create a new project
horus run [files...] # Build and run your app
horus build [files...] # Build without running
horus test [filter] # Run tests
horus check [path] # Validate horus.toml and workspace
horus clean # Clean build artifacts and shared memory
horus lock # Pin every dependency version in horus.lock
horus scripts [name] # Run a script from [scripts] in horus.toml
# Monitoring & Debugging
horus monitor [port] # Monitor your system (web or TUI)
horus topic <command> # Topic introspection (list, echo, info, hz, bw, pub)
horus node <command> # Node management (list, info, kill, restart, pause, resume)
horus log [node] # View and filter logs
horus blackbox # Inspect BlackBox flight recorder (alias: bb)
# Coordinate Frames
horus frame <command> # HORUS Frames (list, echo, tree, info, can, hz,
# record, play, diff, tune, calibrate, hand-eye)
# Package Management
horus add <name> # Add a dependency to horus.toml (this project)
horus install <name> # Install a package, driver, or plugin (smart detection)
horus remove <name> # Remove a dependency from horus.toml
horus uninstall <name> # Uninstall an installed package or plugin
horus list # List installed packages and plugins
horus info <name> # Show details about a package or plugin
horus update [package] # Update packages
horus publish # Publish current package to registry
horus unpublish <name> # Unpublish a version (name@version syntax)
horus search <query> # Search for available packages/plugins
horus cache <command> # Cache management (info, list, clean, purge)
# Parameters & Messages
horus param <command> # Parameter management (get, set, list, delete, reset, dump, load, save)
horus msg <command> # Message types (list, info, hash) and codegen (gen)
# Advanced
horus launch <file> # Launch multiple nodes from YAML
horus deploy [target] # Deploy to remote robot
horus record <command> # Record/replay for debugging and testing
# Code Quality
horus fmt # Format Rust and Python sources
horus lint # Lint (clippy + ruff/pylint)
# Maintenance
horus doctor # Check the toolchain and environment
horus setup-rt # Configure the kernel and limits for real-time
horus self update # Update the horus CLI itself
horus man # Write the man page to stdout
# Environment & Auth
horus env --init # Set up shell integration (cargo/pip/cmake)
horus auth <command> # Login to registry
horus init - Initialize Workspace
What it does: Initializes a HORUS workspace in the current directory, creating the necessary configuration files.
Why it's useful: Quickly set up an existing directory as a HORUS project without creating new files from templates.
Basic Usage
# Initialize in current directory (uses directory name)
horus init
# Initialize with custom name
horus init --name my_robot
Options
horus init [OPTIONS]
Options:
-n, --name <NAME> Workspace name (defaults to directory name)
Examples
Initialize existing code as HORUS project:
cd ~/my-robot-code
horus init
# Creates horus.toml with project configuration
Initialize with specific name:
horus init --name sensor_array
What Gets Created
Running horus init creates:
horus.toml- Project manifest with a[package]table (name and version).horus/- Build environment directory
This is useful when you have existing code and want to add HORUS support, or when setting up a workspace that will contain multiple HORUS projects.
horus new - Create Projects
What it does: Creates a new HORUS project with all the boilerplate set up for you.
Why it's useful: Minimal configuration required. Select a language and begin development.
Basic Usage
# Interactive mode (asks you questions)
horus new my_project
# Rust with node! macro (recommended for reduced boilerplate)
horus new my_project --macro
# Python project
horus new my_project --python
Options
horus new <NAME> [OPTIONS]
Options:
-m, --macro Rust with node! macro (less boilerplate)
--rust Plain Rust project
--python Python project
--cpp C++ project
--from <EXAMPLE> Start from a shipped example instead of the blank
template (e.g. --from differential_drive); the
language comes from the example, so the language
flags do not apply
-w, --workspace Create as a workspace with multiple crates
-l, --lib Create as a library crate (instead of binary)
-o, --output <PATH> Where to create it (default: current directory)
-y, --yes Accept defaults without prompting
Without a language flag, horus new asks two interactive questions. Pass -y to take
the defaults instead — that is what makes the command usable in scripts, Dockerfiles and
CI.
The short forms -r, -p and -c are still accepted here, but they are deprecated
and stop being accepted in HORUS 0.4.0 — on horus run, horus build and horus test
the same three letters mean --release, --package and --clean, and nothing errors
when the habit crosses over. Use the long form.
Pass a --from name that does not exist to see the list of shipped examples.
Examples
Start with Rust + macros (easiest):
horus new temperature_monitor --macro
cd temperature_monitor
horus run
Python for prototyping:
horus new sensor_test --python
cd sensor_test
python main.py
Put it somewhere specific:
horus new robot_controller --output ~/projects/robots
horus run - Build and Run
What it does: Compiles your code and runs it. Handles all the build tools for you.
Why it's useful: One command works for Rust and Python. For Rust, it auto-generates Cargo.toml from horus.toml and uses Cargo for compilation. For Python, it handles the appropriate tooling.
Basic Usage
# Run current directory (finds main.rs or main.py)
horus run
# Run specific file
horus run src/controller.rs
# Run optimized (release mode)
horus run --release
Options
horus run [FILES...] [OPTIONS] [-- ARGS]
Options:
-r, --release Optimize for speed (recommended for benchmarks)
-c, --clean Remove cached build artifacts and dependencies
(Use after updating HORUS or when compilation fails)
-q, --quiet Suppress progress indicators
-v, --verbose Increase output verbosity (show debug messages)
-d, --drivers <LIST> Override detected drivers (comma-separated)
Example: --drivers camera,lidar,imu
-e, --enable <LIST> Enable capabilities (comma-separated)
Example: --enable cuda,editor,python
-p, --package <NAME> Run a specific workspace member by name
--sim [<DRIVERS>...] Run in simulation mode using horus-sim3d.
With no arguments, simulates every [hardware] entry
marked `sim = true`; with arguments, simulates only
the named ones.
--net Enable LAN network replication (transparent
cross-machine topics over UDP multicast)
--record <NAME> Enable recording for this session
--json Output run results as JSON (for CI/AI tooling)
--json-diagnostics Output build diagnostics as JSON lines
--no-hooks Skip [hooks] execution
-h, --help Print help
-V, --version Print version
-- <ARGS> Arguments for your program
Simulating hardware with --sim
--sim swaps a driver for a stub. Which drivers it swaps is decided by the sim = true
flag on the [hardware] entry itself — nothing else:
[hardware.lidar]
use = "rplidar"
sim = true # horus run --sim replaces this one with a stub
[hardware.motors]
use = "roboclaw" # no sim flag — stays on the real backend under --sim
horus run --sim # every entry with sim = true
horus run --sim lidar # only lidar, even if others are marked
Naming a driver that is not marked sim = true does nothing: the argument list narrows
the simulated set, it does not create it. So horus run --sim motors above still drives
real motors.
An older form put the simulated backend in a separate [sim-drivers] table. That table
still parses, and is then read by nothing — a project relying on it has silently lost its
simulation override and runs its real hardware under --sim. horus check reports it as
a warning naming the replacement. See
[drivers] and [sim-drivers].
Using --enable for Capabilities
The --enable flag lets you quickly enable features without editing horus.toml:
# Enable CUDA GPU acceleration
horus run --enable cuda
# Enable multiple capabilities
horus run --enable cuda,editor,python
Available capabilities:
| Capability | Description |
|---|---|
cuda, gpu | CUDA GPU acceleration |
editor | Scene editor UI |
visual | Visualization support |
python, py | Python bindings |
headless | No rendering (for training) |
opencv | OpenCV backend |
io-uring | io_uring networking backend |
ultra-low-latency | Ultra-low-latency tuning |
sim, simulation | Simulation mode (no hardware features) |
net, network | LAN replication — same as horus run --net |
full | All features |
Anything not in this list is passed straight through to cargo --features, so an
unrecognised name fails the build. net/network and sim/simulation are recognised
names that add no feature to your own crate — net is applied to the horus dependency
in the generated manifest instead, which is why horus run --enable net works. Hardware backends are not capabilities —
select those with -d/--drivers:
horus run -d camera,lidar,imu --release
Or configure in horus.toml:
enable = ["cuda", "editor"]
--enable sim is not --sim. The sim capability only tells the build to
leave hardware features off; it adds no Cargo features of its own. The separate
horus run --sim flag is what actually runs your drivers against horus-sim3d.
Why --release Matters
Debug builds have significantly higher overhead than release builds due to runtime checks and lack of optimizations.
Debug mode (default): Fast compilation, slower execution
- Use case: Development iteration
- Includes overflow checks, bounds checking, assertions
Release mode (--release): Slower compilation, optimized execution
- Use case: Performance testing, benchmarks, production deployment
- Full compiler optimizations enabled
Common Mistake: measuring performance from a debug build. Debug builds carry
overflow and bounds checks and are typically one to two orders of magnitude slower
per tick, so a debug run looks slow even when the system is healthy. Always use
--release before you draw any conclusion from a timing number.
For real, measured IPC latencies, see the benchmark tables in the HORUS repository's
benchmarks/README.md rather than numbers printed by a debug run.
Rule of thumb: Always use --release when:
- Measuring performance
- Running benchmarks
- Testing real-time control loops
- Deploying to production
- Wondering "why is HORUS slow?"
Why --clean Matters
The --clean flag throws away cached build artifacts so the next build starts fresh.
When to use --clean:
-
After updating HORUS - Most common use case
# You updated horus CLI to a new version horus run --cleanThis fixes version mismatch errors like:
error: the package `horus` depends on `horus_core 0.1.0`, but `horus_core 0.1.3` is installed -
Compilation fails mysteriously
horus run --clean # Sometimes cached state gets corrupted -
Dependencies changed
# You modified horus.toml dependencies horus run --clean
What it does:
- Empties
.horus/cache/and.horus/bin/(compiled binaries) - Removes the project-root
target/and__pycache__/directories - For projects with a root
Cargo.toml, also runscargo cleanagainst.horus/target/ - Next build rebuilds everything from scratch
Trade-off:
- First build after
--cleanis slower (5-30 seconds) - Subsequent builds are fast again (incremental compilation)
Note: The --clean flag only affects the current project, not the global HORUS package cache. Use horus clean --all to clear that too.
Examples
Daily development:
horus run
# Fast iteration, slower execution
Testing performance:
horus run --release
# See real speed
Build for CI without running (use horus build):
horus build --release
Fresh build (when things act weird or after updating HORUS):
horus run --clean --release
After updating HORUS CLI (fixes version mismatch errors):
# Clean removes cached build artifacts and forces a fresh build
horus run --clean
Pass arguments to your program:
horus run -- --config robot.yaml --verbose
What horus run Detects
With no arguments, horus run figures out what to build:
- A single entrypoint —
main.rs,main.py, ormain.cpp, at the project root or undersrc/ - A glob of files (see Concurrent Multi-Process Execution below)
- A workspace, declared with a
[workspace]section inhorus.toml— it generates a workspaceCargo.tomlplus per-member manifests and builds withcargo build --workspace - A project with its own root
Cargo.toml, built directly from it withCARGO_TARGET_DIR=.horus/target
Example of a single-file structure:
// main.rs - everything in one file
use horus::prelude::*;
struct SensorNode { /* ... */ }
impl Node for SensorNode { /* ... */ }
struct ControlNode { /* ... */ }
impl Node for ControlNode { /* ... */ }
fn main() -> Result<()> {
let mut scheduler = Scheduler::new();
scheduler.add(SensorNode::new()?).order(0).done();
scheduler.add(ControlNode::new()?).order(1).done();
scheduler.run()
}
Multi-crate workspaces:
# Scaffold a workspace
horus new my_robot --workspace
# Build every member (builds only — nothing is executed)
horus run
# Build and run a single member
horus run -p sensor_driver
# Build a single member without running it
horus build -p sensor_driver
Concurrent Multi-Process Execution
HORUS supports running multiple node files concurrently as separate processes using glob patterns. This is ideal for distributed robotics systems where nodes need to run independently.
Basic Usage:
horus run "nodes/*.py" # Run all Python nodes concurrently
horus run "src/*.rs" # Run all Rust nodes concurrently
How it works:
- Phase 1 (Build): Builds all files sequentially, respecting Cargo's file lock
- Phase 2 (Execute): Spawns all processes concurrently with their own schedulers
- Each process communicates via HORUS shared memory IPC
Features:
- Color-coded output: Each node is prefixed with
[node_name]in a unique color - Graceful shutdown: Ctrl+C cleanly terminates all processes
- Multi-language: Works with Rust and Python
- Automatic detection: No flags needed, just use glob patterns
Example output:
$ horus run "nodes/*.py"
Executing 3 files concurrently:
1. nodes/sensor.py (python)
2. nodes/controller.py (python)
3. nodes/logger.py (python)
Phase 1: Building all files...
Phase 2: Starting all processes...
Started [sensor]
Started [controller]
Started [logger]
All processes running. Press Ctrl+C to stop.
[sensor] Sensor reading: 25.3°C
[controller] Motor speed: 45%
[logger] System operational
[sensor] Sensor reading: 26.1°C
[controller] Motor speed: 50%
[logger] System operational
When to use concurrent execution:
- Multi-node systems where each node is in a separate file
- Distributed control architectures (similar to ROS nodes)
- Testing multiple nodes simultaneously
- Microservices-style robotics architectures
When to use single-process execution:
- All nodes in one file (typical for simple projects)
- Projects requiring deterministic scheduling across all nodes
- Maximum performance with minimal overhead
Important: Each process runs its own scheduler. Nodes communicate through HORUS shared memory topics (/dev/shm/horus_<namespace>/), not direct function calls.
horus check - Validate Project
What it does: Validates horus.toml, source files, and the workspace configuration.
Why it's useful: Quickly diagnose configuration issues, missing dependencies, or environment problems before building.
check reads the repository; horus doctor
reads the machine it is on — toolchains, real-time capability, shared memory. Neither
substitutes for the other: a project can be valid on a machine that cannot build it, and
a healthy machine says nothing about whether the manifest parses. horus check --health
runs the machine side without leaving check.
Basic Usage
# Check current directory
horus check
# Check a specific path
horus check path/to/project
# Quiet mode (errors only)
horus check --quiet
Options
horus check [OPTIONS] [PATH]
Arguments:
[PATH] Path to file, directory, or workspace (default: current directory)
Options:
--full Run full validation (manifest + doctor + fmt + lint + deps)
--health Check the machine instead of the project - same as `horus doctor`
--json Output as JSON
-q, --quiet Only show errors, suppress warnings
-v, --verbose Increase output verbosity (show debug messages)
-h, --help Print help
-V, --version Print version
Examples
Validate before building:
horus check
# Validates horus.toml, dependencies, and environment
CI/CD validation:
#!/bin/bash
if ! horus check --quiet; then
echo "Validation failed"
exit 1
fi
horus build --release
horus monitor - Monitor Everything
Alias: horus mon
What it does: Opens a visual monitor showing all your running nodes, messages, and performance.
Why it's useful: Debug problems visually. See message flow in real-time. Monitor performance.
Prerequisite:
horus monitoris provided by thehorus-monitorplugin. Install it once withhorus install horus-monitor; all arguments are forwarded to the plugin. Without it the command exits with "The monitor plugin is not installed."
Basic Usage
# Web monitor (opens in browser)
horus monitor
# Different port
horus monitor 8080
# Text-based (for SSH)
horus monitor --tui
# Reset monitor password before starting
horus monitor --reset-password
What You See
The monitor shows:
- All running nodes - Names, status, tick rates
- Message flow - What's talking to what
- Performance - CPU, memory, latency per node
- Topics - All active communication channels
- Graph view - Visual network of your system
Examples
Start monitoring (in a second terminal):
# Terminal 1: Run your app
horus run --release
# Terminal 2: Watch it
horus monitor
Access from your phone:
horus monitor
# Visit http://your-computer-ip:3000 from phone
Monitor over SSH:
ssh robot@192.168.1.100
horus monitor --tui
See Monitor Guide for detailed features.
Package Management
What it does: Install and manage reusable components.
Why it's useful: Don't reinvent the wheel. Use components others have built and tested.
This section is the overview. horus install, horus remove, and horus uninstall
each get a full option list further down the page.
Commands
# Install a package
horus install <package>
# Remove a dependency from horus.toml
horus remove <name>
# Uninstall an installed package or plugin
horus uninstall <name>
# List installed packages and plugins
horus list
# Update packages (all dependencies if no name is given)
horus update [package]
# Publish current package to registry
horus publish
# Unpublish a package from registry
horus unpublish <package>@<version>
# Generate signing key pair
horus auth signing-key
Examples
Install a package:
horus install pid-controller
Install specific version:
horus install pid-controller@1.2.0
Install into a particular workspace (horus install is global by default — it does not look at the directory you are standing in and never prompts; pass -t <workspace-name> to install into a registered workspace instead):
horus install common-utils --target my_robot
See what's installed:
horus list
Search for packages:
horus search sensor
Drop a dependency (edits horus.toml):
horus remove pid-controller
Uninstall an installed package:
horus uninstall pid-controller
Publish your package to registry:
# First login
horus auth login
# Then publish from your project directory
horus publish
Unpublish from registry (irreversible!):
horus unpublish my-package@1.0.0
horus list - What Is Installed
horus list # packages and plugins visible from here
horus list --global # global scope only
horus list --all # local + global
horus list --json # machine-readable
Lists installed packages and plugins — the things horus install put on this
machine. It is not the runtime list you get from horus topic list,
horus node list, horus param list or horus msg list, which ask a running
system what it currently has. If nothing is running, those return nothing while
horus list still prints your packages.
| Flag | What it does |
|---|---|
-g, --global | Global scope packages only |
-a, --all | Local and global together |
--json | JSON output |
horus info - Details About One Package
horus info rplidar
horus info rplidar --json
Version, description, owner and dependency information for a package or plugin, whether or not it is installed.
horus search - Find a Package
Alias: horus s
horus search lidar
horus search motor --category motor
horus search camera --json
| Flag | What it does |
|---|---|
-c, --category <CATEGORY> | camera, lidar, imu, motor, servo, bus, gps, simulation, cli |
--json | JSON output |
horus update - Move Dependencies Forward
horus update # every dependency in horus.toml
horus update rplidar # just this one
horus update --dry-run # show what would change, write nothing
horus update --global # global scope packages
-n is the short form of --dry-run, the same as on horus publish,
horus clean, horus deploy, horus launch and horus migrate.
| Flag | What it does |
|---|---|
-n, --dry-run | Report the changes without applying them |
-g, --global | Update global scope packages |
horus publish - Send a Release to the Registry
horus auth login # once
horus publish --dry-run # validate everything without uploading
horus publish
Packages the current project and uploads it. Run it from the project directory —
the manifest it reads is the horus.toml next to you.
publish here means publish a package. It is unrelated to the publish() call
in the Rust, C++ and Python APIs, which sends one message on a topic; the CLI
equivalent of that is horus topic pub.
| Flag | What it does |
|---|---|
-n, --dry-run | Validate and package without uploading |
horus unpublish - Take a Version Down
horus unpublish my-package@1.0.0
horus unpublish my-package@1.0.0 --yes # skip the confirmation
Irreversible, and it breaks anyone who depends on that exact version. Prefer
horus yank, which hides a
version from new installs while existing lockfiles keep resolving.
| Flag | What it does |
|---|---|
-y, --yes | Skip the confirmation prompt |
Registry Lifecycle
Once a package is published, these commands manage its life without deleting it. Removal is deliberately not an option — anything already in someone's lockfile keeps resolving.
horus yank / horus unyank - Hide a Bad Version
horus yank my-pkg@0.2.1 --reason "panics on empty scan"
horus unyank my-pkg@0.2.1
Yanking hides a version from new installs. Builds that already pin it in a lockfile
continue to work, which is the point: you stop the bleeding for new users without
breaking robots in the field. Both take <name>@<version> — the version is required.
| Flag | What it does |
|---|---|
--reason <REASON> | Recorded with the yank, shown to anyone who tries to install it |
horus deprecate / horus undeprecate - Signpost a Successor
horus deprecate old-lidar -m "moved to @horus/lidar-unified"
horus undeprecate old-lidar
Deprecation applies to the package, not a version, and nothing stops installing or resolving. It is a message to humans — use it when the package still works but should not be picked for new projects.
| Flag | What it does |
|---|---|
-m, --message <MESSAGE> | Where users should go instead. Say the replacement by name. |
horus owner - Ownership
horus owner list my-pkg
horus owner add my-pkg alice
horus owner remove my-pkg bob
horus owner transfer my-pkg new-maintainer
horus owner pending # transfers waiting on you
horus owner accept 7f3a1c2e # transfer ID from `horus owner pending`
horus owner reject 7f3a1c2e # transfer ID from `horus owner pending`
Transfers are two-sided: transfer proposes, and the recipient must accept. Until
then it sits in their pending list, so a package cannot be pushed onto someone who
does not want it.
horus fmt - Format Code
Formats every language in the project with its own formatter — rustfmt for
Rust, ruff/black for Python — so a mixed-language project takes one command
instead of three.
horus fmt # format in place
horus fmt --check # report what would change, exit non-zero if any
horus fmt -- --edition 2021 # pass arguments through to the underlying tool
| Flag | What it does |
|---|---|
--check | Reports without writing. Use this one in CI. |
-- <args> | Everything after -- is forwarded to the underlying formatter |
horus lint - Lint Code
Runs clippy for Rust and ruff (or pylint) for Python.
horus lint # report
horus lint --fix # apply the fixes that are safe to apply
horus lint --types # also run the Python type checker (mypy/pyright)
| Flag | What it does |
|---|---|
--fix | Applies the auto-fixable subset |
--types | Adds mypy/pyright over the Python sources |
-- <args> | Forwarded to the underlying linter |
Both commands are accepted as built-ins in the [hooks] table, so a project can
run them automatically — see Configuration:
[hooks]
pre_test = ["fmt", "lint"]
horus config - Read and Write horus.toml
horus config list # every value in the manifest
horus config get package.version
horus config set package.version 0.3.0
Edits the manifest in place using dotted paths, which is what you want from a script or a release job — no TOML parsing in your shell.
horus schema - Editor Support for horus.toml
horus schema > horus-schema.json
horus schema -o horus-schema.json
Prints the JSON Schema for horus.toml. Point an editor at it and you get autocomplete,
hover documentation and inline errors on the manifest instead of discovering a typo at
build time. In VS Code with Even Better TOML, or any taplo-based setup:
[[schema]]
path = "horus-schema.json"
include = ["**/horus.toml"]
| Flag | What it does |
|---|---|
-o, --output <OUTPUT> | Write to a file instead of stdout |
horus completion - Shell Completions
horus completion bash > /etc/bash_completion.d/horus
horus completion zsh > "${fpath[1]}/_horus"
horus completion fish > ~/.config/fish/completions/horus.fish
Supported shells: bash, zsh, fish, elvish, powershell.
horus deps - Dependency Insight
horus deps tree # full dependency graph
horus deps why serde # what pulled this in
horus deps outdated # what has newer versions
horus deps audit # known security advisories
why is the one you reach for when something appeared in your build and you did not put
it there. audit is worth wiring into CI.
horus doc - Generate and Extract API Documentation
horus doc --open # build docs and open them
horus doc --extract --md # machine-readable, for LLM context
horus doc --extract --json -o api.json # structured API dump
horus doc --coverage # how much of the API is documented
horus doc --watch # regenerate whenever a source file changes
horus doc --diff baseline.json # what changed against a baseline
Beyond generating browsable docs, --extract dumps the API in a machine-readable form.
--diff against a stored baseline is how you catch an unintended breaking change in
review.
| Flag | What it does |
|---|---|
--open | Open in a browser after generating |
--extract | Emit machine-readable API documentation |
--json / --md / --html | Output format for --extract |
--brief / --full | One line per symbol; --full adds doc comments |
--all | Include private and crate-only symbols |
--lang <LANG> | Restrict to rust, cpp or python |
--coverage | Report documentation coverage |
--diff <BASELINE> | Compare against a baseline JSON file |
-o, --output <OUTPUT> | Write to a file instead of stdout |
horus bench - Run Benchmarks
horus bench # every benchmark
horus bench topic # only those matching "topic"
horus bench -- --save-baseline main
Anything after -- is forwarded to the underlying benchmark harness.
horus migrate - Import Dependencies into horus.toml
horus migrate --dry-run # show what would change
horus migrate
Reads Cargo.toml, pyproject.toml and CMakeLists.txt next to your manifest and
folds the dependencies it finds into horus.toml, so a project that predates HORUS
does not have to be re-listed by hand.
This requires an existing horus.toml and will exit if there is none — run
horus init first. It is not a converter for the horus.yaml used by HORUS 0.1.x;
no such converter ships today, and a 0.1.x project must have its manifest rewritten by
hand.
| Flag | What it does |
|---|---|
-n, --dry-run | Print the changes without writing them |
-f, --force | Skip confirmation prompts |
horus doctor - Environment Health Check
What it does: Checks the toolchains, system dependencies and real-time readiness of the machine you are on.
Why it's useful: Answers "is this machine set up to build and run HORUS" before you spend time debugging a build.
horus doctor # Check toolchains and environment
horus doctor --fix # Install missing toolchains and system dependencies
horus doctor --rt # RT readiness: system audit + jitter benchmark + IPC benchmark
horus doctor --json # Machine-readable output
horus doctor -v # Detailed output for each check
--rt is the one to run on a robot before trusting a control loop: it audits the
real-time configuration and then measures actual jitter and IPC latency, rather than only
reporting what the system claims to support.
Toolchains are graded per language, and each language is graded against the tools the
HORUS commands actually invoke. For C++ that means cmake and a compiler (g++,
clang++ or MSVC) and a build program (make or ninja): horus build runs
cmake --build, and cmake on its own configures a build without being able to perform
one. For Rust it includes clippy and rustfmt, which horus lint and horus fmt need
and which a minimal-profile rustup does not install.
A language that is missing a required tool is reported as blocked, one missing only an optional tool as degraded:
! Toolchains — Python ready · Rust missing clippy, rustfmt · C++ needs a C++ compiler, make or ninja
Either way it is a warning, not a pass — horus doctor exits 1 when any check warns and
2 when any check fails, so an incomplete language never renders as a green tick next to
a healthy one. Run horus doctor -v to see every tool, its version, and the command that
installs the ones that are missing:
a C++ compiler: not found (g++, clang++ or MSVC — cmake picks one) - install: sudo apt install build-essential
There is no per-language filter (--for cpp and similar do not exist); -v plus the
summary line is the whole surface.
horus setup-rt - Real-Time Setup
Configures the kernel and system limits HORUS needs for low-jitter scheduling.
horus doctor points here when it finds a standard kernel:
! Real-Time — Standard kernel, jitter ±100μs (run `horus setup-rt` for ±20μs)
horus setup-rt --check # Report current RT status, change nothing
horus setup-rt # Apply the configuration
horus setup-rt --undo # Remove the limits file
| Flag | What it does |
|---|---|
--check | Reports what is and is not configured, and exits without changing anything |
--undo | Removes the limits file HORUS installed |
This needs root and may install a kernel package. Both of the changes it makes are prompted, and both default to no:
| What it changes | When | Prompt |
|---|---|---|
/etc/security/limits.d/99-horus-rt.conf — memlock unlimited and rtprio 99, scoped to the user that ran the command, never * | Always, unless the file already exists | Asks |
| The distribution's real-time kernel package | Only when the running kernel is not already PREEMPT_RT | Asks |
The kernel package is chosen from the distribution in /etc/os-release. The
names differ, and --undo prints the matching removal command for the same
distribution:
Distro (ID=) | Installs | Removes with |
|---|---|---|
ubuntu | linux-image-realtime | sudo apt remove linux-image-realtime |
debian | linux-image-rt-amd64 (x86_64), linux-image-rt-arm64 (aarch64) | sudo apt remove linux-image-rt-amd64 |
fedora | kernel-rt kernel-rt-core | sudo dnf remove kernel-rt kernel-rt-core |
arch | linux-rt linux-rt-headers | sudo pacman -R linux-rt linux-rt-headers |
Anything else prints the four commands above and changes nothing. A kernel
install that fails is reported as a failure — setup-rt will not tell you to
reboot into a kernel it did not install.
Declining the kernel prompt aborts the run with a non-zero exit; declining the limits prompt prints the two lines to add by hand and carries on.
Non-interactive runs decline. Without a terminal on stdin — CI, a
provisioning script, a pipe — every prompt answers no and nothing is installed.
Set HORUS_ASSUME_YES=1 to accept both without asking; horus setup-rt has no
-y flag of its own.
--undo removes only the limits file, and only says it did when the rm
succeeded. The kernel package and any isolcpus boot parameter have to be
undone by hand — it prints the commands.
Start with --check if you want to see the plan first.
Without it, HORUS still runs: you get a standard kernel's jitter (roughly
±100 μs rather than ±20 μs) and memory locking may be unavailable, which
.no_alloc() nodes will report.
horus env - Shell Integration
What it does: Defines shell functions that shadow six native tools —
cargo, pip, pip3, cmake, conan and vcpkg — so that inside a HORUS
project they delegate to horus <tool>, which keeps horus.toml in sync with
what you install. Outside a project they pass straight through to the real tool.
Why it's useful: Lets you keep using the toolchain commands you already know
without wiring up paths by hand, and keeps the manifest honest when you
cargo add something.
What it changes on your machine
horus env --init is not read-only. It writes:
| Path | Contents |
|---|---|
~/.horus/env.sh | The shell functions, for bash and zsh |
~/.horus/env.fish | The same, for fish |
~/.bashrc, ~/.zshrc | A two-line block sourcing env.sh |
~/.config/fish/conf.d/horus.fish | The fish equivalent |
The installer runs it for you. To install without it:
HORUS_NO_SHELL_INTEGRATION=1 curl -fsSL https://github.com/softmata/horus/raw/main/install.sh | bash
horus env --uninstall removes all of the above, including the lines it added
to your RC files.
Options
# Write shell integration files and add them to your shell RC
horus env --init
# Remove shell integration from your shell RC files
horus env --uninstall
Examples
Enable shell integration:
horus env --init
# Then restart your shell (or source your RC file)
Disable shell integration:
horus env --uninstall
Note:
horus envdoes not snapshot, save, or restore package environments. To pin exact dependency versions for reproducible builds, usehorus lock.
horus auth - Login to Registry
What it does: Authenticate so you can publish packages.
Why it's useful: Secure access to the package registry.
Commands
# Login with GitHub
horus auth login
# Generate API key (for CI/CD)
horus auth api-key
# Generate an ed25519 signing key pair for signing published packages
horus auth signing-key
# Trust a publisher's public key for signature verification
horus auth trust-publisher <name> <path-to-.pub-or-64-char-hex-key>
# List trusted publisher keys
horus auth publishers
# Check who you are
horus auth whoami
# Logout
horus auth logout
# Manage API keys
horus auth keys list
horus auth keys revoke <key_id>
Examples
First time setup:
horus auth login
# Opens browser for GitHub login
Check you're logged in:
horus auth whoami
Generate API key for CI/CD:
horus auth api-key --name github-actions --environment ci-cd
# Save the generated key in your CI secrets
Logout:
horus auth logout
horus build - Build Without Running
What it does: Compiles your project without executing it.
Why it's useful: Validate compilation, prepare for deployment, or integrate with CI/CD pipelines.
Basic Usage
# Build current project
horus build
# Build in release mode
horus build --release
# Clean build (remove cached artifacts first)
horus build --clean
Options
horus build [FILES...] [OPTIONS]
Options:
-r, --release Build in release mode (optimized)
-c, --clean Clean before building
-q, --quiet Suppress progress indicators
-d, --drivers <LIST> Override detected drivers (comma-separated)
-e, --enable <LIST> Enable capabilities (comma-separated)
-p, --package <NAME> Build a specific workspace member by name
-h, --help Print help
Examples
CI/CD build validation:
horus build --release
# Exit code 0 = success, non-zero = failure
Clean release build for deployment:
horus build --clean --release
horus test - Run Tests
What it does: Runs your HORUS project's test suite.
Why it's useful: Validate functionality, run integration tests with simulation, and ensure code quality.
Basic Usage
# Run all tests
horus test
# Run tests matching a filter
horus test my_node
# Run with parallel execution
horus test --parallel
# Run simulation tests
horus test --sim
Options
horus test [OPTIONS] [FILTER]
Arguments:
[FILTER] Test name filter (runs tests matching this string)
Options:
-r, --release Run tests in release mode
--parallel Allow parallel test execution
--sim Enable simulation mode (no hardware required)
--integration Run integration tests (tests marked #[ignore])
--nocapture Show test output
-j, --test-threads <N> Number of test threads (default: 1)
--no-build Skip the build step
--json Output test results as JSON (for CI tooling)
--no-hooks Skip [hooks] execution
--verbose Verbose output
-d, --drivers <LIST> Override detected drivers (comma-separated)
-e, --enable <LIST> Enable capabilities (comma-separated)
-h, --help Print help
Examples
Run specific tests:
horus test sensor_node --nocapture
Fast parallel test run:
horus test --parallel --release
Integration tests with simulator:
horus test --integration --sim
horus clean - Clean Build Artifacts
What it does: Removes build artifacts, cached dependencies, and shared memory files.
Why it's useful: Fix corrupted builds, reclaim disk space, or reset shared memory after crashes.
Basic Usage
# Clean everything (build + shared memory + cache)
horus clean --all
# Only clean shared memory
horus clean --shm
# Preview what would be cleaned
horus clean --dry-run
Options
horus clean [OPTIONS]
Options:
--shm Only clean shared memory
-a, --all Clean everything (build cache + shared memory + horus cache)
-n, --dry-run Show what would be cleaned without removing anything
-f, --force Force clean even if HORUS processes are running
--all-namespaces Also remove live namespaces belonging to other processes
--json Output as JSON
-h, --help Print help
-V, --version Print version
Examples
After a crash (clean stale shared memory):
horus clean --shm
Full reset before deployment:
horus clean --all
horus build --release
horus topic - Topic Introspection
Alias: horus t
What it does: Inspect, monitor, and interact with HORUS topics (shared memory communication channels).
Why it's useful: Debug message flow, verify data publishing, and measure topic rates.
Subcommands
horus topic list # List all active topics
horus topic echo <topic> # Print messages as they arrive
horus topic info <topic> # Show topic details (type, publishers, subscribers)
horus topic hz <topic> # Measure publishing rate (-w/--window N, default 10)
horus topic bw <topic> # Measure bandwidth (bytes/sec; -w/--window N)
horus topic pub <topic> <msg> # Publish a message
Examples
List all topics:
horus topic list
# Output:
# Active Topics:
#
# NAME SIZE MSGS RATE STATUS
# --------------------------------------------------------------------------
# /cmd_vel 16 B 1240 50.0 Hz active
# /scan 1.4 KB 248 10.0 Hz active
# /odom 736 B 620 25.0 Hz active
#
# Total: 3 topic(s)
Add --verbose to print each topic's message type, publishers, and subscribers
instead of the compact table.
Monitor a topic in real-time:
horus topic echo /scan
# Prints each LaserScan message as it arrives
Check publishing rate:
horus topic hz /cmd_vel
# Output: average rate: 50.00 Hz (window: 10)
Publish test message:
horus topic pub /cmd_vel '{"timestamp_ns": 0, "linear": 1.0, "angular": 0.5}'
horus topic pub sends the JSON you hand it as-is — it does not validate the
payload against a message type, so the field names have to match what the
subscriber expects. The fields above are CmdVel's: timestamp_ns: u64,
linear: f32, angular: f32.
Note:
CmdVel,LaserScanandOdometrycome from thehorus-roboticspackage rather than HORUS's ownhorus_types, buthorus msg listscans both, so they appear in its output alongside the built-ins andhorus msg info CmdVelworks as expected.
horus service - Service Introspection
Alias: horus srv
What it does: List, inspect and call request/response services.
Why it's useful: Exercise a service from the terminal without writing a client.
Subcommands
horus service list # List all active services
horus service find <filter> # Find services whose name contains <filter>
horus service info <name> # Show request/response type info
horus service call <name> <json> # Call a service with a JSON request
Examples
horus service call add '{"a": 3, "b": 4}'
horus service call slow_op '{}' --timeout 30 # default is 5s
The CLI delivers requests through a JSON file gateway under
/dev/shm/horus_default/topics/.service_gateway/, and only the Rust ServiceServer
polls it. horus service list will discover a C++ service — it finds them by scanning
shared memory for the .request topic — but horus service call cannot invoke one. Use
a ServiceClient for those.
horus action - Action Introspection
Alias: horus a
What it does: List and inspect long-running actions, send goals, and cancel them.
Why it's useful: Drive a goal-based behaviour by hand while debugging.
Subcommands
horus action list # List all active actions
horus action info <name> # Show detailed info about an action
horus action send-goal <name> <json> # Send a goal (JSON)
horus action cancel-goal <name> # Cancel goals on an action server
Examples
# Send a goal and block until the result arrives
horus action send-goal navigate '{"x": 2.0, "y": 1.0}' --wait
# Cancel one goal, or every goal if --goal-id is omitted
horus action cancel-goal navigate --goal-id 7
horus action cancel-goal navigate
horus node - Node Management
Alias: horus n
What it does: List, inspect, and control running HORUS nodes.
Why it's useful: Debug node states, restart misbehaving nodes, or pause nodes during testing.
Subcommands
horus node list # List all running nodes
horus node info <node> # Show detailed node information
horus node kill <node> # Terminate a node
horus node restart <node> # Restart a node
horus node pause <node> # Pause a node's tick execution
horus node resume <node> # Resume a paused node
Examples
List all running nodes:
horus node list
# Output:
# NAME STATUS PRIORITY RATE TICKS SOURCE
# ---------------------------------------------------------------------------
# SensorNode Running 50 100 Hz 12043 12345
# ControllerNode Running 40 50 Hz 6021 12346
# LoggerNode Paused 0 10 Hz 1204 12347
SOURCE carries the PID for a local node, or a marker such as [bridged:…] /
[horus_net:…] for one reached through a bridge or the network. CPU and memory are not
in this table — horus node info <node> reports those.
Get detailed node info:
horus node info SensorNode
# Shows: tick count, error count, subscribed topics, published topics
Restart a stuck node:
horus node restart ControllerNode
Pause/resume for debugging:
horus node pause SensorNode
# ... inspect state ...
horus node resume SensorNode
horus log - View and Filter Logs
What it does: View, filter, and follow HORUS system logs.
Why it's useful: Debug issues, monitor specific nodes, and track errors in real-time.
Basic Usage
# View all recent logs
horus log
# Filter by node
horus log SensorNode
# Follow logs in real-time
horus log --follow
# Show only errors
horus log --level error
Options
horus log [OPTIONS] [NODE]
Arguments:
[NODE] Filter by node name
Options:
-l, --level <LEVEL> Filter by log level (trace, debug, info, warn, error)
-s, --since <SINCE> Show logs from last duration (e.g., "5m", "1h", "30s")
-f, --follow Follow log output in real-time
-n, --count <COUNT> Number of recent log entries to show
--clear Clear logs instead of viewing
--clear-all Clear all logs (including file-based logs)
-h, --help Print help
Logs are machine-wide, not project-scoped
There is no project scoping. Logs go into one shared-memory ring per namespace, at
/dev/shm/horus_<namespace>/logs, and the default namespace is default for every
process on the box — the same design as ROS 2's domain_id=0. So horus log in an empty
directory with no project in it still prints whatever ran last:
$ cd /tmp/empty && ls -a
. ..
$ horus log -n 1
13:01:22.299 PUB [pyattr_controller] uniq_livea_topic {"angular":0.0,"linear":1.0}
The banner it prints, (showing local + remote logs), means this machine plus peers
reached over horus_net — not this project.
To scope logs to one project, give it a namespace. HORUS_NAMESPACE isolates the
shared-memory tree — topics and the log ring live under /dev/shm/horus_<namespace>/ —
so it has to be set for both the run and the viewer. Parameters are not in that tree:
they are read from .horus/config/params.yaml relative to the working directory, and a
namespace does not scope them.
HORUS_NAMESPACE=my_robot horus run # in the project
HORUS_NAMESPACE=my_robot horus log # only that project's logs
Without it, treat the node name in each line as the only indication of which project the
entry came from — and check it before concluding anything from a log during incident
analysis. See HORUS_NAMESPACE for the full effect
of the setting.
Examples
Follow logs from a specific node:
horus log SensorNode --follow
View errors from last 10 minutes:
horus log --level error --since 10m
Show last 50 warnings and errors:
horus log --level warn --count 50
horus param - Parameter Management
Alias: horus p
What it does: Manage node parameters at runtime (get, set, list, dump, save, load).
Why it's useful: Tune robot behavior without recompiling, persist configurations, and debug parameter values.
Subcommands
horus param list # List all parameters
horus param get <key> # Get parameter value
horus param set <key> <value> # Set parameter value
horus param delete <key> # Delete a parameter
horus param reset # Reset all parameters to defaults
horus param load <file> # Load parameters from YAML file
horus param save [<file>] # Save parameters to YAML file (default: .horus/config/params.yaml)
horus param dump # Dump all parameters as YAML to stdout
Examples
List all parameters:
horus param list
# Output:
# Parameters:
# KEY VALUE TYPE
# ----------------------------------------------------------------
# /SensorNode/sample_rate 100 int
# /SensorNode/filter_size 5 int
# /ControllerNode/kp 1.5 float
# /ControllerNode/ki 0.1 float
#
# Total: 4 parameter(s)
# Location: Stored in: .horus/config/params.yaml
--verbose prints a Key: / Value: / Type: block per parameter instead of the
table, and --json prints JSON for scripting.
Tune a controller at runtime:
horus param set /ControllerNode/kp 2.0
horus param set /ControllerNode/ki 0.2
Save and restore configuration:
# Save current params
horus param save robot_config.yaml
# Later, restore them
horus param load robot_config.yaml
horus frame - HORUS Frames (Coordinate Transforms)
Alias: horus tf
What it does: Inspect and monitor coordinate frame transforms (similar to ROS tf).
Why it's useful: Debug transform chains, visualize frame relationships, and verify sensor mounting.
Subcommands
horus frame list # List all frames
horus frame echo <source> <target> # Echo transform between two frames
horus frame tree # Show frame tree hierarchy
horus frame info <frame> # Detailed frame information
horus frame can <source> <target> # Check if transform is possible
horus frame hz # Monitor frame update rates
# Recording and comparison
horus frame echo a b --once # Print one result and exit
horus frame record -o run.tfr # Record transforms to a .tfr file (-d/--duration secs)
horus frame play run.tfr # Replay a recording (--speed 2.0 to fast-forward)
horus frame diff a.tfr b.tfr # Compare two recordings
# --threshold-m / --threshold-deg set what counts
# as a difference (default 0.001 m)
# Calibration
horus frame tune <frame> # Interactively nudge a static frame's offset
# --step-m / --step-deg set the nudge size
horus frame calibrate --points-file pairs.csv # Sensor-to-base by SVD registration
horus frame hand-eye --robot-poses r.csv --sensor-poses s.csv # Solve AX=XB
Examples
View frame tree:
horus frame tree
# Output:
# TransformFrame Tree Structure:
#
# └── world (root)
# └── base_link (static)
# ├── camera_frame (static)
# ├── imu_frame (static)
# └── laser_frame (static)
#
# Total: 5 frames
Monitor a transform:
horus frame echo laser_frame world
# Prints: translation [x, y, z] rotation [qx, qy, qz, qw]
Check transform chain:
horus frame can laser_frame world
# Output:
# Checking transform from laser_frame to world
#
# Available: Yes
# Chain: laser_frame → base_link → world
horus msg - Message Types and Code Generation
Alias: horus m
What it does: Inspect HORUS message type definitions and schemas.
Why it's useful: Understand message structures, debug serialization issues, and verify type compatibility.
Subcommands
horus msg list # List all message types (-f/--filter <name-or-module>)
horus msg info <type> # Show message definition
horus msg hash <type> # Show definition hash (for compatibility checking)
horus msg gen # Generate Rust, C++ and Python types from msgs/*.hmsg
horus msg gen --check # Verify the generated files are up to date without rewriting them
All four subcommands also accept --json.
Examples
List available message types:
horus msg list
# Output:
# MESSAGE TYPE MODULE FIELDS
# -------------------------------------------------------
# Heartbeat diagnostics 6
# GenericMessage generic 6
# Pose2D math 4
# Twist math 3
# Clock time 6
Show message definition:
horus msg info Twist
# Output:
# Message Type Definition
#
# Type: Twist
# Module: math
# Source: /path/to/horus/horus_types/src/math.rs
#
# Description:
# Full 6-DOF velocity (linear + angular) for 3D robots.
# ...
#
# Fields:
# linear: [f64; 3]
# angular: [f64; 3]
# timestamp_ns: u64
#
# Hash: 0x...
Field descriptions are not part of this output — the doc comment appears once,
under Description:. Use horus msg hash <type> on both machines when you need
to confirm two builds agree on a message.
horus msg gen reads msgs/*.hmsg and writes the Rust, C++ and Python types into
.horus/generated/. See Custom Messages for the
.hmsg syntax and the CI check.
horus launch - Launch Multiple Nodes
Alias: horus l
What it does: Launch multiple nodes from a YAML configuration file.
Why it's useful: Start complex multi-node systems with one command, define node dependencies and parameters.
Basic Usage
# Launch from file
horus launch robot.yaml
# Preview without launching
horus launch robot.yaml --dry-run
# Launch with namespace
horus launch robot.yaml --namespace robot1
Options
horus launch [OPTIONS] [FILE]
Arguments:
[FILE] Path to launch file (YAML). Omit when using --status
Options:
-n, --dry-run Show what would launch without actually launching
--namespace <NAMESPACE> Namespace prefix for all nodes
--list List nodes in the launch file without launching
--status Show active launch sessions (no file needed)
--stop <STOP> Stop a running launch session by name
--shutdown-timeout <N> Seconds to wait for graceful shutdown before
SIGKILL (default: 2)
-h, --help Print help
--status and --stop are how you manage a system after it is up: --status lists the
running sessions, --stop <name> shuts one down, and --shutdown-timeout sets how long
each node gets to exit cleanly before it is killed.
Launch File Format
Every node must specify either command (a program to execute) or package (a
HORUS package to run). A node with neither is rejected at launch time.
# robot.yaml
nodes:
- name: sensor_node
command: ./target/release/sensor
rate_hz: 100
params:
sample_rate: 100
- name: controller
package: my_robot
rate_hz: 50
depends_on: [sensor_node]
params:
kp: 1.5
ki: 0.1
- name: logger
command: python3 src/logger.py
rate_hz: 10
Node fields: name, package, command, args, rate_hz, params, env,
namespace, depends_on, start_delay, restart (never, always, on-failure),
and priority.
Note:
priorityis currently inert — it is exported to the child process but nothing reads it. Set priority with.priority()on the Rust builder orpriority=on the PythonNodeinstead, both of which reach the scheduler.
Examples
Launch robot system:
horus launch robot.yaml
Launch with namespace (for multi-robot):
horus launch robot.yaml --namespace robot1
horus launch robot.yaml --namespace robot2
horus deploy - Deploy to Remote Robot
What it does: Cross-compile and deploy your project to a remote robot over SSH.
Why it's useful: Deploy from development machine to embedded robot, supports multiple architectures.
Basic Usage
# Deploy to configured target
horus deploy robot@192.168.1.100
# Deploy and run immediately
horus deploy robot@192.168.1.100 --run
# Deploy to specific architecture
horus deploy robot@192.168.1.100 --arch aarch64
Options
horus deploy [OPTIONS] [TARGETS]...
Arguments:
[TARGETS]... Target(s) — named targets from deploy.yaml or direct user@host
Options:
--all Deploy to ALL targets in deploy.yaml
--parallel Deploy to multiple targets in parallel
-d, --dir <REMOTE_DIR> Remote directory (default: ~/horus_deploy)
-a, --arch <ARCH> Target architecture (aarch64, armv7, x86_64, native)
--run Run the project after deploying
--debug Build in debug mode instead of release
-p, --port <PORT> SSH port (default: 22)
-i, --identity <IDENTITY> SSH identity file
-n, --dry-run Show what would be done without doing it
--list List configured deployment targets
-h, --help Print help
Examples
Deploy to Raspberry Pi:
horus deploy pi@raspberrypi.local --arch aarch64
Deploy and run on NVIDIA Jetson:
horus deploy ubuntu@jetson.local --arch aarch64 --run
Configure named targets in .horus/deploy.yaml:
targets:
jetson:
host: ubuntu@192.168.1.50
arch: aarch64
pi:
host: pi@192.168.1.51
arch: aarch64
Each target takes host plus optional arch, dir, port, and identity.
Then deploy with:
horus deploy jetson --run
horus install - Install Package/Driver/Plugin
Alias: horus i
What it does: Smart package installer that auto-detects whether you're installing a package, driver, or plugin.
Why it's useful: Single command for all installations, handles dependencies automatically.
Basic Usage
# Install a package (auto-detected)
horus install pid-controller
# Install with specific version
horus install sensor-fusion@1.2.0
# Install a driver (drivers install by package name)
horus install camera-driver
# Install as a CLI plugin
horus install horus-visualizer --plugin
Options
horus install [OPTIONS] <NAME>
Arguments:
<NAME> Package/driver/plugin name to install
Options:
--plugin Force install as plugin
-t, --target <NAME> Install into a specific workspace/project
--json Output as JSON
-h, --help Print help
Pin a version with the name@version argument syntax. (A hidden legacy --ver <VER>
alias still exists, but it has no -v short form — -v is the global --verbose.)
Examples
Install common packages:
horus install kalman-filter
horus install pid-controller@2.0.0
Install hardware drivers:
horus install realsense-driver
horus install rplidar-driver
horus remove - Remove a Dependency from horus.toml
What it does: Removes a dependency entry from your project's horus.toml.
Why it's useful: Drop a dependency you no longer need, or switch to an alternative implementation.
Basic Usage
# Remove a dependency from horus.toml
horus remove pid-controller
# Remove and also clean unused packages from the cache
horus remove sensor-fusion --purge
Options
horus remove [OPTIONS] <NAME>
Arguments:
<NAME> Dependency name
Options:
--purge Also clean unused packages from the cache
-h, --help Print help
horus uninstall - Uninstall an Installed Package or Plugin
What it does: Uninstalls a standalone package or plugin that was installed globally with horus install.
Why it's useful: horus remove only edits horus.toml; use horus uninstall to actually remove something installed on the machine.
Basic Usage
# Uninstall a package or plugin
horus uninstall horus-visualizer
# Uninstall and purge cached files
horus uninstall sensor-fusion --purge
Options
horus uninstall [OPTIONS] <NAME>
Arguments:
<NAME> Package or plugin name
Options:
--purge Also purge cached files
-h, --help Print help
horus plugin - Plugin Management
Alias: horus plugins
What it does: Manage HORUS plugins (extensions that add CLI commands or features).
Why it's useful: Enable/disable plugins, verify plugin integrity.
Subcommands
horus plugin enable <plugin> # Enable a disabled plugin
horus plugin disable <plugin> # Disable a plugin (--reason <TEXT>)
horus plugin verify [plugin] # Verify plugin integrity (--json)
horus plugin trust <plugin> # Allow a project-local plugin to execute
horus plugin untrust <plugin> # Revoke execution permission
horus plugin trusted # List plugins trusted for execution (--json)
Plugins that ship inside a checkout's .horus/ directory are unsigned and are
refused at execution time until you trust them on this machine. horus plugin trust records the plugin's content hash in an out-of-repo trust store; cloning
a repository never grants its plugins the right to run. If a plugin refuses to
execute, this is the command you want.
Plugins are installed, listed, and removed with the ordinary package commands —
there is no horus plugin install or horus plugin list:
horus list # List installed packages and plugins
horus search <query> # Search for available packages/plugins
horus info <plugin> # Show detailed plugin info
horus install <plugin> # Install a plugin from registry
horus uninstall <plugin> # Uninstall an installed plugin
Examples
List plugins (horus list shows packages and plugins together):
horus list
Search and install a plugin:
horus search visualizer
horus install horus-visualizer
Disable a plugin temporarily:
horus plugin disable horus-ros2-bridge --reason "debugging"
horus cache - Cache Management
What it does: Manage the HORUS package cache (downloaded packages, compiled artifacts).
Why it's useful: Reclaim disk space, troubleshoot package issues.
Subcommands
horus cache info # Show cache statistics (size, package count)
horus cache list # List all cached packages
horus cache clean # Remove unused packages (--dry-run to preview)
horus cache purge # Remove ALL cached packages (-y to skip confirmation)
Examples
Check cache usage:
horus cache info
# Output:
# Cache directory: ~/.cache/horus
# Total size: 1.2 GB
# Packages: 45
Clean unused packages:
horus cache clean
# Removes packages not used by any project
horus record - Record/Replay Management
Alias: horus rec
What it does: Manage recorded sessions for debugging and testing.
Why it's useful: Replay exact scenarios, compare runs, debug timing-sensitive issues.
Subcommands
horus record list # List all recordings (-l/--long, --json)
horus record info <session> # Show recording details (--json)
horus record replay <session> # Replay a recording
horus record delete <session> # Delete a recording (-f/--force)
horus record clean # Delete old sessions (--older-than <DAYS>, -n/--dry-run, -f/--force)
horus record diff <a> <b> # Compare two recordings (-n/--limit N)
horus record export <session> -o <file> # Export to another format (-f json|csv, default json)
horus record inject <session> # Inject recorded data into live scheduler
Examples
List recordings:
horus record list
# Output:
# ✓ Found 2 recording session(s):
#
# rec_001
# rec_002
# With -l/--long, each line gains a file count and size:
horus record list --long
# Output:
# ✓ Found 2 recording session(s):
#
# rec_001 (4 files, 45.0 MB)
# rec_002 (3 files, 18.2 MB)
There is no date, duration or node count in this listing — --long adds only the file
count and total size.
Replay a session:
horus record replay rec_001
Compare two runs:
horus record diff rec_001 rec_002
# Shows differences in timing, message counts, errors
Inject recorded data into live system:
# Use recorded sensor data with live controller
horus record inject rec_001 --nodes SensorNode
horus blackbox - BlackBox Flight Recorder
Alias: horus bb
What it does: Inspects the BlackBox flight recorder for post-mortem crash analysis. The BlackBox automatically records scheduler events, errors, deadline misses, and safety state changes.
Why it's useful: After a crash or anomaly, review exactly what happened — which nodes failed, when deadlines were missed, and what the safety state was at each tick.
Basic Usage
# View all recorded events
horus blackbox
# Show only anomalies (errors, deadline misses, WCET violations, e-stops)
horus blackbox --anomalies
# Follow mode — stream events in real-time (like tail -f)
horus blackbox --follow
Options
horus blackbox [OPTIONS]
Options:
-a, --anomalies Show only anomalies (errors, deadline misses,
WCET violations, e-stops)
-f, --follow Follow mode — stream new events as they arrive
-t, --tick <RANGE> Filter by tick range (e.g. "4500-4510" or "4500")
-n, --node <NAME> Filter by node name (partial, case-insensitive)
-e, --event <TYPE> Filter by event type (e.g. "DeadlineMiss", "NodeError")
--json Output as machine-readable JSON
-l, --last <N> Show only the last N events
-p, --path <DIR> Custom blackbox directory (default: the nearest
.horus/blackbox/ holding records, else the
machine-global store)
--clear Clear all blackbox data (with confirmation)
Which directory it reads
With no --path, blackbox resolves the directory in three steps:
- Walk up from the current directory — at most ten levels — for a
.horus/blackbox/that actually holds ablackbox.walor ablackbox.json. This is why it works fromsrc/and not only from the project root. - Failing that, a
.horus/blackbox/directory in the current directory, even an empty one. - Failing that, the machine-global store — shared by every project on the box:
| Platform | Fallback directory |
|---|---|
| Linux | $XDG_DATA_HOME/horus/blackbox, else ~/.local/share/horus/blackbox |
| macOS | ~/Library/Application Support/horus/blackbox |
| Windows | %LOCALAPPDATA%\horus\data\blackbox |
The first line of output names the directory it read — whether it found events or not — so check it before drawing conclusions:
BLACKBOX 42 events from /home/you/robot/.horus/blackbox
INFO No blackbox events found in /home/you/.local/share/horus/blackbox
Run horus blackbox in a directory with no .horus/blackbox/ above it and the events you
see may come from a different project's run. During incident analysis that is worse than
seeing nothing. Pass --path .horus/blackbox to pin it, or cd into the project first.
Examples
View recent anomalies:
horus bb --anomalies --last 20
Filter by node and tick range:
horus bb --node controller --tick 4500-4510
Stream events in real-time while debugging:
horus bb --follow --anomalies
Export to JSON for external analysis:
horus bb --json > blackbox_dump.json
Clear old data:
horus bb --clear
horus add - Add a Dependency to This Project
What it does: writes a dependency into the horus.toml next to you. Nothing is
installed system-wide and nothing is executed — the entry is resolved the next time
you build.
add and install are the pair
people mix up:
Edits horus.toml? | Scope | |
|---|---|---|
horus add <name> | yes | this project |
horus install <name> | no | the machine (global by default; -t <workspace> targets one) |
horus remove undoes add; horus uninstall undoes install.
Basic Usage
# A Rust crate
horus add serde --source crates.io
# Pin a version with name@version
horus add serde@1.0
# A Python package
horus add numpy --source pypi
# A driver, recorded in [drivers] rather than [dependencies]
horus add rplidar --driver
# A test-only dependency
horus add proptest --dev
Options
| Flag | What it does |
|---|---|
-s, --source <SOURCE> | crates.io, pypi, system, registry, git, path |
-F, --features <FEATURES> | Features to enable, e.g. --features derive,serde |
--dev | Add to [dev-dependencies] |
--driver | Add to [drivers] |
--json | JSON output |
horus lock - Pin Every Dependency Version
What it does: resolves the dependency graph and writes horus.lock, so the same
source builds the same way on the robot as it did on your laptop.
# Generate or refresh horus.lock
horus lock
# Fail if the lockfile is stale — for CI
horus lock --check
--check writes nothing; it exits non-zero when horus.toml and horus.lock
disagree, which is the shape a CI job wants.
| Flag | What it does |
|---|---|
--check | Verify the lockfile is up to date instead of regenerating it |
horus scripts - Run a Project Script
What it does: runs an entry from the [scripts] table of horus.toml, so the
commands a project needs live with the project instead of in someone's shell history.
# List the scripts this project defines
horus scripts
# Run one
horus scripts calibrate
# Pass arguments through
horus scripts calibrate -- --sensor front
horus script (singular) is an alias for the same command. Everything after -- is
handed to the script untouched.
See Configuration → [scripts] for how to define them.
horus self update - Update the CLI Itself
What it does: replaces the running horus binary with the latest release.
horus self update
This is the CLI, not your project: horus update moves your dependencies
forward, horus self update moves horus forward. If HORUS was installed from a
package manager, update it there instead.
horus man - Man Page
What it does: writes a roff man page for horus to stdout.
# Read it now
horus man | man -l -
# Install it for the current user
mkdir -p ~/.local/share/man/man1
horus man > ~/.local/share/man/man1/horus.1
man horus
install.sh already does this for you; the command exists for package maintainers
and for anyone who built from source. The page is rendered from the same clap
definition the binary parses arguments with, so it cannot describe a command this
binary does not have.
Common Workflows
First Time Using HORUS
# Create a project
horus new my_first_app --macro
cd my_first_app
# Run it
horus run --release
# Monitor it (new terminal)
horus monitor
Daily Development
# Make changes to code
vim src/main.rs
# Test quickly
horus run
# Test for real
horus run --release
Deploy to Production
# Clean build
horus run --clean --release
# Pin exact dependency versions
horus lock
# Run in production mode
horus run --release
Share Your Work
# Login once
horus auth login
# Publish
horus publish
# Others can now:
horus install your-package-name
Troubleshooting
"command not found: horus"
Add cargo to your PATH:
export PATH="$HOME/.cargo/bin:$PATH"
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
"Port already in use"
# Use different port
horus monitor 3001
# Or kill the old process
lsof -ti:3000 | xargs kill -9
Build is slow
First build is always slow (5-10 min). After that it's fast (seconds).
Use --release only when you need speed, not during development.
"Failed to create Topic"
Topic name conflict. Try a unique name or clean up stale shared memory.
Note: HORUS automatically cleans up shared memory after each run using session isolation. This error usually means a previous run crashed.
# Clean stale HORUS shared memory (if needed after crashes)
horus clean --shm
# Add --force if HORUS processes are still running
horus clean --shm --force
Shared memory is namespaced (/dev/shm/horus_<namespace>/ on Linux). horus clean --shm removes only stale namespaces — a live namespace belonging to another process is left alone, and the command reports how many it spared. Add --force to also remove your own current namespace, or --all-namespaces for a machine-wide sweep that removes every namespace, live ones included.
Environment Variables
Optional configuration:
# Custom registry (for companies)
export HORUS_REGISTRY_URL=https://your-company-registry.com
For debug output, use the CLI's own verbosity flag rather than an environment
variable — horus installs its own log bridge in place of env_logger, so
RUST_LOG has no effect:
horus run --verbose
CI/CD authentication: there is deliberately no
HORUS_API_KEYvariable — nothing reads it. Registry credentials are read from~/.config/horus/auth.json, so write that file from your secret store. Generate the key withhorus auth api-key --name ci --environment ci-cd.
Utility Scripts
Beyond the horus CLI, the repository includes helpful scripts:
./install.sh # Install or update HORUS
./uninstall.sh # Remove HORUS from this machine
See Troubleshooting & Maintenance for complete details.
Next Steps
Now that you know the commands:
- Quick Start - Build your first app
- node! Macro - Write less code
- Monitor Guide - Master monitoring
- Examples - See real applications
Having issues? Check the Troubleshooting Guide for solutions to common problems.