# How Peer-to-Peer Connections Work Between CasaOS Instances: WebSocket Signaling Architecture

> Discover how CasaOS enables peer-to-peer connections between instances using WebSocket signaling. Learn about peer IDs, metadata parsing, and the active peer registry for seamless communication.

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

---

**CasaOS implements peer-to-peer discovery through a WebSocket-based signaling service that assigns unique peer IDs, parses device metadata, and maintains an active peer registry in SQLite, enabling instances to discover and communicate with each other.**

Understanding how peer-to-peer connections work between CasaOS instances requires examining the signaling layer that coordinates discovery without handling the actual data transfer. The architecture relies on a central WebSocket service to broker introductions between nodes, after which peers can establish direct connections via HTTP, SMB, or WebRTC using the exchanged metadata.

## WebSocket Connection Establishment

The peer-to-peer lifecycle begins when a CasaOS UI instance initiates a WebSocket connection to the central server. This connection serves as the persistent signaling channel for all subsequent peer discovery operations.

### The /ws Endpoint and Peer ID Generation

When a browser or client opens the web interface, it triggers `GET /ws` handled by `ConnectWebSocket` in [`route/v1/file.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/file.go) (lines 67-78). The server upgrades the HTTP connection to WebSocket using `upgraderFile.Upgrade`, then generates a unique identifier for the peer:

```go
// Server-side WebSocket upgrade and peer registration
func ConnectWebSocket(ctx echo.Context) error {
    conn, err := upgraderFile.Upgrade(writer, request, nil)
    // Generate or reuse peer ID
    peerID := uuid.NewString()
    // Set cookie for persistent identification
    ctx.SetCookie(&http.Cookie{
        Name:  "peerid",
        Value: peerID,
    })
}

```

The server stores this `peerid` in a cookie (`route/v1/file.go:103-108`) to maintain session continuity across reconnections.

### User-Agent Parsing and Device Metadata

Upon connection, the server extracts device metadata by parsing the `User-Agent` header. The `GetName` function in [`service/socket.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/socket.go) (lines 45-68) utilizes `useragent.Parse` to derive a human-readable display name (e.g., "Chrome Windows") along with operating system, browser model, and device type. This metadata attaches to the peer record to help users identify their devices in the network.

## Peer Data Persistence and Management

CasaOS maintains peer state in a local SQLite database using GORM, ensuring persistence across server restarts while managing the lifecycle of active connections.

### SQLite Storage with GORM

Peer metadata persists in the **`peer_drive`** table defined by `PeerDriveDBModel` in [`service/model/o_drive.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_drive.go) (lines 3-16). The `PeerService` interface in [`service/peer.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/peer.go) (lines 19-59) implements CRUD operations through methods including `CreatePeer`, `GetPeerByID`, and `DeletePeer`.

When a new WebSocket connection establishes, the server invokes `service.MyService.Peer().CreatePeer(&peerModel)` to store the peer record. For returning peers, the system looks up existing entries by ID, name, or user-agent string using `GetPeerBy*` methods.

```go
// Peer service interface definition
type PeerService interface {
    GetPeerByID(id string) model.PeerDriveDBModel
    GetPeers() []model.PeerDriveDBModel
    CreatePeer(m *model.PeerDriveDBModel)
    DeletePeer(id string)
}

```

### Peer Lifecycle and Cleanup

The system enforces a cap on stored peer records to prevent database bloat. When more than ten peers exist in the registry, `service.Peer().DeletePeer` automatically removes the oldest inactive entries. This cleanup routine ensures that the `peer_drive` table reflects only recent, relevant devices without manual intervention.

## Peer Discovery and Broadcasting

Once a peer registers, the signaling layer notifies the network and distributes the current topology to all connected clients.

### Real-time Peer List Distribution

Immediately after storing a new peer, the server broadcasts a `peer-joined` event to all connected WebSocket clients. Simultaneously, it distributes a complete `peers` list message with `type: "peers"`, marking each entry with an **online** status flag to indicate active connections. This broadcast mechanism ensures every CasaOS instance maintains real-time awareness of available peers without polling.

### HTTP API for Peer Retrieval

Clients can also fetch the current peer registry via the REST endpoint `GET /api/v1/peers` (implemented in `route/v1/file.go:1102-1109`). The `GetPeers` handler returns a JSON array of all peers, flagging currently connected WebSocket clients as online:

```go
// Retrieve online peers via HTTP API
func GetPeers(ctx echo.Context) error {
    peers := service.MyService.Peer().GetPeers()
    // Mark online status for connected clients
    return ctx.JSON(http.StatusOK, peers)
}

```

## Client Implementation Example

To connect to the CasaOS signaling layer from a new instance, clients establish a WebSocket connection and include their peer identifier:

```go
// Establish WebSocket connection from UI/client
wsURL := fmt.Sprintf("ws://%s/ws?peer=%s", serverHost, existingPeerID)
conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)

```

This connection enables the client to receive real-time updates about other CasaOS instances joining or leaving the network, including their IP addresses, display names, and connection capabilities.

## Summary

- **WebSocket signaling**: CasaOS uses a persistent WebSocket connection at `GET /ws` to manage peer presence, implemented in [`route/v1/file.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/file.go).
- **UUID identification**: Each peer receives a unique `peerid` stored in cookies and the SQLite `peer_drive` table via GORM models defined in [`service/model/o_drive.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_drive.go).
- **Metadata extraction**: The `User-Agent` header parses into device names and OS information through [`service/socket.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/socket.go), creating human-readable peer labels.
- **Automatic cleanup**: The `PeerService` in [`service/peer.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/peer.go) limits the registry to ten peers by deleting oldest entries first.
- **Dual discovery**: Peers learn about each other through broadcast WebSocket events and the `GET /api/v1/peers` HTTP endpoint.

## Frequently Asked Questions

### How does CasaOS generate unique peer identifiers?

CasaOS generates peer IDs using `uuid.NewString()` when a client first connects to the WebSocket endpoint. The server stores this identifier in a `peerid` cookie returned to the client, allowing persistent recognition across browser sessions. If a client reconnects with an existing cookie, the server retrieves the prior peer record from the SQLite database rather than creating a duplicate entry.

### What database does CasaOS use to store peer information?

CasaOS stores peer metadata in a local **SQLite** database table named `peer_drive`, defined by the `PeerDriveDBModel` struct in [`service/model/o_drive.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_drive.go). The `PeerService` interface in [`service/peer.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/peer.go) handles all database operations using GORM, providing methods to create, retrieve, and delete peer records as connections establish and terminate.

### How does CasaOS handle offline or stale peer records?

The system automatically maintains the peer registry by removing inactive entries when the count exceeds ten peers. The `DeletePeer` method removes the oldest records first, ensuring the database does not accumulate obsolete device entries. Additionally, the WebSocket connection state determines the **online** flag in peer lists, immediately reflecting disconnections without waiting for database cleanup.

### Can CasaOS instances communicate directly without the central server?

The CasaOS signaling layer only facilitates **discovery** and connection brokering. While the WebSocket service ([`route/v1/file.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/file.go)) handles peer registration and metadata exchange, actual file transfers or synchronization occur directly between instances using HTTP, SMB, or WebRTC protocols. Once peers discover each other through the signaling layer, they communicate peer-to-peer without routing data through the central server.