Index: .dockerignore
===================================================================
--- .dockerignore	(revision 5a2cb69938b7be48d3e1d8f2ae4465720f9e2721)
+++ .dockerignore	(revision 5a2cb69938b7be48d3e1d8f2ae4465720f9e2721)
@@ -0,0 +1,16 @@
+# Build-context schoon houden — deze horen NIET in het image
+node_modules
+npm-debug.log*
+.git
+.gitignore
+.env
+.env.*
+!.env.example
+storage
+.beads
+test
+*.md
+Dockerfile
+docker-compose.yml
+.dockerignore
+.github
Index: Dockerfile
===================================================================
--- Dockerfile	(revision 5a2cb69938b7be48d3e1d8f2ae4465720f9e2721)
+++ Dockerfile	(revision 5a2cb69938b7be48d3e1d8f2ae4465720f9e2721)
@@ -0,0 +1,38 @@
+# syntax=docker/dockerfile:1
+# ──────────────────────────────────────────────────────────────────────────
+# Klonkt — zelf-host image. Multi-stage: native deps compileren in een volle
+# image, dan een slanke runtime. ffmpeg wordt meegebundeld via ffmpeg-static
+# (npm), cwebp komt uit het Debian-pakket 'webp'.
+# ──────────────────────────────────────────────────────────────────────────
+
+# ---- builder: native modules (better-sqlite3) + ffmpeg-static ophalen ----
+FROM node:20-bookworm AS builder
+WORKDIR /app
+# Build-tools voor het geval better-sqlite3 from-source moet (anders prebuilt).
+RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ \
+    && rm -rf /var/lib/apt/lists/*
+COPY package.json package-lock.json ./
+# Productie-deps; install-scripts draaien (better-sqlite3 build + ffmpeg download).
+RUN npm ci --omit=dev
+
+# ---- runtime: slank image + cwebp ----
+FROM node:20-bookworm-slim AS runtime
+ENV NODE_ENV=production \
+    PORT=3000
+WORKDIR /app
+# cwebp = afbeelding→WebP (optioneel in de app, maar handig); ca-certificates
+# voor uitgaande HTTPS (license-server, SMTP, Google).
+RUN apt-get update && apt-get install -y --no-install-recommends webp ca-certificates \
+    && rm -rf /var/lib/apt/lists/*
+# node_modules (incl. gecompileerde better-sqlite3 + gebundelde ffmpeg) uit builder.
+COPY --from=builder /app/node_modules ./node_modules
+# App-broncode.
+COPY . .
+# Persistente data leeft hier (DB, media, audio) — mount-punt voor een volume.
+RUN mkdir -p storage/media storage/audio && chown -R node:node /app
+USER node
+EXPOSE 3000
+# Simpele healthcheck via Node's ingebouwde fetch (Node 20).
+HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
+  CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
+CMD ["node", "src/server.js"]
Index: README.md
===================================================================
--- README.md	(revision 7bd97a629fcb56f05ae5ed7abd7ab73aa7649e55)
+++ README.md	(revision 5a2cb69938b7be48d3e1d8f2ae4465720f9e2721)
@@ -9,5 +9,5 @@
 ## Filosofie
 
-- **Editorial feel.** Magazine-typografie (Fraunces + Literata + Plus Jakarta), royale spacing, 8 paletten waaronder Sage / Paper / Ocean / Forest / Stone / Midnight / Sunset / Cream. Light + dark per palette.
+- **Editorial feel.** Magazine-typografie (Fraunces + Literata + Plus Jakarta), royale spacing, 12 paletten (o.a. Sage, Paper, Ocean, Forest, Stone, Midnight, Sunset, Cream, Rose, Goud, Terracotta, Lilac). Light + dark per palette.
 - **App-feel waar het telt.** Geen full page reloads — htmx swaps + (binnenkort) View Transitions. Voelt als app, niet als website.
 - **Server-rendered.** Geen build step, geen SPA-tax. EJS + htmx + minimale Alpine.
@@ -26,15 +26,59 @@
 | **2.0 — E2EE DMs** | MLS protocol via `@openmls/openmls`, single-device first | ⚪ apart project |
 
-## Quick start
+## Zelf hosten
+
+Klonkt is een **Node-app** (geen PHP) — draai 'm op een VPS, in Docker of op een
+Node-hostingplatform. **Niet** op klassieke shared PHP-hosting. De database
+(SQLite) maakt zichzelf aan bij de eerste start; er is geen los installatiescript.
+
+### Optie A — Docker (aanbevolen)
+
+Alles (Node, ffmpeg, cwebp) zit in het image; je hoeft alleen Docker te hebben.
 
 ```bash
-npm install
+git clone <repo-url> klonkt && cd klonkt
 cp .env.example .env
-# zet SESSION_SECRET op iets random (>=32 chars in production)
-npm run migrate
-npm run dev
+# vul in .env minimaal in: SESSION_SECRET (>=32 random tekens) + PUBLIC_BASE_URL
+docker compose up -d
 ```
 
-Open http://localhost:3000
+De app draait nu op poort 3000. Alle data (database + geüploade media/audio)
+blijft bewaard in het `klonkt-data`-volume, ook na een update. Updaten:
+
+```bash
+git pull && docker compose up -d --build
+```
+
+### Optie B — direct met Node (Node 20+)
+
+```bash
+git clone <repo-url> klonkt && cd klonkt
+npm ci
+cp .env.example .env        # SESSION_SECRET + PUBLIC_BASE_URL invullen
+npm start                   # database wordt bij de eerste start aangemaakt
+```
+
+Open http://localhost:3000. Voor productie: zet 'm achter een procesmanager
+(pm2/systemd) zodat 'ie aanblijft. `cwebp` is optioneel (Debian/Ubuntu:
+`apt install webp`) voor WebP-afbeeldingen — ontbreekt 'ie, dan wordt het
+origineel bewaard. (`npm run dev` = watch-mode voor ontwikkeling.)
+
+### HTTPS (productie)
+
+Zet een reverse-proxy vóór de app voor TLS. Met **Caddy** (automatisch
+Let's Encrypt) volstaat één blok:
+
+```caddy
+jouwdomein.nl {
+    reverse_proxy localhost:3000
+    encode gzip zstd
+}
+```
+
+### Eerste keer
+
+Open je site en ga naar **`/auth/register`** — de **eerste gebruiker wordt
+automatisch beheerder**; daarna sluit registratie zich. Wachtwoord kwijt?
+`npm run reset-admin` (in Docker: `docker compose exec klonkt npm run reset-admin`).
 
 ## Stack
Index: docker-compose.yml
===================================================================
--- docker-compose.yml	(revision 5a2cb69938b7be48d3e1d8f2ae4465720f9e2721)
+++ docker-compose.yml	(revision 5a2cb69938b7be48d3e1d8f2ae4465720f9e2721)
@@ -0,0 +1,28 @@
+# Klonkt — zelf-host met Docker Compose.
+#
+#   1. cp .env.example .env   en vul SESSION_SECRET + PUBLIC_BASE_URL in
+#   2. docker compose up -d
+#   3. open je site en maak via /auth/register je beheerdersaccount aan
+#
+# Standaard luistert de app op poort 3000. Zet er een reverse-proxy (Caddy/
+# nginx) vóór voor HTTPS op je domein — zie README.
+services:
+  klonkt:
+    build: .
+    image: klonkt:latest
+    restart: unless-stopped
+    env_file: .env
+    environment:
+      # In de container draait 'ie altijd op 3000 en in productie-modus,
+      # ongeacht wat er in .env staat.
+      NODE_ENV: production
+      PORT: "3000"
+    ports:
+      # host:container — wijzig de host-poort (links) als 3000 al bezet is.
+      - "3000:3000"
+    volumes:
+      # Alle data (database, geüploade media + audio) blijft hier bewaard.
+      - klonkt-data:/app/storage
+
+volumes:
+  klonkt-data:
