PTENES
MÓDULO 6.2

🔐 Secrets y seguridad

Cuando la app sale de tu máquina y empieza a ejecutarse en la nube, necesita claves para acceder a servicios, y esas claves no pueden estar en el código. Aquí afianzas la disciplina de los secretos: variables de entorno (local vs. nube), .env.example y lectura mediante process.env, OAuth y refresh token, las reglas de oro, RLS para aislar datos en la base de datos, hacer auditorías de seguridad y entender por qué una app abierta se convierte en un riesgo financiero. Módulo práctico: hay código que copias y ejecutas.

8
Temas
~60
Minutos
Avanzado
Nivel
Práctico
Tipo
Progreso del módulo 0% · 0 de 8

🔐 El salto de «prototipo» a «producto seguro»

Una app en tu máquina incluso puede tener la clave pegada en el código sin que nadie la vea. Pero cuando la subes a GitHub y funciona en la nube, cualquier descuido con los secretos se convierte en un problema real: una clave filtrada gasta tu dinero y un dato expuesto se convierte en un incidente. Este módulo reúne los hábitos que mantienen todo bajo control: dónde los secretos se guardan, cómo el código los lee, y quién puede ver qué.

Antes de los ocho temas, mira la idea que los une a todos: el código es público (va a GitHub), pero los secretos quedan fuera de él, en una bóveda separada, y solo se inyectan al momento de ejecutarlo. Una capa más: la base con RLS — garantiza que, incluso con la app en línea, un usuario nunca vea los datos de otro.

📦 Código (público) sube a GitHub · sin clave 🔐 Bóveda de secretos .env (local) · dashboard (nube) variables de entorno inyecta en runtime 🛡️ Base de datos + RLS cada usuario solo ve sus datos lo público queda en el código · lo secreto queda en la bóveda · la base de datos protege los datos

Diagrama ilustrativo — seguridad en capas: ningún secreto en el código, claves solo en la bóveda e inyectadas en runtime, y la base de datos aislando los datos con RLS independientemente de los bugs de la app.

1

🔑 Variables de entorno

Qué es

Una variable de entorno es un valor que queda almacenado fuera del código y se inyecta en el programa solo al ejecutarlo (en runtime). El efecto es simple y poderoso: el código nunca contiene la clave. Donde antes escribirías const key = "sk-ant-abc123...", ahora escribe const key = process.env.ANTHROPIC_API_KEY — el valor real proviene del entorno, no del archivo.

¿Qué es?Runtime — el momento en que el programa está ejecutándose de verdad. Clave de API — una contraseña que le da a tu app acceso a un servicio (Anthropic, Firecrawl, Google). Codificado de forma fija — valor escrito directamente en el código (lo que queremos evitar con los secretos).

✗ Incorrecto: clave dentro del código const key = "sk-ant-abc123secreta..." ✓ Correcto: código sin secretos const key = process.env.ANTHROPIC_API_KEY 🔐 entorno (bóveda) inyecta el valor solo en runtime

Diagrama ilustrativo — el código se convierte en una referencia («dame la clave llamada ANTHROPIC_API_KEY»); el valor real vive en el entorno y nunca aparece en el repositorio.

Por qué aprender

Es el mecanismo que separa «lo que es público» (el código) de «lo que es secreto» (las claves). Sin él, todos los secretos viajan junto con el código, y basta con que un git push para que quede expuesto en GitHub para siempre, aunque lo borres después. Una variable de entorno es la base de todo lo que viene en los siguientes temas: es el fundamento de la gestión de secretos.

Conceptos clave

Fuera del código

El secreto no está en el archivo.

En tiempo de ejecución

Se inyecta solo al momento de ejecutarse.

Por nombre

El código pide la clave por nombre.

Nada hard-coded

La clave nunca se escribe a mano.

2

🗄️ Local (.env) vs. nube (dashboard)

Qué es

El mismo secreto vive en dos lugares diferentes, según dónde se ejecute el código. En el modelo local, queda en un archivo .env en tu máquina. En el modelo nube, queda en el panel de Environment Variables de la plataforma (Trigger.dev, Vercel), que inyecta los valores en tiempo de ejecución. El detalle que sorprende a todos: el Trigger.dev no puede leer tu .env local — en la nube, solo conoce lo que registraste en el dashboard.

¿Qué es?Environment Variables (dashboard) — la pantalla de la plataforma donde agregas cada secreto como par clave/valor. Entorno de development — para probar; production — lo que realmente se ejecuta para los usuarios.

🎯 Objetivo: guardar todos los secretos del proyecto fuera del código, en un archivo local.

archivo .env (cópialo, completa los valores)
# .env — NUNCA vai para o GitHub (precisa estar no .gitignore)
ANTHROPIC_API_KEY=<sua-chave>
FIRECRAWL_API_KEY=<sua-chave>

# Google (acesso via OAuth — ver tópico 4)
GOOGLE_CLIENT_ID=<seu-client-id>
GOOGLE_CLIENT_SECRET=<seu-client-secret>
GOOGLE_REFRESH_TOKEN=<seu-refresh-token>
SHEET_ID=<id-da-sua-planilha>

✅ Cómo verificar: ejecuta cat .gitignore y confirma que haya una línea .env — si el archivo aparece en git status como «por commitear», DETENTE y agrégalo al .gitignore antes de cualquier push.

🎯 Consejo práctico

Al pasar a producción, registra cada clave del .env también en el dashboard de la nube — y marca tanto development cuánto production. Olvidar registrar una clave en production es la causa clásica del error que «solo falla en prod»: en tu equipo todo funciona, pero en la nube la app falla con «key is undefined».

Por qué aprender

Porque «funcionó en mi máquina» no basta: el entorno de la nube es otro, con otro almacén de secretos. Entender que son dos lugares —y que deben estar sincronizados— evita el error más frustrante del deploy. Y refuerza la regla del tema 5: puedes (y a veces debes) usar claves distintas para local y producción.

Conceptos clave

Local = .env

Archivo en tu máquina.

Nube = dashboard

Panel de env vars.

No leas el .env

La nube ignora tu archivo local.

Dev + prod

Regístralas en ambos entornos.

3

📄 .env.example y process.env

Qué es

O .env.example es un plantilla segura para hacer commit: enumera los nombres de las claves que el proyecto necesita, pero sin ningún valor. Documenta «lo que este proyecto exige» sin exponer ni un solo secreto. En cambio, el process.env es cómo el código lee estos valores en runtime: nunca los escribe directamente. En la práctica, le pides a Claude Code que cree el .env, confirmar que está en el .gitignore, generar el .env.example y ajustar el código para leer desde process.env.

🎯 Objetivo: documentar qué secretos necesita el proyecto, sin exponer ninguno; este archivo SÍ puede ir a GitHub.

archivo .env.example (seguro para hacer commit)
# .env.example — só os NOMES, nenhum valor. Seguro de commitar.
# Copie para .env e preencha os valores reais.
ANTHROPIC_API_KEY=
FIRECRAWL_API_KEY=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REFRESH_TOKEN=
SHEET_ID=

✅ Cómo verificar: abre el archivo; ningún valor después del =. Quien clone el repo entenderá qué debe configurar, pero no recibirá ninguno de tus secretos.

🎯 Objetivo: leer el secreto en tiempo de ejecución, sin escribirlo nunca en el código.

código (lee del entorno)
// lê o valor do ambiente — o código fica sem segredo
const key = process.env.ANTHROPIC_API_KEY;

// falha rápido e claro se a chave não foi configurada
if (!key) {
  throw new Error("Faltou ANTHROPIC_API_KEY no ambiente (.env ou dashboard).");
}

✅ Cómo verificar: ejecuta el código sin la clave en el entorno; debe fallar con un mensaje claro, no con un error confuso más adelante. Este "fallo rápido" ahorra horas de depuración.

🎯 Objetivo: dejar que Claude Code configure todo el esquema de secretos de una vez.

prompt para Claude Code (en Plan Mode)
Configure as variáveis de ambiente do projeto para estas chaves:
ANTHROPIC_API_KEY, FIRECRAWL_API_KEY, GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN, SHEET_ID.

Faça, nesta ordem:
1. crie o arquivo .env (com placeholders <sua-chave>);
2. garanta que .env está no .gitignore;
3. crie um .env.example com as mesmas chaves SEM valores;
4. ajuste o código para ler tudo de process.env (nada hard-coded).
Não cole nenhuma chave real no chat.

✅ Cómo verificar: al final, git status muestra .env.example como archivo nuevo, pero NO muestra .env. Busca «sk-» entre comillas en el código para asegurarte de que ninguna clave haya quedado hard-coded.

Por qué aprender

Porque un proyecto sin .env.example es un rompecabezas: quien clona no sabe qué claves necesita y acaba descubriéndolo mediante prueba y error. El par .env.example + process.env resuelve esto con elegancia: documenta lo necesario y mantiene el código limpio de secretos. Es la práctica estándar de cualquier proyecto profesional.

Conceptos clave

.env.example

Solo los nombres, sin valor.

Listo para commit

El example puede subirse; el .env no.

process.env

Lectura en runtime.

Documenta

Di qué necesita el proyecto.

4

🔄 OAuth y refresh token

Qué es

OAuth es el protocolo que permite delegar el acceso a un servicio (por ejemplo, Google) sin exponer tu contraseña. En vez de darle tu contraseña a la app, autorizas a la app a actuar en tu nombre y esta recibe tokens. El refresh token es el token de larga duración que renueva el acceso por sí solo cuando expira el acceso temporal — es lo que mantiene funcionando a un agente alojado sin que tengas que volver a iniciar sesión cada vez.

¿Qué es?Client ID / Client secret — la «identidad» de tu app en Google Cloud Console. Access token — acceso breve, caduca rápido. Refresh token — acceso prolongado, genera nuevos access tokens. Cuenta de servicio — una cuenta de robot alternativa (aquí usamos el OAuth del usuario, más sencillo para Sheets/Drive personales).

Por qué aprender

Porque así es como los agentes en la nube acceden a Google Sheets, Drive y Gmail sin que abras el navegador en cada ejecución. El camino concreto: crear credenciales OAuth (client ID + secret) en Google Cloud Console y obtener un refresh token. El flujo estándar por OAuth Playground a veces falla con «access blocked» o «request is invalid»; la alternativa que funciona es un script de terminal que imprime una URL de autorización y, al final, te devuelve el refresh token para que lo pegues en el .env y en las variables de entorno de Trigger.dev.

✓ Acceso delegado (OAuth)

  • ✓Autorizar la app sin entregar la contraseña.
  • ✓El refresh token renueva el acceso automáticamente.
  • ✓Si el Playground falla, genera el token mediante un script en la terminal.

✗ Atajos peligrosos

  • ✗Pegar tu contraseña de Google directamente en la app.
  • ✗Registrar manualmente cada vez que se ejecuta el agente.
  • ✗Hacer commit del refresh token en el repositorio.

🎯 Consejo práctico

Si el OAuth Playground se bloquea con "access blocked / request is invalid", no insistas. Pídele a Claude Code un pequeño script de terminal que use tu GOOGLE_CLIENT_ID e GOOGLE_CLIENT_SECRET, imprime la URL de autorización, recibe el código que devuelve Google y suelta el GOOGLE_REFRESH_TOKEN en la consola; luego solo hay que pegarlo en el .env (y en el dashboard de la nube).

Conceptos clave

Acceso delegado

Autoriza sin dar la contraseña.

Refresh token

Renueva el acceso automáticamente.

Client ID/secret

Identidad de la app.

Fallback en la terminal

El script genera el token.

5

🏆 Reglas de oro

Qué es

Tres reglas innegociables para manejar secretos. Un: nunca pegues claves de API en el chat de la IA: el chat no es el lugar para un secreto. Dos: nunca hagas commit del .env en GitHub: una vez en el historial, considera que la clave quedó expuesta. Tres: rota/regenera las claves periódicamente y, de ser posible, usa claves diferentes para local y producción. La imagen mental: trata una clave de API como el acceso a una tarjeta de crédito.

El ciclo de vida de un secreto seguro

1

Generar la clave en el panel del servicio (Anthropic, Firecrawl) y copiarla directo para el .env — sin pasar por el chat.

2

Aislar: o .env en el .gitignore; el valor registrado en el dashboard de la nube (dev y prod).

3

Usar a través de process.env — nunca hard-coded, nunca impreso en un registro.

4

Rotar: si sospechas que hubo una filtración, regenera la clave en el panel — la anterior deja de funcionar de inmediato. Las claves separadas para dev/prod limitan los daños.

⚠️ Clave = tarjeta de crédito

Una clave filtrada no solo da «acceso»: permite que alguien ejecute llamadas de IA en tu cuenta, acumulando uso y costos a tu nombre. En cuestión de minutos, los bots rastrean GitHub en busca de claves expuestas. Por eso la regla es estricta: si se filtra, rótala de inmediato.

Por qué aprender

Porque la mayoría de los incidentes de seguridad con apps de IA no se deben a un hacker sofisticado: se deben a una clave pegada en el lugar equivocado. Interiorizar estas tres reglas es lo que diferencia un prototipo descuidado de un producto que puedes dejar funcionando con tranquilidad.

Conceptos clave

Nunca en el chat

Clave fuera de la conversación con la IA.

Nunca hagas commit

.env siempre en .gitignore.

Rotar

Regénéralo si tienes dudas.

Clave = tarjeta

Si se filtra, alguien gasta por ti.

6

🛡️ RLS — aislar datos en la base de datos

Qué es

RLS (Row-Level Security) son reglas que viven dentro de la propia base de datos (en Supabase, por ejemplo) e impiden que un usuario vea las filas de otro, incluso si hay un bug en el frontend. Es una capa extra de defensa: la base de datos protege los datos independientemente de la app. Si tu código pide por error "todos los leads", la base de datos, con RLS, devuelve solo los leads de ese usuario.

¿Qué es?Fila (row) — un registro en la tabla (p. ej., un lead). Policy — la regla que dice "quién puede ver o modificar qué filas". Defensa en profundidad — no confiar en una sola capa; si una falla, la otra responde.

Frontend con un bug pide «todos los leads» 🛡️ Base de datos con RLS filtra por user_id (policy) solo los leads del usuario ✓ leads ajenos bloqueados ✗ la base de datos es la última línea de defensa: protege incluso si la app es incorrecta

Diagrama ilustrativo — con RLS, la regla de aislamiento reside en la base de datos; un error en el frontend ya no puede filtrar los datos de otro usuario.

🎯 Objetivo: aislar los datos por usuario en la propia base, con una política RLS en la tabla de leads.

SQL en el editor de Supabase (copia y ejecuta)
-- liga o Row-Level Security na tabela
alter table leads enable row level security;

-- política: cada usuário só enxerga as próprias linhas
-- (compara o dono da linha com o usuário autenticado)
create policy "cada um vê só os seus leads"
on leads
for select
using ( user_id = auth.uid() );

✅ Cómo verificar: inicia sesión como el usuario A y lista los leads; solo aparecen los suyos. Inicia sesión como B e intenta leer los de A: la base de datos devuelve una lista vacía. El aislamiento está garantizado en la base de datos, no en la app.

🎯 Consejo práctico

Al crear el proyecto en Supabase: define una contraseña bancaria distinta de la contraseña de inicio de sesión de la cuenta, elige la región más cercana a los usuarios, habilita el RLS en las tablas con datos de usuario y ejecuta la migración SQL que crea la tabla. Tener RLS desactivado en una tabla expuesta es la falla de seguridad nº 1 en apps hechas a las apuradas.

Por qué aprender

Porque vas a construir rápido, y el código hecho rápido tiene errores. RLS es el cinturón de seguridad: aunque el frontend falle, la base de datos no permite que un usuario vea los datos de otro. Es la diferencia entre «un error molesto» y «una filtración de datos que se vuelve noticia».

Conceptos clave

Regla en la base de datos

La protección está en la base.

Aislar por usuario

Cada uno solo ve lo suyo.

Capa extra

Independiente del frontend.

Defensa en profundidad

Si una falla, la otra te respalda.

7

🔍 Auditoría de seguridad

Qué es

Antes de dejar la app en producción, le pides a la IA que actúa como especialista en seguridad y revisa el proyecto con una lista de cuatro puntos: rutas protegidas, ninguna clave expuesta en el frontend, secretos solo en variables de entorno e RLS habilitado. Una buena práctica es limpiar la conversación antes (el contexto fresco audita mejor) y, después, corregir todo lo que falle.

🎯 Objetivo: hacer una auditoría de seguridad explícita antes del deploy.

prompt para Claude Code (limpia la conversación antes)
Aja como especialista de segurança e audite este app.
Verifique, item por item:
1. rotas protegidas (nada sensível acessível sem login);
2. nenhuma chave de API exposta no frontend / no bundle;
3. segredos APENAS em variáveis de ambiente (nada hard-coded);
4. RLS habilitado em todas as tabelas com dados de usuário.

Liste o que FALHAR e, para cada falha, diga exatamente
como corrigir. Não invente: se não der pra confirmar, diga.

✅ Cómo verificar: recibes una lista de hallazgos que permiten tomar medidas. Corrige cada fallo y vuelve a ejecutar la auditoría hasta que la lista quede vacía; solo entonces haz el deploy a producción.

✓ Auditoría que funciona

  • ✓Limpiar el contexto antes para hacer un análisis desde cero.
  • ✓Checklist explícito de 4 puntos.
  • ✓Corrige lo que falle y vuelve a auditar hasta que no quede nada.

✗ Qué sale mal

  • ✗Creer que «compiló» = «es seguro».
  • ✗Dejar que la clave de la API llegue al frontend.
  • ✗Subir a producción sin revisar nunca la seguridad.

Por qué aprender

Porque con vibe coding construyes rápido y es fácil dejar una puerta abierta sin darte cuenta. Una auditoría explícita, con checklist, cuesta poco (un prompt) y detecta justamente lo que se pasó por alto. Es el «trust but verify» aplicado a la seguridad: no confíes en que quedó seguro; verifícalo.

Conceptos clave

Especialista

Pídele a la IA el rol.

Checklist de 4

Rutas, claves, env, RLS.

Contexto limpio

Audita con una conversación nueva.

Corregir y repetir

Hasta que la lista quede vacía.

8

⚠️ App abierta = riesgo

Qué es

Un app abierta — sin autenticación, es un riesgo financiero concreto: cualquiera que tenga la URL puede activar tu workflow y consumir tus tokens de IA. Como pagas por el uso, el uso de otras personas se carga a tu cuenta. Por eso, no dejes una app sin inicio de sesión funcionando en producción por mucho tiempo: antes de compartirla, agrega autenticación (Supabase) y límites de uso.

¿Qué es?Autenticación — exigir que inicien sesión antes de usar la app. Límite de uso (rate limit / quota) — un límite de cuántas veces alguien puede llamar a la app. Token de IA — la unidad de consumo que le pagas al proveedor en cada llamada al modelo.

⚠️ La cuenta que llega a fin de mes

Publicas una app interesante, compartes el enlace y te vas a dormir. Un bot encuentra la URL, activa el workflow miles de veces y cada activación llama al modelo de IA. Por la mañana, la factura del proveedor se disparó, con un uso que no fue tuyo. App abierta + llamada de IA paga = riesgo directo para tu bolsillo.

Por qué aprender

Porque es el disparador natural para el siguiente módulo. Agregar inicio de sesión con Supabase y límites de uso no es solo seguridad: es el primer paso de un modelo de negocio: si vas a controlar quién lo usa y cuánto, estás a un paso de cobrar por ello. Este razonamiento motiva el modelo freemium con Stripe que ves en 6.3.

Conceptos clave

URL = acceso

Quien tiene el enlace, lo activa.

Pagas por el uso

Los tokens de otros terminan en tu cuenta.

Autenticación

Inicia sesión antes de usarlo (Supabase).

Límites de uso

Límite por usuario → freemium.

Fijando: dónde NUNCA ¿se debe agregar una clave de API?

📌 Resumen del módulo

✓
Variables de entorno — el secreto vive fuera del código y se inyecta en runtime; el código nunca contiene la clave.
✓
Local vs. nube — .env en la máquina, dashboard en la nube; Trigger.dev no lee tu .env: registra dev y prod.
✓
.env.example y process.env — plantilla con nombres (segura para hacer commit) + lectura en tiempo de ejecución, sin nada hard-coded.
✓
OAuth y refresh token — acceso delegado sin contraseña; el refresh token se renueva solo; como alternativa, usa un script en la terminal.
✓
Reglas de oro — nunca en el chat, nunca subir el .env al repositorio, rotarla; la clave es como una tarjeta de crédito.
✓
RLS en la base de datos — las reglas en Supabase aíslan los datos por usuario incluso si hay un bug en el frontend (defensa en profundidad).
✓
Auditoría de seguridad — la IA, como especialista, revisa las rutas, las claves, env y RLS; corrige lo que falle antes del deploy.
✓
App abierto = riesgo — sin inicio de sesión, cualquiera puede gastar tus tokens; agrega autenticación y límites de uso.

Siguiente módulo:

6.3 — Confiabilidad y monetización