Rewrite docs to not assume a local folder layout

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.
This commit is contained in:
2026-09-21 21:45:02 +02:00
parent 58f3c5925b
commit deefb6d0cd
4 changed files with 43 additions and 33 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
# Production configuration for compose.yml (no overlay needed — the base # Production configuration for compose.yml (no overlay needed: the base
# file is already the production shape: no published ports, everything # file is already the production shape, with no published ports and
# reachable only on infra-net). # everything reachable only on infra-net).
# #
# cp .env.prod.example .env.prod # cp .env.prod.example .env.prod
# chmod 600 .env.prod # chmod 600 .env.prod
+32 -22
View File
@@ -1,18 +1,23 @@
# Infrastructure # Infrastructure
The technology services shared by every project under `Documentos/` The technology services shared across projects (currently Musify, Ping and
(currently Musify, Ping and docker-manager): PostgreSQL, Zitadel (OIDC), docker-manager, each its own git repository): PostgreSQL, Zitadel (OIDC),
SeaweedFS (S3-compatible storage), RabbitMQ and Jaeger. None of these have SeaweedFS (S3-compatible storage), RabbitMQ and Jaeger. None of these have
any business logic of their own, so instead of each project running (and any business logic of their own, so instead of each project running (and
provisioning, and keeping in sync) its own copy, they run once, here. provisioning, and keeping in sync) its own copy, they run once, here.
This repo doesn't assume anything about where the other projects are
checked out. Each one just needs a local path to this repo's checkout (see
"The Zitadel admin PAT" below), which you set once in that project's own
`.env`.
Each project keeps its own independent, fast `docker compose up` for the Each project keeps its own independent, fast `docker compose up` for the
things that actually are its own: API/worker/gateway containers, database things that actually are its own: API/worker/gateway containers, database
migrations, its own OIDC apps and storage bucket (created against the shared migrations, its own OIDC apps and storage bucket. Those get created against
Zitadel/SeaweedFS below by its own `zitadel-init`/`seaweedfs-init` one-shot the shared Zitadel/SeaweedFS below by the project's own `zitadel-init` and
jobs — see e.g. `Musify/MusifyM/deploy/compose.yml`). A project's dev stack `seaweedfs-init` one-shot jobs (see e.g. `Musify/MusifyM/deploy/compose.yml`).
only needs this one already running; nothing here needs to know a consuming A project's dev stack only needs this one already running; nothing here
project exists. needs to know a consuming project exists.
## What's here vs. what stays in each project ## What's here vs. what stays in each project
@@ -23,7 +28,7 @@ project exists.
| SeaweedFS (S3 storage) | The project's own bucket (`seaweedfs-init`), key layout | | SeaweedFS (S3 storage) | The project's own bucket (`seaweedfs-init`), key layout |
| RabbitMQ (broker) | The project's own exchanges/queues/consumers | | RabbitMQ (broker) | The project's own exchanges/queues/consumers |
| Jaeger (OTLP collector + UI) | The project's own instrumentation | | Jaeger (OTLP collector + UI) | The project's own instrumentation |
| — | LiveKit (Ping only — a single-project SFU with its own port range, not generalized) | | (not shared) | LiveKit (Ping only, a single-project SFU with its own port range) |
## Running it ## Running it
@@ -36,8 +41,8 @@ That starts Postgres, Zitadel, SeaweedFS, RabbitMQ and Jaeger, with their
ports published to `localhost`. Add `--profile tools` to also start pgAdmin. ports published to `localhost`. Add `--profile tools` to also start pgAdmin.
In production, the base `compose.yml` file **is** the production shape In production, the base `compose.yml` file **is** the production shape
already — no published ports, everything reachable only on the internal already. There are no published ports, and everything is reachable only on
`infra-net` network — so no overlay is needed: the internal `infra-net` network, so no overlay is needed:
```bash ```bash
cp .env.prod.example .env.prod cp .env.prod.example .env.prod
@@ -46,7 +51,7 @@ docker compose --env-file .env.prod -f compose.yml up -d
``` ```
A consuming project's own production compose joins `infra-net` as an A consuming project's own production compose joins `infra-net` as an
external network to reach `postgres` / `zitadel` / `seaweedfs` / `rabbitmq` / external network to reach `postgres`, `zitadel`, `seaweedfs`, `rabbitmq` and
`jaeger` by container name (see Musify's `deploy/compose.prod.yml`). `jaeger` by container name (see Musify's `deploy/compose.prod.yml`).
### Ports (development) ### Ports (development)
@@ -61,10 +66,10 @@ external network to reach `postgres` / `zitadel` / `seaweedfs` / `rabbitmq` /
| pgAdmin (`--profile tools`) | `5050` | Postgres UI | | pgAdmin (`--profile tools`) | `5050` | Postgres UI |
A consuming project reaches all of these at `host.docker.internal:<port>` A consuming project reaches all of these at `host.docker.internal:<port>`
from inside its own containers — the same address the browser and the host from inside its own containers. That's the same address the browser and the
use — so the address is valid whichever way that project's own apps are host use, so it stays valid whichever way that project's own apps are
running (in Docker or from the IDE). No shared Docker network is needed in running (in Docker or from the IDE). No shared Docker network is needed in
development; that's what keeps every project's own `up` independent and development, which is what keeps every project's own `up` independent and
fast, with this stack as the only prerequisite. fast, with this stack as the only prerequisite.
### Shared secrets ### Shared secrets
@@ -73,17 +78,22 @@ fast, with this stack as the only prerequisite.
`RABBITMQ_DEFAULT_USER`/`RABBITMQ_DEFAULT_PASS` and `PUBLIC_AUTH_URL` (i.e. `RABBITMQ_DEFAULT_USER`/`RABBITMQ_DEFAULT_PASS` and `PUBLIC_AUTH_URL` (i.e.
`http://host.docker.internal:${ZITADEL_EXTERNAL_PORT}` in dev) are the values `http://host.docker.internal:${ZITADEL_EXTERNAL_PORT}` in dev) are the values
every consuming project's own `.env.example` copies and documents as "must every consuming project's own `.env.example` copies and documents as "must
match this repo's `.env`" — this file is the source of truth for them. match this repo's `.env`". This file is the source of truth for them.
### The Zitadel admin PAT ### The Zitadel admin PAT
`zitadel-init` (in the compose file of whichever project runs it first) `zitadel-init` (in the compose file of whichever project runs it first)
writes a service-account personal access token to `zitadel/.output/admin-sa.pat` writes a service-account personal access token to `zitadel/.output/admin-sa.pat`
on first init. Every consuming project mounts that same folder read-only — on first init. Every consuming project mounts that same folder read-only, via
by default at `../../../Infrastructure/zitadel/.output`, three levels up from its own `ZITADEL_ADMIN_PAT_DIR` setting, to call the Zitadel management API
a `deploy/` folder — to call the Zitadel management API and create its own and create its own project/apps.
project/apps. Not versioned; regenerated whenever the Zitadel volume is
reset. That setting has no universally correct value. It's a path (relative or
absolute) to wherever *you* cloned this repo on your machine, from the
consuming project's `deploy/` folder. Each project's `.env.example` ships
with an example value that assumes these repos happen to sit as sibling
folders. If yours don't, change it. Not versioned, and regenerated whenever
the Zitadel volume is reset.
## Resetting ## Resetting
@@ -93,14 +103,14 @@ docker compose -f compose.yml -f compose.dev.yml down -v # stop, wipe volu
``` ```
Wiping the volumes resets Zitadel (all projects/apps/users) and SeaweedFS Wiping the volumes resets Zitadel (all projects/apps/users) and SeaweedFS
(all buckets) — every consuming project's own `zitadel-init`/`seaweedfs-init` (all buckets). Every consuming project's own `zitadel-init`/`seaweedfs-init`
re-creates its piece on the next `up`, since both are idempotent. re-creates its piece on the next `up`, since both are idempotent.
## Layout ## Layout
``` ```
Infrastructure/ Infrastructure/
├─ compose.yml # postgres, zitadel, seaweedfs, rabbitmq, jaeger — the prod shape ├─ compose.yml # postgres, zitadel, seaweedfs, rabbitmq, jaeger: the prod shape
├─ compose.dev.yml # dev overlay: publishes ports, adds pgAdmin ├─ compose.dev.yml # dev overlay: publishes ports, adds pgAdmin
├─ .env.example / .env.prod.example ├─ .env.example / .env.prod.example
├─ postgres-init/ # creates the per-project application databases ├─ postgres-init/ # creates the per-project application databases
+5 -5
View File
@@ -1,10 +1,10 @@
# Shared infrastructure — identity, storage, database, messaging and tracing # Shared infrastructure: identity, storage, database, messaging and tracing
# for every project under Documentos/ (Musify, Ping, docker-manager, ...). # for every project (Musify, Ping, docker-manager, ...).
# #
# This file is the production shape: no published ports, no domains. Every # This file is the production shape: no published ports, no domains. Every
# service is reachable only on the `infra-net` network. Development reaches # service is reachable only on the `infra-net` network. Development reaches
# it through compose.dev.yml, which publishes the ports to the host instead # it through compose.dev.yml, which publishes the ports to the host instead
# of joining a network — see README.md for why. # of joining a network. See README.md for why.
# #
# dev docker compose -f compose.yml -f compose.dev.yml up -d # dev docker compose -f compose.yml -f compose.dev.yml up -d
# prod docker compose --env-file .env.prod -f compose.yml -f compose.prod.yml up -d # prod docker compose --env-file .env.prod -f compose.yml -f compose.prod.yml up -d
@@ -22,7 +22,7 @@ services:
# --- Database --------------------------------------------------------- # --- Database ---------------------------------------------------------
# One instance, one database per consuming project (see postgres-init/). # One instance, one database per consuming project (see postgres-init/).
# Zitadel provisions its own `zitadel` database itself, from the admin # Zitadel provisions its own `zitadel` database itself, from the admin
# credentials below — it does not need an init script. # credentials below, so it does not need an init script.
postgres: postgres:
image: postgres:17-alpine image: postgres:17-alpine
container_name: infra-postgres container_name: infra-postgres
@@ -88,7 +88,7 @@ services:
# S3-compatible object store shared by every project. All-in-one: master + # S3-compatible object store shared by every project. All-in-one: master +
# volume + filer + S3 gateway. The S3 identity file is rendered from the # volume + filer + S3 gateway. The S3 identity file is rendered from the
# environment on start so the credentials can never drift from the ones # environment on start so the credentials can never drift from the ones
# handed to consuming projects. No bucket is created here — each # handed to consuming projects. No bucket is created here; each
# consuming project's own seaweedfs-init one-shot creates its own. # consuming project's own seaweedfs-init one-shot creates its own.
seaweedfs: seaweedfs:
image: chrislusf/seaweedfs:latest image: chrislusf/seaweedfs:latest
+3 -3
View File
@@ -1,8 +1,8 @@
-- Runs once, only on first init of the postgres_data volume. -- Runs once, only on first init of the postgres_data volume.
-- Zitadel provisions its own "zitadel" database itself, from the -- Zitadel provisions its own "zitadel" database itself, from the
-- ZITADEL_DATABASE_POSTGRES_ADMIN_* credentials in compose.yml — it does not -- ZITADEL_DATABASE_POSTGRES_ADMIN_* credentials in compose.yml, so it does
-- need to be created here. This script only creates the plain application -- not need to be created here. This script only creates the plain
-- databases each project's own connection string points at. -- application databases each project's own connection string points at.
CREATE DATABASE musify_db; CREATE DATABASE musify_db;
CREATE DATABASE ping_db; CREATE DATABASE ping_db;