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
7.1 KiB
Architecture
English · Português
Acauã is a single Rust product: one daemon/CLI, one shared core, and one embedded web dashboard. The shipped runtime has no Python and no external database. This document records the structural decisions and the six corrections applied to the original blueprint.
Stack
| Layer | Choice | Why |
|---|---|---|
| Core, CLI and daemon | Rust 2021 (MSRV 1.88) | One toolchain, memory safety, no runtime dependency |
| HTTP API | Axum + Tokio + Tower HTTP | Small async surface, easy to embed |
| Web Dashboard | Vanilla HTML/CSS/JS | No frontend build step, assets embedded in the binary via rust-embed |
| Analytical store | DuckDB + Parquet | Mandatory embedded columnar store, linked to official prebuilt libduckdb.so |
| Deployment | Docker / docker-compose | Collective SOC daemon, unprivileged non-root user, read-only rootfs |
| Scientific validation | Python in research/ only |
astropy/scipy/networkx for fixtures, never shipped |
The workspace has two crates: acaua-core and acaua-cli. Both use version.workspace = true.
Components
Zeek logs ──► acaua-core::zeek ──► normalized events ──► storage
│
├──► pillar1 (beaconing)
├──► pillar2 (persistent sessions)
├──► pillar3 (protocol anomalies)
└──► pillar4 (fusion) ──► alerts + evidence
│
acaua-cli (serve) ──► axum API ──► embedded web dashboard ──────────┘
zeek: bounded parser and path confinement. It must never follow a symlink outside the dataset root.storage: DuckDB in-memory engine over daily partitioned Parquet files with a seven-day TTL.pillar1–pillar4: pure functions over normalized events. They return scores plus evidence, never only a float.fusion: fixed-order, bounded combination. It must be symmetric and reproducible.doctor: local environment checks; reports prebuilt DuckDB linkage and containerized Zeek status.update: checks for updates against the Gitea Releases API.
The six corrections
1. Rayleigh/Schuster, not pure Lomb–Scargle
Event timestamps are a one-dimensional point process: there is no y value to
fit. The primary periodogram is therefore Rayleigh/Schuster:
P(T) = (2 / N) * [ (sum cos(2*pi*t_i / T))^2 + (sum sin(2*pi*t_i / T))^2 ]
Generalized Lomb–Scargle is reserved for the future extension that evaluates a real response attribute (such as payload size or duration) against time.
2. Guard the zero-dispersion case in MADM
A synthetic or unjittered beacon produces equal intervals. The median absolute
deviation from the median (MADM) is then 0.0. Dividing by 0.0 or treating it
as a missing value is wrong: zero dispersion is the most suspicious outcome. The
implementation guards this case with an explicit score assignment (1.0) and a
documented sentinel.
3. Replace Pearson skewness with Bowley skewness
Sample skewness based on the third central moment is sensitive to single outliers and can diverge. Bowley skewness uses quartiles:
B = (Q3 + Q1 - 2*Q2) / (Q3 - Q1)
It is bounded in [-1.0, 1.0] by definition and resistant to extreme outliers.
When Q3 == Q1 (concentrated data), the implementation assigns 0.0 with a
sentinel flag.
4. Isolate the Python research workspace
research/ has its own virtual environment and dependencies. It is excluded
from the Cargo workspace, ignored by Docker builds and CI releases, and tested
only when research validation scripts are invoked directly.
5. Documented degraded mode for NetFlow/IPFIX
NetFlow v9 and IPFIX do not carry DNS query names, TLS SNI, certificate chains or HTTP headers. Any analysis that depends on protocol features is unavailable. Acauã documents this as a degraded mode and never claims parity between flow records and full Zeek logs.
6. Single HTTP and RPC boundary
The daemon exposes an Axum API on loopback by default. The embedded web UI communicates with the daemon exclusively via standard HTTP calls.
API
The HTTP API is served by acaua serve:
| Method | Path | Meaning |
|---|---|---|
| GET | /api/health |
Liveness and version |
| GET | /api/status |
Uptime, data directory, dataset summary |
| GET | /api/alerts |
Paginated alerts with evidence |
| POST | /api/analyze |
Ingest and analyze a dataset directory |
| GET | /api/update |
Check for newer releases |
| GET | /logo.png |
Embedded brand asset |
| GET | /* |
Embedded UI fallback (rust-embed) |
serve binds to 127.0.0.1:8787 by default. Exposing it on an external network
requires an authenticating reverse proxy.
Storage
DuckDB is a mandatory dependency linked dynamically to the official prebuilt
libduckdb.so (version 1.5.6 pinned with SHA-256 in scripts/fetch-libduckdb.sh).
Parquet, JSON and ICU extensions are statically built into the official library.
The schema consists of daily partitioned Parquet files under the configured
data directory, with in-memory DuckDB querying and a seven-day TTL.
Updates
Updates for Acauã are delivered via container images (docker compose pull && docker compose up -d).
The acaua update command queries the Gitea Releases API for ciberfauna/acaua and
notifies operators when a newer version tag is available. Binary self-overwrite
(update --apply) is removed: in a collective SOC daemon running under container
orchestration, updates must be controlled by image pulls rather than self-mutating
processes.
Rejected alternatives
- Python service with FastAPI/ClickHouse: Rejected. A Python runtime introduces heavy container footprints and unnecessary memory boundaries; ClickHouse adds an external server operational burden for datasets that fit an embedded columnar store. The product is 100% Rust.
- Desktop application (Tauri / AppImage / NSIS): Rejected. Unlike Guará (local raw disk forensics) and Sabiá (Wi-Fi monitor-mode hardware), Acauã has no local hardware dependencies: it is a multi-user SOC daemon consuming Zeek logs. A desktop wrapper adds maintenance and security surface without operational benefits.
Deployment
- Docker Compose (Official): Multi-arch container image (
git.lucasrguerra.dev.br/ciberfauna/acaua), running as unprivileged useracaua(UID 10001), read-only root filesystem, dropped capabilities. - Native compilation:
scripts/fetch-libduckdb.sh ~/.cache/acaua/duckdbfollowed bycargo build --release -p acaua-cli.
Failure behavior
Malformed input is skipped with a counted, bounded diagnostic. A missing log is
not fatal. A corrupt store is reported by doctor and never silently recreated
over existing data.
Versioning and compatibility
SemVer 2.0.0. Evidence JSON carries a schema version. Any change to a score or
to the evidence shape is a breaking change for 0.x minor versions and must be
recorded in the changelog.