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

  1. Rotating DIRECTUS_KEY invalidates ALL JWTs — any cached tokens in other services break
  2. docker compose down/up can reset permission linkages
  3. Always verify public permissions AFTER rotation
  4. Admin static token must be explicitly set via API after rotation
  5. Test public endpoints (not just authenticated) during rotation verify