

Pusher-compatible pub/sub with three durability levels — plus Flows, a visual pipeline that filters, transforms, aggregates, and fans out every message server-side. No consumer service. No job queue.
import PingerchipsServer from 'pingerchips-js-server';
const pc = new PingerchipsServer(KEY, SECRET);
await pc.trigger('orders', 'new', { id: 42, total: 99 });const ch = await pc.subscribe('orders', {
filter: { 'data.total': { $gte: 100 } },
after_serial: last,
});
ch.bind('new', notify);
// Pusher protocol — existing clients just workChannel type comes from the name prefix. Durability is configured per channel from the dashboard — each level is a separate code path, so you never pay for a guarantee you didn't ask for.
No replay, no WAL, nothing on the hot path but the broadcast. Cursors, presence, live scores.
Replay buffer. Subscribers reconnect with after_serial and get the gap. Notifications, feeds, task dispatch.
WAL-synced. Nothing lost, nothing double-processed. Billing events, state machines, anything that must not replay twice.
| (no prefix) | Public | no auth |
| private- | Private | HMAC token |
| presence- | Presence | HMAC token + member data |
pc.subscribe('[meta]occupancy:orders')
// { connections, publishers,
// subscribers, presenceMembers }Filter, transform, aggregate, delay, throttle, debounce, validate, route. Eight node types, chained visually, running on the server.
A Flow is a chain of nodes from a trigger to a send-to-channel. It hot-reloads — save it, the next message uses it, no deploy.
Click a node to see its config.
Build rules with fields, operators, and AND/OR groups — or drop to raw JSON. It compiles to an ExSift query the server runs before broadcast.
{
"$and": [
{
"data.event": {
"$eq": "order.created"
}
},
{
"data.total": {
"$gte": 100
}
}
]
}Transform rewrites each message with JavaScript. Reduce folds a stream into a running value — counters, rollups, windows — and the accumulator survives restarts.
export default function transform(message) {
return { ...message,
priority: message.total > 100 ? 'high' : 'normal',
};
}export default function reducer(acc, message) {
return {
count: (acc.count ?? 0) + 1,
total: (acc.total ?? 0) + message.amount,
};
}Reject messages that don't match a shape. Build the shape as a field tree — nested objects, arrays, required flags — or paste JSON Schema.
Click a node to see its config.
{
"$and": [
{
"data.event": {
"$eq": "order.created"
}
},
{
"data.total": {
"$gte": 100
}
}
]
}export default function transform(message) {
return { ...message,
priority: message.total > 100 ? 'high' : 'normal',
};
}export default function reducer(acc, message) {
return {
count: (acc.count ?? 0) + 1,
total: (acc.total ?? 0) + message.amount,
};
}Click a node to see its config.
pass only if a rule matches — visual builder or JSON
rewrite the message with JavaScript
fold a stream into a running value
hold the message for a set duration
cap delivery to N per window
collapse a burst to the last in a window
reject messages that don't match a shape
emit the result to one or more channels
The pipeline runs on the server, between publish and delivery. You never deploy a worker to reshape an event.
Stripe hits one channel. A Flow filters by event type, reshapes the payload, and sends to invoice-{{id}} so each customer's tab only gets its own updates.
~30 minutesPublish every app event to one L1 channel. A Flow filters to what this user cares about; they reconnect with after_serial and never miss one.
~1 hourA Reduce node folds a firehose of events into { count, total } per window. Subscribers get the rollup, not the raw stream.
an afternoonAn L2 channel for tasks. Workers claim with a dedup key; the WAL guarantees a task is delivered once and never double-processed.
~2 hoursA Schema Validator node rejects malformed events at the edge, before they reach a single subscriber or your database.
20 minutesPoint your existing Pusher client at Pingerchips. Same protocol, same code. Add durability and Flows per channel when you want them.
10 minutesPusher-compatible pub/sub, a visual pipeline, three durability levels. Self-hostable, free while in beta.