Self-hosted Evolu CRDT relay with structured logging, metrics, and quota management.
Wraps @evolu/nodejs — all sync protocol and CRDT logic is from Evolu. This project adds the operational layer for running a relay in production.
Built for getbased, a blood work dashboard that uses Evolu for cross-device sync.
The official Evolu relay works but lacks operational tooling:
- Logging is all-or-nothing (silent or raw SQL dump)
- Quota is hardcoded at 1MB (too small for real use)
- No health endpoint (health probes cause WebSocket errors)
- No metrics (can't see owner count, storage usage, connections)
This wrapper fixes all of that without forking the Evolu monorepo.
See evoluhq/evolu#661 for the full writeup.
docker run -d \
--name relay \
-p 4000:4000 \
-p 4001:4001 \
-v relay-data:/data \
-e QUOTA_PER_OWNER_MB=10 \
-e ADMIN_TOKEN=your-secret \
getbased-relay:latestOr with docker-compose:
docker compose up -dRequires Node.js >= 22 and TypeScript.
npm install
npm run build
npm startAll settings via environment variables. See .env.example for the full list.
| Variable | Default | Description |
|---|---|---|
RELAY_PORT |
4000 |
Evolu WebSocket relay port |
ADMIN_PORT |
4001 |
Health/metrics HTTP port |
QUOTA_PER_OWNER_MB |
10 |
Max stored bytes per identity |
QUOTA_GLOBAL_MB |
1000 |
Max total stored bytes |
OWNER_TTL_DAYS |
90 |
Days before owner flagged as stale |
LOG_LEVEL |
info |
debug, info, warn, error |
LOG_FORMAT |
json |
json or text |
ADMIN_TOKEN |
(none) | Bearer token for /metrics (omit for open access) |
Relay port (default 4000) — Evolu WebSocket sync. Connect your Evolu client with:
transports: [{ type: "WebSocket", url: "wss://your-relay.example.com" }]Admin port (default 4001, localhost only) — HTTP endpoints:
GET /health— Always public. Returns{"status":"ok","uptime":...}GET /metrics— RequiresAuthorization: Bearer <ADMIN_TOKEN>if configured. Returns owner count, per-owner storage, DB size, connection count, quota settings.
The relay port serves WebSocket only. Use Caddy or nginx for TLS termination:
# Caddyfile
sync.example.com {
reverse_proxy localhost:4000
}
The admin port binds to 127.0.0.1 — access it via SSH tunnel or add a proxied route.
┌─────────────────────────┐
│ getbased-relay │
│ │
Evolu clients ──WSS──▶ │ :4000 @evolu/nodejs │──▶ SQLite DB
│ (CRDT relay) │ (/data/*.db)
│ │
Uptime monitors ─HTTP─▶ │ :4001 Admin server │──▶ Read-only queries
│ (/health,/metrics)│
└─────────────────────────┘
- src/index.ts — Entry point, wiring, signal handlers
- src/lib/config.ts — Env var parsing with defaults and typed
RelayConfiginterface - src/lib/logger.ts — Custom Console that intercepts Evolu's 17 relay events, emits structured JSON at configurable levels
- src/lib/quota.ts — Per-owner + global disk quota via
isOwnerWithinQuotacallback - src/lib/owner-tracker.ts — Last-seen tracking via relay subscribe events, persisted to sidecar file
- src/lib/metrics.ts — Read-only SQLite queries against the relay DB
- src/lib/admin-server.ts — HTTP server for
/healthand/metrics, timing-safe token auth - src/lib/startup-check.ts — DB integrity validation on boot (magic bytes, PRAGMA check, table audit)
A simple HTTP API that stores per-profile lab context behind token auth. MCP servers and bot plugins use it to query health data on behalf of messenger/bot interfaces. Runs alongside the Evolu relay as a separate service.
Each authenticated token gets a JSON file in /opt/context-gateway/data/ (filename is a hash of the token). Context is stored per-profile, so multiple profiles can coexist under the same token. The format is backward-compatible with the old single-context layout — existing files are migrated on first write.
All endpoints require Authorization: Bearer <token>.
| Method | Path | Description |
|---|---|---|
POST /api/context |
— | Push context. Body: { context, profileId, profiles } |
GET /api/context |
— | Get the default profile's context |
GET /api/context?profile=<id> |
— | Get a specific profile's context |
The docker-compose.yml runs both services:
| Service | Port | Purpose |
|---|---|---|
| Evolu relay | :4000 |
CRDT sync (WebSocket) |
| Context gateway | :4001 |
Lab context API (HTTP) |
docker compose up -dBuilt on Evolu by Daniel Steigerwald. All sync protocol, CRDT logic, and SQLite storage are from Evolu — this project only adds the operational wrapper.
GPL-3.0