Package Management

Note: Publishing packages requires the registry backend to be deployed. Installing public packages works immediately.

HORUS provides a comprehensive package management system for sharing and discovering robotics components. Create reusable nodes, message types, and algorithms that the community can use.

Quick Reference

TaskCommand
Add a dependencyhorus add serde
Add with versionhorus add serde@1.0 --features derive
Install a tool/pluginhorus install horus-sim3d
Remove a packagehorus remove pid-controller
Search registryhorus search slam
List installedhorus list
Update allhorus update
Authenticatehorus auth login
Publishhorus publish

Overview

The package system allows you to:

  • Install packages from multiple sources (HORUS registry, crates.io, PyPI)
  • Publish your work for others to use
  • Manage dependencies automatically
  • Version control with semantic versioning
  • Search and discover community packages

Package Sources

HORUS supports packages from multiple sources:

SourceDescriptionCommand
HORUS RegistryCurated robotics packageshorus add pid-controller or horus install pid-controller
crates.ioRust ecosystem packageshorus add serde
PyPIPython ecosystem packageshorus add numpy
GitGit repositorieshorus add https://github.com/org/repo
Local PathLocal filesystemhorus add ./path/to/pkg

horus add vs horus install

CommandLikePurposeModifies horus.toml?
horus add serdecargo addAdd project dependencyYes
horus install slam-toolboxcargo installInstall standalone tool/plugin globallyNo

Use horus add for libraries your project depends on. Use horus install for standalone tools, plugins, and CLI extensions.

Quick Start

Adding Project Dependencies

# Add from crates.io (auto-detected for Rust projects)
horus add serde
horus add tokio

# Add from PyPI (auto-detected for Python projects)
horus add numpy
horus add opencv-python

# Add with specific version and features
horus add serde@1.0 --features derive

# Add from HORUS registry
horus add pid-controller

Installing Standalone Packages

# Install a standalone tool or plugin globally
horus install horus-sim3d
horus install rplidar-driver@1.2.0
horus install horus-visualizer --plugin

Automatic Source Detection

HORUS automatically detects the package source:

  1. First checks HORUS registry
  2. Then checks both PyPI and crates.io
  3. If found in multiple sources, prompts you to choose:
Package 'package_name' found in BOTH PyPI and crates.io

Which package source do you want to use?
  [1] [PYTHON] PyPI (Python package)
  [2] [RUST] crates.io (Rust binary)
  [3] [FAIL] Cancel installation

Choice [1-3]:

System Package Detection

If a package is already installed system-wide, HORUS offers to reuse it:

Package 'ripgrep' v14.0.0 already installed system-wide

  [1] Use system package (no download)
  [2] Install fresh copy to HORUS
  [3] Cancel

Choice [1-3]:

What happens during installation:

  1. Detects package source (HORUS registry, crates.io, or PyPI)
  2. Downloads package from the appropriate source
  3. Resolves dependencies automatically
  4. Caches locally in ~/.horus/cache/ or .horus/packages/
  5. Makes package available for use

Using an Installed Package

// In your main.rs or any file
use pid_controller::PIDNode;
use horus::prelude::*;

fn main() -> Result<()> {
    let mut scheduler = Scheduler::new();

    // Use the installed package
    let pid = PIDNode::new(1.0, 0.1, 0.01);
    scheduler.add(pid).order(5).build()?;

    scheduler.run()?;
    Ok(())
}

Publishing Your Package

# 1. Authenticate first (one-time)
horus auth login

# 2. Navigate to your project
cd my-awesome-controller

# 3. Publish
horus publish

Package Locations

Local Packages

Project-local (default):

my_project/
── .horus/
   ── packages/
       ── pid-controller@1.0.0/      # HORUS registry
       ── serde@1.0.200/             # crates.io
       ── pypi_numpy@1.24.0/         # PyPI (prefixed with pypi_)
── src/
    ── main.rs

Why use local:

  • Different projects can use different versions
  • Clean separation per project
  • Easy to delete with project

Global Packages

System-wide (installed with -g flag):

~/.horus/
── cache/
    ── pid-controller@1.0.0/         # HORUS registry
    ── serde@1.0.200/                # crates.io
    ── pypi_numpy@1.24.0/            # PyPI packages
    ── git_abc123/                   # Git dependencies

Naming conventions by source:

SourceDirectory FormatExample
HORUS Registry<name>@<version>/pid-controller@1.0.0/
crates.io<name>@<version>/serde@1.0.200/
PyPIpypi_<name>@<version>/pypi_numpy@1.24.0/
Gitgit_<hash>/git_abc123def/

Why use global:

  • Share common packages across all projects
  • Save disk space (one copy for everything)
  • Faster install after first download

Priority Order & Smart Dependency Resolution

When resolving packages, HORUS checks in this order:

1. Project-local .horus/packages/ (highest priority)

  • Checked first, ALWAYS wins
  • Can be symlink to global OR real directory
  • Enables local override of broken global packages

2. Global cache ~/.horus/cache/

  • Only checked if not found locally
  • Shared across all projects
  • Version-specific directories (e.g., serde@1.0.228/)

3. System install /usr/local/lib/horus/ (if available)

  • Last resort fallback

Smart Dependency Resolution:

When you run horus add, HORUS auto-detects the source and writes to horus.toml:

# Add a dependency (auto-detects source)
horus add serde                     # Rust project → crates.io
horus add numpy                     # Python project → PyPI
horus add pid-controller            # → horus registry

# Dependencies are fetched on next horus build/run

Local Package Cache:

Project dependencies installed from the horus registry are cached in .horus/packages/ with symlinks to the global cache (~/.horus/cache/) for disk efficiency. Local copies always take precedence over global ones.

Benefits:

  • Local override - Bypass broken global packages
  • Version isolation - Different projects can use different versions
  • Disk efficient - Shares global cache when possible
  • Zero config - Works automatically

Commit horus.lock to git so teammates get identical dependency versions with horus build.

Package Commands

horus add

Add a project dependency to horus.toml. Auto-detects the source (crates.io, PyPI, horus registry) from your project language. Like cargo add.

Usage:

horus add <package[@version]> [OPTIONS]

Options:

  • -s, --source <SOURCE> - Override source: crates-io, pypi, system, registry, git, path
  • -F, --features <FEATURES> - Enable features (comma-separated)
  • --dev - Add to [dev-dependencies]
  • --driver - Add to [hardware]

Examples:

# Auto-detected source based on project language
horus add serde                     # Rust project → crates.io
horus add numpy                     # Python project → PyPI
horus add pid-controller            # → horus registry

# With version and features
horus add serde@1.0 --features derive
horus add tokio --features full

# Explicit source override
horus add opencv --source system

# Dev dependencies
horus add criterion --dev

# Drivers
horus add camera --driver

horus install

Install a standalone package or plugin globally. Like cargo install — does NOT modify horus.toml.

Usage:

horus install <package[@version]> [OPTIONS]

Options:

  • --plugin - Install as CLI plugin
  • -t, --target <NAME> - Target workspace/project name

Examples:

# Install standalone tool/plugin
horus install horus-sim3d
horus install rplidar-driver@1.2.0
horus install horus-visualizer --plugin

Installing from crates.io

When installing Rust packages from crates.io, HORUS uses cargo install under the hood:

horus install ripgrep

Output:

Installing ripgrep from crates.io...
  Compiling ripgrep...
  Installing with cargo...

Package installed: ripgrep@14.0.0
Location: ~/.horus/cache/ripgrep@14.0.0/

Requirements:

  • Rust toolchain must be installed (rustup)
  • cargo must be available in PATH

Adding PyPI Dependencies

Add Python packages as project dependencies with horus add:

horus add numpy
horus add opencv-python

This writes to horus.toml [dependencies] with source = "pypi". The package is installed via pip on the next horus build or horus run.

Requirements:

  • Python 3.x must be installed
  • pip must be available in PATH

When using horus run, Python package paths are automatically configured — no manual sys.path needed.

HORUS Registry Output:

Installing pid-controller@1.2.0...
 Downloaded (245 KB)
 Extracted to .horus/packages/pid-controller@1.2.0/
 Installed dependencies: control-utils@1.0.0
 Build successful

Package installed: pid-controller@1.2.0
Location: .horus/packages/pid-controller@1.2.0/

Usage:
  use pid_controller::PIDNode;

horus remove

Uninstall a package.

Usage:

horus remove <package>

Options:

  • -g, --global - Remove from global cache
  • -t, --target <NAME> - Target workspace/project name

Examples:

# Remove local package
horus remove motion-planner

# Remove from global cache
horus remove common-utils -g

# Remove from specific workspace
horus remove pid-controller -t my-project

Output:

Removing pid-controller@1.2.0...
 Removed from .horus/packages/
 Freed 892 KB

Package removed: pid-controller@1.2.0

horus list

List installed packages or search the registry.

Usage:

horus list [QUERY] [OPTIONS]

Options:

  • -g, --global - List global cache packages
  • -a, --all - List all (local + global)

List Local Packages:

horus list

Output:

Local packages:
  pid-controller 1.2.0
  motion-planner 2.0.1
  sensor-drivers 1.5.0

List Global Cache:

horus list -g

Search Registry:

# Search by keyword
horus list sensor

Output:

Found 3 package(s):
  sensor-fusion 2.1.0 - Kalman filter fusion
  sensor-drivers 1.5.0 - LIDAR/IMU/camera drivers
  sensor-calibration 1.0.0 - Calibration tools

horus update

Update installed packages to their latest versions.

Usage:

horus update [PACKAGE] [OPTIONS]

Options:

  • -g, --global - Update global cache packages
  • --dry-run - Show what would be updated without making changes

Examples:

# Update all local packages
horus update

# Update a specific package
horus update pid-controller

# Update global packages
horus update -g

# Preview updates without applying
horus update --dry-run

horus search

Search the registry for packages.

Usage:

horus search <query> [OPTIONS]

Options:

  • --category <CATEGORY> - Filter by category (driver, algorithm, plugin, tool)

Examples:

horus search slam
horus search lidar --category driver

Dependency Management

Version Constraints

horus add pid-controller@1.2.0       # Exact version
horus add motion-planner@^2.0        # Compatible (2.x.x, not 3.0.0)
horus add sensor-drivers@~1.5.0      # Patch updates (1.5.x)

Automatic Resolution

HORUS automatically resolves and installs transitive dependencies:

horus add robot-controller
Resolving dependencies...
  robot-controller@1.0.0
  ├── motion-planner@2.0.1
  │   └── pathfinding-utils@1.2.0
  └── pid-controller@1.2.0
      └── control-utils@1.0.0

Installing 5 packages...
✓ All dependencies installed

Specifying Dependencies

Use horus add for all dependency sources:

# HORUS registry
horus add pid-controller
horus add motion-planner

# crates.io
horus add serde --source crates-io

# PyPI
horus add numpy --source pypi

# System library
horus add opencv --source system

See Configuration Reference for details on horus.toml dependency syntax.


Common Workflows

Using Multiple Packages

# Add dependencies
horus add pid-controller
horus add motion-planner
horus add sensor-fusion
use pid_controller::PIDController;
use motion_planner::AStarPlanner;
use sensor_fusion::KalmanFilter;
use horus::prelude::*;

fn main() -> Result<()> {
    let mut scheduler = Scheduler::new();
    // Combine multiple packages into your system
    Ok(())
}

Updating Dependencies

# Update all packages to latest versions
horus update

# Update a specific package
horus update pid-controller

# Preview updates without applying
horus update --dry-run

Publishing

To publish packages to the HORUS registry, see the Registry Author Guide on the registry website. It covers:

  • Authentication (horus auth login)
  • Publishing workflow (horus publish)
  • CI/CD integration (GitHub Actions, GitLab CI)
  • Package versioning and lifecycle (yank, deprecate, owner)
  • API key management

Quick start:

horus auth login     # One-time GitHub OAuth
cd my-package
horus publish        # Publish to registry

Troubleshooting

Package Not Found

Error: Package 'nonexistent-package' not found in registry
# Check spelling / search for the correct name
horus search sensor

Version Conflict

Error: Version conflict
  robot-controller requires motion-planner ^2.0
  sensor-fusion requires motion-planner ^1.5
# Update the conflicting package to a compatible version
horus add sensor-fusion@2.0.0

Build Failures

# Clean and retry
horus remove my-package
horus add my-package

# Check the registry for known issues
horus info my-package

Registry Unavailable

# Use cached packages for offline development
horus list -g

# Check connectivity
horus doctor

Next Steps


See Also