Acauã: Network Detection & Threat Hunting, heuristic analysis over a rolling 7-day window using deterministic math (time series, graph theory, information theory) instead of opaque ML models. Third product in the Ciberfauna ecosystem, alongside Guará and Sabiá. Follows the ecosystem's established conventions: OpenWolf project memory, bilingual PT-BR/EN docs and UI, Gitea Actions CI/CD on the same self-hosted runners, Conventional Commits, SemVer, logo and productName handling, MIT OR Apache-2.0 licensing. Architecture decision (made with the project owner after a dedicated consultation, not the original blueprint's suggested stack): Rust throughout, not Python/FastAPI/React. Most of the statistics (Bowley skew, MADM, Shannon entropy, PCR, Z-score, novel-edge detection, pivot chains) reduces to SQL over an embedded DuckDB/Parquet store; only the spectral analysis and graph centrality need host-language code, and both have workable Rust crates. Documented in full, including six corrections to the original blueprint's math (Rayleigh/Schuster instead of a degenerate Lomb-Scargle over bare timestamps, an explicit sentinel for Bowley skew's Q3=Q1 case, Jensen-Shannon instead of unbounded KL, a dedicated long-connection detector, JA4 vs. JA4+ licensing caution, deterministic per-alert evidence), in docs/architecture.md. Skeleton only: crates/acaua-core has the four detection pillars, fusion, storage, zeek log parsing and the updater as typed stubs that compile and return a clear "skeleton" status rather than real detection logic — that is separate, future work. crates/acaua-cli has serve/analyze/doctor/update. apps/gui is a minimal Tauri shell. Also already includes the one-click desktop self-update mechanism shared with Guará and Sabiá (AppImage/.deb/Windows installer), even with no release published yet, for consistency across the ecosystem. No Gitea repository exists for this project yet; this is a local-only commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
8.2 KiB
Arquitetura
English · Português
O Acauã é um único produto Rust: um daemon/CLI, um núcleo compartilhado, uma UI web embutida e um shell Tauri. O runtime distribuído não tem Python nem banco externo. Este documento registra as decisões estruturais e as seis correções aplicadas ao blueprint original.
Stack
| Camada | Escolha | Por quê |
|---|---|---|
| Núcleo, CLI e daemon | Rust 2021 | Uma toolchain, segurança de memória, sem dependência de runtime |
| API HTTP | Axum + Tokio + Tower HTTP | Superfície async pequena e fácil de embutir |
| UI | HTML/CSS/JS vanilla | Sem etapa de build de frontend, assets embutidos no binário |
| Desktop | Tauri 2 | Janela nativa sobre a mesma UI |
| Armazenamento analítico | DuckDB + Parquet (feature opcional duckdb) |
Embutido, colunar, local-first |
| Validação científica | Python só em research/ |
astropy/scipy/networkx para fixtures, nunca distribuído |
O workspace tem dois crates: acaua-core e acaua-cli. A GUI fica em
apps/gui, fora do workspace Cargo, como no Guará e no Sabiá. A spec citou
version.workspace = true para o manifesto do desktop; manter a GUI fora do
workspace torna isso impossível, então o check.sh impõe igualdade de SemVer
entre Cargo.toml, acaua-cli/Cargo.toml, o manifesto do desktop e
tauri.conf.json. É um desvio deliberado, não um esquecimento.
Componentes
Logs Zeek ──► acaua-core::zeek ──► eventos normalizados ──► armazenamento
│
├──► pilar1 (beaconing)
├──► pilar2 (sessões persistentes)
├──► pilar3 (anomalias de protocolo)
└──► pilar4 (fusão) ──► alertas + evidência
│
acaua-cli ──► API axum ──► UI embutida ◄── shell Tauri ────────────┘
zeek: parser limitado e confinamento de caminho. Nunca segue link simbólico para fora da raiz do dataset.storage: escritor DuckDB/Parquet com TTL de sete dias. Desligado por padrão para o esqueleto não exigir build C++.pillar1–pillar4: funções puras sobre eventos normalizados. Devolvem pontuação e evidência, nunca só um float.fusion: combinação limitada e em ordem fixa. Precisa ser simétrica e reproduzível.doctor: checagens do ambiente local; sem chamadas de rede exceto a checagem explícita de atualização.update: cliente de Gitea Releases só para a CLI.
As seis correções
1. Rayleigh/Schuster, não Lomb–Scargle puro
Timestamps de eventos são um processo pontual unidimensional: não existe um y
para ajustar. O periodograma primário é, portanto, Rayleigh/Schuster:
P(f) = |sum_k exp(2*pi*i*f*t_k)|^2 / N
O Lomb–Scargle generalizado só é usado quando existe um y real (por exemplo,
bytes por bucket). Isso evita inventar uma variável dependente e mantém a
distribuição nula honesta.
2. Bowley com sentinela explícito
O coeficiente de Bowley usa quartis. Quando Q3 == Q1, o denominador é zero; a
implementação devolve um sentinela documentado (0.0 com a flag de evidência
degenerate_quartiles) em vez de NaN, infinito ou panic. O sentinela faz parte
do registro do alerta, não é fallback silencioso.
3. Jensen–Shannon, não KL puro
KL diverge quando a distribuição de referência tem zero onde a observada não tem. A fusão usa Jensen–Shannon, que é simétrica, limitada e definida para bins com probabilidade zero com um piso de suavização documentado. Isso torna a pontuação comparável entre pilares e segura para histogramas esparsos.
4. Sessões persistentes têm detector próprio
Uma conexão aberta por horas nunca produz um intervalo curto, então um detector de beaconing pode ignorá-la. O pilar 2 pontua duração, lacunas ociosas e fluxos longos repetidos diretamente. Os dois pilares podem disparar; a fusão trata isso como evidências separadas, não como contagem dupla do mesmo sinal.
5. Cautela com a licença JA4
A impressão digital JA4 de cliente TLS pode ser usada. JA4S, JA4H, JA4X, JA4SSH e JA4L têm licença não-OSI e não são habilitadas por padrão. Qualquer uso futuro exige decisão explícita de licenciamento e opt-in documentado. O conjunto padrão precisa continuar redistribuível sob MIT/Apache-2.0.
6. Determinismo e evidência JSON por alerta
Todo alerta carrega um registro JSON: hash do dataset, versão do código, pilar, entradas, valores intermediários, sentinelas, pesos e pontuação final. As reduções rodam em ordem fixa; iteração de hash map ou agendamento de threads não pode mudar uma pontuação. Com a mesma entrada e versão, a evidência precisa ser byte a byte idêntica.
Fluxo de dados
acaua analyze <dir>valida o diretório, lê logs suportados e normaliza eventos. Neste esqueleto ele valida e devolve um relatório stub.- Eventos normalizados vão para o armazenamento local (quando a feature
duckdbestá ligada) com TTL de sete dias. - Cada pilar roda sobre uma janela determinística e devolve candidatos com evidência.
- A fusão combina candidatos, aplica limiares e grava alertas.
serveexpõe a API e a UI embutida; o shell Tauri abre a mesma UI contra um daemon local ou remoto.
API
| Método | Caminho | Finalidade |
|---|---|---|
| GET | /api/health |
Liveness e versão |
| GET | /api/status |
Uptime, diretório de dados, estado dos pilares |
| GET | /api/alerts |
Alertas paginados com evidência |
| POST | /api/analyze |
Analisar um caminho de dataset |
| POST | /api/update |
Checar atualização da CLI |
| GET | /logo.png |
Asset de marca embutido |
| GET | /* |
Fallback da UI embutida |
O serve escuta em 127.0.0.1:8787 por padrão. Não há camada de autenticação
no esqueleto; expor em interface de rede exige proxy reverso com autenticação.
Armazenamento
DuckDB é dependência opcional (features = ["duckdb"], bundled). O
esqueleto padrão não compila o backend C++. Quando ligado, o schema é de eventos
normalizados append-only mais uma tabela de alertas, com exportação Parquet e
retenção de sete dias. O arquivo do banco fica sob o diretório de dados
configurado e nunca fora dele.
Atualizações
update.rs consulta a API de Gitea Releases do repositório acaua, escolhe o
asset da plataforma atual, baixa por HTTPS com curl, verifica SHA-256 e
substitui o binário da CLI. Ele se recusa a atualizar automaticamente uma GUI em
execução; o desktop só informa que existe versão nova. Isso espelha o updater do
Sabiá e evita um segundo mecanismo de atualização.
O plugin updater do Tauri e seu fluxo de manifesto e assinatura não são usados de propósito: adicionariam um segundo caminho de assinatura e distribuição, e o desktop não deve se auto-atualizar. O caminho único da CLI é mais fácil de auditar e mantém os assets de release em tarballs mais SHA-256.
Alternativas rejeitadas
Um blueprint anterior propunha um serviço Python e um banco colunar externo. Os
dois são rejeitados. Um serviço Python adicionaria um segundo runtime, custo de
empacotamento e uma fronteira de rede dentro de uma ferramenta local-first; um
banco externo adicionaria servidor e custo operacional para datasets que cabem em
um armazenamento colunar embutido. O produto é 100% Rust, com Python confinado a
research/.
Implantação
- Nativa:
cargo build --release -p acaua-cli, depoisacaua serve. - Desktop: bundles Tauri para Linux e Windows, construídos no CI.
- Docker: imagem enxuta de runtime para laboratório; veja docker.md.
Comportamento em falha
Entrada malformada é ignorada com diagnóstico contado e limitado. Log ausente não
é fatal. Store corrompido é reportado pelo doctor e nunca recriado por cima de
dados existentes. Atualização falha mantém o binário atual.
Versionamento e compatibilidade
SemVer 2.0.0. O JSON de evidência carrega versão de schema. Qualquer mudança em
pontuação ou no formato da evidência é quebra para versões menores 0.x e
precisa constar no changelog.