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.

⚠️`[sim-drivers]` is not consulted

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:

CapabilityDescription
cuda, gpuCUDA GPU acceleration
editorScene editor UI
visualVisualization support
python, pyPython bindings
headlessNo rendering (for training)
opencvOpenCV backend
io-uringio_uring networking backend
ultra-low-latencyUltra-low-latency tuning
sim, simulationSimulation mode (no hardware features)
net, networkLAN replication — same as horus run --net
fullAll 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:

  1. After updating HORUS - Most common use case

    # You updated horus CLI to a new version
    horus run --clean
    

    This fixes version mismatch errors like:

    error: the package `horus` depends on `horus_core 0.1.0`,
    but `horus_core 0.1.3` is installed
    
  2. Compilation fails mysteriously

    horus run --clean
    # Sometimes cached state gets corrupted
    
  3. 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 runs cargo clean against .horus/target/
  • Next build rebuilds everything from scratch

Trade-off:

  • First build after --clean is 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, or main.cpp, at the project root or under src/
  • A glob of files (see Concurrent Multi-Process Execution below)
  • A workspace, declared with a [workspace] section in horus.toml — it generates a workspace Cargo.toml plus per-member manifests and builds with cargo build --workspace
  • A project with its own root Cargo.toml, built directly from it with CARGO_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:

  1. Phase 1 (Build): Builds all files sequentially, respecting Cargo's file lock
  2. Phase 2 (Execute): Spawns all processes concurrently with their own schedulers
  3. 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 monitor is provided by the horus-monitor plugin. Install it once with horus 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.

FlagWhat it does
-g, --globalGlobal scope packages only
-a, --allLocal and global together
--jsonJSON 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
FlagWhat it does
-c, --category <CATEGORY>camera, lidar, imu, motor, servo, bus, gps, simulation, cli
--jsonJSON 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.

FlagWhat it does
-n, --dry-runReport the changes without applying them
-g, --globalUpdate 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.

FlagWhat it does
-n, --dry-runValidate 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.

FlagWhat it does
-y, --yesSkip 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.

FlagWhat 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.

FlagWhat 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
FlagWhat it does
--checkReports 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)
FlagWhat it does
--fixApplies the auto-fixable subset
--typesAdds 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"]
FlagWhat 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.

FlagWhat it does
--openOpen in a browser after generating
--extractEmit machine-readable API documentation
--json / --md / --htmlOutput format for --extract
--brief / --fullOne line per symbol; --full adds doc comments
--allInclude private and crate-only symbols
--lang <LANG>Restrict to rust, cpp or python
--coverageReport 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.

FlagWhat it does
-n, --dry-runPrint the changes without writing them
-f, --forceSkip 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
FlagWhat it does
--checkReports what is and is not configured, and exits without changing anything
--undoRemoves 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 changesWhenPrompt
/etc/security/limits.d/99-horus-rt.confmemlock unlimited and rtprio 99, scoped to the user that ran the command, never *Always, unless the file already existsAsks
The distribution's real-time kernel packageOnly when the running kernel is not already PREEMPT_RTAsks

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=)InstallsRemoves with
ubuntulinux-image-realtimesudo apt remove linux-image-realtime
debianlinux-image-rt-amd64 (x86_64), linux-image-rt-arm64 (aarch64)sudo apt remove linux-image-rt-amd64
fedorakernel-rt kernel-rt-coresudo dnf remove kernel-rt kernel-rt-core
archlinux-rt linux-rt-headerssudo 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:

PathContents
~/.horus/env.shThe shell functions, for bash and zsh
~/.horus/env.fishThe same, for fish
~/.bashrc, ~/.zshrcA two-line block sourcing env.sh
~/.config/fish/conf.d/horus.fishThe 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 env does not snapshot, save, or restore package environments. To pin exact dependency versions for reproducible builds, use horus 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, LaserScan and Odometry come from the horus-robotics package rather than HORUS's own horus_types, but horus msg list scans both, so they appear in its output alongside the built-ins and horus msg info CmdVel works 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
ℹ️`service call` reaches Rust servers only

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

⚠️`horus log` shows every HORUS process on this machine

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: priority is currently inert — it is exported to the child process but nothing reads it. Set priority with .priority() on the Rust builder or priority= on the Python Node instead, 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:

  1. Walk up from the current directory — at most ten levels — for a .horus/blackbox/ that actually holds a blackbox.wal or a blackbox.json. This is why it works from src/ and not only from the project root.
  2. Failing that, a .horus/blackbox/ directory in the current directory, even an empty one.
  3. Failing that, the machine-global store — shared by every project on the box:
PlatformFallback 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
⚠️Outside a project you are reading the global store

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>yesthis project
horus install <name>nothe 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

FlagWhat it does
-s, --source <SOURCE>crates.io, pypi, system, registry, git, path
-F, --features <FEATURES>Features to enable, e.g. --features derive,serde
--devAdd to [dev-dependencies]
--driverAdd to [drivers]
--jsonJSON 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.

FlagWhat it does
--checkVerify 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_KEY variable — nothing reads it. Registry credentials are read from ~/.config/horus/auth.json, so write that file from your secret store. Generate the key with horus 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:

  1. Quick Start - Build your first app
  2. node! Macro - Write less code
  3. Monitor Guide - Master monitoring
  4. Examples - See real applications

Having issues? Check the Troubleshooting Guide for solutions to common problems.