{
 "id": "8.5",
 "title": "DNS and domains",
 "department": "Ops and infrastructure",
 "department_slug": "ops-and-infrastructure",
 "direction": null,
 "branded": false,
 "status": "draft",
 "owner": "Param",
 "last_verified": "Mon Oct 26",
 "holds": "Every domain, where it points, the Mac DNS lesson",
 "urls": {
  "html": "https://handbook.joinsquareup.com/ops-and-infrastructure/dns-and-domains",
  "markdown": "https://handbook.joinsquareup.com/ops-and-infrastructure/dns-and-domains.md",
  "json": "https://handbook.joinsquareup.com/ops-and-infrastructure/dns-and-domains.json"
 },
 "other_directions": [],
 "tldr": {
  "headline": "One load balancer, a few Cloudflare records, a two minute check",
  "points": [
   {
    "icon": "map",
    "text": "feed, brew and demo all point at one load balancer, DNS only, with no Cloudflare proxy."
   },
   {
    "icon": "clock",
    "text": "The proxy had added about half a second per request, so it is off."
   },
   {
    "icon": "warn",
    "text": "On 19 Oct the site was up; Param's Mac router resolver timed out for one name."
   },
   {
    "icon": "check",
    "text": "Before calling an outage, run three checks: two dig lookups and one pinned curl."
   },
   {
    "icon": "flag",
    "text": "Use one level under joinsquareup.com only, since Universal SSL skips deeper names."
   }
  ],
  "stat": {
   "value": "34.54.65.239",
   "label": "The load balancer address"
  },
  "decide": "Who owns the Cloudflare zone, and when does production leave feed.joinsquareup.com?",
  "mood": "waiting"
 },
 "sources": [
  "brew/.wt-prod/docs/production.md:3, 15",
  "brew origin/main a559de6: deploy.sh:41, tools/prod-env.sh:7-9, tools/deploy-demo.sh:16",
  "brand-directions/_tools/deploy-handbook.py:2-18, brand-directions/_tools/deploy-docs.py:8",
  "memory brew-dns-local, brew-prod-backend, brew-prototype-repo, brew-demo-mode, brand-directions-2026-10-25, brew-handbook, brew-shopify-connector-qa, shopos-feed-any-brand, arata-feed-demo"
 ],
 "open_questions": [
  "**Who owns the Cloudflare account and the joinsquareup.com zone?** No note names it for this zone. Proposal: Param owns it, with Sai as a second admin so a lost login never locks the domain.",
  "**When do we move production off feed.joinsquareup.com?** Proposal: when the product name is picked, in one planned change with every callback URL checked from the list above."
 ],
 "parts": {
  "Answer": "Every Brew address lives under joinsquareup.com in Cloudflare, most of them pointing straight at one Google load balancer, and this page lists each record, where it points, and the Mac DNS lesson that turns a false outage into a two-minute check.",
  "Today": "### Records we use\n\n| Address | Points at | Cloudflare mode | Serves | Source |\n|---|---|---|---|---|\n| feed.joinsquareup.com | A 34.54.65.239, the Google load balancer `squareup-urlmap` | DNS only | Production, `brew-web` behind Cloud Armor | [src: brew/.wt-prod/docs/production.md:15] [src: memory brew-prod-backend] |\n| brew.joinsquareup.com | A 34.54.65.239 | DNS only | Staging, `shopos-feed`, host rule `shopos-feed-matcher` | [src: memory brew-prototype-repo] [src: memory brew-dns-local] |\n| demo.joinsquareup.com | A 34.54.65.239 | DNS only | Demo, `brew-demo`, host rule `brew-demo-matcher` | [src: memory brew-demo-mode] [src: memory brew-dns-local] |\n| docs.joinsquareup.com | CNAME squareup-docs.web.app | DNS only | Shared docs, Firebase Hosting | [src: memory brand-directions-2026-10-25] |\n| handbook.joinsquareup.com | Firebase Hosting site `squareup-handbook`, which only rewrites to Cloud Run `handbook-comments`, behind Cloudflare Access | Not yet confirmed | This handbook | [src: brand-directions/_tools/deploy-handbook.py:2-18] [src: memory brew-handbook] |\n\n- The load balancer has its own certificate, so the Brew records skip Cloudflare's proxy; the proxy had added about half a second per request. [src: memory shopos-feed-any-brand]\n- Cloudflare's Universal SSL does not cover two-level subdomains such as `v1.arata.joinsquareup.com`; use one level. [src: memory arata-feed-demo]\n- DNS changes by script use the Cloudflare token in Secret Manager `CLOUDFLARE_DNS_ONBOARDING_TOKEN`. [src: memory brew-demo-mode]\n- production.md says staging's entry is a Cloud Run domain mapping; the 15 and 19 Oct notes say a load balancer host rule. The notes are more detailed; confirm. [src: brew/.wt-prod/docs/production.md:15] [src: memory brew-prototype-repo]\n\n### Where an address is written in code or settings\n\n| Address | Written in | Source |\n|---|---|---|\n| feed.joinsquareup.com | `PROD_URL`, `PUBLIC_URL` on production; `PROD_URL` on staging | [src: brew/tools/prod-env.sh:7-9] [src: brew/deploy.sh:41] |\n| brew.joinsquareup.com | `PUBLIC_URL` on staging; `STAGING_URL`, `SLACK_RELAY_URL` on production | [src: brew/deploy.sh:41] [src: brew/tools/prod-env.sh:9] |\n| demo.joinsquareup.com | `PUBLIC_URL` on the demo; `DEMO_URL` on production | [src: brew/tools/deploy-demo.sh:16] [src: brew/tools/prod-env.sh:9] |\n| Callback URLs on both sites | Google sign-in, Google Ads and GA4, Meta, Slack consoles | [src: brew/.wt-prod/docs/production.md:73-77] |\n| Arata's Shopify own app | redirect `https://brew.joinsquareup.com/shopify/own/callback` | [src: memory brew-shopify-connector-qa] |\n\n### Retired\n\n- arata, arata-v1 and feed-demo records and their services were removed on 15 Oct. [src: memory brew-prototype-repo]\n- feed.joinsquareup.com was retired on 15 Oct and came back as production on 25 Oct. [src: memory brew-prototype-repo] [src: memory brew-prod-backend]\n\n### The Mac DNS lesson, 19 Oct\n\nParam saw brew.joinsquareup.com \"not working\". The site was up; the Mac's router resolver on an IPv6-only network timed out for that one name. [src: memory brew-dns-local]",
  "Tomorrow": "| Item | Version | Tag |\n|---|---|---|\n| feed.joinsquareup.com as production | v1 | Stays until the name is picked |\n| A production domain on the product's own name | v2 | New, waits on the name |\n| feed.joinsquareup.com redirecting to the new domain | v2 | Changes |\n| Every callback URL listed in one place, checked before a domain move | v1 | New |",
  "How we do it": "### Someone says the site is down\n\nRun these three before calling it an outage. [src: memory brew-dns-local]\n\n```bash\ndig +short brew.joinsquareup.com @8.8.8.8\ndig +short brew.joinsquareup.com @1.1.1.1\ncurl -s -o /dev/null -w '%{http_code}\\n' --resolve brew.joinsquareup.com:443:34.54.65.239 https://brew.joinsquareup.com/\n```\n\nIf public DNS answers 34.54.65.239 and the pinned curl says 200, the site is fine. Reconnect Wi-Fi, switch network, or set DNS to 1.1.1.1 or Google DNS64 `2001:4860:4860::6464`.\n\n### Adding a record\n\n1. One level under joinsquareup.com only.\n2. For a Cloud Run service: a serverless NEG, a backend, a host rule on `squareup-urlmap`, then an A record to 34.54.65.239, DNS only.\n3. Add the new address to every OAuth console that needs a callback.\n4. Add the record to the table above.",
  "Numbers": "None; DNS has no metric we track. The proposed uptime check lives in [Monitoring and alerts](/ops-and-infrastructure/monitoring-and-alerts).",
  "Open": "1. **Who owns the Cloudflare account and the joinsquareup.com zone?** No note names it for this zone. Proposal: Param owns it, with Sai as a second admin so a lost login never locks the domain.\n2. **When do we move production off feed.joinsquareup.com?** Proposal: when the product name is picked, in one planned change with every callback URL checked from the list above."
 }
}