NUXT-IMG-H1 — Immutable dual-surface pin + controlled no-pull production recreate of the Nuxt image (2026-07-21)
title: "NUXT-IMG-H1 — Immutable dual-surface pin + controlled no-pull production recreate of the Nuxt image (2026-07-21)" date: 2026-07-21 date_utc: 2026-07-21T16:10:00Z mission: NUXT-IMG-H1 (REV2) host: Contabo vmi3080463 / 38.242.240.89 author: Claude (Opus 4.8, 1M ctx) — Claude Code mode: production controlled mutation — dual-surface immutable image pin + single --no-deps --pull never --force-recreate of nuxt only verdict: PASS_NUXT_IMG_H1_IMMUTABLE_DUAL_PIN_NO_PULL_RECREATE_VERIFIED payload: REDACTED tags: [current-state, nuxt-img-h1, docker-image, immutable-pin, oci-index-digest, no-pull-recreate, dual-surface-pin, crash-fast-detector, preexisting-heap-oom, zero-credential-change, PASS_NUXT_IMG_H1_IMMUTABLE_DUAL_PIN_NO_PULL_RECREATE_VERIFIED, NUXT_IMAGE_RECREATE_READY, NUXT_IMAGE_FALLBACK_STATUS, POST_CRASH_FAST_DETECTOR, 2026-07-21]
Source: direct measurement over SSH root on
vmi3080463, live docker/compose, and 5 predecessor reports (C0 rescue, C2B1-R0 packet, Wave-A1, Balo one-click, NUXT-403-R1) in the KB. Every secret value =REDACTED. Image content digests, config-hash, sha256 file hashes, HMAC fingerprints (16-hex, keyed with an ephemeral per-run key), sizes, counts, timestamps and HTTP status codes are not secrets and are recorded verbatim.
1. EXECUTIVE VERDICT
OVERALL = PASS_NUXT_IMG_H1_IMMUTABLE_DUAL_PIN_NO_PULL_RECREATE_VERIFIED
C0_CONTENT_CHAIN_MATCH = YES (live == C0-rescued OCI index chain 72715a…; no drift after C0)
ENV_PIN = EXACT_REPO_DIGEST
COMPOSE_FALLBACK_PIN = EXACT_REPO_DIGEST
NUXT_IMAGE_FALLBACK_STATUS = PINNED
NO_PULL_RECREATE = PASS
UNEXPECTED_REGISTRY_CONTACT = 0
OTHER_CONTAINER_MUTATION = 0
NORMALIZED_CONFIG_DRIFT = 0 (functional; only the intended pin + cosmetic mount-order + --no-deps/config-hash labels)
ROUTE_REGRESSION = 0
BALO_GUARD_PRE_FAIL = 0 (PRE 37/37 PASS)
BALO_GUARD_POST_FAIL = 0 (POST 37/37 PASS)
POST_CRASH_FAST_DETECTOR = PASS # 15-min detector: no restart/crash/heap signature
ROLLBACK_ATTEMPTS = 0
=> NUXT_IMAGE_REFERENCE_PINNED = EXACT_REPO_DIGEST
=> NUXT_IMAGE_FALLBACK_STATUS = PINNED
=> NUXT_NO_PULL_RECREATE_PROVEN = YES
=> NUXT_IMAGE_RECREATE_READY = YES
=> LONG_TERM_NUXT_STABILITY_PROVEN = NO
=> NUXT_CREDENTIAL_CHANGED = NO
=> C2B1_ROTATION = HOLD
=> OPENS: C2B1-S1-DESIGN (only). No repoint / rotation / service-identity work begun.
One line: the exact production Nuxt image (the C0-proven sole-runnable 72715a…, which cannot be pulled —
Artifact Registry API disabled) was pinned by its immutable OCI index digest on both surfaces (.env
NUXT_SSR_IMAGE and the Compose :latest fallback), then the nuxt service only was recreated exactly once
with --no-deps --pull never --force-recreate — same image content, zero pull, zero credential change, zero
other-container mutation, PRE/POST routes and Balo guard identical.
2. OWNER AUTHORIZATION
OWNER_GO_NUXT_IMG_H1_REV2 — authorized exactly: (1) pin NUXT_SSR_IMAGE to the exact immutable RepoDigest;
(2) replace the Compose :latest fallback with the same RepoDigest; (3) recreate the nuxt service exactly once
via local image with --pull never; (4) at most one rollback on a defined H1-caused error. No credential,
permission, Directus data or app-source change. Owner also mandated (mid-mission) a full live re-measurement of the
7 gates at execution time and no widening of Gate H0 / no exception re-ask; both were honored.
Execution window (Owner-selected "Option 2 — afternoon"). After the 13:00Z code-backup completed (13:01Z "completed successfully"), re-measured at 15:07:53Z; all gates live-re-confirmed; completion planned ≥2 h before the 18:37Z Directus backup.
3. LIVE RESTART BASELINE & CONFIDENCE (Gate H1, re-measured — no 10:20Z reuse)
| Field | Value |
|---|---|
| Container (PRE) | incomex-nuxt id 42079c55bf9c… |
| Container created | 2026-07-17T03:49:51Z |
| PRE StartedAt | 2026-07-21T11:06:59Z (uptime ~4 h at T0) |
| PRE RestartCount | 81 (was 80 at the 10:16Z read; +1 restart during the wait — the pre-existing heap cycle) |
| ExitCode / OOMKilled | 0 / false · Health healthy · policy unless-stopped |
NUXT_OBSERVED_RESTART_INTERVAL_RANGE_HOURS |
cumulative avg ≈ 1.3 h (81 restarts / ~107 h) but the most recent measured interval ≈ 18.8 h (16:20:59Z→11:06:59Z) — restarts are front-loaded (crash-loop soon after the 07-17 creation), long-stable since |
NUXT_RESTART_PATTERN_CONFIDENCE |
LOW (irregular; not a fixed period) |
KNOWN_PREEXISTING_CRASH_SIGNATURES |
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory (V8 old-space exhaustion ≈256 MB under mem_limit 512m); secondary TypeError: a.code.localeCompare is not a function on GET /api/registry/matrix |
PRE_CRASH_FAST_DETECTOR (§4 mission): observed 15:07:53Z→15:23:16Z (>15 min) — RestartCount stayed 81,
StartedAt unchanged, status running/healthy across every reading ⇒ no restart in the PRE window;
HOLD_NUXT_IMG_H1_PREEXISTING_INSTABILITY_WINDOW not triggered.
4. LIMITED MEANING OF THE 15-MINUTE CRASH DETECTOR
The 15-minute POST detector (§16) proves only: the container did not crash immediately after recreate, routes
kept working, and no immediate config error surfaced. It does NOT prove long-term stability. Because the
pre-existing V8 heap-OOM cycle is real and irregular (§3), a crash after the window does not by itself prove H1
failure or a pre-existing defect — later classification must rest on crash signature + content/config drift + route
behavior + timing vs baseline, not on timing alone. LONG_TERM_NUXT_STABILITY_PROVEN = NO.
5. OCI / CONTAINERD CONTENT CHAIN (Gate H2 — live == C0, no drift)
Identity model = containerd image store: the running image ID is the OCI index digest, not the config digest (the classic "image-ID == config-JSON" assumption does not hold here — carried from C0).
RUNNING_IMAGE_ID sha256:72715a92885c9b2a467cabccdb1e3b8e5221dbfdae68e59f9daef0af08bfab30 (OCI index)
└─ amd64 image manifest sha256:c1ac411b1762…
├─ config sha256:36fb4d5b20fa…
│ └─ rootfs.diff_ids (7) == docker image inspect .RootFS.Layers (7, ordered)
└─ 7 layer blobs
RootFS DiffIDs (7, ordered):
989e799e… e881f558… 3bcb0b08… 1dcaf7a5… 276c9d92… 5833683e… fcb35c07…
Platform amd64/linux · Created 2026-02-17T08:24:32Z · RepoTags […/nuxt-ssr:latest] · RepoDigests […@sha256:72715a…]
Live chain (10:16Z and re-measured 15:07:53Z) is identical to the C0-rescued chain ⇒
LIVE_IMAGE_CONTENT_CHAIN = C0_RESCUED_CONTENT_CHAIN; HOLD_NUXT_IMG_H1_IMAGE_DRIFT_AFTER_C0 not triggered.
6. EXACT REPODIGEST (Gate H3 — local-resolvable)
asia-southeast1-docker.pkg.dev/github-chatgpt-ggcloud/web-test/nuxt-ssr@sha256:72715a92885c9b2a467cabccdb1e3b8e5221dbfdae68e59f9daef0af08bfab30
docker image inspect <ref> resolves fully against the local daemon → id 72715a…. This is the OCI index
digest (not the config 36fb4d5b…); not :latest, not a mutable tag, not an ID prefix, and needs no network.
Unlike agent-data (a local-only self-digest), this image carries a fully-qualified RepoDigest that self-resolves.
HOLD_NUXT_IMG_H1_LOCAL_DIGEST_REFERENCE_UNRESOLVED not triggered.
7. PRE ROUTES & BALO GUARD (baseline)
| Route (local nginx, Host: vps.incomexsaigoncorp.vn) | PRE |
|---|---|
GET / |
200 (degraded homepage — pre-existing 0-CMS-block fallback; still 200) |
GET /reports |
200 |
GET /api/health |
200 |
GET /api/registry/counts |
200 |
GET /api/registry/health |
200 |
GET /api/discovery/relations |
400 (endpoint's own no-param baseline) |
GET /reports/report-balo-table |
200 |
GET /api/balo/reconcile |
404 (POST-only) |
POST /api/balo/reconcile |
403 (fail-closed, no session) |
BALO_ONE_CLICK_GUARD (/opt/incomex/dot/bin/dot-balo-one-click-guard, read-only): PASS checks=37 ⇒
BALO_GUARD_PRE_TOTAL=37, BALO_GUARD_PRE_PASS=37, BALO_GUARD_PRE_FAIL=0. (Count measured, not hardcoded.)
8. CHECKPOINT & ONE-ROLLBACK CEILING
/root/nuxt-img-h1-checkpoint-20260721T150753Z/ (dir 700 root:root; all files 600):
docker-compose.yml.PRE (sha256 557a2d65…), env.PRE (66752c8b…), nuxt-inspect.PRE/POST.json,
nuxt-normalized.PRE/POST.txt, nuxt-env-hmac.PRE/POST.txt, containers-detail.PRE.txt, routes.PRE/POST.txt,
balo-guard.PRE/POST.txt, hmac.key, rollback.sh, compose-image-lines.PRE/POST.txt. Not in KB/Git/Drive.
rollback.sh restores the PRE compose+.env and recreates nuxt via --no-deps --pull never --force-recreate,
registry/KB/GitHub/network-independent. MAXIMUM_ROLLBACK_ATTEMPTS = 1. ROLLBACK_ATTEMPTS = 0 (none
needed). Checkpoint retained until C2B1-S3 / C2B1-R1 complete.
9. .env PIN (Surface A)
/opt/incomex/docker/.env had no NUXT_SSR_IMAGE (added). New line 59:
NUXT_SSR_IMAGE=asia-southeast1-docker.pkg.dev/github-chatgpt-ggcloud/web-test/nuxt-ssr@sha256:72715a92885c…fab30
File mode preserved root:root 600; sha256 66752c8b… → c8d384ce… (size 2231→2482). ENV_PIN = EXACT_REPO_DIGEST.
10. COMPOSE FALLBACK PIN (Surface B) + effective resolution + cross-service no-drift
/opt/incomex/docker/docker-compose.yml:162:
- image: ${NUXT_SSR_IMAGE:-asia-…/nuxt-ssr:latest}
+ image: ${NUXT_SSR_IMAGE:-asia-…/nuxt-ssr@sha256:72715a92885c…fab30}
Mode preserved root:root 644; sha256 557a2d65… → 43dec381… (size 9512→9577). No nuxt-ssr:latest remains.
Effective resolution both ways → the exact digest:
- Path 1 (normal,
.envsets the var):docker compose config→…nuxt-ssr@sha256:72715a… - Path 2 (fallback only,
--env-file /dev/null):docker compose config→…nuxt-ssr@sha256:72715a…
COMPOSE_FALLBACK_PIN = EXACT_REPO_DIGEST; EFFECTIVE_NUXT_IMAGE = EXACT_REPO_DIGEST; NUXT_IMAGE_FALLBACK_STATUS = PINNED.
Cross-service no-drift — the image: lines of every other service are byte-identical PRE→POST (only line 162
changed): postgres:16 · qdrant/qdrant:latest · directus/directus:11.5 ·
agent-data-vps-prod:9acd60503290-20260717 · nginx:alpine. FAIL_NUXT_IMG_H1_CROSS_SERVICE_CONFIG_DRIFT not
triggered.
11. EXPECTED 503 ALERT WINDOW (printed immediately before recreate)
EXPECTED_ALERT_WINDOW_START_UTC = 2026-07-21T15:51:53Z
EXPECTED_ALERT_WINDOW_END_UTC = 2026-07-21T15:53:53Z
EXPECTED_ALERT = [OPS Proxy] Down 503 followed by Up
CAUSE = planned Nuxt-only recreate (immutable digest pin, --pull never)
Uptime Kuma / notifications were not disabled. The new container was healthy by 15:53:37Z (inside the window).
12. NO-PULL RECREATE RESULT (Gate §12–§13)
Pre-proof: digest exists local (id 72715a…); --pull never/--no-deps/--force-recreate supported (Docker
29.2.1, Compose v5.0.2); dry-run plan = only incomex-nuxt Recreate (no pull, no build, no other service);
nuxt has no build: section; locks free; checkpoint valid.
Exact command (nothing added): docker compose up -d --no-deps --pull never --force-recreate nuxt
Container incomex-nuxt Recreate → Recreated → Starting → Started
PRE_CID 42079c55bf9c… → POST_CID 68fd38378828… (CID changed exactly once)
POST_IMAGE_ID = sha256:72715a92885c…fab30 == C0 OCI index digest
POST_CONFIG_IMAGE = …/nuxt-ssr@sha256:72715a…fab30 (container now references the immutable digest)
POST_RESTARTS = 0 (fresh container) · STATUS running · HEALTH healthy
Orphan incomex-agent-api-executor preserved (no --remove-orphans). UNEXPECTED_REGISTRY_CONTACT = 0;
no pull, no build. Docker healthy used only as a supplementary signal, never as primary evidence.
13. PRE/POST NORMALIZED DIFF (Gate §14)
docker inspect normalized fields, PRE vs POST — the only differences are the intended pin and benign
bookkeeping:
| Field | PRE | POST | Verdict |
|---|---|---|---|
Config.Image (IMAGE_REF) |
…nuxt-ssr:latest |
…nuxt-ssr@sha256:72715a… |
INTENDED (the pin) |
.Image (IMAGE_ID) |
72715a… |
72715a… |
identical |
| Mounts (set) | nuxt-output:rw + /run/incomex:ro | same set (array reordered) | identical set (cosmetic order) |
Label com.docker.compose.config-hash |
1e390b96… |
d1e184eb… |
intended (downstream of image-ref change) |
Label com.docker.compose.depends_on |
directus:service_healthy:false |
"" |
benign artifact of the required --no-deps |
Label com.docker.compose.image |
72715a… |
72715a… |
identical |
| entrypoint / cmd / user / workdir / restart / mem_limit(512m) / healthcheck / networks(+aliases) / ports / group_add(987) / logconfig | — | — | all identical |
| Env var names + secret HMAC fingerprints (21 vars, same ephemeral key) | — | — | 0 differences ⇒ every secret byte-identical (incl. NUXT_DIRECTUS_SERVICE_TOKEN) |
NORMALIZED_CONFIG_DRIFT = 0 (functional). NUXT_CREDENTIAL_CHANGED = NO.
14. POST ROUTES & BALO GUARD (Gate §14–§15)
POST routes are identical to PRE (0 diff) for all 9 probes (/=200, /reports=200, /api/health=200,
registry/counts=200, registry/health=200, discovery/relations=400, report-balo-table=200, balo GET=404,
balo POST=403) ⇒ POST_ROUTE_RESULT >= PRE_ROUTE_RESULT; ROUTE_REGRESSION = 0.
BALO_ONE_CLICK_GUARD POST: PASS checks=37 ⇒ BALO_GUARD_POST_TOTAL=37 = PRE, BALO_GUARD_POST_PASS=37,
BALO_GUARD_POST_FAIL=0. FAIL_NUXT_IMG_H1_BALO_REGRESSION not triggered.
The pre-existing homepage internal-SSR 403 (anonymous GET directus:8055/items/pages) is unchanged and masked by
the homepage-simple fallback (public / = 200); it is not caused by H1 (predates it; NUXT-403-R1).
15. CRASH-FAST OBSERVATION (Gate §16)
POST_CRASH_FAST_DETECTOR — observed from recreate 15:51:55Z for ≥15 min (→ ~16:08Z):
cid = 68fd38378828… (UNCHANGED since recreate) · restarts = 0 · status = running · health = healthy
started = 2026-07-21T15:51:55Z · heap_hits_since_recreate = 0 · window 15:51:55Z → 16:08:10Z (16m15s ≥ 15 min)
Verdict: POST_CRASH_FAST_DETECTOR = PASS — the container did not crash or restart, emitted no heap/OOM
signature, and showed no immediate config error in the window. Meaning is bounded per §4.
16. OTHER-CONTAINER NO-MUTATION PROOF (Gate §13/§17)
All 10 non-nuxt containers — ids, StartedAt, RestartCount, status — identical PRE (15:11Z) vs immediately
POST-recreate:
agent-api-executor 1d841832 (r0) · agent-data 09188c72 (r0) · claude-kb b1b40494 (r2) ·
claude-mcp c7039e29 (r2) · directus 2245d86d (r0) · nginx e50172b5 (r0) · qdrant 2b5c328f (r0) ·
pg-restore-test 0cbc668c (r0) · postgres 22540b78 (r0) · uptime-kuma 81d9f483 (r0).
OTHER_CONTAINER_MUTATION = 0. Container set = 11/11.
17. LONG-TERM STABILITY LIMITATION
LONG_TERM_NUXT_STABILITY_PROVEN = NO. The Nuxt container carries a pre-existing V8 JS-heap-OOM crash cycle
(§3) driven by mem_limit 512m and heavy endpoints (e.g. the localeCompare error path on /api/registry/matrix),
independent of the image pin. H1 changed the image reference only (same content, same 512m, same code mount);
it neither introduced nor remediated that cycle. A future heap crash is expected and, per §4/§18, must be classified
by signature+drift, not timing. Heap remediation is out of scope (no heap-remediation mission opened here).
Post-window crash interpretation: a crash after 16:08Z does not by itself prove H1 failure or a pre-existing defect; classify by crash signature vs the §3 heap signature, content/config drift, route behavior and timing.
18. GATE OPENED NEXT
PASS_NUXT_IMG_H1_IMMUTABLE_DUAL_PIN_NO_PULL_RECREATE_VERIFIED sets NUXT_IMAGE_RECREATE_READY = YES and opens
C2B1-S1-DESIGN only. It does not open a repoint, credential migration, service-identity provisioning, or
rotation. C2B1_ROTATION stays HOLD. NUXT_CREDENTIAL_CHANGED = NO.
Carried backlog (unchanged by H1): 4 LOCAL_ONLY_UNPROVEN images (agent-data / agent-api-executor / claude-kb
/ claude-mcp) — the prune/rmi prohibition still protects the whole group; X5 single-recipient recovery-key
dependency (DB+Nuxt share 31799F…1837BA) — second independent recipient still PRIORITY RAISED; :latest
mutable tag on qdrant; homepage 0-CMS-block degradation (NUXT-403-R1). Rollback checkpoint retained.
— End —