Talus — Architecture
Internal architecture of Talus: the kernel-side eBPF programs, the userspace event pipeline, the sliding-window alerting heuristic, and the output layer.
1. System overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ KERNEL SPACE │
│ syscall entry eBPF tracepoint programs map │
│ ┌───────────┐ ┌──────────────────────────────────┐ ┌──────────────┐ │
│ │ execve │──►│ process-monitor-ebpf │──►│ EVENTS │ │
│ │ openat │ │ #[tracepoint] sys_enter_execve │ │ PerfEventArray│ │
│ └───────────┘ │ #[tracepoint] sys_enter_openat │ └──────┬───────┘ │
│ └──────────────────────────────────┘ │ per-CPU │
└─────────────────────────────────────────────────────────────────┼───────────┘
┌─────────────────────────────────────────────────────────────────▼───────────┐
│ USERSPACE │
│ reader thread ──► MPSC channel ──► Monitor (sliding window + alerting) │
│ │ │
│ TUI │ JSON │ Plain │ Diagnose │
└──────────────────────────────────────────────────────────────────────────────┘
Two crates form the workspace:
| Crate | Role | Toolchain |
|---|---|---|
process-monitor-ebpf | Kernel-side tracepoint programs, #![no_std], aya-ebpf | Rust nightly (-Z build-std) |
process-monitor | Userspace: loader, reader thread, monitor core, output modes | Rust stable |
2. Kernel side — process-monitor-ebpf
Both programs run in tracepoint context on syscall entry, before the kernel
copies arguments, so all userspace pointers are read with bpf_probe_read_user
— never dereferenced. This keeps the code verifier-safe.
The kernel and userspace agree on a fixed #[repr(C)] layout so records are
memcpy'd across the perf buffer without serialization:
pub struct ProcessEvent {
pub event_type: u8, // 0 = EXEC, 1 = OPEN
pub pid: u32,
pub uid: u32,
pub comm: [u8; 16], // process comm (truncated)
pub filename: [u8; 64], // target path (truncated)
}
An 85-byte payload that occupies 92 bytes on the wire — small and fixed-size, which makes per-CPU perf buffering cheap (no allocation, no variable-length encoding in kernel context).
3. Userspace — process-monitor
Startup sequence (Monitor::start)
- Privilege check — bails unless
geteuid() == 0. - Object load —
aya::Ebpf::load_fileparses the compiled eBPF object. - Program load + attach — each
TracePointprogram attaches tosyscalls/sys_enter_execve/sys_enter_openat. - Map hand-off — the
EVENTSPerfEventArraymoves into the reader thread. - Channel — MPSC connects reader thread → monitor.
Reader thread (talus-reader)
- Enumerates online CPUs, opens one
PerfEventArrayBufferper CPU. - Decodes batches into pre-allocated
BytesMutpools (zero per-event allocation in the hot loop). - Counts
events.lost(perf-buffer overruns) and forwardsMsg::Lost. - Idles 1 ms when no buffer has data — ~1 ms latency, near-zero idle CPU.
Monitor core
stats: HashMap<u32, ProcStats> // pid → cumulative stats
windows: HashMap<u32, VecDeque<Instant>> // pid → open timestamps (1 s window)
handle_event records stats, pushes Open timestamps onto the PID's sliding
window, evicts entries older than 1 s, and emits an Alert exactly when the
window crosses the configured threshold (--alert-threshold, 0 disables).
Output layer
Monitor::poll returns a Vec<Output> per tick (Event | Alert) routed by
mode: TUI (frankentui: 7-panel cyberpunk interface — events, process tree,
network, top files, extensions, alerts, heatmap) · JSON (NDJSON) · Plain
· Diagnose (verifies tracepoint IDs under /sys/kernel/tracing/events,
loads + attaches, listens 5 s, prints counters).
4. Data flow summary
kernel userspace reader monitor core output
────────── ───────────────── ───────────── ──────
openat entry ──► EVENTS map ──► perf buffer ──► Msg::Event ──► sliding window ──► TUI / JSON / plain
(per CPU) │ │
└─ Msg::Lost ────► lost counter ──► status bar
└─ Alert (threshold) ──► alerts panel
5. Performance characteristics
| Aspect | Design |
|---|---|
| Kernel overhead | Two tracepoint programs; fixed-size record; no allocation |
| Userspace decode | Pre-allocated BytesMut pools; zero per-event allocation |
| Latency | Events typically visible in < 1 ms |
| Idle CPU | Reader sleeps 1 ms when no buffers have data |
| Memory | Sliding window evicts every poll; maps bounded by live PIDs |
| Binary | Full LTO + strip = "symbols" + panic = "abort" release profile |