Release / Plan (reuse the published image?) (push) Successful in 3s
CI / Docker image (build and smoke test) (push) Skipped
CI / Format and clippy (push) Successful in 4m32s
CI / Dependency audit (push) Successful in 11s
CI / Tests (Linux) (push) Successful in 9m15s
Release / ci (push) Successful in 9m18s
Release / Build image (arm64) (push) Failing after 7m7s
Release / Build image (amd64) (push) Failing after 13m53s
Release / Publish tags (push) Skipped
Release / Gitea release (push) Skipped
Acaua is a collective SOC daemon consuming only Zeek log files, not a single-user tool needing local hardware like its siblings. Drop the Tauri desktop shell, installers and self-update entirely; the server and its web dashboard ship as a container, updated by pulling a new image tag. Link against the official prebuilt libduckdb instead of compiling it from source via the bundled feature, cutting image build time from 15+ minutes to about 3. Fix a real bug found along the way: as PID 1 in a container, the daemon ignored SIGTERM and was always killed after the stop timeout; it now shuts down gracefully. Also corrects the Pillar 1 beaconing score: the period estimate had a harmonic alias (a perfectly periodic 4h series resolved to 600s), and the old score formula flagged pure random traffic as suspicious (p95 = 0.839 on Poisson noise). The new formula was validated against 300 samples per scenario. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
163 lines
7.6 KiB
Markdown
163 lines
7.6 KiB
Markdown
# Arquitetura
|
||
|
||
[English](../architecture.md) · **Português**
|
||
|
||
O Acauã é um produto único em Rust: um daemon/CLI, um núcleo compartilhado e um
|
||
dashboard web embutido. 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 | Motivo |
|
||
|---|---|---|
|
||
| Core, CLI e daemon | Rust 2021 (MSRV 1.88) | Toolchain única, segurança de memória, sem dependências externas de runtime |
|
||
| API HTTP | Axum + Tokio + Tower HTTP | Superfície assíncrona enxuta, fácil de embutir |
|
||
| Dashboard Web | HTML/CSS/JS vanilla | Sem etapa de build de frontend, assets embutidos no binário via `rust-embed` |
|
||
| Armazenamento analítico | DuckDB + Parquet | Armazenamento colunar embutido obrigatório, linkado à `libduckdb.so` oficial pré-compilada |
|
||
| Implantação | Docker / docker-compose | Daemon de SOC coletivo, usuário não-root sem privilégios, rootfs somente leitura |
|
||
| Validação científica | Python apenas em `research/` | astropy/scipy/networkx para fixtures, nunca distribuído |
|
||
|
||
O workspace possui dois crates: `acaua-core` e `acaua-cli`. Ambos utilizam `version.workspace = true`.
|
||
|
||
## Componentes
|
||
|
||
```
|
||
Logs Zeek ──► acaua-core::zeek ──► eventos normalizados ──► storage
|
||
│
|
||
├──► pillar1 (beaconing)
|
||
├──► pillar2 (sessões persistentes)
|
||
├──► pillar3 (anomalias de protocolo)
|
||
└──► pillar4 (fusão) ──► alertas + evidência
|
||
│
|
||
acaua-cli (serve) ──► API axum ──► dashboard web embutido ──────────┘
|
||
```
|
||
|
||
- `zeek`: parser delimitado e confinamento de caminhos. Nunca segue symlink fora
|
||
da raiz do dataset.
|
||
- `storage`: motor DuckDB em memória sobre arquivos Parquet particionados por
|
||
dia com TTL de sete dias.
|
||
- `pillar1`–`pillar4`: funções puras sobre eventos normalizados. Retornam scores
|
||
e evidência, nunca apenas um float.
|
||
- `fusion`: combinação limitada em ordem fixa. Deve ser simétrica e reproduzível.
|
||
- `doctor`: checagens do ambiente local; reporta linkagem da DuckDB pré-compilada
|
||
e status do Zeek em container.
|
||
- `update`: checa novas versões consultando a API de Releases do Gitea.
|
||
|
||
## As seis correções
|
||
|
||
### 1. Rayleigh/Schuster, não Lomb–Scargle puro
|
||
|
||
Timestamps de eventos são um processo pontual unidimensional: não há valor `y`
|
||
para ajustar. O periodograma primário é, portanto, Rayleigh/Schuster:
|
||
|
||
```text
|
||
P(T) = (2 / N) * [ (sum cos(2*pi*t_i / T))^2 + (sum sin(2*pi*t_i / T))^2 ]
|
||
```
|
||
|
||
Lomb–Scargle generalizado fica reservado para a extensão futura que avaliará um
|
||
atributo real de resposta (como tamanho de payload ou duração) contra o tempo.
|
||
|
||
### 2. Proteger o caso de dispersão zero no MADM
|
||
|
||
Um beacon sintético ou sem jitter produz intervalos perfeitamente iguais. O
|
||
desvio absoluto mediano em relação à mediana (MADM) é então `0.0`. Dividir por
|
||
`0.0` ou tratá-lo como dado ausente é incorreto: dispersão zero é o resultado mais
|
||
suspeito possível. A implementação protege esse caso com atribuição explícita de
|
||
score (`1.0`) e sentinela documentado.
|
||
|
||
### 3. Substituir assimetria de Pearson por assimetria de Bowley
|
||
|
||
A assimetria amostral baseada no terceiro momento central é sensível a outliers
|
||
individuais e pode divergir. A assimetria de Bowley utiliza quartis:
|
||
|
||
```text
|
||
B = (Q3 + Q1 - 2*Q2) / (Q3 - Q1)
|
||
```
|
||
|
||
Ela é limitada em `[-1.0, 1.0]` por definição e resistente a outliers extremos.
|
||
Quando `Q3 == Q1` (dados concentrados), a implementação atribui `0.0` com flag
|
||
sentinela.
|
||
|
||
### 4. Isolar o espaço de pesquisa Python
|
||
|
||
`research/` tem seu próprio ambiente virtual e dependências. Ele é excluído do
|
||
workspace Cargo, ignorado em builds Docker e releases de CI, e testado apenas
|
||
quando scripts de validação de pesquisa forem invocados diretamente.
|
||
|
||
### 5. Modo degradado documentado para NetFlow/IPFIX
|
||
|
||
NetFlow v9 e IPFIX não carregam nomes de consulta DNS, SNI TLS, cadeias de
|
||
certificados ou cabeçalhos HTTP. Qualquer análise que dependa de recursos de
|
||
protocolo fica indisponível. O Acauã documenta isso como modo degradado e nunca
|
||
alega paridade entre registros de fluxo e logs Zeek completos.
|
||
|
||
### 6. Fronteira única HTTP e RPC
|
||
|
||
O daemon expõe uma API Axum em loopback por padrão. A UI web embutida se
|
||
comunica com o daemon exclusivamente por chamadas HTTP padrão.
|
||
|
||
## API
|
||
|
||
A API HTTP é servida por `acaua serve`:
|
||
|
||
| Método | Caminho | Significado |
|
||
|---|---|---|
|
||
| GET | `/api/health` | Liveness e versão |
|
||
| GET | `/api/status` | Uptime, diretório de dados, sumário do dataset |
|
||
| GET | `/api/alerts` | Alertas paginados com evidência |
|
||
| POST | `/api/analyze` | Ingestão e análise de diretório de dataset |
|
||
| GET | `/api/update` | Checagem de novas releases |
|
||
| GET | `/logo.png` | Asset de marca embutido |
|
||
| GET | `/*` | Fallback de UI embutida (`rust-embed`) |
|
||
|
||
`serve` escuta em `127.0.0.1:8787` por padrão. Expô-lo em uma rede externa exige
|
||
um proxy reverso com autenticação.
|
||
|
||
## Armazenamento
|
||
|
||
DuckDB é uma dependência obrigatória linkada dinamicamente à `libduckdb.so`
|
||
oficial pré-compilada (versão 1.5.6 fixada com SHA-256 em `scripts/fetch-libduckdb.sh`).
|
||
Extensões Parquet, JSON e ICU já vêm compiladas estaticamente na biblioteca oficial.
|
||
O esquema consiste em arquivos Parquet particionados por dia sob o diretório de
|
||
dados configurado, com consultas DuckDB em memória e TTL de sete dias.
|
||
|
||
## Atualizações
|
||
|
||
Atualizações do Acauã são distribuídas por imagens de container (`docker compose pull && docker compose up -d`).
|
||
O comando `acaua update` consulta a API de Releases do Gitea para `ciberfauna/acaua` e
|
||
notifica os operadores quando uma tag de versão mais nova estiver disponível. A
|
||
sobrescrita binária em disco (`update --apply`) foi removida: em um daemon de SOC
|
||
coletivo orquestrado via container, atualizações devem ser controladas por puxar
|
||
imagens novas em vez de processos que alteram o próprio executável.
|
||
|
||
## Alternativas rejeitadas
|
||
|
||
1. **Serviço Python com FastAPI/ClickHouse:** Rejeitado. Um runtime Python
|
||
introduziria imagens pesadas e fronteiras de memória desnecessárias; o
|
||
ClickHouse adicionaria um servidor externo e custo operacional para datasets
|
||
que cabem perfeitamente em um armazenamento colunar embutido. O produto é 100% Rust.
|
||
2. **Aplicativo desktop (Tauri / AppImage / NSIS):** Rejeitado. Diferente do Guará
|
||
(forense em disco bruto local) e do Sabiá (hardware Wi-Fi em modo monitor), o
|
||
Acauã não tem dependência de hardware local: é um daemon de SOC multiusuário
|
||
consumindo logs do Zeek. Um wrapper desktop traria manutenção e superfície de
|
||
segurança sem ganho operacional.
|
||
|
||
## Implantação
|
||
|
||
- **Docker Compose (Oficial):** Imagem multi-arch (`git.lucasrguerra.dev.br/ciberfauna/acaua`),
|
||
rodando com usuário não-privilegiado `acaua` (UID 10001), filesystem somente leitura e capabilities revogadas.
|
||
- **Compilação nativa:** `scripts/fetch-libduckdb.sh ~/.cache/acaua/duckdb` seguido de `cargo build --release -p acaua-cli`.
|
||
|
||
## Comportamento em falhas
|
||
|
||
Entradas malformadas são ignoradas com diagnóstico delimitado e contado. Um log
|
||
ausente não é fatal. Um banco corrompido é reportado pelo `doctor` e nunca
|
||
recriado silenciosamente sobre dados existentes.
|
||
|
||
## Versionamento e compatibilidade
|
||
|
||
SemVer 2.0.0. O JSON de evidência carrega uma versão de esquema. Qualquer
|
||
alteração em uma pontuação ou no formato de evidência é breaking change para
|
||
versões minor `0.x` e deve constar no changelog.
|