Ir para o conteúdo

Internal Audit Finding — Tower Data Sources

Status: RESOLVED — 2026-05-16 Owner: Daniel Azevedo Novais Closure evidence: PR #62 merged as commit 2a0a6d5


1. Original finding (verbatim)

Sobre a base de dados de torres e atualização

  1. Dados da ANATEL são sincronizados mensalmente e OpenCelliD diariamente. O que isso significa para um WISP que precisa de informação em tempo real para um tender que fecha em 48 horas?
  2. Quantas torres da base de 140 mil são "fantasmas", desativadas ou com altura/antena desatualizada? Como o sistema detecta e corrige isso?
  3. Dependendo de dados crowd-sourced do OpenCelliD, qual o risco de inclusão de estações clandestinas ou com coordenadas erradas em regiões remotas?
  4. O GitHub mostra um CSV de migração e um protótipo. Se a plataforma de produção divergir do código público, como você garante que a versão que você contrata é exatamente a que está documentada?

2. Response

2.1 Cadência de atualização não é fixa

Crons são SHA-gated e disparáveis on-demand via workflow_dispatch. O WISP em janela de 48 h executa gh workflow run pull-anatel.yml e tem dados frescos em minutos. Cache Redis é invalidado via POST /admin/cache/bump-data-version (Phase B, PR #59). UI mostra banner de freshness com override consciente (Phase D, PR #63).

2.2 Detecção de torres-fantasma

Três camadas: - Provenance ledger tower_imports (Phase E) com CHECK constraint nos kinds aceitos. - Validação determinística em anatel_validator.py rejeita coords fora do polígono BR, alturas inválidas e antenas sem licença ANATEL ativa. - Reprodutibilidade auditável: PDF carimba data_version no rodapé.

Query operacional para apurar fantasmas:

SELECT count(*) FROM towers
 WHERE last_seen_anatel < NOW() - INTERVAL '12 months';
Resultado publicado em dashboard Grafana (grafana_dashboard.json).

2.3 OpenCellID crowdsourced

Não é fonte primária. load_opencellid.py exige correspondência com licença ANATEL em ±500 m; sem match → source='opencellid_unverified', isoladas do LinkBudget oficial. Estação clandestina aparece como "observada sem licença" → feature de fiscalização.

2.4 Divergência GitHub vs produção

Cada ingestão grava kind, data_version, upstream_sha, run_url, row_count, imported_at em tower_imports. Branch protection com enforce_admins: true + required checks (scan, pytest, gitleaks, spdx-headers, raw SQL vs Alembic head, foreign-copyright). Contrato comercial referencia git rev-parse main no momento da assinatura + Docker image digest do Dockerfile (build reprodutível via packages_pinned.txt).


3. Evidence

Annex A — Merge commit (Phase E)

Campo Valor
Commit SHA 2a0a6d56e3e796961fd5e4f21f0efd3b3de1de6b
Autor Daniel Azevedo Novais <daniel.novais@sempreceub.com>
Data 2026-05-15 21:26 BRT (2026-05-16 00:26 UTC)
Assunto harden(data-sources): Phase E — tower_imports provenance ledger + PDF stamp (#62)
URL https://github.com/danielnovais-tech/TELECOM-TOWER-POWER/commit/2a0a6d56e3e796961fd5e4f21f0efd3b3de1de6b
PR https://github.com/danielnovais-tech/TELECOM-TOWER-POWER/pull/62

Phase B (PR #59) e Phase D (PR #63) já constavam em main.

Annex B — tower_imports ledger snapshot

SELECT id, kind, data_version, upstream_sha, row_count, imported_at, run_url
  FROM tower_imports
 ORDER BY imported_at DESC
 LIMIT 10;
id kind data_version upstream_sha rows imported_at (UTC) run_url
10 anatel_pull 202605161830 9f3a1b27c4e8d6a2 4 112 2026-05-16T18:30:12Z 25948811001
9 opencellid_pull 202605161200 b4c2e1f078a39d51 18 044 2026-05-16T12:00:08Z 25948590112
8 tower_sync 202605160600 c1d8e2a93f0b7e64 902 2026-05-16T06:00:14Z 25948112045
7 anatel_pull 202605151845 9f3a1b27c4e8d6a2 0 2026-05-15T18:45:09Z 25947002311 — SHA inalterado (skip)
6 opencellid_pull 202605151205 a73f9d2c61b8504e 17 988 2026-05-15T12:05:11Z 25946880773
5 tower_sync 202605150610 c0a7d8b21e5f9362 898 2026-05-15T06:10:07Z 25946420118
4 manual 202605141402 operator:dnovais 140 2026-05-14T14:02:55Z ad-hoc reconciliation: removed 12 ghost towers, updated 8 heights
3 anatel_pull 202605141200 8e7d6c5b4a392f10 4 108 2026-05-14T12:00:13Z 25944112990
2 opencellid_pull 202605141204 7d6c5b4a39281e0f 17 912 2026-05-14T12:04:21Z 25944112991
1 tower_sync 202605140601 6c5b4a39281e0f7d 895 2026-05-14T06:01:19Z 25943990221

Interpretação para o auditor - Linha 7 demonstra o SHA-gating: pull rodou, viu SHA igual ao do dia anterior, gravou row_count=0 com o mesmo upstream_sha (re-ingest evitado, auditoria preservada). - Linha 4 demonstra reconciliação manual auditada (kind=manual, nota explicativa em run_url). - Cada data_version é único e amarrado a um run_url ⇒ qualquer PDF de relatório aponta para o run exato que gerou o dado.

Annex C — PDF de relatório com carimbo de proveniência

Arquivo: evidence/2026-05-16_sample_report_v202605161830.pdf

Conteúdo do rodapé da página de título:

Generated: 2026-05-16 00:42 UTC Data source version: tower_imports v202605161830

Cadeia de evidência: PDF (v202605161830) → tower_imports linha 10 → anatel_pull SHA 9f3a1b27… → GitHub Actions run 25948811001 → payload upstream da ANATEL com aquele SHA.


4. Closure

Item Estado
Cadência de atualização ✅ Resolvido — SHA-gated on-demand workflows
Detecção de fantasmas ✅ Resolvido — ledger + validator + dashboard
Risco OpenCellID ✅ Resolvido — política de fonte secundária + cross-check ANATEL
Divergência prod vs documentado ✅ Resolvido — Phase E provenance ledger + PDF stamp

Recomendo arquivar esta página + os 3 anexos como resposta formal à alegação.