A minimal authenticated HTTP API for managing libvirt domains (VMs) on a host. It exposes two endpoints: read a VM's state and run a lifecycle action.
- Go 1.26+ (build only)
- A running libvirt daemon, and permission for the server process to
read/write its socket (
/var/run/libvirt/libvirt-sock).
The server is configured entirely via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
LICTOR_KEY |
yes | — | API key clients must send. |
LICTOR_ADDR |
no | 0.0.0.0:8080 |
Address the server listens on. |
LICTOR_LOG_LEVEL |
no | info |
Log level (debug, info, warn, error) |
If LICTOR_KEY is unset the server exits at startup.
export LICTOR_KEY=$(openssl rand -hex 16)
go run .
# or build a binary:
go build -o lictor . && ./lictorThe server speaks both HTTP/1.1 and HTTP/2 cleartext (h2c) on the same port.
The server itself does not terminate TLS, so the API key crosses the network unencrypted. Run it on localhost or a trusted network, or put it behind a TLS-terminating reverse proxy.
Every request must include the API key:
x-api-key: <LICTOR_KEY>
Requests with a missing or incorrect key get 401 and {"error":"unauthorized"}.
Full specification: openapi.yaml.
Returns the libvirt state of {target}.
curl -H "x-api-key: $LICTOR_KEY" http://localhost:8080/vm/web/state
# 200 {"vm":"web","state":"running"}| Status | When |
|---|---|
200 |
State retrieved. |
401 |
Missing/invalid API key. |
404 |
The target domain does not exist. |
500 |
libvirt failed for any other reason. |
Runs {action} against {target}. Each action maps to a single libvirt
operation from an exhaustive allowlist, nothing else can reach the daemon.
| Action | Effect |
|---|---|
start |
Boot the domain. |
shutdown |
Graceful shutdown. |
suspend |
Pause to RAM. |
resume |
Resume a suspended domain. |
destroy |
Force off (hard stop). |
A 200 means libvirt accepted the operation. shutdown in particular is
asynchronous, the guest may take time to power off, or ignore the request
entirely.
curl -X POST -H "x-api-key: $LICTOR_KEY" http://localhost:8080/vm/web/start
# 200 {"vm":"web","action":"start","ok":true}| Status | When |
|---|---|
200 |
Action applied. |
400 |
Action not in the allowlist. |
401 |
Missing/invalid API key. |
404 |
The target domain does not exist. |
500 |
libvirt failed for any other reason. |
Errors are always JSON: {"error":"<message>"}.
