yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
Collaboration Server Setup Guide

This guide explains how to set up a WebSocket server for yaze's real-time collaboration feature, enabling multiple users to edit ROMs together.

Quick Start with yaze-server

The official collaboration server is yaze-server, a Node.js WebSocket server with:

  • Real-time session management
  • AI agent integration (Gemini/Genkit)
  • ROM synchronization and diff broadcasting
  • Rate limiting and security features

Local Development

git clone https://github.com/scawful/yaze-server.git
cd yaze-server
npm ci
npm start
# Server runs on ws://localhost:8765 (default port 8765)

Production Deployment

For production, deploy yaze-server behind an SSL proxy when possible:

  • halext-server: ws://org.halext.org:8765 (pm2 process yaze-collab, no TLS on 8765 today; front with nginx/Caddy for wss:// if desired)
  • Self-hosted: Deploy to Railway, Render, Fly.io, or your own VPS

Current halext deployment (ssh halext-server)

  • Process: pm2 yaze-collab
  • Port: 8765 (plain WS/HTTP; add TLS proxy for WSS)
  • Health: http://org.halext.org:8765/health, metrics at /metrics
  • AI: enable with GEMINI_API_KEY or AI_AGENT_ENDPOINT + ENABLE_AI_AGENT=true

Server v2.1 Features

  • Persistence: Configurable SQLite storage (SQLITE_DB_PATH env var)
  • Admin API: Protected endpoints for session/room management
  • Enhanced Health: AI status, TLS detection, persistence info in /health
  • Configurable Limits: Tunable rate limits via environment variables

Overview

The yaze web app (WASM build) supports real-time collaboration through WebSocket connections. Since GitHub Pages only serves static files, you'll need a separate WebSocket server to enable this feature.

Architecture

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User A │ │ WebSocket │ │ User B │
│ (Browser) │◄───►│ Server │◄───►│ (Browser) │
│ yaze WASM │ │ (Your Server) │ │ yaze WASM │
└─────────────────┘ └─────────────────┘ └─────────────────┘

Protocol Specification

The halext deployment (yaze-collab pm2 on port 8765) runs the official yaze-server v2.0 and speaks two compatible protocols:

  • WASM compatibility protocol – used by the current web/WASM build (flat type JSON messages, no payload wrapper).
  • Session/AI protocol – used by advanced clients (desktop/editor + AI/ROM sync/proposals) with { type, payload } envelopes.

WASM compatibility protocol (web build)

All messages are JSON with a type field and flat attributes.

Client → Server

  • create: room, name?, user, user_id, color?, password?
  • join: room, user, user_id, color?, password?
  • leave: room, user_id
  • change: room, user_id, offset, old_data, new_data, timestamp?
  • cursor: room, user_id, editor, x, y, map_id
  • ping: optional keep-alive ({ "type": "ping" })

Server → Client

  • create_response: { "type": "create_response", "success": true, "session_name": "..." }
  • join_response: { "type": "join_response", "success": true, "session_name": "..." }
  • users: { "type": "users", "list": [{ "id": "...", "name": "...", "color": "#4ECDC4", "active": true }] }
  • change: Echoed to room with timestamp added
  • cursor: Broadcast presence updates
  • error: { "type": "error", "message": "...", "payload": { "error": "..." } }
  • pong: { "type": "pong", "payload": { "timestamp": 1700000000000 } }

Notes

  • Passwords are supported (password hashed server-side); rooms are deleted when empty.
  • Rate limits: 100 messages/min/IP; 10 join/host attempts/min/IP.
  • Size limits: ROM diffs ≤ 5 MB, snapshots ≤ 10 MB. Heartbeat every 30s terminates dead sockets.

Session/AI protocol (advanced clients)

Messages use { "type": "...", "payload": { ... } }.

Key client messages

  • host_session: session_name, username, rom_hash?, ai_enabled? (default true), session_password?
  • join_session: session_code, username, session_password?
  • chat_message: sender, message, message_type?, metadata?
  • rom_sync: sender, diff_data (base64), rom_hash
  • snapshot_share: sender, snapshot_data (base64), snapshot_type
  • proposal_share / proposal_vote / proposal_update
  • ai_query: username, query (requires ENABLE_AI_AGENT plus GEMINI_API_KEY or AI_AGENT_ENDPOINT)
  • leave_session, ping

Key server broadcasts

  • session_hosted, session_joined, participant_joined, participant_left
  • chat_message, rom_sync, snapshot_shared
  • proposal_shared, proposal_vote_received, proposal_updated
  • ai_response (only when AI is enabled and configured)
  • pong, error, server_shutdown

See yaze-server/README.md for full payload examples.


Deployment Options

Self-Hosted (VPS/Dedicated Server)

  1. Install Node.js 18+
  2. Clone/copy the server code and install deps:
    npm ci
  3. Configure environment variables:

    Core Settings:

    PORT=8765 # WebSocket/HTTP port (default: 8765)
    ENABLE_AI_AGENT=true # Enable AI query handling (default: true)
    GEMINI_API_KEY=your_api_key # Gemini API key for AI responses
    AI_AGENT_ENDPOINT=http://... # Alternative: external AI endpoint

    Persistence (v2.1+):

    SQLITE_DB_PATH=/var/lib/yaze-collab.db # File-based persistence (default: :memory:)

    Rate Limiting:

    RATE_LIMIT_MAX_MESSAGES=100 # Messages per minute per IP (default: 100)
    JOIN_LIMIT_MAX_ATTEMPTS=10 # Join/host attempts per minute per IP (default: 10)

    Admin API:

    ADMIN_API_KEY=your_secret_key # Protect admin endpoints (optional)
  4. Run with PM2 for process management:
    npm install -g pm2
    pm2 start server.js --name yaze-collab --env production
    pm2 save
  5. Add TLS reverse proxy (recommended)

nginx example (translate wss:// to local ws://localhost:8765):

server {
listen 443 ssl;
server_name collab.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /ws {
proxy_pass http://localhost:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
}

Caddy example:

collab.yourdomain.com {
reverse_proxy /ws localhost:8765 {
header_up Upgrade {>Upgrade}
header_up Connection {>Connection}
}
tls you@example.com
}

Why: avoids mixed-content errors in browsers, encrypts ROM diffs/chat/passwords, and centralizes cert/ALPN handling.

Platform-as-a-Service

Platform Pros Cons
Railway Easy deploy, free tier Limited free hours
Render Free tier, auto-deploy Spins down on inactivity
Fly.io Global edge, generous free More complex setup
Deno Deploy Free, edge deployment Deno runtime only
Cloudflare Workers Free tier, global edge Durable Objects cost

Client Configuration

Method 1: JavaScript Configuration (Recommended)

Add before loading yaze:

<script>
window.YAZE_CONFIG = {
collaboration: {
serverUrl: 'ws://org.halext.org:8765' // use wss:// if you front with TLS
}
};
</script>

Method 2: Meta Tag

<meta name="yaze-collab-server" content="ws://org.halext.org:8765">

Method 3: Runtime Configuration

In your integration code:

auto& collab = WasmCollaboration::GetInstance();
collab.SetWebSocketUrl("ws://org.halext.org:8765");

Security Considerations

  • Transport: terminate TLS in front of the server (wss://). The halext deployment currently runs plain ws://org.halext.org:8765; add nginx/Caddy to secure it.
  • Built-in guardrails: 100 messages/min/IP, 10 join/host attempts/min/IP, 5 MB ROM diff limit, 10 MB snapshot limit, 30s heartbeat that drops dead sockets.
  • Passwords: supported on both protocols (password for WASM, session_password for full sessions). Hashing is SHA-256 on the server side.
  • AI: only enabled when ENABLE_AI_AGENT=true and GEMINI_API_KEY or AI_AGENT_ENDPOINT is set. Leave unset to disable AI endpoints.
  • Persistence: halext uses in-memory SQLite (sessions reset on restart). For durability, run sqlite on disk (SQLITE_DB_PATH=/var/lib/yaze-collab.db) or swap to Postgres/MySQL with a lightweight adapter. Add backups/retention for audit.
  • Authentication: front the service with an auth gateway if you need verified identities; yaze-server does not issue tokens itself.

Troubleshooting

  • Handshake issues: Match the scheme to the deployment. halext runs ws://org.halext.org:8765; use wss:// only when you have a TLS proxy forwarding Upgrade headers.
  • Health checks: curl http://org.halext.org:8765/health and /metrics to confirm the service is live.
  • TLS errors: If you front with nginx/Caddy, ensure HTTP/1.1, Upgrade/Connection headers, and a valid certificate. Remove wss:// if you have not enabled TLS.
  • Disconnects/rate limits: Server sends heartbeats every 30s and enforces limits. Check pm2 logs yaze-collab on halext for details.
  • Performance: Keep diffs under 5 MB, snapshots under 10 MB, and batch cursor updates on the client. Enable compression at the proxy if needed.

Operations Playbook (halext-friendly)

  • Status: curl http://org.halext.org:8765/health and /metrics; add /metrics scrape to Prometheus if available.
  • Logs: pm2 logs yaze-collab (rotate externally if needed).
  • Restart/Redeploy: pm2 restart yaze-collab; pm2 list to verify uptime.
  • Admin actions: Use Admin API (see below) or block abusive IPs at the proxy.
  • Scaling path: add Redis pub/sub for multi-instance broadcast; place proxy in front with sticky room affinity if you shard.

Admin API (v2.1+)

Protected endpoints for server administration. Set ADMIN_API_KEY to require authentication.

Authentication

Include the key in requests:

curl -H "X-Admin-Key: your_secret_key" http://localhost:8765/admin/sessions
# Or as query param: http://localhost:8765/admin/sessions?admin_key=your_secret_key

Endpoints

List all sessions/rooms:

GET /admin/sessions
# Response: { sessions: [...], wasm_rooms: [...], total_connections: N }

List users in a session:

GET /admin/sessions/:code/users
# Response: { code: "ABC123", type: "full"|"wasm", users: [...] }

Close a session (kick all users):

DELETE /admin/sessions/:code
# Body: { "reason": "Maintenance" } (optional)
# Response: { success: true, code: "ABC123", reason: "..." }

Kick a specific user:

DELETE /admin/sessions/:code/users/:userId
# Body: { "reason": "Violation of rules" } (optional)
# Response: { success: true, code: "ABC123", userId: "user-123", reason: "..." }

Broadcast message to session:

POST /admin/sessions/:code/broadcast
# Body: { "message": "Server maintenance in 5 minutes", "message_type": "admin" }
# Response: { success: true, code: "ABC123", recipients: N }

Halext TLS Deployment Guide

Step-by-step guide to add WSS (TLS) to the halext deployment.

Prerequisites

  • SSH access to halext-server
  • Domain DNS pointing to server (e.g., collab.halext.org or use existing org.halext.org)
  • Certbot or existing SSL certificates

Option A: nginx reverse proxy

  1. Install nginx (if not present):
    sudo apt update && sudo apt install nginx certbot python3-certbot-nginx
  2. Create nginx config:
    sudo nano /etc/nginx/sites-available/yaze-collab
    server {
    listen 80;
    server_name collab.halext.org; # or org.halext.org
    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
    }
    server {
    listen 443 ssl http2;
    server_name collab.halext.org;
    ssl_certificate /etc/letsencrypt/live/collab.halext.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/collab.halext.org/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    # WebSocket proxy
    location / {
    proxy_pass http://127.0.0.1:8765;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 86400;
    proxy_send_timeout 86400;
    }
    }
  3. Enable site and get certificate:
    sudo ln -s /etc/nginx/sites-available/yaze-collab /etc/nginx/sites-enabled/
    sudo certbot --nginx -d collab.halext.org
    sudo nginx -t && sudo systemctl reload nginx
  4. Update client config to use WSS:
    window.YAZE_CONFIG = {
    collaboration: { serverUrl: 'wss://collab.halext.org' }
    };

Option B: Caddy (simpler, auto-TLS)

  1. Install Caddy:
    sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
    sudo apt update && sudo apt install caddy
  2. Create Caddyfile:
    sudo nano /etc/caddy/Caddyfile
    collab.halext.org {
    reverse_proxy localhost:8765
    # Caddy handles TLS automatically
    }
  3. Reload Caddy:
    sudo systemctl reload caddy

Verify TLS is working

# Check health endpoint shows TLS detected
curl -s https://collab.halext.org/health | jq '.tls'
# Expected: { "detected": true, "note": "Request via TLS proxy" }
# Test WebSocket connection
wscat -c wss://collab.halext.org

PM2 Ecosystem File

For more control, use a PM2 ecosystem file:

ecosystem.config.js:

module.exports = {
apps: [{
name: 'yaze-collab',
script: 'server.js',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '500M',
env: {
NODE_ENV: 'development',
PORT: 8765
},
env_production: {
NODE_ENV: 'production',
PORT: 8765,
SQLITE_DB_PATH: '/var/lib/yaze-collab/sessions.db',
ENABLE_AI_AGENT: 'true',
// GEMINI_API_KEY: 'your_key_here',
// ADMIN_API_KEY: 'your_admin_key'
}
}]
};

Usage:

pm2 start ecosystem.config.js --env production
pm2 save
pm2 startup # Enable startup on boot

Persistence & Backup

Enable file-based persistence

# Create data directory
sudo mkdir -p /var/lib/yaze-collab
sudo chown $(whoami) /var/lib/yaze-collab
# Set environment variable
export SQLITE_DB_PATH=/var/lib/yaze-collab/sessions.db
pm2 restart yaze-collab --update-env

Backup strategy

# Daily backup cron job
echo "0 3 * * * sqlite3 /var/lib/yaze-collab/sessions.db '.backup /backups/yaze-collab-$(date +%Y%m%d).db'" | crontab -
# Retain last 7 days
echo "0 4 * * * find /backups -name 'yaze-collab-*.db' -mtime +7 -delete" | crontab -e

Health endpoint (v2.1+)

The /health endpoint now reports persistence status:

{
"status": "healthy",
"version": "2.1",
"persistence": {
"type": "file",
"path": "/var/lib/yaze-collab/sessions.db"
},
"ai": {
"enabled": true,
"configured": true,
"provider": "gemini"
},
"tls": {
"detected": true,
"note": "Request via TLS proxy"
}
}

Client UX hints

  • Surface server status in the web UI by calling /health once on load and showing: server reachable, AI enabled/disabled (from health/metrics), and whether TLS is in use.
  • Default window.YAZE_CONFIG.collaboration.serverUrl to wss://collab.yourdomain.com/ws when a TLS proxy is present; fall back to ws://localhost:8765 for local dev.
  • Show a small banner when AI is disabled or when the connection is downgraded to plain WS to set user expectations.

Example: Complete Docker Deployment

Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY server.js .
EXPOSE 8765
CMD ["node", "server.js"]

docker-compose.yml:

version: '3.8'
services:
collab:
build: .
ports:
- "8765:8765"
restart: unless-stopped
environment:
- NODE_ENV=production
- ENABLE_AI_AGENT=true
# Uncomment one of the following if AI responses are desired
# - GEMINI_API_KEY=your_api_key
# - AI_AGENT_ENDPOINT=http://ai-service:5000

Deploy:

docker-compose up -d

Testing Your Server

Use wscat to test:

npm install -g wscat
wscat -c ws://org.halext.org:8765
# Send create message
{"type":"create","room":"TEST01","name":"Test","user":"TestUser","user_id":"test-123","color":"#FF0000"}
# Check response
# < {"type":"create_response","success":true,"session_name":"Test"}
# Test full session protocol (AI disabled)
{"type":"host_session","payload":{"session_name":"DocsCheck","username":"tester","ai_enabled":false}}

Health check:

curl http://org.halext.org:8765/health
curl http://org.halext.org:8765/metrics

Or use the browser console on your yaze deployment:

window.YAZE_CONFIG = {
collaboration: { serverUrl: 'ws://org.halext.org:8765' }
};
// Then use the collaboration UI in yaze