KB-2205

IU Core 2000x — Directus registration apply + permission-quirk blocker

4 min read Revision 1
iu-core2000xdirectusregistrationpermissionsexternal-blocker

03 — Directus registration apply + exact external blocker

1. What was attempted

The 1500x directus_registration.build_registration_package(executor, collection_name) was driven end-to-end against the live incomex-directus admin REST endpoint (http://incomex-directus:8055). The driver:

  1. Logged in via POST /auth/login -> token (321 chars, never logged).
  2. Discovered no existing collection for the target name.
  3. Built the registration package against the live PG view via discover_three_axis_fields -> 16 fields across 5 axis groups.
  4. POST /collections with schema=null and the 2000x meta body — succeeded HTTP 200, collection metadata persisted.
  5. POST /fields/<collection>/<field> for each of the 16 fields — all 16 returned HTTP 403 FORBIDDEN even though the caller holds the Administrator role with admin_access=true.
  6. GET /items/<collection> — HTTP 403 FORBIDDEN as well.

2. Defect-fixes applied to directus_registration.py

Two payload changes were necessary to make POST /collections succeed:

  • meta body was missing searchable: True (Directus v11 validation).
  • schema was being sent as {"name": collection}, which makes Directus try to CREATE the underlying table; for a view-backed registration schema=None tells Directus to use an existing relation.

The driver also passes collection_name=DIRECTUS_THREE_AXIS_VIEW (v_ui_iu_three_axis_envelope) so Directus matches against the existing view name.

3. Exact external blocker — Directus permission quirk

Even after a clean POST /collections and an explicit POST /permissions row (policy=<Administrator>, action=read, fields=["*"]), Directus v11 returns HTTP 403 on POST /fields, GET /fields, and GET /items.

User/role/policy resolution:

user.id       = 6abdec55-d911-44df-af96-3cf60b9654af (admin@example.com)
user.role.id  = a40a1070-0b62-4a7e-b2c0-bd4bce9d41ac (Administrator)
role.policies = [{ policy: 8a613123-2538-4943-9d67-b6d261a4789c }]
policy.admin_access = true   policy.app_access = true

Two hypotheses (operator decision):

  1. Directus v11 treats schema=null view-backed collections as system-adjacent and refuses runtime POST /fields because field management normally CREATEs columns in the underlying table.
  2. Token issued at /auth/login doesn't reflect policy changes without a refresh — but cleanup-deleted test permission row was confirmed deleted via DELETE /permissions/1484.

4. Disposition — DONE_WITH_EXTERNAL_BLOCKER

  • Directus collection metadata registered. Visible in GET /collections among the 264 collections.
  • Field/items access blocked by the Directus v11 quirk.
  • Package reversible. build_disable_package() returns a single DELETE /collections/v_ui_iu_three_axis_envelope step.

5. Operator next steps (deploy-gated)

Path A — promote the view to a materialised table

Migration 022_iu_three_axis_envelope_table.sql: CREATE TABLE iu_three_axis_envelope (...) mirroring the view + refresh trigger. Directus then registers it as a normal managed collection.

Path B — keep the view; configure Directus via UI

The Directus admin UI bypasses the REST permission check. Operator adds the 16 fields and grants READ permission via the UI.

Both paths are reversible. The IU Core code layer doesn't change.

6. Five-layer impact

layer impact
PG none
Directus 1 row in directus_collections for v_ui_iu_three_axis_envelope; reversible via DELETE /collections/<name>
Nuxt none
AgentData none
Qdrant none
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-2000x-qdrant-reindex-directus-apply-external-pass-open-goal/03-directus-apply-and-blocker.md