Haxo#

Role#

The Haxophone — an open-hardware electronic saxophone HAT on a Raspberry Pi Zero 2 W. A single-purpose musical instrument: nothing in the fleet depends on it, and it deliberately has no Tailscale (verifiably absent from the host config; it is reached over Wi-Fi on the LAN for the occasional rebuild).

Declared in hosts/default.nix as class rpi, tags ["rpi"], aarch64 — built via the nixos-raspberrypi flake (raspberry-pi-02.base board module), so the kernel/firmware/bootloader are a cache hit from nixos-raspberrypi.cachix.org. That is the whole point of the Zero 2 W: the original Zero W 1.1 is ARMv6, uncached, and a NixOS closure for it is a days-long emulated build (see the honest status below).

What it runs#

  • haxophone.servicepkgs.haxo-rs (packaged in pkgs/haxo-rs/), the Rust note engine: scans the HAT’s key matrix + breath-pressure sensor via rppal GPIO/I2C and drives FluidSynth — a MIDI wind controller wired straight into a softsynth. Runs as root (rppal needs raw GPIO/I2C), --prog-number 66 selects the GM sax patch, soundfont is nixpkgs’ FluidR3_GM2-2.sf2. Audio goes out the HAT’s I2S (upstream’s USB-MIDI-gadget ExecStartPre is dropped).
  • Utility packages only: fluidsynth, alsa-utils, i2c-tools, libraspberrypi. Docs/man/glances/clamav are all off — it’s a 512 MB Pi.

Hardware#

Raspberry Pi Zero 2 W — BCM2710A1 (4× Cortex-A53, aarch64), same SoC family as octopi’s Pi 3B. The Haxophone HAT is configured through nixos-raspberrypi’s structured config.txt interface (hardware.raspberry-pi.config.all, values taken from haxo-rs’s scripts/provision.sh):

  • MAX98357A I2S codec/amp (dt-overlays.max98357a, sdmode-pin = 4) — onboard analog audio is forced off so the HAT’s I2S card is the ALSA default.
  • MPXV7007DP breath-pressure sensor on I2C at 400 kHz (i2c_arm_baudrate = 400000); hardware.i2c.enable provides /dev/i2c-* and the i2c group for haxo-rs and i2cdetect.

Builds never happen on-device: nix.settings.max-jobs = 0 offloads everything to the fleet builders/substituters.

Building the SD image#

sops-nix derives the host’s age identity from its SSH host key, so the image must ship haxo’s pre-generated host key or first boot can’t decrypt the tsunami password (users.mutableUsers = false ⇒ no login at all) nor the Wi-Fi PSK. The private key is never committed — it is injected at image build time from a path you control, and the matching .pub is derived during the build:

HAXO_HOST_KEY_PATH=/secure/ssh_host_ed25519_key \
  nix build .#nixosConfigurations.haxo.config.system.build.sdImage --impure

If HAXO_HOST_KEY_PATH is unset the key step is skipped (fine for nix flake check, useless for a real flash). The image is uncompressed (sdImage.compressImage = false).

Networking#

Wi-Fi only (wlan0) on FalseBlue-Basic, with the same brcmfmac quirks as octopi: WPA2-Personal forced (authProtocols = ["WPA-PSK"] — WPA3/SAE never associates on this chip) and PMF/802.11w disabled (ieee80211w=0 — the FullMAC firmware mishandles the global pmf=1 NixOS injects; association dies at status=16). The PSK comes from the shared sops wifi secret octopi already uses. networking.interfaces is force-cleared so systemd doesn’t wait ~90 s for the eno1 that core/default.nix declares by default.

Honest status (what’s live vs. declared)#

The NixOS host config is complete and builds, but per the workaround comment in pkgs/default.nix (PR #235), the physical haxophone is still the original Pi Zero W 1.1 (ARMv6) running Raspberry Pi OS. Until the Zero 2 W swap lands, only the haxo-rs binary is cross-compiled from this flake and copied over:

nix build .#haxo-rs-armv6l
scp result/bin/haxo001 pi@haxophone:/usr/local/bin/

Outstanding before the NixOS host is real:

  1. sops onboarding — haxo’s key must be added as a recipient in .sops.yaml before it can decrypt the shared wifi PSK (called out in the host config).
  2. Host key generation + injection — the HAXO_HOST_KEY_PATH build above, then flash.
  3. Retire the haxo-rs-armv6l cross-compile workaround in pkgs/default.nix once the host is live.
  • OctoPi — the other Pi in the fleet; haxo reuses its nixos-raspberrypi pattern and its brcmfmac Wi-Fi workarounds.
  • Lafayette — the site it lives at.