Infraestructura completa — custodiam-infra¶
Orquestación del stack completo: PostgreSQL + Keycloak + API + Web + ntfy, con tres modos mutuamente excluyentes (dev / tunnel / prod) y just como interfaz preferida.
Decisiones arquitectónicas relevantes
- ADR-007 — Docker Compose como orquestador local (no Kubernetes).
- ADR-009 — 2 bases de datos (
custodiampara API,custodiam_kcpara Keycloak) en una sola instancia PostgreSQL. - ADR-019 — Gestión de secretos con sops + age.
- ADR-020 — Tres modos de despliegue mutuamente excluyentes (dev / tunnel / prod) con guard de cross-mode.
- ADR-022 — Cloudflare Tunnel para exponer el stack vía HTTPS sin abrir puertos.
Requisitos¶
- Docker Desktop 4.x (instalación aquí)
just1.40+ (recomendado; los scripts shell siguen funcionando sinjust)- Clave age en
~/.config/sops/age/keys.txtpara descifrar el.env.sopsdel repo (opcional; existe fallback con.envplano)
Clonar y arrancar¶
git clone https://github.com/custodiam/custodiam-infra.git
cd custodiam-infra
# Opción A — recomendada: usa el .env.sops cifrado del repo (requiere clave age)
# Los wrappers descifran al vuelo, sin acción manual.
# Opción B — fallback sin sops: copia la plantilla plana
cp docker/.env.example docker/.env
# Edita docker/.env con tus passwords (POSTGRES_PASSWORD, KEYCLOAK_PASSWORD, DOMAIN, ...)
# Levanta el stack de DESARROLLO local (puertos expuestos al host + hot reload)
just dev
# Equivalente sin just: ./scripts/dev-up.sh
# Siembra los usuarios de test del realm custodiam
just seed
# Equivalente: ./scripts/seed-test-users.sh
Tras just dev, los servicios quedan disponibles en:
| Servicio | URL local | Profile |
|---|---|---|
| API (FastAPI) | http://localhost:8000 | (default) |
| Swagger UI | http://localhost:8000/docs | (default) |
| App Web (Flutter PWA) | http://localhost:3000 | (default) |
| Keycloak (admin console) | http://localhost:8080 | (default) |
| ntfy (web UI) | http://localhost:8090 | (default) |
| PostgreSQL | localhost:5432 |
(default) |
| Mock OIDC server (testing) | http://localhost:8888 | test (opt-in) |
Tres modos de despliegue¶
just dev # Desarrollo local: puertos expuestos, hot reload, sin túnel
just tunnel # Staging vía Cloudflare Tunnel: puertos internos, KC_HOSTNAME=auth.${DOMAIN}
just prod # Producción: tunnel + KC_HOSTNAME_STRICT=true + DEBUG=false
just down # Bajar el stack (los volúmenes con datos sobreviven)
Política de cross-mode
Los tres modos son mutuamente excluyentes (ADR-020). Cada wrapper hace un guard: si detecta que ya hay un modo distinto activo, lo aborta con un mensaje claro y pide ejecutar just down primero. Esto evita escenarios mixtos como "estoy en dev pero también tengo el túnel activo".
Comandos esenciales¶
# Ver estado de servicios
just status
# o: docker compose ps
# Ver logs de un servicio
just logs-api
just logs-keycloak
just logs-tunnel
# Entrar a la BD desde dentro del contenedor
docker compose exec postgres psql -U custodiam custodiam # BD de la API
docker compose exec postgres psql -U custodiam custodiam_kc # BD de Keycloak
# Wipe destructivo de volúmenes (¡destruye BD!)
./scripts/down.sh --volumes
Estructura del repo¶
custodiam-infra/
├── docker/
│ ├── docker-compose.yml # Base (postgres, keycloak, api, web, ntfy)
│ ├── docker-compose.dev.yml # Override desarrollo (puertos + hot reload)
│ ├── docker-compose.prod.yml # Override producción (tunnel + endurecimiento)
│ ├── .env.sops # Secretos cifrados con sops + age (versionado)
│ ├── .env.example # Plantilla plana (fallback)
│ └── init-db.sh # Crea las 2 BDs al inicializar el volumen
├── keycloak/
│ └── realm-custodiam.json # Export del realm con clientes preconfigurados
├── scripts/
│ ├── dev-up.sh # Wrapper modo desarrollo
│ ├── tunnel-up.sh # Wrapper modo tunnel
│ ├── prod-up.sh # Wrapper modo producción
│ ├── down.sh # Wrapper de parada
│ └── seed-test-users.sh # Siembra usuarios de test
└── justfile # Atajos a los scripts (interfaz recomendada)
Gestión de secretos con sops + age¶
El archivo docker/.env.sops es la fuente de verdad para los secretos del entorno del equipo (passwords de PostgreSQL, Keycloak, tokens de Cloudflare Tunnel, etc.). Vive cifrado en el repo con sops + age; los destinatarios autorizados están listados en .sops.yaml.
Los wrappers (dev-up.sh, tunnel-up.sh) detectan .env.sops automáticamente, lo descifran a un tempfile con trap de limpieza y lo pasan a Docker Compose vía --env-file. Si solo existe .env plano, lo usan como fallback.
Operaciones canónicas:
# Rotar un secret
sops docker/.env.sops # abre editor, edita el valor, guarda
# Añadir destinatario nuevo (otro dev)
# 1. Editar .sops.yaml y añadir la clave pública age
# 2. Re-cifrar reconociendo el nuevo destinatario
sops updatekeys docker/.env.sops
Siguientes pasos¶
- Backend API — desarrollar fuera de Docker contra el PostgreSQL y Keycloak del stack.
- App Flutter — arrancar la app Flutter contra el backend del stack.
- Arquitectura — diagrama del sistema, polyrepo, decisiones de infraestructura.