feat(infra): add local-dev Docker Compose stack
CI / commits (pull_request) Successful in 1m4s
CI / check (pull_request) Successful in 1m12s
CI / scan (pull_request) Successful in 1m12s
CI / a11y (pull_request) Successful in 37s
CI / perf (pull_request) Successful in 1m29s

Bring up Postgres + Redis + OTel Collector in one command so
contributors can run the BFF end-to-end without each setting up
the runtime services manually. Replaces the throwaway
`docker run postgres:17-alpine` one-liner that was in §3 of
docs/development.md.

What lands:

- `infra/local/dev.compose.yml` — three core services
  (postgres 17.2-alpine, redis 7.4-alpine,
  otel/opentelemetry-collector-contrib 0.115.0) plus two
  viewers gated behind Compose profiles (pgweb under
  `--profile dbtools`, Jaeger 1.62 under
  `--profile observability`). All ports overridable via .env;
  state in named volumes; healthchecks on the data services.
- `infra/local/.env.example` — credentials + ports template.
  POSTGRES_PASSWORD and REDIS_PASSWORD are mandatory (compose
  refuses to boot without them); other keys default sensibly.
- `infra/local/init/postgres/01-init.sql` — bootstrap SQL per
  ADR-0013: `audit_owner` / `audit_writer` / `audit_reader` /
  `audit_archiver` roles plus `audit` schema, with default
  privileges encoding the append-only contract (INSERT to
  writer, SELECT to reader, DELETE to archiver, no UPDATE /
  TRUNCATE to anyone). Applied on first Postgres boot only;
  documented re-run procedure in infra/README.md.
- `infra/local/otel-collector.yaml` — pipeline: OTLP gRPC/HTTP
  → batch → debug exporter (always) + forward to `jaeger:4317`
  (no-op when the observability profile is off; logs warn-level
  retry but doesn't block other exporters).

Surrounding docs updated:

- `infra/README.md` — new "Local-dev stack" section with
  service inventory, port table, first-time setup, operational
  tips. The `local/` row replaces the previous placeholder.
- `docs/development.md` §3 — rewritten to walk through the
  compose-based setup; cross-links to `infra/README.md` for
  the full reference. Roadmap entry for "Local infra recipe"
  removed from §8 (now implemented); "Observability dev-loop"
  entry adjusted to point at the new Jaeger profile.

Production parity is intentionally left for the on-prem
infrastructure ADR (phase 3b) — this is the dev-only stack.
This commit is contained in:
Julien Gautier
2026-05-08 18:54:22 +02:00
parent a93ee16091
commit 6123953165
6 changed files with 390 additions and 37 deletions
+48
View File
@@ -0,0 +1,48 @@
-- Bootstrap SQL for local-dev Postgres, applied on FIRST boot only
-- (Postgres image runs files in /docker-entrypoint-initdb.d once,
-- when the data volume is empty).
--
-- Implements the role + schema layout from ADR-0013 (Audit trail,
-- separated Postgres schema, append-only by Postgres role grants).
-- Production deployment manifests (future infrastructure ADR) will
-- replicate the same layout with real service accounts; in dev the
-- default `portal` user (created automatically from POSTGRES_USER)
-- is the schema owner, and the audit roles are granted to it for
-- testing role-based access patterns locally.
-- ---------------------------------------------------------------- Audit roles
-- NOLOGIN: these are permission containers, not login users. The BFF
-- connects as a login user that has been GRANTed the role.
CREATE ROLE audit_owner NOLOGIN;
CREATE ROLE audit_writer NOLOGIN;
CREATE ROLE audit_reader NOLOGIN;
CREATE ROLE audit_archiver NOLOGIN;
-- ---------------------------------------------------------------- Audit schema
-- Owned by audit_owner so DEFAULT PRIVILEGES below take effect for
-- every future table created under the schema (Prisma migration etc.).
CREATE SCHEMA audit AUTHORIZATION audit_owner;
GRANT USAGE ON SCHEMA audit TO audit_writer, audit_reader, audit_archiver;
-- ---------------------------------------------------------------- Append-only contract
-- ADR-0013 §"Append-only by role grants":
-- audit_writer → INSERT only
-- audit_reader → SELECT only
-- audit_archiver → DELETE only (used to prune past retention)
-- nobody → UPDATE, TRUNCATE
ALTER DEFAULT PRIVILEGES FOR ROLE audit_owner IN SCHEMA audit
GRANT INSERT ON TABLES TO audit_writer;
ALTER DEFAULT PRIVILEGES FOR ROLE audit_owner IN SCHEMA audit
GRANT SELECT ON TABLES TO audit_reader;
ALTER DEFAULT PRIVILEGES FOR ROLE audit_owner IN SCHEMA audit
GRANT DELETE ON TABLES TO audit_archiver;
-- ---------------------------------------------------------------- Dev convenience
-- The default `portal` superuser bypasses these grants anyway, but
-- granting the audit roles explicitly lets us test role-based access
-- with `SET ROLE audit_writer;` etc. from a psql session against the
-- dev DB. Production never grants all four to one user.
GRANT audit_owner, audit_writer, audit_reader, audit_archiver TO portal;