Pools Codex no Hermes: recuperação segura de contas
Um runbook para diagnosticar uma conta OAuth quebrada, reautenticar sem disputar o refresh token e preservar a ordem do pool.
Quando uma conta Codex expira, fica presa em cooldown ou aparece duplicada no pool, o caminho seguro não é apagar e cadastrar de novo às cegas. Primeiro separamos autenticação de quota, identificamos a conta pelo JWT em memória, fazemos backup e só então substituímos o par rotativo de tokens preservando a entrada original.
access_token e refresh_token como um par. Para atualizar uma conta existente, reautentique com um label temporário, valide chatgpt_account_id e sub, faça o merge na entrada original e remova duplicatas somente depois da validação.
O modelo mental correto
Um credential pool reúne várias credenciais do mesmo provider. O Hermes escolhe uma entrada saudável, registra falhas e pode girar para outra. Fallback é diferente: troca para outro provider inteiro.
| Mecanismo | Escopo | Exemplo |
|---|---|---|
| Credential pool | Mesmo provider | carla → michel no openai-codex |
| Fallback provider | Outro provider | openai-codex → deepseek |
No comportamento padrão, fill_first, o Hermes usa a primeira entrada saudável pela sua prioridade numérica. A implementação ordena as entradas por priority; a posição física no array JSON não é a autoridade. O fallback só deve entrar depois que o pool não puder atender à requisição.
401 normalmente é problema de autenticação. Um 429 usage_limit_reached é quota do provider. Reautenticar não cria quota nova, e resetar o status local não libera uma janela que ainda está exaurida no upstream.
Estado desejado e snapshot desta instalação
A configuração operacional mantida neste host é um pool pequeno e explícito. Labels distinguem as contas sem colocar identidade ou segredo no nome.
| Label | Prioridade | Papel | Estado verificado |
|---|---|---|---|
carla |
0 | Principal | Selecionada pelo pool |
michel |
1 | Fallback do mesmo provider | Disponível |
Snapshot local confirmado durante a atualização desta página: Hermes Agent v0.20.5 (2026.8.19), checkout em main, commit 057dcdf2, pool openai-codex com duas credenciais e fallback configurado para deepseek-v4-flash. Isso é um retrato datado, não uma promessa permanente: sempre repita a triagem antes de operar.
O Desktop é apenas a interface. O arquivo e os processos importantes ficam no backend remoto, em ~/.hermes/auth.json — neste host, /home/hermes/.hermes/auth.json. Execute os comandos no backend, não no Mac que exibe a janela do Desktop.
Triagem antes de alterar qualquer coisa
Comece somente com leitura. O objetivo é saber se o problema é token, quota, duplicação, processo antigo ou apenas uma mensagem de status persistida.
date --iso-8601=seconds
hermes --version
hermes auth list openai-codex
hermes auth status openai-codex
stat -c '%A %U:%G %s %y %n' ~/.hermes/auth.json
Para inventariar o pool sem imprimir segredos, mostre apenas metadados não sensíveis:
python3 - <<'PY'
import json
from pathlib import Path
path = Path.home() / ".hermes" / "auth.json"
data = json.loads(path.read_text())
entries = data.get("credential_pool", {}).get("openai-codex", [])
print("count=", len(entries))
for entry in entries:
print({
"label": entry.get("label"),
"priority": entry.get("priority"),
"auth_type": entry.get("auth_type"),
"source": entry.get("source"),
"last_status": entry.get("last_status"),
"last_error_reason": entry.get("last_error_reason"),
"refresh_present": bool(entry.get("refresh_token")),
})
print("singleton_present=", "openai-codex" in data.get("providers", {}))
print("suppressed_sources=", data.get("suppressed_sources", {}).get("openai-codex"))
PY
hermes auth list e hermes auth status confirmam principalmente o estado local e a existência do login. Eles não provam que cada access token funciona no endpoint remoto. Faça uma chamada controlada depois da correção.
Identidade da conta sem expor credenciais
O label é operacional, não é prova de identidade. Para decidir se uma entrada temporária pode substituir uma entrada existente, decodifique o payload do JWT somente em memória e compare:
chatgpt_account_id, usado também no headerChatGPT-Account-Id;sub, o subject do token;- fingerprint local do access token, apenas mascarado;
- presença e validade do refresh token;
- expiração do novo access token.
O relatório deve dizer apenas “identidade coincide” ou “identidade diferente”, além de fingerprints curtos se necessário. Nunca registre JWT, access token, refresh token, id_token, chave API ou o ID completo da conta. Uma entrada com label diferente e a mesma identidade é duplicata até prova em contrário.
Reautenticação segura
1. Fazer backup e reduzir concorrência
Antes de editar o arquivo, faça um backup no backend e preserve sua permissão restrita:
label=carla
stamp=$(date -u +%Y%m%dT%H%M%SZ)
backup="$HOME/.hermes/auth.json.before-$label-$stamp"
cp -a -- "$HOME/.hermes/auth.json" "$backup"
chmod 600 -- "$backup"
stat -c '%A %U:%G %s %n' "$backup"
Durante a troca, não rode outro processo que possa renovar o mesmo OAuth. Em particular, não use um script de consulta de uso com refresh habilitado para entradas hermes-pool. O refresh do Codex é rotativo e pode ser single-use; dois escritores podem produzir refresh_token_reused ou invalid_grant.
2. Fazer login com label temporário
hermes auth add openai-codex --type oauth --label carla-reauth
Autorize a conta correta no navegador. O label temporário evita destruir a entrada original antes de a identidade ser confirmada. O comando aceita também --no-browser quando o login será concluído manualmente.
Depois do login, pode aparecer mais de uma linha para a mesma conta: a entrada manual com o label escolhido e uma entrada device_code sem label operacional, sem que isso represente duas assinaturas. Compare a identidade antes de remover qualquer linha.
Merge controlado na entrada original
A CLI atual oferece add, list, remove, reset, status e logout, mas não oferece um auth update transacional para trocar tokens mantendo a entrada. Por isso, não faça “remove e add” diretamente.
Para a conta que precisa ser recuperada:
- Localize a entrada original pelo label exato, não por índice mutável.
- Valide que o token novo e o token antigo pertencem à mesma conta.
- Confirme que o token novo não está expirado e possui refresh token.
- Faça uma escrita protegida pelo lock do auth store.
- Substitua somente
access_token,refresh_tokene, quando disponível,last_refresh. - Preserve
id,label,priority,source,base_urle os demais metadados OAuth. - Limpe o status antigo:
last_status, timestamps e camposlast_error_*.
id_token nem persista chatgpt_account_id quando ele puder ser derivado do JWT.
Limpeza de duplicatas e auto-semeadura
Após o merge validado, remova somente as entradas temporárias ou duplicadas, usando o label exato:
hermes auth remove openai-codex carla-reauth
# Use o segundo comando somente se a entrada existir e já tiver sido validada:
hermes auth remove openai-codex device_code
Os índices mudam depois de cada remoção. Portanto, não remova “#3” e “#4” de memória. Liste novamente, confira o label e faça uma operação por vez. Uma entrada proveniente do singleton pode reaparecer quando o pool for carregado; depois da limpeza, confira também se o bloco correspondente em providers foi removido ou se a fonte foi suprimida.
Reautenticar com hermes auth add limpa supressões daquele provider de propósito. Isso é correto para um novo login, mas significa que um device_code removido pode voltar a ser semeado durante a operação. Se o pool deve ser o único dono daquela conta, a supressão precisa ser aplicada depois do merge e verificada no arquivo bruto.
Quota, cooldown e refresh não são a mesma coisa
| Sinal | Interpretação | Ação correta |
|---|---|---|
401 token_expired |
Access token inválido ou expirado | Deixar o Hermes tentar refresh; se falhar, reautenticar e fazer merge seguro |
refresh_token_reused |
Refresh rotativo foi consumido por outro processo ou repetido | Parar escritores concorrentes e ressincronizar o par mais novo |
429 usage_limit_reached |
Quota upstream da conta | Consultar o reset real; não tratar como login quebrado |
last_error_reset_at |
Horário de recuperação persistido pelo backend | Usar esse timestamp; não inventar um TTL universal |
hermes auth reset openai-codex limpa estados locais de cooldown/exaustão; não renova token e não fabrica quota. Depois de usar esse comando, leia o auth.json bruto: nesta instalação, o merge defensivo do estado persistido pode readotar um registro antigo de exaustão e fazer o comando parecer bem-sucedido sem resolver o 429. Se isso ocorrer, pare de repetir o reset e classifique como problema de persistência.
Validação depois da mudança
A validação precisa cobrir três camadas: arquivo, endpoint remoto e caminho oficial do Hermes.
1. Arquivo e pool
hermes auth list openai-codex
hermes auth status openai-codex
python3 - <<'PY'
import json
from pathlib import Path
path = Path.home() / ".hermes" / "auth.json"
data = json.loads(path.read_text())
entries = data.get("credential_pool", {}).get("openai-codex", [])
print("pool_count=", len(entries))
print("labels_priorities=", [
(e.get("label"), e.get("priority")) for e in entries
])
print("singleton_present=", "openai-codex" in data.get("providers", {}))
print("suppressed_sources=", data.get("suppressed_sources", {}).get("openai-codex"))
PY
Para o pool mantido neste host, o resultado esperado é exatamente duas entradas, carla com prioridade 0 e michel com prioridade 1. Uma entrada adicional não deve ser aceita só porque aparece como saudável.
2. Endpoint remoto
Valide cada conta, quando necessário, com uma consulta de uso somente leitura no backend. O verificador deve derivar o ChatGPT-Account-Id do JWT em memória, enviar o header junto com o bearer token e relatar apenas label, código HTTP e tamanho da resposta. Não coloque o token em argumento de shell, histórico, log ou documentação.
3. Caminho oficial do Hermes
hermes chat -q 'Responda apenas OK' \
--provider openai-codex \
-m <modelo-configurado> \
-Q
Se houver um gateway ou sessão de longa duração, ele pode manter um objeto de pool em memória. Depois de uma alteração persistente, recarregue ou reinicie o processo do backend de forma supervisionada somente se a validação indicar estado antigo. Não use pkill amplo e não reinicie o Desktop como primeira medida.
O que aconteceu nesta recuperação
O caso real que motivou este runbook seguiu esta sequência:
- A entrada original
carlapermaneceu como prioridade0, mas seu access token estava expirado. - Uma reautenticação produziu
carla-reauthe também uma entrada automáticadevice_code; o pool chegou a quatro linhas. - A comparação em memória mostrou que as três linhas da Carla tinham a mesma identidade de conta; Michel era distinta.
- Foi criado backup com permissão
0600. - O par novo foi mesclado na entrada original, preservando identidade interna, label, prioridade, origem, URL e metadados.
- Os estados antigos de erro foram limpos; as duplicatas e o singleton foram removidos/suprimidos para não voltar a disputar refresh.
- O arquivo voltou a conter exatamente duas credenciais; as duas consultas remotas responderam
HTTP 200; uma chamada real do Hermes respondeuOK.
Nenhum token, refresh token, ID completo ou conteúdo integral de auth.json deve entrar no relatório. O backup é a rede de segurança operacional; mantenha-o privado e não o publique junto com esta página.
Checklist de bolso
- Rodar triagem somente leitura no backend remoto.
- Distinguir
401de429antes de reautenticar. - Parar ou evitar escritores concorrentes de OAuth.
- Fazer backup
0600doauth.json. - Adicionar OAuth com label temporário.
- Comparar
chatgpt_account_idesubem memória. - Mesclar somente o par de tokens na entrada original.
- Preservar
id, label, prioridade, origem, URL e metadados. - Limpar estados antigos e remover duplicatas por label exato.
- Verificar que nenhuma fonte automática recriou uma entrada.
- Validar arquivo, endpoint remoto e uma chamada real do Hermes.
auth logout ou auth reset como atualização de token; não apagar a conta original antes do merge; não copiar o singleton inteiro; não remover por índice depois que o pool mudou; não executar refresh por script externo durante o login; não expor credenciais em logs ou relatórios.
Fontes e evidências
- Documentação oficial do Hermes — Credential Pools
~/.hermes/auth.json— armazenamento persistente do backend; conteúdo sensível não é reproduzido aquiagent/credential_pool.py— prioridade, seleção, cooldown, refresh e sincronização sob lockhermes_cli/auth_commands.py— comandosadd,list,remove,resetestatushermes_cli/auth.py— login OAuth, persistência e sincronização do auth storeagent/credential_sources.py— limpeza e supressão das fontes semeadas automaticamente- Comandos verificados no host:
hermes --version,hermes auth --help,hermes auth list openai-codexehermes auth status openai-codex