API Reference¶
Eight.ly OS is a single Go binary. Everything the dashboard does, it does by calling this same HTTP API — there is no separate backend for the UI.
Base URL: http://box.example.lan:8080/api/
The spec¶
The API is described by an OpenAPI 3.1 document, generated straight from the Go router by scripts/gen-openapi.py — it isn't hand-written, so it can't describe a route that doesn't exist. Every box serves its own copy live:
curl http://box.example.lan:8080/api/openapi.json
Fetch it and read it with any OpenAPI tool (Swagger UI, Postman, eosctl api) rather than copying routes out of this page by hand — this page gives you the shape of the API, not the whole spec.
Authentication¶
Two ways to authenticate:
- API key —
Authorization: Bearer eos_.... Create one from Settings > Security > API Keys in the dashboard. See API Keys. - Signed-cookie session — what the dashboard itself uses.
POST /api/auth/loginissues an HMAC-SHA256 signed cookie (not a JWT); every subsequent request just needs the cookie.
Log in (session cookie)¶
curl -c cookies.txt -X POST http://box.example.lan:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "your-password"}'
{
"success": true,
"data": { "id": "...", "username": "admin", "role": "admin", "status": "active" }
}
curl -b cookies.txt http://box.example.lan:8080/api/health
Repeated failed logins are throttled per-IP and per-username (429 Too Many Requests with a Retry-After header); every login attempt, success or failure, is audit-logged.
Cookie sessions need a CSRF token; API keys don't¶
A state-changing request (POST/PUT/PATCH/DELETE) made with a session cookie must also carry a double-submit CSRF token — the server rejects it otherwise. A request authenticated with Authorization: Bearer eos_... is exempt: a bearer token can't be replayed cross-origin the way a cookie can, so there's nothing for CSRF to protect against on that path. If you're scripting against the API, using an API key sidesteps CSRF entirely — that's the main reason the CLI, the MCP server, and any automation should use one rather than logging in.
API keys¶
curl -H "Authorization: Bearer eos_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
http://box.example.lan:8080/api/health
Full detail — creating, listing, and revoking keys, and what a key can do — is on the API Keys page.
Response envelope¶
Success responses across the API share one shape:
{
"success": true,
"data": { }
}
Parse .data.
Error bodies are less consistent. The spec's own declared response schema says {"success": false, "message": "..."}, but some handlers don't follow it — a few return {"success": false, "error": "..."} instead, and a handful of older ones return a bare {"error": "..."} with no success field at all. Check the HTTP status code first, and look for a message under either message or error rather than assuming one key name.
Common status codes: 400 bad request, 401 not authenticated, 403 not authorized, 404 not found, 429 rate limited, 500 server error.
API areas¶
Counted straight from the spec: 521 distinct paths, 695 operations, across 69 subsystem groups.
This table shows the largest groups and a representative slice — not the full spec.
| Area | Base path | Ops (paths) | Covers | Guide |
|---|---|---|---|---|
| storage | /api/storage/* | 80 (69) | SnapRAID, MergerFS/array, disks, cache, the Shelver | Storage |
| auth | /api/auth/* | 52 (35) | Login, session, API keys, users, groups, roles, 2FA, realms | Datacenter Management |
| node | /api/node/* | 49 (38) | Host info, services, disks, network, shell (WebSocket) | Node Management |
| firewall | /api/firewall/* | 33 (9) | nftables rules, aliases, IP sets, groups | Firewall |
| zfs | /api/zfs/* | 28 (17) | ZFS pool create, import, dataset, snapshot, health | Storage |
| samba / nfs | /api/samba/*, /api/nfs/* | 23 + 7 | SMB shares, NFS exports, users | File Sharing |
| system | /api/system/* | 22 (22) | System info, GPU, sensors, updates | Node Management |
| nova | /api/nova/* | 20 (17) | AI assistant — models, chat, engines, chats, memory | Nova AI |
| snapraid | /api/snapraid/* | 19 (14) | Parity sync, scrub, check, fix, config | Storage |
| ha | /api/ha/* | 18 (11) | Rehome high availability — fencing, groups, affinity | Advanced Features |
| health | /api/health/* | 18 (18) | System health, subsystem status | Node Management |
| network | /api/network/* | 17 (14) | Interfaces, Docker networks, ports | Networking |
| sdn | /api/sdn/* | 17 (11) | Software-defined networking (experimental) | Advanced Features |
| apps | /api/apps/* | 14 (12) | App Store catalog, search, install, taxonomy | Getting Started with Apps |
| vm | /api/vm/* | 14 (10) | VM lifecycle, templates, ISOs | VM Management |
| vpn | /api/vpn/* | 14 (12) | Cloudflare tunnel, VPN | — |
| cluster | /api/cluster/* | 13 (11) | Multi-node clustering | Advanced Features |
| notifications | /api/notifications/* | 13 (8) | Email/webhook targets, event rules, history | Notifications |
| acme | /api/acme/* | 11 (6) | Let's Encrypt certificate management | ACME Certs |
| docker | /api/docker/* | 11 (8) | Docker networks, stacks | Web Dashboard |
| backup | /api/backup/* | 10 (6) | Local + EBS backup jobs, restore | Backup |
| license | /api/license/* | 10 (10) | Activation, heartbeat, catalog sync | Licensing |
| lxc | /api/lxc/* | 9 (6) | LXC container lifecycle, App Store install | LXC |
| containers | /api/containers/* | 4 (4) | Docker container lifecycle, stats, exec shell | Web Dashboard |
| files | /api/files/* | 4 (4) | File browser (list, mkdir, upload, delete) | Web Dashboard |
| ceph | /api/ceph/* | 3 (1) | Ceph distributed storage (experimental) | Advanced Features |
| events | /api/events* | 7 (5) | SSE event stream + stream management | — |
RBAC is enforced per subsystem, deny-by-default — each API package wires its own role requirements, and an endpoint with no role wired rejects the request. Three roles:
| Role | Access |
|---|---|
admin | Full access to all endpoints |
operator | Deploy apps, manage containers/VMs, view storage — no user management or security settings |
viewer | Read-only access to all GET endpoints |
An API key has no role of its own — it inherits whichever of these three roles the account that created it holds. See API Keys.
Common endpoints¶
# System health
curl -b cookies.txt http://box.example.lan:8080/api/health
# List containers
curl -b cookies.txt http://box.example.lan:8080/api/containers
# List VMs
curl -b cookies.txt http://box.example.lan:8080/api/vm/list
# Storage status
curl -b cookies.txt http://box.example.lan:8080/api/storage/status
# App catalog
curl -b cookies.txt http://box.example.lan:8080/api/apps
# Nova AI chat
curl -X POST -b cookies.txt \
-H "Content-Type: application/json" \
-d '{"message": "how much disk space is free?"}' \
http://box.example.lan:8080/api/nova/chat
Prefer not to hand-write curl calls at all? eosctl is a command line client generated from this same spec, and eosctl-mcp exposes it to AI assistants as MCP tools.
Streaming¶
curl -N -b cookies.txt http://box.example.lan:8080/api/events
One shared SSE connection streams system metrics, container state changes, storage events, and Docker events — the dashboard uses a single EventSource for all of it.
Nova AI chat responses stream as NDJSON, emitting thinking, tool_call, tool_result, and content events.
WebSocket endpoints¶
| Endpoint | Purpose |
|---|---|
/api/node/shell | Host web terminal (PTY over WebSocket, tmux-backed, admin-only) |
/api/containers/shell/{id} | Container console |