Rodando Codex CLI e Codex App em paralelo: duas ou mais contas e instâncias no mesmo Mac
English summary (PT-BR article): A practical, opinionated guide to running two or more Codex CLI and Codex App instances and accounts in parallel on the same machine, using
CODEX_HOMEisolation, config profiles, and native inter-instance communication (codex exec,codex queue,codex agents,codex app-server).
Codex CLI e Codex App em paralelo: como rodar duas ou mais contas e instâncias no mesmo Mac
O mesmo binário. O mesmo Mac. Duas contas completamente diferentes.
No primeiro terminal, codex abre autenticado na conta A — a sua conta principal, com os repositórios confiados, os skills, as sessões e a organização de sempre. No segundo terminal, codex abre autenticado na conta B — uma conta separada, com histórico, contexto e permissões próprias. Nenhum toca o estado do outro.
Parece mágica. Não é. É um detalhe de configuração que quase ninguém documenta direito: o Codex não é preso a um único diretório de configuração. Ele resolve tudo — login, config, sessões, skills, histórico — a partir de uma variável de ambiente chamada CODEX_HOME.
Se você entende isso, abre um leque de possibilidades que vai muito além de "rodar dois terminais":
- separar conta pessoal de conta de trabalho sem ficar fazendo logout/login;
- rodar duas instâncias em paralelo no mesmo repositório, cada uma com um modelo ou um profile de permissão diferente;
- isolar uma instância experimental (novo skill, novo provider) sem risco de corromper a principal;
- e, o mais interessante, fazer as duas instâncias conversarem entre si usando recursos nativos do Codex — sem instalar nada.
Este artigo é um tutorial completo, baseado numa configuração real que já roda em produção num Mac: a conta A em ~/.codex e a conta B em ~/.codex-lemon-dev, acessadas por wrappers chamados codex-lemon-dev-cli e codex-lemon-dev.
A tese central é simples:
Uma instância do Codex é definida pelo seu CODEX_HOME. Controle o CODEX_HOME e você controla quantos mundos isolados quiser rodar ao mesmo tempo.
O que uma "instância" do Codex realmente é
Antes de configurar qualquer coisa, vale entender o que você está configurando. Muita gente imagina o Codex como "um app" ou "um CLI". Na prática, o que existe é um binário (codex) que lê, a cada execução, um diretório de estado.
Por padrão, esse diretório é ~/.codex. É lá que moram:
~/.codex/
├── auth.json # login (tokens, account_id, auth_mode)
├── config.toml # modelo, profiles, projetos, MCP servers, features
├── sessions/ # sessões interativas
├── skills/ # skills carregados sob demanda
├── plugins/ # plugins e marketplaces
├── thread_history_1.sqlite # histórico de conversas
├── logs_2.sqlite # logs
├── state_5.sqlite # estado do daemon/agentes
└── ...
Tudo que faz de uma instância "aquela" instância — a conta logada, o modelo, os projetos confiáveis, as conversas passadas — está dentro desse diretório. Não existe um estado global mágico em outro lugar.
Isso tem uma consequência prática enorme:
Se você apontar o CODEX_HOME para outro diretório, você tem uma instância completamente nova. Mesmo binário. Estado zero. Conta própria.
A documentação oficial confirma isso na página de environment variables: o CODEX_HOME sobrepõe o diretório de configuração padrão. O binário não se importa se esse diretório é ~/.codex, ~/.codex-lemon-dev ou qualquer outro caminho que você criar.
Por que você iria querer duas instâncias
Antes de sujar as mãos, deixe o porquê claro. Rodar duas instâncias não é vaidade de power-user. É a resposta a problemas reais.
1. Duas contas, zero logout
Quem trabalha com IA acaba com mais de uma conta OpenAI: uma pessoal, uma da empresa, talvez uma de teste. Fazer codex logout e codex login toda vez é desgastante e arriscado — você perde o contexto, esquece em qual conta está e, pior, mistura histórico.
Com CODEX_HOME isolado, cada conta vive no seu próprio diretório. Você nunca mais faz logout para trocar de conta. Só muda o wrapper que chama.
2. Paralelismo real no mesmo repositório
Dois agentes trabalhando ao mesmo tempo é a base de muita automação: um revisa enquanto o outro implementa, um pesquisa docs enquanto o outro roda testes. Com uma instância só, você fica preso a uma conversa por vez (ou a worktrees, que resolvem outra coisa).
Com duas instâncias, você tem dois cérebros independentes rodando de verdade em paralelo.
3. Isolamento de experimentos
Um novo skill, um novo provider via --oss, uma mudança agressiva de sandbox_mode — nada disso deveria arriscar a instância que você usa todo dia. Uma instância descartável é a forma mais barata de experimentar sem medo.
4. Perfis de risco diferentes
Você pode ter uma instância "fundo total" (sandbox danger-full-access) e outra "somente leitura" (sandbox read-only) apontando para o mesmo repositório. Escolhe a instância conforme o risco da tarefa, sem reconfigurar nada.
Passo 1 — A conta A (a padrão, ~/.codex)
A conta A normalmente já existe. É a que o Codex usa por padrão quando você roda codex sem nenhuma variável especial.
Para conferir qual conta está ativa:
codex login status
Se você ainda não fez login, o Codex oferece três caminhos oficiais:
# 1. Login via ChatGPT (abre o browser, fluxo OAuth)
codex login
# 2. Login via device code (útil em headless / CI)
codex login --device-auth
# 3. Login via API key (lê a chave do stdin)
printenv OPENAI_API_KEY | codex login --with-api-key
O resultado fica gravado em ~/.codex/auth.json. Você pode abrir esse arquivo e ver o campo auth_mode (chatgpt para login de assinatura, ou apikey) e o account_id — é assim que você confirma qual conta está de fato logada.
A página oficial de authentication detalha os métodos de sign-in. Vale a leitura se você trabalha com orgs múltiplas ou workload identity.
Passo 2 — A conta B (isolada em ~/.codex-lemon-dev)
Aqui está o coração do tutorial. Para criar uma segunda instância com uma segunda conta, você não instala nada de novo. Você só cria um diretório limpo e faz login lá dentro.
# Cria o novo CODEX_HOME
mkdir -p ~/.codex-lemon-dev
# Faz login da conta B DENTRO desse diretório
CODEX_HOME=~/.codex-lemon-dev codex login
A partir desse momento, ~/.codex-lemon-dev é um mundo completo: auth.json próprio, config.toml próprio, sessions/ próprias, skills/ próprios.
Confirme que deu certo comparando os dois auth.json:
cat ~/.codex/auth.json | grep account_id
cat ~/.codex-lemon-dev/auth.json | grep account_id
Se os account_id forem diferentes, você tem duas contas rodando lado a lado.
O gotcha que quebra todo mundo: OPENAI_API_KEY
Existe um erro silencioso que faz a conta B autenticar na conta errada — ou, pior, parecer logada sem estar.
O Codex resolve a autenticação nesta ordem de prioridade:
- variável de ambiente (
OPENAI_API_KEY,CODEX_ACCESS_TOKEN); - login salvo no
CODEX_HOME(auth.json).
Se o seu shell exporta OPENAI_API_KEY globalmente (muito comum em .zshrc ou .bashrc), qualquer codex que você rodar vai ignorar o login do auth.json e usar a API key. Isso significa que a sua conta B, logada via ChatGPT, na prática autentica como a API key da conta A — o isolamento vai pro espaço sem nenhum aviso.
A correção é uma linha no wrapper:
unset OPENAI_API_KEY
É exatamente isso que o wrapper codex-lemon-dev-cli faz (você vai vê-lo completo daqui a pouco). Essa linha é a diferença entre "duas contas de verdade" e "duas janelas que no fundo são a mesma chave".
Passo 3 — Wrappers: transformando a troca de conta em um comando
Rodar CODEX_HOME=... codex toda vez funciona, mas é frágil. Você esquece o prefixo, o shell antigo vaza a variável, e o isolamento desanda.
A solução é um wrapper — um script mínimo que encapsula o CODEX_HOME e o unset da API key. Dois wrappers cobrem os dois produtos: CLI e App.
Wrapper do CLI: codex-lemon-dev-cli
Crie ~/.local/bin/codex-lemon-dev-cli:
#!/bin/zsh
# Conta B / Lemon Dev — CLI
# Isola a Conta B em ~/.codex-lemon-dev sem tocar em ~/.codex (Conta A).
set -e
export CODEX_HOME="$HOME/.codex-lemon-dev"
mkdir -p "$CODEX_HOME"
# O .zshrc exporta OPENAI_API_KEY globalmente, o que faria a Conta B autenticar
# por API key em vez do login ChatGPT proprio. Removido apenas neste processo.
unset OPENAI_API_KEY
exec codex "$@"
Torne executável:
chmod +x ~/.local/bin/codex-lemon-dev-cli
Agora, a troca de conta é explícita e à prova de erro:
codex # Conta A (padrão, ~/.codex)
codex-lemon-dev-cli # Conta B (~/.codex-lemon-dev)
Wrapper do App: codex-lemon-dev
O CLI é simples. O Codex App tem uma pegadinha extra: ele é um app Chromium e, por padrão, é single-instance. Se você tentar abrir uma segunda cópia, o macOS simplesmente encaminha para a primeira instância já aberta — e o CODEX_HOME dela prevalece. Ou seja, sem cuidado, a "conta B" do App abre, na real, a conta A.
A correção é dar à segunda instância um --user-data-dir próprio. Cada user-data-dir é um perfil Chromium independente, que roda como processo separado e coexiste com o outro.
#!/bin/zsh
# Conta B / Lemon Dev — Desktop App
# Abre o Codex Desktop com CODEX_HOME apontando para a Conta B.
set -e
export CODEX_HOME="$HOME/.codex-lemon-dev"
mkdir -p "$CODEX_HOME"
unset OPENAI_API_KEY
APP="/Applications/ChatGPT.app"
BIN="$APP/Contents/MacOS/ChatGPT"
if [[ ! -x "$BIN" ]]; then
print -u2 "codex-lemon-dev: $APP nao encontrado."
exec codex app "$@"
fi
# O app e Chromium e e single-instance por user-data-dir. Com um perfil proprio,
# a Conta B abre como processo separado e coexiste com a Conta A.
PROFILE="$CODEX_HOME/chromium-profile"
mkdir -p "$PROFILE"
# Executa o binario direto: `open` nao propaga variaveis de ambiente.
exec "$BIN" --user-data-dir="$PROFILE" "$@"
Dois detalhes que fazem toda a diferença:
exec "$BIN"em vez deopen— oopendo macOS não propaga variáveis de ambiente para o processo filho. Se você usasseopen -a ChatGPT, oCODEX_HOMEse perderia. Executar o binário direto garante que oCODEX_HOMEchegue intacto.--user-data-dirdedicado — é o que quebra o single-instance do Chromium e permite as duas contas coexistirem.
A documentação oficial de desktop app e de config cobre a base do app e do arquivo de configuração, respectivamente.
Passo 4 — Profiles: múltiplas personalidades dentro da mesma instância
Até aqui, o mecanismo foi "um CODEX_HOME por conta". Mas existe um segundo eixo de configuração, mais fino: os profiles.
Um profile é um conjunto de overrides dentro de um config.toml. Você define [profiles.nome] e seleciona com -p ou --profile. Exemplo real:
# ~/.codex/config.toml
[profiles.full_auto]
approval_policy = "on-request"
sandbox_mode = "workspace-write"
[profiles.readonly_quiet]
approval_policy = "never"
sandbox_mode = "read-only"
[profiles.deep_research]
model = "gpt-5"
model_reasoning_effort = "high"
Uso:
codex -p readonly_quiet # só leitura, nunca pede aprovação
codex -p deep_research # modelo maior, raciocínio alto
codex exec -p full_auto "..." # não-interativo com profile
Você também pode fazer override pontual com -c:
codex -c 'model="gpt-5.6-luna"' -c 'sandbox_mode="workspace-write"'
A referência completa de chaves está na página de config reference. E a página de permissions explica os profiles de permissão beta (filesystem e rede).
Combinando os dois eixos
A confusão mais comum é achar que CODEX_HOME e profile fazem a mesma coisa. Não fazem:
| Eixo | O que isola | Mecanismo |
|---|---|---|
CODEX_HOME | conta, sessões, histórico, skills, plugins | diretório de estado diferente |
profile | modelo, permissões, política de aprovação | overrides no mesmo config.toml |
Você usa CODEX_HOME para quem está falando (a conta), e profile para como aquela conta se comporta (modelo, sandbox, aprovação). Os dois se combinam livremente:
CODEX_HOME=~/.codex-lemon-dev codex -p deep_research
Conta B, perfil de pesquisa profunda. Sem conflito.
Passo 5 — Rodando duas instâncias CLI em paralelo
Agora a parte divertida. Você tem dois wrappers (ou um wrapper + o binário puro) e pode abrir duas sessões ao mesmo tempo.
O jeito mais direto é o tmux (ou duas janelas de terminal):
# painel 1 — Conta A
codex
# painel 2 — Conta B
codex-lemon-dev-cli
Cada painel é uma sessão independente: histórico próprio, estado próprio, conta própria. Podem estar no mesmo repositório sem conflito, porque o estado não fica no repositório — fica no CODEX_HOME.
Para trabalho não-interativo (automação, CI, scripts), o codex exec é a ferramenta certa:
# conta A executa uma tarefa e devolve o resultado no stdout
codex exec "liste os TODOs do repositório"
# conta B executa uma tarefa diferente em paralelo
codex-lemon-dev-cli exec "rode os testes e resuma as falhas"
# resume/fork de uma sessão existente
codex exec resume --last
codex exec fork <session-id>
O exec aceita stdin também — o que abre a porta para o próximo tópico:
cat arquivo.txt | codex exec "resuma este arquivo"
Passo 6 — Fazendo as duas instâncias conversarem (nativo)
É aqui que o tutorial fica de verdade interessante. O Codex tem recursos nativos para uma instância falar com a outra. Você não instala nada — são subcomandos que já vêm no binário.
6.1 codex exec encadeado: uma passa o bastão para a outra
O codex exec lê do stdin e escreve no stdout. Isso significa que você pode encadear instâncias como se fossem funções de um pipe:
# Conta A pesquisa; a saída vira entrada da Conta B, que implementa
codex exec "liste os 3 bugs mais críticos do projeto" \
| codex-lemon-dev-cli exec "implemente correções para os bugs listados"
A conta A faz o trabalho de análise, a conta B recebe o resultado e age. Dois cérebros, uma tarefa, zero integração customizada.
Você pode inclusive pedir a uma instância para comandar a outra:
codex exec "gere um plano de migração em markdown" > plano.md
codex-lemon-dev-cli exec "execute o plano em plano.md, passo a passo" < plano.md
6.2 codex queue: injetar mensagem numa sessão que já está rodando
Se a instância B já está em uma sessão interativa (num terminal aberto), você pode enfileirar uma mensagem naquela sessão sem precisar digitar nela:
codex queue --thread <session-id-ou-nome> --message "pode revisar o PR #42 agora?"
A sessão B recebe a mensagem como se você tivesse digitado ali. É a forma mais barata de "chamar a atenção" de outra instância rodando.
6.3 codex agents e app-server: o daemon compartilhado
O Codex tem um daemon local (app-server) que registra todas as sessões de agentes. O subcomando agents lista essas sessões:
codex agents
Isso é a espinha dorsal de uma arquitetura multi-instância: um daemon, várias sessões, todas endereçáveis. Os subcomandos app-server daemon, app-server proxy e remote-control dão controle programático sobre esse daemon (a documentação oficial de app-server detalha o protocolo para embutir o Codex no seu produto).
6.4 codex mcp-server: uma instância vira ferramenta da outra
Para quem quer o grau máximo de integração, o Codex pode se expor como um servidor MCP em stdio:
codex mcp-server
Aí você registra a instância como um MCP server na outra — a conta A passa a enxergar a conta B como uma ferramenta que ela pode invocar. É o caminho de "agente chamando agente" por protocolo aberto.
Nota: a documentação marca o
mcp-serverstandalone como deprecated, apontando para oapp-server(ou o plugin do Codex para Claude Code) como sucessor. Para novos projetos, prefira o app-server. Mas o comando ainda funciona e serve bem como experimento.
6.5 Um exemplo completo de orquestração
Juntando tudo, dá para montar um mini-orquestrador só com o que é nativo:
#!/bin/zsh
# Conta A (estrategista) planeja; Conta B (executor) implementa.
set -e
# 1. Conta A produz o plano
codex exec "analise o repo e gere um plano de refactor em markdown" > /tmp/plano.md
# 2. Conta B executa o plano
codex-lemon-dev-cli exec "execute o plano a seguir" < /tmp/plano.md
# 3. Conta A revisa o diff produzido pela B
git diff | codex exec "revise este diff e aponte riscos"
Três passos, duas contas, nenhuma dependência externa. A conta A planeja e revisa; a conta B executa. Cada uma com seu contexto e sua política de aprovação.
O que pode dar errado (e como evitar)
Configuração de múltiplas instâncias falha de forma bem previsível. Aqui estão os erros que custam caro.
1. OPENAI_API_KEY vazando entre instâncias
Já mencionei, mas repito porque é o erro número um. Se a API key global está no shell, todo codex a usa, ignorando o auth.json do CODEX_HOME. Symptom: "logei na conta B mas ela ainda usa a cota/config da conta A".
Fix: unset OPENAI_API_KEY no wrapper — e confirme com codex login status qual conta está ativa.
2. App "conta B" abrindo na conta A
Symptom: você roda codex-lemon-dev e a janela que abre é a conta A. Causa: single-instance do Chromium.
Fix: --user-data-dir exclusivo por instância, como no wrapper acima.
3. Achar que CODEX_HOME e profile são a mesma coisa
São eixos ortogonais. CODEX_HOME troca a identidade; profile troca o comportamento. Usar um quando precisa do outro gera isolamento incompleto ou overrides confusos.
4. Esquecer que cada CODEX_HOME tem seus próprios skills e plugins
Um skill instalado na conta A não existe na conta B. Se você depende de um skill específico, precisa instalá-lo (ou symlinkar) no CODEX_HOME da segunda instância também. Isso vale para marketplaces, MCP servers e memórias.
5. Sandbox por instância, não por terminais
O sandbox_mode (workspace-write, read-only, danger-full-access) é configurado por instância/profile. Se a conta B rodar com danger-full-access e você não quiser isso, ajuste o config.toml do ~/.codex-lemon-dev, não do ~/.codex. A página de sandboxing cobre os modos.
Um checklist operacional
Antes de usar no dia a dia, passe por este filtro:
-
codex login statusmostra a conta A correta? -
CODEX_HOME=~/.codex-lemon-dev codex login statusmostra a conta B? - Os dois
account_idsão diferentes? - O wrapper do CLI faz
unset OPENAI_API_KEY? - O wrapper do App usa
--user-data-direxclusivo? - Os profiles que você quer existem no
config.tomlcerto? -
codex -p <profile>seleciona o profile esperado? - As duas instâncias rodam em paralelo sem erro?
-
codex execencadeado entre as duas funciona?
Se todos os checks passarem, você tem um setup de multi-instância sólido.
Conclusão
O mercado gosta de falar de modelos e benchmarks. Mas, no uso diário, muito do poder de uma ferramenta como o Codex vem de um detalhe menos glamouroso: onde ele guarda o seu estado, e como você controla isso.