Access Hub Agent
Đồng bộ từ mã nguồn lúc 10:57, 03/10/2026
Skip to content

L3 - Monitoring Platform - Agent - Enroll, Credentials và Config (Danh tính, cấu hình, CLI) ​

Trạng thái
Bản nháp
Phiên bản
0.1, ngày 30/09/2026

Ghi chú: khi tài liệu và mã khác nhau, mã thắng

Đã làm nghĩa là có mã và kiểm thử trong repo. Chỉ thiết kế nghĩa là có trong tài liệu nhưng chưa có mã. Chỗ lệch được ghi ở mục nợ kỹ thuật.

Thông tin tài liệu đầy đủ
TrườngGiá trị
Tên trangL3 - Monitoring Platform - Agent - Enroll, Credentials và Config
Trạng tháiBẢN NHÁP (tài liệu chưa sẵn sàng trình thẩm định)
Phiên bảnv0.1, 2026-09-30: bản nháp đầu tiên, dựng từ mã tại commit a23f929 (internal/enroll, internal/creds, internal/hostinfo, internal/config, internal/cli, phần nạp lại trong internal/agent/agent.go) và hợp đồng Agent - Collector ở docs/10-wire-contract.md. Phần internal/cli/cli.go đang bị sửa chưa commit bởi AGT-6 (lệnh service, uninstall), xem L2 D-13
Tên dự ánMonitoring Platform (Access Hub Monitoring)
Bên thẩm định / Phê duyệtchưa chỉ định. Chưa ai sign-off
Tài liệu tầng trên (Parent)L2 - Monitoring Platform - Agent - SAD, thành phần CMP-6 Enroll và Credentials, CMP-7 Config Loader (mục 2.3) và phần nạp lại cấu hình, CLI của CMP-1 Runtime Core (L2 D-14). FR-01, FR-02, FR-03, FR-07, FR-14, FR-15, FR-16, FR-17, FR-20, FR-21, L2-NFR-06, 12, 13. Truy vết tiếp lên L1: L1 HLD mục 2 (Mục tiêu 5), mục 3 (T7)
Tài liệu anh emL3 Collectors, L3 WAL và Sender, L3 Service và Packaging
Mục lục0 Governance, 1 Phạm vi, 2 Yêu cầu, 3 Kiến trúc, 4 Domain model, 5 Hợp đồng API, 6 Dữ liệu vật lý, 7 Thuật toán, 8 Xử lý lỗi, 9 Suy thoái, 10 Đồng thời, 11 Bảo mật, 12 Cấu hình, 13 Telemetry, 14 Kiểm thử, 15 Trình tự xây dựng, Phụ lục A, B, C

Quy ước nhãn trạng thái (giống L2): ĐÃ HIỆN THỰC (đã xác minh trong mã), THIẾT KẾ, CHƯA XÂY (chỉ có trong tài liệu thiết kế), MỘT PHẦN, ĐỀ XUẤT. Khi mã và tài liệu khác nhau, mã thắng và sai lệch được nêu trong tài liệu này và gom về L2 mục 16.2. Mọi con số chưa có trong mã hoặc giao thức được gắn nhãn "đề xuất".


0. Front Matter & Approvals ​

Governance metadata

TrườngGiá trị
ComponentCMP-6 Enroll và Credentials (internal/enroll, internal/creds, internal/hostinfo), CMP-7 Config Loader (internal/config), cộng lớp CLI (internal/cli: enroll, check-config, collect-once, status, diag, khung lệnh, mã thoát) và đường nạp lại SIGHUP (internal/cli/run.go, Agent.Reload)
Truy vết L2L2-SAD-agent.md mục 2.3, 3 (FR-01 đến FR-03, FR-07, FR-14 đến FR-17, FR-20, FR-21), 4 (L2-NFR-06, 12, 13), 7, 16 (R-01, R-02, R-06, D-01, D-04, D-05, D-08, D-11, D-13, D-14)
Phân loại rủi roĐề xuất, chưa xác nhận: Tier 2, cao hơn mức kế thừa của L2 mục 14. Lý do: đây là nơi duy nhất agent giữ bí mật dài hạn (agent_token, License) và nơi quyết định agent nói chuyện với Collector nào (R-02, R-06)
Data classificationBí mật cho License (dùng một lần, sống ngắn) và agent_token (dài hạn, văn bản rõ trong credentials.json). Nội bộ cho agent.yaml, hostname, IP, thông tin hệ điều hành, machine_id đã băm. machine_id thô không bao giờ rời máy
Blast radiusMột máy chủ. Lỗi nặng nhất: rò agent_token cho phép đẩy số liệu giả đúng cho máy đó (R-02). Enroll lỗi làm mất một License (token dùng một lần, phải xin lại). Cấu hình sai làm agent không khởi động (mã 3), không ảnh hưởng máy khác

Sign-off gate

Vai tròTênTrách nhiệm duyệtTrạng tháiNgày
Tech lead thành phầnchưa chỉ địnhĐúng đắn của luồng enroll, lưu thông tin đăng nhập, nạp cấu hìnhChưa duyệtchưa có
SA hệ thống Agentchưa chỉ địnhNhất quán với L2 và giao thứcChưa duyệtchưa có
Bảo mậtchưa chỉ địnhXử lý token, quyền tệp, thứ tự ưu tiên cấu hình, hiển thị bí mậtChưa duyệtchưa có
SA Access Hubchưa chỉ địnhNhất quán quy tắc ràng buộc License với Server (422 binding_failed)Chưa duyệtchưa có

1. Component Scope & Non-Goals ​

Vai trò: cụm này là danh tính và cấu hình cục bộ của agent. Nó gồm ba việc: (1) đổi License dùng một lần lấy agent_id và agent_token rồi lưu bền (mẫu trao đổi thông tin xác thực một lần có lưu nguyên tử), (2) nạp cấu hình YAML nghiêm ngặt theo thứ tự cờ, biến môi trường, tệp, mặc định (mẫu cấu hình phân lớp có kiểm tra toàn bộ lỗi một lượt), (3) cung cấp khung CLI và mã thoát ổn định cho người vận hành. Mọi thứ chạy cục bộ, chỉ enroll gọi mạng (một lần POST /agent/v1/enroll).

Sơ đồ ngữ cảnh

mermaid
flowchart LR
  classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff
  classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff
  classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff
  classDef sensitive fill:#5a2d2d,stroke:#d96f6f,color:#fff
  classDef infra fill:#444,stroke:#aaa,color:#fff

  ADM(["Quản trị viên máy chủ"]):::infra
  COLL(["Collector: /agent/v1/enroll"]):::infra
  FS[("agent.yaml, credentials.json, license.token")]:::datastore

  subgraph BC["Enroll, Credentials, Config"]
    CLI["CLI và mã thoát"]:::owned
    ENR["Enroll"]:::owned
    CRD["Credentials Store"]:::sensitive
    CFG["Config Loader"]:::owned
  end

  RT["Runtime: Run và Reload"]:::bc

  ADM -->|"lệnh và tín hiệu"| CLI
  CLI -->|"nạp cấu hình"| CFG
  CLI -->|"enroll"| ENR
  ENR -->|"POST enroll"| COLL
  ENR -->|"lưu nguyên tử"| CRD
  CRD -->|"tệp 0600"| FS
  CFG -->|"đọc YAML"| FS
  RT -->|"Load và Reload"| CFG
  RT -->|"Load thông tin đăng nhập"| CRD

Mô tả quan hệ

ChiềuBênNội dung
VàoQuản trị viênLệnh CLI, cờ (--token, --token-file, --token-stdin, --force, --config, --collector, --state-dir, --log-level), biến môi trường (AH_LICENSE, AH_LICENSE_FILE, AH_CONFIG, AH_COLLECTOR_URL, AH_STATE_DIR, AH_LOG_LEVEL, AH_INSECURE_SKIP_VERIFY), tệp agent.yaml, tệp license.token, tín hiệu SIGHUP và SIGUSR1
VàoHệ điều hành/etc/machine-id (hoặc /var/lib/dbus/machine-id), hostname, địa chỉ giao diện, /etc/os-release, /proc/sys/kernel/osrelease
RaCollectorMột POST /agent/v1/enroll (không có header Authorization). Phản hồi: agent_id, agent_token, collector_url, config, server_time_ms
RaThư mục state_dircredentials.json (0600, thư mục 0700)
RaRuntime*config.Config (đã kiểm tra), *creds.Credentials, ErrNotEnrolled, mã thoát
RaNgười vận hànhThông điệp lỗi có số dòng, gợi ý xử lý theo mã lỗi enroll, mã thoát 0 đến 6

Trong phạm vi (bao phủ)

Trong phạm viGhi chú
Phân giải License theo nguồn và thứ tự ưu tiên, kiểm tra hợp lệ, xóa an toàn tệp tokeninternal/enroll/enroll.go. ĐÃ HIỆN THỰC
Luồng enroll.Do: gom thông tin máy, gọi POST /enroll, lưu thông tin đăng nhập, chặn enroll lặpinternal/enroll/enroll.go. ĐÃ HIỆN THỰC
Kho creds.Store: lưu nguyên tử 0600, nạp có kiểm tra quyền, che token, MachineID băm có muốiinternal/creds/creds.go. ĐÃ HIỆN THỰC (Linux)
Gom thông tin máy: hostname, IP dùng được (tối đa 16), thông tin hệ điều hànhinternal/hostinfo/hostinfo.go. ĐÃ HIỆN THỰC
Nạp cấu hình: mặc định, YAML nghiêm ngặt, biến môi trường, cờ, kiểm tra toàn bộ, báo số dònginternal/config/load.go, types.go, validate.go, defaults.go. ĐÃ HIỆN THỰC
Che thông tin nhạy cảm trong URL hiển thị (RedactURL)internal/config/redact.go. ĐÃ HIỆN THỰC
Khung CLI: bảng lệnh, cờ chung, mã thoát, trợ giúp, parseinternal/cli/cli.go. ĐÃ HIỆN THỰC
Lệnh enroll, check-config, collect-once, versioninternal/cli/enroll.go, checkconfig.go, collectonce.go, version.go. ĐÃ HIỆN THỰC
Nạp lại SIGHUP cho cấu hình cục bộ, giữ cấu hình cũ nếu lỗiinternal/cli/run.go, Agent.Reload. ĐÃ HIỆN THỰC. Chỉ cập nhật tham số ở L3 WAL và Sender FR-W16
Kiểm tra danh tính máy lúc chạy (identity_mismatch)internal/agent/agent.go. ĐÃ HIỆN THỰC, mô tả chi tiết ở L3 WAL và Sender FR-W14. Tài liệu này mô tả phía machine_id và enroll --force
status, diagTHIẾT KẾ, CHƯA XÂY. Hai lệnh là stub trả thông báo và mã 1 (L2 D-04, AGT-10)
Cấu hình từ xa (GET /config theo ETag, allow_remote_config)THIẾT KẾ, CHƯA XÂY (L2 D-01, AGT-10). Khóa allow_remote_config được đọc nhưng không dùng
Xoay token POST /credentials/renewĐÃ HIỆN THỰC phía agent: internal/renew, creds.TokenHolder, transport.Client.Renew. Chờ Access Hub endpoint renew để chạy thật (L2 D-08)
Lưu token bằng DPAPI, kiểm tra ACL trên WindowsTHIẾT KẾ, CHƯA XÂY (L2 D-05, AGT-7)

Ngoài phạm vi

Không thuộc BCThuộc về
Đóng lô, WAL, gửi, backoff, xử lý 401 và 403 khi chạyL3 WAL và Sender
Đọc /proc, danh sách trắng mẫu, cắt seriesL3 Collectors
Tạo tài khoản accesshub-agent, thư mục /etc/accesshub-agent, unit systemd, script gói gọi enroll, install.shL3 Service và Packaging
Phát hành, ràng buộc, thu hồi License, quy tắc khớp Server theo IP và hostnameAccess Hub và Collector (quyết định ở Access Hub, giao thức mục 2.2)
Vòng chu kỳ, dừng êm, log (logx), thống kêRuntime Core (L2 D-14). Chỉ phần nạp lại và kiểm tra danh tính được nêu ở đây

2. Detailed Requirements & Acceptance Criteria ​

Functional Requirements

#Trách nhiệmGiải thíchHiện thực ở
FR-E01Nguồn LicenseBa cờ loại trừ nhau: --token, --token-file, --token-stdin. Nếu không có cờ, thử AH_LICENSE_FILE, rồi AH_LICENSE, rồi tệp mặc định license.token (nếu tồn tại). Nhiều cờ cùng lúc là lỗi. --token in cảnh báo vì lộ trong danh sách tiến trìnhenroll.go (ResolveToken). ĐÃ HIỆN THỰC
FR-E02Kiểm tra tokenCắt khoảng trắng hai đầu, từ chối rỗng, dài quá MaxTokenLen (256), hoặc có khoảng trắng bên trong. Đọc stdin và tệp bị chặn bằng MaxTokenLen*4 byteenroll.go (validated, fromFile). ĐÃ HIỆN THỰC
FR-E03Tệp token an toànTệp token có quyền rộng hơn 0600 thì cảnh báo "run chmod 600". Tệp chỉ định tường minh không có thì lỗi, tệp mặc định không có thì coi như không có token. Sau enroll thành công, mọi tệp token đã dùng (từ --token-file, AH_LICENSE_FILE hoặc tệp mặc định) bị ghi đè số không, Sync, rồi xóa. Enroll bị từ chối hoặc lỗi thì tệp được giữenroll.go (fromFile, Wipe), cli/enroll.go. ĐÃ HIỆN THỰC
FR-E04Gom thông tin máyHostname, tối đa 16 IP unicast toàn cục (bỏ loopback, link-local, multicast, không xác định, loại trùng), hệ điều hành (family, name, version, kernel, arch) từ os-release và kernel, có dự phòng bằng runtime.GOOS và GOARCHhostinfo.go (Gather, UsableIPs, ReadOS). ĐÃ HIỆN THỰC
FR-E05machine_id bămsha256("accesshub-agent/machine-id/v1:" + machine-id) dạng hex. Không tìm được machine-id thì lỗi no machine id found. machine_id thô không rời máycreds.go (MachineID). ĐÃ HIỆN THỰC
FR-E06Đổi token lấy danh tínhPOST /agent/v1/enroll không có Authorization, mang license, hostname, ip_addresses, machine_id, agent_version, os. Nhận agent_id, agent_token. Lưu agent_id, agent_token, collector_url (của phản hồi), machine_id, enrolled_atenroll.go (Do). ĐÃ HIỆN THỰC. Trường config của phản hồi bị bỏ qua (L2 D-01, D-11)
FR-E07Chặn enroll lặpĐã có credentials.json và không có --force thì từ chối trước khi gọi mạng, nêu agent_id hiện có. Có --force thì enroll lại và ghi đè. Agent cũ không bị thu hồi phía Collector bởi lệnh nàyenroll.go (Do, ErrAlreadyEnrolled). ĐÃ HIỆN THỰC
FR-E08Lưu thất bại vẫn báo đúngNếu Collector đã trả token nhưng ghi đĩa lỗi, trả *SaveError nói rõ token đã bị tiêu hao và cần token mới. Tệp token không bị xóaenroll.go (SaveError), cli/enroll.go. ĐÃ HIỆN THỰC
FR-E09Ánh xạ lỗi enroll ra mã thoátThiếu hoặc sai token, cờ xung đột: mã 2. Collector từ chối (400, 401, 403, 409, 422, 426): mã 5 kèm gợi ý theo code (token_used, already_enrolled, binding_failed, 426). Collector lỗi khác hoặc lỗi mạng: mã 6. Đã enroll, lỗi ghi: mã 1. Cấu hình sai hoặc không dựng được client: mã 3cli/enroll.go (reportEnrollError). ĐÃ HIỆN THỰC
FR-E10Kho thông tin đăng nhậpGhi credentials.json bằng tệp tạm .credentials-*.tmp cùng thư mục: Chmod 0600 trước khi ghi, Write, Sync, Close, Rename, rồi Sync thư mục. Thư mục MkdirAll 0700. Lỗi thì dọn tệp tạm và giữ tệp cũcreds.go (Save). ĐÃ HIỆN THỰC
FR-E11Nạp có kiểm traKhông có tệp: ErrNotEnrolled. Trên không phải Windows, quyền có bit 0o077: ErrInsecurePermissions. JSON hỏng hoặc thiếu agent_id hay agent_token: lỗi. Windows không kiểm tra ACLcreds.go (Load). ĐÃ HIỆN THỰC (Linux)
FR-E12Che tokenCredentials.String() và LogValue() che token. Result của enroll không chứa tokencreds.go, enroll.go. ĐÃ HIỆN THỰC
FR-E13Nạp cấu hình phân lớpThứ tự: Default(), giải mã YAML KnownFields(true), applyEnv (chỉ 4 biến), applyOverrides (3 cờ), Validate, rồi cắt / cuối của collector_url. Cờ thắng env thắng tệp thắng mặc địnhload.go. ĐÃ HIỆN THỰC
FR-E14Đường dẫn cấu hìnhThứ tự: cờ --config, biến AH_CONFIG, mặc định theo nền tảng. Đường dẫn chỉ định tường minh mà tệp không có thì lỗi. Tệp mặc định không có thì dùng mặc định và envload.go (ResolvePath), defaults.go. ĐÃ HIỆN THỰC
FR-E15Báo toàn bộ lỗi một lượtKhóa lạ, sai kiểu, giá trị ngoài miền đều thành Issue có số dòng (khi có), gom hết rồi báo. Lỗi cú pháp YAML dừng trước env. Lỗi biến AH_INSECURE_SKIP_VERIFY không phải bool thành một issueload.go, validate.go. ĐÃ HIỆN THỰC
FR-E16Miền giá trị cấu hìnhcollector_url bắt buộc, chỉ https, có host, không user info, query, fragment. interval 10 s đến 300 s. send_timeout 1 s đến 1 phút. collect_timeout 1 s đến 30 s và nhỏ hơn interval. buffer.max_bytes 4 MiB đến 1 GiB. buffer.max_age 1 phút đến 7 ngày. catch_up_batches 1 đến 10. max_series 1 đến 500. memory_limit 16 MiB đến 4 GiB. Mức log, định dạng log, regex (tối đa 200 ký tự, biên dịch được), checks (tối đa 50), labels (tối đa 20, khóa dành riêng bị cấm, giá trị tối đa 128 byte)validate.go. ĐÃ HIỆN THỰC
FR-E17check-configNạp cấu hình, in config OK hoặc no config file ..., using defaults and environment, in tóm tắt (URL, thư mục state, chu kỳ, đệm, số series, log, proxy đã che). In WARNING nếu insecure_skip_verify bật. Không gọi mạngcli/checkconfig.go. ĐÃ HIỆN THỰC. Giao thức nói lệnh này dùng ping, mã không làm (OQ-E2)
FR-E18collect-onceThu một lần (hai mẫu cách nhau --gap, mặc định 1 s, 100 ms đến 1 phút) và in JSON (time, samples, dropped, errors, duration). Không gọi Collector, dùng URL giữ chỗ khi chưa cấu hình. Phần thu thuộc L3 Collectorscli/collectonce.go. ĐÃ HIỆN THỰC
FR-E19Nạp lại SIGHUPKhi nhận SIGHUP (Linux), nạp lại từ cùng Options, cập nhật mức log, gọi Agent.Reload. Lỗi bất kỳ thì ghi config reload rejected, keeping the running configuration và giữ cấu hình cũ. SIGUSR1 ghi log Status()cli/run.go, signals_unix.go. ĐÃ HIỆN THỰC. Windows không có tín hiệu (signals_windows.go)
FR-E20Khung CLIBảng lệnh run, enroll, status, version, check-config, diag, service, uninstall, collect-once. Không có lệnh hoặc lệnh lạ: mã 2. Cờ sai hoặc tham số thừa: mã 2. -h: mã 0. Mã thoát 0 OK, 1 lỗi, 2 cách dùng, 3 cấu hình, 4 chưa enroll, 5 enroll bị từ chối, 6 không kết nối đượccli/cli.go. ĐÃ HIỆN THỰC. service và uninstall thuộc AGT-6, chưa commit
FR-E21run liên quan danh tínhrun nạp cấu hình, dựng log, đặt GOMEMLIMIT nếu env chưa đặt, chạy Agent.Run. Chưa enroll: thông điệp this host is not enrolled, run 'accesshub-agent enroll' first và mã 4. Các lỗi khác của Run (kể cả ErrInsecurePermissions): ghi log và mã 1cli/run.go. ĐÃ HIỆN THỰC
FR-E22status, diagHiện là stub in not implemented yet (planned in AGT-10) và mã 1cli/cli.go (stub). THIẾT KẾ, CHƯA XÂY (L2 D-04)
FR-E23Cấu hình từ xaThăm dò GET /config mỗi 300 s với jitter ±20% và khi config_etag đổi, áp dụng khóa cho phép, allow_remote_config:false thì bỏ qua trừ min_agent_version, đổi collector_url từ xa chỉ từ giai đoạn 3 (ADR 0003)THIẾT KẾ, CHƯA XÂY (L2 D-01, AGT-10). Mã: không gán OnConfigETag, không ghi remote-config.json
FR-E24Xoay tokenPOST /credentials/renew mỗi 30 ngày, token cũ còn hiệu lực thêm 24 giờĐÃ HIỆN THỰC phía agent (internal/renew, cấu hình credentials.renew_after). Token mới ghi credentials.json (kèm token_issued_at) trước khi dùng; lỗi ghi thì giữ token cũ và thử lại sau 1 giờ (Access Hub cho phép thử lại bằng token cũ); 403 dừng; 503 và 429 thử lại theo Retry-After hoặc 1 giờ
FR-E25Bảo vệ token trên WindowsDPAPI và ACLTHIẾT KẾ, CHƯA XÂY (L2 D-05, AGT-7)

Non-Functional Requirements

NFRTarget (Ý nghĩa)Parent L2-NFR (Kiểu)Satisfied-by (Tactic → Mục)
NFR-E01Token (enroll và agent) không xuất hiện trong log, lỗi, String(), đầu ra CLIL2-NFR-13 (Allocated)LogValue che token, Result không có token, lỗi mạng chỉ mang URL (11). TestCredentialsNeverPrintTheToken, TestTokenNeverReachesTheLogs
NFR-E02credentials.json chỉ chủ sở hữu đọc được, không bao giờ ở trạng thái ghi dởL2-NFR-12 và L2-NFR-07 (Allocated một phần)Chmod 0600 trước ghi, tệp tạm rồi Rename (7.4.1). TestSaveLoadRoundTripWith0600, TestSaveIsAtomicAndLeavesNoTempFiles
NFR-E03Từ chối thông tin đăng nhập có quyền rộng trên LinuxL2-NFR-12 (Allocated)Load kiểm mode & 0o077 (7.3). TestLoadRejectsLoosePermissionsCorruptAndIncomplete
NFR-E04Enroll không bao giờ làm mất token âm thầm: hoặc lưu được, hoặc báo rõ token đã tiêu haoL2 FR-01 (Owned)SaveError, không xóa tệp token khi lỗi (7.2 luồng 5). TestEnrollSaveFailureIsReportedAndKeepsTokenFile
NFR-E05Đầu vào token bị chặn kích thước, không đọc vô hạnKế thừa ANFR (bảo mật) (Owned)LimitReader và kiểm MaxTokenLen*4 (11). TestResolveTokenErrors
NFR-E06Cấu hình sai không bao giờ làm agent chạy với cấu hình nửa vời: khởi động thì thoát mã 3, nạp lại thì giữ cấu hình cũL2 FR-14, FR-15 (Owned)Validate trả lỗi trước khi dùng (7.2 luồng 7, 8). TestRunRejectsBadConfig, TestSignalsReloadAndStatus
NFR-E07Mã thoát ổn định để script gói và người vận hành dựa vàoL2 FR-16 (Owned)Hằng Exit* (5.1). TestEnrollRejections, TestEnrollConnectionProblemsExitSix, TestRunWithoutCredentialsExitsNotEnrolled
NFR-E08Nạp cấu hình không panic với đầu vào bất kỳKế thừa ANFR (độ tin cậy) (Owned)FuzzLoadNeverPanics
NFR-E09collector_url bắt buộc HTTPS, không thông tin người dùng trong URLL2-NFR-11 (Allocated một phần)validate.go (11). Xác minh chứng chỉ thuộc L3 WAL và Sender
NFR-E10Khởi động sẵn sàng thu thập dưới 3 giây, phần cấu hình và danh tính đóng góp không đáng kểL2-NFR-06 (Allocated một phần)Chỉ đọc một tệp YAML và một credentials.json. Chưa có phép đo (L2 D-16)

Acceptance Criteria

ACKịch bản đặc tả (Given / When / Then)Truy vết → Test ID
AC-E01Given nhiều nguồn token, When phân giải, Then thứ tự là cờ rồi AH_LICENSE_FILE rồi AH_LICENSE rồi tệp mặc định. Given hai cờ token cùng lúc, Then lỗi xung độtTestResolveTokenPriority, TestLicenseSources
AC-E02Given token rỗng, quá dài, có khoảng trắng bên trong, hoặc không có nguồn, When phân giải, Then lỗi đúng loại và enroll thoát mã 2TestResolveTokenErrors, TestEnrollUsageErrors
AC-E03Given tệp token có quyền rộng, When phân giải, Then cảnh báo chmod 600 nhưng vẫn dùngTestLoosePermissionsWarn
AC-E04Given enroll thành công bằng --token-file, When xong, Then credentials.json 0600 tồn tại và tệp token đã bị ghi đè và xóaTestWipeOverwritesAndRemoves, TestEnrollFromFileStoresCredentialsAndWipesTheFile
AC-E05Given Collector trả 400, 401, 403, 409, 422 hoặc 426, When enroll, Then thông điệp "enrollment rejected" kèm gợi ý theo code và mã thoát 5TestEnrollRejections
AC-E06Given Collector 5xx hoặc không liên lạc được, When enroll, Then mã thoát 6TestEnrollConnectionProblemsExitSix
AC-E07Given đã enroll, When enroll không --force, Then từ chối trước khi gọi mạng. Given --force, Then enroll lạiTestEnrollRefusesWhenAlreadyEnrolledUnlessForced
AC-E08Given Collector trả token nhưng ghi đĩa lỗi, When enroll, Then báo token đã tiêu hao, mã 1, tệp token còn nguyênTestEnrollSaveFailureIsReportedAndKeepsTokenFile
AC-E09Given cấu hình sai, When enroll, Then mã 3 và không gọi mạngTestEnrollRejectsBadConfig
AC-E10Given Save rồi Load, When kiểm, Then dữ liệu khớp và quyền là 0600. Given lỗi ghi, Then tệp cũ còn nguyên, không còn tệp tạmTestSaveLoadRoundTripWith0600, TestSaveIsAtomicAndLeavesNoTempFiles, TestFailedSaveKeepsOldFile
AC-E11Given tệp quyền rộng, hỏng, hoặc thiếu trường, When Load, Then từ chối với lỗi phân biệtTestLoadRejectsLoosePermissionsCorruptAndIncomplete
AC-E12Given Credentials được in hoặc ghi log, When kiểm, Then token không xuất hiệnTestCredentialsNeverPrintTheToken, TestTokenNeverReachesTheLogs
AC-E13Given Delete và Exists, When gọi, Then xóa tệp không có cũng không lỗiTestDeleteAndExists
AC-E14Given cùng machine-id, When tính, Then kết quả là băm có muối ổn định, khác machine-id thôTestMachineIDIsSaltedHash
AC-E15Given giao diện mạng có địa chỉ đủ loại, When lọc, Then chỉ giữ unicast toàn cục, loại trùng, tối đa 16. Given thiếu os-release, Then dự phòngTestUsableIPsFiltersAndCaps, TestReadOS, TestReadOSMissingFilesFallBack, TestGatherDoesNotPanic
AC-E16Given tệp tối thiểu, When nạp, Then áp đủ mặc định. Given mẫu đầy đủ trong docs, Then nạp đúngTestLoadMinimalAppliesDefaults, TestLoadFullSampleFromDocs
AC-E17Given nhiều lỗi trong tệp, When nạp, Then mọi lỗi được báo một lượt, có số dòng và tên tệpTestLoadErrorsCarryLineNumbers, TestLoadReportsAllIssues, TestErrorFormatIncludesFileAndLine
AC-E18Given tệp không có, When nạp, Then lỗi nếu chỉ định tường minh, không lỗi nếu mặc định. Given tệp rỗng, Then đòi collector_urlTestMissingFile, TestEmptyFileNeedsURL
AC-E19Given cờ, env, tệp cùng đặt một khóa, When nạp, Then cờ thắng env thắng tệp. Given AH_INSECURE_SKIP_VERIFY, Then phân tích boolTestPrecedenceFlagsOverEnvOverFile, TestEnvInsecureSkipVerify, TestResolvePath
AC-E20Given kích thước và URL, When phân tích, Then ParseSize đúng và RedactURL che mật khẩu. Given đầu vào bất kỳ, Then không panicTestParseSize, TestRedactURL, FuzzLoadNeverPanics
AC-E21Given check-config, When cấu hình hợp lệ, hỏng, dùng env, hoặc tệp tường minh không có, Then in kết quả và mã đúngTestCheckConfigValid, TestCheckConfigReportsLineNumbers, TestCheckConfigUsesEnvConfigPathAndOverrides, TestCheckConfigMissingExplicitFile
AC-E22Given không đối số, lệnh lạ, cờ sai, When chạy, Then mã 2. Given -h, Then liệt kê đủ lệnh. Given version, Then in phiên bảnTestNoArgsAndUnknownCommandAreUsageErrors, TestFlagErrorsAreUsageErrors, TestHelpListsEveryCommand, TestVersionOutput
AC-E23Given status hoặc diag, When chạy, Then thông báo chưa hiện thực và mã khác 0TestStubsFailClearly
AC-E24Given run với cấu hình sai hoặc chưa enroll, When chạy, Then mã 3 hoặc mã 4. Given ngữ cảnh bị hủy, Then dừngTestRunRejectsBadConfig, TestRunWithoutCredentialsExitsNotEnrolled, TestRunStopsOnContextCancel
AC-E25Given SIGHUP và SIGUSR1, When nhận, Then nạp lại cấu hình và ghi trạng tháiTestSignalsReloadAndStatus
AC-E26Given collect-once, When chạy, Then in JSON hợp lệ. Given --gap ngoài miền hoặc cấu hình sai, Then từ chốiTestCollectOncePrintsJSON, TestCollectOnceRejectsBadGapAndConfig

Quality Attribute Scenarios

Mã kịch bản / NFRNguồn & Kích thíchMôi trườngPhản hồi của hệ thống (Tactic)Thước đo chất lượng (Measure)
QAS-E01 / NFR-E01Người vận hành chạy enroll --token hoặc có lỗi khi ghi logMáy nhiều người dùngCảnh báo lộ trong danh sách tiến trình, token che ở String() và log, Result không có tokenKhông chuỗi token nào trong log và đầu ra. --token vẫn hiện ở ps (khuyến nghị dùng tệp hoặc stdin)
QAS-E02 / NFR-E02Mất điện đúng lúc ghi credentials.jsonEnroll hoặc enroll lạiTệp tạm rồi Rename cùng thư mục, Sync thư mụcTệp cũ còn nguyên hoặc tệp mới đầy đủ. Không có tệp nửa chừng. Kiểm bằng TestFailedSaveKeepsOldFile (lỗi ghi, không mô phỏng mất điện)
QAS-E03 / NFR-E04Đĩa đầy hoặc thư mục state không ghi được sau khi Collector đã cấp tokenEnroll*SaveError, thông điệp nói token đã tiêu hao, giữ tệp tokenNgười vận hành biết phải xin token mới. Phía Collector token vẫn bị đánh dấu đã dùng
QAS-E04 / NFR-E06Quản trị viên sửa agent.yaml sai rồi gửi SIGHUPAgent đang chạyNạp lại thất bại, ghi log, giữ cấu hình cũAgent không dừng, không đổi hành vi. Log chứa toàn bộ lỗi có số dòng
QAS-E05 / NFR-E03Ai đó chmod 644 credentials.jsonAgent khởi động lạiLoad trả ErrInsecurePermissions, run ghi log và thoát mã 1Agent không chạy với tệp token lộ quyền. Mã 1 chứ không phải mã 4 (OQ-E6)
QAS-E06 / NFR-E07Script gói gọi enroll bằng stdinCài đặt tự độngMã thoát phân loại: 2 cách dùng, 3 cấu hình, 5 bị từ chối, 6 không kết nốiScript phân biệt được lỗi người dùng và lỗi mạng mà không đọc thông điệp

3. Kiến trúc ứng dụng ​

3.1. Kiến trúc runtime ​

Cụm này không có tiến trình hay goroutine riêng. Nó là thư viện được gọi từ ba điểm vào trong cùng một tiến trình: lệnh enroll (chạy một lần rồi thoát), lệnh run (nạp cấu hình và thông tin đăng nhập lúc khởi động, sau đó nạp lại cấu hình khi có SIGHUP) và lệnh check-config hoặc collect-once (chỉ đọc). Chỉ có một goroutine nền liên quan: handleSignals trong internal/cli/run.go, nó gọi config.Load rồi Agent.Reload (con trỏ cấu hình nguyên tử).

mermaid
flowchart LR
  classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff
  classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff
  classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff
  classDef sensitive fill:#5a2d2d,stroke:#d96f6f,color:#fff
  classDef infra fill:#444,stroke:#aaa,color:#fff

  CLI["Khung CLI · parse, mã thoát"]:::owned
  ENR["Enroll · ResolveToken, Do, Wipe"]:::owned
  CFG["Config Loader · Load, Validate"]:::owned
  CRD["Credentials Store · Load, Save"]:::sensitive
  HI["Hostinfo · Gather"]:::owned
  FILES[("agent.yaml, credentials.json, license.token")]:::datastore
  TRN["Transport Client · Enroll"]:::bc
  COLL(["Collector · POST enroll"]):::infra

  CLI -->|"Load"| CFG
  CLI -->|"ResolveToken, Do"| ENR
  ENR -->|"Gather, MachineID"| HI
  ENR -->|"Enroll"| TRN
  TRN -->|"HTTPS protobuf"| COLL
  ENR -->|"Save"| CRD
  CRD -->|"tệp 0600"| FILES
  CFG -->|"đọc YAML"| FILES
  CLI -.->|"đọc token, Wipe"| FILES

Chú giải: nét liền là lời gọi đồng bộ trong cùng goroutine (Transport là HTTP chặn tới send_timeout), nét đứt là thao tác tệp trực tiếp của lớp CLI. Lúc chạy thường (run), chỉ còn đường CLI → Config và Runtime → Credentials (xem L3 WAL và Sender).

Bảng connector

ConnectorTừTớiCơ chếĐồng bộGhi chú
CN-E1Lệnh CLIConfig Loaderconfig.Load(Options)Đồng bộMọi lệnh đọc cấu hình trừ version. Lỗi trả *config.Errors
CN-E2enrollEnrollResolveToken(Source), Do(ctx, Params), Wipe(path)Đồng bộDo là nơi duy nhất trong cụm này gọi mạng
CN-E3EnrollHostinfohostinfo.Gather() và creds.MachineID()Đồng bộĐọc /etc/machine-id, os-release, giao diện mạng. cli/enroll.go gọi readMachineID trước khi dựng client
CN-E4EnrollTransportClient.Enroll(ctx, *EnrollRequest)Đồng bộ, chặn tới send_timeoutChi tiết ở L3 WAL và Sender. Không thử lại
CN-E5EnrollCredentials StoreStore.Exists, Store.Load, Store.SaveĐồng bộSave chặn tới fsync xong
CN-E6RuntimeCredentials Storecreds.NewStore(cfg.StateDir).Load()Đồng bộMỗi lần Agent.Run bắt đầu. Không đọc lại khi chạy
CN-E7Handler tín hiệuConfig Loaderconfig.Load(opts) rồi log.SetLevel, Agent.Reload(cfg)Đồng bộ trong goroutine handleSignalsReload chỉ atomic.Store con trỏ cấu hình
CN-E8RuntimeConfig (con trỏ nguyên tử)a.cfg.Load() mỗi chu kỳĐồng bộVòng chính áp interval, catch_up_batches, khóa truyền tải (L3 WAL và Sender FR-W16)

3.2. Kiến trúc module ​

mermaid
flowchart TB
  classDef pub fill:#1f3a5f,stroke:#4a90d9,color:#fff
  classDef intn fill:#3a3a3a,stroke:#888,color:#fff
  classDef ext fill:#3a3a3a,stroke:#888,color:#fff

  subgraph LA["Lớp lệnh: cli"]
    CLIC["cli: commands, parse, Exit codes"]:::pub
    CLIE["enroll.go, checkconfig.go, collectonce.go, run.go"]:::intn
  end
  subgraph LB["Lớp nghiệp vụ"]
    ENRP["enroll: ResolveToken, Do, Wipe"]:::pub
    CRDP["creds: Store, Credentials, MachineID"]:::pub
    HIP["hostinfo: Gather, UsableIPs, ReadOS"]:::pub
  end
  subgraph LC["Lớp cấu hình"]
    CFGP["config: Load, Validate, Default, Redact"]:::pub
    CFGT["types.go, defaults.go, validate.go"]:::intn
  end
  EXT(["transport, agent, logx, version, collector engine"]):::ext

  CLIC --> CLIE
  CLIE --> ENRP
  CLIE --> CFGP
  ENRP --> CRDP
  ENRP --> HIP
  CRDP -.-> CFGP
  CFGP --> CFGT
  CLIE -.-> EXT
  ENRP -.-> EXT

Chú giải: xanh dương là bề mặt công khai của gói, xám là nội bộ hoặc bên ngoài. Mũi tên liền là phụ thuộc biên dịch, mũi tên đứt là phụ thuộc vào gói dùng chung. creds không import config: đường nét đứt creds tới config chỉ biểu thị việc Store được dựng từ cfg.StateDir do lớp gọi truyền vào. config không phụ thuộc creds, enroll hay cli.

3.2.1. Cấu trúc: gói enroll, creds, hostinfo ​

Thành phầnTệpVai tròGhi chú
Source, Resolved, ResolveTokenenroll/enroll.goChọn License theo nguồn và ưu tiênLỗi không chứa token. Resolved.File nhớ tệp để xóa sau
Params, Result, Doenroll/enroll.goMột lần enroll: chặn lặp, gọi Collector, lưuResult chỉ có AgentID, CollectorURL
ErrNoToken, ErrConflictingSources, ErrInvalidToken, ErrAlreadyEnrolled, SaveErrorenroll/enroll.goLỗi có kiểu để CLI ánh xạ mã thoátSaveError bọc lỗi ghi và nói token đã tiêu hao
Wipe(path)enroll/enroll.goGhi đè số không, Sync, xóaLỗi ghi hoặc Sync thì vẫn cố xóa tệp (os.Remove) rồi trả lỗi
Credentials, Storecreds/creds.goMô hình và kho credentials.jsonStore{Path} dựng bằng NewStore(stateDir)
ErrNotEnrolled, ErrInsecurePermissionscreds/creds.goLỗi có kiểu cho Loadagent.ErrNotEnrolled là bí danh của creds.ErrNotEnrolled
MachineID(paths...)creds/creds.goBăm có muối của machine-idĐường dẫn mặc định /etc/machine-id, /var/lib/dbus/machine-id
Info, OS, Gather, UsableIPs, ReadOShostinfo/hostinfo.goThông tin máy để Collector ràng buộc ServerThiếu mảnh nào thì để rỗng, không lỗi

3.2.2. Cấu trúc: gói config ​

Thành phầnTệpVai tròGhi chú
Config và các con (BufferConfig, LimitsConfig, LogConfig, MetricsConfig, Check)config/types.goCấu hình hiệu lực, thẻ YAMLDuration và ByteSize có UnmarshalYAML riêng
Default(), DefaultConfigPath(), DefaultStateDir(), DefaultLicensePath()config/defaults.goGiá trị và đường dẫn mặc định theo nền tảngLinux: /etc/accesshub-agent/agent.yaml, /var/lib/accesshub-agent, /etc/accesshub-agent/license.token. Windows: dưới C:\ProgramData\AccessHubAgent
Options, Overrides, ResolvePath, Loadconfig/load.goĐường ống nạp phân lớpOptions.Explicit làm tệp thiếu thành lỗi
Issue, Errorsconfig/load.goLỗi có số dòng, gom nhiều lỗiErrors.Error() xuống dòng mỗi issue, dạng file:line: path: message
Validate(cfg, lineIndex)config/validate.goKiểm miền giá trị và quy tắc chéoTrả nhiều Issue, không dừng ở lỗi đầu
RedactURLconfig/redact.goChe mật khẩu trong URL hiển thịChỉ che mật khẩu, thay bằng xxxxx

3.2.3. Cấu trúc: lớp cli ​

Thành phầnTệpVai tròGhi chú
Env, Run/bảng commands()cli/cli.goDựng môi trường (stdin, stdout, stderr, Getenv, Ctx) và phân phối lệnhEnv tiêm được trong kiểm thử
commonFlags, parse, newFlagSetcli/cli.goBốn cờ chung --config, --collector, --state-dir, --log-levelparse trả mã 2 khi sai, mã 0 khi -h
Hằng Exit*cli/cli.goMã thoát 0 đến 6Xem 5.1
enrollCmd, reportEnrollError, enrollHintcli/enroll.goLệnh enrollreadMachineID là biến để kiểm thử thay
checkConfigCmd, printSummarycli/checkconfig.goLệnh check-configKhông gọi mạng
collectOnceCmdcli/collectonce.goLệnh collect-oncenewEngine là biến để kiểm thử thay. placeholderURL thỏa bước kiểm tra cấu hình
runCmd, handleSignals, reloadcli/run.goLệnh run, xử lý SIGHUP, SIGUSR1reloadSignal() và statusSignal() trả SIGHUP và SIGUSR1 trên Linux, nil trên Windows
stubcli/cli.gostatus, diagIn not implemented yet (planned in AGT-10), mã 1
serviceCmd, uninstallCmdcli/service.goLệnh service, uninstallThuộc L3 Service và Packaging (AGT-6, chưa commit)

4. Domain model ​

Có hai nhóm khái niệm độc lập: danh tính (License, thông tin đăng nhập, thông tin máy) và cấu hình. Mỗi nhóm một sơ đồ.

mermaid
classDiagram
  namespace Vung_Danh_Tinh {
    class Credentials {
      <<Aggregate Root>>
      +AgentID string
      +AgentToken secret
      +CollectorURL string
      +MachineID hash
      +EnrolledAt time
    }
    class CredentialsStore {
      <<Repository>>
      +Path state_dir_credentials_json
    }
    class License {
      <<Value Object>>
      +Token secret
      +File optional_path
    }
    class HostInfo {
      <<Value Object>>
      +Hostname string
      +IPs up_to_16
      +OS family_name_version_kernel_arch
    }
    class EnrollResult {
      <<Value Object>>
      +AgentID string
      +CollectorURL string
    }
  }
  CredentialsStore --> Credentials : lưu và nạp
  License ..> Credentials : đổi lấy
  HostInfo ..> Credentials : gửi kèm để ràng buộc
  Credentials ..> EnrollResult : rút gọn không có token
mermaid
classDiagram
  namespace Vung_Cau_Hinh {
    class Config {
      <<Aggregate Root>>
      +CollectorURL https_only
      +StateDir string
      +Interval 10s_to_300s
      +Buffer BufferConfig
      +Limits LimitsConfig
      +Log LogConfig
      +Metrics MetricsConfig
      +Checks up_to_50
      +Labels up_to_20
    }
    class LoadOptions {
      <<Value Object>>
      +Path string
      +Explicit bool
      +Overrides three_flags
    }
    class ConfigErrors {
      <<Value Object>>
      +File string
      +Issues list_of_Issue
    }
    class Issue {
      <<Value Object>>
      +Line int
      +Path string
      +Msg string
    }
  }
  LoadOptions ..> Config : nạp ra
  Config ..> ConfigErrors : hoặc trả lỗi
  ConfigErrors *-- Issue : gồm nhiều

Bảng thực thể

Thực thểVai trò DDDĐịnh danhVòng đờiGhi chú
CredentialsAggregate Rootagent_id (do Collector cấp)Tạo khi enroll thành công, thay thế nguyên khối khi --force, không bao giờ sửa từng trườngHiện không có trường hết hạn hay xoay. CollectorURL chỉ là dữ liệu lưu (D-11)
CredentialsStoreRepositoryĐường dẫn tệpSống suốt tiến trìnhLoad, Save, Exists, Delete. Ghi nguyên tử
LicenseValue ObjectKhông (giá trị)Sống trong bộ nhớ trong lệnh enroll. Tệp nguồn bị xóa sau thành côngCắt khoảng trắng, tối đa 256 ký tự
HostInfoValue ObjectKhôngTính lại mỗi lần enrollPhía Collector dùng để khớp Server (quy tắc ở Access Hub)
EnrollResultValue ObjectKhôngTrả cho CLI để inKhông có token
ConfigAggregate RootKhông (một bản hiệu lực mỗi tiến trình)Tạo lúc khởi động, thay nguyên khối bằng Reload. Bất biến sau khi tạoCon trỏ nguyên tử, không sửa tại chỗ
LoadOptionsValue ObjectKhôngDựng một lần ở cli, tái dùng cho mọi lần nạp lạiExplicit phân biệt tệp do người dùng chỉ định
ConfigErrors, IssueValue ObjectKhôngSinh khi nạp lỗiMỗi issue có thể có số dòng

Bất biến (Invariants)

MãBất biếnThi hành bởi
INV-E1credentials.json hoặc không tồn tại, hoặc đầy đủ và quyền 0600. Không bao giờ ở trạng thái ghi dởStore.Save (tệp tạm, Rename). TestSaveIsAtomicAndLeavesNoTempFiles, TestFailedSaveKeepsOldFile
INV-E2Token không bao giờ được in: String(), LogValue(), Result, lỗi enroll đều không chứa tokencreds.go, enroll.go. TestCredentialsNeverPrintTheToken
INV-E3Tệp License chỉ bị xóa sau khi Store.Save thành côngcli/enroll.go (Wipe sau Do). TestEnrollSaveFailureIsReportedAndKeepsTokenFile
INV-E4machine_id lưu và gửi luôn là băm có muối, không bao giờ là machine-id thôMachineID. TestMachineIDIsSaltedHash
INV-E5Load không bao giờ trả Credentials thiếu agent_id hoặc agent_tokenStore.Load. TestLoadRejectsLoosePermissionsCorruptAndIncomplete
INV-E6Khi đã có thông tin đăng nhập và không có --force, enroll không gọi mạngDo kiểm Store.Exists() trước Client.Enroll. TestEnrollRefusesWhenAlreadyEnrolledUnlessForced
INV-E7Mức ưu tiên cố định cờ > env > tệp > mặc địnhLoad. TestPrecedenceFlagsOverEnvOverFile
INV-E8Cấu hình không hợp lệ không bao giờ trở thành cấu hình đang chạyLoad trả nil, *Errors nếu có bất kỳ issue. reload giữ bản cũ. TestSignalsReloadAndStatus
INV-E9collector_url hiệu lực luôn là https, có host, không user info, không query, không fragment, không / cuốiValidate, TrimRight. TestLoadFullSampleFromDocs
INV-E10Nhãn cấu hình không thể đặt company_id, server_id, agent_idValidate. Ranh giới tenant vẫn do Collector áp (L3 Collectors)

5. API Contract Specification ​

Cụm này không mở cổng. Hợp đồng gồm: (1) giao diện dòng lệnh và tín hiệu cho người vận hành (mã thoát là hợp đồng), (2) một lời gọi HTTPS tới Collector (POST /agent/v1/enroll) và (3) hai định dạng tệp do cụm này sở hữu (credentials.json, agent.yaml).

5.1. Operations (Public API) ​

#Operation / Kênh EventLoại hìnhHướng gọi (Caller → BC)Ngữ nghĩa nghiệp vụ
1accesshub-agent enroll [--token | --token-file | --token-stdin] [--force]Lệnh CLIQuản trị viên, script gói → CLIĐổi License lấy danh tính và lưu. Một lần. Thoát 0, 1, 2, 3, 5, 6
2accesshub-agent check-configLệnh CLIQuản trị viên → CLINạp và kiểm tra cấu hình, in tóm tắt. Thoát 0, 2, 3. Không gọi mạng
3accesshub-agent collect-once [--gap d]Lệnh CLIQuản trị viên → CLIThu một lần in JSON. Thoát 0, 1, 2, 3
4accesshub-agent runLệnh CLIsystemd, quản trị viên → CLIChạy agent. Thoát 0, 1, 3, 4. Chi tiết vòng chạy ở L3 WAL và Sender
5accesshub-agent status, diagLệnh CLIQuản trị viên → CLIStub, thoát 1. THIẾT KẾ, CHƯA XÂY (AGT-10)
6accesshub-agent versionLệnh CLIQuản trị viên → CLIIn phiên bản, thoát 0
7Cờ chung --config, --collector, --state-dir, --log-levelCờ CLIQuản trị viên → CLIGhi đè env và tệp. Dùng bởi run, enroll, check-config, collect-once
8SIGHUPTín hiệu OSQuản trị viên, systemctl reload → tiến trình runNạp lại cấu hình cục bộ. Chỉ Linux
9SIGUSR1Tín hiệu OSQuản trị viên → tiến trình runGhi log Status(). Chỉ Linux
10POST /agent/v1/enrollHTTPS, protobufTransport → CollectorĐổi token. Không có header Authorization
11GET /agent/v1/config, POST /agent/v1/credentials/renewHTTPS(chưa có người gọi)THIẾT KẾ, CHƯA XÂY ở lớp cấu hình và danh tính (AGT-10). Client có GetConfig dùng cho thăm dò 401 (L3 WAL và Sender)
12enroll.ResolveToken, enroll.Do, enroll.Wipe, creds.Store, config.Load, config.Validate, hostinfo.GatherHàm Gocli, agent → góiGiao diện trong tiến trình

Mã thoát (hợp đồng ổn định)

MãTênKhi nào
0ExitOKThành công, hoặc -h
1ExitErrorLỗi chung: ghi thông tin đăng nhập lỗi, đã enroll (không --force), không đọc được machine-id, Run lỗi, lệnh stub
2ExitUsageKhông có lệnh, lệnh lạ, cờ sai, tham số thừa, không có token, cờ token xung đột, token sai dạng, --gap ngoài miền
3ExitConfigCấu hình sai (khóa lạ, giá trị ngoài miền, tệp chỉ định không có), không dựng được client enroll
4ExitNotEnrolledrun khi chưa có credentials.json
5ExitEnrollRejectedCollector trả 400, 401, 403, 409, 422 hoặc 426 cho enroll
6ExitNoConnectionenroll gặp lỗi mạng hoặc Collector trả mã khác (5xx và còn lại)

5.2. Request / Response Schema ​

[POST /agent/v1/enroll]

FieldKiểuGhi chú
ReqX-AH-Proto, X-AH-Agent-Version, X-Request-Id, User-Agentheader!Do Client.do thêm, giống mọi yêu cầu
ReqAuthorizationheaderKhông gửi (chưa có token)
ReqContent-Typeheader!application/x-protobuf
ReqEnrollRequest.licensestring!Đã kiểm tra dạng, tối đa 256 ký tự
ReqEnrollRequest.hostnamestring!Từ os.Hostname
ReqEnrollRequest.ip_addresses[]string[]?IP unicast toàn cục, tối đa 16, loại trùng
ReqEnrollRequest.machine_idstring!Băm có muối, không phải machine-id thô
ReqEnrollRequest.agent_versionstring!version.Get().Version
ReqEnrollRequest.os{family, name, version, kernel, arch}OsInfo!Dự phòng bằng runtime.GOOS, runtime.GOARCH
ResEnrollResponse.agent_idstring!Thiếu thì client từ chối phản hồi
ResEnrollResponse.agent_tokenstring!Hiển thị đúng một lần. Thiếu thì client từ chối. Lưu ở credentials.json
ResEnrollResponse.collector_urlstring?Lưu, không dùng (D-11)
ResEnrollResponse.configAgentConfig?Bỏ qua (D-01, D-11)
ResEnrollResponse.server_time_msint64?Bỏ qua ở enroll
ResLỗiJSON hoặc protobufcode, message. Thông điệp cắt 200 ký tự (APIError)

[credentials.json] (tệp do cụm này sở hữu)

FieldKiểuBắt buộcGhi chú
agent_idstring!Không được rỗng khi nạp
agent_tokenstring!Không được rỗng khi nạp. Văn bản rõ (R-02)
collector_urlstring?omitempty. Không dùng lúc chạy (D-11)
machine_idstring?Băm. Rỗng thì bỏ qua kiểm tra danh tính ở Run
enrolled_atRFC 3339 UTC!Thông tin, không dùng để quyết định

[agent.yaml] (tệp do cụm này sở hữu, khóa cấp cao)

KhóaKiểuMặc địnhGhi chú
collector_urlURL https(bắt buộc)Có thể đến từ env hoặc cờ. Tệp rỗng thì đòi khóa này
ca_file, insecure_skip_verify, proxy_urlchuỗi, bool, URL http(s)rỗng, false, rỗngTruyền tải, dùng ở L3 WAL và Sender
state_dirđường dẫntheo nền tảngKhông được rỗng
interval, send_timeout, collect_timeoutthời lượng30 s, 15 s, 5 sMiền ở 12.1
allow_remote_configbooltrueĐược đọc, không dùng (D-01)
checks_allow_link_localboolfalseĐược đọc, không dùng (L2 D-02)
buffer{max_bytes, max_age, catch_up_batches}kích thước, thời lượng, số50 MiB, 24 h, 2L3 WAL và Sender
limits{max_series, memory_limit}số, kích thước500, 64 MiBL3 Collectors và Runtime
log{level, format, file}chuỗiinfo, text, rỗnglevel đổi được khi SIGHUP
metrics{cpu, memory, disk, net, uptime}đối tượngtất cả bậtL3 Collectors
checks[]danh sáchrỗngĐược kiểm tra, không có bộ thu (L2 D-02)
labelsbảng chuỗirỗngTối đa 20

5.3. Error Codes ​

Lỗi enroll từ Collector được phân loại theo trạng thái HTTP rồi theo code (do Access Hub quyết định, giao thức mục 2.2). Mã code dưới đây chỉ gồm những mã mà enrollHint xử lý riêng.

Trạng tháicodeÝ nghĩaGợi ý CLIMã thoát
409token_usedLicense đã dùng"request a new one"5
409already_enrolledMáy đã enroll ở Collector"ask an administrator to reset it"5
422binding_failedKhông khớp Server theo hostname và IP"check hostname and IP addresses"5
426(không phân biệt)Phiên bản agent quá cũ"upgrade the agent"5
400, 401, 403(khác)Yêu cầu hoặc token không hợp lệ, bị cấmthông điệp gốc của Collector5
5xx và còn lạibất kỳCollector không khả dụngcollector unavailable: ...6
(không có phản hồi)Lỗi mạng, TLS, hết thời giancannot reach the collector: ...6

Lỗi cục bộ: ErrNoToken, ErrConflictingSources, ErrInvalidToken thoát 2. ErrAlreadyEnrolled và *SaveError thoát 1. Lỗi cấu hình thoát 3, mỗi issue một dòng file:line: path: message. ErrNotEnrolled ở run thoát 4.

5.4. Versioning ​

Mã thoát, tên cờ và tên biến môi trường là hợp đồng với script gói (postinst, install.sh, L3 Service và Packaging). Mã hiện không có số phiên bản hợp đồng CLI và không có deprecation policy (OQ-E9). credentials.json không có trường phiên bản định dạng (OQ-E7). agent.yaml không có trường version, khóa lạ bị từ chối (KnownFields(true)) nên nâng cấp agent thêm khóa mới thì tệp của phiên bản mới không chạy được trên agent cũ (hệ quả của chế độ nghiêm ngặt). Giao thức /agent/v1 và X-AH-Proto: 1 xem L3 WAL và Sender.

5.5. Authz - shared responsibility & permission matrix ​

Cụm này không phân quyền người dùng ứng dụng. Kiểm soát truy cập là quyền hệ điều hành lên tệp và quyền chạy lệnh.

Phân định trách nhiệm Auth

LớpAi sở hữuGồm
Phát hành và ràng buộc LicenseAccess Hub, CollectorCấp token dùng một lần, quy tắc khớp Server theo IP và hostname
Chuyển token tới máyQuản trị viênNgoài băng. Cụm này chỉ chọn nguồn an toàn (tệp, stdin)
Lưu agent_tokenCụm nàycredentials.json 0600, thư mục 0700, từ chối quyền rộng
Tài khoản sở hữu tệp, quyền thư mục /etc/accesshub-agentL3 Service và PackagingTài khoản accesshub-agent, unit systemd
Quyền gọi enroll, runHệ điều hànhNgười chạy phải ghi được state_dir (thường accesshub-agent hoặc root lúc cài). Không có kiểm tra ngoài quyền tệp

Permission matrix

Public API (5.1)quản trị viên (root hoặc accesshub-agent)người dùng thường
1 enrollĐược, nếu ghi được state_dir và đọc được tệp token. Nếu chạy bằng root, credentials.json thuộc root với quyền 0600 nên dịch vụ chạy bằng accesshub-agent không đọc được (OQ-E5). Thông báo của service install dặn chạy enroll bằng người dùng accesshub-agentKhông ghi được state_dir mặc định nên SaveError sau khi đã tiêu hao token (OQ-E5)
2, 3, 6 (check-config, collect-once, version)ĐượcĐược, nếu đọc được agent.yaml (gói cài 0640 root:accesshub-agent nên cần là root hoặc thuộc nhóm dịch vụ, xem L3 Service và Packaging)
4 runĐượcKhông đọc được credentials.json 0600 của người khác
8, 9 (SIGHUP, SIGUSR1)Được (cùng người dùng hoặc root)Không (hệ điều hành chặn)
10 POST enrollKhông áp dụng (người gọi là tiến trình)Không áp dụng

6. Data Schema (Physical) ​

Cụm này ghi đĩa đúng một tệp (state_dir/credentials.json) và xóa tối đa một tệp (tệp License). Nó đọc agent.yaml, /etc/machine-id, os-release. Mục 6.1 mô tả bố cục tệp thay cho lược đồ cơ sở dữ liệu.

6.1. Cài đặt vật lý ​

Bảng ánh xạ

Phân hệ (3.2)Aggregate (4)StoreCấu trúc vật lý cốt lõi
credsCredentialsTệp state_dir/credentials.json (mặc định /var/lib/accesshub-agent/credentials.json)JSON một đối tượng, quyền 0600, thư mục 0700. Tệp tạm .credentials-*.tmp cùng thư mục trong lúc ghi
enrollLicenseTệp license.token (mặc định /etc/accesshub-agent/license.token) hoặc đường dẫn do người vận hành chọnVăn bản một dòng. Bị ghi đè số không rồi xóa sau khi enroll thành công
configConfigTệp agent.yaml (mặc định /etc/accesshub-agent/agent.yaml)YAML. Chỉ đọc. Cụm này không bao giờ ghi
hostinfo, credsHostInfo, machine id/etc/machine-id (dự phòng /var/lib/dbus/machine-id), /etc/os-release, /proc/sys/kernel/osreleaseChỉ đọc

Lược đồ (tệp và quyền)

mermaid
classDiagram
  namespace Thu_Muc_State {
    class stateDir {
      path state_dir
      mode 0700
    }
    class credentialsFile {
      name credentials_json
      mode 0600
      fields agent_id_agent_token_collector_url_machine_id_enrolled_at
    }
    class credentialsTmp {
      name dot_credentials_star_tmp
      mode 0600_before_write
    }
    class walDir {
      name wal
      owner L3_WAL_and_Sender
    }
  }
  namespace Thu_Muc_Cau_Hinh {
    class etcDir {
      path etc_accesshub_agent
      owner L3_Service_and_Packaging
    }
    class agentYaml {
      name agent_yaml
      access read_only
    }
    class licenseFile {
      name license
      mode 0600_recommended
      lifetime deleted_after_enroll
    }
  }
  stateDir *-- credentialsFile : chứa một
  stateDir *-- walDir : chứa một
  credentialsFile ..> credentialsTmp : ghi qua rồi đổi tên
  etcDir *-- agentYaml : chứa
  etcDir *-- licenseFile : chứa tạm

Ghi chú lược đồ: tệp tạm được tạo bằng CreateTemp (quyền mặc định 0600 của Go) và Chmod 0600 trước khi ghi nội dung. Rename trong cùng thư mục nên nguyên tử trên hệ tệp POSIX, sau đó syncDir ghi thư mục xuống đĩa (lỗi syncDir bị bỏ qua, nên sau mất điện đúng lúc đó tệp có thể quay về bản cũ, đã nêu ở OQ-E8). Không có khóa ngoại. Quyền 0600 bắt buộc khi nạp trên Linux, không kiểm trên Windows.

6.2. Phân loại dữ liệu & retention ​

Phần tử / Trường dữ liệuPhân lớp dữ liệuThời hạn lưu trữ (Retention)Cơ chế bảo vệ kỹ thuật
agent_token (trong credentials.json)Bí mậtĐến khi ghi đè bằng enroll --force, hoặc gỡ cài đặt (uninstall --purge, xem L3 Service và Packaging). Không có hết hạn hay xoay (AGT-10)0600, thư mục 0700, ghi nguyên tử, từ chối quyền rộng, che ở log. Không mã hóa ở trạng thái nghỉ (R-02)
agent_id, enrolled_at, collector_urlNội bộNhư trên0600 cùng tệp
machine_id (băm)Nội bộNhư trênBăm có muối, không đảo ngược được về machine-id thô
License (tệp hoặc env)Bí mậtSống ngắn. Tệp bị ghi đè số không và xóa sau enroll thành công. Nếu enroll thất bại, tệp còn (có thể đã hết hiệu lực)Chọn nguồn an toàn (tệp, stdin), cảnh báo --token, kiểm quyền 0600. Env AH_LICENSE tồn tại suốt đời tiến trình và hiển thị ở /proc/<pid>/environ cho cùng người dùng và root
agent.yamlNội bộ (có thể chứa proxy_url với mật khẩu)Do người vận hành quản lýRedactURL che mật khẩu proxy khi in. Quyền tệp do gói đặt (L3 Service và Packaging)
hostname, ip_addresses, thông tin OS (gửi lúc enroll)Nội bộDo Collector quyết địnhTLS. Không lưu cục bộ ngoài bộ nhớ trong lệnh enroll

7. Thuật toán & Luồng nghiệp vụ ​

7.1. Luồng nghiệp vụ chính (happy path) ​

Luồng 1: enroll thành công bằng --token-stdin (kiểu script gói)

mermaid
sequenceDiagram
  participant OP as Người vận hành hoặc postinstall
  participant CLI as enrollCmd
  participant CFG as config.Load
  participant ENR as enroll
  participant COL as Collector
  participant STO as creds.Store

  OP->>CLI: accesshub-agent enroll --token-stdin
  CLI->>CFG: Load(Options)
  CFG-->>CLI: Config đã kiểm
  CLI->>ENR: ResolveToken(Source)
  ENR-->>CLI: Resolved gồm token (File rỗng vì đọc từ stdin)
  CLI->>ENR: Do(Params gồm MachineID và hostinfo)
  ENR->>COL: POST /agent/v1/enroll (protobuf, không Authorization)
  COL-->>ENR: 200 gồm agent_id và agent_token
  ENR->>STO: Save(Credentials)
  STO-->>ENR: ghi nguyên tử 0600
  ENR-->>CLI: Result gồm agent_id, không có token
  CLI-->>OP: in enrolled as agent id, mã thoát 0

Thứ tự là bất biến: kiểm cấu hình trước, rồi phân giải token, rồi mới gọi mạng (TestEnrollRejectsBadConfig khẳng định cấu hình sai không gây lời gọi mạng). Nếu nguồn token là tệp (Resolved.File khác rỗng) thì sau khi Do thành công, enroll.Wipe ghi đè bằng số không rồi xóa tệp (TestEnrollFromFileStoresCredentialsAndWipesTheFile). Lỗi xóa tệp chỉ thành cảnh báo, mã thoát vẫn 0.

Luồng 2: run khởi động, nạp cấu hình và danh tính

mermaid
sequenceDiagram
  participant SYS as systemd hoặc người vận hành
  participant RUN as runCmd
  participant CFG as config.Load
  participant AG as Agent.Run
  participant STO as creds.Store
  participant MID as creds.MachineID

  SYS->>RUN: accesshub-agent run
  RUN->>CFG: Load(Options)
  CFG-->>RUN: Config
  Note over RUN: dựng logger, đặt GOMEMLIMIT nếu biến môi trường chưa đặt
  RUN->>AG: New(cfg) rồi Run(ctx)
  AG->>STO: Load()
  STO-->>AG: Credentials (đã qua kiểm quyền và kiểm đủ trường)
  Note over AG: log agent started, creds được che token
  AG->>MID: MachineID()
  MID-->>AG: băm hiện tại khớp băm lúc enroll
  Note over AG: dựng engine, client, hàng đợi rồi vào vòng thu và gửi

runCmd nạp cấu hình một lần trước khi tạo Agent. Agent.Run nạp credentials.json đúng một lần (token nằm trong Sender.Token tới khi tiến trình thoát) nên sửa tệp lúc đang chạy không có tác dụng cho tới lần khởi động lại.

Luồng 3: SIGHUP nạp lại cấu hình thành công

mermaid
sequenceDiagram
  participant OP as Quản trị viên
  participant SIG as handleSignals
  participant CFG as config.Load
  participant LOG as logx.Logger
  participant AG as Agent
  participant RUN as Vòng chính

  OP->>SIG: SIGHUP (systemctl reload hoặc kill)
  SIG->>CFG: Load(cùng Options lúc khởi động)
  CFG-->>SIG: Config mới đã kiểm
  SIG->>LOG: SetLevel(log.level)
  SIG->>AG: Reload(cfg)
  Note over AG: atomic.Store con trỏ cấu hình
  SIG->>LOG: Info config reloaded
  RUN->>AG: đầu chu kỳ kế đọc con trỏ mới
  Note over RUN: áp dụng interval, catch_up_batches, và dựng lại client nếu transportKey đổi

Việc nạp lại dùng chính Options của lúc khởi động, gồm cả ba cờ ghi đè (--collector, --state-dir, --log-level) nên cờ vẫn thắng tệp sau khi nạp lại. Bảng các khóa có hiệu lực ở 7.4.5.

7.2. Luồng thay thế và lỗi ​

Luồng 4: Collector từ chối enroll (400, 401, 403, 409, 422, 426)

mermaid
sequenceDiagram
  participant CLI as enrollCmd
  participant ENR as enroll.Do
  participant COL as Collector
  participant OP as Người vận hành

  CLI->>ENR: Do(Params)
  ENR->>COL: POST /agent/v1/enroll
  COL-->>ENR: 4xx kèm code (token_used, already_enrolled, binding_failed hoặc khác)
  ENR-->>CLI: lỗi APIError
  Note over CLI: reportEnrollError chọn gợi ý theo code, riêng 426 nhắc nâng cấp agent
  CLI-->>OP: enrollment rejected kèm gợi ý, mã thoát 5
  Note over CLI: tệp token không bị xóa, credentials.json không được tạo

Đây là đường bình thường của token hết hạn hoặc đã dùng. Vì tệp token chỉ bị xóa khi thành công, người vận hành có thể thử lại sau khi sửa nguyên nhân (ví dụ sửa hostname cho binding_failed). Nếu token thực sự đã tiêu hao thì phải xin token mới.

Luồng 5: Collector đã cấp token nhưng ghi đĩa lỗi (SaveError)

mermaid
sequenceDiagram
  participant CLI as enrollCmd
  participant ENR as enroll.Do
  participant COL as Collector
  participant STO as creds.Store
  participant OP as Người vận hành

  ENR->>COL: POST /agent/v1/enroll
  COL-->>ENR: 200 gồm agent_token (token enroll đã bị tiêu hao)
  ENR->>STO: Save(Credentials)
  STO-->>ENR: lỗi (đĩa đầy, thư mục chỉ đọc hoặc không đủ quyền)
  ENR-->>CLI: SaveError bọc lỗi gốc
  CLI-->>OP: enrolled but the credentials could not be saved, token is now spent
  Note over CLI: mã thoát 1, tệp token được giữ nguyên, không có credentials.json mới

Đây là nhánh nguy hiểm nhất của enroll: phía Collector đã ghi nhận agent nhưng máy không giữ được danh tính. Thông điệp nói thẳng rằng cần token mới (NFR-E04). Đường khắc phục thủ công: sửa quyền hoặc dung lượng của state_dir, xin token mới, enroll lại (Collector có thể trả already_enrolled nếu chưa đặt lại bản ghi, xem OQ-E10).

Luồng 6: enroll lặp bị chặn trước khi gọi mạng

mermaid
sequenceDiagram
  participant CLI as enrollCmd
  participant ENR as enroll.Do
  participant STO as creds.Store
  participant OP as Người vận hành

  CLI->>ENR: Do(Params, Force=false)
  ENR->>STO: Exists()
  STO-->>ENR: true
  ENR->>STO: Load()
  STO-->>ENR: Credentials (hoặc lỗi nếu tệp hỏng)
  ENR-->>CLI: ErrAlreadyEnrolled kèm agent_id hiện có
  CLI-->>OP: this host is already enrolled as agent, use --force, mã thoát 1
  Note over ENR: không có lời gọi mạng nào xảy ra

Với --force, Do bỏ qua nhánh này và ghi đè credentials.json. Cờ này không thu hồi token cũ ở Collector (xem OQ-E4).

Luồng 7: cấu hình sai lúc khởi động

mermaid
sequenceDiagram
  participant SYS as systemd hoặc người vận hành
  participant RUN as runCmd
  participant CFG as config.Load
  participant ERR as stderr

  SYS->>RUN: accesshub-agent run
  RUN->>CFG: Load(Options)
  CFG->>CFG: giải mã YAML strict, applyEnv, applyOverrides, Validate
  CFG-->>RUN: Errors gồm nhiều Issue
  RUN->>ERR: mỗi dòng theo dạng tệp, số dòng, đường khóa, thông điệp
  RUN-->>SYS: mã thoát 3
  Note over SYS: không có logger nên lỗi ra stderr, systemd ghi vào journal

Hai điểm cần nhớ. Một: lỗi cú pháp hoặc khóa lạ của YAML dừng ngay, không chạy tiếp applyEnv và Validate, nên người dùng có thể phải sửa hai vòng (FR-E15). Hai: check-config đi đúng đường này nên là công cụ kiểm trước khi khởi động lại (TestCheckConfigReportsLineNumbers).

Luồng 8: SIGHUP bị từ chối, giữ cấu hình đang chạy

mermaid
sequenceDiagram
  participant OP as Quản trị viên
  participant SIG as handleSignals
  participant CFG as config.Load
  participant LOG as logx.Logger
  participant AG as Agent

  OP->>SIG: SIGHUP sau khi sửa agent.yaml sai
  SIG->>CFG: Load(Options)
  CFG-->>SIG: Errors
  SIG->>LOG: Error config reload rejected, keeping the running configuration
  Note over AG: con trỏ cấu hình không đổi, agent tiếp tục gửi bình thường

Có một nhánh từ chối thứ hai: Load thành công nhưng SetLevel lỗi thì log config reload rejected và không gọi Reload (reload trong internal/cli/run.go). Hiện không có kiểm thử riêng cho nhánh từ chối của reload (chỉ TestSignalsReloadAndStatus kiểm nhánh thành công và SIGUSR1), ghi ở OQ-E11.

Luồng 9: run khi chưa enroll hoặc tệp danh tính không dùng được

mermaid
sequenceDiagram
  participant RUN as runCmd
  participant AG as Agent.Run
  participant STO as creds.Store
  participant OP as Người vận hành

  RUN->>AG: Run(ctx)
  AG->>STO: Load()
  alt không có credentials.json
    STO-->>AG: ErrNotEnrolled
    AG-->>RUN: ErrNotEnrolled
    RUN-->>OP: this host is not enrolled, run enroll first, mã thoát 4
  else quyền rộng, JSON hỏng hoặc thiếu trường
    STO-->>AG: ErrInsecurePermissions hoặc lỗi hỏng hoặc thiếu trường
    AG-->>RUN: lỗi
    RUN-->>OP: log agent failed, mã thoát 1
  end

Quyền rộng cho mã 1 chứ không phải mã 4 (OQ-E6): script cần phân biệt "chưa enroll" với "tệp hỏng" phải đọc thông điệp. Kiểm thử: TestRunWithoutCredentialsExitsNotEnrolled, TestRunWithoutCredentialsIsNotEnrolled, TestLoadRejectsLoosePermissionsCorruptAndIncomplete.

Luồng 10: machine_id đổi (ảnh máy ảo nhân bản hoặc khôi phục)

mermaid
sequenceDiagram
  participant AG as Agent.Run
  participant MID as creds.MachineID
  participant LOG as logx.Logger
  participant CTX as ctx

  AG->>MID: MachineID()
  MID-->>AG: băm hiện tại khác băm trong credentials.json
  AG->>LOG: Error machine id changed since enrollment, delivery stopped
  Note over AG: halt identity_mismatch, không dựng engine, không gửi
  AG->>CTX: chờ ctx hủy
  CTX-->>AG: SIGTERM
  AG->>LOG: Info agent stopped

Tiến trình không thoát: systemd thấy dịch vụ vẫn "active" nhưng không có dữ liệu nào đi ra. Trạng thái thấy được qua Status() (SIGUSR1) dạng state=identity_mismatch. Khôi phục bằng enroll --force rồi khởi động lại (TestMachineIDMismatchStopsDelivery). Nếu không đọc được machine_id hiện tại thì chỉ cảnh báo và bỏ qua kiểm tra. Nếu credentials.json cũ không có machine_id (rỗng) thì cũng bỏ qua, vì điều kiện là c.MachineID != "".

Luồng 11: lỗi nguồn token (không có, xung đột, sai dạng)

mermaid
sequenceDiagram
  participant OP as Người vận hành
  participant CLI as enrollCmd
  participant ENR as ResolveToken

  OP->>CLI: enroll (không cờ, không biến, không tệp mặc định)
  CLI->>ENR: ResolveToken(Source)
  ENR-->>CLI: ErrNoToken
  CLI-->>OP: no License, mã thoát 2
  OP->>CLI: enroll --token a --token-stdin
  CLI->>ENR: ResolveToken(Source)
  ENR-->>CLI: ErrConflictingSources
  CLI-->>OP: use only one of the flags, mã thoát 2
  OP->>CLI: enroll --token-file với token rỗng hoặc dài hơn 256 hoặc có khoảng trắng
  ENR-->>CLI: ErrInvalidToken
  CLI-->>OP: token is malformed, mã thoát 2

Mọi lỗi này trả trước khi gọi mạng và không bao giờ chứa token (NFR-E01, TestResolveTokenErrors, TestEnrollUsageErrors). Lỗi I/O khi đọc tệp hoặc stdin (không phải ba lỗi trên) cho mã 1 vì enrollCmd chỉ ánh xạ ba lỗi kia sang mã 2.

Luồng 12: Collector không liên lạc được hoặc 5xx lúc enroll

mermaid
sequenceDiagram
  participant CLI as enrollCmd
  participant TRN as transport.Client
  participant COL as Collector
  participant OP as Người vận hành

  CLI->>TRN: Enroll(req)
  TRN->>COL: POST /agent/v1/enroll
  COL-->>TRN: 5xx hoặc lỗi mạng, TLS hoặc DNS
  TRN-->>CLI: APIError 5xx hoặc lỗi mạng
  CLI-->>OP: collector unavailable hoặc cannot reach the collector, mã thoát 6
  Note over CLI: tệp token được giữ, enroll không tự thử lại

Enroll không có vòng thử lại: postinstall (L3 Service và Packaging) coi mã 6 là "enroll sau", và không làm hỏng gói. Người vận hành chạy lại lệnh khi mạng ổn.

Luồng 13: đổi collector_url bằng SIGHUP nhưng cấu hình mới không dựng được client

mermaid
sequenceDiagram
  participant SIG as handleSignals
  participant AG as Agent
  participant RUN as Vòng chính
  participant LOG as logx.Logger

  SIG->>AG: Reload(cfg có ca_file trỏ tệp không đọc được)
  RUN->>AG: đầu chu kỳ đọc cấu hình, transportKey khác khóa cũ
  RUN->>RUN: clientFor(cfg)
  RUN->>LOG: Error new collector settings rejected, keeping the old ones
  Note over RUN: giữ client cũ, cấu hình mới vẫn nằm trong con trỏ

Đây là lỗ hổng nhỏ của nạp lại: Validate không kiểm tệp ca_file có tồn tại (chỉ dựng client mới phát hiện), nên Reload báo thành công ở CLI (config reloaded) nhưng client cũ vẫn dùng, và mỗi chu kỳ sau đó transportKey vẫn khác khóa cũ nên sẽ thử dựng lại và log lỗi lặp (xem OQ-E12). Mã: internal/agent/agent.go, TestReloadSwitchesCollector kiểm nhánh đổi thành công.

7.3. Máy trạng thái ​

Mỗi sơ đồ chỉ mô hình hóa một thực thể. Nhãn cạnh theo dạng trigger [guard] / event.

7.3.1. Tệp thông tin đăng nhập (credentials.json)

mermaid
stateDiagram-v2
  [*] --> Absent
  Absent --> Present : enroll thành công / Save tạo tệp 0600
  Present --> Present : enroll force thành công / Save ghi đè nguyên tử
  Present --> Loose : chmod rộng hơn 0600 / không có
  Loose --> Present : chmod 600 / không có
  Present --> Corrupt : tệp bị sửa hoặc cắt cụt / không có
  Corrupt --> Present : enroll force thành công / Save ghi đè
  Present --> Absent : uninstall purge / Delete
  Loose --> Absent : uninstall purge / Delete
  Corrupt --> Absent : uninstall purge / Delete
Đường (trigger)Nơi hiện thựcKiểm thử
Absent sang Present (enroll)creds.Store.Save gọi từ enroll.DoTestSaveLoadRoundTripWith0600, TestEnrollFromFileStoresCredentialsAndWipesTheFile
Present sang Present (enroll force)enroll.Do bỏ kiểm Exists khi ForceTestEnrollRefusesWhenAlreadyEnrolledUnlessForced
Save lỗi giữa chừng (giữ trạng thái cũ)Save dọn tệp tạm, không chạm tệp đíchTestFailedSaveKeepsOldFile, TestSaveIsAtomicAndLeavesNoTempFiles
Present sang Loose và ngược lạiDo người vận hành chmod, Load kiểm mode & 0o077 (không phải Windows)TestLoadRejectsLoosePermissionsCorruptAndIncomplete
Present sang CorruptLoad trả lỗi is corrupt hoặc is incompleteTestLoadRejectsLoosePermissionsCorruptAndIncomplete
Loose, Corrupt sang Present (enroll force)Do kiểm Exists rồi bỏ qua vì ForceChưa có kiểm thử riêng cho đường "khôi phục từ Corrupt bằng force" (OQ-E11)
Bất kỳ sang Absent (uninstall purge)creds.Store.Delete hoặc xóa thư mục state (L3 Service và Packaging)TestDeleteAndExists, TestServiceControlAndUninstall

Trạng thái Loose và Corrupt không được lưu ở đâu, chúng là kết quả của việc Load đọc tệp mỗi lần.

7.3.2. Cấu hình đang chạy (trong một tiến trình run)

mermaid
stateDiagram-v2
  [*] --> Booting
  Booting --> Active : Load và Validate thành công / Agent.Run bắt đầu
  Booting --> [*] : Load thất bại / thoát mã 3
  Active --> Active : SIGHUP [Load thành công] / Reload thay con trỏ
  Active --> Active : SIGHUP [Load thất bại] / log reload rejected, giữ cấu hình cũ
  Active --> [*] : SIGTERM hoặc SIGINT / thoát mã 0
Đường (trigger)Nơi hiện thựcKiểm thử
Booting sang ActiverunCmd gọi config.Load, agent.New, Agent.RunTestRunDeliversBatchesEveryInterval
Booting sang thoát mã 3reportConfigErrorTestRunRejectsBadConfig
Active sang Active (nạp lại thành công)reload, Agent.ReloadTestSignalsReloadAndStatus, TestReloadSwitchesCollector
Active sang Active (nạp lại bị từ chối)reload log Error và returnChưa có kiểm thử riêng (OQ-E11)
Active sang thoátsignal.NotifyContext hủy ctx, Run trả nilTestRunStopsOnContextCancel

Tín hiệu nạp lại chỉ có trên nền tảng không phải Windows (signals_unix.go, build tag !windows, mục tiêu thực tế là Linux). Trên Windows reloadSignal() trả nil nên không có nạp nóng, phải khởi động lại dịch vụ (D-05, AGT-7).

7.4. Thuật toán và quyết định thiết kế ​

7.4.1. Ghi thông tin đăng nhập nguyên tử ​

Vấn đề: credentials.json chứa token dài hạn. Mất điện hoặc đĩa đầy giữa chừng không được để lại tệp nửa vời (agent sẽ không khởi động) hay tệp có quyền rộng dù chỉ trong thoáng chốc (token lộ).

Giải pháp (creds.Store.Save): (1) MkdirAll(dir, 0700). (2) CreateTemp(dir, ".credentials-*.tmp"), tức cùng thư mục với đích để Rename không vượt hệ tệp. (3) Chmod(0600) trước khi ghi nội dung. (4) Write JSON thụt lề. (5) Sync tệp. (6) Close. (7) Rename đè đích (nguyên tử trên POSIX). (8) syncDir(dir) để bền hóa thao tác đổi tên. Mỗi lỗi ở bước 3 đến 7 gọi cleanup xóa tệp tạm và trả lỗi, tệp đích cũ còn nguyên.

Trade-off: syncDir bỏ qua mọi lỗi (chú thích mã: không phải nền tảng nào cũng hỗ trợ), nên trên hệ tệp không fsync thư mục được, một lần mất điện ngay sau Rename có thể làm tệp đích quay về bản cũ hoặc mất. Với enroll điều này nghĩa là token mới đã tiêu hao mà không lưu được, đúng kịch bản SaveError nhưng không được báo (OQ-E8). Việc ghi không có khóa tệp nên hai lần enroll --force chạy song song là "người ghi sau thắng", chấp nhận được vì enroll là thao tác thủ công.

7.4.2. Phân giải nguồn token và bảo vệ đầu vào ​

Vấn đề: token phải tới được agent bằng nhiều đường (tay, script, cấu hình hóa), nhưng không được lộ qua danh sách tiến trình, log hay bị đọc vô hạn từ một tệp hay đường ống độc hại.

Giải pháp (enroll.ResolveToken): (1) Đếm số cờ tường minh (--token, --token-file, --token-stdin), hơn một thì ErrConflictingSources. (2) Nếu có cờ: --token (kèm cảnh báo danh sách tiến trình), rồi stdin (io.LimitReader ở MaxTokenLen*4 = 1024 byte), rồi tệp. (3) Nếu không cờ: AH_LICENSE_FILE, rồi AH_LICENSE, rồi tệp mặc định DefaultLicensePath() (chỉ khi tệp tồn tại). (4) fromFile từ chối tệp lớn hơn 1024 byte trước khi đọc, cảnh báo nếu quyền rộng hơn 0600 nhưng vẫn dùng. (5) validated cắt khoảng trắng hai đầu rồi từ chối rỗng, dài hơn 256, hay có khoảng trắng bên trong. (6) Mọi lỗi trả về đều không chứa token.

Trade-off: ưu tiên "cờ hơn biến môi trường hơn tệp mặc định" giúp script ghi đè, nhưng AH_LICENSE tồn tại trong /proc/<pid>/environ suốt vòng đời tiến trình enroll (chỉ chủ tiến trình và root đọc được, rủi ro thấp hơn --token). Đường khuyến nghị là --token-stdin hay tệp 0600 bị xóa sau dùng. Việc cảnh báo chứ không chặn quyền rộng là chọn lựa thân thiện: chặn sẽ làm hỏng enroll tự động khi umask lạ.

7.4.3. Nạp cấu hình phân lớp và gom lỗi có số dòng ​

Vấn đề: người vận hành sửa YAML bằng tay, cần biết tất cả lỗi và dòng nào, không phải sửa từng lỗi một lần chạy. Nhưng cấu hình phải đúng tuyệt đối trước khi dùng.

Giải pháp (config.Load): (1) Default(). (2) Đọc tệp, thiếu tệp chỉ là lỗi nếu Explicit (đường từ cờ hoặc AH_CONFIG). (3) Giải mã YAML bằng KnownFields(true): khóa lạ thành unknown key "x", lỗi kiểu thành Issue kèm số dòng rút từ thông điệp yaml. (4) lineIndex đi qua cây YAML dựng bản đồ "đường khóa chấm sang số dòng" (buffer.max_bytes, checks[0].port). (5) Nếu bước 3 có lỗi thì trả ngay (không chạy env, không Validate). (6) applyEnv (chỉ 4 biến AH_*), applyOverrides (3 cờ), (7) Validate gom hết lỗi miền giá trị, dùng lineIndex để gắn dòng, (8) cắt / cuối của collector_url. Kết quả lỗi in tệp:dòng: đường: thông điệp.

Trade-off: bước 5 nghĩa là lỗi cú pháp che lỗi miền giá trị cho tới vòng sửa sau (chấp nhận, vì không thể kiểm miền trên cấu trúc đã giải mã hỏng). Thứ tự "tệp, env, cờ" có một hệ quả tinh tế: Validate chạy sau khi trộn, nên giá trị từ env hoặc cờ sai cũng bị bắt, nhưng số dòng chỉ có cho khóa có trong tệp (khóa chỉ do env đặt thì Line bằng 0 và dòng bị bỏ khỏi thông điệp). KnownFields(true) đảm bảo gõ sai khóa không bị bỏ qua thầm lặng, đổi lại khóa mới thêm ở phiên bản sau làm agent cũ từ chối khởi động (rủi ro khi triển khai cấu hình chung cho đội agent nhiều phiên bản, OQ-E13).

7.4.4. Danh tính máy và chống nhân bản ​

Vấn đề: ảnh máy ảo được nhân bản sẽ mang theo credentials.json và nhiều máy sẽ gửi số liệu dưới cùng một agent_id, làm sai dữ liệu của máy thật.

Giải pháp: lúc enroll, MachineID đọc /etc/machine-id (hoặc /var/lib/dbus/machine-id), cắt khoảng trắng, rồi tính sha256("accesshub-agent/machine-id/v1:" + id) dạng hex, và lưu băm (không phải id thô) vào credentials.json và gửi cho Collector. Lúc run, Agent.Run tính lại và so, khác thì halt("identity_mismatch") (luồng 10).

Trade-off: dựa vào machine-id của hệ điều hành: máy nhân bản mà không chạy systemd-machine-id-setup thì có cùng id và không bị phát hiện. Ngược lại, khi nâng cấp hệ điều hành làm đổi machine-id hợp lệ thì agent ngừng gửi và cần enroll lại (an toàn hơn là gửi sai). Muối cố định v1 cho phép Collector xác minh mà không thấy id thô, và cho phép đổi thuật toán bằng v2 sau này (chưa có cơ chế chuyển, OQ-E7 liên quan).

7.4.5. Nạp nóng: khóa nào có hiệu lực ngay ​

Vấn đề: SIGHUP phải đổi được những gì an toàn đổi, và nói rõ những gì không đổi, để người vận hành không tin nhầm rằng mình đã áp dụng một thay đổi.

Giải pháp: Reload chỉ thay con trỏ cấu hình. Vòng chính đọc con trỏ ở đầu mỗi chu kỳ và chỉ dùng một số trường:

KhóaHiệu lực sau SIGHUPCơ chế
log.levelNgaylog.SetLevel trong reload
intervalTừ lần timer.Reset kếtimer.Reset(cfg.Interval.D()) cuối mỗi chu kỳ
buffer.catch_up_batchesChu kỳ kếs.CatchUp = cfg.Buffer.CatchUpBatches
collector_url, ca_file, proxy_url, insecure_skip_verify, send_timeoutChu kỳ kế, nếu dựng được clienttransportKey đổi thì clientFor (luồng 13)
log.format, log.fileKhôngLogger dựng một lần ở runCmd
metrics.*, checks, labels, limits.max_series, collect_timeoutKhôngEngine dựng một lần trong Run (a.newEngine)
buffer.max_bytes, buffer.max_age, state_dirKhôngWAL mở một lần, state_dir còn quyết định nơi nạp credentials.json
limits.memory_limitKhôngdebug.SetMemoryLimit gọi một lần ở runCmd
allow_remote_configKhông có tác dụngChưa có đường cấu hình từ xa (D-01)

Trade-off: dựng lại engine hay WAL lúc chạy cần dừng thu giữa chu kỳ và đổi tên thư mục, rủi ro mất dữ liệu cao so với lợi ích nhỏ, nên hiện bắt buộc khởi động lại (systemctl restart) cho các khóa này. Hậu quả: config reloaded được log dù một số khóa trong tệp đã đổi mà chưa có hiệu lực. ĐỀ XUẤT: log danh sách khóa đã đổi nhưng cần khởi động lại (OQ-E14).

7.4.6. Ánh xạ lỗi enroll sang mã thoát và gợi ý ​

Vấn đề: script gói (postinstall) và người vận hành cần biết nên làm gì tiếp, mà không phân tích văn bản.

Giải pháp (reportEnrollError): ErrAlreadyEnrolled và *SaveError cho mã 1 (vấn đề cục bộ). *transport.APIError với trạng thái 400, 401, 403, 409, 422, 426 cho mã 5 kèm gợi ý theo code (token_used, already_enrolled, binding_failed) hoặc lời nhắc nâng cấp với 426. Trạng thái khác (5xx, 429) và mọi lỗi mạng cho mã 6. Mã 2 (cách dùng) và mã 3 (cấu hình) được trả sớm hơn, trước khi gọi mạng.

Trade-off: 429 rơi vào mã 6 ("không có kết nối") dù thực chất là "thử lại sau": chấp nhận vì postinstall xử lý cả hai giống nhau (enroll sau). Mã 1 bị dùng cho cả "đã enroll" và "lưu lỗi", khác nhau hoàn toàn về hành động cần làm, nên script chỉ phân biệt được qua thông điệp.


8. Xử lý lỗi ​

8.1. Xử lý các nhánh lỗi (error branches) ​

NhánhĐiều kiệnXử lýQuan sát
Không có nguồn tokenKhông cờ, không biến, không tệp mặc địnhErrNoToken, mã 2 (luồng 11)stderr
Hai cờ token cùng lúc--token, --token-file, --token-stdinErrConflictingSources, mã 2stderr
Token sai dạngRỗng, dài hơn 256, có khoảng trắng, tệp lớn hơn 1024 byteErrInvalidToken, mã 2stderr
Tệp token chỉ định nhưng không có--token-file hoặc AH_LICENSE_FILEtoken file: ..., mã 1 (không phải mã 2, OQ-E6 cùng họ)stderr
Đọc stdin lỗiI/Oread token from stdin, mã 1stderr
Không đọc được machine-idKhông có /etc/machine-id và /var/lib/dbus/machine-idcannot determine the machine id, mã 1. Kiểm thử CI phải thay readMachineIDstderr
Cấu hình sai khi enrollValidate lỗiIn các Issue, mã 3, chưa gọi mạngstderr
Tùy chọn client sai khi enrollURL sai hoặc ca_file không đọc đượcIn lỗi, mã 3stderr
Collector từ chối400, 401, 403, 409, 422, 426Luồng 4, mã 5stderr
Collector 5xx, 429, mạngKhông liên lạc đượcLuồng 12, mã 6, tệp token giữstderr
Lưu thất bạiĐĩa đầy, quyềnLuồng 5, SaveError, mã 1stderr
Đã enroll, không --forcecredentials.json tồn tạiLuồng 6, mã 1stderr
Không xóa được tệp token sau enrollWipe lỗiCảnh báo remove it manually, mã vẫn 0stderr
Cấu hình sai khi runLuồng 7Mã 3stderr, journal
SIGHUP cấu hình saiLuồng 8Giữ cấu hình cũLog Error
Chưa enroll khi runLuồng 9Mã 4stderr
Tệp danh tính quyền rộng, hỏng, thiếu trườngLuồng 9agent failed, mã 1Log Error
machine_id lệchLuồng 10Dừng gửi, tiến trình sốngLog Error, Status()
Client mới không dựng được sau SIGHUPLuồng 13Giữ client cũLog Error mỗi chu kỳ

8.2. Fail-fast ​

Điều kiệnHành viMã thoát hoặc lỗi
Đối số, lệnh, cờ saiIn trợ giúp hoặc lỗi, không làm gìMã 2 (TestNoArgsAndUnknownCommandAreUsageErrors, TestFlagErrorsAreUsageErrors)
Cấu hình sai lúc khởi độngKhông khởi độngMã 3 (TestRunRejectsBadConfig)
Chưa enrollRun trả ErrNotEnrolledMã 4
collector_url không phải HTTPS hoặc có thông tin người dùngValidate từ chốiMã 3
Tệp danh tính quyền rộngLoad từ chối (không phải Windows)Mã 1
Ghi credentials.json lỗiSaveErrorMã 1

8.3. Race conditions ​

Tình huốngCơ chế hoặc hậu quảGhi chú
Reload (goroutine tín hiệu) với vòng chính đọc cấu hìnhatomic.Pointer[config.Config], cấu hình không bao giờ bị sửa tại chỗĐọc thấy trọn bản cũ hoặc trọn bản mới
Hai tiến trình enroll song song cho cùng state_dirKhông có khóa. Mỗi bên Save nguyên tử nên tệp luôn trọn vẹn, nhưng bên ghi sau thắng và bên kia có agent_id khác trên CollectorHiếm, thao tác thủ công (OQ-E15)
enroll --force chạy khi dịch vụ đang chạyDịch vụ giữ token cũ trong RAM, tệp mới không có hiệu lực tới khi khởi động lại, và nếu Collector đã thu hồi token cũ thì dịch vụ gặp 401 hoặc 403Sau --force phải khởi động lại dịch vụ (ghi trong hướng dẫn vận hành)
SIGHUP đúng lúc sửa tệp dởCó thể đọc thấy YAML nửa vời, bị từ chối, giữ cấu hình cũAn toàn. Người vận hành nên ghi tệp nguyên tử (mv)
Save với Load đồng thời (chạy run lúc enroll --force)Load thấy tệp cũ hoặc mới nhờ RenameAn toàn
Đọc token từ tệp lúc tệp bị xóaStat thành công nhưng ReadFile lỗiTrả token file: ..., mã 1

9. Suy thoái dịch vụ & Khả năng phục hồi ​

9.1. Ma trận suy thoái theo phụ thuộc ​

Phụ thuộcLỗiSuy thoáiBảo vệPhục hồi
Collector (lúc enroll)Không tới được, 5xx, 429Không enroll được. Agent chưa có danh tính nên không chạyMã thoát 6 phân loại, postinstall không làm hỏng góiThủ công: chạy lại enroll khi mạng ổn
Collector (lúc run)Không tới đượcKhông ảnh hưởng thành phần này: danh tính và cấu hình đã nằm cục bộcredentials.json đọc từ đĩaTheo L3 WAL và Sender
Đĩa state_dirĐầy, chỉ đọc, hỏngEnroll: SaveError. Run: không đọc được danh tính thì không chạyTệp tạm rồi Rename giữ tệp cũThủ công: sửa đĩa, enroll lại với token mới (nếu lỗi lúc enroll)
Tệp agent.yamlSai cú pháp hoặc miền giá trịKhởi động: không chạy (mã 3). SIGHUP: giữ cấu hình cũValidate gom hết lỗi, check-config kiểm trướcSửa tệp rồi SIGHUP hoặc khởi động lại
/etc/machine-idMất hoặc đổiMất: bỏ qua kiểm danh tính, enroll lỗi. Đổi: dừng gửi (luồng 10)Cảnh báo log, dừng thay vì gửi saiThủ công: enroll lại với --force
Quyền tệpBị chmod rộngAgent không khởi động (mã 1)ErrInsecurePermissionschmod 600. Dịch vụ systemd sẽ tự thử khởi động lại theo chính sách Restart (L3 Service và Packaging)
Đồng hồ (để tính enrolled_at)Lệchenrolled_at sai, không ảnh hưởng chức năngChỉ thông tinKhông cần
Biến môi trườngThiếu hoặc sai kiểuAH_INSECURE_SKIP_VERIFY sai kiểu thì cấu hình bị từ chốiBáo lỗi có tên biếnSửa biến

9.2. Backup, DR, RTO và RPO ​

credentials.json là bí mật duy nhất không tái tạo được từ cục bộ: mất nó nghĩa là phải xin token enroll mới và (tùy Collector) đặt lại bản ghi máy. Không có cơ chế sao lưu trong agent. Không khuyến nghị sao lưu tệp này vào kho chung, vì đó là nhân rộng bí mật và trái với cơ chế chống nhân bản (luồng 10).

Chỉ sốGiá trịGhi chú
RPO của danh tínhBằng 0 với ghi nguyên tử thành côngSync tệp và Rename, nhưng syncDir bỏ qua lỗi (OQ-E8)
RTO khi mất credentials.jsonTheo thời gian xin token mới cộng thời gian enrollPhụ thuộc quy trình vận hành và Collector. Chưa đo, không đề xuất con số
RTO khi sửa cấu hình saiGần như tức thì với SIGHUP, hoặc khởi động lại dịch vụThao tác thủ công
Cấu hình agent.yamlNên nằm trong quản lý cấu hình của đội vận hành (Ansible, Puppet)Do gói cài đặt chỉ tạo tệp mẫu (L3 Service và Packaging)

10. Đồng thời & Toàn vẹn dữ liệu ​

10.1. Ranh giới giao dịch ​

Không có cơ sở dữ liệu hay giao dịch. Các đơn vị nguyên tử là thao tác tệp và lời gọi HTTP:

Đơn vịRanh giớiĐảm bảo
Ghi credentials.jsonTệp tạm, Chmod, Write, Sync, Close, Rename, syncDirĐọc thấy trọn bản cũ hoặc trọn bản mới. Quyền 0600 trước khi có nội dung
Đổi token lấy danh tínhPOST /enroll rồi SaveKhông nguyên tử giữa hai bước: Collector đã tiêu hao token trước khi tệp được ghi. Khi ghi lỗi chỉ có thể báo rõ (SaveError)
Xóa tệp tokenWipe: mở, ghi số không, Sync, đóng, RemoveChỉ sau khi Save thành công. Lỗi ghi đè vẫn cố Remove
Trộn cấu hìnhLoad dựng Config mới hoàn toàn, rồi Store con trỏKhông bao giờ có cấu hình nửa vời
Kiểm danh tính lúc runĐọc credentials.json một lầnQuyết định "gửi hay dừng" dựa trên ảnh chụp lúc khởi động

10.2. Cơ chế đồng thời ​

Cơ chếNơi dùngMục đích
atomic.Pointer[config.Config]Agent.cfgNạp nóng không khóa
sync.Mutex trong Agent (mu)send, haltedStatus() đọc an toàn từ goroutine tín hiệu
signal.Notify kênh đệm 4runCmdTránh mất tín hiệu khi nhận dồn
signal.NotifyContextrunCmdSIGINT, SIGTERM hủy ctx để dừng êm
Ghi tệp tạm cùng thư mục rồi Renamecreds.SaveNguyên tử, không khóa tệp
Không có khóa liên tiến trìnhstate_dirXem 8.3 và OQ-E15

11. Bảo mật ​

11.1. Bảo mật ba lớp ​

LớpBiện phápBằng chứng
Kênh truyềncollector_url bắt buộc https (không user info, query, fragment). Enroll dùng cùng client TLS với gửi số liệu (TLS tối thiểu 1.2, không theo redirect, ghim CA tùy chọn, L3 WAL và Sender 11.1). POST /enroll không gửi Authorization, chỉ token enroll trong thânvalidate.go, TestEnrollRejectsBadConfig
Danh tính và bí mậtĐầu vào token bị chặn cỡ (LimitReader, 1024 byte, 256 ký tự). --token bị cảnh báo. Credentials.String() và LogValue() che token. Result không có token. Lỗi enroll không chứa token. RedactURL che mật khẩu proxy trong check-configTestResolveTokenErrors, TestCredentialsNeverPrintTheToken, TestRedactURL
Dữ liệu nghỉcredentials.json 0600 trong thư mục 0700, Load từ chối quyền rộng (không phải Windows). Tệp token được ghi đè số không rồi xóa sau dùng. machine_id chỉ lưu dạng băm có muốiTestSaveLoadRoundTripWith0600, TestLoadRejectsLoosePermissionsCorruptAndIncomplete, TestWipeOverwritesAndRemoves, TestMachineIDIsSaltedHash

Các điểm yếu còn lại, nêu thẳng (R-02):

  • Token dài hạn nằm dạng rõ trong credentials.json (bảo vệ chỉ bằng quyền tệp). Trên Windows chưa có DPAPI và không có kiểm ACL (THIẾT KẾ, CHƯA XÂY, D-05).
  • AH_LICENSE tồn tại trong /proc/<pid>/environ của tiến trình enroll.
  • Ghi đè số không bằng Write rồi Remove không đảm bảo xóa vật lý trên SSD hoặc hệ tệp copy-on-write. Bù lại token enroll dùng một lần nên tệp đã xóa không còn giá trị sau khi enroll thành công.
  • Chạy enroll bằng root tạo credentials.json thuộc root 0600 mà dịch vụ (tài khoản accesshub-agent) không đọc được (OQ-E5). Hướng dẫn và postinstall đều yêu cầu enroll bằng tài khoản dịch vụ.

11.2. Đường ống phân quyền năm bước ​

Thành phần này không nhận yêu cầu vào qua mạng, nên "đường ống" áp vào thao tác CLI và lời gọi ra:

BướcAi làmGhi chú
1. Xác thực người thao tácHệ điều hành (quyền tệp, tài khoản chạy lệnh)Agent không có đăng nhập riêng. service install và uninstall đòi root, kiểm bằng Euid trong internal/svc (TestServiceNeedsRootAndAKnownAction)
2. Xác thực nguồn tokenCục bộKiểm dạng, kích thước, cảnh báo quyền tệp
3. Xác thực kênhTransport (TLS, CA)collector_url HTTPS
4. Xác thực token enrollCollector400, 401, 403 (mã 5)
5. Ràng buộc danh tínhCollector gắn agent_id, company_id, server_id vào token. Agent lưu machine_id băm để tự kiểmAgent không tự nhận nhãn định danh (L3 Collectors INV-2)

11.3. Điểm neo zero-trust ​

Điểm neoÝ nghĩa
Không cổng mởAgent chỉ là client, không có thể bị tấn công qua lệnh cấu hình từ xa (D-01 chưa xây)
Token enroll dùng một lầnLộ tệp token sau enroll vô hại. Lộ trước enroll thì người đầu tiên dùng thắng, Collector ràng buộc máy bằng hostname và IP (binding_failed)
Không tin cấu hình ngoài miềnMọi giá trị qua Validate với giới hạn cứng (HardMaxSeries 500, MaxChecks 50, MaxRegexLen 200, MaxLabelValue 128, MaxStaticLabels 20), tránh cấu hình làm cạn tài nguyên máy chủ
Không tin nhãn tự khaiNhãn company_id, server_id, agent_id bị cấm ở Validate (reservedLabels)
Không tin ảnh máy nhân bảnKiểm machine_id lúc khởi động (luồng 10)
Không tin khóa lạKnownFields(true) từ chối khóa không biết thay vì bỏ qua

12. Cấu hình & Tinh chỉnh ​

12.1. Tunables ​

Nguồn và thứ tự ưu tiên: mặc định (Default()), tệp YAML, biến AH_* (chỉ 4 biến), cờ CLI (chỉ 3 cờ), theo 7.4.3. Đường tệp: cờ --config, rồi AH_CONFIG, rồi /etc/accesshub-agent/agent.yaml (Windows: C:\ProgramData\AccessHubAgent\agent.yaml). Cột "Nạp nóng" theo 7.4.5.

KhóaMặc định (dải hợp lệ)Ý nghĩaNạp nóngNguồn ghi đè
collector_urlkhông có (bắt buộc, https, có host, không user info, query, fragment)Đích enroll và gửiCó (nếu dựng được client)AH_COLLECTOR_URL, --collector
ca_filerỗngCA riêng để ghimCó (nếu đọc được)Không
insecure_skip_verifyfalseBỏ kiểm chứng chỉ, chỉ để thử nghiệmCóAH_INSECURE_SKIP_VERIFY
proxy_urlrỗng (http hoặc https, có host)Proxy tường minhCóKhông
state_dir/var/lib/accesshub-agent (không được rỗng)Chứa credentials.json và wal/KhôngAH_STATE_DIR, --state-dir
interval30 s (10 s đến 300 s)Chu kỳ thu và gửiCóKhông
send_timeout15 s (1 s đến 1 phút)Hạn một yêu cầu HTTPCó (dựng lại client)Không
collect_timeout5 s (1 s đến 30 s, phải nhỏ hơn interval)Hạn một lần thuKhôngKhông
buffer.max_bytes50 MiB (4 MiB đến 1 GiB)Trần WALKhôngKhông
buffer.max_age24 h (1 phút đến 7 ngày)Tuổi tối đa bản ghi WALKhôngKhông
buffer.catch_up_batches2 (1 đến 10)Số lô gửi bù thêm mỗi tickCóKhông
limits.max_series500 (1 đến 500)Trần số series mỗi chu kỳKhôngKhông
limits.memory_limit64 MiB (16 MiB đến 4 GiB)debug.SetMemoryLimitKhôngGOMEMLIMIT (bỏ qua khóa này nếu đã đặt)
log.levelinfo (debug, info, warn, error)Mức logCóAH_LOG_LEVEL, --log-level
log.formattext (text, json)Định dạng logKhôngKhông
log.filerỗng (stderr)Tệp logKhôngKhông
checks, labels, metrics.*, checks_allow_link_localXem L3 CollectorsThuộc thành phần thuKhôngKhông
allow_remote_configtrueChưa dùngKhông có tác dụngKhông

Giới hạn cứng biên dịch (internal/config/defaults.go): HardMaxSeries 500, MinInterval 10 s, MaxInterval 300 s, MaxChecks 50, MaxRegexLen 200, MaxLabelValue 128, MaxStaticLabels 20. Dải của từng khóa kiểm ở validate.go. Chuỗi cỡ chấp nhận dạng 64MiB, 50MB hoặc số nguyên (ParseSize, TestParseSize), khoảng thời gian dạng 30s (time.ParseDuration).

Tham số CLI (không nằm trong tệp)

Tham sốMặc địnhÝ nghĩa
--configtheo nền tảngĐường tệp cấu hình, chỉ định tường minh thì tệp thiếu là lỗi
--collector, --state-dir, --log-levelrỗngGhi đè env và tệp
enroll --token-file, --token-stdin, --token, --forcerỗng, false, rỗng, falseNguồn token và ghi đè (xem FR-E01, FR-E07)
collect-once --gap1 s (100 ms đến 1 phút)Khoảng giữa hai mẫu
AH_LICENSE_FILE, AH_LICENSErỗngNguồn token dự phòng khi không có cờ
Tệp token mặc định/etc/accesshub-agent/license.token (Windows C:\ProgramData\AccessHubAgent\license.token)Nguồn cuối cùng

Hằng số biên dịch (không cấu hình được)

HằngGiá trịVai trò
MaxTokenLen256 ký tựTrần token. Đầu vào đọc tối đa MaxTokenLen*4 = 1024 byte
Quyền tệp credentials.json0600, thư mục 0700Cứng trong Save
Muối machineIDSaltaccesshub-agent/machine-id/v1:Băm machine_id, đổi muối là đổi danh tính (OQ-E7)
Đường machine-id/etc/machine-id, /var/lib/dbus/machine-idThứ tự thử
Số IP gửi lúc enroll16Trần UsableIPs
Số lần thử lại enroll0Không có vòng thử lại

12.2. Feature flags ​

Không có cờ tính năng hoạt động. allow_remote_config được phân tích, mặc định true, và chưa được dùng ở đâu (L2 D-01, D-11): agent không áp dụng cấu hình từ xa và bỏ qua trường config trong phản hồi enroll. Cần nhớ rằng mặc định true nghĩa là khi tính năng cấu hình từ xa được xây, các agent cũ sẽ nhận cấu hình từ xa trừ khi người vận hành đặt false rõ ràng (ĐỀ XUẤT: cân nhắc mặc định false cho tới khi có cơ chế ký cấu hình, OQ-E3).


13. Telemetry & Vận hành ​

13.1. Metrics ​

Thành phần này không phát chỉ số nào. Các chỉ số agent_* do bộ thu tự thân (L3 Collectors) và Sender (L3 WAL và Sender) phát, không mô tả danh tính hay cấu hình. Khoảng trống (ĐỀ XUẤT, OQ-E17): chưa có chỉ số cho trạng thái identity_mismatch, số lần nạp lại bị từ chối, hay tuổi của credentials.json. Hiện quan sát qua log và Status() (13.4).

13.2. Log schema ​

Log theo logx (xem L3 WAL và Sender 13.2 về định dạng và che trường nhạy cảm). Trước khi logger được dựng (cấu hình sai, cờ sai) lỗi ra thẳng stderr với tiền tố accesshub-agent: và không có mức log.

Sự kiện (msg)MứcTrường thêmKhi nào
agent startedInfoversion, collector, interval, creds (token che)Sau khi nạp danh tính
cannot read the machine id, skipping the identity checkWarnerrKhông đọc được machine-id lúc khởi động
machine id changed since enrollment (cloned or restored image), delivery stopped, re-enroll with --forceErrorLệch danh tính (luồng 10)
agent failedErrorerrRun trả lỗi khác ErrNotEnrolled, gồm quyền rộng, hỏng, thiếu trường
config reload rejected, keeping the running configurationErrorerr (nhiều dòng tệp:dòng: ...)SIGHUP mà Load lỗi
config reload rejectedErrorerrSIGHUP mà SetLevel lỗi
config reloadedInfoSIGHUP thành công
new collector settings rejected, keeping the old onesErrorerrDựng client mới lỗi sau nạp lại (luồng 13)
statusInfostateNhận SIGUSR1
agent stoppedInfoDừng êm

Đầu ra CLI (stderr hoặc stdout, không qua slog): enrolled as agent <id>, warning: --token puts the secret in the process list, warning: token file <path> is readable by other users, run chmod 600, enrollment rejected: <gợi ý>, collector unavailable: ..., cannot reach the collector: ..., this host is not enrolled, run 'accesshub-agent enroll' first, config OK: <path>, no config file at <path>, using defaults and environment. Các chuỗi tiếng Anh này là tín hiệu ổn định cho script nhưng chưa được coi là hợp đồng (OQ-E9).

13.3. Alert to runbook ​

Thành phần không tự cảnh báo (ĐỀ XUẤT, chưa có ngưỡng thực đo).

Điều kiệnRunbook
enroll thoát mã 5 với token_usedToken đã dùng: xin token mới
enroll thoát mã 5 với binding_failedCollector không khớp máy với bản ghi server: kiểm hostname và IP, sửa bản ghi ở Access Hub
enroll thoát mã 5 với 426Nâng cấp agent
enroll thoát mã 6Mạng, TLS hoặc DNS tới Collector: kiểm tra curl, ca_file, proxy_url rồi chạy lại
enroll thoát mã 1 với token is now spentSửa đĩa hoặc quyền state_dir, xin token mới, enroll lại
run thoát mã 4Chạy enroll bằng tài khoản dịch vụ (sudo -u accesshub-agent ...)
run thoát mã 1 với quyền tệpchmod 600 credentials.json và kiểm chủ sở hữu (OQ-E5)
run thoát mã 3Chạy check-config để xem mọi lỗi và dòng
Log config reload rejectedSửa agent.yaml theo số dòng, gửi lại SIGHUP
Log machine id changedMáy nhân bản hoặc đổi machine-id: enroll --force rồi khởi động lại dịch vụ
Log new collector settings rejected lặpKiểm tệp ca_file và proxy_url vừa đổi (OQ-E12)

13.4. Probes ​

Không có endpoint. Quan sát cục bộ:

KênhNội dung
check-configKiểm cấu hình, in tóm tắt (không gọi mạng). Dùng trước khi nạp lại hoặc khởi động lại. In cảnh báo khi insecure_skip_verify bật
SIGUSR1Log status kèm Agent.Status(), gồm state=identity_mismatch khi dừng vì lệch danh tính
status, diagStub (mã 1), AGT-10
collect-onceThu một lần và in JSON, không cần danh tính hay mạng. Dùng để kiểm bộ thu (L3 Collectors)
Mã thoát0 đến 6 như 5.1

check-config không kiểm kết nối hay chứng chỉ (OQ-E2) và không kiểm tệp ca_file tồn tại (OQ-E12).

13.5. Trace propagation ​

Không áp dụng cho thành phần này ngoài việc lời gọi enroll mang X-Request-Id và X-AH-Agent-Version như mọi lời gọi khác (L3 WAL và Sender 13.5). Log phía agent không ghi X-Request-Id, nên đối chiếu một lần enroll thất bại với log Collector phải dựa vào thời điểm và tên máy.


14. Kế hoạch kiểm thử ​

Loại Test & Phạm viÁnh xạ mục tiêuMục tiêu kỹ thuậtVí dụ kịch bản (Test ID)
Unit: phân giải tokenAC-E01 đến E04, NFR-E05Thứ tự nguồn, xung đột, sai dạng, cảnh báo quyền, LimitReaderTestResolveTokenPriority, TestResolveTokenErrors, TestLoosePermissionsWarn, TestWipeOverwritesAndRemoves
CLI: luồng enrollAC-E04 đến E09, NFR-E04, E07Enroll từ tệp và xóa tệp, nguồn token, từ chối, không kết nối, enroll lặp, lưu lỗi, cấu hình saiTestEnrollFromFileStoresCredentialsAndWipesTheFile, TestLicenseSources, TestEnrollUsageErrors, TestEnrollRejections, TestEnrollConnectionProblemsExitSix, TestEnrollRefusesWhenAlreadyEnrolledUnlessForced, TestEnrollSaveFailureIsReportedAndKeepsTokenFile, TestEnrollRejectsBadConfig
Unit: kho danh tínhAC-E10 đến E14, NFR-E01 đến E03Nguyên tử, 0600, giữ tệp cũ khi lỗi, từ chối quyền rộng hoặc hỏng, che token, băm machine_idTestSaveLoadRoundTripWith0600, TestSaveIsAtomicAndLeavesNoTempFiles, TestFailedSaveKeepsOldFile, TestLoadRejectsLoosePermissionsCorruptAndIncomplete, TestCredentialsNeverPrintTheToken, TestDeleteAndExists, TestMachineIDIsSaltedHash
Unit: thông tin máyAC-E15Lọc IP, trần 16, dự phòng khi thiếu os-releaseTestUsableIPsFiltersAndCaps, TestReadOS, TestReadOSMissingFilesFallBack, TestGatherDoesNotPanic
Unit: nạp cấu hìnhAC-E16 đến E20, NFR-E06, E08, E09Mặc định, mẫu đầy đủ, số dòng, gom lỗi, tệp thiếu, độ ưu tiên, env, cỡ, che URL, fuzzTestLoadMinimalAppliesDefaults, TestLoadFullSampleFromDocs, TestLoadErrorsCarryLineNumbers, TestLoadReportsAllIssues, TestErrorFormatIncludesFileAndLine, TestMissingFile, TestEmptyFileNeedsURL, TestPrecedenceFlagsOverEnvOverFile, TestEnvInsecureSkipVerify, TestResolvePath, TestParseSize, TestRedactURL, FuzzLoadNeverPanics
CLI: khung lệnhAC-E21 đến E23, E26, NFR-E07Mã thoát, trợ giúp, phiên bản, check-config, stub, collect-onceTestNoArgsAndUnknownCommandAreUsageErrors, TestHelpListsEveryCommand, TestVersionOutput, TestCheckConfigValid, TestCheckConfigReportsLineNumbers, TestCheckConfigUsesEnvConfigPathAndOverrides, TestCheckConfigMissingExplicitFile, TestFlagErrorsAreUsageErrors, TestStubsFailClearly, TestCollectOncePrintsJSON, TestCollectOnceRejectsBadGapAndConfig
CLI và Agent: runAC-E24, E25Cấu hình sai, chưa enroll, dừng theo ctx, SIGHUP và SIGUSR1, lệch danh tính, đổi CollectorTestRunRejectsBadConfig, TestRunWithoutCredentialsExitsNotEnrolled, TestRunWithoutCredentialsIsNotEnrolled, TestRunStopsOnContextCancel, TestSignalsReloadAndStatus, TestMachineIDMismatchStopsDelivery, TestReloadSwitchesCollector
Chưa có: luật Validate theo từng khóaNFR-E06, NFR-E09Mỗi miền giá trị (interval, buffer, limits, regex, checks, labels, URL) có một ca sai riêngĐỀ XUẤT: bảng ca kiểm thử theo khóa (OQ-E11)
Chưa có: nhánh nạp lại bị từ chốiNFR-E06reload giữ cấu hình cũ và log lỗiĐỀ XUẤT: kiểm thử reload với tệp sai (OQ-E11)
Chưa có: khôi phục từ tệp hỏng bằng --forceAC-E07Đường Corrupt sang Present (7.3.1)ĐỀ XUẤT (OQ-E11)
Chưa có: ca_file hỏng sau nạp lạiLuồng 13Client cũ được giữ, log lặpĐỀ XUẤT (OQ-E12)
Chưa có: syncDir lỗi, hai enroll song songNFR-E02Hành vi khi hệ tệp không hỗ trợ fsync thư mục, tranh chấpĐỀ XUẤT (OQ-E8, OQ-E15)
Chưa có: WindowsFR-E25Quyền tệp và DPAPITHIẾT KẾ, CHƯA XÂY (D-05)

Lưu ý khi chạy: kiểm thử creds về quyền bị bỏ qua khi chạy bằng root hoặc trên Windows (creds_test.go kiểm os.Geteuid() == 0), nên kết quả trong CI chạy root không chứng minh được nhánh ErrInsecurePermissions. Chạy kiểm thử bằng người dùng thường. readMachineID được thay bằng giả trong kiểm thử CLI vì container CI thường không có /etc/machine-id.


15. Trình tự triển khai ​

15.1. Ma trận milestone ​

MilestoneNội dungPhụ thuộcĐóng góp nghiệm thu
E1Kho danh tính creds (Save nguyên tử, Load kiểm quyền, MachineID) và hostinfo. ĐÃ HIỆN THỰCKhôngAC-E10 đến E15
E2Nạp cấu hình phân lớp, kiểm miền, số dòng, check-config. ĐÃ HIỆN THỰCKhôngAC-E16 đến E21
E3enroll (nguồn token, Do, ánh xạ mã thoát, xóa tệp token). ĐÃ HIỆN THỰCE1, E2AC-E01 đến E09
E4Khung CLI, run, SIGHUP, SIGUSR1, collect-once. ĐÃ HIỆN THỰC (tín hiệu chỉ trên nền tảng không phải Windows)E1, E2, E3AC-E22 đến E26
E5Khép khoảng trống độ bền và kiểm thử: bền hóa thư mục (OQ-E8), khóa state_dir (OQ-E15), kiểm ca_file lúc Validate (OQ-E12), báo khóa cần khởi động lại (OQ-E14), thêm kiểm thử còn thiếu (OQ-E11). ĐỀ XUẤTE4AC mới (chưa có)
E6Khép hợp đồng vận hành: mã thoát riêng cho tệp danh tính hỏng (OQ-E6), phiên bản hợp đồng CLI (OQ-E9), định dạng có phiên bản cho credentials.json (OQ-E7), cảnh báo root enroll (OQ-E5). ĐỀ XUẤTE4AC mới (chưa có)
E7status, diag, kiểm kết nối trong check-config. THIẾT KẾ, CHƯA XÂY (AGT-10, D-04, OQ-E2)E4FR-E22
E8Cấu hình từ xa, xoay token. THIẾT KẾ, CHƯA XÂY (D-01, D-08, cần Collector)E4 và CollectorFR-E23, E24
E9Bảo vệ token và ACL Windows (DPAPI). THIẾT KẾ, CHƯA XÂY (D-05, AGT-7)E1, E4FR-E25

15.2. Sơ đồ phụ thuộc milestone ​

mermaid
flowchart LR
  E1["E1 · Kho danh tính"]
  E2["E2 · Nạp cấu hình"]
  E3["E3 · enroll"]
  E4["E4 · CLI và tín hiệu"]
  E5["E5 · Khép độ bền"]
  E6["E6 · Khép hợp đồng"]
  E7["E7 · status và diag"]
  E8["E8 · Cấu hình từ xa"]
  E9["E9 · Windows"]
  E1 --> E3
  E2 --> E3
  E3 --> E4
  E4 --> E5
  E4 --> E6
  E4 --> E7
  E4 --> E8
  E4 --> E9
  style E1 fill:#2d4a3e,stroke:#5fb37a,color:#fff
  style E2 fill:#2d4a3e,stroke:#5fb37a,color:#fff
  style E3 fill:#2d4a3e,stroke:#5fb37a,color:#fff
  style E4 fill:#2d4a3e,stroke:#5fb37a,color:#fff
  style E5 fill:#3a3320,stroke:#d9b84a,color:#fff
  style E6 fill:#3a3320,stroke:#d9b84a,color:#fff
  style E7 fill:#444,stroke:#aaa,color:#fff
  style E8 fill:#444,stroke:#aaa,color:#fff
  style E9 fill:#444,stroke:#aaa,color:#fff

Chú giải: xanh lá là đã hiện thực, vàng là đề xuất, xám là thiết kế chưa xây. Đường găng tới mọi khoản còn lại là E1 hoặc E2, rồi E3, rồi E4. Sau E4 các khoản E5 đến E9 độc lập nhau, riêng E8 cần thêm phía Collector.


Phụ lục A: Open Questions ​

#Câu hỏiHành vi tạm thờiOwnerMã theo dõi
OQ-E1Trợ giúp của enroll --token-file chỉ nói "deleted after a successful enroll", nhưng mọi tệp token đã dùng (cờ, AH_LICENSE_FILE, tệp mặc định) đều bị xóa. Có cập nhật trợ giúp và tài liệu không?Hành vi đúng như mã, chỉ trợ giúp chưa đủchưa chỉ địnhL3-ENR-OQ1
OQ-E2check-config không kiểm kết nối tới Collector, chứng chỉ hay tệp CA. Có thêm tùy chọn ping không (L2 D-04)?Chỉ kiểm cấu hình cục bộchưa chỉ địnhL3-ENR-OQ2
OQ-E3allow_remote_config mặc định true dù chưa dùng. Khi cấu hình từ xa được xây (D-01), mặc định nên là gì và có cần ký cấu hình?Không có tác dụng, phản hồi config của enroll bị bỏchưa chỉ địnhL3-ENR-OQ3
OQ-E4enroll --force không thu hồi token cũ ở Collector, và dịch vụ đang chạy giữ token cũ trong RAM. Có cần bước thu hồi hoặc tự khởi động lại dịch vụ?Ghi trong hướng dẫn: sau --force phải khởi động lại dịch vụchưa chỉ địnhL3-ENR-OQ4
OQ-E5Enroll bằng root tạo credentials.json thuộc root 0600 mà dịch vụ không đọc được. Có cần chown về tài khoản dịch vụ hoặc từ chối chạy bằng root?Hướng dẫn và postinstall yêu cầu enroll bằng tài khoản dịch vụchưa chỉ địnhL3-ENR-OQ5
OQ-E6Tệp danh tính quyền rộng hoặc hỏng thoát mã 1, không phải mã 4, và tệp token chỉ định mà thiếu cũng mã 1. Có cần mã riêng để script phân biệt?Mã 1, phải đọc thông điệpchưa chỉ địnhL3-ENR-OQ6
OQ-E7credentials.json và muối machine_id chưa có số phiên bản định dạng. Đổi định dạng hoặc thuật toán băm thì nâng cấp thế nào?Không có. Đổi định dạng buộc enroll lạichưa chỉ địnhL3-ENR-OQ7
OQ-E8syncDir bỏ qua lỗi, nên sau mất điện ngay khi đổi tên tệp có thể mất credentials.json mà không báo. Có cần báo hoặc coi là lỗi lưu?Bỏ qua lỗichưa chỉ địnhL3-ENR-OQ8
OQ-E9Mã thoát, tên cờ, tên biến môi trường và chuỗi thông điệp chưa có phiên bản hợp đồng và chính sách ngừng hỗ trợ, trong khi script gói và hướng dẫn dựa vào chúngKhông đổi nếu không cầnchưa chỉ địnhL3-ENR-OQ9
OQ-E10Sau SaveError, token đã tiêu hao: Collector có cho enroll lại cùng máy bằng token mới hay trả already_enrolled? Quy trình đặt lại bản ghi ở phía Access Hub là gì?Thông điệp báo cần token mới, phần còn lại do quản trị viên xử lýchưa chỉ địnhL3-ENR-OQ10
OQ-E11Còn khoảng trống kiểm thử: luật Validate theo từng khóa, nhánh reload bị từ chối, khôi phục từ tệp hỏng bằng --force (xem §14)Chỉ được phủ gián tiếp bởi kiểm thử mẫu đầy đủ và kiểm thử lỗi có số dòngchưa chỉ địnhL3-ENR-OQ11
OQ-E12Validate không kiểm tệp ca_file tồn tại, nên SIGHUP đổi ca_file hỏng báo config reloaded rồi log lỗi lặp mỗi chu kỳ (luồng 13). Có kiểm khi nạp không?Giữ client cũchưa chỉ địnhL3-ENR-OQ12
OQ-E13KnownFields(true) làm agent cũ từ chối khóa mới. Khi phát hành cấu hình chung cho đội nhiều phiên bản, xử lý thế nào (ví dụ bỏ qua có cảnh báo khóa lạ)?Từ chối (mã 3)chưa chỉ địnhL3-ENR-OQ13
OQ-E14config reloaded được log dù một số khóa chỉ có hiệu lực sau khởi động lại (bảng 7.4.5). Có log danh sách khóa đã đổi nhưng cần khởi động lại?Chỉ log config reloadedchưa chỉ địnhL3-ENR-OQ14
OQ-E15Không có khóa liên tiến trình trên state_dir: hai enroll hoặc enroll cùng lúc với dịch vụ có thể giẫm nhau. Có cần flock? (Cùng họ OQ-W11 của L3 WAL và Sender)Không xử lýchưa chỉ địnhL3-ENR-OQ15
OQ-E16Thông báo của service trên Windows nêu AGT-8 nhưng L2 gán dịch vụ Windows cho AGT-7 (AGT-8 là Checks). Sửa thông báo hay sửa L2? ĐÃ XỬ LÝ: thông báo nay nêu AGT-7Đã sửa thông báođã đóngL3-ENR-OQ16
OQ-E17Chưa có chỉ số cho lệch danh tính, nạp lại bị từ chối, tuổi credentials.json. Có thêm vào bộ thu tự thân không?Chỉ logchưa chỉ địnhL3-ENR-OQ17
OQ-E18Nhãn tĩnh trong agent.yaml kiểm bằng regex rộng hơn giao thức (OQ-C5 ở L3 Collectors). Thống nhất ở đâu?Theo mã hiện tạichưa chỉ địnhL3-ENR-OQ18
OQ-E19Quyền và chủ sở hữu của agent.yaml do gói cài quyết định (L3 Service và Packaging). Cấu hình có thể chứa proxy_url kèm mật khẩu: có yêu cầu 0640 và nhóm dịch vụ không? ĐÃ XỬ LÝ: gói cài 0640 root:accesshub-agent0640 root:accesshub-agentđã đóngL3-ENR-OQ19

Phụ lục B: ADR nội bộ ​

Mã ADRQuyết địnhTrạng tháiĐộng lực
ADR-E01Token enroll dùng một lần, đổi lấy token agent dài hạn lưu cục bộĐã hiện thựcGiảm phơi nhiễm: token enroll lộ vô hại sau khi dùng. Đánh đổi: token dài hạn nằm dạng rõ trên đĩa (R-02)
ADR-E02Ghi credentials.json bằng tệp tạm, Chmod 0600 trước khi ghi, RenameĐã hiện thựcKhông có khoảnh khắc token lộ quyền hay nửa vời. Đánh đổi: syncDir bỏ qua lỗi (OQ-E8)
ADR-E03Load từ chối quyền rộng thay vì tự sửaĐã hiện thựcBuộc người vận hành biết mình đã lộ, không che giấu. Đánh đổi: mã thoát 1 chưa phân biệt (OQ-E6)
ADR-E04Băm machine_id có muối, gửi băm cho Collector, so lúc khởi động và dừng gửi nếu lệchĐã hiện thựcChống ảnh máy nhân bản mạo danh mà không lộ id thô. Đánh đổi: machine-id nhân bản nguyên vẹn thì không phát hiện được
ADR-E05Lệch danh tính thì dừng gửi nhưng tiến trình vẫn sốngĐã hiện thựcTránh vòng khởi động lại của systemd. Đánh đổi: giám sát thấy active dù không có dữ liệu (cùng họ OQ-W7)
ADR-E06Cấu hình YAML nghiêm ngặt (KnownFields), gom hết lỗi có số dòng, cấu hình sai thì không chạyĐã hiện thựcBắt lỗi gõ sai khóa, sửa một lượt. Đánh đổi: khóa mới làm agent cũ từ chối (OQ-E13)
ADR-E07Thứ tự ưu tiên mặc định, tệp, env, cờ, với env giới hạn 4 biếnĐã hiện thựcBề mặt nhỏ, dễ dự đoán, đủ cho đóng gói. Đánh đổi: muốn đổi khóa khác phải sửa tệp
ADR-E08Nạp nóng chỉ thay con trỏ cấu hình nguyên tử, không dựng lại engine hay WALĐã hiện thựcLoại rủi ro mất dữ liệu khi dựng lại giữa chu kỳ. Đánh đổi: nhiều khóa cần khởi động lại (OQ-E14)
ADR-E09enroll không thử lại, mã thoát phân loại để script quyết địnhĐã hiện thựcĐơn giản, postinstall không bao giờ làm hỏng gói vì enroll. Đánh đổi: phải chạy lại thủ công khi mạng lỗi
ADR-E10Token nhận qua --token-stdin hoặc tệp 0600 bị xóa sau dùng, --token chỉ cảnh báoĐã hiện thựcTránh lộ trong danh sách tiến trình nhưng không chặn người dùng cần tiện. Đánh đổi: AH_LICENSE lộ trong /proc/<pid>/environ

Phụ lục C: Section Profile ​

Phân mụcHồ sơ quy chuẩnTrạng thái điềnGiải trình
§0 Metadata & Sign-offBắt buộcĐã điềnNgười ký để "chưa chỉ định"
§1 ScopeBắt buộcĐã điền
§2 Yêu cầuBắt buộcĐã điềnFR-E01 đến E25, NFR-E01 đến E10, AC-E01 đến E26, QAS-E01 đến E06
§3 Kiến trúcBắt buộcĐã điền
§4 Domain modelBắt buộcĐã điềnHai vùng: danh tính và cấu hình
§5 API contractBắt buộcĐiền thu gọnKhông có API phục vụ. Hợp đồng gồm CLI, mã thoát, định dạng tệp và lời gọi enroll ra
§6 Data schemaTùy chọnĐã điềnLược đồ tệp credentials.json và agent.yaml thay cho CSDL
§7 Thuật toánBắt buộcĐã điền13 luồng, hai máy trạng thái, sáu thuật toán
§8 Xử lý lỗiBắt buộcĐã điền
§9 Suy thoáiBắt buộcĐã điền9.2: credentials.json không sao lưu, tái cấp bằng enroll
§10 Đồng thờiBắt buộcĐiền thu gọnKhông có giao dịch CSDL, ranh giới là thao tác tệp
§11 Bảo mậtBắt buộcĐã điềnNêu thẳng các điểm yếu còn lại (R-02)
§12 Cấu hìnhBắt buộcĐã điềnKhông có feature flag hoạt động
§13 TelemetryBắt buộcĐiền thu gọnKhông phát chỉ số, không probe mạng, không trace phân tán
§14 Kiểm thửBắt buộcĐã điềnCó 6 khoảng trống đề xuất
§15 Triển khaiBổ sungĐã điền
Trang này có giúp được bạn không?
Sửa trang này

Nội dung đồng bộ từ kho mã access-hub-agent lúc 10:57, 03/10/2026. Khi tài liệu và mã khác nhau, mã thắng.