Sinds de deploy van de Azure SSO-feature (PR #17) is PocketBase op de live omgeving (Labs, learning-platform.labs.respellion.tech) down: de container crash-loopt en reageert nergens op. Elke /api/* call geeft 502 ("Backend unavailable").
Klikken op "Sign in with Microsoft" geeft een serverfout (de frontend kan auth-methods / authWithOAuth2 niet bereiken).
Root cause (primair) — migratie-replay tegen een bestaande database
Vóór PR #17 draaide PocketBase op de server zonder--migrationsDir:
De collections op de server zijn destijds out-of-band aangemaakt (via scripts/setup-pb-collections.mjs / admin UI), dus de _migrations-ledger in pb_data is leeg — PocketBase heeft daar nooit een migratie gedraaid.
PR #17 (3af105b) voegde voor het eerst de mount + flag toe:
Bij de eerstvolgende start probeert PocketBase daardoor de volledige historie (±80 migratiebestanden) opnieuw af te spelen tegen een database die dat schema al heeft. De allereerste migratie faalt al:
pocketbase-learning ... Restarting (0) Less than a second ago
Error: failed to apply migration 1778948471_created_content.js:
id: The model id is invalid or already exists.;
name: Collection name must be unique (case insensitive)..
(bron: TEMP DIAGNOSTIC-stap in Gitea Actions run #86, 2026-07-02 — live containerlogs van de server)
Een gefaalde migratie abort pocketbase serve → restart: unless-stopped → oneindige crash-loop sinds 24 juni (run #83, deploy van fab7a12). Caddy geeft 502 op alle /api/*.
Waarom de eerdere fixes niet hielpen
d519107 (relation-velden verwijderen/terugzetten) en 89d3395 (OAuth2 conditioneel bij ontbrekende env) repareren allebei 1781000000_team_members_to_auth.js — maar de server komt daar nooit aan: hij sneuvelt al op het eerste bestand. De ENTRA_* secrets staan overigens sinds 24-06 13:55Z correct in Gitea (vóór de eerste deploy), dus ontbrekende secrets waren níet de oorzaak van de outage.
De as-merged migratie crasht óók op elke database mét data. Lokaal gereproduceerd (PocketBase v0.30.4, fab7a12-versie, verse DB met volledige migratieketen):
CAUGHT#1 GoError: [team_members] failed to delete due to existing relation references: micro_learning_completions, theme_session_completions
Error: failed to apply migration 1781000000_team_members_to_auth.js: name: Collection name must be unique (case insensitive).; oauth2: (providers: (0: (clientId: cannot be blank; clientSecret: cannot be blank.).).).
De try/catch (_) {} slikt de delete-fout in, waarna de create faalt. Dit is op main deels gefixt (d519107), maar destructief: veld verwijderen + terugzetten gooit de team_member_id-kolomdata weg. De niet-gemergde branch fix/issue-16-azure-pb-crash (2628041) lost dit netter op (relation → text-conversie, data blijft staan) maar is nooit gemerged.
Env-afhankelijke one-shot migratie ("de trap"). Migraties draaien exact één keer. Als 1781000000 een keer applied wordt terwijl ENTRA_CLIENT_ID/SECRET leeg zijn (89d3395-gedrag), wordt de collection permanent zonder OAuth2-provider aangemaakt; secrets later toevoegen doet dan niets meer. Provider-configuratie hoort niet in een one-shot migratie thuis maar in een idempotente reconciliatie bij elke start (pb_hook onBootstrap).
Silent error swallowing (catch (_) {}) maskeerde de echte fout twee weken lang.
Geen health-gate in de deploy: run #83 t/m #88 zijn allemaal "success" terwijl PocketBase crash-loopt. De playbooks checken alleen docker compose up, niet of PB gezond opkomt.
docker-compose.yml (root) vereist nu .env.local (env_file zonder required: false, d519107): lokale compose-start faalt hard als dat bestand ontbreekt.
(Minor) Twee migraties delen prefix 1781000000 (created_theme_sessions, team_members_to_auth) — werkt nu (lexicografisch), maar fragiel.
Voorgestelde fix
Baseline ledger-sync migratie (sorteert vóór alles, bv. 1000000000_baseline_ledger_sync.js): detecteert een out-of-band-geprovisioned DB (schema bestaat, ledger leeg) en schrijft de historische migratiebestanden als "applied" in _migrations, zodat de replay overgeslagen wordt en de keten doorloopt naar de echte nieuwe migraties. Concept lokaal bewezen: de runner checkt de ledger per bestand, dus dit werkt binnen één boot. Op verse DB's en bestaande dev-DB's is dit een no-op.
1781000000_team_members_to_auth.js robuust maken op basis van branch fix/issue-16-azure-pb-crash (2628041): relation-velden → text-conversie (geen dataverlies, unieke index herbouwd), idempotent (skip als team_members al auth is), geen silent catches, provider alleen koppelen als env aanwezig.
OIDC-provider reconciliatie in pb_hooks (onBootstrap): bij elke start de OAuth2-config van team_members bijwerken vanuit ENTRA_* env (idempotent). Heelt ook omgevingen waar de migratie al zonder secrets applied is.
Health-gate in beide deploy-playbooks: na docker compose up wachten tot PocketBase healthy is (health-endpoint/containerstatus), anders faalt de deploy zichtbaar in CI.
docker-compose.yml: env_file optioneel maken (required: false).
Verificatie (Definition of Done)
Verse DB, zonder ENTRA-env → PB boot, WARN, geen provider (geen crash)
Verse DB, mét ENTRA-env → PB boot, auth-methods toont Microsoft/OIDC
Server-scenario: DB geprovisioned zonder ledger (setup-script) + data in completions → PB boot, ledger gesynct, team_members = auth-collection, provider actief, team_member_id-data behouden
Bestaande lokale dev-DB (ledger wél gevuld) → no-op, boot schoon
Migratie al applied zonder secrets → hook zet provider alsnog aan bij eerstvolgende start mét secrets
Deploy-pipeline faalt zichtbaar als PB niet healthy wordt
E2E op Labs: login met Microsoft werkt door de perimeter-gate heen (popup + /api/oauth2-redirect + realtime)
Omgevingen
Labs (de facto productie voor gebruikers): down sinds 24-06 — dit issue.
Production (infra/production): Deploy to Production is nog nooit uitgevoerd (0 runs). Bij een eerste échte prod-deploy geldt: lege pb_data → keten applied schoon; bestaande out-of-band DB → zelfde fix dekt het af.
## Symptomen
- Sinds de deploy van de Azure SSO-feature (PR #17) is PocketBase op de live omgeving (Labs, `learning-platform.labs.respellion.tech`) **down**: de container crash-loopt en reageert nergens op. Elke `/api/*` call geeft 502 ("Backend unavailable").
- Klikken op **"Sign in with Microsoft"** geeft een serverfout (de frontend kan `auth-methods` / `authWithOAuth2` niet bereiken).
## Root cause (primair) — migratie-replay tegen een bestaande database
Vóór PR #17 draaide PocketBase op de server **zonder** `--migrationsDir`:
```yaml
# infra/development/site/compose.yml (vóór 3af105b)
command: ["serve", "--http=0.0.0.0:8090", "--dir=/pb/pb_data"]
```
De collections op de server zijn destijds **out-of-band** aangemaakt (via `scripts/setup-pb-collections.mjs` / admin UI), dus de `_migrations`-ledger in `pb_data` is **leeg** — PocketBase heeft daar nooit een migratie gedraaid.
PR #17 (3af105b) voegde voor het eerst de mount + flag toe:
```yaml
command: ["serve", ..., "--migrationsDir=/pb/pb_migrations", "--hooksDir=/pb/pb_hooks"]
volumes:
- ./pb_migrations:/pb/pb_migrations
```
Bij de eerstvolgende start probeert PocketBase daardoor **de volledige historie (±80 migratiebestanden) opnieuw af te spelen** tegen een database die dat schema al heeft. De allereerste migratie faalt al:
```
pocketbase-learning ... Restarting (0) Less than a second ago
Error: failed to apply migration 1778948471_created_content.js:
id: The model id is invalid or already exists.;
name: Collection name must be unique (case insensitive)..
```
*(bron: TEMP DIAGNOSTIC-stap in Gitea Actions run #86, 2026-07-02 — live containerlogs van de server)*
Een gefaalde migratie abort `pocketbase serve` → `restart: unless-stopped` → **oneindige crash-loop sinds 24 juni (run #83, deploy van fab7a12)**. Caddy geeft 502 op alle `/api/*`.
### Waarom de eerdere fixes niet hielpen
`d519107` (relation-velden verwijderen/terugzetten) en `89d3395` (OAuth2 conditioneel bij ontbrekende env) repareren allebei `1781000000_team_members_to_auth.js` — maar **de server komt daar nooit aan**: hij sneuvelt al op het eerste bestand. De ENTRA_* secrets staan overigens sinds 24-06 13:55Z correct in Gitea (vóór de eerste deploy), dus ontbrekende secrets waren níet de oorzaak van de outage.
## Secundaire defecten (lokaal gereproduceerd/geverifieerd)
1. **De as-merged migratie crasht óók op elke database mét data.** Lokaal gereproduceerd (PocketBase v0.30.4, fab7a12-versie, verse DB met volledige migratieketen):
```
CAUGHT#1 GoError: [team_members] failed to delete due to existing relation references: micro_learning_completions, theme_session_completions
Error: failed to apply migration 1781000000_team_members_to_auth.js: name: Collection name must be unique (case insensitive).; oauth2: (providers: (0: (clientId: cannot be blank; clientSecret: cannot be blank.).).).
```
De `try/catch (_) {}` slikt de delete-fout in, waarna de create faalt. Dit is op main deels gefixt (d519107), maar **destructief**: veld verwijderen + terugzetten gooit de `team_member_id`-kolomdata weg. De niet-gemergde branch `fix/issue-16-azure-pb-crash` (2628041) lost dit netter op (relation → text-conversie, data blijft staan) maar is nooit gemerged.
2. **Env-afhankelijke one-shot migratie ("de trap").** Migraties draaien exact één keer. Als `1781000000` een keer applied wordt terwijl `ENTRA_CLIENT_ID/SECRET` leeg zijn (89d3395-gedrag), wordt de collection **permanent zonder OAuth2-provider** aangemaakt; secrets later toevoegen doet dan niets meer. Provider-configuratie hoort niet in een one-shot migratie thuis maar in een idempotente reconciliatie bij elke start (pb_hook `onBootstrap`).
3. **Silent error swallowing** (`catch (_) {}`) maskeerde de echte fout twee weken lang.
4. **Geen health-gate in de deploy**: run #83 t/m #88 zijn allemaal "success" terwijl PocketBase crash-loopt. De playbooks checken alleen `docker compose up`, niet of PB gezond opkomt.
5. **`docker-compose.yml` (root) vereist nu `.env.local`** (`env_file` zonder `required: false`, d519107): lokale compose-start faalt hard als dat bestand ontbreekt.
6. *(Minor)* Twee migraties delen prefix `1781000000` (`created_theme_sessions`, `team_members_to_auth`) — werkt nu (lexicografisch), maar fragiel.
## Voorgestelde fix
1. **Baseline ledger-sync migratie** (sorteert vóór alles, bv. `1000000000_baseline_ledger_sync.js`): detecteert een out-of-band-geprovisioned DB (schema bestaat, ledger leeg) en schrijft de historische migratiebestanden als "applied" in `_migrations`, zodat de replay overgeslagen wordt en de keten doorloopt naar de echte nieuwe migraties. *Concept lokaal bewezen: de runner checkt de ledger per bestand, dus dit werkt binnen één boot.* Op verse DB's en bestaande dev-DB's is dit een no-op.
2. **`1781000000_team_members_to_auth.js` robuust maken** op basis van branch `fix/issue-16-azure-pb-crash` (2628041): relation-velden → text-conversie (geen dataverlies, unieke index herbouwd), idempotent (skip als `team_members` al auth is), geen silent catches, provider alleen koppelen als env aanwezig.
3. **OIDC-provider reconciliatie in `pb_hooks`** (`onBootstrap`): bij elke start de OAuth2-config van `team_members` bijwerken vanuit `ENTRA_*` env (idempotent). Heelt ook omgevingen waar de migratie al zonder secrets applied is.
4. **Health-gate in beide deploy-playbooks**: na `docker compose up` wachten tot PocketBase healthy is (health-endpoint/containerstatus), anders faalt de deploy zichtbaar in CI.
5. **`docker-compose.yml`**: `env_file` optioneel maken (`required: false`).
## Verificatie (Definition of Done)
- [ ] Verse DB, zonder ENTRA-env → PB boot, WARN, geen provider (geen crash)
- [ ] Verse DB, mét ENTRA-env → PB boot, `auth-methods` toont Microsoft/OIDC
- [ ] **Server-scenario**: DB geprovisioned zonder ledger (setup-script) + data in completions → PB boot, ledger gesynct, `team_members` = auth-collection, provider actief, `team_member_id`-data behouden
- [ ] Bestaande lokale dev-DB (ledger wél gevuld) → no-op, boot schoon
- [ ] Migratie al applied zonder secrets → hook zet provider alsnog aan bij eerstvolgende start mét secrets
- [ ] Deploy-pipeline faalt zichtbaar als PB niet healthy wordt
- [ ] E2E op Labs: login met Microsoft werkt door de perimeter-gate heen (popup + `/api/oauth2-redirect` + realtime)
## Omgevingen
- **Labs (de facto productie voor gebruikers): down sinds 24-06** — dit issue.
- **Production (`infra/production`)**: `Deploy to Production` is nog nooit uitgevoerd (0 runs). Bij een eerste échte prod-deploy geldt: lege `pb_data` → keten applied schoon; bestaande out-of-band DB → zelfde fix dekt het af.
## Referenties
- Crash-bewijs: Gitea Actions run #86 (task 1211), TEMP DIAGNOSTIC-stap (ad88f06, branch `fix-sso`)
- Eerste kapotte deploy: run #83 (fab7a12, 2026-06-24T14:07Z)
- Laatste deploy (nog steeds kapot): run #88 (89d3395, 2026-07-11T08:23Z)
- Gerelateerd: #16 (feature), PR #17, niet-gemergde branch `fix/issue-16-azure-pb-crash` (ed1b1eb, 2628041)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Symptomen
learning-platform.labs.respellion.tech) down: de container crash-loopt en reageert nergens op. Elke/api/*call geeft 502 ("Backend unavailable").auth-methods/authWithOAuth2niet bereiken).Root cause (primair) — migratie-replay tegen een bestaande database
Vóór PR #17 draaide PocketBase op de server zonder
--migrationsDir:De collections op de server zijn destijds out-of-band aangemaakt (via
scripts/setup-pb-collections.mjs/ admin UI), dus de_migrations-ledger inpb_datais leeg — PocketBase heeft daar nooit een migratie gedraaid.PR #17 (
3af105b) voegde voor het eerst de mount + flag toe:Bij de eerstvolgende start probeert PocketBase daardoor de volledige historie (±80 migratiebestanden) opnieuw af te spelen tegen een database die dat schema al heeft. De allereerste migratie faalt al:
(bron: TEMP DIAGNOSTIC-stap in Gitea Actions run #86, 2026-07-02 — live containerlogs van de server)
Een gefaalde migratie abort
pocketbase serve→restart: unless-stopped→ oneindige crash-loop sinds 24 juni (run #83, deploy vanfab7a12). Caddy geeft 502 op alle/api/*.Waarom de eerdere fixes niet hielpen
d519107(relation-velden verwijderen/terugzetten) en89d3395(OAuth2 conditioneel bij ontbrekende env) repareren allebei1781000000_team_members_to_auth.js— maar de server komt daar nooit aan: hij sneuvelt al op het eerste bestand. De ENTRA_* secrets staan overigens sinds 24-06 13:55Z correct in Gitea (vóór de eerste deploy), dus ontbrekende secrets waren níet de oorzaak van de outage.Secundaire defecten (lokaal gereproduceerd/geverifieerd)
De as-merged migratie crasht óók op elke database mét data. Lokaal gereproduceerd (PocketBase v0.30.4, fab7a12-versie, verse DB met volledige migratieketen):
De
try/catch (_) {}slikt de delete-fout in, waarna de create faalt. Dit is op main deels gefixt (d519107), maar destructief: veld verwijderen + terugzetten gooit deteam_member_id-kolomdata weg. De niet-gemergde branchfix/issue-16-azure-pb-crash(2628041) lost dit netter op (relation → text-conversie, data blijft staan) maar is nooit gemerged.Env-afhankelijke one-shot migratie ("de trap"). Migraties draaien exact één keer. Als
1781000000een keer applied wordt terwijlENTRA_CLIENT_ID/SECRETleeg zijn (89d3395-gedrag), wordt de collection permanent zonder OAuth2-provider aangemaakt; secrets later toevoegen doet dan niets meer. Provider-configuratie hoort niet in een one-shot migratie thuis maar in een idempotente reconciliatie bij elke start (pb_hookonBootstrap).Silent error swallowing (
catch (_) {}) maskeerde de echte fout twee weken lang.Geen health-gate in de deploy: run #83 t/m #88 zijn allemaal "success" terwijl PocketBase crash-loopt. De playbooks checken alleen
docker compose up, niet of PB gezond opkomt.docker-compose.yml(root) vereist nu.env.local(env_filezonderrequired: false,d519107): lokale compose-start faalt hard als dat bestand ontbreekt.(Minor) Twee migraties delen prefix
1781000000(created_theme_sessions,team_members_to_auth) — werkt nu (lexicografisch), maar fragiel.Voorgestelde fix
1000000000_baseline_ledger_sync.js): detecteert een out-of-band-geprovisioned DB (schema bestaat, ledger leeg) en schrijft de historische migratiebestanden als "applied" in_migrations, zodat de replay overgeslagen wordt en de keten doorloopt naar de echte nieuwe migraties. Concept lokaal bewezen: de runner checkt de ledger per bestand, dus dit werkt binnen één boot. Op verse DB's en bestaande dev-DB's is dit een no-op.1781000000_team_members_to_auth.jsrobuust maken op basis van branchfix/issue-16-azure-pb-crash(2628041): relation-velden → text-conversie (geen dataverlies, unieke index herbouwd), idempotent (skip alsteam_membersal auth is), geen silent catches, provider alleen koppelen als env aanwezig.pb_hooks(onBootstrap): bij elke start de OAuth2-config vanteam_membersbijwerken vanuitENTRA_*env (idempotent). Heelt ook omgevingen waar de migratie al zonder secrets applied is.docker compose upwachten tot PocketBase healthy is (health-endpoint/containerstatus), anders faalt de deploy zichtbaar in CI.docker-compose.yml:env_fileoptioneel maken (required: false).Verificatie (Definition of Done)
auth-methodstoont Microsoft/OIDCteam_members= auth-collection, provider actief,team_member_id-data behouden/api/oauth2-redirect+ realtime)Omgevingen
infra/production):Deploy to Productionis nog nooit uitgevoerd (0 runs). Bij een eerste échte prod-deploy geldt: legepb_data→ keten applied schoon; bestaande out-of-band DB → zelfde fix dekt het af.Referenties
ad88f06, branchfix-sso)fab7a12, 2026-06-24T14:07Z)89d3395, 2026-07-11T08:23Z)fix/issue-16-azure-pb-crash(ed1b1eb,2628041)