Add infrastructure skeleton for review (refs #21)
check / flake-check (pull_request) Has been cancelled

Lays out the agreed shape from the design discussion: one flake at the
root with three nixosConfigurations (mqtt-ingest, db-host, app-host),
vendored Supabase docker-compose pointed at the external db-host,
Caddy → Kong → PostgREST/GoTrue, a Go MQTT→Postgres ingester stub,
deploy-rs node map, and a Gitea Actions workflow that deploys in
db → app → ingest order on push to main.

No real implementation yet — every host module has TODOs marking the
gaps (real hardware config, actual hostnames, HiveMQ packaging,
vendor the full upstream Supabase compose, etc.).
This commit is contained in:
Klaus
2026-05-27 17:23:02 +00:00
parent 71d74d3b83
commit 2d57db8182
34 changed files with 1005 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
-- Gebos Postgres bootstrap.
-- Runs once via services.postgresql.initialScript on db-host.
--
-- Sets up:
-- * Extensions Supabase + TimescaleDB need
-- * Supabase's expected schemas and admin roles
-- * The gebos_ingest role (BYPASSRLS) used by the MQTT ingester
-- * A telemetry hypertable with tenant_id column for RLS
--
-- TODO: split into ordered files once it grows. For the skeleton, one file is fine.
create extension if not exists timescaledb;
create extension if not exists pgcrypto;
create extension if not exists pgjwt;
create extension if not exists pg_graphql;
create extension if not exists pgsodium;
-- create extension if not exists supabase_vault; -- TODO: vendor or package
-- Supabase schemas
create schema if not exists auth;
create schema if not exists storage;
create schema if not exists _analytics;
create schema if not exists _supavisor;
-- Roles Supabase services connect as. Passwords come from secrets.env, not here.
do $$ begin
create role anon nologin noinherit;
exception when duplicate_object then null; end $$;
do $$ begin
create role authenticated nologin noinherit;
exception when duplicate_object then null; end $$;
do $$ begin
create role service_role nologin noinherit bypassrls;
exception when duplicate_object then null; end $$;
do $$ begin
create role authenticator noinherit login;
grant anon, authenticated, service_role to authenticator;
exception when duplicate_object then null; end $$;
do $$ begin
create role supabase_admin login createrole createdb replication bypassrls;
exception when duplicate_object then null; end $$;
do $$ begin
create role supabase_auth_admin login createrole;
grant usage on schema auth to supabase_auth_admin;
exception when duplicate_object then null; end $$;
-- Ingester writes telemetry; bypasses RLS because at write time no user is
-- authenticated. Trust boundary is the broker ACL ("who can publish to what
-- tenant's topic"), not the database.
do $$ begin
create role gebos_ingest login bypassrls;
exception when duplicate_object then null; end $$;
-- Telemetry hypertable. Tenant lives on the row so standard RLS works.
create table if not exists public.telemetry (
ts timestamptz not null,
tenant_id uuid not null,
device_id uuid not null,
metric text not null,
value double precision,
payload jsonb
);
select create_hypertable('public.telemetry', 'ts', if_not_exists => true);
create index if not exists telemetry_tenant_device_ts_idx
on public.telemetry (tenant_id, device_id, ts desc);
alter table public.telemetry enable row level security;
create policy telemetry_tenant_isolation on public.telemetry
for select
using (tenant_id = current_setting('app.tenant_id', true)::uuid);
grant select on public.telemetry to authenticated;
grant insert on public.telemetry to gebos_ingest;