docs: comprehensive repository polish + step-by-step user guide

Documentation Rewrites:
- AI_INIT.md: fix duplicate section numbers, add file responsibility map,
  fix stale manual mirror instruction, add room ID constraint
- PRIVACY.md: add TL;DR statement, data retention table, explicit
  <all_urls> justification, self-hosted instance disclaimer
- CONTRIBUTING.md: add local testing guide, version warning, room ID
  constraint, bug report requirements
- shared/README.md: complete event table (all 15 events), fix stale
  manifest.json reference
- docs/SYNC_GUIDE.md: Chrome→Browser, add README.md to sync list,
  drop stale RC5 reference
- server/README.md: sync env defaults with .env.example (1000/50)

New Documentation:
- docs/HOW_IT_WORKS.md: 10-step walkthrough covering room creation,
  invitation bridge flow, synchronized playback, force sync protocol,
  heartbeat system, and episode auto-sync. Includes exact data payloads.

Infrastructure Cleanup:
- docker-compose.yml: remove deprecated version key
- .dockerignore: remove dead .bat/.sh patterns
- README.md: add self-hosting extension config tip, link HOW_IT_WORKS
This commit is contained in:
MacBook
2026-05-04 05:41:43 +02:00
parent 47ca7563b7
commit 5157428e74
11 changed files with 320 additions and 42 deletions
-2
View File
@@ -7,7 +7,5 @@ extension/
website/
scripts/
*.md
*.bat
*.sh
.env
server/.env
+27 -15
View File
@@ -25,7 +25,7 @@ KoalaSync is a specialized tool for **synchronized video playback** across multi
> [!IMPORTANT]
> **Single Source of Truth**: `shared/constants.js` and `shared/blacklist.js` are the master files. They must be synchronized to the `extension/shared/` directory using `node scripts/build-extension.js`.
> - **Extension Modules** (`background.js`, `popup.js`) import directly from `./shared/constants.js`.
> - **Content Scripts** (`content.js`) use a **manual synchronous mirror** to prevent race conditions during page load. Always verify parity after sync.
> - **Content Scripts** (`content.js`) use a **marker-injected synchronous copy** of the constants. The build script automatically replaces the marked blocks — no manual mirroring needed.
## 3. Mandatory Reading
Before touching any code, you MUST read the following documents in order:
@@ -36,10 +36,20 @@ Before touching any code, you MUST read the following documents in order:
## 4. The "Vanilla JS Mirror" Pattern
To avoid boot-time race conditions in Manifest V3 without a bundler, the following architectural trade-off is enforced:
- **Synchronous Execution**: `content.js` MUST execute synchronously to catch early media events.
- **Manual Mirroring**: `content.js` maintains a manual mirror of the `EVENTS` constants from `shared/constants.js`.
- **Maintenance**: Developers must ensure that any changes to `shared/constants.js` are manually reflected in `content.js` after running the build script.
- **Automated Injection**: The build script (`node scripts/build-extension.js`) automatically injects `EVENTS` and `HEARTBEAT_INTERVAL` into `content.js` using marker-based replacement (see `scripts/README.md` for marker details).
- **Maintenance**: After modifying `shared/constants.js`, simply run the build script. No manual mirroring is required.
## 5. Design Guidelines
## 5. File Responsibility Map
| File | Responsibility |
|:-----|:---------------|
| `background.js` | WebSocket client, state orchestrator, event router, session persistence |
| `content.js` | Video element detection, media control, event origin detection (loop prevention) |
| `popup.js` | UI rendering, user input handling, peer display, invitation link generation |
| `bridge.js` | Landing page ↔ extension communication for invitation join flow |
| `server/index.js` | Room management, message relay, rate limiting, authentication, peer lifecycle |
## 6. Design Guidelines
The popup UI follows a strict design system. Do not modify these variables or the layout structure without explicit approval.
- **Font**: System font stack. **MANDATORY**: No external CDNs or Google Fonts to ensure 100% privacy.
- **Popup Width**: Fixed at `320px`.
@@ -53,7 +63,7 @@ The popup UI follows a strict design system. Do not modify these variables or th
| `--success` | `#22c55e` | Success states / Online dot |
| `--error` | `#ef4444` | Errors / Offline dot |
## 5. Non-Negotiables (Core Logic)
## 7. Non-Negotiables (Core Logic)
The following features are critical and must not be removed or fundamentally altered:
- **Two-Phase Force Sync**: The `Prepare``ACK``Execute` flow ensures all peers are buffered before playback resumes.
- **Episode Auto-Sync**: Ensures series binges stay perfectly synced. A lobby initiates during title transitions, freezing peers until everyone is ready.
@@ -67,21 +77,22 @@ The following features are critical and must not be removed or fundamentally alt
- **SW Keep-alive**: Use of `chrome.alarms` to prevent the Manifest V3 Service Worker from suspending.
- **Diagnostics**: The "Dev" tab provides real-time access to the underlying `<video>` state for troubleshooting.
- **Persistence**: `peerId` and `username` must be stored to remain stable across sessions.
- **Room ID Format**: Room IDs are restricted to `[a-zA-Z0-9-]` only (alphanumeric + hyphens). This is enforced server-side.
## 6. Technical Constraints
## 8. Technical Constraints
- **No Bundler**: The extension uses plain ES Modules. Do not introduce build steps or npm packages into the `extension/` folder.
- **Manual Protocol**: `background.js` implements a subset of the Socket.IO wire protocol natives.
- **Manual Protocol**: `background.js` implements a subset of the Socket.IO wire protocol natively.
- **Server Transport**: Restricted to `websocket` only. Polling is disabled.
- **Docker Context**: The Docker build must run from the **Repo Root**.
- **Manifest Settings**: `run_at` must remain `document_idle`, and `all_frames` must remain `false`.
## 7. Security & Deployment
## 9. Security & Deployment
- **Tokens**: Security tokens are intentionally managed via `shared/constants.js` and server `.env`.
- **Environment**: `.env` is excluded via `.gitignore`. Only `.env.example` should be committed.
- **Revocation**: `MIN_VERSION` check on the server is used to deprecate old extension versions.
- **Invitation Links**: Correctly propagate server URLs, Room IDs, and Passwords via the URL hash to the bridge.
## 8. Common Workflows
## 10. Common Workflows
### Releasing a New Version (CRITICAL WORKFLOW FOR AI AGENTS)
> [!CAUTION]
@@ -98,11 +109,12 @@ The following features are critical and must not be removed or fundamentally alt
3. Implement the handler in `server/index.js` and `background.js`.
### Testing Locally
1. Load `extension/` as an "Unpacked Extension" in Chrome.
2. Start the server from the root: `docker-compose up --build`.
3. Use **different browser profiles** or vendors to test multi-peer logic.
4. Use the **Dev tab** to verify real-time video element metadata.
1. Run the build script: `node scripts/build-extension.js`.
2. Load `dist/chrome/` as an "Unpacked Extension" in Chrome (or `dist/firefox/` in Firefox).
3. Start the server from the root: `docker-compose up --build`.
4. Use **different browser profiles** or vendors to test multi-peer logic.
5. Use the **Dev tab** to verify real-time video element metadata.
### Locking Old Versions
1. Increase `APP_VERSION` in `shared/constants.js`.
2. Update `MIN_VERSION` in the server's `.env` file and restart.
1. Update `MIN_VERSION` in the server's `.env` file to the minimum acceptable version.
2. Restart the server. Older extensions will be rejected with a "Version too old" error.
+27 -4
View File
@@ -11,8 +11,19 @@ Thank you for your interest in contributing to KoalaSync! We welcome all contrib
### 2. Setup
1. Clone the repository.
2. Run `npm install` in the root directory to install build dependencies.
3. Run the build script to synchronize protocol constants and generate browser bundles:
```bash
node scripts/build-extension.js
```
### 3. Protocol Synchronization
### 3. Testing Locally
1. Load `dist/chrome/` as an "Unpacked Extension" in Chrome (`chrome://extensions/` → Developer Mode → Load Unpacked).
2. For Firefox, load `dist/firefox/` via `about:debugging` → "Load Temporary Add-on".
3. Start the relay server: `docker-compose up --build`.
4. Use **two different browser profiles** (or Chrome + Firefox) to test multi-peer synchronization.
5. Use the extension's **Dev tab** to verify real-time video element metadata (`readyState`, `currentTime`, `paused`).
### 4. Protocol Synchronization
KoalaSync uses a "Single Source of Truth" for protocol constants in `shared/constants.js`.
- **CRITICAL**: If you modify the constants, you MUST run the build script:
```bash
@@ -20,10 +31,15 @@ KoalaSync uses a "Single Source of Truth" for protocol constants in `shared/cons
```
This will automatically synchronize the changes to the extension and generate the browser-specific bundles in the `dist/` folder.
### 4. Code Standards
### 5. Code Standards
- **Vanilla JS**: The extension must remain dependency-free. Do not add npm packages to the `extension/` directory.
- **Privacy**: Do not add external requests (CDNs, fonts, etc.).
- **Privacy**: Do not add external requests (CDNs, fonts, analytics, etc.).
- **Comments**: Maintain the existing documentation style, especially for complex sync logic.
- **Room IDs**: Room IDs are restricted to `[a-zA-Z0-9-]` (alphanumeric + hyphens only). Ensure any UI that generates room IDs follows this constraint.
### 6. Version Numbers
> [!IMPORTANT]
> **Do NOT manually bump version numbers.** The CI pipeline automatically injects the version from the git tag into `manifest.base.json`, `shared/constants.js`, and `package.json` during release builds. Manually changing version numbers in a PR will cause conflicts.
## Pull Request Process
1. Create a new branch for your feature or bugfix.
@@ -31,5 +47,12 @@ KoalaSync uses a "Single Source of Truth" for protocol constants in `shared/cons
3. Update relevant documentation (e.g., `docs/ARCHITECTURE.md` if you change the protocol).
4. Submit your PR with a clear description of the changes.
## Bug Reports
When reporting a bug, please include:
- **Browser**: Chrome / Firefox / Edge + version number.
- **Extension Version**: Visible in the popup's Dev tab.
- **Dev Tab Output**: Copy the connection status, logs, and video debug info from the Dev tab.
- **Steps to Reproduce**: A clear sequence of actions that triggers the issue.
## Security
If you find a security vulnerability, please do not open a public issue. Instead, refer to our `SECURITY.md` for disclosure instructions.
If you find a security vulnerability, please do not open a public issue. Instead, refer to our [SECURITY.md](SECURITY.md) for responsible disclosure instructions.
+19 -1
View File
@@ -1,5 +1,7 @@
# Privacy Policy
**KoalaSync does not collect, store, or sell any personal data.**
KoalaSync is designed with a **Security-First & Volatile** architecture. This means we prioritize keeping your data out of persistent storage, though certain technical data must be processed temporarily to ensure service stability and security.
## 1. Data Processing (In-Memory Only)
@@ -8,6 +10,16 @@ KoalaSync does not use a database. All active session data exists only in the se
- **Room Passwords**: If you set a room password, it is stored only as a secure **bcrypt hash** in RAM. The server never sees or stores your plaintext password.
- **Routing Maps**: The server maintains ephemeral lookup tables (`socketToRoom`, `peerToSocket`) to route messages between peers. These contain only transport identifiers and are purged on disconnect.
### Data Retention
| Data Type | Maximum Retention | Trigger for Deletion |
|:----------|:------------------|:---------------------|
| Session data (peerId, username, video metadata) | Duration of session | User leaves room or disconnects |
| Room state | 2 hours max | Last peer leaves, or inactivity timeout |
| Failed auth lockout records | 15 minutes | Automatic expiry |
| Auth failure records | 1 hour | Periodic cleanup |
| Connection rate-limit counters | 60 seconds | Automatic expiry |
| Event rate-limit counters | 10 seconds | Automatic expiry + periodic cleanup |
## 2. Security & Rate Limiting
To prevent abuse and brute-force attacks, the following data is processed:
- **Brute-Force Protection**: If multiple failed password attempts are detected, the server stores the `IP address` and `Room ID` in a temporary RAM-based lockout list for a maximum of 15 minutes.
@@ -17,8 +29,11 @@ To prevent abuse and brute-force attacks, the following data is processed:
## 3. Extension Permissions
The browser extension requires the following permissions:
- `storage`: To remember your local preferences.
- `storage`: To remember your local preferences (username, server URL, room settings).
- `tabs` & `scripting`: To detect and control video elements on the pages you choose to sync.
- `<all_urls>` (host permission): Required to detect `<video>` elements on any website the user chooses to synchronize. The extension only activates on the specific tab the user has actively selected — it does not scan, monitor, or interact with any other tabs or pages.
- `alarms`: To keep the background service worker alive during active sync sessions.
- `notifications`: To display sync status updates (e.g., "Peer joined", "Force Sync initiated").
- **No History Access**: We do not read, store, or transmit your browsing history. We only interact with the specific tab you have actively selected for synchronization.
## 4. Zero Third-Party Requests
@@ -27,6 +42,9 @@ KoalaSync is completely self-contained:
- **No Analytics**: We do not use Google Analytics, tracking pixels, or any third-party telemetry.
- **No External Fonts**: We use system font stacks to prevent tracking via font services.
## 5. Self-Hosted Instances
This privacy policy applies to the **official KoalaSync relay server** at `sync.shik3i.net`. If you choose to self-host a relay server using our open-source Docker image, the data handling practices of that instance are the responsibility of the server operator.
---
**Auditable & Open Source**: Because KoalaSync is open source, you can verify these claims by reviewing the [Server Source Code](https://github.com/Shik3i/KoalaSync/blob/main/server/index.js) and the [Extension Logic](https://github.com/Shik3i/KoalaSync/blob/main/extension/content.js).
+3
View File
@@ -67,12 +67,15 @@ docker-compose up -d
```
The server will be available at `ws://localhost:3000`. See [docker-compose.example.yml](docker-compose.example.yml) for advanced configuration.
To connect your extension to a self-hosted server, open the popup → **Room** tab → select **Custom Server** → enter your server's WebSocket URL (e.g., `ws://localhost:3000`).
---
### 📖 Documentation & Links
- **[PRIVACY.md](PRIVACY.md)**: Data Handling and Privacy Policy.
- **[CONTRIBUTING.md](CONTRIBUTING.md)**: How to help make KoalaSync better.
- **[HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md)**: Step-by-step walkthrough of the complete user flow.
- **[ARCHITECTURE.md](docs/ARCHITECTURE.md)**: Deep-dive into the two-phase sync and heartbeat logic.
- **[SECURITY.md](SECURITY.md)**: Disclosure policy and security practices.
-1
View File
@@ -1,4 +1,3 @@
version: '3.8'
services:
server:
+213
View File
@@ -0,0 +1,213 @@
# KoalaSync — How It Works (Step-by-Step)
This guide walks through the complete user flow of KoalaSync, from creating a room to synchronized playback. It is designed for **store reviewers**, **end-users**, and **manual testers** to understand exactly what happens at each step, what data is sent, and where it goes.
---
## Step 1: Installing the Extension
1. Download the extension from the [Releases](https://github.com/Shik3i/KoalaSync/releases) page (or install from the Chrome Web Store / Firefox Add-ons).
2. The extension adds a small icon to your browser toolbar.
3. On first install, a unique 8-character **Peer ID** is generated locally and stored in `chrome.storage.local`. This ID is never sent to any external service — it only travels to the relay server when you join a room.
> **What's stored locally**: `peerId` (8-char hex), `username` (customizable), `serverUrl`, `filterNoise` preference. All stored via `chrome.storage.sync` and `chrome.storage.local`.
---
## Step 2: Connecting to the Relay Server
When you open the extension popup, the background service worker connects to the relay server:
1. **WebSocket Handshake**: `background.js` opens a WebSocket to `wss://sync.shik3i.net/socket.io/?EIO=4&transport=websocket`.
2. **Security Checks** (server-side):
- The server checks the client's **IP rate limit** (max 10 connections per 60 seconds).
- The server validates the **authentication token** (hardcoded in `shared/constants.js`) to verify this is a legitimate KoalaSync client.
- The server checks the **extension version** against `MIN_VERSION` to reject outdated clients.
3. **Connection Established**: The server responds with an Engine.IO handshake (`0{...}`), followed by a Socket.IO namespace join (`40`). The connection status dot in the popup turns green.
> **Data sent to server**: `token` (authentication), `version` (e.g., `1.3.1`). No personal data is transmitted during connection.
---
## Step 3: Creating a Room
Click **"Create Room"** in the popup's Room tab:
1. The extension generates a random Room ID (e.g., `happy-koala-42`) and a random 6-character password.
2. Room IDs are restricted to `[a-zA-Z0-9-]` (alphanumeric + hyphens only).
3. The extension emits a `JOIN_ROOM` event to the server.
> **Data sent in `JOIN_ROOM`**:
> ```json
> {
> "roomId": "happy-koala-42",
> "password": "x7k2m9",
> "peerId": "a1b2c3d4",
> "username": "MyName",
> "tabTitle": "YouTube - My Video",
> "protocolVersion": "1.0.0"
> }
> ```
4. **Server-side processing**:
- All fields are **sanitized**: `roomId` is stripped of invalid characters and clamped to 64 chars; `peerId` clamped to 16 chars; `password` clamped to 128 chars; `username` clamped to 30 chars.
- The server **hashes the password** with bcrypt and stores the hash in RAM (the plaintext is never stored).
- A new room object is created in memory with the peer's data.
- The server responds with `ROOM_DATA` containing the list of peers in the room.
5. **Popup updates**: The Room tab switches to the "Active Room" view, showing your Room ID and an invitation link.
---
## Step 4: Sharing an Invitation Link
Click the **📋 Copy** button next to the invite link:
1. The extension constructs a URL in this format:
```
https://koalasync.shik3i.net/join.html#join:<roomId>:<password>:<serverFlag>:<encodedServerUrl>
```
- `serverFlag`: `0` for official server, `1` for custom server.
- `encodedServerUrl`: Only populated if using a custom server.
2. **Important**: The room credentials are in the **URL hash** (`#`), which means they are **never sent to the web server** — the hash fragment stays entirely in the browser. The landing page server never sees your room ID or password.
3. Send this link to your friend via any messaging app.
---
## Step 5: Your Friend Opens the Invitation Link
When your friend opens the link in their browser:
1. **`join.html` loads** on `koalasync.shik3i.net`. The page displays "INVITATION DETECTED" with the Room ID.
2. **Extension detection**: The page checks for `document.documentElement.dataset.koalasyncInstalled`, which is set by `bridge.js` (a content script injected only on `koalasync.shik3i.net`).
3. **If the extension IS installed**:
- The page shows "Joining room automatically..."
- After 500ms, the page dispatches a `KOALASYNC_JOIN_REQUEST` custom DOM event with `{ roomId, password, useCustomServer, serverUrl }`.
- `bridge.js` catches this event and forwards it to `background.js` via `chrome.runtime.sendMessage`.
- `background.js` stores the credentials in `chrome.storage.sync` and emits `JOIN_ROOM` to the server.
- The server validates the password against the stored bcrypt hash.
- On success, the server responds with `ROOM_DATA` and broadcasts `PEER_STATUS { status: 'joined' }` to all existing peers.
- The join page updates to show "✅ Successfully joined!".
4. **If the extension is NOT installed**:
- The page shows download links (Chrome Web Store / GitHub).
- The user installs the extension, returns to the link, and the flow continues from step 3.
---
## Step 6: Selecting a Video Tab
Both users now need to select which browser tab contains the video to sync:
1. Open a video on any website (YouTube, Twitch, Netflix, etc.).
2. In the extension popup → **Sync** tab → use the **"Target Tab"** dropdown.
3. The dropdown lists all open tabs, filtered to exclude noise (search engines, social media — configurable via Settings).
4. Tabs with a **matching video title** are highlighted with a ⭐ prefix for easy identification.
5. Selecting a tab causes `background.js` to set `currentTabId` and inject `content.js` into that tab via `chrome.scripting.executeScript`.
> **What `content.js` does on injection**: Finds the first `<video>` element on the page and attaches event listeners for `play`, `pause`, `seeked`, `timeupdate`, and `volumechange`. It uses an `expectedEvents` Set to distinguish between user actions and programmatic actions (loop prevention).
---
## Step 7: Synchronized Playback
When User A presses **Play** on their video:
1. `content.js` detects the native `play` event on the `<video>` element.
2. It checks the `expectedEvents` Set — if this event was expected (caused by a remote command), it's consumed silently. If not, it's a **user action**.
3. For user actions, `content.js` sends `{ type: 'CONTENT_EVENT', action: 'play', payload: { currentTime, ... } }` to `background.js`.
4. `background.js` adds an `actionTimestamp` and emits the `PLAY` event to the server.
5. **Server relay**: The server sanitizes all fields (strings clamped, numbers validated, booleans type-checked) and constructs a clean `relayPayload` with `senderId` set to User A's `peerId`. The raw client data is never forwarded directly.
6. The server broadcasts the sanitized payload to all other peers in the room.
7. User B's `background.js` receives the `PLAY` event and calls `routeToContent()`, which sends a `SERVER_COMMAND` message to User B's `content.js`.
8. User B's `content.js` adds `'playing'` to its `expectedEvents` Set (so it won't echo the event back), then calls `video.play()`.
> **The same flow applies to Pause and Seek**, with Seek additionally sending `targetTime` for the time position.
---
## Step 8: Force Sync (Two-Phase Protocol)
If videos drift out of sync, either user can click **"Force Sync"**:
### Phase 1 — Prepare
1. The initiator's `content.js` captures the current `video.currentTime` as the `targetTime`.
2. `background.js` emits `FORCE_SYNC_PREPARE` with `{ targetTime }` to all peers.
3. All peers (including the initiator) **pause** their video and **seek** to `targetTime`.
4. Each peer's `content.js` polls `video.readyState` until it reaches `≥ 3` (buffered enough to play), with an 8-second timeout.
5. Once buffered, each peer sends `FORCE_SYNC_ACK` back.
### Phase 2 — Execute
6. Once all ACKs are received (or after 8.5 seconds), the initiator emits `FORCE_SYNC_EXECUTE`.
7. All peers call `video.play()` simultaneously, achieving frame-perfect sync.
> **Why two phases?** Without buffering confirmation, peers with slower connections would start playing before they've loaded the target timestamp, causing immediate desync.
---
## Step 9: Heartbeat & Peer Health
While in a room, two heartbeats keep the session alive:
| Heartbeat | Interval | Source | Purpose |
|:----------|:---------|:-------|:--------|
| **Background** | 30 seconds | `background.js` | Signals "I'm still connected" even without a video |
| **Content** | 15 seconds | `content.js` | Sends video metadata: `currentTime`, `mediaTitle`, `playbackState`, `volume`, `muted` |
- **Server Reaper**: Every 2 minutes, the server checks for peers with no activity for 5+ minutes and disconnects them ("dead peer pruning").
- **Room Cleanup**: Empty rooms are deleted immediately. Inactive rooms are pruned after 2 hours.
---
## Step 10: Leaving a Room
When a user clicks **"Leave"** or closes their browser:
1. `background.js` emits `LEAVE_ROOM` (or the WebSocket `disconnect` fires automatically).
2. The server calls `removePeerFromRoom()`, which:
- Removes the peer from the room's `peers` Set, `peerIds` Map, and `peerData` Map.
- Removes the socket from the global `socketToRoom` and `peerToSocket` maps.
- Broadcasts `PEER_STATUS { status: 'left' }` to remaining peers.
- If the room is now empty, **deletes the room entirely** — no data persists.
3. The event rate-limit counter for that socket is also cleaned up.
> **After disconnect, zero data about the user remains on the server.** There is no database, no log file, no analytics record. The session existed only in RAM and is now gone.
---
## Episode Auto-Sync Flow
When watching a series and an episode ends:
1. `content.js` monitors the [Media Session API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Session_API) for title changes.
2. When a new title is detected, the peer broadcasts `EPISODE_LOBBY` with the expected new title.
3. All peers' videos freeze. The UI shows an "Episode Lobby" card with peer readiness status.
4. Each peer's `content.js` polls for the new title to appear in the `<video>` element's metadata.
5. Once a peer detects the matching title, they send `EPISODE_READY`.
6. When all peers report ready, the lobby resolves and playback resumes simultaneously.
---
## Data Flow Summary
```
┌─────────────┐ WebSocket ┌──────────────┐ WebSocket ┌─────────────┐
│ Extension │ ←─────────────────→│ Relay Server │←──────────────────→│ Extension │
│ (User A) │ JOIN_ROOM │ (RAM only) │ JOIN_ROOM │ (User B) │
│ │ PLAY/PAUSE/SEEK │ │ PLAY/PAUSE/SEEK │ │
│ │ FORCE_SYNC_* │ Sanitizes & │ FORCE_SYNC_* │ │
│ │ PEER_STATUS │ relays only │ PEER_STATUS │ │
│ │ EPISODE_* │ │ EPISODE_* │ │
└──────┬──────┘ └───────────────┘ └──────┬──────┘
│ │
┌────┴─────┐ ┌─────┴────┐
│ content │ Listens to <video> events │ content │
│ .js │ Controls playback │ .js │
└──────────┘ └──────────┘
```
> **The relay server is a pure message forwarder.** It never interprets video content, accesses URLs, or stores session history. All media control happens locally inside each user's browser via the `<video>` DOM API.
+1
View File
@@ -2,5 +2,6 @@
This directory contains deep-dives into the KoalaSync protocol and architecture.
- [HOW_IT_WORKS.md](HOW_IT_WORKS.md): Step-by-step walkthrough of every user flow, from room creation to synchronized playback. Ideal for store reviewers and manual testers.
- [ARCHITECTURE.md](ARCHITECTURE.md): Communication flows, Dual Heartbeat, and Sync logic.
- [SYNC_GUIDE.md](SYNC_GUIDE.md): Protocol constants and sync requirements.
+6 -5
View File
@@ -1,7 +1,7 @@
# KoalaSync Protocol Synchronization Guide
## Why do we need to sync?
KoalaSync uses a "Single Source of Truth" for its communication protocol constants located in the root `shared/` directory. However, Chrome Extensions (Manifest V3) are strictly sandboxed and **cannot load or import files from outside their root directory**.
KoalaSync uses a "Single Source of Truth" for its communication protocol constants located in the root `shared/` directory. However, Browser Extensions (Manifest V3) are strictly sandboxed and **cannot load or import files from outside their root directory**.
To ensure that the extension and the relay server are always using the exact same event names and protocol versions, we maintain a mirrored copy of the shared files within the `extension/shared/` folder.
@@ -22,12 +22,13 @@ node scripts/build-extension.js
## What does it do?
The build script performs the following actions:
1. Synchronizes protocol constants by copying `shared/constants.js` and `shared/blacklist.js` into `extension/shared/`.
2. Compiles browser-specific manifest files.
3. Packages the final ready-to-publish extension artifacts for Chrome and Firefox into the `dist/` directory.
1. Synchronizes protocol constants by copying `shared/constants.js`, `shared/blacklist.js`, and `shared/README.md` into `extension/shared/`.
2. Injects `EVENTS` and `HEARTBEAT_INTERVAL` into `content.js` via marker-based replacement.
3. Compiles browser-specific manifest files.
4. Packages the final ready-to-publish extension artifacts for Chrome and Firefox into the `dist/` directory.
## Protocol Versioning
As of v1.0.0-RC5, the system enforces a strict `protocolVersion` check during the `JOIN_ROOM` handshake.
The system enforces a strict `protocolVersion` check during the `JOIN_ROOM` handshake.
- The version is defined in `shared/constants.js`.
- If the extension and server versions mismatch, the server will reject the connection with an `Incompatible protocol version` error.
- **Always run the build script** after bumping the version number to ensure both components are updated.
+2 -2
View File
@@ -13,8 +13,8 @@ A high-performance Node.js relay server for synchronized video playback.
Copy `.env.example` to `.env` and configure your settings.
```bash
PORT=3000
MAX_ROOMS=100
MAX_PEERS_PER_ROOM=20
MAX_ROOMS=1000
MAX_PEERS_PER_ROOM=50
MIN_VERSION=1.0.0
```
+22 -12
View File
@@ -4,20 +4,30 @@ This directory contains constants and protocol definitions used by both the exte
## Syncing with the Extension
> [!IMPORTANT]
> Every time this file is modified, you must run `node scripts/build-extension.js` to keep the extension's copy up to date.
> Every time this directory is modified, you must run `node scripts/build-extension.js` to keep the extension's copy up to date.
Because Chrome Extensions cannot load files outside their root directory, `constants.js` must be copied to `extension/shared/constants.js` whenever it is modified.
Because Browser Extensions (Manifest V3) cannot load files outside their root directory, all files in this directory must be copied to `extension/shared/` whenever they are modified. The build script handles this automatically.
## Security & Versioning Constants
- `OFFICIAL_SERVER_TOKEN`: A 32-byte hex token required to connect to the official relay server.
- `APP_VERSION`: The current version of the extension. Used by the server to enforce minimum version requirements (Revocation). This must always be in sync with `manifest.json`.
- `APP_VERSION`: The current version of the extension. Automatically injected from the git tag during CI release builds.
- `OFFICIAL_SERVER_URL`: The default endpoint for the official KoalaSync relay.
- `ROOM_DATA`: Server response with current room state (peers).
- `PLAY`: Sync command to start playback.
- `PAUSE`: Sync command to pause playback.
- `SEEK`: Sync command to change the current time.
- `PEER_STATUS`: Heartbeat or join/leave notification for peers.
- `FORCE_SYNC_PREPARE`: Phase 1 of Force Sync (Pause & Seek).
- `FORCE_SYNC_ACK`: Peer confirmation of Phase 1 readiness.
- `FORCE_SYNC_EXECUTE`: Phase 2 of Force Sync (Start Playback).
- `ERROR`: Generic error message from the server.
## Protocol Events
For the complete and current event list, see the `EVENTS` object in [`constants.js`](constants.js). Key events include:
| Event | Direction | Purpose |
|:------|:----------|:--------|
| `JOIN_ROOM` | Client → Server | Request to join a room with credentials |
| `LEAVE_ROOM` | Client → Server | Leave the current room |
| `ROOM_DATA` | Server → Client | Current room state (peers list) |
| `PLAY` / `PAUSE` / `SEEK` | Bidirectional relay | Media control commands |
| `PEER_STATUS` | Bidirectional relay | Heartbeat or join/leave notification |
| `FORCE_SYNC_PREPARE` | Bidirectional relay | Phase 1: Pause & seek to target time |
| `FORCE_SYNC_ACK` | Bidirectional relay | Phase 1 confirmation: peer is buffered |
| `FORCE_SYNC_EXECUTE` | Bidirectional relay | Phase 2: Resume playback simultaneously |
| `EVENT_ACK` | Server → Client | Delivery confirmation for UI feedback |
| `EPISODE_LOBBY` | Bidirectional relay | Episode transition: waiting for all peers |
| `EPISODE_READY` | Bidirectional relay | Episode confirmation: peer has loaded |
| `GET_ROOMS` / `ROOM_LIST` | Client ↔ Server | Room discovery |
| `ERROR` | Server → Client | Error message |