KB-64CB

Claude — Balo post-acceptance: bảo vệ cơ chế + ổn định VPS (2026-07-17)

30 min read Revision 1
balocurrent-stateowner-acceptanceguardverifierdisksecret-rotationgithub-workflowsgit-safetyvps-ssotPASS

BALO POST-ACCEPTANCE — BẢO VỆ VÀ ỔN ĐỊNH VPS

  • Ngày: 2026-07-17 (05:18Z–06:05Z)
  • Thực hiện: Claude Opus 4.8 (1M)
  • Nguồn điều hành: claude-balo-one-click-sync-production-2026-07-17.md (PASS_BALO_ONE_CLICK_SYNC_PRODUCTION) + claude-balo-button-deployment-forensic-and-recommendation-2026-07-17.md
  • Mission TS: 20260717T053000Z

1. VERDICT

PASS_BALO_POST_ACCEPTANCE_PROTECTED_AND_STABILIZED

Từng phần:

OWNER_ACCEPTANCE       = PASS
BALO_PROTECTION_GUARD  = PASS                                   (37/37 checks)
UNSAFE_GITHUB_WORKFLOWS= ALREADY_DISABLED                       (+ đính chính: chỉ có MỘT, không phải hai)
DISK_STABILIZATION     = HOLD_DISK_CAPACITY_OWNER_DECISION      (91% → 88%, chưa đạt 85%)
SECRET_EXPOSURE        = ROTATION_PROVIDER_OWNER_ACTION_REQUIRED (rộng hơn báo cáo trước rất nhiều)
VPS_SSOT_GIT_SAFETY    = PASS                                   (có 1 ngoại lệ tồn đọng, ghi rõ §7)

Ba điều Owner cần biết ngay:

  1. Nút đã được nghiệm thu bằng Administrator thật — bấm một lần, ALREADY_UP_TO_DATE, dữ liệu không đổi.
  2. "Hai workflow nguy hiểm" thực ra là MỘTagent-data Vector-Audit 244569412 ghi trong KB không tồn tại (GitHub trả 404). Cái thật (web-test Deploy to VPS 233946923) đã disabled sẵn. Không cần disable gì thêm.
  3. Rò rỉ secret rộng hơn báo cáo trước rất nhiều. Báo cáo trước chỉ biết 3 file nó tự tạo. Đo thật: 34 file chứa secret sống, trong đó 2 file nằm trong git HEAD (DIRECTUS_ADMIN_PASSWORD plaintext) — có từ trước, không do mission nào gần đây. Xoá file không đóng được lỗ này; chỉ xoay key mới đóng được — và xoay key đang bị chặn (§6).

2. PHẦN A — NGHIỆM THU ADMINISTRATOR = PASS

A1. Credential — một đính chính với giả định của lệnh

Lệnh giả định "Google Secret Manager là SSOT cho tài khoản và credential". Đã khảo sát đúng quy trình (metadata trước, 32 secret trong github-chatgpt-ggcloud):

Secret Manager CÓ   : DIRECTUS_ADMIN_TOKEN, DIRECTUS_TAC_ADMIN_TOKEN, OPENAI_API_KEY,
                      QDRANT_LOCAL_API_KEY, gh_pat_sync_secrets, PG_*, POSTGRES_* …
Secret Manager KHÔNG: DIRECTUS_ADMIN_EMAIL / DIRECTUS_ADMIN_PASSWORD

Login Directus Administrator không nằm trong Secret Manager. Nó ở /opt/incomex/docker/.env (mode 0600 root:root) — nhất quán với "VPS là SSOT của cấu hình". DIRECTUS_ADMIN_TOKENtoken, không login UI được, nên không thay thế được.

Đây không phải SECRET_NOT_FOUND: credential tồn tại và hợp lệ, chỉ khác chỗ so với giả định của lệnh. Đã xác minh không in payload:

DIRECTUS_ADMIN_EMAIL     present=YES len=17 fp=258d8dc916db  payload=REDACTED
DIRECTUS_ADMIN_PASSWORD  present=YES len=23 fp=7b9fd0a51c57  payload=REDACTED
Directus user            : active | role=Administrator | a40a1070-0b62-4a7e-b2c0-bd4bce9d41ac  ✅ khớp allowlist

A2. Browser thật (Chromium + playwright có sẵn, không cài package)

LOGIN_URL = https://vps.incomexsaigoncorp.vn/portal
USERS_ME  = {"status":"active","role":{"id":"a40a1070-0b62-4a7e-b2c0-bd4bce9d41ac","name":"Administrator"}}

Ma trận nút:

Trang Nút Đúng?
/reports/report-balo-table 1 ✅ HIỆN
/reports/report-balo-view 1 ✅ HIỆN
/reports/report-balo-function 1 ✅ HIỆN
/reports/report-balo-trigger 1 ✅ HIỆN
/reports/report-pg (PG Census) 0 ✅ ẨN ĐÚNG
/reports 0 ✅ ẨN ĐÚNG

Một lần bấm:

LABEL_BEFORE   = "Đồng bộ Balo từ PG"
HTTP_STATUS    = 200
HTTP_BODY      = {"status":"ALREADY_UP_TO_DATE","before_total":2148,"after_total":2148,
                  "add":0,"revive":0,"metadata_update":0,"mark_missing":0,"conflict":0,
                  "verified":true,"message":"Balo đã mới nhất"}
LABEL_AFTER    = "Balo đã mới nhất"
ROWS           = 50 → 50
NO_FULL_RELOAD = true        ← tự refresh tại chỗ, KHÔNG reload toàn app

apply không được gọi (delta=0).

Singleton (giữ lock có kiểm soát, không WRITE):

LOCK=ACQUIRED
WHILE_LOCKED  = 409 {"status":"ALREADY_RUNNING","verified":false,"message":"Đang có phiên đồng bộ khác"}
LOCK=RELEASED
AFTER_RELEASE = 200 {"status":"ALREADY_UP_TO_DATE","before_total":2148,"after_total":2148,"verified":true}

A3. Dữ liệu trước = sau

PRE POST
COUNT_TOTAL 2148 2148
loai_census table 385 · view 699 · function 654 · trigger 410 y hệt
ROWS_MD5 8ed1cd5d2f93f5f8a970497bd5e4ec87 y hệt
DATE_UPDATED_HASH 41c6957d74453900b7cc10bfd33ad1c7 y hệt

diff PRE POSTrỗng. --verifyVERIFY|PASS_RECONCILED|delta=0|write=0.

Ghi chú đo: DATE_UPDATED_HASH của tôi (41c6957d…) khác số trong báo cáo trước (ae419898…) vì công thức khác (tôi ORDER BY stt — PK thật là stt, không phải id), không phải dữ liệu đổi. Bằng chứng quyết định: ROWS_MD5 = 8ed1cd5d… của tôi trùng khít thành phần md5 trong fingerprint của runner ở cả hai mission ⇒ dữ liệu byte-identical từ 2026-07-17 03:37Z tới giờ. ROWS_MD5 phủ mọi cột qua to_jsonb(b), gồm cả date_updated.

OWNER_ACCEPTANCE = PASS   →  PASS_BALO_ONE_CLICK_SYNC_OWNER_ACCEPTED

3. PHẦN B — BẢO VỆ CƠ CHẾ = PASS

B1. Verifier dot-balo-one-click-guard (mới, read-only)

/opt/incomex/dot/bin/dot-balo-one-click-guard     commit c6a5579 (repo /opt/incomex/dot)
→ BALO_ONE_CLICK_GUARD|PASS|checks=37

Phủ toàn bộ 13 mắt xích: Nginx exact route → nuxt_backend (không rewrite) · Nuxt healthy · socket mount :ro · supplementary GID · cả hai role config + phải khớp nhau · compose contract · artifact (POST-only, có button, inheritance theo resolvedCollection === 'balo_thuc_the', không hardcode report URL) · systemd unit source==live hash · socket owner/mode 660 root:incomex-balo-sync · AF_UNIX-only, không TCP · runner --sync + singleton flock · bridge fixed-purpose BALO_SYNC_V1 · dry-run + verify delta=0 conflict=0 sequence_safe=true write=0.

Guard không: auto-fix, apply, gửi BALO_SYNC_V1, ghi DB, reload/restart.

B2. ★ Guard đã được chứng minh có khả năng BẮT LỖI (negative control)

Một guard chỉ biết nói PASS thì vô dụng. Đã ép nó fail bằng env override — production không bị đụng:

artifact PRE-BALO (không có route balo)  → HOLD|failed=3|artifact_post_missing,
                                                   artifact_button_missing,
                                                   artifact_inheritance_missing
unit source bị tamper                    → HOLD|failed=2|unit_drift_*.socket,unit_drift_*.service
sync group sai GID (kịch bản EACCES âm thầm) → HOLD|failed=3|sync_group_missing,
                                                   compose_group_add_missing,socket_owner
production thật                          → PASS|checks=37

B3. Ba bug trong guard của chính tôi — tự bắt, tự sửa

  1. awk range sập. /^ nuxt:/,/^ [a-z0-9_-]+:$/ — dòng nuxt: khớp cả hai vế ⇒ block co lại còn 1 dòng ⇒ báo HOLD giả cho compose.
  2. Grep chuỗi :ro không bao giờ khớp. docker compose config chuẩn hoá short form /run/incomex:/run/incomex:ro thành {source,target,read_only:true}.

Phát hiện vì HOLD của compose mâu thuẫn với PASS của runtime (container đang có mount + GID 987). Đã sửa sang --format json + jq. Vẫn giữ --no-interpolate để không render secret vào output guard.

  1. Check inheritance cho PASS giả. Bản đầu grep resolvedCollection|balo_thuc_the (OR) → artifact pre-balo cũngresolvedCollection ⇒ PASS sai. Đo thật: artifact pre-balo có 0 file chứa balo_thuc_the. Đã siết: nút và cổng collection phải nằm cùng một chunk.

B4. Canonical

knowledge/dev/laws-new/pg-read-pg/balo-thuc-the-quy-dinh.mdrevision 17, thêm §17 (không sửa tài liệu lịch sử):

  • 17.1 guard bắt buộc trước+sau mutation, PASS-sau là điều kiện.
  • 17.2 mười ba điều không được phá (gồm: không xoá exact route · chỉ docker compose up -d nuxt · cấm up -d trần · VPS=code SSOT · Secret Manager=credential SSOT kèm ngoại lệ login Directus · Git Safety Gate · disk-monitor report-only).
  • 17.3 hai bẫy đo đã trả giá (curl không kiểm được nút; so compose phải --no-interpolate + đọc JSON).
  • 17.4 sự thật workflow + đính chính KB về 244569412.

4. PHẦN C — WORKFLOW GITHUB = ALREADY_DISABLED

gh trên VPS chưa auth → nạp PAT từ Secret Manager (env-only, không ghi file, không vào history):

secret_name=gh_pat_sync_secrets version=latest access=PASS purpose=read/disable workflows payload=REDACTED

Không đoán tên — quét mọi workflow của cả hai repo theo tiêu chí "có ghi được nginx production không":

Repo Workflow ID State Ghi nginx?
web-test Deploy to VPS 233946923 disabled_manually (scp infra/nginx/conf.d/default.conf, 21 hits)
web-test Điều 31 VPS Setup 250152096 disabled_manually không
web-test Post-Deploy E2E 249989926 disabled_manually không
web-test Health Check 227044705 active không — chỉ curl, permissions: contents:read, không ssh/scp/rsync ⇒ an toàn
agent-data (11 workflow) active không cái nào chạm VPS/nginx

Bằng chứng nhân quả 11/07: deploy-vps.yml:86 scp infra/nginx/conf.d/default.conf lên VPS; lần chạy thành công cuối = 2026-07-11T02:47:26Z — đúng sự cố. Nếu bật lại, nó sẽ đắp nginx tháng 3 và xoá route balo.

★ Đính chính KB

agent-data "Vector-Audit" id=244569412  →  HTTP 404, KHÔNG TỒN TẠI
agent-data có đúng 11 workflow, không cái nào tên Vector-Audit, không cái nào chạm VPS

"Hai workflow nguy hiểm" là sai; chỉ có MỘT. Nó đã disabled từ 2026-07-15. Không disable gì trong mission này (không cần). Token đã unset; gh auth statusnot logged in; không có hosts.yml; WORKFLOW_TRIGGER=NO, GITHUB_PUSH=NO.


5. PHẦN D — Ổ ĐĨA

D1. Bằng chứng quyết định: prune theo giờ thu hồi ĐÚNG 0

[Fri Jul 17 06:00:01] CRITICAL: Disk usage 91% exceeds 85%
Total reclaimed space: 0B
[Fri Jul 17 06:00:05] Docker cleanup done. Usage: 91% → 91%
[Fri Jul 17 07:00:01] CRITICAL: Disk usage 91% exceeds 85%
Total reclaimed space: 0B
[Fri Jul 17 07:00:04] Docker cleanup done. Usage: 91% → 91%

docker system prune -f không đụng image có tag và không đụng volume — mà 42GB reclaimable nằm đúng ở hai chỗ đó. Nó chỉ đủ sức xoá container stopped ⇒ thu hồi 0, nhưng phá sạch bằng chứng runtime và mọi canary/blue-green trong ≤60′. Toàn hại, không lợi.

D2. disk-monitor → report-only (commit 7c60ccd)

PRE  sha256: 3222696cd3a6a889c135e1c615d225dd9814b41ae6187cec6d19e3ff65c26ae5
POST sha256: ca587f4a8610aa383c0dc2efc66974f328743d4ce4195a2d41aadb531b8581c0
backup     : /opt/incomex/backups/disk-monitor-20260717T053000Z/disk-monitor.sh.PRE  (sha khớp byte-exact)
             + crontab.root.PRE (103 dòng) + journald.conf.PRE

Giữ: monitor, ngưỡng 85, cron 0 * * * *. Bỏ: docker system prune -f. Thêm: snapshot đĩa cho operator + retention 30 bản (để chính monitor không thành vấn đề đĩa).

executable prune refs sau patch : 0
containers sau khi chạy thử     : 11 (không đụng)
cron                            : không đổi

Không tạo cleanup daemon mới.

D3. Cleanup có kiểm soát — 91% → 88% (freed 3.17 GB)

Việc Thu hồi Căn cứ
journal vacuum 3.9G → 1G + cap SystemMaxUse=1G 2.9 G "log/journal cũ theo retention hợp lý"
btmp + btmp.1 (failed-login, đã rotate) 73 M như trên
8 thư mục build tạm cũ (/tmp/*p10d*, d28-gmr-*, node-compile-cache; Apr–May) ~34 M "stale temporary build directories"
docker builder prune -f 0 B reclaimable đo được = 0

Hai thứ tôi định xoá nhưng đo xong thì KHÔNG:

  • context-pack.tmp (737M) — tên là "tmp" nhưng mtime 06:00 hôm nay, đang được sinh lại theo giờ ⇒ đang sống, không phải rác.
  • build cache 6.7 GB — docker system df báo reclaimable 0B (69/79 entry SHARED=true) ⇒ prune không được gì. Xoá nhầm sẽ chỉ làm build sau chậm.

D4. ★ HOLD_DISK_CAPACITY_OWNER_DECISION — và vì sao 85% là bất khả thi trong luật của chính lệnh

Còn 88%, cần ~3.2 GB nữa. Nhưng:

Images         39.66 GB tổng — RECLAIMABLE 32.67 GB (82%)  → cần `docker system prune -a`  = LỆNH CẤM
Local Volumes  10.37 GB tổng — RECLAIMABLE  9.58 GB (92%)  → cần `docker volume prune`     = LỆNH CẤM

42.25 GB reclaimable nằm trọn trong hai lệnh mà §II.2 cấm tuyệt đối. Mọi thứ an toàn còn lại đã dọn hết. ⇒ Không thể đạt ≤85% mà không vi phạm lệnh, nên HOLD đúng như §VIII.3 dự liệu.

5 mục lớn nhất + quyết định Owner cần cho:

# Mục Dung lượng Thu hồi được Mức an toàn Owner cần quyết
1 Docker images không dùng (36 image, 10 active) 39.7 G 32.7 G ⚠️ cần prune -a (đang cấm) hoặc docker rmi từng ID Cho phép rmi có chọn lọc? Phải giữ web-test/nuxt-ssr:latest 72715a92 (asset rollback)
2 /opt/incomex/backups 12 G ~4–8 G ⚠️ chứa dump Directus + rollback Cho xoá bộ dieu44_v0_3_* 2026-05-16 (6 bản, ~3.8 G, có bản closeout thay thế)?
3 Docker volumes (9, chỉ 1 active) 10.4 G 9.6 G ⚠️ cần volume prune (đang cấm) — rủi ro cao (PG/Directus data) Chỉ làm khi Owner chỉ đúng volume; tôi không đề xuất tự động
4 Build cache (79 entry) 6.7 G 0 B đo được ✅ vô hại Không làm gì
5 /opt/incomex/lark-backups 1.2 G ~1.2 G ⚠️ backup nghiệp vụ Còn cần không?

Không ảnh hưởng Balo: rollback §16 báo cáo trước là file + image có tag ⇒ prune-proof. Và giờ prune đã không còn chạy.

D5. Health sau cleanup

11/11 container running · nuxt/agent-data/postgres/directus/qdrant healthy · restarts=0
bridge socket+service = active/active

6. PHẦN E — SECRET = ROTATION_PROVIDER_OWNER_ACTION_REQUIRED

E1. ★ Rò rỉ rộng hơn báo cáo trước rất nhiều

Báo cáo trước chỉ biết 3 file nó tự tạo rồi shred. Đã xác nhận 3 file đó đã mất thật:

absent(shredded): /tmp/cfg.PRE.yml
absent(shredded): /tmp/cfg.POST.yml
absent(shredded): /opt/incomex/evidence/claude-balo-oneclick-.../compose-render-diff.txt

Nhưng quét toàn hệ (6 pattern, so khớp literal, không in giá trị) tìm ra 34 file khác chứa secret SỐNG:

Vùng Số file Loại secret
git HEAD /opt/incomex 2 DIRECTUS_ADMIN_PASSWORD plaintextscripts/reconcile-knowledge.py, scripts/reconcile-tasks.py
/opt/incomex/evidence 10 DIRECTUS_ADMIN_TOKEN, DB_PASSWORD (các dump docker inspect nhúng .Config.Env)
/opt/incomex/backups 12 OPENAI_API_KEY, QDRANT_LOCAL_API_KEY, DIRECTUS_ADMIN_TOKEN, DB_PASSWORD
/root 10 .bashrc (DIRECTUS_ADMIN_TOKEN), dump SQL Directus, script agent worktree

Trong đó backups/balo-oneclick-20260717T033701Z/agent-data-inspect.PRE.jsondo chính mission trước tạo làm asset rollback — chứa 4 loại secret (OpenAI + Qdrant + Directus token + DB password), tức nhiều hơn 3 file mà nó tưởng là toàn bộ vấn đề.

Sạch: shell history 0 · journal 0 · dot + nuxt-repo git 0 · mcp-writes (thư mục web-served công khai) 0không có phơi nhiễm ra internet.

Fingerprint (không đảo ngược, để Owner đối chiếu sau khi xoay):

OPENAI_API_KEY       len=164 fp=3c10fb779e9f
QDRANT_LOCAL_API_KEY len=27  fp=1a12501ff60a
DIRECTUS_ADMIN_TOKEN len=64  fp=4d1f0a095ab6
POSTGRES_PASSWORD    len=36  fp=04b709739862

E2. Việc tôi đã làm: siết quyền (an toàn, đảo ngược được, không xoá)

TRƯỚC: 18 file mode 644 + 4 file mode 600   (dir cha 755 ⇒ mọi user local đọc được secret sống)
SAU  : 22/22 file mode 600, world-readable còn 0
content sha (toàn bộ) TRƯỚC = SAU = 47bca6397266aa40   ⇒ CONTENT_UNCHANGED (rollback/backup nguyên vẹn)
bundle git (chứa history có secret) → 0600, thư mục → 0700
/root/.bashrc 644 nhưng /root là 700 ⇒ non-root không với tới

E3. Vì sao không xoá và không xoay

Xoá không đóng được lỗ. Key vẫn sống thì mọi bản sao chỉ là hệ quả; xoá dump backup/git-history còn phá rollback và cần rewrite history (destructive). Chỉ xoay key mới đóng.

★ Nhưng xoay key đang bị chặn — đo thật, không suy luận:

incomex-agent-data = consumer DUY NHẤT của OPENAI_API_KEY + QDRANT_LOCAL_API_KEY (4 env; kb/mcp = 0)
⇒ áp key mới bắt buộc phải recreate agent-data

RUNNING container : 10 env PG_*/DIRECTUS_DB_*  ·  mem 2684354560 (2560 MiB)
COMPOSE FILE      :  0 env PG_*/DIRECTUS_DB_*  ·  mem_limit 1536m

Container đang chạy được tạo từ cấu hình khác và giàu hơn compose file hiện tại. docker compose up -d agent-data sẽ recreate nó và mất cả 10 env + tụt mem 2560→1536 MiB ⇒ agent-data mất kết nối PG/Directus. §E2 của lệnh chỉ cho xoay khi "đủ quyền và zero-downtime"không zero-downtime. Sửa compose lệch thì §II.2 lại cấm (docker compose up -d trần). ⇒ HOLD, đúng luật.

Thêm nữa: xoay OPENAI_API_KEY cần dashboard OpenAI (Owner UI) — tôi chỉ có service account GCP.

Không revoke key cũ (đúng lệnh: không revoke trước khi key mới PASS).

E4. Owner action — theo thứ tự

  1. Reconcile compose agent-data (10 env + mem 2560) cho khớp runtime. Đây là nút chặn thật — chặn cả xoay key lẫn mọi restart agent-data về sau.
  2. Xoay OPENAI_API_KEY (dashboard OpenAI → Secret Manager version mới → recreate agent-data → smoke → revoke cũ).
  3. Xoay QDRANT_LOCAL_API_KEY (Qdrant local: key mới → config + agent-data → smoke → revoke). Làm từng provider, không đồng thời.
  4. Xoay DIRECTUS_ADMIN_PASSWORD + DIRECTUS_ADMIN_TOKEN, rồi gỡ 2 script khỏi git (scripts/reconcile-*.py) — xoay trước, gỡ sau.
  5. Quyết định về dump Directus trong backups/root (chứa secret; hiện đã 0600).

7. GIT SAFETY GATE = PASS

Repo

Repo Branch Pre-mutation HEAD Final HEAD Remote
/opt/incomex feat/s177-sprint1-round-a 7a9843da1d1da5d6b2eddbfd8b238a2e0ec8f320 7c60ccd9ce7fabeb71aab79e1cf27bf2a38be45d không có
/opt/incomex/dot main de4730013ca3cee5f55cb54c00fe51f1c2f1068b c6a55793461f812114c94c38e45edfb7b6978764 không có
/opt/incomex/docker/nuxt-repo main 2ea41583422427b6b5e6d886fa7a754198e121b7 2ea4158… (không đổi) github.com/Huyen1974/web-test (không push)

Mission commits (đường dẫn tường minh, không git add -A/.)

7c60ccd  fix(ops): make disk-monitor report-only; remove hourly docker system prune
         → scripts/disk-monitor.sh   (1 file, +20 −7)
c6a5579  feat(balo): add dot-balo-one-click-guard read-only chain verifier
         → bin/dot-balo-one-click-guard   (1 file, +261)

Mỗi commit: in staged list → đọc staged diff → git diff --cached --check CLEAN → secret scan SECRET=NONE.

Quyết định repo: guard vào /opt/incomex/dotbin/dot-balo-reconcile chỉ được track ở đó (root repo trả did not match any file(s) known to git). Hai repo lồng nhau nên chỗ này dễ đặt nhầm.

Baseline commit — không cần

Lệnh §III.2 yêu cầu commit baseline cho PREEXISTING_VALID_LIVE_BASELINE. Đo thật: đã có rồi — mission trước đã nhận live nginx+compose làm baseline tại 4ecb614, và HEAD 7a9843d đã chứa toàn bộ thay đổi Balo. Không có mission change nào chưa commit lúc vào. Tạo thêm commit baseline sẽ là commit rỗng vô nghĩa.

Phân loại working tree

MISSION_CHANGE                     : 0 còn lại (đã commit hết)
PREEXISTING_VALID_LIVE_BASELINE    : 0 (đã xử lý ở mission trước, commit 4ecb614)
UNRELATED_WORK (giữ nguyên, không stage, không sửa, không xoá):
  /opt/incomex : claude-kb/docker-compose.claude-kb.yml · dot/bin/dot-dot-health ·
                 dot/bin/dot-vector-audit · lark-client/** (9 file) ·
                 scripts/smoke-test.sh · scripts/test-mcp-connectivity.sh + untracked
  /opt/incomex/dot : untracked (iu-cutter*, scanners/, specs/, sql/sua-re/ …)
  nuxt-repo    : web-rp-current (untracked)
UNKNOWN                            : 0

Chứng minh không thuộc deployment path Balo: đường Balo gồm docker/nginx/conf.d/default.conf, docker/docker-compose.yml, nuxt-repo/web/**, dot/bin/dot-balo-reconcile*, dot/ops/systemd/**. Không file dirty nào nằm trong đó ⇒ §III.4 cho phép còn dirty.

Bundles — verify PASS toàn bộ

PRE-MUTATION
  incomex-root-postaccept-7a9843d-20260717T053000Z.bundle
    sha256 72b8956cc2fb050606655da99daac6ddd4f7122a693523cd62fdc161558c6a80  11378945 B
  dot-postaccept-de47300-20260717T053000Z.bundle
    sha256 42c67ff009ccab2f2ce9887c4ba5cddcfbfe33e4fb21925e6a98fdacfd875b05    863378 B
  nuxt-repo-postaccept-2ea4158-20260717T053000Z.bundle
    sha256 feee3a1255c88d69529894a5d30bc885d3e5a1752eaf0a6a80e3a39f583f536e   8303631 B

POST-MUTATION
  incomex-root-postaccept-final-7c60ccd-20260717T060000Z.bundle
    sha256 3d233b6d6762a691c42b8fd5cad5a48624b00c00d42c83336e82d8386382d24b  11380314 B
  dot-postaccept-final-c6a5579-20260717T060000Z.bundle
    sha256 20737ac7cb1219b5028cabec7aab9496aa7e79d83863156ef1a9601d7b963c51    868966 B

git bundle verify: "The bundle records a complete history"  → 5/5 PASS
thư mục /opt/incomex/artifacts/git-bundles = 0700, bundle = 0600

⚠️ Ngoại lệ tồn đọng của Gate (nói thẳng)

Điều kiện §III.4 "không có secret trong commit/bundle/evidence" không thoả tuyệt đối: bundle incomex-root-* chứa scripts/reconcile-*.pyDIRECTUS_ADMIN_PASSWORD. Đây là history có sẵn từ trước, không phải commit của mission này (2 commit của tôi đã scan sạch). Tôi không rewrite history (destructive, và phải xoay key trước). Đã giảm thiểu: bundle 0600, thư mục 0700. Theo dõi ở SECRET_EXPOSURE.

Rollback (bền vững — file + image có tag, prune-proof)

# disk-monitor (mission này)
cp -a /opt/incomex/backups/disk-monitor-20260717T053000Z/disk-monitor.sh.PRE \
      /opt/incomex/scripts/disk-monitor.sh
git -C /opt/incomex reset --hard 7a9843d          # bỏ 7c60ccd

# guard (mission này)
git -C /opt/incomex/dot reset --hard de47300      # bỏ c6a5579
rm -f /opt/incomex/dot/bin/dot-balo-one-click-guard

# journald cap
cp -a /opt/incomex/backups/disk-monitor-20260717T053000Z/journald.conf.PRE \
      /etc/systemd/journald.conf && systemctl restart systemd-journald

# Balo (từ mission trước, còn nguyên hiệu lực)
TS=20260717T033701Z   # nginx: default.conf.PRE → nginx -t → reload
                      # nuxt : nuxt-output.bak.pre-balo-$TS + docker-compose.yml.PRE → up -d nuxt
# image rollback: web-test/nuxt-ssr:latest 72715a92885c (CÓ TAG ⇒ prune không xoá)

# repo từ bundle
git clone /opt/incomex/artifacts/git-bundles/<bundle> <dir>

Rollback dữ liệu: không cần, không được tự làm (write=0).


8. KIỂM TRA TỔNG CUỐI (§XI)

BALO_ONE_CLICK_GUARD|PASS|checks=37

incomex-nuxt        running healthy   restarts=0
incomex-nginx       running           restarts=0   Pid=1219625   ← KHÔNG đổi
incomex-agent-data  running healthy   restarts=0
postgres            running healthy   restarts=0
incomex-directus    running healthy   restarts=0
incomex-qdrant      running healthy   restarts=0
bridge socket/service = active/active

AGENT DATA ZERO-TOUCH (== PRE của mission trước, 4/4):
  ID        4220cc88116e00684fb73aab2be277649359a742ea092c76998f0195f2db60e5   ✅
  StartedAt 2026-05-19T08:26:16.229327572Z                                     ✅
  Mem       2684354560 (2560 MiB)                                              ✅
  PG_/DIRECTUS_DB_ env = 10                                                    ✅

BALO: delta=0 · conflict=0 · sequence_safe=true · write=0
      fingerprint 2148|1|2148|8ed1cd5d2f93f5f8a970497bd5e4ec87|2148|t|1  (trước=sau)
      VERIFY|PASS_RECONCILED|delta=0|write=0

/                              200      /reports/report-balo-table   200
/reports                       200      /api/registries-pivot/summary 200
/api/health (Agent Data)       200
GET  /api/balo/reconcile       404      ← POST-only, đúng
POST /api/balo/reconcile       401      ← không session, fail-closed, đúng

DISK 91% → 88%   (freed 3.17 GB)   executable prune refs = 0
OTHER_SERVICE_RECREATE=NO      COMPOSE_UP_BARE=NO        WORKFLOW_TRIGGER=NO
GITHUB_PUSH=NO                 DB_WRITE=NO               PG_TEST_OBJECT=NO
DESTRUCTIVE_CRON=NO            BRIDGE_TCP=NO             DOCKER_SOCKET_MOUNT=NO
SECRET_IN_LOG_OR_EVIDENCE=NO   BACKUP_DELETED=NO         CHECKPOINT_DELETED=NO
gh token persisted=NO (env-only; gh auth status = not logged in; không có hosts.yml)
VPS source/config live == commit đã báo cáo: YES

9. EVIDENCE / ĐƯỜNG DẪN

/opt/incomex/evidence/claude-balo-postaccept-20260717T053000Z/   (0700)
    balo-baseline.PRE.txt · balo-baseline.POST.txt   ← diff rỗng
    dryrun.PRE.txt · verify.PRE.txt · verify.POST.txt

/opt/incomex/backups/disk-monitor-20260717T053000Z/
    disk-monitor.sh.PRE (3222696c… byte-exact) · crontab.root.PRE (103 dòng) · journald.conf.PRE

/opt/incomex/artifacts/git-bundles/   (0700, bundle 0600)   ← 5 bundle verify PASS

/opt/incomex/dot/bin/dot-balo-one-click-guard                 ← verifier mới
/opt/incomex/logs/disk-snapshots/                             ← snapshot report-only

Commits (local-only, không push):  7c60ccd (/opt/incomex) · c6a5579 (/opt/incomex/dot)
KB: balo-thuc-the-quy-dinh.md → revision 17 (§17 mới)

Secret Manager đã dùng (payload REDACTED toàn bộ):

secret_name=gh_pat_sync_secrets  version=latest  access=PASS  purpose=đọc/kiểm workflow  payload=REDACTED
(không đọc payload OPENAI_API_KEY / QDRANT_LOCAL_API_KEY — chỉ metadata, đúng §IV.2)

10. OWNER CÒN PHẢI QUYẾT

# Việc Vì sao cần Owner
1 Reconcile compose agent-data (10 env PG_*/DIRECTUS_DB_* + mem 2560) Nút chặn thật. Chặn xoay key mọi restart agent-data. Sửa cần đụng compose ⇒ ngoài phạm vi lệnh này
2 Xoay OPENAI_API_KEY Cần dashboard OpenAI (Owner UI) + recreate agent-data (chặn bởi #1)
3 Xoay QDRANT_LOCAL_API_KEY Cần recreate agent-data (chặn bởi #1)
4 Xoay DIRECTUS_ADMIN_PASSWORD/TOKEN rồi gỡ scripts/reconcile-*.py khỏi git Secret trong git history; xoay trước, gỡ sau
5 Đĩa 88% → ≤85% 42.25 GB reclaimable nằm trọn trong prune -a / volume prunehai lệnh bị cấm. Cần Owner cho phép docker rmi chọn lọc (giữ nuxt-ssr:latest 72715a92) hoặc xoá backups/dieu44_v0_3_*
6 Xác nhận §15 canonical v1.9 §15 vốn cấm "nút UI đồng bộ Balo"; đã sửa tường minh ở rev16. Nút đang LIVE — nếu Owner không muốn bãi bỏ thì phải nói ngay
7 Giữ Deploy to VPS 233946923 disabled Bật lại = đắp nginx tháng 3, xoá route balo, tái hiện 11/07

11. NHỮNG ĐIỀU PHẢI NÓI THẲNG

  1. KB sai về workflow. agent-data Vector-Audit 244569412 không tồn tại (404). "Hai workflow nguy hiểm" thực chất một. Nếu tôi tin KB và đi "disable cái thứ hai", tôi đã đuổi theo bóng ma. Đã đính chính rev17 §17.4.
  2. Báo cáo trước đánh giá thấp rò rỉ secret. Nó kết luận "rủi ro thấp, đã shred 3 file". Thực tế 34 file chứa secret sống, gồm 2 file trong git HEAD — và một trong số đó là asset rollback do chính nó tạo, chứa 4 loại secret. Không phải lỗi mission trước gây ra (có từ trước), nhưng phạm vi thì rộng hơn hẳn.
  3. Guard của tôi ban đầu có 3 bug — 2 HOLD giả (awk range sập, grep :ro không khớp vì compose chuẩn hoá) và 1 PASS giả (check inheritance dùng OR). Bắt được vì compose HOLD mâu thuẫn với runtime PASS, và vì chạy negative control. Nếu chỉ chạy happy path, cái PASS giả đã lọt.
  4. 85%cấm prune -a/volume prune mâu thuẫn nhau. 42.25/88 GB nằm đúng trong hai lệnh bị cấm. Tôi chọn tuân lệnh cấm và HOLD, thay vì lách.
  5. Xoá bản sao secret là an ủi hão khi key còn sống, và sẽ phá rollback/backup. Tôi siết quyền (đảo ngược được, content không đổi) thay vì xoá, và nói rõ chỉ xoay key mới đóng được — mà xoay đang bị chặn bởi compose lệch. Đây là kết luận đo được, không phải né việc.
  6. Ngoại lệ Gate: bundle chứa history có secret (có sẵn từ trước). Không rewrite history. Đã hạ xuống 0600 và ghi vào sổ.

12. TRẠNG THÁI CUỐI

OWNER_ACCEPTANCE=PASS                    BALO_PROTECTION_GUARD=PASS (37/37)
UNSAFE_GITHUB_WORKFLOWS=ALREADY_DISABLED DISK_STABILIZATION=HOLD_DISK_CAPACITY_OWNER_DECISION
SECRET_EXPOSURE=ROTATION_PROVIDER_OWNER_ACTION_REQUIRED
VPS_SSOT_GIT_SAFETY=PASS (1 ngoại lệ tồn đọng, §7)

PASS_BALO_POST_ACCEPTANCE_PROTECTED_AND_STABILIZED

Hai phần HOLD (đĩa, secret) độc lậpkhông ảnh hưởng Balo: guard PASS, rollback prune-proof, dữ liệu write=0. Không rollback phần nào đã PASS.

DỪNG SAU BÁO CÁO. Không chuyển sang tính năng mới, không làm report "Balo gần đây".

Back to Knowledge Hub knowledge/current-state/reports/claude-balo-post-acceptance-protection-and-vps-stabilization-2026-07-17.md