L3 - Nền tảng giám sát máy chủ - Collector - Admin API
Quy ước tên: L3 - <P&L> - <Hệ thống L2> - <Thành phần>. P&L: chưa chỉ định. Hệ thống L2: Collector. Thành phần: Admin API (/internal/v1), API nội bộ để Access Hub đẩy agent, đọc trạng thái, truy vấn chuỗi thời gian và xóa dữ liệu theo tenant. Mã: internal/admin/admin.go, internal/catalog, nối dây buildAdmin trong internal/app/wiring.go.
Quy ước đánh dấu: "Đã làm" là có mã và kiểm thử. "Chỉ thiết kế" là chỉ có trong docs/. "Đề xuất" là ý của tác giả bản nháp. Mã thắng khi lệch tài liệu.
0. Front matter
Thông tin tài liệu đầy đủ
| Trạng thái | BẢN NHÁP, phiên bản 0.1, 2026-09-30. Người phê duyệt: chưa chỉ định |
| Thành phần (Component) | Admin API (router, xác thực token, handler), Catalog chỉ số |
| Truy vết L2 | L2 SAD Collector: thành phần "Admin API" (mục 2.3, 6.1), FR đẩy agent, đọc trạng thái, truy vấn, xóa dữ liệu (mục 3), NFR cô lập tenant (mục 4), mục 9 (Security), rủi ro AR-007, AR-010. Mục tiêu L1: G1, G4 (qua L2) |
| Tài liệu cha | L2 SAD Collector |
| Tài liệu anh em | Ingest API, Registry, Enroll, Presence, Bus, Worker, TSDB, Alerting, Outbox |
| Tier | Đề xuất Cấp 3 (kế thừa L2, chưa xác nhận) |
| Phân loại dữ liệu | Nhạy cảm vừa: API có quyền xóa dữ liệu và thu hồi agent của mọi tenant. Token admin là bí mật |
| Blast radius | Lộ token admin cho phép đọc và xóa dữ liệu mọi tenant, đẩy agent giả. Lỗi tenant ở đây lẫn dữ liệu giữa công ty |
| Sign-off gate | Chưa chỉ định. Không có ai đã sign-off |
Truy vết mục L3 đến thành phần L2:
| Mục L3 | Thành phần hoặc mục L2 |
|---|---|
| 1 Phạm vi | 2.3 Các thành phần chính |
| 2 Yêu cầu | 3 Functional, 4 NFR |
| 3 Kiến trúc | 6.1 Component table |
| 4, 6 Domain, dữ liệu | 7.1 Data Model |
| 5 Hợp đồng | 6.2 Integration (Access Hub đến Collector) |
| 7 Thuật toán | 8 Key flows |
| 8, 9 Lỗi, suy thoái | 6.3 Resilience |
| 11 Bảo mật | 9 Security, cô lập tenant |
| 13 Telemetry | 13 Observability |
1. Scope and Non-Goals
1.1 Vai trò
Cổng điều khiển của Access Hub (control plane) vào Collector (data plane). Access Hub: đẩy hoặc thu hồi agent, hỏi trạng thái máy chủ và tổng hợp công ty, truy vấn chuỗi thời gian cho giao diện, xóa dữ liệu khi gỡ máy chủ hoặc công ty. Collector không có giao diện người dùng; mọi thứ người dùng thấy đều do Access Hub gọi API này.
1.2 Context diagram
flowchart LR
classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff;
classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff;
classDef entity fill:#3a3320,stroke:#d9b84a,color:#fff;
classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff;
HUB(["Access Hub (ext)"]):::entity
ADM["Admin API"]:::owned
RG["Registry"]:::bc
PS["Presence"]:::bc
TS["TSDB adapter"]:::bc
CAT["Catalog"]:::bc
VM[("VictoriaMetrics")]:::datastore
HUB -->|"HTTPS Bearer"| ADM
ADM --> RG
ADM --> PS
ADM --> TS
ADM --> CAT
TS --> VM| Chiều | Bên | Nội dung |
|---|---|---|
| Vào | Access Hub | 9 endpoint dưới /internal/v1 |
| Ra | Registry, Presence | Đẩy, thu hồi, xóa, đếm |
| Ra | TSDB adapter | Latest, Range, DeleteServer, DeleteCompany |
| Ra | Catalog | Kiểm tra tên chỉ số hợp lệ, danh sách chỉ số trạng thái |
1.3 Trong phạm vi
- Router, xác thực Bearer, định dạng phản hồi và lỗi.
- Đẩy và thu hồi agent.
- Trạng thái máy chủ, tổng hợp công ty, trạng thái collector.
- Truy vấn khoảng một chỉ số của một máy chủ.
- Xóa dữ liệu theo máy chủ và theo công ty.
- Nạp lại cấu hình (
reload):rulesđã nối vào bộ đồng bộ luật (COL-8) và trả 202,settingsvẫn trả 501.
1.4 Ngoài phạm vi
| Nội dung | Thuộc về |
|---|---|
| Phân quyền người dùng cuối, vai trò RBAC | Access Hub (Collector chỉ tin token admin) |
| Tạo cảnh báo, luật, thông báo | Access Hub (collector chỉ đánh giá luật, COL-8) |
Nạp lại cài đặt theo công ty (reload/settings) | Chưa làm (501). reload/rules đã có |
| Phát hiện down | L3 Alerting |
| Ghi dữ liệu | L3 Ingest, L3 Bus, Worker, TSDB |
2. Requirements
2.1 Functional Requirements
| # | Trách nhiệm | Giải thích | Hiện thực ở |
|---|---|---|---|
| FR-ADM-01 | Mọi endpoint cần token | Sai hoặc thiếu token thì 401 | TestAuthRequiredEverywhere |
| FR-ADM-02 | Tham số khởi tạo hợp lệ | Thiếu token, registry, presence, store, catalog hoặc ngưỡng thì từ chối | TestNewRejectsBadOptions |
| FR-ADM-03 | Lỗi định tuyến rõ ràng | 404 đường dẫn lạ, 405 sai phương thức kèm Allow | TestRoutingErrors |
| FR-ADM-04 | Đẩy agent | PUT /agents/{id} kiểm tra nghiêm và lưu | TestPutAndRevokeAgent, TestPutAgentValidation |
| FR-ADM-05 | Thu hồi agent | DELETE /agents/{id} luôn 204 (idempotent) | TestPutAndRevokeAgent |
| FR-ADM-06 | Reload | 404 với kind lạ, 501 khi chưa hỗ trợ (settings), 202 khi hàm reload chấp nhận (rules) | TestReload |
| FR-ADM-07 | Trạng thái máy chủ | Agent, lần thấy cuối, trạng thái online, down hoặc unknown, chỉ số mới nhất | TestServerStatusAndSummary |
| FR-ADM-08 | Tổng hợp công ty | Tổng, online, offline | TestServerStatusAndSummary |
| FR-ADM-09 | Truy vấn chuỗi | Chỉ số thuộc catalog, khoảng tối đa 31 ngày (400 ngày khi có vm-long), cắt theo retention của gói | TestSeriesQuery, TestSeriesRetentionClampMaxRangeAndPlannedStep, TestSeriesDefaultMaxRange |
| FR-ADM-10 | Xóa theo tenant | Xóa máy chủ hoặc công ty ở cả registry và TSDB | TestDeleteDataIsTenantScoped |
| FR-ADM-11 | Trạng thái collector | Phiên bản, vai trò, uptime, độ sâu bus, outbox, đếm agent | TestStatus |
| FR-ADM-12 | Cô lập tenant đầu cuối | Hai tenant không thấy dữ liệu của nhau qua Ingest rồi Admin | TestTwoTenantIsolationThroughIngestAndAdminAPI |
| FR-ADM-13 | Catalog hợp lệ | Tên chỉ số hợp lệ, status_metrics phải có trong catalog | TestEveryDefaultNameIsValid, TestParseRejectsUnknownStatusMetric, TestDefault |
2.2 Non-Functional Requirements
Allocated:
| NFR L2 | Target | Cách đáp ứng ở L3 |
|---|---|---|
| NFR-12 (cô lập tenant) | company_id bắt buộc ở mọi thao tác theo phạm vi | tenant(c) gọi tsdb.CheckTenant |
| NFR-05 (bảo mật API nội bộ) | Token mạnh, so sánh hằng thời gian | Token tối thiểu 24 ký tự, so sánh băm SHA-256 bằng ConstantTimeCompare |
Inherited: giới hạn HTTP server (timeout, MaxHeaderBytes 64 KiB, TLS tối thiểu 1.2 nếu bật), httpx (request id, health), listener admin mặc định 127.0.0.1:9101.
Owned:
| ID | Target | Parent L2-NFR | Satisfied-by |
|---|---|---|---|
| NFR-ADM-01 | Body tối đa 64 KiB | NFR-05 | maxBody, io.LimitReader |
| NFR-ADM-02 | Khoảng truy vấn tối đa 31 ngày, 400 ngày khi có rollup | NFR-01 | Options.MaxRange, DefaultMaxRange, tsdb.LongRetention |
| NFR-ADM-03 | Phản hồi lỗi thống nhất, không lộ chi tiết nội bộ | NFR-05 | Mã lỗi cố định, lỗi backend thành 503 unavailable |
| NFR-ADM-04 | Mọi lời gọi truy vết được | NFR-06 | X-Request-Id trả về và ghi log |
2.3 Acceptance Criteria
| AC | Given / When / Then | Test ID |
|---|---|---|
| AC-ADM-01 | Given không có header Authorization, When gọi bất kỳ endpoint, Then 401 và WWW-Authenticate | TestAuthRequiredEverywhere |
| AC-ADM-02 | Given PUT /agents/a1 với agent_id khác path, When gọi, Then 400 | TestPutAgentValidation |
| AC-ADM-03 | Given state=active mà token_hash không phải SHA-256 hex thường, When gọi, Then 400 | TestPutAgentValidation |
| AC-ADM-04 | Given agent đã thu hồi hoặc chưa tồn tại, When DELETE, Then 204 | TestPutAndRevokeAgent |
| AC-ADM-05 | Given truy vấn thiếu company_id, When gọi, Then 400 | TestServerStatusAndSummary, TestSeriesQuery |
| AC-ADM-06 | Given metric ngoài catalog, When truy vấn, Then 400 | TestSeriesQuery |
| AC-ADM-07 | Given khoảng dài hơn giới hạn (31 ngày, hoặc 400 ngày khi có rollup), When truy vấn, Then 400 | TestSeriesDefaultMaxRange, TestSeriesRetentionClampMaxRangeAndPlannedStep |
| AC-ADM-08 | Given xóa dữ liệu máy chủ công ty A, When xong, Then dữ liệu công ty B còn nguyên | TestDeleteDataIsTenantScoped |
| AC-ADM-09 | Given POST /reload/rules, When worker nối Options.Reload, Then 202 và luật được kéo lại ngay (settings vẫn 501) | TestReload |
2.4 Quality Attribute Scenarios
| Nguồn | Kích thích | Môi trường | Phản hồi | Thước đo |
|---|---|---|---|---|
| Kẻ tấn công trong mạng | Dò token admin | Bình thường | 401, so sánh hằng thời gian | Không rò thông tin độ dài hay tiền tố qua thời gian phản hồi |
| Access Hub | Gọi truy vấn khi VictoriaMetrics lỗi | Bình thường | 503 unavailable | Không rò lỗi nội bộ |
| Quản trị | Xóa nhầm công ty | Bình thường | Xóa ngay, không hoàn tác | Ghi log mức warn kèm request_id, company_id (đề xuất thêm bước xác nhận ở Access Hub) |
| Access Hub | Đẩy agent với version cũ | Bình thường | Registry bỏ bản cũ, API vẫn trả 200 | Xem L3 Registry |
3. Application Architecture
3.1 Kiến trúc thời chạy (C&C)
flowchart LR
classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff;
classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff;
AU["Auth (Bearer)"]:::owned
RT["Router /internal/v1"]:::owned
HD["Handlers"]:::owned
RG["Registry"]:::bc
PS["Presence"]:::bc
TS["TSDB Store"]:::bc
CT["Catalog"]:::bc
RT --> AU
RT --> HD
HD --> RG
HD --> PS
HD --> TS
HD --> CT| Thành phần | Trách nhiệm | Vòng đời |
|---|---|---|
| Router | http.ServeMux mẫu Go 1.22: mỗi đường dẫn gắn bảng phương thức, sai phương thức thì 405 | Theo tiến trình |
| Auth | Kiểm Authorization: Bearer, so sánh SHA-256 hằng thời gian, chạy trước khi kiểm phương thức | Theo yêu cầu |
| Handlers | Kiểm đầu vào, gọi Registry, Presence, Store, Catalog, dựng phản hồi {"data":...} | Theo yêu cầu |
| Catalog | Tệp metrics.yaml nhúng: tên chỉ số, nhãn cho phép, status_metrics | Nạp một lần |
| Kết nối | Kiểu | Chi tiết |
|---|---|---|
| Access Hub đến Admin | Đồng bộ | HTTPS (triển khai) hoặc HTTP nội bộ ở dev |
| Admin đến Registry, Presence | Đồng bộ | Timeout Redis 500 ms |
| Admin đến Store | Đồng bộ | tsdb.timeout |
Listener admin (mặc định 127.0.0.1:9101) cũng phục vụ /healthz và /readyz (httpx.AddHealth), không cần token.
3.2 Module view
flowchart TB
classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff;
classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff;
APP["internal/app"]:::bc
ADM["internal/admin"]:::owned
CAT["internal/catalog"]:::owned
HX["internal/httpx"]:::bc
RGS["internal/registry"]:::bc
PRE["internal/presence"]:::bc
TSP["internal/tsdb"]:::bc
HCP["internal/hubclient (AgentRecord)"]:::bc
APP --> ADM
ADM --> CAT
ADM --> HX
ADM --> RGS
ADM --> PRE
ADM --> TSP
ADM --> HCPTham số ngưỡng Threshold = max(3 x alerting.agent_interval, alerting.down_min) (cùng công thức với Detector), dùng để tính online.
4. Domain Model
classDiagram
class AgentRecord {
<<hubclient>>
agent_id
company_id
server_id
state
token_hash
prev_token_hash
version
}
class ServerStatus {
<<response>>
server_id
company_id
state
last_seen
agents[]
latest[]
}
class AgentStatus {
agent_id
state
agent_version
last_seen
down
down_since
}
class SeriesResult {
<<response>>
metric
agg
step_seconds
series[]
}
class Catalog {
<<embedded>>
metrics
status_metrics
}
ServerStatus "1" --> "*" AgentStatus
ServerStatus --> Catalog
SeriesResult --> CatalogBất biến:
company_idbắt buộc cho mọi thao tác theo máy chủ (tham số truy vấncompany_id) và mọi tạo bản ghi (trong body).- Trạng thái máy chủ:
downnếu có agent down;onlinenếu có agent thấy thật trong ngưỡng; ngược lạiunknown. - ID (agent, công ty, máy chủ) khớp
tsdb.ValidID; chỉ số phải có trong catalog.
5. API Contract
5.1 Operations
Tiền tố /internal/v1. Mọi endpoint yêu cầu Authorization: Bearer <admin token>.
| Method | Path | Mục đích | Phản hồi thành công |
|---|---|---|---|
| PUT | /agents/{id} | Đẩy hoặc cập nhật agent | 200 |
| DELETE | /agents/{id} | Thu hồi agent (idempotent) | 204 |
| POST | /reload/{kind} | Nạp lại rules hoặc settings | 202 cho rules, 501 cho settings |
| GET | /servers/{id}/status?company_id= | Trạng thái máy chủ | 200 |
| GET | /servers/{id}/series?company_id=&metric=&from=&to=&step=&agg= | Truy vấn khoảng | 200 |
| DELETE | /servers/{id}/data?company_id= | Xóa dữ liệu máy chủ | 200 |
| GET | /companies/{id}/summary | Tổng hợp công ty | 200 |
| DELETE | /companies/{id}/data | Xóa dữ liệu công ty | 200 |
| GET | /status | Trạng thái collector | 200 |
Lệch tài liệu: docs/03 và docs/06 mô tả tập endpoint khác (xem L2 TD list). Mã thắng. POST /reload/rules chạy thật (app.reload gọi Syncer.Reload khi có worker, hoặc tăng tín hiệu Redis rules:reload khi admin tách tiến trình). settings trả 501.
5.2 Request và Response schema
Ký hiệu ! bắt buộc, ? tùy chọn. Phản hồi thành công bọc trong {"data": ...}.
PUT /agents/{id}
Body: AgentRecord (tối đa 64 KiB)
agent_id?: string (nếu có phải bằng {id})
company_id!, server_id!: ValidID
state!: "pending" | "active" | "revoked"
token_hash: 64 hex thường (bắt buộc khi active; tùy chọn khi pending, revoked)
prev_token_hash?: 64 hex thường
prev_token_expires_at?, expires_at?: timestamp
version?: int >= 0
200: { data: { agent_id, state, version } }
GET /servers/{id}/status?company_id=
200: { data: { server_id, company_id, state: "online"|"down"|"unknown", last_seen,
agents![]: { agent_id, state, agent_version?, last_seen, down, down_since? },
latest![]: { metric, labels, ts_ms, value } } }
404 nếu không có agent nào của máy chủ trong công ty.
GET /servers/{id}/series
company_id!, metric! (trong catalog)
from? (RFC3339 hoặc giây unix, mặc định to - 1 giờ), to? (mặc định hiện tại)
step? ("5m" hoặc số giây), agg? (chuẩn hóa bởi tsdb.Query.Normalize)
200: { data: { server_id, company_id, metric, agg, from, to, step_seconds,
series![]: { labels, points![]: [ts_ms, value] } } }
DELETE /servers/{id}/data?company_id=
200: { data: { server_id, company_id, agents_removed, series_deleted: true } }
GET /companies/{id}/summary
200: { data: { company_id, agents_total, online, offline } }
DELETE /companies/{id}/data
200: { data: { company_id, agents_removed, series_deleted: true } }
GET /status
200: { data: { collector_id, version, roles[], uptime_seconds, bus_depth,
outbox?: { depth, oldest_age_seconds, dead_letter },
agents: { total, online, down } } }
POST /reload/{kind} kind in {rules, settings}
202: { data: { reload, accepted: true } } (chưa có)Lỗi: {"error": {"code": "...", "message": "..."}}.
5.3 Error codes
| HTTP | code | Khi nào |
|---|---|---|
| 400 | bad_request | ID sai, body không phải JSON agent, agent_id lệch path, thiếu hoặc sai company_id, state hoặc băm sai, version âm, chỉ số ngoài catalog, thời gian, bước hoặc khoảng sai, khoảng quá 31 ngày |
| 401 | unauthorized | Thiếu hoặc sai token (kèm WWW-Authenticate: Bearer realm="collector-admin") |
| 404 | not_found | Đường dẫn lạ (sau khi xác thực), kind reload lạ, máy chủ không có agent |
| 405 | method_not_allowed | Sai phương thức (kèm Allow) |
| 500 | server_error | Không dựng được truy vấn |
| 501 | not_implemented | reload chưa hỗ trợ |
| 503 | unavailable | Registry, TSDB hoặc reload lỗi tạm thời |
Xác thực chạy trước kiểm phương thức, nên người chưa xác thực không thăm dò được đường dẫn (đường dẫn lạ vẫn đòi token trước khi trả 404).
5.4 Versioning
Đường dẫn mang phiên bản /internal/v1. Thay đổi phá vỡ thì tạo /internal/v2. Chưa có chính sách bảo trì đồng thời hai phiên bản (đề xuất ghi khi cần, OQ-ADM-4).
5.5 Authz
| Chủ thể | Quyền |
|---|---|
| Access Hub (giữ token admin) | Tất cả 9 endpoint |
| Bất kỳ ai khác | Không có quyền (401) |
Token admin là một bí mật dùng chung, không có phạm vi theo công ty: Access Hub phải tự bảo đảm company_id gửi sang khớp người dùng (xem 11).
6. Physical Data Schema
6.1 Mapping
Admin API không có kho dữ liệu riêng. Nó đọc và ghi qua các thành phần khác.
| Thao tác | Kho | Khóa hoặc chuỗi |
|---|---|---|
| Đẩy, thu hồi agent | Registry (Redis hoặc bộ nhớ) | reg:* (L3 Registry) |
| Trạng thái, tổng hợp, đếm | Presence | pres:* |
| Truy vấn, xóa dữ liệu | VictoriaMetrics | Chuỗi ah_<tên> có nhãn company_id, server_id |
| Catalog | Tệp nhúng internal/catalog/metrics.yaml | Bản sao máy đọc được của catalog chỉ số phía agent: thêm chỉ số ở một nơi phải cập nhật cả hai, chỉ số thiếu sẽ bị bỏ ở ingest |
6.2 Phân loại và lưu giữ
| Dữ liệu | Phân loại | Lưu giữ |
|---|---|---|
| Token admin | Bí mật | Trong cấu hình (admin.token, admin.token_file, AHC_ADMIN_TOKEN); không vào log |
| Log thao tác | Trung bình | Theo hệ thống log (agent pushed, agent revoked, company data deleted, server data deleted) |
7. Algorithms
7.1 Sequences (đường thành công)
Đẩy agent:
sequenceDiagram
participant HB as Access Hub (ext)
participant AD as Admin API
participant RG as Registry
HB->>AD: PUT /agents/{id} (Bearer, JSON)
AD->>AD: kiểm token, kiểm đầu vào
AD->>RG: Put(FromRecord)
RG-->>AD: OK
AD-->>HB: 200 (agent_id, state, version)Trạng thái máy chủ:
sequenceDiagram
participant HB as Access Hub (ext)
participant AD as Admin API
participant RG as Registry
participant PS as Presence
participant TS as TSDB
HB->>AD: GET /servers/{id}/status?company_id=
AD->>RG: ByServer(company, server)
RG-->>AD: agents
AD->>PS: Get(agent) cho từng agent
AD->>TS: Latest(company, server, status_metrics)
TS-->>AD: điểm mới nhất
AD-->>HB: 200 (state, agents, latest)7.2 Sequences (đường lỗi)
sequenceDiagram
participant HB as Access Hub (ext)
participant AD as Admin API
participant TS as TSDB
HB->>AD: GET /servers/{id}/series
alt token sai
AD-->>HB: 401 unauthorized
else tham số sai
AD-->>HB: 400 bad_request
else TSDB lỗi
AD->>TS: Range
TS--)AD: lỗi
AD-->>HB: 503 unavailable
end7.3 State machines
API không giữ trạng thái. Vòng đời agent do Registry quản lý (xem L3 Registry mục 7.3). Admin chỉ kích hoạt các chuyển trạng thái:
| Lời gọi | Chuyển trạng thái |
|---|---|
PUT với state=active | pending hoặc active đến active (phiên bản mới hơn) |
PUT với state=revoked hoặc DELETE | đến revoked (tombstone) |
DELETE .../data | đến không tồn tại (bản ghi bị xóa, chuỗi bị xóa) |
7.4 Core algorithms
| Vấn đề | Giải pháp | Trade-off |
|---|---|---|
| Xác thực an toàn thời gian | So sánh băm SHA-256 của hai chuỗi bằng ConstantTimeCompare | Không lộ độ dài, nhưng token dùng chung |
| Không thăm dò đường dẫn | Xác thực trước định tuyến phương thức, đường dẫn lạ cũng đòi token | |
| Trạng thái máy chủ | Duyệt agent active, lấy presence: down thắng, rồi online nếu lần thấy thật trong ngưỡng | Máy chủ nhiều agent chỉ cần một down là down |
| Bộ chọn truy vấn an toàn | tsdb.CheckTenant và Query.Normalize tạo bộ chọn, ValidID chặn ký tự lạ | Phụ thuộc đúng đắn của adapter |
| Xóa | Xóa registry trước, rồi TSDB | Lỗi giữa chừng để lại trạng thái nửa xóa, gọi lại xóa an toàn (idempotent) |
| Thu hồi | Get rồi Revoke nếu có, luôn 204 | Không phân biệt agent không tồn tại với đã thu hồi |
| Giới hạn đọc body | io.LimitReader(64 KiB) rồi giải mã JSON | Body vượt 64 KiB bị cắt và lỗi giải mã thành 400, không báo lý do "quá lớn" |
8. Error Handling
8.1 Bảng lỗi theo bước
| Bước lỗi | Nguyên nhân | Cơ chế xử lý | Trạng thái cuối |
|---|---|---|---|
| Xác thực | Token sai | 401, không ghi chi tiết | Không đổi |
| Kiểm đầu vào | ID, băm, state, thời gian sai | 400 với thông điệp cố định | Không đổi |
| Registry | Redis lỗi | 503 unavailable, log warn kèm op, request_id | Không đổi hoặc ghi dở (xem 10.1) |
| TSDB | VictoriaMetrics lỗi | 503 unavailable | Không đổi |
| Xóa | Registry xóa xong, TSDB lỗi | 503; agent đã xóa, chuỗi còn | Gọi lại xóa để hoàn tất |
| Truy vấn | ErrBadQuery | 400 | Không đổi |
| Khác | Lỗi dựng truy vấn | 500 server_error | Không đổi |
8.2 Fail-fast
admin.tokenngắn hơn 24 ký tự khi bật vai tròadminthì collector không khởi động (mã thoát 3).admin.listenrỗng thì lỗi cấu hình.- Thiếu thành phần phụ thuộc thì
Newtrả lỗi (TestNewRejectsBadOptions).
8.3 Race conditions
| Tình huống | Xử lý |
|---|---|
Hai PUT đồng thời cùng agent | Registry so sánh Version |
DELETE agent trong lúc ingest tra token | Tombstone; cache nút có thể còn bản cũ tối đa cache_ttl |
| Xóa dữ liệu trong lúc worker đang ghi | Mẫu ghi sau lệnh xóa vẫn xuất hiện lại (không có khóa giữa xóa và ghi); Access Hub nên thu hồi agent trước khi xóa (đề xuất, OQ-ADM-3) |
9. Degradation
Nguyên tắc: Admin lỗi không được ảnh hưởng đường ingest. Đây là mặt phẳng điều khiển, chấp nhận 503 khi phụ thuộc lỗi.
9.1 Dependency matrix
| Dependency lỗi | Hành vi | Kiểu suy thoái | Hệ quả |
|---|---|---|---|
| Redis | PUT, DELETE, trạng thái trả 503 | Từ chối | Access Hub thử lại |
| VictoriaMetrics | series, status (phần latest), xóa trả 503 | Từ chối | Giao diện không có biểu đồ |
| Access Hub không gọi được | Collector vẫn ingest bằng registry cache | Không ảnh hưởng | Registry dần lệch cho đến khi đồng bộ (Syncer) |
| Presence lỗi | Trả trạng thái thiếu hoặc unknown | Giảm chức năng |
9.2 Backup và recovery
API không giữ dữ liệu. Phục hồi dựa trên Registry và VictoriaMetrics. Xóa dữ liệu không hoàn tác; không có thùng rác. RTO và RPO chính thức: chưa định nghĩa (đề xuất, OQ-ADM-1).
10. Concurrency
10.1 Ranh giới giao dịch
Mỗi lời gọi là một thao tác độc lập; xóa theo công ty gồm hai bước (registry, rồi TSDB) không cùng giao dịch. Lỗi ở bước hai để lại trạng thái nửa xóa, gọi lại hoàn tất. Put Redis nhiều khóa: xem L3 Registry mục 10.1.
10.2 Cơ chế
| Cơ chế | Mô tả | Mối nguy |
|---|---|---|
http.ServeMux đồng thời | Mỗi request một goroutine | Không có giới hạn tốc độ ở Admin (chỉ một client tin cậy, đề xuất thêm nếu mở rộng, OQ-ADM-5) |
So sánh Version (Registry) | Chống ghi đè cũ | |
io.LimitReader | Chặn body lớn | Cắt im lặng (xem 7.4) |
| Xóa không khóa với ghi | Bia mộ purge (COL-11): ghi trước khi xóa, worker bỏ lô của tenant đã xóa, xóa TSDB lần hai sau 10 giây | Worker tách tiến trình thấy bia mộ chậm tới 2 giây, lần xóa thứ hai bù khoảng này |
11. Security
11.1 Ba lớp
| Lớp | Biện pháp |
|---|---|
| Truyền thông | Listener riêng admin.listen (mặc định 127.0.0.1:9101, không công khai). Sản xuất: TLS hoặc mạng nội bộ đáng tin cậy, giới hạn nguồn truy cập ở tầng mạng (đề xuất, nội dung docs/09) |
| Mã | Bearer token tối thiểu 24 ký tự, so sánh băm hằng thời gian; xác thực trước định tuyến; ID và băm kiểm định dạng; chỉ số phải có trong catalog; lỗi backend không lộ chi tiết |
| Dữ liệu | Token từ tệp hoặc biến môi trường (AHC_ADMIN_TOKEN, admin.token_file), không có trong YAML trong repo; log thao tác không chứa token |
11.2 Pipeline phân quyền năm bước
- Nhận request, đọc
Authorization. - Kiểm tiền tố
Bearer, băm SHA-256, so sánh hằng thời gian với băm token cấu hình. - Nếu sai: 401 kèm
WWW-Authenticate, không thêm thông tin. - Định tuyến và kiểm phương thức (405 kèm
Allow). - Kiểm đầu vào và
company_id/ID hợp lệ, rồi mới chạm Registry, Presence hoặc TSDB.
Không có bước kiểm "người dùng này có quyền trên công ty này": niềm tin nằm hoàn toàn ở Access Hub giữ token.
11.3 Chỉ mục neo
| Nguyên tắc L2 | Biện pháp nội bộ |
|---|---|
| NFR-12 cô lập tenant | company_id bắt buộc, bộ chọn truy vấn và xóa luôn gắn công ty, ByServer theo công ty |
| 9.1 danh tính | Token admin một bí mật duy nhất |
| 9.3 secrets | Token từ tệp hoặc biến môi trường |
Rủi ro còn mở (để rà soát): (1) DELETE /agents/{id} không nhận company_id, nên agent ID toàn cục bị thu hồi chỉ bằng ID (chấp nhận được khi chỉ Access Hub giữ token, nhưng khác quy tắc "company_id bắt buộc"; OQ-ADM-2). (2) Token dùng chung, không xoay vòng và không có nhiều token (đề xuất). (3) Không có audit log riêng ngoài log chung.
12. Configuration
12.1 Tunables
Biến môi trường: AHC_ADMIN_LISTEN, AHC_ADMIN_TOKEN, AHC_ADMIN_TOKEN_FILE. Còn lại chỉ YAML.
| Tham số | Default | Ý nghĩa và tác động | Mục liên quan |
|---|---|---|---|
admin.listen | 127.0.0.1:9101 | Địa chỉ listener admin (bắt buộc khi có vai trò admin) | 3.1 |
admin.token | rỗng | Token Bearer, tối thiểu 24 ký tự | 11 |
admin.token_file | rỗng | Đọc token từ tệp (cắt khoảng trắng hai đầu), chỉ dùng khi token rỗng | 11 |
roles | all | Phải chứa admin (hoặc all) để bật API | 3.1 |
alerting.agent_interval, alerting.down_min | 30 giây, 90 giây | Xác định ngưỡng online (cùng công thức với Detector) | 7.1 |
tsdb.url, tsdb.timeout | xem L3 Bus, Worker, TSDB | Truy vấn và xóa | 7.1 |
Hằng số cố định: body 64 KiB, khoảng truy vấn tối đa 31 ngày (400 ngày khi cấu hình có tsdb.long_url), from mặc định to - 1 giờ.
12.2 Feature flags
Không có. Vai trò admin bật hoặc tắt toàn bộ API. POST /reload/rules đã nối (COL-8), settings là điểm mở rộng chưa nối.
13. Telemetry
13.1 Metrics
Admin API chưa có metric riêng (theo danh sách metric đã xác minh trong mã; không có ahc_admin_*). Có các metric chung ahc_build_info, ahc_uptime_seconds, ahc_goroutines. Đề xuất: bổ sung ahc_admin_requests_total{endpoint,code} và ahc_admin_request_duration_seconds{endpoint} (OQ-ADM-6).
13.2 Log schema
JSON một dòng: time, level, msg, request_id, collector_id.
msg | Mức | Trường |
|---|---|---|
agent pushed | info | request_id, agent_id, company_id, state |
agent revoked | info | request_id, agent_id, company_id |
server data deleted | warn | request_id, company_id, server_id, agents |
company data deleted | warn | request_id, company_id, agents |
admin backend error | warn | op, request_id, err |
Không ghi token hay băm token.
13.3 Cảnh báo đến runbook
Chưa có luật. Đề xuất: tỷ lệ 401 tăng đột biến (dò token), 503 kéo dài, mọi lần xóa dữ liệu công ty (thông báo cho quản trị). Runbook: docs/09 mục 6.
13.4 Probes
/healthz và /readyz được phục vụ trên listener admin (xem 3.1). /readyz kiểm "redis" và "tsdb".
13.5 Trace propagation
X-Request-Id: nhận từ request hoặc sinh mới (httpx.RequestID), trả về trong header và ghi vào log. Chưa có trace phân tán.
14. Test Plan
| Loại | Kiểm thử | Vị trí |
|---|---|---|
| Xác thực, định tuyến | TestAuthRequiredEverywhere, TestNewRejectsBadOptions, TestRoutingErrors | internal/admin/admin_test.go |
| Agent | TestPutAndRevokeAgent, TestPutAgentValidation | internal/admin/admin_test.go |
| Reload | TestReload | internal/admin/admin_test.go |
| Đọc | TestServerStatusAndSummary, TestSeriesQuery, TestStatus | internal/admin/admin_test.go |
| Xóa | TestDeleteDataIsTenantScoped | internal/admin/admin_test.go |
| Catalog | TestDefault, TestEveryDefaultNameIsValid, TestParseRejectsUnknownStatusMetric | internal/catalog/catalog_test.go |
| Tích hợp | TestTwoTenantIsolationThroughIngestAndAdminAPI, TestRoleGatingListeners, TestSilentAgentGoesDownThenUpAtAccessHub | internal/app |
| Chưa có | Kiểm thử bảo mật thăm dò thời gian, kiểm thử tải Admin, xóa trong lúc ghi, kiểm thử hợp đồng với Access Hub thật |
Bản nháp này không chạy kiểm thử.
15. Implementation Sequence
15.1 Milestone matrix
| Milestone | Nội dung | Phụ thuộc | Đóng góp nghiệm thu |
|---|---|---|---|
| COL-7 | API admin: đẩy registry, trạng thái, series (giá trị mới nhất), kiểm thử hai tenant qua ingest và admin | COL-3, COL-5 | AC-ADM-01..08 |
| COL-8 | Reload luật thật (/reload/rules), GET /status có mục rules. settings chưa làm | COL-7 | AC-ADM-09 |
| COL-10 (xong 2026-10-01) | Rollup, API series tự chọn nguồn, khoảng tối đa 400 ngày khi có vm-long, cắt theo retention của gói (metrics_retention_days), step_seconds là bước thực dùng, /reload/settings xóa cache gói và báo worker qua Redis | COL-7 | AC-ADM-06, 07 |
| COL-11 (xong 2026-10-01) | Purge có bia mộ ghi trước, xóa TSDB lần hai sau 10 giây, worker bỏ lô và quên trạng thái tenant; giới hạn series theo công ty ở ingest | COL-7 | AC-ADM-08, TestPurgeWritesATombstoneFirstAndDeletesTwice, TestPurgeDropsSamplesInFlightAndForgetsAlerts |
HUB-14 (chưa xây, đặc tả ở docs/06 mục 8.1) | Access Hub gọi purge Server và Company | COL-11 | Đầu cuối |
| Metric Admin (đề xuất) | ahc_admin_* | COL-7 | Telemetry |
| Nhiều token hoặc xoay token (đề xuất) | COL-7 | Bảo mật |
Tên lấy từ docs/11-roadmap.md. Lệch tài liệu: roadmap xếp purge dữ liệu (COL-11) vào giai đoạn 2, chưa làm, nhưng mã đã có DELETE /servers/{id}/data và DELETE /companies/{id}/data (kèm TestDeleteDataIsTenantScoped). Mã thắng: roadmap cần cập nhật (OQ-ADM-7). Đã đóng ở COL-11 (2026-10-01): roadmap ghi phần bổ sung (bia mộ, xóa lần hai, quên trạng thái).
15.2 Dependency flowchart
flowchart LR
C3["COL-3 registry"] --> C7["COL-7 admin API"]
C5["COL-5 adapter VM"] --> C7
C7 --> C8["COL-8 reload luật (xong)"]
C7 --> C10["COL-10 rollup (xong)"]
C7 --> MT["metric admin (đề xuất)"]Appendix A. Open Questions
| # | Câu hỏi | Hành vi tạm thời | Owner | Mã theo dõi |
|---|---|---|---|---|
| 1 | RTO và RPO cho API và xóa dữ liệu | Chưa định nghĩa | Chưa chỉ định | OQ-ADM-1 |
| 2 | DELETE /agents/{id} có cần company_id để khớp quy tắc tenant | Chỉ theo ID | Chưa chỉ định | OQ-ADM-2 |
| 3 | Quy trình xóa an toàn (thu hồi agent trước, chặn ghi, rồi xóa) | Xóa không khóa | Chưa chỉ định | OQ-ADM-3 |
| 4 | Chính sách phiên bản /internal/v1 và v2 | Chưa có | Chưa chỉ định | OQ-ADM-4 |
| 5 | Giới hạn tốc độ Admin | Không có | Chưa chỉ định | OQ-ADM-5 |
| 6 | Bổ sung metric ahc_admin_* | Không có | Chưa chỉ định | OQ-ADM-6 |
| 7 | Cập nhật roadmap: DELETE .../data đã có trong mã nhưng COL-11 ghi chưa làm | Mã là nguồn sự thật | Chưa chỉ định | OQ-ADM-7 |
| 8 | Nhiều token admin, xoay token | Một token | Chưa chỉ định | OQ-ADM-8 |
| 9 | Presence có bị xóa khi DeleteServer hoặc DeleteCompany không (Redis: xác minh hook) | Chưa xác minh | Tác giả L3 | OQ-ADM-9 |
| 10 | Nguồn đồng bộ ConfigFor và cài đặt theo công ty | Luật đã xong, phần này chưa làm | Chưa chỉ định | OQ-ADM-10 |
Appendix B. ADR nội bộ
| Mã ADR | Quyết định | Trạng thái | Driver |
|---|---|---|---|
| ADR 0007 | Access Hub là nguồn sự thật agent, đẩy sang Collector | Chấp nhận | Tách control và data plane |
| ADR nội bộ (đề xuất) | Một token admin dùng chung, so sánh băm hằng thời gian | Đề xuất, là hành vi mã | Đơn giản cho một client tin cậy |
| ADR nội bộ (đề xuất) | Xác thực trước định tuyến, 404 cũng đòi token | Đề xuất, là hành vi mã | Không thăm dò đường dẫn |
| ADR nội bộ (đề xuất) | DELETE /agents idempotent, luôn 204 | Đề xuất, là hành vi mã | Thử lại an toàn |
| ADR nội bộ (đề xuất) | company_id bắt buộc qua tham số truy vấn cho mọi thao tác theo máy chủ | Đề xuất, là hành vi mã | NFR-12 |
Appendix C. Section Profile
| Phân mục | Hồ sơ | Trạng thái điền | Giải trình |
|---|---|---|---|
| 0 đến 4 | Bắt buộc | Đã điền | |
| 5 API contract | Bắt buộc | Đã điền | Lấy trực tiếp từ admin.go |
| 6 Physical data | Bắt buộc | Điền (không có kho riêng) | Admin không có dữ liệu riêng |
| 7.1 đến 7.4 | Bắt buộc | Đã điền | |
| 8, 9, 10, 11, 12 | Bắt buộc | Đã điền | |
| 13 Telemetry | Bắt buộc | Điền một phần | Chưa có metric riêng (OQ-ADM-6) |
| 14, 15 | Bắt buộc | Đã điền | Roadmap lệch mã ở purge (OQ-ADM-7) |