4 KiB
Noisebell
Door status monitor for Noisebridge. A Raspberry Pi watches a door sensor and reports open/closed state; remote services cache the data and fan it out to Discord and an Atom feed.
Architecture
Pi (door sensor) ──webhook──▸ Cache Service ──webhook──▸ Discord Bot
│ │
polls Pi ◂──┘ Atom feed ◂──┘
RSS Service
The Pi runs a small HTTP service that reads GPIO and exposes GET / (status) and GET /info. It also pushes state changes to the cache service via webhook.
The Cache Service is the central hub. It polls the Pi for status and info, stores everything in SQLite, and forwards state changes to downstream webhooks (Discord, etc.). It exposes a read API for consumers.
The Discord Bot receives webhooks from the cache service and posts embeds to a Discord channel.
The RSS Service fetches history from the cache service and serves an Atom feed of door events from the last 7 days.
Services
| Service | Crate | Default Port | Description |
|---|---|---|---|
| Cache | noisebell-cache |
3000 | Polls Pi, caches state in SQLite, forwards webhooks |
| Discord | noisebell-discord |
3001 | Posts door status embeds to Discord |
| RSS | noisebell-rss |
3002 | Serves Atom feed of recent door events |
Cache API
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/status |
None | Current door status (status, timestamp, last_seen) |
GET |
/info |
None | Pi system info (JSON blob) |
GET |
/history |
None | Last 100 state changes |
POST |
/webhook |
Bearer token | Inbound webhook from Pi |
GET |
/health |
None | Returns 200 OK |
Building
All remote services live in a Cargo workspace under remote/.
cd remote
cargo build --release
Or with Nix:
cd remote
nix build .#noisebell-cache
nix build .#noisebell-discord
nix build .#noisebell-rss
NixOS Configuration
The flake at remote/flake.nix exports NixOS modules. Example configuration:
{
inputs.noisebell.url = "git+https://git.extremist.software/jet/noisebell?dir=remote";
outputs = { self, nixpkgs, noisebell, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
noisebell.nixosModules.default
({ ... }: {
services.noisebell-cache = {
enable = true;
domain = "cache.noisebell.example.com";
piAddress = "http://noisebell-pi:80";
piApiKeyFile = "/run/secrets/noisebell-pi-api-key";
inboundApiKeyFile = "/run/secrets/noisebell-inbound-api-key";
outboundWebhooks = [
{
url = "http://localhost:3001/webhook";
secretFile = "/run/secrets/noisebell-discord-webhook-secret";
}
];
};
services.noisebell-discord = {
enable = true;
domain = "discord.noisebell.example.com";
discordTokenFile = "/run/secrets/noisebell-discord-token";
channelId = "123456789012345678";
webhookSecretFile = "/run/secrets/noisebell-discord-webhook-secret";
};
services.noisebell-rss = {
enable = true;
domain = "rss.noisebell.example.com";
cacheUrl = "http://localhost:3000";
};
})
];
};
};
}
Secrets
| Secret | Used by | Description |
|---|---|---|
piApiKeyFile |
Cache | Bearer token for authenticating to Pi GET endpoints |
inboundApiKeyFile |
Cache | Bearer token the Pi uses when POSTing to /webhook |
outboundWebhooks[].secretFile |
Cache | Bearer token sent with outbound webhook POSTs |
discordTokenFile |
Discord | Discord bot token |
webhookSecretFile |
Discord | Must match the cache's outbound webhook secret |