KB-7F09

C2B1-R0 — Option B: Direct Decomposition (least-privilege identity set) design [RECOMMENDED]

5 min read Revision 1
current-statec2b1-r0DIRECT_DECOMPOSITIONoption-brecommendedleast-privilegeidentity-setnuxt-serviceagent-data-syncreconcile-writerdot-observerSYNC_SECRETread-only2026-07-21

Option B — Direct Decomposition (least-privilege identity set)

Concept

Provision a small set of least-privilege machine identities, one per capability cluster, repoint each consumer to its own identity, verify (positive + negative), then rotate admin. No throwaway broad token; the end-state is the desired least-privilege posture with no second pass.

Identity set (provisioned in S1-APPLY — NOT created in R0)

Measured scope is small, so the nominal 5 groups collapse to 4 live + 1 deferred:

# Identity Consumers covered Capability (content only; admin_access=FALSE) Arm
1 nuxt-service incomex-nuxt READ registry/discovery/transitive collections; READ+WRITE workflows static token
2 agent-data-sync incomex-agent-data READ sync-target collections; DELETE sync-target items static token
3 reconcile-writer reconcile-tasks, reconcile-knowledge READ+WRITE tasks, task_comments, knowledge_documents static token (replaces password login)
4 dot-observer dot-collection-health, dot-apr-health (+ dot-dot-coverage if API-confirmed) READ+WRITE system_issues; READ health metadata static token (replaces password login)
5 dot-operator (DEFERRED) dot-apr-execute — (job is BROKEN; provision only after it is fixed)

Notes:

  • The mission's "Nuxt read" and "Nuxt workflow writer" groups are merged into nuxt-service: it is a single container with a single token var; splitting into two tokens in one process buys nothing. (Evidence: compose L174 single arm; all fallbacks UNSET.)
  • Converting the password-login consumers (reconcile, dot-*) to static tokens removes the 25-fail lockout vector entirely (token auth does not increment login attempts) and removes session churn.
  • Scanners (orphan, misclass) and dot-dot-health need no identity — they are PG-direct; just remove their dead credential exports (hygiene).

Exact permission rows per identity are designed in S1-DESIGN, not R0.

Migration sequence (blast-radius-first)

  1. Now / no gate: provision identities 1–4; repoint the 4 file consumers (reconcile×2, dot-collection-health, dot-apr-health) → edit protected files only, no restart. This removes the password blast-radius and the lockout risk immediately.
  2. Now / no gate: docker save agent-data image → repoint agent-data-sync → recreate agent-data.
  3. Parallel: run NUXT-IMG-H1 (pin image to local digest 72715a..., docker save backup, prove a no-pull recreate on a throwaway clone).
  4. After NUXT-IMG-H1: repoint Nuxt → nuxt-service → recreate nuxt.
  5. C2B1-S2 negative verification per identity (each identity is denied everything outside its envelope; admin-surface calls 403). C2B1-R1 proves machine-dependency-on-admin = 0.
  6. C2B1-X: rotate admin password + static token. C2B1-V: encrypted backup + restore proof.

Rollback

Per-identity and per-consumer. Each repoint reverts by restoring one file/compose line (+ recreate for containers). Admin credential remains valid until X, so fall-back is always available.

Trade-off

  • + End-state is least-privilege with no throwaway broad token and no second pass.
  • + Password→token conversion eliminates the lockout vector.
  • ~2h more design/verify than Option A (4 policies vs 1).
  • = Same Nuxt-image gate and same 2 restarts as Option A.

Return values

DIRECT_DECOMPOSITION_ETA_OPERATOR_HOURS = ~8h   (4 identities+policies 3h; file repoints 0.75h;
                                                 agent-data 0.5h; NUXT-IMG-H1 1.5h; nuxt 0.5h;
                                                 rotate 0.5h; per-identity verify 1.25h)
DIRECT_DECOMPOSITION_ETA_ELAPSED_HOURS  = ~48-72h (staged verification per identity; cautious)
DIRECT_DECOMPOSITION_RESTART_COUNT      = 2       (agent-data, nuxt) — Directus NOT restarted
DIRECT_DECOMPOSITION_BLOCKERS           =
   - NUXT_IMAGE_RECREATE_READY = NO  -> NUXT-IMG-H1 mandatory before nuxt repoint (SHARED with Option A)
   - agent-data sync-target collection set is dynamic -> enumerate exactly in S1 (coarse set known)
   - dot-dot-coverage API-vs-PG usage low-confidence -> confirm in S1 (fold into dot-observer or drop)
   - SYNC_SECRET == admin token -> must be decoupled before/with C2B1-X
Back to Knowledge Hub knowledge/current-state/reports/c2b1-r0-bootstrap-admin-containment-decision-2026-07-21/05-direct-decomposition-design.md