Jon Staab 43eaad1621
Docker / build-and-push-image (push) Successful in 52m8s
Differentiate checkout id/session id
2026-06-03 14:27:57 -07:00
2026-06-02 15:11:19 -07:00
2026-06-02 10:01:43 -07:00
2026-04-09 14:11:30 -07:00
2026-06-02 10:01:43 -07:00
2026-05-29 11:32:06 -07:00
2026-06-02 14:17:27 -07:00
2026-05-05 17:47:13 -07:00

Caravel

A multi-tenant platform for hosting Nostr community relays, built on top of zooid.

Deployment

Caravel ships as a single Docker image, built from the repository-root Dockerfile and published by .gitea/workflows/docker-publish.yml as gitea.coracle.social/coracle/caravel. One container runs both services: the backend API on port 2892 and the frontend on port 3000.

Both the backend and the frontend are compiled into the image at build time. The frontend is built with placeholder config that the entrypoint replaces with the real values when the container starts, so one image can be deployed with any configuration — no rebuild required.

Caravel needs a reachable zooid instance (the Local Development section below shows how to run one). Substitute your own values for the placeholders below:

docker run -d \
  --name caravel \
  -p 2892:2892 \
  -p 3000:3000 \
  -v my-caravel-data:/app/data \
  -e PLATFORM_NAME=Caravel \
  -e RELAY_DOMAIN=example.com \
  -e APP_URL=https://example.com \
  -e ZOOID_API_URL=http://zooid:3334 \
  -e DATABASE_URL=sqlite://data/caravel.db \
  -e SERVER_URL=https://api.example.com \
  -e SERVER_PORT=2892 \
  -e SERVER_ADMIN_PUBKEYS=<your-hex-pubkey> \
  -e SERVER_ALLOW_ORIGINS=https://example.com \
  -e ROBOT_SECRET=<hex-nostr-secret-key> \
  -e ROBOT_NAME=Caravel \
  -e ROBOT_DESCRIPTION="Relay manager bot" \
  -e ROBOT_PICTURE=https://example.com/robot.png \
  -e ROBOT_WALLET=<nwc-connection-uri> \
  -e ROBOT_OUTBOX_RELAYS=wss://relay.damus.io,wss://relay.primal.net,wss://nos.lol \
  -e ROBOT_INDEXER_RELAYS=wss://purplepag.es,wss://relay.damus.io,wss://indexer.coracle.social \
  -e ROBOT_MESSAGING_RELAYS=wss://auth.nostr1.com,wss://relay.keychat.io,wss://relay.ditto.pub \
  -e BLOSSOM_S3_ENDPOINT=https://s3.example.com \
  -e BLOSSOM_S3_REGION=us-east-1 \
  -e BLOSSOM_S3_BUCKET=caravel-blossom \
  -e BLOSSOM_S3_ACCESS_KEY=<s3-access-key> \
  -e BLOSSOM_S3_SECRET_KEY=<s3-secret-key> \
  -e LIVEKIT_URL=wss://livekit.example.com \
  -e LIVEKIT_API_KEY=<livekit-api-key> \
  -e LIVEKIT_API_SECRET=<livekit-api-secret> \
  -e STRIPE_SECRET_KEY=<stripe-secret-key> \
  gitea.coracle.social/coracle/caravel

Notes:

  • Every backend variable above is required — the server exits on startup if any is missing or empty. See backend/.env.template for what each one means.
  • ROBOT_SECRET is the robot account's hex nostr secret key; its pubkey must be in zooid's API_WHITELIST (the backend signs zooid requests with it via NIP-98).
  • SERVER_ALLOW_ORIGINS must include the frontend's public origin, or browsers will be blocked by CORS.
  • The frontend's VITE_ prefixed env variables are automatically populated from the provided env variables.
  • -v my-caravel-data:/app/data persists the SQLite database.

To build the image yourself instead of pulling it:

docker build -t caravel .

Local Development

Prerequisites

  • Rust (for the backend)
  • Bun (for the frontend)
  • just (task runner)
  • onchange (npm i -g onchange, used by just dev for backend file watching)
  • Docker (for zooid)

1. Start zooid

Zooid is the relay engine that Caravel manages. The backend authenticates to zooid's API using NIP-98, signing requests with a Nostr secret key. Zooid must be configured to accept requests from the corresponding public key.

Generate a keypair to use for this. The hex secret key goes in the backend's ZOOID_API_SECRET, and the hex public key goes in zooid's API_WHITELIST.

docker run -it \
  -p 3334:3334 \
  -e API_HOST=127.0.0.1:3334 \
  -e API_WHITELIST=<hex-pubkey-matching-ZOOID_API_SECRET> \
  -v ./config:/app/config \
  -v ./media:/app/media \
  -v ./data:/app/data \
  gitea.coracle.social/coracle/zooid

2. Configure the backend

Copy the template and fill in the required values:

cp backend/.env.template backend/.env

At minimum for local dev, set:

Variable Value Notes
ADMINS Your hex pubkey Gives you admin access in the UI
ZOOID_API_SECRET Hex Nostr secret key The keypair whose pubkey you put in API_WHITELIST above
RELAY_DOMAIN localhost Base domain appended to relay subdomains

The rest of the defaults work as-is. ROBOT_*, LIVEKIT_*, billing, and Stripe vars are optional for basic local development.

3. Configure the frontend

cp frontend/.env.template frontend/.env

The defaults (VITE_API_URL=http://127.0.0.1:2892) point at the backend and work out of the box.

4. Install dependencies and run

cd frontend && bun install && cd ..
just dev

This starts the backend (with auto-reload on file changes) at http://127.0.0.1:2892 and the frontend at http://127.0.0.1:5173.

Project docs

S
Description
Hosting manager and dashboard for zooid relays
Readme 4.7 MiB
Languages
TypeScript 59.9%
Rust 36.6%
JavaScript 2%
Dockerfile 0.7%
Shell 0.3%
Other 0.4%