diff --git a/README.md b/README.md index c4ec3901..d1ccec05 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,85 @@ -

- Warmbly -

+
+ + + + Warmbly + + +

The open-source agentic cold email and warmup platform.

+ +

+ Discord + Docs + CI status + Latest release + License +

+ +

+ Features · + How it works · + Quick start · + Self-hosting · + Docs · + Community +

+ +

⭐ 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. + Campaigns + Unified inbox

-

- CI status - Latest release - Go version - License - Documentation -

+## How it works -

- Mailboxes · - Integrations · - Quick start · - Warmup · - How it works · - Self-hosting · - Docs · - Contributing -

+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
- 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
- Unified inbox · every connected mailbox and reply in one place -

- -

- Mailboxes
- 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 ⭐ + +warmbly-star + ## 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 @@ + + + + + + + +