# How CasaOS Implements WebSocket Connections for Real-Time Hardware Status

> Learn how CasaOS uses Gorilla WebSocket for real-time hardware status updates. Discover its efficient connection management and background goroutine for live data.

- Repository: [IceWhale/CasaOS](https://github.com/IceWhaleTech/CasaOS)
- Tags: internals
- Published: 2026-06-28

---

**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`](https://github.com/IceWhaleTech/CasaOS/blob/main/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`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/file.go) performs the protocol upgrade using Gorilla's `Upgrader` struct:

```go
// 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`](https://github.com/IceWhaleTech/CasaOS/blob/main/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`](https://github.com/IceWhaleTech/CasaOS/blob/main/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:

```go
// 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:

1. **Data aggregation**: Calls `GetList` to fetch current peer and hardware status
2. **Connection filtering**: Writes the JSON payload to each socket, retaining only connections without errors
3. **Lifecycle management**: Sets `SocketRun = false` when 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 `Peer` service maintains the authoritative hardware state
- The `Notify` service queries this state via `GetList(types.NOTIFY_APP)`
- Every two seconds, `SendMeg` serializes the updated peer list into JSON
- All active connections in `WebSocketConns` receive the payload via `WriteMessage`

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:

```javascript
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 `WebSocketConns` empties, the loop sets `SocketRun = false` and stops iterating
- **Restart on demand**: New connections hitting the `/ws` endpoint 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 **`/ws` endpoint** in [`route/v1/file.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/file.go) upgrades requests and stores connections in the global `WebSocketConns` slice
- **`SendMeg`** in [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go) broadcasts 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`](https://github.com/IceWhaleTech/CasaOS/blob/main/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`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1.go) and handled by the `ConnectWebSocket` function in [`route/v1/file.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/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.