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

L3 - Nền tảng giám sát máy chủ - Collector - Registry, Enroll, Presence ​

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: Registry (danh tính agent), Resolver (tra ngược Access Hub), Syncer (đồng bộ), hub client, enroll proxy và Presence (lần thấy cuối). Mã: internal/registry, internal/hubclient, internal/presence, phần enroll của internal/ingest/handlers.go.

Trạng thái
Bản nháp
Phiên bản
0.1, ngày 30/09/2026
Tài liệu cha
L2 SAD Collector
Cấp độ hệ thống
Cấp 3, đề xuất

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

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

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áiBẢ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)Registry (memory và Redis), Resolver, Syncer, hub client HTTP, enroll proxy, Presence tracker (memory và Redis)
Truy vết L2L2 SAD Collector: thành phần "Registry, Resolver, Enroll proxy, Presence" (mục 2.3, 6.1), FR 1, 2 (mục 3), NFR 7, 9 (mục 4), mục 7.1 (AgentRecord, PresenceEntry), rủi ro AR-005, AR-009. Mục tiêu L1: G1, G5 (qua L2)
Tài liệu chaL2 SAD Collector
Tài liệu anh emIngest API, Alerting, Outbox, Admin API
TierĐề xuất Cấp 3 (kế thừa L2, chưa xác nhận)
Phân loại dữ liệuNhạy cảm vừa: băm token, định danh agent, công ty, máy chủ. Không lưu token rõ
Blast radiusRegistry sai làm agent bị từ chối (401, 403) hoặc, tệ hơn, gắn sai tenant. Mất Redis: ingest hỏi lại Access Hub có giới hạn. Presence sai gây báo down nhầm hoặc bỏ sót
Sign-off gateChưa chỉ định. Không có ai đã sign-off

Truy vết mục L3 đến thành phần L2:

Mục L3Thành phần hoặc mục L2
1 Phạm vi2.3 Các thành phần chính
2 Yêu cầu3 Functional (1, 2), 4 NFR
3 Kiến trúc6.1 Component table
4, 6 Domain, dữ liệu7.1 Data Model
5 Hợp đồng6.2 Integration (Ingest đến Access Hub, đồng bộ registry)
7 Thuật toán8.1, 8.2 (enroll, state agent)
8, 9 Lỗi, suy thoái6.3 Resilience, 12.2 Reliability
11 Bảo mật9.1 Identity, 9.3 Secrets
13 Telemetry13 Observability

1. Scope and Non-Goals ​

1.1 Vai trò ​

Trả lời câu hỏi "token này là agent nào, của công ty nào, máy chủ nào, còn dùng được không" mà không phải hỏi Access Hub ở mỗi request. Registry là bản sao đọc của dữ liệu agent do Access Hub sở hữu (nguồn sự thật). Presence lưu lần thấy cuối của mỗi agent để Detector phát hiện mất tín hiệu.

1.2 Context diagram ​

mermaid
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;

    ING["Ingest API"]:::bc
    ADM["Admin API"]:::bc
    DET["Detector"]:::bc
    HUB(["Access Hub (ext)"]):::entity
    RG["Registry và Resolver"]:::owned
    PS["Presence"]:::owned
    RED[("Redis")]:::datastore

    ING -->|"tra token, ghi sau enroll"| RG
    ING -->|"cập nhật lần thấy cuối"| PS
    ADM -->|"đẩy và thu hồi agent"| RG
    DET -->|"lấy agent quá hạn"| PS
    RG -->|"tra ngược, enroll, đồng bộ"| HUB
    RG -->|"lưu bản ghi"| RED
    PS -->|"lưu lần thấy"| RED
ChiềuBênNội dung
VàoIngest APILookup theo băm token, Put sau enroll, Touch, Ensure, Remove
VàoAdmin APIPut (đẩy agent), Revoke, DeleteServer, DeleteCompany, ByServer, Counts
VàoDetector, ReporterExpired, DrainSeen, Counts, CompanyCounts (L3 Alerting)
RaAccess HubPOST /enroll, GET /agents?token_hash=, GET /agents?since=&limit=
RaRedisKhóa reg:* và pres:* (tiền tố redis.prefix, mặc định ah:)

1.3 Trong phạm vi ​

  • Định nghĩa AgentRecord, trạng thái, băm token, hạn, phiên bản.
  • Hai hiện thực Registry (bộ nhớ, Redis), cache nút và cache âm.
  • Resolver: tra registry, rồi Access Hub có bảo vệ (cache âm, giới hạn, single-flight).
  • Syncer kéo thay đổi theo since.
  • hub client HTTP (internal/hubclient).
  • Luồng enroll proxy (phần nghiệp vụ, không phần HTTP của Ingest).
  • Presence tracker (bộ nhớ, Redis).

1.4 Ngoài phạm vi ​

Nội dungKhông thuộc BCThuộc về
Tạo agent, xác thực License, ràng buộc máy chủ, sinh agent tokenCóAccess Hub (HUB-*)
Xoay token (renew)CóChưa làm (stub 503 ở Ingest)
HTTP của /agent/v1/enroll (header, giới hạn, mã lỗi)CóL3 Ingest API
Quyết định "agent down" và sự kiệnCóL3 Alerting, Outbox
Endpoint Admin đẩy agentCóL3 Admin API
Dọn bản ghi theo TTLCóChưa làm (registry và presence không TTL, AR-009 của L2)

2. Requirements ​

2.1 Functional Requirements ​

#Trách nhiệmGiải thíchHiện thực ở
FR-REG-01Tra danh tính theo băm tokenTrả AgentRecord gồm agent, công ty, máy chủ, trạng thái, hạninternal/registry/registry.go, TestLookupFlow
FR-REG-02Chống ghi đè bằng phiên bản cũPut không cho Version cũ ghi đè mới (Version 0 luôn được áp dụng)TestStaleVersionIgnored
FR-REG-03Thu hồi có tombstoneBăm thu hồi được giữ để trả 403 thay vì 401TestRevokeKeepsTombstone, TestPutRevokedStateTombstones
FR-REG-04Token cũ còn hiệu lực đến hạnprev_token_hash có hạn theo prev_token_expires_at; thiếu hoặc sai nghĩa là đã hết hạnTestFromRecordPreviousToken, TestTokenRotationKeepsPreviousUntilExpiry
FR-REG-05Hết hạn và pendingBản ghi hết hạn hoặc pending coi như không dùng đượcTestPendingAndExpiredTokens
FR-REG-06Xóa theo phạm viDeleteServer, DeleteCompany, ByServer giới hạn trong công tyTestDeleteScopes, TestByServerIsCompanyScoped
FR-REG-07Tra ngược Access Hub khi missCache âm, giới hạn toàn cục, single-flightTestResolveFallsBackToHubAndCaches, TestNegativeCache, TestGlobalLimiterProtectsHub, TestConcurrentMissesCollapse
FR-REG-08Access Hub lỗi không thành token saiTrả ErrHubUnavailable (ingest trả 503)TestHubDownIsNotAnInvalidToken
FR-REG-09Đồng bộ theo trangSyncOnce kéo 500 bản ghi mỗi trang đến hết; Run chạy ngay rồi theo chu kỳTestSyncerPagesAndAdvances
FR-REG-10Redis hỏng thì hỏi Access HubLỗi Redis đọc thành ErrNotFoundTestRedisRegistryDownFallsBackToNotFound
FR-ENR-01Enroll proxyChuyển tiếp sang Access Hub, lưu agent active phiên bản 0, xóa cache âm, trả agent token một lầnTestEnrollProxy (Ingest)
FR-ENR-02Ánh xạ lỗi enroll409 thành already_enrolled hoặc token_used, 422 thành binding_failed, 429 thành rate_limited, tạm thời thành 503, khác thành 401TestEnrollErrors, TestEnrollPassesIPAndErrors (hubclient)
FR-PRS-01Lần thấy cuốiTouch cập nhật, Ensure gieo thời gian ân hạn, Remove xóaTestMemoryTracker, TestRedisTracker, TestEnsureSeedsGraceAndIsNotReported
FR-PRS-02Một chủ duy nhất cho chuyển downTiến trình SADD vào pres:down trả 1 sở hữu chuyển trạng tháiTestRedisTrackerSharedAcrossProcesses
FR-PRS-03Chỉ báo lần thấy thậtReal=false nghĩa là thời gian chỉ là hạt giống, không báo hubTestDrainSeenOnlyReportsProgress, TestLastSeenNeverGoesBackwards
FR-PRS-04Phục hồiTouch trên agent đang down đưa về up và kích hoạt OnAgentUpTestTouchRecoversDownAgent

2.2 Non-Functional Requirements ​

Allocated:

NFR L2TargetCách đáp ứng ở L3
NFR-08 (chịu mất Access Hub)Ingest chạy bằng registry cacheRegistry Redis không TTL, cache nút cache_ttl 15 giây
NFR-12 (cô lập tenant)Không lẫn tenantBản ghi mang company_id; chỉ mục theo company_id; ByServer theo công ty
NFR-02 (sẵn sàng)Mất một node không mất dữ liệuTrạng thái dùng chung trong Redis

Inherited: timeout client Redis 500 ms mặc định và lệnh bị chặn từ internal/redisx; client HTTP từ internal/hubclient.

Owned:

IDTargetParent L2-NFRSatisfied-by
NFR-REG-01Một miss đồng thời chỉ gây một lần gọi Access HubNFR-08single-flight, TestConcurrentMissesCollapse
NFR-REG-02Tra Access Hub tối đa 50 mỗi giây mỗi nút (mặc định)NFR-08hub.lookup_per_second, TestGlobalLimiterProtectsHub
NFR-REG-03Cache âm 60 giây, tối đa 100.000 mụcNFR-08TestNegativeCache
NFR-REG-04Cache nút tối đa 200.000 mục, TTL 15 giâyNFR-01internal/registry/redis.go
NFR-REG-05Một tiến trình sở hữu mỗi chuyển downNFR-13internal/presence/redis.go

2.3 Acceptance Criteria ​

ACGiven / When / ThenTest ID
AC-REG-01Given token chưa có trong registry, When tra, Then hỏi Access Hub một lần, lưu, tra lại thành côngTestResolveFallsBackToHubAndCaches
AC-REG-02Given Access Hub báo không tồn tại, When tra lại trong 60 giây, Then không gọi Access Hub lần nữaTestNegativeCache
AC-REG-03Given Access Hub lỗi, When tra, Then ErrHubUnavailable, không phải "token sai"TestHubDownIsNotAnInvalidToken
AC-REG-04Given bản ghi version 5, When Put version 3, Then bản ghi giữ version 5TestStaleVersionIgnored
AC-REG-05Given agent bị thu hồi, When tra băm cũ, Then ErrRevokedTestRevokeKeepsTombstone
AC-REG-06Given Redis hỏng, When tra, Then ErrNotFound rồi hỏi Access HubTestRedisRegistryDownFallsBackToNotFound
AC-REG-07Given hai tiến trình cùng quét, When agent quá hạn, Then chỉ một tiến trình nhận chuyển downTestRedisTrackerSharedAcrossProcesses
AC-REG-08Given bản ghi công ty A, When ByServer với công ty B, Then rỗngTestByServerIsCompanyScoped

2.4 Quality Attribute Scenarios ​

NguồnKích thíchMôi trườngPhản hồiThước đo
AgentGửi token lần đầu sau restart RedisRedis rỗngIngest hỏi Access Hub (có giới hạn), lưu lạiKhông quá 50 lần tra mỗi giây mỗi nút
Kẻ tấn côngGửi hàng loạt token ngẫu nhiênBất kỳCache âm 60 giây và giới hạn chặn tải lên Access HubSố lần gọi Access Hub không vượt trần
Access HubMất kết nốiToken đã cacheIngest vẫn xác thựcKhông 401 sai
Quản trịThu hồi agentBình thườngIngest trả 403 trong vòng cache_ttl (15 giây) sau khi bản ghi cập nhật403 sau tối đa cache TTL (đề xuất xác nhận khi đo)

3. Application Architecture ​

3.1 Kiến trúc thời chạy (C&C) ​

mermaid
flowchart LR
    classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff;
    classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff;
    classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff;

    RS["Resolver"]:::bc
    RGM["Registry memory"]:::bc
    RGR["Registry Redis"]:::bc
    NC["Cache âm và limiter"]:::bc
    HC["Hub client"]:::owned
    SY["Syncer"]:::bc
    PT["Presence tracker"]:::bc
    RED[("Redis")]:::datastore

    RS --> RGR
    RS --> NC
    RS --> HC
    SY --> HC
    SY --> RGR
    RGR --> RED
    RGM -.-> RGR
    PT --> RED

Mũi tên chỉ chiều khởi tạo lời gọi. Registry memory là hiện thực thay thế của cùng interface (dùng khi không có Redis), vẽ nét đứt.

Thành phầnTrách nhiệmVòng đời
ResolverLookup theo băm: registry, cache âm, limiter toàn cục, single-flight, hub, Put, tra lạiTheo tiến trình
Registry memoryHai map byID, byHash, tập revoked, hook OnUpsert, OnRevoke, OnDeleteTheo tiến trình
Registry RedisBản ghi trong Redis, cache nút (tối đa 200.000 mục, cache_ttl)Bền trong Redis
Cache âm và limiterCache âm 60 giây tối đa 100.000 mục; limiter hub-lookup toàn cụcTheo tiến trình
Hub client (HTTP)Gọi <hub>/api/v1/monitoring/collector, Bearer hub.tokenTheo tiến trình
SyncerKéo since theo trang 500; chạy trên vai trò worker và admin khi có hubTheo tiến trình
Presence trackerGhi và đọc lần thấy cuối trong bộ nhớ hoặc RedisBền trong Redis
Kết nốiKiểuChi tiết
Resolver đến RegistryĐồng bộTimeout client Redis 500 ms
Resolver đến Hub clientĐồng bộ, single-flighthub.timeout 10 giây, một lần gọi cho nhiều người chờ
Syncer đến Hub clientĐồng bộ, định kỳhub.sync_interval 1 phút, 500 mỗi trang
Tất cả đến RedisĐồng bộClient internal/redisx (RESP2) với danh sách lệnh bị chặn

3.2 Module view ​

mermaid
flowchart TB
    classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff;
    classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff;

    APP["internal/app"]:::bc
    ING["internal/ingest"]:::bc
    ADM["internal/admin"]:::bc
    RGS["internal/registry"]:::owned
    HCP["internal/hubclient"]:::owned
    PRE["internal/presence"]:::owned
    RX["internal/redisx"]:::owned
    RLP["internal/ratelimit"]:::owned
    TS["internal/tsdb (ValidID)"]:::owned

    APP --> RGS
    APP --> PRE
    ING --> RGS
    ING --> PRE
    ING --> HCP
    ADM --> RGS
    ADM --> PRE
    RGS --> HCP
    RGS --> RX
    RGS --> RLP
    PRE --> RX

Tệp: registry.go (interface, bộ nhớ), redis.go (Redis), resolver.go (Resolver, Syncer, FromRecord), hubclient.go, presence.go (interface, bộ nhớ), redis.go (Redis).


4. Domain Model ​

mermaid
classDiagram
    class AgentRecord {
        <<registry>>
        agent_id
        company_id
        server_id
        state
        token_hash
        expires_at
        prev_token_hash
        prev_token_expires_at
        version
    }
    class AgentState {
        <<enum>>
        pending
        active
        revoked
    }
    class Registry {
        <<interface>>
        Lookup
        Put
        Revoke
        Get
        ByServer
        DeleteCompany
        DeleteServer
        Count
    }
    class Resolver {
        <<service>>
        Lookup
    }
    class PresenceEntry {
        <<presence>>
        agent_id
        company_id
        server_id
        last_seen_ms
        real
        down
    }
    Registry "1" --> "*" AgentRecord
    AgentRecord --> AgentState
    Resolver --> Registry
    PresenceEntry ..> AgentRecord : ref agent_id

Bất biến:

  • Token chỉ lưu dạng băm SHA-256 (64 ký tự hex thường). token_hash bắt buộc khi trạng thái active.
  • Put không cho phiên bản cũ ghi đè phiên bản mới. Phiên bản 0 (từ enroll) luôn áp dụng.
  • Băm đã thu hồi được giữ làm tombstone để trả 403.
  • company_id, server_id, agent_id phải khớp ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$ (tsdb.ValidID, dùng ở Admin).

5. API Contract ​

Thành phần này không có API HTTP riêng phơi bày cho agent. Hợp đồng là: (a) interface Go nội bộ, (b) các lời gọi ra Access Hub, (c) Admin API đẩy agent (L3 Admin API).

5.1 Operations ​

Interface Registry (Go): Lookup(hash), Put(record), Revoke(agentID), Get(agentID), ByServer(company, server), DeleteCompany(company), DeleteServer(company, server), Count().

Hub client (cơ sở <hub.base_url>/api/v1/monitoring/collector, Bearer hub.token, phản hồi có thể bọc trong data, trần đọc 8 MiB, timeout mặc định 15 giây nếu không đặt):

MethodPathDùng cho
POST/enrollEnroll proxy
GET/agents?token_hash=Tra ngược một token
GET/agents?since=&limit=Đồng bộ theo trang
POST/eventsGửi sự kiện (xem L3 Alerting)
POST/agents/seenlast_seen
POST/inventoryKiểm kê
POST/heartbeatHeartbeat collector

APIError.Temporary đúng khi HTTP 429 hoặc 5xx.

5.2 Request và Response schema ​

Ký hiệu ! bắt buộc, ? tùy chọn. Hợp đồng chi tiết phía Access Hub là HUB-* (docs/06-access-hub-integration.md), API thật chưa xây, dev dùng cmd/mockhub.

AgentRecord (registry, JSON ở Admin và hub):
  agent_id!: string
  company_id!: string
  server_id!: string
  state!: "pending" | "active" | "revoked"
  token_hash?: string (64 hex thường, bắt buộc khi active)
  expires_at?: timestamp
  prev_token_hash?: string
  prev_token_expires_at?: timestamp
  version!: int >= 0

GET /agents?token_hash=  -> AgentRecord hoặc 404
GET /agents?since=&limit= -> { agents![]: AgentRecord, more!: bool, next_since?: cursor }
POST /enroll (Access Hub) -> { agent_id!, company_id!, server_id!, token_hash!, agent_token!, config! }

Nếu kết quả enroll thiếu trường, Ingest trả 503 (kết quả không đầy đủ).

Errors: ErrNotFound, ErrRevoked, ErrExpired, ErrPending, ErrHubUnavailable.

5.3 Error codes ​

Lỗi nội bộHTTP ở IngestGhi chú
ErrNotFound, ErrExpired, ErrPending401 unauthorized
ErrRevoked403 agent_revoked
ErrHubUnavailable503, Retry-After: 30Bị giới hạn hoặc Access Hub lỗi
Khác503 server_error
Enroll: hub 409409 already_enrolled hoặc token_used
Enroll: hub 422422 binding_failed
Enroll: hub 429429 rate_limited, Retry-After: 60
Enroll: lỗi tạm thời503, Retry-After: 30
Enroll: lỗi khác401

Hợp đồng mã HTTP đầy đủ ở L3 Ingest API mục 5.3.

5.4 Versioning ​

Interface Go là nội bộ, đổi được cùng mã. Hợp đồng với Access Hub: tiền tố /api/v1/monitoring/collector. Trường version của AgentRecord là đồng hồ logic chống ghi đè. Không có cơ chế tương thích N và N-1 cho hub API (đề xuất: bổ sung khi HUB-* ổn định).

5.5 Authz ​

HướngCơ chế
Collector gọi Access HubBearer hub.token (đọc từ hub.token_file hoặc AHC_HUB_TOKEN(_FILE))
Access Hub gọi Collector (đẩy agent)Qua Admin API, Bearer admin token (L3 Admin API)
Chủ thểĐọc registryGhi registryXóa theo tenant
Ingest (nội bộ)CóChỉ sau enroll thành côngKhông
Admin (qua token admin)CóCóCó
SyncerKhôngCó (từ Access Hub)Không

6. Physical Data Schema ​

6.1 Mapping ​

Redis (tiền tố ah:, cấu hình redis.prefix). Không đặt TTL cho khóa registry.

KhóaKiểuNội dung
reg:tok:{hash}hashagent_id, company_id, server_id, state, exp, ver
reg:agent:{id}hashcompany_id, server_id, state, ver, tokens (danh sách băm)
reg:srv:{co}:{s}setagent của một máy chủ
reg:co:{co}setagent của một công ty
reg:allsetmọi agent
pres:a:{agent}hashtrạng thái presence của agent
pres:up, pres:up:{co}zsetagent đang up, điểm là lần thấy cuối (ms)
pres:down, pres:down:{co}setagent đang down
pres:unrepzsetagent có lần thấy thật chưa báo hub
mermaid
flowchart TB
    classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff;

    TOK[("reg:tok:hash")]:::datastore
    AGT[("reg:agent:id")]:::datastore
    SRV[("reg:srv:co:s")]:::datastore
    CO[("reg:co:co")]:::datastore
    ALL[("reg:all")]:::datastore
    PA[("pres:a:agent")]:::datastore
    UP[("pres:up")]:::datastore
    DN[("pres:down")]:::datastore

    TOK --> AGT
    AGT --> SRV
    AGT --> CO
    AGT --> ALL
    PA --> UP
    PA --> DN

Sơ đồ cho thấy quan hệ tham chiếu giữa khóa (băm trỏ tới agent, agent nằm trong các tập chỉ mục).

Put xóa khóa token cũ không còn trong danh sách rồi cập nhật các tập. Đọc Redis lỗi được coi như ErrNotFound.

6.2 Phân loại và lưu giữ ​

Dữ liệuPhân loạiLưu giữ
reg:*Nhạy cảm vừa (băm token)Không TTL. Xóa khi đồng bộ, Admin, DeleteServer, DeleteCompany. Tombstone thu hồi giữ vô thời hạn (đề xuất: đặt chính sách dọn, AR-009)
pres:*ThấpXóa khi Remove, thu hồi, xóa máy chủ

Redis dev: AOF everysec, maxmemory 256mb, noeviction. Khi Redis đầy ghi lỗi chứ không xóa khóa âm thầm.


7. Algorithms ​

7.1 Sequences (đường thành công) ​

Tra token lần đầu (miss rồi hỏi Access Hub):

mermaid
sequenceDiagram
    participant IN as Ingest
    participant RS as Resolver
    participant RG as Registry
    participant HB as Access Hub (ext)

    IN->>RS: Lookup(băm token)
    RS->>RG: Lookup
    RG-->>RS: ErrNotFound
    RS->>RS: cache âm, limiter hub-lookup, single-flight
    RS->>HB: GET /agents?token_hash=
    HB-->>RS: AgentRecord active
    RS->>RG: Put(record)
    RS->>RG: Lookup lại
    RG-->>RS: AgentRecord
    RS-->>IN: danh tính (công ty, máy chủ)

Enroll:

mermaid
sequenceDiagram
    participant AG as Agent (ext)
    participant IN as Ingest
    participant HB as Access Hub (ext)
    participant RG as Registry

    AG->>IN: POST /agent/v1/enroll
    IN->>HB: POST /enroll (X-Forwarded-For chứa IP client, timeout 20s)
    HB-->>IN: agent, công ty, máy chủ, token băm, agent_token
    IN->>RG: Put(active, version 0)
    IN->>IN: xóa cache âm của băm token
    IN-->>AG: agent_token, collector_url, config

7.2 Sequences (đường lỗi) ​

mermaid
sequenceDiagram
    participant IN as Ingest
    participant RS as Resolver
    participant RG as Registry
    participant HB as Access Hub (ext)

    IN->>RS: Lookup(băm token)
    RS->>RG: Lookup
    alt Redis hỏng
        RG-->>RS: ErrNotFound (lỗi Redis đọc như không thấy)
        RS->>HB: GET /agents?token_hash=
    else thu hồi
        RG-->>RS: ErrRevoked
        RS-->>IN: ErrRevoked (403)
    end
    alt Access Hub không có token
        HB-->>RS: 404
        RS->>RS: ghi cache âm 60 giây
        RS-->>IN: ErrNotFound (401)
    else Access Hub lỗi hoặc bị giới hạn
        HB--)RS: lỗi hoặc không gọi được
        RS-->>IN: ErrHubUnavailable (503)
    end

7.3 State machines ​

mermaid
stateDiagram-v2
    direction LR
    state "Chờ (pending)" as PEND
    state "Hoạt động (active)" as ACT
    state "Thu hồi (revoked)" as REV
    [*] --> PEND
    PEND --> ACT: Put active
    ACT --> ACT: Put phiên bản mới hơn
    ACT --> REV: Revoke hoặc Put revoked
    PEND --> REV: Revoke
    REV --> [*]: DeleteServer hoặc DeleteCompany
    ACT --> [*]: DeleteServer hoặc DeleteCompany
mermaid
stateDiagram-v2
    direction LR
    state "Up" as UP
    state "Down" as DOWN
    [*] --> UP: Ensure (gieo ân hạn)
    UP --> UP: Touch
    UP --> DOWN: quá hạn, SADD pres:down trả 1
    DOWN --> UP: Touch
    UP --> [*]: Remove
    DOWN --> [*]: Remove
Chuyển trạng tháiSequence
pending đến activeEnroll (7.1) hoặc Syncer kéo bản ghi active
active đến revokedAdmin DELETE /agents/{id} hoặc Syncer nhận bản ghi revoked
Up đến DownL3 Alerting mục 7.1
Down đến UpTouch từ Ingest rồi OnAgentUp

Trạng thái pending tồn tại trong mô hình; Ingest luôn coi là 401.

7.4 Core algorithms ​

Vấn đềGiải phápTrade-off
Bão miss khi Redis rỗng hoặc token giảCache âm 60 giây (tối đa 100.000), limiter toàn cục hub-lookup 50/giây, single-flight theo bămToken mới hợp lệ có thể bị từ chối tạm (503) khi quá tải tra ngược
Ghi chồng bản cũSo sánh Version, bỏ bản cũ hơnPhụ thuộc Access Hub tăng version đúng
Token xoay chồng lấnprev_token_hash có hạn; thiếu hoặc sai hạn tính là đã hết (Unix(1,0))Hạn sai làm token cũ chết sớm (an toàn hơn giữ quá hạn)
Hai tiến trình cùng thấy agent quá hạnSADD pres:down trả 1 thì sở hữu chuyển trạng tháiNếu tiến trình chết sau SADD, sự kiện có thể mất (AR-001 của L2)
Báo last_seen một lầnDrainSeen dùng ZPOPMIN nên mỗi mục đến một bên gọiNếu gửi hub lỗi sau khi rút, mục đó không báo lại cho đến lần Touch sau
Khởi động lại, ân hạnEnsure gieo thời gian làm hạt giống, Real=false không báo hubAgent chưa từng báo cáo vẫn bị coi down sau ngưỡng
Lỗi RedisĐọc lỗi thành ErrNotFound để rơi về Access HubChe lỗi Redis (đã có metric ahc_registry_lookups_total{result} để thấy)
Bản ghi mồ côiĐồng bộ và Admin xóa theo phạm viKhông TTL nên cần chính sách dọn (AR-009)

Quét presence theo lô 500 mỗi lượt.


8. Error Handling ​

8.1 Bảng lỗi theo bước ​

Bước lỗiNguyên nhânCơ chế xử lýTrạng thái cuối
Tra RedisMất kết nối, timeout 500 msThành ErrNotFound, rơi về Access HubBản ghi nạp lại sau khi hub trả lời
Tra Access HubTimeout 10 giây, 5xxErrHubUnavailable, không ghi cache âmIngest 503, agent thử lại
Tra Access Hub404Ghi cache âm 60 giây401
Tra Access HubBị limiter chặnErrHubUnavailable503 Retry-After: 30
EnrollAccess Hub 409, 422, 429Ánh xạ mã lỗi (mục 5.3)Agent nhận mã lỗi, exit code 5 ở phía agent
EnrollAccess Hub timeout, 5xx503Agent thử lại (exit code 6)
EnrollKết quả thiếu trường503Agent thử lại
EnrollKhông có hub503
Đồng bộAccess Hub lỗiGhi log, thử lại chu kỳ sau; không xóa gìRegistry cũ tiếp tục dùng
Put RedisLỗi ghiTrả lỗi cho người gọiEnroll trả lỗi, Admin trả 503
PresenceLỗi RedisTrả lỗi cho người gọiXem L3 Alerting

8.2 Fail-fast ​

  • hub.token bắt buộc khi có hub.base_url (lỗi cấu hình, thoát mã 3).
  • Redis là bắt buộc khi tách vai trò (TestSplitRolesNeedRedis).
  • TestNewRedisRequiresClients, TestNewValidatesOptions kiểm tra tham số khởi tạo.

8.3 Race conditions ​

Tình huốngXử lý
Hai request cùng token missSingle-flight
Put chồng nhau (Syncer và Admin)So sánh Version
Hai tiến trình quét presenceSADD trả 1 chọn một bên
Thu hồi đồng thời với traTombstone giữ băm; cache nút có thể còn bản cũ tối đa cache_ttl (15 giây)

9. Degradation ​

Nguyên tắc: mất Access Hub hay Redis không được biến thành "token sai". Nếu không xác định được thì từ chối tạm (503) thay vì từ chối xác thực (401).

9.1 Dependency matrix ​

Dependency lỗiHành viKiểu suy thoáiHệ quả
Access HubTra bằng registry và cache; enroll 503Giảm chức năngAgent mới không enroll được
RedisRegistry đọc lỗi rơi về Access Hub có giới hạn; presence không cập nhật đượcGiảm chức năngTăng tải Access Hub; Detector mù trong lúc Redis mất (AR-005 của L2)
Cả Redis và Access HubMọi token chưa có trong cache nút bị 503Từ chốiAgent đệm WAL

9.2 Backup và recovery ​

Registry dựng lại được: từ Access Hub qua Syncer (kéo since từ 0) hoặc dần qua tra ngược. RTO khởi tạo lại registry rỗng: chưa đo; đề xuất vài phút với 10.000 agent ở 500 mỗi trang (20 trang). Presence dựng lại bằng Ensure và các lần Touch. RPO không áp dụng cho registry (nguồn sự thật ở Access Hub). Redis dev có AOF everysec và replica. Sao lưu Redis riêng: chưa có.


10. Concurrency ​

10.1 Ranh giới giao dịch ​

Mỗi Put Redis cập nhật nhiều khóa (token, agent, các tập) không nằm trong một giao dịch nguyên tử theo như mã đã đọc. Đề xuất xác minh: nếu có MULTI hoặc pipeline thì ghi lại. Hệ quả tiềm ẩn: một lượt Put dở dang có thể để khóa chỉ mục lệch tạm thời, lần Put sau sửa lại.

10.2 Cơ chế ​

Cơ chếMô tảMối nguy
Single-flight theo bămGộp tra ngược trùngLỗi của một lần gọi lan tới mọi người chờ
Limiter hub-lookupToken bucket toàn nútTải hợp lệ bị 503 khi tăng vọt
So sánh VersionLoại bản ghi cũPhiên bản không tăng đơn điệu làm mất cập nhật
SADD pres:downChọn chủ chuyển downMất sự kiện nếu chủ chết ngay sau
ZPOPMIN (DrainSeen)Mỗi mục một bên nhậnMất mục nếu báo hub lỗi
Hook OnUpsert, OnRevoke, OnDelete (bộ nhớ)Dọn series limiter và presenceHook chạy trong khóa, phải nhanh

11. Security ​

11.1 Ba lớp ​

LớpBiện pháp
Truyền thôngCollector đến Access Hub dùng HTTPS (triển khai) và Bearer hub.token; Redis có mật khẩu và TLS tùy chọn (redis.tls, tls_ca_file)
MãToken chỉ dạng băm SHA-256; company_id bắt buộc ở mọi thao tác theo phạm vi; ID kiểm tra định dạng; client Redis chặn lệnh nguy hiểm
Dữ liệuKhông lưu token rõ; hub.token và mật khẩu Redis đọc từ tệp hoặc biến môi trường, không có trong YAML trong repo

11.2 Pipeline phân quyền năm bước (áp dụng khi ingest tra token) ​

  1. Băm token.
  2. Tra registry (Redis rồi cache nút).
  3. Nếu miss: cache âm, limiter, single-flight, tra Access Hub.
  4. Kiểm tra trạng thái và hạn (active, chưa hết hạn; prev_token còn hạn).
  5. Trả AgentRecord; Ingest gắn company_id, server_id từ bản ghi này vào dữ liệu.

11.3 Chỉ mục neo ​

Nguyên tắc L2Biện pháp nội bộ
P1 danh tính từ registryBước 5, bản ghi chỉ ghi qua enroll thành công, Syncer hoặc Admin
9.1 token mờBăm SHA-256, tombstone thu hồi
9.3 secretshub.token, redis.password từ tệp hoặc biến môi trường
NFR-12 cô lập tenantByServer(company, server), chỉ mục reg:co:{co}

Rủi ro còn mở: License đi qua Collector (proxy) nên log phải che; Collector không ghi License ở log (đề xuất rà soát internal/ingest/handlers.go và hubclient).


12. Configuration ​

12.1 Tunables ​

Biến môi trường tương ứng: AHC_HUB_URL, AHC_HUB_TOKEN(_FILE), AHC_REDIS_ADDR, AHC_REDIS_USERNAME, AHC_REDIS_PASSWORD(_FILE), AHC_REDIS_PREFIX, AHC_REDIS_DB, AHC_REDIS_TLS, AHC_REDIS_TLS_CA_FILE. Các khóa còn lại chỉ YAML.

Tham sốDefaultÝ nghĩa và tác độngMục liên quan
hub.base_urlrỗngĐịa chỉ Access Hub. Rỗng thì không có tra ngược, enroll trả 5037.1
hub.token (hoặc token_file)rỗngBắt buộc khi có base_url11
hub.timeout10 giâyTimeout gọi Access Hub9
hub.sync_interval1 phútChu kỳ Syncer7.1
hub.lookup_per_second50Trần tra ngược toàn nút7.4
redis.addrrỗngRỗng thì registry và presence dùng bộ nhớ3.1
redis.prefixah:Tiền tố khóa6.1
redis.db0DB Redis6.1
redis.cache_ttl15 giâyTTL cache nút của registry8.3
redis.tls, tls.ca_filetắtTLS tới Redis11.1

Hằng số cố định trong mã (không cấu hình được): cache âm 60 giây tối đa 100.000 mục, cache nút tối đa 200.000 mục, trang đồng bộ 500, lô quét presence 500, timeout enroll 20 giây, trần phản hồi hub 8 MiB, timeout mặc định client hub 15 giây.

12.2 Feature flags ​

Không có. redis.addr rỗng là công tắc chọn hiện thực bộ nhớ thay Redis (chỉ hợp lý cho một tiến trình, dev đơn giản).


13. Telemetry ​

13.1 Metrics ​

TênKiểuNhãnNgữ nghĩa
ahc_registry_lookups_totalcounterresultKết quả tra token
ahc_registry_agentsgaugeSố agent trong registry (từ Count)
ahc_ingest_enroll_totalcounterresultKết quả enroll (xem L3 Ingest)
ahc_agents_downgaugeAgent đang down (xem L3 Alerting)

Giá trị nhãn result của ahc_registry_lookups_total: lấy từ mã khi rà soát. docs/10 liệt kê mem, redis, hub, miss nhưng chưa xác minh với mã.

13.2 Log schema ​

JSON một dòng: time, level, msg, request_id, collector_id. Sự kiện đáng ghi: đồng bộ registry (số bản ghi, trang), lỗi tra Access Hub, enroll thất bại (không kèm token). Không ghi băm token đầy đủ (đề xuất: chỉ 8 ký tự đầu nếu cần).

13.3 Cảnh báo đến runbook ​

Chưa có luật. Đề xuất: cảnh báo khi ahc_registry_lookups_total miss tăng mạnh (Redis rỗng hoặc tấn công), khi Syncer không chạy thành công quá 10 phút, khi ahc_registry_agents giảm đột ngột. Runbook: docs/09 mục 6.

13.4 Probes ​

ProbeĐiểm cuốiÝ nghĩa
Startupcollector -check trong ExecStartPreCấu hình hợp lệ
Liveness/healthzTiến trình sống
Readiness/readyz ("redis")Redis trả lời trong 2 giây

13.5 Trace propagation ​

X-Request-Id từ Ingest; hub client chuyển X-Forwarded-For với IP client khi enroll. Chưa có trace phân tán.


14. Test Plan ​

LoạiKiểm thửVị trí
Đơn vị RegistryTestLookupFlow, TestRevokeKeepsTombstone, TestPutRevokedStateTombstones, TestPendingAndExpiredTokens, TestTokenRotationKeepsPreviousUntilExpiry, TestStaleVersionIgnored, TestDeleteScopes, TestByServerIsCompanyScopedinternal/registry/registry_test.go
Đơn vị ResolverTestResolveFallsBackToHubAndCaches, TestNegativeCache, TestHubDownIsNotAnInvalidToken, TestGlobalLimiterProtectsHub, TestConcurrentMissesCollapse, TestSyncerPagesAndAdvances, TestFromRecordPreviousTokeninternal/registry/resolver_test.go
Hợp đồng RedisTestRedisRegistryContractFake, TestRedisRegistryContractLive (Redis thật tùy chọn), TestRedisRegistryDownFallsBackToNotFoundinternal/registry/redis_test.go
Hub clientTestEnrollPassesIPAndErrors, TestSeenAndInventory, TestTemporaryErrors, TestBadCollectorTokenRejectedinternal/hubclient/hubclient_test.go
PresenceTestMemoryTracker, TestRedisTracker, TestRedisTrackerSharedAcrossProcesses, TestRedisTrackerCleansUp, TestEnsureSeedsGraceAndIsNotReported, TestTouchRecoversDownAgent, TestDrainSeenOnlyReportsProgress, TestLastSeenNeverGoesBackwards, TestCountsAndRemoveinternal/presence
Enroll (Ingest)TestEnrollProxy, TestEnrollErrors, TestEnrollRateLimitPerIP, TestEnrollWithoutHubinternal/ingest/ingest_test.go
Tích hợpTestSplitProcessesShareStateThroughRedisinternal/app/events_test.go hoặc app_test.go
Chưa cóKiểm thử hợp đồng với API Access Hub thật (HUB-*), đo tải tra ngược, kiểm tra dọn tombstone

TestRedisRegistryContractLive chỉ chạy khi có Redis thật qua biến môi trường kiểm thử. Bản nháp này không chạy kiểm thử để tránh đụng Redis 6380.


15. Implementation Sequence ​

15.1 Milestone matrix ​

MilestoneNội dungPhụ thuộcĐóng góp nghiệm thu
COL-3Registry bộ nhớ và Redis, Resolver, giới hạn, lần thấy cuốiCOL-1AC-REG-01 đến 06, 08
COL-4Enroll proxy, Syncer, thu hồiCOL-3AC-REG-05
COL-RPresence Redis, SADD một chủ, tách vai tròCOL-3AC-REG-07
COL-14 (chưa làm)Redis HA, cache nhiều tầng cho registryCOL-RCải thiện AR-005
Renew (chưa làm)prev_token_hash thật từ Access HubHUB-*FR-REG-04 ở mức đầu cuối
HUB-* (chưa xây)API Access Hub thật thay mockhubHợp đồng đầu cuối

15.2 Dependency flowchart ​

mermaid
flowchart LR
    C3["COL-3 registry, resolver"] --> C4["COL-4 enroll, syncer"]
    C3 --> CR["COL-R presence Redis"]
    C4 --> HUBX["HUB-* API thật"]
    CR --> C14["COL-14 Redis HA"]
    HUBX --> RN["renew thật"]

Appendix A. Open Questions ​

#Câu hỏiHành vi tạm thờiOwnerMã theo dõi
1Chính sách dọn registry và presence không TTL (tombstone, agent mồ côi)Chỉ xóa qua đồng bộ và AdminChưa chỉ địnhOQ-REG-1
2Put Redis có nguyên tử không, hay cần MULTI?Chưa xác minhChưa chỉ địnhOQ-REG-2
3Giá trị nhãn result của ahc_registry_lookups_totalTheo mãChưa chỉ địnhOQ-REG-3
4Hợp đồng GET /agents và /enroll thật của Access Hub (HUB-*)MockhubChưa chỉ địnhOQ-REG-4
5Thu hồi có cần đẩy (push) tức thời thay vì chờ tối đa cache_ttl?Admin đẩy, cache nút 15 giâyChưa chỉ địnhOQ-REG-5
6Log enroll có bảo đảm không chứa License?Đề xuất rà soátChưa chỉ địnhOQ-REG-6

Appendix B. ADR nội bộ ​

Mã ADRQuyết địnhTrạng tháiDriver
ADR 0007Token mờ, đồng bộ registry, tra ngượcChấp nhậnThu hồi nhanh
ADR 0004Access Hub là nguồn sự thậtChấp nhậnTách control và data plane
ADR 0012 (đề xuất)Redis dùng chung cho trạng thái nóngĐề xuấtCOL-R
ADR nội bộ (đề xuất)Lỗi Redis đọc thành ErrNotFound để rơi về Access HubĐề xuất, là hành vi mãGiữ ingest sống khi Redis hỏng
ADR nội bộ (đề xuất)Registry không TTL, dọn bằng đồng bộĐề xuất, là hành vi mãTránh mất danh tính khi mất Access Hub lâu

Appendix C. Section Profile ​

Phân mụcHồ sơTrạng thái điềnGiải trình
0 đến 4Bắt buộcĐã điền
5 API contractBắt buộcĐiền theo interface nội bộKhông có API HTTP riêng; hợp đồng hub là HUB-* chưa xây
6 Physical dataBắt buộcĐã điềnBảng khóa Redis
7.1, 7.2, 7.3, 7.4Bắt buộcĐã điền
8, 9, 10, 11Bắt buộcĐã điềnMục 10.1 cần xác minh tính nguyên tử
12, 13Bắt buộcĐã điềnNhãn result cần xác minh
14Bắt buộcĐã điềnKiểm thử chưa chạy trong bản nháp
15Bắt buộcĐã điền
Trang này có giúp được bạn không?
Sửa trang này

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