# Screen Sharing System — Signaling Server

Self-hostable WebSocket signaling server for the [Screen Sharing System](https://wordpress.org/plugins/screen-sharing-system/) WordPress plugin.

## Requirements

- Node.js 16+ **or** Docker

## Quick start with Docker (recommended)

```bash
docker compose up -d
```

The server listens on port **3000** by default. To change the port:

```bash
PORT=8080 docker compose up -d
```

## Quick start without Docker

```bash
npm install
node server.js
```

## WordPress plugin configuration

Go to **Screen Sharing → Settings** and set:

| Field | Value |
|-------|-------|
| WebSocket Server | `wss://your-domain.com` (or `ws://your-ip` for local) |
| WebSocket Port | `3000` (or your custom port) |

The plugin appends `/socket.io` to the URL — the signaling server accepts connections on any path.

## Reverse proxy (nginx, for production with TLS)

```nginx
server {
    listen 443 ssl;
    server_name signaling.your-domain.com;

    ssl_certificate     /etc/ssl/your-cert.pem;
    ssl_certificate_key /etc/ssl/your-key.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 3600s;
    }
}
```

Then set WebSocket Server = `wss://signaling.your-domain.com`, Port = `443`.

## Protocol reference

All messages are JSON frames over a plain WebSocket connection.

### Client → Server

| `type` | Fields | Description |
|--------|--------|-------------|
| `register` | `id` | Register with a numeric ID (generated by the plugin) |
| `getClients` | — | Request the list of connected IDs |
| `startSharing` | `offer`, `id`, `password` | Announce sharing; stores offer + password server-side |
| `viewingRequest` | `id`, `password` | Request to view sharer `id` |
| `offer` | `offer`, `target` | Forward WebRTC offer to `target` |
| `answer` | `answer`, `target` | Forward WebRTC answer to `target` |
| `candidate` | `candidate`, `target` | Forward ICE candidate to `target` |
| `requestOffer` | `target` | Ask `target` to (re)send an offer |

### Server → Client

| `type` | Fields | Description |
|--------|--------|-------------|
| `clients` | `clients[]` | List of registered IDs |
| `viewingApproved` | — | Password matched, sharer has been notified |
| `viewingDenied` | — | Wrong password or sharer not found |
| `offerRequest` | `id` | Sharer receives this; must send a fresh offer to `id` |
| `offer` | `offer`, `target` | Forwarded offer (target = original sender's ID) |
| `answer` | `answer`, `target` | Forwarded answer |
| `candidate` | `candidate`, `target` | Forwarded ICE candidate |
| `clientDisconnected` | `id` | A peer has left |

## Session recording — note on the WebM format

Recordings are saved as `.webm` files generated by the browser's `MediaRecorder` API. Because the browser writes the file as a continuous stream, the duration metadata and seek index are not embedded at the start of the file. As a result:

- **Chrome / Edge** — play the file correctly, timeline navigation works.
- **Firefox** — plays correctly in most cases.
- **VLC and other desktop players** — may play the file but timeline navigation (seeking) is often unavailable or unreliable.

**To get a fully seekable file**, run one of these after downloading:

With **ffmpeg** (recommended):
```bash
ffmpeg -i session-recording.webm -c copy session-fixed.webm
```

With **mkvmerge** (part of MKVToolNix):
```bash
mkvmerge -o session-fixed.mkv session-recording.webm
```

Both commands rebuild the index without re-encoding the video.

## Health check

```
GET http://your-host:3000/health
→ 200 OK
```
