Pusher-compatible realtime WebSocket server, built on Cloudflare Durable Objects.
All server code lives in apps/server/.
Click the button above to deploy to Cloudflare Workers. After deployment, create an app record manually:
- Go to Cloudflare Dashboard
- Navigate to Compute > Durable Objects >
DatabaseDO - Open Data Studio > Choose
By Namemethod > Enterdefaultin the input - Select the
appstable > Add Row
Fill in the row with your own key/secret pair:
| Column | Value |
|---|---|
id |
Your app id |
key |
Your app key |
secret |
Your app secret |
max_connections |
Recommended 10000 (or -1 for unlimited) |
enable_client_events |
1 (must be integer) |
NOTE: A Dashboard could be built in the future to improve this experience and track live statistics!
- Runtime: Cloudflare Workers (Hibernatable WebSockets)
- State: Durable Objects with SQLite storage
- Framework: Hono + Kysely ORM
- Protocol: Pusher Channels Protocol (compatible with all official Pusher SDKs)
Two Durable Objects power the server:
| DO | Purpose |
|---|---|
ServerDO |
WebSocket lifecycle, channel subscriptions, message broadcasting |
DatabaseDO |
App registration and auth (key/secret pairs, persisted in SQLite) |
Each app is identified by a unique key/secret pair. The server verifies HMAC-signed requests for server-to-client broadcasts.
| Method | Path | Description |
|---|---|---|
GET |
/:key/sockets |
Active socket count |
GET |
/:key/channels |
Channel subscription counts |
POST |
/:key/events |
Trigger a single event |
POST |
/:key/batch_events |
Trigger multiple events |
import Pusher from "pusher-js"
const pusher = new Pusher("APP_KEY", {
wsHost: "your-worker.workers.dev",
wssPort: 443,
forceTLS: true,
disableStats: true,
enabledTransports: ["ws"],
cluster: "socketo",
});
const channel = pusher.subscribe("my-channel").bind("my-event", (message) => {
console.log(message);
})channel.trigger("client-my-event", { message: "Hello from client" });Trigger events from your backend via Pusher:
import Pusher from "pusher"
const pusher = new Pusher({
appId: "APP_ID",
key: "APP_KEY",
secret: "APP_SECRET",
host: "your-worker.workers.dev",
useTLS: true,
});
// Trigger an event named 'my-event' on a channel called 'my-channel'
await pusher.trigger("my-channel", "my-event", {
message: "Hello from server",
});bun install-
Start the server locally:
bun run dev # all apps bun run --filter=@apps/server dev # server only
-
Run database migrations:
curl -X POST http://localhost:8787/migrate
-
Add your app record via Local Explorer:
- Go to http://localhost:8787/cdn-cgi/explorer
- Click Add Row
- Fill and save.
Deploy manually:
bun run --filter=@apps/server deploy- In-memory config caching:
AppHandlercaches the app config (key/secret,enable_client_events,max_connections, etc.) in memory after the first database read. If you update an app's configuration via Data Studio / Local Explorer while theServerDOinstance is still active (i.e., hasn't been evicted or hibernated), the running instance will continue using the old cached values until it restarts. To force a refresh, you must trigger aServerDOrestart (e.g., by deploying a new version or causing the Durable Object to hibernate and wake up).