dorfteich/docs/architecture
Claude Fable 5 f0a82bad20
Some checks failed
CD / Build and push images (push) Successful in 3m16s
CI / Lint, typecheck, test (push) Successful in 3m5s
CD / Deploy to Test (push) Successful in 13s
CD / Smoke tests against Test (push) Failing after 3m35s
CD / Promote to Int (push) Has been skipped
CI / Auth e2e pack (push) Successful in 5m6s
CI / Import/export fidelity gate (push) Successful in 43s
CI / Build container images (push) Has been skipped
Add the first-run setup wizard API with env-backed secret store (#80)
When the api runs against a database without the setup.completedAt
marker, a global SetupGuard answers every non-exempt route with 503
setup_required; only /setup/*, health probes, and the session routes
stay reachable. The wizard steps (POST /setup/admin|instance|smtp|
registration|complete) write straight to their production homes; the
Site Admin step signs its creator in, later steps require that session.
Completing sets the marker and locks every step permanently (410, also
across restarts, and not reopenable via PATCH /admin/settings).

SMTP entered in the wizard is verified with a live delivery test first
(failure blocks the step with the transport error as detail) and then
persisted to the new env-backed secret store: a mode-600 dotenv file on
the new `secrets` volume (SECRETS_FILE). Explicit container env always
wins over the store; empty compose-passed strings count as unset. The
mail transport now resolves lazily through SmtpConfigService so wizard
changes apply without a restart.

SETUP_ADMIN_* env pre-seeds the whole wizard at boot for automated
deploys; a backfill migration marks instances that already have a Site
Admin as completed, and seed/vitest global-setup do the same for
fixture databases. The setup e2e suite provisions its own fresh
database (CREATE DATABASE + migrate deploy) per run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
2026-07-11 15:10:28 +02:00
..
adr Move stages, runner and deploy target to dedicated host ONE 2026-07-11 08:05:36 +02:00
data-model.md Add plugin storage, install API, and directory watcher (#71) 2026-07-10 16:55:26 +02:00
deployment.md Add the first-run setup wizard API with env-backed secret store (#80) 2026-07-11 15:10:28 +02:00
operations.md Scaffold pnpm monorepo with lint, format, and test tooling 2026-07-04 19:06:27 +02:00
permissions.md Scaffold pnpm monorepo with lint, format, and test tooling 2026-07-04 19:06:27 +02:00
plugin-architecture.md Add pageTool plugins with toc and page-index references (#77) 2026-07-11 13:27:30 +02:00
README.md Enforce permissions in API guards and retire interim access (#52) 2026-07-09 16:31:41 +02:00
realtime-collaboration.md Scaffold pnpm monorepo with lint, format, and test tooling 2026-07-04 19:06:27 +02:00
roadmap.md Move stages, runner and deploy target to dedicated host ONE 2026-07-11 08:05:36 +02:00
security.md Scaffold pnpm monorepo with lint, format, and test tooling 2026-07-04 19:06:27 +02:00

Dorfteich — Architecture Overview

This directory is the authoritative architecture documentation. Every implementation story references the documents here; when a story and this documentation disagree, clarify before coding.

System context

Dorfteich is shipped as a set of Docker containers behind a reverse proxy. One deployment = one instance (e.g. dorfteich.online, or a self-hosted installation).

flowchart LR
    subgraph clients [Clients]
        B[Browser SPA<br/>React + TipTap + Yjs]
    end
    subgraph instance [Dorfteich instance - Docker Compose]
        RP[Reverse proxy]
        WEB[web<br/>static SPA assets]
        API[api<br/>NestJS REST]
        COLLAB[collab<br/>Hocuspocus WebSocket]
        PG[(PostgreSQL)]
        PAN[pandoc-server<br/>doc conversion]
        GOT[Gotenberg<br/>HTML to PDF]
        VOL[/uploads + plugins volume/]
    end
    B -- HTTPS --> RP
    RP --> WEB
    RP -- /api --> API
    RP -- /collab WebSocket --> COLLAB
    API --> PG
    COLLAB --> PG
    API --> PAN
    API --> GOT
    API --> VOL
    COLLAB -. permission checks .-> API
  • web serves the single-page application (static assets).
  • api owns all business logic: auth, ponds, pages, labels, permissions, quotas, import/export, plugin management, admin functions.
  • collab synchronizes CRDT documents (page content) and awareness (cursors) over WebSocket and persists document state to PostgreSQL. It authenticates clients with short-lived tokens issued by api.
  • pandoc-server and Gotenberg are internal-only conversion sidecars (Word/OpenOffice import/export, PDF export).
  • All page content lives in PostgreSQL; binary uploads (images, attachments) and installed plugins live on a Docker volume.

Documents

Architecture Decision Records (adr/)

ADR Decision
0001 TypeScript everywhere, pnpm monorepo
0002 PostgreSQL as the only database
0003 Yjs CRDT + Hocuspocus for real-time and offline collaboration
0004 TipTap (ProseMirror) as the WYSIWYG editor
0005 React + Vite single-page application
0006 NestJS + Prisma for the API server
0007 Cookie sessions, Argon2id, OIDC-ready identity model
0008 Sandboxed iframe plugins with a message-based API
0009 Pandoc + Gotenberg sidecars for import/export
0010 PostgreSQL full-text search behind a search interface
0011 Filesystem volume for uploads, DB-tracked quotas
0012 i18next with German and English from the start
0013 Page version history via Yjs snapshots, soft-delete trash
0014 CI/CD with Gitea Actions, staged promotion
0015 Nightly pg_dump + uploads sync, 30-day retention, off-host mirror
0016 Self-hosted Google Fonts, per-pond font configuration

Concept documents

Document Contents
data-model.md Entities and relations: users, ponds, pages, labels, grants, quotas, plugins
permissions.md Role model and the "most specific setting wins" resolution algorithm
realtime-collaboration.md CRDT document lifecycle, cursor sync, offline behavior, versioning hooks
plugin-architecture.md Plugin manifest, packaging, sandbox runtime, extension points, admin flows
deployment.md Compose stacks for Dev/Test/Int/Prod, environments, promotion pipeline
operations.md Monitoring, logging, backup/restore, update strategy for self-hosters
security.md Threat-driven security concept: authn/authz, sandboxing, uploads, secrets
roadmap.md Epics and milestones; the order stories are implemented in

Terminology

German (product vision) English (code, docs, issues)
Teich pond
Seite page
Teich-Admin Pond Admin
Bearbeitende Editor
Lesende Reader
Öffentlichkeit Public

Conventions for implementers

  • Language: English for code, comments, commit messages, and issues.
  • Write clear, human-readable code; document the "why", not the "what".
  • UI strings never appear hard-coded — always through i18n resources (see ADR 0012), with German and English translations added in the same change.
  • Every story lists the ADRs it depends on; read them before starting.
  • Every API route declares its access rule explicitly (a permission decorator, @Public(), or the Site-Admin guard — issue #52); permission checks run only through the shared resolution (permissions.md), never ad hoc. 404/403 policy: a denied read answers 404 so the existence of ponds and pages is not revealed; a denied write on something the user may read answers 403. Trash views require write capability and answer 404 on denial (ADR 0013).