Agent-controlled target machine wrangler for low-level software development.
"Paniolo" is the Hawaiian word for cowboy. The idea: an AI agent sits at the reins while you're writing bootloaders, firmware, or OS bring-up code — paniolo gives it the controls to netboot the target, watch its output, send it input, and power-cycle it without human intervention at each iteration.
| Subsystem | Commands | What it does |
|---|---|---|
| Netboot | paniolo netboot |
DHCP + TFTP + HTTP netboot over a direct USB-Ethernet link (Raspberry Pi, plus UEFI PXE / HTTP Boot for EDK2 boards) |
| Remote labs | paniolo --lab … |
Drive targets on remote control hosts transparently over SSH; one git-tracked lab file |
| Link mode | paniolo netif |
Atomically switch the link between netboot and ffx-over-IPv6 modes |
| Video | paniolo video |
HDMI capture via warm-stream daemon; on-device OCR |
| Serial | paniolo serial |
Serial console — interactive (tio) or daemon-backed with timestamped rolling log |
| Power control | paniolo power on/off, paniolo power-cycle, paniolo power-state, paniolo serial dtr/reset |
DTR-based hardware power button (J2 header) and generic shell-command hooks (on/off/cycle/state); helpers: cambrionix (Cambrionix hub ports), usbhub (off-the-shelf USB hub ports), zigplug (Zigbee smart plugs), shellyplug (Shelly Gen2+ plugs/relays over local HTTP RPC) |
| HID injection | paniolo hid |
USB keyboard/mouse injection via a generic helper hook (hidrig KB2040 injector); KVM input from the web console |
| adb (Android targets) | paniolo adb |
Drive an Android DUT over adb — console (shell/run), screen (screencap), and input — one USB cable, no capture/HID/serial rig |
| Dashboard | paniolo console |
Combined video + serial web UI; auto-starts daemons; -i <name> preselects a serial interface |
| Agent skills | paniolo skill |
List the bundled agent guides (driving a target, GUI puppeting, USB-hub power), or print one's SKILL.md for an agent to read |
Full docs live in docs/. Start with the
architecture overview for the whole-system design, then the
per-subsystem guides linked above. The tested-hardware list covers the
bench gear each subsystem is verified with. Hardware-CI integration (KernelCI/LAVA,
Fuchsia/botanist) design and the project requirements tracker are under
docs/ as well.
- macOS 10.14 (Mojave) or later, or Linux (x86-64 / arm64)
- Homebrew (macOS only — Linux uses the system package manager)
- Rust toolchain (
brew install rustupon macOS, orrustup.rson Linux) - On Linux:
sudo apt-get install pkg-config libudev-dev libclang-dev cmake nasm(make installchecks for these and tells you what's missing)
On Linux, prebuilt packages (amd64/arm64 .deb and tarball, Debian 12+ /
Raspberry Pi OS) are attached to each
GitHub Release —
sudo apt install ./paniolo_<version>_<arch>.deb, then run paniolo setup
once for group membership and the optional zigplug helper. Or build from
source:
git clone https://github.com/curtisgalloway/paniolo ~/src/paniolo
cd ~/src/paniolo
make install # paniolo CLI + daemons + OCR helper, in one stepmake install bootstraps the CLI with cargo install --path cli, then runs
paniolo setup, which compiles and installs all of paniolo's binaries. Only
the paniolo CLI lands on PATH (~/.cargo/bin); the daemons and helpers
(hdmicap, serialcap, netbootd, cambrionix, hidrig, usbhub,
shellyplug) and the OCR
helper (visionocr on macOS via swiftc, linuxocr on Linux) install into
the private libexec dir ~/.local/libexec/paniolo/bin, where paniolo finds
them without polluting your PATH — run one directly with
paniolo helper <name> [args…] (no name lists them). One static binary per
component; the core needs no Python environment. (The optional zigplug
Zigbee smart-plug helper is the one Python component — setup installs it as
a uv tool with its shim in the libexec dir.) setup also installs the bundled
agent skills into ~/.local/share/paniolo/skills (the .deb/tarball ship
them under /usr/share/paniolo/skills); paniolo skill [NAME] lists them or
prints one for an agent to read. Netboot is served by the
single-binary netbootd (Rust) engine. (On macOS, setup also installs
netbootd-bpf-helper setuid-root — one sudo — for the netbootd raw-frame
send path.) Configuration is one CLI-managed lab file
(~/.config/paniolo/lab.toml); see
docs/config-redesign.md.
Upgrading from the Python CLI? The old
make installregistered the Pythonpanioloas a uv tool; its~/.local/bin/panioloshim shadows the Rust binary in~/.cargo/bin. Remove it once:uv tool uninstall paniolo(make installwarns if a shadow is detected).
To pick up code changes after pulling or editing, just re-run it:
make install # rebuilds and reinstalls everything (idempotent)Or iterate faster with make rust (build + install the Rust crates only,
skipping the OCR/setuid/zigplug steps). make help lists every target. The
underlying commands still work directly if you prefer — note the helpers
install with --root so they land in libexec, not on PATH:
cargo install --path ~/src/paniolo/cli # if the CLI changed
cargo install --path ~/src/paniolo/hdmicap --root ~/.local/libexec/paniolo # if hdmicap changed
cargo install --path ~/src/paniolo/serialcap --root ~/.local/libexec/paniolo # if serialcap changed
cargo install --path ~/src/paniolo/netbootd --root ~/.local/libexec/paniolo # if netbootd changed (re-run `paniolo setup` to re-setuid the helper on macOS)
cargo install --path ~/src/paniolo/cambrionix --root ~/.local/libexec/paniolo # if cambrionix changed
cargo install --path ~/src/paniolo/hidrig --root ~/.local/libexec/paniolo # if hidrig changed
cargo install --path ~/src/paniolo/usbhub --root ~/.local/libexec/paniolo # if usbhub changed
cargo install --path ~/src/paniolo/shellyplug --root ~/.local/libexec/paniolo # if shellyplug changed
UV_TOOL_BIN_DIR=~/.local/libexec/paniolo/bin uv tool install --force ~/src/paniolo/zigplug # if zigplug changedUSB HID injection (paniolo hid) shells out to a helper speaking the
HID serial protocol — by default hidrig,
the client for the KB2040 injector (see docs/hid.md).
The intended use is an AI agent or script on a dev machine SSHing into the control Mac to drive the target:
# Configure target once
ssh control-mac "paniolo target add target-machine"
ssh control-mac "paniolo netboot set -t target-machine --interface en3 --tftp-root ~/pxe"
ssh control-mac "paniolo power set -t target-machine --cycle-cmd /path/to/power-cycle.sh"
# Deploy a new kernel and boot
TFTP_ROOT=$(ssh control-mac "paniolo netboot tftp-root target-machine")
scp out/kernel.img control-mac:"${TFTP_ROOT}/kernel_2712.img"
ssh control-mac "paniolo netboot start target-machine"
ssh control-mac "paniolo netboot logs -f target-machine"
# Interact with the console
ssh control-mac "paniolo serial log -t target-machine -i console --tail 50"
# Power cycle and repeat
ssh control-mac "paniolo power-cycle target-machine"Apache 2.0 — see LICENSE.