,

Phoenix 21 pede senha por padrão? Só fora do 127.0.0.1

Testei o Phoenix 21.0.0: no padrão (127.0.0.1), 4 de 4 chamadas sem senha responderam 200 e uma chave falsa no prompt voltou inteira. Em 0.0.0.0, as 4 deram 401.

Rogério Rodrigues

Rogério Rodrigues

Engenheiro de IA · mestrando em Informática em Saúde (UFSC)

Cartaz de aviso amarelo com faixas pretas: Phoenix 21.0.0 em 127.0.0.1 responde 200 sem senha e o segredo sintético voltou; em 0.0.0.0 responde 401 e o login admin/admin dá 403. 4 de 4 chamadas sem credencial por cenário.

O Phoenix 21.0.0, painel aberto de trace e avaliação de LLM, saiu em 30/10/2026 com a linha “enable auth by default” na release. Fui conferir. No padrão, escutando em 127.0.0.1, mandei um trace com uma chave falsa no prompt e li de volta sem senha nenhuma: 4 de 4 chamadas sem credencial responderam 200, e a chave voltou inteira. Com PHOENIX_HOST=0.0.0.0, as mesmas 4 chamadas deram 401.

A senha vem ligada, sim. Mas só quando o painel escuta fora do loopback. O código e o teste estão em demos/12-phoenix-auth-padrao, no nursia-research-lab.

Por que painel de trace aberto é problema?

Trace de LLM guarda o prompt e a resposta. Se alguém colou uma chave, um CPF ou um trecho de contrato na conversa, isso fica no span. Quem abre o painel lê tudo isso. Ferramenta de observabilidade de IA costuma juntar muito texto sensível num lugar só, e nem sempre recebe o cuidado de um banco de produção.

Por isso a mudança da 21.0.0 importa. Antes, ligar a autenticação exigia definir PHOENIX_SECRET; a release da 21 diz “without requiring PHOENIX_SECRET”. Na 21, a autenticação pode ligar sem essa variável. A pergunta que eu queria responder: liga em que caso?

Como eu testei

Python 3.11, arize-phoenix==21.0.0 instalado do PyPI num venv limpo, CPU, sem chave de API e sem conta. O script sobe o phoenix serve quatro vezes, cada uma com banco novo e portas livres:

  1. PHOENIX_HOST=127.0.0.1, nenhuma variável de auth (o padrão);
  2. PHOENIX_HOST=0.0.0.0, nenhuma variável de auth;
  3. 0.0.0.0 com PHOENIX_ENABLE_AUTH=false;
  4. 0.0.0.0 com PHOENIX_DEFAULT_ADMIN_INITIAL_PASSWORD definida.

Em cada cenário, sem credencial nenhuma, ele manda 1 trace OTLP com um span de LLM cujo prompt carrega sk-SINTETICO-0000-nao-e-chave-real-7f3a. Depois lista projetos, lê os spans, consulta o GraphQL e tenta entrar com admin@localhost / admin. No cenário 4, entra com a senha inicial e repete a listagem com o cookie da sessão, como controle. Por fim, tenta o /healthz pelo IP de rede da máquina, não pelo 127.0.0.1.

O trace é montado direto em protobuf, sem SDK de instrumentação, pra o teste não depender de provedor de modelo:

span = Span(
    trace_id=os.urandom(16), span_id=os.urandom(8), name="llm.chamado-sintetico",
    kind=Span.SPAN_KIND_CLIENT, start_time_unix_nano=agora - 1_000_000,
    end_time_unix_nano=agora,
    attributes=[kv("openinference.span.kind", "LLM"), kv("input.value", PROMPT),
                kv("output.value", "Resumo sintético do chamado.")],
)
st, _, _ = http("POST", base + "/v1/traces", trace_sintetico(), "application/x-protobuf")
st, corpo, _ = http("GET", base + "/v1/projects/default/spans?limit=50")
r["segredo_na_resposta_sem_credencial"] = SEGREDO in corpo

O que deu

Saída de results/resultado.json, sem retoque:

Chamada sem credencial 1 · 127.0.0.1 2 · 0.0.0.0 3 · 0.0.0.0 + auth=false 4 · 0.0.0.0 + senha inicial
gravar trace 200 401 200 401
listar projetos 200 401 200 401
ler spans 200 401 200 401
chave falsa na resposta sim não sim não
GraphQL 200 401 200 401
login admin@localhost / admin 405 403 405 401
login com a senha inicial – – – 204
listar projetos com a sessão – – – 200
/healthz pelo IP da máquina sem conexão 200 200 200

Três leituras.

No padrão, o loopback é a única proteção. O painel não aceita conexão pelo IP de rede (última linha), então quem está em outra máquina não chega nele. Quem está na mesma máquina lê tudo. O 405 no login é a rota /auth/login que nem existe quando a auth está desligada.

Fora do loopback, fecha nas 4 chamadas testadas. As 4 chamadas deram 401: o envio do trace foi recusado, e a leitura também. O login com admin@localhost / admin deu 403: a senha padrão continua sendo admin, mas o Phoenix não abre sessão com ela e manda trocar antes (POST /auth/password-reset). Disso eu gostei. A senha fraca existe, mas não serve pra entrar.

Uma variável reabre as 4 chamadas. PHOENIX_ENABLE_AUTH=false em 0.0.0.0 deu o pior cenário dos quatro: aberto pra rede e sem senha. A variável explícita manda mais que a regra do host.

A regra está numa função curta em phoenix/config.py:

def auth_enabled_for_host(host: Optional[str]) -> bool:
    """Use PHOENIX_ENABLE_AUTH if set; otherwise require auth for non-loopback hosts."""
    if (explicit := _bool_val(ENV_PHOENIX_ENABLE_AUTH)) is not None:
        return explicit
    return not is_loopback_host(host)

Onde isso falha

A função olha o host configurado, não quem está conectando. Daí sai o caso que me preocupa: um Phoenix em 127.0.0.1 com um nginx, um ssh -L ou um kubectl port-forward na frente. Pro Phoenix, ele continua em loopback e segue sem senha. Pra quem acessa pelo proxy, é um painel aberto. Isso eu tirei da leitura do código. Não montei o proxy e não medi.

O mesmo vale pra máquina compartilhada: servidor de pesquisa, VM de time, devcontainer com porta encaminhada pelo editor. Loopback protege da rede, não de quem já está dentro.

O que não funcionou

Na primeira rodada, o controle positivo do cenário 4 deu 401 mesmo depois do login. Achei que fosse bug do Phoenix. Era meu: o dict() que eu fiz dos cabeçalhos juntava os vários Set-Cookie numa string só, e o cookie de acesso chegava quebrado. Troquei por headers.get_all("Set-Cookie") e o login com sessão deu 200. Fica o registro: antes de dizer que a ferramenta falhou, confira o seu cliente HTTP.

Limites do teste

  • Não rodei a versão 20.x. O “antes” vem da release e do guia de migração.
  • Não rodei a imagem Docker, o Helm nem o Databricks. O cenário 2 imita o PHOENIX_HOST=:: que o guia diz que o Docker usa.
  • Testei 4 chamadas da API, não todas as rotas nem a interface web.
  • Não testei PHOENIX_SECRET. Pelo guia, sem ela, quem tem cópia do banco decifra as chaves de provedor salvas no Phoenix.
  • Licença: Elastic License 2.0. Não é OSI. Pelo texto da licença, uso interno é permitido; oferecer o Phoenix como serviço gerenciado a terceiros é restrito. Leia a licença antes.

O que eu faria

O que foi bom: a senha admin não abre sessão, e fora do loopback nada passa sem credencial nas chamadas que testei. O que não foi: a proteção depende do host configurado, e a release fala em “auth by default” sem esse detalhe na frase. Pra quem serve: time que já usa LLM e quer ver prompt e resposta de cada chamada sem mandar trace pra fora de casa.

Se você roda Phoenix no próprio notebook, sozinho, o padrão da 21 resolve. Se roda em servidor, VM compartilhada ou atrás de proxy, eu definiria as três coisas no primeiro deploy, sem esperar o padrão decidir:

export PHOENIX_ENABLE_AUTH=true
export PHOENIX_DEFAULT_ADMIN_INITIAL_PASSWORD="$(openssl rand -base64 24)"
export PHOENIX_SECRET="$(openssl rand -base64 32)"
phoenix serve

Dessas três, só a senha inicial entrou no teste (cenário 4); PHOENIX_ENABLE_AUTH=true e PHOENIX_SECRET eu não rodei isoladas. O padrão novo é bom, mas ele depende de um detalhe (o host) que muda sem ninguém perceber quando o projeto sai do notebook.

O que custa implementar

  • Os clientes quebram primeiro. Com a auth ligada, o envio de trace sem credencial deu 401. Todo app instrumentado precisa de uma API key do Phoenix antes do deploy, senão o painel fica vazio e ninguém percebe.
  • Script que entrava com admin para. O login com a senha padrão volta 403 e um token de troca. Automação de deploy que fazia login assim precisa da senha inicial definida antes do primeiro start.
  • Trocar o PHOENIX_SECRET custa redigitar chave. Pelo guia de migração, definir ou trocar a variável deixa ilegíveis as credenciais já salvas, e você digita de novo as chaves de provedor que ainda usa. Isso eu não testei.
  • Exige um dono. Alguém do time guarda a senha e o segredo num cofre, gera e revoga API key e decide quem entra no painel. Sem essa pessoa, a auth vira mais um segredo num .env esquecido.

Quando eu não usaria

  • O padrão loopback, sem auth explícita, em máquina compartilhada, VM de time ou atrás de proxy.
  • PHOENIX_ENABLE_AUTH=false em qualquer lugar que não seja o meu notebook.
  • O Phoenix como produto que eu ofereço a clientes como serviço gerenciado: a Elastic License 2.0 restringe isso.
  • O painel como única defesa de dado pessoal. O trace grava o prompt cru: a chave falsa voltou inteira no cenário aberto. Se o prompt pode ter CPF, o certo é anonimizar antes de gravar o span, não confiar só na senha do painel.

E fica a pergunta: no seu time, quem tem acesso ao painel onde ficam os prompts?

Fonte e como reproduzir

Código, resultado e teste: github.com/rogeriorrodrigues/nursia-research-lab/tree/main/demos/12-phoenix-auth-padrao (MIT).

pip install -r requirements.txt
python3 demo.py
python3 -m pytest tests -q -s

Fontes primárias: release 21.0.0 do Phoenix (30/10/2026) e guia de migração v20 para v21. Todo dado do teste é sintético; a “chave” é inválida por construção.

English summary: Phoenix 21.0.0 turns authentication on by default only when it listens on a non-loopback host. On the default 127.0.0.1, 4 of 4 unauthenticated calls returned 200 and a synthetic API key in the prompt came back in the spans response; with PHOENIX_HOST=0.0.0.0 the same calls returned 401 and admin/admin login returned 403. A reverse proxy in front of a loopback Phoenix is still open (read from code, not measured).

Demo publicada no repositório do NursIA (PPGINFOS/UFSC, bolsa FAPESC). Publicado em 06/11/2026.

Conhece alguém que precisa ler isso?

Manda pra essa pessoa. Leva dez segundos e é o que mais ajuda esse trabalho a chegar em quem usa IA com dado de verdade.

Rogério Rodrigues

Rogério Rodrigues

Engenheiro de IA há 20 anos em software, mestrando em Informática em Saúde na UFSC. Escrevo o que rodei: IA com dado de saúde, FHIR e LGPD, com código aberto.

Ficou uma dúvida? Discorda de algo? Me chama.

Ver publicações científicas