feat: harden firewall policies telemetry and operations
quality / changes (push) Successful in 7s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 30s
quality / web (push) Successful in 1m3s
quality / api (push) Successful in 1m15s
quality / linux-agent-smoke (push) Failing after 27s
quality / docker-check (push) Failing after 2m28s
CD / quality (push) Failing after 5m52s
CD / publish (push) Skipped

Implement reliable agent apply, telemetry v2, analytics retention, guarded rollouts, enrollment, labels and webhooks. Improve ReUI frontend data safety and bounded list loading, and add Docker smoke, recovery and resource checks. Document local verification and pending native acceptance.
This commit is contained in:
Denozordec committed 2026-10-26 13:10:58 +07:00
1 parent aa475848fd
commit 9fa6269174
242 files changed
+19872 -3057

No files matched your search

+214 -39
View File
@@ -12,6 +12,98 @@ servers:
description: Local API
security: []
paths:
/v1/agent/credentials:
$ref: './openapi.schemas.json#/paths/~1v1~1agent~1credentials'
/v1/agent/token-rotation:
$ref: './openapi.schemas.json#/paths/~1v1~1agent~1token-rotation'
/api/v1/agents/{id}/token-rotation:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1agents~1{id}~1token-rotation'
/api/v1/policies/snapshots:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1policies~1snapshots'
/api/v1/policies/snapshots/{id}:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1policies~1snapshots~1{id}'
/api/v1/policies/snapshots/{id}/diff:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1policies~1snapshots~1{id}~1diff'
/api/v1/policies/rollouts:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1policies~1rollouts'
/api/v1/policies/rollouts/{id}:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1policies~1rollouts~1{id}'
/api/v1/policies/rollouts/{id}/confirm:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1policies~1rollouts~1{id}~1confirm'
/api/v1/policies/rollouts/{id}/rollback:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1policies~1rollouts~1{id}~1rollback'
/api/v1/agents/{id}/policy-pin/release:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1agents~1{id}~1policy-pin~1release'
/api/v1/agent-labels:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1agent-labels'
/api/v1/agent-labels/{id}:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1agent-labels~1{id}'
/api/v1/agents/{id}/labels:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1agents~1{id}~1labels'
/api/v1/agent-labels/preview-assignment:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1agent-labels~1preview-assignment'
/api/v1/agent-labels/assign-policy-sets:
$ref: './openapi.schemas.json#/paths/~1api~1v1~1agent-labels~1assign-policy-sets'
/api/v1/webhooks:
get:
summary: List webhook endpoints and bounded queue counts (settings admin)
tags: [ops]
security: [{ bearerAuth: [] }]
responses:
'200':
description: Endpoint metadata without secrets, configured flag, limits and queue counts
'403':
description: Settings administrator required
post:
summary: Create webhook endpoint (at most 20, settings admin)
tags: [ops]
security: [{ bearerAuth: [] }]
requestBody:
required: true
content:
application/json:
schema:
$ref: './openapi.schemas.json#/components/schemas/CreateWebhook'
responses:
'201':
description: Endpoint metadata with secret_configured; secret never returned
'503':
description: EVOFW_SECRET_KEY missing or shorter than 32 characters
/api/v1/webhooks/{id}:
parameters:
- $ref: '#/components/parameters/Id'
patch:
summary: Update webhook endpoint; changed destination, signing secret or events cancel pending delivery
tags: [ops]
security: [{ bearerAuth: [] }]
requestBody:
required: true
content:
application/json:
schema:
$ref: './openapi.schemas.json#/components/schemas/PatchWebhook'
responses:
'200':
description: Updated endpoint metadata without secret
'503':
description: EVOFW_SECRET_KEY unavailable
delete:
summary: Delete endpoint and cancel queued deliveries (settings admin)
tags: [ops]
security: [{ bearerAuth: [] }]
responses:
'200':
description: Deleted
/api/v1/webhooks/{id}/deliveries:
parameters:
- $ref: '#/components/parameters/Id'
get:
summary: Last 100 pending or terminal deliveries, with sanitized error codes (settings admin)
tags: [ops]
security: [{ bearerAuth: [] }]
responses:
'200':
description: Items and total count, excluding payload bytes and signing secrets
/health:
get:
summary: Liveness
@@ -20,6 +112,42 @@ paths:
'200':
description: OK
/ready:
get:
summary: SQLite-backed readiness
tags: [system]
responses:
'200':
description: Ready
'503':
description: Database unavailable
/api/v1/diagnostics:
get:
summary: Bounded runtime and delivery queue diagnostics
description: Requires fw:settings:admin. Returns DB/WAL sizes, filesystem capacity, audit outbox and limiter counters, RSS/heap and uptime. Memory databases and unavailable filesystem metrics return null; no credentials or queued payloads are returned.
tags: [ops]
security: [{ bearerAuth: [] }]
responses:
'200':
description: Runtime diagnostics
content:
application/json:
schema:
type: object
required: [database, audit_outbox, limiter, process]
properties:
database: { type: object, additionalProperties: true }
audit_outbox: { type: object, additionalProperties: true }
limiter: { type: object, additionalProperties: true }
process: { type: object, additionalProperties: true }
'401':
description: Authentication required
'403':
description: Missing settings administrator permission
'429':
description: Authenticated user request limit exceeded
/api/v1/dashboard:
get:
summary: Dashboard KPI
@@ -29,6 +157,37 @@ paths:
'200':
description: Stats
/api/v1/ui/summary:
get:
summary: Permission-filtered navigation and dashboard summary
tags: [ops]
security: [{ bearerAuth: [] }]
responses:
'200':
description: Only sections visible to the authenticated user are included
content:
application/json:
schema:
$ref: './openapi.schemas.json#/components/schemas/UiSummary'
/api/v1/stats/timeseries:
get:
summary: Coverage-aware fleet or agent packet series
tags: [ops]
security: [{ bearerAuth: [] }]
parameters:
- { name: from, in: query, schema: { type: string, format: date-time } }
- { name: to, in: query, schema: { type: string, format: date-time } }
- { name: resolution, in: query, schema: { type: string, enum: [1m, 1h, 1d] } }
- { name: agent_ids, in: query, description: Comma-separated agent IDs (at most 100), schema: { type: string } }
responses:
'200':
description: At most 1440 points and 100 agents; unknown counters are null
content:
application/json:
schema:
$ref: './openapi.schemas.json#/components/schemas/TimeseriesResponse'
/api/v1/install-context:
get:
summary: Public install base URL + enroll seed hint
@@ -229,6 +388,10 @@ paths:
parameters:
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Offset'
- name: view
in: query
description: Select summary to omit expanded source configuration and entries
schema: { type: string, enum: [summary] }
responses:
'200':
description: Lists (items + total)
@@ -242,14 +405,30 @@ paths:
/api/v1/lists/{id}:
get:
summary: List detail + entries
summary: List detail; use view=page for bounded entries and whole-list filtering
tags: [lists]
security: [{ bearerAuth: [] }]
parameters:
- $ref: '#/components/parameters/Id'
- { name: view, in: query, schema: { type: string, enum: [page] } }
- { name: limit, in: query, description: Requires view=page, schema: { type: integer, minimum: 1, maximum: 200, default: 100 } }
- { name: offset, in: query, description: Requires view=page, schema: { type: integer, minimum: 0, maximum: 10000000, default: 0 } }
- { name: q, in: query, description: Whole-list text search before pagination; requires view=page, schema: { type: string, maxLength: 256 } }
- name: filters
in: query
description: >-
JSON array (at most 10 filters), evaluated over the whole list before paging.
Each filter has field value or kind, operator is/is_not/is_any_of/is_not_any_of/
contains/not_contains/starts_with/ends_with/empty/not_empty and values array
(at most 20 strings of at most 256 characters). Requires view=page.
schema: { type: string }
responses:
'200':
description: Detail
description: >-
Default retains full legacy detail. view=page returns metadata plus total,
limit, offset, entries and items (at most limit). Each item has kind,
value, list_name, resolved_count, resolved_cidrs and resolved_truncated;
nested resolved previews are bounded. Invalid page/filter input returns 400.
delete:
summary: Delete list
tags: [lists]
@@ -278,15 +457,23 @@ paths:
security: [{ bearerAuth: [] }]
parameters:
- $ref: '#/components/parameters/Id'
- name: return
in: query
schema: { type: string, enum: [summary] }
description: Opt in to bounded count/content_hash/changed response instead of expanded detail
responses:
'200':
description: Updated
description: Updated; return=summary uses ListImportSummary from generated contracts
delete:
summary: Delete entry
tags: [lists]
security: [{ bearerAuth: [] }]
parameters:
- $ref: '#/components/parameters/Id'
- name: return
in: query
schema: { type: string, enum: [summary] }
description: Opt in to bounded count/content_hash/changed response instead of expanded detail
responses:
'200':
description: Deleted
@@ -748,11 +935,28 @@ paths:
/v1/agent/enroll:
post:
summary: Enroll agent (X-EvoFW-Seed)
summary: Enroll with single-use install-link credential (legacy global seed refused)
tags: [agent-public]
parameters:
- name: X-EvoFW-Enrollment-Credential
in: header
required: true
schema: { type: string }
requestBody:
required: true
content:
application/json:
schema:
$ref: './openapi.schemas.json#/components/schemas/EnrollWithCredential'
responses:
'200':
description: Exact consumed enrollment retry; same client; no-store
'201':
description: Pending agent
description: Newly enrolled pending agent; no-store
'401':
description: Scoped enrollment credential required
'409':
description: Credential already consumed with different enrollment
/v1/agent/policy:
get:
@@ -762,6 +966,10 @@ paths:
responses:
'200':
description: Policy (includes script_sha256 for Linux self-update; not part of policy.hash)
content:
application/json:
schema:
$ref: './openapi.schemas.json#/components/schemas/AgentPolicy'
/v1/agent/policy.rsc:
get:
@@ -791,40 +999,7 @@ paths:
content:
application/json:
schema:
type: object
required: [status]
properties:
status: { type: string }
prefix_count: { type: integer }
packets_dropped: { type: integer }
packets_accepted: { type: integer }
kernel_method: { type: string }
error: { type: string }
source: { type: string }
ip_hits:
type: array
maxItems: 200
description: Linux per-element drop counters (packets > 0)
items:
type: object
required: [ip, packets]
properties:
ip: { type: string, maxLength: 64 }
packets: { type: integer, minimum: 0 }
port_hits:
type: array
maxItems: 500
description: Linux nft deny_port_hits (ip × proto × dport, packets > 0)
items:
type: object
required: [ip, port, protocol, packets]
properties:
ip: { type: string, maxLength: 64 }
port: { type: integer, minimum: 1, maximum: 65535 }
protocol: { type: string, enum: [tcp, udp] }
packets: { type: integer, minimum: 0 }
host_firewall:
$ref: '#/components/schemas/HostFirewallPayload'
$ref: './openapi.schemas.json#/components/schemas/ApplyReport'
responses:
'200':
description: OK