frametogether-server

Crates.ioframetogether-server
lib.rsframetogether-server
version0.1.0
created_at2025-08-02 02:28:37.140333+00
updated_at2025-08-02 02:28:37.140333+00
descriptionSync video playback with friends!
homepage
repositoryhttps://github.com/un1970ix/frametogether-server
max_upload_size
id1778066
size106,742
bedir ekim (un1970ix)

documentation

README

FrameTogether Server

A lightweight WebSocket server for synchronizing video playback across multiple browsers. Built with Rust for performance and reliability.

This server communicates with the FrameTogether browser extension.

You can self-host FrameTogether to run your own instance, or use the default public instance hosted by the author at frametogether.19702038.xyz. The default instance temporarily stores IP addresses only in RAM for rate limiting and does not persist or share them. The author is not liable for any data handling or security issues on self-hosted or third-party instances.

Current Status: Built specifically for MUBI video synchronization. Future versions may support additional platforms.

FrameTogether is an independent project and is not affiliated with, endorsed by, or connected to MUBI or its affiliates in any way.

Features

  • Real-time video synchronization.
  • Anonymous rooms with auto-generated user names.
  • Host-based control so first user controls playback.
  • Automatic cleanup of empty rooms.
  • Rate limiting and connection management.
  • Zero configuration defaults.

Planned Features

  • Prometheus metrics endpoint for monitoring.
  • Additional video platform support.

Quick Start

Using Docker

Clone the repository:

git clone git@github.com:un1970ix/frametogether-server.git
cd frametogether-server

Start the server:

docker compose up -d

The server will start on port 47642. To use a different port:

PORT=8080 docker compose up -d

Building from Source

Clone the repository:

git clone git@github.com:un1970ix/frametogether-server.git
cd frametogether-server

Requirements:

  • Rust 1.88 or later.
cargo build --release
./target/release/frametogether-server

Configuration

Configuration via environment variables:

Variable Default Description
PORT or ADDR 47642 Server port or full address.
MAX_ROOMS 1000 Maximum concurrent rooms.
RATE_LIMIT_PER_SECOND 30 Requests per second per IP.
RUST_LOG info Logging level options are debug, info, warn, or error.

Protocol

The server uses WebSocket with JSON messages.

Client to Server

Create Room

{"type": "Create"}

Join Room

{"type": "Join", "room_id": "abc123"}

Update Video State (Host only.)

{"type": "State", "time": 120.5, "paused": false}

Leave Room

{"type": "Leave"}

Heartbeat (Keeps connection alive.)

{"type": "Heartbeat"}

Server to Client

Room Created

{
  "type": "RoomCreated",
  "room_id": "abc123"
}

Room Joined

{
  "type": "RoomJoined",
  "room_id": "abc123",
  "is_host": true,
  "your_name": "Happy Panda",
  "participants": []
}

Video Sync (Sent to non-hosts.)

{
  "type": "Sync",
  "time": 120.5,
  "paused": false
}

User Joined

{
  "type": "UserJoined",
  "user": {"name": "Clever Fox", "is_host": false}
}

User Left

{
  "type": "UserLeft",
  "user_name": "Clever Fox"
}

Error

{
  "type": "Error",
  "message": "Room not found"
}

Architecture

The server maintains all state in memory:

  • No database required.
  • Rooms are automatically cleaned up when empty.
  • Connection IDs are incremental and never reset.
  • Each connection gets a unique animal name per room. (Just for fun, I may add functionality later.)

Security

  • Rate limiting per IP address.
  • Input validation on all messages.
  • Runs as non-root user in Docker.
  • Memory-safe Rust prevents common vulnerabilities.
  • Maximum room limit prevents resource exhaustion.
  • Real IP detection works behind proxies.

Development

Run with debug logging:

RUST_LOG=debug cargo run

Format code:

cargo fmt

Check for issues:

cargo clippy

Deployment

The included compose.yml provides a production-ready setup:

  • Automatic restart on failure.
  • Health checks via /health endpoint.
  • Runs as unprivileged user.

For production, place behind a reverse proxy (Such as nginx or Caddy.) for:

  • SSL/TLS termination.
  • Additional rate limiting.
  • Load balancing multiple instances.

Reverse Proxy Configuration

For nginx:

location /sync {
    proxy_pass http://localhost:47642;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

License

See the LICENSE file for details.

Contributing

Pull requests are welcome. Please run cargo fmt and cargo clippy before submitting.

Commit count: 0

cargo fmt