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

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.

27 phút đọcCập nhật 02/10/2026access-hub-collector, docs/06-access-hub-integration.md

1. Nguyên tắc tích hợp ​

  1. 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.
  2. 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).
  3. 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.
  4. 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.
  5. 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ảngCột chínhGhi chú
monitoring_collectorsid, uuid, name, base_url, admin_url, status, version, last_heartbeat_at, agent_count, settings jsonToà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_licensesid, uuid, company_id, name, token_hash, server_id nullable, collector_id nullable, expires_at, max_uses, used_count, revoked_at, created_by, last_used_atToken băm SHA-256, chỉ hiện bản rõ một lần lúc tạo. CompanyScope, Auditable
monitoring_agentsid, 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_reasonstate: 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_rulesid, 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_byXem 05-alerting. Auditable, Trashable
monitoring_silencesid, uuid, company_id, scope json, starts_at, ends_at, reason, created_byGĐ 2
monitoring_alertsid, 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_countChỉ lưu vòng đời alert, không lưu từng mẫu. CompanyScope
monitoring_alert_eventsid, alert_id, event_id unique, seq, type, occurred_at, payload jsonNhật ký sự kiện, khử trùng theo event_id. Có chính sách dọn (retention) theo cài đặt
monitoring_checksid, uuid, company_id, scope json, type, target json, interval_seconds, timeout_seconds, enabledGĐ 2. Kiểm tra port, tcp, http, cert, service
monitoring_inventory_factsid, server_id, company_id, facts json, facts_hash, reported_atMột dòng cho mỗi máy chủ, ghi đè bản mới nhất
monitoring_settingscompany_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ẫnMục đíchGhi chú
POST /enrollChuyển tiếp yêu cầu enroll của agentBody: 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ộ registryTrả 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 missTrả 404 nếu không có (collector đặt cache âm 60 giây)
GET /rules?since=&limit=Đồng bộ luậtHợp đồng chi tiết ở mục 4.1
GET /silences?since=&limit=Đồng bộ silenceHợ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 /eventsNhận lô sự kiệnTố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/seenCậ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 /inventoryNhận kiểm kê theo lôGhi monitoring_inventory_facts, so sánh với Server, xem mục 6
POST /heartbeatCollector báo sốngcollector_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 tyTham 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}:

json
{
  "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ỏ. since là 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ập resolved_server_ids củ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 theo sync_version tăng dần, tối đa limit (trần 500, mặc định 100). next_since là sync_version lớn nhất trong trang (hoặc since nếu trống), more=true khi còn trang sau. version củ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ới sync_version mớ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_id của luật khớp company_id của lô và máy thuộc scope.all, hoặc resolved_server_ids hợp scope.server_ids. Access Hub giải tag, môi trường, vai trò, datacenter thành resolved_server_ids (chỉ máy của cùng công ty) và phải nâng sync_version khi kết quả giải đổi. Collector không tự tra thuộc tính máy chủ.
  • Trường. kind là threshold, check hoặc agent_down (collector bỏ qua agent_down, đã dựng sẵn). operator thuộc >, >=, <, <=, ==, !=. threshold bắt buộc. recover_threshold tùy chọn, phải nằm cùng phía với ngưỡng (<= threshold cho > và >=, >= threshold cho < và <=), không dùng với == và !=. severity thuộc info, warning, critical. no_data thuộc ignore, alert, resolve. metric phải thuộc catalog, nhãn trong matchers phả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ườngGiá trịDùng để
server_statusmaintenance, 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_idid subnet của máyGom group_down theo khóa subnet:<id>
datacenterid hoặc tên trung tâm dữ liệuGom 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}:

json
{
  "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_version là 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ập resolved_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ới deleted=true và sync_version mới. Silence hết hạn tự nhiên không cần bia mộ, collector tự so ends_at.
  • Thời gian: starts_at, ends_at là RFC3339 (UTC). Khoảng áp dụng [starts_at, ends_at), so với timestamp của sự kiện (xem 05 mục 4.1).
  • Phạm vi: all=true là mọi máy của company_id đó; nếu không thì máy thuộc server_ids hợp resolved_server_ids. Access Hub giải tag, môi trường, vai trò, datacenter thành resolved_server_ids (chỉ máy cùng công ty). resolved_server_ids cũng được chấp nhận ở cấp trên cùng của silence.
  • Cách ly: collector luôn so company_id của silence với sự kiện, nhưng Access Hub không được trả server_ids ché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 đa alerting.silences_sync_interval, mặc định 1 phút).

4.4 Hợp đồng GET /settings/{company_id} (COL-9) ​

json
{
  "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:

json
{"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ườngSự kiệnÝ nghĩa
suppressed, suppress_reasonmọi loạisuppress_reason thuộc maintenance, silence, decommissioned
group_key, member_server_ids, group_totalgroup_down, group_update, group_resolvedgroup_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
transitionsalert.flappingSố 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 đồ):

  1. 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 trong PlanCatalog::SETTING_LIMIT_KEYS; không nhầm với cột monitoring_settings.retention_days hiệ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ả null khi gói không giới hạn. Endpoint giữ quyền monitoring.collector, không nhận tham số khác, không trả dữ liệu công ty khác.
  2. 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).
  3. 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_days mà from bị cắt, hiển thị ghi chú "Gói hiện tại lưu N ngày" (vi, en). from, to gửi xuống collector là RFC3339 UTC, company_id, server_id lấy từ bản ghi Server, không lấy từ tham số người dùng.
  4. 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).
  5. Nếu muốn ghi đè theo công ty, thêm cột metrics_retention_days nullable vào monitoring_settings và trả giá trị nhỏ hơn giữa ghi đè và gói.
  6. 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ực new so với old = metrics_retention_effective_days, trong cùng giao dịch với thay đổi gói (lockForUpdate dòng settings):
    • Giảm (new < old, hoặc old không giới hạn và new có 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_days hoặc new không giới hạn): xóa previous_days và grace_until. Tăng nhưng vẫn dưới previous_days khi còn ân hạn: giữ ân hạn.
    • Luôn ghi effective_days = new, changed_at = now, audit monitoring.retention_changed (công ty, cũ, mới, hết ân hạn), rồi sau commit gọi POST /internal/v1/reload/settings.
    • GET /settings/{company_id} trả metrics_retention_days = new, metrics_retention_previous_days, metrics_retention_grace_until (RFC3339 UTC, null khi đã qua). Không bao giờ rút ngắn grace_until hiệ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_before là "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 API series củ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ữ previous lớn nhất và không rút ngắn ân hạn; nâng lại quá previous xó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.

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.

json
// 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):

  1. Agent phải active. token_hash khớp token_hash hiệ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.
  2. token_hash khớp prev_token_hash cò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 thay token_hash, giữ nguyên prev_token_hash và hạn của nó. Như vậy thử lại là an toàn, token chưa lưu bị bỏ.
  3. Không khớp, agent revoked, hoặc agent_id không tồn tại: 404 not_found (không phân biệt để không lộ thông tin). Collector trả agent 401.
  4. Nâng version của agent (con trỏ GET /agents?since=) để mọi collector khác nhận cặp hash mới qua đồng bộ.
  5. 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.
  6. 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):

json
{"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": "..."}
  • type và target: service {name} (tên dịch vụ, ^[A-Za-z0-9@._-]{1,128}$), port {port, proto} (1 đến 65535, tcp hoặc udp), 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_seconds 1 đến 15. interval_seconds (30 đến 3600) chỉ lưu ở Access Hub: Check củ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ự id nế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.checks của từng agent theo resolved_server_ids (cách ly theo company_id), ETag đổi khi tập check của agent đổi, agent nhận trong tối đa 5 phút hoặc ngay khi config_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_left với nhãn name, port/proto hoặc target. Cảnh báo dùng luật kind=check (mục 4.1), Access Hub tạo luật mặc định khi tạo check (ví dụ http_up < 1 trong 2 phút mức critical, cert_days_left < 14 mức warning).
  • Bảng: monitoring_checks (mục 2) thêm name, sync_version, deleted_at. CompanyScope, Auditable, quyền monitoring.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ỗi ingest.checks_sync_interval (mặc định 1 phút).
  • Quyết định của collector (COL-20): (1) enabled vắng nghĩa là bật, chỉ false mới tắt; (2) scope.all = true là toàn công ty, collector gộp với resolved_server_ids (Access Hub vẫn nên điền resolved_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:port sai) 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 theo id tăng dần; (5) chỉ trường đúng loại được chép sang agent; (6) interval_seconds không đi qua giao thức. Cô lập: chỉ mục theo (company_id của check, server_id), tra bằng company_id và server_id củ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_ids củ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ăngEndpoint (tóm tắt)Quyền
Danh sách, tạo, thu hồi License/api/v1/monitoring/licensesmonitoring.manage
Danh sách agent, chi tiết, thu hồi, gán lại Server/api/v1/monitoring/agentsmonitoring.view, .manage
CRUD luật, bật tắt, nhân bản, xem thử/api/v1/monitoring/rulesmonitoring.manage
Alert: danh sách, chi tiết, ack, đóng thủ công, lịch sử sự kiện/api/v1/monitoring/alertsmonitoring.view, .manage
Silence/api/v1/monitoring/silencesmonitoring.manage
Tổng quan/api/v1/monitoring/overviewmonitoring.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/seriesmonitoring.view + servers.view
Kiểm kê từ agent, áp dụng vào Server/api/v1/servers/{uuid}/inventory, POST .../inventory/applymonitoring.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ờ:

json
{"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"}
  }
}]}
  • facts là protojson của InventoryFacts vớ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_id do collector gắn từ registry. Access Hub vẫn phải kiểm monitoring_agents có id = agent_id, company_id, server_id khớ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_facts theo server_id (unique), bỏ qua khi reported_at cũ hơn bản đang có, facts_hash = SHA-256 của facts dạng JSON chuẩn hóa (khóa sắp xếp). Không sửa Server (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ền servers.manage và audit): operating_system với os.name, cpu_cores với cpu.cores, memory_mb với memory_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_gb với tổng disks[].total_bytes / 1073741824 (lệch dưới 5 phần trăm), ip_address có nằm trong nics[].ips không, hostname so 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_id không khớp agent bị từ chối, reported_at cũ không ghi đè, agent revoked bị 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à Server do IaC quản lý (HasIacOrigin).

  • Access Hub lưu monitoring_inventory_facts, so sánh với Server (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ền servers.manage bấ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ật Server khá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ái maintenance và 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ự đổi Server.status, chỉ tạo alert.

7. Thông báo, audit, thời gian thực ​

  • alert.opened, alert.resolved, alert.reminder, group_down kích hoạt thông báo qua hệ thống NotificationTemplate, NotificationPreference, WebhookChannel hiệ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ào monitoring_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ọi DELETE /internal/v1/servers/{id}/data?company_id= qua job xếp hàng có thử lại, agent gắn với server đó chuyển revoked hoặc unbound theo lựa chọn.
  • Xóa vĩnh viễn Company (purge): job gọi DELETE /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) ​

  1. Kích hoạt. Server bị xóa vĩnh viễn (force delete, không phải thùng rác) và Company bị 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ỗi company_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.
  2. Job. PurgeMonitoringDataJob(company_id, server_id|null) trên hàng đợi mặc định, tries 10, backoff 30, 60, 120, 300, 600 giây, uniqueId theo cặp id để không xếp trùng. Gọi qua MonitoringCollectorClient (timeout 30 giây cho lời gọi xóa, khác 3 giây của truy vấn), company_id, server_id lấ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.
  3. 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ấu monitoring_agents, monitoring_alerts, monitoring_alert_events, monitoring_inventory_facts, monitoring_silences của phạm vi đó trong cùng giao dịch với việc xóa Server hoặc Company, 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ả trong rejected với reason unknown_server để collector chuyển dead-letter, không trả 5xx.
  4. Audit. Ghi monitoring.data_purged với company_id, server_id, agents_removed, tombstone_until từ 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).
  5. Kiểm thử Pest. Force delete Server dispatch đúng job với company_id củ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ọn monitoring_alert_events và 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ệcGĐ
HUB-1Migration, model, factory, seeder cho các bảng mục 2 (chia theo giai đoạn)0 đến 2
HUB-2Quyền monitoring.*, vai trò Monitoring Collector mặc định0
HUB-3routes/api/monitoring.php, rate limiter monitoring-collector0
HUB-4API collector: enroll, agents, seen, heartbeat1
HUB-5API collector: events, xử lý alert, thông báo1
HUB-6License: model, API, giao diện, bộ tạo lệnh cài1
HUB-7Trang Agents, Tổng quan, tab sức khỏe máy chủ (giá trị mới nhất)1
HUB-8MonitoringCollectorClient, ngắt mạch, giao diện trạng thái không có dữ liệu1
HUB-9Luật ngưỡng: CRUD, giao diện, đồng bộ, bộ luật mặc định2
HUB-10Checks (port, http, cert, service) và giao diện (đặc tả mục 4.8)2
HUB-11Biểu đồ trên tab sức khỏe, chọn khoảng thời gian2
HUB-12Kiểm kê: nhận, so sánh, áp dụng (hợp đồng mục 6.1)2
HUB-13Silence và trạng thái bảo trì2
HUB-14Purge Server và Company gọi collector2
HUB-15Quản lý collector, gán agent, di chuyển agent3
HUB-16Trang phát hành agent, kênh cập nhật, dừng khẩn cấp3
HUB-17Tí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.

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-collector lúc 15:42, 03/10/2026. Khi tài liệu và mã khác nhau, mã thắng.