Two pieces: the static site (Firebase Hosting) and
the advisor API (Cloud Run). Firebase rewrites
/api/** to the Cloud Run service, so the browser only ever
talks to one origin.
From server/:
# One-time: enable APIs and set project
gcloud config set project YOUR_GCP_PROJECT
gcloud services enable run.googleapis.com artifactregistry.googleapis.com
# Build & deploy the container (name must match firebase.json serviceId)
gcloud run deploy alfonso-advisor \
--source . \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars ADVISOR_MODEL=claude-haiku-4-5,ALLOWED_ORIGINS=https://edmhs.us
# Store the API key as a secret (recommended over --set-env-vars for keys)
echo -n "sk-ant-..." | gcloud secrets create anthropic-api-key --data-file=-
gcloud run services update alfonso-advisor --region us-central1 \
--set-secrets ANTHROPIC_API_KEY=anthropic-api-key:latest
Verify: curl https://<cloud-run-url>/healthz →
{"ok":true,...,"configured":true}.
From the project root (where firebase.json lives):
firebase use YOUR_FIREBASE_PROJECT # the "edm-web" project
firebase deploy --only hosting
firebase.json already: - serves
ui_kits/website/ as the site root (index.html,
the .jsx sections, measure.html), - rewrites
/api/** → the alfonso-advisor Cloud Run
service in us-central1.
If your Cloud Run service name or region differ, update
firebase.json → hosting.rewrites[0].run to
match.
ui_kits/website/FloorAdvisor.jsx → ask():
1. POST /api/advisor with { message }
(same-origin; Firebase forwards to Cloud Run). 2. If that fails
and the in-Claude preview helper
window.claude.complete exists, it falls back to that (so
the design preview keeps working). 3. Otherwise it shows the friendly
“advisor is offline, use the estimate form” message.
ADVISOR_RATE_PER_MIN, default 10/min) to cap API
spend.ADVISOR_MODEL env var
(haiku/sonnet families).server/main.py mirror
the ones in FloorAdvisor.jsx — if you change pricing,
update both.