C2B1-HYB-S1 — Transitional Non-Admin Machine Identity Provisioning & Shadow Auth Verify (2026-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_READYMissionC2B1-HYB-S1(+ appendix S1-BIS) · host Contabovmi3080463· UTC window 2026-07-22 09:43Z → 10:25Z Authorization:OWNER_AUTHORIZATION = GRANTED_BY_GPT_UNDER_OWNER_DELEGATION· pathHYBRID_FAST_CONTAINMENTAdditive-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_ROTATIONremainsHOLD.
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-countsiterates 155meta_catalog.registry_collectionvalues (incl. system collectiondirectus_fieldsand 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_accessunchanged (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 (
diffempty). No restart/recreate. - Consumer config untouched:
docker/.env,docker-compose.yml,reconcile/directus-admin.env,agent-data.envmtimes 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 toc2b1/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
PPIDis 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 varPPID. - 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
$fetchBearer directly (not always SDK);/items/universal_edges&/items/pivot_resultsare not Directus collections (pg views) → graceful-empty, excluded. - agent-data
directus_sync.pycreate/update go to Postgres directly (psycopg2), only GET+DELETE use the Directus token → token surface =knowledge_documentsread+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.shCLOUD fallback). directus.incomexsaigoncorp.vn→ 38.242.240.89 → nginxdirectus_backend(this VPS). Static-token Bearer creates no session; only/auth/logindoes.
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