Segredos
As três formas de entregar uma credencial ao Pepe, o que cada uma protege de verdade, e o que nenhuma delas resolve.
O Pepe precisa de credenciais: a chave de API de um provedor de modelo, o token de um bot, o segredo que assina um webhook. Existem três jeitos de fornecer isso, e eles se somam, não se substituem.
1. Variável de ambiente (o padrão de sempre)
"api_key": "${OPENAI_API_KEY}"
O arquivo de configuração guarda só o nome da variável, nunca o valor. Um backup vazado ou um commit por descuido não entrega nada. Isso é como o Pepe sempre funcionou e continua funcionando exatamente assim.
2. Um cofre
Em vez de guardar o segredo, um valor de configuração pode simplesmente dizer onde ele mora, e o Pepe busca no momento em que precisa:
// 1Password
"api_key": "exec:op read op://Trabalho/openai/key"
// HashiCorp Vault
"api_key": "exec:vault kv get -field=key secret/openai"
// AWS Secrets Manager
"api_key": "exec:aws secretsmanager get-secret-value --secret-id openai --query SecretString --output text"
Esses três exemplos não são três integrações separadas, é sempre o mesmo contrato:
um comando que imprime o segredo na saída padrão, ponto final. O Pepe não sabe o
que é 1Password, nem existe uma lista fechada de cofres suportados esperando por mais
um nome. O chaveiro do macOS (security find-generic-password -w -s openai), o
gcloud secrets versions access, o pass show, a CLI do Bitwarden, ou um script que
você escreveu hoje de manhã, todos funcionam hoje mesmo, porque todos têm em comum
imprimir um segredo quando rodados.
Um arquivo também serve, e é basicamente isso que uma montagem de secret do Docker ou do Kubernetes é:
"api_key": "file:/run/secrets/openai_key"
O que um cofre te dá de verdade
Revogar uma chave direto no cofre a derruba em até um minuto, sem precisar de ssh,
edição ou reinício de nada. O segredo também não fica exposto no ambiente, então
um agente induzido a rodar env não acha nada ali. E, ao contrário de uma variável de
ambiente, o cofre sabe registrar quem leu o quê.
Quando o próprio cofre precisa de uma credencial
Na maioria das vezes precisa: um token de service account, um endereço, um perfil. Nomeie só o que for estritamente necessário:
"secrets": { "vault_env": ["OP_SERVICE_ACCOUNT_TOKEN"] }
O Pepe não faz ideia do que aquela variável significa: ele só a repassa para o comando que você configurou, e nada mais do ambiente viaja junto. Assim, um comando que busca um segredo não consegue, de carona, ler os outros.
Os custos, sem esconder nada
O valor resolvido fica em cache na memória por 60 segundos, porque abrir um cofre custa algumas centenas de milissegundos, e um Pepe com tráfego alto pagaria esse preço a cada chamada de modelo se não fosse assim. Na prática, isso significa que o segredo chega a viver no processo por até um minuto: a janela fica menor, mas não desaparece.
Um cofre trancado ou fora do ar aparece como um segredo não configurado, nunca como um segredo errado. O Pepe prefere admitir que não tem a chave a tentar autenticar com metade dela.
3. Nenhum dos dois: o agente simplesmente não vê nada disso
Seja qual for o método escolhido acima, uma coisa não muda: o shell do agente não herda os segredos do Pepe.
Vale explicar isso com calma, porque o esquema ${ENV_VAR} costuma sugerir uma
segurança maior do que de fato entrega. Ele tira o segredo do arquivo de
configuração, isso é real. Só que, até pouco tempo, isso não protegia em nada o
agente: o segredo ainda precisava existir em algum lugar para o Pepe usar, e esse
lugar era justamente o processo do qual o shell do agente nasce como filho. Um
echo $OPENAI_API_KEY devolvia a chave direto. Um simples env também, e chegar a
esse comando é o trabalho de uma única prompt injection bem colocada.
Hoje, um comando rodado pelo agente recebe o ambiente do Pepe menos as credenciais:
cada ${VAR} referenciada na configuração (é justamente ler essa variável que a
transforma num segredo do Pepe) e qualquer variável cujo próprio nome já denuncia o
que é (GITHUB_TOKEN, AWS_SECRET_ACCESS_KEY). PATH, HOME e o resto do ambiente
comum continuam lá, porque um agente incapaz de achar o git é um agente quebrado, e
um agente quebrado é o tipo de coisa que faz um humano irritado arrancar todas as
travas de proteção sozinho.
Quando a própria tarefa exige credencial
Às vezes o trabalho que você passa ao agente já nasce credenciado: “acha o login do Postgres no 1Password e roda a migração.” Nesse caso, o ideal é simplesmente pedir em linguagem natural e deixar o agente resolver sozinho, do mesmo jeito que resolve tudo o mais, sem você precisar cabear cada segredo à mão.
Esse é o único caso em que o agente realmente precisa de um segredo no próprio shell:
a CLI do cofre (op) e o token que a destrava. Por isso existe um opt-in deliberado.
Coloque o nome do token do cofre em secrets.expose_env e ele sobrevive à limpeza,
chegando inteiro ao shell do agente:
"secrets": { "expose_env": ["OP_SERVICE_ACCOUNT_TOKEN"] }
A partir daí o agente roda op por conta própria: op vault list, op item get "Prod DB", e usa o que encontrar. A skill vaults, já embutida, ensina o fluxo
inteiro, com a regra que mais importa: preferir sempre op run e op inject,
que entregam o segredo direto a um comando ou a um template sem nunca imprimir o
valor, em vez de simplesmente rodar op read e deixar o valor exposto. Se o op
estiver faltando, o próprio agente instala. E se o token existir mas ainda estiver
sendo removido do shell dele, o agente pode adicionar o nome ao expose_env sozinho,
usando o config_set (que aceita só uma lista de nomes, nunca um valor, e ainda passa
pela barreira de permissão), em vez de esperar você abrir essa porta manualmente.
PGPASSWORD=…, Bearer …, um JWT), antes que aquilo chegue ao modelo ou ao trace. Um env solto, um erro verboso demais, até um valor que o próprio agente leia via op read: tudo isso é pego. Só escapa um segredo que o Pepe nem conhece nem reconhece visualmente como segredo, e é para isso que a skill empurra o agente ao op run, com o escopo do token limitando o resto. Use um token com escopo bem estreito, ou simplesmente não ative isso.Se um token acabar colado no chat
Considere-o comprometido. Não pelo lugar onde ele parou, mas pelos lugares por onde já passou: digitar num chat significa que ele já foi enviado ao provedor do modelo, gravado na conversa e gravado no trace em disco.
Por isso o Pepe salva e te avisa, em vez de recusar a escrita: recusar não desfaz
o vazamento, só deixa você numa posição pior. O caminho certo é revogar aquele token,
emitir um novo e colocá-lo numa variável de ambiente ou num cofre. O pepe doctor
continua alertando sobre isso até você resolver.