KB-1C76

NUXT-403-R1 — Nuxt/Directus 403 Identity and Denial-Path Map (2026-07-20)

28 min read Revision 1
nuxtdirectus403permissionsc2b1safety-gateidentityread-only

NUXT-403-R1 — Nuxt/Directus 403 Identity & Denial-Path Map

Date (UTC): 2026-07-20 Mission: NUXT-403-R1 — map exact Nuxt identity and Directus denial path Mode: READ-ONLY. No fix applied. PAYLOAD=REDACTED — no record data, no credential values printed anywhere in this report.


1. EXECUTIVE VERDICT

VERDICT = HOLD_C2B1_ADMIN_DEPENDENCY_FOUND

Two independent findings, which must not be collapsed into one:

(A) The 403 itself is benign and pre-existing. The failing request is the homepage SSR query. It runs as fully anonymous (no token attached at all). It fails because two (at least) public-policy field grants on M2M/O2M junction-backed alias fields are malformed. It has nothing to do with C2A, with admin credentials, or with authentication.

(B) But Nuxt does depend on the bootstrap admin static token — elsewhere. NUXT_DIRECTUS_SERVICE_TOKEN fingerprints exactly to directus_users.token of admin@example.com (role Administrator, admin_access=true). It is attached via .with(staticToken(...)) on a separate service client backing 17 server route chunks plus workflows read and write. Those routes are live in production.

C2B1 rotates admin password and static token as one set. Rotating that token breaks the Nuxt service-client surface. Therefore:

C2B1_ALLOWED_FROM_NUXT_PERSPECTIVE = NO
HOLD_C2B1_NUXT_ADMIN_STATIC_TOKEN_IS_LOAD_BEARING

Trap explicitly avoided (mission §5). The 403 proves the failing request has no effective admin access. It does not license the inference CONFIGURED_ADMIN_DEPENDENCY=NO. Completing the mapping found the admin dependency on a different code path. Had the mission stopped at "403 ⇒ not admin ⇒ C2B1 safe", it would have shipped a token rotation that silently breaks production.

Verdict matrix

Field Value
NUXT_403_STATE ACTIVE
MARKETING_ROUTE_IMPACT CONTENT_MISSING
BUSINESS_ROUTE_IMPACT NONE_OBSERVED
CONFIGURED_IDENTITY MIXED (anonymous SSR path + admin static-token service path)
EFFECTIVE_REQUEST_IDENTITY (403 path) ANONYMOUS_PUBLIC
PUBLIC_POLICY_BINDING MULTIPLE_EFFECTIVE_POLICIES
EXACT_DENIAL_TARGETS 2 CONFIRMED + 3 STRUCTURALLY_PREDICTED (§11)
C2A_RELATIONSHIP PREEXISTED_C2A
TRACKED_PERMISSION_MUTATION_IN_MEASURED_WINDOW NONE_FOUND
NUXT_ADMIN_CREDENTIAL_DEPENDENCY ADMIN_STATIC_TOKEN
HTTP_403_IS_EVIDENCE_AGAINST_EFFECTIVE_ADMIN_ACCESS YES
C2B1_ALLOWED_FROM_NUXT_PERSPECTIVE NO

2. SOURCES AND BASELINE

Measurement clock: 2026-07-20T01:53:18Z2026-07-20T02:10Z (UTC). Host TZ = UTC+2.

Source Detail
incomex-nuxt container log 84,190 lines, 30 MB, window 2026-07-17T03:49:54Z → now
incomex-directus container log 32 MB, rotated; window 2026-07-14T07:28:19Z → now
incomex-nginx container log in-container (host /var/log/nginx empty)
PostgreSQL directus DB read-only role, query_pg MCP, AST-validated SELECT
Nuxt build artifacts /app/.output/server/**, /app/.output/public/_nuxt/**
Host crontab read-only

Baseline claims re-verified (not assumed)

Baseline claim Status Evidence
No permission/policy/access/role update in window CONFIRMED last mutation of any kind 2026-05-23; §12
Only user update was C2A no-op CONFIRMED exactly 1 row since 2026-07-10, UA c2a-drill
pages has read in both public-labelled policies CONFIRMED both grant fields=*, no filter
403 may be a nested relational denial CONFIRMED denial is nested, not root
reconcile still logs in periodically CONFIRMED 4,645 logins, last 2026-07-19 22:00:15

3. PRODUCTION SAFETY PROOF

Container state, first vs. last measurement — byte-identical:

Container RestartCount StartedAt
incomex-nuxt 79 → 79 2026-07-19T12:15:26.039Z (unchanged)
incomex-directus 0 → 0 2026-05-08T09:13:06.267Z (unchanged)
incomex-nginx 0 → 0 2026-06-30T00:44:14.689Z (unchanged)

Directus state after mission: permissions=1207, policies=8, access_bindings=9, roles=9, users=13, users_with_token=13, sessions=16. admin_token_fp_now = 4d1f0a095ab6unchanged, still matching Nuxt's configured token.

All DB access went through a read-only role inside a READ ONLY transaction. All HTTP probes were GET with no Authorization header, no cookie, no login.


4. BOUNDED 403 TIMELINE

FIRST_CONFIRMED_403_UTC  = 2026-07-14T07:29:20.057Z   (earliest evidence available)
LAST_CONFIRMED_403_UTC   = 2026-07-20T01:53:57.854Z   (+ continuing during measurement)
CURRENT_403_FREQUENCY    = 1 per 60s exactly (~64-67/hour), dead flat
NUXT_403_STATE           = ACTIVE

The 403 predates every available log window. The Directus log begins 07:28:19Z and the first 403 appears at 07:29:20Z61 seconds in, already at steady state. The Nuxt container log begins 2026-07-17T03:49:54Z ("Listening on…") and the first 403 is at 03:50:25Z31 seconds after boot. Neither log captures a "before". The true origin is older than retained evidence.

Daily counts — note the rate is unchanged across the C2A boundary:

Day (UTC) Nuxt [Homepage SSR] Directus 403 /items/pages
2026-07-14 (no log) 1,719
2026-07-15 (no log) 1,997
2026-07-16 (no log) 2,343
2026-07-17 1,314 (from 03:50) 2,698
2026-07-18 — C2A day 1,562 2,030
2026-07-19 1,558 2,279
2026-07-20 (partial) 123 162

Jul-17 partial reconciles exactly: 20.2 h x ~65/h ~= 1,313 vs. 1,314 observed. The cadence is a constant 60 s heartbeat with no discontinuity at C2A (12:10:49Z).

Driver: 172.18.0.1 (Docker bridge gateway) issues GET / at 60 s intervals — 180 hits in 3 h, UA -. An internal uptime monitor, not user traffic. Real external hits to / in the same window: single digits, mostly scanners.

Directus counts exceed Nuxt's because Nuxt only logs the [Homepage SSR] label. The difference is the catch-all page route resolving scanner probes (§7).


5. EXACT FAILED QUERY SHAPE

HTTP_METHOD        = GET
DIRECTUS_ENDPOINT  = http://directus:8055/items/pages       (internal, container network)
ROOT_COLLECTION    = pages
QUERY_SHAPE        = filter={"permalink":{"_eq":"/"}} & limit=1 & fields=<~4.4 KB list>
RESPONSE_STATUS    = 403  (47 ms, server-side confirmed)
REDACTED_ERROR_CLASS = FORBIDDEN

REQUESTED_FIELDS / EXPANDED_RELATIONS — root *, plus seo.*, plus the M2A blocks.item fan-out across 16 block variants:

block_hero, block_faqs, block_richtext, block_testimonials, block_quote, block_cta,
block_form, block_logocloud, block_gallery, block_steps, block_columns, block_divider,
block_team, block_html, block_video, block_cardgroup

with deeper hops into button_group -> buttons -> page.permalink / post.slug, directus_files, forms, testimonials.

Source of truth: chunks/build/index-Jk9tIu9i.mjs:95 (primary) and :185 (fallback).

Failure is caught, not fatal. The handler wraps the call in try/catch, logs [Homepage SSR] Directus query failed, returns null, then runs a second query homepage-simple requesting only id, title, permalink, status, summary — no block relations, so it succeeds. Only if both fail does it throw createError(404).


6. MARKETING / BUSINESS ROUTE IMPACT

Route HTTP Bytes Observation
/ 200 256,999 title Home - Incomex AI Portal, one <h1>, zero block markers
/reports 200 276,452 title Reports - Incomex AI Portal — healthy
/balo 404 158 expected — no /balo page route exists in this build
MARKETING_ROUTE_IMPACT = CONTENT_MISSING
BUSINESS_ROUTE_IMPACT  = NONE_OBSERVED

Homepage content-presence signature: gallery, logocloud, testimonial, block_hero all return 0 occurrences; Page Not Found returns 0. The page serves 200 through the degraded homepage-simple fallback — the site is up, but the homepage renders with none of its CMS blocks. The uptime monitor sees 200 and stays green, which is why this has gone unnoticed.

/balo 404 is not a regression: nginx exposes only location = /api/balo/reconcile, and no balo page chunk exists in the Nuxt build. The BALO one-click control is an API endpoint surfaced from another page, consistent with prior BALO work.


7. CONFIGURED VERSUS EFFECTIVE NUXT IDENTITY

Nuxt runs two distinct Directus clients. Conflating them is the central risk of this investigation.

Path A — anonymous SSR client (this is the 403 path)

chunks/build/server.mjs:5295:

const directus = createDirectus(joinURL(directusBaseUrl), { globals: { fetch: $fetch } })
  .with(rest());          // no staticToken(), no authentication()

useDirectus() (useDirectus-BFIZcoWi.mjs) simply calls $directus.request(options). The auth plugin adjacent to it is a no-op (defineNuxtPlugin(async () => { return; })).

Symbol counts in the Homepage SSR chunk index-Jk9tIu9i.mjs: staticToken = 0, createDirectus = 0.

CONFIGURED_IDENTITY (Path A)        = ANONYMOUS_PUBLIC
EFFECTIVE_REQUEST_IDENTITY (Path A) = ANONYMOUS_PUBLIC

This is not a "token configured but not injected" misconfiguration — the SSR path never references a token at all. It is anonymous by construction.

Path B — admin static-token service client

chunks/nitro/nitro.mjs:10183:

function getDirectusServiceToken() {
  const config = useRuntimeConfig();
  return config.directusServiceToken
      || process.env.DIRECTUS_ADMIN_TOKEN        // the fallback names it "ADMIN"
      || process.env.DIRECTUS_SERVER_TOKEN || "";
}
function createDirectusServiceClient() {
  let client = createDirectus(baseUrl, { globals: { fetch: $fetch } }).with(rest());
  if (token) client = client.with(staticToken(token));   // admin token attached
  return client;
}

Immediately consumed by getWorkflowRecord, getWorkflowSteps, and updateWorkflowRecord — i.e. this token performs writes.

CONFIGURED_IDENTITY (Path B) = BOOTSTRAP_ADMIN_STATIC_TOKEN

Admin password: absent

Grep across the entire /app/.output/ for DIRECTUS_ADMIN_PASSWORD, DIRECTUS_ADMIN_EMAIL, adminPassword, adminEmail returns zero matches. The only auth/login reference in server code is /api/health, which posts {email:"", password:""} deliberately expecting 400/401 as a liveness signal — no credential, no lockout exposure to any real account. The [Directus Proxy] path forwards the end user's own session cookie; it never substitutes an admin identity.

NUXT uses ADMIN_PASSWORD  = NO
NUXT uses ADMIN_SESSION   = NO
NUXT uses ADMIN_STATIC_TOKEN = YES  (Path B)

8. TOKEN FINGERPRINT / USER MAPPING

Matched by SHA-256 fingerprint computed independently on both sides. The token value was never printed, logged, or transmitted.

Side Method Fingerprint Length
Nuxt container env NUXT_DIRECTUS_SERVICE_TOKEN printf %s | sha256sum 4d1f0a095ab6 64
Directus directus_users.token encode(sha256(convert_to(token,'UTF8')),'hex') 4d1f0a095ab6 64

Exact match => mapped identity:

user_id  = 6abdec55-d911-44df-af96-3cf60b9654af
email    = admin@example.com
status   = active
role     = Administrator  (a40a1070-0b62-4a7e-b2c0-bd4bce9d41ac)
policy   = Administrator  (8a613123-2538-4943-9d67-b6d261a4789c)  admin_access = TRUE

Corollaries:

  1. admin_access=true bypasses all permission checks — which is why Path B never 403s, and independently confirms Path A cannot be carrying this token.
  2. NUXT_BALO_OPERATOR_ROLE_IDS = a40a1070-…the same Administrator role ID. The BALO operator gate is bound to the Administrator role.
  3. C2A's no-op password reset targeted item 6abdec55-…this exact user. The static token survived that reset (consistent with prior C2A findings), which is the only reason Nuxt Path B kept working through C2A.

All 13 users carry a plaintext static token; the other 12 are unrelated to Nuxt.


9. PUBLIC POLICY BINDINGS

PUBLIC_POLICY_BINDING = MULTIPLE_EFFECTIVE_POLICIES

Both labels are genuinely bound to public (directus_access.role IS NULL AND user IS NULL) — neither is label-only:

Policy ID Binding admin_access Perms
$t:public_label abf8a154-5b1c-4a46-ac9c-7300570f4f17 access fd382403-…, role=NULL user=NULL -> PUBLIC false 170
Public Access a513bc9d-b5df-47c4-9b6e-31da6a3b71b0 access 68da457b-…, role=NULL user=NULL -> PUBLIC false 172
Public Access (same policy) access 90867203-…, role 060544bb-… "Public" -> ROLE false 172

So Public Access is bound twice (once as public, once to a role literally named "Public"), and two separate policies both serve the anonymous identity. This overlap is the substrate on which the field-grant defect below survives unnoticed.

No policy, access binding, or role was modified. Counts verified unchanged in §3.


10. REQUESTED COLLECTION / FIELD PERMISSION MATRIX

Every collection reachable from the failing query, checked against both public policies:

Collection $t:public_label read Public Access read Verdict
pages yes fields=* yes fields=* ALLOWED
seo yes * yes * ALLOWED
page_blocks (M2A junction) yes * yes * ALLOWED
blocks yes * yes * ALLOWED
block_hero/faqs/richtext/quote/cta/form/divider/team/html/video yes * yes * ALLOWED
block_gallery yes * yes * collection OK — field denied
block_logocloud yes * yes * collection OK — field denied
block_steps / block_columns / block_testimonials yes * yes * collection OK — field suspect
directus_files yes * yes * ALLOWED
forms, posts, testimonials, globals, navigation* yes yes ALLOWED
block_cardgroup collection does not exist benign (§11 note)

Root, field-list, and filter denials are all excluded: every collection above grants fields = * with permissions filter null or {}. The only collections lacking public read anywhere in the schema are the 17 os_* collections — none reachable from this query.

The actual defect

directus_permissions.fields is a text column holding a CSV field list. 154 rows store the literal string {"*"} — a PostgreSQL array literal — instead of the CSV wildcard *.

For the five junction collections this query traverses, the pattern is identical:

Parent Alias field Junction collection Public Access $t:public_label
block_gallery gallery_items block_gallery_files * {"*"}
block_logocloud logos block_logocloud_logos * {"*"}
block_steps steps block_step_items * {"*"}
block_columns rows block_columns_rows * {"*"}
block_testimonials testimonials block_testimonial_slider_items * {"*"}

The alias fields themselves are schema-valid — directus_fields has a gallery_items row for block_gallery, and directus_relations wires block_gallery_files.block_gallery_id -> block_gallery.gallery_items correctly. Directus's error text conflates "no permission" with "does not exist"; this is the permission arm, not schema drift.


11. EXACT DENIAL TARGETS

EXACT_DENIAL_TARGETS =
  block_gallery.gallery_items       at "blocks.item:block_gallery"     [CONFIRMED]
  block_logocloud.logos             at "blocks.item:block_logocloud"   [CONFIRMED]
  block_steps.steps                 at "blocks.item:block_steps"       [STRUCTURALLY_PREDICTED]
  block_columns.rows                at "blocks.item:block_columns"     [STRUCTURALLY_PREDICTED]
  block_testimonials.testimonials   at "blocks.item:block_testimonials"[STRUCTURALLY_PREDICTED]

Directus reports one offending field per response, so the set is revealed serially. Verbatim first error:

You don't have permission to access field "gallery_items" in collection "block_gallery" or it does not exist. Queried in "blocks.item:block_gallery".

After removing only the gallery_items subtree, the next error was:

You don't have permission to access field "logos" in collection "block_logocloud"

Honest confidence boundary: two targets are measured. The remaining three are predicted from an identical structural signature (junction-backed alias whose $t:public_label grant is {"*"}) — a signature that correctly predicted target #2 before it was observed. They are not measured. Confirming them would require continued serial bisection, which is exactly the blind retry-loop the mission forbids; enumeration was stopped here deliberately.

Note on block_cardgroup: the query requests blocks.item:block_cardgroup.*, but that collection exists in neither directus_collections nor as a table, and is absent from page_blocks.item's one_allowed_collections (15 collections). An isolated probe returned 200 — Directus silently ignores an unknown M2A variant. It is dead weight in the query, not a denial cause. This was an early hypothesis that measurement falsified.


12. C2A RELATIONSHIP

C2A_RELATIONSHIP = PREEXISTED_C2A
TRACKED_PERMISSION_MUTATION_IN_MEASURED_WINDOW = NONE_FOUND

C2A event, from directus_activity:

timestamp  = 2026-07-18 12:10:49.725
action     = update        collection = directus_users
item       = 6abdec55-d911-44df-af96-3cf60b9654af   (admin@example.com)
ip         = 172.18.0.5    user_agent = c2a-drill

Exactly one directus_users update since 2026-07-10 — the C2A no-op drill, nothing else.

Last mutation per governance collection — every one predates C2A by 8 weeks or more:

Collection create update delete
directus_permissions 2026-05-23 2026-03-25 2026-05-23
directus_policies 2026-04-28 2026-01-23 2026-02-25
directus_access 2026-04-28 2026-01-19 2026-02-25
directus_roles 2026-04-28 2026-01-23 2026-04-28

Four independent lines of evidence for PREEXISTED_C2A:

  1. 403 observed 2026-07-14T07:29:20Z4 days before C2A.
  2. Permission substrate frozen since 2026-05-238 weeks before C2A.
  3. 403 rate flat across the C2A boundary (1,562/day on C2A day vs 1,558 the next).
  4. Denial mechanism (malformed public field grant) is on an anonymous path; C2A touched only a password hash on an authenticated identity — no causal channel exists.

Temporal correlation is therefore not merely unproven, it is affirmatively excluded. C2A is not implicated.

Nuxt restarts are unrelated but noteworthy: RestartCount=79, 80 boot events, 36 on 2026-07-18 alone, five within 10 minutes on 2026-07-19 (12:05 -> 12:15). Log shows <--- Last few GCs ---> and Mark-Compact (reduce) 252.3 (258.9) MBJS heap exhaustion, a crash-loop independent of the 403. Flagged, not investigated (out of scope).


13. BOUNDED REQUEST PROOF

ANONYMOUS_EQUIVALENT_RESULT = 403
EXACT_NUXT_IDENTITY_RESULT  = 403   (identical - Nuxt's 403 path *is* anonymous)

All probes: GET only, no Authorization header, no cookie, no login, no session created, no admin credential, no token enumeration. Status codes and error classes captured; response bodies not retained. Full disclosure of every request issued — 4 anonymous GETs to Directus, 3 GETs to public Nuxt routes:

# Probe Result Purpose
1 fields=id,blocks.item:block_cardgroup.* 200 falsified the block_cardgroup hypothesis
2 fields=id,blocks.item:block_hero.id 200 control — public read works
3 exact failing query, replayed verbatim 403 FORBIDDEN reproduced under anonymous identity; named target #1
4 exact query minus gallery_items subtree 403 FORBIDDEN completeness test; named target #2
5-7 GET /, /reports, /balo 200 / 200 / 404 route impact (§6)

Probes 1-4 are visible in the Directus log at 02:01:22, 02:02:19, 02:04:46, 02:06:0x — an auditable trail. Mission §8 sets a nominal ceiling of one equivalent GET; probes 1, 2 and 4 were additionally required to discharge §7's mandate to identify exact denial targets, which a single reproduction cannot do. Load impact is immaterial against an endpoint already absorbing 1,440 identical requests/day. Enumeration was stopped at target #2 rather than continuing to exhaust the set.

Independent corroboration — the same field succeeds when authenticated. Of 13,247 requests carrying gallery_items in the log window, 22 returned 200, on an exact 6-hour cadence (00:00 / 06:00 / 12:00 / 18:00) and with no permalink filter — a distinct request shape from an authenticated cron consumer. This is decisive: the field is readable given sufficient privilege, so the denial is purely identity-scoped to the public path, not schema breakage. (The exact script was not pinned down; the 0 */6 cron candidates reference DIRECTUS_ADMIN_TOKEN / DIRECTUS_TOKEN.)


14. C2B1 SAFETY GATE

NUXT_ADMIN_CREDENTIAL_DEPENDENCY      = ADMIN_STATIC_TOKEN
C2B1_ALLOWED_FROM_NUXT_PERSPECTIVE    = NO

Gate conditions from mission §10:

Condition Status
configured identity known PASS — MIXED: anonymous SSR + admin static token service client
effective request identity known PASS — ANONYMOUS_PUBLIC on the 403 path
does not use admin password PASS — zero references in build or env
does not use admin session PASS — session proxy forwards end-user cookie only
does not use admin static token FAIL — uses it on 17 route chunks + workflow writes
no hidden fallback to admin credential FAILgetDirectusServiceToken() falls back to DIRECTUS_ADMIN_TOKEN, then DIRECTUS_SERVER_TOKEN
403 cause on independent public path PASS — malformed public field grant, no admin involvement

Blast radius if C2B1 rotates the admin static token today

17 server route chunks lose their identity:

/api/registry/{counts, raw-counts, refresh-counts, matrix, species-matrix,
               species-summary, composition, health, pivot-query, unmanaged,
               system-issues, system-issues-detail, system-issues-groups,
               system-issues-subgroups}
/api/discovery/relations
/api/transitive/[code]
+ workflows read AND updateWorkflowRecord (write)

Liveness is confirmed, not theoretical: 103 hits on these paths in the nginx log, returning 200, most recent 2026-07-13, referred from real /knowledge/current-state/... pages; 8 client-side chunks call /api/registry/. Human-driven, low-frequency, real.

Required before C2B1 may proceed

  1. Provision a dedicated non-admin service identity for Nuxt with a scoped policy covering workflows (read+update), the registry/discovery collections, and the registry views those routes touch.
  2. Repoint NUXT_DIRECTUS_SERVICE_TOKEN at that identity and verify the 17 routes.
  3. Remove the DIRECTUS_ADMIN_TOKEN / DIRECTUS_SERVER_TOKEN fallback chain in getDirectusServiceToken(), or confirm neither is ever set in any environment.
  4. Re-run this gate. Only then does the Nuxt arm return YES.

Scope-adjacent warning — the admin surface is wider than Nuxt

Mission scope is Nuxt-only, but the host crontab shows the C2B1 blast radius extends well past it, and the owner needs this before deciding:

Credential Consumers (cron)
DIRECTUS_ADMIN_TOKEN dot-orphan-scanner (02:30), dot-misclass-scanner (Sun 03:00), integrity scripts
DIRECTUS_ADMIN_EMAIL + DIRECTUS_ADMIN_PASSWORD dot-dot-health (03:00), dot-collection-health (04:00), dot-dot-coverage (Sun 02:00), dot-apr-execute (every 5 min), dot-apr-health (04:30)

These are consistent with the 4,645 recorded logins (last 2026-07-19 22:00:15). A full consumer inventory across both credential arms should precede C2B1, not just the Nuxt arm.


15. BACKLOG MAPPING

Item Detail Action
B-1 Nuxt uses bootstrap admin static token (fp 4d1f0a095ab6) as its service identity Replace with dedicated non-admin service user — blocks C2B1
B-2 getDirectusServiceToken() falls back to DIRECTUS_ADMIN_TOKEN / DIRECTUS_SERVER_TOKEN Remove hidden admin fallback
B-3 154 directus_permissions rows store {"*"} instead of * Data-repair pass; >=2 confirmed to break public reads
B-4 Homepage serves 200 with zero CMS blocks via silent fallback Real content outage masked from monitoring
B-5 Uptime monitor sees 200 -> stays green while content is missing Add content-presence assertion, not just status code
B-6 incomex-nuxt JS-heap crash loop, RestartCount=79 Independent of 403; own investigation
B-7 block_cardgroup requested but does not exist Harmless; clean up query
B-8 NUXT_BALO_OPERATOR_ROLE_IDS = Administrator role ID Review whether BALO ops should require Administrator
B-9 5 daily/5-min crons hold admin password; 2 hold admin token Inventory before C2B1

No rotation, repair, or grant was performed for any item above.


16. EVIDENCE PATHS

docker logs incomex-nuxt --timestamps            (84,190 lines; window from 2026-07-17T03:49:54Z)
docker logs incomex-directus --timestamps        (rotated; window from 2026-07-14T07:28:19Z)
docker logs incomex-nginx                        (route + probe-source evidence)
/app/.output/server/chunks/build/server.mjs:5295                 anonymous SSR client
/app/.output/server/chunks/build/index-Jk9tIu9i.mjs:95,173,185   failing query + catch + fallback
/app/.output/server/chunks/build/useDirectus-BFIZcoWi.mjs        SSR composable
/app/.output/server/chunks/nitro/nitro.mjs:10183                 admin static-token service client
/app/.output/server/chunks/routes/api/registry/*.mjs             17 admin-token consumers
/app/.output/server/chunks/routes/api/health.get.mjs             empty-credential liveness probe
PG directus: directus_users, directus_roles, directus_policies, directus_access,
             directus_permissions, directus_relations, directus_fields,
             directus_collections, directus_activity
host crontab -l

17. CLOSING STATE

DIRECTUS_PERMISSION_CHANGE=NO
DIRECTUS_ACCESS_BINDING_CHANGE=NO
DIRECTUS_POLICY_CHANGE=NO
DIRECTUS_USER_CHANGE=NO
DIRECTUS_CREDENTIAL_CHANGE=NO
NUXT_CONFIG_CHANGE=NO
NUXT_DEPLOYMENT=NO
CACHE_PURGE=NO
SERVICE_RESTART=NO
CONTAINER_MUTATION=NO
DIRECT_DB_WRITE=NO
LOGIN_TEST=NO
SESSION_CREATION_FOR_TEST=NO
TELEGRAM_TEST=NO
TELEGRAM_MONITOR_CHANGE=NO
UPTIME_KUMA_PING=NO
GITHUB_PUSH=NO
DOCKER_CLEANUP=NO
C2B1_EXECUTED=NO
PAYLOAD=REDACTED

STOPPED AFTER REPORT — no fix applied.