How Peer-to-Peer Connections Work Between CasaOS Instances: WebSocket Signaling Architecture
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 (lines 67-78). The server upgrades the HTTP connection to WebSocket using upgraderFile.Upgrade, then generates a unique identifier for the peer:
// 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 (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 (lines 3-16). The PeerService interface in 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.
// 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:
// 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:
// 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 /wsto manage peer presence, implemented inroute/v1/file.go. - UUID identification: Each peer receives a unique
peeridstored in cookies and the SQLitepeer_drivetable via GORM models defined inservice/model/o_drive.go. - Metadata extraction: The
User-Agentheader parses into device names and OS information throughservice/socket.go, creating human-readable peer labels. - Automatic cleanup: The
PeerServiceinservice/peer.golimits 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/peersHTTP 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. The PeerService interface in 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) 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.
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 →