Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

2nd Summit

A community hub of curated experiences — movement, discovery, connection — for people in their next chapter of life (50+). Members browse a curated catalog, reserve spots (free RSVP today, Stripe later), and partner-ticketed events capture the registration on-platform before handing off to the partner site. Behind it: a scraping pipeline that discovers events per market, human approval gates, a marketing stack (email campaigns, journeys, Meta ads), and an internal AI wiki.

Stack

Layer Tech
Frontend Next.js 15 App Router on Vercel, Tailwind + shadcn/ui, recharts
Backend Convex (database, functions, crons, file storage, HTTP actions)
Auth Convex Auth — passwordless email OTP primary (ADR-009) + Google SSO; password kept as legacy sign-in only
Email Resend via @convex-dev/resend component; marketing on a separate subdomain (ADR-012)
Ads/attribution Meta pixel + Conversions API (ADR-013), first-touch attribution (ADR-011), first-party usage analytics
LLMs OpenAI (extraction/validation/wiki/marketing), Perplexity (discovery), Serper (source discovery)
Observability Sentry (frontend, ADR-014), errorLogs table for Convex failures, /admin/health
Testing vitest + convex-test (unit), Playwright (e2e), GitHub Actions CI

Local development

npm install
npm run dev        # runs `next dev` + `convex dev` in parallel

First run: convex dev provisions a dev deployment and writes .env.local. Copy .env.local.example for the frontend vars, and set Convex-side vars with npx convex env set KEY value (see the env table below). There are two deployments — dev and prod — and each holds its own env vars.

Bootstrap an admin (after signing up once through the UI):

npx convex run users:setRoleInternal '{"email":"you@example.com","role":"admin"}'

Seeds: eventCategories:seed (21 default categories, admin-gated), metrics:seedMockMetrics / attribution:seedMockAttribution (demo dashboards — purge later with metrics:purgeMockMetrics), testing:seed (e2e fixtures, requires ALLOW_TEST_SEED=true).

Testing

npm test                # unit: reserve money-paths, registration derivation,
                        # event mutations, slot invariants, claims (convex-test)
npm run test:e2e:ui     # Playwright with the step-through UI (watch the browser)
npm run test:e2e:headed # full visible-browser run

E2E runs against its own Convex deployment (dev/e2e), provisioned once and reused (ADR-035):

npm run e2e:up       # create/push dev/e2e — the test:e2e scripts do this for you
npm run test:e2e
npm run e2e:status   # which deployment, and whether the e2e flags are set

Never set TEST_STATIC_OTP or ALLOW_TEST_SEED on your dev deployment. While TEST_STATIC_OTP is set, every sign-in code is a published constant and sign-in is refused for any address that is not @resend.dev; the suite also deletes rows to rebuild its fixture world. The suite now refuses to run against whatever .env.local names, for exactly that reason. (DEV_OTP_LOG=true on your dev deployment is unrelated and recommended — it prints the real code to the Convex log so you can sign in without a verified sending domain.)

Deployments

Deployment Type What it's for
production prod the real thing
staging prod pre-prod mirror; prod semantics (server errors hidden from clients, admin-only edits)
local:… / dev/<you> dev what npm run dev uses. Named in .env.local
dev/e2e dev the Playwright suite owns it: wiped and reseeded every run. Addressed by a scoped deploy key in .env.e2e.local, never by .env.local
preview/<branch> preview per-branch, auto-expiring

CI (.github/workflows/): ci.yml runs lint/typecheck/unit on every push; e2e.yml runs nightly + on demand against an anonymous local Convex backend it provisions per run (so it needs no dev/e2e); backup.yml exports the prod deployment nightly (30-day artifacts).

Deploy

Vercel builds run npx convex deploy --cmd 'npm run build' (see vercel.json), so functions/schema and frontend ship atomically — set CONVEX_DEPLOY_KEY (production deploy key) in the Vercel env, scoped to Production only.

Three environments (ADR-026): main → Vercel Production → Convex prod; any PR → Vercel Preview → Convex staging, which is where non-technical co-founders acceptance-test a feature before it merges. Staging is a long-lived deployment (npx convex deployment create staging --type prod), not a per-PR one, so it keeps its data — which is only safe with one PR open at a time. Full setup, the two variables that silently break isolation, and the prod-vs-staging env matrix: documentation/RUNBOOK.md → Environments.

App map

app/
  (user)/            public storefront: experiences catalog + detail, calendar,
                     blog, reserve/[slotId] (+ confirmed), story/[reservationId],
                     account, ambassador/[slotId], preferences (onboarding)
  admin/             console: events (board/table + editor), curate wizard,
                     reservations, markets, categories, blog, stories,
                     campaigns, journeys, audience, discovery, crawler, logs,
                     usage, kpi, attribution, users
  wiki/              internal AI wiki ("Data Brain", noindexed, auth-gated)
  auth/              OTP / Google / legacy password sign-in
  api/               ics/[reservationId], out/[eventId] (tracked partner
                     redirect), unsubscribe (RFC 8058)

Convex map

  • Storefront: events, experiences (public projections), slots, reservations (internal + external registrationType), waitlist, stories, ambassadors, adminEvents (aggregates)
  • Pipeline (see documentation/scraping-pipeline.md): marketSources, eventDiscoveryJobs, crawlQueue, actions/* (discover, crawl, extract, validate), human gates in /admin/curate + /admin/events
  • Marketing: campaigns, emailCampaigns + campaignWorker, journeys, audience, metaAds, marketingImport
  • Analytics: usage (first-party events + rollup + dashboard), computedMetrics (weekly KPI cron), metrics, attribution
  • Platform: auth, users, emails, errorLogs, retention (nightly purges), migrations (@convex-dev/migrations ledger), crons (9 jobs), lib/ (auth, limiter, registration, slots, reportError, …)

Environment variables

Var Where Purpose
CONVEX_DEPLOYMENT, NEXT_PUBLIC_CONVEX_URL .env.local only written by convex dev. NEXT_PUBLIC_CONVEX_URL must not exist in Vercel — convex deploy injects the right one per build, and a stored value overrides it, letting a preview frontend read the production backend (and sending staging analytics to prod via lib/usage.ts)
NEXT_PUBLIC_CONVEX_SITE_URL .env.local / Vercel .convex.site origin for HTTP actions (usage ingest)
NEXT_PUBLIC_SITE_URL Vercel canonical origin (sitemap, JSON-LD, .ics)
NEXT_PUBLIC_META_PIXEL_ID Vercel Meta browser pixel (ADR-013)
NEXT_PUBLIC_SENTRY_DSN, SENTRY_ORG, SENTRY_PROJECT, SENTRY_AUTH_TOKEN Vercel Sentry (ADR-014); no-op unset
SITE_URL convex env origin used inside emails
OPENAI_API_KEY, OPENAI_MARKETING_MODEL, OPENAI_WIKI_MODEL convex env LLM calls
PERPLEXITY_API_KEY, SERPER_API_KEY convex env discovery
RESEND_API_KEY, RESEND_FROM, RESEND_MARKETING_FROM, RESEND_TEST_MODE, RESEND_WEBHOOK_SECRET convex env email (ADR-008/012)
UNSUBSCRIBE_SECRET, MARKETING_POSTAL_ADDRESS convex env one-click unsubscribe + CAN-SPAM
AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET convex env Google SSO
META_ACCESS_TOKEN, META_PIXEL_ID, META_AD_ACCOUNT_ID, META_PAGE_ID, META_TEST_EVENT_CODE convex env Conversions API + insights import
DEV_OTP_LOG convex env (dev only) prints the real sign-in code to the Convex log — never on prod
TEST_STATIC_OTP, ALLOW_TEST_SEED convex env (e2e deployment only) set by the Playwright run and removed by its teardown. Never set by hand: TEST_STATIC_OTP blocks human sign-in on whatever deployment carries it (ADR-035)
EMAIL_STUB convex env (test deployments only) "true" routes every message into a stubbedEmails row instead of Resend, so a test run sends nothing while the send paths still run. Never on prod or staging — it swallows every member's mail (ADR-035)
CONVEX_DEPLOY_KEY (in .env.e2e.local) generated, gitignored scoped to dev/e2e; how the suite reaches it without touching .env.local
CONVEX_DEPLOY_KEY Vercel build-time Convex deploy. Two entries: the production key scoped to Production only, and the staging key scoped to Preview only. A production key visible to Preview makes every PR build fail (by design) — see RUNBOOK → Environments
SCHEDULED_CRAWLS convex env (staging) set to off — gates crawl-all-markets and discover-sources, the only two crons that spend on external APIs. Unset everywhere else
DEV_EMAIL_REDIRECT convex env (dev + staging) every outbound message goes to this one inbox. Never on prod — it silently swallows every member's mail
CONVEX_PROD_DEPLOY_KEY GitHub secret nightly backup export

Documentation

  • documentation/DECISIONS.md — ADR log (the "why"); newest first
  • documentation/RUNBOOK.md — deploy, rollback, backups, key rotation, crons
  • documentation/scraping-pipeline.md — pipeline behavior spec (versioned)
  • documentation/browse-modes.md — the four browsing modes and every grouping rule behind them, with data-coverage baselines (versioned). Start here to change what a collection contains or what "a weekday morning" means.
  • documentation/BRAND.md — voice/copy reference
  • documentation/CHANGELOG.md — narrative history of big batches
  • documentation/TODO.md — intentionally deferred work
  • CLAUDE.md — agent working conventions

About

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages