Skip to content

oneaxxall/pulse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

8 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Pulse β€” Self-Hosted Pusher-Compatible WebSocket Server

License: AGPL v3 Node Version TypeScript Bundle

Pulse is a high-performance, self-hosted WebSocket server that implements the Pusher protocol for real-time bidirectional messaging. It is a fork of Soketi with significant enhancements, modernized dependencies, and production-ready features built on top of uWebSockets.js β€” a C++-compiled WebSocket and HTTP library.

It provides a drop-in replacement for Pusher.com Channels, allowing you to run your own real-time infrastructure behind your firewall, on your own hardware, or in any cloud environment.


✨ Features

  • Pusher Protocol Compatible β€” Works seamlessly with Pusher JS SDK, Laravel Echo, and Pusher backend SDKs (PHP, Node.js, Python, etc.)
  • High Performance β€” Powered by uWebSockets.js, a C++-native engine capable of tens of thousands of concurrent connections
  • Node.js v18 – v24 Support β€” Fully compatible with the latest Node.js runtimes up to version 24
  • Channel Types β€” Public, Private, Encrypted Private, and Presence channels
  • Client Events β€” Enable client-to-client messaging on private channels
  • Horizontal Scaling β€” Multiple adapters: Local, Redis, NATS, Cluster
  • Rate Limiting β€” Granular rate limiting for backend events, client events, and read requests
  • Webhooks β€” HTTP webhook notifications with optional AWS Lambda support
  • Webhook Logger β€” Persist webhook deliveries to daily log files (webhook-YYYY-MM-DD.log) or database (MySQL/PostgreSQL)
  • Production-Ready Bundling β€” Single-file bundle via esbuild for zero-dependency deployment
  • Prometheus Metrics β€” Expose real-time server metrics on a dedicated HTTP endpoint
  • Queue System β€” Async event processing via sync or Redis (BullMQ)
  • Multi-App Support β€” Serve multiple applications with isolated credentials
  • Graceful Shutdown β€” Drain active connections before terminating
  • SSL/TLS Support β€” Serve secure WSS connections
  • Docker Support β€” Containerized deployment ready
  • PM2 Support β€” Process management via PM2 clustering
  • CORS β€” Fully configurable CORS headers

πŸ“‹ Table of Contents


Relationship to Soketi

Pulse is a fork of Soketi β€” an excellent open-source WebSocket server. This fork introduces additional features and improvements that align with specific production requirements. We are grateful to the Soketi maintainers and contributors for their foundational work.

Key Differences

Aspect Soketi Pulse
Node.js Support v14 – v18 v18 – v24
Webhook Logging Not available File & Database (MySQL/PostgreSQL)
Production Bundling Requires npm install on server esbuild single-file bundle
Environment Prefix SOKETI_ PULSE_
AWS Lambda Webhooks Not available Supported
Database Pooling Not available Knex connection pooling
Graceful Shutdown Basic Configurable grace period
CLI Binary soketi pulse

πŸ“¦ Requirements

Dependency Version
Node.js v18 – v24
npm v8+
Memory Minimum 256 MB (recommended: 1 GB+)

Optional:

  • Redis β€” For Redis adapter, rate limiter, cache, and queue
  • NATS β€” For NATS adapter
  • MySQL / PostgreSQL β€” For database-backed app managers and webhook logging
  • Docker β€” Containerized deployment
  • PM2 β€” Production process management

Note: Due to uWebSockets.js limitations, Pulse cannot run on CentOS 7. Tested on CentOS 8 and Ubuntu.


πŸ—οΈ Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Pulse Server                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   HTTP   β”‚  β”‚ WebSocket β”‚  β”‚  Metrics  β”‚  β”‚   Queue    β”‚   β”‚
β”‚  β”‚  Handler β”‚  β”‚  Handler  β”‚  β”‚  (Prom)   β”‚  β”‚  Processor β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜   β”‚
β”‚       β”‚             β”‚             β”‚               β”‚          β”‚
β”‚  β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚                    Core Engine                         β”‚    β”‚
β”‚  β”‚              (uWebSockets.js / Node.js)                β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                           β”‚                                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚                 Adapter Layer                           β”‚    β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚    β”‚
β”‚  β”‚  β”‚ Local β”‚  β”‚ Redis β”‚  β”‚ NATS β”‚  β”‚Cluster β”‚          β”‚    β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚              App Manager Layer                         β”‚    β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚    β”‚
β”‚  β”‚  β”‚ Array β”‚  β”‚ MySQL β”‚  β”‚PostgreSQLβ”‚                  β”‚    β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚         Webhook Logger Layer                            β”‚    β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚    β”‚
β”‚  β”‚  β”‚  File Logger  β”‚  β”‚  Database Logger  β”‚             β”‚    β”‚
β”‚  β”‚  β”‚ (Daily Rotate)β”‚  β”‚ (MySQL/Postgres) β”‚              β”‚    β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸš€ Installation

CLI Installation

git clone https://github.com/oneaxxall/pulse.git
cd pulse
npm install
npm run build
npm link

Now use the pulse command anywhere:

pulse start

Docker

docker build -t pulse .
docker run -d --name pulse -p 6001:6001 pulse

Or use docker-compose:

docker-compose up -d

PM2

pm2 start pulse -- start

Production Bundle (Zero-Dependency)

./production.sh
# Creates pulse-production.tar.gz (~35MB)
# Contains a single bundled JS file β€” no npm install needed on target server!

βš™οΈ Configuration

Configuration methods (in order of precedence):

  1. JSON config file β€” pulse --config=app.json
  2. Environment variables β€” .env file (prefix: PULSE_)
  3. Default configuration β€” Defined in src/server.ts

Default Credentials

Parameter Default Value
App ID app-id
App Key app-key
App Secret app-secret
Port 6001

Key Environment Variables

Variable Default Description
PULSE_DEBUG 1 Enable debug logging
PULSE_ADAPTER_DRIVER local Adapter driver
PULSE_PORT 6001 Server port
PULSE_MANAGER_DRIVER array App manager driver
PULSE_WEBHOOKS_LOGS_ENABLED false Enable webhook logging to files
PULSE_WEBHOOKS_LOGS_DB_ENABLED false Enable webhook logging to database

🎯 Usage

Client Connection (Pusher JS SDK)

const pusher = new Pusher('app-key', {
    wsHost: 'your-server.com',
    wsPort: 6001,
    forceTLS: false,
    enabledTransports: ['ws', 'wss'],
});

const channel = pusher.subscribe('my-channel');
channel.bind('my-event', data => {
    console.log('Received:', data);
});

Client Connection (Laravel Echo)

import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'app-key',
    wsHost: 'your-server.com',
    wsPort: 6001,
    forceTLS: false,
    disableStats: true,
});

Backend Trigger (PHP)

$pusher = new Pusher\Pusher('app-key', 'app-secret', 'app-id', [
    'host' => 'your-server.com',
    'port' => 6001,
    'scheme' => 'http',
]);

$pusher->trigger('my-channel', 'my-event', ['message' => 'Hello World!']);

Backend Trigger (REST API)

curl -X POST http://your-server.com:6001/apps/app-id/events \
  -H "Content-Type: application/json" \
  -d '{"name": "my-event", "channel": "my-channel", "data": "{\"message\":\"Hello World!\"}"}'

πŸ“‘ Channel Types

Type Prefix Description
Public (none) Open to all clients
Private private- Requires auth; supports client events
Encrypted Private private-encrypted- End-to-end encrypted private channel
Presence presence- Tracks connected users
Cache cache-* Caches channel state

πŸ”„ Horizontal Scaling

Architecture Adapter Rate Limiter
Single node, single thread local local
Single node, multi-thread (PM2) cluster cluster
Multi-node, same network cluster / redis cluster / redis
Multi-node, multi-network redis redis

πŸ“ Webhook Logger

Pulse includes a built-in Webhook Logger that persists all webhook deliveries for auditing, debugging, and compliance.

File Logging

PULSE_WEBHOOKS_LOGS_ENABLED=true
PULSE_WEBHOOKS_LOGS_DIR=logs

Log files are rotated daily: webhook-YYYY-MM-DD.log

Database Logging

PULSE_WEBHOOKS_LOGS_DB_ENABLED=true

Note: Database logging works with mysql/postgres manager driver. Run the SQL schema from configurations/pds-pusher-manager.sql.


πŸ“¦ Production Bundling

Pulse uses esbuild to compile the entire application into a single JavaScript file.

Feature Traditional Pulse Bundled
npm install on server Required Not needed
Deployment size ~200MB+ ~35MB (compressed)
Startup time Slow Instant
Dependency conflicts Possible Eliminated
File count Thousands Single JS file
./production.sh
# Output: pulse-production.tar.gz (~35MB)

Deploy

tar -xzf pulse-production.tar.gz
./run-pulse-server.sh

πŸ“Š Monitoring

Pulse exposes Prometheus-compatible metrics on port 9601:

PULSE_METRICS_ENABLED=true
PULSE_METRICS_SERVER_PORT=9601

🌐 API Endpoints

Method Endpoint Description
GET /ready Health check
GET /health Detailed health
GET /usage Memory usage
GET /accept-traffic Traffic capacity check
GET /apps/:appId/channels List channels
POST /apps/:appId/events Trigger event
POST /apps/:appId/events/batch Batch trigger events

πŸ”— Webhooks

Events: client_event, channel_occupied, channel_vacated, member_added, member_removed. Supports AWS Lambda triggers.


β›” Rate Limiting

Limiter Scope Default
Backend events POST /events Unlimited (-1)
Client events Per socket Unlimited (-1)
Read requests Read APIs Unlimited (-1)

πŸ“ Project Structure

pulse/
β”œβ”€β”€ bin/                  # CLI entry points
β”œβ”€β”€ certs/                # SSL examples
β”œβ”€β”€ configurations/       # DB schemas, Nginx, Supervisor configs
β”œβ”€β”€ docs/                 # Documentation
β”œβ”€β”€ sdk/                  # SDK examples
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ adapters/         # Local, Redis, NATS, Cluster adapters
β”‚   β”œβ”€β”€ app-managers/     # Array, MySQL, PostgreSQL managers
β”‚   β”œβ”€β”€ cache-managers/   # Memory, Redis cache
β”‚   β”œβ”€β”€ channels/         # Public, private, presence, encrypted
β”‚   β”œβ”€β”€ cli/              # CLI implementation
β”‚   β”œβ”€β”€ metrics/          # Prometheus drivers
β”‚   β”œβ”€β”€ queues/           # Sync, Redis queue drivers
β”‚   β”œβ”€β”€ rate-limiters/    # Local, Redis, cluster
β”‚   β”œβ”€β”€ types/            # TypeScript definitions
β”‚   β”œβ”€β”€ webhook-log-adapters/ # File & DB webhook log storage
β”‚   β”œβ”€β”€ server.ts         # Core server
β”‚   β”œβ”€β”€ ws-handler.ts     # WebSocket handler
β”‚   β”œβ”€β”€ http-handler.ts   # REST API handler
β”‚   └── webhook-logger.ts # Webhook logging system
β”œβ”€β”€ tools/
β”‚   └── build.js          # esbuild bundling script
β”œβ”€β”€ main.ts               # Entry point
β”œβ”€β”€ production.sh         # Production bundling script
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
β”œβ”€β”€ Dockerfile
└── docker-compose.yml

πŸ› οΈ Development

npm run dev        # Hot-reload development
npm run build      # Compile TypeScript
npm test           # Run tests
npm run test:local # Verbose tests
npm run lint       # Lint source
npm run bundle     # Production bundle

⚠️ Known Limitations

  • CentOS 7 not supported
  • Windows not extensively tested
  • Subset of Pusher HTTP API implemented (core features complete)

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Commit your changes
  4. Push and open a Pull Request

πŸ™ Acknowledgements

  • Soketi β€” The original project that Pulse is forked from
  • Pusher.com β€” For the protocol specification and inspiration
  • uNetworking/uWebSockets.js β€” The high-performance WebSocket engine
  • Laravel β€” For Laravel Echo and broadcasting integration

πŸ“„ License

AGPL-3.0 License


oneaxxall

About

High-performance, self-hosted Pusher-compatible WebSocket server. Fork of Soketi with Node.js v24 support, webhook logging (file/database), esbuild production bundling, and Prometheus metrics. Built on uWebSockets.js.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors