Overview
The Daemon WebSocket API provides a persistent, bidirectional connection for daemon clients to receive and process messages in real time. Unlike the REST API where clients poll for updates, the WebSocket connection enables Shannon to push incoming messages (from Slack, LINE, or system events) directly to connected daemons. The core protocol revolves around a claim-based message dispatch model: Shannon broadcasts messages to eligible connections, and daemons race to claim exclusive processing rights before replying.Endpoint
Authentication
Authentication is performed before the WebSocket upgrade using the same middleware as REST endpoints.Connection Lifecycle
1
HTTP Upgrade
Client sends
GET /v1/ws/messages with authentication headers. The server validates credentials before upgrading.2
WebSocket Established
Server upgrades to WebSocket (gorilla/websocket, 4KB read/write buffers, CheckOrigin allows all origins).
3
Connection Confirmed
Server sends a
connected message to confirm the connection is ready.4
Bidirectional Messaging
Both sides exchange JSON messages. The server dispatches incoming messages; the client claims, processes, and replies.
5
Keep-Alive
Server sends WebSocket pings every 20 seconds. Client must respond with a pong within 60 seconds or the connection is closed.
Connection Parameters
Message Envelope
All messages (both directions) follow a consistent envelope format:Server-to-Client Messages
connected
Sent once immediately after the WebSocket connection is established.
message
An inbound message dispatched for processing. This is the primary message type — it carries messages from channel webhooks (Slack, LINE) or system events.
MessagePayload Fields
system
System-level notification from Shannon.
claim_ack
Response to a client’s claim request, indicating whether the claim was granted.
Client-to-Server Messages
claim
Claim exclusive processing rights for a message. Only one client can successfully claim a given message.
progress
Send a heartbeat/progress update while processing a claimed message. This extends the claim lease, preventing timeout.
reply
Send the completed response for a claimed message. Shannon routes this back to the originating channel (Slack, LINE, etc.).
ReplyPayload Fields
disconnect
Gracefully close the connection.
Claim Flow
The claim flow is the core protocol for distributed message processing. It ensures exactly one daemon processes each message, even when multiple daemons are connected.1
Message Dispatch
When a message arrives (via channel webhook or system), the Gateway dispatches it to all eligible WebSocket connections indexed by
tenant:user.2
Claim Race
Each daemon that wants to process the message sends a
claim request with the message_id.3
Atomic Resolution
The Gateway atomically claims the message in Redis (
SETNX). The first client wins; all others receive {"granted": false}.4
Processing
The winning daemon processes the message. It can optionally send
progress messages to extend the claim lease and signal activity.5
Reply
The daemon sends a
reply with the completed response. Shannon routes it back to the originating channel.Claim Metadata
When a message is claimed, the Gateway stores metadata in Redis with a 60-second TTL:Pending message metadata has a 90-second TTL. If a claimed message is not replied to within 60 seconds, the claim expires and the message can be re-dispatched.
Hub Architecture
The WebSocket Hub manages all active connections with these routing strategies:- Tenant-user indexing — Connections are indexed by
"tenant:user"key for targeted dispatch - Sticky thread routing — Messages from the same thread (
"channel_type:thread_id") are routed to the same connection when possible - Redis-backed claims — Distributed claim resolution ensures consistency across multiple Gateway instances
Reply Routing
When the Gateway receives areply from a daemon, it routes the response based on claim metadata:
- Workflow reply — If
workflow_idexists in claim metadata, the Gateway signals the associated Temporal workflow with the reply content - Channel reply — Otherwise, the reply is routed back to the originating channel (Slack post, LINE push message, etc.)
Error Handling
Next Steps
Channels API
Manage channel integrations for Slack and LINE
Streaming
Server-Sent Events for task streaming