Skip to content

gondolier — MVP Launch Plan

Target: Shipped, billable MVP on gondolier.dev Date: TBD (start date + 4-6 weeks)

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.

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
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) │

Set up all cloud services, CI/CD, and the deployment pipeline.

  • 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_accounts table 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.dev with 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)
  • 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
  • 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

User-facing flow for signing up and creating an organization.

  • 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.

  • OAuth login via GitHub (simplest start — target GitHub repo users first)
    • GET /auth/github — redirect to GitHub OAuth
    • GET /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 KEY
github_id TEXT UNIQUE NOT NULL
github_login TEXT NOT NULL
github_avatar TEXT
email TEXT
created_at TIMESTAMPTZ DEFAULT NOW()
updated_at TIMESTAMPTZ DEFAULT NOW()
  • organizations table:
id UUID PRIMARY KEY
name TEXT NOT NULL
slug TEXT UNIQUE NOT NULL -- for URL: app.gondolier.dev/{slug}
owner_id UUID REFERENCES user_accounts(id)
stripe_customer_id TEXT UNIQUE
stripe_subscription_id TEXT UNIQUE
tier TEXT DEFAULT 'free' -- free, pro, team, enterprise
created_at TIMESTAMPTZ DEFAULT NOW()
updated_at TIMESTAMPTZ DEFAULT NOW()
  • organization_members table:
organization_id UUID REFERENCES organizations(id)
user_id UUID REFERENCES user_accounts(id)
role TEXT DEFAULT 'member' -- admin, member
PRIMARY KEY (organization_id, user_id)
  • Onboard flow: create org → add first connection → add first repo → activate queue

Web UI for managing tenants, connections, repos, and monitoring queues.

  • 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.

  • 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
  • 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

Stripe integration for subscription management.

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

  • POST /api/v1/billing/checkout — create Stripe checkout session
    • Pass tier and organization_id
    • Redirect to Stripe checkout
    • Return to success URL: https://app.gondolier.dev/settings/billing
  • 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 events
    • customer.subscription.created → activate tier
    • customer.subscription.updated → update tier
    • customer.subscription.deleted → downgrade to free
    • invoice.payment_failed → notify tenant, grace period

DB schema: billing table:

id UUID PRIMARY KEY
organization_id UUID REFERENCES organizations(id)
stripe_subscription_id TEXT UNIQUE
tier TEXT DEFAULT 'free'
status TEXT DEFAULT 'active' -- active, past_due, canceled, trialing
current_period_start TIMESTAMPTZ
current_period_end TIMESTAMPTZ
metadata JSONB
created_at TIMESTAMPTZ DEFAULT NOW()
updated_at TIMESTAMPTZ DEFAULT NOW()
  • 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)

Comprehensive docs at docs.gondolier.dev.

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.md
  • 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.


  • 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)
  • Invite 3-5 friendly users for beta testing
  • Collect feedback on onboarding flow
  • Fix critical bugs
  • Document known issues
  • 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

  • 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

Here’s everything YOU need to do manually, batched for efficiency:

Already completed:

  1. 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
  2. Cloudflare

    • Worker deployed: gondolier.laputa-cloud-co.workers.dev + route gondolier.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)

Still needs setup:

  1. Stripedeferred for MVP (stub checkout/portal for now)
  2. Resenddeferred (bounce notifications use HTTP webhook only)
  3. PostHogdeferred (no analytics for MVP)
  4. CI/CD pipeline — GitHub/Forgejo Actions workflow
  5. Sell site, dashboard — Cloudflare Pages deployment
  6. Docs — ✅ Pre-built HTML in docs/; Astro presentation layer pending

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:

  1. Register GitHub OAuth app
  2. Configure GitHub client ID/secret in Cloudflare Secrets
  3. Deploy auth endpoints
  4. Test login flow
  5. Create Stripe test checkout
  6. Test webhook delivery
  7. Build sell site (Cloudflare Pages)
  8. Build dashboard stub (Cloudflare Pages)
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).

  • 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