mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-26 12:08:15 +00:00
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:
@@ -7,7 +7,5 @@ extension/
|
|||||||
website/
|
website/
|
||||||
scripts/
|
scripts/
|
||||||
*.md
|
*.md
|
||||||
*.bat
|
|
||||||
*.sh
|
|
||||||
.env
|
.env
|
||||||
server/.env
|
server/.env
|
||||||
|
|||||||
+27
-15
@@ -25,7 +25,7 @@ KoalaSync is a specialized tool for **synchronized video playback** across multi
|
|||||||
> [!IMPORTANT]
|
> [!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`.
|
> **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`.
|
> - **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
|
## 3. Mandatory Reading
|
||||||
Before touching any code, you MUST read the following documents in order:
|
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
|
## 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:
|
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.
|
- **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`.
|
- **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**: Developers must ensure that any changes to `shared/constants.js` are manually reflected in `content.js` after running the build script.
|
- **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.
|
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.
|
- **Font**: System font stack. **MANDATORY**: No external CDNs or Google Fonts to ensure 100% privacy.
|
||||||
- **Popup Width**: Fixed at `320px`.
|
- **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 |
|
| `--success` | `#22c55e` | Success states / Online dot |
|
||||||
| `--error` | `#ef4444` | Errors / Offline 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:
|
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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **Server Transport**: Restricted to `websocket` only. Polling is disabled.
|
||||||
- **Docker Context**: The Docker build must run from the **Repo Root**.
|
- **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`.
|
- **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`.
|
- **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.
|
- **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.
|
- **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.
|
- **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)
|
### Releasing a New Version (CRITICAL WORKFLOW FOR AI AGENTS)
|
||||||
> [!CAUTION]
|
> [!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`.
|
3. Implement the handler in `server/index.js` and `background.js`.
|
||||||
|
|
||||||
### Testing Locally
|
### Testing Locally
|
||||||
1. Load `extension/` as an "Unpacked Extension" in Chrome.
|
1. Run the build script: `node scripts/build-extension.js`.
|
||||||
2. Start the server from the root: `docker-compose up --build`.
|
2. Load `dist/chrome/` as an "Unpacked Extension" in Chrome (or `dist/firefox/` in Firefox).
|
||||||
3. Use **different browser profiles** or vendors to test multi-peer logic.
|
3. Start the server from the root: `docker-compose up --build`.
|
||||||
4. Use the **Dev tab** to verify real-time video element metadata.
|
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
|
### Locking Old Versions
|
||||||
1. Increase `APP_VERSION` in `shared/constants.js`.
|
1. Update `MIN_VERSION` in the server's `.env` file to the minimum acceptable version.
|
||||||
2. Update `MIN_VERSION` in the server's `.env` file and restart.
|
2. Restart the server. Older extensions will be rejected with a "Version too old" error.
|
||||||
|
|||||||
+27
-4
@@ -11,8 +11,19 @@ Thank you for your interest in contributing to KoalaSync! We welcome all contrib
|
|||||||
### 2. Setup
|
### 2. Setup
|
||||||
1. Clone the repository.
|
1. Clone the repository.
|
||||||
2. Run `npm install` in the root directory to install build dependencies.
|
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`.
|
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:
|
- **CRITICAL**: If you modify the constants, you MUST run the build script:
|
||||||
```bash
|
```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.
|
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.
|
- **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.
|
- **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
|
## Pull Request Process
|
||||||
1. Create a new branch for your feature or bugfix.
|
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).
|
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.
|
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
|
## 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
@@ -1,5 +1,7 @@
|
|||||||
# Privacy Policy
|
# 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.
|
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)
|
## 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.
|
- **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.
|
- **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
|
## 2. Security & Rate Limiting
|
||||||
To prevent abuse and brute-force attacks, the following data is processed:
|
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.
|
- **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
|
## 3. Extension Permissions
|
||||||
The browser extension requires the following 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.
|
- `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.
|
- **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
|
## 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 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.
|
- **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).
|
**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).
|
||||||
|
|||||||
@@ -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.
|
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
|
### 📖 Documentation & Links
|
||||||
|
|
||||||
- **[PRIVACY.md](PRIVACY.md)**: Data Handling and Privacy Policy.
|
- **[PRIVACY.md](PRIVACY.md)**: Data Handling and Privacy Policy.
|
||||||
- **[CONTRIBUTING.md](CONTRIBUTING.md)**: How to help make KoalaSync better.
|
- **[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.
|
- **[ARCHITECTURE.md](docs/ARCHITECTURE.md)**: Deep-dive into the two-phase sync and heartbeat logic.
|
||||||
- **[SECURITY.md](SECURITY.md)**: Disclosure policy and security practices.
|
- **[SECURITY.md](SECURITY.md)**: Disclosure policy and security practices.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
server:
|
server:
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -2,5 +2,6 @@
|
|||||||
|
|
||||||
This directory contains deep-dives into the KoalaSync protocol and architecture.
|
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.
|
- [ARCHITECTURE.md](ARCHITECTURE.md): Communication flows, Dual Heartbeat, and Sync logic.
|
||||||
- [SYNC_GUIDE.md](SYNC_GUIDE.md): Protocol constants and sync requirements.
|
- [SYNC_GUIDE.md](SYNC_GUIDE.md): Protocol constants and sync requirements.
|
||||||
|
|||||||
+6
-5
@@ -1,7 +1,7 @@
|
|||||||
# KoalaSync Protocol Synchronization Guide
|
# KoalaSync Protocol Synchronization Guide
|
||||||
|
|
||||||
## Why do we need to sync?
|
## 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.
|
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?
|
## What does it do?
|
||||||
The build script performs the following actions:
|
The build script performs the following actions:
|
||||||
1. Synchronizes protocol constants by copying `shared/constants.js` and `shared/blacklist.js` into `extension/shared/`.
|
1. Synchronizes protocol constants by copying `shared/constants.js`, `shared/blacklist.js`, and `shared/README.md` into `extension/shared/`.
|
||||||
2. Compiles browser-specific manifest files.
|
2. Injects `EVENTS` and `HEARTBEAT_INTERVAL` into `content.js` via marker-based replacement.
|
||||||
3. Packages the final ready-to-publish extension artifacts for Chrome and Firefox into the `dist/` directory.
|
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
|
## 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`.
|
- 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.
|
- 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.
|
- **Always run the build script** after bumping the version number to ensure both components are updated.
|
||||||
|
|||||||
+2
-2
@@ -13,8 +13,8 @@ A high-performance Node.js relay server for synchronized video playback.
|
|||||||
Copy `.env.example` to `.env` and configure your settings.
|
Copy `.env.example` to `.env` and configure your settings.
|
||||||
```bash
|
```bash
|
||||||
PORT=3000
|
PORT=3000
|
||||||
MAX_ROOMS=100
|
MAX_ROOMS=1000
|
||||||
MAX_PEERS_PER_ROOM=20
|
MAX_PEERS_PER_ROOM=50
|
||||||
MIN_VERSION=1.0.0
|
MIN_VERSION=1.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+22
-12
@@ -4,20 +4,30 @@ This directory contains constants and protocol definitions used by both the exte
|
|||||||
|
|
||||||
## Syncing with the Extension
|
## Syncing with the Extension
|
||||||
> [!IMPORTANT]
|
> [!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
|
## Security & Versioning Constants
|
||||||
- `OFFICIAL_SERVER_TOKEN`: A 32-byte hex token required to connect to the official relay server.
|
- `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.
|
- `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.
|
## Protocol Events
|
||||||
- `PAUSE`: Sync command to pause playback.
|
For the complete and current event list, see the `EVENTS` object in [`constants.js`](constants.js). Key events include:
|
||||||
- `SEEK`: Sync command to change the current time.
|
|
||||||
- `PEER_STATUS`: Heartbeat or join/leave notification for peers.
|
| Event | Direction | Purpose |
|
||||||
- `FORCE_SYNC_PREPARE`: Phase 1 of Force Sync (Pause & Seek).
|
|:------|:----------|:--------|
|
||||||
- `FORCE_SYNC_ACK`: Peer confirmation of Phase 1 readiness.
|
| `JOIN_ROOM` | Client → Server | Request to join a room with credentials |
|
||||||
- `FORCE_SYNC_EXECUTE`: Phase 2 of Force Sync (Start Playback).
|
| `LEAVE_ROOM` | Client → Server | Leave the current room |
|
||||||
- `ERROR`: Generic error message from the server.
|
| `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 |
|
||||||
|
|||||||
Reference in New Issue
Block a user