Workspace domains runbook
Policy and decisions: Workspace domains: access and abuse controls.Components
Environment
apps/web (server only):
Worker (
apps/workspace-domains):
Cloudflare setup
The zone, apex and fallback origin are configuration, not code. Production uses the values below; moving the SaaS zone means changing the environment, the token and the Worker’s account, then re-verifying domains.- SaaS zone (production
ttr.gg). Enable SSL for SaaS, create the fallback origin (productionsaas.ttr.gg, a proxied record), and keep the existing platform hosts (ttr.gg,www.ttr.gg,fysh.ttr.gg) on their routes. They stay reserved in the hostname policy; any other existing record or route on the zone also blocks that name from becoming a platform subdomain. - API token for apps/web, the SaaS zone only: DNS Edit (platform subdomain records and conflict checks), SSL and Certificates Edit (custom hostnames), Workers Routes Edit (per-host routes and conflict checks). No account-level permissions. Store it as a sensitive environment variable.
- Deploy token (operator only): account Workers Scripts Edit for publishing
tuturuuu-workspace-domains. Do not give this scope to apps/web.
Personal-account dependency
The production SaaS zonettr.gg lives in the owner’s personal Cloudflare
account, not a Tuturuuu organization account. Consequences:
- Rex owns rotation of
CLOUDFLARE_SAAS_API_TOKEN. - Selene scopes the token to
ttr.ggonly, with DNS Edit, SSL and Certificates Edit and Workers Routes Edit, and keeps it a sensitive environment variable. - Losing access to that account stops new domains and certificate renewals; existing records keep serving until removed. Moving to an organization zone is a configuration change (zone ID, account ID, apex, fallback origin, token) plus re-verification of every domain.
Deploy the Worker
workers_dev and preview URLs off and no routes. For a bootstrap
host before apps/web ships, attach a route manually: Workers Routes, pattern
<host>/*, Worker tuturuuu-workspace-domains, and point the host at the zone
(custom hostname for external domains, proxied record for a platform subdomain).
The bootstrap hosts vhp.ttr.gg and vohoangphuc.com are seeded by migration
20261111120200; their operator-made routes are not tracked on the rows, so
removing them through the API leaves those routes for manual cleanup.
The Worker reads published content from the edge site API, never from the
public delivery route, which requires a session in production. Site answers
contain published entries only, with an allowlist of fields and absolute asset
URLs on the app origin.
Worker behavior: GET and HEAD only (405 otherwise), /__health returns ok,
/robots.txt for known hosts, 404 for unknown hosts, strict CSP without scripts,
nosniff, HSTS, no cookies. Resolve and site answers are cached for 60 seconds
with the Cache API. Origin requests send User-Agent: tuturuuu-workspace-domains/1
because apps/web rejects anonymous API calls without one.
Polar add-on (billing launch)
Do not create the product until billing is approved. Then:- Create a recurring Polar product priced at $10 per unit per month, seat-based so
the quantity is the number of domains, with metadata
product_kind: custom_domain_addon. - Set
WORKSPACE_DOMAINS_POLAR_PRODUCT_IDto its ID. - Webhooks from the existing Polar endpoint reconcile subscriptions into
private.workspace_domain_addons; product sync skips the add-on so it never appears as a plan. - After preview sign-off, set
update private.workspace_domain_settings set paid_addon_enabled = true;andWORKSPACE_DOMAINS_BILLING_ENABLED=true.
Operations
- Kill switch:
update private.workspace_domain_settings set enabled = false;blocks adds, verification and serving through the resolver (the Worker returns 404 once its 60-second cache expires; clearSTATIC_HOST_MAPtoo). - Cap override: insert into
private.workspace_domain_caps (ws_id, domain_cap, reason). - Audit:
private.workspace_domain_auditbyws_id, newest first. - Stuck provider cleanup: removed rows keep their Cloudflare IDs and an
errorsuch asprovider_cleanup_403; delete those resources manually. - CLI:
ttr domains list|add <host>|verify <host>|remove <host> --confirm <host> --workspace <id> [--json].