noisebell/pi/README.md

182 lines
4.5 KiB
Markdown

# 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/<timestamp>/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 <token>`.
**`GET /`**
```json
{"status": "open", "timestamp": 1710000000}
```