Claude — Balo post-acceptance: bảo vệ cơ chế + ổn định VPS (2026-07-17)
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:
- 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. - "Hai workflow nguy hiểm" thực ra là MỘT —
agent-data Vector-Audit 244569412ghi 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. - 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_PASSWORDplaintext) — 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_TOKEN là token, 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 POST → rỗng. --verify → VERIFY|PASS_RECONCILED|delta=0|write=0.
Ghi chú đo:
DATE_UPDATED_HASHcủa tôi (41c6957d…) khác số trong báo cáo trước (ae419898…) vì công thức khác (tôiORDER BY stt— PK thật làstt, không phảiid), 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_MD5phủ mọi cột quato_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
- awk range sập.
/^ nuxt:/,/^ [a-z0-9_-]+:$/— dòngnuxt:khớp cả hai vế ⇒ block co lại còn 1 dòng ⇒ báo HOLD giả cho compose. - Grep chuỗi
:rokhông bao giờ khớp.docker compose configchuẩn hoá short form/run/incomex:/run/incomex:rothà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.
- Check inheritance cho PASS giả. Bản đầu grep
resolvedCollection|balo_thuc_the(OR) → artifact pre-balo cũng córesolvedCollection⇒ PASS sai. Đo thật: artifact pre-balo có 0 file chứabalo_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.md → revision 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ấmup -dtrầ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á (
curlkhô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 | ✅ CÓ (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 status → not 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%
Vì 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 dfbáo reclaimable 0B (69/79 entrySHARED=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 plaintext — scripts/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.json — do 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) 0 ⇒ khô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ự
- 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. - Xoay
OPENAI_API_KEY(dashboard OpenAI → Secret Manager version mới → recreate agent-data → smoke → revoke cũ). - 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. - Xoay
DIRECTUS_ADMIN_PASSWORD+DIRECTUS_ADMIN_TOKEN, rồi gỡ 2 script khỏi git (scripts/reconcile-*.py) — xoay trước, gỡ sau. - 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/dotvìbin/dot-balo-reconcilechỉ đượ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-*.py có DIRECTUS_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 và 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 prune — hai 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
- KB sai về workflow.
agent-data Vector-Audit 244569412khô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. - 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.
- Guard của tôi ban đầu có 3 bug — 2 HOLD giả (awk range sập, grep
:rokhô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. 85%vàcấm prune -a/volume prunemâ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.- 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.
- 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ập và khô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".