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.
1143 lines
35 KiB
YAML
1143 lines
35 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: EvoFirewall API
|
|
version: 0.2.0
|
|
description: |
|
|
Centralized firewall control plane.
|
|
|
|
**HTTP SoT:** this OpenAPI document.
|
|
**Runtime validation:** Zod schemas in `@evofw/shared` (authoritative for request bodies).
|
|
servers:
|
|
- url: http://localhost:8080
|
|
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
|
|
tags: [system]
|
|
responses:
|
|
'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
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'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
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: Install context
|
|
|
|
/api/v1/install-links:
|
|
get:
|
|
summary: List install links
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Limit'
|
|
- $ref: '#/components/parameters/Offset'
|
|
responses:
|
|
'200':
|
|
description: Links (items + total)
|
|
post:
|
|
summary: Create install link + invited agent
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
|
|
/api/v1/install-links/{id}:
|
|
delete:
|
|
summary: Revoke install link
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Revoked
|
|
|
|
/api/v1/agents:
|
|
get:
|
|
summary: List agents
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Limit'
|
|
- $ref: '#/components/parameters/Offset'
|
|
responses:
|
|
'200':
|
|
description: Agents (items + total)
|
|
|
|
/api/v1/agents/approve-bulk:
|
|
post:
|
|
summary: Approve many pending/invited agents in one request
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [agent_ids]
|
|
properties:
|
|
agent_ids:
|
|
type: array
|
|
maxItems: 1000
|
|
items: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Approved agents (skips non-pending/invited)
|
|
|
|
/api/v1/agents/{id}:
|
|
get:
|
|
summary: Get agent
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Agent
|
|
patch:
|
|
summary: Update agent
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Updated
|
|
delete:
|
|
summary: Delete agent
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Deleted
|
|
|
|
/api/v1/agents/{id}/approve:
|
|
post:
|
|
summary: Approve pending/invited agent
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Approved
|
|
|
|
/api/v1/agents/{id}/revoke:
|
|
post:
|
|
summary: Revoke agent
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Revoked
|
|
|
|
/api/v1/agents/{id}/preview:
|
|
get:
|
|
summary: Policy preview (truncated CIDRs)
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
- name: limit_cidrs
|
|
in: query
|
|
schema: { type: integer, default: 50, maximum: 5000 }
|
|
responses:
|
|
'200':
|
|
description: Preview
|
|
|
|
/api/v1/agents/{id}/policy-sets:
|
|
get:
|
|
summary: Assigned policy sets
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Sets
|
|
put:
|
|
summary: Replace assigned policy sets (order = sort)
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Updated
|
|
|
|
/api/v1/agents/{id}/overrides:
|
|
get:
|
|
summary: List IP overrides
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Overrides
|
|
post:
|
|
summary: Create override
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Created
|
|
|
|
/api/v1/agents/{id}/overrides/{overrideId}:
|
|
delete:
|
|
summary: Delete override
|
|
tags: [agents]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
- name: overrideId
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Deleted
|
|
|
|
/api/v1/lists:
|
|
get:
|
|
summary: List IP lists
|
|
tags: [lists]
|
|
security: [{ bearerAuth: [] }]
|
|
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)
|
|
post:
|
|
summary: Create IP list
|
|
tags: [lists]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: Created
|
|
|
|
/api/v1/lists/{id}:
|
|
get:
|
|
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: >-
|
|
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]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Deleted
|
|
|
|
/api/v1/lists/{id}/refresh:
|
|
post:
|
|
summary: Refresh remote/json/evobgp list
|
|
tags: [lists]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Refreshed
|
|
|
|
/api/v1/lists/{id}/entries:
|
|
post:
|
|
summary: Add manual entries
|
|
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: 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
|
|
|
|
/api/v1/policy-sets:
|
|
get:
|
|
summary: List policy sets
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: Sets
|
|
post:
|
|
summary: Create policy set
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: Created
|
|
|
|
/api/v1/policy-sets/{id}:
|
|
get:
|
|
summary: Get policy set
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Set
|
|
patch:
|
|
summary: Patch policy set
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Updated
|
|
delete:
|
|
summary: Delete policy set
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Deleted
|
|
|
|
/api/v1/policy-sets/{id}/rules:
|
|
get:
|
|
summary: Rules in set
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Rules
|
|
|
|
/api/v1/policy-sets/{id}/rules/reorder:
|
|
put:
|
|
summary: Reorder rules in a set
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Reordered
|
|
|
|
/api/v1/policy-sets/{id}/agents:
|
|
put:
|
|
summary: Replace which agents have this set assigned
|
|
description: >-
|
|
Listed agents gain the set (other assignments preserved), unlisted
|
|
agents lose it. Body — { agent_ids: string[] } (empty array clears).
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: '{ agent_ids, added, removed }'
|
|
'404':
|
|
description: Set or agent not found
|
|
|
|
/api/v1/rules:
|
|
get:
|
|
summary: List policy rules (optional set filter)
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Limit'
|
|
- $ref: '#/components/parameters/Offset'
|
|
responses:
|
|
'200':
|
|
description: Rules (items + total)
|
|
post:
|
|
summary: Create policy rule
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: Created
|
|
|
|
/api/v1/rules/{id}:
|
|
patch:
|
|
summary: Patch rule
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Updated
|
|
delete:
|
|
summary: Delete rule
|
|
tags: [policies]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Deleted
|
|
|
|
/api/v1/stats/recent:
|
|
get:
|
|
summary: Recent apply samples
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: Samples
|
|
|
|
/api/v1/agents/{id}/stats:
|
|
get:
|
|
summary: Apply samples for one agent
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Samples
|
|
|
|
/api/v1/agents/{id}/stats/reset:
|
|
post:
|
|
summary: Reset packet totals, samples, and per-IP block stats
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Agent after reset
|
|
|
|
/api/v1/agents/{id}/blocked-ips:
|
|
get:
|
|
summary: Per-IP/CIDR drop counters (Linux nft/ipset; MikroTik HITS)
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Top blocked IPs by accumulated packets (+ optional top ports per IP on Linux)
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required: [ip, packets, first_seen_at, last_seen_at]
|
|
properties:
|
|
ip: { type: string }
|
|
packets: { type: integer }
|
|
first_seen_at: { type: string, format: date-time }
|
|
last_seen_at: { type: string, format: date-time }
|
|
ports:
|
|
type: array
|
|
description: Top destination ports for this IP (Linux nft)
|
|
items:
|
|
type: object
|
|
required: [port, protocol, packets]
|
|
properties:
|
|
port: { type: integer, minimum: 1, maximum: 65535 }
|
|
protocol: { type: string, enum: [tcp, udp] }
|
|
packets: { type: integer }
|
|
|
|
/api/v1/agents/{id}/blocked-ports:
|
|
get:
|
|
summary: Aggregate destination ports hit by denied sources (Linux nft)
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Top ports by accumulated packets across all blocked IPs
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required: [port, protocol, packets, last_seen_at]
|
|
properties:
|
|
port: { type: integer, minimum: 1, maximum: 65535 }
|
|
protocol: { type: string, enum: [tcp, udp] }
|
|
packets: { type: integer }
|
|
last_seen_at: { type: string, format: date-time }
|
|
|
|
/api/v1/agents/{id}/port-rules:
|
|
get:
|
|
summary: Desired Port ACL rules (Linux)
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Port ACL rules
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/AgentPortRule'
|
|
post:
|
|
summary: Create Port ACL rule
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateAgentPortRule'
|
|
responses:
|
|
'200':
|
|
description: Created rule
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AgentPortRule'
|
|
'400':
|
|
description: Validation / non-linux
|
|
|
|
/api/v1/agents/{id}/port-rules/import:
|
|
post:
|
|
summary: Import Port ACL from IP list or policy set sources
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [from, action, ports]
|
|
properties:
|
|
from: { type: string, enum: [list, set] }
|
|
list_id: { type: string }
|
|
set_id: { type: string }
|
|
action: { type: string, enum: [open, close] }
|
|
protocol: { type: string, enum: [tcp, udp, both], default: tcp }
|
|
ports:
|
|
type: array
|
|
minItems: 1
|
|
maxItems: 50
|
|
items:
|
|
type: object
|
|
required: [port_start]
|
|
properties:
|
|
port_start: { type: integer, minimum: 1, maximum: 65535 }
|
|
port_end: { type: integer, minimum: 1, maximum: 65535 }
|
|
enabled: { type: boolean, default: true }
|
|
comment: { type: string, maxLength: 500 }
|
|
responses:
|
|
'200':
|
|
description: Created rules
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/AgentPortRule'
|
|
|
|
/api/v1/agents/{id}/port-rules/{ruleId}:
|
|
patch:
|
|
summary: Update Port ACL rule
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
- name: ruleId
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UpdateAgentPortRule'
|
|
responses:
|
|
'200':
|
|
description: Updated rule
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AgentPortRule'
|
|
delete:
|
|
summary: Delete Port ACL rule
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
- name: ruleId
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Deleted
|
|
|
|
/api/v1/agents/{id}/host-firewall:
|
|
get:
|
|
summary: Last observed host firewall snapshot (Linux)
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- $ref: '#/components/parameters/Id'
|
|
responses:
|
|
'200':
|
|
description: Snapshot (empty if never reported)
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
collected_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
raw_digest:
|
|
type: string
|
|
nullable: true
|
|
rules:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/HostFwRule'
|
|
listeners:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/HostListener'
|
|
|
|
/api/v1/integrations/evobgp/communities:
|
|
get:
|
|
summary: Proxy EvoBGP communities
|
|
tags: [integrations]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: Communities
|
|
|
|
/api/v1/settings:
|
|
get:
|
|
summary: Settings map (token masked)
|
|
tags: [settings]
|
|
security: [{ bearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: Settings
|
|
put:
|
|
summary: Update whitelisted settings keys
|
|
tags: [settings]
|
|
security: [{ bearerAuth: [] }]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties:
|
|
type: string
|
|
description: Keys — enroll_seed, evobgp_api_url, evobgp_api_token, agent_sync_interval_sec, show_quick_actions
|
|
responses:
|
|
'200':
|
|
description: Saved
|
|
'400':
|
|
description: Unknown key / validation
|
|
|
|
/api/v1/audit:
|
|
get:
|
|
summary: Local audit log
|
|
tags: [ops]
|
|
security: [{ bearerAuth: [] }]
|
|
parameters:
|
|
- name: action
|
|
in: query
|
|
schema: { type: string }
|
|
- name: severity
|
|
in: query
|
|
schema: { type: string, enum: [info, warning, critical] }
|
|
- name: limit
|
|
in: query
|
|
schema: { type: integer, default: 200 }
|
|
responses:
|
|
'200':
|
|
description: Audit entries
|
|
|
|
/v1/agent/install.sh:
|
|
get:
|
|
summary: Agent install script
|
|
tags: [agent-public]
|
|
responses:
|
|
'200':
|
|
description: Shell script
|
|
|
|
/v1/agent/sync-script:
|
|
get:
|
|
summary: Linux sync agent (evofw-firewall.sh)
|
|
tags: [agent-public]
|
|
parameters:
|
|
- name: If-None-Match
|
|
in: header
|
|
schema: { type: string }
|
|
description: ETag from a previous GET (`"<sha256>"`)
|
|
responses:
|
|
'200':
|
|
description: Shell script
|
|
headers:
|
|
ETag:
|
|
schema: { type: string }
|
|
description: '"<sha256 of file bytes>"'
|
|
X-Evofw-Script-Sha256:
|
|
schema: { type: string }
|
|
description: Hex sha256 of the script body
|
|
'304':
|
|
description: Script unchanged
|
|
|
|
/v1/agent/enroll:
|
|
post:
|
|
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: Newly enrolled pending agent; no-store
|
|
'401':
|
|
description: Scoped enrollment credential required
|
|
'409':
|
|
description: Credential already consumed with different enrollment
|
|
|
|
/v1/agent/policy:
|
|
get:
|
|
summary: Evaluated policy for agent
|
|
tags: [agent]
|
|
security: [{ agentToken: [] }]
|
|
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:
|
|
summary: MikroTik RSC policy
|
|
tags: [agent]
|
|
security: [{ agentToken: [] }]
|
|
responses:
|
|
'200':
|
|
description: RSC text
|
|
|
|
/v1/agent/heartbeat:
|
|
post:
|
|
summary: Agent heartbeat
|
|
tags: [agent]
|
|
security: [{ agentToken: [] }]
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
|
|
/v1/agent/apply-report:
|
|
post:
|
|
summary: Apply report + packet stats (+ optional ip_hits / port_hits / host_firewall)
|
|
tags: [agent]
|
|
security: [{ agentToken: [] }]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: './openapi.schemas.json#/components/schemas/ApplyReport'
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
|
|
components:
|
|
parameters:
|
|
Id:
|
|
name: id
|
|
in: path
|
|
required: true
|
|
schema: { type: string }
|
|
Limit:
|
|
name: limit
|
|
in: query
|
|
schema: { type: integer, minimum: 1, maximum: 1000 }
|
|
description: Page size (without limit/offset the full list is returned)
|
|
Offset:
|
|
name: offset
|
|
in: query
|
|
schema: { type: integer, minimum: 0 }
|
|
description: Page offset (response includes total)
|
|
schemas:
|
|
AgentPortRule:
|
|
type: object
|
|
required:
|
|
[
|
|
id,
|
|
agent_id,
|
|
action,
|
|
protocol,
|
|
port_start,
|
|
port_end,
|
|
src_kind,
|
|
enabled,
|
|
priority,
|
|
created_at,
|
|
updated_at,
|
|
]
|
|
properties:
|
|
id: { type: string }
|
|
agent_id: { type: string }
|
|
action: { type: string, enum: [open, close] }
|
|
protocol: { type: string, enum: [tcp, udp, both] }
|
|
port_start: { type: integer, minimum: 1, maximum: 65535 }
|
|
port_end: { type: integer, minimum: 1, maximum: 65535 }
|
|
src_kind: { type: string, enum: [all, cidr, list] }
|
|
src_cidr: { type: string, nullable: true }
|
|
list_id: { type: string, nullable: true }
|
|
list_name: { type: string, nullable: true }
|
|
enabled: { type: boolean }
|
|
comment: { type: string, nullable: true }
|
|
priority: { type: integer }
|
|
created_at: { type: string, format: date-time }
|
|
updated_at: { type: string, format: date-time }
|
|
CreateAgentPortRule:
|
|
type: object
|
|
required: [action, port_start]
|
|
properties:
|
|
action: { type: string, enum: [open, close] }
|
|
protocol: { type: string, enum: [tcp, udp, both], default: tcp }
|
|
port_start: { type: integer, minimum: 1, maximum: 65535 }
|
|
port_end: { type: integer, minimum: 1, maximum: 65535 }
|
|
src_kind: { type: string, enum: [all, cidr, list], default: all }
|
|
src_cidr: { type: string }
|
|
list_id: { type: string }
|
|
enabled: { type: boolean, default: true }
|
|
comment: { type: string, maxLength: 500 }
|
|
priority: { type: integer, default: 100 }
|
|
UpdateAgentPortRule:
|
|
type: object
|
|
properties:
|
|
action: { type: string, enum: [open, close] }
|
|
protocol: { type: string, enum: [tcp, udp, both] }
|
|
port_start: { type: integer, minimum: 1, maximum: 65535 }
|
|
port_end: { type: integer, minimum: 1, maximum: 65535 }
|
|
src_kind: { type: string, enum: [all, cidr, list] }
|
|
src_cidr: { type: string, nullable: true }
|
|
list_id: { type: string, nullable: true }
|
|
enabled: { type: boolean }
|
|
comment: { type: string, maxLength: 500, nullable: true }
|
|
priority: { type: integer }
|
|
HostFwRule:
|
|
type: object
|
|
required: [ownership, backend, raw]
|
|
properties:
|
|
ownership: { type: string, enum: [evofw, foreign] }
|
|
backend:
|
|
type: string
|
|
enum: [nft, iptables, ufw, firewalld, listener]
|
|
table: { type: string }
|
|
chain: { type: string }
|
|
action: { type: string }
|
|
protocol: { type: string }
|
|
dport: { type: string }
|
|
sport: { type: string }
|
|
saddr: { type: string }
|
|
daddr: { type: string }
|
|
comment: { type: string }
|
|
raw: { type: string, maxLength: 512 }
|
|
HostListener:
|
|
type: object
|
|
required: [protocol, port, address]
|
|
properties:
|
|
protocol: { type: string }
|
|
port: { type: integer, minimum: 0, maximum: 65535 }
|
|
address: { type: string }
|
|
process: { type: string }
|
|
HostFirewallPayload:
|
|
type: object
|
|
properties:
|
|
rules:
|
|
type: array
|
|
maxItems: 500
|
|
items:
|
|
$ref: '#/components/schemas/HostFwRule'
|
|
listeners:
|
|
type: array
|
|
maxItems: 200
|
|
items:
|
|
$ref: '#/components/schemas/HostListener'
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
description: Portal JWT (app id fw)
|
|
agentToken:
|
|
type: http
|
|
scheme: bearer
|
|
description: Agent enroll token
|
|
|
|
tags:
|
|
- name: system
|
|
- name: ops
|
|
- name: agents
|
|
- name: lists
|
|
- name: policies
|
|
- name: settings
|
|
- name: integrations
|
|
- name: agent-public
|
|
- name: agent
|