4 KiB
4 KiB
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
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)
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.