Variáveis de ambiente no Claude Code: .env, Zod, Secrets e deploy seguro
Gerencie variáveis de ambiente e Secrets com Claude Code: .env.example, validação Zod, CI/CD, redação e rotação.
Quando alguém começa a usar Claude Code para criar login, pagamentos, webhooks ou integrações de IA, o primeiro problema sério em produção geralmente não está na interface. Ele aparece na configuração: uma URL de banco ausente, uma chave de staging usada em produção, um WEBHOOK_SECRET impresso em logs de CI ou uma API key real enviada ao GitHub.
Este guia mostra um padrão pronto para implementar gerenciamento de variáveis de ambiente e Secrets com Claude Code. Uma variável de ambiente é um valor entregue à aplicação em tempo de execução, como PORT ou APP_ORIGIN. Um Secret é um valor que pode ser abusado se vazar, como ANTHROPIC_API_KEY, DATABASE_URL ou WEBHOOK_SECRET. Secrets podem chegar por variáveis de ambiente, mas precisam de regras mais rígidas.
Para a configuração do próprio Claude Code, consulte a referência oficial Claude Code environment variables. Para validar a configuração da aplicação, usaremos Zod. Em CI/CD e deploy, use como base GitHub Actions secrets, Vercel environment variables, Cloudflare Workers variables and secrets e Docker secrets. Cada plataforma tem sua interface, mas a regra é a mesma: versionar nomes e regras, nunca valores secretos.
Para reforçar o contexto de segurança, leia também boas práticas de segurança com Claude Code e autenticação JWT com Claude Code.
Trate .env como contrato
Um arquivo .env é útil, mas não deve virar um caderno particular de cada pessoa. A equipe precisa de três camadas:
- Declaração:
.env.examplelista as chaves necessárias - Validação: Zod falha no boot quando algo está ausente ou inválido
- Operação: CI/CD e produção injetam valores pelos Secrets da plataforma
flowchart LR
Dev["local .env.local"] --> Schema["Zod schema"]
CI["GitHub Actions secrets"] --> Schema
Prod["Vercel / Cloudflare / Docker secrets"] --> Schema
Schema --> App["Type-safe app config"]
Schema --> Logs["Redacted logs"]
Example[".env.example"] --> Dev
O ponto prático é que todas as entradas passam pelo mesmo schema. Claude Code deve receber nomes de chaves, regras de validação e comportamento de erro, não valores reais de produção.
Casos de uso reais
| Caso | Variáveis comuns | Falha típica |
|---|---|---|
| Desenvolvimento local | APP_ORIGIN, DATABASE_URL, ANTHROPIC_API_KEY | A app só funciona na máquina de uma pessoa |
| Verificação de webhooks | STRIPE_WEBHOOK_SECRET, WEBHOOK_SECRET | Requisições falsas são aceitas |
| Testes em CI | CI_DATABASE_URL, TEST_API_KEY | PR passa, mas deploy falha |
| Produção | DATABASE_URL, SESSION_SECRET, APP_ORIGIN | Banco errado, cookie errado ou credential vazado |
| Rotação de Secrets | ANTHROPIC_API_KEY_NEXT | Uma chave antiga comprometida continua válida |
No fluxo de Masa para ClaudeCodeLab, o ganho maior não foi apenas criar .env.example, mas fazer a aplicação se recusar a iniciar quando o contrato está incompleto. Isso transforma surpresas de deploy em falhas revisáveis no PR.
1. Separe os arquivos
.env.example é documentação, não armazenamento de valores reais. .env.local pertence a uma máquina local. .env.production.example é uma checklist de produção sem secrets.
mkdir -p src/config
touch .env.example .env.local .env.production.example src/config/env.ts
# .gitignore
.env
.env.*
!.env.example
!.env.production.example
# Cloudflare local secrets
.dev.vars
.dev.vars.*
# .env.example
APP_ENV=local
NODE_ENV=development
PORT=3000
APP_ORIGIN=http://localhost:3000
DATABASE_URL=postgresql://app:app@localhost:5432/app
ANTHROPIC_API_KEY=replace-with-local-dev-key
WEBHOOK_SECRET=replace-with-32-plus-character-secret
PUBLIC_ANALYTICS_KEY=
LOG_LEVEL=info
# .env.production.example
APP_ENV=production
NODE_ENV=production
PORT=3000
APP_ORIGIN=https://example.com
DATABASE_URL=<set-in-platform-secret-store>
ANTHROPIC_API_KEY=<set-in-platform-secret-store>
WEBHOOK_SECRET=<set-in-platform-secret-store>
PUBLIC_ANALYTICS_KEY=<optional-public-key>
LOG_LEVEL=info
Os placeholders não são defaults seguros. Eles só dizem que um valor precisa ser fornecido em outro lugar.
2. Valide com Zod no boot
Em Node.js, variáveis de ambiente chegam como texto. Mesmo PORT=3000 é string, então usamos z.coerce.number() para converter e validar.
npm install zod dotenv
npm install -D tsx typescript @types/node
// src/config/env.ts
import "dotenv/config";
import { z } from "zod";
const secretNamePattern = /(SECRET|TOKEN|PASSWORD|API_KEY|DATABASE_URL|DSN)/i;
function redactValue(key: string, value: unknown): string {
if (value === undefined || value === null || value === "") return "<empty>";
const text = String(value);
if (!secretNamePattern.test(key)) return text;
if (text.length <= 8) return "<redacted>";
return `${text.slice(0, 4)}...${text.slice(-4)}`;
}
const envSchema = z.object({
APP_ENV: z.enum(["local", "development", "staging", "production"]).default("local"),
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
PORT: z.coerce.number().int().min(1).max(65535).default(3000),
APP_ORIGIN: z.string().url(),
DATABASE_URL: z.string().url(),
ANTHROPIC_API_KEY: z.string().min(20, "ANTHROPIC_API_KEY is too short"),
WEBHOOK_SECRET: z.string().min(32, "WEBHOOK_SECRET must be at least 32 characters"),
PUBLIC_ANALYTICS_KEY: z.string().optional(),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
});
const parsed = envSchema.safeParse(process.env);
if (!parsed.success) {
console.error("Environment validation failed:");
for (const issue of parsed.error.issues) {
const key = String(issue.path[0] ?? "unknown");
console.error(`- ${key}: ${issue.message}; current=${redactValue(key, process.env[key])}`);
}
process.exit(1);
}
export const env = Object.freeze(parsed.data);
export type AppEnv = typeof env;
export function isProduction(): boolean {
return env.APP_ENV === "production";
}
export function publicEnv() {
return {
APP_ENV: env.APP_ENV,
APP_ORIGIN: env.APP_ORIGIN,
PUBLIC_ANALYTICS_KEY: env.PUBLIC_ANALYTICS_KEY ?? "",
};
}
Teste localmente:
cp .env.example .env.local
npx tsx src/config/env.ts
Depois peça ao Claude Code para centralizar os acessos:
Encontre todas as leituras diretas de process.env neste repositório.
Somente src/config/env.ts deve ler process.env diretamente.
Nos outros arquivos, proponha importar env de src/config/env.ts.
Não imprima secrets em logs, erros ou snapshots de teste.
3. Redija antes de logar
Vazamentos não acontecem só no Git. Eles aparecem em logs de CI, saída de debug, error tracking, gravações de tela ou prompts colados no Claude Code.
// src/config/redact.ts
const sensitiveKeyPattern = /(SECRET|TOKEN|PASSWORD|API_KEY|DATABASE_URL|AUTH|COOKIE|PRIVATE)/i;
export function redactSecrets(input: Record<string, unknown>): Record<string, string> {
return Object.fromEntries(
Object.entries(input).map(([key, value]) => {
if (value === undefined || value === null || value === "") return [key, "<empty>"];
const text = String(value);
if (!sensitiveKeyPattern.test(key)) return [key, text];
return [key, text.length <= 10 ? "<redacted>" : `${text.slice(0, 4)}...${text.slice(-4)}`];
}),
);
}
import { env } from "./env";
import { redactSecrets } from "./redact";
console.info("Loaded config", redactSecrets(env));
Redaction é uma proteção final. O melhor log é aquele que nunca recebe o secret.
4. Injete valores via CI/CD
GitHub Actions permite secrets em nível de repositório, ambiente ou organização. Não reutilize credenciais de produção em testes normais de PR; crie valores de CI com escopo menor.
# .github/workflows/env-check.yml
name: env-check
on:
pull_request:
push:
branches: [main]
jobs:
validate-env:
runs-on: ubuntu-latest
env:
APP_ENV: development
NODE_ENV: test
PORT: 3000
APP_ORIGIN: http://localhost:3000
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
WEBHOOK_SECRET: ${{ secrets.WEBHOOK_SECRET }}
LOG_LEVEL: info
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci
- name: Mask runtime-only values
run: echo "::add-mask::$APP_ORIGIN"
- run: npx tsx src/config/env.ts
- run: npm test -- --runInBand
Nem todo workflow recebe secrets nas mesmas condições. Fork PRs, reusable workflows e eventos automatizados podem ter restrições. Mantenha o job de validação explícito e não escreva secrets em arquivos gerados.
5. Docker, Vercel e Cloudflare
No Docker, não coloque ENV API_KEY=... no Dockerfile. Para teste local, um env file é aceitável; em produção, prefira o mecanismo de secrets do runtime.
# local only
docker run --rm --env-file .env.local my-app:latest
Se o runtime expõe secrets como arquivos, suporte a convenção NAME_FILE:
// src/config/secret-file.ts
import fs from "node:fs";
export function readEnvOrFile(name: string): string | undefined {
const direct = process.env[name];
if (direct) return direct;
const filePath = process.env[`${name}_FILE`];
if (!filePath) return undefined;
return fs.readFileSync(filePath, "utf8").trim();
}
Na Vercel, separe Production, Preview e Development. Tenha cuidado com prefixos visíveis no navegador, como NEXT_PUBLIC_. No Cloudflare Workers, valores chegam por bindings, pelo parâmetro env ou por Secrets da plataforma. Não prometa portabilidade automática: documente o ponto de injeção por plataforma e deixe o schema como fonte da verdade.
Revise a configuração de ambiente para Vercel, Cloudflare e Docker.
Não leia nem peça valores reais de produção.
Verifique chaves obrigatórias, public vs secret, build-time vs runtime e notas de rotação.
6. Escreva um playbook de rotação
Rotação de Secrets não deve ser improvisada durante um incidente:
- Identifique escopo: serviço, ambiente, permissões e responsável
- Crie o novo valor com permissões mínimas
- Adicione como
*_NEXTquando for possível aceitar dois valores - Faça deploy aceitando valor antigo e novo por uma janela curta
- Troque o tráfego e verifique health checks
- Revogue o valor antigo
- Procure exposição em Git, CI, logs e prompts
- Atualize
.env.examplee notas operacionais
Webhook secret, API key e senha de banco têm mecânicas diferentes. Documente dono, janela e rollback para cada tipo.
Falhas comuns
| Falha | Causa | Correção |
|---|---|---|
.env commitado | .gitignore veio tarde | Revogue chaves; limpar histórico não basta |
Secret em NEXT_PUBLIC_ | Prefixo público mal entendido | Separe convenções public/private |
console.log(process.env) | Debug apressado | Use redaction e revisão de logs |
| Produção não inicia | Chave obrigatória ausente | Rode src/config/env.ts no CI |
| Valor local no build de produção | Confusão build-time/runtime | Documente injeção por plataforma |
| Chave real colada no Claude Code | Prompt confundido com compartilhamento de secret | Envie nomes e regras, não valores |
Prompt pronto para Claude Code
Implemente gerenciamento de variáveis de ambiente para este projeto.
Requisitos:
- Criar .env.example e .env.production.example
- Manter .env, .env.* e .dev.vars* fora do Git
- Adicionar schema Zod em src/config/env.ts e falhar no boot se houver valor ausente ou inválido
- Centralizar leituras diretas de process.env em src/config/env.ts
- Redigir secrets em logs de diagnóstico
- Adicionar job GitHub Actions que valida env em pull requests
- Escrever notas curtas de deploy para Vercel, Cloudflare e Docker
Não leia API keys reais nem URLs de banco de produção. Trabalhe apenas com nomes de chaves e regras de validação.
Conclusão
Usar Claude Code para gestão de ambiente não significa colar secrets no chat. Significa implementar um contrato: .env.example declara as chaves, Zod valida no boot, logs são redigidos, CI/CD e a plataforma injetam valores reais, e a equipe tem um playbook de rotação.
ClaudeCodeLab oferece consultoria Claude Code, treinamento de equipes, revisão de segurança de repositórios e templates para autenticação, pagamentos, CI/CD e operações de conteúdo. Se sua equipe quer acelerar sem vazar chaves de produção, esta base deve ser uma das primeiras a padronizar.
No repositório de teste de Masa, esse padrão encontrou antes do deploy uma chave de produção ausente, um Webhook secret que poderia aparecer em logs e um .env.example desatualizado. A validação de boot com Zod é simples, mas transforma configuração em contrato verificável.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.