SVSANNVIT
Erste Schritte

Hosting mit Docker

Betreiben Sie den vollständigen Sannvit-Ledger-Stack — Postgres, Keycloak, die Ledger API, Blob Storage und das Dashboard — mit Docker Compose.

Docker Compose ist der schnellste Weg, eine Sannvit-Ledger-Instanz zu betreiben — egal ob als lokale Entwicklungsumgebung oder als Single-Host-Produktions-Deployment. Im Folgenden wird davon ausgegangen, dass Sie das Repository bereits geklont haben.

Voraussetzungen

  • Docker + Docker Compose
  • Node.js — zum Anwenden der Datenbank-Migrationen und (optional) für native Entwicklung außerhalb von Containern

Was in den Compose-Dateien steckt

Der Stack ist in eine Basisdatei plus Overrides aufgeteilt, die Sie nach Bedarf hinzufügen:

ServiceCompose-DateiWas es ist
dbdocker-compose.ymlEinfaches postgres:17 — die einzige Datenbank im Stack
keycloakdocker-compose.keycloak.ymlAuthentifizierung — Direct Access Grant, keine gehostete Login-UI
api / alertingdocker-compose.api.ymlDer Ingest-Service, plus sein eigenständiger Integritäts-/Alerting-Runner als zweiter Container
dashboarddocker-compose.dashboard.ymlDie Browser-UI
rustfsdocker-compose.rustfs.ymlGebündelter S3-kompatibler Blob Store — optional, siehe Eigenen S3-kompatiblen Speicher verwenden
maildocker-compose.mail.ymlSMTP-Abfangdienst nur für die Entwicklung (Mailpit), damit Einladungs-/Reset-/Alert-E-Mails sichtbar landen
nginx / caddydocker-compose.nginx.yml / docker-compose.caddy.ymlOptionaler TLS-terminierender Reverse Proxy vor den Subdomains app. / api. / auth.

Die App spricht direkt mit einfachem Postgres, und Keycloak übernimmt die Authentifizierung — ohne zusätzliche Services dazwischen — was den Footprint klein genug hält, um auf der eigenen Infrastruktur eines Kunden installiert zu werden.

1. Umgebung konfigurieren

.env liegt im Repository-Root und wird von jeder Compose-Datei gelesen.

cp .env.example .env

Füllen Sie mindestens diese Secrets aus, bevor Sie irgendetwas starten — openssl rand -hex 24 für jedes:

  • POSTGRES_PASSWORD
  • JWT_SECRET
  • KEYCLOAK_ADMIN_PASSWORD
  • KEYCLOAK_SERVICE_CLIENT_SECRET
  • S3_ROOT_PASSWORD (nur nötig, wenn Sie den gebündelten Blob Store verwenden)
  • EXPORT_SECRET_KEY (nur nötig, bevor Sie das SFTP-Export-Feature für Nachweispakete nutzen)

Für die lokale Arbeit reichen reine Entwicklungswerte — verwenden Sie diese nur nirgendwo produktiv weiter.

2. Stack starten

Entwicklung

docker/run.sh verwaltet, welche Compose-Override-Dateien aktiv sind, und liest .env immer aus dem Repository-Root, unabhängig vom Arbeitsverzeichnis Ihrer Shell:

cd docker
./run.sh config add keycloak rustfs mail api api.dev dashboard dashboard.dev
./run.sh start

api.dev / dashboard.dev legen Hot-Reload (bind-gemounteter Quellcode, tsx watch / Nuxt HMR) über ihre jeweiligen Basisservices.

Produktion (Einzelner Host)

npm run prod

was äquivalent ist zu:

cd docker
COMPOSE_FILE=docker-compose.yml:docker-compose.keycloak.yml:docker-compose.rustfs.yml:docker-compose.api.yml:docker-compose.dashboard.yml \
COMPOSE_ENV_FILES=../.env COMPOSE_IGNORE_ORPHANS=true \
docker compose up -d --wait --build

!WARNING Bevor Sie das im Ernstfall tun:

  • Die Werte in .env.example sind Platzhalter, keine Secrets — setzen Sie für jeden oben genannten Eintrag echte, eindeutige Werte.
  • Stellen Sie einen echten TLS-terminierenden Reverse Proxy davor (docker-compose.nginx.yml oder .caddy.yml), anstatt rohe Service-Ports offenzulegen, und setzen Sie PROXY_DOMAIN in .env.
  • Ändern Sie das Client-Secret von Keycloaks ledger-service gegenüber dem Entwicklungs-Default aus dem Realm-Export (docker/volumes/keycloak/ledger-realm.json) über die Keycloak-Admin-Konsole und aktualisieren Sie KEYCLOAK_SERVICE_CLIENT_SECRET in .env entsprechend.
  • Richten Sie SMTP_* auf ein echtes Relay statt auf Mailpit, und setzen Sie DASHBOARD_URL auf die echte öffentliche Dashboard-Domain — siehe E-Mail (SMTP) unten.
  • Richten Sie ordentliche Backup-Verfahren für das db-Volume ein.

3. Datenbankschema anwenden

Dies ist ein einmaliger Schritt pro frischer Datenbank — nicht Teil von run.sh start. Warten Sie, bis db und keycloak als healthy gemeldet werden (./run.sh status), dann vom Repository-Root aus:

for f in api/drizzle/0*.sql; do
  psql -h localhost -U postgres -d postgres -v ON_ERROR_STOP=1 -f "$f"
done

cd api
npm install
npm run db:functions   # wendet src/db/functions/*.sql an — keine drizzle-kit-Objekte
npm run seed:demo      # Demo-Org + Team + Ingest-Key (Key wird einmal ausgegeben, aufbewahren)

Es gibt keinen vorab angelegten Dashboard-Benutzer — seed:demo legt nur eine Demo-Org/-Team/-Ingest-Key für die SDK-Seite an. Um einen menschlichen Login zu erhalten, bootstrappen Sie stattdessen den ersten Admin (aus api/, sobald db und keycloak laufen und die Migrationen angewendet sind):

npm run seed:admin -- you@example.com --org-name="Your Org Name"

Dies gibt einmalig ein generiertes Passwort aus. Jeder weitere Benutzer wird über das Dashboard eingeladen (Organization → Users → Invite) durch einen bestehenden compliance_admin, nicht über dieses Script.

4. Fertig

  • Dashboard — http://localhost:3000
  • Ledger API — http://localhost:4010 (GET /healthz)
  • Keycloak-Admin — http://localhost:8180
  • Mailpit (E-Mail-Abfangdienst für die Entwicklung) — http://localhost:8025
  • Blob-Store-Konsole — http://localhost:9001 (gebündeltes RustFS, falls beibehalten)

Nützliche run.sh-Befehle

./run.sh logs [service]     # Logs verfolgen
./run.sh status             # docker compose ps
./run.sh recreate [service] # einzelnen Service neu erstellen
./run.sh stop                # alles stoppen

Wenn Sie docker compose direkt statt run.sh aufrufen, muss die Env-Datei explizit angegeben werden, z. B. vom Repository-Root aus:

docker compose --env-file .env -f docker/docker-compose.yml -f docker/docker-compose.keycloak.yml up -d

Eigenen S3-kompatiblen Speicher verwenden

Standardmäßig bündelt der Stack einen RustFS-Container als Blob Store für Payload-Inhalte — das Ledger selbst speichert nur einen Zeiger + Hash. Wenn Sie lieber auf einen bestehenden Bucket zeigen möchten (AWS S3, Cloudflare R2, Backblaze B2, eigenes MinIO/RustFS usw.):

  1. Fügen Sie den rustfs-Override nicht hinzu — überspringen Sie ./run.sh config add rustfs, oder führen Sie ./run.sh config remove rustfs aus, falls er bereits vorhanden ist.
  2. Setzen Sie in .env S3_ENDPOINT, S3_PORT, S3_USE_SSL und optional S3_REGION auf den externen Provider sowie S3_ACCESS_KEY / S3_SECRET_KEY auf Zugangsdaten, die auf diesen Bucket beschränkt sind. Setzen Sie LEDGER_BUCKET auf den Bucket-Namen.
  3. Der Bucket wird beim ersten Upload automatisch angelegt, falls er noch nicht existiert — legen Sie ihn also entweder vorab an oder gewähren Sie den angegebenen Zugangsdaten die Berechtigung CreateBucket.

Die append-only zweite Integritätsebene ist ein zweiter Bucket (LEDGER_INTEGRITY_BUCKET, Standard ledger-integrity) auf demselben Endpoint, angelegt mit aktiviertem S3 Object Lock — unlöschbar für sein Aufbewahrungsfenster, selbst für Admins.

!CAUTION Der gebündelte RustFS-Container hat bekannte Bugs in seiner Object-Lock-Implementierung. Für die Entwicklung ist das unproblematisch, aber Produktions-Deployments sollten LEDGER_INTEGRITY_BUCKET über den Bring-your-own-S3-Weg auf eine ausgereifte Object-Lock-Implementierung richten — echtes AWS S3, Backblaze B2 oder GCS über S3-Interop.

E-Mail (SMTP)

Die Ledger API versendet Einladungs-, Passwort-Reset- und Alert-Digest-E-Mails per SMTP.

Lokale Entwicklung — E-Mails abfangen statt versenden:

./run.sh config add mail
./run.sh start

Jede E-Mail landet unter http://localhost:8025 (Mailpit) statt in einem echten Postfach.

Produktion — echte E-Mail zustellen:

  1. Besorgen Sie sich SMTP-Zugangsdaten von einem E-Mail-Provider (SendGrid, Postmark, AWS SES, Resend, Mailgun oder einem bestehenden Firmen-Relay).
  2. Setzen Sie SMTP_ADMIN_EMAIL, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS und SMTP_SENDER_NAME in der .env dieser Umgebung. Lassen Sie SMTP_ALLOW_INSECURE_AUTH unset — es existiert nur für Mailpit.
  3. Setzen Sie DASHBOARD_URL auf die echte öffentliche Dashboard-Domain — sie wird in Einladungs-/Reset-E-Mail-Links eingebettet.
  4. Fügen Sie den mail-Override in der Produktion nicht hinzu, sonst fängt er echte Mail ab.
  5. Wenden Sie die Änderung an: ./run.sh recreate api alerting.

Wenn Ihr Provider eine Domain-Verifizierung erfordert (SPF/DKIM, Absenderidentität — üblich bei SES und Postmark), muss das auf Seiten des Providers abgeschlossen sein, bevor Mail tatsächlich zugestellt wird.

Aktualisieren

  1. Prüfen Sie den Changelog auf Breaking Changes in den Images, die dieser Stack weiterhin verwendet (Postgres, Keycloak, RustFS, Mailpit).
  2. Aktualisieren Sie bei Bedarf die Image-Tags in der jeweiligen docker-compose.*.yml.
  3. Ziehen Sie die neuesten Images: docker compose pull.
  4. Bauen Sie api / dashboard neu, falls sich deren Quellcode geändert hat: docker compose -f docker-compose.api.yml -f docker-compose.dashboard.yml up -d --build.

Sichern Sie Ihre Datenbank vor jedem Update.

Benötigen Sie stattdessen horizontale Skalierung?

Für ein Cluster-Deployment über einen einzelnen Compose-Host hinaus gibt es einen Helm-basierten Kubernetes-Weg — Images bauen/pushen, Secrets, DNS und helm install — getrennt von Docker Compose behandelt.