Files
vacuum-wall/docs/api.md
T
mteehan dcb581a359 Update documentation for config/ migration, new install model, and firewall config API
Reflect config/ vs data/ split, install-time Jinja2 templating, auto-discovered
paths, ZeroSSL/ACME support, and the new firewall save-then-apply API in all
reference docs.
2026-05-14 03:31:59 +00:00

23 KiB

REST API Reference

All endpoints are served by the management WebUI Flask application bound to 127.0.0.1:9090, proxied through nginx with SSL termination and HTTP basic authentication. Requests target the management domain (e.g., https://<hostname>.local/api/...).

Every request and response uses Content-Type: application/json.

Conventions

Success Responses

{
  "ok": true,
  "data": <value>
}

The data field contains the payload, which may be an object, array, string, or null.

Error Responses

{
  "ok": false,
  "error": "<human-readable message>"
}

Error responses carry one of the following HTTP status codes:

Code Meaning
400 Bad request — invalid body, missing required field, or malformed value
404 Not found — the requested resource does not exist
500 Internal server error — unexpected failure in the backend

Firewall API

Endpoints prefixed with /api/firewall/.... Interact with firewalld for zone management, rich rules, NAT, and masquerade.

Zone Management

List All Zones

GET /api/firewall/zones

Returns active zone-to-interface mappings and all available zone definitions.

Response:

Field Type Description
data.active object<name, [interface, ...]> Currently assigned interfaces per zone
data.available [string, ...] All zones known to firewalld

Get Zone Details

GET /api/firewall/zones/<name>

Return detailed configuration for a single zone.

Response (data):

Field Type Description
target string Zone target (e.g., "default", "ACCEPT", "REJECT")
interfaces [string, ...] Interfaces assigned to this zone
services [string, ...] Services allowed through the zone
ports [string, ...] Explicit port rules (format: "443/tcp")
masquerade boolean Whether masquerade (NAT) is enabled
forward_ports [{port: number, proto: string, toaddr: string, toport: number}, ...] Port forward rules
rich_rules [string, ...] Rich rule definitions

Returns HTTP 404 if the zone does not exist.


Create Zone

POST /api/firewall/zones

Create a new firewalld zone.

Request Body:

Field Type Required Description
name string Yes Zone name
target string No Zone target; defaults to "default"

Response: data is null on success.


Delete Zone

DELETE /api/firewall/zones/<name>

Remove a zone from firewalld.

Response: data is null on success.

Returns HTTP 404 if the zone does not exist.

Zone Configuration

Set Zone Interfaces

POST /api/firewall/zones/<name>/interfaces

Replace all interfaces assigned to the zone with the provided list.

Request Body:

Field Type Required Description
interfaces [string, ...] Yes List of interface names

Response (data):

Field Type Description
zone string Zone name
interfaces [string, ...] List of interface names now assigned to the zone

Set Zone Services

POST /api/firewall/zones/<name>/services

Replace all services allowed in the zone with the provided list.

Request Body:

Field Type Required Description
services [string, ...] Yes List of firewalld service names

Response (data):

Field Type Description
zone string Zone name
services [string, ...] List of services now allowed in the zone

Firewall Rules

Add Rich Rule

POST /api/firewall/rich-rules

Add a firewalld rich rule to a zone.

Request Body:

Field Type Required Description
zone string Yes Zone to add the rule to
rule string Yes Full rich rule string

Response (data):

Field Type Description
zone string Zone name
rule string Full rich rule string

Remove Rich Rule

DELETE /api/firewall/rich-rules

Remove an existing rich rule from a zone. The rule string must match exactly.

Request Body:

Field Type Required Description
zone string Yes Zone the rule belongs to
rule string Yes Exact rich rule string to remove

Response (data):

Field Type Description
zone string Zone name
rule string Exact rich rule string that was removed

List Rich Rules

GET /api/firewall/rich-rules/<zone>

Return all rich rules for the specified zone.

Response:

Field Type Description
data [string, ...] Rich rule strings

NAT

Enable / Disable Masquerade

POST /api/firewall/masquerade

Toggle masquerade (source NAT) for a zone.

Request Body:

Field Type Required Description
zone string Yes Zone to configure
enable boolean Yes true to enable, false to disable

Response (data):

Field Type Description
zone string Zone name
masquerade boolean Whether masquerade is now enabled for the zone

Add Port Forward

POST /api/firewall/forward-port

Add a port forwarding rule to a zone.

Request Body:

Field Type Required Description
zone string Yes Zone to add the rule to
port number Yes External port
proto string Yes Protocol ("tcp" or "udp")
toaddr string No Internal destination address
toport number No Internal destination port

Response (data):

Field Type Description
zone string Zone name
port number External port
proto string Protocol ("tcp" or "udp")

Remove Port Forward

DELETE /api/firewall/forward-port

Remove a port forwarding rule. The body must match the original rule exactly.

Request Body:

Field Type Required Description
zone string Yes Zone the rule belongs to
port number Yes External port
proto string Yes Protocol ("tcp" or "udp")
toaddr string No Internal destination address
toport number No Internal destination port

Response (data):

Field Type Description
zone string Zone name
port number External port
proto string Protocol ("tcp" or "udp")

Info

Available Services

GET /api/firewall/services

List all service names known to firewalld.

Response:

Field Type Description
data [string, ...] Service names

Available Interfaces

GET /api/firewall/interfaces

List all network interfaces currently available on the system.

Response:

Field Type Description
data [string, ...] Interface names

DHCP / DNS API

Endpoints prefixed with /api/dhcp/.... Manage dnsmasq configuration, DHCP leases, and custom DNS records.

Configuration

Get Configuration

GET /api/dhcp/config

Return the current DHCP/DNS configuration object.

Response:

Field Type Description
data object Full dnsmasq configuration dictionary

Replace Configuration

POST /api/dhcp/config

Replace the entire configuration with the provided JSON object.

Request Body:

Field Type Required Description
(entire body) object Yes Complete configuration object

Response: data is null on success.


Partial Update Configuration

PATCH /api/dhcp/config

Deep-merge the provided fields into the existing configuration. Useful for targeted updates (e.g., changing DNS upstream servers without replacing the full config).

Request Body:

Field Type Required Description
(any subset) any Yes Fields to merge into the existing config

Response: data is null on success.


Apply Configuration

POST /api/dhcp/apply

Write the in-memory configuration to /etc/dnsmasq.d/vacuum-wall.conf and reload the dnsmasq service.

Response: data is null on success.

Leases

Get Live Leases

GET /api/dhcp/leases

Return the current DHCP lease table from dnsmasq.

Response:

Field Type Description
data [object, ...] Array of lease objects

Add Static Lease

POST /api/dhcp/static-lease

Add a static (reserved) DHCP lease.

Request Body:

Field Type Required Description
mac string Yes MAC address ("aa:bb:cc:dd:ee:ff")
ip string Yes Reserved IP address
hostname string No Hostname for the reservation

Response (data):

Field Type Description
mac string MAC address
ip string Reserved IP address
hostname string Hostname for the reservation

Remove Static Lease

DELETE /api/dhcp/static-lease?mac=aa:bb:cc:dd:ee:ff

Remove a previously configured static lease.

Query Parameters:

Field Type Required Description
mac string Yes MAC address of the lease to remove

Response: data is null on success.

Returns HTTP 404 if no matching lease is found.

DNS Records

Add DNS Record

POST /api/dhcp/dns-record

Add a custom DNS A record served by dnsmasq.

Request Body:

Field Type Required Description
name string Yes Fully qualified domain name
address string Yes IP address to resolve to

Response (data):

Field Type Description
name string Fully qualified domain name
address string IP address
hostname string Short hostname

Remove DNS Record

DELETE /api/dhcp/dns-record?name=nas.lan

Remove a custom DNS record.

Query Parameters:

Field Type Required Description
name string Yes Fully qualified domain name to remove

Response: data is null on success.

Returns HTTP 404 if no matching record is found.


Proxy API

Endpoints prefixed with /api/proxy/.... Manage reverse proxy domains, nginx configuration generation, and the management WebUI proxy.

Domain Management

List All Domains

GET /api/proxy/domains

Return all configured proxy domains.

Response:

Field Type Description
data [object, ...] Array of domain configuration objects

Add Domain

POST /api/proxy/domains

Add a new reverse proxy domain.

Request Body:

Field Type Required Description
domain string Yes Domain name to proxy
backend_host string Yes Backend server IP or hostname
backend_port number Yes Backend server port
backend_proto string No Backend protocol ("http" or "https"); defaults to "http"

Response (data):

Field Type Description
domain string Domain name

Returns HTTP 400 if the domain is already configured.


Get Domain Details

GET /api/proxy/domains/<domain>

Return the configuration for a single proxy domain.

Response (data):

Field Type Description
domain string Domain name
backend_host string Backend server address
backend_port number Backend server port
backend_proto string Backend protocol

Returns HTTP 404 if the domain is not configured.


Update Domain

PUT /api/proxy/domains/<domain>

Update one or more fields of an existing domain entry. Only the fields present in the body are modified.

Request Body:

Field Type Required Description
backend_host string No Backend server IP or hostname
backend_port number No Backend server port
backend_proto string No Backend protocol

Response (data):

Field Type Description
domain string Domain name

Returns HTTP 404 if the domain is not configured.


Remove Domain

DELETE /api/proxy/domains/<domain>

Remove a proxy domain and its nginx configuration.

Response (data):

Field Type Description
domain string Domain name

Returns HTTP 404 if the domain is not configured.

Apply / Test

Apply Configuration

POST /api/proxy/apply

Regenerate nginx configuration files for all proxy domains and reload the nginx service.

Response: data is null on success.

Returns HTTP 500 if nginx config generation fails or the reload fails.


Test Configuration

POST /api/proxy/test

Run nginx -t against the generated configuration without reloading. Useful for validating changes before applying.

Response:

Field Type Description
data.valid boolean Whether the configuration syntax is valid
data.output string Raw nginx test output

Management

Configure Management WebUI Proxy

POST /api/proxy/management

Configure the nginx proxy block for the management WebUI itself, including optional HTTP basic authentication.

Request Body:

Field Type Required Description
domain string Yes Management domain (e.g., "myhost.local")
flask_host string No Flask app bind host; defaults to "127.0.0.1"
flask_port number No Flask app bind port; defaults to 9090
auth_user string No Username for basic auth. An .htpasswd entry is created when this field is present.
auth_pass string No Password for basic auth. Used together with auth_user.

Response: data is null on success.

If auth_user and auth_pass are provided, the endpoint creates or updates the corresponding .htpasswd file entry.


Certificate API

Endpoints prefixed with /api/certs/.... Manage TLS certificates via ACME (ZeroSSL, Let's Encrypt, etc.).

Listing & Details

List All Certificates

GET /api/certs/list

Return all managed certificates with metadata.

Response:

Field Type Description
data [object, ...] Array of certificate objects

Each certificate object:

Field Type Description
domain string Domain the certificate covers
expires_at string Expiration date (ISO 8601)
days_until_expiry number Remaining days until expiration
cert_path string Path to the certificate file
key_path string Path to the private key file

Get Certificate Details

GET /api/certs/<domain>

Return details for a single certificate.

Response (data):

Field Type Description
domain string Domain
expires_at string Expiration date (ISO 8601)
days_until_expiry number Remaining days
cert_path string Certificate file path
key_path string Private key file path

Returns HTTP 404 if no certificate is found for the domain.

Operations

Issue Certificate

POST /api/certs/issue

Request a new certificate for a domain.

Request Body:

Field Type Required Description
domain string Yes Domain to issue the certificate for
webroot string No Custom webroot path for HTTP-01 validation

Response: data is null on success.

Returns HTTP 400 if the domain is missing or the request is malformed. Returns HTTP 500 if the ACME challenge or certificate issuance fails.


Renew Certificate

POST /api/certs/<domain>/renew

Force-renew an existing certificate, regardless of its current expiry status.

Response: data is null on success.

Returns HTTP 404 if the certificate is not found. Returns HTTP 500 if renewal fails.


Remove Certificate

DELETE /api/certs/<domain>

Delete a certificate and remove it from auto-renewal tracking.

Response: data is null on success.

Returns HTTP 404 if the certificate is not found.

Account

Set ACME Contact Email

POST /api/certs/email

Set or update the ACME account contact email (used by the CA for expiration and security notices).

Request Body:

Field Type Required Description
email string Yes Contact email address

Response (data):

Field Type Description
email string Contact email address

WireGuard API

Endpoints prefixed with /api/wireguard/.... Manage the WireGuard VPN server, peers, and client configuration.

Configuration

Get Configuration

GET /api/wireguard/config

Return the current WireGuard server configuration. The private_key field is stripped from the response.

Response:

Field Type Description
data object Full WireGuard configuration dictionary (private_key omitted)

Replace Configuration

POST /api/wireguard/config

Replace the entire WireGuard configuration. The private_key field is stripped from the response.

Request Body:

Field Type Required Description
(entire body) object Yes Complete WireGuard configuration object

Response:

Field Type Description
data object Updated configuration (private_key omitted)

Tunnel Control

Apply Configuration

POST /api/wireguard/apply

Write the current configuration to wg0.conf on disk and bring the WireGuard tunnel up.

Response: data is null on success.

Returns HTTP 500 if config write or interface bring-up fails.


Bring Tunnel Down

POST /api/wireguard/down

Bring down the WireGuard tunnel interface (wg0).

Response: data is null on success.

Status

Tunnel Status

GET /api/wireguard/status

Return live tunnel state, including interface metrics and per-peer connection statistics.

Response (data):

Field Type Description
up boolean Whether the tunnel interface is up
interface object Interface info (listen port, public key, etc.)
peers [object, ...] Per-peer connection stats (handshake time, transfer bytes, endpoint, etc.)

Initialize

POST /api/wireguard/initialize

Perform first-time setup: generate a server key pair, write an initial configuration, and prepare for peer enrollment. This endpoint is idempotent — calling it multiple times has no additional effect.

Response: data is null on success.

Peer Management

Add Peer

POST /api/wireguard/add-peer

Add a new WireGuard peer. A key pair is auto-generated for the peer. The response includes peer details with the private key stripped.

Request Body:

Field Type Required Description
name string Yes Peer identifier name
endpoint string No Allowed endpoint address ("ip:port")
allowed_ips [string, ...] No Allowed IPs; defaults to ["0.0.0.0/0"]
persistent_keepalive number No Persistent keepalive interval in seconds

Response (data):

Field Type Description
name string Peer name
public_key string Peer's public key
allowed_ips [string, ...] Allowed IPs
endpoint string Allowed endpoint
persistent_keepalive number Keepalive interval

Remove Peer

DELETE /api/wireguard/remove-peer?name=alice

Remove a configured peer.

Query Parameters:

Field Type Required Description
name string Yes Peer name to remove

Response (data):

Field Type Description
name string Peer name

Returns HTTP 404 if the peer is not found.


List Peers

GET /api/wireguard/peers

Return all configured peers. Private keys are stripped from the response.

Response:

Field Type Description
data [object, ...] Array of peer objects (private keys omitted)

Peer Connection Status

GET /api/wireguard/peer-status

Return live per-peer connection status from wg show, including last handshake time, transfer bytes, and current endpoint.

Response:

Field Type Description
data [object, ...] Array of live peer status objects

Client Configuration

Generate Client Config

POST /api/wireguard/generate-client

Generate a complete WireGuard client configuration file for provisioning a device. The returned config includes the peer's private key for the client to use.

Request Body:

Field Type Required Description
name string Yes Peer name to generate config for
server_endpoint string Yes Server public address ("ip:port") for the client's [Peer] section

Response (data):

Field Type Description
config string Complete WireGuard client config text ([Interface] + [Peer] block)

The client config includes the generated private key so the client can be provisioned directly. Note that this is the only endpoint that returns a WireGuard private key — all other endpoints strip private keys from responses.

Returns HTTP 404 if the peer is not found.