Giao thức
Đây là nguồn sự thật của giao thức giữa agent và collector, và của API nội bộ giữa Access Hub và collector. Phiên bản giao thức hiện tại: 1. Giao thức giữa collector và Access Hub là API v1 của Access Hub, mô tả ở 06-access-hub-integration và lấy từ OpenAPI do Scramble sinh ra.
1. Quy ước chung (agent -> collector)
- Cơ sở URL:
https://<collector>/agent/v1. Chỉ TLS 1.2 trở lên, HTTP/1.1 hoặc HTTP/2. - Agent chỉ kết nối đi ra. Không có cổng nghe trên agent.
- Nội dung chính là protobuf (
Content-Type: application/x-protobuf), nén bằngzstdhoặcgzipquaContent-Encoding. Collector luôn chấp nhận không nén. JSON (application/json) chỉ được chấp nhận khi collector bậtdebug.accept_json, dùng để gỡ lỗi. - Thời gian: số nguyên mili giây từ epoch UTC.
Header
| Header | Bắt buộc | Ý nghĩa |
|---|---|---|
Authorization: Bearer <agent_token> | Mọi API trừ enroll và ping | Token của agent |
X-AH-Proto | Có | Phiên bản giao thức agent hỗ trợ, ví dụ 1 |
X-AH-Agent-Version | Có | Semver của agent, ví dụ 1.4.2 |
X-Request-Id | Khuyến nghị | UUID mỗi request, xuất hiện trong log hai phía |
Content-Type, Content-Encoding | Khi có body | Như trên |
Danh tính (agent, công ty, máy chủ) chỉ lấy từ token qua registry. Mọi trường danh tính nằm trong body (nếu có) bị bỏ qua khi ghi số liệu.
Giới hạn
| Giới hạn | Giá trị mặc định | Vượt thì |
|---|---|---|
| Kích thước body sau nén | 1 MiB | 413 |
| Kích thước sau giải nén | 8 MiB | 413 |
| Số series khác nhau của một agent (hoạt động trong 1 giờ) | 500 | Series mới bị bỏ, trả cảnh báo trong response, tăng metric series_limit_dropped |
| Số series khác nhau của một công ty trên một node ingest (COL-11) | Tắt (ingest.limits.max_series_per_company: 0) | Series mới bị bỏ với cảnh báo company series limit reached, lý do company_series_limit |
| Số điểm trong một request | 20.000 | 413 |
| Tên chỉ số | ^[a-z][a-z0-9_]{0,63}$ | Series bị bỏ |
| Khóa nhãn | ^[a-z][a-z0-9_]{0,31}$ | Series bị bỏ |
| Giá trị nhãn | tối đa 128 byte UTF-8 | Cắt bớt |
Nhãn dành riêng (company_id, server_id, agent_id) | Không được gửi | Bị bỏ và ghi log |
| Cửa sổ thời gian điểm dữ liệu | Không cũ hơn 24 giờ, không quá 5 phút ở tương lai | Điểm bị bỏ, đếm vào response |
| Tốc độ | 4 request/phút/agent (burst 10) cho metrics, config 2/phút | 429 |
Mã trạng thái và lỗi
Lỗi trả JSON: {"error":{"code":"<mã>","message":"<mô tả>"}}, có thể kèm Retry-After (giây).
| HTTP | code | Ý nghĩa | Agent làm gì |
|---|---|---|---|
| 200/202 | Thành công | Xóa lô khỏi bộ đệm | |
| 304 | Cấu hình không đổi | Giữ nguyên | |
| 400 | bad_request | Body sai định dạng | Bỏ lô (không thử lại), ghi log |
| 401 | unauthorized | Token sai hoặc hết hạn | Ngừng gửi, thử lại config mỗi 10 phút, báo trạng thái unauthorized |
| 403 | agent_revoked | Agent đã bị thu hồi | Ngừng vĩnh viễn cho đến khi enroll lại |
| 409 | already_enrolled, token_used | Xung đột khi enroll | Báo lỗi, không thử lại tự động |
| 413 | too_large | Vượt giới hạn | Tách nhỏ lô rồi gửi lại một lần, nếu vẫn lỗi thì bỏ |
| 422 | binding_failed, quota_exceeded, invalid_series | Không ghép được máy chủ, gói của công ty đã đủ số agent (monitoring_agents, do Access Hub kiểm tra lúc enroll), hoặc dữ liệu không hợp lệ | Lỗi enroll: báo, không thử lại tự động. Lỗi dữ liệu: bỏ phần lỗi |
| 426 | upgrade_required | Giao thức agent quá cũ | Tiếp tục gửi nếu có thể, báo cần nâng cấp |
| 429 | rate_limited | Quá tốc độ | Tôn trọng Retry-After, backoff |
| 500, 502, 503, 504 | server_error, unavailable | Lỗi phía hub | Giữ lô, backoff |
| Lỗi mạng, timeout | Giữ lô, backoff |
Backoff của agent: khởi đầu 5 giây, nhân đôi mỗi lần, tối đa 300 giây, full jitter (chọn ngẫu nhiên trong [0, backoff]), luôn tôn trọng Retry-After nếu lớn hơn. Reset sau lần thành công đầu tiên.
Jitter khởi động: thời điểm gửi đầu tiên và pha của chu kỳ lệch ngẫu nhiên xác định theo hash(agent_id) mod interval, để hàng nghìn agent khởi động cùng lúc không đồng loạt gửi cùng một giây.
2. Các endpoint agent -> collector
2.1 GET /agent/v1/ping
Không xác thực. Trả 200 {"server_time_ms":..., "proto_min":1, "proto_max":1}. Dùng cho check-config và kiểm tra kết nối lúc cài.
2.2 POST /agent/v1/enroll
Không dùng Authorization. Body EnrollRequest: license, hostname, ip_addresses[], machine_id, os {family, name, version, kernel, arch}, agent_version.
Response EnrollResponse: agent_id, agent_token (chỉ hiện đúng lần này), collector_url, config (cấu hình khởi tạo, xem 2.4), server_time_ms.
Quy tắc ghép máy chủ (quyết định ở Access Hub, collector chỉ chuyển tiếp):
- License gắn sẵn
server_id: ghép thẳng, khuyến nghị dùng. - Không gắn sẵn: thử khớp
ip_addressesvớiServer.ip_addresstrong công ty của token, sau đó khớphostname. - Không khớp: theo cấu hình công ty, hoặc trả
422 binding_failed, hoặc tạoServernháp cần duyệt (mặc định tắt).
machine_id (Linux: /etc/machine-id, Windows: MachineGuid) dùng để phát hiện cùng một máy enroll lại và để phát hiện máy nhân bản từ image (cùng machine_id nhưng khác hostname).
2.3 POST /agent/v1/metrics
Body MetricsBatch:
MetricsBatch {
uint64 seq // tăng dần theo agent, dùng để gỡ lỗi và chống lặp
int64 sent_at_ms
repeated Series series
AgentStats stats // số liệu tự thân của agent
}
Series { string name; map<string,string> labels; repeated Point points }
Point { int64 ts_ms; double value }Response MetricsAck: server_time_ms (để agent tính độ lệch đồng hồ), ack_seq, config_etag (khác etag hiện có thì agent gọi config ngay), accepted_points, dropped_points, warnings[].
Gửi lặp cùng một điểm là an toàn: TSDB khử trùng theo (series, ts). Gói metrics đồng thời là heartbeat: mỗi lần nhận thành công cập nhật last_seen. Agent không có gì để gửi vẫn phải gửi gói rỗng đúng chu kỳ.
2.4 GET /agent/v1/config
Hỗ trợ If-None-Match: <etag>. Trả 304 hoặc 200 AgentConfig:
AgentConfig {
string etag
uint32 interval_seconds // 10..300, 0 = không đặt (agent giữ giá trị cục bộ, mặc định 30)
CollectorsConfig collectors // bật tắt từng bộ thu, lọc mount, lọc interface
repeated Check checks // port, tcp, http, cert, service (xem 03-metrics-catalog của agent)
Limits limits // max_series, max_batch_bytes, buffer_bytes
string min_agent_version // dưới ngưỡng này thì cảnh báo cần nâng cấp
string collector_url // đổi giá trị này để chuyển agent sang collector khác
string log_level
}Checks (COL-20): collector kéo check từ Access Hub (06 mục 4.8) và điền checks theo cặp (company_id, server_id) của agent đã xác thực, không bao giờ theo nội dung request. checks tối đa 50, mỗi check chỉ mang trường của đúng loại, timeout_seconds mặc định 5, expect_status mặc định 200. ETag cấu hình phụ thuộc tập check, đổi thì MetricsAck.config_etag đổi và agent tải lại. Chưa có GET /checks ở Access Hub thì checks rỗng.
Trường không đặt (AGT-10): giá trị 0, chuỗi rỗng hoặc thông điệp vắng nghĩa là trung tâm không quản lý trường đó, agent giữ giá trị cục bộ: interval_seconds = 0, collectors vắng (khi có thì thay toàn bộ cờ bật tắt cpu, memory, disk, net, uptime; danh sách lọc rỗng giữ danh sách cục bộ), limits.max_series = 0, checks rỗng. limits.max_series chỉ là trần (agent lấy giá trị nhỏ hơn). log_level và collector_url không thuộc danh sách trắng của agent ở GĐ 1 và 2. Khi Access Hub chưa quản lý cấu hình cho agent, collector chỉ trả limits và collector_url (thông tin), mọi agent chạy theo agent.yaml của mình. Giá trị ngoài miền (chu kỳ, regex sai hoặc dài quá 200 ký tự) bị bỏ từng khóa kèm cảnh báo; nếu kết quả vẫn làm cấu hình agent không hợp lệ thì agent bỏ cả bản và giữ cấu hình cục bộ. min_agent_version luôn được đọc (chỉ để cảnh báo), kể cả khi allow_remote_config: false.
Agent gọi config mỗi 300 giây (jitter cộng trừ 20%, pha lệch theo agent_id) và khi config_etag trong MetricsAck đổi (tối đa một lần mỗi 30 giây). Bản hợp lệ gần nhất lưu ở remote-config.pb trong thư mục trạng thái (0600) và được áp ngay khi khởi động lại. Cấu hình cục bộ có thể khóa (allow_remote_config: false), khi đó bỏ qua toàn bộ cấu hình từ xa, chỉ giữ min_agent_version (chỉ dùng để cảnh báo). Thay đổi collector_url từ xa chỉ được chấp nhận từ giai đoạn 3 và với điều kiện: kênh https, chữ ký Ed25519, collector mới trả lời ping hợp lệ (quy tắc này được cả hai bên tuân thủ), và không áp dụng cho agent đã khóa cấu hình từ xa.
2.5 POST /agent/v1/inventory
Body InventoryFacts: os {...}, cpu {model, cores, threads}, memory_total_bytes, disks[] {mount, fstype, total_bytes}, nics[] {name, ips[]}, boot_time_ms, timezone, agent_build {version, commit, go_version}. Địa chỉ MAC mặc định không gửi. Gửi khi khởi động, mỗi 6 giờ, và khi băm nội dung thay đổi. Collector chuyển tiếp lô sang Access Hub. Access Hub không tự ghi đè Server (xem 06).
2.6 POST /agent/v1/credentials/renew
Cần Authorization hiện tại. Trả agent_token mới. Token cũ còn hiệu lực thêm 24 giờ để agent chuyển đổi an toàn. Agent gọi mỗi 30 ngày (cấu hình được), và gọi lại khi lần lưu token mới ra đĩa thất bại (dùng token cũ trong thời gian chồng lấn).
2.7 GET /agent/v1/update (giai đoạn 3)
Query os, arch, version, channel. Trả manifest {version, url, sha256, signature_ed25519, min_version, rollout_percent, released_at} hoặc 204. Cohort theo hash(agent_id) mod 100 < rollout_percent. Có công tắc dừng khẩn cấp ở phía collector.
3. Phiên bản hóa
X-AH-Protolà số nguyên chính. Collector hỗ trợ N và N-1 tối thiểu 12 tháng.- Thêm trường protobuf theo quy tắc tương thích (không đổi số trường, không tái dùng số đã xóa). Trường mới do agent cũ không gửi phải có mặc định an toàn.
- Endpoint mới hoặc đổi ngữ nghĩa không tương thích: tăng
vtrong đường dẫn (/agent/v2) và giữv1theo chính sách trên. buf breakingchạy trong CI của cả hai repo để chặn thay đổi phá vỡ.
4. API nội bộ Access Hub -> collector (vai trò admin)
Tiền tố /internal/v1. Xác thực Authorization: Bearer <collector_admin_token> (bí mật riêng, xoay được), chỉ mở trong mạng nội bộ, đi qua TLS. Access Hub luôn truyền company_id tường minh; collector tự chèn bộ lọc nhãn company_id, server_id vào mọi truy vấn.
| Method, đường dẫn | Ý nghĩa |
|---|---|
PUT /internal/v1/agents/{agent_id} | Đẩy bản ghi registry mới hoặc cập nhật (token_hash, company_id, server_id, state) |
DELETE /internal/v1/agents/{agent_id} | Thu hồi ngay: xóa khỏi cache, từ chối request kế tiếp |
POST /internal/v1/reload/rules | Yêu cầu kéo lại luật từ Access Hub ngay |
POST /internal/v1/reload/settings | Nạp lại cấu hình theo công ty (interval mặc định, giới hạn series) |
GET /internal/v1/servers/{server_id}/status?company_id= | Trạng thái, last_seen, giá trị mới nhất của các chỉ số chính |
GET /internal/v1/servers/{server_id}/series?company_id=&metric=&from=&to=&step=&agg= | Chuỗi thời gian, tự chọn raw hoặc rollup theo khoảng thời gian. metric phải thuộc danh sách trắng (xem catalog). agg thuộc avg,min,max,last |
GET /internal/v1/companies/{company_id}/summary | Số máy online, offline, số alert đang mở (cho tổng quan) |
DELETE /internal/v1/companies/{company_id}/data | Xóa toàn bộ series, registry, trạng thái của công ty |
DELETE /internal/v1/servers/{server_id}/data?company_id= | Xóa dữ liệu của một máy chủ |
GET /internal/v1/status | Phiên bản, vai trò, độ trễ bus, độ sâu outbox, số agent online |
Ghi chú triển khai (COL-7):
- Mọi phản hồi thành công bọc trong
{"data": ...}, lỗi dạng{"error":{"code","message"}}với mãbad_request,unauthorized,not_found,method_not_allowed,not_implemented,unavailable. Token admin so sánh hằng thời gian và là bí mật riêng, khác token hub. PUT /agents/{id}nhậnAgentRecord(company_id,server_id,statelàpending|active|revoked,token_hashlà SHA-256 hex thường,prev_token_hash,prev_token_expires_at,version).DELETEidempotent, luôn trả 204, agent bị thu hồi nhận 403 ở lần gửi kế tiếp. Với Redis registry, các node ingest có thể còn cache tối đaredis.cache_ttl(mặc định 15 giây).POST /reload/rules(COL-8) kéo lại luật từ Access Hub ngay và trả 202{"data":{"reload":"rules","accepted":true}}. Khi admin chạy cùng tiến trình với worker, 202 nghĩa là đã kéo và áp xong (503unavailablenếu Access Hub không gọi được, bộ luật đang chạy giữ nguyên). Khi admin chạy tách tiến trình khỏi worker, lệnh tăng bộ đếm Redis<prefix>rules:reloadvà các worker nạp lại trong vòng khoảng 2 giây.POST /reload/silences(COL-9) kéo lại silence, cùng cơ chế (202, hoặc bộ đếm Redis<prefix>silences:reloadkhi admin tách tiến trình).POST /reload/checks(COL-20) kéo lại check trung tâm, cùng cơ chế (202, hoặc bộ đếm Redis<prefix>checks:reloadkhi admin tách tiến trình khỏi ingest);GET /statuscó thêm mụcchecks(loaded,invalid,disabled,servers,truncated,since,last_sync_at,last_sync_error).POST /reload/settings(COL-9, mở rộng ở COL-10) xóa cache cài đặt theo công ty: giới hạn retention theo gói của tiến trình admin, ngưỡnggroup_downcủa worker cùng tiến trình, và khi admin tách tiến trình thì tăng bộ đếm Redis<prefix>settings:reloadđể các worker xóa cache trong khoảng 2 giây. Trả 202, chỉ trả 501not_implementedkhi không có Access Hub.GET /statuscó thêm mụcsilences(active,loaded,since,last_sync_at,last_sync_error) vàrules.flapping.GET /statuscó thêm mụcrules(loaded,invalid,pending,firing,no_data,since,last_sync_at,last_sync_error,invalid_rules).series:from,tolà RFC3339 (dùngZ, dấu+trong URL phải mã hóa) hoặc epoch giây, mặc định 1 giờ gần nhất. Khoảng tối đa 400 ngày khi collector cótsdb.long_url(rollup), 31 ngày khi không có.steplà duration (5m) hoặc số giây, tối thiểu 30s, tối đa 11000 điểm; bỏ trống thì collector chọn khoảng chia 500 điểm. Truy vấn luôn ràng buộc cảcompany_idlẫnserver_id, nên gọi chéo công ty trả về rỗng.serieschọn nguồn (COL-10):fromcách hiện tại không quá 7 ngày đọcvm-raw; xa hơn đọc rollup có ô (5 phút hoặc 1 giờ) lớn nhất mà không rộng hơnstep, vàstepđược làm tròn lên bội số của ô. Mỗi điểm gộp lại các ô trongstepbằng cùng hàm (maxcủa các ômax, ...), nên đỉnh không bị mất. Rollup trống mà khoảng còn nằm trong retention thô (30 ngày) thì đọc lạivm-raw.step_secondstrong phản hồi là bước thực dùng, có thể lớn hơn bước yêu cầu.seriestheo gói (COL-10, ân hạn từ Q17): collector cắtfromvềnow - giới hạn đọc, với giới hạn đọc =metrics_retention_previous_dayskhi còn ân hạn (metrics_retention_grace_untilở tương lai), ngược lạimetrics_retention_days(mục 5.1). Phản hồi có thêmretention_days(retention hiện tại, chỉ khi công ty có giới hạn); trong ân hạn có thêmretention_previous_days(vắng nếu trước đó không giới hạn),retention_grace_untilvàretention_locked_before(dữ liệu trước mốc này chỉ đọc, sẽ bị xóa khi hết ân hạn: giao diện nên tô khác vùng này).fromlà giá trị sau khi cắt. Khoảng nằm trọn ngoài giới hạn trả 200 vớiseriesrỗng. Collector cache 5 phút,POST /reload/settingsxóa cache. Không gọi được Access Hub thì giữ chính sách đã biết, công ty chưa từng biết thì không cắt.GET /statuscó thêm mụcretention(lần quét gần nhất của janitor:at,companies,skipped,days_deleted,legacy_samples_moved,errors,dry_run,duration_seconds), chỉ số tổng, không cócompany_id.statustrả 404 nếu máy chủ không thuộc công ty.statelàonline,downhoặcunknown(chưa từng có tín hiệu thật hoặc im lặng nhưng chưa bị kết luận mất).summarytrảagents_total,online,offline. Chưa cóopen_alerts: số alert đang mở do Access Hub tính từmonitoring_alerts, collector chỉ giữ trạng thái đánh giá nội bộ (xemGET /status, mụcrules).- Xóa dữ liệu (COL-11): collector ghi bia mộ cho công ty hoặc máy trước (Redis
<prefix>purge:tombstones, sống 15 phút), rồi xóa registry (agent ngừng gửi được), rồi xóa TSDB (vm-rawvàvm-long), và tự xóa TSDB thêm một lần sau 10 giây. Worker đọc bia mộ mỗi 2 giây (ngay lập tức nếu cùng tiến trình với admin): bỏ mọi lô của công ty hoặc máy đó còn trên bus hoặc trong bộ đệm ghi, quên trạng thái alert, flapping và nhómgroup_downcủa nó không phát sự kiện (Access Hub tự xóa alert khi purge). Phản hồi cótombstone_until. Không ghi được bia mộ thì trả 503 và chưa xóa gì, Access Hub gọi lại. Gọi lặp là an toàn (idempotent).
Định dạng trả về của series (trong data):
{
"server_id": "srv_a", "company_id": "co_a", "metric": "cpu_usage_percent",
"agg": "avg", "from": "2026-01-01T00:00:00Z", "to": "2026-01-01T01:00:00Z", "step_seconds": 60,
"series": [{"labels": {}, "points": [[1767225600000, 12.4], [1767225660000, 13.1]]}]
}Điểm là [ts_ms, value]. Không có trường source (raw hay rollup do collector tự chọn). Trường tùy chọn retention_days xuất hiện khi gói của công ty giới hạn thời gian đọc (COL-10). Mọi thay đổi ở mục này là bổ sung, tương thích ngược với Access Hub đang gọi.
5. API collector -> Access Hub
Danh sách đầy đủ và hợp đồng ở 06-access-hub-integration mục "API dành cho collector". Tóm tắt: enroll, agents (đồng bộ registry), rules (đồng bộ luật), events, seen, inventory, collectors/heartbeat. Tất cả idempotent, có phiên bản since, giới hạn tốc độ riêng.
5.1 Hợp đồng retention trong GET /settings/{company_id} (phiên bản hợp đồng 2, ngày 02/10/2026)
Bổ sung tương thích ngược vào phản hồi của 06 mục 4.4. Mọi trường là tùy chọn, null hoặc vắng có nghĩa như cột "Vắng".
| Trường | Kiểu | Ý nghĩa | Vắng |
|---|---|---|---|
metrics_retention_days | số nguyên 1 đến 3650 | Retention của gói đang hiệu lực | Không giới hạn theo gói |
metrics_retention_previous_days | số nguyên 1 đến 3650 | Retention ngay trước lần giảm gần nhất còn trong ân hạn (lớn nhất nếu giảm nhiều lần trong ân hạn) | Trước đó không giới hạn (chỉ có nghĩa khi có grace_until) |
metrics_retention_grace_until | RFC3339 UTC | Hết ân hạn 30 ngày tính từ lần giảm gần nhất | Không có ân hạn |
Ngữ nghĩa phía collector (chính sách Q17, ADR 0016):
- Đọc: trong ân hạn, công ty đọc được tới
previous_days(không giới hạn nếu trước đó không giới hạn); hết ân hạn thì tớimetrics_retention_days. - Vùng khóa: dữ liệu cũ hơn
now - metrics_retention_dayskhông bao giờ được ghi thêm (worker bỏ mẫu), kể cả trong ân hạn. - Xóa vật lý: janitor xóa cả ngày (UTC) nằm hoàn toàn ngoài cửa sổ đọc, ở cả kho thô và kho rollup, chạy mỗi giờ. Trong ân hạn không xóa gì trong cửa sổ
previous_days. Dữ liệu rời đĩa muộn nhất khoảng 1 ngày sau khi ra khỏi cửa sổ. - An toàn: không xóa khi Access Hub không trả lời; luôn giữ ít nhất 1 ngày; công ty mới thấy chờ 24 giờ trước lần xóa đầu; collector tự nhớ retention lớn nhất thấy trong 30 ngày qua và không xóa dưới mức đó (phòng khi Access Hub quên gửi ân hạn). Tăng retention có hiệu lực ngay cho đọc; dữ liệu đã xóa không lấy lại được.
- Access Hub gọi
POST /internal/v1/reload/settingssau mỗi lần đổi để đọc có hiệu lực ngay (janitor luôn hỏi lại Access Hub, không dùng cache).
6. Tệp .proto
Đặt trong proto/accesshub/agent/v1/agent.proto, gói accesshub.agent.v1, sinh mã Go cho cả agent và collector bằng buf generate. Bản nháp đã có ở proto/accesshub/agent/v1/agent.proto (hạng mục X-2 sẽ chốt, go_package còn là giá trị giữ chỗ chờ quyết định Q1). Repo collector là chủ sở hữu, agent phụ thuộc qua Go module dùng chung .../proto hoặc sao chép có kiểm tra CI (xem 13-risks-open-questions, câu hỏi về Go module path).