Package Management
Note: Publishing packages requires the marketplace 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.
Overview
The package system allows you to:
- Install packages from the registry
- Publish your work for others to use
- Manage dependencies automatically
- Version control with semantic versioning
- Search and discover community packages
Quick Start
Installing a Package
# Install latest version
horus pkg install pid-controller
# Install specific version
horus pkg install pid-controller -v 1.2.0
# Install globally (share across all projects)
horus pkg install sensor-drivers -g
What happens during installation:
- Downloads package from registry
- Resolves dependencies automatically
- Caches locally in
~/.horus/cache/or.horus/packages/ - Makes package available via
usestatements
Using an Installed Package
// In your main.rs or any file
use pid_controller::PIDNode;
use horus::prelude::*;
fn main() {
let mut scheduler = Scheduler::new();
// Use the installed package
let pid = PIDNode::new(1.0, 0.1, 0.01);
scheduler.register(Box::new(pid), 5, Some(true));
scheduler.tick_all().expect("Scheduler failed");
}
Publishing Your Package
# 1. Authenticate first (one-time)
horus auth login --github
# 2. Navigate to your project
cd my-awesome-controller
# 3. Publish
horus pkg publish
Package Locations
Local Packages
Project-local (default):
my_project/
── .horus/
── packages/
── pid-controller@1.0.0/
── sensor-drivers@2.1.3/
── 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/
── pid-controller@1.2.0/
── sensor-drivers@2.1.3/
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 Installation Behavior:
When you run horus pkg install, HORUS automatically chooses the best strategy:
# Default behavior (no flags)
horus pkg install serde
# If package exists in global cache:
# Install to global cache
# Create symlink: .horus/packages/serde -> ~/.horus/cache/serde@1.0.228/
# Disk efficient!
# If package NOT in global cache:
# Install directly to .horus/packages/serde@1.0.228/
# No symlink, real directory
# Isolated from global!
Override Broken Global Cache:
Local packages always win, so you can override corrupted global packages:
# Scenario: Global cache has broken serde@1.0.228
~/.horus/cache/serde@1.0.228/ # Corrupted
# Fix: Install working version locally
rm .horus/packages/serde # Remove symlink to broken global
horus pkg install serde -v 1.0.150 # Install working version locally
# Result:
.horus/packages/serde@1.0.150/ # Real directory, not symlink
# horus run will use this, ignoring broken global!
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
See Environment Management for more details on how this solves dependency hell.
Package Commands
horus pkg install
Install packages from the registry.
Usage:
horus pkg install <package> [OPTIONS]
Options:
-v, --ver <VERSION>- Install specific version (default: latest)-g, --global- Install to global cache-t, --target <NAME>- Target workspace/project name
Examples:
# Latest version
horus pkg install motion-planner
# Specific version
horus pkg install motion-planner -v 2.0.1
# Global installation
horus pkg install common-utils -g
# Install to specific workspace
horus pkg install pid-controller -t my-project
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 pkg remove
Uninstall a package.
Usage:
horus pkg remove <package>
Options:
-g, --global- Remove from global cache-t, --target <NAME>- Target workspace/project name
Examples:
# Remove local package
horus pkg remove motion-planner
# Remove from global cache
horus pkg remove common-utils -g
# Remove from specific workspace
horus pkg 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 pkg list
List installed packages or search the registry.
Usage:
horus pkg list [QUERY] [OPTIONS]
Options:
-g, --global- List global cache packages-a, --all- List all (local + global)
List Local Packages:
horus pkg list
Output:
Local packages:
pid-controller 1.2.0
motion-planner 2.0.1
sensor-drivers 1.5.0
List Global Cache:
horus pkg list -g
Search Registry:
# Search by keyword
horus pkg 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 pkg unpublish
Remove a package version from the registry (irreversible!).
Usage:
horus pkg unpublish <package> <version> [OPTIONS]
Options:
-y, --yes- Skip confirmation prompt
Examples:
# Unpublish a specific version
horus pkg unpublish my-package 1.0.0
# Skip confirmation prompt
horus pkg unpublish my-package 1.0.0 -y
Output:
Unpublishing my-package v1.0.0...
Warning: This action is IRREVERSIBLE and will:
• Delete my-package v1.0.0 from the registry
• Make this version unavailable for download
• Cannot be undone
Type the package name 'my-package' to confirm: my-package
Successfully unpublished my-package v1.0.0
The package is no longer available on the registry
Note: Detailed package information can be viewed on the registry web interface at https://marketplace.horus-registry.dev
Publishing Packages
Prerequisites
Before publishing:
-
Authenticate with the registry:
horus auth login --github -
Complete Cargo.toml metadata:
[package] name = "my-awesome-package" version = "1.0.0" authors = ["Your Name <you@example.com>"] description = "Brief description of your package" license = "MIT" repository = "https://github.com/username/my-awesome-package" [package.metadata.horus] category = "control" # Navigation, Vision, Perception, Control, App, Manipulation, Simulation, Utilities keywords = ["pid", "controller", "motion"] -
Test your package locally:
horus run --release
Publishing Workflow
# 1. Navigate to package directory
cd my-awesome-package
# 2. Verify everything builds
horus run --build-only --release
# 3. Publish
horus pkg publish
Interactive prompts:
Publishing my-awesome-package v1.0.0...
Package metadata validated
Build successful
Tests passed
Documentation generated
Package size: 245 KB (compressed)
Publish to registry? [y/N]: y
Uploading...
Uploaded to registry
Published: my-awesome-package@1.0.0
Registry URL: https://marketplace.horus-registry.dev/packages/my-awesome-package
Others can now install with:
horus pkg install my-awesome-package
Adding Documentation and Source Links
After publishing, you'll be prompted to add optional metadata to help users discover and use your package:
Documentation Options
External Documentation URL: Link to your hosted documentation website (e.g., GitHub Pages, ReadTheDocs, custom site):
Documentation
Add documentation? (y/n): y
Documentation options:
1. External URL - Link to online documentation
2. Local /docs - Bundle markdown files in a /docs folder
Choose option (1/2/skip): 1
Enter documentation URL: https://my-package-docs.example.com
Documentation URL: https://my-package-docs.example.com
Local Documentation (Bundled Markdown): Include markdown files directly in your package for built-in documentation viewing:
Documentation
Found local /docs folder with markdown files
Add documentation? (y/n): y
Documentation options:
1. External URL - Link to online documentation
2. Local /docs - Bundle markdown files in a /docs folder
[i] Your /docs folder should contain .md files organized as:
/docs/README.md (main documentation)
/docs/getting-started.md (guides)
/docs/api.md (API reference)
Choose option (1/2/skip): 2
Will bundle local /docs folder with package
Local Docs Structure:
my-package/
── docs/
── README.md # Main documentation page
── getting-started.md # Installation and setup guide
── api.md # API reference
── examples.md # Usage examples
── src/
── lib.rs
── Cargo.toml
Benefits of Local Docs:
- Users can view docs directly from the marketplace
- Works offline
- Version-specific documentation
- Automatic rendering with syntax highlighting
- No external hosting required
Source Repository
Link to your GitHub, GitLab, or other repository:
Source Repository
Auto-detected: https://github.com/username/my-package
Add source repository? (y/n): y
Use detected URL? (y/n): y
Source repository: https://github.com/username/my-package
Manual Entry: If auto-detection doesn't work or you want to use a different URL:
Source Repository
Add source repository? (y/n): y
[i] Enter the URL where your code is hosted:
• GitHub: https://github.com/username/repo
• GitLab: https://gitlab.com/username/repo
• Other: Any public repository URL
Enter source repository URL: https://gitlab.com/robotics/my-package
Source repository: https://gitlab.com/robotics/my-package
Complete Publishing Example
$ cd my-sensor-package
$ horus pkg publish
Publishing my-sensor-package v1.0.0...
Uploaded to registry
Published: my-sensor-package@1.0.0
View at: https://marketplace.horus-registry.dev/packages/my-sensor-package
Package Metadata (optional)
Help users discover and use your package by adding:
Documentation
Found local /docs folder with markdown files
Add documentation? (y/n): y
Documentation options:
1. External URL - Link to online documentation
2. Local /docs - Bundle markdown files in a /docs folder
[i] Your /docs folder should contain .md files organized as:
/docs/README.md (main documentation)
/docs/getting-started.md (guides)
/docs/api.md (API reference)
Choose option (1/2/skip): 2
Will bundle local /docs folder with package
Source Repository
Auto-detected: https://github.com/robotics-lab/my-sensor-package
Add source repository? (y/n): y
Use detected URL? (y/n): y
Source repository: https://github.com/robotics-lab/my-sensor-package
Updating package metadata...
Package metadata updated!
How Users See Your Links
On the marketplace, your package will display:
─────────────────────────────────────────
my-sensor-package v1.0.0
High-performance sensor fusion
[View Details] [Docs] [Source]
Markdown Viewer GitHub Repo
─────────────────────────────────────────
- Docs Button: Only appears if you added documentation
- External URL: Opens in new tab
- Local docs: Opens built-in markdown viewer
- Source Button: Only appears if you added source URL
- Opens repository in new tab
Version Management
Semantic Versioning:
1.0.0- Major.Minor.Patch1.0.01.0.1- Patch: Bug fixes only1.0.01.1.0- Minor: New features (backward compatible)1.0.02.0.0- Major: Breaking changes
Publishing new version:
# 1. Update version in Cargo.toml
[package]
version = "1.1.0"
# 2. Publish
horus pkg publish
Version constraints in dependencies:
[dependencies]
pid-controller = "1.2.0" # Exact version
motion-planner = "^2.0" # Compatible (2.x.x, not 3.0.0)
sensor-drivers = "~1.5.0" # Patch updates (1.5.x)
Dependency Management
Automatic Resolution
HORUS automatically resolves and installs dependencies:
horus pkg install robot-controller
Output:
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
In your Cargo.toml:
[dependencies]
horus = "0.1"
pid-controller = { version = "1.2", registry = "horus" }
motion-planner = "2.0"
# Optional dependencies
[dependencies]
advanced-planning = { version = "3.0", optional = true }
[features]
planning = ["advanced-planning"]
Package Structure
Minimal Package
my-package/
── Cargo.toml # Package metadata
── src/
── lib.rs # Library entry point
── nodes/
── my_node.rs # Your node implementation
── examples/
── demo.rs # Usage example
── README.md # Documentation
Library Package (lib.rs)
// src/lib.rs
pub mod nodes;
pub mod messages;
pub mod utils;
// Re-export commonly used items
pub use nodes::MyControllerNode;
pub use messages::MyMessage;
Node Implementation
// src/nodes/my_node.rs
use horus::prelude::*;
pub struct MyControllerNode {
pub input: Hub<f64>,
pub output: Hub<f64>,
gain: f64,
}
impl MyControllerNode {
pub fn new(gain: f64) -> Self {
Self {
input: Hub::new("input").expect("Failed to create input hub"),
output: Hub::new("output").expect("Failed to create output hub"),
gain,
}
}
}
impl Node for MyControllerNode {
fn name(&self) -> &'static str {
"MyController"
}
fn tick(&mut self, ctx: Option<&mut NodeInfo>) {
if let Some(value) = self.input.recv(ctx) {
let result = value * self.gain;
self.output.send(result, ctx).ok();
}
}
}
Example Usage
// examples/demo.rs
use my_package::MyControllerNode;
use horus::prelude::*;
fn main() {
let mut scheduler = Scheduler::new();
let controller = MyControllerNode::new(2.5);
scheduler.register(Box::new(controller), 5, Some(true));
scheduler.tick_all().expect("Scheduler failed");
}
Test the example:
horus run examples/demo.rs --release
Best Practices
Package Design
Single Responsibility:
# Good: Focused packages
pid-controller # Just PID control
motion-planner # Just path planning
sensor-fusion # Just sensor fusion
# Bad: Kitchen sink package
robotics-everything # Too broad, hard to maintain
Clear Interfaces:
// Good: Simple, clear API
pub struct PIDController {
pub fn new(kp: f64, ki: f64, kd: f64) -> Self { ... }
pub fn update(&mut self, error: f64) -> f64 { ... }
}
// Bad: Complex, unclear API
pub struct Controller {
pub fn do_stuff(&mut self, x: f64, y: Option<f64>, z: &str) -> Result<Vec<f64>, Box<dyn Error>> { ... }
}
Documentation
Include comprehensive README:
# PID Controller
Production-ready PID controller for HORUS robotics framework.
## Features
- Anti-windup protection
- Derivative filtering
- Output clamping
## Installation
```bash
horus pkg install pid-controller
Usage
use pid_controller::PIDController;
let mut pid = PIDController::new(1.0, 0.1, 0.01);
let output = pid.update(error);
Examples
See examples/ directory for complete examples.
License
MIT
### Testing
**Always test before publishing:**
```bash
# Run tests
cargo test
# Run examples
horus run examples/demo.rs --release
# Build in release mode
horus run --build-only --release
Versioning Strategy
Semantic Versioning:
0.x.x- Development (expect breaking changes)1.0.0- First stable release1.x.x- Stable with backward compatibility2.0.0- Major rewrite or breaking changes
Changelog:
# Changelog
## [1.2.0] - 2025-10-09
### Added
- Anti-windup protection
- Configurable output limits
### Fixed
- Derivative kick on setpoint change
## [1.1.0] - 2025-09-15
### Added
- Derivative filtering
## [1.0.0] - 2025-08-01
- Initial stable release
Common Workflows
Creating a Package Library
# 1. Create new project as library
horus new my-sensor-lib --rust
# 2. Update Cargo.toml
[package]
name = "my-sensor-lib"
version = "0.1.0"
[lib]
name = "my_sensor_lib"
path = "src/lib.rs"
# 3. Implement in src/lib.rs
pub mod drivers;
pub mod calibration;
# 4. Add examples
mkdir examples
# Create examples/demo.rs
# 5. Test
horus run examples/demo.rs
# 6. Publish
horus auth login --github
horus pkg publish
Using Multiple Packages
# Install packages
horus pkg install pid-controller
horus pkg install motion-planner
horus pkg install sensor-fusion
# Use in your project
use pid_controller::PIDController;
use motion_planner::AStarPlanner;
use sensor_fusion::KalmanFilter;
use horus::prelude::*;
fn main() {
let mut scheduler = Scheduler::new();
// Combine multiple packages
let pid = PIDController::new(1.0, 0.1, 0.01);
let planner = AStarPlanner::new();
let filter = KalmanFilter::new();
// Register nodes...
}
Updating Dependencies
# Update specific package to newer version
horus pkg install pid-controller -v 1.3.0
# Check available versions on registry
horus pkg list pid-controller
Troubleshooting
Package Not Found
Error:
Error: Package 'nonexistent-package' not found in registry
Solutions:
# Check spelling
horus pkg list nonexistent
# Search registry for correct package name
horus pkg list correct-package
Version Conflict
Error:
Error: Version conflict
robot-controller requires motion-planner ^2.0
sensor-fusion requires motion-planner ^1.5
Solutions:
# Option 1: Update conflicting package
horus pkg install sensor-fusion -v 2.0.0 # Install compatible version
# Option 2: Pin version manually
# Edit Cargo.toml:
[dependencies]
motion-planner = "2.0" # Force version 2.0
Build Failures
Error:
Error: Failed to build package 'my-package'
Solutions:
# Clean and rebuild
horus pkg remove my-package
horus pkg install my-package
# Check dependencies via registry web interface
# Visit https://marketplace.horus-registry.dev
# Install dependencies manually if needed
horus pkg install dependency-name
Authentication Required
Error:
Error: Authentication required to publish packages
Run: horus auth login --github
Solution:
horus auth login --github
# Opens browser for GitHub OAuth
Registry Unavailable
Error:
Error: Failed to connect to registry
Solutions:
# Check internet connection
ping marketplace.horus-registry.dev
# Try again later (registry might be down)
# Use cached packages if available
ls ~/.horus/cache/
Registry API
Direct API Access
You can interact with the registry programmatically:
Search packages:
curl https://marketplace.horus-registry.dev/api/packages?q=sensor
Get package info:
curl https://marketplace.horus-registry.dev/api/packages/pid-controller
Download package:
curl -o pkg.tar.gz https://marketplace.horus-registry.dev/api/packages/pid-controller/1.2.0/download
Next Steps
- Environment Management - Freeze and restore package sets
- Authentication - Manage registry credentials
- CLI Reference - Complete command documentation