Matthew Meszaros 3837cd3898 feat: bound auth login network waits
Add request and mail-delivery timeouts around auth flows so login requests cannot hang indefinitely when notification delivery stalls.

Allow the local admin dev origin through default CORS and update context-aware lint fixes so the repository lint gate passes.
2026-06-01 16:56:24 +02:00
2026-06-01 11:36:04 +02:00
2026-05-30 04:17:10 +00:00
2026-06-01 11:36:04 +02:00
2026-06-01 03:09:17 +02:00
2026-06-01 03:09:17 +02:00
2026-01-17 09:19:43 +00:00
2026-05-31 09:11:20 +02:00

Warmbly

Open-source cold email and mailbox warmup you can self-host.
Your IPs, your database, your infrastructure — no vendor lock-in.

License: Apache 2.0  Go 1.25  PostgreSQL 16  Self-hostable  PRs welcome

Features · Quick start · Architecture · Self-hosting · Docs · Contributing


What is Warmbly

Warmbly is a cold-outreach and mailbox-warmup platform that runs on your own infrastructure. Hosted services keep your sender reputation and your data in someone else's IP pool and someone else's database; building it yourself is months of plumbing. Warmbly is the middle path — the features you'd expect from a SaaS, running on the boxes you control.

It runs on a single VPS for a small setup, or across a fleet of cheap servers with many IPs per box. The same code handles both.

Features

  • Self-hostable — one binary per service. Postgres, Redis, and an event bus are the whole stack. No required calls to AWS, GCP, Stripe, or Cloudflare.
  • Distributed workers — one worker per IP, many workers per box. Reputation is tracked per IP, not per machine. Multi-IP install in a single command.
  • Pluggable backends — KMS, blob store, event bus, and codec are all chosen at deploy time: AWS or local AES, Kafka or NATS, S3 or filesystem.
  • Real warmup — pool-based warmup with spam-score tracking and auto-blocking on token-forgery patterns. Free and premium pools stay isolated.
  • Mailbox-first safety — per-mailbox send caps and spacing, gradual warmup ramps. A worker's safe volume is the sum of its mailboxes' budgets, not a flat per-worker limit.
  • Envelope encryption — KMS-wrapped per-user data keys. Workers fetch them over HTTPS and never touch Postgres directly.
  • Admin UI — a separate React app for workers, sending identities, mailboxes, warmup pools, audit log, and backend selection.
  • Real-time tracking — open/click pixel and redirect service with deduplication at both the tracker and the consumer, resistant to replays.

Quick start

You'll need Docker, Go 1.25, and pnpm. The fastest path brings infra up in Docker and the app services up against it:

git clone https://github.com/warmbly/warmbly && cd warmbly

make infra   # Postgres, Redis, Kafka + supporting services (run once, leave up)
make app     # backend, consumer, worker, tracking, realtime, dashboard

open http://localhost:5173

For the fastest dev loop, run the Go services natively instead of in Docker (no image rebuilds on code changes):

make infra            # once
make run              # backend + consumer + worker in one terminal
make web              # dashboard dev server → http://localhost:5173

make run connects to infra on the same machine by default. To point it at infra hosted on another box, pass INFRA_HOST:

make run INFRA_HOST=192.168.1.50

The admin app and marketing site run on demand from their own terminals:

make admin   # → http://localhost:5174
make site    # → http://localhost:4321

The admin app needs an account with admin permissions. There's no way to bootstrap the first admin from the UI, so sign up through the dashboard, then promote yourself from the host:

make grant-admin EMAIL=you@example.com               # super (all permissions)
make grant-admin EMAIL=you@example.com ROLE=support  # support | ops | analyst
make revoke-admin EMAIL=you@example.com

For production, see Self-hosting.

Architecture

Warmbly splits cleanly into a control plane and an execution plane.

The control plane is the backend API, the event consumer, Postgres, Redis, and the event bus. It owns all stateful data and decides what to send and where.

The execution plane is the distributed worker fleet — one Go binary per machine, one worker process per IP. Workers receive commands over the event bus, fetch their encryption keys over HTTPS, send and sync mail, and emit telemetry back. Workers never connect to Postgres.

That separation is the point: workers scale horizontally across many cheap machines, each one a sending identity rather than a database client, so outbound volume spreads across many IPs instead of concentrating in one runtime. The full write-up is in resources/architecture.md.

Self-hosting

Every external dependency has an open-source path, selected by an environment variable. A self-hoster pays only for the boxes they rent.

Concern Self-host default Cloud option
Database PostgreSQL 16 RDS / Cloud SQL
Cache Redis (or Valkey) ElastiCache
Event bus NATS JetStream (1 binary) Kafka, MSK
Blob storage Filesystem S3, MinIO, R2, B2
KMS / root key Local AES master key AWS KMS, Vault, GCP
Encrypted keys PostgreSQL table DynamoDB / Scylla
Codec JSON Avro + Schema Registry
Captcha Bypass token (trusted) Cloudflare Turnstile
Payments Off Stripe

A minimal self-hosted backend env:

KMS_PROVIDER=local
KMS_LOCAL_MASTER_KEY=$(openssl rand -base64 32)

ENCRYPTED_KEYS_PROVIDER=postgres
INTERNAL_API_TOKEN=$(openssl rand -base64 32)

BLOB_PROVIDER=filesystem
BLOB_FS_ROOT=/var/lib/warmbly/blobs

EVENTBUS_PROVIDER=nats
NATS_URL=nats://localhost:4222

CODEC_PROVIDER=json

Workers add three variables so they read encryption keys over HTTPS instead of from a database:

ENCRYPTED_KEYS_PROVIDER=http
ENCRYPTED_KEYS_BACKEND_URL=https://api.yourdomain.com
ENCRYPTED_KEYS_WORKER_TOKEN=<same value as INTERNAL_API_TOKEN>

Multi-IP workers

One machine with many attached IPs becomes many sending identities with a single command. Each IP gets its own systemd unit and a deterministic identity, so reputation persists across reinstalls:

sudo ./scripts/install-worker.sh \
  --kafka kafka.yourdomain.com:9092 \
  --redis redis://cache.yourdomain.com:6379 \
  --ips 5.6.7.11,5.6.7.12,5.6.7.13,5.6.7.14

Full runbook: docs/MULTI_IP_WORKERS.md.

For the complete dependency audit and how to replace each cloud service, see docs/VENDOR_LOCKIN.md.

Stack

Component Tech
Backend API Go 1.25 + Gin
Consumer Go (event-bus driven)
Worker Go (Kafka / NATS subscriber)
Tracking Rust + Axum
Realtime Elixir + Phoenix Channels
Dashboard React 19 + Vite + Tailwind v4
Admin UI React 19 + Vite + Tailwind v4
Database PostgreSQL 16
Cache Redis 7 (or Valkey / KeyDB)
Event bus NATS JetStream (default) or Kafka

Project layout

cmd/
  backend/      REST API + admin orchestration
  consumer/     event-bus consumer → Postgres
  worker/       distributed sender (one per IP)
  seed/         local-dev fixtures
internal/
  api/          HTTP handlers, routes, middleware
  app/          business services (auth, email, campaign, ...)
  client/       SMTP/IMAP client + per-egress bind-IP
  events/       publisher + event schemas
  infrastructure/  pluggable codec, eventbus, kms, storage, encryptedkeys
  models/       domain types
  repository/   Postgres data access
tracking/       Rust open/click service
realtime/       Elixir WebSocket gateway
web/            user dashboard (Vite + React)
admin/          admin UI (Vite + React)
scripts/        worker installer and dev tooling
docs/           operator docs (auth, lock-in, multi-IP)
resources/      architecture and design notes

Building and testing

# build
go build ./cmd/...
cd tracking && cargo build --release
cd realtime && mix deps.get && mix release
cd web      && pnpm install && pnpm build
cd admin    && pnpm install && pnpm build

# test
go test ./...
cd web      && pnpm typecheck && pnpm lint
cd tracking && cargo test
cd realtime && mix test

Documentation

Doc What it covers
docs/VENDOR_LOCKIN.md Every external dependency and how to replace it
docs/INTERNAL_API_AUTH.md How workers authenticate to the backend
docs/MULTI_IP_WORKERS.md Many-IP worker deployment recipe
resources/architecture.md Control plane vs execution plane, encryption model
resources/local-development.md Docker Compose, profiles, seeding
resources/deployment-guide.md Production control plane + worker fleet
resources/Events.md Event bus reference
resources/EMSG.md Encrypted-message blob format
CONTRIBUTING.md Contribution guidelines and local checks

Security

Found a vulnerability? Email security@warmbly.com rather than opening a public issue. We prefer responsible disclosure and credit reporters in the release notes. The encryption model is documented in resources/architecture.md and the internal-API auth model in docs/INTERNAL_API_AUTH.md.

License

Apache License 2.0. Copyright 2026 Mindroot Ltd. See LICENSE.

Languages
Go 41.3%
TypeScript 36.5%
Swift 9.9%
Astro 9.3%
Elixir 0.9%
Other 2%