Variables de entorno
Las env vars están validadas con Zod al boot de ambos repos. Si falta una requerida, el proceso falla con un mensaje claro listando qué falta.
tcgcards-api (backend)
Sección titulada «tcgcards-api (backend)»Definidas en src/config.ts (Zod schema). Validadas al startup del Cloud Run.
| Variable | Requerida | Default | Propósito | Ubicación en prod |
|---|---|---|---|---|
NODE_ENV | no | development | Entorno (development/test/production) | Cloud Run env |
PORT | no | 3000 | Puerto Express | Cloud Run env (manejado por Cloud Run) |
LOG_LEVEL | no | info | Nivel Pino (fatal/error/warn/info/debug/trace/silent) | Cloud Run env |
MONGODB_URI | sí | — | Connection string Atlas | GCP Secret Manager |
MONGODB_DB_NAME | no | api-cards | Database name | Cloud Run env |
ADMIN_TOKEN | sí | — | Token para /admin/stats y /admin/sync/:tcg (mín 16 chars) | GCP Secret Manager |
INTERNAL_JWT_SECRET | sí | — | Firma JWTs HS256 (mín 32 chars) | GCP Secret Manager. Mismo valor en Vercel. |
INTERNAL_AUTH_SECRET | sí | — | Valida X-Internal-Auth (mín 32 chars) | GCP Secret Manager. Mismo valor en Vercel. |
INTERNAL_CRON_SECRET | sí | — | Valida X-Internal-Cron-Secret (mín 16 chars) | GCP Secret Manager + Cloud Scheduler headers |
CLOUDINARY_CLOUD_NAME | sí | — | Nombre del cloud | Cloud Run env (público, no secret) |
CLOUDINARY_API_KEY | sí | — | API key | GCP Secret Manager |
CLOUDINARY_API_SECRET | sí | — | API secret | GCP Secret Manager |
RESEND_API_KEY | sí | — | API key Resend | GCP Secret Manager |
RESEND_FROM_EMAIL | no | onboarding@resend.dev | Email “From” | Cloud Run env (típicamente notificaciones@tcgcards.cl) |
WEB_PUBLIC_URL | no | https://tcgcards-web.vercel.app | URL del frontend (para links en emails) | Cloud Run env (en prod: https://tcgcards.cl) |
ADMIN_NOTIFICATION_EMAIL | no | disputas@tcgcards.cl | Email para alertas admin | Cloud Run env |
MP_WEBHOOK_SECRET | sí | — | Valida firma de webhooks MP | GCP Secret Manager |
MP_ACCESS_TOKEN | sí | — | Access token MP (prefix APP_USR-) | GCP Secret Manager |
BANK_ACCOUNT_ENCRYPTION_KEY | sí | — | Clave AES-GCM (32 bytes base64) para encrypt de cuentas bancarias | GCP Secret Manager |
OCR_SPACE_API_KEY | sí | — | API key de OCR.space (Engine 3) usada por el escáner de cartas | GCP Secret Manager (secret id ocr-space-api-key) |
REFUND_MP_WINDOW_DAYS | no | 30 | Días desde el pago dentro de los cuales se reembolsa a la tarjeta vía MP; pasado el plazo (o si MP rechaza), al wallet. No está en config.ts: se lee directo en RefundService (igual que MercadoPagoService lee MP_ACCESS_TOKEN lazy). 30 es elección conservadora; MP permite hasta 180. Ver Decisiones → Pagos | Cloud Run env (normalmente sin setear → default 30) |
Flags solo-staging (fail-closed en prod)
Sección titulada «Flags solo-staging (fail-closed en prod)»Estas no se setean en prod. config.ts valida al boot que no aparezcan en producción (defensa fail-closed), así que su presencia en el código de main es inerte.
| Variable | Dónde se setea | Propósito |
|---|---|---|
MP_FAKE_PAYMENTS | tcgcards-api-staging (Cloud Run, modo skip) | Activa el seam de pruebas: el server fabrica pagos sintéticos (getPayment/refund) para la suite E2E headless, sin tocar MercadoPago, corriendo el flujo de dinero REAL. createPreference queda real (el checkout manual del browser funciona). Doble fail-closed: aborta el boot si NODE_ENV=production o si MONGODB_DB_NAME no contiene "staging". Ver Suite E2E de flujos de dinero. |
MP_FAKE_PREFERENCE | (no se setea por defecto) | Opcional, junto con MP_FAKE_PAYMENTS. Hace que createPreference también sea sintética → acelera la suite headless (no llama a MP en el checkout) pero rompe el checkout manual del browser (link /_fake_mp_checkout/... inexistente). Por eso deploy-staging.sh no lo setea. |
MP_SKIP_SIGNATURE_VERIFICATION | tcgcards-api-staging (modo skip) | Saltea la verificación de firma del webhook de MP (las credenciales TEST no entregan firma verificable). config.ts aborta el boot si está en true con NODE_ENV=production. |
STAGING_MP_MODE | GitHub → tcgcards-api → Variables | skip (default actual) o verify. El pipeline de staging lo pasa a deploy-staging.sh; en skip se activan los dos flags de arriba (NODE_ENV=development). |
Sync del catálogo (workflow de GitHub Actions)
Sección titulada «Sync del catálogo (workflow de GitHub Actions)»Estas variables viven en sync-incremental.yml (no en Cloud Run — el servidor API no las necesita):
| Variable | Dónde | Para qué |
|---|---|---|
MYL_SYNC | hardcoded 'true' en el workflow | Activa el source de Mitos y Leyendas en el registry. Sin ella el source no se registra (gate de seguridad: mergear el código no activa nada). |
MYL_EDITIONS | (solo manual) | Lista de slugs separados por coma para limitar el universo de ediciones de Mitos — útil para reintentos puntuales y pruebas. |
R2_ENDPOINT / R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY | GitHub → Secrets | Credenciales del bucket tcgcards-img en Cloudflare R2 (espejo de imágenes de Mitos, servidas por img.tcgcards.cl). Token “Object Read & Write” acotado al bucket. |
R2_BUCKET / R2_PUBLIC_BASE_URL | hardcoded en el workflow | tcgcards-img / https://img.tcgcards.cl. |
Ver Mitos y Leyendas (fuente propia).
Cómo leer un secret de GCP Secret Manager
Sección titulada «Cómo leer un secret de GCP Secret Manager»gcloud secrets versions access latest \ --secret=MONGODB_URI \ --project=api-cards-prodCómo agregar/modificar un secret
Sección titulada «Cómo agregar/modificar un secret»echo -n "NUEVO_VALOR" | gcloud secrets versions add MONGODB_URI \ --data-file=- \ --project=api-cards-prodDespués es obligatorio redeploy del Cloud Run para que la nueva versión se aplique. Ver runbook deploy.
tcgcards-web (frontend)
Sección titulada «tcgcards-web (frontend)»Definidas en src/lib/env.ts (Zod schema). Validadas al startup de Next.js.
| Variable | Requerida | Default | Propósito | Ubicación en prod |
|---|---|---|---|---|
TCGCARDS_API_URL | sí | — | URL base del backend (server-only) | Vercel env |
NEXT_PUBLIC_API_URL | sí | — | Igual valor pero expuesta al client bundle (para componentes client como SearchAutocomplete) | Vercel env |
NEXT_PUBLIC_SITE_URL | sí | — | URL pública del sitio (metadata, OG, canonical) | Vercel env (https://tcgcards.cl) |
AUTH_SECRET | sí | — | Secreto Auth.js para cifrar cookies de sesión (mín 32 chars). Generar con openssl rand -hex 32 | Vercel env |
AUTH_GOOGLE_ID | sí | — | Google OAuth Client ID | Vercel env (config en Google Cloud Console) |
AUTH_GOOGLE_SECRET | sí | — | Google OAuth Client Secret | Vercel env |
INTERNAL_JWT_SECRET | sí | — | Igual valor que en el backend; firma JWTs propios | Vercel env. Mismo valor que el backend. |
INTERNAL_AUTH_SECRET | sí | — | Igual valor que en el backend; autentica server actions → API | Vercel env. Mismo valor que el backend. |
NEXT_PUBLIC_SCANNER_ENABLED | no | — (apagado) | Feature flag del escáner. Los 3 entry points (header desktop, header mobile, form de venta) solo se renderizan si vale exactamente 'true'. Pública: se lee directo de process.env, no está en el Zod schema de env.ts | Vercel env |
NEXT_PUBLIC_ENABLE_TEST_LOGIN | no | — (apagado) | Solo staging. Habilita el test-login (entrar como cualquier email sin Google, para probar flujos con usuarios ficticios). Triple gate fail-closed: requiere este flag 'true' Y VERCEL_ENV !== 'production' (en prod el provider de credenciales ni se registra). Se setea solo en el env Preview de Vercel (staging). El código vive solo en la rama staging del web — nunca en main. | Vercel env — solo Preview/staging |
Cómo leer/modificar env vars en Vercel
Sección titulada «Cómo leer/modificar env vars en Vercel»Desde dashboard:
- https://vercel.com → proyecto
tcgcards-web→ Settings → Environment Variables - Cada variable tiene environments: Production, Preview, Development. Generalmente queremos los 3 si la app local también la necesita.
Desde CLI:
# Listarvercel env ls
# Agregar (interactive)vercel env add NEXT_PUBLIC_SITE_URL production
# Quitarvercel env rm NEXT_PUBLIC_SITE_URL productionDespués de cambiar una env var, Vercel requiere redeploy para que aplique. Hacer push de cualquier commit o vercel --prod.
¿Y las “públicas” vs “secretas”?
Sección titulada «¿Y las “públicas” vs “secretas”?»- Variables con prefix
NEXT_PUBLIC_se incluyen en el bundle del browser. Nunca poner secrets ahí — cualquiera puede leerlas inspeccionando el JS. TCGCARDS_API_URL(sin prefixNEXT_PUBLIC_) solo existe en server-side. Se importa viaimport { env } from '@/lib/env'que tieneimport 'server-only'arriba — si alguien la importa en un client component, falla el build.
Resumen — qué secrets son críticos y por qué
Sección titulada «Resumen — qué secrets son críticos y por qué»| Secret | Si se filtra… |
|---|---|
MONGODB_URI | acceso total a la BD productiva → catastrófico |
MP_ACCESS_TOKEN | pueden iniciar cobros y reembolsos como nosotros → muy grave |
MP_WEBHOOK_SECRET | pueden forgear webhooks para acreditar pagos falsos → muy grave |
BANK_ACCOUNT_ENCRYPTION_KEY | decryptan las cuentas bancarias guardadas → catastrófico |
INTERNAL_AUTH_SECRET / INTERNAL_JWT_SECRET | pueden hacer requests autenticados a la API como cualquier user → grave |
INTERNAL_CRON_SECRET | pueden dispararnos los crons a discreción → moderado |
AUTH_SECRET (Auth.js) | pueden forgear sesiones de cualquier user → grave |
AUTH_GOOGLE_SECRET | hijack del flujo OAuth → grave |
CLOUDINARY_API_SECRET | pueden subir imágenes y borrar las existentes → moderado |
RESEND_API_KEY | pueden mandar emails como tcgcards → moderado (reputación) |
ADMIN_TOKEN | acceso a triggers de sync de catálogo → moderado |
OCR_SPACE_API_KEY | pueden agotar/abusar nuestro cupo de OCR.space → bajo (free tier, resetea diario; a lo sumo deja el escáner inutilizable hasta el reset) |
Ver runbook rotación de credenciales para los pasos exactos cuando algo se filtre o periódicamente.