This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
fact (File ACTivity) is a file integrity monitoring tool designed for PCI DSS compliance. It's implemented as a BPF agent that:
- Attaches BPF programs to LSM (Linux Security Module) hooks in the kernel
- Receives file system events from the kernel via ring buffers
- Enriches events with process and file metadata
- Outputs events via gRPC or JSON for further processing
- Supports hot-reload of configuration via SIGHUP
- Exposes Prometheus metrics
The project requires modern kernel features (BTF symbols, LSM hooks, BPF trampolines) and is tested on RHEL 9.6+/10+, RHCOS 4.16+, and Fedora CoreOS 43.
This is a Cargo workspace with three main crates:
-
fact: Main binary that loads BPF programs, processes events, and handles output
src/bpf/: Rust code for loading and managing BPF programs (uses aya library)checks.rs: Kernel capability detection (e.g.,bpf_d_pathsupport)
src/event/: Event processing and enrichment logicsrc/config/: Configuration parsing and hot-reload viaReloadersrc/output/: gRPC and JSON output handlerssrc/metrics/: Prometheus metrics subsystemexporter.rs: HTTP metrics exposition (prefixstackrox_fact)kernel_metrics.rs: Per-hook LSM event countershost_scanner.rs: HostScanner-specific metrics (scans, inodes, files)
src/host_scanner.rs: Periodic host filesystem scanning and userspace inode trackingsrc/endpoints.rs: HTTP server for Prometheus metrics endpointsrc/rate_limiter.rs: Event rate limiting viagovernorcratesrc/pre_flight.rs: Pre-flight checks (verifies LSM BPF capability)src/host_info.rs: Host mount handling, distro detection, kernel/arch info
-
fact-api: gRPC API definitions generated from protobuf files in
third_party/stackrox/proto -
fact-ebpf: BPF program implementation
src/bpf/*.{c,h}: C code for BPF programs (main.c,checks.c) and headers (event definitions, maps, types, vmlinux)src/lib.rs: Rust bindings and types for BPF maps/events- Build script compiles C code to BPF bytecode and generates Rust bindings via bindgen
- Kernel LSM hooks trigger BPF programs (in
fact-ebpf/src/bpf/main.c);checks.cruns kernel capability probes at startup - BPF programs write events to ring buffer
Bpfworker (infact/src/bpf/mod.rs) reads from ring buffer, sends to channelHostScanner(infact/src/host_scanner.rs) periodically scans monitored paths and handles userspace inode tracking- Events pass through rate limiting (
fact/src/rate_limiter.rs) - Output handlers (in
fact/src/output/) send to gRPC or stdout as JSON
- Cargo build scripts (
build.rsfiles) automatically compile BPF C code - BPF object files are embedded in the Rust binary
- No manual BPF compilation needed for normal development
- Config loaded from YAML files or environment variables/CLI args
Reloadermonitors for SIGHUP and reloads config without restart- Paths to monitor can be specified via
--pathsorFACT_PATHS
# Standard build
cargo build
# Release build (optimized)
cargo build --release
# Check without building
cargo check# Run with sudo (required for BPF)
cargo run --release --config 'target."cfg(all())".runner="sudo -E"'
# With path monitoring
cargo run --release --config 'target."cfg(all())".runner="sudo -E"' -- -p /etc -p /var/log
# Skip pre-flight checks (if LSM hook detection fails)
cargo run --release --config 'target."cfg(all())".runner="sudo -E"' -- --skip-pre-flightFor agents/automated testing, prefer using pytest integration tests (no sudo required):
# Set up Python virtual environment (first time only)
python3 -m venv .venv
source .venv/bin/activate
pip install -r tests/requirements.txt
# Build container image first
make image
# Run integration tests with pytest (recommended for agents)
cd tests/
pytest --image="<image-tag>" # e.g., pytest --image="quay.io/stackrox-io/fact:latest"
# Run specific test file
pytest test_file_open.py --image="<image-tag>"Rust unit tests (for development):
# Run Rust unit tests (excludes BPF tests, no sudo needed)
cargo test
# Run BPF-specific unit tests (requires sudo, avoid in automated workflows)
cargo test --config 'target."cfg(all())".runner="sudo -E"' --features=bpf-testOther test targets:
# Run integration tests via Make (uses ansible, requires VMs)
make integration-tests
# Run performance tests
make performance-tests# Format Rust and C code
make format
# Check formatting without modifying files
make format-check# Build container image
make image
# Build mock server for testing
make mock-serverGenerate compile_commands.json for clangd on x86_64:
bear -- clang -target bpf -O2 -g -c -Wall -Werror -D__TARGET_ARCH_x86_64 fact-ebpf/src/bpf/main.c -o /dev/nullFor arm64, use -D__TARGET_ARCH_aarch64 instead.
- Edit C files in
fact-ebpf/src/bpf/ - Follow existing patterns in
main.cfor LSM hook attachments - Format with
make -C fact-ebpf format - Test with
cargo test --features=bpf-test(requires sudo)
- Event definitions are in
fact-ebpf/src/bpf/events.h(C) andfact-ebpf/src/lib.rs(Rust bindings) - Processing logic is in
fact/src/event/mod.rsandfact/src/event/process.rs - Changes to event structure require updates to both C and Rust definitions
- Configuration schema is in
fact/src/config/mod.rs - Hot-reload logic is in
fact/src/config/reloader.rs - Add unit tests in
fact/src/config/tests.rs
- Preferred testing for agents: Use pytest integration tests in
tests/directory (no sudo required) - Python environment: All Python dependencies must be installed in a virtual environment at
.venv - All BPF operations require root/sudo privileges (avoid in automated testing when possible)
- The
bpf-testfeature gates tests that load actual BPF programs and requires sudo - Build scripts handle BPF compilation automatically - no need to run clang manually
- Pytest integration tests require a built container image (use
make imagefirst) - The project uses
sudo -Eto preserve environment variables when running with elevated privileges - SIGHUP triggers configuration reload without restarting the process
- Proto files live in a git submodule (
third_party/stackrox); rungit submodule update --initafter a fresh clone to buildfact-api