2026-10-06_identity-infra-compose_spec

Spec — Wine2You Identity Stack: Infra Compose (task 1)

Date: 2026-10-06 Repo: digid-co/wine2you Target host: Oracle OCI wine2you-dev-zrh (ARM aarch64, 2 OCPU / 12 GB, Oracle Linux 9.8, Docker 29.8.2, /opt/compose)

1. Goal & Boundary

Deliverable: a self-contained Docker Compose stack that brings up the four backing services of the Wine2You identity/authorization/API-key architecture, version-pinned and ARM-ready, with a bootstrap script and a README. No application code in this task.

In scope:

Out of scope (do NOT build here):

2. Type & API Contracts

Required services, ports, images (ARM64-compatible; pin exact tags):

Service Image (pin) Ports (host→container) Persistence
postgres postgres:16-alpine 127.0.0.1:5432→5432 named volume pgdata
zitadel ghcr.io/zitadel/zitadel:latest (pin to a concrete tag at build time; ARM) 127.0.0.1:8080→8080 uses postgres
openfga openfga/openfga:latest (pin concrete tag; ARM) 127.0.0.1:8081→8080 (http), 127.0.0.1:8082→8081 (grpc) uses postgres
unkey record the chosen self-host path + pinned images in the README (Unkey self-host uses multiple images: api/dashboard/worker + its own DB) 127.0.0.1:8083→8080 its own DB (MySQL or libSQL)

Networking: one user-defined bridge network w2y-identity. All host port bindings are 127.0.0.1-only (nothing publicly exposed; the box reaches them via tailnet/SSH). restart: unless-stopped on every service. Healthchecks on postgres and openfga minimum.

Postgres databases to create (via bootstrap/init-databases.sql or ZITADEL's own init): zitadel, openfga, w2y_module — owner w2y, password from ${POSTGRES_PASSWORD}.

Environment contract (.env.example — keys only, no values):

POSTGRES_PASSWORD=
ZITADEL_MASTERKEY=
ZITADEL_EXTERNALDOMAIN=
ZITADEL_EXTERNALSECURE=
OPENFGA_DATASTORE_URI=
UNKEY_MASTER_KEY=
UNKEY_DATABASE_URI=

ZITADEL must run with its supported Postgres config vars (e.g. ZITADEL_DATABASE_POSTGRES_*), not a hand-rolled DSN, per upstream docs.

3. File Execution Matrix

Action Path
Create infra/identity/compose.yaml
Create infra/identity/.env.example
Create infra/identity/bootstrap/init-databases.sql
Create infra/identity/README.md
Modify .gitignore (add infra/identity/.env)

4. Verification & Testing Strategy

Run on the target host after docker compose up -d:

cd infra/identity
docker compose config -q                      # schema valid
docker compose ps                             # all services Up/healthy
curl -sf http://127.0.0.1:8080/debug/health   # ZITADEL -> 200
curl -sf http://127.0.0.1:8081/healthz        # OpenFGA  -> 200 {"status":"SERVING"}
docker compose exec -T postgres psql -U w2y -l | grep -E 'zitadel|openfga|w2y_module'

Record the exact pinned image tags in the README.

Constraints: ARM64 (Oracle Ampere) — every image must have an arm64 manifest; verify with docker manifest inspect <image> before pinning. Pin concrete tags (no floating latest in the delivered file). Never commit secrets.