ghostguild-org/docs/LAUNCH_READINESS.md
Jennie Robinson Faber 33ba082b82
Some checks failed
Test / vitest (push) Successful in 11m7s
Test / playwright (push) Failing after 9m38s
Test / visual (push) Failing after 9m31s
Test / Notify on failure (push) Successful in 2s
docs: consolidate open issues into BACKLOG.md
Single source of truth for every open issue across the codebase. Pulls
from LAUNCH_READINESS.md (post-launch sections), TODO.md (deferred
features + simplify follow-ups + wave-Slack pilot), and a fresh sweep
of in-code TODO/FIXME comments.

LAUNCH_READINESS.md now keeps only the pre-cutover deploy checklist and
points to BACKLOG.md for everything else. Cutover note corrected — it
has not happened yet.

Force-added BACKLOG.md despite the /docs/ gitignore rule because
LAUNCH_READINESS.md is tracked and now references it.
2026-04-30 15:37:26 +01:00

8.3 KiB

Launch Readiness

Status as of 2026-04-30. Cutover has not happened yet. Code is on local main; deploy steps below still need to execute.

Pre-cutover deploy checklist is the live content on this page. Everything else (post-launch work, bylaws decoupling, deferred features, simplify follow-ups, a11y) lives in BACKLOG.md. Completed launch-blocker items are archived — see ~/.claude/projects/-Users-jennie-Sites-ghostguild-org/memory/project_launch_readiness_archive.md.


Current state

  • Vitest snapshot 2026-04-25 ~18:23 local: 703 passing / 8 failing / 2 skipped (713 total). The previously-flagged 6 helcim-payment failures are now green. The 8 current failures are in tests/server/api/auth-verify.test.js and tests/server/api/cancel-subscription.smoke.test.js, both belonging to in-flight Phase 5 fixes (#10 and #9) being landed by parallel impl subagents — they will resolve as those branches merge.
  • All launch code is on local main: Helcim plan consolidation, contribution-amount redesign, cadence UX unification, and receipts Phase 1. Not pushed — site is not on Netlify yet.
  • Helcim plan consolidation migration ran against prod 2026-04-18 (Monthly plan id 50302, Annual plan id 50303).
  • Contribution-amount migration has NOT yet been run against prod.
  • Receipts Phase 1 code is shipped; remaining work is deploy-time only (see Deploy checklist).

P0 — Must fix before launch

None outstanding.


P1 — Strongly preferred before launch

None outstanding.


Deploy checklist

Applies when the app is deployed to Dokploy on Hetzner. Build is via the in-repo Dockerfile (node:20-alpine, runs node .output/server/index.mjs on port 3000); Dokploy autodetects it. Traefik (Dokploy's reverse proxy) handles SSL; oidc-provider.ts:194 and the rate-limit middleware already trust X-Forwarded-Proto / X-Forwarded-For.

One-time host setup

  • Provision the Dokploy app pointing at this repo. Build context: repo root. Default Dockerfile. Container port: 3000.
  • Set env vars in the Dokploy UI (full list below). The validate-env.js Nitro plugin fails fast at boot if MONGODB_URI / JWT_SECRET / RESEND_API_KEY / HELCIM_API_TOKEN are missing — container refuses to start, so misconfig surfaces immediately in logs.
  • BASE_URL must exactly match the public origin (e.g. https://ghostguild.org, no trailing slash). The /api/helcim/customer origin check at server/api/helcim/customer.post.js:11-15 does exact-match comparison against the Origin header — if BASE_URL is wrong or unset, signup 403s.
  • NODE_ENV=production must be set. Without it: Secure cookie flag, HSTS, and CSP all silently no-op.
  • Add a Dokploy Scheduled Task for daily reconciliation. Command:
    curl -fsS -X POST "$BASE_URL/api/internal/reconcile-payments" -H "X-Reconcile-Token: $NUXT_RECONCILE_TOKEN"
    
    Schedule: 0 4 * * * (or any time of day). The Nitro route does the heavy lifting (Mongo iteration, Helcim API, retries) — the scheduler just wakes it up.

Cutover

  • Push local main to origin/main.
  • Run node scripts/migrate-contribution-amount.cjs --apply against prod Mongo BEFORE the new code serves traffic. Idempotent; dry-run on local counted 34 members. Requires MONGODB_URI in env. The script writes contributionAmount (Number) derived from existing contributionTier (String) on every Member doc; the old field is left intact for a window.
  • Set NUXT_HELCIM_MONTHLY_PLAN_ID=50302 in Dokploy env.
  • Set NUXT_HELCIM_ANNUAL_PLAN_ID=50303 in Dokploy env.
  • Set NUXT_RECONCILE_TOKEN to any 32+ char random string. Shared secret between the Dokploy scheduled task and /api/internal/reconcile-payments.
  • Deploy.
  • Run node scripts/reconcile-helcim-payments.mjs --apply against prod Mongo AFTER the new code serves traffic to backfill Payment records for pre-existing members. Idempotent (unique helcimTransactionId); the daily Dokploy cron picks it up from there.
  • Prod audit for pre-fix series-pass bypass registrations. Fixed in f34b062 + 4e1888a (2026-04-20). Before that, child events of pass-only series (tickets.requiresSeriesTicket=true && tickets.allowIndividualEventTickets=false) accepted drop-in registrations from non-pass-holders. For every such series, list its child-event registrations where the registrant is not in the parent series' pass-holder list, filter to registeredAt < 2026-04-20, and decide per-case: grandfather (keep + notify), refund + unregister, or silently unregister. Local Mongo was scrubbed of 2 such rows on 2026-04-20; prod was intentionally untouched.
  • Helcim dashboard: disable the default payment-confirmation email for plans 50302 + 50303. We send our own CRA-safe confirmation via Resend (server/emails/paymentConfirmation.js) triggered from upsertPaymentFromHelcim; leaving Helcim's default on = duplicate emails.
  • Run one real test charge against the deployed app and verify (a) a Payment doc in Mongo with amount, paymentType, status: 'success', and (b) exactly one CRA-compliant confirmation email (charity name + "not an official donation receipt" disclaimer; no banned assertive phrasing).
  • Rotate HELCIM_API_TOKEN in the Helcim merchant portal and update the Dokploy env var. The token was previously exposed in window.__NUXT__ payload until commit 208638e.
  • Trigger the daily reconcile task once manually in Dokploy to confirm scheduled task + token are wired correctly. Expect a [reconcile] done {...} log line.

Env vars required in Dokploy (reference):

  • NODE_ENV=production
  • BASE_URL (exact public origin, no trailing slash)
  • MONGODB_URI
  • JWT_SECRET (or NUXT_JWT_SECRET — the NUXT_ variant wins)
  • RESEND_API_KEY
  • HELCIM_API_TOKEN
  • NUXT_HELCIM_MONTHLY_PLAN_ID=50302
  • NUXT_HELCIM_ANNUAL_PLAN_ID=50303
  • NUXT_PUBLIC_HELCIM_PORTAL_URL
  • NUXT_RECONCILE_TOKEN (32+ char random string)
  • SLACK_BOT_TOKEN
  • OIDC_COOKIE_SECRET

Fixed 2026-04-25

Day-of-launch security and correctness audit. All commit shas TBD until Phase 5.

CRITICAL (security)

  • Fix #1HELCIM_API_TOKEN removed from public runtime config + dead useHelcim.js deleted. Token must be rotated post-deploy (was previously exposed via window.__NUXT__).
  • Fix #2/api/helcim/customer gated with origin check + per-IP/email rate limit + magic-link email verification (replaces unauthenticated setAuthCookie).
  • Fix #3/api/events/[id]/payment deleted (dead code with auth bypass). processHelcimPayment stub + eventPaymentSchema removed.
  • Fix #4/api/helcim/initialize-payment re-derives ticket amount server-side via calculateTicketPrice; new series_ticket metadata type.
  • Fix #5/api/helcim/customer upgrades existing status:guest members in place rather than rejecting with 409.

HIGH (correctness)

  • Fix #6 — Recurring reconciliation: Netlify scheduled function calls /api/internal/reconcile-payments daily. Requires NUXT_RECONCILE_TOKEN env var.
  • Fix #7validateBeforeSave: false added to event subdoc saves (waitlist endpoints) to dodge legacy location validators.
  • Fix #8 — Series-pass purchase always creates a guest Member when caller is unauthenticated, mirroring event-ticket flow.
  • Fix #9cancel-subscription leaves status active (per ratified bylaws); adds lastCancelledAt audit field.
  • Fix #10/api/auth/verify uses validateBody with .strict() Zod schema.
  • Fix #11 — Added 8 vitest cases for cancel-subscription.post.js (was uncovered).

Side-quests

  • Visual audit Phase 4 changes (events/series surface)
  • Per-fix branch verification: see docs/superpowers/specs/2026-04-25-fix-*.md

Manual browser tests still needed

None outstanding. All launch-blocking flows verified via local dev or cloudflared tunnel with real Helcim test card + real email (see archive for the full log). The one remaining browser verification is the staging test charge bundled into the Deploy checklist above.


Post-launch & deferred work

Bylaws decoupling, post-launch a11y, ASVS Phase 4, deferred features, simplify-pass follow-ups, known gotchas, wave-Slack pilot follow-ups — everything that isn't a deploy step has moved to BACKLOG.md.