> ## Documentation Index
> Fetch the complete documentation index at: https://songbird.studiocollective.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Realtime Collaboration

# Realtime Collaboration

Songbird supports realtime collaboration — multiple users can work on the same project simultaneously. Changes merge automatically using structured, music-aware merge strategies that understand the `.bird` file format, mixer state, and plugin state.

## How It Works

Collaboration is built on three layers:

1. **Collab Server** — A lightweight Node.js WebSocket server that manages project rooms, syncs file changes, and merges concurrent edits
2. **React Client** — A WebSocket client in the React UI that auto-pushes changes on every commit and applies remote changes
3. **Rust State Sync** — The `songbird-state` collab modules (`collab.rs`, `collab_state_sync.rs`, `collab_mixer_sync.rs`) handle file reading for push, and write + reload remote files with echo suppression via the sync engine's guard flags

```
┌──────────────┐         ┌──────────────┐
│  Songbird A  │         │  Songbird B  │
│  (User 1)    │         │  (User 2)    │
└──────┬───────┘         └──────┬───────┘
       │  WebSocket              │  WebSocket
       ▼                         ▼
┌─────────────────────────────────────────┐
│         Collaboration Server            │
│                                         │
│  ┌─────────────┐  ┌──────────────────┐  │
│  │  Room Mgmt  │  │  Merge Engine    │  │
│  │  (invite    │  │  (.bird-aware)   │  │
│  │   codes)    │  │  (.json-aware)   │  │
│  └─────────────┘  └──────────────────┘  │
│                                         │
│  ┌─────────────┐  ┌──────────────────┐  │
│  │  Presence   │  │  File State      │  │
│  │  & Cursors  │  │  (per-project)   │  │
│  └─────────────┘  └──────────────────┘  │
└─────────────────────────────────────────┘
```

## Quick Start

### 1. Start the Server

```bash theme={null}
# On your server (or locally for testing)
./utils/collab-server.sh

# Custom port
PORT=9000 ./utils/collab-server.sh --port 9000
```

The server starts on `ws://localhost:8765` by default.

### 2. Create a Room

In Songbird, open the Collaboration panel. Enter the server URL, your name, and click **Connect**. Then click **Create Room** — you'll receive an invite code (e.g., `a1b2c3d4`).

### 3. Invite a Collaborator

Share the invite code. The other user connects to the same server and clicks **Join Room** with the invite code. They receive the current project files automatically.

### 4. Collaborate

Changes sync automatically:

* Edit notes, arrangements, or structure in `.bird` → auto-synced
* Move faders, pan knobs, toggle mute/solo → auto-synced
* Tweak plugin parameters → auto-synced

## Sync Protocol

### Message Types

| Direction       | Type                      | Description                                         |
| --------------- | ------------------------- | --------------------------------------------------- |
| Client → Server | `create`                  | Create a new room (returns invite code)             |
| Client → Server | `join`                    | Join a room with invite code                        |
| Client → Server | `push`                    | Push file changes after a commit                    |
| Client → Server | `pull`                    | Request current file state                          |
| Client → Server | `presence`                | Send cursor/tool position                           |
| Client → Server | `startTunnel`             | Start a Cloudflare tunnel to expose the server port |
| Client → Server | `stopTunnel`              | Stop the active tunnel                              |
| Client → Server | `mixerUpdate`             | Real-time mixer change (vol/pan/mute/solo)          |
| Client → Server | `cursorMove`              | Figma-style cursor position update                  |
| Server → Client | `created`                 | Room created confirmation + invite code             |
| Server → Client | `joined`                  | Joined room + current files + peer list             |
| Server → Client | `update`                  | Remote peer pushed changes (merged)                 |
| Server → Client | `pushAck`                 | Push acknowledged (may include merge result)        |
| Server → Client | `presence`                | Remote peer cursor update                           |
| Server → Client | `peerJoined` / `peerLeft` | Peer connectivity                                   |
| Server → Client | `tunnelStarted`           | Tunnel active — includes public URL                 |
| Server → Client | `tunnelStopped`           | Tunnel shut down                                    |
| Server → Client | `mixerUpdate`             | Remote peer changed mixer state                     |
| Server → Client | `cursorMove`              | Remote peer cursor position                         |

### Sync Flow

```
User A edits kick pattern in verse
  → commitAndNotify("[mixer] kick pattern")
  → historyChanged event fires in React
  → collabSocket auto-push (100ms delay)
  → collabGetProjectFiles (reads .bird + .state.json + .edit.json from Rust)
  → WebSocket send → server

Server receives push:
  → Three-way merge (base vs current state vs incoming)
  → .bird: track/section-level merge
  → .json: per-property deep merge
  → Broadcast merged result to User B

User B receives update:
  → collabApplyRemoteFiles (writes files via Rust backend)
  → Rust reloads changed files (same path as undo/redo)
  → undo_redo_in_progress guard prevents echo commits
  → Local git commit records the remote change
  → historyChanged updates the UI
```

## Intelligent Merge

The server doesn't do naive line-by-line text merge. It understands Songbird's file formats.

### `.bird` File Merge

The merge engine parses `.bird` files into a structured tree:

```
bird file
├── sig (bpm, key, scale)
├── tracks (global definitions)
├── arr (section order + bar counts)
└── sections
    ├── sec intro
    │   ├── trk 1 kick: {p, v, n, t, sw, ...}
    │   └── trk 2 bass: {p, v, n, t, sw, ...}
    └── sec verse
        ├── trk 1 kick: {p, v, n, t, sw, ...}
        └── trk 2 bass: {p, v, n, t, sw, ...}
```

Merging happens at the **track-per-section** level:

| Scenario                                                                           | Result                              |
| ---------------------------------------------------------------------------------- | ----------------------------------- |
| User A edits `trk 1 kick` in `sec verse`, User B edits `trk 2 bass` in `sec verse` | ✅ Auto-merge (different tracks)     |
| User A edits `trk 1` in `sec intro`, User B edits `trk 1` in `sec verse`           | ✅ Auto-merge (different sections)   |
| User A adds a new section, User B adds a different section                         | ✅ Auto-merge (both appended)        |
| User A changes BPM, User B edits notes                                             | ✅ Both applied (independent blocks) |
| User A and B both edit `trk 1 kick` in `sec verse`                                 | 🔄 Last-write-wins                  |

### JSON File Merge (`daw.state.json`, `daw.edit.json`)

JSON files are merged with recursive deep-merge at the property level:

| Scenario                                        | Result                                |
| ----------------------------------------------- | ------------------------------------- |
| A changes track 1 volume, B changes track 2 pan | ✅ Auto-merge                          |
| A mutes track 3, B solos track 3                | ✅ Both applied (different properties) |
| A and B both change track 1 volume              | 🔄 Last-write-wins                    |

### Conflict Resolution

All conflicts use **last-write-wins** — the most recent push takes precedence. This is intentionally simple: users naturally learn to avoid stepping on each other's toes, and the granularity (track + section level) means conflicts are rare in practice.

## Presence

The server broadcasts real-time presence updates:

* **Who's online** — list of connected peers
* **Cursor position** — which section/track/tool each user is working with
* **Activity feed** — human-readable log of remote changes

Presence includes **Figma-style cursor overlays**: each peer's mouse position is rendered as a colored arrow with their name label in the arrangement view, piano roll, and mixer. Cursors fade after 3 seconds of inactivity and are removed after 10 seconds.

## Tunnel (Share Session)

The collab server has a built-in **Cloudflare Tunnel** integration via the `untun` npm package. This lets you expose the collab server to the internet with a single click — no manual Cloudflare/Tailscale setup, no port forwarding.

1. Click **Share Session (Public Tunnel)** in the Collaboration panel
2. The server spawns a Cloudflare Tunnel on the collab port (default 8765)
3. A public `https://` URL is generated and displayed — share it with collaborators
4. Remote peers connect to the tunnel URL instead of `localhost`
5. Click **Stop tunnel** when done

The tunnel is TLS-encrypted in transit and uses Cloudflare's free tier (no account required).

## Optimistic Mixer Sync

Mixer fader changes (volume, pan, mute, solo) are sent **optimistically** over the WebSocket in real time, bypassing the git commit cycle. This provides \~5-20ms latency for continuous controls like fader drags, compared to \~100-300ms via the commit path.

* **Outbound**: Local changes are throttled to \~30Hz and sent as `mixerUpdate` messages
* **Inbound**: Remote changes are applied directly to the Zustand store
* **Local lock**: While the user is dragging a slider, remote updates for that control are ignored to prevent jitter
* **Source of truth**: The git commit layer remains authoritative. Optimistic updates are just a fast preview.

## Deployment

The collab server is a single Node.js process with no external dependencies (no database, no Redis). It can run on any machine with Node.js 18+.

### Local Development

```bash theme={null}
./utils/collab-server.sh
```

### Production (Ubuntu Server)

```bash theme={null}
# Install Node.js if needed
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Clone and install
cd /opt/songbird-collab
npm install

# Run with systemd or pm2
PORT=8765 node server.js
```

### Audio Assets

Audio samples and recordings are not synced via the collab server. For large audio assets, use a cloud storage bucket (GCS or AWS S3) with Git LFS.

## Key Files

| File                                                  | Purpose                                                                           |
| ----------------------------------------------------- | --------------------------------------------------------------------------------- |
| `server/server.js`                                    | WebSocket server — room management, push/pull sync, presence, merge orchestration |
| `server/merge/bird-merge.js`                          | `.bird` parser, serializer, and three-way structured merge                        |
| `server/merge/json-merge.js`                          | Recursive deep-merge for JSON state files                                         |
| `server/test-merge.js`                                | Merge engine test suite (7 tests)                                                 |
| `react_ui/src/services/collabSocket.ts`               | WebSocket client — connect, push/pull, auto-push on commit                        |
| `react_ui/src/data/slices/collab.ts`                  | Zustand store — connection state, peers, activity feed                            |
| `react_ui/src/components/CollabPanel.tsx`             | UI — connect dialog, room create/join, peer list, tunnel controls                 |
| `react_ui/src/components/CollabCursors.tsx`           | Figma-style peer cursor overlay component + tracking hook                         |
| `react_ui/src/services/collabMixerSync.ts`            | Optimistic mixer sync — local lock, throttled broadcast, inbound apply            |
| `rust/crates/songbird-state/src/collab.rs`            | Rust collab core — state sync for push/pull                                       |
| `rust/crates/songbird-state/src/collab_state_sync.rs` | Collab state synchronization                                                      |
| `rust/crates/songbird-state/src/collab_mixer_sync.rs` | Optimistic mixer sync for collaboration                                           |
| `utils/collab-server.sh`                              | Server launch script                                                              |

## Design Decisions

* **WebSocket from React** — JavaScript has native WebSocket support. Running the WebSocket client in React reuses the existing Tauri IPC bridge for file I/O.
* **File-level sync, not git-object sync** — Sending full file contents (not git objects/patches) is simpler and the files are small text. Each client maintains its own local git history for undo/redo independently.
* **Server-side merge** — The server runs the structured merge engine. This is simpler than client-side merge and ensures a single source of truth. The tradeoff is offline editing doesn't auto-merge (but local git undo/redo still works fine).
* **Last-write-wins** — Simple, predictable, and users rarely conflict at the track/section level in practice. No complex conflict resolution UI needed.
* **Echo suppression on apply** — When applying remote files, the `undo_redo_in_progress` guard flag is set (same mechanism as undo/redo) to prevent echo-pushing the same changes back to the server.
