KB-DFAC

Claude — C2B-PRE-V: Cổng tiền-kiểm lần chạy production tự nhiên đầu tiên + đo phụ lục (2026-07-19)

30 min read Revision 1
current-statec2b-pre-vdirectusbackup-encryptionverificationread-onlynatural-run-pendingwait-verdictgpgsecret-manageriam-exposureprivate-key-accessretention-dormantplaintext-residualdrive-capacityuptime-kumasynthetic-heartbeat-excludedcredential-rotation-blockedzero-mutation2026-07-19WAIT_FOR_FIRST_NATURAL_PRODUCTION_RUN

Claude — C2B-PRE-V: Cổng tiền-kiểm lần chạy production tự nhiên đầu tiên + đo phụ lục (2026-07-19)

Thực thi read-only 2026-07-19 07:08–07:25 UTC (09:08–09:25 CEST). PAYLOAD=REDACTED — không một giá trị password/token/khoá riêng nào xuất hiện trong tài liệu này.


0. LƯU Ý VỀ TÊN FILE — ĐỌC TRƯỚC

Mission C2B-PRE-V chỉ định báo cáo tên claude-c2b-pre-first-natural-production-run-verification-2026-07-20.md.

Tôi CỐ Ý không dùng tên đó, vì lần chạy tự nhiên chưa xảy ra. Một tài liệu tên "first natural production run verification" mà nội dung là "chưa có run nào" chính là false-green ở cấp tiêu đề.

Tên file đó được giữ nguyên, để dành cho báo cáo xác minh hậu-run thật sự, sau 2026-07-19T18:37:00Z. Đây là deviation duy nhất so với §13, và nó được khai báo tường minh.


1. Executive verdict

VERDICT = WAIT_FOR_FIRST_NATURAL_PRODUCTION_RUN

C2B_PRE_FINAL_STATUS       = PENDING_FIRST_NATURAL_RUN   (nhánh WAIT của §11, KHÔNG phải nhánh FAIL)
NEXT_ALLOWED_MISSION       = RERUN C2B-PRE-V sau 2026-07-19T18:37:00Z
C2B1_ALLOWED               = NO
CREDENTIAL_ROTATION_BLOCKED = YES

Lần cron production tự nhiên đầu tiên dưới script mã hoá chưa chạy, chưa từng chạy, và không đang chạy. Không có failure nào để sửa — chỉ đơn giản là chưa tới giờ. Đây là nhánh WAIT, không phải FIX_EXACT_OBSERVED_BACKUP_FAILURE.

Vì mission cấm manual-trigger, tôi DỪNG tại đây và không chạy bất cứ thứ gì.

Ba phát hiện phải đọc, độc lập với verdict WAIT:

# Phát hiện Mức
F1 VPS service account secretmanager.versions.access trên CẢ HAI khoá riêng (Directus Lark). Khoá no-passphrase. ⇒ chiếm root VPS = giải mã được toàn bộ backup. Invariant tài liệu SAI. 🔴
F2 Retention của 14 artifact plaintext cũ đã trở thành DORMANT. Chúng sẽ không bao giờ tự hết hạn — nằm trên Drive vĩnh viễn cùng 13 static token plaintext. 🔴
F3 8 bản dump Directus plaintext (~928 MB) nằm trên đĩa VPS tại /opt/incomex/backups/pg/, sinh bởi cron 02:27 độc lập, C2B-PRE không chạm tới. 🟠

Sức chứa Drive không phải rủi ro: còn ~4.97 TiB ⇒ ~52 năm ⇒ DRIVE_CAPACITY_RISK=LOW.


2. Nguồn đã đọc

Nguồn Ghi chú
knowledge/current-state/reports/claude-c2b-pre-encrypt-directus-database-backup-2026-07-18.md rev1 verdict PASS_PENDING_FIRST_NATURAL_PRODUCTION_RUN_VERIFICATION
knowledge/current-state/reports/claude-c2a-directus-bootstrap-admin-rotation-readiness-2026-07-18.md rev1 liệt kê trong KB index
Live evidence (ưu tiên hơn memory và report) crontab, script, log, status dir, Drive, GCP IAM, Kuma DB, docker
/root/c2bpre-checkpoint-20260719T040200Z/ checkpoint gốc của C2B-PRE
/root/c2bpre-work/runbook.md runbook khôi phục

Nơi live evidence mâu thuẫn memory → live thắng, và tôi ghi rõ đính chính (§13).


3. CHỨNG MINH LỊCH THẬT VÀ RUN CHƯA XẢY RA (J5 + §3)

3.1 Lịch live — đọc trực tiếp, không tin mốc trong báo cáo cũ

LIVE_SCHEDULE = crontab root, dòng 54:  37 20 * * * /opt/incomex/scripts/backup-to-gdrive.sh

Scoping timezone đã kiểm chính xác: CRON_TZ đầu tiên xuất hiện ở dòng 56 — tức SAU dòng 54. Không có CRON_TZ nào trước đó ⇒ dòng 54 chạy theo system TZ.

System TZ = Europe/Berlin (CEST, +0200), NTP synchronized = yes
⇒ 20:37 CEST = 18:37 UTC     ← mốc 20:37 CEST trong báo cáo cũ được XÁC NHẬN bằng đo live

Cron daemon active (từ 2026-02-12) ⇒ lần chạy sẽ nổ đúng giờ.

⚠️ Bẫy đo đã gặp và đã sửa: date -u -d "today 20:37" trả 20:37:00ZSAI, vì cờ -u khiến GNU date parse đầu vào theo UTC luôn. Giá trị đúng phải lấy qua epoch: NEXT_EPOCH=$(date -d "today 20:37" +%s); date -u -d @$NEXT_EPOCH. Kết quả đúng: 18:37:00Z. Nếu không bắt lỗi này, toàn bộ mốc giờ trong báo cáo sẽ lệch 2 giờ.

3.2 Sáu nguồn độc lập — tất cả cùng kết luận: CHƯA CHẠY

# Nguồn Đo được Kết luận
1 Cron schedule next = 2026-07-19T18:37:00Z; now = 2026-07-19T07:20:22Z; còn 40,598 s ≈ 11h17m chưa tới giờ
2 Script mtime 2026-07-19T05:48:10Z cài SAU run cuối (18:37:01Z ngày 07-18) ⇒ run cuối chạy bản CŨ
3 Cron journal Jul 18 20:37:01 CRON[1243296]: (root) CMD (backup-to-gdrive.sh)không có bản ghi nào ngày Jul 19 scheduler chưa gọi
4 Backup log mtime 2026-07-18 20:38:48 CEST; 0 marker định dạng mới (BACKUP START run_id=, DIRECTUS_DB_BACKUP PASS/FAIL) script mới chưa ghi log lần nào
5 Status marker /var/lib/incomex/backup-status/ KHÔNG TỒN TẠI script line 48 mkdir -p chưa từng thực thi
6 Drive prefix incomex-encrypted-v1/directory not found chưa có artifact mã hoá nào

Nguồn #5 là bằng chứng mạnh nhất: script tạo STATUS_DIRdòng 48, trước mọi thứ khác. Thư mục không tồn tại ⇒ script production chưa chạy một lần nào, kể cả thất bại sớm.

3.3 Dòng thời gian đối chiếu

2026-07-18T18:37:01Z  ← run tự nhiên CUỐI (script CŨ, plaintext) → vps-backup-20260718_203701.tar.gz
2026-07-18T18:38:48Z  ← heartbeat THẬT cuối (msg="OK archive=259M", định dạng CŨ)
2026-07-19T02:02:00Z  ← C2B-PRE tạo checkpoint
2026-07-19T04:08:23Z  ← tạo secret DIRECTUS_BACKUP_GPG_PRIVATEKEY_RECOVERY
2026-07-19T04:10:46Z  ← heartbeat SYNTHETIC (msg="probe")            ⚠️ LOẠI TRỪ
2026-07-19T05:48:10Z  ← cài script mã hoá (mtime)
2026-07-19T07:20:22Z  ← THỜI ĐIỂM KIỂM TRA NÀY
─────────────────────────────────────────────────────────────
2026-07-19T18:37:00Z  ← RUN TỰ NHIÊN ĐẦU TIÊN (còn 11h17m)          ⏳ CHƯA XẢY RA
NATURAL_RUN_ID          = (chưa tồn tại)
SCHEDULED_UTC           = 2026-07-19T18:37:00Z
STARTED_UTC             = N/A
COMPLETED_UTC           = N/A
DURATION                = N/A
EXIT_STATUS             = N/A
NATURAL_RUN_COMPLETION_PROOF = NONE — run chưa được scheduler gọi

4. Loại trừ heartbeat synthetic (§3, J1)

Đọc read-only SQLite Kuma tại /opt/incomex/uptime-kuma/kuma.db (mode=ro). Monitor id=14 "PG Backup GDrive" type=push active=1. Kuma lưu giờ UTC (kiểm chứng: bản ghi 2026-07-18 18:38:48 ↔ backup kết thúc 20:38:48 CEST).

time (UTC) status msg phân loại
2026-07-19 04:10:46.238 1 (up) probe 🚫 SYNTHETIC — LOẠI TRỪ
2026-07-18 18:38:48.969 1 (up) OK archive=259M heartbeat THẬT cuối, script CŨ
SYNTHETIC_HEARTBEAT_2026_07_19_0410UTC = DISCLOSED_AND_EXCLUDED
NATURAL_RUN_MONITOR_SIGNAL             = NO_REAL_SIGNAL

msg="probe" tự nó chứng minh đây là khảo sát, không phải run thật: script chỉ có thể gửi OK+encrypted&ping=<bytes> (thành công) hoặc FAIL_stage=…_class=… (thất bại). Không đường nào sinh ra probe.

Không gửi heartbeat sửa chữa. Không ping endpoint. Đọc DB thuần tuý.


5. Singleton lock và status (§4)

BACKUP_STATUS = INCONCLUSIVE   (không phải PASS/FAIL/STALE — chưa có run nào để có trạng thái)
Kiểm Kết quả
Status dir /var/lib/incomex/backup-status/ KHÔNG TỒN TẠI
Status file không tồn tại
Lock .directus-db-backup.lock không tồn tại ⇒ không bị giữ
Stale RUNNING marker 0
Overlapping run 0
Tiến trình backup / pg_dump / gpg --batch đang chạy NONE
Staging /var/lib/incomex/backup-staging/ RỖNG

Không có stale RUNNING, không có lock treo, không có process mồ côi. Trạng thái sạch để đón run đầu.

Ghi chú: backup-staging/ tồn tại (mtime 2026-07-19 08:23 CEST) nhưng rỗng, trong khi backup-status/ không tồn tại. Vì script tạo cả hai trong cùng một lệnh mkdir -p (dòng 48), sự bất đối xứng này chứng minh staging do harness C2B-PRE tạo, không phải do script production chạy.


6. Artifact mã hoá — baseline PRE (§5)

ENCRYPTED_PREFIX_COUNT_PRE  = 0     ← prefix incomex-encrypted-v1/ CHƯA TỒN TẠI trên Drive
ENCRYPTED_PREFIX_COUNT_POST = 0     (chưa có run)
NEW_ARTIFACT_COUNT          = 0
NEW_ARTIFACT_NAME           = N/A
NEW_ARTIFACT_BYTES          = N/A
CIPHERTEXT_SHA256_MATCH     = N/A
ENCRYPTED_PREFIX_CURRENT_BYTES = 0

rclone lsl gdrive-backup:incomex-encrypted-v1/NOTICE: Failed to lsl: directory not found.

Đây là baseline sạch, có giá trị: run tự nhiên phải đưa count 0 → 2 artifact .gpg (incomex-prod-directus-db-*.sql.gz.gpg + incomex-prod-vps-config-*.tar.gz.gpg) cộng 2 sidecar .meta.json. Bất kỳ con số nào khác ở lần kiểm hậu-run đều là bất thường.

⚠️ Đính chính cho mission hậu-run: §5 yêu cầu ENCRYPTED_PREFIX_INCREMENT=1. Đọc script (dòng 242–245) cho thấy vòng upload đẩy 4 file: 2 .gpg + 2 .meta.json. Vậy kỳ vọng đúng là +2 artifact .gpg (+2 sidecar), không phải +1. Nếu áp máy móc "tăng đúng 1" sẽ báo FAIL sai.

Khoá người nhận (kiểm live, khớp hằng số trong script):

RECIPIENT_FPR = 31799F7AC051EBA68FB3F13BDCC1369B371837BA   (pub RSA4096)
ENC_KEYID     = E800896AF06E9B61                            (subkey mã hoá RSA4096)
uid           = INCOMEX Directus DB Backup (C2B-PRE encrypt-only; private key held in GCP Secret Manager)
pubkey_sha256 = 5e6c0864df2f8f4c94a8477db0d60bc8cd0cbf237f0adf53c87ea7562cb24f9e

7. Plaintext, partial và quét bí mật (§7)

ABANDONED_PARTIAL_COUNT      = 0
UPLOAD_STAGING_PLAINTEXT_COPY = 0
LOCAL_TEMP_PLAINTEXT_COPY     = 0   (trong đường backup mã hoá)
CIPHERTEXT_KNOWN_SECRET_MATCH = N/A — chưa có ciphertext để quét
LOG_STATUS_SECRET_MATCH       = 0   (không có status file; log không có marker mới)

find /var/lib/incomex /opt/incomex/backups /tmp /var/tmp -maxdepth 3 \( -name "*.partial" -o -name "incomex-prod-*" \)rỗng.

🟠 F3 — Bản dump plaintext trên đĩa, NGOÀI phạm vi C2B-PRE

/opt/incomex/backups/pg/ chứa 8 bản dump Directus plaintext (gzip, KHÔNG mã hoá):

directus_2026-07-12_0027.sql.gz … directus_2026-07-19_0027.sql.gz
mỗi bản ~114.7–116.8 MB · mode -rw------- (root) · tổng ≈ 928 MB
bản mới nhất: 2026-07-19 02:27 CEST (hôm nay)

Sinh bởi cron độc lập: 27 2 * * * /opt/incomex/scripts/pg-backup.sh. Có heartbeat riêng (monitor 12 "PG Backup Local", 2026-07-19 00:27:52Z OK size=112M).

Đã kiểm và loại trừ đường rò lên Drive:

  • pg-backup.sh không xuất hiện trong danh sách script tham chiếu gdrive-backup: ⇒ local-only.
  • Bundle CFG của script mới không nuốt thư mục này: nó chỉ lấy /opt/incomex/docker (maxdepth 2, *.yml), find /opt/incomex -path "*/nginx*" (chỉ *.conf/*.template), và /opt/incomex/scripts/*. Không pattern nào khớp /opt/incomex/backups/pg/*.sql.gz.

Kết luận: không phải đường phát tán mới, nhưng một bản sao plaintext còn sống chứa toàn bộ 13 static token, nằm ngay trên máy mà service account của nó đọc được khoá riêng (F1). C2B-PRE không đóng bề mặt này. Cần một mission riêng.


8. Retention và an toàn artifact lịch sử (§6, J3)

OLD_PLAINTEXT_PREFIX_COUNT_PRE  = 14
OLD_PLAINTEXT_PREFIX_COUNT_POST = 14        ✅ BẰNG NHAU
HISTORICAL_ARTIFACT_DELETE      = 0         ✅
MIXED_FORMAT_RETENTION_DELETE   = 0         ✅
ENCRYPTED_PREFIX_INCREMENT      = 0         (chưa có run)

Đối chiếu với evidence PRE của chính C2B-PRE (evidence/drive-root-files.PRE): 16 file / 14 vps-backup-*. Đo lại bây giờ: 16 file / 14 vps-backup-*. Không xoá, không thêm.

🔴 F2 — Phân loại retention

OLD_PREFIX_RETENTION = DORMANT_NOT_EVALUATED_AFTER_MIGRATION
Thuộc tính Giá trị đo
Prefix Drive root của remote gdrive-backup: (root_folder_id=1JWbDOPc16EHwDxo9t9zueZr423Q5IDEQ)
Matcher grep -c "^vps-backup-"
Retention count-based, MAX_REMOTE_BACKUPS=14
Code path còn active? KHÔNG — script mới chỉ đếm rồi log "untouched", không có lệnh xoá remote nào
Lần retention gần nhất 2026-07-18 20:38 — script xoá vps-backup-20260704_203701.tar.gz
Có thể tự bị xoá trong tương lai? KHÔNG

Kiểm hết mọi code path còn có thể xoá trên Drive:

  • backup-to-gdrive.sh (đang chạy) → 0 lệnh rclone delete*. Chỉ REMOTE_LEGACY=$(… grep -c "^vps-backup-") rồi log.
  • backup-to-gdrive.sh.pre-fix-1779157585, .pre-s174 rclone deletefile, nhưng là file backup dormant, cron dòng 54 gọi đường dẫn trần ⇒ không bao giờ được gọi.
  • code-backup-to-gdrive.sh:96 → có xoá, nhưng bị khoá cứng trong --drive-root-folder-id riêng (WEB_FOLDER_ID, AGENT_FOLDER_ID) và matcher ^${repo_name}_[0-9]{8}-[0-9]{4}_ICT\.tar\.gz$không thể chạm vps-backup-* ở Drive root.

Hệ quả vận hành: count-based retention chỉ kích hoạt khi có artifact mới khớp ^vps-backup-. Script mới ghi tên khác (incomex-prod-*) vào prefix khác. Vì vậy count đứng yên ở 14 vô thời hạn:

  • ✅ Tốt: phao cứu sinh cũ không bị mất trước khi backup mã hoá được chứng minh.
  • 🔴 Xấu: 14 artifact plaintext này — mỗi bản chứa toàn bộ Directus DB kèm 13 static token plaintext — sẽ nằm trên Drive vĩnh viễn, không tự hết hạn. Dọn chúng phải là một mission cleanup riêng, có Owner phê duyệt.
OLD_PLAINTEXT_BACKUP_ROLE = TEMPORARY_FALLBACK_UNTIL_ENCRYPTED_NATURAL_RUN_PROVEN
                          + HISTORICAL_CREDENTIAL_EXPOSURE      (cả hai cùng đúng)

9. Sức chứa và thời gian còn lại của remote (J0)

REMOTE_TYPE = PERSONAL_DRIVE

Căn cứ: rclone type=drive, scope=drive + root_folder_id; rclone about trả quota theo user (Shared Drive không báo kiểu này); runbook chỉ định đăng nhập nmhuyen@gmail.com. Là suy ra, không phải đọc trực tiếp API about.user — nếu cần chắc chắn tuyệt đối thì xác nhận trong Drive UI.

DRIVE_QUOTA_STATUS      = KNOWN
DRIVE_QUOTA_TOTAL_BYTES ≈ 5,497,558,138,880   (5 TiB)
DRIVE_QUOTA_USED_BYTES  ≈    31,186,000,000   (29.045 GiB)
DRIVE_QUOTA_FREE_BYTES  ≈ 5,460,254,000,000   (4.966 TiB)
DRIVE_TRASHED_BYTES     ≈    15,331,000,000   (14.279 GiB — vẫn tính vào quota)
ENCRYPTED_PREFIX_CURRENT_BYTES      = 0
OLD_PLAINTEXT_PREFIX_CURRENT_BYTES  = 3,822,611,835   (14 file, tổng chính xác)

Ước lượng tăng trưởng:

NATURAL_ARTIFACT_BYTES     = N/A (chưa có run)
OBSERVED_DAILY_GROWTH_BYTES ≈ 271,000,000 – 290,000,000   (ước lượng)
ESTIMATED_DAYS_UNTIL_FULL   ≈ 19,000 ngày ≈ 52 năm
GROWTH_ESTIMATE_SOURCE = LEGACY_ARTIFACT_AND_LOCAL_DUMP_SIZES
                         (KHÔNG phải natural run, và cũng KHÔNG phải test artifact —
                          C2B-PRE không để lại test artifact nào để đo)
DRIVE_CAPACITY_RISK = LOW

Cơ sở: artifact legacy mới nhất 271,385,746 B; dump PG local hôm nay 116,812,785 B (xấp xỉ phần DB sau pg_dump|gzip|gpg, GPG chỉ thêm ~0.1%). Phần CFG ≈ phần còn lại.

Không chạm ngưỡng nào: không RETENTION_DECISION_URGENT_OWNER_ACTION_REQUIRED (<90 ngày), không BACKUP_CONTINUITY_HIGH_RISK (<30 ngày), không BACKUP_CONTINUITY_IMMEDIATE_BLOCKER.

⚠️ Lưu ý thiết kế: prefix mã hoá không có retention remote nào cả (append-only, tăng vô hạn). Ở quy mô hiện tại điều đó vô hại trong nhiều thập kỷ, nhưng nó là quyết định có chủ đích cần được ghi nhận, không phải thiếu sót bị bỏ quên.


10. Ngữ nghĩa heartbeat (J1)

Vị trí up trong script (dòng ~263, ngay trước exit 0):

Điều kiện J1 Đạt? Bằng chứng
1. encrypted pipeline PASS PG_DUMP/ENCRYPT kiểm PIPESTATUS fail-closed (dòng 135–138)
2. final artifact rename PASS mv -f *.partial → final gated || fail VERIFY ATOMIC_RENAME_FAILED (212–213)
3. upload PASS rclone copyto … || fail UPLOAD UPLOAD_FAILED (242–245)
4. remote existence/verification PASS ⚠️ một phần chỉ có kiểm tra tích hợp của rclone; rclone lsf sau đó có || truekhông phải gate
5. status marker success sẵn sàng status_write PASS COMPLETE chạy trước heartbeat (dòng 258)
HEARTBEAT_SEMANTICS = AFTER_UPLOAD_WITHOUT_REMOTE_VERIFY

Đánh giá cân bằng: heartbeat nằm đúng chỗ trong pipeline — nó thực sự ở sau upload thành công và sau khi status marker PASS đã ghi. Điểm trừ duy nhất là script không tự đọc ngược lại remote để xác nhận; nó uỷ thác hoàn toàn cho exit code của rclone. Trên thực tế rclone copyto v1.73 tới Drive so khớp MD5 sau khi truyền và trả lỗi nếu lệch — nên khoảng trống là hẹp, không phải hở toang. Nhưng theo đúng định nghĩa của J1, đây không phải AFTER_REMOTE_SUCCESS.

Hệ quả bắt buộc cho mission hậu-run:

NATURAL_RUN_MONITOR_SIGNAL KHÔNG được kết luận VERIFIED_REAL
ACTIVE_MONITORING_CLAIM = HOLD_HEARTBEAT_SEMANTICS_INVALID

Containment full PASS vẫn có thể đạt nếu artifact + remote verification thủ công + status marker + fail-visible log đều PASS — nhưng báo cáo hậu-run phải nói rõ active heartbeat monitoring chưa đáng tin cậy.

Đường fail thì lành mạnh: fail() ghi status FAIL, log một dòng verdict, gửi status=down&msg=FAIL_stage=…_class=…. Thất bại sẽ nhìn thấy được.

Backlog (KHÔNG sửa trong mission này): thêm một gate đọc-ngược trước heartbeat, ví dụ rclone lsjson đối chiếu size/hash của artifact vừa upload, để nâng lên AFTER_REMOTE_SUCCESS.


11. Tải xuống / giải mã / chứng minh định dạng (§8)

KHÔNG THỰC HIỆN — không có artifact tự nhiên để tải.

NATURAL_ARTIFACT_DECRYPT     = N/A
DUMP_COMPLETE                = N/A
TABLE_COUNT                  = N/A
KNOWN_TOKEN_RECOVERY_COUNT   = N/A
DECRYPTED_TEMP_CLEANUP       = N/A (không có gì để dọn)

Mission cấm dùng artifact test isolated; C2B-PRE cũng không để lại artifact nào. Không có khoá riêng nào được lấy về, không có gì được giải mã, không tạo vật liệu tạm.

Tôi CỐ Ý không tải vps-backup-20260718_203701.tar.gz để chứng minh khả năng phục hồi. Nó là plaintext chứa 13 static token còn sống; tải nó về VPS sẽ tự tay tạo thêm một bản sao plaintext — đúng thứ toàn bộ chuỗi mission C0→C2B đang cố loại bỏ. Chứng minh phục hồi cho bản legacy, nếu Owner cần, phải làm ngoài VPS trong một mission có phê duyệt riêng.


12. Phao phục hồi hiện tại (J4)

J4 áp dụng khi run không đạt full PASS. Ở đây run chưa xảy ra, nên tôi ghi phao hiện hành để Owner thấy sàn phục hồi, không trình hai lựa chọn A/B — vì chưa có failure nào để phải chọn.

NEWEST_PROVEN_RESTORABLE_OFFSITE_PRODUCTION_BACKUP_UTC = 2026-07-18T18:37:01Z
   artifact = vps-backup-20260718_203701.tar.gz  (271,385,746 B, Drive root)
NEWEST_..._AGE_DAYS  ≈ 0.53 ngày (~12.7 giờ tại thời điểm đo)
BACKUP_RECOVERY_GAP_START_UTC   = không có gap
CURRENT_RECOVERY_GAP_HOURS_OR_DAYS = 0 — chuỗi backup đêm chưa hề đứt

Mức độ chứng minh, nói thẳng: đây là artifact production (do cron sinh, đúng chu kỳ, kích thước nhất quán với 13 bản liền trước 263–282 MB), định dạng plaintext .tar.gz nên đọc được không cần khoá. Chưa kiểm tính toàn vẹn bên trong, vì lý do ở §11. Đây là giới hạn có chủ ý, không phải sơ suất.

Ngoài ra còn 8 dump PG local (§7) làm phao phụ — nhưng on-VPS, mất VPS là mất luôn.


13. 🔴 F1 — Phân loại an ninh quyền truy cập khoá riêng (J2)

DOCUMENTED_INVARIANT_CONTRADICTED_BY_MEASUREMENT = YES

Câu đúng, phải dùng từ nay:

"Private key is not stored persistently on the VPS, but the VPS service account can currently retrieve it from Secret Manager."

Câu CẤM dùng: "VPS NEVER holds/can access the private key."

PRIVATE_KEY_PERSISTED_ON_VPS                    = NO
VPS_SERVICE_ACCOUNT_CAN_CURRENTLY_FETCH_PRIVATE_KEY = YES
VPS_ROOT_COMPROMISE_DECRYPTION_PROTECTED        = NO

Đo bằng cách nào (không materialize khoá)

Dùng API secretmanager…:testIamPermissions — API introspection thuần tuý, trả về danh sách quyền, không bao giờ trả nội dung secret. Access token được nạp qua file config curl -K chmod 600 + trap shred để không lọt vào argv (ps không thấy).

Tôi chủ động KHÔNG chạy gcloud secrets versions access — lệnh đó sẽ đặt khoá riêng RSA4096 lên đĩa VPS và vào output của tôi.

Kết quả

Identity: cursor-ci-builder@github-chatgpt-ggcloud.iam.gserviceaccount.com (account active trên VPS), project github-chatgpt-ggcloud.

Secret versions.access versions.list secrets.get
DIRECTUS_BACKUP_GPG_PRIVATEKEY_RECOVERY
LARK_BACKUP_GPG_PRIVATEKEY_RECOVERY
DIRECTUS_ADMIN_TOKEN

Metadata khoá (evidence/encryption-key.META) làm mọi thứ tệ hơn một bậc:

key_algo = RSA4096/RSA4096 no-passphrase (protection = Secret Manager IAM)
sm_secret = projects/github-chatgpt-ggcloud/secrets/DIRECTUS_BACKUP_GPG_PRIVATEKEY_RECOVERY

Không có passphrase. IAM là lớp bảo vệ duy nhất. Ai chiếm được root VPS thì kế thừa credential của service account, gọi versions.access, lấy khoá RSA4096 trần, và giải mã được mọi artifact trong prefix mã hoá.

⚠️ Ghi nhận quá trình: hai lệnh đầu (projects get-iam-policy, secrets get-iam-policy) đều PERMISSION_DENIED. Suy ra "vậy SA không đọc được khoá" là SAIgetIamPolicyversions.access là hai quyền khác nhau. Chỉ testIamPermissions mới trả lời đúng câu hỏi. Memory ban đầu đúng; nếu dừng ở lệnh thứ hai tôi đã báo cáo ngược 180°.

Mô hình phơi nhiễm của s177-lark-backup

Xác minh read-only:

Hạng mục Directus backup s177-lark-backup
Key secret DIRECTUS_BACKUP_GPG_PRIVATEKEY_RECOVERY LARK_BACKUP_GPG_PRIVATEKEY_RECOVERY
Project github-chatgpt-ggcloud github-chatgpt-ggcloud (cùng)
Identity vận hành cron root → cursor-ci-builder@ s177-lark-backup.service User=rootcursor-ci-builder@ (cùng)
versions.access trên khoá riêng ✅ CÓ
Cơ chế materialize khi chạy chỉ dùng public key trên đĩa chỉ dùng LARK_BACKUP_GPG_PUBKEY; script chỉ fetch LARK_APP_ID/LARK_APP_SECRET, không fetch khoá riêng
LARK_BACKUP_PRIVATE_KEY_EXPOSURE_MODEL = SAME_AS_DIRECTUS_CONFIRMED
AFFECTED_SYSTEMS = s177-lark-backup, directus-db-backup

Đủ điều kiện ghi cả hai vào AFFECTED_SYSTEMS: exact mapping đã chứng minh — cùng project, cùng identity, cùng quyền, trên đúng resource khoá của từng hệ. Lưu ý phân biệt: vận hành bình thường cả hai đều không chạm khoá riêng (đúng thiết kế encrypt-only); cái bị bác bỏ là tuyên bố về khả năng — capability tồn tại ở cả hai.

Không sửa IAM. Không sửa canonical documentation trong mission này. Backlog:

  1. O1 — sửa canonical: thay mọi khẳng định "VPS NEVER can access the private key" bằng câu đúng ở trên. Áp cho cả tài liệu Lark.
  2. Mission IAM least-privilege riêng — thu hẹp secretmanager.versions.access của cursor-ci-builder@, đặc biệt gỡ quyền trên hai secret *_GPG_PRIVATEKEY_RECOVERY (VPS không cần chúng để mã hoá). Đây là thay đổi biến VPS_ROOT_COMPROMISE_DECRYPTION_PROTECTED từ NO thành YES.

14. Đĩa và sức khoẻ runtime (§10)

BACKUP_DISK_IMPACT        = 0 bytes (không có run)
CURRENT_DISK_USED_PERCENT = 89%   (90,562,031,616 / 102,888,095,744 B; còn 12,309,286,912 B)
ABANDONED_BACKUP_BYTES    = 0
CONTAINER_MUTATION        = 0
SERVICE_HEALTH            = PASS

Đối chiếu với evidence/disk-avail-bytes.PRE của C2B-PRE:

PRE  = 12,349,874,176 B
NOW  = 12,309,286,912 B
Δ    =    −40,587,264 B (−40.6 MB qua ~5 giờ)

Trôi vận hành bình thường (log, journal). Không do backup — không có backup nào chạy.

Container (11 đang chạy, tất cả healthy hoặc up):

Container Status Restarts StartedAt
postgres Up 3 months (healthy) 0 2026-04-17T05:35:18Z
incomex-directus Up 2 months (healthy) 0 2026-05-08T09:13:06Z
incomex-qdrant Up 4 months (healthy) 0 2026-03-18T07:03:47Z
uptime-kuma Up 3 months (healthy) 0 2026-04-08T09:18:38Z
incomex-agent-data Up 46 hours (healthy) 0 2026-07-17T09:45:44Z
incomex-nginx Up 2 weeks 0 2026-06-30T00:44:14Z
incomex-agent-api-executor Up 6 weeks (healthy) 0 2026-06-04T08:46:26Z
incomex-claude-mcp Up 7 days (healthy) 2 2026-07-12T00:45:39Z
incomex-claude-kb Up 3 days (healthy) 2 2026-07-16T03:25:22Z
pg-restore-test-20260520T031054Z Up 2 months 0 2026-05-20T03:10:55Z
incomex-nuxt Up 53 minutes (healthy) 50 2026-07-19T06:22:14Z

PostgreSQL và Directus — hai thành phần mà backup phụ thuộc — đều healthy, uptime dài, 0 restart.

Ghi chú ngoặc về Nuxt — KHÔNG điều tra

incomex-nuxt RestartCount=50, khởi động lại lúc 2026-07-19T06:22:14Z, ExitCode=0. Khớp chính xác vấn đề có sẵn từ trước đã ghi ở C2A (tự restart ~1–2h, ExitCode=0).

Bracket: mission này chạy 07:08–07:25Z. Lần restart lúc 06:22:14Z xảy ra 46 phút TRƯỚC lệnh đầu tiên của tôi. Tôi không chạy lệnh nào chạm Nuxt — không docker mutation, không restart, không sửa config. Không quy cho mission này. Không điều tra (mission cấm).


15. Điều hành sau verdict

C2B_PRE_FINAL_STATUS = PENDING_FIRST_NATURAL_RUN
NEXT_ALLOWED_MISSION = RERUN C2B-PRE-V (xác minh hậu-run) sau 2026-07-19T18:37:00Z

KHÔNG phải FIX_EXACT_OBSERVED_BACKUP_FAILURE — không có failure nào được quan sát. KHÔNG phải C2B1_BOOTSTRAP_ADMIN_CREDENTIAL_ROTATION — điều kiện tiên quyết chưa đạt.

CREDENTIAL_ROTATION_BLOCKED = YES. Xoay credential trước khi backup mã hoá được chứng minh là phục hồi được sẽ khiến hệ thống rơi vào trạng thái mà bản backup mới nhất có thể không giải mã được credential cũ đã mất — không có đường lùi.

Danh mục kiểm cho mission hậu-run (dùng nguyên baseline này)

Kiểm Kỳ vọng
Prefix incomex-encrypted-v1/ từ không tồn tại → tồn tại
Artifact .gpg 0 → 2 (không phải +1 — xem §6)
Sidecar .meta.json 0 → 2
vps-backup-* ở Drive root giữ nguyên 14 (3,822,611,835 B)
Status dir từ không tồn tại → tồn tại, status=PASS stage=COMPLETE
Log marker xuất hiện BACKUP START run_id= + DIRECTUS_DB_BACKUP PASS
Kuma monitor 14 heartbeat mới OK+encrypted&ping=<bytes> ~18:38–18:40Z, không phải probe
Lock / process đã release / không còn
.partial 0
Đĩa giảm ≤ ~300 MB rồi hồi (LOCAL_KEEP=1)

16. Đường dẫn evidence

Lịch:        crontab -l (root) dòng 54; journalctl -u cron
Script:      /opt/incomex/scripts/backup-to-gdrive.sh   (11,731 B, mtime 2026-07-19T05:48:10Z)
Log:         /opt/incomex/logs/backup-gdrive.log        (8,747,485 B, mtime 2026-07-18 20:38:48 CEST)
Status:      /var/lib/incomex/backup-status/            ← KHÔNG TỒN TẠI
Staging:     /var/lib/incomex/backup-staging/           ← RỖNG
Checkpoint:  /root/c2bpre-checkpoint-20260719T040200Z/{backup,evidence,rollback,bundle}
             ├── evidence/drive-root-files.PRE      (16 dòng / 14 vps-backup-*)
             ├── evidence/disk-avail-bytes.PRE      (12,349,874,176)
             └── evidence/encryption-key.META
Harness:     /root/c2bpre-work/{runbook.md,harness.sh,remote-test.sh,run-failtests.sh}
Drive:       gdrive-backup: (root_folder_id=1JWbDOPc16EHwDxo9t9zueZr423Q5IDEQ)
             ├── incomex-encrypted-v1/              ← directory not found
             ├── 14 × vps-backup-*.tar.gz           (3,822,611,835 B)
             └── incomex-recovery-docs/directus-backup-restore-runbook.md
Kuma:        /opt/incomex/uptime-kuma/kuma.db (đọc mode=ro) — monitor id=14 "PG Backup GDrive"
Local dumps: /opt/incomex/backups/pg/directus_2026-07-{12..19}_0027.sql.gz (8 file, ~928 MB)
GCP:         project github-chatgpt-ggcloud
             identity cursor-ci-builder@github-chatgpt-ggcloud.iam.gserviceaccount.com

17. PAYLOAD=REDACTED — chứng minh

  • Không lệnh nào của tôi in ra password, static token, hay khoá riêng.
  • Access token GCP đưa vào curl qua file config chmod 600 + trap shredkhông vào argv.
  • gcloud secrets versions access không được gọi trên bất kỳ secret nào — chỉ dùng list (tên + thời gian tạo) và testIamPermissions (danh sách quyền).
  • rclone config show được lọc qua sed che token/client_secret/client_id/ service_account_credentials trước khi ra output.
  • Không artifact nào được tải; không ciphertext nào được giải mã; không có vật liệu decrypted để dọn.
  • Chỉ đọc metadata khoá công khai (fingerprint, keyid, uid) — đúng những giá trị vốn đã có trong source script.

18. Kết thúc bắt buộc

MANUAL_BACKUP_TRIGGER        = NO
UPTIME_KUMA_PING             = NO
BACKUP_SCRIPT_CHANGE         = NO
CRON_TIMER_CHANGE            = NO
RETENTION_CHANGE             = NO
ARTIFACT_DELETE              = NO
DIRECTUS_PASSWORD_CHANGE     = NO
DIRECTUS_STATIC_TOKEN_CHANGE = NO
DIRECTUS_SESSION_REVOCATION  = NO
SECRET_MANAGER_MUTATION      = NO
IAM_MUTATION                 = NO
CONTAINER_MUTATION           = NO
SERVICE_RESTART_BY_MISSION   = NO
DIRECT_DB_WRITE              = NO
GITHUB_PUSH                  = NO
DOCKER_CLEANUP               = NO
C2B1_EXECUTED                = NO

Ngoài ra: PRIVATE_KEY_MATERIALIZED = NO · ARTIFACT_DOWNLOADED = NO · FILESYSTEM_WRITE_ON_VPS = NO (file config curl tạm đã shred ngay trong cùng lệnh).

DỪNG sau báo cáo.

Back to Knowledge Hub knowledge/current-state/reports/claude-c2b-pre-v-prerun-gate-and-appendix-measurements-2026-07-19.md