From 4eedbbfb1e654a5f5d5df7961510beb54e8d93cd Mon Sep 17 00:00:00 2001 From: Jonny Nguyen Date: Wed, 24 Jun 2026 22:40:25 -0700 Subject: [PATCH] Add initial documentation for Warmbox, including product brief, current state analysis, glossary, domain model, warmup recipes, primary-satellite topology, scheduling, reply rate engine, spam rescue, AI content policy, statistics, ESP compliance, system architecture, API evolution, and frontend UX specification. --- docs/00-product-brief.md | 84 ++++++++++++ docs/01-current-state-and-gaps.md | 79 +++++++++++ docs/02-glossary.md | 20 +++ docs/03-domain-model.md | 123 +++++++++++++++++ docs/04-warmup-recipes.md | 126 ++++++++++++++++++ docs/05-primary-satellite-topology.md | 120 +++++++++++++++++ docs/06-scheduling-and-timing.md | 91 +++++++++++++ docs/07-reply-rate-engine.md | 94 +++++++++++++ docs/08-spam-rescue-and-placement.md | 129 ++++++++++++++++++ docs/09-ai-content-policy.md | 111 ++++++++++++++++ docs/10-statistics-and-reporting.md | 115 ++++++++++++++++ docs/11-esp-compliance-playbook.md | 143 ++++++++++++++++++++ docs/12-architecture.md | 158 ++++++++++++++++++++++ docs/13-api-evolution.md | 122 +++++++++++++++++ docs/14-frontend-ux-spec.md | 144 ++++++++++++++++++++ docs/15-security-and-compliance.md | 73 +++++++++++ docs/16-roadmap.md | 172 ++++++++++++++++++++++++ docs/17-agile-epics-backlog.md | 181 ++++++++++++++++++++++++++ docs/18-open-questions.md | 61 +++++++++ docs/19-risk-register.md | 55 ++++++++ docs/README.md | 52 ++++++++ 21 files changed, 2253 insertions(+) create mode 100644 docs/00-product-brief.md create mode 100644 docs/01-current-state-and-gaps.md create mode 100644 docs/02-glossary.md create mode 100644 docs/03-domain-model.md create mode 100644 docs/04-warmup-recipes.md create mode 100644 docs/05-primary-satellite-topology.md create mode 100644 docs/06-scheduling-and-timing.md create mode 100644 docs/07-reply-rate-engine.md create mode 100644 docs/08-spam-rescue-and-placement.md create mode 100644 docs/09-ai-content-policy.md create mode 100644 docs/10-statistics-and-reporting.md create mode 100644 docs/11-esp-compliance-playbook.md create mode 100644 docs/12-architecture.md create mode 100644 docs/13-api-evolution.md create mode 100644 docs/14-frontend-ux-spec.md create mode 100644 docs/15-security-and-compliance.md create mode 100644 docs/16-roadmap.md create mode 100644 docs/17-agile-epics-backlog.md create mode 100644 docs/18-open-questions.md create mode 100644 docs/19-risk-register.md create mode 100644 docs/README.md diff --git a/docs/00-product-brief.md b/docs/00-product-brief.md new file mode 100644 index 0000000..2bcee55 --- /dev/null +++ b/docs/00-product-brief.md @@ -0,0 +1,84 @@ +# Product Brief — Warmbox + +## One-liner + +Warmbox is a self-hosted (today) / SaaS (tomorrow) cold email warmup engine that safely builds sender reputation for **primary outbound mailboxes** using a controlled network of **satellite inboxes**, realistic AI-generated conversations, and human-like engagement signals — without triggering ESP anti-abuse systems. + +## Problem + +New or cold mailboxes used for B2B outreach land in spam. ESPs (Google, Microsoft, Yahoo) evaluate: + +- Sending volume patterns (ramps, spikes, consistency) +- Authentication (SPF, DKIM, DMARC) +- Engagement (replies, not-spam actions, spam complaints) +- Content signals (spam triggers, templated/robotic text) + +Commercial warmup tools exist but carry risks: toxic peer pools, detectable patterns, placeholder-filled AI content, and ESP policies against artificial warmup networks. + +## Solution + +Warmbox gives operators **full control** over: + +- Which mailboxes are warming (primaries) vs supporting (satellites) +- Warmup **recipe** (progressive ramp, flat, randomized, custom) +- Volume bounds (1–50/day), duration (30–45 days), reply rate (30–45%) +- Spam rescue and placement visibility per primary +- AI content quality guardrails (no placeholders, no spam triggers) + +## Target users (personas) + +### P1 — Cold email operator (FillCare / agency owner) + +- Warms `hello@fillcareapp.com` and future sales domains +- Owns 4–10 personal Gmail/Yahoo/Live satellites +- Needs confidence before connecting Instantly/Smartlead +- Wants dashboard: sent, replied, spam rate, ramp progress + +### P2 — Deliverability-focused growth lead + +- Manages multiple primaries across brands +- Needs recipes, custom schedules, alerts on spam spikes +- Cares about Google Postmaster / Microsoft SNDS integration (future) + +### P3 — SaaS customer (future) + +- Signs up, connects mailboxes via OAuth or app passwords +- Pays per primary mailbox warmed +- Never sees other customers' satellites (tenant isolation) + +## Goals + +| Goal | Measure | +|------|---------| +| Inbox placement | ≥95% warmup emails in inbox (not spam) after week 3 | +| Spam rescue | 100% of detected warmup-in-spam rescued within rescuer cycle | +| Reply realism | Reply rate within configured ±5% of target | +| Content quality | 0% emails with bracket placeholders; <1% AI validation failures | +| ESP safety | No account suspensions attributable to Warmbox patterns | +| Operator trust | Per-primary daily stats accurate within 1 message | + +## Non-goals (MVP SaaS phase) + +- Sending live cold campaigns (delegate to Instantly, Smartlead, Apollo) +- Shared public peer network at launch (optional later) +- Mobile native apps +- White-label reseller portal (future) + +## Competitive positioning + +| Capability | Warmbox | Typical warmup SaaS | +|------------|---------|---------------------| +| Self-hosted option | Yes | Rare | +| Own satellite pool | Yes | Often forced shared pool | +| Recipe control | Full (4 types) | Often progressive only | +| Open source core | Possible | No | +| ESP-aware behavior | Designed-in | Varies | + +## Success criteria for "production ready" + +1. Multiple primaries warming concurrently with independent recipes +2. Satellites only engage assigned primaries (no random cross-primary noise) +3. Statistics dashboard matches raw logs +4. Auto-pause on auth errors, bounce spikes, or spam-rate thresholds +5. Web UI for all operator workflows +6. Documented ESP compliance checklist passed before go-live diff --git a/docs/01-current-state-and-gaps.md b/docs/01-current-state-and-gaps.md new file mode 100644 index 0000000..8f60883 --- /dev/null +++ b/docs/01-current-state-and-gaps.md @@ -0,0 +1,79 @@ +# Current State & Gap Analysis + +## What exists today (MVP) + +### Data layer + +| Model | Purpose | +|-------|---------| +| `Inbox` | Flat mailbox record (no role) | +| `WarmupLog` | Message lifecycle: sent → rescued_from_spam → replied → complete | +| `Setting` | Global key-value config | + +### Jobs + +| Job | Behavior | +|-----|----------| +| Dispatcher | Random sender/receiver from all active inboxes; AI email; SMTP send | +| Rescuer | Spam folder scan; move to INBOX; flag; AI reply (~100% reply when found) | +| Ramp bump | Midnight `currentRamp += 2` up to `dailyLimit` | + +### API + +- Inbox CRUD + connection test +- Paginated logs +- Global settings (cron, industries) + +### Strengths + +- Working SMTP/IMAP connector with encrypted credentials +- Spam rescue on Gmail/Yahoo/Outlook folder names +- AI via OpenRouter (free models) +- Error inbox marking (`status: error`) +- Prisma 7 + SQLite portability + +## Gap matrix + +| Feature | Current | Target | Priority | +|---------|---------|--------|----------| +| Primary vs satellite roles | All equal | Role-based pairing | P0 | +| Multiple primaries | Implicit | Explicit campaigns per primary | P0 | +| Grow recipe | Partial ramp | Named recipe engine | P0 | +| Flat recipe | Missing | Constant daily volume | P0 | +| Randomized recipe | Missing | Daily variance algorithm | P1 | +| Custom recipe | Missing | Per-day schedule editor | P1 | +| Warmup duration 30–45d | Missing | Campaign start/end | P0 | +| Min emails/day (1) | Missing | Enforced floor | P0 | +| Max emails/day (40–50) | Partial | Validated cap | P0 | +| Reply rate 30% | ~100% | Probabilistic sampling | P0 | +| Per-primary statistics | Missing | Dashboard API | P0 | +| Spam count per primary | Partial (per log) | Aggregated metrics | P0 | +| AI placeholder blocking | Prompt only | Post-generation validation | P0 | +| Send time jitter | Missing | Human-like windows | P1 | +| Web UI | Missing | Full operator console | P1 | +| Multi-tenant | Missing | Org/workspace model | P2 | +| Peer network | Missing | Optional phase 3 | P3 | +| DNS/auth checks | Missing | SPF/DKIM/DMARC validation | P1 | +| Auto-pause on bad signals | Partial (auth only) | Spam/bounce thresholds | P1 | +| Tests | Missing | Unit + integration | P1 | + +## Architectural debt + +1. **Flat inbox pool** — Dispatcher treats every inbox as equal; primaries can email each other. +2. **Global ramp** — Not tied to campaign start date or recipe. +3. **SQLite** — Fine for self-hosted; need Postgres path for SaaS. +4. **No schedule entity** — "Planned sends today" is implicit, not queryable. +5. **Spam rescue granularity** — Marks all pending logs from sender, not per UID. +6. **No idempotency** — Cron overlap could double-send without locks. + +## Recommended migration path + +``` +Phase A: Schema + roles + recipes (backend only) +Phase B: Statistics API + reply rate engine +Phase C: Web UI (self-hosted) +Phase D: Hardening (jitter, validation, auto-pause) +Phase E: Multi-tenant + optional peer network +``` + +See [16-roadmap.md](./16-roadmap.md) for sprint breakdown. diff --git a/docs/02-glossary.md b/docs/02-glossary.md new file mode 100644 index 0000000..2ac22fb --- /dev/null +++ b/docs/02-glossary.md @@ -0,0 +1,20 @@ +# Glossary + +| Term | Definition | +|------|------------| +| **Primary mailbox** | The cold outbound address being warmed (e.g. `hello@fillcareapp.com`). Receives engagement signals; volume ramps per recipe. | +| **Satellite mailbox** | Supporting inbox (Gmail, Yahoo, Live) that sends to and replies from primaries. Not the main outreach address. | +| **Warmup campaign** | Configuration binding one primary to a recipe, duration, volume bounds, reply rate, and assigned satellites. | +| **Recipe** | Algorithm that determines how many emails to send per day over the campaign timeline. | +| **Ramp** | Progressive increase in daily send volume (Grow recipe). | +| **Reply rate** | Percentage of received warmup emails that get an AI-generated reply (not 100%). | +| **Spam rescue** | IMAP action: find warmup email in Spam/Junk, move to INBOX, mark read/flagged/important. | +| **Placement** | Where a message landed: inbox, spam, or unknown. | +| **ESP** | Email Service Provider (Google, Microsoft, Yahoo). | +| **Engagement signal** | Reply, not-spam move, flag, open (future) — positive reputation input. | +| **Peer network** | Shared pool of third-party mailboxes (optional future feature). | +| **Send window** | Time-of-day range when sends are allowed (e.g. 08:00–18:00 local). | +| **Jitter** | Random delay between sends to avoid machine-like cadence. | +| **Warmup log** | Single message transaction with lifecycle status. | +| **Daily stat** | Aggregated per-primary metrics for a calendar day. | +| **Tenant** | Future SaaS customer organization with isolated data. | diff --git a/docs/03-domain-model.md b/docs/03-domain-model.md new file mode 100644 index 0000000..e34ec55 --- /dev/null +++ b/docs/03-domain-model.md @@ -0,0 +1,123 @@ +# Domain Model (Target) + +Evolution from flat `Inbox` to campaign-centric model. Implement incrementally; not all entities ship in phase 1. + +## Entity relationship (target) + +```mermaid +erDiagram + Workspace ||--o{ Mailbox : owns + Workspace ||--o{ WarmupCampaign : has + Mailbox ||--o{ WarmupCampaign : "primary" + WarmupCampaign ||--o{ CampaignSatellite : assigns + Mailbox ||--o{ CampaignSatellite : "satellite" + WarmupCampaign ||--o{ DailySchedule : plans + WarmupCampaign ||--o{ DailyStat : tracks + WarmupCampaign ||--o{ WarmupLog : generates + Mailbox ||--o{ WarmupLog : sends + Mailbox ||--o{ WarmupLog : receives +``` + +## Mailbox (evolved Inbox) + +| Field | Type | Notes | +|-------|------|-------| +| id | cuid | | +| workspaceId | cuid | Future multi-tenant; nullable for self-hosted | +| email | string | Unique per workspace | +| role | enum | `primary` \| `satellite` \| `unassigned` | +| smtpHost, smtpPort, imapHost, imapPort | | Connection | +| username, password | | Encrypted | +| provider | enum | `gmail` \| `yahoo` \| `outlook` \| `custom` | +| status | enum | `active` \| `paused` \| `error` \| `warming` \| `completed` | +| industry | string | AI context | +| dnsStatus | json | SPF/DKIM/DMARC check results (future) | +| lastError, errorAt | | | + +**Rule:** A mailbox can be primary in one campaign and satellite in another only if explicitly allowed (default: one role per campaign assignment). + +## WarmupCampaign + +One campaign = one primary mailbox warming with one recipe. + +| Field | Type | Notes | +|-------|------|-------| +| id | cuid | | +| workspaceId | cuid | | +| primaryMailboxId | cuid | FK Mailbox | +| name | string | e.g. "FillCare hello@" | +| recipe | enum | `grow` \| `flat` \| `randomized` \| `custom` | +| status | enum | `draft` \| `active` \| `paused` \| `completed` \| `failed` | +| durationDays | int | Default 30, max 45 | +| startedAt | datetime | | +| endsAt | datetime | Computed | +| minEmailsPerDay | int | Default 1 | +| maxEmailsPerDay | int | Default 40, max 50 | +| replyRatePercent | int | Default 30, max 45 | +| customSchedule | json | For `custom` recipe: `[{ day, sends }]` | +| timezone | string | Send window TZ | +| sendWindowStart | time | e.g. 08:00 | +| sendWindowEnd | time | e.g. 18:00 | +| currentDay | int | Day index since start | +| rampState | json | Recipe-specific state | + +## CampaignSatellite (join) + +| Field | Type | +|-------|------| +| campaignId | cuid | +| satelliteMailboxId | cuid | +| weight | int | Optional load balancing (default 1) | +| active | boolean | + +## DailySchedule + +Pre-computed plan for a campaign day (generated at campaign start or nightly). + +| Field | Type | +|-------|------| +| campaignId | cuid | +| date | date | +| dayIndex | int | +| plannedSends | int | +| plannedReplies | int | +| actualSends | int | +| actualReplies | int | +| spamDetected | int | +| spamRescued | int | + +## WarmupLog (evolved) + +Add `campaignId`, `placement` (`inbox` \| `spam` \| `unknown`), `scheduledAt`, `sentAt`, `repliedAt`. + +## DailyStat (materialized) + +Rollup for fast dashboard queries. + +| Field | Type | +|-------|------| +| campaignId | cuid | +| date | date | +| sent, received, replied, spamDetected, spamRescued | int | +| replyRateActual | float | +| inboxPlacementRate | float | + +## Setting (global / workspace) + +Move cron and AI config to workspace scope over time. + +## Migration from current schema + +| Current | Target | +|---------|--------| +| `Inbox` | `Mailbox` + `role` | +| Per-inbox `dailyLimit`, `currentRamp` | `WarmupCampaign` fields | +| Flat dispatcher | Campaign-aware dispatcher | +| `WarmupLog` | Add `campaignId` | + +### Phase 1 migration (minimal) + +1. Add `role` to `Inbox` (`primary` \| `satellite`) +2. Add `WarmupCampaign` table +3. Add `campaignId` to `WarmupLog` +4. Keep backward compat: inboxes without campaign use legacy flat mode (deprecated) diff --git a/docs/04-warmup-recipes.md b/docs/04-warmup-recipes.md new file mode 100644 index 0000000..0db8f16 --- /dev/null +++ b/docs/04-warmup-recipes.md @@ -0,0 +1,126 @@ +# Warmup Recipes + +Four recipes control **how many emails the primary receives/sends per day** over the campaign duration. + +## Global constraints (all recipes) + +| Parameter | Default | Min | Max | +|-----------|---------|-----|-----| +| Duration | 30 days | 14 | 45 | +| Min emails/day | 1 | 1 | — | +| Max emails/day | 40 | — | 50 | +| Reply rate | 30% | 10% | 45% | +| Volume change cap | +20% / 48h | — | Industry best practice | + +## 1. Grow — Progressive (recommended) + +**Use when:** New domain or cold mailbox. Safest default. + +### Behavior + +- Start at `minEmailsPerDay` (default 1) on day 1 +- Increase daily toward `maxEmailsPerDay` over `durationDays` +- Never increase more than 20% vs prior 48h average +- After reaching max, hold flat until campaign end +- Optionally continue background warmup post-campaign at 20% of max + +### Algorithm (pseudocode) + +``` +totalGrowthDays = durationDays * 0.7 // reach max by ~70% of campaign +dailyIncrement = (maxEmails - minEmails) / totalGrowthDays + +for day in 1..durationDays: + target = minEmails + floor(dailyIncrement * min(day, totalGrowthDays)) + target = min(target, maxEmails) + target = cap48hIncrease(previousTargets, target, 0.20) + schedule[day] = target +``` + +### Example (30 days, min=1, max=40) + +| Day | Planned sends | +|-----|---------------| +| 1 | 1 | +| 7 | ~8 | +| 14 | ~18 | +| 21 | ~30 | +| 24+ | 40 | + +## 2. Flat + +**Use when:** Mailbox already warm; maintain reputation during live outreach. + +### Behavior + +- Constant `maxEmailsPerDay` every day +- No ramp +- Same reply rate and rescue rules + +``` +schedule[day] = maxEmailsPerDay // for all days +``` + +## 3. Randomized + +**Use when:** Experimentation; avoid perfectly predictable patterns. + +### Behavior + +- Daily volume random within `[minEmailsPerDay, maxEmailsPerDay]` +- Weighted toward middle of range (normal distribution) +- 48h spike cap still applies +- Seed per campaign for reproducibility (debugging) + +``` +schedule[day] = randomInt(min, max, seed=campaignId+day) +schedule[day] = cap48hIncrease(...) +``` + +## 4. Custom + +**Use when:** Expert operators want per-day control. + +### Behavior + +- User defines array: `[{ day: 1, sends: 2 }, { day: 2, sends: 5 }, ...]` +- Validate: each value within [min, max], length ≤ durationDays +- Missing days: hold last defined value or interpolate (configurable) +- UI: spreadsheet-style editor + import CSV + +## Recipe selection API (future) + +```json +{ + "recipe": "grow", + "durationDays": 30, + "minEmailsPerDay": 1, + "maxEmailsPerDay": 40, + "replyRatePercent": 30 +} +``` + +## Dispatcher integration + +1. Load active campaigns +2. For each campaign, read `DailySchedule` for today +3. If `actualSends < plannedSends`, schedule next send slot with jitter +4. Pick satellite from `CampaignSatellite` (weighted random) +5. Primary is always receiver for satellite→primary sends; primary may send replies back + +## Post-campaign + +| Mode | Behavior | +|------|----------| +| Complete | Stop sends; mark campaign `completed` | +| Maintenance | Switch to Flat at 20% of max (recommended) | +| Pause | Operator pause; resume later | + +## UI copy (recommended labels) + +| Recipe | Label | Subtitle | +|--------|-------|----------| +| grow | Grow | Progressive ramp — recommended for new mailboxes | +| flat | Flat | Steady volume — for already-warm inboxes | +| randomized | Randomized | Variable daily volume — for testing | +| custom | Custom | Define your own day-by-day schedule | diff --git a/docs/05-primary-satellite-topology.md b/docs/05-primary-satellite-topology.md new file mode 100644 index 0000000..040b074 --- /dev/null +++ b/docs/05-primary-satellite-topology.md @@ -0,0 +1,120 @@ +# Primary & Satellite Topology + +## Core principle + +**Primaries** are the mailboxes whose reputation matters for cold outreach. +**Satellites** exist only to generate realistic, controlled engagement with primaries. + +``` + ┌─────────────────┐ + │ Primary │ + │ hello@fillcare │ + └────────┬────────┘ + │ + ┌───────────────────┼───────────────────┐ + │ │ │ + ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ + │Satellite│ │Satellite│ │Satellite│ + │ Gmail 1 │ │ Yahoo │ │ Live │ + └─────────┘ └─────────┘ └─────────┘ +``` + +## Traffic rules + +| Direction | Allowed | Notes | +|-----------|---------|-------| +| Satellite → Primary | Yes | Main warmup send | +| Primary → Satellite | Yes | Reply to satellite (completes thread) | +| Satellite → Satellite | No | Wastes quota; looks unnatural | +| Primary → Primary | No | Never warm primaries against each other | +| Satellite → unrelated external | No | Only campaign-assigned primaries | + +## Multi-primary support + +One operator can run **N campaigns** concurrently: + +``` +Campaign A: primary=hello@fillcareapp.com ← satellites [gmail1, yahoo1, live1] +Campaign B: primary=sales@otherdomain.com ← satellites [gmail2, yahoo2] +``` + +Satellites may be **shared** across campaigns only if explicitly enabled (default: dedicated per primary to avoid cross-brand leakage). + +## FillCare example setup + +| Mailbox | Role | Provider | +|---------|------|----------| +| hello@fillcareapp.com | Primary | Google Workspace | +| personal@gmail.com | Satellite | Gmail | +| personal@yahoo.com | Satellite | Yahoo | +| personal@live.com | Satellite | Outlook | +| (+ 1–2 more) | Satellite | Mixed | + +### Minimum viable pool + +- 1 primary + **2 satellites** (technical minimum) +- Recommended: 1 primary + **4–6 satellites** for natural variety and reply distribution + +## Satellite lifecycle + +```mermaid +stateDiagram-v2 + [*] --> Unassigned: Add mailbox + Unassigned --> Active: Assign to campaign + Active --> Paused: Operator pause + Paused --> Active: Resume + Active --> Error: Auth/IMAP failure + Error --> Active: Fix + test + Active --> Unassigned: Remove from campaign +``` + +## Add / remove operations + +### Add satellite + +1. `POST /mailboxes` with `role: satellite` +2. Test SMTP/IMAP +3. `POST /campaigns/:id/satellites` to assign + +### Remove satellite + +1. `DELETE /campaigns/:id/satellites/:mailboxId` — stops new sends +2. In-flight threads complete or timeout +3. Optional: `DELETE /mailboxes/:id` if fully removed + +### Add primary + +1. `POST /mailboxes` with `role: primary` +2. `POST /campaigns` with recipe + bounds +3. Assign satellites +4. Start campaign (`status: active`) + +## Peer network (phase 3 — optional) + +When enabled, additional engagement from vetted peer mailboxes: + +| Aspect | Own satellites | Peer network | +|--------|----------------|--------------| +| Control | Full | Limited | +| ESP risk | Lower | Higher (pool quality) | +| Cost | User supplies accounts | Platform fee | +| Detection | User responsible | Platform must maintain pool health | + +**Recommendation:** Ship own-satellite-only first. Peer network requires bounce monitoring, DNS validation, and pool rotation — significant ops burden. + +## Dispatcher pairing (target logic) + +``` +for each campaign where status=active: + if today.sends < schedule.plannedSends: + satellite = pickWeighted(campaign.satellites) + primary = campaign.primaryMailbox + queueSend(from=satellite, to=primary) +``` + +Reply path (rescuer): + +``` +if shouldReply(campaign.replyRatePercent): + reply(from=primary, to=satellite, inReplyTo=original) +``` diff --git a/docs/06-scheduling-and-timing.md b/docs/06-scheduling-and-timing.md new file mode 100644 index 0000000..d702635 --- /dev/null +++ b/docs/06-scheduling-and-timing.md @@ -0,0 +1,91 @@ +# Scheduling & Timing + +## Campaign duration + +| Setting | Value | +|---------|-------| +| Recommended | 30–45 days | +| Minimum | 14 days (not recommended for new domains) | +| Maximum | 45 days (MVP cap) | + +`endsAt = startedAt + durationDays` + +## Daily send distribution + +### Send windows + +Avoid off-hours sends that signal automation. + +| Parameter | Default | +|-----------|---------| +| Timezone | Campaign or mailbox TZ | +| Window start | 08:00 | +| Window end | 18:00 | +| Weekends | Optional: reduce volume 50% or pause | + +### Jitter between sends + +| Parameter | Default | Rationale | +|-----------|---------|-----------| +| Min gap | 45 minutes | Avoid burst patterns | +| Max gap | 180 minutes | Stay within daily quota | +| Distribution | Uniform random | Human-like variance | + +``` +nextSendAt = lastSendAt + random(45min, 180min) +if nextSendAt outside window: roll to next window start +``` + +## 48-hour volume cap + +Industry guidance: never increase average daily volume more than **20% in 48 hours**. + +```typescript +function cap48hIncrease(history: number[], proposed: number): number { + const avg48h = average(last48h(history)); + return Math.min(proposed, Math.ceil(avg48h * 1.20)); +} +``` + +Applies to all recipes except explicit expert override in Custom (with warning). + +## Cron vs queue architecture + +### Current (MVP) + +- `node-cron` fires dispatcher every 15 min +- All eligible sends in one batch + +### Target + +| Component | Role | +|-----------|------| +| Scheduler cron | Every 5 min: enqueue due sends | +| Send queue | BullMQ / pg-boss / SQLite queue | +| Worker | One send per job with jitter | +| Rescuer cron | Every 10 min: IMAP scan per mailbox | + +Benefits: precise timing, backoff, idempotency keys, pause per campaign. + +## Campaign day rollover + +At midnight (campaign TZ): + +1. Increment `currentDay` +2. Load `DailySchedule` for new day +3. Reset `actualSends`, `actualReplies` counters +4. If `currentDay > durationDays` → complete or maintenance mode + +## Maintenance mode (post-campaign) + +After primary campaign completes: + +- Continue Flat recipe at **20% of maxEmailsPerDay** +- Keeps engagement signals during live cold outreach +- Operator can disable + +## Timezone handling + +- Store all timestamps UTC in DB +- Display in campaign timezone in UI +- Send windows evaluated in campaign timezone diff --git a/docs/07-reply-rate-engine.md b/docs/07-reply-rate-engine.md new file mode 100644 index 0000000..29921d5 --- /dev/null +++ b/docs/07-reply-rate-engine.md @@ -0,0 +1,94 @@ +# Reply Rate Engine + +## Problem + +Current MVP replies to **every** matched warmup email (~100%). Real humans and reputable warmup tools reply to a **fraction** of mail (typically 30%, max recommended 45%). + +## Configuration + +| Parameter | Default | Min | Max recommended | +|-----------|---------|-----|-----------------| +| `replyRatePercent` | 30 | 10 | 45 | + +Set per `WarmupCampaign`. Hard cap at 45% in validation. + +## Semantics + +**Reply rate** = of warmup emails received by the primary (or sent to primary from satellites), the percentage that receive an AI-generated reply that day. + +Not the same as cold-email response rate — this is warmup engagement simulation only. + +## Algorithm + +### On rescuer cycle (per received warmup message) + +``` +function shouldReply(campaign, message, statsToday): + target = campaign.replyRatePercent + actual = statsToday.replies / max(statsToday.received, 1) * 100 + + if actual >= target: + return false // quota met for today + + // Probabilistic: bias toward hitting target by end of day + remaining = statsToday.received - statsToday.replies + needed = ceil(statsToday.received * target/100) - statsToday.replies + probability = needed / max(remaining, 1) + + return random() < probability +``` + +### Properties + +- Deterministic enough to converge on daily target +- Stochastic enough to avoid fixed patterns +- Never exceeds 45% even if configured wrong (clamp) + +## What still happens at 0% reply (for a given message) + +Even when not replying: + +1. Spam rescue still runs (move to inbox, flag) +2. Mark as read +3. Log status may be `complete` without `replied` — new status: `engaged_no_reply` + +### Extended WarmupStatus enum + +| Status | Meaning | +|--------|---------| +| sent | Delivered from satellite | +| delivered | Confirmed in primary inbox (optional) | +| rescued_from_spam | Moved from spam folder | +| engaged_no_reply | Read/flagged, no reply sent | +| replied | Reply sent | +| complete | Thread closed | + +## Reply direction + +| Step | From | To | +|------|------|-----| +| 1. Warmup send | Satellite | Primary | +| 2. Reply (if sampled) | Primary | Satellite | + +Maintains realistic thread: primary appears engaged with incoming mail. + +## Daily reply budget + +``` +plannedReplies = ceil(plannedSends * replyRatePercent / 100) +``` + +Shown on dashboard: `replies today: 8/12 planned` + +## Edge cases + +| Case | Handling | +|------|----------| +| All messages arrive at 23:59 | Spread replies across rescuer cycles | +| Reply SMTP fails | Retry once; log error; don't count toward rate | +| Primary in error | Pause replies; satellites may still send | +| Satellite in error | Exclude from pool; rebalance | + +## ESP rationale + +100% reply rate on automated mail is a detection signal. Variable reply rate + rescue without reply on some messages mimics real mailbox behavior more closely. diff --git a/docs/08-spam-rescue-and-placement.md b/docs/08-spam-rescue-and-placement.md new file mode 100644 index 0000000..7de3ce2 --- /dev/null +++ b/docs/08-spam-rescue-and-placement.md @@ -0,0 +1,129 @@ +# Spam Rescue & Placement Tracking + +## Objectives + +1. Detect when warmup emails land in spam/junk +2. Rescue them (move to inbox, positive signals) +3. Track spam rate per primary for dashboard and auto-pause +4. Never leave warmup mail unhandled in spam folders + +## Folder discovery + +### Default folder list (try in order) + +| Provider | Folders | +|----------|---------| +| Gmail | `[Gmail]/Spam` | +| Outlook | `Junk` | +| Yahoo | `Bulk` or `Spam` | +| Generic | `Spam`, `Junk` | + +### Target enhancement + +1. `LIST` IMAP folders on connect; match known spam patterns +2. Store per-mailbox `spamFolderPath` after first discovery +3. Provider enum shortcuts connection presets + +## Rescue workflow + +```mermaid +sequenceDiagram + participant R as Rescuer + participant IMAP as Primary IMAP + participant DB as Database + + R->>IMAP: Open spam folder + R->>IMAP: SEARCH unseen FROM satellite + IMAP-->>R: UIDs + loop Each UID + R->>IMAP: MOVE to INBOX + R->>DB: Log placement=spam, status=rescued + R->>IMAP: FLAGS Seen, Flagged + R->>IMAP: FLAGS Important (best effort) + R->>R: shouldReply? + alt Reply sampled + R->>SMTP: Send reply + else No reply + R->>DB: status=engaged_no_reply + end + end +``` + +## Per-message matching (fix MVP weakness) + +Current MVP marks all pending logs from sender when any spam found. Target: + +| Priority | Match key | +|----------|-----------| +| 1 | `Message-ID` header exact match | +| 2 | `In-Reply-To` chain | +| 3 | From + normalized subject + 24h window | + +One UID → one WarmupLog row. + +## Placement tracking + +Add to `WarmupLog` or separate `PlacementEvent`: + +| Field | Values | +|-------|--------| +| placement | `inbox`, `spam`, `unknown` | +| detectedAt | timestamp | +| rescuedAt | timestamp nullable | +| folder | string | + +### Inbox placement rate + +``` +inboxRate = 1 - (spamDetected / totalReceived) +``` + +Target: ≥95% inbox after week 3. + +## INBOX processing (non-spam path) + +Scan primary INBOX for unread warmup from assigned satellites: + +- Same matching rules +- If already in inbox: flag + optional reply (no move) +- Log `placement=inbox` + +## Auto-pause triggers + +| Condition | Action | +|-----------|--------| +| Spam rate >30% for 3 consecutive days | Pause campaign; alert operator | +| Spam rate >50% in 24h | Immediate pause | +| Rescue failures >5 in row | Mark primary `error` | +| Auth failure | Mark `error`; stop all jobs | + +## Provider-specific notes + +### Gmail / Google Workspace + +- `\Important` via labels (`useLabels: true`) +- Postmaster Tools integration (future): pull domain reputation +- App passwords required; OAuth preferred for SaaS + +### Microsoft 365 / Outlook + +- Junk Email folder naming varies +- Enable IMAP in admin settings +- SNDS integration (future) + +### Yahoo + +- App password required +- Bulk folder common + +## Compliance considerations + +Rescue actions simulate **user marking not spam** — legitimate when acting on mail the user owns. Warmbox must only access mailboxes the operator controls. + +**Do not** rescue mail the operator does not own (peer network phase requires explicit consent). + +## Future: seed placement tests + +Optional inbox placement probes (like GlockApps) — out of MVP scope but document hook: + +`POST /campaigns/:id/placement-test` → send test to probe addresses → report inbox/spam diff --git a/docs/09-ai-content-policy.md b/docs/09-ai-content-policy.md new file mode 100644 index 0000000..43efc0a --- /dev/null +++ b/docs/09-ai-content-policy.md @@ -0,0 +1,111 @@ +# AI Content Policy + +## Requirements + +All warmup email subjects and bodies must be: + +- Professional B2B tone +- Contextually appropriate to `industry` (Childcare, SaaS, Finance, etc.) +- Free of spam trigger words (see blocklist) +- **Free of placeholders** — no `[Name]`, `[Your company]`, `{{variable}}`, etc. +- Plain text only (MVP) — no HTML, images, tracking pixels, or links in warmup mail +- Unique enough across sends — no identical consecutive bodies + +## Forbidden patterns (auto-reject) + +Regex / validation pass after generation: + +``` +\[.*?\] # bracket placeholders +\{\{.*?\}\} # mustache +<.*?@.*?> # angle email placeholders in body +\[Your .*?\] +\[Director .*?\] +\[Company .*?\] +INSERT .* HERE +TODO +lorem ipsum +click here +act now +limited time +free money +guarantee +``` + +## Spam trigger word tiers + +### Tier 1 — hard block (regenerate) + +`free`, `winner`, `cash`, `viagra`, `casino`, `lottery`, `act now`, `100% free`, `risk-free` + +### Tier 2 — soft warning (regenerate once, then flag for review) + +`urgent`, `limited`, `offer`, `discount`, `sale`, `promotion`, `buy now` + +## Generation pipeline + +```mermaid +flowchart LR + A[AI Generate] --> B[JSON Parse] + B --> C[Placeholder scan] + C --> D[Spam word scan] + D --> E[Length check] + E --> F{Pass?} + F -->|No| G[Regenerate max 3] + F -->|Yes| H[Store + Send] + G --> A +``` + +## Prompt guidelines (system) + +### Initial email + +- 80–150 words +- Specific but generic business topic (scheduling, follow-up, intro) +- Realistic subject ≤60 chars +- No signatures with fake phone numbers +- Sign off with first name only if name provided in mailbox profile (future) + +### Reply + +- 40–100 words +- Reference specific points from incoming body (quote or paraphrase) +- Natural continuation, not sales pitch +- `Re:` subject handling + +## Content diversity + +| Technique | Purpose | +|-----------|---------| +| Temperature 0.8–0.9 | Variation | +| Industry rotation | Context diversity | +| Thread memory (last 3 bodies) | Avoid repetition in replies | +| Optional spintax-free rewriting | Structural variation (phase 2) | + +## Mailbox profile (future) + +```json +{ + "senderName": "Jon", + "companyName": "FillCare", + "title": "Co-founder" +} +``` + +AI uses real values instead of inventing or placeholdering. + +## Failure handling + +| Failure | Action | +|---------|--------| +| 3 validation failures | Skip send slot; log `ai_validation_failed` | +| API down | Retry with backoff; don't send template fallback | +| JSON parse error | Retry once with stricter prompt | + +## Audit + +Store `contentVersion` and `validationResult` on `WarmupLog` for debugging disputes. + +## Why no links in warmup + +Tracking links and URLs are strong spam signals during reputation building. Warmup content should be link-free until placement stabilizes (phase 2: optional benign links). diff --git a/docs/10-statistics-and-reporting.md b/docs/10-statistics-and-reporting.md new file mode 100644 index 0000000..153e331 --- /dev/null +++ b/docs/10-statistics-and-reporting.md @@ -0,0 +1,115 @@ +# Statistics & Reporting + +## Operator questions the dashboard must answer + +1. How many emails are **scheduled** today for each primary? +2. How many were **sent** today? +3. How many were **replied** today? +4. How many landed in **spam** vs inbox? +5. Is warmup **on track** for the recipe? +6. Are any mailboxes in **error**? + +## Per-primary campaign dashboard + +### Summary cards + +| Card | Source | +|------|--------| +| Campaign day | `currentDay / durationDays` | +| Today's sends | `actualSends / plannedSends` | +| Today's replies | `actualReplies / plannedReplies` | +| Inbox rate (7d) | `DailyStat` rollup | +| Spam rescues (7d) | Sum `spamRescued` | +| Status | active / paused / error | + +### Time series charts (30–45 day window) + +| Chart | Series | +|-------|--------| +| Volume | planned vs actual sends | +| Engagement | replies, rescues | +| Placement | inbox % vs spam % | +| Ramp progress | cumulative sends vs recipe curve | + +### Tables + +| Table | Columns | +|-------|---------| +| Recent activity | time, satellite, subject, status, placement | +| Satellite health | email, status, last send, last error | +| Daily breakdown | date, sent, replied, spam, inbox rate | + +## API endpoints (target) + +``` +GET /api/campaigns/:id/stats?range=7d|30d|all +GET /api/campaigns/:id/stats/today +GET /api/campaigns/:id/stats/daily?from=&to= +GET /api/mailboxes/:id/stats +GET /api/workspace/overview +``` + +### Response example (`/stats/today`) + +```json +{ + "campaignId": "...", + "date": "2026-06-20", + "dayIndex": 12, + "planned": { "sends": 18, "replies": 6 }, + "actual": { "sends": 15, "replies": 5, "spamDetected": 2, "spamRescued": 2 }, + "rates": { "reply": 33.3, "inbox": 86.7 }, + "recipe": "grow", + "onTrack": true +} +``` + +## Aggregation strategy + +### Real-time counters + +Update `DailySchedule.actualSends` on each successful send (transactional). + +### Nightly rollup + +Job at 00:05 campaign TZ: + +- Finalize yesterday's `DailyStat` +- Compute 7d rolling averages +- Evaluate auto-pause rules + +### Materialized vs computed + +| Metric | Strategy | +|--------|----------| +| Today's counts | Real-time counters | +| Historical | `DailyStat` table | +| Inbox rate | `PlacementEvent` aggregation | +| Recipe curve overlay | Computed from `DailySchedule` | + +## Export + +- CSV export of daily stats +- JSON export for BI (future) + +## Alerts (phase 2) + +| Alert | Channel | +|-------|---------| +| Spam rate threshold | Email, webhook | +| Mailbox error | Email, in-app | +| Campaign completed | In-app | +| Ramp behind schedule | In-app | + +## FillCare example view + +**hello@fillcareapp.com — Day 12/30 — Grow** + +| Metric | Value | +|--------|-------| +| Scheduled today | 18 | +| Sent | 15 | +| Replied | 5 (33%) | +| Spam detected | 2 | +| Spam rescued | 2 | +| Inbox rate (7d) | 94% | diff --git a/docs/11-esp-compliance-playbook.md b/docs/11-esp-compliance-playbook.md new file mode 100644 index 0000000..457f401 --- /dev/null +++ b/docs/11-esp-compliance-playbook.md @@ -0,0 +1,143 @@ +# ESP Compliance Playbook + +How to avoid Google, Microsoft, and Yahoo flagging Warmbox as an abusive or detectable warmup service. + +## Threat model + +ESPs detect warmup services via: + +| Signal | Detection vector | +|--------|------------------| +| Volume spikes | Sudden send increases | +| Machine cadence | Sends every N minutes exactly | +| Identical content | Same body hash across mailboxes | +| Artificial pools | Known warmup IP/domain clusters | +| 100% engagement | Every message opened/replied | +| Bad pool hygiene | High bounce rate in network | +| Missing auth | SPF/DKIM/DMARC failures | +| Spam complaints | User marks as spam | + +## Warmbox mitigations + +### 1. Own satellite pool (phase 1) + +User controls all mailboxes. No unknown third-party addresses. Lower pool-toxicity risk than public warmup networks. + +### 2. Human-like scheduling + +- Send windows (business hours) +- Random jitter 45–180 min between sends +- 48h volume cap +20% +- Weekend volume reduction (optional) + +### 3. Realistic engagement + +- Reply rate 30–45%, not 100% +- Some messages: rescue + read, no reply +- Varied AI content with validation +- Threaded replies with proper `In-Reply-To` + +### 4. Content hygiene + +- Plain text, no links/pixels in warmup +- No placeholder templates +- Industry-appropriate language +- No sales spam triggers + +### 5. Authentication + +Pre-flight checks before campaign start: + +| Check | Required | +|-------|----------| +| SPF record | Pass or warn | +| DKIM signing | Pass or warn | +| DMARC policy | Present (p=none minimum) | +| Reverse DNS | Warn if missing | + +`POST /mailboxes/:id/dns-check` (future) + +### 6. Volume discipline + +| Rule | Value | +|------|-------| +| Max warmup sends/day/mailbox | 50 hard cap | +| Recommended max | 40 | +| New domain age before warmup | 14 days recommended | +| Never stop warmup during cold sends | Maintenance mode 20% | + +### 7. Auto-pause + +| Trigger | Action | +|---------|--------| +| Bounce rate >5% | Pause campaign | +| Spam rate >30% (3 days) | Pause + alert | +| Auth errors | Mark error, stop | +| Complaint signal (future) | Immediate pause | + +### 8. Peer network (phase 3 only) + +If added: + +- Verify DNS on every peer mailbox +- Remove bouncing addresses within 24h +- No cross-tenant data leakage +- Rate limit peer assignments +- Monitor pool spam rate aggregate + +## Provider-specific guidance + +### Google Workspace / Gmail + +- Use app passwords or OAuth +- Enable [Postmaster Tools](https://postmaster.google.com) +- Target spam rate <0.1% (alert at 0.3%) +- Avoid connecting consumer Gmail accounts with suspicious bulk patterns +- `[Gmail]/Spam` rescue is user-owned mail — acceptable + +### Microsoft 365 / Outlook + +- Enable IMAP/SMTP auth +- Respect sending limits (~30/min, ~10k/day) +- Monitor SNDS (future) +- Junk folder rescue + +### Yahoo + +- App password required +- Lower daily limits than Google +- Watch Bulk folder + +## What Warmbox is NOT + +- Not a cold email sender — don't blast prospects through warmup +- Not a link tracker — no pixels in warmup phase +- Not a shared bot network — own satellites first + +## Legal & ToS + +Operators must: + +- Own or control all connected mailboxes +- Comply with provider Terms of Service +- Not use warmup to deceive recipients (warmup is internal engagement only) + +Document in Terms of Service before SaaS launch. + +## Compliance checklist (pre go-live) + +- [ ] SPF/DKIM/DMARC verified on primary domain +- [ ] Domain age ≥14 days +- [ ] Recipe = Grow, duration ≥30 days +- [ ] Reply rate ≤45% +- [ ] ≥4 satellites assigned +- [ ] AI validation enabled +- [ ] Send window configured +- [ ] Postmaster Tools connected (Gmail) +- [ ] Test campaign 48h with 2 sends/day before full ramp + +## Rumor: "ESPs detect warmup services" + +**Partially true.** ESPs detect **patterns**, not brand names. Mitigation is behavioral realism + own pool + auth + volume discipline — not hiding that warmup exists. + +Warmbox should be undetectable as a **low-quality bot network**, not invisible as a product. diff --git a/docs/12-architecture.md b/docs/12-architecture.md new file mode 100644 index 0000000..dea6375 --- /dev/null +++ b/docs/12-architecture.md @@ -0,0 +1,158 @@ +# System Architecture + +## Deployment model (hybrid) + +**Phase 1–2:** Self-hosted single-tenant (FillCare) with web UI +**Phase 3+:** Multi-tenant SaaS on shared infrastructure + +Design all entities with optional `workspaceId` from day one. + +## High-level architecture + +```mermaid +flowchart TB + subgraph client [Client Layer] + WebUI[Web Dashboard] + API_Consumers[API / CLI] + end + + subgraph app [Application Layer] + API[Express API] + Auth[Auth Middleware] + CampaignSvc[Campaign Service] + RecipeSvc[Recipe Engine] + StatsSvc[Statistics Service] + ContentSvc[AI Content Service] + end + + subgraph workers [Worker Layer] + Scheduler[Cron Scheduler] + SendWorker[Send Queue Worker] + RescueWorker[Rescuer Worker] + StatsWorker[Stats Rollup Worker] + end + + subgraph connectors [Connectors] + SMTP[nodemailer] + IMAP[imapflow] + AI[OpenRouter] + end + + subgraph data [Data Layer] + DB[(SQLite → Postgres)] + Queue[(Job Queue)] + end + + WebUI --> API + API_Consumers --> API + API --> Auth + API --> CampaignSvc + API --> StatsSvc + Scheduler --> SendWorker + Scheduler --> RescueWorker + SendWorker --> SMTP + SendWorker --> ContentSvc + ContentSvc --> AI + RescueWorker --> IMAP + RescueWorker --> ContentSvc + workers --> DB + API --> DB + SendWorker --> Queue +``` + +## Component responsibilities + +| Component | Responsibility | +|-----------|----------------| +| **API** | CRUD mailboxes, campaigns, settings; stats queries | +| **Campaign Service** | Lifecycle: draft → active → paused → completed | +| **Recipe Engine** | Compute `DailySchedule` from recipe params | +| **Dispatcher / Send Worker** | Enqueue sends with jitter; role-aware pairing | +| **Rescuer Worker** | IMAP spam scan, rescue, reply sampling | +| **AI Content Service** | Generate + validate email bodies | +| **Stats Service** | Aggregations, rollups, on-track calculation | +| **Scheduler** | Cron triggers; reload on settings change | + +## Data store evolution + +| Phase | Database | Queue | +|-------|----------|-------| +| MVP (now) | SQLite | In-process cron | +| Phase 2 | SQLite | SQLite-backed queue or BullMQ | +| SaaS | PostgreSQL | Redis + BullMQ | + +Use Prisma driver adapters; swap datasource URL for Postgres. + +## Multi-tenant (future) + +```mermaid +flowchart LR + subgraph tenantA [Workspace A] + A1[Primary] + A2[Satellites] + end + subgraph tenantB [Workspace B] + B1[Primary] + B2[Satellites] + end + API --> tenantA + API --> tenantB +``` + +- Row-level isolation via `workspaceId` +- JWT auth with workspace scope +- Encrypted credentials per workspace key (KMS in SaaS) + +## Frontend architecture (recommended) + +| Layer | Tech options | +|-------|----------------| +| Framework | Next.js or Vite + React | +| UI | shadcn/ui + Tailwind | +| Data | TanStack Query | +| Charts | Recharts | +| Auth | Clerk / Auth.js (SaaS) | + +Serve as separate app or static build proxied by Express. + +## Observability + +| Concern | Tool | +|---------|------| +| Logs | Pino (existing) | +| Errors | Sentry | +| Metrics | Prometheus / Grafana (SaaS) | +| Job tracing | Correlation ID per send | + +## Security boundaries + +- Credentials encrypted at rest (AES-256-GCM) +- API key (self-hosted) → JWT (SaaS) +- No plaintext passwords in logs +- IMAP/SMTP connections TLS-only + +## Integration points (external) + +| System | Integration | Phase | +|--------|-------------|-------| +| Instantly / Smartlead | Read-only: pause warmup when they send | 3 | +| Google Postmaster | Pull domain stats | 3 | +| Webhooks | Campaign events | 2 | +| Stripe | Billing per primary | SaaS | + +## Directory structure (target) + +``` +warmbox/ +├── apps/ +│ ├── api/ # Express (current src/) +│ └── web/ # Dashboard +├── packages/ +│ ├── database/ # Prisma schema +│ ├── recipe-engine/ +│ └── content-validator/ +├── docs/ +└── prisma/ +``` + +Monorepo optional; single package acceptable through phase 2. diff --git a/docs/13-api-evolution.md b/docs/13-api-evolution.md new file mode 100644 index 0000000..cd73412 --- /dev/null +++ b/docs/13-api-evolution.md @@ -0,0 +1,122 @@ +# API Evolution + +## Current endpoints (v0) + +| Method | Path | +|--------|------| +| GET | `/health` | +| POST/GET/PATCH/DELETE | `/api/inboxes` | +| POST | `/api/inboxes/:id/test` | +| GET | `/api/logs` | +| GET/PATCH | `/api/settings` | + +## Target REST API (v1) + +### Mailboxes + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/api/mailboxes` | Create (role: primary \| satellite) | +| GET | `/api/mailboxes` | List with filters | +| GET | `/api/mailboxes/:id` | Detail | +| PATCH | `/api/mailboxes/:id` | Update | +| DELETE | `/api/mailboxes/:id` | Remove | +| POST | `/api/mailboxes/:id/test` | SMTP + IMAP test | +| POST | `/api/mailboxes/:id/dns-check` | SPF/DKIM/DMARC (future) | + +### Campaigns + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/api/campaigns` | Create warmup campaign | +| GET | `/api/campaigns` | List | +| GET | `/api/campaigns/:id` | Detail + stats summary | +| PATCH | `/api/campaigns/:id` | Update recipe/limits | +| POST | `/api/campaigns/:id/start` | Activate | +| POST | `/api/campaigns/:id/pause` | Pause | +| POST | `/api/campaigns/:id/complete` | End campaign | +| DELETE | `/api/campaigns/:id` | Delete draft only | + +### Campaign satellites + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/campaigns/:id/satellites` | List assigned | +| POST | `/api/campaigns/:id/satellites` | Assign `{ mailboxId }` | +| DELETE | `/api/campaigns/:id/satellites/:mailboxId` | Unassign | + +### Statistics + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/campaigns/:id/stats/today` | Today's metrics | +| GET | `/api/campaigns/:id/stats/daily` | Time series | +| GET | `/api/campaigns/:id/stats` | Summary + range | +| GET | `/api/workspace/overview` | All primaries at a glance | + +### Logs + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/logs` | Paginated (existing, add campaignId filter) | + +### Recipes + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/api/recipes/preview` | Preview schedule without saving | +| GET | `/api/recipes/templates` | Default params per recipe type | + +### Jobs (operator) + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/api/jobs/dispatcher/run` | Manual trigger (dev) | +| POST | `/api/jobs/rescuer/run` | Manual trigger (dev) | + +### Settings + +| Method | Path | Description | +|--------|------|-------------| +| GET/PATCH | `/api/settings` | Global + AI config | + +## Create campaign example + +```json +POST /api/campaigns +{ + "primaryMailboxId": "clx...", + "name": "FillCare hello@", + "recipe": "grow", + "durationDays": 30, + "minEmailsPerDay": 1, + "maxEmailsPerDay": 40, + "replyRatePercent": 30, + "satelliteMailboxIds": ["clx...", "clx...", "clx..."], + "timezone": "America/Vancouver", + "sendWindowStart": "08:00", + "sendWindowEnd": "18:00" +} +``` + +## Backward compatibility + +- Keep `/api/inboxes` as alias to `/api/mailboxes` for one release +- Deprecation header on v0 routes + +## Error format + +```json +{ + "error": "Human message", + "code": "CAMPAIGN_SATELLITE_MIN", + "details": { "minRequired": 2 } +} +``` + +## Auth evolution + +| Phase | Auth | +|-------|------| +| Self-hosted | Optional `x-api-key` | +| SaaS | JWT + workspace header | diff --git a/docs/14-frontend-ux-spec.md b/docs/14-frontend-ux-spec.md new file mode 100644 index 0000000..651632a --- /dev/null +++ b/docs/14-frontend-ux-spec.md @@ -0,0 +1,144 @@ +# Frontend UX Specification + +## Design principles + +1. **Confidence-first** — show health, spam rate, and on-track status prominently +2. **Progressive disclosure** — simple defaults; expert options collapsed +3. **Safe defaults** — Grow, 30 days, 30% reply, max 40/day pre-selected +4. **Mobile-readable** — dashboard works on tablet; setup flows desktop-first + +## Tech recommendation + +- **Vite + React + TypeScript** +- **shadcn/ui** + Tailwind +- **TanStack Query** for API state +- **Recharts** for time series + +## Information architecture + +``` +Dashboard (overview) +├── Campaigns +│ ├── [Campaign detail] +│ │ ├── Overview tab +│ │ ├── Activity log tab +│ │ ├── Satellites tab +│ │ └── Settings tab +│ └── New campaign wizard +├── Mailboxes +│ ├── All mailboxes +│ ├── Add primary +│ └── Add satellite +├── Activity (global log) +└── Settings + ├── AI provider + ├── Schedule defaults + └── API keys +``` + +## Key screens + +### 1. Dashboard (home) + +**Purpose:** At-a-glance health of all warming primaries. + +| Section | Content | +|---------|---------| +| Hero stats | Active campaigns, total sends today, avg inbox rate | +| Campaign cards | One card per primary: day X/Y, sends, spam %, status badge | +| Alerts | Error mailboxes, high spam rate, paused campaigns | +| Quick actions | Add primary, Add satellite, New campaign | + +### 2. New campaign wizard (4 steps) + +**Step 1 — Primary** +Select existing primary mailbox or add new. + +**Step 2 — Recipe** +Radio cards: Grow (recommended), Flat, Randomized, Custom. +Sliders: duration 30–45, min/max sends, reply rate 30%. + +**Step 3 — Satellites** +Multi-select from satellite pool; minimum 2 warning; recommend 4+. + +**Step 4 — Review** +Preview ramp chart; confirm; Start campaign. + +### 3. Campaign detail — Overview + +| Widget | Data | +|--------|------| +| Progress ring | Day 12/30 | +| Sends today | 15/18 bar | +| Replies today | 5/6 bar | +| Inbox rate 7d | 94% with trend arrow | +| Ramp chart | Planned vs actual line chart | +| Placement | Inbox vs spam stacked bar | + +Actions: Pause, Edit settings, Export CSV. + +### 4. Campaign detail — Satellites + +Table: email, provider, status, last activity, remove button. +Add satellite dropdown. + +### 5. Campaign detail — Activity + +Filterable log table (reuse `/api/logs`). + +### 6. Add mailbox modal + +Tabs: Primary | Satellite +Fields: email, provider preset (Gmail/Yahoo/Outlook/Custom), SMTP/IMAP auto-fill, app password, industry, test connection button. + +Provider presets fill host/port from [05-primary-satellite-topology.md](./05-primary-satellite-topology.md). + +### 7. Mailbox detail + +Connection status, DNS check results (future), assigned campaigns, error history. + +### 8. Settings + +- AI: API key, model, test generation button +- Defaults: recipe, duration, reply rate +- Danger zone: reset campaign, delete mailbox + +## UX patterns + +| Pattern | Use | +|---------|-----| +| Status badges | active=green, paused=yellow, error=red, completed=blue | +| Empty states | Guided CTA to add first primary | +| Loading skeletons | Stats cards | +| Optimistic updates | Pause/resume | +| Toast notifications | Test connection result, save settings | + +## Recipe selector UI + +``` +┌─────────────────────────────────────────┐ +│ ● Grow (Recommended) │ +│ Progressive ramp over 30 days │ +├─────────────────────────────────────────┤ +│ ○ Flat │ +│ Steady volume every day │ +├─────────────────────────────────────────┤ +│ ○ Randomized │ +│ Variable volume for testing │ +├─────────────────────────────────────────┤ +│ ○ Custom │ +│ Expert day-by-day schedule │ +└─────────────────────────────────────────┘ +``` + +## Accessibility + +- WCAG 2.1 AA contrast on charts +- Keyboard navigation for wizards +- Screen reader labels on status badges + +## MVP frontend scope (phase 2) + +**In scope:** Dashboard, campaign wizard, campaign detail, mailbox CRUD, connection test, activity log. + +**Out of scope:** Billing, team members, OAuth connect, mobile app. diff --git a/docs/15-security-and-compliance.md b/docs/15-security-and-compliance.md new file mode 100644 index 0000000..db99267 --- /dev/null +++ b/docs/15-security-and-compliance.md @@ -0,0 +1,73 @@ +# Security & Compliance + +## Credential handling + +| Data | Storage | Access | +|------|---------|--------| +| Mailbox passwords | AES-256-GCM encrypted | Decrypt only in send/IMAP workers | +| AI API keys | Env or encrypted settings | Never returned in GET settings | +| API keys | Env | Header auth self-hosted | + +### SaaS evolution + +- Per-tenant encryption keys (KMS) +- Secrets rotation without re-entering passwords where OAuth applies +- Audit log for credential access + +## Authentication roadmap + +| Phase | Model | +|-------|-------| +| 1 | Optional single API key | +| 2 | Session auth for web UI | +| 3 | Multi-tenant JWT + RBAC | + +### Roles (SaaS) + +| Role | Permissions | +|------|-------------| +| Owner | All + billing | +| Admin | Campaigns, mailboxes | +| Viewer | Read-only stats | + +## Data privacy + +- Warmup email bodies may contain AI-generated business text — not PII by default +- Store minimum content needed for thread continuity +- Retention policy: 90 days for logs (configurable) +- GDPR: export + delete workspace data + +## Transport security + +- TLS required for SMTP (587/465) and IMAP (993) +- HTTPS for API and UI +- No credential logging in Pino output + +## Abuse prevention (SaaS) + +- Rate limit API +- Verify email domain ownership before warming (DNS TXT challenge) +- Block disposable email domains for signup +- Monitor outbound volume per tenant + +## Legal + +Before public SaaS: + +- Terms of Service (user owns mailboxes, compliant use) +- Privacy Policy +- Acceptable Use Policy (no spamming prospects via warmup) +- ESP ToS disclaimer + +## Incident response + +| Event | Response | +|-------|----------| +| Credential leak | Rotate ENCRYPTION_KEY, force password re-entry | +| ESP suspension | Pause all campaigns for affected mailbox | +| AI generating harmful content | Block send, alert operator | + +## Compliance frameworks (future) + +- SOC 2 Type II (SaaS scale) +- Not required for self-hosted FillCare deployment diff --git a/docs/16-roadmap.md b/docs/16-roadmap.md new file mode 100644 index 0000000..5969ef1 --- /dev/null +++ b/docs/16-roadmap.md @@ -0,0 +1,172 @@ +# Product Roadmap + +## Phase 0 — Foundation (complete) + +**Status:** Done + +- Barebone MVP: flat inbox pool, dispatcher, rescuer, AI, REST API +- Prisma 7 + SQLite +- OpenRouter AI integration +- Documentation scaffold (this folder) + +--- + +## Phase 1 — Production warmup core (8–10 weeks) + +**Goal:** Reliable multi-primary warmup with recipes, roles, and stats. Self-hosted backend only. + +### Sprint 1.1 — Domain model & roles (2 weeks) + +- Add `role` to mailbox (primary / satellite) +- Add `WarmupCampaign` entity +- Campaign ↔ satellite assignments +- Migrate existing inboxes +- Deprecate flat random pairing + +**Deliverable:** API to create campaign with satellites; no sends yet. + +### Sprint 1.2 — Recipe engine (2 weeks) + +- Implement Grow, Flat, Randomized, Custom +- `DailySchedule` generation +- Duration 30–45 days, min/max validation +- 48h volume cap +- `POST /recipes/preview` + +**Deliverable:** Unit-tested schedule for all 4 recipes. + +### Sprint 1.3 — Campaign dispatcher (2 weeks) + +- Role-aware send: satellite → primary only +- Reply path: primary → satellite +- Jitter + send windows +- Campaign day rollover +- Idempotency keys + +**Deliverable:** End-to-end warmup with one primary + 4 satellites. + +### Sprint 1.4 — Reply rate engine (1 week) + +- Probabilistic 30% default replies +- `engaged_no_reply` status +- Daily reply budget tracking + +**Deliverable:** Reply rate within ±5% of target over 7 days. + +### Sprint 1.5 — Stats API (1 week) + +- `DailyStat` rollups +- `/campaigns/:id/stats/today` and `/stats/daily` +- On-track calculation + +**Deliverable:** JSON stats matching manual log counts. + +### Sprint 1.6 — AI content validator (1 week) + +- Placeholder regex rejection +- Spam word tiers +- Regenerate loop (max 3) +- Mailbox profile (name, company) + +**Deliverable:** Zero placeholder emails in 100-test run. + +--- + +## Phase 2 — Operator experience (6–8 weeks) + +**Goal:** Web UI + hardening for FillCare daily use. + +### Sprint 2.1 — Web dashboard MVP (3 weeks) + +- Vite + React + shadcn +- Dashboard, campaign wizard, campaign detail +- Mailbox add/test flows + +### Sprint 2.2 — Spam rescue hardening (2 weeks) + +- Per-UID log matching +- Placement tracking +- Folder auto-discovery +- Auto-pause on spam thresholds + +### Sprint 2.3 — DNS & health checks (1 week) + +- SPF/DKIM/DMARC preflight +- Provider presets (Gmail/Yahoo/Outlook) + +### Sprint 2.4 — Job queue (2 weeks) + +- Replace batch cron with queued sends +- Manual job triggers for dev +- Better overlap protection + +--- + +## Phase 3 — SaaS readiness (8–12 weeks) + +**Goal:** Multi-tenant, billing, optional integrations. + +- Postgres migration path +- Workspace + JWT auth +- Stripe billing per primary +- Webhooks (campaign events) +- Google Postmaster integration +- Session-based web auth +- Optional peer network (beta) + +--- + +## Phase 4 — Scale & ecosystem (ongoing) + +- Instantly / Smartlead sync +- Inbox placement tests +- Team roles +- White-label +- Mobile notifications + +--- + +## Milestone timeline (visual) + +``` +2026 Q2 [Phase 1: Core backend ████████░░] +2026 Q3 [Phase 2: Web UI + hardening ░░░░████████] +2026 Q4 [Phase 3: SaaS beta ░░░░░░░░████] +``` + +--- + +## FillCare-specific milestone + +| Week | Milestone | +|------|-----------| +| 1 | hello@fillcareapp.com campaign created, 4 satellites assigned | +| 2 | Grow recipe running, stats visible via API | +| 4 | Web dashboard live | +| 6 | 30-day warmup complete, inbox rate ≥95% | +| 8 | Maintenance mode + cold email tool connected | + +--- + +## Definition of done (per phase) + +### Phase 1 done when + +- [ ] 2+ primaries warming concurrently +- [ ] All 4 recipes pass integration tests +- [ ] Stats API accurate +- [ ] Reply rate configurable and honored +- [ ] AI validator blocks placeholders +- [ ] ESP compliance checklist passable + +### Phase 2 done when + +- [ ] Operator never needs curl for daily use +- [ ] Auto-pause tested on simulated spam spike +- [ ] FillCare running 30-day campaign via UI + +### Phase 3 done when + +- [ ] Second customer onboarded in isolated workspace +- [ ] Billing active +- [ ] Security review complete diff --git a/docs/17-agile-epics-backlog.md b/docs/17-agile-epics-backlog.md new file mode 100644 index 0000000..283b6a8 --- /dev/null +++ b/docs/17-agile-epics-backlog.md @@ -0,0 +1,181 @@ +# Agile Epics & Backlog + +## Epic map + +| Epic ID | Name | Phase | Priority | +|---------|------|-------|----------| +| E1 | Campaign & role model | 1 | P0 | +| E2 | Recipe engine | 1 | P0 | +| E3 | Campaign dispatcher | 1 | P0 | +| E4 | Reply rate engine | 1 | P0 | +| E5 | Statistics & reporting | 1 | P0 | +| E6 | AI content safety | 1 | P0 | +| E7 | Spam rescue v2 | 2 | P0 | +| E8 | Web dashboard | 2 | P1 | +| E9 | DNS & deliverability checks | 2 | P1 | +| E10 | Job queue | 2 | P1 | +| E11 | Multi-tenant SaaS | 3 | P2 | +| E12 | Peer network (optional) | 3 | P3 | +| E13 | External integrations | 3–4 | P3 | + +--- + +## E1 — Campaign & role model + +### Stories + +| ID | Story | Points | +|----|-------|--------| +| E1-1 | As an operator, I can mark a mailbox as primary or satellite | 3 | +| E1-2 | As an operator, I can create a warmup campaign for a primary | 5 | +| E1-3 | As an operator, I can assign/remove satellites from a campaign | 3 | +| E1-4 | As an operator, I can run multiple campaigns for different primaries | 5 | +| E1-5 | As a developer, migrate existing Inbox rows to new schema | 3 | + +**Integration:** Prisma migration + `/api/campaigns` routes + +--- + +## E2 — Recipe engine + +### Stories + +| ID | Story | Points | +|----|-------|--------| +| E2-1 | As an operator, I can select Grow recipe with 30-day duration | 5 | +| E2-2 | As an operator, I can select Flat recipe | 3 | +| E2-3 | As an operator, I can select Randomized recipe | 5 | +| E2-4 | As an expert, I can define Custom per-day schedule | 8 | +| E2-5 | As an operator, I can preview ramp chart before starting | 3 | +| E2-6 | System enforces min 1, max 50 emails/day | 2 | +| E2-7 | System enforces 48h +20% volume cap | 5 | + +**Integration:** `packages/recipe-engine` or `src/services/recipe.service.ts` + +--- + +## E3 — Campaign dispatcher + +### Stories + +| ID | Story | Points | +|----|-------|--------| +| E3-1 | Satellites send only to assigned primary | 5 | +| E3-2 | Primary replies only to assigned satellites | 5 | +| E3-3 | Sends respect daily schedule from recipe | 5 | +| E3-4 | Sends distributed with jitter in send window | 5 | +| E3-5 | Campaign auto-completes after durationDays | 3 | +| E3-6 | Maintenance mode at 20% volume post-campaign | 5 | + +**Integration:** Replace `dispatcher.job.ts` with campaign-aware version + +--- + +## E4 — Reply rate engine + +### Stories + +| ID | Story | Points | +|----|-------|--------| +| E4-1 | Operator sets reply rate 30% (default) | 2 | +| E4-2 | System caps reply rate at 45% | 1 | +| E4-3 | Rescuer samples replies probabilistically | 8 | +| E4-4 | Non-replied messages still rescued and flagged | 3 | +| E4-5 | Dashboard shows reply rate actual vs target | 3 | + +--- + +## E5 — Statistics & reporting + +### Stories + +| ID | Story | Points | +|----|-------|--------| +| E5-1 | API returns today's planned vs actual sends | 5 | +| E5-2 | API returns daily time series for 30 days | 5 | +| E5-3 | API returns spam detected/rescued counts | 3 | +| E5-4 | API returns inbox placement rate | 5 | +| E5-5 | Nightly rollup job materializes DailyStat | 5 | +| E5-6 | Workspace overview endpoint for all primaries | 5 | + +--- + +## E6 — AI content safety + +### Stories + +| ID | Story | Points | +|----|-------|--------| +| E6-1 | Validator rejects bracket placeholders | 3 | +| E6-2 | Validator rejects tier-1 spam words | 3 | +| E6-3 | Auto-regenerate up to 3 times on failure | 3 | +| E6-4 | Mailbox profile supplies real name/company | 3 | +| E6-5 | Plain text only, no links in warmup body | 2 | + +--- + +## E7 — Spam rescue v2 + +### Stories + +| ID | Story | Points | +|----|-------|--------| +| E7-1 | Match spam UID to single WarmupLog | 5 | +| E7-2 | Track placement inbox vs spam | 3 | +| E7-3 | Auto-discover spam folder per provider | 5 | +| E7-4 | Auto-pause campaign on high spam rate | 5 | + +--- + +## E8 — Web dashboard + +### Stories + +| ID | Story | Points | +|----|-------|--------| +| E8-1 | Dashboard shows all active campaigns | 5 | +| E8-2 | 4-step campaign wizard | 8 | +| E8-3 | Campaign detail with charts | 8 | +| E8-4 | Add mailbox with provider presets | 5 | +| E8-5 | Connection test from UI | 3 | +| E8-6 | Global activity log view | 3 | + +--- + +## Integration breakdown (build order) + +```mermaid +flowchart TD + E1[E1 Campaign model] --> E2[E2 Recipe engine] + E2 --> E3[E3 Dispatcher] + E1 --> E4[E4 Reply rate] + E3 --> E5[E5 Statistics] + E3 --> E6[E6 AI validator] + E4 --> E7[E7 Spam rescue v2] + E5 --> E8[E8 Web UI] + E6 --> E8 + E7 --> E8 +``` + +## Suggested first sprint (2 weeks) + +1. E1-1, E1-2, E1-3 (campaign CRUD + roles) +2. E2-1, E2-6 (Grow recipe + validation) +3. E6-1 (placeholder validator) + +**Sprint goal:** Create FillCare campaign via API; preview Grow schedule; no sends yet. + +## Story point scale + +| Points | Meaning | +|--------|---------| +| 1–2 | Few hours | +| 3 | Half day | +| 5 | 1 day | +| 8 | 2–3 days | + +## Backlog hygiene + +- Every story links to spec doc section +- Acceptance criteria required before sprint commit +- Demo: FillCare primary visible in stats after each epic diff --git a/docs/18-open-questions.md b/docs/18-open-questions.md new file mode 100644 index 0000000..55970d5 --- /dev/null +++ b/docs/18-open-questions.md @@ -0,0 +1,61 @@ +# Open Questions + +Decisions needed before or during implementation. Update this doc as answers are confirmed. + +## Confirmed + +| Question | Answer | Date | +|----------|--------|------| +| Deployment model | Hybrid: self-hosted first, SaaS-ready | 2026-06 | +| Satellite pool | Own satellites first; peer network optional later | 2026-06 | +| Warmup duration | 30–45 days recommended | 2026-06 | +| Reply rate default | 30%, max 45% | 2026-06 | +| Daily volume | Min 1, default max 40, cap 50 | 2026-06 | + +## Product + +| # | Question | Options | Impact | +|---|----------|---------|--------| +| Q1 | Can one satellite serve multiple primaries? | A) No (dedicated) B) Yes with toggle | Dispatcher pairing | +| Q2 | Should primary ever send unsolicited to satellites (not replies)? | A) No B) Yes for variety | Traffic rules | +| Q3 | Post-campaign behavior default? | A) Stop B) Maintenance 20% C) Ask user | Campaign lifecycle | +| Q4 | Weekend sending? | A) Same volume B) 50% C) Pause | Scheduling | +| Q5 | FillCare: is hello@ on Google Workspace? | Confirm SMTP/IMAP hosts | Onboarding docs | + +## Technical + +| # | Question | Options | Impact | +|---|----------|---------|--------| +| Q6 | Job queue tech for phase 2? | A) BullMQ+Redis B) pg-boss C) SQLite queue | Infra | +| Q7 | When to migrate SQLite → Postgres? | A) Phase 3 B) Phase 2 if UI heavy | Schema hosting | +| Q8 | OAuth for Gmail/Outlook in SaaS? | A) App passwords only B) OAuth phase 3 | Auth complexity | +| Q9 | Monorepo split api/web? | A) Yes phase 2 B) Stay single package | Repo structure | +| Q10 | Warmup content: store full body forever? | A) 90d retention B) Forever | Storage/privacy | + +## Business (SaaS phase) + +| # | Question | Options | Impact | +|---|----------|---------|--------| +| Q11 | Pricing model? | Per primary / per satellite / flat | Billing | +| Q12 | Free tier? | Yes limited primaries / No | GTM | +| Q13 | Brand name: Warmbox or FillCare-internal? | | Marketing | +| Q14 | Open source core? | A) Private B) OSS + hosted | License | + +## Compliance + +| # | Question | Options | Impact | +|---|----------|---------|--------| +| Q15 | Require DNS verification before campaign start? | A) Warn B) Block | Onboarding friction | +| Q16 | Canada (PIPEDA) data residency for SaaS? | | Hosting region | + +## How to resolve + +1. Add answer to **Confirmed** table +2. Update affected spec docs +3. Create implementation story in [17-agile-epics-backlog.md](./17-agile-epics-backlog.md) + +## Questions for you (next conversation) + +1. **Q5** — Is `hello@fillcareapp.com` on Google Workspace, Microsoft 365, or cPanel? +2. **Q1** — Should satellites be dedicated per primary, or shared across your primaries? +3. **Q3** — After 30-day warmup completes, auto-switch to maintenance mode or stop? diff --git a/docs/19-risk-register.md b/docs/19-risk-register.md new file mode 100644 index 0000000..aad8711 --- /dev/null +++ b/docs/19-risk-register.md @@ -0,0 +1,55 @@ +# Risk Register + +| ID | Risk | Likelihood | Impact | Mitigation | Owner | +|----|------|------------|--------|------------|-------| +| R1 | ESP suspends satellite Gmail account | Medium | High | Own pool, volume caps, app passwords, monitor errors | Operator | +| R2 | ESP detects warmup patterns | Medium | High | Jitter, reply sampling, content diversity, no links | Engineering | +| R3 | AI generates placeholder/spam content | Medium | High | Post-validation, regen loop, blocklists | Engineering | +| R4 | Shared peer pool toxicity (phase 3) | High | Critical | Delay peer network; bounce monitoring | Product | +| R5 | SQLite corruption / lock under load | Low | Medium | Migrate to Postgres before SaaS; queue writes | Engineering | +| R6 | Credential breach | Low | Critical | Encryption, no log leaks, KMS for SaaS | Security | +| R7 | IMAP spam folder mismatch | Medium | Medium | Auto-discovery, provider presets | Engineering | +| R8 | 100% reply rate detection | High (now) | Medium | Reply rate engine (E4) | Engineering | +| R9 | Operator sets max 50 on day 1 | Medium | High | Grow recipe default; UI warnings | Product | +| R10 | Google Postmaster spam rate >0.3% | Medium | High | Auto-pause, reduce volume, rescue | Operations | +| R11 | Legal/ToS violation (warmup as deception) | Low | High | ToS: only owned mailboxes; internal engagement | Legal | +| R12 | OpenRouter free model quality | Medium | Low | Model fallback list; paid tier option | Engineering | +| R13 | Cron overlap double-sends | Medium | Medium | Idempotency keys, job queue | Engineering | +| R14 | Primary warms too fast vs domain age | Medium | High | Domain age check; min 14-day warning | Product | +| R15 | Competitor ESP crackdown on warmup industry | Low | High | Differentiate on own-pool + self-hosted control | Product | + +## Risk heat map + +``` +Impact + High | R4 R6 R9 R10 R11 R15 + | R1 R2 R3 R8 R14 + Medium | R5 R7 R13 + Low | R12 + +------------------ + Low Med High + Likelihood +``` + +## Top 5 actions (before scaling FillCare) + +1. **Implement E4** — reply rate sampling (addresses R8, R2) +2. **Implement E6** — AI validator (addresses R3) +3. **Implement E2** — Grow recipe enforced (addresses R9, R14) +4. **Run DNS checklist** on fillcareapp.com (addresses R10, R14) +5. **Add auto-pause** on spam rate (addresses R10, R1) + +## Monitoring triggers + +| Metric | Warning | Critical | +|--------|---------|----------| +| Spam placement rate | >15% 7d avg | >30% 3d avg | +| Bounce rate | >2% | >5% | +| Auth failures | 1 per mailbox | 3 consecutive | +| AI validation fail rate | >5% | >15% | +| Reply rate deviation | >±10% from target | >±20% | + +## Review cadence + +- Weekly during active warmup campaigns +- Update register after each sprint retro diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..7ceb213 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,52 @@ +# Warmbox Documentation + +Planning artifacts for evolving Warmbox from a barebone MVP into a production-grade cold email warmup platform and eventual SaaS. + +## Decisions (confirmed) + +| Decision | Choice | +|----------|--------| +| Deployment | **Hybrid** — self-hosted + web UI first; architecture ready for multi-tenant SaaS later | +| Satellite pool | **Own satellites first**; optional shared peer network in a later phase | +| Warmup duration | **30–45 days** recommended default | +| Reply rate default | **30%** (max recommended **45%**) | +| Daily volume | Min **1**, default max **40**, hard cap **50** | + +## Document index + +| # | Document | Purpose | +|---|----------|---------| +| 00 | [Product Brief](./00-product-brief.md) | Vision, personas, success metrics | +| 01 | [Current State & Gaps](./01-current-state-and-gaps.md) | MVP inventory vs target | +| 02 | [Glossary](./02-glossary.md) | Domain language | +| 03 | [Domain Model](./03-domain-model.md) | Future entities & relationships | +| 04 | [Warmup Recipes](./04-warmup-recipes.md) | Grow, Flat, Randomized, Custom specs | +| 05 | [Primary & Satellite Topology](./05-primary-satellite-topology.md) | Multi-primary warmup design | +| 06 | [Scheduling & Timing](./06-scheduling-and-timing.md) | Duration, windows, jitter | +| 07 | [Reply Rate Engine](./07-reply-rate-engine.md) | Probabilistic replies | +| 08 | [Spam Rescue](./08-spam-rescue-and-placement.md) | Rescue, placement tracking | +| 09 | [AI Content Policy](./09-ai-content-policy.md) | Safe B2B content rules | +| 10 | [Statistics & Reporting](./10-statistics-and-reporting.md) | Metrics & dashboards | +| 11 | [ESP Compliance Playbook](./11-esp-compliance-playbook.md) | Anti-detection, DNS, policies | +| 12 | [Architecture](./12-architecture.md) | System design (hybrid → SaaS) | +| 13 | [API Evolution](./13-api-evolution.md) | REST roadmap | +| 14 | [Frontend UX Spec](./14-frontend-ux-spec.md) | Web UI screens & flows | +| 15 | [Security & Compliance](./15-security-and-compliance.md) | Credentials, privacy, legal | +| 16 | [Roadmap](./16-roadmap.md) | Phased delivery plan | +| 17 | [Agile Epics & Backlog](./17-agile-epics-backlog.md) | Epics, stories, integrations | +| 18 | [Open Questions](./18-open-questions.md) | Unresolved decisions | +| 19 | [Risk Register](./19-risk-register.md) | Risks & mitigations | + +## How to use these docs + +1. **Product** — Start with `00`, `16`, `17` for prioritization. +2. **Engineering** — Use `03`–`13` as implementation specs per integration. +3. **Each sprint** — Pick one epic from `17`, cross-reference the relevant spec doc, update `18` when decisions are made. + +## Agile workflow (recommended) + +``` +Epic (17) → Technical spec (03–13) → Implementation PR → Stats/metrics validation (10) +``` + +Sprints should ship **vertical slices** (e.g. "Primary inbox + Grow recipe + daily stats") rather than horizontal layers only.