Launch-day playbook — v1 go-live
Owner: operator (Kristerpher) + sre-agent (EyeTok on-call) Created: 2026-05-30 UTC Go/no-go gate: #3151 Parent epics: #3126 (Pre-launch readiness), #94 (v1.0 Production Release) Last reviewed: 2026-05-30 UTC
This runbook is the operator's step-by-step guide for the actual launch day. It does not define what "ready" means — that is #3151. It assumes every checkbox in #3151 is confirmed before Section 3 (T-0) is executed.
Section 1 — T-7 day pre-launch verification
Run this section 7 days before the planned launch date. Every item must be "confirmed done" before the T-0 sequence starts. If any item is unresolved on T-7, escalate immediately — some items (attorney, FinCEN) have external lead times that cannot be compressed.
1A — Secrets + infrastructure
- [ ]
WAITLIST_OPT_IN_SIGNING_KEYminted + verified on prod (#3127)
bash
heroku config:get WAITLIST_OPT_IN_SIGNING_KEY --app raxx-api-prod | wc -c
# Expect > 5
- [ ]
POSTMARK_TEMPLATE_REMINDER_2FAset on prod (#3128)
bash
heroku config:get POSTMARK_TEMPLATE_REMINDER_2FA --app raxx-api-prod | wc -c
# Expect > 5
- [ ]
POSTMARK_TEMPLATE_WAITLIST_CONFIRMATIONset on prod (#3129)
bash
heroku config:get POSTMARK_TEMPLATE_WAITLIST_CONFIRMATION --app raxx-api-prod | wc -c
# Expect > 5
- [ ]
FREESCOUT_API_KEYwired as GH Actions secret + smoke passing (#3130)
bash
gh secret list --repo raxx-app/TradeMasterAPI | grep FREESCOUT_API_KEY
# Expect: FREESCOUT_API_KEY listed with a recent Updated date
- [ ]
GPG_BACKUP_PUBLIC_KEYset + BCP daily vault snapshot passing (#3131)
bash
gh secret list --repo raxx-app/TradeMasterAPI | grep GPG_BACKUP_PUBLIC_KEY
# Expect: present. If missing, follow docs/ops/runbooks/bcp-smoke-monthly-review.md §Operator action item 1.
gh run list --workflow bcp-vault-snapshot-daily.yml --limit 1 --json conclusion -q '.[0].conclusion'
# Expect: success
- [ ]
RAXX_DEV_BOTcredentials in vault, token mint verified (#3132)
bash
# From docs/ops/runbooks/github-app-credentials.md
python3 scripts/agents/mint_github_token.py --bot raxx-dev-bot --export | grep -c TOKEN
# Expect: 1 (token line present, no errors)
- [ ]
RAXX_PM_BOTcredentials in vault, token mint verified (#3133)
bash
python3 scripts/agents/mint_github_token.py --bot raxx-pm-bot --export | grep -c TOKEN
# Expect: 1
- [ ]
STAGING_DATABASE_URLGH Environment secret set (#3134)
bash
gh api repos/raxx-app/TradeMasterAPI/environments/staging/secrets \
| python3 -c "import sys,json; [print(s['name']) for s in json.load(sys.stdin)['secrets']]" \
| grep STAGING_DATABASE_URL
# Expect: STAGING_DATABASE_URL
- [ ] Sentry alert rule for
skipped_no_postmark_tokenactive (#3135)
bash
python3 scripts/ops/create_sentry_alert_rule.py --dry-run
# Expect: existing rule ID printed, exit 0 — see docs/ops/runbooks/sentry-alert-rules.md
- [ ] CF Access removed from
raxx.appandapi.raxx.app(#1025)
bash
curl -sI https://raxx.app/ | head -3
# Expect: HTTP/2 200 — NOT a 302 to cloudflareaccess.com
curl -sI https://api.raxx.app/health | head -3
# Expect: HTTP/2 200
If either still returns 302: follow docs/ops/runbooks/cf-access.md.
1B — Legal + operator actions
All items below are operator-confirmed manual steps. There is no automated check. Record confirmation in a comment on #3151.
- [ ] Securities attorney engaged + §202(a)(11) opinion received (#3141)
- [ ] Hero copy + disclaimer cluster cleared by attorney (#3141, #2865)
- [ ] IP assignment executed (founder → MooseQuest LLC) (#3142)
- [ ] FinCEN BOI report filed — MooseQuest LLC (#3143) — hard deadline 2026-06-21 UTC
- [ ] Privacy Policy + Terms of Service live on
getraxx.com(#3149) - [ ] PA fictitious-name (DBA "Raxx") registered (#3145)
- [ ] PA SaaS sales-tax posture confirmed by CPA (#3146)
1C — Quality + stability
- [ ] FreeScout CVE re-verified — no open CVEs against the running version (#3137)
bash
heroku run --app raxx-freescout-prod -- php artisan raxx:freescout-version 2>/dev/null \
|| curl -s https://tickets.raxx.app/admin | grep -i "FreeScout"
# Record version. Cross-check against https://github.com/freescout-help-desk/freescout/releases
Full procedure: docs/ops/runbooks/freescout.md §Version + CVE audit.
- [ ] FreeScout admin UI configured (logo, favicon, APP_NAME, footer, color) (#3138)
Manual: log into https://tickets.raxx.app/admin and confirm each setting.
- [ ] FreeScout 5-state ticket workflow implemented (choice of A, A+B, or C) (#3139)
Confirm option was selected and implemented per operator comment on #3139. (#3139 closed.)
- [ ] FreeScout 1.8.215+ confirmed
bash
curl -s "https://tickets.raxx.app/admin" | grep -i "version\|1\.8\." | head -3
- [ ] BCP smoke green for 2+ consecutive months OR expedited 1-week drill complete with operator sign-off (#3131, #3140)
bash
gh run list --workflow bcp-smoke-monthly.yml --limit 3 --json conclusion,createdAt \
| python3 -m json.tool
# Expect: at least 2 entries with "conclusion": "success"
# If fewer than 2 successful runs: confirm expedited drill outcome with operator sign-off in #3140.
- [ ] BCP restore drill completed — Postgres + FreeScout — PASS result (#3140)
bash
gh run list --workflow bcp-smoke-monthly.yml --limit 1 --json conclusion -q '.[0].conclusion'
# Expect: success
# Also check operator sign-off comment on #3140.
- [ ] MBT engine 7-day dogfood soak completed — SOAK PASS, max drift < 2%/day (#3148)
Confirmed done — #3148 is CLOSED. Verify no follow-on bug cards are open:
bash
gh issue list --label "area:mbt,bug" --state open
# Expect: 0 results, or all known minor items are accepted by operator
- [ ] Order Ticket V2 help-mode copy reviewed + approved (#3147)
Confirm operator sign-off comment on #3147.
- [ ] Mobile breakpoint sweep at 390px passed — Order Ticket + Options Chain + AppHeader (#3150)
Confirmed done — #3150 is CLOSED.
- [ ] Sentry green for 72 consecutive hours (no new P0/P1 events)
bash
# Check via Sentry web UI — filter project=trademaster-raptor, environment=production,
# last 72h, sort by first-seen desc. No P0/P1 (unresolved CRITICAL/ERROR tagged as launch-blocker).
# URL: https://sentry.io/organizations/moosequest/issues/?project=trademaster-raptor
- [ ] All v1 PRs merged,
mainis green, no deploy freeze active
bash
curl -sI https://console.raxx.app/api/internal/deploy-freeze | grep -i "x-deploy-freeze"
# If header shows frozen: release via https://console.raxx.app/console/deploy-freeze
gh run list --workflow deploy-heroku.yml --limit 1 --json conclusion,headBranch \
| python3 -m json.tool
# Expect: conclusion=success, headBranch=main (or most recent prod deploy)
Section 2 — T-24h checks
Run this section the evening before launch day.
- [ ] Heroku release counts stable on
raxx-api-prodandraxx-console-prod
bash
heroku releases --app raxx-api-prod --num 5
heroku releases --app raxx-console-prod --num 5
# Expect: no failed releases in the last 5 entries
- [ ] Synthetic gate green in the last 24 hours
bash
gh run list --workflow synthetic-gate.yml --limit 5 --json conclusion,createdAt \
| python3 -m json.tool
# Expect: all conclusions=success within the last 24h
Any failure: follow docs/ops/runbooks/synthetic-check-diagnosis.md.
- [ ] No P1 GH issues open
bash
gh issue list --label "priority:critical" --state open --limit 20
# Expect: 0 or 0 that are NOT already operator-accepted
- [ ] Postgres backup exists from the last 24 hours
bash
heroku pg:backups --app raxx-api-prod
# Most recent "Completed" backup should be < 24h old
If no recent backup: heroku pg:backups:capture --app raxx-api-prod
- [ ] Sentry error rate normal — no new issues in last 24h
bash
# Sentry web UI: https://sentry.io/organizations/moosequest/issues/?project=trademaster-raptor
# Filter: last 24h, sort by first-seen. No unexpected new issue classes.
- [ ] Deploy freeze OFF
bash
curl -sI https://console.raxx.app/api/internal/deploy-freeze | grep -i "x-deploy-freeze"
# Expect: header absent OR value=false
- [ ] Signup smoke passing on prod
bash
RAXX_SMOKE_ALLOW_PROD=1 make smoke-signup-prod
# Expect: SMOKE VERDICT: PASS
# Full runbook: docs/ops/runbooks/signup-smoke.md
Section 3 — T-0 launch trigger sequence
Execute in order. Each step has a verification. Do not proceed to the next step until the current step is confirmed.
Before starting: announce in ops Slack channel that the launch sequence is live.
Step 1 — Remove noindex from docs.raxx.app and getraxx.com
Follow docs/ops/runbooks/launch-day-cutover.md §1–§3 exactly:
- In
frontend/docs/_headers, changeX-Robots-Tag: noindex, nofollowtoX-Robots-Tag: index, follow. - In
scripts/build-customer-docs.py, changecontent="noindex, nofollow"tocontent="index, follow". - In
frontend/getraxx-landing/public/_headers, changeX-Robots-Tag: noindex, nofollow, noarchive, nosnippettoX-Robots-Tag: index, follow. - Open one PR for all three changes, merge to main.
Verification (after CI deploy — ~2–5 min):
curl -sI https://docs.raxx.app/ | grep -i x-robots
# Expect: x-robots-tag: index, follow
curl -sI https://getraxx.com/ | grep -i x-robots
# Expect: x-robots-tag: index, follow
Step 2 — Remove CF Access gate from getraxx.com (if not already removed)
Follow docs/ops/runbooks/getraxx-launch-day-cf-access-removal.md exactly:
# Source credentials
export CLOUDFLARE_API_TOKEN=$(infisical secrets get CF_ACCESS_MGMT \
--path /MooseQuest/cloudflare/ --plain)
export TF_VAR_cf_access_account_id=$(infisical secrets get CF_ACCESS_ACCOUNT_ID_MOOSEQUEST \
--path /MooseQuest/cloudflare/ --plain)
# Plan — confirm 3 resources to destroy
cd terraform/modules/cf-access-getraxx
terraform plan -destroy
# Destroy
terraform destroy
Wait 30 seconds for CF propagation.
Verification:
curl -sI https://getraxx.com/ | head -3
# Expect: HTTP/2 200 (NOT 302 to cloudflareaccess.com)
curl -sI https://www.getraxx.com/ | head -3
# Expect: HTTP/2 301 (www → apex redirect; this is correct)
If still 302 after 90 seconds: follow the emergency rollback procedure in
docs/ops/runbooks/getraxx-launch-day-cf-access-removal.md §Emergency rollback.
Step 3 — Flip waitlist flag open
heroku config:set FLAG_WAITLIST_DATASTORE=1 --app raxx-api-prod >/dev/null 2>&1
heroku config:set FLAG_GETRAXX_WAITLIST=1 --app raxx-api-prod >/dev/null 2>&1
Verification:
heroku config:get FLAG_WAITLIST_DATASTORE --app raxx-api-prod
# Expect: 1
heroku config:get FLAG_GETRAXX_WAITLIST --app raxx-api-prod
# Expect: 1
Test from a fresh incognito browser: visit https://getraxx.com/ and confirm the
waitlist form is visible and interactive.
Step 4 — Confirm console flag reconciler shows no drift
# Flag reconciler runs on a 5-min cycle. After the config:set in Step 3,
# wait 5 minutes then verify no drift alert fires.
# Check: https://console.raxx.app/console/flags
# Expect: all flags show "synced" (not "drift"). No operator-decision modal.
Full procedure: docs/ops/feature-flags-runbook.md.
Step 5 — Trigger waitlist email blast (operator action)
This is a manual operator step. Postmark dashboard or the waitlist blast script.
Prerequisite: POSTMARK_TEMPLATE_WAITLIST_CONFIRMATION confirmed set (Section 1A, item 3).
# Verify Postmark token is active before blast
heroku run --app raxx-api-prod \
python -c "from api.services.postmark_client import test_postmark_token; test_postmark_token()"
# Expect: Postmark token valid
After blast: monitor ops@raxx.app for any bounce / delivery failure notifications.
Step 6 — Post-launch announcement (operator action)
Operator-owned. Raxx is live when Step 1–5 are complete. Communicate externally per the operator's plan. The sre-agent does not own external-facing communications.
Section 4 — T+1h smoke
Run from a fresh anonymous browser session (incognito, no stored credentials):
- [ ]
https://getraxx.com/loads — HTTP 200, no CF Access gate, waitlist form visible - [ ] Waitlist signup form → submit → confirmation email arrives at the test inbox within 5 minutes
- [ ] Signup flow on
https://raxx.app/signup→ passkey enrollment → land on/dashboard
bash
# Backend validation via signup smoke (parallel to browser test)
RAXX_SMOKE_ALLOW_PROD=1 make smoke-signup-prod
# Expect: SMOKE VERDICT: PASS
- [ ]
/strategies→ create new strategy → save → strategy appears in list - [ ]
/trading→ MBT flow visible and accessible (paper trade mode) - [ ]
/options→ chain loads → contract clickable → action menu shows → ticket prefills - [ ]
https://tickets.raxx.app/→ anonymous ticket submission form visible
bash
curl -sI https://tickets.raxx.app/ | head -3
# Expect: HTTP/2 200
- [ ] Console flag reconciler: no drift modal on
https://console.raxx.app/console/flags
bash
# Drift modal fires when Heroku config vs YAML is out of sync.
# After Steps 3–4 above, all flags should be synced.
- [ ] Sentry: no CRITICAL events in the first hour
bash
# Sentry web UI: https://sentry.io/organizations/moosequest/issues/?project=trademaster-raptor
# Filter: last 1h, level=critical. Expect: 0 results.
- [ ] Heroku metrics: no memory spike or H14 errors in first hour
bash
heroku logs --app raxx-api-prod --tail --num 50 | grep -E "H1[0-9]|Error R1[0-9]"
# Expect: no H-series router errors
Section 5 — T+24h posture flip
Digest posture switches to per-event the moment real customers exist. Execute these changes within 24 hours of the first customer signup.
Sentry alert routing: digest → immediate
Sentry is already configured to email ops@raxx.app on skipped_no_postmark_token
(#3135). For general CRITICAL/ERROR issues, verify the alert frequency is set to
immediate (not digest):
# Sentry web UI: https://sentry.io/organizations/moosequest/alerts/rules/
# For each rule in trademaster-raptor: set frequency to "Immediately" (not "Once per day").
# See docs/ops/runbooks/sentry-alert-rules.md §Adding new alert rules
Synthetic gate failures: immediate Slack notification
The synthetic gate already posts to #raxx-ops-alert-sev2 on every failure. No change
needed if the channel is monitored. Confirm the webhook is active:
gh secret list --repo raxx-app/TradeMasterAPI | grep SLACK_WEBHOOK_URL
# Expect: present with a recent Updated date
Heroku rate-limit alert threshold
Current ceiling: 9,000 requests/hr (raised 2026-05-14). Alert threshold: 6,000/hr (66% ceiling). If the request rate approaches this level, investigate before it becomes a 429 incident.
heroku logs --app raxx-api-prod --num 1000 | grep "router" | wc -l
# Rough requests-per-log-page estimate; use Heroku metrics dashboard for precise rate
Section 6 — Rollback procedure
If any step in Section 3 breaks and the launch must be aborted:
6A — Restore noindex headers
Revert the docs + getraxx headers PR (one-line revert per surface):
# In frontend/docs/_headers
X-Robots-Tag: noindex, nofollow
# In frontend/getraxx-landing/public/_headers
X-Robots-Tag: noindex, nofollow, noarchive, nosnippet
Open a revert PR to main, merge immediately. CI redeploys in ~2–5 min.
Verification:
curl -sI https://docs.raxx.app/ | grep -i x-robots
# Expect: x-robots-tag: noindex, nofollow
curl -sI https://getraxx.com/ | grep -i x-robots
# Expect: x-robots-tag: noindex, nofollow, noarchive, nosnippet
6B — Re-apply CF Access gate on getraxx.com
cd terraform/modules/cf-access-getraxx
terraform apply
# Recreates the access application and policy from tfvars.
Expected: ~30s for CF propagation. Verify:
curl -sI https://getraxx.com/ | head -3
# Expect: HTTP/2 302 to cloudflareaccess.com
If terraform state has drifted: follow docs/ops/runbooks/terraform-cf-access-state-imports.md.
6C — Flip launch-cohort flags back
heroku config:set FLAG_WAITLIST_DATASTORE=0 --app raxx-api-prod >/dev/null 2>&1
heroku config:set FLAG_GETRAXX_WAITLIST=0 --app raxx-api-prod >/dev/null 2>&1
Verification:
heroku config:get FLAG_WAITLIST_DATASTORE --app raxx-api-prod
# Expect: 0 (or absent)
6D — Incident posture
- Post a one-line update in the ops Slack channel: reason + estimated hold time.
- Open a SEV1 GH issue with the timeline: first signal → now, steps attempted, current state.
- Do not re-attempt T-0 until all Section 1 items are re-verified.
Rollback SLA: 6B (CF Access) should be complete within 10 minutes of the decision to abort. 6A (noindex) CI deploy takes an additional 2–5 minutes.
Section 7 — T+1 week soak
Check these items 7 days after T-0.
7A — MBT drift
# Open #3148 or the drift-orchestrator workflow logs
gh run list --workflow drift-orchestrator-cron.yml --limit 10 --json conclusion,createdAt \
| python3 -m json.tool
# Expect: all conclusions=success; no drift > 2%/day in any run
7B — Sentry top issues by user impact
# Sentry web UI: https://sentry.io/organizations/moosequest/issues/?project=trademaster-raptor
# Sort by: Users (distinct users affected, not event count) — last 7 days
# Expected: no P0/P1 issues in top 5; any new issue classes triaged and assigned
7C — Postmark delivery health
- Bounce rate < 2% for the waitlist confirmation + welcome emails
- No spam complaint spikes
- Check: Postmark dashboard →
https://account.postmarkapp.com/→ Message Streams
7D — First customer support queue
curl -s "https://tickets.raxx.app/api/v1/conversations" \
-H "X-FreeScout-API-Key: $FREESCOUT_API_KEY" \
| python3 -c "import sys,json; data=json.load(sys.stdin); print('open:', data.get('meta',{}).get('pagination',{}).get('count','?'))"
# Review open ticket trend: is the volume expected? Any patterns indicating a UX gap?
7E — Heroku cost check
heroku apps:info --app raxx-api-prod | grep -i "dyno\|addons"
# Review the current dyno count and add-on plan against the cost model in
# docs/architecture/heroku-cost-model.md (if present)
# Expect: no unexpected tier upgrades from traffic spikes
7F — Signup funnel
- Waitlist signups received (from Postgres
waitlist_emailstable) - Signups converted to passkey enrollments
- Any signups that bounced at the confirmation email step
heroku run --app raxx-api-prod -- python -c "
import sys; sys.path.insert(0, '/app'); import os; os.chdir('/app')
from backend_v2.db.engine import get_engine
from sqlalchemy import text
with get_engine().connect() as conn:
total = conn.execute(text('SELECT COUNT(*) FROM waitlist_emails')).scalar()
confirmed = conn.execute(text(\"SELECT COUNT(*) FROM waitlist_emails WHERE confirmed = true\")).scalar()
print(f'total={total} confirmed={confirmed} conversion_rate={confirmed/max(total,1)*100:.1f}%')
"
7G — Stripe first invoices (when billing is wired)
Not applicable at v1 if Stripe is not yet live. Verify no accidental Stripe charges have fired in test mode by checking the Stripe dashboard.
7H — Churn signal
Any signup that enrolled a passkey but has not returned to /dashboard within 7 days
is a potential early-churn signal. This is informational — pass to the operator for
product review, not an SRE action item.
References
- Go/no-go gate: #3151
- Pre-launch readiness epic: #3126
- v1.0 release epic: #94
- docs/antlers noindex + getraxx noindex cutover:
docs/ops/runbooks/launch-day-cutover.md - getraxx.com CF Access removal:
docs/ops/runbooks/getraxx-launch-day-cf-access-removal.md - CF Access runbook (raxx.app):
docs/ops/runbooks/cf-access.md - Signup smoke:
docs/ops/runbooks/signup-smoke.md - Synthetic check diagnosis:
docs/ops/runbooks/synthetic-check-diagnosis.md - Sentry alert rules:
docs/ops/runbooks/sentry-alert-rules.md - Deploy freeze:
docs/ops/runbooks/deploy-freeze.md - BCP smoke review:
docs/ops/runbooks/bcp-smoke-monthly-review.md - Flag reconciler:
docs/ops/feature-flags-runbook.md - EyeTok on-call severity routing:
docs/architecture/oncall-severity-routing.md - Heroku app names:
docs/architecture/heroku-app-names.md