Production-oriented realtime chat template built with Bun + native WebSocket + Drizzle.
BunRelay is designed to be cloned and evolved into real products, not demo-only experiments:
- Native
Bun.serve()for HTTP + WS in one process. - Shared protocol contracts with Zod (
packages/shared). - SQLite-first local dev and Postgres-ready deployment mode.
- Auth stack with refresh token rotation + replay detection.
- Built-in observability baseline (structured logs, request IDs, metrics endpoints).
- Fast startup path: run local with SQLite and no Docker.
- Clear migration path: switch to Postgres via environment variables.
- Typed protocol boundaries: server/client event schemas in one shared package.
- Practical security defaults: rate limits, payload caps, WS heartbeat/auth timeouts.
- Operational baseline included: metrics for HTTP/WS/security and JSON logs.
Client (browser/mobile/backend)
| REST (/auth/*, /rooms/*, /health, /ops/*)
| WS (/ws)
v
Bun.serve() entrypoint (apps/server/src/index.ts)
|-- REST routes (auth, rooms, health, metrics)
|-- WS handler/router (auth, room join, messaging, delivery, read, typing, presence)
|-- Services (message, room, presence, audit, pubsub)
|-- Auth/JWT middleware + session checks
v
Drizzle ORM
|-- SQLite (default)
'-- Postgres (DB_DRIVER=postgres)
|
Pub/Sub layer (optional)
|-- Redis channel fanout for multi-instance WS delivery
'-- Local-only fallback when REDIS_URL is not set
| Area | Choice |
|---|---|
| Runtime | Bun |
| HTTP + WS | Native Bun.serve() |
| ORM | Drizzle ORM |
| Database | SQLite / Postgres |
| Validation | Zod |
| Auth | JWT (jose) + hashed opaque refresh tokens |
| Demo UI | Vanilla HTML/JS (apps/server/public/index.html) |
POST /auth/loginandPOST /auth/refresh- Refresh token rotation with reuse detection and session revocation
- WS event flow: auth, room join, send/receive, delivered, read, typing, heartbeat
- Optional Redis Pub/Sub fanout for multi-instance realtime delivery (local fallback without Redis)
- Message idempotency via client
messageId - Room-scoped presence and snapshots, including WS room member updates
- Room creation and management APIs: DM/group creation, member add/remove, role change, ownership transfer
- Group governance hardening with
ownerrole and protected ownership flows - Room audit trail (
room_audit_log) + paginatedGET /rooms/:roomId/audit - HTTP + WS limits and defensive validation
- Metrics endpoints:
GET /ops/metrics(JSON) andGET /ops/metrics.prom(Prometheus format) - GitHub Actions for CI/CD and optional SonarQube scanning
bun install
bun run db:migrate
bun run db:seed
bun run devOpen:
- App:
http://localhost:3000 - Health:
http://localhost:3000/health - Metrics JSON:
http://localhost:3000/ops/metrics - Metrics Prometheus:
http://localhost:3000/ops/metrics.prom
Seed users:
alicebobcarlosdianaerin
Password for all seeded users: password123
docker compose up --buildThe compose flow starts Postgres, Redis, and server, then runs:
bun run db:migratebun run db:seedbun run start
Defaults live in apps/server/src/config/env.ts, so .env is optional for local runs.
cp .env.example .envPowerShell:
Copy-Item .env.example .env| Variable | Default | Description |
|---|---|---|
NODE_ENV |
development |
development, test, production |
LOG_LEVEL |
info |
debug, info, warn, error |
HOST |
0.0.0.0 |
server host |
PORT |
3000 |
HTTP/WS port |
DB_DRIVER |
sqlite |
sqlite or postgres |
DB_URL |
apps/server/data/dev.sqlite |
SQLite path or Postgres URL |
REDIS_URL |
empty | optional Redis URL for multi-instance pub/sub |
JWT_SECRET |
dev-secret-change-me-at-least-32-characters |
HS256 secret, use a strong one |
JWT_ACCESS_TTL_SEC |
900 |
access token TTL |
JWT_REFRESH_TTL_SEC |
604800 |
refresh/session TTL |
HTTP_MAX_BODY_BYTES |
16384 |
max REST JSON body size |
WS_MAX_MESSAGE_BYTES |
16384 |
max WS frame size |
WS_RATE_LIMIT_PER_SEC |
50 |
per-connection token bucket |
WS_AUTH_TIMEOUT_MS |
5000 |
max time before auth:hello |
WS_HEARTBEAT_TIMEOUT_MS |
45000 |
max silence before close |
MESSAGE_MAX_CHARS |
4000 |
max message content length |
TRACING_ENABLED |
false |
enable tracing spans/context pipeline |
TRACING_SERVICE_NAME |
bunrelay-server |
service name attached to exported spans/log sink batches |
TRACING_OTLP_HTTP_URL |
empty | OTLP traces endpoint (example: http://localhost:4318/v1/traces) |
TRACING_OTLP_HEADERS |
empty | custom OTLP headers (key=value,key2=value2) |
TRACING_SAMPLING_RATIO |
1 |
root span sampling ratio (0..1) |
TRACING_EXPORT_BATCH_SIZE |
64 |
max spans per export batch |
TRACING_EXPORT_INTERVAL_MS |
5000 |
periodic export flush interval |
TRACING_EXPORT_TIMEOUT_MS |
2500 |
OTLP export request timeout |
LOG_SINK_HTTP_URLS |
empty | comma-separated external HTTP sink URLs |
LOG_SINK_HTTP_HEADERS |
empty | headers for log sink requests (key=value,key2=value2) |
LOG_SINK_BATCH_SIZE |
100 |
max records per sink push |
LOG_SINK_FLUSH_INTERVAL_MS |
2000 |
periodic log sink flush interval |
LOG_SINK_TIMEOUT_MS |
2500 |
sink request timeout |
LOG_SINK_BUFFER_MAX |
2000 |
in-memory sink buffer cap per endpoint |
| Script | Command | Purpose |
|---|---|---|
bun run dev |
bun run --cwd apps/server dev |
run server with watch |
bun run start |
bun run --cwd apps/server start |
run server without watch |
bun run db:migrate |
bun run --cwd apps/server db:migrate |
apply migrations |
bun run db:seed |
bun run --cwd apps/server db:seed |
load demo users/rooms |
bun run typecheck |
root + shared typecheck | static type validation |
bun run test |
bun run --cwd apps/server test |
auth + e2e tests |
bun run test:ui-smoke |
node scripts/ui-smoke.mjs |
browser smoke (login, DM/group create, member governance, audit) |
bun run ci |
typecheck + test |
local CI equivalent |
Detailed reference moved to docs/api.md:
- REST endpoints and auth flow
- WS event contract and sequencing
- Error model and operational limits
- Ready-to-run curl examples and troubleshooting hints
- JWT access tokens (
HS256) with user + session identity. - Opaque refresh tokens stored as SHA-256 hashes.
- Refresh replay detection revokes session.
- Login rate limiting (
5/minper IP + username outside tests). - WS auth timeout + heartbeat timeout + per-connection event throttling.
- Payload size limits and strict Zod validation on REST and WS envelopes.
- Group governance rules with owner/admin/member roles, protected owner transfer, and owner safety constraints.
- Audit logging for room governance mutations (
room_created, member add/remove, role updates, owner transfer).
- Structured JSON logs (
ts,level,event, context fields). - Automatic request correlation with
x-request-id. - Trace correlation via W3C
traceparent(ingress + response propagation on HTTP). - Optional OTLP/HTTP span export (
TRACING_OTLP_HTTP_URL). - Optional external log sink fanout via batched HTTP JSON (
LOG_SINK_HTTP_URLS). - HTTP counters: totals, status classes, route-level counters, avg latency.
- WS counters: upgrades, active connections, in/out by event type, invalid payloads.
- Security event counters exposed in JSON and Prometheus formats.
- CI workflow:
.github/workflows/ci.yml - CD workflow:
.github/workflows/cd.yml - SonarQube workflow:
.github/workflows/sonarqube.yml(runs only when required secrets/vars are present) - Container publish target:
ghcr.io/GustavoQnt/bunrelay
- GitHub repo hardening (required checks, branch protection, environment secrets)
- Optional frontend productization (UX polish, reconnect/error states, accessibility QA)
BunRelay e um template de chat em tempo real para acelerar times que querem sair rapido do prototipo e evoluir para algo operavel.
Pontos fortes:
- Backend Bun puro com HTTP + WebSocket no mesmo processo.
- Contratos compartilhados com Zod.
- Desenvolvimento local simples com SQLite.
- Modo Postgres pronto para ambiente mais realista.
- Modo multi-instancia opcional com Redis Pub/Sub.
- Auth com refresh rotativo e deteccao de reuso.
- Governanca de grupos com papel
ownere transferencia de ownership. - Trilha de auditoria de mudancas de membros e ownership.
- Observabilidade baseline pronta (logs JSON + metricas).
bun install
bun run db:migrate
bun run db:seed
bun run devEndpoints uteis:
http://localhost:3000/healthhttp://localhost:3000/ops/metricshttp://localhost:3000/ops/metrics.prom
Usuarios de seed: alice, bob, carlos, diana, erin
Senha: password123
docker compose up --buildCopie:
Copy-Item .env.example .envNo minimo, altere JWT_SECRET para um valor forte fora de ambiente local.
- API REST e protocolo WS:
docs/api.md - Plano tecnico/roadmap:
docs/plans/2026-02-20-bunrelay-design.md - Integracoes observability avancadas: OTLP traces + HTTP log sinks via
.env.example