⭐ Help us reach more senders and grow the Warmbly community. Star this repo!
+
+
+## Warmbly
+
+Warmbly runs cold email campaigns from the mailboxes you already own and warms
+them so they keep landing in the inbox. Opens, clicks, and replies land in a
+shared dashboard the moment they happen, and it's AI-native, so your team and its
+agents work in it together, live.
+
+https://github.com/user-attachments/assets/378a510a-bb99-425f-925e-04300184938b
+
+## Features
+
+- **Campaigns** - multi-step sequences with per-mailbox caps and spacing
+- **Unified inbox** - every mailbox and reply in one place
+- **CRM** - contacts, pipelines, deals, tasks, meetings
+- **Warmup** - a pool of monitored mailboxes, not throwaway accounts
+- **Deliverability** - bounces, complaints, suppression, inbox placement
+- **Automations** - visual reply playbooks with AI steps
+- **Integrations** - HubSpot, Slack, Zapier, REST API, webhooks
+- **Realtime** - live presence and edits across your team
- Open-source cold email and mailbox warmup you can self-host.
- Your sending IPs, your database, your servers.
+
+
+Warmbly splits into a **control plane** (backend API, consumer, Postgres, Redis,
+and the event bus) that owns all state, and an **execution plane** of
+interchangeable Go workers that send and sync mail. Workers never touch Postgres,
+and outbound mail leaves through each mailbox's own provider, not the worker's IP,
+so you add throughput by running more workers.
-
-
- Campaigns · multi-step sequences with per-mailbox daily caps and spacing
-
+```mermaid
+flowchart LR
+ MB["Your mailboxes"] --> API
+ subgraph CP["Control plane"]
+ direction TB
+ API["Backend API"] --> DB[("Postgres")]
+ API --> BUS{{"Event bus"}}
+ end
+ BUS --> W1["Worker"]
+ BUS --> W2["Worker"]
+ BUS --> W3["Worker"]
+ W1 --> P["Gmail · Microsoft · SMTP"]
+ W2 --> P
+ W3 --> P
+ P --> R["Recipients"]
+```
-
-
- Unified inbox · every connected mailbox and reply in one place
-
-
-
-
- Mailboxes · warmup state and health for every account
-
-
-## What is Warmbly
-
-Warmbly is a cold outreach platform. You connect your mailboxes, write sequenced
-campaigns, and it sends the mail, tracks the replies, and keeps your sender
-reputation healthy. The difference from hosted tools is where it runs: your
-sending IPs, your Postgres, your servers. Nothing is tied to a vendor's database.
-
-Everything a sending team needs sits in one dashboard:
-
-- **Campaigns** send multi-step sequences with per-mailbox daily caps and spacing.
-- **The unified inbox** pulls every connected mailbox and its replies into one view.
-- **A built-in CRM** tracks contacts, pipelines, deals, tasks, and meetings.
-- **Deliverability** surfaces bounces, complaints, suppression, and inbox placement.
-- **Automations** run branching reply playbooks on a visual canvas.
-- **Integrations** sync the CRM and automations out to HubSpot, Slack, and more.
-- **Warmup** builds real sender reputation through our pool, covered next.
-
-The dashboard is collaborative in real time: teammates see each other's live
-cursors, presence, and edits across campaigns, the CRM, and the automation
-canvas, with no refresh.
-
-The same code runs on a single VPS or across a fleet of cheap servers with many
-IPs per box, so you add capacity by adding machines.
-
-## Connect your mailboxes
-
-Warmbly sends and receives through the mailboxes you already own. There are three
-ways to connect one, and you can mix them freely across a workspace:
-
-- **Google / Gmail and Google Workspace.** Connect with one-click OAuth, no app
- password to store or rotate. Sending goes through the Gmail API.
-- **Microsoft 365 / Outlook.** Connect with one-click OAuth over authenticated
- SMTP and IMAP.
-- **Any other provider over SMTP + IMAP.** Zoho, Fastmail, a self-hosted mail
- server, anything that speaks SMTP and IMAP. Add the host, port, and an app
- password.
-
-Each mailbox warms, sends, and syncs on its own, with its own daily cap, minimum
-spacing between sends, and reputation tracked per IP. Replies stream into the
-unified inbox in near real time. Credentials and OAuth tokens are sealed with
-per-organization envelope encryption and are only decrypted on the worker that
-owns the mailbox, never stored in plaintext.
-
-## Integrations
-
-Automations and the built-in CRM connect out to the tools you already run: ping
-Slack on a positive reply, push a won deal to your CRM, book meetings straight
-from replies, or fan events out to your own stack.
-
-| Category | Providers |
-|---------------|----------------------------------------|
-| CRM | HubSpot, Salesforce, Pipedrive, Close |
-| Automation | Zapier, Make, n8n |
-| Notifications | Slack, Discord |
-| Meetings | Calendly, Cal.com |
-| Data | Google Sheets |
-
-Everything is also reachable through a scoped REST API, HMAC-signed webhooks, and
-a realtime WebSocket, so you can wire Warmbly into anything that is not on the
-list. Open tracking, click tracking, and reply detection feed the same event
-stream. See the [API reference](https://docs.warmbly.com/api/).
-
-## Warmup
-
-Warmup only produces meaningful results with a pool of real mailboxes warming
-against each other. Warmbly maintains that pool, so the practical path to real
-reputation is to run warmup through Warmbly: your mailboxes hold genuine
-conversations with monitored inboxes instead of throwaway accounts, even if you
-only have a few. If you operate enough mailboxes of your own to sustain a healthy
-pool, you can host warmup yourself instead.
-
-Either way the safeguards are the same. Volume starts low and ramps gradually per
-mailbox, replies happen at a natural rate, and every warmup message carries a
-verification token. Mailboxes that show spam patterns or forged tokens are scored
-and auto-blocked from the pool, so it stays clean for everyone in it. Free and
-premium pools are kept separate.
+Secrets use envelope encryption, with a local AES master key by default or AWS KMS
+if you prefer. Full write-up in the
+[architecture docs](https://docs.warmbly.com/development/architecture/).
## Quick start
@@ -127,79 +90,22 @@ git clone https://github.com/warmbly/warmbly && cd warmbly
make dev
```
-That one command brings up the backing services in Docker (Postgres, Redis,
-NATS), waits for them, applies migrations, seeds demo data, and starts the
-backend, consumer, worker, dashboard, and admin natively, with realtime and
-tracking as containers (so you need no Elixir or Rust toolchain). Open
-`http://localhost:5173` and log in with `dev@warmbly.com` / `password123`.
-Ctrl-C stops the app; the Docker infra stays up so the next `make dev` is fast.
-
-The Go services run natively (recompiling in a second or two on save), so the
-same command is also the day-to-day loop. If you prefer separate terminals, the
-stack splits into `make infra` + `make run` + `make web`. To run the whole
-no-cloud stack in Docker instead, use `make up` (see [Self-hosting](#self-hosting)).
-
-The first admin account cannot be created from the UI. Sign up through the
-dashboard, then promote yourself from the host with
-`make grant-admin EMAIL=you@example.com` and open the admin app with `make admin`.
-Full local setup, seeding, and troubleshooting live in the
+One command brings up the backing services in Docker, applies migrations, seeds
+demo data, and starts the backend, worker, and dashboard natively. Open
+`http://localhost:5173` and log in with `dev@warmbly.com` / `password123`. Full
+setup lives in the
[local development guide](https://docs.warmbly.com/development/local-development/).
-## How it works
-
-Warmbly is split 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 every piece of stateful data and decides what gets sent
-and from where.
-
-The execution plane is the worker fleet: one Go binary per machine. Workers take
-commands off the event bus, fetch their encryption keys over HTTPS, send and sync
-mail, and report telemetry back. **Workers never connect to Postgres.** They are
-interchangeable executors, so you add throughput by running more of them; outbound
-mail leaves through each mailbox's own provider (Gmail API, Graph, or SMTP relay),
-not the worker's IP.
-
-Secrets use envelope encryption: a per-organization data key, wrapped by a root
-key, seals mailbox credentials and message content. The root key is a local AES
-master key by default (no cloud), or AWS KMS if you prefer. The full write-up is
-in the [architecture docs](https://docs.warmbly.com/development/architecture/).
-
## Self-hosting
-Warmbly runs with **no cloud account of any kind**: no AWS, no GCP, no Stripe,
-no Kafka. One command brings up the whole platform on local, open-source pieces:
+Warmbly runs with **no cloud account of any kind**: no AWS, no GCP, no Stripe, no
+Kafka. One command brings up the whole platform on local, open-source pieces:
```bash
git clone https://github.com/warmbly/warmbly && cd warmbly
make up # or: docker compose up --build
```
-Dashboard on `:5173`, admin on `:5174`, API on `:8080`. The first build compiles
-the images once (a couple of minutes; they are CGO-free), then it is up. Load
-optional demo data with `make up` running via
-`docker compose --profile seed run --rm seed`.
-
-Before exposing it, set your own secrets in a `.env` next to `docker-compose.yml`.
-At minimum: `AUTH_SECRET`, `CREDENTIALS_ENCRYPTION_KEY`, `INTERNAL_API_TOKEN`,
-and `KMS_LOCAL_MASTER_KEY` (run `make gen-key`). The full list is in
-[`deploy/config/env.example`](deploy/config/env.example).
-
-**Reaching it from another machine.** Every service already binds to `0.0.0.0`
-(the Docker port mappings), so it listens on all interfaces. To make the app
-usable from a LAN IP or a domain instead of `localhost`, set one variable in
-`.env`, and everything (API URL, CORS, websocket, tracking, blob URLs) derives
-from it:
-
-```bash
-PUBLIC_HOST=192.168.1.50 # your machine's LAN IP, or your domain
-```
-
-Then open `http://192.168.1.50:5173`. For a public domain over HTTPS, put a
-reverse proxy (Caddy or nginx) in front terminating TLS, set `PUBLIC_HOST` to the
-domain, and build the frontends as static bundles rather than serving the Vite
-dev server.
-
Every external dependency is picked by an environment variable, so you swap in a
cloud service only if you want one:
@@ -207,67 +113,38 @@ cloud service only if you want one:
|----------------|----------------------------|------------------------------|
| Database | PostgreSQL 16 | RDS / Cloud SQL, any Postgres |
| Cache | Redis (or Valkey) | ElastiCache |
-| Event bus | **NATS JetStream** (~15 MB, one binary) | Kafka (`-tags kafka`) |
+| Event bus | **NATS JetStream** | Kafka (`-tags kafka`) |
| Blob storage | **Filesystem** | S3, MinIO, R2, B2 |
| KMS / root key | **Local AES master key** | AWS KMS |
-| Task scheduler | **In-process Postgres poller** | GCP Cloud Tasks |
-| Codec | JSON | Avro + Schema Registry |
-| Captcha | Off | Cloudflare Turnstile |
| Payments | **Off (everything unlocked)** | Stripe |
-NATS is the default because it is one small binary versus Kafka's
-JVM + Zookeeper + Schema Registry, and it keeps the image CGO-free so builds are
-fast. Kafka is fully supported: build the images with `--build-arg GO_TAGS=kafka`
-(and the tracking image with `CARGO_FEATURES=kafka`), set `EVENTBUS_PROVIDER=kafka`,
-and point `KAFKA_BOOTSTRAP_SERVERS` at your cluster.
-
-**Scaling is by mailboxes and workers, not IPs.** Outbound mail goes through each
-mailbox's own provider (Gmail API, Microsoft Graph, or the mailbox's SMTP relay),
-so the source IP is the provider's, never the worker's. Add throughput by
-connecting more mailboxes and running more workers: `docker compose up --scale
-worker=3`, or attach a machine you already own through the admin panel's SSH
-enrollment. Workers are interchangeable executors.
-
-**Connecting Gmail mailboxes** needs your own Google Cloud OAuth client: set
-`BOX_GOOGLE_CLIENT_ID` / `BOX_GOOGLE_CLIENT_SECRET` (a Web application client with
-the authorized redirect URI `/addresses/google/callback`) on the
-backend and every worker. Without it you can still connect any mailbox over
-SMTP/IMAP with an app password, and Microsoft 365 over its own OAuth client.
-
-Keep two secrets safe: `KMS_LOCAL_MASTER_KEY` (`make gen-key`) and
-`CREDENTIALS_ENCRYPTION_KEY` seal every stored mailbox credential, and losing them
-is unrecoverable. Full env reference and day-2 operations are in the
+Scaling is by mailboxes and workers, not IPs. Reaching it from another machine,
+connecting Gmail, and day-2 operations are all in the
[deployment guide](https://docs.warmbly.com/development/deployment-guide/).
-## Tech 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 |
-
## Documentation
-The full docs live at **[docs.warmbly.com](https://docs.warmbly.com)**: product
-guides, the API reference, and the engineering docs. Start here:
+The full docs live at **[docs.warmbly.com](https://docs.warmbly.com)**.
| Read this | To learn |
|-----------|----------|
| [Local development](https://docs.warmbly.com/development/local-development/) | Every make target, the native services, and how seeding works |
-| [Sandbox](https://docs.warmbly.com/development/sandbox/) | One command spins up a full demo org that sends, replies, opens, clicks, and warms itself |
-| [Architecture](https://docs.warmbly.com/development/architecture/) | How the control plane and the workers split the job, plus the encryption model |
+| [Architecture](https://docs.warmbly.com/development/architecture/) | How the control plane and workers split the job, plus the encryption model |
| [Deployment guide](https://docs.warmbly.com/development/deployment-guide/) | Taking it to production and scaling the worker fleet |
-| [Event system](https://docs.warmbly.com/development/events/) | The event bus and every topic that flows across it |
| [API reference](https://docs.warmbly.com/api/) | Endpoints, auth, permissions, and webhooks |
+## Community
+
+Have a question, found a bug, or want to shape where Warmbly goes next?
+
+- **[Discord](https://dc.warmbly.com)** - chat with the team and other senders
+- **[GitHub Issues](https://github.com/warmbly/warmbly/issues)** - report bugs and request features
+- **Email** - reach us at `team@warmbly.com`
+
+## Star the repository ⭐
+
+
+
## Contributing
Pull requests are welcome. Keep each one to a single logical change, and open an
diff --git a/docs/assets/README.md b/docs/assets/README.md
index 2909bcc2..351f0ebf 100644
--- a/docs/assets/README.md
+++ b/docs/assets/README.md
@@ -5,10 +5,11 @@ loads quickly on GitHub.
| File | Where it shows up |
|-----------------------------|--------------------------------------|
-| `banner.jpg` | Hero banner at the top of the README |
-| `dashboard-campaigns.png` | Gallery: campaigns list |
-| `dashboard-inbox.png` | Gallery: unified inbox |
-| `dashboard-mailboxes.png` | Gallery: mailboxes and warmup state |
+| `logo-dark.svg` | Logo + wordmark, white, for dark GitHub theme |
+| `logo-light.svg` | Logo + wordmark, dark ink, for light GitHub theme |
+| `dashboard-campaigns.png` | Features: campaigns list |
+| `dashboard-inbox.png` | Features: unified inbox |
+| `dashboard-mailboxes.png` | Spare product screenshot (mailboxes) |
The dashboard images are exports of the product design. When real, anonymized
screenshots of the running app are available, swap them in at the same paths and
diff --git a/docs/assets/banner.jpg b/docs/assets/banner.jpg
deleted file mode 100644
index 91a3d165..00000000
Binary files a/docs/assets/banner.jpg and /dev/null differ
diff --git a/docs/assets/logo-dark.svg b/docs/assets/logo-dark.svg
new file mode 100644
index 00000000..4f65a9ed
--- /dev/null
+++ b/docs/assets/logo-dark.svg
@@ -0,0 +1,8 @@
+
diff --git a/docs/assets/logo-light.svg b/docs/assets/logo-light.svg
new file mode 100644
index 00000000..a354cd0a
--- /dev/null
+++ b/docs/assets/logo-light.svg
@@ -0,0 +1,8 @@
+