KB-AAE1

C2B-PRE-V3 REV2 — Xác minh giải mã + restore cô lập bản backup mã hoá tự nhiên đầu tiên (2026-07-21)

19 min read Revision 1
current-statec2b-pre-v3directusbackup-encryptiondrivedecryptisolated-restorenegative-controldisk-headroom-gatedr-statusfdw-user-mappingzero-mutation2026-07-21PASS_V3_FIRST_NATURAL_DRIVE_ARTIFACT_DECRYPT_AND_RESTORE_VERIFIED

title: "C2B-PRE-V3 REV2 — Xác minh giải mã + restore cô lập bản backup mã hoá tự nhiên đầu tiên (2026-07-21)" date: 2026-07-21 date_utc: 2026-07-21T05:20:00Z mission: C2B-PRE-V3 REV2 host: Contabo vmi3080463 / 38.242.240.89 author: Claude (Opus 4.8, 1M ctx) — Claude Code mode: read-only-on-production + ephemeral isolated restore verdict: PASS_V3_FIRST_NATURAL_DRIVE_ARTIFACT_DECRYPT_AND_RESTORE_VERIFIED payload: REDACTED tags: [current-state, c2b-pre-v3, directus, backup-encryption, drive, decrypt, isolated-restore, negative-control, disk-headroom-gate, dr-status, fdw-user-mapping, zero-mutation, 2026-07-21]

Tác giả: Claude (Opus 4.8). Nguồn: đo trực tiếp qua SSH root + đọc toàn văn script live, restore runbook, và 4 báo cáo C2B-PRE tiền nhiệm trong KB. Mọi giá trị secret = REDACTED. Không có token/password/private-key/record nhạy cảm nào trong báo cáo này. Fingerprint / keyid / sha256 / kích thước / số đếm không phải secret và được ghi nguyên. directus_users.token không được đọc; chỉ đếm số dòng.


1. EXECUTIVE VERDICT

OVERALL = PASS_V3_FIRST_NATURAL_DRIVE_ARTIFACT_DECRYPT_AND_RESTORE_VERIFIED

FIRST_NATURAL_RUN_AFTER_F1        = run_id 20260720T183701Z-3823707  (started 2026-07-20T18:37:01Z)
CONTROL_FLOW                      = PASS (đủ 5 stage → status=PASS stage=COMPLETE)
ARTIFACT_ON_DRIVE                 = PRESENT + BYTE-IDENTICAL (sha256/md5/size khớp 4 nguồn)
DOWNLOAD_FROM_DRIVE               = PASS (sha256 khớp, không truncate)
DECRYPT (khoá riêng từ Secret Manager) = PASS (gpg rc=0)
DUMP_INTEGRITY                    = PASS (385 CREATE TABLE, footer "dump complete", 3,931,740 dòng)
ISOLATED_RESTORE                  = PASS (psql rc=0, 0 error / 0 warning, ON_ERROR_STOP=1)
STRUCTURAL_MATCH_vs_PRODUCTION    = PASS (385 base tables; users/collections/fields/permissions khớp tuyệt đối)
NEGATIVE_CONTROL                  = PASS (ciphertext cắt cụt → fail-closed rc gpg=2/gunzip=1/psql=3)
EPHEMERAL_CLEANUP                 = PASS (0 residual container/volume/network/key/plaintext)
PRODUCTION_NO_MUTATION            = PASS (postgres id+StartedAt+restarts bất biến; chỉ read-only)

=> NEWEST_PROVEN_RESTORABLE_OFFSITE_PRODUCTION_BACKUP_UTC = 2026-07-20T18:37:01Z   (trước nhiệm vụ này = NONE)

C2B1_ALLOWED = NO  (bị chặn bởi phụ thuộc admin-token riêng — xem §14, KHÔNG do backup)
NEXT: KHÔNG tự động bắt đầu rotation / permission repair / historical cleanup.

Một câu: Bản backup mã hoá tự nhiên đầu tiên sau F1 (đêm 2026-07-20) đã được tải từ Google Drive, giải mã bằng khoá riêng lấy từ Secret Manager, và restore sạch (0 lỗi) vào một PostgreSQL cô lập; cấu trúc/số đếm khớp production; môi trường tạm đã xoá sạch; production không bị mutation. Đây là bản production off-site đầu tiên được CHỨNG MINH restore được. "Kuma/Telegram xanh" không phải là bằng chứng — bằng chứng là restore này.

Cửa sổ thực thi: 2026-07-21T04:42Z → 05:19Z (UTC).


2. CỔNG DUNG LƯỢNG ĐĨA (V0) — PASS

Docker root /var/lib/docker, thư mục tải, và volume PG cùng nằm trên /dev/sda1 (ext4) ⇒ ràng buộc là free của /.

Đại lượng Giá trị (bytes) Ghi chú
FREE_BYTES / (trước) 11,469,422,592 (≈10.68 GiB) 89% used, 96G tổng
Inode free / 11,912,591 (9% used) thừa
encrypted_download 120,900,398 (≈0.11 GiB) ciphertext DB
unavoidable_plaintext_temp 0 streaming gpg|gunzip|psql, SQL không chạm đĩa
restored_database (ước) 1,789,860,887 (prod directus = 1707 MB) thực đo sau: 1585 MB
WAL/temp/overlay allowance ~2.0 GiB container throwaway đã tune fsync=off, full_page_writes=off, synchronous_commit=off, max_wal_size=1GB
MEASURED_PEAK_ESTIMATE ≈ 3.8 GiB download + 0 + restoredDB + WAL/overlay
Điều kiện FREE ≥ 2 × PEAK → 10.68 ≥ 7.6 GiB PASS (biên ≥ 3 GiB; PASS cả khi PEAK 4.2 GiB ⇒ 8.4 < 10.68)

/dev/shm tmpfs 5.84 GiB — chỉ dùng làm GPG temp home (khoá riêng nằm trong RAM, không chạm đĩa bền). Không xoá/prune/vacuum/dừng service để vượt cổng. Đo thực tế lúc peak: free tụt 10.57 → 8.01 GiB (dùng ~2.5 GiB), nằm gọn trong ngân sách; sau cleanup phục hồi 10.68 GiB.


3. CỔNG THỜI GIAN & BACKUP LOCK (V1) — PASS

Đọc live crontab: dòng 54 37 20 * * * /opt/incomex/scripts/backup-to-gdrive.sh. CRON_TZ đầu tiên nằm ở dòng 56 (SAU dòng 54) ⇒ dòng 54 chạy theo TZ hệ thống Europe/Berlin (CEST, +0200)18:37:00Z mỗi đêm. (Comment dòng 53 "20:00 UTC / 3AM Hanoi" là stale/SAI — lịch thực là 18:37Z.)

UTC
Run kế tiếp 2026-07-21T18:37:00Z
Lúc thực thi 2026-07-21T04:42Z → 05:19Z
Cách run kế tiếp ≈ 13h18m
Hoàn tất trước run kế tiếp ≫ 2h ✅
Backup lock FREE (.directus-db-backup.lock 0 byte, fuser rỗng, pgrep rỗng) trước & sau

Không tạm dừng/đổi lịch backup. Không trùng cửa sổ backup.


4. BASELINE TÀI NGUYÊN TIỀN TỒN (V2)

Containers (11, running): postgres (id 22540b78de8f…, postgres:16, StartedAt 2026-04-17T05:35:18.48439927Z, restarts 0) · incomex-directus · incomex-nuxt · incomex-qdrant · incomex-agent-data · incomex-agent-api-executor · incomex-nginx · incomex-claude-mcp · incomex-claude-kb · uptime-kuma · pg-restore-test-20260520T031054Z (postgres:16, up 2 tháng).

Networks: docker_incomex (production — container V3 KHÔNG được join), claude_mcp_net, bridge, host, none.

Databases trong cluster production (chỉ đếm tên/kích thước, read-only): directus 1707 MB · directus_gov_test_20260602 1202 MB · incomex_metadata 393 MB · workflow 7591 kB · postgres 7519 kB. Container pg-restore-test-… chứa DB restore_test.

Các hiện vật này PREEXISTED, không do C2B-PRE tạo, không mở, không xoá — xem §13.


5. TƯƠNG QUAN NATURAL-RUN / ARTIFACT (V3) — DUY NHẤT

Chuỗi thời gian tái dựng từ primary evidence (git + log + status + mtime), không dùng tóm tắt chat:

Mốc UTC Sự kiện Bằng chứng
≤ 2026-07-18 18:37 Backup plaintext cũ (vps-backup-*.tar.gz) log block "BACKUP DONE", "Archive: 259M"
2026-07-19 18:37:01 Encrypted run ĐẦU → chết ~1s tại START (bug empty-staging pre-F1) log chỉ có 1 dòng START rồi tắt
2026-07-20 04:09:59 F1 fix LIVE (mtime script) sha256 live 19e43432…, byte-identical commit
2026-07-20 04:11:45 commit 2ce85a63 "handle empty first-run staging…" (KHÔNG push; repo 0 remote) git show -s
2026-07-20 18:37:01 Run tự nhiên ĐẦU sau F1 → PASS ← đối tượng xác minh
2026-07-21 18:37 Run kế tiếp (chưa xảy ra) không có START mới trong log

Định danh duy nhất (status file + log cùng run):

run_id            = 20260720T183701Z-3823707
started_utc       = 20260720T183701Z
last_success_utc  = 20260720T183949Z
status/stage      = PASS / COMPLETE      failed_utc = (rỗng)
artifact_db       = incomex-prod-directus-db-20260720T183701Z.sql.gz.gpg
artifact_db_sha256= 445f430cdb48b996ad53b338372d20fc4b3b1f1039ee299d96665c19a8590191
recipient_fpr     = 31799F7AC051EBA68FB3F13BDCC1369B371837BA

Log xác nhận đủ control flow: PRECHECK LOCAL-PRUNE staging_input=0 deleted=0 (bug empty-staging đã hết), [1/5]…[5/5], 4 file Copied (new), Encrypted artifacts in prefix: 2, DIRECTUS_DB_BACKUP PASS, và HEARTBEAT sent to Kuma (pg-backup-gdrive) lúc 18:40:44Z.

Heartbeat ↔ run: dòng log heartbeat chỉ in khi curl -fsS thành công (HTTP 2xx từ Kuma), nên nó chứng minh Kuma đã nhận ping status=up của chính run này. (Kênh Kuma→Telegram: xem §14 — monitoring là Uptime Kuma, forwarding Telegram cho backup-failure chưa từng được chứng minh end-to-end; và HEARTBEAT_SEMANTICS = AFTER_UPLOAD_WITHOUT_REMOTE_VERIFY.)


6. METADATA & HASH TRÊN DRIVE (V4) — PASS

Prefix gdrive-backup:incomex-encrypted-v1/đúng 4 object (2 .gpg + 2 .meta.json), không có sibling plaintext / .partial / staging:

Đối tượng Size sha256 ID Drive
…directus-db-…gpg 120,900,398 445f430cdb48…0191 1uY_QF4BY525…
…directus-db-…gpg.meta.json 528 4a483e6b… 1Y-Udve…
…vps-config-…tar.gz.gpg 167,501,839 2ae8f2b9… 1N42Yiiz…
…vps-config-…gpg.meta.json 536 804034f9… 11_HyaSH…

Bốn nguồn khớp tuyệt đối cho artifact DB: Drive lsjson = nội dung .meta.json = status file = bản staging local → size 120900398, sha256 445f430cdb48…0191, md5 3cfdca8b…. ModTime Drive 2026-07-20T18:37:40Z.

Tải xuống thật (rclone copyto từ Drive vào /root/<mission-id>/): size 120900398, sha256 445f430cdb48…0191 (khớp), header PGP :pubkey enc packet … keyid E800896AF06E9B61 (đúng subkey mã hoá, không truncate). Không sửa/ghi đè artifact remote.


7. GIẢI MÃ (V5) — PASS

  • Khoá riêng: Secret Manager DIRECTUS_BACKUP_GPG_PRIVATEKEY_RECOVERY version 1, project github-chatgpt-ggcloud; identity live = SA cursor-ci-builder@… (versions.access). Probe an toàn (chỉ đếm byte): gcloud_rc=0 secret_bytes=7532 (khớp runbook).
  • Import vào GPG home tmpfs /dev/shm/<mission-id>/gnupg (0700) — khoá riêng không chạm đĩa bền. Fingerprint chính = 31799F7AC051EBA68FB3F13BDCC1369B371837BA ✓; subkey giải mã …E800896AF06E9B61 PRESENT ✓.
  • Định dạng: single-layer pg_dump --no-owner --no-acl | gzip | gpg ⇒ giải mã = một gpg -d, plain SQL.
  • Dump integrity (1 lần streaming decrypt, không tạo file plaintext, đã loại dòng COPY để không in dữ liệu): dòng đầu --, CREATE_TABLE_COUNT=385 (khớp runbook & production), DUMP_COMPLETE=YES (footer "PostgreSQL database dump complete" ⇒ không cắt cụt), TOTAL_LINES=3,931,740; schema cutter_governance / iu_core / sandbox_tac; extension btree_gist / pgcrypto / postgres_fdw (đều có sẵn trong postgres:16).

8. RESTORE CÔ LẬP (V6) — PASS

Tài nguyên cô lập (mission-id c2b-pre-v3-20260721-00648e):

  • Container postgres:16 (tái dùng image sẵn có, không pull), 0 host-published port (ports=map[5432/tcp:[]]), chỉ ở network …-net (--internal), KHÔNGdocker_incomex, volume riêng …-pgdata, không mount production PGDATA.
  • Restore bằng docker exec -i psql (unix socket, không cần network). CREATE SERVER/USER MAPPING chỉ ghi catalog, không kết nối ra ngoài; network --internal cũng chặn mọi tuyến tới production.

Phát hiện (finding thật, không phải lỗi backup): restore đầu dừng tại ERROR: role "workflow_admin" does not exist — dòng 21235 dump chứa CREATE USER MAPPING FOR workflow_admin SERVER incomex_meta_srv; (postgres_fdw). --no-owner --no-acl không loại user mapping ⇒ restore vào cluster trắng bắt buộc tạo trước các role workflow_admin, context_pack_readonly, directus. Runbook hiện không ghi bước này (xem §14, GAP-1).

Restore sạch sau khi tạo role (streaming gpg -d | gunzip | psql, ON_ERROR_STOP=1, plaintext SQL không chạm đĩa):

restore_seconds = 88
PIPESTATUS        gpg=0  gunzip=0  psql=0     (kiểm psql-cuối trước theo bài học SIGPIPE)
PSQL_ERROR_LINES = 0     PSQL_WARNING_LINES = 0
restored size     = 1585 MB

9. XÁC MINH CẤU TRÚC & SỐ ĐẾM (V6) — PASS

Đại lượng Restored Production (read-only) Dump
total base tables 385 385 385 (CREATE TABLE)
public base tables 351 351
directus_users 13 13
directus_collections 167 167
directus_fields 1497 1497
directus_permissions 1207 1207
  • Base tables theo schema: public 351 · cutter_governance 24 · iu_core 2 · sandbox_tac 8 = 385.
  • 12/12 bảng hệ thống directus có mặt (directus_users, collections, fields, permissions, sessions, flows, operations, files, roles, relations, revisions, activity).
  • Index: 974. Constraint: PK 375 · FK 161 · UNIQUE 90 · CHECK 366 · exclusion 1.
  • Ước tổng dòng (post-ANALYZE): ≈ 3,857,568.
  • Dữ liệu nghiệp vụ thật hiện diện (top theo size): directus_revisions 693 MB/433,863 · birth_registry 275 MB/1,216,820 · entity_labels 132 MB/904,485 · system_issues 131 MB/226,325 · event_outbox 101 MB/217,752 · directus_activity 85 MB/440,315 · knowledge_documents 44 MB/9,588 · event_read 33 MB/217,391.
  • FDW foreign server incomex_meta_srv restore đúng (catalog-only, không kết nối).

Bảng định-nghĩa-schema khớp production tuyệt đối ⇒ backup nắm trọn schema/config Directus và restore đồng nhất. Chỉ đếm/khung; không đọc/in bất kỳ token/record nào.


10. NEGATIVE CONTROL (V7) — PASS (fail-closed)

Fixture: cắt 2 MB đầu của ciphertext (không phải bản copy đầy đủ; không đụng Drive). Streaming vào DB c2b_pre_v3_negctl:

NEG PIPESTATUS  gpg=2  gunzip=1  psql=3   → FAILED_CLOSED_AS_EXPECTED
gpg: block_filter: pending bytes!             (OpenPGP stream cụt)
gzip: stdin: unexpected end of file           (gzip cụt)
psql ERROR: missing data for column … COPY birth_registry line 102549  (ON_ERROR_STOP bắt được)

Không PASS, không upload (không gọi rclone), DB negctl + fixture đã xoá (0 residual).

Bài học phương pháp (quan trọng): dump plain sinh toàn bộ 351 CREATE TABLE trước phần dữ liệu ⇒ bản cắt cụt vẫn tạo được khung 351 bảng nhưng dữ liệu dở dang. Do đó kiểm "bảng có tồn tại" KHÔNG đủ — cổng thẩm quyền là exit code ON_ERROR_STOP=1: restore dương tính qua cổng (rc=0, 0 lỗi), negative control trượt cổng (psql rc=3). Đây chính là lý do "Kuma xanh" không thay được restore-verify.


11. DỌN DẸP TÀI NGUYÊN V3 (V8) — PASS

Kiểm Kết quả
container/volume/network mang mission-id 0 / 0 / 0
workdir /root/<mid> removed (NO)
tmpfs GPG home /dev/shm/<mid> (khoá riêng) removed (NO)
tiến trình mang mission-id 0
ciphertext download + temp (scan.awk, *.stderr, psql.stdout) removed
VPS default keyring secret keys 0 (encrypt-only invariant nguyên vẹn)
Drive prefix incomex-encrypted-v1/ vẫn 4 object (chỉ tải, không ghi/xoá)
free / sau cleanup 11,471,077,376 (≈10.68 GiB) — phục hồi

Workdir trước khi xoá không chứa file khoá riêng (chỉ ciphertext + meta + log tạm) — khoá chỉ sống trong tmpfs, nay đã mất. Không quét/in token.


12. CHỨNG MINH PRODUCTION KHÔNG MUTATION (V8)

  • postgres: id=22540b78de8f… · StartedAt=2026-04-17T05:35:18.48439927Z · restarts=0 · running=truebất biến so với baseline (không restart, không tạo lại).
  • Các container khác (incomex-directus, incomex-nuxt, pg-restore-test-…, …) vẫn present/healthy, không đụng.
  • Chỉ read-only SELECT trên production (pg_database_size, list DB, count(*) bảng ổn định). Không ghi production; không đọc directus_users.token.
  • Backup pipeline nguyên vẹn: không sửa script/cron/systemd/compose/staging; status vẫn PASS/COMPLETE của run 2026-07-20; lock FREE.
  • Drive: chỉ download; prefix mã hoá & 14 bản plaintext legacy ở Drive root không đụng.

13. BẢN SAO LỊCH SỬ NGHI CHỨA CREDENTIAL (SUSPECTED_CREDENTIAL_BEARING_HISTORICAL_COPY)

Ghi nhận, không mở, không xoá, không đọc token:

  1. Container pg-restore-test-20260520T031054Z (up 2 tháng) — chứa DB restore_test. Pre-existing, không do mission tạo.
  2. DB directus_gov_test_20260602 (1202 MB) trong cluster production — pre-existing, không có off-site chứng minh.
  3. 14 bản vps-backup-*.tar.gz plaintext ở Drive root (per runbook §6 + báo cáo tiền nhiệm) — vẫn chứa toàn bộ static token đọc được; retention DORMANT (đóng băng vĩnh viễn).
  4. 8 dump plaintext /opt/incomex/backups/pg/directus_*.sql.gz (~0.9 GB, cron 02:27 độc lập) — per báo cáo tiền nhiệm.
  5. DB incomex_metadata / workflow / postgresngoài phạm vi off-site (chỉ directus được backup mã hoá).

Không khẳng định số token cụ thể (không đọc). Dọn dẹp là nhiệm vụ riêng, cần Owner quyết định.


14. BLOCKERS & NHIỆM VỤ MỞ TIẾP

Nâng cấp DR đạt được: NEWEST_PROVEN_RESTORABLE_OFFSITE_PRODUCTION_BACKUP_UTC = 2026-07-20T18:37:01Z (trước nhiệm vụ = NONE). Đây là điều kiện ENCRYPTED_BACKUP_RECOVERABILITY = PASS mà DOC-3/DOC-4 chờ.

GAP-1 (mới, restore runbook): runbook /opt/incomex/docs/recovery/directus-backup-restore-runbook.md thiếu bước tạo role workflow_admin, context_pack_readonly, directus trước khi nạp dump (do postgres_fdw USER MAPPING). Nếu nạp bằng psql không ON_ERROR_STOP, các lệnh này báo lỗi nhưng psql vẫn tiếp tục ⇒ restore dở dang mà exit 0 (rủi ro false-green). Đề xuất bổ sung §3.5b + khuyến nghị ON_ERROR_STOP=1.

Vẫn OPEN (đều PREEXISTED, không phải phát sinh từ nhiệm vụ này):

  • 🔴 C2B1 = NO: NUXT_DIRECTUS_SERVICE_TOKEN chính là static token của admin@example.com (báo cáo NUXT-403-R1) ⇒ xoay nó giết 17 route ⇒ chặn rotation. Không liên quan tính restore-được của backup.
  • 🔴 SA cursor-ci-builder@ đọc được khoá riêng (IAM versions.access) ⇒ thuộc tính "chiếm VPS không giải mã được" không giữ ⇒ backlog: siết IAM least-privilege trên cả hai *_GPG_PRIVATEKEY_RECOVERY.
  • 🟠 HEARTBEAT_SEMANTICS = AFTER_UPLOAD_WITHOUT_REMOTE_VERIFY (tin rc rclone, không đọc-lại remote trước khi báo up) → backlog cổng remote-verify (rclone lsjson size/hash) trước heartbeat.
  • 🟠 Monitoring là Uptime Kuma only; giao Telegram cho backup-failure chưa chứng minh end-to-end; khoảng cách interval Kuma có thể trễ báo động.
  • 🟠 DIRECTUS_OFFSITE_BACKUP_SCOPE = DATABASE_DIRECTUS_ONLY — 4 DB khác + Qdrant chưa có off-site chứng minh.
  • 🟠 Retention prefix mã hoá append-only (tăng ~271–290 MB/ngày; Drive cá nhân ~5 TiB free ⇒ rủi ro thấp) + 14 plaintext DORMANT + 8 dump plaintext VPS + defect nhãn-stage SIGPIPE T8 (tiền tồn, ngoài phạm vi).

Ràng buộc verdict: chỉ verdict PASS này mở đường cho C2B1-R0 và PERM-R0 xét tiếp; KHÔNG tự động bắt đầu rotation / permission repair / historical cleanup.


15. Đọc trước (V2) — nguồn đã đối chiếu

Toàn văn: script live backup-to-gdrive.sh (16,123 B, sha256 19e43432…, byte-identical commit 2ce85a63); restore runbook (/opt/incomex/docs/recovery/…, 291 dòng); 4 báo cáo C2B-PRE trong KB — encryption (…2026-07-18, PASS_PENDING_FIRST_NATURAL_PRODUCTION_RUN_VERIFICATION), prerun-gate (…2026-07-19, WAIT_FOR_FIRST_NATURAL_PRODUCTION_RUN), first-natural-run V3 (…2026-07-20, FAIL_ENCRYPTED_BACKUP_NOT_PRODUCED), F1 (…2026-07-20, PASS_EXACT_NATURAL_BACKUP_CONTROL_FLOW_FAILURE_REPAIRED). Không có "rollback runbook" riêng cho backup trên máy (các báo cáo rollback tháng 6 thuộc laws-new, không liên quan). Không dùng tóm tắt chat thay bằng chứng — mọi khẳng định neo vào đo trực tiếp.

— Hết —

Back to Knowledge Hub knowledge/current-state/reports/claude-c2b-pre-v3-first-natural-encrypted-backup-restore-verification-2026-07-21.md