milestone(phase-2): close beepbox prod hardening (BEE-1799..1804)#8
Merged
Conversation
Refactor flat infra/*.tf into modules/ + envs/{dev,prod}/. Migrate dev
state from local to gs://beeping-platform-dev-terraform/beepbox/dev.
Apply to prod (beeping-platform-prod): SA dedicado beepbox-server@…prod,
secrets beepbox-api-keys + beepbox-rate-limit-rpm (placeholders),
IAM least-privilege, Firebase Hosting site beeping-platform-prod-api.
Prod Cloud Run revision 00002-4lg ahora corre con SA dedicado y
BEEPBOX_CORS_ALLOWED_ORIGINS=https://beeping.io (sin localhost).
Imagen prod sigue :latest (build 2026-04-23) — BEE-1800 la actualiza
al sha de dev una vez T1 verde.
State migration: bloques `moved {}` en envs/dev/main.tf cubren los 15
recursos renombrados al meterlos en módulos. Bloques `import {}` en
envs/prod/main.tf importan AR repo + Cloud Run service + IAM public.
Plan dev post-migración: 0 changes. Plan prod: 0 changes post-apply.
ignore_changes en cloud-run module añade `client` + `client_version`
para que terraform no haga ping-pong con annotations que gcloud deploy
añade en CI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erify CORS Copy beepbox-server:sha-96cf4a8 from dev AR to prod AR via `crane copy` (no rebuild — same binary, dev/prod parity). Deploy to prod Cloud Run (revision 00003-68l). Env vars already in place from BEE-1799 Terraform apply (BEEPBOX_CORS_ALLOWED_ORIGINS=https://beeping.io). Verified preflight responses for 4 origins on prod: https://beeping.io → 204 + Access-Control-Allow-Origin (✓ allowed) http://localhost:3000 → 204 sin Allow-Origin (✓ blocked by browser) https://www.beeping.io → 204 sin Allow-Origin (✓ blocked: subdomain not whitelisted) https://app.beeping.io → 204 sin Allow-Origin (✓ blocked: subdomain not whitelisted) Dev sigue OK: localhost:3000 + dev firebase hosts permitidos. Update docs/ops/environments.md: imagen prod ahora alineada con dev, cierra el riesgo "Prod imagen desfasada". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eploys
Bootstrap WIF in beeping-platform-dev (113052191496) and
beeping-platform-prod (638946150252):
- Workload Identity Pool `github-actions` (per project)
- OIDC Provider `github` with attribute condition
`assertion.repository == 'beeping-io/beepbox'` (locks token exchange
to this repo only)
- SA `github-actions-deploy@{project}.iam.gserviceaccount.com` with
artifactregistry.writer + run.admin + iam.serviceAccountUser
- principalSet binding so the pool can impersonate the SA
GitHub repo secrets set: WIF_PROVIDER_DEV, WIF_SA_DEV, WIF_PROVIDER_PROD,
WIF_SA_PROD (no long-lived keys; just the provider path + SA email).
deploy.yml refactor:
- workflow_dispatch with `target: dev|prod` choice input
- release events default to prod
- Single deploy job that resolves project + WIF secrets per target via
conditional secret selection (prod ? PROD_secrets : DEV_secrets)
- Rollback step now scopes --project to the target
New docs/ops/ci-cd.md documents the full WIF setup, the security model
(repo-scoped tokens), repo secret list, and idempotent re-bootstrap script.
Cierra pending-001.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…st-BEE-1687) scripts/smoke.sh was written before BEE-1687 added API key auth to /v1/* endpoints. Without an API key, /v1/encode and /v1/decode now correctly return 401 — but the script was still asserting 200 / 400, causing CI smoke step to fail and triggering automatic rollback even on healthy deploys. Update assertions to reflect the auth-required reality. With an API key (passed as $2), the assertions still match the original positive paths (200 / 400). T4 (BEE-1802) will mint test keys so CI can run the authenticated path too. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ping.io
Add `google_firebase_hosting_custom_domain` to firebase-hosting module
behind a `custom_domain` variable. Set tfvars in both envs:
- prod: beepbox.beeping.io
- dev: beepbox-dev.beeping.io
Naming follows ecosystem convention: {service}[-{env}].beeping.io —
each Beeping service gets its own subdomain matching repo name. NOT
api[-{env}].beeping.io (which would mislead about which service is
"the API" in a multi-service ecosystem).
Apply registers the domains in Firebase (state: NEEDS_DNS). Firebase
auto-issues an SSL cert once the CNAMEs land in GoDaddy:
beepbox-dev CNAME beeping-platform-dev-api.web.app
beepbox CNAME beeping-platform-prod-api.web.app
`wait_dns_verification = false` so terraform apply doesn't block on
user-driven DNS work.
Doc updates:
- docs/api/openapi.yaml: servers list now uses canonical hosts (prod
first, then dev, then localhost for self-hosting)
- docs/api/reference.md: canonical hosts table + auth header in examples
- docs/ops/deploy-runbook.md: per-env URL tables + /healthz GFE
intercept documented (closes pending-002)
- docs/ops/environments.md: snapshot reflects custom-domain state
- docs/PENDING.md: pending-002 removed (resolved doc-side by this task)
.gitignore: ignore .claude/ (local Claude Code state).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/mint-test-key.py: minimal Python script that replicates
generateApiKeyCore() from beeping-www/functions/src/generateApiKey.ts
using Firestore REST directly. Bypasses the Callable Function (which
requires a Firebase ID token) but writes to the same apiKeys/{sha256}
collection so beepbox-server's HttpKeyStore validates it identically.
Auth via gcloud Application Default Credentials. Owner UID is the
synthetic `beepbox-sdk-test` (not a real Firebase user).
Generated keys live in beepbox/.env.local (gitignored) and synced to
GCP Secret Manager beepbox-env-local-dev v3 — restore-able on any
machine via ~/.claude/scripts/restore-env-from-gcp.sh.
E2E verified end-to-end on both envs:
- BEEPBOX_TEST_KEY_DEV → /v1/encode → 200 + valid WAV; /v1/decode
round-trip → 200 with confidence 1.0, decoded "a1b2c0000"
- BEEPBOX_TEST_KEY_PROD → same, identical bytes back
docs/ops/test-credentials.md: new doc covers provenance, restore
flow, rotation, what these keys are NOT (not for prod consumers,
not for load tests, not embeddable in public source).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI markdown lint flagged multiple consecutive blank lines after removing pending-002. The placeholder comment block left two trailing newlines instead of one. Trim to exactly one EOF newline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes Phase 2 of the Beeping Platform project: prod hardening + SDK readiness. Six tasks land together as a single squash merge.
infra/envs/{dev,prod}/+modules/), GCS state backend, prod parity (SA dedicado, secrets, Firebase Hosting site)sha-96cf4a8) + CORS prod-onlyhttps://beeping.ioallUsersen CFs es plumbing de Callable Functions, auth real enrequest.auth.uidscripts/mint-test-key.py, sync a GCP Secret Managerdeploy.ymlcontarget=dev|prod, CI auto-deploy verdebeepbox.beeping.io(prod) +beepbox-dev.beeping.io(dev), SSL Google Trust ServicesOutcomes
gh workflow run deploy.yml -f target={dev|prod}orelease: published→ build + push + deploy + smoke + auto-rollback. WIF, sin keys long-lived.https://beeping.io(sin localhost, sin subdominios).BEEPBOX_TEST_KEY_DEV+BEEPBOX_TEST_KEY_PRODen.env.local+ GCP Secret Manager. Round-trip encode/decode verde en ambos.https://beepbox.beeping.io+https://beepbox-dev.beeping.io. OpenAPI servers actualizados.Test plan
terraform planen dev y prod → 0 changes post-applycurl https://beepbox-server-{dev,prod}/version→ 200 en amboslocalhost:3000rebota,https://beeping.iopermitido,www.beeping.iorebotatarget=dev sha-96cf4a8→ 5/5 smoke pass + WIF auth verdebash scripts/smoke.shlocal sin auth → 5/5 pass (401 esperado en/v1/*)https://beepbox.beeping.io/versionyhttps://beepbox-dev.beeping.io/version→ 200 + TLS válido (Google Trust Services)/v1/encode200 + WAV PCM 44.1kHz, decode round-tripconfidence: 1.0, decoded: "a1b2c0000"bash scripts/validate-openapi.sh→ spec OK con nuevos serversDoc updates
docs/ops/{environments,ci-cd,test-credentials}.mddocs/api/{openapi.yaml,reference.md},docs/ops/deploy-runbook.md,docs/README.md,docs/PENDING.mdinfra/README.mdCloses BEE-1799, BEE-1800, BEE-1801, BEE-1802, BEE-1803, BEE-1804.
🤖 Generated with Claude Code