WebSocket Serverless is a fully open-source, serverless real-time messaging server compatible with the Pusher Protocol v7. Built natively for Cloudflare Workers and Cloudflare Durable Objects, it utilizes Cloudflare's WebSockets Hibernation API to deliver high-concurrency real-time channels with zero idle infrastructure costs.
Designed as a modern serverless successor to self-hosted servers like Poxa (Elixir) and standard Pusher Channels, this project allows teams to run private, scalable real-time infrastructure directly on Cloudflare's global edge network.
- Key Technical Features
- One-Click Deployment
- Manual Installation & Local Development
- Architecture & Execution Model
- Client Integration Guides
- Authentication & Access Control
- REST API Specification
- Configuration Reference
- CI/CD & Automated Versioning
- Feature Matrix
- Community & Governance
- Related Projects
- Author & License
- Pusher Protocol v7 Wire Standard: Drop-in backend replacement for Pusher client SDKs across Web, Mobile (Swift/Android), and Backend runtimes.
- WebSockets Hibernation API: Leverages Cloudflare Durable Objects hibernation to handle thousands of open TCP/WebSocket connections without incurring ongoing CPU memory charges while sockets are idle.
- Channel Routing:
- Public Channels: Unauthenticated broadcast channels.
- Private Channels (
private-*): HMAC-SHA256 authenticated channels for protected messaging. - Presence Channels (
presence-*): User tracking with automaticpusher_internal:member_addedandpusher_internal:member_removedevent fan-out. - Hono Routing Framework: Ultra-fast routing layer handling the edge REST API requests and WebSocket upgrade lifecycle.
- Pusher REST API v1: Complete implementation of publishing endpoints (
/apps/:app_id/events,/apps/:app_id/batch_events,/apps/:app_id/channels,/apps/:app_id/channels/:channel_name/users). - Serverless Webhooks Engine: Dispatches background HTTP POST payloads for channel occupancy (
channel_occupied,channel_vacated) and presence state transitions. - Integrated Admin Dashboard: Self-hosted glassmorphic web dashboard containing real-time channel metrics, interactive REST event studio, live socket debugging inspector, and code generation tools.
- Cloudflare One / Cloudflare Access Integration: Supports zero-trust authentication using
Cf-Access-Jwt-Assertionheaders alongside traditional password authentication.
Explore the interactive frontend Admin Console on GitHub Pages: 👉 https://mcontartesi.github.io/websocket-serverless/
Note
The GitHub Pages link serves a static preview of the Admin Console UI. To run the live WebSocket engine with Cloudflare Workers & Durable Objects, use the 1-Click Deployment below.
Deploy your own 100% serverless Pusher-compatible WebSocket server directly to Cloudflare:
- Node.js version 18.x or later (LTS recommended)
- Cloudflare Wrangler CLI (
npm install -g wrangler) - A Cloudflare account with Durable Objects enabled (Workers Paid plan required for production deployment)
-
Clone the repository:
git clone https://github.com/mcontartesi/websocket-serverless.git cd websocket-serverless -
Install project dependencies:
npm install
-
Start local emulation server:
npm run dev
The local development server will start at
http://localhost:8787, serving both the HTTP REST API endpoints and the static Admin Dashboard. -
Execute automated test suite:
npm test -
Deploy to Cloudflare Workers:
npm run deploy
Follow this 2-minute guide to verify real-time event broadcasting between a browser subscriber and an API publisher.
When connecting official Pusher client SDKs, pass the following configuration parameters:
| Parameter | Local Dev Value | Production Value | Description |
|---|---|---|---|
app_id |
ws-app |
Configured in wrangler.jsonc |
Primary Application Identifier |
key |
ws-key |
Configured in wrangler.jsonc |
Public Client Key |
secret |
ws-secret |
Configured in wrangler.jsonc |
Private HMAC Signature Secret |
cluster |
mt1 |
mt1 |
Required by Pusher JS v8 SDK |
wsHost |
localhost |
your-worker.workers.dev |
Worker domain host |
wsPort |
8787 |
443 |
HTTP / WS Port |
wssPort |
8787 |
443 |
HTTPS / WSS Port |
forceTLS |
false |
true |
Enforces WSS encrypted sockets |
Save the following code as test-client.html and open it in your browser:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Serverless Test</title>
<!-- Official Pusher JS Library -->
<script src="https://js.pusher.com/8.0.1/pusher.min.js"></script>
</head>
<body>
<h2>Real-Time Event Stream</h2>
<div id="status">Connecting...</div>
<div id="logs" style="font-family: monospace; background: #1e1e1e; color: #4af626; padding: 15px; border-radius: 8px; margin-top: 15px;"></div>
<script>
// Initialize Pusher Client pointing to your WebSocket Serverless endpoint
const pusher = new Pusher('ws-key', {
cluster: 'mt1',
wsHost: window.location.hostname || 'localhost',
wsPort: 8787,
wssPort: 8787,
forceTLS: false,
disableStats: true,
enabledTransports: ['ws', 'wss']
});
pusher.connection.bind('connected', () => {
document.getElementById('status').innerHTML = '<strong>Connected to WebSocket Serverless!</strong> Socket ID: ' + pusher.connection.socket_id;
});
// Subscribe to a public channel
const channel = pusher.subscribe('my-channel');
// Bind to custom event
channel.bind('my-event', function(data) {
const logs = document.getElementById('logs');
logs.innerHTML += '<div>[' + new Date().toLocaleTimeString() + '] Event received: ' + JSON.stringify(data) + '</div>';
});
</script>
</body>
</html>In your terminal, execute the following HTTP REST call:
curl -X POST "http://localhost:8787/apps/ws-app/events" \
-H "Content-Type: application/json" \
-d '{
"channel": "my-channel",
"name": "my-event",
"data": { "message": "Real-time delivery confirmed!", "timestamp": 1700000000 }
}'The event will instantly appear rendered inside the browser window on test-client.html.
Alternatively, publish events using the official pusher Node.js library:
const Pusher = require('pusher');
const pusher = new Pusher({
appId: 'ws-app',
key: 'ws-key',
secret: 'ws-secret',
host: 'localhost',
port: '8787',
useTLS: false
});
pusher.trigger('my-channel', 'my-event', {
user: 'Maximiliano',
text: 'Hello from Node.js backend!'
}); +---------------------------------------+
| Cloudflare Global Edge Network |
+-------------------+-------------------+
|
v
+-------------------+-------------------+
| Cloudflare Worker Router |
| (HTTP REST API & WS Upgrade Path) |
+---------+-------------------+---------+
| |
HTTP REST API / Auth WebSocket Upgrade
| |
v v
+--------------+---+ +-----------+--------------+
| WebsocketHub | | ChannelDO (Durable |
| Channel Registry | | Object + Hibernation) |
+------------------+ +-----------+--------------+
|
+-----------+--------------+
| Subscribed Sockets |
| (Public/Private/Presence)|
+--------------------------+
- Worker Router Layer: Evaluates incoming HTTP requests. REST API calls are authenticated and routed directly to the appropriate Durable Object instance.
- Channel Durable Object (
ChannelDO): Each Pusher channel (app_id:channel_name) maps to a specific Durable Object instance. Sockets are registered usingctx.acceptWebSocket(). - Hibernation Model: When no active frames are being transmitted, Cloudflare hibernates the Durable Object instance. Sockets remain connected at the edge, incurring zero CPU cost until an event payload arrives or a frame is sent.
For detailed architectural analysis, see docs/ARCHITECTURE.md.
Install official pusher-js package:
npm install pusher-jsInitialize connection pointing to your deployed Worker domain:
import Pusher from 'pusher-js';
const pusher = new Pusher('ws-key', {
cluster: 'mt1',
wsHost: 'your-worker.workers.dev',
wsPort: 443,
wssPort: 443,
forceTLS: true,
disableStats: true,
enabledTransports: ['ws', 'wss']
});
// Subscribe to a public channel
const channel = pusher.subscribe('orders-channel');
channel.bind('order:created', (data) => {
console.log('New Order Received:', data);
});Configure Laravel Echo in resources/js/bootstrap.js:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY || 'ws-key',
cluster: 'mt1',
wsHost: process.env.MIX_PUSHER_HOST || 'your-worker.workers.dev',
wsPort: 443,
wssPort: 443,
forceTLS: true,
disableStats: true,
enabledTransports: ['ws', 'wss']
});
window.Echo.channel('orders')
.listen('OrderPlaced', (event) => {
console.log('Order status updated:', event);
});Using official pusher Python SDK:
import pusher
pusher_client = pusher.Pusher(
app_id='ws-app',
key='ws-key',
secret='ws-secret',
host='your-worker.workers.dev',
ssl=True
)
pusher_client.trigger('chat-room', 'message:created', {
'user': 'Maximiliano',
'text': 'Serverless real-time messaging configured successfully'
})Publish events via cURL without requiring client SDKs:
curl -X POST "https://your-worker.workers.dev/apps/ws-app/events" \
-H "Content-Type: application/json" \
-d '{
"name": "notification:send",
"channel": "user-100",
"data": {"title": "System Alert", "body": "Maintenance completed"}
}'For channels prefixed with private- or presence-, client subscriptions require valid HMAC-SHA256 signatures generated using your application secret.
- Private Channel Signature:
HMAC-SHA256(socket_id + ":" + channel_name, app_secret) - Presence Channel Signature:
HMAC-SHA256(socket_id + ":" + channel_name + ":" + channel_data, app_secret)
The built-in Admin Dashboard (/) supports two authentication mechanisms:
- Password Authentication: Username (
ADMIN_USERNAME) and password (ADMIN_PASSWORD) authentication configured via environment variables. - Cloudflare One / Cloudflare Access: Sockets and HTTP requests protected behind Cloudflare Access headers (
Cf-Access-Jwt-AssertionorCf-Access-Authenticated-User-Email) authenticate automatically without manual password entry.
| Endpoint | Method | Body Payload / Description |
|---|---|---|
/apps/:app_id/events |
POST |
{"name": "string", "channel": "string", "data": any} — Broadcasts event to target channel |
/apps/:app_id/batch_events |
POST |
{"batch": [{"name": "string", "channel": "string", "data": any}]} — Batch event trigger |
/apps/:app_id/channels |
GET |
Returns list of occupied channels and member metrics |
/apps/:app_id/channels/:channel_name |
GET |
Returns occupancy metrics for target channel |
/apps/:app_id/channels/:channel_name/users |
GET |
Returns subscriber list for presence channel |
/health |
GET |
Returns server health status and runtime operational metadata |
For detailed wire protocol specs, see docs/PUSHER_COMPATIBILITY.md.
Configuration parameters are declared in wrangler.jsonc or environment secrets:
| Variable | Type | Default Value | Description |
|---|---|---|---|
DEFAULT_APP_ID |
String | ws-app |
Primary Pusher Application ID |
DEFAULT_APP_KEY |
String | ws-key |
Public client application key |
DEFAULT_APP_SECRET |
String | ws-secret |
Private HMAC signing secret |
ADMIN_USERNAME |
String | admin |
Admin Console login username |
ADMIN_PASSWORD |
String | ws-admin-secret |
Admin Console login password |
The project incorporates three automated GitHub Actions workflows:
- CI Pipeline (
.github/workflows/ci.yml): Executes TypeScript type checks (npx tsc --noEmit), Biome linter, and Vitest test suite with code coverage on all pushes and pull requests. - Code Quality Guardrails: Enforces code style and prevents bad commits using
Husky,lint-staged, and@biomejs/biome. - Automated Versioning (
.github/workflows/release.yml): Usessemantic-releaseto generate semver releases, tags, changelog updates, and GitHub Release entries. - Deployment Pipeline (
.github/workflows/deploy.yml): Deploys application updates to Cloudflare Workers using Wrangler Action upon publishing a release.
| Feature | Pusher Channels | Poxa (Elixir) | WebSocket Serverless |
|---|---|---|---|
| Runtime Infrastructure | Proprietary Cloud | Self-hosted BEAM | Cloudflare Edge Network |
| Idle Resource Cost | Monthly Flat Rate | VM CPU/RAM Allocation | $0 / Zero Idle RAM |
| Hibernation Engine | No | No | Yes (DO Hibernation) |
| Protocol Version | v7 Wire Format | v7 Wire Format | v7 Wire Format |
| Zero-Trust Auth | Third-party | Basic Auth | Native Cloudflare One |
| CI/CD Pipeline | Proprietary | Manual | GitHub Actions + SemVer |
- Contributing Guide: Code standards, PR workflow, and commit guidelines.
- Code of Conduct: Code of Conduct for contributors.
- Changelog: Historical version releases and patch notes.
- Security Policy: Vulnerability reporting procedures.
- Health Monitor: 100% Serverless Edge Uptime Monitoring & Status Page solution built for Cloudflare Workers.
Created and maintained by Maximiliano Contartesi.
- Email: maxiconta [at] gmail [dot] com
- Medium: @maxiconta
- LinkedIn: maxiconta
- GitHub: @mcontartesi
Licensed under the MIT License. Copyright (c) 2026 Maximiliano Contartesi.