Local development
Thunderclap Local is the Thunderclap engine in a Docker image. Run it on your machine, in your tests and in CI instead of the managed service: it speaks the same protocol, so your code stays the same and only the address and credentials change. It works offline, and nothing you do with it counts towards your plan.
For development, tests and CI only
Thunderclap Local is free to use while you build and test, for companies too, but its licence doesn't allow production: anything that serves real users runs on the managed service.
Run it
docker run --rm -p 6001:6001 ghcr.io/bside-tech/thunderclap-devIt starts with a default app and prints its credentials:
THUNDERCLAP_APP_ID=localTHUNDERCLAP_KEY=local-keyTHUNDERCLAP_SECRET=local-secretTHUNDERCLAP_URL=http://localhost:6001The default app has client events switched on. The image runs on Intel and ARM machines, Apple silicon included.
Point the SDKs at it
Both SDKs take a url. Without one they connect to the managed service, so set THUNDERCLAP_URL in your development and test environments only, and the same code reaches production with production's credentials.
import { Thunderclap } from "@thunderclapjs/client";
const thunderclap = new Thunderclap({
key: process.env.NEXT_PUBLIC_THUNDERCLAP_KEY,
url: process.env.NEXT_PUBLIC_THUNDERCLAP_URL, // http://localhost:6001 in development
});import { ThunderclapServer } from "@thunderclapjs/server";
const thunderclap = new ThunderclapServer({
appId: process.env.THUNDERCLAP_APP_ID,
secret: process.env.THUNDERCLAP_SECRET,
url: process.env.THUNDERCLAP_URL,
});Everything else works as in the other guides: channels, private channels and their tokens, presence, cache channels, missed messages and webhooks.
Configure it
| Variable | Default | What it sets |
|---|---|---|
THUNDERCLAP_APPS | The default app | The apps it serves, as a JSON array (below) |
PORT | 6001 | The port it listens on inside the container |
HOST | 0.0.0.0 | The address it listens on inside the container |
Your own apps
To use the same credentials as your .env files, or several apps, list them in THUNDERCLAP_APPS. The default app is then not served.
docker run --rm -p 6001:6001 \
-e THUNDERCLAP_APPS='[{"id":"chat-dev","key":"chat-dev-key","secret":"chat-dev-secret","clientEvents":true}]' \
ghcr.io/bside-tech/thunderclap-dev| Field | Required | What it is |
|---|---|---|
id | Yes | The app id (THUNDERCLAP_APP_ID) |
key | Yes | The public key (THUNDERCLAP_KEY) |
secret | Yes, or secrets | The secret (THUNDERCLAP_SECRET) |
secrets | No | Up to two secrets, both valid, to try a rotation |
clientEvents | No | true to allow client events |
webhooks | No | Webhook endpoints (below) |
Any values work: Thunderclap Local accepts whatever ids, keys and secrets you give it. A malformed THUNDERCLAP_APPS stops the container with an error saying what's wrong.
Webhooks
Each endpoint has a url, and optionally the events it receives, among channel_existence, presence, client_events and cache_miss (all but client_events by default). Requests are signed with the app's secrets, so verify them with verifyWebhook as in the webhooks guide; plain http:// URLs are fine locally.
The container reaches your machine at host.docker.internal. Docker Desktop provides that name; on Linux, add --add-host=host.docker.internal:host-gateway.
docker run --rm -p 6001:6001 --add-host=host.docker.internal:host-gateway \
-e THUNDERCLAP_APPS='[{"id":"local","key":"local-key","secret":"local-secret","webhooks":[{"url":"http://host.docker.internal:3000/thunderclap/webhooks"}]}]' \
ghcr.io/bside-tech/thunderclap-devDocker Compose
services:
thunderclap:
image: ghcr.io/bside-tech/thunderclap-dev:0
ports:
- "6001:6001"Your other services reach it at http://thunderclap:6001, and browsers on your machine at http://localhost:6001.
In CI
Run it as a service container, so your tests have a realtime backend without credentials or network access. With GitHub Actions:
jobs:
test:
runs-on: ubuntu-latest
services:
thunderclap:
image: ghcr.io/bside-tech/thunderclap-dev:0
ports:
- 6001:6001
env:
THUNDERCLAP_URL: http://localhost:6001
THUNDERCLAP_APP_ID: local
THUNDERCLAP_KEY: local-key
THUNDERCLAP_SECRET: local-secret
steps:
- uses: actions/checkout@v5
- run: npm ci && npm testHow it differs from the service
Thunderclap Local runs the engine in one process, with everything in memory. That's what you want for development, but it isn't the service:
- Restarting it starts over. Presence members and cache channels are gone, and channels start a new sequence, so clients that stayed connected get a gap with
reset: true(missed messages). - No plans and no usage. Nothing is metered, capped or rate limited, so it won't show you a plan's limits. The size limits of messages and channels are the same.
- The dashboard doesn't see it. The Debug tab and usage pages show the managed service's apps only.
- It doesn't scale. It's one process on your machine, so run load tests against the managed service.
Versions
Each release is tagged with its version (0.1.0), its minor and major versions (0.1, 0) and latest. Pin a major version, such as ghcr.io/bside-tech/thunderclap-dev:0, to get fixes without breaking changes.
Licence
The Thunderclap Local Licence lets anyone, companies included, use the image for free on developers' machines, in automated tests and in CI. It doesn't allow serving real users, offering it as a service, or redistributing or modifying it. The SDKs stay MIT-licensed, whatever you connect them to.