LỆNH 3C — Đường nút Balo hoàn chỉnh ở candidate, chưa deploy
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:
- fixed-purpose host bridge qua Unix socket;
- 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:
- 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.
- 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.
- 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_tokenhoặc Firebase__sessiontheo 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.idnằm trong privatebaloOperatorRoleIds; - 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:
- xác minh lại container UID/GID rồi cài tmpfiles/socket/service templates;
- cấu hình private/public
NUXT_BALO_OPERATOR_ROLE_IDS=a40a1070-0b62-4a7e-b2c0-bd4bce9d41ac; - bind-mount đúng một socket vào Nuxt, không mount Docker socket/DOT/SSH key;
- backup/rollback, cài/start bridge, deploy candidate Nuxt;
- verify production auth/CSRF/lock/response và nội dung Balo;
- 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.