KB-7DA4

LỆNH 3C — Đường nút Balo hoàn chỉnh ở candidate, chưa deploy

13 min read Revision 1
codexreportbalonuxtdotunix-socketcandidate2026-07-16

LỆNH 3C — Đường nút Balo hoàn chỉnh ở candidate, chưa deploy

Ngày thực hiện: 2026-07-16
Verdict: READY_FOR_BALO_SYNC_BUTTON_INSTALL_DEPLOY_GATE
Production: ZERO-TOUCH — chưa cài bridge, chưa deploy Nuxt, chưa WRITE Balo

0. Kết luận điều hành

Candidate đã hoàn chỉnh theo kiến trúc khóa:

Browser
→ POST /api/balo/reconcile
→ request gate + Directus Administrator auth phía server
→ /run/incomex/balo-reconcile.sock
→ fixed-purpose bridge
→ dot-balo-reconcile --sync
→ atomic apply chỉ khi delta an toàn
→ verify delta=0
→ JSON whitelist
→ refresh SharedDirectusTable

Hai quyết định Owner được áp dụng nguyên trạng:

  1. fixed-purpose host bridge qua Unix socket;
  2. chỉ Directus role Administrator được phép vận hành ban đầu.

Không sửa hoặc tái sử dụng executor plan-only. Không cài service, không tạo production socket, không sửa compose, không restart/recreate container, không deploy Nuxt và không push GitHub.

1. Luật, tuyên ngôn và Gate 0

Đã đọc trực tiếp:

  • .claude/skills/incomex-rules.md — 36 mục, 8 bước, hai mũ;
  • Operating Rules SSOT v7.58 rev51;
  • Hiến pháp v4.6.3 rev44;
  • Quy định Balo canonical v1.9 rev15;
  • Điều 35 DOT Governance v5.2 rev13;
  • báo cáo Lệnh 3B rev1.

Ba câu tuyên ngôn:

  1. Vĩnh viễn: một DOT canonical, một bridge fixed-purpose, một endpoint và một vị trí nút theo collection; report Balo tương lai tự thừa hưởng.
  2. Nhầm được không: có; sai role, sai Origin, sai marker, concurrent WRITE hoặc bridge nhận command đều nguy hiểm, nên tất cả lớp đều fail-closed.
  3. Tự động: một thao tác có thẩm quyền chạy trọn preflight → apply nếu cần → verify → refresh, không retry WRITE và không hardcode số live.

Gate 0 source:

DOT_BASE_HEAD=bddfdcc5a504ff6f4c1bc4fdf075dc8dde209402
NUXT_BASE_HEAD=ae694716d5647ce1ed7f3118274645f5d2cf060b
NUXT_UNRELATED_STATUS=? web-rp-current
BACKUP=/opt/incomex/backups/lenh3c-candidate-pre-20260716

Role đọc live:

a40a1070-0b62-4a7e-b2c0-bd4bce9d41ac|Administrator
e7c71c3d-c0a5-4b07-b8f7-53d2dd995384|Agent
c60f9c5e-70ad-4477-96ac-00c28ffe7935|Editor
81a7571c-1cab-4978-8f36-bc9e1406c3b9|tac-admin

Allowlist server/private dùng đúng Administrator role ID, không dựa riêng vào tên. Config rỗng trả 503/HOLD_AUTH_NOT_CONFIGURED.

2. DOT canonical --sync và singleton lock

File:

/opt/incomex/dot/bin/dot-balo-reconcile
sha256=a14eb489509280449924e09ae45415a2ab0086294797dfe739e49c481293dc9a

Giữ nguyên --dry-run, --apply, --verify; thêm --sync dùng lại hàm đọc SQL canonical hiện có, không nhân đôi SQL reconcile.

Lock mặc định:

flock -n /run/lock/incomex-balo-reconcile-sync.lock

Lock bao trọn dry-run → apply nếu cần → verify. Kết quả cuối luôn theo contract:

BALO_SYNC_RESULT|status=...|before_total=...|after_total=...|add=...|revive=...|metadata_update=...|mark_missing=...|conflict=...|verified=...

Status whitelist:

UPDATED | ALREADY_UP_TO_DATE | HOLD | FAILED | ALREADY_RUNNING

Lock thật được giữ từ tiến trình khác:

BALO_SYNC_RESULT|status=ALREADY_RUNNING|before_total=0|after_total=0|add=0|revive=0|metadata_update=0|mark_missing=0|conflict=0|verified=false
REAL_LOCK_RC=75

3. Bridge fixed-purpose và systemd template

Source:

/opt/incomex/dot/bin/dot-balo-reconcile-bridge
sha256=6994c34af9f47f92b36bf6e6f60f3172dce4b067dd6213a0a42f0d54a5d7b6be

Protocol duy nhất:

socket=/run/incomex/balo-reconcile.sock
request bytes=BALO_SYNC_V1\n
response=exactly one JSON line
runner argv=[/opt/incomex/dot/bin/dot-balo-reconcile, --sync]
shell=false

Bridge có timeout 120 giây, output cap 64 KiB mỗi stream, exact marker count, exact field set, kiểm kiểu/số/status/exit-code/verified và không trả stdout/stderr thô. Request khác byte contract bị từ chối.

Templates chỉ nằm trong source repo:

/opt/incomex/dot/ops/systemd/incomex-balo-reconcile.service
/opt/incomex/dot/ops/systemd/incomex-balo-reconcile.socket
/opt/incomex/dot/ops/tmpfiles.d/incomex-balo-reconcile.conf

Socket template dùng mode 0660, group số 65533, khớp GID live của user nuxtjs trong container. Installation gate sau phải xác minh lại UID/GID trước khi cài/mount.

SYSTEMD_TEMPLATE_VERIFY|PASS

Test instance dùng socket trong khu evidence, không dùng production socket:

{"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"}
REAL_BRIDGE_TEST_SOCKET_REMOVED=YES
PRODUCTION_SOCKET_EXISTS=NO
EVIDENCE=/opt/incomex/evidence/lenh3c-balo-sync-candidate-20260716

4. Nuxt endpoint, auth và same-origin/CSRF

Endpoint:

POST /api/balo/reconcile
/opt/incomex/docker/nuxt-repo/web/server/api/balo/reconcile.post.ts
sha256=3423f16c38de01a3e190f1ebc8255f625d760c9635959d1585342cad3d397332

Request gate bắt buộc:

method=POST
body=empty
Origin=config.public.siteUrl origin
Sec-Fetch-Site=same-origin
X-Incomex-Action=balo-reconcile-v1

Auth server-side:

  • forward directus_session_token hoặc Firebase __session theo pattern proxy hiện hành; chấp nhận user bearer token nếu request hiện hành có;
  • gọi Directus /users/me?fields=id,status,role.id,role.name;
  • yêu cầu user tồn tại, status=active, role.id nằm trong private baloOperatorRoleIds;
  • không session: 401; sai role: 403; config role rỗng: 503;
  • không tự chèn Directus service token.

Bridge JSON được parse lại theo exact schema ở Nuxt. Mapping:

UPDATED/ALREADY_UP_TO_DATE → 200
ALREADY_RUNNING/HOLD       → 409
FAILED                     → 500

Không retry WRITE.

5. Nút dùng chung và inheritance

Component:

/opt/incomex/docker/nuxt-repo/web/components/BaloSyncButton.vue
sha256=4a66ea7b17ea9dae955169d0783e63b8eacb41ba50e01009ced516527611ce31

Vị trí duy nhất:

SharedDirectusTable title bar
→ cạnh nút “Làm mới”
→ v-if resolvedCollection === 'balo_thuc_the'
→ client role ID thuộc UX allowlist

Sau UPDATED hoặc ALREADY_UP_TO_DATE, component emit synced và gọi đúng handleRefresh() hiện có; không reload app, không broadcast tab. Server vẫn kiểm quyền lại độc lập.

Verifier inheritance:

BALO_UI_INHERITANCE|PASS|current=4|future=1|non_balo_hidden=1

Nghĩa là bốn report Balo hiện tại đều tự có điều kiện nút, fixture report Balo tương lai cũng có, report collection khác bị ẩn. Không hardcode URL/report ID.

6. Test bridge, lock, auth và response

Bridge fake-runner:

delta=0                    PASS
updated                    PASS
hold                       PASS
failed                     PASS
malformed output           PASS fail-closed
timeout                    PASS fail-closed
invalid request bytes      PASS rejected
concurrency                PASS
runner apply workflow      exactly 1
second concurrent request  ALREADY_RUNNING

Output:

Ran 3 tests in 2.109s
OK
DOT_CANDIDATE_VERIFY|PASS

Endpoint fixture chạy Nitro build thật với fake Directus session và fake bridge trên port/socket evidence:

ENDPOINT_FIXTURE|no_session|HTTP=401|PASS
ENDPOINT_FIXTURE|editor|HTTP=403|PASS
ENDPOINT_FIXTURE|agent|HTTP=403|PASS
ENDPOINT_FIXTURE|tac_admin|HTTP=403|PASS
ENDPOINT_FIXTURE|wrong_origin|HTTP=403|PASS
ENDPOINT_FIXTURE|missing_header|HTTP=403|PASS
ENDPOINT_FIXTURE|nonempty_body|HTTP=400|PASS
ENDPOINT_FIXTURE|administrator_updated|HTTP=200|PASS
ENDPOINT_FIXTURE|already_running|HTTP=409|PASS
ENDPOINT_FIXTURE|malformed_fail_closed|HTTP=500|PASS
ENDPOINT_FIXTURE_SUMMARY|PASS|cases=10

Pure contract fixture bổ sung:

BALO_ENDPOINT_FIXTURES|PASS|auth=5|request=6|bridge=8

7. DOT thật delta=0 — chứng minh write=0

Trước/sau/final fingerprint:

2148|1|2148|8ed1cd5d2f93f5f8a970497bd5e4ec87|2148|t|1

Kết quả --sync thật:

TOTAL|2148|2148|0|0|0|0|0
BALO_RESULT|add=0|revive=0|metadata_update=0|mark_missing=0|conflict=0|sequence_safe=t
FINGERPRINT|unchanged=PASS
BALO_SYNC_RESULT|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
REAL_SYNC_RC=0
REAL_SYNC_APPLY_CALLED=NO

Final verifier production read-only:

VERIFY|PASS_RECONCILED|delta=0|write=0

8. Typecheck, build và inventory

corepack pnpm typecheck → PASS
corepack pnpm build     → Build complete

Build có hai warning cũ không thuộc mission: browserslist data cũ và SVG checkerboard escaping; không có error.

Inventory:

base handlers=44, APIs=40
candidate handlers=45, APIs=41
new route=/api/balo/reconcile method=POST
new handler sha256=86df628758862b1d0781b78b013edfc699fe4e9a723aacb9c015db18d6b8947a
Matrix marker files in build=9
Reports no-store/handleRefresh markers=4

Canary source/build:

/knowledge/pivot                                      PASS
/reports                                              PASS
/reports/[report] → report-pg/report-balo-table       PASS
/api/registries-pivot/summary                         PASS
/api/reports/live                                     PASS
/api/balo/reconcile                                   PASS, POST only

9. Git và bundle

DOT commit:

88abb2f79548392794c8962fa443ca76da2664bd
feat(balo): add locked sync bridge candidate
mission status clean

Nuxt commit:

2ea41583422427b6b5e6d886fa7a754198e121b7
feat(reports): add guarded Balo sync candidate
status còn đúng linked worktree có sẵn: ? web-rp-current

DOT bundle mới ngoài worktree:

path=/opt/incomex/artifacts/git-bundles/dot-lenh3c-88abb2f-20260716.bundle
sha256=e73066286e1282339b953292c4c50c5fc51462e8181f5d6185bcb89a546c1d6c
size=863787
ref=88abb2f79548392794c8962fa443ca76da2664bd refs/heads/main
history=complete
git bundle verify=PASS

Không git add -A, không stage web-rp-current, không push GitHub.

10. Production zero-touch

BRIDGE_PRODUCTION_INSTALL=NOT_RUN
SYSTEMD_DAEMON_RELOAD=NOT_RUN
PRODUCTION_SOCKET_CREATED=NOT_RUN
NUXT_DEPLOY=NOT_RUN
SOCKET_MOUNT_TO_NUXT=NOT_RUN
COMPOSE_CHANGE=NOT_RUN
CONTAINER_RECREATE_RESTART_RELOAD=NOT_RUN
BALO_WRITE=NOT_RUN
CRON_CHANGE=NOT_RUN
GITHUB_PUSH=NOT_RUN

Runtime cuối mission:

/incomex-nuxt|started=2026-07-11T02:50:33.302411141Z|restart=0|running=true
/incomex-agent-api-executor|started=2026-06-04T08:46:26.827398196Z|restart=0|running=true
PRODUCTION_OUTPUT_HAS_BALO_ROUTE=NO
PRODUCTION_SOCKET_EXISTS=NO
SYSTEMD_INSTALLED=NO

Production Balo:

total=2148
table=385
view=699
function=654
trigger=410
delta=0
write=0
fingerprint=8ed1cd5d2f93f5f8a970497bd5e4ec87

11. Gate kế tiếp cần Owner

Candidate sẵn sàng cho một mission cài/deploy riêng. Mission đó phải có Owner gate và tối thiểu:

  1. xác minh lại container UID/GID rồi cài tmpfiles/socket/service templates;
  2. cấu hình private/public NUXT_BALO_OPERATOR_ROLE_IDS=a40a1070-0b62-4a7e-b2c0-bd4bce9d41ac;
  3. bind-mount đúng một socket vào Nuxt, không mount Docker socket/DOT/SSH key;
  4. backup/rollback, cài/start bridge, deploy candidate Nuxt;
  5. verify production auth/CSRF/lock/response và nội dung Balo;
  6. chỉ sau production PASS mới cân nhắc sao ngược GitHub trong gate riêng.

Không có việc nào trong danh sách trên được tự thực hiện ở Lệnh 3C.

12. Evidence paths

DOT repo=/opt/incomex/dot
Nuxt repo=/opt/incomex/docker/nuxt-repo
backup=/opt/incomex/backups/lenh3c-candidate-pre-20260716
evidence=/opt/incomex/evidence/lenh3c-balo-sync-candidate-20260716
build log=/opt/incomex/evidence/lenh3c-balo-sync-candidate-20260716/nuxt-build.log
endpoint fixture log=/opt/incomex/evidence/lenh3c-balo-sync-candidate-20260716/endpoint-fixture-nitro.log
bundle=/opt/incomex/artifacts/git-bundles/dot-lenh3c-88abb2f-20260716.bundle
report=knowledge/current-state/reports/codex-lenh-3c-balo-sync-candidate-2026-07-16.md

CHỐT: READY_FOR_BALO_SYNC_BUTTON_INSTALL_DEPLOY_GATE. DỪNG tại Owner gate; chưa install bridge, chưa deploy Nuxt, chưa WRITE production.