Kiến trúc tổng thể
Trang này giải thích vì sao hệ thống gồm 11 container với server chỉ nghe loopback sau nginx, và mỗi lựa chọn kiến trúc đánh đổi điều gì.
Vì sao stack có hình dạng như hiện tại: 11 containers, loopback, strip header, JWT verify. Lệnh vận hành: Vận hành hằng ngày.
Sơ đồ runtime (production)
Vì sao server bind loopback?
agentjira_server từng publish 0.0.0.0:8080. Kết hợp danh tính qua header, ai chạm được port đó đều
gửi được x-forwarded-email: attacker@x.com và approve task — merge code agent vào nhánh chính.
Hai chốt độc lập chặn lại:
- Server verify chữ ký Cloudflare Access JWT và bỏ qua
x-forwarded-rolehoàn toàn. - nginx client xoá sạch
X-Forwarded-Email/User/Name/Roletừ request inbound.
Không bao giờ republish 8080 ra 0.0.0.0.
Vì sao nginx đứng giữa?
- Là mặt tiền duy nhất (
:8081) serve SPA, proxy/api+ WebSocket (timeout dài cho stream realtime). - Là nơi strip header danh tính do client gửi — server chỉ tin JWT đã verify hoặc proxy được liệt kê
(
TRUSTED_PROXY_IPS+TRUST_PROXY_IDENTITY=1ở dev). - Ở DEV FULL-STACK,
nginx.dev.conf.templateđóng vai Cloudflare: tiêmX-Forwarded-Emailtừ đúng 1 IP tĩnh (.10) — request từ host tới127.0.0.1:8080bị NAT thành IP gateway bridge nên giả mạo header vẫn fail, nhờ đó pentest Human Gate local mới có ý nghĩa.
WebSocket room theo Space (0C4)
Mặc định (cờ tắt) mọi event tới mọi browser — vừa phí vừa là cross-space read (member Space B
nhận tiêu đề task Space A). Bật AGENTBOARD_WS_TOPICS, socket phải subscribe từng Space và
chỉ nhận event của room đã vào:
broadcast(message, spaceId)—spaceIdbắt buộc;nullchỉ dành cho sự kiện toàn cục (tạo/xoá Space). Fail-closed: socket chưa vào room thì không nhận gì.- Danh tính subscribe lấy từ upgrade request, không tin gì client tự khai; không member →
FORBIDDEN, chưa giải được danh tính →IDENTITY_PENDING(thử lại được, khác với bị từ chối). - Tắt cờ = broadcast toàn cục y Phase 1. Vì bật cờ mà socket không bao giờ nối được thì tính năng
vừa bật coi như không tồn tại — nên thiếu
AGENT_BOARD_PUBLIC_URL/ host ngoàiALLOWED_HOSTSlà server từ chối boot.
Khối 0B — Arkon substrate (gọn)
- Space-membership / provisioning: tạo Space mở transaction → Department Arkon + Wiki Home + 5 persona + mint MCP token (mã hoá at-rest).
- Plan-apply / spec-mirror: plan duyệt xong apply nguyên tử; spec task mirror sang Wiki.
- MCP-adapter: bơm
.mcp.json+.claude/settings.jsonvào worktree mỗi run, thu hồi sau run. - Token housekeeping: user tokens (danh tính thật), persona tokens (
@personatheo task), reconciler Space role →global_rolechạy định kỳ.
11 containers làm gì?
| Nhóm | Container | Vai trò |
|---|---|---|
| Board | agentjira_server, agentjira_client, agentjira_db | API + runtime agent, SPA, Postgres Board |
| Arkon API | arkon_api, arkon_migrator | FastAPI + Alembic migration lúc boot |
| Arkon data | arkon_postgres, arkon_redis, arkon_minio | pgvector, queue MRP, object storage |
| Arkon worker | arkon_worker, arkon_worker_skills | Pipeline biên dịch Wiki, worker skills |
| Arkon UI | arkon_frontend | Portal :3119 |
| Docs (mới) | agentjira_docs | Site tài liệu này :8082 — tĩnh, không DB |