KB-3B23
S135D — Fix Directus 403 Regression Report
4 min read Revision 1
reports135dpermissionsregressionhotfix
S135D — Fix Directus 403 Regression Report
Date: 2026-03-18 | Agent: Claude CLI
Phase 1: Investigation Findings
Permissions before fix:
- Total public permissions (role=NULL filter): 0 (confirmed wipe)
- But permissions existed under 2 public POLICIES: abf8a154 (57 perms) + a513bc9d (37 perms)
- These covered CMS blocks, pages, posts, seo — but NOT registry collections
- S132-C registry collections (meta_catalog, dot_tools, etc.) were missing from all public policies
- 20 orphaned permissions with policy=None
Root cause:
S135B secret rotation (KEY/SECRET/ADMIN_TOKEN) + recovery (docker compose down/up) caused Directus to reset its policy-permission linkage. The CMS public permissions survived (in the named policies), but the S132-C registry permissions were lost — likely because they were under the default public role mechanism which got reset when KEY changed.
Admin token:
The GSM rotated token (2388dcec...) wasn't matching the DB static token. Fixed by updating via PATCH /users/me with JWT auth.
Phase 2: Permissions Restored
21 collections added to policy a513bc9d (Public Access) with action=read, fields=[*]:
| ID | Collection | Status |
|---|---|---|
| 451 | meta_catalog | restored |
| 452 | dot_tools | restored |
| 453 | taxonomy | restored |
| 454 | table_registry | restored |
| 455 | workflows | restored |
| 456 | workflow_steps | restored |
| 457 | workflow_change_requests | restored |
| 458 | workflow_categories | restored |
| 459 | workflow_step_relations | restored |
| 460 | modules | restored |
| 461 | agents | restored |
| 462 | checkpoint_types | restored |
| 463 | checkpoint_sets | restored |
| 464 | checkpoint_set_items | restored |
| 465 | entity_dependencies | restored |
| 466 | trigger_registry | restored |
| 467 | task_comments | restored |
| 468 | tasks | restored |
| 469 | system_issues | restored |
| 470 | changelog | restored |
| 471 | v_registry_counts | restored |
Phase 3: Verification
API verify (public, no token):
- meta_catalog: 200 (data returned)
- dot_tools: 200 (data returned)
- taxonomy: 200
- table_registry: 200
- v_registry_counts: 200
Security verify (public WRITE blocked):
- POST meta_catalog: 500 (blocked)
- PATCH dot_tools/1: 403 (blocked)
- DELETE tasks/1: 403 (blocked)
Website verify:
- / → 200
- /knowledge → 200
- /knowledge/registries → 200
- SSR payload contains: CAT-000, CAT-001, DOT Tools, etc. (client-side rendered table)
Known pre-existing issue:
- navigation/main returns empty items (junction table has no entries for main — data issue, not permissions)
- FORBIDDEN=1 in page payload from navigation_navigation_items nested field access
Production health:
- Agent Data: healthy (418 docs, 771 vectors)
- Directus: ok
- Nuxt: 200
- All 6 containers healthy
Lessons Learned for Secret Rotation
- Rotating DIRECTUS_KEY invalidates ALL JWTs — any cached tokens in other services break
- docker compose down/up can reset permission linkages
- Always verify public permissions AFTER rotation
- Admin static token must be explicitly set via API after rotation
- Test public endpoints (not just authenticated) during rotation verify