WebSocket Protocol

Amurg uses JSON-encoded messages over WebSocket for all real-time communication. There are two WebSocket connections: Runtime ↔ Hub (/ws/runtime) and Client ↔ Hub (/ws/client). All messages share a common envelope format.

Envelope Format

Every message is a JSON object with a type field that determines the payload structure:

{
  "type": "message.type",
  "id": "optional-message-uuid",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:00Z",
  "payload": { ... }
}
Field Type Description
type string Message type identifier (required)
id string Message UUID for idempotency (optional)
session_id string Target session (optional, not used in hello/ack/ping)
ts string (ISO 8601) Timestamp of the message
payload object Type-specific payload (optional)

Runtime ↔ Hub Messages

These messages flow over the /ws/runtime connection between the runtime process and the hub.

Type Direction Description
runtime.hello Runtime → Hub Initial authentication and endpoint registration
hello.ack Hub → Runtime Acknowledge runtime registration
session.create Hub → Runtime Request runtime to create a new session
session.created Runtime → Hub Confirm session creation (with optional native handle)
session.close Both Close a session (either side may initiate)
user.message Hub → Runtime Forward user input to the agent
agent.output Runtime → Hub Stream agent output (stdout/stderr/system)
turn.started Runtime → Hub Agent has begun responding to a user message
turn.completed Runtime → Hub Agent turn is complete (with optional exit code)
stop.request Hub → Runtime Request the runtime to stop/cancel a session
stop.ack Runtime → Hub Acknowledge stop request
permission.request Runtime → Hub Agent requests tool approval from the user
permission.response Hub → Runtime User's approval or denial of tool permission
file.upload Hub → Runtime User-uploaded file forwarded to the agent (base64)
file.available Runtime → Hub Agent-produced file sent to the hub (base64)
endpoint.config_update Hub → Runtime Apply security/limits config override to an endpoint
endpoint.config_ack Runtime → Hub Acknowledge config update
runtime.token_refresh Hub → Runtime Provide a refreshed authentication token
ping / pong Both Connection liveness heartbeat

Runtime ↔ Hub Message Details

runtime.hello

Runtime → Hub — Sent immediately after WebSocket connection. Authenticates the runtime and registers all available endpoints.

{
  "type": "runtime.hello",
  "ts": "2024-01-15T10:30:00Z",
  "payload": {
    "runtime_id": "prod-runtime-01",
    "token": "pre-shared-runtime-token",
    "org_id": "default",
    "endpoints": [
      {
        "id": "claude-code-prod",
        "profile": "claude-code",
        "name": "Claude Code (prod)",
        "tags": { "env": "production" },
        "caps": {
          "native_session_ids": false,
          "turn_completion": true,
          "resume_attach": false,
          "exec_model": "interactive"
        },
        "security": {
          "permission_mode": "strict",
          "allowed_tools": ["Bash", "Read", "Write"]
        }
      }
    ]
  }
}

hello.ack

Hub → Runtime — Confirms or rejects the runtime registration.

{
  "type": "hello.ack",
  "ts": "2024-01-15T10:30:00Z",
  "payload": { "ok": true }
}

session.create

Hub → Runtime — Requests the runtime to create a new agent session on the specified endpoint.

{
  "type": "session.create",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:01Z",
  "payload": {
    "session_id": "session-uuid",
    "endpoint_id": "claude-code-prod",
    "user_id": "user-uuid"
  }
}

session.created

Runtime → Hub — Confirms session creation. May include a native session handle for integrated profiles.

{
  "type": "session.created",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:01Z",
  "payload": {
    "session_id": "session-uuid",
    "ok": true,
    "native_handle": ""
  }
}

user.message

Hub → Runtime — Forwards user input to the agent. The message_id is a client-generated UUID for idempotency.

{
  "type": "user.message",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:05Z",
  "payload": {
    "session_id": "session-uuid",
    "message_id": "msg-uuid",
    "content": "List all files in the current directory"
  }
}

agent.output

Runtime → Hub — Streams agent output. Each chunk has a monotonic seq number. The channel field indicates the output source. Set final: true on the last chunk of a turn.

{
  "type": "agent.output",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:06Z",
  "payload": {
    "session_id": "session-uuid",
    "message_id": "out-uuid",
    "seq": 1,
    "channel": "stdout",
    "content": "file1.txt  file2.txt  src/",
    "final": false
  }
}

turn.started

Runtime → Hub — Signals that the agent has begun processing a user message.

{
  "type": "turn.started",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:05Z",
  "payload": {
    "session_id": "session-uuid",
    "in_response_to": "msg-uuid"
  }
}

turn.completed

Runtime → Hub — Signals that the agent has finished responding. For run-to-completion profiles, includes the process exit code.

{
  "type": "turn.completed",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:08Z",
  "payload": {
    "session_id": "session-uuid",
    "in_response_to": "msg-uuid",
    "exit_code": 0
  }
}

stop.request / stop.ack

Hub → Runtime / Runtime → Hub — Hub requests the runtime to stop or cancel the current operation. The runtime acknowledges with success or failure.

// stop.request (Hub → Runtime)
{
  "type": "stop.request",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:10Z",
  "payload": { "session_id": "session-uuid" }
}

// stop.ack (Runtime → Hub)
{
  "type": "stop.ack",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:10Z",
  "payload": { "session_id": "session-uuid", "ok": true }
}

session.close

Both — Either side can initiate a session close with an optional reason.

{
  "type": "session.close",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:35:00Z",
  "payload": {
    "session_id": "session-uuid",
    "reason": "user_requested"
  }
}

permission.request

Runtime → Hub — Sent when an agent needs user approval for a tool action. The hub tracks pending requests with a 60-second timeout. If no response arrives in time, the request is auto-denied.

{
  "type": "permission.request",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:31:02Z",
  "payload": {
    "session_id": "session-uuid",
    "request_id": "req-uuid",
    "tool": "Bash",
    "description": "Execute: rm -rf /tmp/build",
    "resource": "/tmp/build"
  }
}
Field Type Description
request_id string Unique ID for correlating request/response
tool string Name of the tool requesting permission (e.g. "Bash", "Write")
description string Human-readable description of the action
resource string Optional resource path or identifier

permission.response

Hub → Runtime (via Client interaction) — Carries the user's approval or denial back to the runtime. The always_allow flag indicates the user checked "always allow this tool," which the runtime may use to skip future prompts for that tool.

{
  "type": "permission.response",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:31:05Z",
  "payload": {
    "session_id": "session-uuid",
    "request_id": "req-uuid",
    "approved": true,
    "always_allow": false
  }
}

file.upload

Hub → Runtime — Forwards a user-uploaded file to the runtime as base64-encoded data. The hub stores the file on disk and also forwards it via WebSocket.

{
  "type": "file.upload",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:32:00Z",
  "payload": {
    "session_id": "session-uuid",
    "metadata": {
      "file_id": "file-uuid",
      "name": "input.csv",
      "mime_type": "text/csv",
      "size": 1024
    },
    "data": "base64-encoded-content..."
  }
}

file.available

Runtime → Hub — Notifies the hub that the agent has produced a file. The hub stores the file and makes it available for download via the HTTP API.

{
  "type": "file.available",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:33:00Z",
  "payload": {
    "session_id": "session-uuid",
    "metadata": {
      "file_id": "file-uuid",
      "name": "output.png",
      "mime_type": "image/png",
      "size": 45678
    },
    "data": "base64-encoded-content..."
  }
}

ping / pong

Both — Heartbeat messages for connection liveness detection.

{ "type": "ping", "ts": "2024-01-15T10:30:00Z" }
{ "type": "pong", "ts": "2024-01-15T10:30:00Z" }

Client ↔ Hub Messages

These messages flow over the /ws/client connection between the UI client and the hub.

Type Direction Description
client.subscribe Client → Hub Subscribe to live updates for a session (with replay from after_seq)
client.unsubscribe Client → Hub Stop receiving live updates for a session
user.message Client → Hub Send a user message (routed to runtime)
agent.output Hub → Client Streamed agent output
turn.started Hub → Client Agent turn has begun
turn.completed Hub → Client Agent turn is complete
permission.request Hub → Client Agent tool permission prompt (relayed from runtime)
permission.response Client → Hub User's approval/denial (forwarded to runtime)
history.response Hub → Client Replayed messages for a subscribed session
session.closed Hub → Client Session has been closed (by user, admin, or idle reaper)
error Hub → Client Error response (e.g. turn_in_progress, auth_failed)

client.subscribe

Client → Hub — Subscribe to live updates for a session. The after_seq field enables reconnect replay: the hub sends all stored messages with a sequence number greater than after_seq.

{
  "type": "client.subscribe",
  "ts": "2024-01-15T10:30:00Z",
  "payload": {
    "session_id": "session-uuid",
    "after_seq": 0
  }
}

error

Hub → Client — Error messages include a machine-readable code and a human-readable message.

{
  "type": "error",
  "session_id": "session-uuid",
  "ts": "2024-01-15T10:30:05Z",
  "payload": {
    "code": "turn_in_progress",
    "message": "A turn is already in progress for this session"
  }
}

Sequence Diagrams

Runtime Connection

Runtime                          Hub
  |                               |
  |---[WS connect /ws/runtime]--->|
  |                               |
  |---runtime.hello-------------->|
  |   (token + endpoints)         |
  |                               |---[validate token]
  |                               |---[register endpoints]
  |<--hello.ack-------------------|
  |   (ok: true)                  |
  |                               |
  |<==== bidirectional channel ==>|

Session Lifecycle

User/Client           Hub                Runtime            Agent
  |                    |                    |                   |
  |--POST /sessions--->|                    |                   |
  |                    |--session.create--->|                   |
  |                    |                    |---spawn/attach--->|
  |                    |<-session.created---|                   |
  |<--201 {session}----|                    |                   |
  |                    |                    |                   |
  |--WS: subscribe---->|                    |                   |
  |--WS: user.message->|                    |                   |
  |                    |--user.message----->|                   |
  |                    |                    |---stdin---------->|
  |                    |<-turn.started------|                   |
  |<-WS: turn.started--|                    |                   |
  |                    |                    |<--stdout----------|
  |                    |<-agent.output------|                   |
  |<-WS: agent.output--|                    |                   |
  |                    |                    |<--exit/idle-------|
  |                    |<-turn.completed----|                   |
  |<-WS: turn.complete-|                    |                   |
  |                    |                    |                   |

Permission Flow

User/Client           Hub                Runtime            Agent
  |                    |                    |                   |
  |                    |                    |<--tool call-------|
  |                    |<-perm.request------|                   |
  |                    |   (tool, desc)     |   [blocked]       |
  |<-WS: perm.request--|                    |                   |
  |   [show banner]    |                    |                   |
  |--WS: perm.response>|                    |                   |
  |   (approved/denied)|                    |                   |
  |                    |--perm.response---->|                   |
  |                    |                    |---resume/deny---->|
  |                    |                    |                   |
  |                    |  [60s timeout]     |                   |
  |                    |  [auto-deny if     |                   |
  |                    |   no response]     |                   |

File Transfer Flow

User/Client           Hub                Runtime            Agent
  |                    |                    |                   |
  |  --- Upload (user to agent) ---        |                   |
  |--POST /files------>|                    |                   |
  |  (multipart)       |--file.upload------>|                   |
  |                    |  (base64 over WS)  |---file.input----->|
  |                    |                    |                   |
  |  --- Download (agent to user) ---      |                   |
  |                    |                    |<--file.output------|
  |                    |<-file.available----|                   |
  |                    |  (base64 over WS)  |                   |
  |                    |---store to disk--->|                   |
  |<-WS: file message--|                    |                   |
  |--GET /files/{id}-->|                    |                   |
  |<--binary download--|                    |                   |

Reconnect & Resume

Client                Hub
  |                    |
  |--[connection lost]-|
  |                    |
  |--[WS reconnect]--->|
  |--subscribe-------->|
  |   (after_seq: 42)  |
  |                    |---[query messages > seq 42]
  |<-history.response--|
  |   (missed msgs)    |
  |                    |
  |<==== live stream ==>|