Repository navigation
getting started
A quick guide to get you up and running with Soketi, the high-performance Pusher protocol-compatible WebSocket server written in Rust.
- Introduction
- Quick Start
- Installation
- Basic Configuration
- Your First Connection
- WebSocket Connection Examples
- Next Steps
Soketi is a fast, scalable WebSocket server that implements the Pusher protocol. It allows you to build real-time applications with ease, supporting public channels, private channels, and presence channels.
The fastest way to get started with Soketi is using Docker:
docker run -p 6001:6001 -p 9601:9601 \
-e SOKETI_DEFAULT_APP_ID=app-id \
-e SOKETI_DEFAULT_APP_KEY=app-key \
-e SOKETI_DEFAULT_APP_SECRET=app-secret \
quay.io/soketi/soketi:latest-16-alpineYour Soketi server is now running on ws://localhost:6001!
Docker is the easiest way to run Soketi:
docker pull quay.io/soketi/soketi:latest-16-alpineIf you have Rust installed, you can build from source:
git clone https://github.com/soketi/soketi.rs.git
cd soketi.rs
cargo build --release
./target/release/soketiDownload pre-built binaries from the releases page.
Create a config.json file:
{
"app_manager": {
"driver": "array",
"array": {
"apps": [
{
"id": "app-id",
"key": "app-key",
"secret": "app-secret",
"max_connections": 100,
"enable_client_messages": true,
"enabled": true,
"max_backend_events_per_second": 100,
"max_client_events_per_second": 100,
"max_read_requests_per_second": 100
}
]
}
},
"host": "0.0.0.0",
"port": 6001,
"metrics": {
"enabled": true,
"port": 9601,
"host": "0.0.0.0"
}
}Start Soketi with your configuration:
soketi --config-file config.jsonNote: Use
--config-file(not--config) to specify the configuration file path.
Install the Pusher JavaScript library:
npm install pusher-jsConnect to your Soketi server:
import Pusher from 'pusher-js';
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
forceTLS: false,
encrypted: false,
enabledTransports: ['ws', 'wss'],
});
const channel = pusher.subscribe('my-channel');
channel.bind('my-event', (data: any) => {
console.log('Received:', data);
});
console.log('Connected to Soketi!');You can trigger events using the HTTP API:
curl -X POST http://localhost:6001/apps/app-id/events \
-H "Content-Type: application/json" \
-d '{
"name": "my-event",
"channel": "my-channel",
"data": "{\"message\": \"Hello from Soketi!\"}"
}'Public channels are open to all clients and don't require authentication:
import Pusher from 'pusher-js';
// Initialize Pusher client
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
forceTLS: false,
encrypted: false,
enabledTransports: ['ws', 'wss'],
cluster: 'mt1', // Optional, for compatibility
});
// Subscribe to a public channel
const channel = pusher.subscribe('public-channel');
// Bind to events
channel.bind('message', (data: any) => {
console.log('New message:', data);
});
// Connection state events
pusher.connection.bind('connected', () => {
console.log('Connected to Soketi');
});
pusher.connection.bind('disconnected', () => {
console.log('Disconnected from Soketi');
});
pusher.connection.bind('error', (err: any) => {
console.error('Connection error:', err);
});Private channels require authentication before clients can subscribe:
import Pusher from 'pusher-js';
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
forceTLS: false,
encrypted: false,
enabledTransports: ['ws', 'wss'],
authEndpoint: '/pusher/auth', // Your auth endpoint
auth: {
headers: {
'Authorization': 'Bearer your-token-here'
}
}
});
// Subscribe to a private channel (must start with 'private-')
const privateChannel = pusher.subscribe('private-user-123');
privateChannel.bind('pusher:subscription_succeeded', () => {
console.log('Successfully subscribed to private channel');
});
privateChannel.bind('notification', (data: any) => {
console.log('Private notification:', data);
});
privateChannel.bind('pusher:subscription_error', (status: any) => {
console.error('Subscription failed:', status);
});Presence channels track which users are subscribed and provide member information:
import Pusher from 'pusher-js';
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
forceTLS: false,
encrypted: false,
enabledTransports: ['ws', 'wss'],
authEndpoint: '/pusher/auth',
auth: {
headers: {
'Authorization': 'Bearer your-token-here'
}
}
});
// Subscribe to a presence channel (must start with 'presence-')
const presenceChannel = pusher.subscribe('presence-chat-room');
// When subscription succeeds, get all members
presenceChannel.bind('pusher:subscription_succeeded', (members: any) => {
console.log('Current members:', members.count);
members.each((member: any) => {
console.log('Member:', member.id, member.info);
});
});
// When a new member joins
presenceChannel.bind('pusher:member_added', (member: any) => {
console.log('Member joined:', member.id, member.info);
});
// When a member leaves
presenceChannel.bind('pusher:member_removed', (member: any) => {
console.log('Member left:', member.id);
});
// Regular events on presence channel
presenceChannel.bind('chat-message', (data: any) => {
console.log('Chat message:', data);
});Client events allow clients to send messages directly to other clients on the same channel:
import Pusher from 'pusher-js';
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
forceTLS: false,
encrypted: false,
enabledTransports: ['ws', 'wss'],
authEndpoint: '/pusher/auth',
});
// Client events only work on private and presence channels
const channel = pusher.subscribe('private-chat');
channel.bind('pusher:subscription_succeeded', () => {
// Trigger a client event (must start with 'client-')
channel.trigger('client-typing', {
user: 'John',
isTyping: true
});
});
// Listen for client events from other users
channel.bind('client-typing', (data: any) => {
console.log(`${data.user} is typing:`, data.isTyping);
});Soketi automatically handles reconnection, but you can customize the behavior:
import Pusher from 'pusher-js';
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
forceTLS: false,
encrypted: false,
enabledTransports: ['ws', 'wss'],
// Reconnection settings
activityTimeout: 30000, // 30 seconds
pongTimeout: 10000, // 10 seconds
});
const channel = pusher.subscribe('my-channel');
// Track connection state
pusher.connection.bind('state_change', (states: any) => {
console.log(`Connection state changed from ${states.previous} to ${states.current}`);
});
// Handle reconnection
pusher.connection.bind('connected', () => {
console.log('Connected! Socket ID:', pusher.connection.socket_id);
});
pusher.connection.bind('connecting', () => {
console.log('Connecting to Soketi...');
});
pusher.connection.bind('unavailable', () => {
console.log('Connection unavailable, will retry...');
});
pusher.connection.bind('failed', () => {
console.error('Connection failed permanently');
});Here's a complete example combining multiple concepts:
import Pusher from 'pusher-js';
// Initialize Pusher
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
forceTLS: false,
encrypted: false,
enabledTransports: ['ws', 'wss'],
authEndpoint: '/api/pusher/auth',
auth: {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
}
});
// Subscribe to presence channel
const chatChannel = pusher.subscribe('presence-chat-room');
// Handle subscription success
chatChannel.bind('pusher:subscription_succeeded', (members: any) => {
console.log(`Connected! ${members.count} users online`);
updateUserList(members);
});
// Handle new members
chatChannel.bind('pusher:member_added', (member: any) => {
console.log(`${member.info.name} joined`);
addUserToList(member);
});
// Handle members leaving
chatChannel.bind('pusher:member_removed', (member: any) => {
console.log(`${member.info.name} left`);
removeUserFromList(member);
});
// Handle chat messages
chatChannel.bind('chat-message', (data: any) => {
displayMessage(data);
});
// Handle typing indicators
chatChannel.bind('client-typing', (data: any) => {
showTypingIndicator(data.userId, data.isTyping);
});
// Send a message
function sendMessage(message: string) {
fetch('/api/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
channel: 'presence-chat-room',
event: 'chat-message',
data: {
message,
userId: getCurrentUserId(),
userName: getCurrentUserName(),
timestamp: new Date().toISOString()
}
})
});
}
// Send typing indicator
function sendTypingIndicator(isTyping: boolean) {
chatChannel.trigger('client-typing', {
userId: getCurrentUserId(),
isTyping
});
}
// Connection monitoring
pusher.connection.bind('state_change', (states: any) => {
updateConnectionStatus(states.current);
});
// Helper functions (implement based on your UI)
function updateUserList(members: any) { /* ... */ }
function addUserToList(member: any) { /* ... */ }
function removeUserFromList(member: any) { /* ... */ }
function displayMessage(data: any) { /* ... */ }
function showTypingIndicator(userId: string, isTyping: boolean) { /* ... */ }
function updateConnectionStatus(status: string) { /* ... */ }
function getCurrentUserId(): string { return 'user-123'; }
function getCurrentUserName(): string { return 'John Doe'; }Now that you have Soketi running, explore these topics:
- Installation Guide - Detailed installation options
- Configuration Reference - Complete configuration options
- API Reference - WebSocket and HTTP API documentation
- Deployment Guide - Production deployment with reverse proxies
- Examples - Code examples and tutorials