
Um proxy transparente de redação de PII para tráfego de API de LLM. Fica entre uma aplicação e um provedor de LLM (atualmente Anthropic), pseudonimizando dados sensíveis na saída e restaurando-os na entrada. Construído com FastAPI + httpx.
Um proxy transparente de redação de PII para tráfego de API de LLM. Fica entre sua aplicação e o provedor de LLM, pseudonimizando dados sensíveis na ida e restaurando-os na volta.
Seu LLM nunca vê nomes reais, e-mails, IPs ou domínios — ele trabalha inteiramente com pseudônimos estruturados como [email protected]. Sua aplicação recebe de volta os valores originais, de forma transparente.
Ao usar LLMs para operações de segurança, resposta a incidentes ou qualquer tarefa que envolva dados reais de clientes, você corre o risco de enviar PII para APIs de terceiros. Este proxy resolve isso ao:
# 1. Crie sua configuração
cp config.json.example config.json
# Edite config.json com seus domínios internos, entidades conhecidas, etc.
# 2. Execute com Docker
docker build -t llm-token-proxy .
docker run -p 8090:8080 -v ./config.json:/app/config.json llm-token-proxy
# 3. Aponte sua aplicação para o proxy
export ANTHROPIC_BASE_URL=http://localhost:8090/session/my-session/
Pronto. Suas chamadas de API Anthropic agora passam pelo proxy com PII redigida.

Fluxo típico: Aplicação → Token Proxy (redação PII) → API LLM (apenas pseudônimos) → Token Proxy (restaurar originais) → Aplicação
admin de [email protected])Pseudônimos são determinísticos dentro de uma sessão — o mesmo valor real sempre mapeia para o mesmo pseudônimo.
Quando um LLM está analisando logs de segurança, o provedor de hospedagem e a geolocalização de um endereço IP importam — um login de um IP Hetzner na Alemanha conta uma história diferente de um de um ISP residencial nos EUA. A substituição ingênua por IPs de faixa de documentação (ex.: 198.51.100.x) destrói esse contexto.
Com o banco de dados opcional MaxMind GeoLite2-ASN, o proxy substitui IPs reais por um IP diferente do mesmo ASN e sub-rede. O LLM vê um IP com aparência real que resolve para o mesmo provedor de hospedagem e geografia aproximada — mas não é o endereço real.
10.99.99.x (sem contexto de ASN a preservar)198.51.100.x (faixa de documentação)O IP doador é escolhido deterministicamente via HMAC com um salt por sessão, de modo que o mesmo IP real sempre mapeia para o mesmo doador dentro de uma sessão, mas sessões diferentes produzem mapeamentos diferentes.
O proxy acompanha um config.json vazio — sem listas de palavras ou suposições específicas de domínio. O config.json.example incluído é ajustado para operações de segurança com Microsoft Sentinel e Entra ID (mais de 8.000 nomes de tabelas/colunas KQL, termos de permissão da Graph API, domínios de referência de segurança). Se isso corresponde ao seu caso de uso, copie o que precisar dele. Se você estiver usando o proxy para um domínio diferente (saúde, jurídico, finanças, etc.), comece com o config vazio e construa suas próprias listas.
config.json{
"internal_domains": ["yourcompany.com"],
"partner_domains": ["partnercorp.com"],
"internal_ip_ranges": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"],
"known_persons": ["John Smith"],
"known_orgs": ["YourCompany"],
"known_hostnames": ["DC01", "FS01"],
"ner_enabled": true,
"ner_skiplist": [],
"redaction_enabled": true
}
_internal_)spacy + en_core_web_sm)false, o proxy se torna um mero pass-throughfalse, os domínios passam sem modificação (e-mails, IPs, nomes ainda são redigidos). Útil quando os nomes de domínio carregam contexto importante para o LLM (ex.: distinguir outlook.com de protonmail.com) e não são considerados sensíveis.Gerencie listas de permissão e alterne a redação sem reiniciar:
# Visualizar todas as listas de permissão
curl http://localhost:8090/token-proxy/config/whitelist
# Adicionar termos à ner_skiplist (reduz falsos positivos)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "ner_skiplist", "values": ["EvoSTS", "Hetzner"]}'
# Adicionar domínios à lista de permissão (nunca pseudonimizar estes)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "domain_allowlist", "values": ["github.com"]}'
# Desabilitar redação (modo pass-through)
curl -X POST http://localhost:8090/token-proxy/config/status \
-H "Content-Type: application/json" \
-d '{"redaction_enabled": false}'
Categorias de whitelist: ner_skiplist, domain_allowlist, known_persons, known_orgs, known_hostnames
Inspecione o que o proxy está fazendo em tempo real:
# Listar sessões ativas
curl http://localhost:8090/token-proxy/sessions
# Visualizar mapeamentos de pseudônimos de uma sessão
curl http://localhost:8090/token-proxy/sessions/{session_id}/mappings
# Visualizar registro de atividade de redação
curl http://localhost:8090/token-proxy/sessions/{session_id}/log
# Pesquisar mapeamentos
curl http://localhost:8090/token-proxy/sessions/{session_id}/search?q=admin
# Visualizar payloads capturados (o que o LLM realmente viu)
curl http://localhost:8090/token-proxy/sessions/{session_id}/payloads
# Uso de tokens de uma sessão (tokens de entrada/saída em todas as requisições)
curl http://localhost:8090/token-proxy/sessions/{session_id}/usage
# Estatísticas globais (inclui total_tokens em todas as sessões)
curl http://localhost:8090/token-proxy/stats
O proxy registra input_tokens e output_tokens para cada requisição que encaminha — tanto não-streaming (lido do objeto usage da resposta) quanto streaming (analisado de eventos SSE message_start e message_delta). Como o proxy fica entre sua aplicação e o LLM, você obtém um único ponto de controle para medir o consumo de todos os clientes que o compartilham, sem instrumentar cada um.
curl http://localhost:8090/token-proxy/sessions/my-session/usage
# {
# "session_id": "my-session",
# "request_count": 3,
# "input_tokens": 1240,
# "output_tokens": 587
# }
curl http://localhost:8090/token-proxy/stats | jq .total_tokens
# { "input_tokens": 48213, "output_tokens": 19044 }
O uso por requisição também está incluído em /token-proxy/sessions/{session_id}/log sob usage_counts. Apenas contagens brutas de tokens são rastreadas — o preço é deixado a cargo do chamador.
O proxy suporta streaming SSE (stream: true). Pseudônimos são restaurados em tempo real usando uma abordagem de buffer de cauda que lida com pseudônimos divididos entre chunks SSE.
O proxy usa um padrão de adaptador de provedor. Atualmente suporta:
/v1/messages)Veja CONTRIBUTING.md para como adicionar suporte a outros provedores (OpenAI, Google Gemini, etc.).
en_core_web_sm) detecta nomes de pessoas/organizações em inglês. Nomes em outros idiomas podem ser perdidos, a menos que sejam adicionados a known_persons/known_orgs na configuração.admin [at] acme.com, números de telefone, endereços físicos) não serão capturados. O pipeline de detecção é ajustado para dados estruturados de TI/segurança./token-proxy/config/* e /token-proxy/sessions/* não possuem autenticação. O proxy foi projetado para redes confiáveis/internas — não exponha esses endpoints a redes não confiáveis.# Instalar dependências de desenvolvimento
pip install -e ".[dev,ner]"
python -m spacy download en_core_web_sm
# Executar testes
pytest
# Lint
ruff check token_proxy/ tests/
Apache 2.0 — veja LICENSE.
| Tipo de Entidade | Exemplo Interno | Exemplo Externo |
|---|
[email protected] | [email protected] | |
| Domínio | domain-internal-001.com | domain-external-001.net |
| IP | 10.99.99.1 (RFC1918) | IP doador ciente de ASN (veja abaixo) |
| Pessoa | person_internal_001 | person_external_001 |
| Organização | org_internal_001 | org_external_001 |
| Hostname | host_001 | host_001 |
| Variável | Padrão | Finalidade |
|---|
ANTHROPIC_API_BASE | https://api.anthropic.com | URL da API Anthropic upstream |
TOKEN_PROXY_CONFIG_PATH | /app/config.json | Caminho para o arquivo de configuração |
LOG_LEVEL | info | Nível de registro |
GEOIP_ASN_DB_PATH | /app/data/GeoLite2-ASN.mmdb | Banco de dados MaxMind GeoLite2-ASN (opcional) |