Open-Source Cold Email & Warmup Platform
Warmbly는 메일함을 예열하고 캠페인 전송·답변 triage를 지원하는 오픈소스 콜드 이메일 플랫폼입니다
GrowthOS에 고객 데이터를 업로드하면 매출 누수를 찾아 캠페인을 작성·추적하고, 사용자가 승인하면 작업을 수행한다
Upload your customer data and GrowthOS finds where revenue is leaking, writes the campaign to recover it, and tracks what converted. You approve the decisions — it does the work.
마케팅TypeScript · PLpgSQLGitHub @Mithurn온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Live: https://growos-ai.vercel.app — open the demo with no sign-up. When the AWS backend is switched on, upload your own CSV and the agent runs in ~60 seconds. See DEPLOYMENT.md for how the always-on demo and the on-demand backend fit together.
Traditional CRMs make the marketer do all the thinking: build a segment, write a message, pick a channel, schedule a send. GrowthOS inverts this.
A scheduled growth agent prepares the work while the marketer keeps execution control:
The marketer's job shifts from building to steering.
Three independently deployable services:
┌─────────────────────────────────────────────────────────────┐
│ Frontend — Next.js App Router (Vercel) │
│ Onboarding · Dashboard · Opportunities · Campaigns · Analytics │
└────────────────────────┬────────────────────────────────────┘
│ REST + Auth JWT
┌────────────────────────▼────────────────────────────────────┐
│ Backend API — Express.js (Render) │
│ Ingestion · RFM Engine · AI Orchestrator · Campaign API │
│ requireAuth + resolveCompanyMiddleware on every route │
└──────────┬──────────────────────────────┬───────────────────┘
│ │ PostgreSQL + RLS
┌──────────▼──────────┐ ┌─────────────▼───────────────────┐
│ BullMQ Worker │────▶│ Channel Service │
│ Recipient outbox │ │ Twilio / Resend / Simulator │
└──────────┬──────────┘ └─────────────┬───────────────────┘
│ Redis │ HMAC callbacks
└───────────────────────────────┘
graph TD
subgraph CLIENT["Frontend — Next.js (Vercel)"]
ONB[Onboarding Wizard]
OPP[Opportunities Page]
CAM[Campaigns Page]
ANA[Analytics Page]
end
subgraph BACKEND["Backend API — Express.js (Render)"]
ING[Data Ingestion + RFM Engine]
PER[AI Persona Engine]
OPP_SVC[AI Opportunity Engine]
CAM_SVC[AI Campaign Generator]
LAUNCH[Campaign Launcher]
WH[Webhook Receiver]
end
subgraph CHANNEL["Channel Service — Node.js (Render)"]
QUEUE[Message Queue]
PROV[Twilio / Resend / Simulator]
CB[HMAC Webhook Emitter]
end
subgraph DB["Supabase — PostgreSQL (RLS on all tables)"]
T1[(customers · orders · products)]
T2[(personas · opportunities)]
T3[(campaigns · communications · events)]
end
CSV[CSV Upload] -->|multipart| ONB
ONB -->|POST /api/process-ingestion| ING
ING --> T1
T1 --> PER
PER -->|LLM| T2
T2 --> OPP_SVC
OPP_SVC -->|LLM| T2
T2 --> OPP
OPP --> CAM
CAM -->|LLM| CAM_SVC
CAM_SVC --> T3
CAM -->|launch| LAUNCH
LAUNCH --> T3
LAUNCH -->|transactional recipient outbox| QUEUE
QUEUE --> PROV
PROV --> CB
CB -->|POST /webhooks/channel-status| WH
WH --> T3
T3 --> ANA
requireAuth middleware on every Express route — rejects requests without a valid JWTresolveCompanyMiddleware resolves companyId exclusively from the authenticated user's DB profile — never trusted from the client requestPersisted AI generation uses a prompt → JSON parse → schema validate → DB write contract. Analytics uses deterministic aggregation and a safe fallback when an optional narrative call fails.
| Step | Schema enforced | Fallback |
|---|---|---|
| Persona Engine | { persona_name, description, reasoning } | Skip assignment, log |
| Opportunity Engine | Full typed opportunity object with numeric revenue fields | Discard, continue |
| Campaign Generator | { name, message_content, channel, objective, offer } | Return error to frontend |
QUEUED → SENT → DELIVERED → READ → CLICKED / FAILEDprocessed_webhook_events dedup table prevents duplicate processingsequenceNumber ensures status only advances forwardPromise.all, not sequentiallymain (root directory: frontend) plus the Actions backuprender.yaml links both services to Mithurn/GROWTH-OS main, deploys only after CI checks pass, and runs prisma migrate deploy before the backend starts| Concern | What I did | What I'd do at scale |
|---|---|---|
| Message delivery | Simulator fallback when no provider keys set; Twilio/Resend wired and ready | Add provider keys; no code changes needed |
| Job queue | BullMQ backed by required Redis; a separate worker entrypoint is ready, while the free-tier manifest runs workers in the API process | Enable the dedicated worker service before production traffic |
| Analytics realtime | 5s polling on campaign analytics page | Supabase Realtime subscriptions |
| Webhook ingestion | Transactional Prisma write with deduplication | Stream events when measured volume requires it |
| Cold starts | Health ping pre-warms the Render API | Paid always-on API and worker instances |
| Delivery guarantee | Durable at-least-once jobs; Resend receives a provider idempotency key | Add a provider-side dedup key for Twilio if its API supports one |
companies
└── profiles (user → company mapping, enforces multi-tenancy)
└── customers (external_id, RFM scores, persona assignment)
└── orders → order_items → products
└── personas (AI-generated behavioural archetypes)
└── opportunities (AI-detected, linked to persona distribution)
└── opportunity_customers (audience join table)
└── campaigns (AI-generated copy, channel, status)
└── communications (one row per recipient, status tracked)
└── communication_events (full delivery timeline)
└── processed_webhook_events (idempotency dedup)
└── agents (goal, guardrails, involvement level)
└── agent_actions (activity log — powers the live feed)
└── ingestion_sessions (upload progress tracking)
xeno-grow/
├── frontend/ # Next.js App Router · Tailwind · TypeScript
│ ├── app/
│ │ ├── page.tsx # Dashboard — opportunities + live agent activity
│ │ ├── onboarding/ # CSV upload + ingestion progress wizard
│ │ ├── opportunities/ # AI opportunity browser + goal input
│ │ ├── campaigns/ # Campaign builder + channel refiner
│ │ ├── analytics/ # Live campaign funnel + delivery stats
│ │ ├── personas/ # Customer persona breakdown
│ │ └── intelligence/ # AI intelligence brief
│ ├── lib/
│ │ ├── api.ts # All API calls + SWR cache layer
│ │ └── supabase/ # Auth client + token helpers
│ └── components/
│
├── backend/ # Express.js · Prisma · PostgreSQL · BullMQ · LangGraph
│ ├── src/
│ │ ├── server.ts # API process
│ │ ├── worker.ts # BullMQ worker process
│ │ ├── routes/ # Authenticated HTTP boundaries
│ │ ├── middleware/auth.ts # JWT verification + company resolution
│ │ ├── services/
│ │ │ ├── customer-attributes.ts # RFM + attribute engine
│ │ │ ├── personas.ts # AI persona generation
│ │ │ ├── opportunity-discovery.ts # AI opportunity engine
│ │ │ ├── campaigns.ts # Campaign CRUD + launch + state machine
│ │ │ ├── agent-logger.ts # Activity feed writes
│ │ │ └── agent-orchestrator.ts # Scheduled, overlap-safe agent runs
│ │ └── __tests__/ # Unit and real PostgreSQL integration suites
│ └── prisma/schema.prisma
│
├── channel-service/ # Standalone delivery service · Node.js
│ └── src/
│ ├── server.ts # POST /send endpoint
│ ├── queue.ts # Async message processor
│ ├── webhook.ts # HMAC-signed callback emitter
│ └── providers/
│ ├── twilio.ts # WhatsApp + SMS
│ ├── resend.ts # Email
│ └── simulator.ts # Fallback — QUEUED→SENT→DELIVERED→READ progression
│
├── .github/workflows/ci.yml # Typecheck + test pipeline (3 parallel jobs)
├── render.yaml # Declarative Render deployment config
└── scripts/start-all.sh # Local dev: starts all 3 services
Prerequisites: Node.js 22+, PostgreSQL with pgvector, Redis, Supabase Auth, and an OpenRouter API key
git clone https://github.com/Mithurn/xeno-grow
cd xeno-grow
chmod +x scripts/start-all.sh && ./scripts/start-all.sh
| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend API | http://localhost:3001 |
| Channel Service | http://localhost:5001 |
backend/.env
NEXT_PUBLIC_SUPABASE_URL=
SUPABASE_ANON_KEY=
DATABASE_URL=
OPENROUTER_API_KEY=
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
OPENROUTER_MODEL=openrouter/free
CHANNEL_SERVICE_URL=http://localhost:5001
WEBHOOK_SECRET= # openssl rand -base64 32
FRONTEND_URL=http://localhost:3000
channel-service/.env
CRM_WEBHOOK_URL=http://localhost:3001/api/webhooks/channel-status
WEBHOOK_SECRET= # must match backend exactly
FAILURE_RATE=10 # % of simulated sends that fail
# Optional — real sends when set:
# RESEND_API_KEY=
# TWILIO_ACCOUNT_SID=
# TWILIO_AUTH_TOKEN=
# TWILIO_PHONE_NUMBER=
# TWILIO_WHATSAPP_NUMBER=
frontend/.env.local
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
NEXT_PUBLIC_API_URL=http://localhost:3001/api
cd backend
TOTAL_CUSTOMERS=500 TOTAL_ORDERS=3000 npm run generate:data
# Outputs: backend/generated-data/customers.csv + orders.csv
# Upload both via the onboarding wizard at http://localhost:3000/onboarding
The agent will process your data and surface opportunities in ~60 seconds.
./scripts/verify-local.sh
This applies local migrations, regenerates Prisma, runs backend unit and
Testcontainers integration tests, then lints/builds the frontend and channel
service. If Docker is unavailable, use SKIP_INTEGRATION=1 to run everything
else. Remote databases are refused unless ALLOW_REMOTE_DB=1 and
CONFIRM_REMOTE_DB=I_UNDERSTAND are explicitly set.
./scripts/dev-local.sh
Open http://localhost:3000. Press Ctrl-C to stop all services.