Files
freno-dev/docs/subdomain-setup.md
Michael Freno fd486d8af6 docs: document subdomain DNS configuration and Vercel domain setup (task 12)
- Document Google Domains CNAME records for all four subdomains
- Document Vercel project domain additions
- Document vercel.json rewrite architecture (api pass-through ordering)
- Include verification checklist and auth boundary notes
2026-07-23 09:26:50 -04:00

4.4 KiB
Raw Blame History

Subdomain Setup — freno.me

This document records the DNS and Vercel domain configuration for the four product subdomains.

DNS Configuration

DNS Provider: Google Domains (nameservers: ns-cloud-a1 through ns-cloud-a4.googledomains.com)

Add the following CNAME records in the Google Domains DNS console:

Subdomain Type Target TTL
nessa CNAME cname.vercel-dns.com Automatic
lineage CNAME cname.vercel-dns.com Automatic
gaze CNAME cname.vercel-dns.com Automatic
inputhalo CNAME cname.vercel-dns.com Automatic

After adding records: Wait for DNS propagation (typically minutes) and for Vercel to auto-issue SSL certificates for each subdomain.

Vercel Project Domains

Add the following domains in the Vercel project Settings → Domains:

Domain Redirects to
nessa.freno.me (no redirect — serves src/routes/nessa/* via vercel.json rewrite)
lineage.freno.me (no redirect — serves src/routes/lineage/* via vercel.json rewrite)
gaze.freno.me (no redirect — serves src/routes/gaze/* via vercel.json rewrite)
inputhalo.freno.me (no redirect — serves src/routes/inputhalo/* via vercel.json rewrite)

Do NOT set any of these as the Production Branch domain — freno.me remains the production domain.

Rewrite Architecture (vercel.json)

The rewrites are defined in vercel.json with two groups, ordered precisely:

Group 1: /api/* pass-throughs (must come first)

{ "source": "/api/(.*)", "has": [{ "type": "host", "value": "gaze.freno.me" }], "destination": "/api/$1" }
{ "source": "/api/(.*)", "has": [{ "type": "host", "value": "inputhalo.freno.me" }], "destination": "/api/$1" }
{ "source": "/api/(.*)", "has": [{ "type": "host", "value": "nessa.freno.me" }], "destination": "/api/$1" }
{ "source": "/api/(.*)", "has": [{ "type": "host", "value": "lineage.freno.me" }], "destination": "/api/$1" }

These pass API requests on subdomains straight through to the existing /api/* routes. This enables dual-host Sparkle appcast support: gaze.freno.me/api/Gaze/appcast.xml hits the same src/routes/api/Gaze/appcast.xml.ts route as freno.me/api/Gaze/appcast.xml.

Group 2: /(.*) catch-all rewrites

{ "source": "/(.*)", "has": [{ "type": "host", "value": "nessa.freno.me" }], "destination": "/nessa/$1" }
{ "source": "/(.*)", "has": [{ "type": "host", "value": "lineage.freno.me" }], "destination": "/lineage/$1" }
{ "source": "/(.*)", "has": [{ "type": "host", "value": "gaze.freno.me" }], "destination": "/gaze/$1" }
{ "source": "/(.*)", "has": [{ "type": "host", "value": "inputhalo.freno.me" }], "destination": "/inputhalo/$1" }

These rewrite non-API requests on each subdomain to its internal route prefix. Vercel matches top-to-bottom, so the /api/* rules above catch API paths first.

Verification Checklist

After DNS propagation and Vercel certificate issuance:

  • dig nessa.freno.me returns the Vercel CNAME
  • dig lineage.freno.me returns the Vercel CNAME
  • dig gaze.freno.me returns the Vercel CNAME
  • dig inputhalo.freno.me returns the Vercel CNAME
  • curl -sI https://nessa.freno.me/ | head -1 → HTTP/2 200
  • curl -sI https://lineage.freno.me/ | head -1 → HTTP/2 200
  • curl -sI https://gaze.freno.me/ | head -1 → HTTP/2 200
  • curl -sI https://inputhalo.freno.me/ | head -1 → HTTP/2 200
  • curl -sI https://freno.me/api/Gaze/appcast.xml | head -1 → HTTP/2 200 (regression)
  • curl -sI https://freno.me/api/InputHalo/appcast.xml | head -1 → HTTP/2 200 (regression)
  • curl -sI https://freno.me/ | head -1 → HTTP/2 200 (regression)
  • All four subdomains have valid SSL certificates (no browser warnings)

Auth Boundaries

Auth remains host-scoped — no cookie domain broadening:

  • freno.me web JWT cookies: host-only on freno.me
  • Nessa: Clerk session tokens (independent)
  • Lineage: mobile JWT (independent)
  • Gaze/InputHalo: no web auth

Notes

  • DNS records must be added at Google Domains (not Vercel's DNS) since freno.me uses Google's nameservers.
  • Subdomains will 404 until route files exist in src/routes/<prefix>/* (content tasks 05–11).
  • The bun run build gate is worktree-friendly; run it before deploying.