Every project used the Postgres superuser, the RabbitMQ administrator, an
admin S3 identity and the Zitadel instance-admin PAT, so any one of them
could read or change the others' data.
A new one-shot, `provision` (runs on every `up`, idempotent), gives each
project in x-projects its own:
- Postgres role, owner of its database. No database is open to PUBLIC
anymore, so a role can only connect to its own. An existing database
and everything in it is handed over to the project's role.
- RabbitMQ user, with permissions on its own vhost only.
- Zitadel service account, PROJECT_OWNER of its own project only; its
PAT is written to zitadel/.output/<project>/ for that project's
zitadel-init to mount instead of the admin PAT.
SeaweedFS renders one S3 identity per project, limited to its bucket, and
refuses to start with a clear message on a malformed or repeated key.
The admin accounts stay with this stack. postgres-init is gone: provision
creates the databases. *.sh files are checked out with LF so they run in
the Linux containers from a Windows checkout.
Zitadel v4 redirects every sign-in to /ui/v2/login, which is served by a
separate container (zitadel-login) that this stack didn't run, so logging
in on a fresh instance ended in a 404.
- Add zitadel-login, with its own service account (login-client) whose
PAT Zitadel writes to a volume only that container mounts.
- Configure the Login V2 base URL (ZITADEL_LOGIN_BASE_URL): its own port
in dev, same hostname as Zitadel behind the edge in prod.
- Pin zitadel and zitadel-login to v4.19.1 and add healthchecks.
- Run Zitadel as root, as Zitadel's own compose does, so it can write the
PATs into root-owned mounts (the Linux bind mount and the new volume).
- Route /ui/v2/login on the auth vhost to the login container and drop
the no-op grpc_set_header.
- Document the extra steps for an instance created before this.
Add ZITADEL_ORG_NAME and ZITADEL_ADMIN_{FIRST,LAST,DISPLAY}_NAME (defaults
keep the previous ZITADEL naming for dev); the prod example uses
Iker Demo / admin@ikerdemo.cat.
With tlsMode disabled Zitadel advertises http URLs, so the console calls
http://<domain> and the CSP blocks it. Make it configurable through
ZITADEL_TLS_MODE (default disabled for dev, external in prod).
One nginx owns host ports 80/443 for every project (Gitea, Musify) and
proxies by container name over infra-net and gitea_gitea; a certbot
service renews the Let's Encrypt certificates.
The README previously described this stack as shared by "every project
under Documentos/", which is just this machine's local checkout location,
not something the repo itself should assume. Every project lives in its
own git repository; the path to this repo's checkout is a per-project
setting (ZITADEL_ADMIN_PAT_DIR), not a fixed convention.
Also drops leftover em dashes across the docs and compose comments for
plainer prose.
Postgres, Zitadel and SeaweedFS were previously duplicated or fragilely
cross-referenced across the Musify, Ping and docker-manager repos. This
consolidates them into one place: compose.yml is the production shape
(no published ports, joins infra-net), compose.dev.yml is a thin overlay
that publishes ports for local development.