# Pi Rust service and deployment workflow for the Raspberry Pi at Noisebridge. The current recommended setup is: 1. run Raspberry Pi OS Lite on the Pi 2. keep the Pi itself free of Nix 3. build a static `aarch64` Noisebell binary on your laptop with Nix 4. copy the binary, secrets, and systemd service to the Pi over SSH This avoids the Raspberry Pi Zero 2 W NixOS boot issues while still keeping the application build reproducible. ## What stays on Raspberry Pi OS - bootloader - kernel - firmware - Wi-Fi and local networking - SSH base access - Tailscale package/runtime - Avahi package/runtime ## What Nix manages - building a static `noisebell` binary for `aarch64-linux` - the exact app binary you deploy - encrypted secrets in the repo - repeatable deployment from your laptop ## Initial Pi OS setup ### 1. Flash Raspberry Pi OS Lite ```sh curl -L "https://downloads.raspberrypi.org/raspios_lite_arm64_latest" | xz -d -c | sudo dd of=/dev/sdb bs=16M conv=fsync status=progress && sync ``` ### 2. Configure the flashed SD card Configure it for: - Wi-Fi on `Noisebridge` - SSH enabled - serial enabled if you want a recovery console The helper script is: ```sh sudo scripts/configure-pios-sd.sh /run/media/jet/bootfs /run/media/jet/rootfs ``` This setup expects SSH key login for user `pi`; it does not configure a password. ### 3. Boot the Pi and verify SSH After boot, verify SSH works: ```sh ssh pi@noisebell-pi.local ``` ## Add the Pi host key to age recipients The deploy flow decrypts secrets locally on your laptop, but the Pi host key should still be a recipient for the Pi-facing secrets so the repo stays accurate. Grab the Pi host key: ```sh ssh-keyscan noisebell-pi.local 2>/dev/null | grep ed25519 ``` Add that key to `secrets/secrets.nix` for: - `pi-to-cache-key.age` - `cache-to-pi-key.age` - `tailscale-auth-key.age` Then refresh recipients if needed: ```sh cd secrets agenix -r ``` ## Edit secrets ```sh cd secrets agenix -e pi-to-cache-key.age agenix -e cache-to-pi-key.age agenix -e tailscale-auth-key.age ``` These stay encrypted in git. The deploy script decrypts them locally on your laptop and copies the plaintext files to the Pi as root-only files. ## Deploy to Raspberry Pi OS From your laptop: ```sh scripts/deploy-pios-pi.sh pi@noisebell-pi.local ``` If you only know the IP: ```sh scripts/deploy-pios-pi.sh pi@10.21.x.x ``` That script: 1. builds `.#packages.aarch64-linux.noisebell-static` locally 2. decrypts the Pi-facing secrets locally with `agenix` 3. uploads the binary and secrets to the Pi 4. installs Tailscale and Avahi if needed 5. writes `/etc/noisebell/noisebell.env` 6. installs `noisebell.service` 7. enables and starts the service 8. runs `tailscale up` with the decrypted auth key ## Files written on the Pi The deploy script creates: - `/opt/noisebell/releases//noisebell` - `/opt/noisebell/current` -> current release symlink - `/etc/noisebell/pi-to-cache-key` - `/etc/noisebell/cache-to-pi-key` - `/etc/noisebell/tailscale-auth-key` - `/etc/noisebell/noisebell.env` - `/etc/systemd/system/noisebell.service` All secret files are root-only. ## Tailscale Tailscale is kept on Raspberry Pi OS rather than NixOS. The deploy script: - installs the Tailscale package if missing - enables `tailscaled` - runs `tailscale up --auth-key=... --hostname=noisebell-pi` So Tailscale stays part of the base OS, while its auth key is still managed as an encrypted `age` secret in this repo. ## Later updates Normal iteration is just rerunning the deploy script: ```sh scripts/deploy-pios-pi.sh pi@noisebell-pi.local ``` That rebuilds the binary locally, uploads a new release, refreshes secrets, and restarts the service. ## Service configuration The deployed service uses these environment variables: | Variable | Default | Description | |---|---|---| | `NOISEBELL_GPIO_PIN` | `17` | GPIO pin number | | `NOISEBELL_DEBOUNCE_MS` | `50` | Debounce delay in milliseconds | | `NOISEBELL_PORT` | `80` | HTTP server port | | `NOISEBELL_ENDPOINT_URL` | required | Webhook URL to POST state changes to | | `NOISEBELL_RETRY_ATTEMPTS` | `3` | Webhook retry count | | `NOISEBELL_RETRY_BASE_DELAY_SECS` | `1` | Exponential backoff base delay | | `NOISEBELL_HTTP_TIMEOUT_SECS` | `10` | Outbound request timeout | | `NOISEBELL_BIND_ADDRESS` | `0.0.0.0` | HTTP bind address | | `NOISEBELL_ACTIVE_LOW` | `true` | Low GPIO = door open | ## API All endpoints require `Authorization: Bearer `. **`GET /`** ```json {"status": "open", "timestamp": 1710000000} ```