GitHub - Peppy-bot/peppy: Peppy: The simplified robotics framework

GitHub

8 min read Original article ↗

Tests

Peppy is a modern robotics middleware framework designed for robots. Similar to ROS 2, it provides a distributed communication layer for robotic systems with a big focus on ease of use and explicit configuration.

Full documentation lives at docs.peppy.bot.

🛠️ Installation

curl -fsSL https://peppy.bot/install.sh | sh

The installer puts the peppy CLI on your PATH, registers the background service that builds and supervises nodes (systemd on Linux, launchd on macOS), and configures the Apptainer container runtime used by container nodes. Check that both the CLI and the service are up with:

Peppy runs on Ubuntu 24.04 or later and the Linux distributions built on it, on Debian 13 or later (x86_64/aarch64, tested on Ubuntu 24.04), on Raspberry Pi OS (64-bit, based on Debian 13 or later), and on macOS (Apple Silicon). Of the Raspberry Pi boards, it runs on the Raspberry Pi 5, the Pi 500 and the Compute Module 5. Other Linux distributions, Fedora and Arch Linux among them, are not supported, and neither are Ubuntu 22.04, Debian 12, 32-bit systems and the older Raspberry Pi boards: the installer stops on them before it changes anything.

The installation guide covers version pinning (PEPPY_VERSION), skipping the service install (PEPPY_NO_SERVICE_INSTALL), and managing the service afterwards.

🤖 Try it

The quickstart takes you from nothing installed to a simulated bimanual OpenArm you can drive from your browser, without cloning a repository or writing any code:

peppy stack launch openarm_simulation --with mujoco

That single command adds, builds, and starts every node in the selection, in dependency order, under the background service.

🚀 Key Features

  • Real-time Communication: Low-latency message passing between nodes thanks to Zenoh
  • Three interface kinds: topics for pub/sub streams, services for request/response, and actions for long-running goals with feedback and cancellation
  • Quality of Service: Configurable reliability and performance settings
  • Language Agnostic: Support for Python and Rust (more languages will be supported in the future with C being the next on the list)
  • Cross-platform: Linux and macOS support
  • Supervised node stacks: A background daemon builds, starts, health-checks, and restarts the nodes of a stack; launch files declare a whole robot in one document
  • Container nodes: Nodes can ship as containers built and run on Apptainer, with no change to how they are wired
  • Node repositories: Resolve nodes and launchers by name from repositories you or others publish
  • Explicit over implicit: Every node communication or feature of the framework is controlled through explicit configuration. This allows things like output messages of one node to break the code of other nodes depending on it to avoid implicit crashes at runtime.
  • Not opinionated on build tools: Peppy doesn't force any tool upon its developers. The peppylib library however, is available only in a few supported languages.

Non-goals:

  • We do not aim for API stability between releases until 1.0, preferring to iterate quickly and refine the API as much as possible. But we do follow SemVer.

🧰 The CLI

Command group What it does
peppy node Scaffold, add, build, run, inspect, and stop individual nodes
peppy stack Launch or build a stack from a launcher, list what is running, benchmark interface latency
peppy repo Manage the repositories nodes and launchers are resolved from
peppy container Check and repair the Apptainer container prerequisites
peppy platform Sign in to the platform, enroll this machine in a project's cloud router, and show its status
peppy service Install, serve, stop, uninstall, and reset the background service
peppy info Print the CLI version, container setup, and daemon info
peppy --version Print the CLI version alone, without contacting the daemon

📚 Documentation

LLM-friendly versions of the documentation are available at /llms.txt and /llms-full.txt.

🔨 Building from source

You only need this to work on Peppy itself. To use Peppy, install it with the command above.

New machine setup

Peppy builds apptainer and its bundled squashfuse from source, so the host needs their build dependencies. On Ubuntu:

sudo apt-get install -y make gcc g++ pkg-config squashfs-tools cryptsetup curl ca-certificates \
  libseccomp-dev libfuse3-dev zlib1g-dev liblzo2-dev liblz4-dev liblzma-dev libzstd-dev fuse2fs uidmap

That list mirrors APPTAINER_BUILD_DEPS in crates/containers-internal/build.rs, which the build script asserts before it starts, plus three the constant does not carry: fuse2fs, which apptainer needs at run time to mount EXT3 images; uidmap, which provides the newuidmap that fakeroot needs; and g++, which apptainer's mconfig probes for and which Ubuntu's gcc package does not pull in. macOS builds apptainer inside Lima instead, so the host needs none of them.

Then the toolchains, each from its own project's recommended installer:

  • Rust, with clippy (rustup). Track current stable: the dependency graph moves with it. clippy is a test dependency rather than a lint here, because the generator suites run cargo clippy --all-targets -- -D warnings over the crates they generate.
  • Go (go.dev). containers-internal compiles apptainer with the go it finds on PATH, and apptainer's mconfig enforces a minimum of its own that Ubuntu's golang-go does not meet.
  • pixi (pixi.sh), at or above the requires-pixi in scripts/pixi.toml. generator-internal's build script builds the embedded peppylib .so through a bare pixi run.
  • uv (astral.sh/uv). The Python generator tests and the release scripts exec a bare uv.
  • qemu (apt, or Homebrew with Lima on macOS). The release-script tests boot Lima VMs.
  • Docker, with buildx (apt on Ubuntu; on macOS choose your own runtime). The multi-daemon suite builds its daemon image with docker buildx build.
  • Node.js, only for the docs site in docs/; its package.json names the major it wants.

When Go is installed from the tarball, add /usr/local/go/bin to your ~/.profile. Docker group membership only applies to new logins, so log out and back in before running the multi-daemon suite.

CI runners

CI runs on RunsOn runners: each job gets an EC2 instance of its own, sized by the job's runs-on: label, and the instance is discarded when the job ends. The CI jobs shape the org runner with labels in their workflow; .github/runs-on.yml defines only the arm64 runner, which the aarch64 release stages and the aarch64 install archive of a pull request build on, since RunsOn reads that file from dev alone. The exceptions are the install.sh tests, which run on GitHub-hosted runners because they must start from the stock image a user's machine starts from, and the macOS release stages, which run on Blacksmith because RunsOn has no macOS. Every RunsOn job boots peppy-ubuntu24, or on the aarch64 jobs its arm64 build peppy-ubuntu24-aarch64. Both are built weekly with setup_machine.sh --ci-runner, the script that provisions a development machine, so they carry everything listed above, plus two things beyond it:

  • passwordless sudo for the runner's user. Each container suite installs a peppy release under the job's own directory and runs peppy container setup, which writes an AppArmor profile keyed to that install's path, and a job has no terminal for sudo to prompt at.
  • the aarch64 cross toolchain (rustup target add aarch64-unknown-linux-gnu and gcc-aarch64-linux-gnu) that the cross-check job builds with.

The workflows install nothing on those images: the first step of every cargo job stops the run naming any tool that is missing. A cargo suite keeps its build cache (its target directory, the cargo registry and the build scripts' tool cache in ~/.peppy/tmp) on a sticky disk of its own, which RunsOn restores from the newest snapshot of the same branch, or of dev for a branch's first run, and snapshots again after the job. A job that finds its disk more than half full empties the caches first and builds cold, so a build always has room.

Build

Install cargo & Rust if you have not already, then keep the toolchain current with:

To build Peppy, just type the following:

cargo build --release --all-targets

The public-facing crates (config, peppylib, pmi, and friends) live in peppy-shared/, a sealed tree this workspace depends on by path. The tree is a workspace of its own, excluded from this one, and the dependency runs one way only: nothing in it may depend on crates/, which is what lets platform-backend consume it on its own. peppy-shared/build-helpers/tests/sealed_tree.rs holds every manifest and symlink in the tree to that rule, and CI runs the tree's suites from a checkout holding nothing but the tree, so a reach a manifest cannot show has nothing to resolve to.

Test

The workspace's default-members covers crates/* only, so the slow documentation integration tests are excluded from that run. Run them explicitly with:

cargo test -p docs-integration-tests

The peppy-shared tree holds a workspace of its own, so its suites run from there:

cd peppy-shared
cargo test --workspace --exclude peppylib-py --locked

CI runs all of these, plus the feature-gated container and multi-daemon end-to-end suites and the release-scripts tests. See .github/workflows/tests.yml for the exact commands.

📄 License

Peppy is licensed under the Business Source License 1.1. You may make production use of it, provided that use does not include offering a product or service to third parties whose value derives primarily from Peppy. On 2031-01-01 the license converts to Apache License, Version 2.0.

The peppy-shared/ directory is licensed separately, under the Apache License, Version 2.0.