How CasaOS Implements WebSocket Connections for Real-Time Hardware Status
CasaOS uses the Gorilla WebSocket library to upgrade HTTP connections at the /ws endpoint, stores active connections in a global slice, and broadcasts JSON-encoded hardware status updates through a background goroutine that prunes dead connections automatically.
CasaOS leverages persistent bi-directional communication channels to deliver instantaneous hardware status updates to web clients without requiring page refreshes. This architecture pushes real-time peer and device information from the backend to connected browsers using a lightweight event-driven subsystem. The implementation centers on the Gorilla WebSocket library for connection management and a custom broadcast loop for data distribution.
WebSocket Endpoint Registration and Connection Upgrade
The WebSocket handshake begins at a dedicated endpoint registered in the Echo router. In route/v1.go, the system attaches a GET handler to the /ws path that delegates to the connection upgrade logic.
When a client requests this endpoint, the ConnectWebSocket function in route/v1/file.go performs the protocol upgrade using Gorilla's Upgrader struct:
// route/v1/file.go
func ConnectWebSocket(ctx echo.Context) error {
upgrader := websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
ws, err := upgrader.Upgrade(ctx.Response(), ctx.Request(), nil)
if err != nil {
return err
}
// store the connection globally
service.WebSocketConns = append(service.WebSocketConns, ws)
return nil
}
The CheckOrigin callback allows connections from any origin, enabling cross-origin access for the web interface. Upon successful upgrade, the function immediately appends the resulting *websocket.Conn pointer to a global collection for later broadcasting.
Connection Storage and Global State Management
Active WebSocket connections are maintained in a package-level slice declared in service/service.go. The WebSocketConns variable holds all current client connections as an in-memory registry:
- Global accessibility: Any service can reference this slice to broadcast messages
- Dynamic growth: Connections append to the slice as clients join
- Cleanup responsibility: The broadcast loop manages removal of stale connections
This global state approach eliminates the need for a separate connection manager service, keeping the architecture simple for single-node deployments.
Real-Time Broadcast Logic in the Notify Service
The SendMeg function in service/notify.go implements the core broadcasting mechanism. Running continuously in a background goroutine, this method gathers current hardware status and pushes updates to every connected client:
// service/notify.go (simplified)
func SendMeg() {
for {
// retrieve fresh peer list
list := MyService.Notify().GetList(types.NOTIFY_APP)
payload, _ := json.Marshal(list)
// send to every open websocket
var alive []*websocket.Conn
for _, c := range service.WebSocketConns {
if err := c.WriteMessage(websocket.TextMessage, payload); err == nil {
alive = append(alive, c) // keep only healthy connections
}
}
service.WebSocketConns = alive
if len(alive) == 0 {
service.SocketRun = false
}
time.Sleep(2 * time.Second)
}
}
The function performs three critical operations:
- Data aggregation: Calls
GetListto fetch current peer and hardware status - Connection filtering: Writes the JSON payload to each socket, retaining only connections without errors
- Lifecycle management: Sets
SocketRun = falsewhen no clients remain, halting the loop until new connections arrive
This pruning strategy ensures that dead or disconnected clients do not accumulate in memory, preventing resource leaks over long runtimes.
Hardware Status Integration
When hardware events occur—such as USB device changes or peer appearances—the Peer service updates its internal model. These changes propagate to WebSocket clients through the following flow:
- The
Peerservice maintains the authoritative hardware state - The
Notifyservice queries this state viaGetList(types.NOTIFY_APP) - Every two seconds,
SendMegserializes the updated peer list into JSON - All active connections in
WebSocketConnsreceive the payload viaWriteMessage
This polling-based approach ensures that web interfaces display current hardware topology without requiring manual refreshes or complex event subscription mechanisms.
Client-Side Connection Handling
Web clients establish connections using standard browser WebSocket APIs. The JavaScript implementation connects to the host-relative /ws endpoint and handles incoming status messages:
const ws = new WebSocket('ws://<casa-os-host>/ws');
ws.onmessage = (event) => {
const hardwareStatus = JSON.parse(event.data);
console.log('Real-time status:', hardwareStatus);
};
ws.onclose = () => console.log('WebSocket closed');
Clients receive JSON payloads containing peer lists and hardware state changes, which frontend applications can render immediately to reflect system status changes.
Graceful Shutdown and Connection Lifecycle
The broadcast system implements efficient lifecycle management to conserve CPU cycles when idle. The SocketRun boolean flag controls the SendMeg goroutine execution:
- Automatic suspension: When
WebSocketConnsempties, the loop setsSocketRun = falseand stops iterating - Restart on demand: New connections hitting the
/wsendpoint reactivate the broadcast loop - Resource efficiency: No background processing occurs while no clients are listening
This design optimizes resource usage on low-power hardware typical of CasaOS deployments.
Summary
- Gorilla WebSocket handles the HTTP upgrade and low-level frame management for all connections
- The
/wsendpoint inroute/v1/file.goupgrades requests and stores connections in the globalWebSocketConnsslice SendMeginservice/notify.gobroadcasts JSON-encoded hardware status every two seconds while filtering dead connections- Automatic cleanup removes failed connections from the global slice during each broadcast iteration
- Lifecycle flags pause the broadcast loop when no clients are connected, resuming only when new WebSocket handshakes occur
Frequently Asked Questions
What WebSocket library does CasaOS use?
CasaOS uses the Gorilla WebSocket library to handle protocol upgrades and message framing. This library provides the Upgrader type used in route/v1/file.go to transform HTTP requests into persistent WebSocket connections, along with the WriteMessage method for broadcasting data to clients.
How does CasaOS handle disconnected WebSocket clients?
The system implements optimistic pruning during the broadcast phase. When SendMeg iterates through WebSocketConns, it attempts to write to each connection and collects only those without errors into a new alive slice. Failed writes indicate disconnected clients, which are automatically excluded from the updated global slice, effectively removing dead connections without explicit disconnect handlers.
What endpoint is used for WebSocket connections in CasaOS?
WebSocket connections are established at the GET /ws endpoint. This route is registered in route/v1.go and handled by the ConnectWebSocket function in route/v1/file.go. Clients must request this specific path to trigger the protocol upgrade from HTTP to WebSocket.
How often does CasaOS broadcast hardware status updates?
The broadcast loop executes every two seconds. The SendMeg function includes a time.Sleep(2 * time.Second) call at the end of each iteration, creating a polling interval that balances real-time responsiveness with CPU usage on resource-constrained devices.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →