Go to file
Claude Opus 5 4b9018c033
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 7m32s
CI / Build container images (pull_request) Successful in 4m20s
CI / Auth e2e pack (pull_request) Successful in 9m40s
CI / Import/export fidelity gate (pull_request) Successful in 1m1s
#307: pond-level branding overrides the instance logo and favicon
Built on #306's storage, serving and crop control — a layer, not a parallel
implementation. `resolveBranding` in shared is the ONE place that answers
"which asset applies here?", and both the sidebar logo and the favicon swap
read it.

The decision most likely to be "fixed" by accident, so it is pinned by name
in `branding.test.ts`: **a logo set belongs to one level and variants are
never mixed across levels.** A pond that uploaded only a light logo shows THAT
logo in dark mode; it does not borrow the instance's dark variant. Decided
2026-08-01 — a logo silently swapping to a different image when the viewer
switches theme is a change nobody ordered, and a design that looks wrong is
more honest than one that is quietly substituted. Only a pond with no logo at
all inherits the instance's set, again as a set. The settings screen warns
about a missing dark variant; it never blocks.

Consequences that fall out of that rule and are easy to get wrong:

- The serving route does NOT fall back when given a pond scope. The caller
  already decided which level applies; a "helpful" fallback in the route
  would mix variants across levels behind the resolver's back.
- The logo link's accessible name follows the LEVEL: a pond logo is named by
  the pond, an instance logo by the instance. It is the link home, and a
  link's name has to say where it goes.

- **Charged to the pond's storage quota**, before the write, like attachments.
  Without it branding would be a way around the quota, and replacing a logo
  repeatedly would consume disk with no ceiling. The replaced asset's bytes
  are released FIRST, so re-uploading the same logo costs nothing — and a
  refused upload puts the released reservation back, so a rejection cannot
  leave the pond with more room than it had.
- **Purge removes the branding files.** The purge standard is absolute: after
  it nothing referencing the pond survives, rows or files. Asserted against
  the real purge path, not the new code alone.
- Security unchanged from #306 and not relaxed because the uploader is now an
  ordinary Pond Admin: SVG refused, magic bytes and IHDR checked server-side,
  size caps, content type pinned, no image parsing.
- The favicon swap is driven by the RESOLVED pond, never the raw route
  parameter — an unreadable or unknown slug must not leave a stale icon in
  the tab. That it happens after first paint is accepted and stated in the
  code and the UI: avoiding it would mean server-rendering index.html, which
  is #179's territory.

Same audit id as #306 (`branding.changed`) with `scope: 'pond'` — the catalogue
already carries the field, so no version bump.

Verified: api suite 105 files / 592 tests green; 5 pond-branding e2e tests
(pond scope serves the pond's bytes while the instance level still 404s, the
quota is charged and released exactly, SVG refused at pond level, a reader may
read but not change, purge deletes the files); 9 shared unit tests on the
resolution order including both mixing directions.
2026-08-01 19:55:11 +02:00
.claude graphify: CLAUDE.md-Sektion + PreToolUse-Hooks, graphify-out/ gitignored 2026-07-19 00:46:00 +02:00
.gitea/workflows #303 follow-up: the fonts volume must mount node-owned 2026-08-01 15:12:06 +02:00
apps #307: pond-level branding overrides the instance logo and favicon 2026-08-01 19:55:11 +02:00
deploy #306: instance branding — logo and favicon, cropped in the browser 2026-08-01 19:30:52 +02:00
docs #306: instance branding — logo and favicon, cropped in the browser 2026-08-01 19:30:52 +02:00
fixtures Fix the gates the #116 commit skipped past 2026-07-14 16:53:11 +02:00
packages #307: pond-level branding overrides the instance logo and favicon 2026-08-01 19:55:11 +02:00
scripts #202: SBOM and license report in CI 2026-07-31 04:21:58 +02:00
.dockerignore Add production Dockerfiles and the Compose stack with dev overlay 2026-07-04 19:30:21 +02:00
.editorconfig Scaffold pnpm monorepo with lint, format, and test tooling 2026-07-04 19:06:27 +02:00
.gitignore graphify: CLAUDE.md-Sektion + PreToolUse-Hooks, graphify-out/ gitignored 2026-07-19 00:46:00 +02:00
.node-version #236: pin the Node version 2026-07-31 04:14:55 +02:00
.prettierignore chore: graphify/agent-Config aus Prettier ausnehmen 2026-07-19 01:02:58 +02:00
.prettierrc.json Scaffold pnpm monorepo with lint, format, and test tooling 2026-07-04 19:06:27 +02:00
CLAUDE.md #300: route icon-only controls through IconButton/IconLink 2026-08-01 06:56:13 +02:00
eslint.config.mjs #300: route icon-only controls through IconButton/IconLink 2026-08-01 06:56:13 +02:00
LICENSE Add architecture documentation, ADRs, and operations concept 2026-07-04 14:36:16 +02:00
package.json #202: SBOM and license report in CI 2026-07-31 04:21:58 +02:00
pnpm-lock.yaml #214: OIDC Authorization Code with PKCE, Keycloak as reference IdP 2026-07-31 12:44:52 +02:00
pnpm-workspace.yaml #136 Excalidraw-Block-Plugin 2026-07-19 04:18:04 +02:00
README.md German translations of the seven user-facing docs under docs/de/ 2026-07-12 19:28:50 +02:00
tsconfig.base.json Scaffold pnpm monorepo with lint, format, and test tooling 2026-07-04 19:06:27 +02:00

Dorfteich

Dorfteich is an open-source wiki system built around ponds (German: Teiche) — self-contained wiki spaces that people and teams organize freely with hierarchical labels, directories, and Obsidian-style page relations. Pages are edited in a collaborative WYSIWYG editor with live cursors and offline support.

Key features

  • Real-time collaboration — multiple people edit the same page simultaneously; everyone sees the other participants' cursors and input live. Offline edits merge conflict-free on reconnect (CRDT-based).
  • Ponds — isolated wiki spaces with their own members, permissions, fonts, and page organization. Every registered person gets a personal pond.
  • Flexible organization — hierarchical labels, free page ordering, [[wikilinks]] with backlinks. A classic page tree is possible but never enforced.
  • Fine-grained permissions — roles (Site Admin, Pond Admin, Editor, Reader, Public) can be granted per pond, per label, or per page; the most specific setting wins.
  • Import & export — Markdown as the primary exchange format, plus best-effort structural import from Word/OpenOffice and export to Word/OpenOffice/PDF.
  • Plugins — sandboxed extensions (custom blocks, styles, page tools) installable at runtime without redeploying the instance.
  • Self-hosting first — a single docker compose up plus a guided first-run setup wizard yields a working instance. Start here: docs/self-hosting/README.md.

Documentation

Repository layout

Path Contents
docs/manual/ User-facing manuals: user, pond-admin, site-admin, API, and MCP guides (start at docs/manual/README.md)
docs/developer/ Extending Dorfteich: plugin development and core contributions
docs/architecture/ Architecture documentation: ADRs, data model, permission model, collaboration and plugin concepts, deployment and operations
docs/self-hosting/ Install, update, backup, and troubleshooting guide for running your own instance
apps/ Application packages (web frontend, API server, collaboration server) — created as implementation proceeds
packages/ Shared packages (types, permission logic, plugin SDK)
deploy/ Docker Compose stacks and deployment tooling

Development

Requirements: Node.js ≥ 22 and pnpm (npm install -g pnpm).

pnpm install        # install all workspace dependencies
pnpm lint           # ESLint + Prettier check across the repo
pnpm typecheck      # TypeScript --noEmit in every package
pnpm test           # Vitest in every package
pnpm build          # build every package (dependency order)

The workspace packages live under apps/ (web, api, collab) and packages/ (shared). Shared logic goes into packages/shared and is imported as @dorfteich/shared — never copy code between apps.

Status

Feature-complete for a 1.0: collaboration, permissions, import/export, plugins, public REST API + MCP, backups with off-host copies and in-app restore — all shipped and release-gated. Work is tracked as issues in this repository.

Contributing

Code, comments, and documentation are written in English. Write clear code that humans can follow easily; when in doubt, prefer readability over cleverness. All contributions are accepted under the MIT license.