horus.toml: Single Source of Truth
Every HORUS project has one config file: horus.toml. It replaces the need for separate Cargo.toml and pyproject.toml files. You declare your dependencies once, and HORUS generates everything else.
The Problem
Traditional robotics projects need different config files for each language:
| Language | Config Files |
|---|---|
| Rust | Cargo.toml + Cargo.lock |
| Python | pyproject.toml + requirements.txt |
| Mixed | All of the above |
A robot using Python for ML and Rust for control needs 3+ config files. Adding a dependency means knowing which file to edit.
The HORUS Solution
One file. All languages. All dependencies.
# horus.toml — the only config file you edit
[package]
name = "my-robot"
version = "0.1.0"
[robot]
name = "turtlebot"
description = "robot.urdf" # Path to URDF file
simulator = "sim3d" # Simulator plugin (default)
[dependencies]
# Rust (auto-detected as crates.io)
serde = { version = "1.0", source = "crates.io", features = ["derive"] }
# Python (auto-detected as PyPI)
numpy = { version = ">=1.24", source = "pypi" }
[hardware]
lidar = { use = "rplidar", port = "/dev/ttyUSB0", sim = true }
imu = { use = "bno055", bus = 1, sim = true }
[scripts]
sim = "horus sim start --world warehouse"
deploy = "horus deploy pi@robot --release"
[hooks]
pre_run = ["fmt", "lint"] # Auto-format and lint before every run
When you run horus build, HORUS reads horus.toml and generates native build files:
- Rust deps →
.horus/Cargo.toml - Python deps →
.horus/pyproject.toml
You never see or edit these generated files.
The .horus/ Directory
Every HORUS project has a .horus/ directory containing generated files and build artifacts. It's gitignored and fully managed by horus.
my_project/
├── horus.toml ← You edit this
├── src/
│ ├── main.rs
│ └── main.py
└── .horus/ ← Generated (don't touch)
├── Cargo.toml ← From horus.toml Rust deps
├── pyproject.toml ← From horus.toml Python deps
├── target/ ← Rust build artifacts
└── packages/ ← Cached registry packages
Never edit files inside .horus/. They are regenerated from horus.toml every time you build. If you need to change a dependency, edit horus.toml and run horus build.
If .horus/ gets corrupted, just delete it:
horus clean --all # Remove everything, regenerated on next build
How It Works
| Command | What happens |
|---|---|
horus add serde | Detects crates.io → adds to horus.toml → regenerates .horus/Cargo.toml |
horus add numpy | Detects PyPI → adds to horus.toml → regenerates .horus/pyproject.toml |
horus build | Reads horus.toml → generates all build files → builds all languages |
horus test | Runs cargo test + pytest (all from horus.toml) |
horus remove X | Removes from horus.toml → regenerates affected build files |
Comparison
| Traditional | HORUS | |
|---|---|---|
| Config files | 3+ per language | 1 (horus.toml) |
| Add a dep | Edit the right file, know the syntax | horus add NAME |
| Build | cargo build + pip install + ... | horus build |
| Test | cargo test + pytest | horus test |
| Deploy | Manual cross-compile scripts | horus deploy pi@robot |
| Onboarding | Learn 3 build systems | Learn 1 CLI |
Workspace Projects
For projects that contain multiple crates (e.g., a driver library and a binary that uses it), horus.toml supports a [workspace] section. This lets you manage several crates under a single project root while keeping one unified config file.
# horus.toml — workspace with multiple crates
[package]
name = "my-robot"
version = "0.1.0"
type = "lib" # "lib" for libraries, omit for binaries
[workspace]
members = [
"crates/driver",
"crates/controller",
"crates/my-robot-bin",
]
[dependencies]
serde = { version = "1.0", source = "crates.io", features = ["derive"] }
Each workspace member directory contains its own horus.toml with a [package] section. The root horus.toml defines shared dependencies and the member list. When you run horus build, all members are compiled together, and you can target a specific member with horus build --package controller.
The type = "lib" field in [package] marks a crate as a library (no binary entry point). This is useful for shared code that other workspace members depend on but that is not run directly.
For a complete guide on setting up and working with multi-crate workspaces, see Multi-Crate Workspaces.
Next Steps
- Configuration Reference — Full field reference for horus.toml
- Quick Start — Build your first project
- CLI Reference — All horus commands
All Sections
| Section | Purpose |
|---|---|
[package] | Project name, version, metadata |
[robot] | Robot name, URDF path, simulator selection |
[dependencies] | Project deps from any source (crates.io, PyPI, system, registry) |
[dev-dependencies] | Test/development-only dependencies |
[hardware] | Hardware node configuration (use field + params, sim for simulation swap) |
[scripts] | Custom project commands |
[hooks] | Pre/post action hooks (pre_run, pre_build, pre_test, post_test) |
[ignore] | File/directory exclusion patterns |
enable | Capability flags (cuda, gpio, etc.) |
[cpp] | C++ build configuration |
[workspace] | Multi-crate workspace |
See Configuration Reference for complete field-level documentation.
Design Decisions
Why TOML Instead of YAML
YAML is common in robotics (ROS2 launch files, parameter files), but it has well-documented pitfalls for configuration: implicit type coercion (yes becomes true, 3.10 becomes 3.1), significant whitespace that causes silent errors, and multiple ways to express the same thing. TOML has an unambiguous grammar — every value has an explicit type, indentation is not significant, and there is one canonical way to write each structure. HORUS still uses YAML for launch configs and parameter files where its flexibility is useful, but the project manifest uses TOML because dependency versions and package metadata must be parsed unambiguously.
Why One File Instead of Per-Language Configs
A robot project using Rust for control and Python for ML traditionally needs Cargo.toml, pyproject.toml, and possibly requirements.txt — each with different syntax, different dependency resolution, and different tooling. When a team member adds a dependency, they need to know which file to edit. horus.toml is the single source of truth: all dependencies are declared once with an explicit source field (crates.io, pypi, system, git, path). HORUS generates the native build files (Cargo.toml, pyproject.toml) into .horus/ automatically. One file to learn, one file to review in PRs, one file to validate in CI.
Why Generated Build Files in .horus/ Directory
Native build tools (cargo, pip) need their own config files to function. Rather than forcing users to maintain both horus.toml and Cargo.toml in sync, HORUS generates the native files into a .horus/ directory that is gitignored and fully managed. Users never edit these files. This means cargo build and pip install still work under the hood with their standard tooling — HORUS does not replace build systems, it generates their input. If .horus/ gets corrupted, horus clean --all deletes it and the next build regenerates everything from horus.toml.
Trade-offs
| Area | Benefit | Cost |
|---|---|---|
| TOML format | Unambiguous parsing; no implicit type coercion; explicit types for dependency versions | Less familiar to teams coming from YAML-heavy ROS2 workflows |
| Single manifest | One file to learn, edit, and review; horus add works for any language | Must specify source for non-default package registries (e.g., source = "pypi" for Python deps) |
| Generated build files | Native tooling (cargo, pip) works unchanged; no custom build system to learn | Cannot use Cargo.toml features not exposed by horus.toml without escape hatches; generated files must not be edited |
.horus/ directory | Clean project root; generated artifacts are gitignored and disposable | Extra directory to understand; new contributors may be confused by the absence of Cargo.toml in the project root |
horus add/remove CLI | No need to manually edit TOML or know per-language syntax | Dependency resolution depends on HORUS tooling — cargo add and pip install do not update horus.toml |
| Workspace support | Multi-crate projects with shared dependencies and a single root config | Workspace members still need their own horus.toml with [package] — not fully flat |
See Also
- Package Management — Dependencies and lockfile
- CLI Reference —
horus build,horus add,horus run - Choosing a Language — Rust vs Python project setup