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 Permalink 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 domainfreno.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 -1HTTP/2 200
  • curl -sI https://lineage.freno.me/ | head -1HTTP/2 200
  • curl -sI https://gaze.freno.me/ | head -1HTTP/2 200
  • curl -sI https://inputhalo.freno.me/ | head -1HTTP/2 200
  • curl -sI https://freno.me/api/Gaze/appcast.xml | head -1HTTP/2 200 (regression)
  • curl -sI https://freno.me/api/InputHalo/appcast.xml | head -1HTTP/2 200 (regression)
  • curl -sI https://freno.me/ | head -1HTTP/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 0511).
  • The bun run build gate is worktree-friendly; run it before deploying.