Tích hợp với Access Hub
Tài liệu này là hợp đồng giữa hai phía: phần việc phải làm trong Access Hub (Laravel) và các API mà collector gọi. Access Hub là control plane, nguồn sự thật cho danh tính, kiểm kê, luật và alert.
1. Nguyên tắc tích hợp
- Mọi dữ liệu nghiệp vụ (agent, token, luật, alert, cài đặt) nằm trong MySQL của Access Hub. Không có mẫu số liệu trong MySQL.
- Mọi model mới dùng
CompanyScope,Auditable(khi có thao tác của người dùng), uuid làm khóa công khai, theo đúng quy ước các model hiện có (Server,Connection). - Collector là một "người dùng dịch vụ": user thường có vai trò chứa quyền
monitoring.collector, dùng Sanctum token. Hệ thống hiện chưa có khái niệm service account nên không tạo cơ chế mới. - Tất cả API collector gọi là idempotent và phân trang bằng
since(version tăng dần), để collector khởi động lại hoặc gọi lặp không gây hại. - Access Hub không bao giờ nhận PromQL hay truy cập TSDB trực tiếp. Mọi truy vấn số liệu đi qua API nội bộ của collector (03-protocol mục 4).
2. Bảng dữ liệu mới (MySQL)
| Bảng | Cột chính | Ghi chú |
|---|---|---|
monitoring_collectors | id, uuid, name, base_url, admin_url, status, version, last_heartbeat_at, agent_count, settings json | Toàn cục (không thuộc công ty), chỉ super admin quản lý. GĐ 3 dùng để gán agent theo collector |
monitoring_licenses | id, uuid, company_id, name, token_hash, server_id nullable, collector_id nullable, expires_at, max_uses, used_count, revoked_at, created_by, last_used_at | Token băm SHA-256, chỉ hiện bản rõ một lần lúc tạo. CompanyScope, Auditable |
monitoring_agents | id, uuid, company_id, server_id nullable, collector_id nullable, token_hash, prev_token_hash nullable, prev_token_expires_at nullable, machine_id, hostname, os_family, os_version, arch, agent_version, proto_version, state, last_seen_at, enrolled_at, revoked_at, revoked_reason | state: pending, active, revoked. Trạng thái online/offline lấy từ last_seen_at do seen cập nhật (độ trễ tới 1 phút) và alert agent.down. CompanyScope, Auditable |
monitoring_rules | id, uuid, company_id, version, name, kind, scope json, metric, matchers json, operator, threshold, for_seconds, recover_threshold, recover_for_seconds, severity, no_data, renotify_seconds, enabled, is_default, created_by | Xem 05-alerting. Auditable, Trashable |
monitoring_silences | id, uuid, company_id, scope json, starts_at, ends_at, reason, created_by | GĐ 2 |
monitoring_alerts | id, uuid, company_id, server_id, rule_id nullable, agent_id nullable, series_key, severity, state, opened_at, resolved_at, last_event_seq, last_value, threshold, suppressed, group_id nullable, acknowledged_by, acknowledged_at, event_count | Chỉ lưu vòng đời alert, không lưu từng mẫu. CompanyScope |
monitoring_alert_events | id, alert_id, event_id unique, seq, type, occurred_at, payload json | Nhật ký sự kiện, khử trùng theo event_id. Có chính sách dọn (retention) theo cài đặt |
monitoring_checks | id, uuid, company_id, scope json, type, target json, interval_seconds, timeout_seconds, enabled | GĐ 2. Kiểm tra port, tcp, http, cert, service |
monitoring_inventory_facts | id, server_id, company_id, facts json, facts_hash, reported_at | Một dòng cho mỗi máy chủ, ghi đè bản mới nhất |
monitoring_settings | company_id unique, default_interval, auto_create_server, group_down_min, group_down_ratio, retention_note, ... | Cài đặt theo công ty |
Ghi chú MySQL: các bảng có ghi nhiều (monitoring_agents.last_seen_at, monitoring_alert_events) phải được viết bằng cập nhật theo lô. Chỉ mục: monitoring_agents (company_id, state), (token_hash), (server_id); monitoring_alerts (company_id, state, opened_at), (server_id, state); monitoring_alert_events (event_id).
Đọc sau ghi (read-your-writes) với tách Read/Write: sau enroll thành công, bản ghi agent vừa tạo phải được đọc từ node ghi (sticky write connection) khi collector gọi agents?token_hash=. Với luồng đồng bộ theo since thì chấp nhận độ trễ replica, vì thay đổi đã được đẩy trực tiếp qua PUT /internal/v1/agents/{id}.
3. Quyền mới (PermissionCatalog)
Nhóm mới monitoring gồm: monitoring.view (xem tổng quan, agent, alert, biểu đồ), monitoring.manage (tạo License, sửa luật, thu hồi agent, silence), monitoring.delete (xóa luật, xóa agent), monitoring.collector (chỉ dành cho tài khoản collector, không gán cho người). Người dùng thường không được cấp monitoring.collector qua giao diện gán vai trò thông thường (ẩn khỏi danh sách hoặc cảnh báo rõ).
4. API dành cho collector
Tiền tố /api/v1/monitoring/collector, file route routes/api/monitoring.php, middleware xác thực Sanctum + quyền monitoring.collector, rate limiter riêng monitoring-collector (khóa theo token, giới hạn cao hơn API thường vì có lô). Định dạng phản hồi tuân theo API v1 hiện có (resource, envelope, lỗi).
| Method, đường dẫn | Mục đích | Ghi chú |
|---|---|---|
POST /enroll | Chuyển tiếp yêu cầu enroll của agent | Body: license, hostname, ip_addresses, machine_id, os, agent_version, collector_id. Trả agent_id, company_id, server_id, agent_token, config. Kiểm tra token (hash, hạn, số lần dùng, thu hồi), kiểm tra hạn mức agent của gói (vượt thì 422 {"error":{"code":"quota_exceeded"}}, mục 4.6), ghép Server, tạo monitoring_agents, tạo token, ghi audit. Giới hạn brute-force theo IP nguồn agent (collector truyền X-Forwarded-For tin cậy) |
GET /agents?since=&limit= | Đồng bộ registry | Trả các agent thay đổi từ since (kể cả revoked), kèm token_hash, prev_token_hash. Có next_since |
GET /agents?token_hash= | Tra cứu một token khi cache miss | Trả 404 nếu không có (collector đặt cache âm 60 giây) |
GET /rules?since=&limit= | Đồng bộ luật | Hợp đồng chi tiết ở mục 4.1 |
GET /silences?since=&limit= | Đồng bộ silence | Hợp đồng chi tiết ở mục 4.3 (COL-9) |
GET /servers/attributes?since= | Đồng bộ thuộc tính máy chủ dùng cho scope, nhóm sự cố | server_id, tags, environment, role, subnet_id, datacenter, status. Collector hiện không gọi endpoint này: server_status, subnet_id, datacenter đến qua bản ghi agent (mục 4.2) |
POST /events | Nhận lô sự kiện | Tối đa 200 sự kiện, idempotent theo event_id. Trả danh sách event_id đã nhận (kể cả đã thấy trước đó) và danh sách bị từ chối kèm lý do |
POST /agents/seen | Cập nhật last_seen_at theo lô | Khoảng 1 phút một lần mỗi collector (phải nhỏ hơn một phần ba ngưỡng online của Access Hub), body {collector_id, agents: [{agent_id, last_seen_at, agent_version}]}. last_seen_at chỉ tăng, không giảm. collector_id (tùy chọn) là định danh collector báo cáo: Access Hub gắn các agent chưa có collector, hoặc đang gắn với collector đã ngừng heartbeat, vào collector đó |
POST /inventory | Nhận kiểm kê theo lô | Ghi monitoring_inventory_facts, so sánh với Server, xem mục 6 |
POST /heartbeat | Collector báo sống | collector_id, version, agent_count, ingest_rps, outbox_depth. Hiển thị trong trang quản trị collector |
GET /settings/{company_id} | Cài đặt theo công ty | Tham số gom nhóm group_down_* (COL-9) và metrics_retention_days theo gói (COL-10), mục 4.4. Interval mặc định, giới hạn series chưa dùng |
Mọi phản hồi có server_time. Collector kiểm tra độ lệch đồng hồ và cảnh báo khi vượt 5 giây.
4.1 Hợp đồng GET /rules (COL-8)
Collector gọi GET /api/v1/monitoring/collector/rules?since=<n>&limit=500. Phản hồi bọc {data, server_time}:
{
"data": {
"rules": [
{
"id": "0d6b...", "version": 7, "sync_version": 1042, "company_id": "c1...",
"name": "Đĩa gần đầy", "kind": "threshold",
"scope": {"all": false, "server_ids": [], "tags": ["prod"], "environments": [], "roles": [], "datacenters": []},
"resolved_server_ids": ["s1...", "s2..."],
"metric": "disk_used_percent",
"matchers": {"mount": "/var"},
"operator": ">", "threshold": 90,
"for_seconds": 300, "recover_threshold": 85, "recover_for_seconds": 120,
"severity": "critical", "no_data": "ignore", "renotify_seconds": 3600,
"enabled": true, "deleted": false
}
],
"next_since": 1042,
"more": false
},
"server_time": "2026-09-30T02:15:30Z"
}Quy tắc phía Access Hub phải tuân thủ:
- Con trỏ.
sincelàsync_version, bộ đếm tăng đơn điệu toàn cục (mọi công ty), tăng mỗi khi luật đổi hoặc khi tậpresolved_server_idscủa luật đổi (ví dụ máy đổi tag, môi trường, vai trò, datacenter, hoặc thêm bớt máy). Trả các luật cósync_version > since, sắp theosync_versiontăng dần, tối đalimit(trần 500, mặc định 100).next_sincelàsync_versionlớn nhất trong trang (hoặcsincenếu trống),more=truekhi còn trang sau.versioncủa luật là số phiên bản nghiệp vụ, không dùng làm con trỏ. - Bia mộ. Luật tắt (
enabled=false) hoặc xóa (deleted=true) vẫn phải xuất hiện một lần vớisync_versionmới để collector gỡ khỏi bộ đang chạy. Collector coi cả hai là xóa. - Phạm vi. Collector đánh giá luật khi
company_idcủa luật khớpcompany_idcủa lô và máy thuộcscope.all, hoặcresolved_server_idshợpscope.server_ids. Access Hub giải tag, môi trường, vai trò, datacenter thànhresolved_server_ids(chỉ máy của cùng công ty) và phải nângsync_versionkhi kết quả giải đổi. Collector không tự tra thuộc tính máy chủ. - Trường.
kindlàthreshold,checkhoặcagent_down(collector bỏ quaagent_down, đã dựng sẵn).operatorthuộc>,>=,<,<=,==,!=.thresholdbắt buộc.recover_thresholdtùy chọn, phải nằm cùng phía với ngưỡng (<= thresholdcho>và>=,>= thresholdcho<và<=), không dùng với==và!=.severitythuộcinfo,warning,critical.no_datathuộcignore,alert,resolve.metricphải thuộc catalog, nhãn trongmatchersphải là nhãn được phép của metric đó. - Matchers. Mỗi nhãn nhận một trong ba dạng: chuỗi (bằng), mảng chuỗi (thuộc, tối đa 64), hoặc
{"regex": "..."}(RE2, neo hai đầu, tối đa 128 ký tự). - Luật sai. Access Hub nên kiểm tra trước khi lưu. Luật sai vẫn được collector nhận và bỏ qua từng luật (log,
ahc_rules_rejected_total,GET /internal/v1/status), không ảnh hưởng luật khác.
Sự kiện cảnh báo gửi lại qua POST /events (mục 5 của 05-alerting.md) mang rule_id, rule_version, series_key, severity, value, threshold, labels; alert.resolved có thêm duration_seconds, peak_value, reason (recovered, no_data, data_returned, rule_changed, out_of_scope, rule_removed). Collector không phát sự kiện cho luật sai (phong bì bắt buộc có server_id). suppressed và suppress_reason xem mục 4.3.
4.2 Thuộc tính agent cho chống nhiễu (COL-9)
Bản ghi agent của GET /agents?since= và GET /agents/lookup mang thêm ba trường, đều tùy chọn (thiếu nghĩa là rỗng):
| Trường | Giá trị | Dùng để |
|---|---|---|
server_status | maintenance, decommissioned, hoặc rỗng | Đánh dấu suppressed (suppress_reason cùng tên) cho mọi sự kiện của máy, kể cả agent.down, agent.up |
subnet_id | id subnet của máy | Gom group_down theo khóa subnet:<id> |
datacenter | id hoặc tên trung tâm dữ liệu | Gom group_down theo khóa datacenter:<id> |
Bắt buộc: collector bỏ qua bản ghi có version nhỏ hơn bản đang có, nên Access Hub phải nâng version (và sync_version của GET /agents) của mọi agent thuộc máy mỗi khi Server.status, subnet hoặc datacenter của máy đổi. Nếu không, collector giữ giá trị cũ cho đến khi agent được ghi lại.
4.3 Hợp đồng GET /silences (COL-9)
Collector gọi GET /api/v1/monitoring/collector/silences?since=<n>&limit=500. Phản hồi bọc {data, server_time}:
{
"data": {
"silences": [
{
"id": "5a1c...", "company_id": "c1...", "sync_version": 57,
"scope": {"all": false, "server_ids": ["s1..."], "resolved_server_ids": ["s1...", "s2..."]},
"starts_at": "2026-09-30T02:00:00Z", "ends_at": "2026-09-30T04:00:00Z",
"reason": "Nâng cấp kernel", "deleted": false
}
],
"next_since": 57,
"more": false
},
"server_time": "2026-09-30T02:15:30Z"
}- Con trỏ, phân trang, bia mộ: giống
GET /rules(mục 4.1).sync_versionlà bộ đếm đơn điệu toàn cục của bảng silence, tăng khi tạo, sửa, hủy, hoặc khi tậpresolved_server_idsđổi. Silence bị hủy trước hạn hoặc xóa phải xuất hiện một lần vớideleted=truevàsync_versionmới. Silence hết hạn tự nhiên không cần bia mộ, collector tự soends_at. - Thời gian:
starts_at,ends_atlà RFC3339 (UTC). Khoảng áp dụng[starts_at, ends_at), so với timestamp của sự kiện (xem05mục 4.1). - Phạm vi:
all=truelà mọi máy củacompany_idđó; nếu không thì máy thuộcserver_idshợpresolved_server_ids. Access Hub giải tag, môi trường, vai trò, datacenter thànhresolved_server_ids(chỉ máy cùng công ty).resolved_server_idscũng được chấp nhận ở cấp trên cùng của silence. - Cách ly: collector luôn so
company_idcủa silence với sự kiện, nhưng Access Hub không được trảserver_idschéo công ty. POST /internal/v1/reload/silences(admin API của collector) kéo ngay, Access Hub nên gọi sau khi tạo hoặc hủy silence để hiệu lực tức thì (nếu không chờ tối đaalerting.silences_sync_interval, mặc định 1 phút).
4.4 Hợp đồng GET /settings/{company_id} (COL-9)
{
"data": {"company_id": "c1...", "group_down_min": 8, "group_down_ratio": 0.5, "group_down_window_seconds": null,
"metrics_retention_days": 30},
"server_time": "2026-09-30T02:15:30Z"
}Mỗi trường là số hoặc null (không đặt, collector dùng ghi đè trong cấu hình rồi mặc định 5, 0.30, 60 giây). group_down_min >= 1, group_down_ratio trong (0, 1], group_down_window_seconds >= 1, giá trị ngoài miền bị bỏ qua. Collector cache 5 phút, POST /internal/v1/reload/settings xóa cache. Lỗi gọi (kể cả 404) chỉ làm collector dùng mặc định và thử lại sau 30 giây.
metrics_retention_days (COL-10, số nguyên 1 đến 3650 hoặc null): số ngày công ty được đọc số liệu theo gói, xem mục 4.6. null hoặc giá trị ngoài miền nghĩa là không giới hạn theo gói (chỉ còn retention toàn cụm 400 ngày). Từ 02/10/2026 thêm metrics_retention_previous_days và metrics_retention_grace_until cho ân hạn khi giảm retention (Q17), hợp đồng ở 03-protocol.md mục 5.1:
{"data": {"company_id": "c1...", "metrics_retention_days": 7,
"metrics_retention_previous_days": 90, "metrics_retention_grace_until": "2026-11-01T08:00:00Z"}}4.5 Trường sự kiện mới (COL-9)
POST /events thêm các trường tùy chọn:
| Trường | Sự kiện | Ý nghĩa |
|---|---|---|
suppressed, suppress_reason | mọi loại | suppress_reason thuộc maintenance, silence, decommissioned |
group_key, member_server_ids, group_total | group_down, group_update, group_resolved | group_key dạng subnet:<id> hoặc datacenter:<id>. member_server_ids là các máy đang mất tín hiệu của nhóm (rỗng ở group_resolved). group_total là tổng số máy của nhóm. server_id là máy neo, rule_id=group_down, series_key=group_key |
transitions | alert.flapping | Số lần chuyển trạng thái trong cửa sổ. reason là entered hoặc exited |
Quy tắc phía Access Hub: alert.flapping entered nghĩa là alert giữ mở và sẽ không có opened, resolved nào cho đến exited. Sau exited, alert đóng nếu có alert.resolved đi kèm, nếu không alert vẫn mở. group_down tạo một alert nhóm, các agent.down của member_server_ids (vẫn được phát riêng) gắn làm alert con grouped, group_update đổi danh sách thành viên, group_resolved đóng nhóm.
4.6 Retention số liệu theo gói (COL-10, việc phía Access Hub)
Gói (Free, Pro, Ultimate, ...) có hạn mức metrics_retention_days. Access Hub là nơi quyết định và thực thi hạn mức; collector chỉ nhận con số qua GET /settings/{company_id} và cắt mọi lần đọc GET /internal/v1/servers/{id}/series cho đúng (phòng thủ nhiều lớp, mọi giao diện như web, API v1, ứng dụng di động đều nhận cùng kết quả).
Việc phía Access Hub (gắn với hạng mục gói BIZ và HUB-11 biểu đồ):
GET /settings/{company_id}trảmetrics_retention_days= hạn mức của gói đang hiệu lực của chính công ty đó (nhánh BIZ đã cóQuotaService::limit($company, 'metrics_retention_days')và khóa trongPlanCatalog::SETTING_LIMIT_KEYS; không nhầm với cộtmonitoring_settings.retention_dayshiện có, đó là thời gian giữ sự kiện alert) (với công ty con một cấp: theo quy tắc gói của Access Hub, ví dụ kế thừa gói công ty mẹ; collector không biết quan hệ mẹ con và không cần biết). Trảnullkhi gói không giới hạn. Endpoint giữ quyềnmonitoring.collector, không nhận tham số khác, không trả dữ liệu công ty khác.- Khi gói của công ty đổi (nâng, hạ, hết hạn), sau khi lưu gọi
POST /internal/v1/reload/settings(job xếp hàng, thử lại; lỗi chỉ làm chậm tối đa 5 phút do cache). - Giao diện biểu đồ (HUB-11): bộ chọn khoảng thời gian không cho chọn xa hơn
metrics_retention_days; khi phản hồi córetention_daysmàfrombị cắt, hiển thị ghi chú "Gói hiện tại lưu N ngày" (vi, en).from,togửi xuống collector là RFC3339 UTC,company_id,server_idlấy từ bản ghiServer, không lấy từ tham số người dùng. - Kiểm thử Pest: (a) settings trả đúng hạn mức theo gói, (b) công ty A không đọc được settings của B qua token người dùng thường (endpoint chỉ cho
monitoring.collector), (c) đổi gói phát lệnh reload, (d) trang biểu đồ giới hạn khoảng chọn. Collector đã có kiểm thử tương ứng phía mình (TestSeriesHonoursPlanRetentionPerCompany,TestSeriesRetentionClampMaxRangeAndPlannedStep). - Nếu muốn ghi đè theo công ty, thêm cột
metrics_retention_days nullablevàomonitoring_settingsvà trả giá trị nhỏ hơn giữa ghi đè và gói. - Ghi nhận thay đổi retention (Q17, chính sách nền tảng ngày 01/10/2026). Migration thêm vào
monitoring_settings:metrics_retention_effective_days unsigned int nullable(retention hiệu lực đã báo cho collector lần gần nhất,null= không giới hạn),metrics_retention_previous_days unsigned int nullable,metrics_retention_grace_until timestamp nullable,metrics_retention_changed_at timestamp nullable. Mỗi khi gói, gói gia hạn, hết hạn, đổi công ty mẹ (công ty con kế thừa) hoặc ghi đè làm thay đổi retention hiệu lựcnewso vớiold = metrics_retention_effective_days, trong cùng giao dịch với thay đổi gói (lockForUpdatedòng settings):- Giảm (
new < old, hoặcoldkhông giới hạn vànewcó giới hạn):previous_days = max(old, previous_days nếu ân hạn còn)(không giới hạn thắng mọi số),grace_until = max(grace_until hiện có, now + 30 ngày). - Tăng (
new >= previous_dayshoặcnewkhông giới hạn): xóaprevious_daysvàgrace_until. Tăng nhưng vẫn dướiprevious_dayskhi còn ân hạn: giữ ân hạn. - Luôn ghi
effective_days = new,changed_at = now, auditmonitoring.retention_changed(công ty, cũ, mới, hết ân hạn), rồi sau commit gọiPOST /internal/v1/reload/settings. GET /settings/{company_id}trảmetrics_retention_days = new,metrics_retention_previous_days,metrics_retention_grace_until(RFC3339 UTC,nullkhi đã qua). Không bao giờ rút ngắngrace_untilhiện có.- Thông báo người quản trị công ty (vi, en) khi bắt đầu ân hạn và 7 ngày trước khi hết: "Dữ liệu giám sát cũ hơn N ngày sẽ bị xóa vĩnh viễn ngày D". Trang biểu đồ tô vùng
retention_locked_beforelà "chỉ đọc, sẽ xóa". Trong ân hạn, công cụ của khách là export tự phục vụ ở Access Hub (job theo công ty, đọc APIseriescủa collector, không kèm bí mật); không có đường khôi phục dữ liệu đã xóa từ phía nền tảng. - Kiểm thử Pest: hạ gói tạo ân hạn 30 ngày; hạ hai lần giữ
previouslớn nhất và không rút ngắn ân hạn; nâng lại quápreviousxóa ân hạn; công ty A đổi gói không ảnh hưởng settings của B; hết hạn gói (về Free) cũng tạo ân hạn; reload được gọi sau commit.
- Giảm (
Hạn mức số agent (monitoring_agents của gói) do Access Hub kiểm tra lúc POST /enroll (ví dụ QuotaService::assertCanCreate($company, 'monitoring_agents') của nhánh BIZ, đếm agent active và pending của công ty, vượt thì trả 422 với code = quota_exceeded, collector chuyển nguyên mã lỗi cho agent, mọi 422 khác vẫn là binding_failed). Kiểm tra và tạo agent phải trong cùng giao dịch có khóa theo công ty (SELECT ... FOR UPDATE trên dòng gói hoặc công ty) để hai enroll đồng thời không vượt hạn mức. Kiểm thử Pest: đủ hạn mức thì 422 quota_exceeded, agent revoked không tính, công ty khác không bị ảnh hưởng. Collector không cần biết hạn mức này: agent chỉ tồn tại khi Access Hub đã cấp token.
4.7 Xoay token agent (đặc tả cho Access Hub, chặn phần còn lại của AGT-10)
Collector hiện trả 503 cho POST /agent/v1/credentials/renew vì Access Hub chưa có endpoint. Hợp đồng đề xuất (collector và agent làm theo ngay khi Access Hub có):
Endpoint mới: POST /api/v1/monitoring/collector/agents/{agent_id}/renew, quyền monitoring.collector, rate limiter monitoring-collector.
// Request (collector gửi)
{"token_hash": "<sha256 hex của token agent đang dùng>", "collector_id": "col-1"}
// 200
{"data": {"agent_token": "ahat_...", "old_token_valid_until": "2026-10-02T09:00:00Z",
"agent": {"agent_id": "...", "company_id": "...", "server_id": "...", "state": "active",
"token_hash": "...", "prev_token_hash": "...", "prev_token_expires_at": "2026-10-02T09:00:00Z", "version": 1043}}},
"server_time": "..."}Quy tắc phía Access Hub (một giao dịch, lockForUpdate trên dòng monitoring_agents):
- Agent phải
active.token_hashkhớptoken_hashhiện tại: sinh token mới (ahat_+ 32 byte ngẫu nhiên, base62),prev_token_hash= hash cũ,prev_token_expires_at= now + 24 giờ,token_hash= hash mới. token_hashkhớpprev_token_hashcòn hạn (agent chưa lưu được token lần trước và thử lại bằng token cũ): sinh token mới thaytoken_hash, giữ nguyênprev_token_hashvà hạn của nó. Như vậy thử lại là an toàn, token chưa lưu bị bỏ.- Không khớp, agent
revoked, hoặcagent_idkhông tồn tại: 404not_found(không phân biệt để không lộ thông tin). Collector trả agent 401. - Nâng
versioncủa agent (con trỏGET /agents?since=) để mọi collector khác nhận cặp hash mới qua đồng bộ. - Audit
monitoring.agent_token_rotated(agent_id, company_id, collector_id), không ghi token hay hash. Token bản rõ chỉ có trong phản hồi này. - Giới hạn: tối đa 3 lần xoay mỗi agent mỗi giờ (429).
Phía collector (sẽ làm khi endpoint có): xác thực agent bằng token hiện tại qua registry, gọi endpoint với sha256(token), ghi ngay bản ghi agent trả về vào registry (token mới dùng được ngay ở collector này), trả RenewResponse{agent_token, old_token_valid_until_ms, server_time_ms}; 404 thành 401, 5xx thành 503 có Retry-After; không bao giờ log token. Phía agent: xoay khi token đủ 30 ngày (credentials.renew_after), ghi credentials.json mới nguyên tử trước khi dùng token mới; ghi lỗi thì tiếp tục dùng token cũ (còn 24 giờ) và thử lại sau 1 giờ.
Kiểm thử Pest: xoay thường, thử lại bằng token cũ trong 24 giờ, token cũ quá hạn bị từ chối, agent công ty khác hoặc hash sai trả 404, audit không chứa token, version tăng.
4.8 Checks từ trung tâm (đặc tả HUB-10, cần AGT-8 phía agent)
Đồng bộ: GET /api/v1/monitoring/collector/checks?since=&limit=, cùng quy tắc con trỏ sync_version, phân trang và bia mộ như GET /rules (mục 4.1):
{"data": {"checks": [{
"id": "7c1e...", "company_id": "c1...", "sync_version": 88, "deleted": false, "enabled": true,
"scope": {"all": false, "server_ids": [], "resolved_server_ids": ["s1...", "s2..."]},
"type": "http", "name": "API health",
"target": {"url": "https://api.internal/health", "expect_status": 200},
"timeout_seconds": 5
}], "next_since": 88, "more": false}, "server_time": "..."}typevàtarget:service{name}(tên dịch vụ,^[A-Za-z0-9@._-]{1,128}$),port{port, proto}(1 đến 65535,tcphoặcudp),tcp{target: "host:port"},http{url, expect_status}(chỉhttp,https, không chứa thông tin đăng nhập trong URL),cert{target: "host:port"}.timeout_seconds1 đến 15.interval_seconds(30 đến 3600) chỉ lưu ở Access Hub:Checkcủa giao thức v1 không có trường chu kỳ, agent chạy check mỗi chu kỳ gửi; thêm trường sau là thay đổi tương thích. Access Hub kiểm tra khi lưu; collector kiểm tra lại và bỏ check sai (log, không ảnh hưởng check khác).- Giới hạn: tối đa 50 check áp cho một máy (giới hạn của agent). Access Hub nên chặn khi lưu; collector cắt theo thứ tự
idnếu vượt và ghi cảnh báo. - Phân phối (COL-20, đã cài ở collector): collector gắn check vào
AgentConfig.checkscủa từng agent theoresolved_server_ids(cách ly theocompany_id), ETag đổi khi tập check của agent đổi, agent nhận trong tối đa 5 phút hoặc ngay khiconfig_etagđổi. Agent chặn đích link-local trừ khi bật cục bộ. - Kết quả: agent gửi
service_up,port_up,tcp_connect_up,tcp_connect_seconds,http_up,http_duration_seconds,http_status_code,cert_days_leftvới nhãnname,port/protohoặctarget. Cảnh báo dùng luậtkind=check(mục 4.1), Access Hub tạo luật mặc định khi tạo check (ví dụhttp_up < 1trong 2 phút mứccritical,cert_days_left < 14mứcwarning). - Bảng:
monitoring_checks(mục 2) thêmname,sync_version,deleted_at.CompanyScope,Auditable, quyềnmonitoring.manage.POST /internal/v1/reload/checks(collector đã có, 202) để áp ngay sau khi lưu; nếu không gọi, collector tự kéo mỗiingest.checks_sync_interval(mặc định 1 phút). - Quyết định của collector (COL-20): (1)
enabledvắng nghĩa là bật, chỉfalsemới tắt; (2)scope.all = truelà toàn công ty, collector gộp vớiresolved_server_ids(Access Hub vẫn nên điềnresolved_server_idsđầy đủ cho máy hiện có); (3) check sai (loại lạ, cổng ngoài miền, URL có thông tin đăng nhập,host:portsai) bị bỏ và đếm ởahc_checks_invalid, không ảnh hưởng check khác; (4) vượt 50 check một máy thì cắt theoidtăng dần; (5) chỉ trường đúng loại được chép sang agent; (6)interval_secondskhông đi qua giao thức. Cô lập: chỉ mục theo (company_idcủa check,server_id), tra bằngcompany_idvàserver_idcủa agent đã xác thực. - Kiểm thử Pest: CRUD và quyền, cô lập công ty (check công ty A không xuất hiện trong
resolved_server_idscủa B), bia mộ khi tắt hoặc xóa, kiểm tra đầu vào (URL có mật khẩu, cổng ngoài miền, quá 50 check một máy), luật mặc định được tạo.
5. API dành cho người dùng và giao diện
Cũng nằm trong routes/api/monitoring.php (cho API v1 công khai) và routes/web.php (cho giao diện Inertia).
| Chức năng | Endpoint (tóm tắt) | Quyền |
|---|---|---|
| Danh sách, tạo, thu hồi License | /api/v1/monitoring/licenses | monitoring.manage |
| Danh sách agent, chi tiết, thu hồi, gán lại Server | /api/v1/monitoring/agents | monitoring.view, .manage |
| CRUD luật, bật tắt, nhân bản, xem thử | /api/v1/monitoring/rules | monitoring.manage |
| Alert: danh sách, chi tiết, ack, đóng thủ công, lịch sử sự kiện | /api/v1/monitoring/alerts | monitoring.view, .manage |
| Silence | /api/v1/monitoring/silences | monitoring.manage |
| Tổng quan | /api/v1/monitoring/overview | monitoring.view |
| Sức khỏe máy chủ (giá trị mới nhất, chuỗi thời gian) | /api/v1/servers/{uuid}/health, /health/series | monitoring.view + servers.view |
| Kiểm kê từ agent, áp dụng vào Server | /api/v1/servers/{uuid}/inventory, POST .../inventory/apply | monitoring.view, servers.manage |
Yêu cầu bắt buộc theo quy ước dự án: mỗi endpoint mới có kiểm thử Pest, có kịch bản mẫu tests/ApiSamples/NN_monitoring.php để sinh ví dụ chạy thật cho trang /docs/api-endpoints, dùng authorizeAbility() của Api\Controller, trash và export dùng trait HandlesApiTrash và QueuesApiExport khi phù hợp.
Đường truy vấn số liệu: controller kiểm tra quyền và phạm vi công ty của Server, rồi gọi MonitoringCollectorClient (HTTP client có timeout ngắn 3 giây, thử lại 1 lần, ngắt mạch khi collector lỗi liên tục) với company_id, server_id lấy từ bản ghi, không lấy từ tham số người dùng. Khi collector không sẵn sàng, giao diện hiển thị trạng thái "Không có dữ liệu" thay vì lỗi 500.
6. Kiểm kê và quan hệ với Server
6.1 Hợp đồng POST /inventory (HUB-12, agent gửi từ AGT-9)
Collector gọi POST /api/v1/monitoring/collector/inventory, tối đa 100 mục mỗi lần, chỉ báo cáo mới nhất của mỗi agent, đã khử trùng 1 giờ:
{"items": [{
"agent_id": "9d2f...", "company_id": "c1...", "server_id": "s1...", "reported_at": "2026-10-01T09:00:00Z",
"facts": {
"os": {"family": "linux", "name": "Ubuntu 24.04.4 LTS", "version": "24.04", "kernel": "6.8.0-142-generic", "arch": "amd64"},
"cpu": {"model": "Intel(R) Xeon(R) Gold 6248R CPU @ 3.00GHz", "cores": 4, "threads": 8},
"memory_total_bytes": "16720621568",
"disks": [{"mount": "/", "fstype": "ext4", "total_bytes": "25180848128"}],
"nics": [{"name": "ens33", "ips": ["10.216.4.100"]}],
"boot_time_ms": "1790425890000",
"timezone": "Asia/Ho_Chi_Minh",
"agent_build": {"version": "1.2.0", "commit": "2d2315c", "go_version": "go1.27.1"}
}
}]}factslà protojson củaInventoryFactsvới tên trường proto: số 64 bit (memory_total_bytes,total_bytes,boot_time_ms) là chuỗi, trường rỗng hoặc bằng 0 bị bỏ hẳn (Windows chưa cócpu,disksđến AGT-7). Access Hub đọc bằng ép kiểu số nguyên và mặc định khi thiếu.company_id,server_iddo collector gắn từ registry. Access Hub vẫn phải kiểmmonitoring_agentscóid = agent_id,company_id,server_idkhớp vàstate != revoked, mục sai thì bỏ (không bao giờ ghi kiểm kê chéo công ty).Ghi: upsert
monitoring_inventory_factstheoserver_id(unique), bỏ qua khireported_atcũ hơn bản đang có,facts_hash= SHA-256 củafactsdạng JSON chuẩn hóa (khóa sắp xếp). Không sửaServer(ADR 0011).Phản hồi: 200
{"data": {"accepted": n, "rejected": [{"agent_id", "reason"}]}}kể cả khi có mục bị bỏ. Collector coi mọi 4xx trừ 429 là từ chối vĩnh viễn và bỏ cả lô, 5xx thì thử lại, nên chỉ trả 4xx cho lỗi cả lô (body sai, quá 100 mục, quá 1 MiB).So sánh với
Server(tab sức khỏe, hành động "Áp dụng" có quyềnservers.managevà audit):operating_systemvớios.name,cpu_coresvớicpu.cores,memory_mbvớimemory_total_bytes / 1048576(lệch dưới 5 phần trăm coi như khớp, RAM khả dụng nhỏ hơn RAM vật lý),disk_gbvới tổngdisks[].total_bytes / 1073741824(lệch dưới 5 phần trăm),ip_addresscó nằm trongnics[].ipskhông,hostnameso với hostname lúc enroll. Máy có nguồn IaC: hiển thị chênh lệch, không áp dụng tự động (Q7).Kiểm thử Pest: mục hợp lệ được ghi, mục có
company_idkhông khớp agent bị từ chối,reported_atcũ không ghi đè, agentrevokedbị từ chối, người dùng công ty A không xem được kiểm kê công ty B, chuỗi số 64 bit đọc đúng.Dữ liệu kiểm kê từ agent không tự ghi đè
Server. Agent có thể sai, vàServerdo IaC quản lý (HasIacOrigin).Access Hub lưu
monitoring_inventory_facts, so sánh vớiServer(OS, CPU, RAM, đĩa, IP) và hiển thị chênh lệch trên tab sức khỏe. Người dùng có quyềnservers.managebấm "Áp dụng" để cập nhật các trường chọn lọc, hành động này ghi audit như mọi cập nhậtServerkhác.Với máy có nguồn gốc IaC, cập nhật thủ công tạo một trạng thái lệch (
IacDrift). Cần kiểm tra chồng chéo khi làm HUB-x tương ứng, và quyết định cách xử lý (xem 13).Server.status: trạng tháimaintenancevàdecommissionedđược đồng bộ sang collector để chặn thông báo. Ngược lại, agent mất tín hiệu không tự đổiServer.status, chỉ tạo alert.
7. Thông báo, audit, thời gian thực
alert.opened,alert.resolved,alert.reminder,group_downkích hoạt thông báo qua hệ thốngNotificationTemplate,NotificationPreference,WebhookChannelhiện có. Tạo bộ mẫu thông báo mặc định (vi, en) cho từng loại sự kiện.- Chuông thời gian thực (Reverb) nhận alert của công ty.
- Audit: tạo, sửa, thu hồi License, agent, luật, silence đều qua
AuditRecorder. Sự kiện alert do hệ thống sinh ghi vàomonitoring_alert_events, không ghi audit từng sự kiện (tránh nhiễu), nhưng thao tác ack, đóng thủ công của người dùng thì ghi audit.
8. Vòng đời và dọn dẹp
- Xóa vĩnh viễn
Server: gọiDELETE /internal/v1/servers/{id}/data?company_id=qua job xếp hàng có thử lại, agent gắn với server đó chuyểnrevokedhoặcunboundtheo lựa chọn. - Xóa vĩnh viễn
Company(purge): job gọiDELETE /internal/v1/companies/{id}/data, chờ xác nhận, ghi audit kết quả. Nếu collector không sẵn sàng, job thử lại và cảnh báo quản trị.
8.1 Đặc tả HUB-14 (purge gọi collector, COL-11 đã sẵn sàng)
- Kích hoạt.
Serverbị xóa vĩnh viễn (force delete, không phải thùng rác) vàCompanybị purge (luồng xóa công ty của Access Hub, gồm cả công ty con bị xóa theo công ty mẹ: gọi một lần cho mỗicompany_id, collector không biết quan hệ mẹ con). Không gọi khi chỉ soft delete hoặc tạm ngưng gói. - Job.
PurgeMonitoringDataJob(company_id, server_id|null)trên hàng đợi mặc định,tries10, backoff 30, 60, 120, 300, 600 giây,uniqueIdtheo cặp id để không xếp trùng. Gọi quaMonitoringCollectorClient(timeout 30 giây cho lời gọi xóa, khác 3 giây của truy vấn),company_id,server_idlấy từ bản ghi đã khóa trước khi xóa (lưu vào payload job vì bản ghi sẽ mất). 200 là xong, 400 là lỗi lập trình (ghi log, không thử lại), 401 và 5xx, lỗi mạng thì thử lại. Collector idempotent nên gọi lặp an toàn. - Thứ tự phía Access Hub. Thu hồi agent (
DELETE /internal/v1/agents/{id}hoặc để purge xóa registry), xóa hoặc đánh dấumonitoring_agents,monitoring_alerts,monitoring_alert_events,monitoring_inventory_facts,monitoring_silencescủa phạm vi đó trong cùng giao dịch với việc xóaServerhoặcCompany, sau commit mới dispatch job (afterCommit). Sự kiện collector còn gửi tới cho tenant đã xóa (tối đa vài giây) phải được trả trongrejectedvớireasonunknown_serverđể collector chuyển dead-letter, không trả 5xx. - Audit. Ghi
monitoring.data_purgedvớicompany_id,server_id,agents_removed,tombstone_untiltừ phản hồi. Thất bại hết lượt thử: thông báo quản trị nền tảng chỉ với số lượng và mã lỗi, không kèm tên công ty hay máy (nhân viên nền tảng chỉ thấy tổng hợp). - Kiểm thử Pest. Force delete
Serverdispatch đúng job vớicompany_idcủa chính server; purge công ty A không gọi xóa cho công ty B; job thử lại khi collector 503; job không chạy khi giao dịch rollback; HTTP fake khẳng định đường dẫn và tham số.
- Lệnh dọn định kỳ theo mẫu
PurgeAuditLogCommand: dọnmonitoring_alert_eventsvàmonitoring_alertsđã đóng quá hạn theo cài đặt lưu trữ.
9. Việc phải làm phía Access Hub (danh sách HUB)
| Mã | Việc | GĐ |
|---|---|---|
| HUB-1 | Migration, model, factory, seeder cho các bảng mục 2 (chia theo giai đoạn) | 0 đến 2 |
| HUB-2 | Quyền monitoring.*, vai trò Monitoring Collector mặc định | 0 |
| HUB-3 | routes/api/monitoring.php, rate limiter monitoring-collector | 0 |
| HUB-4 | API collector: enroll, agents, seen, heartbeat | 1 |
| HUB-5 | API collector: events, xử lý alert, thông báo | 1 |
| HUB-6 | License: model, API, giao diện, bộ tạo lệnh cài | 1 |
| HUB-7 | Trang Agents, Tổng quan, tab sức khỏe máy chủ (giá trị mới nhất) | 1 |
| HUB-8 | MonitoringCollectorClient, ngắt mạch, giao diện trạng thái không có dữ liệu | 1 |
| HUB-9 | Luật ngưỡng: CRUD, giao diện, đồng bộ, bộ luật mặc định | 2 |
| HUB-10 | Checks (port, http, cert, service) và giao diện (đặc tả mục 4.8) | 2 |
| HUB-11 | Biểu đồ trên tab sức khỏe, chọn khoảng thời gian | 2 |
| HUB-12 | Kiểm kê: nhận, so sánh, áp dụng (hợp đồng mục 6.1) | 2 |
| HUB-13 | Silence và trạng thái bảo trì | 2 |
| HUB-14 | Purge Server và Company gọi collector | 2 |
| HUB-15 | Quản lý collector, gán agent, di chuyển agent | 3 |
| HUB-16 | Trang phát hành agent, kênh cập nhật, dừng khẩn cấp | 3 |
| HUB-17 | Tích hợp IaC (khai báo luật và License bằng manifest) | 3 |
| HUB-18 | Đẩy alert lên ứng dụng di động (tích hợp kênh push nếu có) | 3 |
Định nghĩa hoàn thành cho mỗi HUB-x: kiểm thử Pest, mẫu API tests/ApiSamples, trang tài liệu trong ứng dụng (kỹ thuật và hướng dẫn, vi và en), khóa dịch vi/en đủ hai locale, Pint, PHPStan, ESLint, php artisan queue:restart khi đụng job hoặc thông báo, kiểm tra cô lập tenant (CompanyScope) bằng kiểm thử tự động.