Operations
1. Triển khai
| Thuộc tính | Giá trị |
|---|---|
| Image | registry/licensing:<tag> |
| Container Port | 3000 (ngoài 31120) |
| Base Path | /v1/api/licensing |
| Snowflake worker | 11 |
| Run modes | RUN_MODE=startup (mặc định), RUN_MODE=migrate |
| Migration mode | bun run migrate / migrate:dev — seed permissions + Policy free-trial (tất cả alwaysRun) |
| Probes | GET /healthz (live), GET /readyz (ready) — mặc định IGNIS |
| Phụ thuộc lúc boot | PostgreSQL (schema licensing), Redis cache, Identity JWKS |
Traefik labels
yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.licensing.rule=PathPrefix(`/v1/api/licensing`)"
- "traefik.http.services.licensing.loadbalancer.server.port=3000"Script build & run
| Script | Mục đích |
|---|---|
bun run rebuild | Clean + compile (luôn dùng cái này; prestart/pretest/premigrate nối vào nó) |
bun run server:dev | Dev server (NODE_ENV=development, .env.development) |
bun run migrate:dev | Chạy migrations / seeds |
bun run test | Tests (cần .env.test) |
bun run lint:fix | ESLint + Prettier |
2. Observability
| Tín hiệu | Nguồn | Xem ở đâu |
|---|---|---|
| Logs | stdout (key-value có cấu trúc, key: %s) | kubectl logs <pod> / Loki |
| Health | GET /healthz, GET /readyz | Portal gateway |
| Audit trail | bảng licensing.LicenseEvent | chỉ query DB — không endpoint REST nào đăng ký (RestPaths.LICENSE_EVENTS tồn tại nhưng chưa wire) |
Trường log & tín hiệu chính
| Trường / message | Nguồn | Ghi chú |
|---|---|---|
requestId | header X-Request-Id | Lan truyền xuyên service |
Ed25519 private key not configured | warn publishCertificate() | Cert đang bị bỏ qua — consumer thấy null |
Application secret not configured | warn publishCertificate() | Như trên |
Failed to update lastValidatedAt | error validate() | Không nghiêm trọng (fire-and-forget) |
Failed to verify license certificate | LicenseMiddleware (phía consumer) | Cert sai/hết hạn/bị sửa → fail-open null |
Loại sự kiện audit (LicenseEventTypes)
created · activated · deactivated · suspended · reinstated · renewed · expired · revoked. (Không validated, không heartbeat.)
3. Bảo mật
| Mối quan tâm | Giảm thiểu |
|---|---|
| AuthN | JWT (ES256, JWKS từ identity) hoặc HTTP Basic |
| AuthZ | Casbin qua PolicyDefinition; permission seed bởi migration 0001/0003/0004. Route read License tạm authorize.skip — xem ADR-0003 |
| Khoá ký cert | Private key Ed25519 trong APP_ENV_LICENSING_ED25519_PRIVATE_KEY — K8s Secret, không bao giờ trong code |
| Secret payload cert | APP_ENV_APPLICATION_SECRET (AES-256-GCM) — phải khớp giữa signer và mọi verifier |
| Chống giả mạo cert | Chữ ký Ed25519; verifier từ chối alg lạ và chữ ký sai |
| Hết hạn cert | certExpiresAt trong payload + Redis EX — cả hai giới hạn bởi APP_ENV_LICENSING_CERT_TTL_SECONDS |
| Secrets | K8s Secret, mount như env |
| TLS | Kết thúc tại gateway |
| Network policy | Cilium — chỉ cho gateway + Redis + Postgres + JWKS identity |
Tin cậy fail-open:
LicenseMiddlewarecoi Redis miss / xác minh thất bại / hết hạn lànull("unknown"), không phải "unlicensed". Cổng consumer phải quyết định rõ ràng cách xử lýnull.
4. Runbook
4.1 Lớp cảnh báo
| Cảnh báo | Kích hoạt | Kiểm tra | Khắc phục | Leo thang |
|---|---|---|---|---|
licensingHighErrorRate | 5xx >5% trong 5m | logs level=error | Kiểm kết nối DB / Redis | backend on-call |
licensingCertsSkipped | warn key not configured lặp lại | xác nhận env Ed25519 + app-secret đã đặt | khôi phục secret, re-publish (xem 4.2) | backend on-call |
licensingExpiryDrift | license qua expiresAt vẫn ACTIVATED | kỳ vọng — hết hạn là lazy | trigger validate, hoặc chạy quét thủ công | doc — thường không phải sự cố |
4.2 Thao tác thường gặp
| Thao tác | Cách làm |
|---|---|
| Tail logs | kubectl logs -n <ns> -f deploy/licensing |
| Chạy migration / seed | RUN_MODE=migrate bun run migrate (trong pod) |
| Ép làm tươi cert cache | Không có endpoint riêng. Trigger bất kỳ hành động vòng đời nào re-publish: suspend → reinstate, hoặc renew. CRUD PATCH /licenses/{id} và sửa feature không re-publish. |
| Xem audit trail | Query trực tiếp licensing.LicenseEvent (không bề mặt REST) |
| Giải phóng một seat activation | DELETE /activations/{id} (không sự kiện audit) hoặc ActivationService.deactivate() (ghi sự kiện) |
4.3 Xoay keypair Ed25519
Xoay public key trên consumer trước, rồi private key trên licensing, rồi re-publish. Xoay private key mà không re-publish để lại cert cũ không xác minh được (consumer fail-open về
null).
4.4 Lưu ý đã biết (không phải bug)
| Hành vi | Chi tiết |
|---|---|
| Lazy expiry | Không reaper. Một license lật sang EXPIRED chỉ ở lần validate() tiếp theo. License không ai validate ở ACTIVATED quá hạn. |
| Không heartbeat / floating seat | IActivationConfig = { limit } thôi; không lastHeartbeatAt, không /validation/heartbeat. |
| Sửa feature không lan toả | validate() đọc DB tươi (tức thì), nhưng cert cache chỉ làm tươi khi có hành động vòng đời. |
| Bypass activation trực tiếp | POST /activations bỏ qua kiểm giới hạn có lock; ưu tiên validate(fingerprint) để cưỡng chế giới hạn. |
Va chạm SVC-00110 | Service code đụng với @nx/outreach — xem ADR-0002. |
4.5 Yêu cầu consumer xuyên service
Mỗi service verifier phải đặt, khớp với signer:
| Biến | Yêu cầu |
|---|---|
APP_ENV_APPLICATION_SECRET | Cùng giá trị với licensing (secret AES-256-GCM) |
APP_ENV_LICENSING_ED25519_PUBLIC_KEY | Public key Ed25519 khớp (PEM) |
5. Trang liên quan
- Configuration
- Certificate System
/runbook/— runbook trung tâm cho sự cố xuyên service- Decisions