2026-10-06_identity-infra-compose_spec-v2

Spec — Wine2You Identity Stack: ZITADEL + OpenFGA compose (task 1, rev 2)

Date: 2026-10-06 (rev 2 — supersedes 2026-10-06_identity-infra-compose_spec.md) Repo: digid-co/wine2you → infra/identity/ Target host: contabo1 (8 vCPU / 23 GB / 144 GB free, Docker 29.8.2) — the shared-services host (hosting rule). NOT the Oracle dev box.

Revision note (why this changed)

1. Goal & Boundary

Deliverable: a Docker Compose stack bringing up ZITADEL + OpenFGA with their own Postgres, version-pinned, plus README + bootstrap SQL. No application code.

In scope: infra/identity/compose.yaml (postgres, zitadel, openfga); .env.example; bootstrap/init-databases.sql; README.md; .gitignore additions.

Out of scope: Unkey (Cloud), the user-mgmt module, storefront/Medusa/Meilisearch, Cloudflare Tunnel, TLS, reverse proxy, any ZITADEL/OpenFGA content.

2. Type & API Contracts

Service Image (pin concrete tag) Ports (host→container) Persistence
postgres postgres:16-alpine 127.0.0.1:5432:5432 volume w2y_pgdata
zitadel ghcr.io/zitadel/zitadel:<concrete-tag> 127.0.0.1:8080:8080 uses postgres
openfga openfga/openfga:<concrete-tag> 127.0.0.1:8081:8080 (http), 127.0.0.1:8082:8081 (grpc) uses postgres

.env.example keys (no values):

POSTGRES_PASSWORD=
ZITADEL_MASTERKEY=
ZITADEL_EXTERNALDOMAIN=
ZITADEL_EXTERNALSECURE=
ZITADEL_DATABASE_POSTGRES_HOST=postgres
ZITADEL_DATABASE_POSTGRES_PORT=5432
ZITADEL_DATABASE_POSTGRES_DATABASE=zitadel
ZITADEL_DATABASE_POSTGRES_USER_USERNAME=w2y
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD=
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME=postgres
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD=
OPENFGA_DATASTORE_ENGINE=postgres
OPENFGA_DATASTORE_URI=
# Unkey is CLOUD (not deployed here):
UNKEY_ROOT_KEY=
UNKEY_API_URL=https://api.unkey.com

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

cd infra/identity
docker compose config -q          # schema valid (no running daemon required)
grep -n "127.0.0.1:" compose.yaml # every published port is loopback-bound

Record exact pinned tags + the contabo1 target in the README. Do not start containers. Prefer multi-arch (linux/arm64 + linux/amd64) tags; if docker manifest inspect is unavailable, say so.

Constraints: pin concrete tags (no floating latest); never commit secrets; no Unkey container.