A Pusher alternative that never holds your secret

Pikoman is a hosted WebSocket service. It has public channels, presence channels with a member list, messages between browsers, an HTTP API for your backend, and end-to-end encrypted channels. That is what Pusher Channels does too. The difference is in the keys.

Pusher signs every request with an app secret, and Pusher keeps a copy of that same secret. One secret can do everything. Pikoman signs with Ed25519 instead. Your browser makes the key pair and we receive only the public half. You can have several API keys, and each one gets only the rights you give it: a key for statistics cannot publish, and a bot's key reaches only its own channels.

Free
100 connections at once per app
We store
Only public keys
Switching
Not a drop-in: client and server code change

Pusher, Ably, PubNub and Pikoman

All four relay messages to connected clients. Here is where they differ.

Pusher Channels Ably PubNub Pikoman
Keys with limited rights No, a key can do everything Yes, per channel Tokens with read and write rights per channel, granted by your server with the secret key Seven rights, plus channel kinds, prefixes, IP addresses and expiry
End-to-end encryption private-encrypted- channels AES-CBC, with a key the clients share A cipher key in the client SDK private-encrypted- channels, AES-256-GCM
Free plan 100 connections, 200,000 messages a day 200 peak connections, 6 million messages a month 200 monthly active users or 1 million transactions 100 connections at once per app
First paid plan $49 a month: 500 connections, 1 million messages a day $29 a month plus usage $98 a month: 1,000 monthly active users Not published yet. Ask us

How the keys work

Pusher Channels Ably Pikoman
How a request is proven HMAC signature with the app secret Key or token Ed25519 signature, valid at most ten minutes
What the provider holds The same secret you sign with The shared key Only public keys
A bot joins a private channel Through a client and your own auth endpoint With its own key With its own key, nothing in between

As we read their public documentation, October 2026. PubNub is left out of the second table because we have not checked how it signs requests. Tell us at hello@pikoman.se if we have something wrong, and we will fix it.

What they cost

Prices per month in US dollars, from each provider's pricing page on 24 October 2026. They count different things, so compare them with your own numbers.

Pusher Channels

A fixed plan for connections at the same time and messages a day.

Plan Price Connections Messages a day
SandboxFree100200,000
Startup$495001 million
Pro$992,0004 million
Business$2995,00010 million
Premium$49910,00020 million
Growth$69915,00040 million
Plus$89920,00060 million
Growth Plus$1,19930,00090 million
EnterpriseCustomCustomCustom

Source: pusher.com/channels/pricing, 24 October 2026.

Ably

A base fee, then usage on top.

Free
6 million messages a month, 200 peak connections, 200 channels
Standard
$29 a month plus usage
Pro
$399 a month plus usage
Usage
$2.50 per million messages, $1.00 per million connection minutes, $1.00 per million channel minutes

Source: ably.com/pricing, 24 October 2026.

PubNub

Priced by monthly active users or transactions.

Free
200 monthly active users or 1 million transactions
Starter
$98 a month, 1,000 monthly active users
Pro
Custom

Source: pubnub.com/pricing, 24 October 2026.

Pikoman

Free up to 100 connections at the same time per app, with up to 100 HTTP requests a second. Every feature on this page is included.

Paid plans are not published yet.

Need more connections? Tell us what you need: hello@pikoman.se

Pusher's free 200,000 messages a day come to about 6 million a month, the same as Ably's free plan. Pusher's price is fixed per plan; Ably's paid plans add what you use to the base fee.

Moving from Pusher

Pikoman is not a drop-in replacement. Pikoman 2.0 has its own protocol, so pusher-js and Laravel Echo cannot connect to it. You change the client code and the server code. The concepts map one to one, so the change is mostly renaming.

In the browser

Install npm install @pikoman/ws@next. subscribe becomes listen and bind becomes on. There is no cluster to choose.

Pusher
import Pusher from "pusher-js";

const pusher = new Pusher("app-key", { cluster: "eu" });

const orders = pusher.subscribe("orders");
orders.bind("order-shipped", (data) => {
  console.log(data.id);
});
Pikoman
import Pikoman from "@pikoman/ws";

const pikoman = new Pikoman("app-yourapp");

const orders = pikoman.listen("orders");
orders.on("order-shipped", (data) => {
  console.log(data.id);
});

Private and presence channels

Channel names keep their private- and presence- prefixes. Pusher's auth endpoint becomes a token endpoint: the client asks GET {auth_url}/{clientId}-{channel} and your backend answers with a join token signed by the app's private key. Messages between members need no client- prefix, and their handlers get the sender's connection id first.

Pusher
const pusher = new Pusher("app-key", {
  cluster: "eu",
  channelAuthorization: { endpoint: "/pusher/auth" },
});

const room = pusher.subscribe("presence-room");
room.bind("client-typing", (data) => showTyping(data));
room.trigger("client-typing", { on: true });
Pikoman
const pikoman = new Pikoman("app-yourapp", {
  auth_url: "/pikoman/auth",
});

const room = pikoman.listenPresence("presence-room");
room.on("typing", (from, data) => showTyping(data));
room.send("typing", { on: true });
Pusher, server (Express)
app.post("/pusher/auth", (req, res) => {
  const { socket_id, channel_name } = req.body;
  if (!mayJoin(req.user, channel_name)) return res.sendStatus(403);
  res.send(pusher.authorizeChannel(socket_id, channel_name, {
    user_id: req.user.id,
    user_info: { name: req.user.name },
  }));
});
Pikoman, server (Express)
import { OfflineAuthenticator } from "@pikoman/ws";

app.get("/pikoman/auth/:join", async (req, res) => {
  const [clientId, channel] = req.params.join.split(/-(.*)/);
  if (!mayJoin(req.user, channel)) return res.sendStatus(403);
  const signer = new OfflineAuthenticator({ name: req.user.name },
    process.env.PIKOMAN_PRIVATE_KEY, 3);
  res.json({ token: await signer.authenticate(clientId, channel) });
});

Connection ids never contain -, so the path splits at the first one. OfflineAuthenticator signs with the private key from the dashboard; 3 makes a full member. A PHP endpoint and one with no library are in the quickstart. A custom request, such as a POST with a CSRF token, works with your own authenticator.

Publishing from your backend

trigger becomes publish. The API key from the dashboard signs a short-lived token for each request, so the key itself is never sent.

Pusher
import Pusher from "pusher";

const pusher = new Pusher({
  appId: "123456",
  key: "app-key",
  secret: process.env.PUSHER_SECRET,
  cluster: "eu",
});

await pusher.trigger("orders", "order-shipped", { id: 1042 });
Pikoman
import { Rest } from "@pikoman/ws";

const rest = new Rest({
  app: "app-yourapp",
  apiKey: process.env.PIKOMAN_API_KEY,
});

await rest.publish("orders", "order-shipped", { id: 1042 });

From PHP or Python, signing the token takes about ten lines, and the request is a plain POST. Encrypted channels keep the private-encrypted- name; how keys reach the clients is in Encrypted channels.

What Pusher has that Pikoman does not

If you depend on one of these, Pusher is the safer choice today.

  • Webhooks

    Pusher can call your server when a channel fills or empties and when members come and go. Pikoman has no webhooks. Your backend can ask with stats and clients instead.

  • Libraries in many languages

    Pusher has official server libraries for many languages, and clients for iOS and Android. Pikoman has a JavaScript client and a Rust crate. Other languages sign tokens by hand, which the docs show for PHP and Python.

  • Laravel broadcasting

    Laravel has a Pusher driver built in, and Laravel Echo speaks Pusher's protocol. Neither works with Pikoman.

  • Push notifications

    Pusher has mobile push notifications as a separate product, Beams. Pikoman only reaches open connections.

  • A long track record

    Pusher has run in production for many years. Pikoman 2.0 is new: the client is a release candidate, and we publish no SLA. The status page shows our measured uptime.

See it before you switch

The demos run on this site's own app and show their socket traffic. When you want your own, sign in with GitHub and get an app key in a minute.

We are rebuilding the dashboard

Self-service sign-up is closed while we do. Until the new dashboard is ready we set up apps by hand: write to us with a line about what you are building, and we will set up your app and get back to you.

Write to hello@pikoman.se