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: /health: get: summary: Liveness tags: [system] responses: '200': description: OK /api/v1/dashboard: get: summary: Dashboard KPI tags: [ops] security: [{ bearerAuth: [] }] responses: '200': description: Stats /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: [] }] responses: '200': description: Links 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: [] }] responses: '200': description: Agents /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: [] }] responses: '200': description: Lists post: summary: Create IP list tags: [lists] security: [{ bearerAuth: [] }] responses: '200': description: Created /api/v1/lists/{id}: get: summary: List detail + entries tags: [lists] security: [{ bearerAuth: [] }] parameters: - $ref: '#/components/parameters/Id' responses: '200': description: Detail 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' responses: '200': description: Updated delete: summary: Delete entry tags: [lists] security: [{ bearerAuth: [] }] parameters: - $ref: '#/components/parameters/Id' 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/rules: get: summary: List policy rules (optional set filter) tags: [policies] security: [{ bearerAuth: [] }] responses: '200': description: Rules 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 (`""`) responses: '200': description: Shell script headers: ETag: schema: { type: string } description: '""' X-Evofw-Script-Sha256: schema: { type: string } description: Hex sha256 of the script body '304': description: Script unchanged /v1/agent/enroll: post: summary: Enroll agent (X-EvoFW-Seed) tags: [agent-public] responses: '201': description: Pending agent /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) /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: 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' responses: '200': description: OK components: parameters: Id: name: id in: path required: true schema: { type: string } 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