- sops-nix flake input, age + ssh-to-age in devShell, .sops.yaml with per-host recipients (real pubkeys still TODO until lars bootstraps). - nix/modules/gebos-secrets.nix centralises per-host secret subsets and renders /run/secrets/gebos-env via sops.templates. Service modules now read EnvironmentFile= from services.gebos.secrets.envFile instead of the placeholder /etc/gebos/secrets.env. - Falls back to a writeText env file when nix/secrets/secrets.yaml is absent, so `nix flake check` and first-boot eval work pre-bootstrap. - nix/secrets/README.md walks through age key + sops bootstrap. - Dev defaults: ingester binary falls back to local tcp/postgres URLs, process-compose adds mosquitto and wires GEBOS_MQTT_BROKER / GEBOS_POSTGRES_URL / GEBOS_POSTGRES_PASSWORD + VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY, frontend defaults to 127.0.0.1:8000 in DEV. Refs #23 (comment #110).
2.5 KiB
Gebos
IoT telemetry stack on bare-metal NixOS.
This repo is a monorepo containing all code, NixOS modules, host configurations,
and CI/CD pipelines for the project. Everything is one flake.nix.
Architecture
Three hosts (see issue #21 for the full design discussion):
| Host | Role | Public hostname |
|---|---|---|
mqtt-ingest |
HiveMQ CE + Go MQTT→Postgres ingester | ingest.ge-bos.de |
db-host |
Postgres 17 + TimescaleDB (telemetry+auth) | internal only |
app-host |
Caddy + Kong + Supabase (compose) + SPA | app.ge-bos.de, api.ge-bos.de |
Public REST surface is PostgREST + SQL /rpc/ functions, fronted by Kong, TLS-terminated by Caddy.
Supabase Studio is bound to 127.0.0.1 on app-host — reach it with ssh -L 3000:127.0.0.1:3000 app-host.
Repo layout
flake.nix
frontend/ # Vite + React SPA
ingester/ # Go MQTT → Postgres
nix/
modules/ # NixOS modules (one per service)
hosts/ # nixosConfigurations: mqtt-ingest, db-host, app-host
supabase/ # vendored Supabase docker-compose, db init SQL
dev/ # process-compose for local development
deploy.nix # deploy-rs node map
.gitea/workflows/ # CI + CD
Local development
nix run .#dev
Brings the full stack up on one machine via process-compose-flake (Postgres, Supabase compose, Kong, Caddy, ingester, Vite dev server). NixOS required.
Deployment
deploy-rs from a Gitea Actions runner on push to main. Closures are built
once, copied to each host, activated with auto-rollback. Order: db-host →
app-host → mqtt-ingest.
nix run github:serokell/deploy-rs -- .#db-host # one host
nix run github:serokell/deploy-rs -- . # all hosts
Secrets
sops-nix + age.
Single encrypted file at nix/secrets/secrets.yaml; each host decrypts only the
keys it needs at activation, rendered into a tmpfs env file consumed by systemd
EnvironmentFile=. Plaintext never enters the Nix store. See
nix/secrets/README.md for bootstrap and rotation.
Local dev needs no secrets bootstrap — nix run .#dev, go run ./ingester,
and npm --prefix frontend run dev all default to the local dev stack values
defined in nix/dev/process-compose.nix.