Channels
Channels are the fundamental way to organize and route real-time messages in Soketify. Pick the right channel type for your use case and everything else follows from there.
Channel Types Overview
Soketify supports five channel types, distinguished entirely by their name prefix. You do not configure channel types separately; the prefix is the type.
| Type | Prefix | Auth Required | User Tracking | E2E Encrypted |
|---|---|---|---|---|
| Public | None | No | No | No |
| Private | private- | Yes | No | No |
| Encrypted | private-encrypted- | Yes | No | Yes |
| Presence | presence- | Yes | Yes | No |
| Cache | cache- | Optional* | No | No |
* Cache channels can be public (cache-), private (private-cache-), or presence (presence-cache-).
Public Channels
Public channels have no prefix and require no authentication. Any client that knows your App Key can subscribe. Use them for data you are comfortable broadcasting openly.
Use Cases
- Live scoreboards and sports feeds
- Public announcements and notifications
- Real-time price tickers
- Activity streams visible to everyone
Example
1// Client: subscribe and listen
2const channel = pusher.subscribe("live-scores");
3
4channel.bind("score-update", (data) => {
5 console.log(`${data.team}: ${data.score}`);
6});
7
8// Server: trigger an event
9await pusher.trigger("live-scores", "score-update", {
10 team: "Home",
11 score: 3,
12 minute: 67,
13});
Warning
Anyone with your App Key can subscribe to public channels. Do not broadcast sensitive user data, private messages, or anything you would not want the whole internet to read.
Private Channels
Private channels use the private- prefix. Before the client can subscribe, it must pass an authorization check against an endpoint on your server. Only clients your server approves will receive events.
Use Cases
- Per-user notification feeds (e.g., order updates)
- Admin-only event streams
- Private DMs between specific users
- Financial or medical data feeds
Example
1// Client: subscribe — auth happens automatically
2const channel = pusher.subscribe("private-user-42");
3
4channel.bind("pusher:subscription_succeeded", () => {
5 console.log("Auth passed, subscribed!");
6});
7
8channel.bind("pusher:subscription_error", (err) => {
9 console.error("Auth failed:", err.status);
10});
11
12channel.bind("order-update", (data) => {
13 console.log("Order:", data.orderId, "→", data.status);
14});
15
16// Server: trigger an event to a specific user
17await pusher.trigger("private-user-42", "order-update", {
18 orderId: "ORD-1234",
19 status: "shipped",
20 trackingUrl: "https://track.example.com/ORD-1234",
21});
See the Authentication guide for how to implement the server-side auth endpoint.
Encrypted Channels
Encrypted channels use the private-encrypted- prefix. They work like private channels, but event payloads are encrypted end-to-end using NaCl Secretbox before they leave your server. Soketify routes the ciphertext but cannot read it. cannot read it. Only subscribers who have been through your auth endpoint get the decryption key.
Use Cases
- Healthcare data that must stay confidential end-to-end
- Financial transactions or account data
- Any scenario where you need to guarantee that the WebSocket infrastructure itself cannot inspect your messages
Server Setup
Encrypted channels require a 32-byte master encryption key on your server. Generate one with:
openssl rand -base64 32Then configure your server SDK with it:
1const Pusher = require("pusher");
2
3const pusher = new Pusher({
4 appId: process.env.SOKETIFY_APP_ID,
5 key: process.env.SOKETIFY_APP_KEY,
6 secret: process.env.SOKETIFY_APP_SECRET,
7 host: "api.soketify.com",
8 port: "443",
9 useTLS: true,
10 cluster: "default",
11 // 32-byte base64 master key — never expose this
12 encryptionMasterKeyBase64: process.env.SOKETIFY_ENCRYPTION_KEY,
13});
Client Setup
On the client side, you subscribe exactly like a private channel. The Pusher SDK handles decryption transparently once the auth endpoint distributes the channel key.
1// Requires pusher-js v6+ built with encryption support
2// npm install pusher-js
3
4const channel = pusher.subscribe("private-encrypted-medical-records");
5
6channel.bind("pusher:subscription_succeeded", () => {
7 console.log("Subscribed and decryption key received");
8});
9
10channel.bind("patient-update", (data) => {
11 // data is already decrypted — Soketify never saw the plaintext
12 console.log("Patient update:", data);
13});
Triggering Events
1// Server: trigger — encryption happens automatically
2await pusher.trigger(
3 "private-encrypted-medical-records",
4 "patient-update",
5 {
6 patientId: "P-9876",
7 status: "discharged",
8 notes: "Sensitive clinical data here",
9 }
10);
Encrypted Channel Limitations
Encrypted channels have a few constraints you should know upfront:
- Only private channels can be encrypted:
private-encrypted-is the only valid prefix - You cannot trigger to multiple channels in a single API call
- Clients cannot send client events on encrypted channels
- Channel and event names are not encrypted, only the payload
- Requires pusher-js v4.3.0+ (v6+ needs the
pusher-with-encryptionbuild)
Key Rotation
To rotate the encryption key, update encryptionMasterKeyBase64 on your server and redeploy. Clients that fail decryption automatically request a fresh channel key from your auth endpoint. Note that messages sent during the rotation window may be lost, so plan for this if continuity is critical.
Presence Channels
Presence channels use the presence- prefix. They extend private channels by tracking exactly who is subscribed at any moment. Every subscriber must provide user identification during authentication, and all members can see who else is in the channel.
Use Cases
- Chat rooms with live online user lists
- Collaborative documents with active viewer awareness
- Multiplayer game lobbies
- Support queues showing available agents
Limits
- Maximum 100 members per presence channel
user_infoobject max 1 KB- User ID max 128 characters
Example
1const channel = pusher.subscribe("presence-chat-room-1");
2
3// Initial member list on subscription success
4channel.bind("pusher:subscription_succeeded", (members) => {
5 console.log("Online:", members.count);
6
7 // Your own info
8 console.log("I am:", members.me.info.name);
9
10 // Iterate all members
11 members.each((member) => {
12 addUserToList(member.id, member.info);
13 });
14});
15
16// Someone joined
17channel.bind("pusher:member_added", (member) => {
18 console.log(`${member.info.name} came online`);
19 addUserToList(member.id, member.info);
20});
21
22// Someone left
23channel.bind("pusher:member_removed", (member) => {
24 console.log(`${member.info.name} went offline`);
25 removeUserFromList(member.id);
26});
27
28// Chat messages
29channel.bind("message", (data) => {
30 console.log(`${data.sender}: ${data.text}`);
31});
What Goes in user_info
The user_info object you provide in your auth endpoint is visible to every member of the channel. Keep it to what makes sense publicly within the channel: display name, avatar URL, role. Do not include emails, internal IDs, or anything sensitive unless the channel is scoped tightly enough for that to be safe.
Cache Channels
Cache channels use the cache- prefix. They automatically store the last triggered event and replay it to new subscribers the moment they subscribe. This eliminates the classic "stale initial state" problem: a client that connects at any point gets the current state immediately without needing a separate API call.
Cache channels can be combined with other types:
cache-: public cache channelprivate-cache-: authenticated cache channelpresence-cache-: presence + cache
Use Cases
- Device location tracking: new viewers get the last known position immediately
- IoT sensor dashboards: show current readings without a polling request
- Match scores, stock prices, or any "current value" that updates periodically
Example
1// Client: subscribing gives you the last event immediately (if cached)
2const channel = pusher.subscribe("cache-vehicle-location");
3
4channel.bind("location-update", (data) => {
5 // This fires immediately on subscribe if there's a cached value,
6 // then again every time the server triggers the event
7 updateMapMarker(data.lat, data.lng);
8});
9
10// Handle cache miss: no event has been triggered yet
11channel.bind("pusher:cache_miss", () => {
12 // The cache is empty — fetch current state yourself or wait
13 fetchInitialLocation().then((loc) => updateMapMarker(loc.lat, loc.lng));
14});
15
16// Server: trigger events as normal — the last one is automatically cached
17await pusher.trigger("cache-vehicle-location", "location-update", {
18 vehicleId: "V-001",
19 lat: 40.7128,
20 lng: -74.0060,
21 timestamp: Date.now(),
22});
Handling Cache Misses with Webhooks (Recommended)
When multiple clients subscribe to an empty cache channel simultaneously, you do not want each one triggering a refill. The smarter approach is to configure a webhook for the cache_miss event. for the cache_miss event. Soketify deduplicates these notifications and sends one webhook to your server, which then triggers a single event to repopulate the cache.
1// Your webhook handler receives a cache_miss event:
2// { name: "cache_miss", channel: "cache-vehicle-location" }
3
4// Repopulate the cache by triggering the event once:
5app.post("/webhooks/pusher", async (req, res) => {
6 const { events } = req.body;
7 for (const event of events) {
8 if (event.name === "cache_miss") {
9 const location = await fetchCurrentLocation(event.channel);
10 await pusher.trigger(event.channel, "location-update", location);
11 }
12 }
13 res.sendStatus(200);
14});
Cache TTL
Cached messages expire after up to 30 minutes, though they may expire sooner. Cache channels are best for values that update frequently enough that a 30-minute-old cache would be refreshed naturally. If you need guaranteed persistence, store data in your own database and serve it via your API.
Cache Channel Limitations
Only server-triggered events are cached. Client events (client-prefix) are not cached. Cached data does not include a built-in timestamp; add one to your event payload if freshness matters.
Channel Naming Conventions
Channel names must follow these rules:
- Maximum 200 characters
- Allowed characters:
a-zA-Z0-9_\-=@.,; - No spaces or special characters outside the allowed set
- Reserved prefixes:
private-,private-encrypted-,presence-,cache-,private-cache-,presence-cache-
Recommended Patterns
// Public channels
"live-scores"
"news-feed"
"stock-prices"
// Private channels — scope to a resource + ID
"private-user-42"
"private-order-ORD-1234"
"private-team-engineering"
// Encrypted channels — same scoping, private data
"private-encrypted-patient-P-9876"
"private-encrypted-account-ACC-001"
// Presence channels — scope to a shared space
"presence-chat-room-general"
"presence-document-doc-456"
"presence-game-lobby-abc"
// Cache channels — current-value streams
"cache-vehicle-V-001"
"private-cache-sensor-readings-room-7"
"presence-cache-live-auction-XYZ"Subscribing and Unsubscribing
1// Subscribe to a channel
2const channel = pusher.subscribe("my-channel");
3
4// Bind to events
5channel.bind("my-event", callback);
6
7// Unbind a specific callback
8channel.unbind("my-event", callback);
9
10// Unbind all callbacks for an event
11channel.unbind("my-event");
12
13// Unbind everything on this channel
14channel.unbind_all();
15
16// Unsubscribe from the channel entirely
17pusher.unsubscribe("my-channel");
Tip
Unsubscribe from channels when they are no longer needed, for instance when a user navigates away, closes a modal, or leaves a room. In React, return a cleanup function from useEffect:
return () => pusher.unsubscribe(channelName);
Client Events
Client events let one connected client send events directly to other subscribers on the same private or presence channel, without going through your server. Event names must be prefixed with client-.
Requirements
- Channel must be
private-orpresence- - Event name must start with
client- - Client events must be enabled for your app in the Dashboard
- Wait for
pusher:subscription_succeededbefore triggering - The sender does not receive its own client events
- Rate limited to 10 client events per second per connection
Example: Typing Indicators
1const channel = pusher.subscribe("private-chat-room-1");
2
3// Wait for subscription before triggering
4channel.bind("pusher:subscription_succeeded", () => {
5 // Now safe to trigger client events
6
7 inputEl.addEventListener("input", () => {
8 channel.trigger("client-typing", { user: "Alice", isTyping: true });
9 });
10
11 inputEl.addEventListener("blur", () => {
12 channel.trigger("client-typing", { user: "Alice", isTyping: false });
13 });
14});
15
16// Receive client events from other subscribers
17channel.bind("client-typing", (data) => {
18 if (data.isTyping) {
19 showTypingIndicator(data.user);
20 } else {
21 hideTypingIndicator(data.user);
22 }
23});
Rate Limiting Tip
The 10/sec rate limit is per connection. For high-frequency events like mouse position or cursor tracking, debounce or throttle your triggers. Sending on every UI event will hit the limit and start dropping messages.
Trust the metadata, not the data
On presence channels, client event callbacks receive a second metadata argument with the sender's user_id. Use that instead of embedding a user ID in the event data, which any client could spoof:
channel.bind("client-msg", (data, metadata) => {
console.log("From:", metadata.user_id); // trusted
});
Subscription Count
When enabled in your app settings, Soketify fires a pusher:subscription_count event whenever the subscriber count on a channel changes. This lets you display live viewer counts without using a full presence channel.
1const channel = pusher.subscribe("live-match-final");
2
3channel.bind("pusher:subscription_count", (data) => {
4 viewerCountEl.textContent = `${data.subscription_count} watching`;
5});
Info
On channels with more than 100 subscribers, this event fires at most every 5 seconds rather than on every change, to avoid flooding all subscribers with count updates.
Next Steps
- Set up channel authentication for private, encrypted, and presence channels
- Learn about the events system : binding, system events, naming rules, and data limits
- Configure webhooks to get server-side notifications on channel activity
- Trigger events via the REST API from your server