A WebSocket signaling server for y-webrtc collaborative editing. This server handles the WebRTC signaling process that enables peer-to-peer connections between clients using Yjs documents.
- Room-based signaling: Clients join named rooms for collaboration
- Peer discovery: Automatic notification when peers join/leave rooms
- Message routing: Forwards WebRTC signaling messages between peers
- Auto cleanup: Removes inactive rooms and handles disconnections
- Health monitoring: Built-in stats and health endpoints
- Lightweight: Minimal dependencies, runs on Val Town
import * as Y from 'yjs'
import { WebrtcProvider } from 'y-webrtc'
// Create a Yjs document
const ydoc = new Y.Doc()
// Connect to the signaling server
const provider = new WebrtcProvider('my-room-name', ydoc, {
signaling: ['ws://your-val-town-url.web.val.run']
})
// Use shared types
const ytext = ydoc.getText('shared-text')
const ymap = ydoc.getMap('shared-map')
import { useEffect, useState } from 'react'
import * as Y from 'yjs'
import { WebrtcProvider } from 'y-webrtc'
function CollaborativeEditor({ roomName }) {
const [ydoc] = useState(() => new Y.Doc())
const [provider, setProvider] = useState(null)
const [text, setText] = useState('')
useEffect(() => {
// Connect to signaling server
const webrtcProvider = new WebrtcProvider(roomName, ydoc, {
signaling: ['ws://your-val-town-url.web.val.run']
})
const ytext = ydoc.getText('content')
// Listen for changes
const updateText = () => setText(ytext.toString())
ytext.observe(updateText)
updateText()
setProvider(webrtcProvider)
return () => {
webrtcProvider.destroy()
}
}, [roomName, ydoc])
const handleChange = (newText) => {
const ytext = ydoc.getText('content')
ydoc.transact(() => {
ytext.delete(0, ytext.length)
ytext.insert(0, newText)
})
}
return (
<textarea
value={text}
onChange={(e) => handleChange(e.target.value)}
placeholder="Start typing to collaborate..."
/>
)
}
- URL:
ws://your-val-town-url.web.val.run
- Protocol: WebSocket upgrade from HTTP
Returns a web interface with server information and real-time statistics.
Returns JSON with current server statistics:
{ "totalRooms": 3, "totalClients": 7, "rooms": [ { "id": "room-1", "clients": 3, "lastActivity": "2024-01-15T10:30:00.000Z" } ] }
Health check endpoint:
{ "status": "ok", "timestamp": "2024-01-15T10:30:00.000Z" }
The server expects JSON messages with the following structure:
{ "type": "join", "room": "room-name", "peerId": "unique-peer-id" }
{ "type": "leave" }
{ "type": "signal", "data": { // WebRTC signaling data (offer, answer, ice candidates, etc.) } }
The server sends messages in the same format:
- Peer joined:
{ "type": "signal", "peerId": "peer-id", "data": { "type": "peer-joined" } }
- Peer left:
{ "type": "signal", "peerId": "peer-id", "data": { "type": "peer-left" } }
- Room joined:
{ "type": "signal", "data": { "type": "room-joined", "peers": ["peer1", "peer2"] } }
- Forwarded signals:
{ "type": "signal", "peerId": "sender-id", "data": {...} }
The server includes several configurable parameters:
- Room cleanup interval: 5 minutes (removes inactive rooms)
- Room inactivity timeout: 30 minutes
- Automatic ping/pong: Keeps connections alive
This server is designed to run on Val Town and will automatically:
- Handle WebSocket upgrades
- Manage room lifecycles
- Route signaling messages
- Provide monitoring endpoints
Simply deploy to Val Town and use the provided URL as your signaling server in y-webrtc configuration.
- No authentication is implemented by default
- Room names should be treated as public
- Consider adding rate limiting for production use
- WebRTC connections are peer-to-peer after signaling
- Connection fails: Ensure WebSocket URL is correct and accessible
- Peers not connecting: Check that both clients are in the same room
- Frequent disconnections: May indicate network issues or server restarts
Monitor the server logs and use the /stats
endpoint to see:
- Active rooms and client counts
- Last activity timestamps
- Connection patterns
The web interface at /
provides real-time statistics and connection information.