gondolier — MVP Launch Plan
gondolier — MVP Launch Plan
Section titled “gondolier — MVP Launch Plan”Target: Shipped, billable MVP on gondolier.dev Date: TBD (start date + 4-6 weeks)
0. What gondolier does
Section titled “0. What gondolier does”Hosted multi-tenant batch merge queue for Forgejo/Gitea. Reuses shunt’s batch-then-bisect engine (single source of truth) and provides the hosted layer: tenant onboarding, encrypted credential management, scheduling, billing, and a web dashboard.
Current state
Section titled “Current state”| Layer | Status |
|---|---|
| Shunt engine | ✅ Imported from GitHub (shunt/mq) |
| Workers cron | ✅ Cron trigger wired — */5 * * * * |
| Supabase schema | ✅ Migrations for tenants, connections, repos, audit log, users, orgs, billing |
| API layer | ✅ REST API for tenant/connection/repo management |
| Crypto | ✅ Envelope AES-GCM (master key → DEK → token) |
| Forge client | ✅ Workers-compatible, implements mq.ForgeClient |
| Stager | ✅ API-based staging via Forgejo Git API, uses mq.MergedRef |
| Lease manager | ✅ Durable Object-based per-(tenant, repo) lease — FIXED |
| Tests | ✅ 46+ passing across all packages |
| Sell site | ❌ Not built |
| Auth/registration | ❌ Not built |
| Billing | ❌ Not built |
| Docs | ✅ Documentation complete — 7 focused docs in docs/ |
| Cron engine tick | ❌ Stub only — cron fires but doesn’t fetch repos or run engine |
| CI/CD pipeline | ❌ Not built |
| Monitoring | ❌ Not built |
Architecture overview
Section titled “Architecture overview”User browser ──► gondolier.dev (sell site + dashboard) │ ├─ Cloudflare Pages (sell site, static) ├─ Cloudflare Workers (API + dashboard) ├─ Cloudflare KV (rate limiting, transient state) ├─ Cloudflare DO (leases) └─ Cloudflare Secrets (master key) │Supabase (PostgreSQL) │ ├─ tenants │ ├─ forge_connections │ ├─ managed_repos │ ├─ audit_log │ └─ user_accounts │ ← NEW: for dashboard auth │Stripe (billing) │ ├─ subscriptions │ ├─ invoices │ └─ webhooks │ │Resend (email) │PostHog (analytics) │Phases
Section titled “Phases”Phase A: Infrastructure & Foundation
Section titled “Phase A: Infrastructure & Foundation”Set up all cloud services, CI/CD, and the deployment pipeline.
A1. Supabase setup (MANUAL — operator)
Section titled “A1. Supabase setup (MANUAL — operator)”- Create Supabase project at
gondolier.supabase.co - Enable Row Level Security (RLS) on all tables
- Create service role key + anon key — store in Cloudflare Secrets
- Run migration 001 (
tenants,forge_connections,managed_repos,audit_log) - Run migration 002 (
user_accounts,organizations,organization_members) - Run migration 003 (
billing) - Add
user_accountstable for dashboard authentication - Configure database backups (Supabase has this by default on paid plans)
A2. Cloudflare setup (MANUAL — operator)
Section titled “A2. Cloudflare setup (MANUAL — operator)”- Register domain
gondolier.devwith Cloudflare - Create Cloudflare Worker:
gondolier- Bound: Supabase REST API (service role)
- Bound: Cloudflare Secrets (master key, Supabase keys)
- Bound: Cloudflare KV (rate limiting namespace)
- Bound: Cloudflare DO (lease manager)
- Create Cloudflare Durable Object:
LeaseDO - Create Cloudflare KV namespace:
RATE_LIMIT_KV - Set Cloudflare Secret:
SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY,ADMIN_KEY,GONDOLIER_MASTER_KEY - Configure Workers cron trigger:
*/5 * * * * - Configure domain:
gondolier.dev→ Worker (unproxied, route handles TLS)
A3. Stripe setup (MANUAL — operator)
Section titled “A3. Stripe setup (MANUAL — operator)”- Create Stripe account
- Create products/pricing tiers (see Pricing section below)
- Create Stripe webhook endpoint →
https://api.gondolier.dev/stripe/webhook - Store webhook secret in Cloudflare Secrets
- Configure Stripe test mode for development
A4. CI/CD pipeline
Section titled “A4. CI/CD pipeline”- GitHub Actions workflow for:
-
push to main: build + test + deploy to Workers preview -
pull_request: build + test + vet -
release: build + test + deploy to Workers production
-
- wrangler.toml update with production deployment target
- Supabase migration CI: run migrations on deploy to production
A5. Resend + PostHog setup (MANUAL — operator)
Section titled “A5. Resend + PostHog setup (MANUAL — operator)”- Create Resend account, verify domain
- Create PostHog project, get API key
- Store keys in Cloudflare Secrets
Phase B: Auth & Registration
Section titled “Phase B: Auth & Registration”User-facing flow for signing up and creating an organization.
B1. Sell site (Cloudflare Pages)
Section titled “B1. Sell site (Cloudflare Pages)”- Build static sell site at
gondolier.dev- Hero section: “Merge queue for Forgejo/Gitea — zero infra”
- How it works: 3 steps (connect forge, add repos, relax)
- Pricing section (see Phase E)
- GitHub/OAuth login CTA
- Footer with docs link, status link, contact
- Responsive, mobile-first
- Deploy to Cloudflare Pages
- Domain:
gondolier.dev→ Pages
Tech: Astro or plain HTML/CSS/JS (keep it simple). No framework bloat.
B2. Dashboard auth (Workers)
Section titled “B2. Dashboard auth (Workers)”- OAuth login via GitHub (simplest start — target GitHub repo users first)
GET /auth/github— redirect to GitHub OAuthGET /auth/github/callback— exchange code for token, create/find account
- Session management:
- JWT signed with Cloudflare Secret key
- Stored in httpOnly cookie
- 30-day TTL, refreshable
-
GET /api/v1/me— get current user -
POST /api/v1/logout— invalidate session
DB schema: user_accounts table:
id UUID PRIMARY KEYgithub_id TEXT UNIQUE NOT NULLgithub_login TEXT NOT NULLgithub_avatar TEXTemail TEXTcreated_at TIMESTAMPTZ DEFAULT NOW()updated_at TIMESTAMPTZ DEFAULT NOW()B3. Organization model
Section titled “B3. Organization model”-
organizationstable:
id UUID PRIMARY KEYname TEXT NOT NULLslug TEXT UNIQUE NOT NULL -- for URL: app.gondolier.dev/{slug}owner_id UUID REFERENCES user_accounts(id)stripe_customer_id TEXT UNIQUEstripe_subscription_id TEXT UNIQUEtier TEXT DEFAULT 'free' -- free, pro, team, enterprisecreated_at TIMESTAMPTZ DEFAULT NOW()updated_at TIMESTAMPTZ DEFAULT NOW()-
organization_memberstable:
organization_id UUID REFERENCES organizations(id)user_id UUID REFERENCES user_accounts(id)role TEXT DEFAULT 'member' -- admin, memberPRIMARY KEY (organization_id, user_id)- Onboard flow: create org → add first connection → add first repo → activate queue
Phase C: Dashboard
Section titled “Phase C: Dashboard”Web UI for managing tenants, connections, repos, and monitoring queues.
C1. Dashboard shell
Section titled “C1. Dashboard shell”- Build dashboard at
app.gondolier.dev - Sidebar navigation: Dashboard, Connections, Repos, Queue, Settings
- Top bar: user avatar, logout, org switcher
- Auth guard: redirect to login if not authenticated
Tech: SvelteKit (Cloudflare Pages compatible) or vanilla HTML/JS.
C2. Dashboard pages
Section titled “C2. Dashboard pages”- Dashboard home: active queues, recent activity, queue health
- Connections page: list forge connections, add/edit/delete, token rotation
- Repos page: list managed repos, per-repo config (base branch, status context, merge style)
- Queue page: current batch status, PR list, staging branch status, CI status
- Settings page: org settings, API keys, notification webhooks, billing info
C3. Queue monitoring
Section titled “C3. Queue monitoring”- Real-time queue status (polling every 30s, or WebSocket if available)
- Batch visualization: show current batch, pending PRs, staging status
- Bounce notifications with reason
- Click-through to PR details on forge
Phase D: Billing
Section titled “Phase D: Billing”Stripe integration for subscription management.
D1. Pricing tiers
Section titled “D1. Pricing tiers”| Tier | Price | Limits |
|---|---|---|
| Free | $0 | 1 repo, 10 PRs/month, community support |
| Pro | $29/mo | 10 repos, 100 PRs/month, email support |
| Team | $99/mo | 50 repos, 500 PRs/month, Slack support |
| Enterprise | Custom | Unlimited, priority support, SSO |
Metering unit: PRs merged per billing period (via audit log count).
D2. Stripe integration
Section titled “D2. Stripe integration”-
POST /api/v1/billing/checkout— create Stripe checkout session- Pass
tierandorganization_id - Redirect to Stripe checkout
- Return to success URL:
https://app.gondolier.dev/settings/billing
- Pass
-
POST /api/v1/billing/portal— create Stripe customer portal- For managing subscription, payment methods
- Return URL:
https://app.gondolier.dev/settings/billing
-
POST /stripe/webhook— handle Stripe eventscustomer.subscription.created→ activate tiercustomer.subscription.updated→ update tiercustomer.subscription.deleted→ downgrade to freeinvoice.payment_failed→ notify tenant, grace period
DB schema: billing table:
id UUID PRIMARY KEYorganization_id UUID REFERENCES organizations(id)stripe_subscription_id TEXT UNIQUEtier TEXT DEFAULT 'free'status TEXT DEFAULT 'active' -- active, past_due, canceled, trialingcurrent_period_start TIMESTAMPTZcurrent_period_end TIMESTAMPTZmetadata JSONBcreated_at TIMESTAMPTZ DEFAULT NOW()updated_at TIMESTAMPTZ DEFAULT NOW()D3. Tier enforcement
Section titled “D3. Tier enforcement”- Rate limit based on tier (KV-based counter)
- Repo count limit per org (enforced on create)
- Auto-downgrade at period end if payment fails (30-day grace)
Phase E: Documentation
Section titled “Phase E: Documentation”Comprehensive docs at docs.gondolier.dev.
E1. Docs structure
Section titled “E1. Docs structure”docs.gondolier.dev/├── getting-started/│ ├── installation.md ← for self-hosted (optional)│ ├── hosted-setup.md ← for gondolier.dev users│ └── first-queue.md ← step-by-step: connect → add repo → merge├── concepts/│ ├── merge-queue.md ← how batch+bisect works│ ├── staging-branch.md ← staging branch mechanics│ ├── bisect.md ← failure isolation│ └── automerge.md ← how PRs land├── self-hosted/ ← optional section│ ├── docker.md│ ├── kubernetes.md│ └── configuration.md├── reference/│ ├── api.md ← REST API reference│ ├── config.md ← .shunt.yml reference│ └── webhooks.md ← bounce webhook format├── admin/│ ├── branch-protection.md ← required settings│ ├── gate-workflow.md ← the mq-gate.yml workflow│ └── bot-setup.md ← creating a bot account├── billing/│ ├── pricing.md│ ├── billing-faq.md│ └── upgrade.md├── troubleshooting/│ ├── staging-fails.md│ ├── ci-not-registered.md│ └── rate-limits.md└── changelog.mdE2. Docs tech
Section titled “E2. Docs tech”- 7 focused docs written in
docs/: ARCHITECTURE, WEBHOOK_DESIGN, LEASING, SECURITY, API, DEPLOYMENT, DEVELOPMENT - Build docs site with Astro or Docusaurus (Astro for simplicity)
- Deploy to Cloudflare Pages
- Domain:
docs.gondolier.dev→ Pages - Include search (Algolia or built-in)
- Include “Edit this page” link to GitHub
The docs themselves are complete — the Astro site is a presentation layer.
Phase F: Deployment & Launch
Section titled “Phase F: Deployment & Launch”F1. Pre-launch checklist
Section titled “F1. Pre-launch checklist”- All tests pass:
go test ./... - Build passes:
go build ./... - Vet passes:
go vet ./... - Supabase migrations run clean on fresh DB
- Workers deploys clean:
wrangler deploy - Sell site loads at gondolier.dev
- Dashboard auth works (GitHub OAuth)
- Registration flow works end-to-end
- Stripe webhook fires correctly
- Bounce notification fires correctly
- Rate limiting works
- Lease management works (Durable Object) — acquire/release fixed, check works; cron not yet wired
- Encryption works (master key → DEK → token)
- Docs site loads at docs.gondolier.dev (pre-built HTML; Astro presentation layer pending)
F2. Soft launch
Section titled “F2. Soft launch”- Invite 3-5 friendly users for beta testing
- Collect feedback on onboarding flow
- Fix critical bugs
- Document known issues
F3. Public launch
Section titled “F3. Public launch”- Announce on Hacker News, r/golang, forgejo Discord
- Update shunt README with hosted link
- Set up status page (e.g., status.gondolier.dev)
- Monitor error rates, latency, queue success rate
Phase G: Post-launch
Section titled “Phase G: Post-launch”- PostHog analytics integration (user journeys, funnel tracking)
- Slack/Discord notification integration (v2)
- Email notifications via Resend (v2)
- Web UI polish (HTML templates for dashboard)
- Multi-repo queue visualization
- Performance optimization (batch tick parallelism)
- Auto-scaling for high-traffic tenants
Manual operator steps summary
Section titled “Manual operator steps summary”Here’s everything YOU need to do manually, batched for efficiency:
Batch 1: Service setup (30-45 minutes)
Section titled “Batch 1: Service setup (30-45 minutes)”Already completed:
-
Supabase ✅
- Project created:
demsvafgntduxpaiggbu.supabase.co - Migrations 001, 002, 003 all pushed
- 8 tables: tenants, forge_connections, managed_repos, audit_log, user_accounts, organizations, organization_members, billing
- Project created:
-
Cloudflare ✅
- Worker deployed:
gondolier.laputa-cloud-co.workers.dev+ routegondolier.dev/* - Durable Object:
LeaseDO— working (acquire/release/check verified) - KV namespace:
RATE_LIMIT_KV— working - Secrets: SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, ADMIN_KEY, GONDOLIER_MASTER_KEY
- Cron trigger:
*/5 * * * *— wired (handler is stub) - DNS: CNAME
gondolier.dev→ workers.dev (unproxied)
- Worker deployed:
Still needs setup:
- Stripe — deferred for MVP (stub checkout/portal for now)
- Resend — deferred (bounce notifications use HTTP webhook only)
- PostHog — deferred (no analytics for MVP)
- CI/CD pipeline — GitHub/Forgejo Actions workflow
- Sell site, dashboard — Cloudflare Pages deployment
- Docs — ✅ Pre-built HTML in
docs/; Astro presentation layer pending
Batch 2: First deploy (15 minutes)
Section titled “Batch 2: First deploy (15 minutes)”Done. Code on main, worker deployed, API tested, cron wired, DO working.
Remaining:
- CI/CD pipeline setup (push to main → test → deploy)
- Verify cron handler actually ticks queues (currently stub)
Batch 3: Auth + billing integration (30-60 minutes)
Section titled “Batch 3: Auth + billing integration (30-60 minutes)”Not started. Still needs:
- Register GitHub OAuth app
- Configure GitHub client ID/secret in Cloudflare Secrets
- Deploy auth endpoints
- Test login flow
- Create Stripe test checkout
- Test webhook delivery
- Build sell site (Cloudflare Pages)
- Build dashboard stub (Cloudflare Pages)
Costs (estimated)
Section titled “Costs (estimated)”| Service | Tier | Monthly Cost |
|---|---|---|
| Cloudflare Workers | Free | $0 |
| Cloudflare KV | Free | $0 |
| Cloudflare DO | Free | $0 |
| Cloudflare Pages | Free | $0 |
| Supabase | Pro ($25/mo) | $25 |
| Stripe | Transaction fees | 2.9% + $0.30 |
| Resend | Free | $0 |
| PostHog | Free | $0 |
| Domain | .dev | ~$15/yr |
| Total | ~$25/mo + fees |
Break-even: ~1 paying Pro customer (or ~9 free users on the verge of upgrading).
Key metrics to track
Section titled “Key metrics to track”- Queue success rate: % of batches that pass gate
- Time in queue: average time from queued to merged
- Bounce rate: % of PRs that bounce
- Queue activation rate: % of managed repos with an active queue
- Tenant churn: % of tenants who cancel
- Revenue per tenant: MRR per customer