Downgraded the version numbers in both package.json files to 0.0.1. This ensures proper semantic versioning to reflect an initial development phase.
Rondevu
An open signaling and tracking server for peer discovery. Enables peers to find each other through a topic-based HTTP API with Origin isolation for organizing peer-to-peer applications.
Features
- 🚀 Fast & Lightweight - Built with Hono framework
- 📂 Topic-Based Organization - Group sessions by topic for easy peer discovery
- 🔒 Origin Isolation - Sessions are isolated by HTTP Origin header to group topics by domain
- 🏷️ Peer Identification - Info field prevents duplicate connections to same peer
- 🔌 Pluggable Storage - Storage interface supports SQLite and in-memory adapters
- 🐳 Docker Ready - Minimal Alpine-based Docker image
- ⏱️ Session Timeout - Configurable session expiration from initiation time
- 🔐 Type Safe - Written in TypeScript with full type definitions
Quick Start
Using Node.js
# Install dependencies
npm install
# Run in development mode
npm run dev
# Build and run in production
npm run build
npm start
Using Docker
# Build the image
docker build -t rondevu .
# Run with default settings (SQLite database)
docker run -p 3000:3000 rondevu
# Run with in-memory storage
docker run -p 3000:3000 -e STORAGE_TYPE=memory rondevu
# Run with custom timeout (10 minutes)
docker run -p 3000:3000 -e SESSION_TIMEOUT=600000 rondevu
Using Cloudflare Workers
# Install Wrangler CLI
npm install -g wrangler
# Login to Cloudflare
wrangler login
# Create KV namespace
wrangler kv:namespace create SESSIONS
# Update wrangler.toml with the KV namespace ID
# Deploy to Cloudflare's edge network
npx wrangler deploy
See DEPLOYMENT.md for detailed instructions.
Configuration
Configuration is done through environment variables:
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3000 |
STORAGE_TYPE |
Storage backend: sqlite or memory |
sqlite |
STORAGE_PATH |
Path to SQLite database file | ./data.db |
SESSION_TIMEOUT |
Session timeout in milliseconds | 300000 |
CORS_ORIGINS |
Comma-separated list of allowed origins | * |
Example .env file
PORT=3000
STORAGE_TYPE=sqlite
STORAGE_PATH=./sessions.db
SESSION_TIMEOUT=300000
CORS_ORIGINS=https://example.com,https://app.example.com
API Documentation
See API.md for complete API documentation.
Quick Overview
List all active topics (with pagination):
curl -X GET http://localhost:3000/ \
-H "Origin: https://example.com"
# Returns: {"topics":[{"topic":"my-room","count":3}],"pagination":{...}}
Create an offer (announce yourself as available):
curl -X POST http://localhost:3000/my-room/offer \
-H "Content-Type: application/json" \
-H "Origin: https://example.com" \
-d '{"info":"peer-123","offer":"<SIGNALING_DATA>"}'
# Returns: {"code":"550e8400-e29b-41d4-a716-446655440000"}
List available peers in a topic:
curl -X GET http://localhost:3000/my-room/sessions \
-H "Origin: https://example.com"
# Returns: {"sessions":[...]}
Connect to a peer:
curl -X POST http://localhost:3000/answer \
-H "Content-Type: application/json" \
-H "Origin: https://example.com" \
-d '{"code":"550e8400-...","answer":"<SIGNALING_DATA>","side":"answerer"}'
# Returns: {"success":true}
Architecture
Storage Interface
The storage layer is abstracted through a simple interface, making it easy to implement custom storage backends:
interface Storage {
createSession(origin: string, topic: string, info: string, offer: string, expiresAt: number): Promise<string>;
listSessionsByTopic(origin: string, topic: string): Promise<Session[]>;
getSession(code: string, origin: string): Promise<Session | null>;
updateSession(code: string, origin: string, update: Partial<Session>): Promise<void>;
deleteSession(code: string): Promise<void>;
cleanup(): Promise<void>;
close(): Promise<void>;
}
Built-in Storage Adapters
SQLite Storage (sqlite.ts)
- For Node.js/Docker deployments
- Persistent file-based or in-memory
- Automatic session cleanup
- Simple and reliable
Cloudflare KV Storage (kv.ts)
- For Cloudflare Workers deployments
- Global edge storage
- Automatic TTL-based expiration
- Distributed and highly available
Custom Storage Adapters
You can implement your own storage adapter by implementing the Storage interface:
import { Storage, Session } from './storage/types';
export class CustomStorage implements Storage {
async createSession(offer: string, expiresAt: number): Promise<string> {
// Your implementation
}
// ... implement other methods
}
Development
Project Structure
rondevu/
├── src/
│ ├── index.ts # Node.js server entry point
│ ├── app.ts # Hono application
│ ├── config.ts # Configuration
│ └── storage/
│ ├── types.ts # Storage interface
│ ├── sqlite.ts # SQLite adapter
│ └── codeGenerator.ts # Code generation utility
├── Dockerfile # Docker build configuration
├── build.js # Build script
├── API.md # API documentation
└── README.md # This file
Building
# Build TypeScript
npm run build
# Run built version
npm start
Docker Build
# Build the image
docker build -t rondevu .
# Run with volume for persistent storage
docker run -p 3000:3000 -v $(pwd)/data:/app/data rondevu
How It Works
- Discover topics (optional): Call
GET /to see all active topics and peer counts - Peer A announces availability by posting to
/:topic/offerwith peer identifier and signaling data - Server generates a unique UUID code and stores the session (bucketed by Origin and topic)
- Peer B discovers available peers using
GET /:topic/sessions - Peer B filters out their own session using the info field to avoid self-connection
- Peer B selects a peer and posts their connection data to
POST /answerwith the session code - Both peers exchange signaling data through
POST /answerendpoint - Both peers poll for updates using
POST /pollto retrieve connection information - Sessions automatically expire after the configured timeout
This allows peers in distributed systems to discover each other without requiring a centralized registry, while maintaining isolation between different applications through Origin headers.
Origin Isolation
Sessions are isolated by the HTTP Origin header, ensuring that:
- Peers can only see sessions from their own origin
- Session codes cannot be accessed cross-origin
- Topics are organized by application domain
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.