KB-2C66

C2B1-HYB-S1 — Transitional Non-Admin Machine Identity Provisioning & Shadow Auth Verify (2026-07-22)

15 min read Revision 1
current-statec2b1-hyb-s1hybrid-fast-containmentdirectustransitional-identitynon-adminmachine-identityleast-privilegecanonical-serializationshadow-authpublic-pathpassword-login-public-pathzero-mutationno-driftdecomposition-deadlinePASS_C2B1_HYB_S1_TRANSITIONAL_IDENTITY_SHADOW_READYTEMPORARY_CONTAINMENT_ONLY2026-07-22

C2B1-HYB-S1 — Transitional Non-Admin Machine Identity Provisioning & Shadow Authentication Verify (2026-07-22)

VERDICT: PASS_C2B1_HYB_S1_TRANSITIONAL_IDENTITY_SHADOW_READY Mission C2B1-HYB-S1 (+ appendix S1-BIS) · host Contabo vmi3080463 · UTC window 2026-07-22 09:43Z → 10:25Z Authorization: OWNER_AUTHORIZATION = GRANTED_BY_GPT_UNDER_OWNER_DELEGATION · path HYBRID_FAST_CONTAINMENT Additive-only production mutation (1 policy + 1 role + 1 access + 1 user + 1 token + 37 permission rows). Zero existing-object mutation, zero consumer repoint, zero container restart, zero credential rotation. No token/password/key value in this report. Gate opened by this PASS: C2B1-HYB-S2 (nothing else). C2B1_ROTATION remains HOLD.


1. Executive verdict

A single transitional, non-admin Directus machine identity was provisioned and shadow-verified. It authenticates, reads the exact union of proven machine-consumer surfaces, and is denied on every admin surface — over both the internal (http://incomex-directus:8055) and public (https://directus.incomexsaigoncorp.vn) paths. No consumer was repointed; the bootstrap admin credential is byte-unchanged; all 11 containers are fingerprint-identical PRE↔POST; the malformed-permission baseline is unchanged at 632.

TRANSITIONAL_IDENTITY_PROVISIONED       = YES
TRANSITIONAL_IDENTITY_ADMIN_ACCESS      = FALSE
TRANSITIONAL_IDENTITY_APP_ACCESS        = FALSE
TRANSITIONAL_TOKEN_AUTHENTICATES        = YES
REPRESENTATIVE_READ_PROBES              = PASS   (internal + public)
ADMIN_NEGATIVE_PROBES                   = PASS   (internal + public)
NEW_PERMISSION_FIELDS_SERIALIZATION     = CANONICAL
MALFORMED_PERMISSION_ROWS_DELTA         = 0      (632 PRE = 632 POST)
EXISTING_OBJECT_MUTATION                = 0
CONSUMER_REPOINTED                      = 0
CONTAINER_MUTATION                      = 0
CREDENTIAL_ROTATED                      = 0
PUBLIC_PATH_SHADOW_AUTH                 = PASS
PASSWORD_LOGIN_PUBLIC_PATH_MAP          = COMPLETE
NEW_BOOTSTRAP_ADMIN_SESSION_CREATED     = 0

2. Recovery continuity (Gate S1-0 — PASS)

Anchor State
B0 Drive artifact incomex-encrypted-v1/c2b1-precut/C2B1_PRECUT_20260722T083250Z/…sql.gz.gpg present, sha256 ed1e76990bc25da6…af99f1 (no drift), 121,276,227 B
B0 protected checkpoint /root/c2b1-hybrid-precut-checkpoint-20260722T085250Z present, root:root 0700, evidence/secret/config/effective intact
H1 checkpoint /root/nuxt-img-h1-checkpoint-20260721T150753Z present, root:root 0700
Git checkpoint 714d4d5 present (= HEAD of /opt/incomex)
Locks (pg-backup / balo-reconcile / nginx-change / dot-sync / lark) all free
Running backup / rclone / deploy / pg_dump none (only a Kuma heartbeat push)

All anchors intact PRE and POST.

3. Reconciled consumer set (Gate S1-1 + Z1 — no drift)

Live Directus credential consumers re-confirmed (read-only). No new live consumer discovered.

Consumer Auth arm Path Directus-token surface (measured from live code)
incomex-nuxt (SSR service client) static token = admin internal reads registry/discovery/transitive collections; workflows r/u; workflow_steps/workflow_step_relations r/c/u/d; workflow_change_requests r/c/u; block_library r; tasks r/c; task_comments c; meta_catalog r/u
incomex-agent-data (directus_sync.py) static token = admin internal knowledge_documents read + delete only (create/update via direct PG, not token)
reconcile-tasks (/opt/incomex/scripts/reconcile-tasks.py) admin password (login→session) internal tasks read, task_comments read (one-way Directus→agent-data; no Directus write)
reconcile-knowledge (reconcile-knowledge.py) admin password internal knowledge_documents read + create (POST) + update (PATCH)
dot-collection-health (/opt/incomex/dot/bin/…) admin password (login→DOT_TOKEN) PUBLIC https://directus.incomexsaigoncorp.vn system_issues create (reads are PG-direct)
dot-apr-health admin password PUBLIC system_issues create

Not in union (re-confirmed): orphan/misclass scanners + dot-dot-health (vestigial arm; PG-direct), dot-dot-coverage (PG-only, no /items/ or /auth/login), dot-apr-execute (broken localhost:8055), claude-mcp (tac-agent, non-admin).

Z1  PASSWORD_LOGIN_PUBLIC_PATH_CONSUMERS = { dot-collection-health, dot-apr-health }
    MUST_BE_REPOINTED_BEFORE_ADMIN_PASSWORD_ROTATION = YES
    (reconcile-tasks/knowledge also password-login but INTERNAL path)

4. Identity object model (Directus 11.5)

Mirrors the proven non-admin tac-agent precedent: user → role → (directus_access) → policy → permissions. admin_access/app_access live on the policy (Directus 11 model).

policy  machine-transition-policy   admin_access=false  app_access=false  enforce_tfa=false
role    machine-transition          → bound to policy via directus_access (role→policy)
user    machine-transition@incomexsaigoncorp.vn   status=active  provider=default  token-only
        (random discarded password; effective policy set = machine-transition-policy ONLY)

5. Exact union permission manifest (37 rows / 21 collections)

All rows: fields='*' (canonical), permissions=NULL, validation=NULL, presets=NULL. Derived strictly from measured code (02-capability-map.csv + live Nuxt bundle /app/.output/server/…, directus_sync.py, reconcile scripts, dot bins).

Collection Actions Evidence
workflows read, update nitro readItem/updateItem("workflows")
workflow_steps read, create, update, delete nitro readItems/createItem/updateItem/deleteItems("workflow_steps")
workflow_step_relations read, create, update, delete nitro …("workflow_step_relations")
workflow_change_requests read, create, update nitro WCR intake
block_library read nitro readItem("block_library")
tasks read, create nitro readItem/createItem("tasks") + reconcile-tasks GET
task_comments read, create nitro createItem("task_comments") + reconcile-tasks GET
knowledge_documents read, create, update, delete reconcile-knowledge GET/POST/PATCH + agent-data GET/DELETE
meta_catalog read, update registry/counts GET + refresh-counts PATCH
system_issues read, create Nuxt registry system-issues GET + dot health POST
agent_views, ai_discussions, ai_discussion_comments, birth_registry, collection_registry, entity_dependencies, entity_labels, entity_species, pivot_definitions, species_collection_map, taxonomy read Nuxt registry/discovery/transitive $fetch /items/<c>

Deliberately excluded (least-privilege, §5/§8):

  • universal_edges, pivot_results — referenced by Nuxt but not Directus-registered collections (pg views only); route code fails graceful-empty. No grant.
  • refresh-counts iterates 155 meta_catalog.registry_collection values (incl. system collection directus_fields and non-collections) with per-item try/catch — not blanket-granted. Manual + graceful-degrade; a transitional identity must be decomposed. Flagged for S2/decomposition review if parity is required.

6. Permission serialization proof (§7 / Z0)

Z0  PERMISSION_ROWS_PRE                 = 1207
    MALFORMED_LITERAL_OBJECT_ROWS_PRE   = 632     (== required 632)  → no baseline drift
    CANONICAL_WILDCARD_ROWS_PRE         = 554
Probe: POST /permissions {fields:["*"]} on agent_views/read → id 1529 → PG read-back fields = "*"  (CANONICAL, not {"*"})
Bulk : 36 further rows created identically; PG verify:
    rows_on_policy=37  canonical=37  malformed_on_policy=0  other=0  nonnull_json_filters=0  distinct_collections=21
POST : PERMISSION_ROWS_POST=1244 (+37)  MALFORMED_LITERAL_OBJECT_ROWS_POST=632 (UNCHANGED)  canonical=591 (+37)
NEW_PERMISSION_FIELDS_SERIALIZATION = CANONICAL   MALFORMED_LITERAL_OBJECT_ROWS_CREATED = 0

The Directus 11.5 REST API (fields:["*"], filters omitted) stores canonical *; the pre-existing 632 malformed {"*"} rows were untouched and not repaired (out of S1 scope).

7. Object IDs created (rollback ledger — /root/c2b1s1/ids.env, root:600)

policy   machine-transition-policy   7c19e8cc-1100-499c-bb69-e2fda6826aa3
role     machine-transition          f03a039e-8058-49ad-893e-8015889443fd
access   (role→policy)               86abbb5e-905d-4d02-9b39-69d71a2f6a6a
user     machine-transition@…        73aa333b-3bf9-4763-b25f-27102ebb06b1
perms    directus_permissions.id     1529 … 1565  (37 contiguous)

8. Protected token storage proof (§10 / Z-token)

FILE  /etc/incomex/c2b1/machine-transition.env   owner=root:root  mode=600  size=620
DIR   /etc/incomex/c2b1                            owner=root:root  mode=700
Token: openssl rand -hex 32 (len 64) · md5 fingerprint eccaca8d40d8 · last4 0dc2

Not written to project .env, Git, KB, shell profile, command line, or Drive. PATCH performed via chmod 600 temp file (-d @file), then shred -u. Only the md5 fingerprint appears in this report.

9. Positive read probes (§11 / Z2) — PASS (internal + public)

/users/me (both paths) → returns machine user id 73aa333b… (Directus scoped-token returns id only — expected). 8/8 representative reads → HTTP 200 on both paths: workflows, meta_catalog, knowledge_documents, tasks, task_comments, system_issues, entity_dependencies, workflow_steps (covers Nuxt, agent-data, reconcile, observer surfaces). No business write performed in S1.

10. Negative admin probes (§12 / Z2) — PASS (internal + public)

All → 403 on both paths:

  • /users?limit=2, /users/6abdec55… (other user), /roles, /policies, /permissions, /access, /settings, /flows
  • out-of-union content: /items/organizations, /items/billing, /items/os_deals, /items/contacts (proves no public/blanket read leak)
  • DELETE /items/system_issues/<nonexistent-uuid> → 403 (denied; nonexistent id ⇒ no data could be affected)

FAIL_C2B1_HYB_S1_ADMIN_SURFACE_EXPOSED = not triggered.

11. Existing-surface no-drift proof (§13 / Z3)

                       PRE      POST
directus_users         13    →  14   (+1 machine-transition)
directus_roles          9    →  10   (+1)
directus_policies       8    →  9    (+1)
directus_access         9    →  10   (+1)
directus_permissions 1207    →  1244 (+37)
malformed {"*"} rows  632    →  632  (UNCHANGED)
  • Bootstrap admin (6abdec55): status=active, token md5 fp bb28e18da2d5 (IDENTICAL), token_len=64, role=a40a1070 (Administrator) — all unchanged; last_access unchanged (2026-07-22 10:00:05).
  • Sessions: 35 → 35, all admin, distinct users=1 → NEW_BOOTSTRAP_ADMIN_SESSION_CREATED_BY_S1=0, EXTERNAL_ACTIVE_SESSION_DELTA=0. (Provisioning used the admin static token / Bearer only — never /auth/login; static-token auth creates no session.)
  • 8 original policies byte-identical (flags + perm-row counts): Administrator (t/t/19), Agent Policy (f/t/257), AI Agent Policy (f/f/329), Editor Policy (f/t/118), Public Access (f/f/172), $t:public_label (f/f/170), tac-admin-policy (f/f/56), tac-agent-policy (f/f/28). Public permissions unchanged.
  • Containers: all 11 IDs/StartedAt/RestartCount/Status IDENTICAL PRE↔POST (diff empty). No restart/recreate.
  • Consumer config untouched: docker/.env, docker-compose.yml, reconcile/directus-admin.env, agent-data.env mtimes 2026‑07‑17/21 (pre-session).
  • Git: S1 wrote nothing under /opt/incomex. Working-tree churn (docs/ui-preview-notebook/*, docs/mcp-writes/ui-preview/*, work/sr-mow-d30d31/*) is concurrent background automation; 0 changed paths relate to c2b1/machine-transition.

12. Rollback readiness (§14)

Single-shot reverse-order rollback available (all IDs in §7 ledger): PATCH token→null → delete user 73aa333b… → delete access 86abbb5e… → delete permissions 1529–1565 → delete role f03a039e… → delete policy 7c19e8cc…. Only additive objects are removed; no pre-existing object is touched. Not executed (S1 PASS).

13. Decomposition deadline & residuals

DECOMPOSITION_START  <= 24h after C2B1-X PASS
DECOMPOSITION_FINISH <= 7 calendar days after C2B1-X PASS   (C2B1_DECOMPOSITION_COMPLETE_DEADLINE)

The transitional identity is a broad content-R/W bridge (non-admin) and MUST be decomposed into per-consumer least-privilege identities, else it becomes permanent scope creep.

Z4  RECOVERY_RECIPIENT_COUNT_PROVEN = 1
    SECOND_INDEPENDENT_RECIPIENT    = REQUIRED_BEFORE_DESTRUCTIVE_VPS_CLEANUP
    SECOND_INDEPENDENT_RECIPIENT    = NOT_A_BLOCKER_FOR_C2B1_HYB_S1

14. Exact gate opened

PASS_C2B1_HYB_S1_TRANSITIONAL_IDENTITY_SHADOW_READY  → opens  C2B1-HYB-S2  (reversible write-capability probe)
C2B1_ROTATION = HOLD (unchanged)   NUXT_IMAGE_RECREATE_READY = YES   B0_FRESH_RECOVERY_POINT = PASS

No consumer repointed, no credential rotated, no cleanup started. STOP after report.


Operator landmines recorded (for successors)

  • Bash PPID is a reserved special variable (parent PID) — PPID=$(…) silently fails "readonly"; a script-echoed permission id was actually the parent PID (real id read back from PG). Never name a var PPID.
  • Directus 11.5 REST POST /permissions {fields:["*"]} (filters omitted) stores canonical *; the malformed {"*"} legacy rows come from a different (non-API) write path.
  • Nuxt registry routes reach Directus via $fetch Bearer directly (not always SDK); /items/universal_edges & /items/pivot_results are not Directus collections (pg views) → graceful-empty, excluded.
  • agent-data directus_sync.py create/update go to Postgres directly (psycopg2), only GET+DELETE use the Directus token → token surface = knowledge_documents read+delete only.
  • reconcile-tasks is read-only to Directus (syncs Directus→agent-data). dot health jobs use the public path; localhost:8055 is unpublished (environment.sh CLOUD fallback).
  • directus.incomexsaigoncorp.vn → 38.242.240.89 → nginx directus_backend (this VPS). Static-token Bearer creates no session; only /auth/login does.

Search terms: PASS_C2B1_HYB_S1_TRANSITIONAL_IDENTITY_SHADOW_READY · TRANSITIONAL_IDENTITY_ADMIN_ACCESS · TEMPORARY_CONTAINMENT_ONLY · NEW_PERMISSION_FIELDS_SERIALIZATION · C2B1_DECOMPOSITION_COMPLETE_DEADLINE

Back to Knowledge Hub knowledge/current-state/reports/c2b1-hybrid-s1-transitional-identity-provisioning-2026-07-22.md