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)
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 CÓ secretmanager.versions.access trên CẢ HAI khoá riêng (Directus và 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:00Z— SAI, vì cờ-ukhiế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_DIR ở dò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 (mtime2026-07-19 08:23 CEST) nhưng rỗng, trong khibackup-status/không tồn tại. Vì script tạo cả hai trong cùng một lệnhmkdir -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.shkhông xuất hiện trong danh sách script tham chiếugdrive-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 là 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 CŨ 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ệnhrclone delete*. ChỉREMOTE_LEGACY=$(… grep -c "^vps-backup-")rồi log.backup-to-gdrive.sh.pre-fix-1779157585,.pre-s174→ có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-idriêng (WEB_FOLDER_ID,AGENT_FOLDER_ID) và matcher^${repo_name}_[0-9]{8}-[0-9]{4}_ICT\.tar\.gz$⇒ không thể chạmvps-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ó || true ⇒ khô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 có 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, và 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 |
✅ CÓ | ✅ | ✅ |
LARK_BACKUP_GPG_PRIVATEKEY_RECOVERY |
✅ CÓ | ✅ | ✅ |
DIRECTUS_ADMIN_TOKEN |
✅ CÓ | ✅ | ✅ |
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) đềuPERMISSION_DENIED. Suy ra "vậy SA không đọc được khoá" là SAI —getIamPolicyvàversions.accesslà hai quyền khác nhau. ChỉtestIamPermissionsmớ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=root → cursor-ci-builder@ (cùng) |
versions.access trên khoá riêng |
✅ CÓ | ✅ 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:
- 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.
- Mission IAM least-privilege riêng — thu hẹp
secretmanager.versions.accesscủacursor-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ếnVPS_ROOT_COMPROMISE_DECRYPTION_PROTECTEDtừ 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 và 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
curlqua file config chmod 600 +trap shred⇒ không vào argv. gcloud secrets versions accesskhông được gọi trên bất kỳ secret nào — chỉ dùnglist(tên + thời gian tạo) vàtestIamPermissions(danh sách quyền).rclone config showđược lọc quasedchetoken/client_secret/client_id/service_account_credentialstrướ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.