The TsgcWS_API_Pusher component has been updated to align with the latest
Pusher Channels protocol specification.
This release introduces support for private-encrypted channels, four new presence and cache channel events,
two new REST API endpoints, enhanced query parameters for existing endpoints, and several bug fixes.
All changes are backward-compatible — existing code continues to work without modification.
Table of Contents
1. Private-Encrypted Channel Support
Pusher Channels supports private-encrypted channels that provide end-to-end encryption of event data
using NaCl Secretbox (XSalsa20-Poly1305). The Pusher servers never see the plaintext — only the communicating
clients can decrypt the messages. Two new channel types have been added to the
TsgcWSPusherChannels enumeration:
| Enum Value | Channel Prefix | Description |
|---|---|---|
pscPrivateEncryptedChannel |
private-encrypted- |
End-to-end encrypted private channel. Data payloads are encrypted with a per-channel shared secret. |
pscPrivateEncryptedCacheChannel |
private-encrypted-cache- |
Same as above, plus cache channel behavior — the last event is delivered to new subscribers. |
The TsgcWSPusherResponseAuthentication class now includes a
SharedSecret property. When subscribing to a
private-encrypted- channel, your authorization endpoint must return
the base64-encoded per-channel shared secret. Set this value in the
OnPusherAuthentication event handler:
procedure TForm1.sgcPusherAuthentication(Sender: TObject;
AuthRequest: TsgcWSPusherRequestAuthentication;
AuthResponse: TsgcWSPusherResponseAuthentication);
begin
// For private-encrypted channels, set the shared secret
// returned by your authorization server
AuthResponse.Secret := 'your-app-secret';
AuthResponse.SharedSecret := 'base64-encoded-per-channel-key';
end;
Subscribing to an Encrypted Channel
// Subscribe to a private-encrypted channel
sgcPusher.Subscribe('my-secret-channel', pscPrivateEncryptedChannel);
// Subscribe to a private-encrypted cache channel
sgcPusher.Subscribe('my-secret-cache', pscPrivateEncryptedCacheChannel);
Publishing on an Encrypted Channel
// Client events work on private-encrypted channels just like private channels
sgcPusher.Publish('my-event', 'my-secret-channel',
pscPrivateEncryptedChannel, '{"message":"Hello encrypted!"}');
2. New WebSocket Events
Four new events have been added to handle Pusher internal protocol messages that were previously unhandled.
These events are dispatched automatically when the corresponding
pusher_internal: messages arrive over the WebSocket connection.
| Event Property | Protocol Message | Description |
|---|---|---|
OnPusherMemberAdded |
pusher_internal:member_added |
Fired when a new user joins a presence channel. Provides the channel name, user ID, and user info. |
OnPusherMemberRemoved |
pusher_internal:member_removed |
Fired when a user leaves a presence channel. Provides the channel name, user ID, and user info. |
OnPusherSubscriptionCount |
pusher_internal:subscription_count |
Fired when the subscription count for a channel changes. Must be enabled in the Pusher dashboard. |
OnPusherCacheMiss |
pusher_internal:cache_miss |
Fired when subscribing to a cache channel that has no cached event available. |
OnPusherMemberAdded / OnPusherMemberRemoved
These events use the new TsgcWSPusherMemberEvent callback type,
which provides the channel name, user ID, and user info as separate parameters:
TsgcWSPusherMemberEvent = procedure(Sender: TObject;
Channel, UserId, UserInfo: String) of object;
procedure TForm1.sgcPusherMemberAdded(Sender: TObject;
Channel, UserId, UserInfo: String);
begin
Memo1.Lines.Add('User joined: ' + UserId + ' on ' + Channel);
if UserInfo '' then
Memo1.Lines.Add(' Info: ' + UserInfo);
end;
procedure TForm1.sgcPusherMemberRemoved(Sender: TObject;
Channel, UserId, UserInfo: String);
begin
Memo1.Lines.Add('User left: ' + UserId + ' from ' + Channel);
end;
OnPusherSubscriptionCount
Uses the new TsgcWSPusherSubscriptionCountEvent callback type.
This event requires the Subscription Count feature to be enabled in your Pusher dashboard settings.
TsgcWSPusherSubscriptionCountEvent = procedure(Sender: TObject;
Channel: String; SubscriptionCount: Integer) of object;
procedure TForm1.sgcPusherSubscriptionCount(Sender: TObject;
Channel: String; SubscriptionCount: Integer);
begin
Memo1.Lines.Add(Channel + ' now has ' + IntToStr(SubscriptionCount) +
' subscribers');
end;
OnPusherCacheMiss
Uses the new TsgcWSPusherCacheMissEvent callback type.
This event is fired when you subscribe to a cache channel (cache-,
private-cache-,
private-encrypted-cache-, or
presence-cache-) and no cached event is available.
TsgcWSPusherCacheMissEvent = procedure(Sender: TObject;
Channel: String) of object;
procedure TForm1.sgcPusherCacheMiss(Sender: TObject; Channel: String);
begin
Memo1.Lines.Add('No cached event for channel: ' + Channel);
end;
3. New REST API Methods
Two new server-side REST API methods have been added to the
TsgcWS_API_Pusher component, covering
the batch events endpoint and user connection termination.
| Method | Pusher Endpoint | Description |
|---|---|---|
TriggerBatchEvents |
POST /apps/{app_id}/batch_events |
Trigger up to 10 events in a single HTTP request. Each event can target a different channel. |
TerminateUserConnections |
POST /apps/{app_id}/users/{user_id}/terminate_connections |
Terminate all WebSocket connections for a specific authenticated user. |
TriggerBatchEvents
function TriggerBatchEvents(const aBatch: String): String;
The aBatch parameter is a JSON string containing a
batch array of event objects. Each object must include
channel,
name, and
data. Maximum 10 events per batch.
var
vBatch, vResult: string;
begin
vBatch :=
'{"batch": [' +
' {"channel": "my-channel-1", "name": "my-event", "data": "{\"msg\":\"hello\"}"}' + ',' +
' {"channel": "my-channel-2", "name": "my-event", "data": "{\"msg\":\"world\"}"}' +
']}';
vResult := sgcPusher.TriggerBatchEvents(vBatch);
Memo1.Lines.Add(vResult);
end;
TerminateUserConnections
function TerminateUserConnections(const aUserId: String): String;
Forcefully terminates all active WebSocket connections for a given user. This is useful for logging out a user across all their devices, or revoking access after a permission change. Requires Pusher User Authentication to be configured.
var
vResult: string;
begin
// Disconnect user "user-123" from all sessions
vResult := sgcPusher.TerminateUserConnections('user-123');
Memo1.Lines.Add(vResult);
end;
4. Updated REST API Methods
Three existing REST API methods have been enhanced with additional optional parameters. Since the new parameters all have default values, existing code is fully backward-compatible.
TriggerEvent
Previous Signature
function TriggerEvent(const aEventName, aChannel, aData: String): String;
New Signature
function TriggerEvent(const aEventName, aChannel, aData: String;
const aSocketId: String = '';
const aInfo: String = ''): String;
| Parameter | Description |
|---|---|
aSocketId |
Exclude this socket ID from receiving the event. Useful for preventing the sender from receiving their own broadcast. |
aInfo |
Comma-separated list of attributes to return. Valid values: subscription_count, user_count. |
var
vResult: string;
begin
// Trigger event, exclude the sender, and request subscription count
vResult := sgcPusher.TriggerEvent('my-event', 'my-channel',
'{"msg":"hello"}', sgcPusher.FSocket_id, 'subscription_count');
Memo1.Lines.Add(vResult);
// Response: {"channels":{"my-channel":{"subscription_count":5}}}
end;
GetChannels
Previous Signature
function GetChannels: String;
New Signature
function GetChannels(
const aFilterByPrefix: String = '';
const aInfo: String = ''): String;
| Parameter | Description |
|---|---|
aFilterByPrefix |
Filter the returned channels by name prefix. Example: presence- returns only presence channels. |
aInfo |
Comma-separated attributes. Valid values: user_count (presence only), subscription_count. |
var
vResult: string;
begin
// List all presence channels with user counts
vResult := sgcPusher.GetChannels('presence-', 'user_count');
Memo1.Lines.Add(vResult);
// Response: {"channels":{"presence-room":{"user_count":3}}}
// List all channels (no filter) - backward-compatible call
vResult := sgcPusher.GetChannels;
Memo1.Lines.Add(vResult);
end;
GetChannel
Previous Signature
function GetChannel(const aChannel: String): String;
New Signature
function GetChannel(const aChannel: String;
const aInfo: String = ''): String;
| Parameter | Description |
|---|---|
aInfo |
Comma-separated attributes. Valid values: subscription_count, user_count, cache. |
var
vResult: string;
begin
// Get channel info with subscription count and user count
vResult := sgcPusher.GetChannel('presence-room',
'subscription_count,user_count');
Memo1.Lines.Add(vResult);
// Response: {"occupied":true,"subscription_count":5,"user_count":3}
// Get basic channel info - backward-compatible call
vResult := sgcPusher.GetChannel('my-channel');
Memo1.Lines.Add(vResult);
end;
5. Bug Fixes
Several bugs were identified and corrected in this release:
| Bug | Impact | Fix |
|---|---|---|
Typo in DoReadEvent: 'puserh:ping' |
Critical Server pings were never recognized, causing the client to miss heartbeat requests. This could lead to the server disconnecting the client for inactivity. | Corrected to 'pusher:ping'. |
DoSendPong null access |
High Accessing JSON.Node['data'].Value without null check could cause an access violation if the ping message has no data field. |
Added null check; defaults to {} when data is absent. |
GetUsers hardcoded prefix |
High The method always prepended presence- to the channel name, doubling the prefix if the user already passed the full channel name (e.g., presence-presence-room). |
Removed hardcoded prefix. Users should now pass the full channel name including the presence- prefix. |
GetUsers double semicolon |
Low A stray double semicolon ;; was present at the end of the method. |
Removed the extra semicolon. |
Breaking change in GetUsers: The GetUsers method no longer
prepends presence- automatically. If your code passes just the channel
name without the prefix (e.g., GetUsers('my-room')), you must update it to
include the full name: GetUsers('presence-my-room').
6. Summary
| Category | Count | Details |
|---|---|---|
| New Channel Types | 2 | pscPrivateEncryptedChannel, pscPrivateEncryptedCacheChannel |
| New WebSocket Events | 4 | OnPusherMemberAdded, OnPusherMemberRemoved, OnPusherSubscriptionCount, OnPusherCacheMiss |
| New REST Methods | 2 | TriggerBatchEvents, TerminateUserConnections |
| Updated REST Methods | 3 | TriggerEvent (+socket_id, +info), GetChannels (+filter, +info), GetChannel (+info) |
| New Properties | 1 | SharedSecret on TsgcWSPusherResponseAuthentication |
| Bug Fixes | 4 | Ping typo, pong null access, GetUsers prefix, double semicolon |
All changes are backward-compatible. New method parameters use default values and new event properties
are optional. The only behavioral change is in
GetUsers, which no longer prepends the
presence- prefix automatically.