10 KiB
API
Base URL examples use http://127.0.0.1:8080.
Operator Authentication
Browser operators sign in with username/password. The server returns a signed HttpOnly session cookie.
POST /api/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "operator-password"
}
Check the current browser session:
GET /api/auth/me
Sign out:
POST /api/auth/logout
Operator API automation may continue using:
Authorization: Bearer operator-token
Health
GET /healthz
Response:
{"status":"ok"}
Agent Enrollment
POST /api/agent/enroll
Content-Type: application/json
Request:
{
"enrollment_token": "dev-enroll-token",
"hostname": "openwrt-router-1",
"openwrt_version": "OpenWrt 23.05.3"
}
Response:
{
"device_id": "dev_...",
"device_token": "tok_..."
}
Agent Heartbeat
POST /api/agent/heartbeat
Authorization: Bearer tok_...
Content-Type: application/json
Request:
{
"device_id": "dev_...",
"inventory": {
"hostname": "openwrt-router-1",
"openwrt_version": "OpenWrt 23.05.3",
"wan_ip": "203.0.113.10/24",
"dhcp_leases": [],
"wifi_clients": []
},
"metrics": {
"loadavg": "0.00 0.01 0.00 1/80 1234",
"memory": {"total_kb": 262144, "used_kb": 131072},
"disk": {"total_kb": 65536, "used_kb": 20000, "used_percent": "31%"},
"interface_counters": [],
"connectivity_checks": [
{
"target": "1.1.1.1",
"reachable": true,
"packet_loss_percent": 0,
"latency_ms": 12.4
}
]
}
}
Response:
{
"commands": [
{
"id": "cmd_...",
"device_id": "dev_...",
"type": "ping",
"args": {"target": "1.1.1.1"},
"status": "queued",
"created_at": "2026-06-01T12:00:00Z"
}
]
}
Agent Next Command
This endpoint is optimized for the MVP shell agent and returns at most one queued command as plain text.
POST /api/agent/commands/next
Authorization: Bearer tok_...
Content-Type: application/json
Request:
{
"device_id": "dev_..."
}
Response:
cmd_... ping {"target":"1.1.1.1"}
If there are no queued commands, the server returns 204 No Content.
Command Result
POST /api/agent/commands/{command_id}/result
Authorization: Bearer tok_...
Content-Type: application/json
Request:
{
"device_id": "dev_...",
"status": "completed",
"exit_code": 0,
"output": "PING 1.1.1.1 ...",
"result": {}
}
List Devices
GET /api/devices
Authorization: Bearer dev-operator-token
Response:
{
"devices": []
}
Get Device
GET /api/devices/{device_id}
Authorization: Bearer dev-operator-token
Update Device Fleet Metadata
PATCH /api/devices/{device_id}/fleet
Authorization: Bearer dev-operator-token
Content-Type: application/json
Request:
{
"group": "home",
"tags": ["edge", "vpn"]
}
Metrics History
GET /api/devices/{device_id}/metrics-history?limit=100
Authorization: Bearer dev-operator-token
Response:
{
"samples": [
{
"id": "met_...",
"device_id": "dev_...",
"inventory": {},
"metrics": {},
"created_at": "2026-06-01T12:00:00Z"
}
]
}
Device Alerts
GET /api/devices/{device_id}/alerts
Authorization: Bearer dev-operator-token
Alerts are computed from current device state, recent metric samples, and recent commands. The MVP alert types are:
offlinememory_highdisk_highwan_ip_changedcommand_attentionwan_downpacket_loss_highlatency_high
Alert statuses:
activeacknowledgedresolved
Response:
{
"alerts": [
{
"id": "disk_high:dev_...",
"device_id": "dev_...",
"type": "disk_high",
"severity": "warning",
"status": "active",
"message": "Disk usage is high",
"details": {"used_percent": 88},
"first_seen_at": "2026-06-01T12:00:00Z",
"last_seen_at": "2026-06-01T12:03:00Z",
"created_at": "2026-06-01T12:00:00Z"
}
]
}
Acknowledge Alert
POST /api/devices/{device_id}/alerts/{alert_id}/acknowledge
Authorization: Bearer dev-operator-token
Acknowledged alerts remain open while the condition still exists. When the condition disappears, the server marks the alert as resolved.
Create Command
POST /api/devices/{device_id}/commands
Authorization: Bearer dev-operator-token
Content-Type: application/json
Request:
{
"type": "ping",
"args": {"target": "1.1.1.1"}
}
Allowed command types:
pingtracerouteroute_showinterfaces_showrebootservice_restartpkg_list_installedpkg_updatepkg_list_upgradablepkg_installpkg_removeuci_showuci_backupuci_previewuci_setuci_commituci_commit_confirmeduci_revertuci_restoreremote_ssh_reverse
Package commands are package-manager aware on the agent. OpenWrt 25.12+ uses apk; older releases usually use opkg. Legacy opkg_* command names are still accepted for compatibility, but the UI uses pkg_*.
UCI Commands
UCI commands are queued through the same command endpoint:
POST /api/devices/{device_id}/commands
Authorization: Bearer dev-operator-token
Content-Type: application/json
Show a config:
{
"type": "uci_show",
"args": {"config": "network"}
}
Create a backup:
{
"type": "uci_backup",
"args": {"config": "network"}
}
Preview a value without leaving staged changes:
{
"type": "uci_preview",
"args": {
"config": "network",
"section": "lan",
"option": "ipaddr",
"value": "192.168.1.1"
}
}
Stage a value:
{
"type": "uci_set",
"args": {
"config": "network",
"section": "lan",
"option": "ipaddr",
"value": "192.168.1.1",
"commit": "false"
}
}
Commit or revert staged changes:
{"type": "uci_commit", "args": {"config": "network"}}
{"type": "uci_revert", "args": {"config": "network"}}
Commit with connectivity confirmation:
{
"type": "uci_commit_confirmed",
"args": {"config": "network", "confirm_seconds": "15"}
}
The agent commits, waits, checks server reachability, and restores the latest local backup if the server is unreachable.
uci_preview includes a DIFF section when the router has the diff command available. It still returns CHANGE, BACKUP, BEFORE, and AFTER sections for routers without diff.
Restore the latest local backup captured by uci_backup, uci_preview, or uci_set:
{"type": "uci_restore", "args": {"config": "network"}}
The MVP agent only allows these UCI config packages:
networkwirelessdhcpfirewallsystem
The MVP UI exposes presets for:
- LAN IP:
network.lan.ipaddr - hostname:
system.@system[0].hostname - Wi-Fi SSID:
wireless.@wifi-iface[0].ssid - Wi-Fi password:
wireless.@wifi-iface[0].key - LAN DHCP toggle:
dhcp.lan.ignore
Sensitive command output keys are redacted before storage and display. Current redaction targets include:
private_keypasswordpasswdsecretpsktoken
Remote Sessions
Remote sessions create a short-lived SSH reverse tunnel command for the agent.
POST /api/devices/{device_id}/remote-sessions
Authorization: Bearer dev-operator-token
Content-Type: application/json
Request:
{
"target": "ssh",
"server_host": "10.10.10.2",
"server_port": 22,
"remote_port": 22022,
"local_port": 22,
"duration_seconds": 900
}
Response:
{
"id": "ras_...",
"device_id": "dev_...",
"target": "ssh",
"status": "queued",
"server_host": "10.10.10.2",
"server_port": 22,
"remote_port": 22022,
"local_host": "127.0.0.1",
"local_port": 22,
"command_id": "cmd_...",
"created_at": "2026-06-01T12:00:00Z",
"expires_at": "2026-06-01T12:15:00Z"
}
List sessions:
GET /api/devices/{device_id}/remote-sessions?limit=25
Authorization: Bearer dev-operator-token
Close a session record:
POST /api/devices/{device_id}/remote-sessions/{session_id}/close
Authorization: Bearer dev-operator-token
The MVP close action updates server state and audit history. Active SSH processes stop by duration timeout on the agent side.
Create Bulk Command
POST /api/devices/bulk-commands
Authorization: Bearer dev-operator-token
Content-Type: application/json
Request:
{
"device_ids": ["dev_..."],
"type": "ping",
"args": {"target": "1.1.1.1"}
}
The server creates one command per unique device id.
List Device Commands
GET /api/devices/{device_id}/commands?limit=50
Authorization: Bearer dev-operator-token
Response:
{
"commands": [
{
"id": "cmd_...",
"device_id": "dev_...",
"type": "ping",
"args": {"target": "1.1.1.1"},
"status": "completed",
"result": {},
"output": "PING ...",
"exit_code": 0,
"attempt_count": 1,
"max_attempts": 3,
"created_at": "2026-06-01T12:00:00Z",
"expires_at": "2026-06-02T12:00:00Z",
"claimed_at": "2026-06-01T12:00:01Z",
"completed_at": "2026-06-01T12:00:03Z",
"expired_at": null
}
]
}
Command lifecycle defaults:
- new commands expire after 24 hours;
- claimed commands are returned to
queuedafter the server-side claim timeout while attempts remain; - claimed commands transition to
expiredaftermax_attemptsis reached; - expired and cancelled commands no longer accept agent results.
Get Command
GET /api/devices/{device_id}/commands/{command_id}
Authorization: Bearer dev-operator-token
Cancel Command
POST /api/devices/{device_id}/commands/{command_id}/cancel
Authorization: Bearer dev-operator-token
Only queued and claimed commands can transition to cancelled.
List Audit Events
GET /api/audit-events?device_id=dev_...&limit=50
Authorization: Bearer dev-operator-token
Response:
{
"audit_events": [
{
"id": "aud_...",
"actor": "operator",
"action": "command.create",
"device_id": "dev_...",
"command_id": "cmd_...",
"details": {"type": "ping"},
"created_at": "2026-06-01T12:00:00Z"
}
]
}