KB-7D62 rev 8

Claude — Khảo sát hậu di chuyển: Service / Secret / Control-plane Reconciliation (2026-07-17)

59 min read Revision 8
current-statesurveyread-onlyforensicreconciliationsecret-rotationcontrol-planenormative-driftvps-ssotgcpgithub-workflowsgdrive-backup2026-07-17READY_AFTER_OWNER_NORMATIVE_DECISION

Khảo sát hậu di chuyển — Service / Secret / Control-plane Reconciliation (2026-07-17)

Mission: forensic READ-ONLY. Không sửa, không xoay secret, không cleanup, không deploy. Tác giả: Claude (Opus 4.8). Host: vmi3080463 / 38.242.240.89 (Contabo). Mọi giá trị secret trong báo cáo này = REDACTED. Không ghi password/token/key/hash-xác-thực.


1. EXECUTIVE VERDICT

READY_AFTER_OWNER_NORMATIVE_DECISION

Khảo sát đủ để mission sau xử lý theo từng service mà không phải đoán. Không HOLD, vì không còn critical unknown nào buộc phải đoán khi mutation: mọi service đều xác định được source/config, credential consumer và data-volume owner; cả 5 GitHub workflow có khả năng ghi VPS đều đang disabled_manually; đích và quyền của backup Drive đã xác định.

Nhưng cần Owner quyết trước Wave A vì 3 việc vượt thẩm quyền agent:

  1. normative_registry thiếu entry cho nút Balo đang LIVE — 47 rows, 0 row nhắc balo/one-click/reconcile (đo thật). Canonical rev17 §16 đã bãi bỏ phần cấm của §15, nhưng registry chưa phản ánh. Đã soạn sẵn proposed entry ở §9 — CHƯA ghi.
  2. Backup Drive = ACTIVE_SECRET_EXFILTRATION_PATH — secret rời VPS 4 lần/ngày (Drive) + 2 lần/ngày (GitHub force-push). Đây là chính sách, Owner phải quyết.
  3. GCP giữ 11 SA key không hạn dùng, 3 SA có roles/owner — quyết định thu hồi là của Owner.

2 đính chính bắt buộc với KB/memory + 1 mục KB ĐÚNG (tôi từng đính chính nhầm)

⚠️ Tự bác bỏ: rev1 của báo cáo này ghi "rò rỉ chỉ 5 file, KB sai" — chính tôi sai, KB đúng. Đã đo lại và sửa ở §6.1. Giữ lại dòng này để mission sau không tin nhầm rev1.

# KB/memory đang ghi Sự thật đo được Bằng chứng
1 workflow 244569412 KHÔNG tồn tại; "chỉ MỘT workflow nguy hiểm" TỒN TẠIVector Audit & Cleanup trong repo agent-data-test (không phải web-test, nên tra nhầm repo → 404). Và có 5 workflow ghi được VPS, không phải 1 gh api repos/Huyen1974/agent-data-test/actions/workflows/244569412 → 200
2 rò rỉ secret 34 file KB VỀ CƠ BẢN ĐÚNG — đây KHÔNG phải đính chính. Đo lại chuẩn (grep -rlF <value> cả 2 cây): DIRECTUS_ADMIN_PASSWORD = 19 file (13 /opt/incomex + 6 /root); DIRECTUS_ADMIN_TOKEN = 34 file (30 + 4); UNION = 46 file. Con số "34" của KB khớp đúng số file chứa TOKEN. Phần "2 file trong git HEAD" = ĐÚNG. (Bản rev1 của chính báo cáo này từng ghi sai "5 file" — do subagent grep hẹp phạm vi; đã tự bác bỏ và đo lại.) grep -rlF cả /opt/incomex + /root
3 Host 16 GB / 8 CPU 11 GiB RAM / 6 vCPU (AMD EPYC) free -h, nproc, lscpu

Phát hiện mới nghiêm trọng nhất (chưa từng có trong KB)

docker compose up -d agent-data sẽ chạy image SAI 4 tháng tuổi, im lặng, không báo lỗi. AGENT_DATA_IMAGE không được set ở bất kỳ đâu (.env: 0 hit; không có env_file; shell env: 0) → compose dùng fallback asia-southeast1-docker.pkg.dev/.../agent-data-test:latest. Image đó tồn tại trong cache local (9421cd9c4303, 4 tháng tuổi) → không pull, không fail, chỉ chạy sai. Cộng thêm mất 10 biến PG và tụt RAM 2560→1536 MiB. Đây là mìn nguy hiểm hơn mọi thứ KB đang theo dõi.


2. HOST MEASURED FACTS (FACT — đo, không nhận từ Owner)

Kernel      Linux 6.8.0-90-generic Ubuntu, x86_64
Uptime      154 ngày (từ ~2026-02-13)
CPU         6 vCPU  AMD EPYC (1 socket × 6 core × 1 thread)   ← KHÔNG phải 8
RAM         11 GiB total | 4.5 GiB used | 3.5 GiB free | 4.2 GiB buff/cache | 7.2 GiB available
Swap        2.0 GiB total | 652 MiB used
Disk /      96 G | 84 G used | 12 G avail | 88%           /dev/sda1
Inode /     12.976M | 1.063M used | 9%                    (không phải bottleneck)
Docker      Server 29.2.1 — Storage Driver: overlayfs (io.containerd.snapshotter.v1)
PSI mem     some avg300=0.00  full avg300=0.00            (không có memory pressure hiện tại)
PSI cpu     some avg300=0.90
PSI io      some avg300=0.66  full avg300=0.59            (io pressure đáng kể, không nguy cấp)

Tổng mem_limit khai báo runtime = 2 (pg) + 1 (qdrant) + 1 (directus) + 2.5 (agent-data) + 0.5 (nuxt) + 0.25 (nginx) + 0.25 (claude-mcp) + 0.25 (claude-kb) + 0.5 (executor) = 8.25 GiB / 11 GiB; uptime-kumapg-restore-test không giới hạn.

Bằng chứng áp lực (FACT): postgresState.OOMKilled = true với RestartCount = 0 → một backend process trong cgroup đã bị OOM-kill mà main process không chết. Đây là bằng chứng thật cho việc mem_limit 2g của postgres đã từng chạm trần. Chưa đủ để đề xuất sizing lại (chưa đo tải đủ dài) — theo đúng §5.3.

⚠️ Storage driver quirk (FACT): vì dùng containerd snapshotter, GraphDriver.Data.UpperDir = nullkhông đọc được upperdir từ host. Con số docker ps -s bên dưới là số Docker báo, chưa đối chiếu được bằng du. Mission sau muốn xác minh phải dùng nerdctl/ctr hoặc docker exec du (exec = spawn process, cân nhắc read-only policy).


3. MASTER SERVICE RECONCILIATION MATRIX

13 dòng = 11 container + 2 host component. Cột theo §5 + cột GIT / VPS-SSOT STATUS bổ sung.

# SERVICE / COMPONENT CURRENT ROLE ERA RUNTIME SoT DECLARED SOURCE REPRODUCIBILITY RUNTIME–DECL DRIFT RESOURCE CONTRACT SECRET CONSUMERS MUTATION SURFACES CANONICAL/NORMATIVE IMAGE/STORAGE GIT / VPS-SSOT BACKUP RISK NEXT ACTION
1 incomex-agent-data Knowledge API (FastAPI) MIXED docker inspect docker/docker-compose.yml svc agent-data DRIFT_BLOCKS_SAFE_RECREATE 3 drift: (a) image agent-data-local:latest vs fallback AR agent-data-test:latest; (b) 10 biến PG_/DIRECTUS_DB_ có ở runtime, compose KHÔNG khai; (c) mem 2560 MiB vs compose 1536m run 2560 MiB / decl 1536m / .env AGENT_DATA_MEM_LIMIT=2560m không được compose tham chiếu API_KEY,OPENAI_API_KEY,QDRANT_API_KEY,DIRECTUS_ADMIN_TOKEN,PG_PASSWORD,DIRECTUS_DB_PASSWORD,GOOGLE_APPLICATION_CREDENTIALS(file) compose; deploy-vps.yml(233947089, disabled); s109-pg-setup.yml(244536611, disabled); vector-audit.yml(244569412, disabled) DRIFT agent-data-local:latest 1.51 GB; bind credentials/google-credentials.json:ro; không volume compose TRACKED_CLEAN; .env GITIGNORED → OUTSIDE_GIT_BACKUP_REQUIRED code: có (repo); runtime env: KHÔNG 🔴 CRITICAL Wave B — vá compose (10 env + mem ref + image pin) TRƯỚC mọi recreate
2 incomex-agent-api-executor DOT API executor GITHUB→VPS docker inspect KHÔNG có trong compose hiện tại (0 hit) nhưng container mang label compose project=docker, service=agent-api-executor NOT_COMPOSE_MANAGED (orphan có nhãn) Toàn phần — service đã bị xoá khỏi compose ở 4ecb614; container vẫn chạy từ 2026-06-04 run 512 MiB / không khai báo OPENAI_API_KEY ROLLBACK-executor-docker-run.sh (UNTRACKED_LIVE); docker run tay MISSING_COVERAGE agent-api-executor-local:v1 180 MB; no mount UNTRACKED_LIVE_CONFIG (script rollback ngoài Git) không 🔴 CRITICALdocker compose up -d --remove-orphans XOÁ container này Wave A — quyết: đưa lại vào compose hay commit script + cấm --remove-orphans
3 incomex-nginx Reverse proxy VPS_SSOT docker inspect compose svc nginx DRIFT_BLOCKS_SAFE_RECREATE (a) mount thứ 5 docs/mcp-writes/ui-preview → /usr/share/nginx/mcp-writes:ro KHÔNG có trong compose; (b) attach thêm claude_mcp_net run 256 MiB = decl 256m ✅ nginx/secrets (bind ro); /etc/letsencrypt compose; deploy-vps.yml 233946923 (disabled) ghi đè conf.d/default.conf + nginx -s reload DRIFT nginx:alpine; 4 bind decl + 1 bind undecl conf.d/default.conf TRACKED_CLEAN sha bd38d294… code có 🔴 HIGH — recreate ⇒ /ui-preview/ 404 câm Wave B — thêm mount + network vào compose
4 postgres DB chính (directus/workflow/metadata) GCP→VPS docker inspect compose svc postgres PARTIAL_PARITY (a) runtime POSTGRES_DB/USER = workflow/workflow_admin (từ .env), compose khai ${PG_DATABASE:-directus}/${PG_USER:-directus}PG_DATABASE/PG_USER không tồn tại trong .env → recreate đổi sang directus/directus (vô hại vì PGDATA đã init, initdb bị bỏ qua); (b) attach thêm claude_mcp_net run 2 GiB = decl 2g ✅ · OOMKilled=true POSTGRES_PASSWORD,PG_PASSWORD compose; ~20 cron docker exec postgres psql (WRITE thật); deploy-vps.yml SQL migrations DRIFT (network) postgres:16; bind /opt/workflow/postgres/{data,backups,init}; writable layer 4.54 GB (chưa xác minh) bind ngoài Git → OUTSIDE_GIT_BACKUP_REQUIRED pg-backup.sh 02:27 + nightly Drive 🟠 MED Wave B — pin POSTGRES_DB/USER, thêm network
5 incomex-directus Headless CMS GCP→VPS docker inspect compose svc directus PARTIAL_PARITY attach thêm claude_mcp_net (alias directus) — compose chỉ khai incomex run 1 GiB = decl 1g ✅ ADMIN_PASSWORD,ADMIN_EMAIL,KEY,SECRET,DB_PASSWORD,AGENT_DATA_API_KEY compose; Directus admin UI; Flows DRIFT (network) directus/directus:11.5; bind uploads,extensions; writable layer 2.64 GB (chưa xác minh) bind ngoài Git → OUTSIDE_GIT_BACKUP_REQUIRED uploads: nightly Drive 🟠 MED Wave B — thêm claude_mcp_net vào compose
6 incomex-nuxt Frontend SSR + Balo button MIXED docker inspect compose svc nuxt CONFIG_PARITY_PASS_RECREATE_NOT_TESTED Không drift — 17/17 env khớp; group_add 987 khớp; 2 mount khớp; image = đúng fallback run 512 MiB = decl 512m ✅ NUXT_DIRECTUS_SERVICE_TOKEN,NUXT_AGENT_DATA_API_KEY,RP_PG_PASSWORD compose; deploy-vps.yml 233946923 (disabled); bind deploys/nuxt-output DRIFT — nút LIVE nhưng normative_registry 0 entry image AR-tag nuxt-ssr:latest 72715a92 246 MB (4 tháng — chỉ là base; code thật ở bind .output) compose TRACKED_CLEAN code có; .output là artifact rebuild được 🟡 LOW-MED Wave A — an toàn nhất để reconcile độc lập
7 incomex-qdrant Vector DB GCP→VPS docker inspect compose svc qdrant CONFIG_PARITY_PASS_RECREATE_NOT_TESTED Không drift run 1 GiB = decl 1g ✅ · user 0:0 (root) QDRANT__SERVICE__API_KEY compose; vector-audit.yml 244569412 (disabled, auto_heal xoá vector) ALIGNED qdrant/qdrant:latest; bind qdrant/data; writable layer 14.6 GB — lớn nhất, chưa xác minh bind ngoài Git → OUTSIDE_GIT_BACKUP_REQUIRED qdrant-backup.sh 03:00 UTC 🟠 MED — :latest không pin Wave B — xác minh 14.6 GB trước recreate
8 incomex-claude-mcp MCP server (Claude) VPS_SSOT docker inspect claude-mcp/docker-compose.claude-mcp.yml CONFIG_PARITY_PASS_RECREATE_NOT_TESTED Không drift run 256 MiB / 0.5 CPU = decl ✅ PG_USER_RO,PG_PASSWORD_RO,MCP_UPSTREAM_KEY,DIRECTUS_TOKEN compose ALIGNED claude-mcp-local:phase1a; mount /var/run/docker.sock compose TRACKED (repo con) code có 🟠 MED-HIGH:ro trên unix socket KHÔNG chặn Docker API write ⇒ có toàn quyền Docker Wave A — ghi nhận capability; cân nhắc socket-proxy
9 incomex-claude-kb KB proxy VPS_SSOT docker inspect claude-kb/docker-compose.claude-kb.yml CONFIG_PARITY_PASS_RECREATE_NOT_TESTED Không drift run 256 MiB / 0.5 CPU = decl ✅ AGENT_DATA_API_KEY compose ALIGNED claude-kb-local:v1 210 MB compose TRACKED code có 🟢 LOW Wave A
10 uptime-kuma Monitoring VPS_SSOT docker inspect không compose, không label NOT_COMPOSE_MANAGED Toàn phần không mem_limit .uptime-kuma-admin-pass (plaintext file) docker run tay MISSING_COVERAGE louislam/uptime-kuma:2; bind /opt/incomex/uptime-kuma; volume uptime-kuma-data 1.12 MB mồ côi (đã chuyển sang bind) ngoài Git ⚠️ chưa xác minh 🟡 LOW Wave D — khai báo compose
11 pg-restore-test-20260520T031054Z Rác drill còn chạy 8 tuần VPS_SSOT docker inspect ❌ không NOT_COMPOSE_MANAGED Toàn phần · restart=no không mem_limit POSTGRES_PASSWORD thủ công MISSING_COVERAGE postgres:16; volume 46dbcc89… 788 MB — volume DUY NHẤT còn active ngoài Git n/a (chính nó là artifact drill) 🟠 MED — chiếm RAM/disk, giữ secret Wave D — Owner xác nhận rồi mới xoá
12 incomex-balo-reconcile (.socket+.service) Bridge Balo one-click VPS_SSOT systemctl cat /etc/systemd/system/ CONFIG_PARITY_PASS_RECREATE_NOT_TESTED Không drift · active+enabled n/a (không env secret) dot-balo-reconcile --sync DRIFT — LIVE, Owner-accepted, canonical §16 OK, registry 0 entry ExecStart=/opt/incomex/dot/bin/dot-balo-reconcile-bridge; socket 0660 root:incomex-balo-sync (gid 987) unit ngoài Git → OUTSIDE_GIT_BACKUP_REQUIRED ⚠️ unit chưa backup 🟡 LOW (hardened: ProtectSystem=strict, RestrictAddressFamilies=AF_UNIX, NoNewPrivileges) Wave A — Owner duyệt proposed entry §9
13 lark-mcp-remote.service Lark MCP remote VPS_SSOT systemctl /etc/systemd/system/ UNKNOWN chưa đối chiếu n/a S177_LARK_MCP_REMOTE_TOKEN (secret DUY NHẤT từng xoay, v2 2026-05-23) systemd UNKNOWN n/a ngoài Git ⚠️ 🟡 LOW Wave E

3.1. Chi tiết drift agent-data (bằng chứng)

RUNTIME  mem_limit = 2684354560 B = 2560 MiB
COMPOSE  mem_limit: 1536m                      (hardcode, KHÔNG dùng ${AGENT_DATA_MEM_LIMIT})
.env     AGENT_DATA_MEM_LIMIT=2560m            ← khớp CHÍNH XÁC runtime ⇒ đây là nguồn gốc drift

RUNTIME env có 10 biến compose KHÔNG khai:
  PG_HOST PG_PORT PG_USER PG_DATABASE PG_PASSWORD
  DIRECTUS_DB_HOST DIRECTUS_DB_PORT DIRECTUS_DB_USER DIRECTUS_DB_DATABASE DIRECTUS_DB_PASSWORD
  → giả thuyết §5.2 của mission: XÁC NHẬN ĐÚNG, đúng 10 biến.
  → .env chỉ có PG_PASSWORD; 9 biến còn lại KHÔNG có nguồn khai báo nào
     ⇒ container được khởi tạo NGOÀI compose hiện hành (StartedAt 2026-05-19, compose sửa 2026-07-17).

IMAGE   runtime  agent-data-local:latest              (9acd60503290, 8 tuần)
        compose  ${AGENT_DATA_IMAGE:-asia-…/agent-data-test:latest}
        AGENT_DATA_IMAGE: .env=0 hit · env_file=none · shell env=0  ⇒ FALLBACK ÁP DỤNG
        fallback image TỒN TẠI local (9421cd9c4303, 4 tháng)  ⇒ KHÔNG pull, KHÔNG fail, CHẠY SAI IM LẶNG

Hệ quả (INFERENCE, độ tin cậy cao): docker compose up -d agent-data hôm nay ⇒ image sai 4 tháng + mất 10 biến DB + RAM 1536m. Service gần như chắc chắn mất kết nối DB. Đây là blocker thật của việc xoay secret PG/Directus.


4. UNASSIGNED STORAGE INVENTORY

docker system df: Images 36 (10 active) 39.66 GB, reclaimable 32.67 GB (82%) · Containers 11 → 21.87 GB · Volumes 9 (1 active) 10.37 GB, reclaimable 9.58 GB (92%) · Build cache 79 → 6.697 GB, reclaimable 100%.

du:  /var/lib/containerd  37 G   ← lớn nhất hệ thống (containerd snapshotter)
     /var/lib/docker      11 G   (volumes 9.7G · containers 542M · buildkit 191M)
     /opt/incomex         17 G   (backups 12G · deploys 937M · context-pack.tmp 739M · lark-backups 1.2G)
     /opt/workflow        3.4 G  (PGDATA)
     /root/backups        1.9 G
     /var/log             1.2 G  (journal 1016.7 M)
RESOURCE TYPE SIZE CREATED KNOWN CONSUMER PROTECTION RECLAIM? CONF EVIDENCE
46dbcc89… volume 788 MB 2026-05-20 pg-restore-test (ĐANG CHẠY) ACTIVE (drill) ❌ không, khi container còn sống HIGH docker volume inspect
pg-dry-run-hb05-2026-05-15-data volume 1.29 GB 2026-05-15 none STALE_CANDIDATE ✅ có HIGH volume ls -qf dangling
pg-dry-run-v0.2-phase-alpha-2026-05-16-data volume 1.344 GB 2026-05-16 none STALE_CANDIDATE ✅ có HIGH idem
pg-dry-run-v0.2-p0-2-2026-05-16-data volume 1.344 GB 2026-05-16 none STALE_CANDIDATE ✅ có HIGH idem
32b405542e7d… volume 1.389 GB 2026-05-17 none (anonymous) ORPHAN_CANDIDATE ✅ có MED idem
5838dcf66252… volume 1.394 GB 2026-05-17 none ORPHAN_CANDIDATE ✅ có MED idem
d2e14fa9f623… volume 1.394 GB 2026-05-17 none ORPHAN_CANDIDATE ✅ có MED idem
886272a48d3e… volume 1.425 GB 2026-05-17 none ORPHAN_CANDIDATE ✅ có MED idem
uptime-kuma-data volume 1.12 MB 2026-02-16 none — uptime-kuma đã chuyển sang bind /opt/incomex/uptime-kuma STALE_CANDIDATE ⚠️ đọc trước khi xoá (có thể là data cũ) MED docker inspect uptime-kuma
asia-…/agent-data/agent-data-test:latest image 1.51 GB 4 tháng KHÔNG container nào — nhưng là fallback của compose 🔴 BẪY — xoá thì compose up fail; giữ thì compose up chạy sai ⚠️ chỉ sau khi pin AGENT_DATA_IMAGE HIGH docker images + .env grep
asia-…/web-test/nuxt-ssr:latest image 246 MB 4 tháng incomex-nuxt (ĐANG CHẠY) 🔴 ROLLBACK_PROTECTED — AR API DISABLED, mất là KHÔNG pull lại được tuyệt đối không HIGH gcloud artifactsSERVICE_DISABLED
6 × agent-data-local:* (pre-b1, pre-c1, s178-cutllm ×2, pre-s178) image ~7.5 GB 2–3 tháng none ROLLBACK_PROTECTED (theo tên) ⚠️ Owner quyết MED docker images
8 × nuxt-ssr-local:* + nuxt-p10d-* image ~2 GB 2–4 tháng none ROLLBACK_PROTECTED (theo tên) ⚠️ Owner quyết MED idem
mysql:8.0 image 1.08 GB 5 tháng none — MySQL retired 2026-03-13 STALE_CANDIDATE ✅ có HIGH compose comment L9
docker-agent-data:latest, docker-nuxt:latest image 1.76 GB 5 tháng none ORPHAN (compose cũ) ✅ có MED idem
hello-world, busybox, alpine/curl, curlimages/curl, python:3.11/3.12-slim, node:20-alpine image ~625 MB 2–21 tháng none STALE_CANDIDATE ✅ có HIGH idem
Build cache cache 6.697 GB none (79 entry, Private 0 B) REBUILDABLE_ARTIFACT an toàn nhất HIGH docker builder du
directus_gov_test_20260602 PG database 1202 MB 2026-06-02 none rõ ràng STALE_CANDIDATE ⚠️ Owner xác nhận MED pg_database_size
/opt/incomex/docker/*.bak* (8 .env.bak + 13 compose bak) file ~60 KB 2–5 tháng none 🔴 .env.bak* = bản sao secret cũ ⚠️ xoá có kiểm soát HIGH ls -la
/opt/incomex/docker/nginx/conf.d/*.bak* (22 file) file nhỏ 2–5 tháng KHÔNG được nginx nạp (không đuôi .conf) STALE_CANDIDATE ✅ có HIGH ls conf.d/
/opt/incomex/docker/directus-dump.sql file 33.7 MB 2026-02-12 none 🔴 DB dump cũ, có thể chứa data nhạy cảm ⚠️ Owner HIGH ls -la
/opt/incomex/context-pack.tmp dir 739 MB đang ghi (09:00 hôm nay) /etc/cron.d/incomex-context-pack-retention ACTIVE MED ls -la
/var/lib/containerd dir 37 GB Docker snapshotter ACTIVE_SHARED ⚠️ chỉ giảm gián tiếp qua rmi HIGH du -xsh

Tổng reclaimable an toàn ≈ 49 GB (images 32.67 + volumes 9.58 + build cache 6.70) trên đĩa 88%. Không prune tổng — mission sau phải rmi/volume rm từng ID có bằng chứng, và phải pin AGENT_DATA_IMAGE TRƯỚC khi đụng tới image AR.


5. SECRET ROTATION DEPENDENCY GRAPH

5.1. Nguồn credential — sự thật khác canonical

Nguồn Vai trò thật (FACT)
Google Secret Manager (github-chatgpt-ggcloud) 32 secret. KHÔNG service nào trên VPS đọc trực tiếp. Không có SM client trong container nào; không có agent sync. ⇒ SM hiện là kho lưu trữ ngoại tuyến, không phải nguồn injection.
/opt/incomex/docker/.env (-rw------- root, gitignored, 30 biến) Nguồn injection THẬT của toàn stack.
claude-mcp/.env, claude-kb/.env gitignored; injection cho 2 MCP.
/opt/incomex/scripts/cron-env.sh injection cho ~8 cron job DOT.
credentials/google-credentials.json GCP SA key dạng file, bind vào agent-data :ro.
/opt/incomex/.git-token (41 B, 600) GitHub PAT plaintext — dùng bởi git-push-gh-daily-v2.sh.

🔴 Drift nền tảng: canonical tuyên bố "Secret Manager là SSOT của credential", nhưng runtime không hề đọc SM. SM và .env có thể đã lệch từ 2026-05-22 mà không ai biết — 31/32 secret chưa từng xoay kể từ lúc tạo. Xoay trong SM không có tác dụng gì lên production. Đây là drift nghiêm trọng nhất về mặt normative.

5.2. Đồ thị (node + edge)

[A] DIRECTUS_ADMIN_PASSWORD  ── SM: (không có secret cùng tên) · .env: CÓ · SM version: n/a
    consumers: incomex-directus(ADMIN_PASSWORD) · cron dot-dot-health · dot-collection-health
               · dot-dot-coverage · dot-apr-execute(*/5 phút!) · reconcile-{knowledge,tasks}.py
    plaintext copies: 19 file (13 /opt/incomex + 6 /root) — gồm 7 .env.bak* cũ
                      + 2 TRACKED&IN_HEAD ở agent-data-repo (chỉ 2 file này vào tar backup)
    backup exposure: 🔴 Drive 4×/ngày + GitHub force-push 2×/ngày + nightly full
    rotation precondition: KHÔNG có — .env sửa được, Directus đọc lúc bootstrap
    service action: recreate incomex-directus (ADMIN_PASSWORD chỉ áp lúc bootstrap)
    post-rotation cleanup: 🔴 REWRITE GIT HISTORY (giá trị nằm trong .git objects)

[B] DIRECTUS_ADMIN_TOKEN  ── SM v1 (2026-05-22) · .env: CÓ
    consumers: incomex-agent-data(DIRECTUS_ADMIN_TOKEN) · incomex-nuxt(NUXT_DIRECTUS_SERVICE_TOKEN)
               · claude-mcp(DIRECTUS_TOKEN) · cron dot-orphan-scanner
    ⇒ 🔴 EDGE: [B] chia sẻ consumer với agent-data ⇒ **xoay [B] ĐÒI HỎI recreate agent-data**
       ⇒ BỊ CHẶN bởi drift #1 (image sai + mất 10 env)

[C] DB_PASSWORD / PG_PASSWORD / POSTGRES_PASSWORD  ── SM: PG_PASSWORD v1, POSTGRES_PASSWORD v1
    consumers: postgres · directus(DB_PASSWORD) · agent-data(PG_PASSWORD + DIRECTUS_DB_PASSWORD)
               · nuxt(RP_PG_PASSWORD) · claude-mcp(PG_PASSWORD_RO — role RIÊNG)
    ⇒ 🔴 EDGE: đụng 4 service ⇒ **agent-data phải reconcile TRƯỚC**

[D] OPENAI_API_KEY  ── SM v1 · .env: CÓ
    consumers: agent-data · agent-api-executor(qua ROLLBACK script grep .env lúc chạy)
    ⇒ EDGE: xoay [D] cần recreate CẢ agent-data (drift) LẪN executor (orphan)

[E] QDRANT_LOCAL_API_KEY  ── SM v1 · consumers: qdrant + agent-data
    ⇒ EDGE: qdrant parity PASS nhưng agent-data drift ⇒ vẫn bị chặn

[F] AGENT_DATA_API_KEY  ── SM v1 · consumers: agent-data · directus(Flows) · nuxt · claude-kb
    ⇒ EDGE: 4 consumer, agent-data là nút thắt

[G] gh_pat_sync_secrets + /opt/incomex/.git-token  ── SM v1 · scope repo+workflow
    ⇒ 🔴 EDGE: token này **ĐỦ QUYỀN BẬT LẠI 5 workflow deploy đang disabled**

[H] vps_contabo_id / vps_contabo_secret  ── SM v1 · granted → chatgpt-deployer@
    ⇒ 🔴 control plane hạ tầng VPS (rebuild/rescue), độc lập mọi service

[I] VPS_SSH_KEY (GitHub secret, KHÔNG ở SM)  ── consumers: 5 workflow deploy (disabled)
                                                 + Health Check 227044705 (ACTIVE, KHÔNG dùng!)
    ⇒ 🔴 xoay được ĐỘC LẬP mọi service — và nên xoay sớm

[J] S177_LARK_MCP_REMOTE_TOKEN  ── SM **v2** (2026-05-23, v1 destroyed) — secret DUY NHẤT từng xoay
    consumers: lark-mcp-remote.service   ⇒ độc lập

5.3. Thứ tự xoay bắt buộc (từ đồ thị, KHÔNG mặc định agent-data trước Directus)

Xoay ĐỘC LẬP ngay, không chờ ai:  [I] VPS_SSH_KEY · [G] GitHub PAT · [H] Contabo API · [J] Lark
BỊ CHẶN bởi agent-data drift:      [B] [C] [D] [E] [F]
Cần rewrite Git history sau xoay:  [A]  ← xoay không đủ; giá trị vẫn nằm trong .git

Đính chính đồ thị: [A] DIRECTUS_ADMIN_PASSWORD và [B] DIRECTUS_ADMIN_TOKENhai credential KHÁC LOẠI (password đăng nhập vs static token), không được gom dù cùng chứa chữ "Directus". [A] không có consumer là agent-data; [B] . ⇒ [A] xoay được mà KHÔNG cần đụng agent-data — đây là lý do Wave A đặt [A] trước [B].


6. GOOGLE DRIVE SECRET-BACKUP VECTOR

VERDICT: ACTIVE_SECRET_EXFILTRATION_PATH

("Exfiltration" = secret rời VPS sang kho khác. Không hàm ý kẻ xấu đã truy cập.)

Hạng mục Sự thật (FACT)
Cơ chế /opt/incomex/scripts/code-backup-to-gdrive.sh qua rclone
Lịch CRON_TZ=Asia/Ho_Chi_Minh + 0 8,12,15,20 * * * = 4×/ngày — khớp lời Owner
Source docker/nuxt-repo, docker/agent-data-reporsync -a, chỉ loại trừ build/cache (node_modules,.nuxt,.output,__pycache__,…)
.git có trong tar? 🔴 — 473 member .git/
Secret trong tar? 🔴 scripts/reconcile-knowledge.py + scripts/reconcile-tasks.py (TRACKED + IN HEAD, commit 4acebbc 2026-04-05)
Mã hoá 🔴 KHÔNGtar.gz trần, không rclone crypt
Đích gdrive-backup: type=drive · scope = drive (toàn quyền R/W TOÀN BỘ Drive) · OAuth user token của NGƯỜI, không phải service account
Danh tính tài khoản Drive UNKNOWN — không xác định được nếu không chạm token
Retention 56 bản remote (= 14 ngày) / 1 local · Drive Trash 13.9 GiB có thể còn bản cũ hơn
Artifact gần nhất agent-data-test_20260717-1300_ICT.tar.gz 40,996,554 B, 2026-07-17T06:00:51Z — đã verify size
Vector thứ 2 🔴 git-push-gh-daily-v2.shforce-push cùng repo lên GitHub 0 6,18 * * * = 2×/ngày
Vector thứ 3 (nặng hơn) 🔴 backup-to-gdrive.sh (37 20 * * *) upload lên Drive root: PG dump đầy đủ + Qdrant snapshot + docker/.env (-name ".env*") + /etc/letsencrypt/live/ với cp -rL ⇒ TLS private key thật + toàn bộ /opt/incomex/scripts/*. Bản mới nhất vps-backup-20260716_203701.tar.gz 281,914,206 B

Phân loại: SECRET_UNSAFE_BACKUP (+ DATA_BACKUP cho job nightly). Không phải CODE_BACKUP thuần.

6.1. 🔴 ĐO LẠI PHẠM VI RÒ RỈ — KB ĐÚNG, KHÔNG PHẢI 5 FILE

Bản rev1 của báo cáo này ghi "5 file" là SAI (subagent grep hẹp phạm vi, chỉ quét source của job backup). Đo lại đầy đủ cả /opt/incomex /root, grep -rlF theo giá trị thật (không in giá trị):

DIRECTUS_ADMIN_PASSWORD:  /opt/incomex = 13   /root = 6    →  19 file
DIRECTUS_ADMIN_TOKEN   :  /opt/incomex = 30   /root = 4    →  34 file
UNION (một trong hai, cả 2 cây)                            →  46 file

Con số "34 file" của KB khớp CHÍNH XÁC số file chứa DIRECTUS_ADMIN_TOKEN — KB đúng, chỉ là đếm theo denominator khác. Phạm vi thật rộng hơn cả KB: 46 file.

13 file chứa DIRECTUS_ADMIN_PASSWORD dưới /opt/incomex:

docker/.env                                  ← nguồn injection hợp lệ (0600)
.env.local                                   ← ⚠️ chưa rõ consumer
docker/.env.bak.20260318T075608              ┐
docker/.env.bak.20260318T075911              │
docker/.env.bak.20260404                     ├─ 🔴 7 BẢN SAO SECRET CŨ (stale)
docker/.env.bak.20260517T021349Z             │   → Wave C4 phải xoá
docker/.env.bak.20260517T030513Z             │
docker/.env.bak.s174-fix-04                  │
docker/.env.mysql-backup-20260313            ┘
scripts/reconcile-knowledge.py               ┐ ngoài Git (bản /opt/incomex)
scripts/reconcile-tasks.py                   ┘
docker/agent-data-repo/scripts/reconcile-knowledge.py   ┐ 🔴 TRACKED + IN HEAD
docker/agent-data-repo/scripts/reconcile-tasks.py       ┘   → vào Drive + GitHub

"2 file trong git HEAD" = ĐÚNG — đúng 2 file agent-data-repo/scripts/reconcile-*.py, và chỉ 2 file này nằm trong tar backup 4×/ngày. 215 file khác chỉ chứa tên biến ${DIRECTUS_ADMIN_PASSWORD} (docs/template/SQL — vô hại).

Bài học đo: giao subagent grep secret phải chốt phạm vi tường minh (/opt/incomex + /root, không --include), nếu không sẽ ra con số nhỏ giả tạo rồi "đính chính" nhầm KB đang đúng.

🔴 Blast radius: xoay DIRECTUS_ADMIN_PASSWORD không đóng được lỗ này — giá trị nằm trong git history, nên sẽ tiếp tục được upload trong .git objects cho tới khi rewrite history; và nó đã nằm trên GitHub branch vps-daily-*. Ngoài ra scope = drive nghĩa là token backup đọc/xoá được toàn bộ Drive cá nhân, không chỉ thư mục backup.

⚠️ Ưu tiên CAO trong rotation graph — secret đang bị sao chép 6 lần/ngày ra 2 kho ngoài.

(Fingerprint đối chiếu giá trị đã được xác lập out-of-band trong quá trình đo và cố ý không ghi vào báo cáo/KB theo §3, kể cả dạng hash rút gọn.)


7. ACTIVE CONTROL-PLANE INVENTORY

7.1. GitHub (đã xác minh lại, KHÔNG tin KB)

34 workflow / 2 repo. Đúng 5 có khả năng ghi VPS — cả 5 đều disabled_manually. Cả 5 dùng VPS_HOST + VPS_SSH_KEY với username: root.

CONTROL PLANE ERA ID TRIGGER TARGET CAPABILITY STATE LAST RUN CRED RISK DISPOSITION
web-test / Deploy to VPS GITHUB_DEPLOY 233946923 workflow_run[Nuxt 3 CI] + dispatch compose, nginx conf, nuxt-output, dot/bin, PG migrations scp+ssh root · compose up -d --force-recreate · nginx -s reload disabled_manually 2026-07-11T02:47:26Z success ✅ VPS_SSH_KEY 🔴 CRITICAL KEEP_DISABLED
agent-data-test / Deploy Agent Data GITHUB_DEPLOY 233947089 🔴 push: main agent-data-repo, rewrite docker-compose.yml rsync+ssh · compose up -d --force-recreate agent-data disabled_manually 2026-04-03 success VPS_SSH_KEY,PG_PASSWORD 🔴 CRITICAL KEEP_DISABLED
agent-data-test / S109 PG Setup GITHUB_DEPLOY 244536611 dispatch rewrite compose, PG data ssh · docker cp · Firestore→PG import disabled_manually 2026-03-11 success VPS_SSH_KEY 🔴 HIGH KEEP_DISABLED
agent-data-test / Vector Audit & Cleanup GITHUB_DEPLOY 244569412 dispatch Qdrant/PG vector data ssh · docker exec incomex-agent-data · auto_heal XOÁ vector disabled_manually 2026-03-11 success VPS_SSH_KEY 🔴 HIGH KEEP_DISABLED — ⚠️ KB ghi "không tồn tại" là SAI
web-test / Điều 31 VPS Setup GITHUB_DEPLOY 250152096 dispatch root crontab, logs/integrity ssh · crontab /tmp/crontab-clean disabled_manually 2026-03-23 success VPS_SSH_KEY 🔴 HIGH KEEP_DISABLED
web-test / Health Check GITHUB_DEPLOY 227044705 cron 0 */6 * * * curl only (0 hit ssh/scp/rsync trong 537 dòng) ACTIVE — đang FAIL 2026-07-17T02:28Z failure 🔴 khai VPS_SSH_KEY vào env L433 mà KHÔNG dùng 🟠 MED RECONCILE — bỏ dòng env L433
28 workflow còn lại mixed không chạm VPS mixed 🟢 LOW KEEP

🔴 Ba rủi ro tái kích hoạt (FACT):

  1. Repo variable DEPLOY_MODE=direct ĐÃ SET ⇒ điều kiện if: của job deploy-direct đã thoả. Việc disable là thứ DUY NHẤT chặn push→deploy root. Không có gate thứ hai.
  2. 233946923 chạy theo workflow_run từ Nuxt 3 CI (ACTIVE) ⇒ bật lại là merge kế tiếp vào main tự deploy root.
  3. 233947089 trigger push: main ⇒ bật lại là push kế tiếp tự deploy, không cần dispatch.
  4. Cả 5 là disabled_manually (không phải disabled_inactivity) ⇒ push không tự bật lại. Cần hành động tường minh. Nhưng PAT trong /opt/incomex/.git-token có scope repo+workflowđủ quyền bật lại.

🔴 Bẫy chết người (FACT, đo cả 2 đầu): web-test main vẫn giữ infra/nginx/conf.d/default.conf sha256 9404dce8… với 4 block well-known. VPS live là bd38d294… với 1. 9404dce8… chính là cấu hình OAuth gây treo KB trong PHASE-2A. Nếu 233946923 được bật và chạy, bước "Sync config files" cp đè rồi nginx -s reloadtái phát lỗi treo KB. ⇒ GitHub KHÔNG phải điểm restore an toàn cho nginx.

7.2. GCP

Toàn bộ API chạy được code đều DISABLED. Chỉ 8 API bật: cloudbilling, cloudresourcemanager, developerknowledge, iam, iamcredentials, secretmanager, serviceusage, telemetry.

CONTROL PLANE STATE CAN AFFECT VPS? RISK DISPOSITION
Cloud Run / Functions / Scheduler / Build triggers / GCE API DISABLED (FACT) NO 🟢 REMOVE_CANDIDATE (Wave E)
Artifact Registry API DISABLED NO (nhưng ⇒ không pull lại được image) 🔴 xem §11 RECONCILE
Secret Manager ENABLED — bề mặt sống duy nhất Gián tiếp 🔴 HIGH KEEP_ACTIVE
IAM / iamcredentials ✅ ENABLED (đúc token) YES 🟠 MED-HIGH RECONCILE
logging.googleapis.com NOT ENABLED 🟠 ⚠️ không có audit log ⇒ KHÔNG biết secret nào đã bị đọc, bởi ai. UNKNOWN không thể khôi phục.

🔴 IAM: 14 SA (0 disabled) · 11 user-managed key, TẤT CẢ validBeforeTime = 9999-12-31 (không hết hạn).

  • chatgpt-api-access@roles/owner + key be15918f… (2024-11-01) ⇒ quyền sở hữu toàn dự án, không hết hạn. Nghiêm trọng nhất.
  • chatgpt-deployer@editor + resourcemanager.projectIamAdmin (⇒ tự nâng thành owner) + serviceusage.serviceUsageAdmin (⇒ bật lại mọi API đã tắt) + secretmanager.admin + được cấp vps_contabo_id/vps_contabo_secret. 3 key, và đang cache trên laptop này.
  • cursor-ci-builder@ — sống trong GitHub Actions; custom role MCPSecretManagerOperator mang tên "minimal" nhưng đọc + ghi payload secret toàn dự án.

7.3. VPS local (cron + systemd)

CONTROL PLANE TRIGGER TARGET CAPABILITY STATE RISK DISPOSITION
disk-monitor.sh cron 0 * * * * REPORT-ONLY đã XÁC MINH trên file LIVE: 0 hit `prune docker rm rmi rm -rf; comment L4 "Auto-prune removed 2026-07-17: reclaimed 0B"; **live == HEAD** (git statussạch). Toànscripts/+dot/bin/`: 0 hit prune
code-backup-to-gdrive.sh cron 0 8,12,15,20 ICT Drive 🔴 secret rời VPS 4×/ngày ACTIVE_RISKY 🔴 CRITICAL RECONCILE (Owner)
backup-to-gdrive.sh cron 37 20 * * * Drive root 🔴 PG dump + .env + TLS private key ACTIVE_RISKY 🔴 CRITICAL RECONCILE (Owner)
git-push-gh-daily-v2.sh cron 0 6,18 GitHub 🔴 force-push repo có secret 2×/ngày ACTIVE_RISKY 🔴 CRITICAL RECONCILE (Owner)
dot-apr-execute --local cron */5 Directus/PG export SYNC_SECRET+DIRECTUS_ADMIN_PASSWORD; WRITE ACTIVE_REQUIRED 🟠 MED KEEP_ACTIVE
~20 cron docker exec postgres psql */10, hằng giờ, hằng ngày PG WRITE thật (refresh_*, auto_apply_approval, fn_expire_stale_approvals) mutation trực tiếp ACTIVE_REQUIRED 🟠 MED KEEP_ACTIVE — ⚠️ phụ thuộc container tên postgres còn sống
pg-backup.sh / qdrant-backup.sh 27 2 / 0 3 UTC local backup ACTIVE_REQUIRED 🟢 KEEP_ACTIVE
env-permissions-guard.sh / db-permissions-guard.sh / mcp-writes-perms.timer */5 / hằng giờ / 15′ chmod guard ACTIVE_REQUIRED 🟢 KEEP_ACTIVE
incomex-balo-reconcile.socket/.service socket-activated Balo PG WRITE dot-balo-reconcile --sync active+enabled 🟡 LOW KEEP_ACTIVE
ui-preview-notebook-daily.timer 04:00 git snapshot ACTIVE 🟢 KEEP_ACTIVE
s177-lark-backup.timer, wf-universal-scanner.timer, process-discovery-policy-scan.timer, run-sr-mow.sh (20 */6) timer/cron scan/backup ACTIVE 🟢 KEEP_ACTIVE
lark-mcp-remote.service, fail2ban, ufw, certbot.timer systemd ACTIVE 🟢 KEEP_ACTIVE
claude-mcp/var/run/docker.sock mount :ro Docker API 🔴 :ro trên unix socket KHÔNG chặn write — connect cần quyền w, và bind :ro không áp EROFS lên socket inode (đã chứng minh bằng chính bridge Balo :ro+gid 987 ⇒ CONNECT_OK) ⇒ toàn quyền Docker ACTIVE_RISKY 🟠 MED-HIGH RECONCILE — cân nhắc docker-socket-proxy

8. NORMATIVE DRIFT SURFACE

# SUBJECT RUNTIME FACT CANONICAL / RULE NORMATIVE_REGISTRY DRIFT TYPE RISK PROPOSED RESOLUTION OWNER? EVIDENCE
1 VPS là code/deploy SSOT /opt/incomex không remote, không upstream; build/deploy tại chỗ VPS SSOT không có entry ALIGNED (thực tế) / MISSING_COVERAGE (registry) 🟢 Ghi entry xác nhận git remote -v rỗng
2 GitHub workflow không được deploy prod ✅ 5/5 workflow ghi-VPS disabled_manually Cấm không có entry MISSING_COVERAGE 🔴 Ghi entry cấm bật lại; nêu DEPLOY_MODE=direct đã thoả gh workflow list
3 Drive = off-site backup, không phải SSOT ✅ đúng là backup… Backup không có entry CONTRADICTORY 🔴 nhưng chứa secret + TLS key + PG dump, không mã hoá, scope=drive ⇒ vi phạm tinh thần "backup an toàn" §6
4 Secret Manager là credential SSOT 🔴 SAI Ở RUNTIME0 service đọc SM; nguồn thật là docker/.env; 31/32 secret chưa xoay từ 2026-05-22 SM = SSOT không có entry 🔴 CONTRADICTORY 🔴 CRITICAL Chọn 1: (a) sửa canonical → "SM = kho lưu trữ/escrow, .env = injection SSOT"; hoặc (b) làm SM thành SSOT thật (thêm cơ chế sync) BẮT BUỘC không có SM client trong container nào
5 Compose phải phản ánh runtime trước khi coi là reproducible 🔴 4/13 lệch: agent-data (3 drift), nginx (mount+net), postgres (env+net), directus (net); executor không có trong compose Rule không có entry DRIFT 🔴 Vá compose theo Wave B trước mọi recreate §3.1
6 Mỗi mutation chỉ tác động đúng service 🔴 docker compose up -d trần đụng cả 7 service + cảnh báo orphan executor; --remove-orphans XOÁ executor Rule không có entry DRIFT 🔴 Ghi entry: cấm up -d trần, cấm --remove-orphans; chỉ up -d <service> label vs compose
7 Git checkpoint/bundle trước mutation ⚠️ /opt/incomex 103 file dirty (15 M + 88 ??), branch feat/s177-sprint1-round-a; dot 19 ?? Rule không có entry DRIFT 🟠 Baseline hoá theo quy trình Git Safety §13.2 git status
8 Balo one-click guard trước/sau thay đổi dot-balo-one-click-guard tồn tại (c6a5579) Rule không có entry MISSING_COVERAGE 🟡 Ghi vào entry §9 git log dot
9 §15 từng cấm nút UI/bridge; rev16–17 đã bãi bỏ phần cấm ✅ Canonical revision 17 chứa: "SỬA ĐỔI 2026-07-17 (§16 thay thế một phần §15). Bản v1.9 của §15 có ghi cấm 'nút UI đồng bộ B…'" §16 > §15 🔴 0 entry MISSING_COVERAGE 🟠 Proposed entry §9 — CHỜ DUYỆT BẮT BUỘC KB get_document rev 17
10 Nút Balo + bridge LIVE, Owner-accepted ✅ nuxt env NUXT_BALO_* + NUXT_PUBLIC_BALO_*; socket active+enabled; guard 37/37 Cho phép (§16) 🔴 normative_registry 47 rows, balo=0, reconcile=0 MISSING_COVERAGE 🟠 idem query_pg đo thật
11 disk-monitor không được prune tự động ĐÃ SỬA + XÁC MINH LIVE Rule không có entry ALIGNED 🟢 Ghi entry chốt grep live file

9. PROPOSED §15/§16 NORMATIVE_REGISTRY ENTRY — NOT APPLIED

⚠️ CHƯA GHI. Mission này chỉ soạn. normative_registry hiện 47 rows, 0 row Balo. Cần Owner duyệt.

subject: Balo one-click sync — nút UI dùng chung cho report có resolvedCollection === 'balo_thuc_the'
supersedes: "§15 (v1.9) — phần cấm 'nút UI đồng bộ Balo'"
authority: "canonical balo-thuc-the-quy-dinh.md §16 (rev17, SỬA ĐỔI 2026-07-17)"
status: ACTIVE — Owner-accepted (PASS_BALO_POST_ACCEPTANCE, Administrator thật nghiệm thu)

decision: |
  Cho phép MỘT nút "Đồng bộ Balo từ PG" dùng chung cho mọi report
  có resolvedCollection === 'balo_thuc_the'.

required_controls:
  - Administrator-only (NUXT_BALO_OPERATOR_ROLE_IDS — BẮT BUỘC cả NUXT_ lẫn NUXT_PUBLIC_;
    thiếu 1 ⇒ endpoint chạy nhưng NÚT VÔ HÌNH)
  - same-origin / CSRF gate
  - fixed-purpose Unix socket bridge (/run/incomex/balo-reconcile.sock, 0660 root:incomex-balo-sync gid 987)
  - DOT --sync (không SQL trực tiếp)
  - atomic apply (dot-pg-atomic-apply)
  - verify sau apply
  - singleton lock
  - guard before/after mutation (dot-balo-one-click-guard, 37/37)

forbidden:
  - direct browser/Directus/Nuxt SQL WRITE
  - arbitrary command bridge
  - public TCP bridge
  - hardcoded report URLs
  - cron sync

evidence:
  - runtime: nuxt env NUXT_BALO_OPERATOR_ROLE_IDS + NUXT_PUBLIC_BALO_OPERATOR_ROLE_IDS + NUXT_BALO_RECONCILE_SOCKET_PATH
  - systemd: incomex-balo-reconcile.socket = active + enabled
  - commits: 4ecb614, 78b523c, 7a9843d, c6a5579, 7c60ccd (local-only, chưa push)
  - guard: dot-balo-one-click-guard 37/37

10. BACKUP MINIMUM ASSESSMENT (không restore drill)

Job Chạy thật? Lịch Artifact gần nhất Đích Mã hoá Phân loại
code-backup-to-gdrive.sh ✅ 2026-07-17 08:00 CEST success 4×/ngày ICT agent-data-test_20260717-1300_ICT.tar.gz 40,996,554 B (size-verified) Drive folder ID 🔴 SECRET_UNSAFE_BACKUP
backup-to-gdrive.sh ✅ 2026-07-16 OK 37 20 * * * vps-backup-20260716_203701.tar.gz 281,914,206 B Drive root 🔴 DATA_BACKUP + SECRET_UNSAFE_BACKUP (TLS private key)
pg-backup.sh ⚠️ có cron, chưa verify artifact 27 2 * * * /opt/incomex/backups/pg/ local DATA_BACKUP
qdrant-backup.sh ⚠️ chưa verify 0 3 * * * UTC local DATA_BACKUP
ui-preview-notebook-daily ✅ 04:00 hôm nay daily git snapshot local CODE_BACKUP
s177-lark-backup.timer ✅ 2026-07-16 ~2 ngày GPG (có LARK_BACKUP_GPG_*) DATA_BACKUP

KHÔNG tuyên bố "đã backup đầy đủ". Khoảng trống thật:

  • 🔴 /opt/incomex (git root, 103 file dirty) KHÔNG nằm trong job Drive nào — job code chỉ đóng nuxt-repo + agent-data-repo. Nightly job có /opt/incomex/scripts/* nhưng không phải toàn repo. ⇒ compose, nginx conf, dot/, 88 file untracked chỉ tồn tại trên 1 đĩa.
  • 🔴 GCS_BUCKET_BACKUP trỏ vào hư khônggcloud storage buckets list0 bucket, storage API tắt ⇒ job nào còn tham chiếu secret này đang ghi vào chỗ không tồn tại / fail âm thầm.
  • 🔴 21.87 GB writable layer (qdrant 14.6 / postgres 4.54 / directus 2.64) không nằm trong bind mount nàokhông job nào backup ⇒ recreate là mất (nếu con số Docker báo là đúng — chưa xác minh được vì snapshotter).
  • ⚠️ Không có Git bundle nào được verify trong mission này (git bundle verify = read-only, nhưng chưa có bundle để verify).

11. TOP BLOCKERS BY SERVICE

Service Blocker Vì sao chặn
agent-data 🔴 3 drift đồng thời (image fallback AR có sẵn local + 10 env thiếu + mem) Chặn xoay [B][C][D][E][F]5/9 secret của toàn hệ
agent-api-executor 🔴 orphan có label compose compose up -d --remove-orphans xoá mất; script recreate untracked
nginx 🔴 mount ui-preview không khai báo recreate ⇒ /ui-preview/ 404 câm, không lỗi
nuxt 🟡 image AR 4 tháng không pull lại được (AR API disabled) mất image = mất khả năng recreate
postgres 🟠 OOMKilled=true; ~20 cron phụ thuộc tên container recreate làm gãy cron docker exec postgres
qdrant 🟠 14.6 GB writable layer chưa giải thích được recreate có thể mất data
Toàn hệ 🔴 SM không phải nguồn injection thật xoay secret ở SM không tác động production
Toàn hệ 🔴 DIRECTUS_ADMIN_PASSWORD trong git history xoay không đủ; phải rewrite history

12. SERVICES SAFE TO RECONCILE INDEPENDENTLY

incomex-nuxt (parity 17/17), incomex-qdrant, incomex-claude-kb, incomex-claude-mcp (parity PASS — nhưng ghi nhận capability docker.sock), incomex-balo-reconcile (unit khớp).

Xoay được ngay, không phụ thuộc service nào: VPS_SSH_KEY (GitHub), gh_pat_sync_secrets + .git-token, vps_contabo_id/secret, S177_LARK_MCP_REMOTE_TOKEN. ✅ DIRECTUS_ADMIN_PASSWORD xoay được mà KHÔNG cần đụng agent-data (khác loại với DIRECTUS_ADMIN_TOKEN) — nhưng phải kèm rewrite history mới đóng được lỗ.

Không độc lập: agent-data (nút thắt), executor (orphan), nginx (mount), postgres/directus (network).


13.1. Các wave

Wave A — parity sẵn + secret rủi ro cao, không chờ ai
  A1 Owner duyệt proposed entry §9 (normative_registry)
  A2 Owner quyết Drive/GitHub secret vector (§6) — đang chảy 6 lần/ngày
  A3 Owner quyết drift #4: SM là SSOT hay escrow (§8)
  A4 Xoay VPS_SSH_KEY · gh PAT · Contabo API  (độc lập hoàn toàn)
  A5 Bỏ dòng env VPS_SSH_KEY L433 của Health Check 227044705
  A6 Quyết số phận executor: đưa vào compose HAY commit ROLLBACK script + cấm --remove-orphans

Wave B — drift chặn secret rotation
  B1 🔴 PIN AGENT_DATA_IMAGE=agent-data-local:latest  ← LÀM TRƯỚC MỌI THỨ KHÁC
  B2 Vá compose agent-data: 10 env PG_*/DIRECTUS_DB_*, mem_limit → ${AGENT_DATA_MEM_LIMIT}
  B3 Vá compose nginx: mount ui-preview + claude_mcp_net
  B4 Vá compose postgres/directus: claude_mcp_net; pin POSTGRES_DB/USER
  B5 Verify từng service (guard + smoke), recreate TỪNG service một

Wave C — revoke + loại plaintext
  C1 Xoay DIRECTUS_ADMIN_PASSWORD + ADMIN_TOKEN + PG/Qdrant/OpenAI (sau B)
  C2 🔴 Rewrite git history 2 file reconcile-*.py (agent-data-repo) — nếu không, C1 vô nghĩa
  C3 Thu hồi key be15918f… (chatgpt-api-access owner) + 10 key không hết hạn
  C4 Xoá .env.bak* (8 file), .git-token → chuyển sang nguồn an toàn
  C5 Thu hẹp rclone scope: drive → drive.file

Wave D — dọn storage có chọn lọc (từng ID, KHÔNG prune tổng)
  D1 Build cache 6.7 GB (an toàn nhất, Private 0 B)
  D2 7 volume dry-run/anonymous ~9.6 GB (sau khi Owner xác nhận pg-restore-test)
  D3 mysql:8.0, docker-*, hello-world/busybox… ~3.5 GB
  D4 directus_gov_test_20260602 (1.2 GB PG database)
  D5 ⚠️ image agent-data AR — CHỈ sau B1
  D6 ❌ TUYỆT ĐỐI GIỮ nuxt-ssr:latest (AR disabled ⇒ không pull lại được)

Wave E — sizing, GCP decommission, restore drill đầy đủ
  E1 Xác minh 21.87 GB writable layer (cần nerdctl/ctr do snapshotter)
  E2 Sizing lại theo 11 GiB/6 vCPU THẬT (không phải 16/8) sau khi đo tải đủ dài
  E3 Bật logging API để có audit trail secret (hiện KHÔNG BIẾT ai đã đọc gì)
  E4 GCP decommission + restore drill

Không mặc định agent-data đứng trước Directus. Đồ thị consumer quyết định: DIRECTUS_ADMIN_PASSWORD không có consumer agent-data ⇒ nằm được ở Wave A/C sớm; còn DIRECTUS_ADMIN_TOKEN ⇒ buộc sau Wave B.

13.2. GIT SAFETY — điều kiện bắt buộc cho MỌI mutation sau này

 1. Đọc và phân loại TOÀN BỘ diff hiện hữu (103 file /opt/incomex + 19 /opt/incomex/dot
    + 1 nuxt-repo). Phân loại: authorized survey artifact / production drift / rác.
 2. Commit baseline VPS hợp lệ — KHÔNG đổi byte live chỉ để commit cho đẹp.
 3. Chỉ stage exact file. 🔴 CẤM `git add -A`, CẤM `git add .`
 4. Tạo Git bundle đầy đủ NGOÀI repo.
 5. SHA256 + `git bundle verify` PASS.
 6. Byte-exact backup cho config/runtime NGOÀI Git:
    docker/.env (gitignored) · ROLLBACK-executor-docker-run.sh (untracked)
    · /etc/systemd/system/incomex-balo-reconcile.{socket,service}
    · bind data: /opt/workflow/postgres, docker/qdrant/data, docker/directus/{uploads,extensions}
 7. Commit thay đổi TRƯỚC deploy/reload/recreate.
 8. Guard + smoke sau mutation (dot-balo-one-click-guard 37/37).
 9. Tạo post-mutation bundle.
10. 🔴 KHÔNG pull, KHÔNG dùng remote đè VPS; KHÔNG push nếu Owner chưa giao.

🔴 Không dùng stopped container hoặc GitHub làm rollback duy nhất. — GitHub web-test giữ nginx 9404dce8… = chính lỗi treo KB, restore từ đó là tái phát bug. — Không có stopped container nào tồn tại (11/11 đang chạy) ⇒ không có rollback dạng đó. — Rollback thật phải dựa vào: Git bundle (Wave §13.2 mục 4) + image tag pre-* local + byte-exact backup.


14. FACTS NEXT AGENT MAY RELY ON

✅ Host: 6 vCPU / 11 GiB RAM / 96 G disk 88% / inode 9% — ĐO THẬT
✅ 11 container: 9 compose-managed (3 file compose), 2 không (uptime-kuma, pg-restore-test)
✅ agent-data runtime = 2560 MiB + ĐÚNG 10 biến PG_*/DIRECTUS_DB_*; compose = 1536m + 0 biến
✅ AGENT_DATA_IMAGE/NUXT_SSR_IMAGE KHÔNG set ở đâu ⇒ compose dùng fallback AR
✅ Fallback image agent-data-test:latest TỒN TẠI local (4 tháng) ⇒ compose up chạy sai IM LẶNG
✅ agent-api-executor: 0 hit trong compose, nhưng CÓ label compose ⇒ orphan
✅ nginx runtime có 5 bind (compose khai 4) — thiếu ui-preview; + claude_mcp_net
✅ postgres OOMKilled=true, RestartCount=0
✅ 5/34 GitHub workflow ghi được VPS — CẢ 5 disabled_manually (đã xác minh lại)
✅ 244569412 TỒN TẠI ở agent-data-test (KB ghi sai)
✅ DEPLOY_MODE=direct đã set ⇒ disable là gate DUY NHẤT
✅ GitHub web-test giữ nginx 9404dce8… (4 well-known) vs live bd38d294… (1)
✅ GCP: chỉ secretmanager + iam bật; Cloud Run/Functions/Scheduler/AR/Build = API DISABLED
✅ 32 secret SM, 31 chưa từng xoay từ 2026-05-22; replication đơn europe-west3
✅ 11 SA key KHÔNG hết hạn; chatgpt-api-access@ có roles/owner + key từ 2024-11-01
✅ KHÔNG service nào đọc Secret Manager — .env là nguồn injection thật
✅ Drive backup 4×/ngày + GitHub push 2×/ngày + nightly (TLS key + PG dump) = ACTIVE_SECRET_EXFILTRATION_PATH
✅ Rò rỉ: DIRECTUS_ADMIN_PASSWORD = 19 file (13 /opt/incomex + 6 /root); DIRECTUS_ADMIN_TOKEN = 34 file;
   UNION = 46 file. KB "34 file" ĐÚNG (= số file chứa TOKEN). 2 TRACKED+IN_HEAD (agent-data-repo).
   7 file .env.bak* chứa password cũ ⇒ Wave C4.
✅ /opt/incomex: KHÔNG remote, branch feat/s177-sprint1-round-a, HEAD 7c60ccd, 103 dirty
✅ docker-compose.yml, nginx/default.conf, disk-monitor.sh, backup scripts = TRACKED_CLEAN
✅ docker/.env = GITIGNORED; ROLLBACK-executor-docker-run.sh = UNTRACKED_LIVE
✅ disk-monitor.sh = REPORT-ONLY, xác minh trên file live; 0 prune trong scripts/ + dot/bin/
✅ normative_registry = 47 rows, 0 row balo/one-click/reconcile
✅ canonical balo-thuc-the-quy-dinh.md = revision 17, có §16 sửa đổi 2026-07-17
✅ PG: directus 1692 MB · directus_gov_test_20260602 1202 MB (stale) · incomex_metadata 386 MB
✅ Reclaimable ≈ 49 GB (image 32.67 + volume 9.58 + build cache 6.70)

15. ASSUMPTIONS NEXT AGENT MUST NOT MAKE

❌ ĐỪNG tin host có 16 GB/8 CPU — thật là 11 GiB/6 vCPU
❌ ĐỪNG tin "chỉ 1 workflow nguy hiểm" — có 5
❌ ĐỪNG tin 244569412 không tồn tại — CÓ, ở agent-data-test
❌ ĐỪNG tin con số "5 file rò rỉ" ở rev1 của chính báo cáo này — SAI, đã tự bác bỏ.
   Thật: PASSWORD 19 file · TOKEN 34 file · UNION 46 file (cả /opt/incomex lẫn /root).
❌ ĐỪNG giao subagent grep secret mà không chốt phạm vi — sẽ ra số nhỏ giả tạo
❌ ĐỪNG tin Secret Manager đang cấp credential cho production — KHÔNG service nào đọc nó
❌ ĐỪNG tin xoay DIRECTUS_ADMIN_PASSWORD là đóng được lỗ — giá trị nằm trong git history
❌ ĐỪNG chạy `docker compose up -d` trần — đụng 7 service + cảnh báo orphan executor
❌ ĐỪNG chạy `--remove-orphans` — XOÁ incomex-agent-api-executor
❌ ĐỪNG recreate agent-data trước khi pin AGENT_DATA_IMAGE — sẽ chạy image sai 4 tháng, KHÔNG báo lỗi
❌ ĐỪNG recreate nginx trước khi thêm mount ui-preview — /ui-preview/ 404 câm
❌ ĐỪNG restore nginx từ GitHub — bản đó là lỗi treo KB (9404dce8…)
❌ ĐỪNG xoá image nuxt-ssr:latest — AR API disabled, KHÔNG pull lại được
❌ ĐỪNG coi volume "dangling" là rác chỉ vì không container nào dùng — 46dbcc89… đang active
❌ ĐỪNG coi "container tạm còn sống" là gate bất khả thi — prune đã bị gỡ 2026-07-17, gate KHẢ THI lại
❌ ĐỪNG mặc định agent-data phải đứng trước Directus — đồ thị consumer quyết định
❌ ĐỪNG tin con số 21.87 GB writable layer là đã xác minh — Docker báo, chưa đối chiếu du được
❌ ĐỪNG dùng stopped container làm rollback — không có container nào stopped
❌ ĐỪNG gom DIRECTUS_ADMIN_PASSWORD với DIRECTUS_ADMIN_TOKEN — khác loại, khác consumer

16. UNKNOWNS

? Danh tính tài khoản Google Drive nhận backup (OAuth user token) — không xác định được nếu không chạm token
? PG_* vs POSTGRES_* trong SM: bộ nào còn sống — cần đọc payload (đã CỐ Ý từ chối)
? Artifact Registry web-test còn tồn tại/size/last-push — bị SERVICE_DISABLED che; muốn biết phải BẬT API (bị cấm)
? Ai đã đọc secret nào trong SM, khi nào — logging API tắt ⇒ KHÔNG khôi phục được
? VPS_SSH_KEY trong GitHub còn hiệu lực với VPS không — cần đọc secret hoặc thử auth (ngoài phạm vi)
? 21.87 GB writable layer (qdrant 14.6 / postgres 4.54 / directus 2.64) thực sự chứa gì — GraphDriver.Data=null do containerd snapshotter
? pg-backup.sh / qdrant-backup.sh artifact gần nhất — chưa verify
? 9 biến PG_*/DIRECTUS_DB_* của agent-data (trừ PG_PASSWORD) lấy từ nguồn nào — không có trong .env
? directus_gov_test_20260602 (1.2 GB) còn ai dùng không
? lark-mcp-remote.service parity — chưa đối chiếu
? 88 file untracked ở /opt/incomex là artifact hợp lệ hay drift — cần phân loại (Git Safety mục 1)

17. EVIDENCE PATHS

HOST      free -h · nproc · lscpu · df -h · df -i · cat /proc/pressure/{memory,cpu,io}
CONTAINER docker ps -a --format · docker inspect <c> | jq · docker ps -as
          docker inspect <c> --format '{{range .Config.Env}}…' | cut -d= -f1   ← names only
COMPOSE   cat /opt/incomex/docker/docker-compose.yml (215 dòng)
          cat claude-{kb,mcp}/docker-compose.*.yml
ENV       grep -vE '^#|^$' docker/.env | cut -d= -f1   ← names only, KHÔNG giá trị
STORAGE   docker system df [-v] · docker images · docker volume ls/inspect · docker builder du
          du -xh --max-depth=1 /var/lib/docker · du -xsh /var/lib/containerd · journalctl --disk-usage
DOCKER    docker info | grep -i 'storage driver|snapshotter'
CRON      crontab -l · ls /etc/cron.d /etc/cron.{hourly,daily,weekly} · ls /var/spool/cron/crontabs
SYSTEMD   systemctl list-timers --all · list-unit-files --state=enabled · systemctl cat <unit>
NGINX     sha256sum docker/nginx/conf.d/default.conf · grep -c well-known · ls conf.d/
GIT       git remote -v · branch --show-current · log --oneline · status --porcelain
          ls-files --error-unmatch · check-ignore -v · diff --stat HEAD
GITHUB    gh api repos/Huyen1974/{web-test,agent-data-test}/actions/workflows[/<id>]   ← GET only
          gh api .../contents/.github/workflows/<f> --jq .content | base64 -d
          gh api repos/Huyen1974/web-test/actions/variables
GCP       gcloud auth list · config list · projects list · services list --enabled
          gcloud secrets list · secrets versions list · secrets get-iam-policy   ← metadata only
          gcloud iam service-accounts list · keys list --managed-by=user
          gcloud {run,functions,scheduler,compute,builds,artifacts} … list   → SERVICE_DISABLED
PG        query_pg (database="directus", READ ONLY tx, SELECT):
          pg_database_size · information_schema.tables · normative_registry counts
KB        search_knowledge · get_document('knowledge/dev/laws-new/pg-read-pg/balo-thuc-the-quy-dinh.md') → rev 17
BACKUP    crontab · cat scripts/{code-backup-to-gdrive,backup-to-gdrive,git-push-gh-daily-v2}.sh
          tar -tzf <artifact> (liệt kê) · rclone lsjson/about   ← read-only subcommands
          grep -E '^\[|^type|^scope' ~/.config/rclone/rclone.conf   ← KHÔNG đọc token
REPORT    KB: knowledge/current-state/reports/claude-post-migration-service-secret-controlplane-reconciliation-survey-2026-07-17.md

18. PROOF OF READ-ONLY SCOPE

SOURCE_CHANGE=NO           CONFIG_CHANGE=NO           GIT_COMMIT=NO
GIT_PUSH=NO                CONTAINER_MUTATION=NO      SYSTEMD_MUTATION=NO
GITHUB_WORKFLOW_MUTATION=NO   GCP_MUTATION=NO         SECRET_MUTATION=NO
DB_WRITE=NO                DISK_CLEANUP=NO

Chứng minh:

  • Docker: chỉ ps, inspect, images, volume ls/inspect, system df, builder du, info, logs. 0 lệnh start/stop/restart/rm/rmi/up/down/prune/exec.
  • systemd: chỉ list-timers, list-unit-files, cat, is-active, is-enabled. 0 start/stop/restart/reload/enable/disable. 0 nginx -s reload.
  • Git: chỉ remote, branch, log, status, diff, ls-files, check-ignore, rev-parse. 0 add/commit/push/pull/reset/checkout/restore/clean.
  • GitHub: GET only (gh api, gh workflow list/view, gh run list). 0 enable/disable/dispatch/rerun.
  • GCP: list/describe/get-iam-policy only. gcloud secrets versions access KHÔNG BAO GIỜ được gọi — 0 payload đọc. Prompt "enable and retry?" auto-trả lời N0 API được bật.
  • PG: query_pg — AST-validated, READ ONLY transaction, read-only role, chỉ SELECT + information_schema. 0 INSERT/UPDATE/DELETE/DDL. 0 Directus WRITE. 0 Balo --sync/--apply.
  • Secret hygiene: mọi lần đọc env đều cut -d= -f1 phía server ⇒ giá trị không bao giờ vào context. docker compose config không chạy (tránh render secret). Báo cáo/KB không chứa password/token/key/connection-string/hash-xác-thực. Fingerprint đối chiếu cố ý không ghi, kể cả dạng rút gọn.
  • WRITE duy nhất: file báo cáo này (scratchpad + KB document).

⚠️ TỰ KHAI MỘT SAI SÓT HYGIENE (không giấu)

Để đo phạm vi rò rỉ (§6.1 — mission §7.1 bắt buộc xác minh), tôi đã đọc giá trị DIRECTUS_ADMIN_PASSWORD/DIRECTUS_ADMIN_TOKEN từ docker/.env vào biến shell trên VPS rồi chạy grep -rlF -- "$V" ….

✅ KHÔNG echo/print giá trị · KHÔNG ghi ra file · KHÔNG vào evidence/report/KB/Git · đã `unset` sau khi dùng
✅ Giá trị KHÔNG bao giờ vào context của agent (chỉ đếm + đường dẫn trả về)
🔴 NHƯNG: `grep -rlF -- "$V"` **expand biến vào argv** ⇒ giá trị **hiện trong `ps`/`/proc/<pid>/cmdline`**
   trên VPS trong ~4 phút quét. Mission §3 ghi rõ "không ghi command line có thể hiện trong ps/history".
   ⇒ **VI PHẠM nhẹ §3.** Giảm nhẹ: host single-tenant, chỉ root đọc được /proc của root; không có
   user khác; không ghi vào shell history (lệnh chạy qua ssh non-interactive, không có HISTFILE).
✅ CÁCH ĐÚNG cho mission sau: đưa pattern qua stdin, KHÔNG qua argv:
   printf '%s' "$V" | grep -rlF -f - /opt/incomex        # value không bao giờ vào argv

Ghi nhận công khai để mission sau (a) biết rủi ro đã phát sinh, (b) dùng đúng cách -f -.

Ghi chú: file báo cáo/evidence là authorized survey artifact theo mission hiện hành — KHÔNG phải production drift, và KHÔNG được commit.

DỪNG sau báo cáo. Không tự soạn hoặc chạy mission triển khai tiếp theo.

Back to Knowledge Hub knowledge/current-state/reports/claude-post-migration-service-secret-controlplane-reconciliation-survey-2026-07-17.md