KB-1CAA

NUXT-IMG-C0 — Off-site encrypted rescue of the sole proven-runnable production Nuxt image (2026-07-21)

20 min read Revision 1
current-statenuxt-img-c0docker-imageoffsite-rescueencrypted-backupopenpgpoci-image-layoutcontent-digest-proofnegative-controlsame-daemon-loadSOLE_PROVEN_RUNNABLE_NUXT_IMAGESAME_DAEMON_IDEMPOTENT_LOAD_PROOFzero-mutationPASS_NUXT_IMG_C0_OFFSITE_ENCRYPTED_RESCUE_AND_LOAD_VERIFIED2026-07-21

title: "NUXT-IMG-C0 — Off-site encrypted rescue of the sole proven-runnable production Nuxt image (2026-07-21)" date: 2026-07-21 date_utc: 2026-07-21T09:12:00Z mission: NUXT-IMG-C0 (REV1 + C0-BIS REV2) host: Contabo vmi3080463 / 38.242.240.89 author: Claude (Opus 4.8, 1M ctx) — Claude Code mode: production READ-ONLY (no container touch) + ephemeral encrypted rescue + idempotent load verify verdict: PASS_NUXT_IMG_C0_OFFSITE_ENCRYPTED_RESCUE_AND_LOAD_VERIFIED payload: REDACTED tags: [current-state, nuxt-img-c0, docker-image, offsite-rescue, encrypted-backup, openpgp, oci-image-layout, content-digest-proof, negative-control, same-daemon-load, SOLE_PROVEN_RUNNABLE_NUXT_IMAGE, zero-mutation, 2026-07-21]

Source: direct measurement over SSH root on vmi3080463, live scripts/keys, and 6 predecessor C2B-PRE / C2B1-R0 reports in the KB. Every secret value = REDACTED. Fingerprints, key IDs, sha256, digests, sizes and counts are not secrets and are recorded verbatim. Application files inside the image were not extracted or printed — only content digests were computed.


1. EXECUTIVE VERDICT

OVERALL = PASS_NUXT_IMG_C0_OFFSITE_ENCRYPTED_RESCUE_AND_LOAD_VERIFIED

RESCUE_RUN_ID                     = 20260721T085609Z-nuxtc0-405619
SOURCE_IMAGE (running)            = sha256:72715a92885c9b2a467cabccdb1e3b8e5221dbfdae68e59f9daef0af08bfab30
CLASS                             = SOLE_PROVEN_RUNNABLE_NUXT_IMAGE (registry pull DENIED; only local copy proven)

BACKUP_WINDOW_GATE                = PASS  (08:33Z→09:12Z; next Drive job 13:00Z; ≥2h margin)
DISK_HEADROOM_GATE                = PASS  (10.66 GiB free + 5.84 GiB tmpfs vs ~57 MB image)
DOCKER_CLEANUP_COLLISION          = NONE  (disk-monitor is REPORT-ONLY since 2026-07-17; no prune/rmi cron)
GPG_RECIPIENT                     = RESOLVED (fpr 31799F7AC051EBA68FB3F13BDCC1369B371837BA)

SAVE|GZIP|GPG pipeline            = PASS (PIPESTATUS 0/0/0; plaintext tar NEVER on disk)
ENCRYPTED_ARTIFACT                = 59,552,591 B  sha256 cdb598694a28f6af5822f1c529aa1dcaaf02bcdb4fcd1c49705c0b2d7089afa1
REMOTE_ARTIFACT_VERIFY            = PASS (remote size + remote sha256 match; independent download sha256 match)
OPENPGP_DECRYPT_INTEGRITY         = PASS (gpg rc=0; PKESK keyid E800896AF06E9B61; not truncated)
ARCHIVE_FORMAT                    = OCI_IMAGE_LAYOUT (oci-layout + index.json + blobs/sha256; 13 blobs)
ARCHIVE_CLEAN_EOF                 = PASS (gunzip rc=0; tar -tf rc=0)
ARCHIVE_CONFIG_DIGEST_MATCH       = FULL (containerd identity model — see §8)
ROOTFS_DIFF_ID_MATCH              = FULL (7/7, ordered)
ARCHIVE_LAYER_CONTENT_VERIFICATION= FULL (7/7 layers gzip→diffid; 13/13 blobs filename==sha256(content))
NEGATIVE_CONTROL_OVERALL_PIPELINE = FAILED_CLOSED (truncated → gpg=2 gunzip=1 docker_load=1)
SAME_DAEMON_IDEMPOTENT_LOAD_PROOF = PASS (Loaded image ID == source; 0 new image/tag; nuxt untouched)

PRODUCTION_CONTAINER_MUTATION     = 0 (11/11 container IDs identical; nuxt+postgres StartedAt/Restarts unchanged)
PLAINTEXT_OR_PRIVATE_KEY_RESIDUE  = 0 (workdir/tmpfs key removed; default keyring 0 secret keys; 0 plaintext tar)

=> NEWEST_PROVEN_OFFSITE_RESTORABLE_NUXT_RUNTIME_IMAGE = 2026-07-21 (RESCUE_RUN_ID above); before = NONE
=> OPENS: NUXT-IMG-H1 (clean-host / no-pull recreate proof). C2B1_ROTATION stays HOLD. No rotation/recreate begun.

One line: the exact Docker image the production incomex-nuxt container runs — which cannot be pulled (Artifact Registry API disabled for the project) — was docker saved by immutable image ID, gzip-compressed and public-key encrypted before leaving the VPS, uploaded to a dedicated Google Drive sub-prefix, then downloaded back, decrypted with the recovery private key, content-digest-verified against the running image, negative-control proven fail-closed, and idempotently docker loaded back to the exact same image ID — with zero production container mutation and zero plaintext/key residue.


2. EXACT SOURCE IMAGE IDENTITY (Gate C1)

Field Value
Container incomex-nuxt (id 42079c55bf9c)
RUNNING_CONTAINER_IMAGE_ID sha256:72715a92885c9b2a467cabccdb1e3b8e5221dbfdae68e59f9daef0af08bfab30
Reconcile vs C2B1-R0 prefix 72715a MATCH
RepoTags [asia-southeast1-docker.pkg.dev/github-chatgpt-ggcloud/web-test/nuxt-ssr:latest] (mutable :latest)
RepoDigests […/nuxt-ssr@sha256:72715a…] — self-referential local digest == image ID, not a registry manifest digest
Platform amd64/linux
Created 2026-02-17T08:24:32.160537673Z
Size 59,802,729 B · RootFS layers = 7
Registry availability (measured) DENIEDdocker manifest inspect"Artifact Registry API has not been used in project 812872501910 … or it is disabled"
Local availability present (docker image inspect OK); running container references this exact ID
Classification SOLE_PROVEN_RUNNABLE_NUXT_IMAGE (only the on-VPS local copy is presently proven; registry pull denied; a rebuild from /opt/incomex/docker/nuxt-repo/Dockerfile is plausible but not proven — that is NUXT-IMG-H1, not claimed here)

3. BASELINE / CONTAINER NO-TOUCH PROOF (Gate C0)

11 running containers (IDs frozen at baseline and re-verified identical at close): incomex-agent-data 09188c72f327 · incomex-nuxt 42079c55bf9c · incomex-agent-api-executor 1d841832a6fe · incomex-nginx e50172b59a25 · incomex-claude-mcp c7039e296421 · pg-restore-test-20260520T031054Z 0cbc668c76f9 · incomex-claude-kb b1b40494fe7d · uptime-kuma 81d9f483b123 · postgres 22540b78de8f · incomex-directus 2245d86d2f6a · incomex-qdrant 2b5c328f0b4d.

Invariant Baseline Close Result
incomex-nuxt image ID sha256:72715a… sha256:72715a… UNCHANGED
incomex-nuxt StartedAt 2026-07-20T16:20:59.980315201Z same UNCHANGED
incomex-nuxt RestartCount 80 80 UNCHANGED (Health=healthy)
postgres StartedAt 2026-04-17T05:35:18.48439927Z same UNCHANGED
Running container count / ID set 11 11 (identical set) UNCHANGED
Docker image-ID set 36 36 UNCHANGED

Docker cleanup-collision check: crontab hourly disk-monitor.sh is REPORT-ONLY — auto-prune was removed 2026-07-17 (its own header: "reclaimed 0B … while deleting every stopped container"); it only writes a df/docker system df snapshot when / >85% and never deletes images/containers. No docker prune/rmi/ system prune/builder prune job exists on any schedule. No backup/save/prune process was running during the mission. ⇒ HOLD_NUXT_IMG_C0_DOCKER_CLEANUP_COLLISION not triggered.


4. DISK / HEADROOM GATE (Gate C2)

/var/lib/docker, the download area, and tmpfs all share /dev/sda1 (ext4). Free / = 11,446,718,464 B (≈10.66 GiB, 89% used), inodes 9% used; tmpfs /dev/shm free = 6,270,754,816 B (≈5.84 GiB). Working set (encrypted artifact ~57 MB + decrypted archive ~57 MB, both in tmpfs) is <2% of headroom. No prune/delete performed to pass this gate. Streaming design ⇒ plaintext image tar never written to persistent disk (§6/§8). HOLD_NUXT_IMG_C0_DISK_HEADROOM not triggered.


5. SENSITIVITY CLASSIFICATION

Artifact treated as HIGH_SENSITIVITY_PROPRIETARY_RUNTIME_ARTIFACT. Only safe metadata was recorded (image ID, config/manifest/layer digests, sizes, platform, timestamps, ciphertext sha256, recipient fingerprint). The image was encrypted before leaving the VPS to the established encrypt-only public recipient; the private recovery key was NOT accessed during the save/upload stage (only during the later verification stage, §9). No application file was extracted or printed — layer verification hashed blob content by streaming.


6. SAVE / ENCRYPT / UPLOAD PIPELINE (fail-closed)

Pipeline (mirrors the proven C2B-PRE mechanism), streamed so plaintext exists only inside the pipe:

docker save sha256:72715a… | gzip -6 | gpg --batch --trust-model always \
     --recipient-file /etc/incomex/backup/directus-backup-recipient.pub --encrypt --output <ciphertext@tmpfs>
PIPESTATUS docker_save=0 gzip=0 gpg=0     (checked last-stage-first, per the SIGPIPE lesson)
Field Value
Ciphertext name incomex-nuxt-image-20260721T085609Z-nuxtc0-405619.docker.tar.gz.gpg
Ciphertext size 59,552,591 B
Ciphertext sha256 cdb598694a28f6af5822f1c529aa1dcaaf02bcdb4fcd1c49705c0b2d7089afa1
PKESK recipient encryption subkey E800896AF06E9B61 (verified on ciphertext)
Recipient primary fpr 31799F7AC051EBA68FB3F13BDCC1369B371837BA (RSA-4096, uid "INCOMEX Directus DB Backup … private key held in GCP Secret Manager")
Compression note ratio ≈1.0 — OCI layer blobs are already gzip; outer gzip retained for pipeline/format symmetry
Dedicated remote prefix gdrive-backup:incomex-encrypted-v1/nuxt-image-rescue/20260721T085609Z-nuxtc0-405619/

The prefix is separate from the flat incomex-encrypted-v1/ directus-DB set and pattern-disjoint from every retention job (see §11). No pre-existing artifact overwritten (target subfolder was verified empty first).


7. DRIVE METADATA & HASHES (remote verify)

Sidecar …meta.json (528–1692 B; payload:REDACTED; contains image ID, digests, sizes, recipient fpr, ciphertext sha256, restore outline — no secret). Remote objects after upload:

Object Size Check
incomex-nuxt-image-20260721T085609Z-nuxtc0-405619.docker.tar.gz.gpg 59,552,591 remote size == local ✓ ; remote sha256 cdb598…afa1 == local ✓
incomex-nuxt-image-20260721T085609Z-nuxtc0-405619.meta.json 1,692 present

Remote object count = 2; .partial/.tmp count = 0; no plaintext sibling. Independent download-back (rclone copyto from Drive) → sha256 cdb598…afa1 == upload ⇒ round-trip byte-identical, not relying on the upload stream alone. REMOTE_ARTIFACT_VERIFY = PASS.


8. ARCHIVE STRUCTURAL & CONTENT-DIGEST VERIFICATION (X2)

Decrypt+decompress of the downloaded ciphertext → tmpfs tar (never on persistent disk): gpg rc=0, gunzip rc=0, tar -tf rc=0 (clean EOF). Decompressed size 59,820,544 B.

Format = OCI_IMAGE_LAYOUT. Identity model = containerd image store: the running image ID 72715a… is the OCI index digest (application/vnd.oci.image.index.v1+json), not the config digest — so the mission's literal "config JSON sha256 == image ID" (which assumes the classic graph driver) is satisfied by anchoring to the index blob and following the content-addressed chain:

RUNNING_IMAGE_ID  sha256:72715a…  (OCI index)         sha256(blob)==RUNNING_IMAGE_ID        → X2.1 FULL
  └─ amd64 image manifest  sha256:c1ac411b1762…       blob integrity FULL
       ├─ config           sha256:36fb4d5b20fa…       sha256(blob)==digest FULL ; == docker-save manifest.json Config
       │     └─ rootfs.diff_ids (7)  == docker image inspect .RootFS.Layers (7, ordered)   → X2.2 ROOTFS_DIFF_ID_MATCH=FULL
       └─ 7 layer blobs                each blob filename==sha256(content); gzip→sha256==config.diff_ids[i]  → X2.3 FULL
  └─ (2nd leaf) buildkit attestation manifest + its config + provenance layer  (non-runtime; integrity verified)
  • A — blob integrity: 13/13 blobs filename == sha256(content).
  • X2.1 config/identity: archive contains a blob whose digest == RUNNING_IMAGE_ID (72715a…), integrity FULL ⇒ ARCHIVE_CONFIG_DIGEST_MATCH = FULL (containerd model).
  • X2.2 RootFS DiffID: archive config rootfs.diff_ids == docker image inspect .RootFS.Layers, 7/7, same order ⇒ ROOTFS_DIFF_ID_MATCH = FULL (docker history NOT used as authority).
  • X2.3 layer/blob: 7/7 layers — each layer blob sha256 == its OCI descriptor digest, and gzip-decompressed sha256 == the corresponding diff_id (mapping = gzip→diffid) ⇒ ARCHIVE_LAYER_CONTENT_VERIFICATION = FULL.
  • Blob accounting: 10 runtime blobs (index + image manifest + config + 7 layers) + 3 non-runtime buildkit attestation/provenance blobs = 13 total; 0 orphan; all integrity-verified. No downgrade to "file exists".

9. SAME-DAEMON IDEMPOTENT LOAD PROOF (X3 — bounded meaning)

Drive artifact → decrypt → decompress → docker load on the live daemon:

docker load  →  "Loaded image ID: sha256:72715a92885c…08bfab30"   (rc=0)
LOADED_IMAGE_ID == SOURCE_IMAGE_ID  ✓
new image IDs after load = NONE ; new tags after load = NONE ; incomex-nuxt StartedAt/Restarts/img unchanged

SAME_DAEMON_IDEMPOTENT_LOAD_PROOF = PASS. Because the image was saved by immutable ID (untagged archive), docker load created no tag and no new object — a true idempotent no-op that the daemon's own content-addressed store validated.

This proves the archive is Docker-parseable, the pipeline completes, and the loaded ID equals the running source ID, with zero production-container effect. It does NOT prove clean-host restore, no-cache restore, Nuxt recreate, registry recovery, or source rebuildability. The composite C0 confidence rests jointly on: OpenPGP integrity + clean gzip/tar EOF + config-digest (identity) match + RootFS DiffID FULL + layer content-digest FULL + negative-control fail-closed + same-daemon load — not on docker load rc=0 alone.

Negative control (X1): truncated ciphertext (first 2 MB dropped) → gpg=2 ("no valid OpenPGP data"), gunzip=1 ("unexpected end of file"), docker_load=1 ("unrecognized image format") ⇒ OVERALL_PIPELINE_RC != 0 = FAILED_CLOSED (authoritative here — even docker load itself rejected it). Image/tag map before==after; no object left by the fixture. FAIL_…_NEGATIVE_CONTROL_FALSE_GREEN not triggered.


10. CLEANUP PROOF

Check Result
tmpfs workdir /dev/shm/nuxt-img-c0-<run> (ciphertext, downloaded copy, GNUPGHOME, meta, logs) removed
mission phase scripts in /dev/shm removed
mission process (nuxt-img-c0/run-id) 0 (independent ps)
any mission file on persistent disk or tmpfs (by run-id/slug) 0
decrypted plaintext docker tar on persistent disk 0 (existed only transiently in tmpfs; removed)
recovery private key only ever in tmpfs GNUPGHOME (now gone); default keyring = 0 secret keys; private-keys-v1.d empty; encrypt-only invariant intact
mission-created Docker object (image/tag/layer) 0 (image-ID set = 36 baseline; source image present; 0 mission tag)
free / vs baseline 11,446,026,240 vs 11,446,718,464 (Δ −676 KB, i.e. baseline ± noise) ; tmpfs reclaimed to 5.84 GiB
Drive artifact (kept) rescue subfolder = 2 objects; sibling directus prefix untouched = 4 objects

Pre-existing, out-of-scope (not mission residue, recorded for honesty): /tmp/lark-gpg-home-* (S177 Lark backup gpg homes on persistent /tmp) and root's empty ~/.gnupg/private-keys-v1.d.


11. REMAINING LIMITATIONS & BACKLOG

Backup-window gate (X0): executed 08:33Z→09:12Z UTC; next Drive jobs = code-backup 13:00Z, directus-DB 18:37Z (Berlin-TZ, empirically confirmed). Completion ≥3h48m before 13:00Z ⇒ BACKUP_WINDOW_GATE = PASS. No backup paused/rescheduled; nothing ran into a backup window.

Negative-control stage exit codes: gpg=2 gunzip=1 docker_load=1 (all non-zero ⇒ fail-closed).

docker-load bounded meaning: see §9 — same-daemon idempotent proof only; clean/no-pull recreate belongs to NUXT-IMG-H1.

Archive format: OCI image layout; identity via OCI index digest (containerd image store) — recorded so H1 does not mis-assume image-ID==config-digest.

X5 — single recovery-key dependency (priority RAISED): the Nuxt rescue and the Directus DB backup share the same single recipient (31799F7AC051EBA68FB3F13BDCC1369B371837BA; private key = GCP Secret Manager DIRECTUS_BACKUP_GPG_PRIVATEKEY_RECOVERY, readable by SA cursor-ci-builder@… via versions.access). ⇒ DATABASE_AND_NUXT_RECOVERY_KEY_DEPENDENCY = SAME_SINGLE_RECIPIENT; SECOND_INDEPENDENT_RECIPIENT_PRIORITY = RAISED. Backlog must add a second, Owner-controlled recipient with an independent handoff channel (not merely another secret under the same SA/IAM trust path) and restore-test it before it counts as real redundancy. No new recipient/key/IAM binding was created in C0.

X4 — running-image rescue backlog (IMAGE_RESCUE_BACKLOG, read-only measured):

Container Image ID12 Registry Dockerfile on VPS Off-site archive Class
incomex-nuxt …/web-test/nuxt-ssr:latest 72715a92885c DENIED /opt/incomex/docker/nuxt-repo/Dockerfile YES (this mission) OFFSITE_ARCHIVE_PROVEN
incomex-agent-data agent-data-vps-prod:9acd60503290-20260717 9acd60503290 none (local self-digest) /opt/incomex/docker/agent-data-repo/Dockerfile LOCAL_ONLY_UNPROVEN
incomex-agent-api-executor agent-api-executor-local:v1 54df5be4d9fc none (local self-digest) /opt/incomex/deploy/agent-api-executor/Dockerfile LOCAL_ONLY_UNPROVEN
incomex-claude-kb claude-kb-local:v1 3cf3065bf66e none (local self-digest) /opt/incomex/claude-kb/Dockerfile LOCAL_ONLY_UNPROVEN
incomex-claude-mcp claude-mcp-local:phase1a 7c0096602a1c none (local self-digest) /opt/incomex/claude-mcp/Dockerfile LOCAL_ONLY_UNPROVEN
incomex-nginx nginx:alpine 1d13701a5f9f public Docker Hub (public) PULLABLE_PUBLIC (live-pull not tested in C0)
postgres / pg-restore-test postgres:16 004f63c1e580 public Docker Hub (public) PULLABLE_PUBLIC (live-pull not tested in C0)
uptime-kuma louislam/uptime-kuma:2 7337368a7787 public Docker Hub (public) PULLABLE_PUBLIC (live-pull not tested in C0)
incomex-directus directus/directus:11.5 23f2f526599b public Docker Hub (public) PULLABLE_PUBLIC (live-pull not tested in C0)
incomex-qdrant qdrant/qdrant:latest 0425e3e03e7f public Docker Hub (mutable :latest) (public) PULLABLE_PUBLIC (live-pull not tested in C0)

The 4 LOCAL_ONLY_UNPROVEN images each have source (Dockerfile) so rebuild is plausible but unproven; presently they are recoverable only from the on-VPS local layer cache. The existing prohibition on docker prune/rmi/system prune/builder prune protects this entire group (not just Nuxt) — it must remain. C0 did not save/upload/build/pull any of them.

Pre-existing / out-of-scope (unchanged by C0): legacy plaintext vps-backup-*.tar.gz (14) + 8 plaintext PG dumps under /opt/incomex/backups/pg/ + /tmp/lark-gpg-home-* persistent gpg homes; :latest mutable tags on nuxt and qdrant; heartbeat-after-upload-without-remote-verify (C2B-PRE backlog).


12. EXACT GATE OPENED

PASS_NUXT_IMG_C0_OFFSITE_ENCRYPTED_RESCUE_AND_LOAD_VERIFIED is the sole condition that opens NUXT-IMG-H1 (clean-host / no-pull recreate proof of the Nuxt image, toward swapping NUXT_DIRECTUS_SERVICE_TOKEN under Option-B Direct Decomposition). NUXT_IMAGE_RECREATE_READY remains NO until H1. C2B1_ROTATION stays HOLD.

Stop after report. No image pinning, no Nuxt recreate, no service-identity design, no credential rotation begun.

— End —