Free

Deixando o Claude fazer deploy em produção

Erros de deploy não dão sintoma. Quatro guardrails — vaults separados, scripts EDITOR, verificação por releitura, switch em runtime por env — pra usar o Claude sem queimar tudo.


A grande diferença entre deploy e escrever código: deploy é one-shot, alto risco, e doloroso de reverter. Escreve uma linha quebrada, um test roda e pega. Escreve uma linha de credentials quebrada, só vai saber no primeiro pagamento real — usuários já passaram o cartão, o dinheiro não caiu na sua conta, os logs são só 400.

Recentemente levei o how2claude de dev local para produção com o Claude: conta Stripe live, wallet x402 mainnet, Google OAuth, Kamal secrets. O Claude não sabe quais valores são de teste e quais são reais. Não sabe que sk_live_ vs sk_test_ é uma diferença de uma letra com consequências apocalípticas. As guardrails você coloca.

Guardrail #1: Arquivos de credentials separados para dev e prod

Rails por padrão usa config/credentials.yml.enc, descriptografado por config/master.key. Se você usa esse default, já perdeu.

Se você coloca sk_live_xxx nesse arquivo e aí roda testes localmente, seu código de teste está usando Stripe de produção. Cada rodada cobra um cartão real.

Separa em dois:
- config/credentials.yml.enc + config/master.key: dev/test, guarda sk_test_xxx
- config/credentials/production.yml.enc + config/credentials/production.key: prod, guarda sk_live_xxx

Ambos .key no .gitignore, ambos .enc commitados. production.key vive só na máquina de deploy.

Aí Kamal secrets tem que apontar pro arquivo certo:

 # .kamal/secrets
-RAILS_MASTER_KEY=$(cat config/master.key)
+RAILS_MASTER_KEY=$(cat config/credentials/production.key)

Dentro do container o RAILS_MASTER_KEY agora aponta pra chave de produção, e o que é descriptografado são credentials de produção. Vi o Claude escrever essa linha — o template default do Kamal é config/master.key (o de dev), que silenciosamente faz deploy do Stripe key de dev pra produção.

Guardrail #2: Use um script EDITOR, não copia-cola

bin/rails credentials:edit --environment production abre um editor interativo. O Claude não consegue dirigir editor interativo. Você também não quer colar manualmente uma dúzia de secrets (um typo e arrebenta tudo).

Use esse padrão:

EDITOR="ruby script/set_prod_webhook_secret.rb" \
  bin/rails credentials:edit --environment production
rm script/set_prod_webhook_secret.rb

script/set_prod_webhook_secret.rb fica assim:

# ARGV[0] é o path do arquivo YAML temporário descriptografado
file = ARGV[0]
require "yaml"
data = YAML.load_file(file) || {}
data["stripe"] ||= {}
data["stripe"]["webhook_secret"] = "whsec_GHWObNAKFh2HPOlJpbGmlYfIiKz1C8EY"
File.write(file, data.to_yaml)

O Rails escreve o YAML descriptografado num arquivo temporário, chama seu "EDITOR" com aquele path como argumento, e re-encripta na saída. Seu "EDITOR" é só um script Ruby que modifica cirurgicamente uma chave e salva.

Por que isso é bom:
- Preciso: só toca stripe.webhook_secret, nada mais.
- Idempotente: rodar duas vezes é igual a rodar uma.
- Auditável: o script é o diff. Claude escreve, você dá uma olhada, sabe exatamente o que vai mudar.
- Some ao deletar: depois do rm, não tem credential em texto puro no disco, nem paste de whsec_... no histórico do shell.

Um script por credential: set_stripe_live_key.rb, set_webhook_secret.rb, set_price_ids.rb, set_wallet_address.rb. rm cada um logo depois.

Guardrail #3: Verifica relendo com Rails runner

Depois de escrever, não confia que escreveu certo. Lê de volta:

bin/rails runner -e production "
c = Rails.application.credentials
puts 'sk_live set: ' + c.dig(:stripe, :secret_key).to_s.start_with?('sk_live_').to_s
puts 'webhook_secret set: ' + c.dig(:stripe, :webhook_secret).to_s.start_with?('whsec_').to_s
puts 'wallet prefix: ' + c.dig(:x402, :wallet_address).to_s[0..5]
puts 'wallet len: ' + c.dig(:x402, :wallet_address).to_s.length.to_s
"

A chave é a checagem de prefixo e comprimento, não só imprimir os valores.

  • Um secret live do Stripe sempre tem prefixo sk_live_. Se você lê de volta sk_test_, o Claude colocou a test key no arquivo prod — um bug que você não vai achar até o primeiro pagamento real.
  • Webhook secrets sempre têm prefixo whsec_. Formato certo ou errado a olho nu.
  • Endereços de carteira EVM sempre têm 42 chars, 0x.... Comprimento errado significa que algum outro caractere se infiltrou.

Adicionar checagens de prefixo bloqueia a maioria dos typos, campos faltando, e ambientes misturados.

Guardrail #4: Conmuta em runtime por env, não em deploy na mão

Dá vontade de pensar "eu mudo a network pra mainnet antes do deploy". Esse tipo de switch depende da memória humana. Cedo ou tarde vai te queimar.

Coloca a regra no initializer:

# config/initializers/x402.rb
X402.configure do |c|
  c.wallet_address = Rails.application.credentials.dig(:x402, :wallet_address)
  c.chain = Rails.env.production? ? "base" : "base-sepolia"
end

Mesmo código, testnet (sepolia) em dev, mainnet (base) em prod. Nada pra mudar na hora do deploy. Claude não pode "esquecer de trocar" porque trocar não é trabalho dele.

Mesmo truque pra basescan_tx_url, visibilidade de Plan (planos dev-only não renderizam em prod), seleção de Stripe price ID, etc. Qualquer coisa que difere entre dev e prod, flipa com Rails.env. Não dependa de lembrar na hora do deploy.

O último passo: caminhe o fluxo real você mesmo

Com as quatro guardrails no lugar, você ainda tem que clicar o pagamento de dinheiro real você mesmo da primeira vez.

No artigo anterior Depurando bugs silenciosos com Claude, descrevi o primeiro clique de pagamento x402 em produção: invalid_string at payTo no console. O 43º caractere do endereço da carteira era um ponto de interrogação de largura total que se infiltrou por um IME chinês. Checagens de prefixo não conseguiam pegar (o 0x tava lá), testes não conseguiam pegar (testes não disparam transações reais), só um clique real fez aparecer.

Guardrails de deploy não são pro Claude. São pra você — automatiza o que pode ser automatizado (prefixos, comprimentos, flips de env) pra que a atenção que sobra vá pras interações reais que automação não cobre.


O fluxo completo de Claude-deploy:

  1. Separa credentials de dev e prod em arquivos .enc + .key distintos.
  2. Pra cada valor a setar, escreve um script Ruby one-shot, alimenta via EDITOR=script, deleta imediatamente.
  3. Depois de cada escrita, lê de volta com Rails runner, checando prefixos e comprimentos.
  4. Flipa todas as diferenças dev/prod via Rails.env, não na hora do deploy.
  5. Clica o botão de dinheiro real você mesmo na primeira vez.

Deploy não é "escrever código com stakes maiores". Deploy é "escrever código onde ninguém te diz que você tá errado". As guardrails existem pra transformar "ninguém te diz" em "você sabe em 15 segundos".