dorfteich/deploy/compose/.env.example
Claude Fable 5 2f7ba65eef
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m11s
CI / Build container images (pull_request) Successful in 1m24s
CI / Import/export fidelity gate (pull_request) Successful in 57s
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Auth e2e pack (pull_request) Successful in 8m49s
CD / Build and push images (push) Successful in 20s
CD / Deploy to Test (push) Successful in 13s
CD / Smoke tests against Test (push) Successful in 1m25s
CD / Promote to Int (push) Successful in 12s
CI / Lint, typecheck, test (push) Successful in 6m24s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Has been cancelled
#218: mirror procedure into an internal registry
Airgapped sites pull from their own registry (ADR 0024). The image list
is GENERATED (deploy/scripts/list-images.sh resolves the compose file
incl. the caddy profile) so a mirror can never silently miss a service;
third-party images gain a configurable ${REGISTRY_PREFIX:-} in the
compose file (digest pins unchanged - Docker verifies the same sha256
regardless of which registry serves it), own images keep IMAGE_PREFIX;
no image reference is ever edited per site.

Step-by-step procedure in deploy/stages.md 5b: generate list, copy
digest-preservingly (docker buildx imagetools create; plain
pull/tag/push as the documented fallback - the digest comparison closes
the loop either way), verify the digest in the mirror against the pin,
point the deployment via REGISTRY_PREFIX/IMAGE_PREFIX.

Executed once end-to-end and recorded as assessor-facing evidence
(docs/vs-nfd/95-mirror-protokoll.md): all four third-party images
mirrored digest-identically into a local registry:2, plus
dorfteich-api:v0.12.0 (sha256:576f1646... identical on both sides; the
imagetools stall against the Gitea registry is recorded with its
workaround). Operations manual's airgap section now lists the mirror
part as available.

Refs #218.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUtYMxwTCMHG9mVHnwbFg8
2026-07-31 14:33:04 +02:00

128 lines
6.3 KiB
Plaintext

# Dorfteich stage configuration. Copy to `.env` (mode 600, never in git)
# next to docker-compose.yml and adjust the values.
# --- required ---------------------------------------------------------------
# PostgreSQL password for the `dorfteich` database user.
POSTGRES_PASSWORD=change-me
# ROOT key of the token key hierarchy (ADR 0020, issue #188): every token
# purpose (collaboration tokens, digest unsubscribe links) derives its own
# HKDF subkey from this value — nothing signs with it directly. The api and
# collab services share this one value; use a long random string
# (e.g. `openssl rand -base64 32`). Min length 16. Rotating it rotates all
# derived keys at once and invalidates outstanding tokens.
COLLAB_TOKEN_SECRET=change-me-to-a-long-random-string
# --- images -----------------------------------------------------------------
# Image name prefix. Stages pull from the Gitea registry, e.g.
# gitea.101010.cloud/stwaidele/dorfteich — local builds use the default.
IMAGE_PREFIX=dorfteich
# Image tag to run: a git SHA, `test`, `int`, or a release tag like v1.2.0.
TAG=latest
# Registry prefix for THIRD-PARTY images (postgres, pandoc, gotenberg,
# caddy) — for airgapped sites pulling from an internal mirror
# (issue #218, ADR 0024). Must end with a slash, e.g.
# `registry.example.gov/mirror/`. Empty = public registries. Own images
# are covered by IMAGE_PREFIX above, which may equally carry a registry.
REGISTRY_PREFIX=
# --- ports (localhost only; the host reverse proxy routes to these) ---------
# Suggested per stage on the shared host (ONE): test 8100/8101/8102,
# int 8110/8111/8112, prod 8120/8121/8122 (web/api/collab).
WEB_PORT=8100
API_PORT=8101
# collab (Hocuspocus) WebSocket server; the proxy routes /collab here.
COLLAB_PORT=8102
# --- behavior ----------------------------------------------------------------
# pino log level: fatal|error|warn|info|debug|trace
LOG_LEVEL=info
# Session bounds in hours (issue #190). ABSOLUTE caps the total session
# lifetime from login (also the cookie maxAge) — activity never extends it.
# IDLE ends sessions unused for that long; activity renews it, enforced
# server-side. Unset = defaults: 168 (7 days) absolute, 72 (3 days) idle.
# VS-NfD reference operation (hardening guide): 12 absolute, 1 idle.
#SESSION_ABSOLUTE_HOURS=168
#SESSION_IDLE_HOURS=72
# Compose project name; set per stage (dorfteich-test, dorfteich-int, …).
COMPOSE_PROJECT_NAME=dorfteich
# --- public URL + mail --------------------------------------------------------
# Public base URL of the stage (scheme + host). E-mail links and the CSRF
# origin check are derived from it — it must match what browsers use.
APP_BASE_URL=https://test.dorfteich.cloud
# SMTP relay for outgoing mail (verification, password reset). Optional:
# leave everything unset and configure the relay in the browser during the
# first-run setup wizard instead (stored on the `secrets` volume, issue #80).
# Values set here always win over wizard-stored ones.
SMTP_HOST=mail.example.com
SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=wiki@example.com
SMTP_PASS=change-me
SMTP_FROM=Dorfteich <wiki@example.com>
# --- optional TLS ingress (`caddy` profile, issue #88) -------------------------
# Only when you have no reverse proxy of your own: start with
# `docker compose --profile caddy up -d`. Caddy terminates TLS for DOMAIN
# via Let's Encrypt (80+443 must be reachable from the internet; keep
# APP_BASE_URL=https://<DOMAIN> in sync). The `localhost` default issues
# an internal-CA certificate instead — good for smoke tests only.
#DOMAIN=wiki.example.com
# Published ports; change only when 80/443 are taken on the host.
#CADDY_HTTP_PORT=80
#CADDY_HTTPS_PORT=443
# --- backups (ADR 0015, issue #83) --------------------------------------------
# The backup sidecar dumps the database and archives the uploads/plugins
# volumes nightly onto the `backups` volume; restore via
# deploy/backup/restore.sh <backup-id>. All values optional.
# Daily run time HH:MM in TZ (default 03:00; set TZ for stage-local time,
# e.g. TZ=Europe/Berlin — unset means UTC).
#TZ=Europe/Berlin
#BACKUP_TIME=03:00
# Local retention in days: 30 (default) for Prod, 7 for Test/Int (ADR 0015).
# A Site Admin can override this in the admin UI (issue #103) — the saved
# setting then wins over this value.
#BACKUP_RETENTION_DAYS=30
# Off-host copies to a Nextcloud (issue #103) are configured entirely in the
# admin UI (Admin -> System -> Backups) — no env values needed here.
# Failure alert: recipient (unset = no mail, failures only in the logs and
# status.json), mail language (de|en), and the label used in the subject
# (defaults to the compose project name).
#BACKUP_MAIL_TO=ops@example.com
#BACKUP_MAIL_LOCALE=en
#BACKUP_INSTANCE_LABEL=dorfteich-test
# Deploy-level allowlist of permissible backup destination HOSTS (issue
# #192, ADR 0026), comma-separated — e.g. "cloud.example.org,172.30.1.10".
# EMPTY (the default) DISABLES every remote target, the admin-configured
# WebDAV/Nextcloud upload and the rsync mirror alike; backups then stay
# local only (the VS-NfD reference configuration). BREAKING: existing
# deployments with a remote target must list its host here, or uploads and
# mirror stop. Deploy-level on purpose: Site-Admins cannot widen it.
#BACKUP_ALLOWED_TARGETS=cloud.example.org,172.30.1.10
# Optional rsync mirror of the backup sets to a private host (issue #84):
# rsync-over-ssh target plus the private key file INSIDE the container —
# put the key on the secrets volume (docker compose cp), never in the repo.
# Full setup walkthrough: deploy/backup-basel.md. Unset = no mirror.
#BACKUP_MIRROR_TARGET=dorfteich-backup@172.30.1.10:/home/RAID/BACKUPS/dorfteich-prod/
#BACKUP_MIRROR_SSH_KEY=/data/secrets/backup_mirror_ed25519
#BACKUP_MIRROR_SSH_PORT=22
# --- first-run setup (optional pre-seeding, issue #80) ------------------------
# A fresh (empty) database makes the instance require the browser setup
# wizard. Automated deploys can skip it entirely by pre-seeding the Site
# Admin here; the wizard then completes and locks itself at first boot.
# All three SETUP_ADMIN_* values are required for pre-seeding to trigger.
#SETUP_ADMIN_USERNAME=admin
#SETUP_ADMIN_EMAIL=admin@example.com
#SETUP_ADMIN_PASSWORD=change-me-please
#SETUP_ADMIN_DISPLAY_NAME=Admin
#SETUP_INSTANCE_NAME=Dorfteich
#SETUP_DEFAULT_LOCALE=en
#SETUP_REGISTRATION_MODE=open