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-dev

It starts with a default app and prints its credentials:

.env
THUNDERCLAP_APP_ID=local
THUNDERCLAP_KEY=local-key
THUNDERCLAP_SECRET=local-secret
THUNDERCLAP_URL=http://localhost:6001

The 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.

client.ts
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
});
server.ts
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

VariableDefaultWhat it sets
THUNDERCLAP_APPSThe default appThe apps it serves, as a JSON array (below)
PORT6001The port it listens on inside the container
HOST0.0.0.0The 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
FieldRequiredWhat it is
idYesThe app id (THUNDERCLAP_APP_ID)
keyYesThe public key (THUNDERCLAP_KEY)
secretYes, or secretsThe secret (THUNDERCLAP_SECRET)
secretsNoUp to two secrets, both valid, to try a rotation
clientEventsNotrue to allow client events
webhooksNoWebhook 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-dev

Docker Compose

compose.yaml
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:

.github/workflows/test.yml
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 test

How 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.