Skip to content

milestone(phase-2): close beepbox prod hardening (BEE-1799..1804)#8

Merged
alfredrc merged 7 commits into
developfrom
milestone/phase-2-prod-hardening
May 2, 2026
Merged

milestone(phase-2): close beepbox prod hardening (BEE-1799..1804)#8
alfredrc merged 7 commits into
developfrom
milestone/phase-2-prod-hardening

Conversation

@alfredrc
Copy link
Copy Markdown
Member

@alfredrc alfredrc commented May 2, 2026

Summary

Closes Phase 2 of the Beeping Platform project: prod hardening + SDK readiness. Six tasks land together as a single squash merge.

Task Type What
BEE-1799 infra Multi-env Terraform (infra/envs/{dev,prod}/ + modules/), GCS state backend, prod parity (SA dedicado, secrets, Firebase Hosting site)
BEE-1800 feat Imagen prod alineada con dev (sha-96cf4a8) + CORS prod-only https://beeping.io
BEE-1801 security Verified — Firebase Auth ya implementada en BEE-1687; allUsers en CFs es plumbing de Callable Functions, auth real en request.auth.uid
BEE-1802 chore Test API keys minteadas (dev + prod) via scripts/mint-test-key.py, sync a GCP Secret Manager
BEE-1803 infra Workload Identity Federation cableado en dev y prod, deploy.yml con target=dev|prod, CI auto-deploy verde
BEE-1804 infra Custom domains live: beepbox.beeping.io (prod) + beepbox-dev.beeping.io (dev), SSL Google Trust Services

Outcomes

  • Dev↔prod parity: misma definición Terraform, misma imagen, mismas env vars, mismo SA model. Riesgos detectados al inicio (prod sin Terraform, sin SA dedicado, sin secrets) cerrados.
  • CI deploys auto: gh workflow run deploy.yml -f target={dev|prod} o release: published → build + push + deploy + smoke + auto-rollback. WIF, sin keys long-lived.
  • CORS por entorno: dev permite localhost + Firebase dev hosts; prod solo https://beeping.io (sin localhost, sin subdominios).
  • SDKs E2E desbloqueados: BEEPBOX_TEST_KEY_DEV + BEEPBOX_TEST_KEY_PROD en .env.local + GCP Secret Manager. Round-trip encode/decode verde en ambos.
  • Endpoints canónicos: https://beepbox.beeping.io + https://beepbox-dev.beeping.io. OpenAPI servers actualizados.

Test plan

  • terraform plan en dev y prod → 0 changes post-apply
  • curl https://beepbox-server-{dev,prod}/version → 200 en ambos
  • CORS preflight prod: localhost:3000 rebota, https://beeping.io permitido, www.beeping.io rebota
  • CI workflow run dispatched target=dev sha-96cf4a8 → 5/5 smoke pass + WIF auth verde
  • bash scripts/smoke.sh local sin auth → 5/5 pass (401 esperado en /v1/*)
  • https://beepbox.beeping.io/version y https://beepbox-dev.beeping.io/version → 200 + TLS válido (Google Trust Services)
  • E2E con BEEPBOX_TEST_KEY_DEV: /v1/encode 200 + WAV PCM 44.1kHz, decode round-trip confidence: 1.0, decoded: "a1b2c0000"
  • Idem con BEEPBOX_TEST_KEY_PROD
  • bash scripts/validate-openapi.sh → spec OK con nuevos servers
  • CI verde en este PR

Doc updates

  • New docs/ops/{environments,ci-cd,test-credentials}.md
  • Updated docs/api/{openapi.yaml,reference.md}, docs/ops/deploy-runbook.md, docs/README.md, docs/PENDING.md
  • New infra/README.md

Closes BEE-1799, BEE-1800, BEE-1801, BEE-1802, BEE-1803, BEE-1804.

🤖 Generated with Claude Code

alfredrc and others added 7 commits May 2, 2026 07:02
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>
@alfredrc alfredrc merged commit ff749c5 into develop May 2, 2026
6 checks passed
@alfredrc alfredrc deleted the milestone/phase-2-prod-hardening branch May 2, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant