Documentação DATGOV
Arquitetura, fluxo de dados e processo de desenvolvimento, publicados diretamente do repositório — sem edição, sem versão paralela.
DATGOV — BIBLE (arquitetura)
> Fonte de decisão de lançamento: docs/LAUNCH_READY_REPORT.md. Este arquivo
> descreve a arquitetura; detalhes por fase em docs/phases-1-6-implementation.md.
Visão
Evidence-First: toda resposta carrega evidência da fonte oficial (URL, hash,
timestamp). Dados comunitários nunca substituem fonte oficial.
Componentes
| Camada | Tech | Onde |
|---|---|---|
| API | FastAPI (backend/datgov/api.py) | container datgov-backend-1 (127.0.0.1:18000) |
| Roteador de fontes | SourceRouter + circuit breaker + cache (backend/datgov/router.py) | idem |
| Rate limit | RedisRateLimiter fixed-window por IP (TTL armado na 1ª batida) | Redis DB 0 (prod) / DB 1 (staging) |
| Persistência | PostgreSQL 16 (schema datgov: clients, api_keys, api_usage_logs…), RLS | datgov-postgres-1 |
| Evidências | EvidenceStore (SQLite content-addressed) | volume |
| MCP p/ IAs | POST /v1/ai/mcp (JSON-RPC tools/list, tools/call) — 11 tools declaradas, 10 realmente funcionais em produção (get_dou_publications existe mas retorna sempre no_authoritative_result — ver seção DOU/InLabs abaixo, bloqueio de infraestrutura, não decisão de negócio nem falta de handler): get_tcu_irregularities (contas.tcu.gov.br/contrata2RS/api/publico/termos-contratuais, filtro por CNPJ local pois o endpoint não filtra no servidor), find_peers_by_cnae (novo 06/09/2026: busca empresas por CNAE com ranking exato/classe/divisão sobre xooriq_cnpj_active_complete, índice btree em cnae_principal), get_procurements (PNCP /v1/contratos?cnpjOrgao=..., janela 365d), get_sanctions (CEIS+CNEP/Portal da Transparência), get_risk_signals/compare_companies (compostos sobre os dois acima), search_cnpj (XooriqCnpjAdapter lê xooriq_cnpj_active_complete — Postgres nativo do host, projeto Xooriq, ~9M CNPJs com refresh semanal automatizado; papel read-only datgov_cnpj_ro, least-privilege, só nas 2 tabelas de CNPJ), check_pep, check_international_sanctions, search_candidato_tse. Deploy real dos 2 tools novos (CNAE + DOU) em produção só em 06/09/2026 — antes disso existiam só no git, nunca tinham sido buildados/restartados | via nginx /api/v1/ai/mcp |
| Watchman (sanções intl.) | moov-io/watchman self-hospedado, containers datgov-watchman (porta 18807 em 127.0.0.1 — restrita após achado real do gate fox-shield, era 0.0.0.0 sem auth —, rede datgov_net), services/watchman/docker-compose.yml. INCLUDED_LISTS=us_ofac,un_csl,eu_csl — 26566 entidades reais indexadas | via WatchmanAdapter → domínio sancoes-intl do SourceRouter |
| Monitoramento contínuo | datgov.monitoring_subscriptions (RLS, mesmo padrão enable-sem-policy das demais tabelas) + backend/datgov/monitor.py::run_once() — reconsulta assinaturas ativas, alerta por e-mail (mailer.py) e por WhatsApp (whatsapp.py, corrigido 2026-09-06 — item P1 "3": achado real de que existiam DOIS sistemas ChatCentral; a versão original usava o antigo (Chatwoot, desativado como painel em 2026-09-04); corrigido para chamar POST /api/messages/send do sistema real que fica, Central Chat Atendimento (atendimento.centralfox.online), sempre via template Meta aprovado datgov_alerta_monitoramento — nunca texto livre, que só entrega dentro da janela de 24h. Pendente: CHATCENTRAL_ALERT_PHONE vazio no secret, aguardando um número real de teste do Sr. Fox para o passo final de verificação end-to-end) só quando content_sha256 muda. Domínios cobertos: pep, sancoes-intl, candidatos-tse, contratacoes (PNCP, adicionado 2026-09-04 — antes disso não era possível assinar "avise quando esse órgão publicar contrato novo"; janela de 7 dias por checagem para não gerar ruído de hash por deslocamento de período) | cron externo (systemd timer), sem dependência nova de scheduler |
| Documentação pública | frontend/app/docs/page.tsx — BIBLE.md/WORKFLOW.md renderizados a partir do próprio repo (conversor markdown→HTML próprio, zero dependência nova) | rota /docs no mesmo frontend, sem subdomínio novo |
| Marca | Kit oficial "Conceito 2 aprovado" (favicons, símbolo no header claro/escuro, logo no schema.org Organization). Arquivo completo em brand-assets/DATGOV_Kit_Conceito_2/ (logos SVG/PNG, redes sociais, WhatsApp, app icons, paleta #0D47A1/#0066FF/#00D4FF/#2B2F36/#F7F9FB) | frontend/public/{favicon*,brand/}; paleta do site (Terminal de Evidências, teal/âmbar) mantida — são identidades visuais distintas, não trocadas |
| Billing | Stripe conta PT acct_1TC3S… (mesma conta oficial do ecossistema), modo LIVE desde 06/09/2026 (achado real: a chave configurada em produção sempre foi sk_test_..., ninguém tinha ativado live; chave live encontrada no cofre e ativada); trial 7 dias em toda assinatura; Starter/Equipe/API-MCP com stripe_price_id live recriados e gravados em subscription_plans (preços test antigos nunca existiram no modo live — objetos Stripe não migram entre test/live). Sessão de checkout live testada de ponta a ponta (cs_live_... real, sem completar pagamento). Pay-as-you-use: Price/Meter metered recriados em modo live, mesmo event_name usado por billing.py::run_once() (agregado de api_usage_logs por dia) | webhook /webhooks/stripe emite api_key + e-mail; payment_provider_id guarda o Stripe customer id; achado FoxShield 06/09/2026 corrigido — client_id do metadata agora é validado contra datgov.clients antes de emitir api_key (IDOR) |
| Frontend | Next.js (frontend/), sistema visual "Terminal de Evidências" (app/globals.css, aprovado 2026-08-05) | datgov-frontend-1 (127.0.0.1:13000) |
| Captura de lead | POST /v1/leads → tabela datgov.leads (RLS) | idem backend |
| Cadastro self-service | Chat na home (signup-chat.tsx) → POST /v1/signup (CPF validado, validators.py) → e-mail com senha temp (mailer.py) → POST /v1/auth/client-login → POST /v1/auth/change-password (forçada, política 8+/maiúscula/minúscula/especial) → GET /v1/auth/me → POST /v1/checkout/create-session | idem backend + frontend/app/dashboard/page.tsx |
| Site IA (S1-S10) | JSON-LD, OG, sitemap/robots (Next.js), llms.txt, data.json, webmcp.js, chat /assistant via gateway LiteLLM | frontend/app/{layout,sitemap,robots}.ts*, frontend/public/, frontend/app/assistant/route.ts |
| OCR | Tesseract (produção) · PaddleOCR (benchmark via GH Actions) | scripts/benchmark_ocr*.py |
| Backup offsite | dump cifrado AES-256 → Cloudflare Workers KV (cron dom 03:20) | scripts/backup_*.sh |
Ambientes
- Prod:
docker compose -f docker-compose.prod.yml -p datgov— backend,
frontend, postgres, redis, nginx, certbot.
- Staging: container
datgov-staging(127.0.0.1:18001), mesma imagem
datgov-backend:latest, Redis DB 1, DATGOV_RATE_LIMIT alto p/ testes de carga.
Padrões
- Secrets: Docker secrets de
/home/fox/.secrets/datgov/(600). Zero em código.
Inclui smtp.env (cópia do vault foxsites_at_centralfox_online, e-mails
saem como "DATGOV <[email protected]>"), litellm_master_key.txt e
xooriq_cnpj_ro_password.txt (papel datgov_cnpj_ro no Postgres do Xooriq).
- Deploy do backend: depois de todo
up -d --force-recreate backend, rodar
docker network connect bridge datgov-backend-1 — necessário pro adapter
xooriq-cnpj alcançar o Postgres nativo do host (172.17.0.1). O Compose não
gerencia esse attach sozinho (limitação real do Docker: a bridge padrão não
aceita alias de rede-escopo, que o Compose sempre tenta criar).
- Métrica de uso:
_persist_usagesó em requisição autenticada (x-api-key);
hoje abre conexão PG por request — pool é o item 1 do plano de escala
(docs/load-test-summary.md).
- Testes:
backend/tests/(pytest,pythonpath=.), CI GitHub Actions
(.github/workflows/ci.yml).
DOU (Diário Oficial da União) via InLabs — código pronto, BLOQUEADO em produção (achado real 06/09/2026)
Status: código implementado e testado como funcional a partir de fora da VPS; não funciona rodando na VPS de produção — bloqueio real de infraestrutura, não decisão de negócio.
- Fonte: Imprensa Nacional (InLabs), gratuita — cadastro self-service confirmado sem cobrança (correção de achado anterior desta mesma BIBLE, que dizia "exige assinatura paga"; nunca foi testado de verdade até 06/09/2026)
- Auth: e-mail + senha, conta real criada e funcional (Docker secrets:
inlabs_email/inlabs_password), login/download/parse de ZIP confirmados funcionando a partir de um IP fora da VPS - Distribuição: ZIP diário por seção (
?p=YYYY-MM-DD&dl=YYYY-MM-DD-DO{1,2,3}[E].zip), busca client-side do CNPJ formatado (000.000.000/0000-00) dentro do XML - Bloqueio real: o WAF (F5 BIG-IP) da Imprensa Nacional rejeita ("Request Rejected") todo tráfego do IP da própria VPS (Contabo/datacenter) — testado e confirmado tanto para
inlabs.in.gov.brquanto para o endpoint público alternativowww.in.gov.br/consulta. Não é um problema de User-Agent, credencial ou código: da VPS, até ohello_worldde exemplo da própria Meta (noutro contexto) funcionaria — aqui é bloqueio de rede antes de qualquer lógica de aplicação. - Relay via Cloudflare Worker — TESTADO 06/09/2026, NÃO FUNCIONA: implementado e publicado um Worker de relay puro (repassa método/corpo/headers, sem inspecionar nada) apontando pra
inlabs.in.gov.br; o mesmo WAF F5 BIG-IP também rejeita ("Request Rejected") o tráfego originado do IP de edge do Cloudflare Workers — não é exclusivo de datacenter tradicional, o bloqueio cobre a faixa de IP do Workers também. Testado com duas configurações de header diferentes, mesmo resultado nas duas. Workers de teste deletados após a verificação (sem custo residual). - Opções restantes, decisão pendente do Sr. Fox: (1) pedir allowlist formal ao suporte do InLabs (único caminho testável que resta); (2) IP residencial/não-datacenter dedicado (custo recorrente); (3) deixar pendente e manter o tool honesto (
no_authoritative_result). - MCP Tool:
get_dou_publications— deployado em produção 06/09/2026, retorna honestamenteno_authoritative_resultenquanto o bloqueio persistir (nunca fabrica dado). Não anunciado no site público até resolver.
Prontidão pra mercado — LGPD, imagem social, headers, auditoria (06/09/2026)
E-mail, Cloudflare e GSC — auditoria e correções (06/09/2026, continuação)
Apps móveis (iOS + Android) via AIFoxApp — reparo de identidade e plano de construção (06/09/2026)
- Pedido do Sr. Fox: reparar o nome dos "aplicativos pré-feitos" do DATGOV e criar o plano de construção dos 2 modelos completos (Apple + Android), tudo via AIFoxApp.
- Investigação real (banco
aifoxappda plataformafoxapps-factory, app_id1b202408-ecf6-4999-97e8-8011ebec1ac4): nome (app_name=DATGOV,package_name=online.centralfox.datgov) 100% consistente nas 6 versões do manifesto white-label e na tabelaapps— sem "Dotgov" nem variante errada em lugar nenhum. - Achado real e corrigido: o ícone dedicado do DATGOV (
apps.icon_url, publicado e ao vivo) nunca chegava aos builds — o endpointPOST /builds/worker/claim-next(fonte única usada pelos dois pipelines reais,android-worker-desktopeios-worker-mac) entregava o manifesto sem mesclar o ícone; todo build feito até então saía com o ícone genérico padrão do chassi. Corrigido emfoxapps-factory/platform/app/routers/builds.py(commit8d83a13), API reiniciada e verificada (/health200), correção vale pra toda a plataforma, não só o DATGOV. - Estado real dos builds: 6 builds Android (2 concluídos, incl. AAB
cf671e7dtestado em emulador real) e 5 builds iOS (3 concluídos, incl. IPA13a06db5assinado no Mac mini M4) — histórico completo, não só o piloto único citado nos docs do AIFoxApp. - Plano de construção completo (rebuild com ícone certo → registro manual nos consoles Play/App Store, únicos gates humanos reais → upload/TestFlight → revisão) documentado em
docs/APP_MOBILE_BUILD_PLAN.md.
- E-mail de suporte:
[email protected]criado no Mailcow (domínio já existia no servidor, mailbox não). Achado crítico corrigido: o DNS do domínio tinhaMX .(nulo, RFC 7505 — declara explicitamente "este domínio não recebe e-mail") eSPF v=spf1 -all(declara "nenhum servidor pode enviar por este domínio") — a caixa teria sido criada mas nunca teria recebido nem enviado nada. Corrigido no Cloudflare (zona real, API Global Key): MX real (mailcow.centralfox.online, prio 10), SPF real (v=spf1 mx ip4:164.68.106.236 -all, mesmo padrão decentralfox.online), e publicado o DKIM que o Mailcow já tinha gerado pro domínio mas nunca tinha sido colocado no DNS (dkim._domainkey.datgov.com.br). Credenciais em~/.secrets/mail/suporte_at_datgov_com_br.env. - Cloudflare — 2 achados de segurança corrigidos: (1)
Always Use HTTPSestava OFF —http://datgov.com.brrespondia 200 servindo o site em texto puro em vez de redirecionar; ligado, agora retorna 301 → https. (2)min_tls_versionestava em1.0(TLS 1.0/1.1, obsoletos/inseguros — relevante por rodar checkout Stripe no mesmo domínio); subido para1.2. Revisado e OK sem necessidade de mudança: SSL modefull, HSTSmax-age=31536000já ativo via nginx, DNSSEC desabilitado (não crítico, não implementado nesta rodada por não ter sido pedido). - Auditoria de grafia do domínio: varredura completa (grep no frontend, backend, docs/BIBLE/CHANGELOG do repo real da VPS + HTML/JSON-LD/llms.txt/data.json/manifest/sitemap/robots ao vivo + campo
url/statement_descriptordos 4 produtos Stripe do DATGOV) — zero ocorrências de "dotgov" ou variantes erradas; 100% consistente comodatgov.com.br. - GSC/sitemap — achado corrigido:
frontend/app/sitemap.tsé mantido manualmente e não incluía/privacidadenem/termos(páginas novas desta sessão) — adicionadas, rebuild+redeploy do frontend, sitemap resubmetido ao Search Console (HTTP 204) e IndexNow disparado pras 2 URLs novas (HTTP 200). Confirmado via GSC API: propriedadesc-domain:datgov.com.brverificada (siteOwner), home comcoverageState: Submitted and indexedeverdict: PASS;/privacidade//termosaindaURL is unknown to Googleno momento da checagem (esperado, acabaram de ser publicadas — sitemap+IndexNow já disparados pra acelerar o crawl).
- LGPD:
/privacidadee/termos(conteúdo real específico do DATGOV, não boilerplate genérico — bases legais art. 7º, direitos do titular art. 18, retenção, compartilhamento com Stripe/provedor de IA do/assistant, transferência internacional). Fluxo de cadastro (signup-chat.tsx) agora exige aceite explícito ("concordo e quero continuar") antes desubmit(); rodapé linka as duas páginas. Achado corrigido no build: template literal decontent.tstinha um backtick não-escapado (`/assistant) que fechava a string cedo e quebrava otsc` — trocado por itálico markdown. - Open Graph:
public/og-image.png(1200×630, brand real — não gerado genérico) referenciado emlayout.tsx(openGraph.images+ blocotwitter). Antes disso o site não tinha preview ao compartilhar link. - Header de segurança:
Referrer-Policy: no-referreradicionado no nginx real (/etc/nginx/sites-enabled/datgov.conf— achado: esse arquivo, nãosites-available, é o que está de fato carregado; os dois haviam divergido). - Auditoria FoxShield DEEP (06/09/2026): rodou por engano contra uma cópia local desconectada do repositório (
/mnt/c/Users/User/Documents/Codex/2026-08-04/que/work/datgov, git órfão sem remote/commits) em vez do repositório real da VPS — alerta de higiene de ambiente registrado para não repetir (auditorias de código deste projeto devem sempre apontar pra/home/fox/projects/datgovna VPS viassh vps). Os 3 achados do relatório eram reais e foram reaplicados manualmente no código de produção: (1) CRITICAL containers rodando como root →USER app/USER nodenos dois Dockerfiles; (2) CRITICAL IDOR no webhook Stripe (client_iddo metadata aceito sem checar se o cliente existe, porquecreate_checkout_sessionaceita qualquerclient_iddo corpo da requisição) → validaçãoselect 1 from datgov.clients where id=%sadicionada reaproveitando a conexão já aberta; (3) já corrigido antes desta auditoria:payment.succeeded(tipo de evento Stripe inexistente, código morto) removido do filtro do webhook. Pendências não-bloqueadoras registradas pelo FoxShield: rate limiting por API key (hoje só por IP) e headers CSP/X-Frame-Options via nginx — ambos fora do escopo desta rodada, não implementados. - Verificação pós-deploy real:
fox_launch_readiness.py datgov.com.br→ 28/28 PASS (antes: OG image e Referrer-Policy em FAIL/WARN).fox_presence_audit.py datgov.com.br→ 16/16 PASS (2 SKIP por falta de credencial GSC utilizável neste script, pré-existente).
DATGOV — WORKFLOW (operação)
Deploy (VPS, diretório /home/fox/projects/datgov)
```bash
docker build -t datgov-backend:latest -f docker/backend.Dockerfile .
docker build -t datgov-frontend:latest -f docker/frontend.Dockerfile .
docker compose -f docker-compose.prod.yml -p datgov up -d --no-deps --force-recreate backend frontend
curl -sf http://127.0.0.1:18000/v1/health # prod local
curl -sf https://datgov.com.br/api/v1/health # prod público
curl -sf https://staging.datgov.com.br/api/v1/health
```
--force-recreate é obrigatório — sem ele, docker compose up só recria o
container se a config do docker-compose.yml mudou; um docker build novo
com a MESMA config fica rodando na imagem antiga (achado real 2026-08-06:
/v1/auth/me deployado mas invisível — container antigo continuava no ar).
Confirmar sempre: `docker exec datgov-backend-1 python3 -c "from datgov.api
import app; print([r.path for r in app.routes])"` mostra a rota nova.
Migration nova (ex.: datgov.leads, datgov.clients.cpf): aplicar direto no
Postgres de produção antes do deploy do backend — `docker exec -i
datgov-postgres-1 psql -U datgov -d datgov < supabase/migrations/<arquivo>.sql`.
Staging usa a mesma imagem; recriar renomeando o antigo (rollback), nunca rm.
Testes
```bash
cd backend && python -m pytest -q # unit/integr.
k6 run -e RATE=150 -e DURATION=60s load-arrival.js # capacidade (staging!)
```
Carga: usar load-arrival.js (constant-arrival-rate) contra 127.0.0.1:18001;
nunca contra produção. Metas e histórico: docs/load-test-summary.md.
Benchmarks OCR
- Tesseract (local):
scripts/benchmark_ocr.py(ground truth regenerável com
scripts/prepare_ocr_ground_truth.py).
- PaddleOCR: workflow
ocr-paddle-benchmark(GitHub Actions,workflow_dispatch,
input limit p/ smoke). Artifacts paddle-benchmark-report-*.
CI
datgov-ci roda em todo push: pytest (backend/) + compileall + build Next.js.
Verificar com gh run list -R PauloFox0105/datgov.
Site IA (S1-S10)
- Verificar os 8 sinais:
curl -sf https://datgov.com.br/{sitemap.xml,robots.txt,llms.txt,data.json,webmcp.js}(200) +curl -s https://datgov.com.br/ | grep -c "application/ld+json\|og:title\|fox-ai-chat". - Atualizar produto/preço/fontes em um único lugar:
frontend/public/llms.txt(fonte única — alimenta também o system prompt do chat/assistant). - Chat usa o gateway LiteLLM via
https://llm.centralfox.online(não127.0.0.1:18895— bloqueado pelo firewall para a subnet docker do DATGOV) com User-Agent de navegador (o WAF rejeita UA padrão do Node). Secret:/home/fox/.secrets/datgov/litellm_master_key.txt. - Rota do chat é
/assistant, nunca/api/*— nginx já roteia todo/api/para o backend Python.
Cadastro self-service (trial 7 dias)
- Fluxo: chat na home (
POST /v1/signup) → e-mail com senha temporária →
POST /v1/auth/client-login → POST /v1/auth/change-password (forçada)
→ GET /v1/auth/me → POST /v1/checkout/create-session (Stripe,
trial_period_days: 7) → webhook ativa e manda a chave de API por e-mail.
- Testar E2E real (nunca simular): signup com CPF de teste válido
111.444.777-35 e um e-mail que você controla de verdade — a senha
temporária só existe no e-mail entregue (Gmail search_threads), não na
resposta da API. Sempre delete from datgov.clients where email=... no
fim do teste.
- Planos → preço Stripe:
subscription_plans.stripe_price_id(não
hardcoded no código — /v1/checkout/create-session recebe o price_id
direto no payload).
- CPF/senha:
backend/datgov/validators.py(dígito verificador real, testado
com 111.444.777-35/-36). Política de senha: 8+ caracteres, maiúscula,
minúscula, caractere especial.
- E-mails:
backend/datgov/mailer.py, vaultsmtp_env(Docker secret),
máx. 2 tentativas. **Nunca envie teste para @centralfox.online
inventado** — o Mailcow rejeita destinatário local inexistente (550
Recipient address rejected); use um e-mail externo real.
Upgrade 2026-08-31 (itens 1-6) — operação
Deployado em produção em 2026-09-02 (commit 8bdd483 em master, merge de feature/sancoes-internacionais). 8 tools MCP confirmadas ao vivo em https://datgov.com.br/api/v1/ai/mcp, /docs HTTP 200, sitemap com 3 URLs, GSC resubmetido, IndexNow pingado (200 em api.indexnow.org/Bing/Yandex). fox_presence_audit.py: 16/16 PASS.
- Watchman (sanções OFAC/UN/EU):
cd services/watchman && docker compose up -d
depois docker network connect datgov_net datgov-watchman (rede separada por
padrão do compose — precisa da conexão manual pra o backend alcançar por nome
de container). Checar saúde: `docker inspect -f "{{.State.Health.Status}}"
datgov-watchman deve dar healthy; curl http://localhost:18807/v2/search?name=X&type=person`
deve responder com entities. Pin de versão obrigatório — a tag :latest
do Docker Hub resolveu para v0.31.3 (desatualizada); usar v0.66.0 ou mais
recente, e sempre setar INCLUDED_LISTS explícito (vazio = 0 listas carregadas
nessa versão, não "carrega tudo" como em versões antigas). **Porta 18807 é
127.0.0.1 de propósito** (achado real do gate fox-shield: estava em 0.0.0.0,
exposta sem auth a qualquer IP sem necessidade — o backend fala com o watchman
por nome de container em datgov_net, nunca pela porta do host). Não reabrir
para 0.0.0.0 sem adicionar autenticação antes.
- Monitoramento contínuo (item 4): agendar
python3 -m datgov.monitorvia
cron/systemd timer (não roda sozinho ainda). Assinaturas ficam em
datgov.monitoring_subscriptions; sem linha ali, run_once() não faz nada.
- Billing pay-as-you-use (item 6): agendar
python3 -m datgov.billingvia
cron, mesmo padrão do monitor. Só reporta uso de cliente com
payment_provider_id populado (Stripe customer id) — o webhook grava isso
automaticamente após a correção do bug, mas clientes que já assinaram ANTES
da correção ficam com payment_provider_id desatualizado até renovar.
- Chave do Portal da Transparência (item 1): secret real via mecanismo
padrão do Docker Compose — secrets.portaldatransparencia_chave (arquivo
em ${DATGOV_SECRETS_DIR}/portaldatransparencia_chave.txt) montado em
/run/secrets/portaldatransparencia_chave no container, lido por
factory._secret("DATGOV_PORTAL_TRANSPARENCIA_CHAVE") (mesmo padrão de
stripe_secret_key/postgres_password). Achado real: a 1ª versão lia de
DATGOV_SECRETS_DIR diretamente (caminho de host), que não existe dentro
do container — nunca teria funcionado em produção. Corrigido antes do
deploy.
- TSE Candidatos (item 3) — RESOLVIDO com dado real:
cdn.tse.jus.br
bloqueia todo cliente automatizado por WAF Akamai (403, testado com e sem
User-Agent de navegador), mas um navegador humano baixa normal. O Sr. Fox
baixou o zip oficial e forneceu; verificado real com CPF de um registro
público (50 colunas confirmadas, NR_CPF_CANDIDATO na posição 20).
Arquivo em backend/data/consulta_cand_2026.zip (NÃO commitado — dado
atualiza 4x/dia no TSE, ficaria stale; está em .gitignore). Montado no
container via volumes: ./backend/data:/app/data:ro +
DATGOV_TSE_CANDIDATOS_URL=file:///app/data/consulta_cand_2026.zip
(mesmo mecanismo file:// já suportado por BaseAdapter via urllib —
nenhum código novo). Para atualizar: baixar o zip mais recente pelo
navegador e substituir o arquivo no host, sem precisar de rebuild/redeploy.
Nota de correção: official-cache (citado antes como fallback genérico)
tem um bug pré-existente — file://data/raw é um caminho malformado que
nunca funcionou; não é o mecanismo real usado aqui.
- Docs públicas:
/docsno frontend já existente lêBIBLE.md/WORKFLOW.md
do repo em build/request time — atualizar os .md já atualiza a página, sem
passo extra de publicação.
Gate FOXQA + FOXSECURITY pré-deploy (upgrade 2026-08-31)
Antes de autorizar deploy de qualquer branch com mudança de superfície (nova
tool MCP, novo serviço, novo endpoint), rodar os dois gates nesta ordem:
1. FOXQA: foxqa_bootstrap_project (se o projeto ainda não existir),
foxqa_register_suite, rodar a suite real (pytest -q) e
foxqa_record_run com o resultado fresco — nunca um número de memória.
foxqa_log_issue para todo NÃO VERIFICADO/bloqueio real, mesmo os que não
bloqueiam o deploy (ex.: bloqueio externo do TSE).
2. FOXSECURITY: agente fox-shield modo deep (pré-lançamento, OWASP) no
diff real da branch. Achados CRITICAL/HIGH bloqueiam até corrigir e
reverificar de verdade (não só aplicar o patch — testar que o problema
sumiu e que o caminho legítimo continua funcionando).
Achado real desta rodada (2026-09-01): check_pep não fazia quote() no cpf
(HIGH) e watchman exposto em 0.0.0.0 sem auth (MEDIUM) — ambos corrigidos e
reverificados no commit 5f47de1 antes de qualquer autorização de deploy.
Segurança
- Gitleaks antes de push:
gitleaks dir . --config .gitleaks.toml - Auditorias e evidências:
security/(hashes, Trivy, resoluções FSA-*) - Backup offsite: cron domingo 03:20 (
scripts/backup_offsite.sh), restauração
testada em docs/offsite-backup-evidence.md.