Classificação automática de env/secrets
A partir da v2.33.0, ao gerar o .deploy.yml o Runner classifica cada variável sozinho. Você não responde mais variável por variável. O motor lê .env/.env.example, o ENV do Dockerfile e o environment: do compose, decide o que é environment: (plain) e o que é secrets:, e escolhe como cada secret é resolvido.
O mesmo motor roda no runner wizard (CLI) e no runner add (quando o repo não tem .deploy.yml). É determinístico — sem IA, sem chute. As mesmas entradas geram sempre o mesmo manifesto.
Onde roda
runner wizard— gerador interativo, com tela de revisão.runner add— geração automática quando o repo não tem.deploy.yml.runner wizard --accept-defaultse ambientes sem terminal (CI, SSH) — modo não-interativo.
Regra 1 — env vs secret
O motor decide entre environment: e secrets: por estas regras, na ordem. A primeira que casar manda:
| Ordem | Regra | Vai para | Exemplos |
|---|---|---|---|
| 1 | Prefixo público | environment: |
NEXT_PUBLIC_*, VITE_*, REACT_APP_*, NUXT_PUBLIC_*, EXPO_PUBLIC_*, PUBLIC_* |
| 2 | Sufixo de infra | environment: |
*_URL, *_HOST, *_PORT, *_PATH, *_DIR, *_ENDPOINT |
| 3 | Nome de secret | secrets: |
*_SECRET, *_PASSWORD, *_TOKEN, *_API_KEY, etc. |
| 4 | Nenhuma | environment: |
qualquer var sem sinal de secret |
O prefixo público vence mesmo com KEY/TOKEN no nome. NEXT_PUBLIC_SUPABASE_ANON_KEY vai para environment: — é pública por design (o build embute ela no bundle do navegador). Já DATABASE_URL, apesar do sufixo _URL, é tratada como secret (a regra 3 de secret HIGH tem prioridade sobre o sufixo de infra).
Regra 2 — como resolver cada secret
Para o que é secret, o motor decide o modo de resolução por categoria:
| Categoria | Exemplos | Resolução no .deploy.yml |
|---|---|---|
| Auto-gerável | JWT_SECRET, SESSION_TOKEN, CSRF_TOKEN, APP_KEY, SECRET_KEY |
"${GENERATE:hex:64}" — o Runner gera um valor novo |
| Externo | STRIPE_SECRET_KEY, SMTP_PASSWORD, GITHUB_TOKEN, SENDGRID_KEY |
"{{::KEY}}" + entra em required: — você fornece no deploy |
| Ambíguo | DATABASE_URL, DB_PASSWORD, secrets genéricos |
Pergunta (interativo) ou "{{::KEY}}" + required: (não-interativo) |
Duas decisões importantes:
- Gera novo para produção. Se um secret auto-gerável já tem valor no
.env(provavelmente um valor de desenvolvimento), o Runner ignora esse valor e gera um novo. Produção não herda o segredo de dev. - Banco é sempre ambíguo. Senhas e URLs de banco (
DATABASE_URL,DB_PASSWORD,REDIS_URL...) nunca são decididas sozinhas — podem ser de um banco interno (gerável) ou externo (você fornece). O Runner pergunta, ou no não-interativo trata como externo ({{::}}+required:).
Pergunta só o ambíguo (modo interativo)
No runner wizard interativo, o Runner resolve sozinho tudo que é claro. Ele só pergunta as variáveis ambíguas:
? ambíguo: DATABASE_URL
classificar como:
> secret: pedir no deploy ({{::}})
secret: usar valor do .env
secret: gerar novo
plain (manter valor)Tela de revisão
Depois de resolver, o wizard mostra uma tabela consolidada para você conferir antes de gravar. Valores de secret aparecem mascarados:
━━━ Revisão env/secrets ━━━
NEXT_PUBLIC_API_URL plain https://api.x # prefixo público (client-side) → plain
LOG_LEVEL plain info # sem sinal de secret → plain
JWT_SECRET secret [gerar novo] # self-mintable → gerado novo p/ prod
STRIPE_SECRET_KEY secret •••••• # provedor externo → required
DATABASE_URL secret {{::deploy}} # ambíguo (você escolheu prompt)Comentários `# DETECTED:` no manifesto
O .deploy.yml gerado documenta de onde veio cada decisão. Isso torna o resultado auditável — você entende e edita com segurança:
environment:
NEXT_PUBLIC_API_URL: https://api.x # DETECTED: prefixo público (client-side) → plain
LOG_LEVEL: info # DETECTED: sem sinal de secret → plain
BASE_DOMAIN: app.exemplo.com # DETECTED: derivado: domínio da instância
secrets:
JWT_SECRET: "${GENERATE:hex:64}" # DETECTED: self-mintable → gerado
STRIPE_SECRET_KEY: "{{::STRIPE_SECRET_KEY}}" # DETECTED: provedor externo → required
DATABASE_URL: "{{::DATABASE_URL}}" # DETECTED: banco → ambíguo
required:
- STRIPE_SECRET_KEY
- DATABASE_URL`required:` + fail-fast
Secrets externos sem valor entram no bloco required:. No deploy, o Runner aborta cedo — antes de tocar o Docker — se alguma variável de required: faltar ou estiver vazia:
runner deploy minha-app
# Error: missing_required: STRIPE_SECRET_KEY, DATABASE_URLForneça os valores e o deploy passa:
runner env set minha-app --key STRIPE_SECRET_KEY --value sk_live_... --secret
runner env set minha-app --key DATABASE_URL --value postgres://... --secret
runner deploy minha-appEnv derivável
O Runner injeta variáveis que dá para deduzir do contexto, sem você definir:
| Variável | De onde vem |
|---|---|
BASE_DOMAIN |
domínio da instância de produção |
CORS_ORIGINS |
vazio, auto-derivado de BASE_DOMAIN (apenas system: sys/api) |
Modo não-interativo
Sem terminal para perguntar (runner add, --accept-defaults, CI, SSH), o motor aplica os defaults seguros:
- Auto-gerável →
${GENERATE:hex:64}. - Externo e ambíguo →
{{::KEY}}+required:(você fornece no deploy).
Nunca grava placeholder vazio (KEY: "") e nunca vaza valor real no manifesto.
Prompt de secret é mascarado (v2.54.0+)
Quando um secret resolvido como "{{::...}}" é pedido no primeiro deploy (via runner add --deploy ou runner deploy), o input entra mascarado — o valor digitado aparece como *** e nunca é ecoado no terminal ou no scrollback:
Praxis merchant secret (oculto): ***********************Só os campos de secrets: são ocultos. Os de environment: (plain) continuam em texto claro, já que não são sensíveis. Isso evita que um secret externo (chave de gateway, senha de banco) fique registrado no histórico do terminal ou em logs de sessão.
Editar depois
A classificação é um ponto de partida. Você sempre pode editar o .deploy.yml gerado. A sintaxe dos placeholders continua válida e documentada em Secrets lifecycle:
"{RANDOM}"/"${GENERATE:hex:N}"— gera valor."{{::Label?default}}"— pede no deploy (com default opcional).- valor literal — usado direto.
Veja também
- Hierarquia env/secrets — o contrato
secret.enc > env > deploy.yml, os 3 papéis do manifesto e caracteres especiais. - Secrets lifecycle — o que acontece depois:
.env/.secrets, bundle encriptado, injeção no container. - Anatomia do .deploy.yml — schema completo do manifesto.
- Wizard state machine — o fluxo de perguntas do
runner wizard.