Raxx · internal docs

internal · gated

Web surface posture — pre-launch lock-down

Author: 2026-04-28 surface-area review (Kristerpher + Claude) Status: living document — update when surfaces are added or postures change.

Principle

Pre-launch, default to least exposure. Operator surfaces (console, vault, internal docs) are CF Access gated. Customer-facing surfaces that are not yet ready for customers (Antlers app, customer docs, support portal) get a CF Access gate too — invite-only access via Cloudflare Zero Trust until GA. The marketing site (getraxx.com) is also CF Access gated pre-launch (operator decision 2026-05-11 UTC) — gate removes on launch day (2026-05-23 UTC) per docs/ops/runbooks/getraxx-launch-day-cf-access-removal.md. The company brand site (moosequest.net) stays public.

Heroku origin URLs (*.herokuapp.com) bypass Cloudflare entirely. The Cloudflare-origin guard middleware (#252 / PR #443 + console port) rejects any request that did not transit Cloudflare.

Surface matrix

Surface Audience Posture today Target posture Action
raxx.app Antlers customer app public, noindex CF Access (Zero Trust) until GA Operator: add CF Access policy on raxx-app Pages project
www.raxx.app redirect DNS NXDOMAIN redirect → raxx.app apex Operator: CNAME + page rule
api.raxx.app Antlers ↔ API CF Access CF Access until GA → public + auth-required at GA No change today
api-staging.raxx.app pre-launch testing CF Access ✓ + Heroku origin EXPOSED Same + origin guard ON Operator: enable FLAG_ENFORCE_CF_ORIGIN=true on raxx-api-staging
console.raxx.app operator only CF Access ✓ + Heroku origin EXPOSED Same + origin guard ON (this PR ports the middleware) Merge this PR + enable FLAG_ENFORCE_CF_ORIGIN=true on raxx-console-prod
vault.raxx.app bot identities + operator CF Access ✓ No change
getraxx.com marketing (pre-launch beta) CF Access gated (2026-05-11) public + WAF + rate limit at launch (2026-05-23) Remove gate: terraform destroy in terraform/modules/cf-access-getraxx/ — see docs/ops/runbooks/getraxx-launch-day-cf-access-removal.md
internal-docs.raxx.app internal docs + design (branded URL) CF Access ✓ (via #572) No change
raxx-mockups.pages.dev internal docs + design (origin, still gated) CF Access ✓ No change
raxx-app.pages.dev preview deploys CF Access ✓ No change
raxx-api-prod-…herokuapp.com should be unreachable partially exposed (502) unreachable Operator: enable FLAG_ENFORCE_CF_ORIGIN=true on raxx-api-prod (after staging soak)
raxx-api-staging-…herokuapp.com should be unreachable fully exposed (200) unreachable Operator: enable flag on raxx-api-staging
raxx-console-prod-…herokuapp.com should be unreachable fully exposed (200) unreachable Merge this PR + enable flag on raxx-console-prod
queue.raxx.app Queue identity/billing service EXPOSED — no CF proxy yet CF proxy + WAF rules + CfOriginGuard ON SRE: terraform apply in terraform/queue/ → confirm CF-RAY on /health → soak → FLAG_ENFORCE_QUEUE_CF_ORIGIN=true. Operator CF dashboard actions required before SRE step (see below)
queue-staging.raxx.app Queue staging EXPOSED CF proxy + WAF rules + CfOriginGuard ON Same as prod, staging first
raxx-queue-prod-….herokuapp.com should be unreachable fully exposed unreachable SRE: enable FLAG_ENFORCE_QUEUE_CF_ORIGIN=true after CF proxy soak
support.raxx.app customer support portal CF Pages raxx-support infra shell live (#653) public + indexable on /; app-level auth on /tickets/; robots.txt disallows /tickets/ #653 done (infra); card 4 = full portal UI; card 3 = API routes
docs.raxx.app customer docs doesn't exist public + indexable #423 (sub of #104)
status.raxx.app public status / health page CF Pages project raxx-status + holding page (#602) public, no CF Access, indexable #602 done; React app ships in #604
moosequest.net brand / studio public ✓ public, registrar locked #217
repo (raxx-app/TradeMasterAPI) engineering private ✓ No change

Operator runbook — ordered actions

1. Enable origin guard on staging API (start the soak)

heroku config:set FLAG_ENFORCE_CF_ORIGIN=true --app raxx-api-staging
heroku ps:restart --app raxx-api-staging

Verify after restart:

# Direct origin → 403 (the change worked)
curl -i https://raxx-api-staging-1a19fb3873b9.herokuapp.com/api/system/status
# CF-proxied → still 200 (CF Access protects this so you'll see the access redirect; that's correct)
curl -i https://api-staging.raxx.app/api/system/status

Soak ≥24 h. Watch heroku logs --app raxx-api-staging --tail | grep direct_origin_blocked for false positives.

2. Enable origin guard on prod API (after staging soak passes)

heroku config:set FLAG_ENFORCE_CF_ORIGIN=true --app raxx-api-prod
heroku ps:restart --app raxx-api-prod

Same verify pattern.

3. Enable origin guard on prod console (after this PR ships)

heroku config:set FLAG_ENFORCE_CF_ORIGIN=true --app raxx-console-prod
heroku ps:restart --app raxx-console-prod

Verify: curl https://raxx-console-prod-ff30a22abccb.herokuapp.com/health still returns 200 (allowlisted), but curl https://raxx-console-prod-ff30a22abccb.herokuapp.com/secrets now returns 403.

4. CF Access gate raxx.app (pre-launch)

Cloudflare Zero Trust → Access → Applications → + Add an application → Self-hosted

When you go GA, either delete the application or change the policy to "Allow everyone" (effectively no gate).

5. Fix getraxx.com DNS

Cloudflare DNS panel → confirm:

If the CF Pages project is missing, run wrangler pages project create getraxx then deploy via the existing pipeline.

6. Add www.raxx.app redirect

Cloudflare DNS panel:

7. Future surfaces

8. Queue surface — new controls (#3593, implemented 2026-06-16)

Status: implemented, rollout pending per ADR-0078.

Controls implemented in PR for #3593:

Control Implementation Status
CF origin guard CfOriginGuard Drogon filter (FLAG_ENFORCE_QUEUE_CF_ORIGIN) Implemented, default OFF — enable after CF proxy confirmed live
Internal API rate limit WAF rule AC2a: block GET /api/v1/internal/* at 120 req/min/IP Implemented in terraform/queue/waf.tf — apply pending
Stripe ASN anomaly log WAF rule AC2b: log webhook POST from non-Stripe ASN (WH-3) Implemented in terraform/queue/waf.tf — apply pending
INT-3 detection Sentry event on CallerIdentity::unknown in InternalAuthFilter Implemented — active when QUEUE_SENTRY_ENABLED is compiled in

SRE rollout sequence (ADR-0078 §8):

  1. terraform apply in terraform/queue/ (SRE) — applies DNS proxy + WAF rules.
  2. Confirm CF proxy live: curl -sI https://queue.raxx.app/health | grep CF-RAY must return a ray ID.
  3. Soak with FLAG_ENFORCE_QUEUE_CF_ORIGIN OFF (≥48h, observe direct_origin_blocked log rate).
  4. Audit caller URLs: all Raptor/Console/Velvet calls must use queue.raxx.app, not raxx-queue-prod.herokuapp.com.
  5. Enable on staging: heroku config:set FLAG_ENFORCE_QUEUE_CF_ORIGIN=true --app raxx-queue-staging >/dev/null 2>&1 && heroku ps:restart --app raxx-queue-staging
  6. Verify staging: curl -i https://raxx-queue-staging.herokuapp.com/health → 403 (direct blocked); curl -i https://queue-staging.raxx.app/health → 200.
  7. Enable on prod: heroku config:set FLAG_ENFORCE_QUEUE_CF_ORIGIN=true --app raxx-queue-prod >/dev/null 2>&1 && heroku ps:restart --app raxx-queue-prod
  8. Update terraform/queue/variables.tf queue_prod_cf_origin_enforced = true.

Operator CF-dashboard items (operator-action, not automated):

What's NOT in this doc

Refs