# Ubuntu-omgeving (Postgres + app) via Tailscale bereikbaar maken vanaf de Mac ## Context Er zijn twee Scrum4Me-omgevingen: - **Omgeving 1** — productie: Vercel + Neon (managed Postgres). - **Omgeving 2** — nieuw: een eigen Ubuntu-server (`scrum4me-srv`) die de **volledige Scrum4Me-app (Next.js achter een reverse proxy) + zelf-gehoste Postgres** gaat draaien. Het doel: vanaf de Mac (`janpeters-macbook-pro`) omgeving 2 kunnen gebruiken — voor (1) een DB-client (psql/GUI), (2) de `scrum4me-docker` runner lokaal in Docker, en (3) lokale dev van de hoofd-Scrum4Me-app. Tailscale is al geïnstalleerd en verbonden op beide machines: - `janpeters-macbook-pro` → `100.73.234.116` - `scrum4me-srv` → `100.118.195.120` (Linux, SSH aan) Wat nog ontbreekt: zowel Postgres als de Next.js-app op de Ubuntu-server luisteren standaard alleen op `localhost` en zijn nog niet bereikbaar over de Tailscale-interface. De database zelf is **al volledig ingericht** (schema + data) — er is geen migratie- of seed-werk nodig, alleen netwerk-, auth- en connectie-configuratie. **Beslissingen (van de gebruiker):** - App-deploy op Ubuntu: **reverse proxy** (nginx/Caddy) vóór Next.js. - DB-toegang: **hele tailnet** mag erbij (`100.64.0.0/10`) — bewuste keuze; later eventueel te versmallen via Tailscale ACLs/groups. - Postgres-rol: **nog onzeker** — het plan voegt een controle toe en adviseert een dedicated rol. **Canonieke `SCRUM4ME_BASE_URL`:** `http://100.118.195.120` (reverse proxy op poort 80 op de Tailscale-interface, **plain HTTP**). Tailscale (WireGuard) verzorgt de transportencryptie binnen de tailnet, dus een tweede TLS-laag is hier niet nodig. Dit raw-IP-adres resolvet ook vanuit een Docker-container (geen MagicDNS-afhankelijkheid). HTTPS op de proxy is optionele hardening — zie de noot onderaan; kies je daarvoor, gebruik dan een hostnaam die óók vanuit Docker oplost en pas álle URL's hieronder consistent aan. **Bevinding uit de codebase:** in `scrum4me-docker` is de DB-koppeling puur config. Zowel `bin/run-one-job.ts` (regel 30, 115) als de MCP-server (`mcp-config.json` regel 9-10) lezen `DATABASE_URL` / `DIRECT_URL` uit de omgeving. `bin/check-tokens.sh` (regel 35-38) doet bovendien een harde `curl ${SCRUM4ME_BASE_URL}/api/products` — onbereikbaarheid is fataal (regel 52-57). Er zijn **geen code-wijzigingen** nodig — alleen `.env`. ## Voorwaarden (aantoonbaar voldaan vóór uitvoering) - [ ] Tailscale actief op beide machines (`tailscale status` toont beide nodes) - [ ] SSH naar scrum4me-srv werkt (`ssh scrum4me-srv echo ok`) - [ ] DB-schema aanwezig (tabellen + data) — géén migratie nodig ## Voorwaarden (input van de gebruiker nodig) - Postgres-rol + wachtwoord + databasenaam op de Ubuntu-server (de "USER", "PASS", "DBNAME" hieronder). Niet in de chat delen — alleen lokaal invullen. - De reverse proxy biedt de app aan op `http://100.118.195.120` (poort 80). Wijkt dit af, pas dan overal de canonieke URL consistent aan. --- ## Deel A — Ubuntu: Postgres openstellen op de Tailscale-interface Uit te voeren op `scrum4me-srv` (via `ssh scrum4me-srv` of `tailscale ssh`). 1. **Tailscale-IP bevestigen** ```bash tailscale status tailscale ip -4 # verwacht: 100.118.195.120 ``` 2. **`listen_addresses` uitbreiden** — Postgres bindt standaard alleen aan localhost. Vind het configbestand en pas aan: ```bash sudo -u postgres psql -c 'SHOW config_file;' # bv. /etc/postgresql/16/main/postgresql.conf ``` Zet in dat bestand: ``` listen_addresses = 'localhost,100.118.195.120' ``` Bewust **niet** `'*'` — zo bindt Postgres alleen aan localhost + het Tailscale-adres, nooit aan de publieke interface. > ⚠️ **Boot-order:** door aan `100.118.195.120` te binden moet `tailscale0` > al bestaan bij boot. Stap A6 maakt de systemd-ordering verplicht — sla A6 > niet over, anders faalt Postgres na een reboot. 3. **Rol, auth-methode en grants controleren/instellen** (voorkomt een login- of permission-fout ná goede netwerkconfig). De rol is nog onzeker, dus eerst inventariseren: ```bash sudo -u postgres psql -c '\du' sudo -u postgres psql -c 'SHOW password_encryption;' ``` **Advies:** gebruik (of maak) een **dedicated runtime-rol** die alleen de rechten heeft die de app/runner nodig heeft — geen superuser: ```sql CREATE ROLE scrum4me_app LOGIN PASSWORD 'lokaal-wachtwoord'; GRANT CONNECT ON DATABASE DBNAME TO scrum4me_app; -- runtime-rechten op het bestaande (gevulde) public-schema: GRANT USAGE ON SCHEMA public TO scrum4me_app; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO scrum4me_app; GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO scrum4me_app; -- zodat ook later toegevoegde tabellen/sequences werken: ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO scrum4me_app; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO scrum4me_app; ``` **Migraties:** deze runtime-rol krijgt bewust géén DDL-rechten. De DB is al ingericht, dus in normale operatie draaien er geen migraties via deze rol. Moet je later vanaf de Mac toch een Prisma-migratie draaien, gebruik dan de DB-owner-rol (apart wachtwoord), niet `scrum4me_app`. **SCRAM-verifier:** stap A4 kiest `scram-sha-256`. Een rol waarvan het wachtwoord nog als **md5** is opgeslagen kan dan niet inloggen. Forceer een SCRAM-verifier door het wachtwoord opnieuw te zetten (alleen lokaal op de server, niet in chat/docs delen): ```sql ALTER ROLE scrum4me_app WITH PASSWORD 'lokaal-wachtwoord'; ``` 4. **`pg_hba.conf` — toegang vanaf de tailnet toestaan** ```bash sudo -u postgres psql -c 'SHOW hba_file;' ``` Voeg een regel toe (boven de bestaande `host`-regels). De gebruiker koos bewust voor toegang vanaf de **hele tailnet**; maak rol en database wel expliciet i.p.v. `all all`: ``` # Scrum4Me clients via Tailscale host DBNAME scrum4me_app 100.64.0.0/10 scram-sha-256 ``` Let op: hiermee mag elke tailnet-node mét geldige credentials verbinden. Wil je dat later inperken, doe dat via Tailscale ACLs/groups of versmal het CIDR naar specifieke node-IP's. 5. **Firewall (defense-in-depth)** — alleen relevant als `ufw` actief is: ```bash sudo ufw status sudo ufw allow in on tailscale0 to any port 5432 proto tcp ``` Open 5432 **nooit** generiek (`sudo ufw allow 5432` zonder interface) — dat zou de DB internet-breed openstellen. 6. **Boot-order — VERPLICHT.** Postgres bindt aan `100.118.195.120`, een adres dat pas bestaat nadat `tailscaled` `tailscale0` heeft opgezet. **Zonder deze override faalt Postgres bij reboot** ("cannot assign requested address"). Voeg een systemd-override toe: ```bash sudo systemctl edit postgresql # of postgresql@-main ``` ```ini [Unit] After=tailscaled.service Requires=tailscaled.service ``` 7. **Postgres herstarten en verifiëren** ```bash sudo systemctl restart postgresql sudo ss -tlnp | grep 5432 # moet 127.0.0.1:5432 én 100.118.195.120:5432 tonen ``` --- ## Deel B — Ubuntu: de Scrum4Me-app (reverse proxy) bereikbaar maken op Tailscale De runner-tokencheck (`check-tokens.sh`) cURL't `${SCRUM4ME_BASE_URL}/api/products` en faalt hard als die URL onbereikbaar is. De Next.js-app draait achter een reverse proxy, dus de **proxy** moet op het Tailscale-adres luisteren — niet alleen op `localhost`. Canoniek: `http://100.118.195.120` (poort 80, plain HTTP). 1. **Reverse proxy op de Tailscale-interface laten luisteren (poort 80)** - **nginx:** in het server-block het `listen`-adres aan het Tailscale-IP binden: ``` listen 100.118.195.120:80; ``` `sudo nginx -t` → `sudo systemctl reload nginx`. - **Caddy:** site-adres `http://100.118.195.120:80` in de `Caddyfile`. - Next.js zelf mag op `127.0.0.1:` blijven; alleen de proxy is extern bereikbaar. 2. **Boot-order — VERPLICHT.** Net als Postgres bindt de proxy aan een adres dat `tailscaled` eerst moet aanmaken. **Zonder deze override faalt nginx/Caddy bij reboot.** ```bash sudo systemctl edit nginx # of caddy ``` ```ini [Unit] After=tailscaled.service Requires=tailscaled.service ``` 3. **Firewall voor poort 80** — alleen bij actieve `ufw`: ```bash sudo ufw allow in on tailscale0 to any port 80 proto tcp ``` 4. **Lokaal op de server verifiëren** ```bash curl -fsS -H "Authorization: Bearer $SCRUM4ME_TOKEN" \ http://100.118.195.120/api/products >/dev/null && echo OK ``` --- ## Deel C — Mac: connectiviteit verifiëren (DB én app) 1. **Tailscale-bereik** (al bevestigd: `scrum4me-srv` zichtbaar op `100.118.195.120`): ```bash tailscale status tailscale ping scrum4me-srv ``` 2. **MagicDNS check** — kan de Mac de server op hostnaam bereiken? ```bash ping -c1 scrum4me-srv ``` Zo ja: native macOS-clients mogen `scrum4me-srv` als host gebruiken. 3. **Postgres — TCP- en psql-test** ```bash nc -vz 100.118.195.120 5432 psql "postgresql://USER:PASS@100.118.195.120:5432/DBNAME?sslmode=disable" -c '\dt' ``` 4. **App — vanaf de Mac én vanuit een Docker-container** (Docker Desktop heeft geen Tailscale-MagicDNS; daarom de canonieke raw-IP-URL): ```bash # vanaf de Mac curl -fsS -H "Authorization: Bearer $SCRUM4ME_TOKEN" \ http://100.118.195.120/api/products >/dev/null && echo "mac OK" # vanuit een container (simuleert de runner) docker run --rm --env SCRUM4ME_TOKEN alpine sh -lc \ 'wget -qO- --header "Authorization: Bearer $SCRUM4ME_TOKEN" \ http://100.118.195.120/api/products >/dev/null && echo "docker OK"' ``` Slaagt de container-test niet (geen route naar de tailnet vanuit Docker Desktop), dan moet Tailscale in/naast de runner-container draaien — apart uit te zoeken; eerst de directe route testen. --- ## Deel D — De drie consumenten koppelen Welke host elke consument gebruikt verschilt — MagicDNS werkt wél native op macOS, maar niet binnen een Docker-container: | Consumer | Host | Reden | |---|---|---| | DB-client (psql/TablePlus) native | `scrum4me-srv` | MagicDNS werkt op macOS | | scrum4me-docker runner (Docker) | `100.118.195.120` | Docker Desktop heeft geen MagicDNS | | Hoofd-app lokale dev | `scrum4me-srv` | MagicDNS werkt op macOS | ### 1. DB-client (psql / TablePlus / DBeaver) — native op macOS Connection-string: ``` postgresql://USER:PASS@scrum4me-srv:5432/DBNAME?sslmode=disable ``` GUI-clients: host `scrum4me-srv` (of `100.118.195.120`), poort `5432`, SSL uit. ### 2. scrum4me-docker runner — bewerk `/Users/janpetervisser/Development/scrum4me-docker/.env` ``` DATABASE_URL=postgresql://USER:PASS@100.118.195.120:5432/DBNAME?sslmode=disable DIRECT_URL=postgresql://USER:PASS@100.118.195.120:5432/DBNAME?sslmode=disable SCRUM4ME_BASE_URL=http://100.118.195.120 ``` Belangrijk: - **Gebruik het rauwe Tailscale-IP, niet `scrum4me-srv`.** Docker Desktop- containers krijgen geen Tailscale-MagicDNS; de hostnaam resolvet niet binnen de container. - Laat de Neon-specifieke params (`channel_binding=require`, `sslmode=verify-full`) weg — die gelden niet voor zelf-gehoste Postgres. - Zorg dat `SCRUM4ME_TOKEN` een token van **omgeving 2** is — de tokencheck loopt tegen de Ubuntu-app, niet meer tegen Vercel. > `SCRUM4ME_TOKEN` haal je op via de Ubuntu-app: Settings → API Tokens → nieuw > token aanmaken. Een bestaand Vercel-token werkt **niet** tegen de > Ubuntu-omgeving. ### 3. Hoofd-Scrum4Me-app (lokale dev) — bewerk `/Users/janpetervisser/Development/Scrum4Me` Dit is een **andere repo** dan `scrum4me-docker`. In die repo de `.env.local` (of `.env`) aanpassen. De app draait native op macOS, dus de MagicDNS-hostnaam `scrum4me-srv` mag hier wél: ``` DATABASE_URL=postgresql://USER:PASS@scrum4me-srv:5432/DBNAME?sslmode=disable DIRECT_URL=postgresql://USER:PASS@scrum4me-srv:5432/DBNAME?sslmode=disable ``` --- ## SSL-keuze Aanbeveling: **`sslmode=disable`** voor Postgres en **plain HTTP** voor de app- URL. Tailscale (WireGuard) versleutelt het transport al end-to-end binnen de tailnet; een tweede TLS-laag op een zelf-gehoste Postgres of op de proxy levert hier vooral configuratie-gedoe op. Optionele hardening (later, samenhangend uit te voeren): - TLS-certs op Postgres + `sslmode=require`. - HTTPS op de reverse proxy. Doe dit dan met een **DNS-naam die ook vanuit Docker oplost** (raw-IP + cert geeft validatiefouten), en pas `SCRUM4ME_BASE_URL` én alle verificatie-`curl`s consistent aan naar die `https://`-hostnaam. ## Twee omgevingen naast elkaar houden Omdat omgeving 1 (Neon) blijft bestaan: bewaar twee env-varianten, bv. `.env.neon` en `.env.ubuntu`, en symlink de actieve naar `.env`: ```bash ln -sf .env.ubuntu .env # activeer Ubuntu-omgeving ln -sf .env.neon .env # activeer Neon-omgeving ``` Lichtgewicht en voorkomt dat je per ongeluk de verkeerde DB raakt. ## Te wijzigen bestanden **Op `scrum4me-srv`:** - `postgresql.conf` — `listen_addresses`. - `pg_hba.conf` — tailnet-regel voor `DBNAME` + dedicated rol. - Postgres-rol — dedicated `scrum4me_app`-rol + grants + `ALTER ROLE ... PASSWORD`. - nginx/Caddy-config — `listen` op `100.118.195.120:80`. - systemd-overrides — `After=/Requires=tailscaled.service` voor `postgresql` en de proxy. - evt. `ufw`-regels voor poort 5432 en 80 op `tailscale0`. **Op de Mac:** - `/Users/janpetervisser/Development/scrum4me-docker/.env` — `DATABASE_URL`, `DIRECT_URL`, `SCRUM4ME_BASE_URL`, `SCRUM4ME_TOKEN`. - `/Users/janpetervisser/Development/Scrum4Me/.env.local` — `DATABASE_URL`, `DIRECT_URL` (andere repo). - Géén codewijzigingen in `scrum4me-docker`. ## Verificatie (end-to-end) 1. **Netwerk:** `nc -vz 100.118.195.120 5432` vanaf de Mac slaagt. 2. **DB-client:** `psql ".../DBNAME?sslmode=disable" -c '\dt'` toont de Scrum4Me-tabellen; een test-`INSERT`/`SELECT` bevestigt dat de `scrum4me_app`-grants kloppen. 3. **App-bereik:** de `curl`/`docker run`-tests uit Deel C-4 geven beide `OK`. 4. **Reboot-test:** herstart `scrum4me-srv`; controleer daarna met `sudo ss -tlnp` dat Postgres én de proxy weer op `100.118.195.120` luisteren, en herhaal de Mac/Docker-connectiviteitstests. 5. **Runner:** na `.env`-update `docker compose up -d --force-recreate`, dan `docker compose logs -f` — `check-tokens.sh` moet "OK: 100.118.195.120:5432 reachable" én "OK: SCRUM4ME_TOKEN works" loggen, en de daemon-loop moet een job kunnen claimen uit de Ubuntu-DB. 6. **Hoofd-app:** lokale dev-server in `/Users/janpetervisser/Development/Scrum4Me` start en leest data uit de Ubuntu-DB. ## Veelvoorkomende fouten | Fout | Oorzaak | Fix | |---|---|---| | `could not translate host name "scrum4me-srv"` | MagicDNS niet actief (Docker) | Gebruik raw IP `100.118.195.120` | | `cannot assign requested address` bij Postgres-start | `tailscale0` bestaat nog niet | A6 systemd-override toevoegen | | `FATAL: password authentication failed` | SCRAM-verifier niet bijgewerkt | `ALTER ROLE scrum4me_app WITH PASSWORD '...'` herhalen | --- # Addendum — uitvoering Ubuntu-kant 2026-05-14 > Deel A + B zijn uitgevoerd. De server bleek een **andere topologie** te hebben > dan dit plan aannam: Postgres én de reverse proxy draaien als **Docker > containers**, niet host-geïnstalleerd. Dit addendum beschrijft wat er feitelijk > is gebeurd. Deel C + D (Mac-kant) staan nog open. ## Vastgestelde topologie (wijkt af van de aannames) | Plan nam aan | Werkelijkheid op `scrum4me-srv` | |---|---| | Host-Postgres (`/etc/postgresql/...`, `systemctl postgresql`) | Docker container `scrum4me-postgres` (postgres:17), data-volume `/srv/scrum4me/postgres` | | Host nginx/Caddy | Docker container `scrum4me-caddy` (caddy:2), al luisterend op `0.0.0.0:80` + `:443` | | Migratie/seed mogelijk nodig | Bevestigd niet nodig — db `scrum4me` was gevuld | Concrete waarden die het plan openliet: - **Host** = dit ís `scrum4me-srv` (`100.118.195.120`) — Deel A/B dus direct uitgevoerd, niet via SSH. - **DBNAME** = `scrum4me` - **Rol** = `scrum4me_app` aangemaakt (non-superuser, DML-only), wachtwoord lokaal gegenereerd via `openssl rand -hex 24`. ## Deel A — zoals feitelijk uitgevoerd (Docker-variant) | Plan-stap | Aanpassing | |---|---| | **A2** `listen_addresses` in `postgresql.conf` | **N.v.t.** — de container luistert intern al op `0.0.0.0`. Host-exposure = Docker port-mapping. In `/srv/scrum4me/compose/docker-compose.yml` toegevoegd: `- "100.118.195.120:5432:5432"` náást de bestaande `127.0.0.1:5432:5432`. Specifiek IP i.p.v. `0.0.0.0` — Docker's iptables-DNAT scoped dan op dat IP, publiek blijft dicht. | | **A3** rol + grants | Identiek SQL, maar uitgevoerd via `docker exec -i scrum4me-postgres psql -U scrum4me -d scrum4me`. Idempotent script (`CREATE ROLE` of `ALTER ROLE ... PASSWORD`). De `ALTER ROLE ... PASSWORD` zet meteen een SCRAM-verifier. **Let op:** `CREATE ROLE` op de gedeelde productie-DB wordt door de auto-mode classifier geblokkeerd — moet via een script dat de gebruiker zelf draait. | | **A4** `pg_hba.conf` | Bestand zit in het data-volume: host-pad `/srv/scrum4me/postgres/pg_hba.conf` (root-owned, sudo nodig). Regel toegevoegd onderaan (append is veilig — first-match, geen conflict). **Bevinding:** de postgres-image heeft al een catch-all `host all all all scram-sha-256` — onze scoped regel is dáárdoor strikt genomen redundant. Echte bescherming = IP-scoped port-binding + ufw. Catch-all strakker maken = aparte taak (hij draagt de docker-netwerk-clients). | | **A5** ufw | Identiek: `ufw allow in on tailscale0 to any port 5432 proto tcp`. | | **A6** boot-order | **Niet** `postgresql.service` (bestaat niet) maar `docker.service`. Drop-in `/etc/systemd/system/docker.service.d/tailscale-order.conf`. Bewust `After=tailscaled.service` + **`Wants=`** i.p.v. het door het plan voorgestelde `Requires=` — `Requires` op `docker.service` is fragiel (faalt tailscaled ooit, dan start de hele docker-stack niet). `After=` lost de race op; `Wants=` trekt tailscaled mee zonder hard-fail. | | **A7** restart + verify | `docker compose up -d postgres` (recreate — `restart` pakt port-wijzigingen niet). `ss -tln` toont nu `127.0.0.1:5432` én `100.118.195.120:5432`. Verificatie met een wegwerp-container: `docker run --rm --network host postgres:17 psql "postgresql://scrum4me_app:...@100.118.195.120:5432/scrum4me?sslmode=disable" -c '\dt'` — `--network host` simuleert exact hoe de Mac het ziet. | ## Deel B — zoals feitelijk uitgevoerd (Docker-variant) | Plan-stap | Aanpassing | |---|---| | **B1** proxy op tailscale-interface | **Grotendeels al gedaan** — de Caddy-container publiceert al `0.0.0.0:80`, dus luistert al op `tailscale0`. Alleen een site-block toegevoegd aan `/srv/scrum4me/caddy/Caddyfile`: `100.118.195.120:80 { reverse_proxy 172.18.0.1:3000 }`. | | **B2** boot-order proxy | **Niet nodig.** Caddy bindt aan `0.0.0.0:80`, niet aan een IP-specifiek adres — er is geen `tailscale0`-race. (Alleen Postgres had de IP-specifieke binding, vandaar dat A6 wél nodig was.) | | **B3** ufw poort 80 | **Niet nodig.** Poort 80 stond al op `ALLOW IN Anywhere`. | | **B4** verifiëren | `curl -sI http://100.118.195.120/` → `200 OK`, geen redirect. `/api/products` → 401 (bereikbaar, auth vereist). | ## Bugs / valkuilen tegengekomen tijdens uitvoering 1. **Caddy single-file bind-mount wordt stale na een atomic-rename edit.** `/srv/scrum4me/caddy/Caddyfile` is als enkel bestand ge-bind-mount. Editors (en de Edit-tooling) schrijven vaak via write-temp + rename = nieuwe inode. De container blijft naar de oude inode wijzen → `caddy reload` leest de **oude** content, schijnbaar zonder fout. Symptoom hier: het nieuwe site-block leek "stil gedropt" door Caddy's adapter, maar de container zág het block simpelweg niet. **Fix / regel:** na een Caddyfile-edit `docker compose up -d --force-recreate caddy` (of `restart`) — **niet** `caddy reload`. De recreate her-bindt de mount op de nieuwe inode. (Eerder in het project werkte een Caddyfile-edit wél, juist omdat daar toevallig een `restart` op volgde.) 2. **`http://` vs `:80` syntax — bleek een rode haring.** Aanvankelijk leek Caddy's Caddyfile-adapter `http://100.118.195.120` te droppen. Geïsoleerd getest werkte beide syntaxen prima; het echte probleem was bug #1 (stale mount). De definitieve regel gebruikt `100.118.195.120:80` — ondubbelzinnig plain-HTTP-op-poort-80. ## Verificatie-status | Plan verificatie-stap | Status | |---|---| | 1. `nc`/TCP naar 5432 | ✓ `psql` als `scrum4me_app` via `100.118.195.120:5432` werkt, ziet tabellen | | 2. DB-client grants | ✓ `SELECT` op `idea_products` werkt onder `scrum4me_app` | | 3. App-bereik | ✓ `http://100.118.195.120/` → 200, `/api/products` → 401 | | 4. **Reboot-test** | ✗ **Nog niet gedaan** — productie-server niet herstart. Handmatig uitvoeren op rustig moment; check daarna `ss -tlnp \| grep 5432`. | | 5. Runner | — Mac-kant (Deel C/D), nog open | | 6. Hoofd-app lokale dev | — Mac-kant, nog open | ## Gewijzigde bestanden op `scrum4me-srv` - `/srv/scrum4me/compose/docker-compose.yml` — postgres `ports`: extra `100.118.195.120:5432:5432` - `/srv/scrum4me/caddy/Caddyfile` — site-block `100.118.195.120:80` - `/srv/scrum4me/postgres/pg_hba.conf` — tailnet-regel (+ `.bak-`) - `/etc/systemd/system/docker.service.d/tailscale-order.conf` — boot-order drop-in (nieuw) - ufw — regel `5432/tcp on tailscale0` - Postgres-rol `scrum4me_app` — aangemaakt met grants op db `scrum4me`