Files
vacuum-wall/docs/api.md
T
mteehan e2f56b8cc8 Initial commit: SSL proxy / firewall appliance
Flask WebUI behind nginx reverse proxy with zone-based firewall, DHCP,
WireGuard, and ACME certificate management.
2026-05-07 22:24:24 +00:00

21 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://wall.lan/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 [{port: number, proto: string}, ...] Explicit port rules
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 is null on success.


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 is null on success.

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 is null on success.


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 is null on success.


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 is null on success.


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 is null on success.


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 is null on success.

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 is null on success.


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 is null on success.


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 is null on success.

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 is null on success.

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 is null on success.

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., "wall.lan")
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 (Let's Encrypt / certbot).

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
expiry 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
expiry 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
standalone boolean No Use standalone (TCP) validation; defaults to false (HTTP-01 via existing webroot)
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 Let's Encrypt for expiration and security notices).

Request Body:

Field Type Required Description
email string Yes Contact email address

Response: data is null on success.


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 is null on success.

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.