Ir al contenido

Deploy y entornos

Vault se despliega como worker único de Cloudflare con dos backends de Supabase como dependencias —el principal de emahub y el de solo lectura sobre emawell— más cuatro servicios externos controlados (Anthropic, GitHub, Stripe, Loops). La operación es simple por diseño: los deploys son manuales, no hay pipeline de CI configurada y la sincronización entre cambios de schema y código del worker se gestiona explícitamente con migraciones numeradas. La audiencia es interna y el producto está en producción continua; las ventanas de despliegue se eligen para tener al equipo disponible si algo falla.

EntornoURLNotas
Dev localhttp://localhost:5173 (SPA) + :8787 (Worker)Vite proxy
Producciónvault.emahealth.ioCF Worker (no Pages)

Vault es single-tenant — un solo dominio, un solo deploy. No hay preview branches.

Ventana de terminal
cd /home/cristianruiz/Dev/emahealth/emavault
npm install
npm run dev # Vite SPA :5173 + proxy /api → :8787
npm run dev:worker # Wrangler dev (Hono) :8787
npm run build # Vite build SPA + worker
npm run deploy # build + wrangler deploy
npm run typecheck # tsc --noEmit
npm run guards # Wave 3 regression guards (scripts/guards.sh)
npm run test # Vitest run (todas las suites)
npm run test:unit # Suite unit
npm run test:integration # Suite integration (timeout 15s)
npm run test:regression # Suite regression
npm run test:perf # Suite performance (timeout 30s)
npm run test:security # Suite security (timeout 20s)

Configurar con wrangler secret put NAME o desde CF dashboard.

SecretPropósito
SUPABASE_URLURL de emahub bzikofdvfvchntvfzyni
SUPABASE_ANON_KEYAnon key (servida via /api/config)
SUPABASE_SERVICE_ROLE_KEYService role (uso interno middleware)
SecretPropósito
PLATFORM_API_KEYOutbound a clinic/lab/well via X-Platform-Key
AIDBOX_BASE_URLURL Aidbox para provisioning de Organizations
AIDBOX_AES_KEYAES-GCM key para encrypt client_secret (S9-3)
SecretPropósito
EMI_API_KEYInbound auth a emi.emahealth.io
EMI_BASE_URLDefault https://emi.emahealth.io
SecretPropósito
ANTHROPIC_API_KEYSonnet — proposal, contract, summary
SecretPropósito
GITHUB_TOKENPersonal access token, scope: repo read-only
GITHUB_WEBHOOK_SECRETHMAC sig verification para /api/github/webhook
SecretPropósito
EMAWELL_SUPABASE_URLURL del proyecto separado emawell
EMAWELL_SUPABASE_SERVICE_ROLE_KEYService role read-only

Si faltan estas dos, /api/well/* retorna 503 “not configured”.

SecretPropósito
LOOPS_API_KEYEmails operativos (invitaciones admin tenant)
# wrangler.toml (extracto)
name = "emavault"
main = "src/index.ts"
compatibility_date = "2026-01-01"
compatibility_flags = ["nodejs_compat"]
[assets]
directory = "./dist" # Vite build output
[triggers]
crons = ["0 * * * *"] # GitHub issues sync horario
[[routes]]
pattern = "vault.emahealth.io/*"
zone_name = "emahealth.io"
Ventana de terminal
# Aplicar migraciones a hub
cd supabase/migrations
psql postgresql://postgres@db.bzikofdvfvchntvfzyni.supabase.co/postgres \
-f 008_audit_trigger.sql

Estado actual: 7 migraciones aplicadas.

MigrationTopic
001_fiscal_config.sqlConfiguración de facturación electrónica + dte_emissions_log
002_pricing_tool.sqlPricing tool — costs, plans, quotes
003_provision_columns.sqlColumnas para provisioning cross-product
004_editorial_metrics.sqlMétricas editorial
005_schema_catchup.sqlSync schemas
006_rename_slug_and_product_status.sqlSlug-based domain (F074)
007_slug_history.sqlSlug rename audit

(El audit_trigger F085 vive como migración aparte fuera del repo local — administrada vía MCP / staging.)

connect-src 'self'
https://*.supabase.co
https://api.anthropic.com
https://emi.emahealth.io
https://api.github.com;
img-src 'self' data: https://avatars.githubusercontent.com;
font-src 'self' fonts.gstatic.com;
style-src 'self' 'unsafe-inline' fonts.googleapis.com;
  1. Crear webhook en github.com/emahealth/<repo>/settings/hooks.
  2. URL: https://vault.emahealth.io/api/github/webhook.
  3. Content type: application/json.
  4. Secret: el valor de GITHUB_WEBHOOK_SECRET.
  5. Events: Issues, Pull requests, Pushes.
  6. SSL verification: enable.

Ver docs/SETUP_GITHUB_WEBHOOK.md para detalles.

HTTPSignificadoSolución
401JWT expiróRe-login
403vault_users.is_active=false o NO_VAULT_USERAdmin activa el usuario
403 + moduleRBAC: rol sin acceso al móduloAdmin actualiza vault_permissions
503Ema Well not configuredSetear EMAWELL_* secrets
503Aidbox unreachableVerificar AIDBOX_BASE_URL
451Provisioning failedRevisar audit_log + retry
  • Public routes ANTES de authMiddleware/api/health, /api/config, /auth/*, /api/github/webhook, /api/internal/tenants. Si las pones después, se bloquean por JWT required.
  • Cross-product endpoints /cross/* ANTES de /:slug/* en /api/products/* — sino el :slug los intercepta.
  • CSP estricto — agregar dominios nuevos cuesta tiempo. Probar en staging antes.
  • Slug rename con history — la URL pre-rename redirige 12 meses. No purgar tenant_slug_history antes de ese plazo.
  • Audit_trigger SECURITY DEFINER — tiene impacto perf en INSERTs pesados. Considerar partitioning de audit_log cuando crezca.
  • Imports relativos — no usar alias @/ por convención del repo.
  • Si NO_VAULT_USER en login: significa que el usuario se autenticó con Google pero no existe en vault_users. Admin debe crearlo primero.
  • No hay GitHub Actions para deploy (preferencia EMA: deploys manuales con npm run deploy o CF git integration).
  • Tests corren localmente antes de merge.
  • Branches: trabajo directo en main post-S9 (solo-dev).
Ventana de terminal
# Auth funciona
curl https://vault.emahealth.io/api/health
# Expect: 200 { status: "ok" }
# Config endpoint público
curl https://vault.emahealth.io/api/config
# Expect: 200 { supabaseUrl, supabaseAnonKey }
# Auth endpoint requiere JWT
curl https://vault.emahealth.io/api/tenants
# Expect: 401 sin Authorization
# CSP headers
curl -I https://vault.emahealth.io/
# Expect: Content-Security-Policy: default-src 'self'; ...

Ver docs/SMOKE_S8b_CROSS_PRODUCT.md y docs/SMOKE_S8c_HUMAN.md para checklist completo post-deploy.