Skip to content

Launch checklist

Use this before pointing production traffic at TideGuard.

  • TOKEN_SECRET set via Wrangler secret / Deploy-to-Cloudflare (tideguard.dev/token or openssl rand -hex 32)
  • Optional production: ADMIN_SECRET set (wrangler secret put ADMIN_SECRET) so visitor HMAC and admin seals are split (token-secret-rotation.md)
  • /admin setup completed: Claim (TOKEN_SECRET + username/password) + Cloudflare verify + Turnstile verify + Finish
  • Per-admin passwords stored offline; login uses Turnstile after finish
  • Recovery phrases saved for each admin (Forgot password)
  • Extra operators invited via Team panel (72h links; copy/mailto before navigating away), not by sharing one password
  • Custom domain attached (custom-domain.md) — full NS or partial CNAME as applicable
  • Cloudflare panel: DNS proxied, IP Geolocation as needed, SSL Full (strict) if origin is ready
Setting Where Guidance
MAX_CONCURRENT_USERS Code default / optional env Origin concurrent capacity you can actually serve
ADMIT_PER_SECOND /admin traffic (or env) Steady admit rate once the room is full
TOKEN_TTL_SECONDS Code default / optional env How long an admission cookie remains valid
Timeslot check-ins Waiting UI (default) nextCheckAt; period = max(5s, ceil(waiting/750)); status ≤ ~750/s; deferred until opensAt when scheduled
Fixed poll override Env (optional) WAITING_ROOM_*_INTERVAL_MS — not recommended; budgets DO requests
Heartbeat timeout Worker vars (180) Drop silent waiters; timeslot period stays under half this window
Missed-slot grace Danger zone / env (120) Seconds after due nextCheckAt before silent waiters expire (30–900); lower = more false drops
Max waiting visitors /admin Danger zone Default 1M safety cap (not a seat fee); /join → 503 queue_full when full; raisable to 50M
QUEUE_TIMEOUT_SECONDS Code default (86400) Max stay in pool (24h); too-short values expire deep timeslots

Rough DO request volume while waiting (timeslot status-only):

periodSec = max(5, ceil(waiting / 750))
statusRps ≈ waiting / periodSec # ≤ 750 once waiting ≥ 3750
visitors × (1 join + waitSeconds / periodSec)

Fixed-interval override (discouraged) still follows:

visitors × (1 join + waitSeconds/pollInterval + waitSeconds/heartbeatInterval)

Use /cost for Cloudflare Workers paid-plan estimates (adaptive by default).

  • Left demo mode: origin proxy enabled with protect-all or path prefixes (protecting-origin.md)
  • Origin Test upstream from /admin#origin returns a reachable status before Go live
  • Origin URL is a public https:// host (loopback / private IPs are rejected)
  • Origin only accepts traffic from Cloudflare — Full (strict) SSL + Authenticated Origin Pulls (see protecting-origin.md)
  • Protected path smoke-test: unauthenticated → /wait; admitted → origin content
  • Confirmed TideGuard cookies (tg_*) are not forwarded upstream
  • Bot Fight / WAF left on; if waiting-room polls fail, add a Skip rule for ticketed /join /status /leave /heartbeat /enter (see protecting-origin.md)
  • Opening time set (or cleared) for launch; /wait shows countdown before open
  • Silent pause smoke-tested: admissions stop; waiting UI unchanged
  • Origin health URL is public HTTPS (private hosts rejected); thresholds tuned; override known
  • If using webhooks: public https:// URL only; Send test OK; signing secret saved (webhooks.md)
  • Understood: one seat per browser profile via ticket; extra devices can take extra seats
  • Visitor Turnstile (if enabled): new browser is challenged; an existing valid ticket resumes; missing widget configuration fails closed
  • Web notifications (if enabled): permission is requested only after a visitor gesture; the waiting tab still stays open
  • GET /health → 200
  • /wait?queue=…&return=/demo joins and eventually admits
  • Branding redirect path (if set) lands on the expected same-origin URL
  • Google Analytics Measurement ID (if set): /wait HTML includes gtag; Realtime shows a hit
  • Click-to-enter (if enabled): Continue issues cookie; hold expiry rejoins
  • Visitor Turnstile (if enabled): missing/invalid challenge rejects /join; /status and /heartbeat remain challenge-free
  • /demo (or origin path) loads with a valid admission cookie
  • Origin sees X-TideGuard-Visitor when proxying (or your app verifies HMAC)
  • Operator-auth GET /metrics?queue=… shows expected waiting/admitted / pause / health
  • Public join/status omit depth unless Show depth is enabled
  • Admin branding Save writes KV once; polls do not