source
kernelcapture proof harness
This package is the Ardur Linux proof harness for process-exec capture with paired process-exit lifecycle metadata and kernel-effect synthetic receipts.
This package is the Ardur Linux proof harness for process-exec capture with paired process-exit lifecycle metadata and kernel-effect synthetic receipts.
What it currently does
- Correlates
exec/exitprocess events to tool-call receipt candidates. - Emits synthetic kernel-effect receipt fields, including:
correlation_methodcorrelation_confidencecoverage_statuscapture_loss
- Enforces honesty behavior:
- ambiguous attribution =>
insufficient_evidence - degraded/unknown coverage =>
insufficient_evidence - capture loss / consumer lag => degraded
insufficient_evidence - daemon restart gap => unknown
insufficient_evidence
- ambiguous attribution =>
- Includes a Linux-only Phase 2 eBPF MVP smoke path that:
- loads the embedded
sched/sched_process_exec+sched/sched_process_exiteBPF tracepoint programs. - reads scoped process exec+exit lifecycle samples from a ringbuf.
- runs deterministic root and child commands.
- projects the observed exec and exit events through the same correlator.
- loads the embedded
- Includes a local-only daemon custody scaffold and read-only preflight inspector for the future root-owned config/state/socket/bpffs boundary without installing, starting, binding, or pinning anything.
- Defines the local JSON-line launch-wrapper-to-daemon protocol contract as deterministic types/tests only; no server, listener, or socket bind exists.
Capture sources
RunLinuxEBPFExecSmoke(Linux only, privileged/gated)- Loads the generated eBPF object with
github.com/cilium/ebpf. - Attaches
sched/sched_process_execandsched/sched_process_exitthrough tracefs/debugfs. - Emits metadata-only lifecycle events: PID, PPID, TID, PID namespace id, cgroup id, monotonic timestamp,
comm, andexit_codeon exit events. - Does not collect argv, env, file contents, network destinations, or raw command payloads.
- Loads the generated eBPF object with
RingbufProcessSource(Linux only)- Uses
github.com/cilium/ebpfringbuf reader. - Supports an already-pinned ringbuf map path for future daemon integration.
- Reads a fixed process-lifecycle sample layout.
- Carries kernel monotonic sample timestamps separately from wall clock.
- Uses
ReplayEventSource(fallback)- Unprivileged deterministic source for local tests/demos.
- Used to prove correlation/loss/restart behavior when privileged loading is unavailable.
BuildDaemonCustodyPlan(local-only scaffold)- Validates root-owned daemon custody defaults for
/etc/ardur,/var/lib/ardur,/run/ardur, and/sys/fs/bpf/ardur. - Rejects repository-controlled privileged paths when repository-root validation context is provided, plus daemon installation flags, daemon startup flags, permissive modes, and non-permission mode bits.
- Returns a dry-run plan only. It does not create directories, bind sockets, pin maps, install service units, or start a privileged process.
- Validates root-owned daemon custody defaults for
InspectDaemonCustodyPreflight(read-only preflight)- Uses an injectable stat/realpath interface so tests do not depend on host
/etc,/var,/run, or/sys/fs/bpf. - Reports structured findings with check name, path category, expected and observed owner/mode, verdict, and remediation text.
- Distinguishes missing paths, symlinks, wrong type, wrong owner, wrong mode, non-permission mode bits, symlink-aware realpath escape, and repository-controlled privileged paths.
- Treats setuid, setgid, and sticky bits as fail-closed custody failures in this scaffold. That strictness is intentional: inherited special bits must be investigated before a future privileged daemon trusts the path.
- Does not repair paths, create directories, bind sockets, pin maps, install services, or start a daemon.
- Uses an injectable stat/realpath interface so tests do not depend on host
DaemonProtocolRequest/DecodeDaemonProtocolRequest(contract only)- Specifies newline-delimited deterministic JSON for
health,register_session,end_session, andsession_status. - Accepts unprivileged session/mission/trace identity plus observed root PID, PID namespace, cgroup id, event class, and bounded TTL.
- Rejects unknown protocol versions, unknown event classes, missing session ids, unbounded TTLs, trailing non-JSON data, and client-supplied daemon-owned privileged path fields.
- Applies the privileged-field guard recursively and case-insensitively so future clients cannot hide daemon-owned filesystem authority inside metadata.
- Keeps daemon-owned config/socket/bpffs paths out of client messages.
- Specifies newline-delimited deterministic JSON for
Generate the eBPF object
The generated object is committed with the package so ordinary unit tests do not require clang. Regenerate only in a Linux dev image with clang/LLVM/libbpf headers available:
cd go
go generate ./pkg/kernelcapture
Verification commands
Default tests are unprivileged and should pass on macOS and Linux:
cd go
go test ./pkg/kernelcapture -count=1
go test -race ./pkg/kernelcapture -count=1
GOOS=linux GOARCH=arm64 go test ./pkg/kernelcapture -c -o /tmp/kernelcapture-linux-arm64.test
The live eBPF smoke is intentionally gated because it needs a Linux kernel, BTF, tracefs/debugfs, BPF privileges, and enough memlock. In the local Podman-machine proof environment it was run with a rootful privileged container:
sudo podman run --rm --privileged --pid=host --ulimit memlock=-1:-1 \
-v /sys/kernel/tracing:/sys/kernel/tracing:rw \
-v /sys/kernel/debug:/sys/kernel/debug:rw \
-v "$PWD/go:/workspace/go" \
-w /workspace/go \
-e ARDUR_RUN_EBPF_SMOKE=1 \
ardur-ebpf-dev:fedora43 \
go test -v ./pkg/kernelcapture -run TestLinuxEBPF -count=1
Rootless privileged containers can still fail if memlock cannot be raised or tracefs/debugfs are not visible. Treat that as environment evidence, not a product pass.
Privileged boundary
This package does not install a daemon, persist maps, open a service, or manage system startup.
BuildDaemonCustodyPlan records the local-only future daemon boundary as validated data:
- config path:
/etc/ardur/kernelcapture-daemon.toml,0600, root-owned - state dir:
/var/lib/ardur/kernelcapture,0700, root-owned - runtime dir/socket:
/run/ardur/kernelcapture/control.sock, socket0600or0660, root-owned - bpffs dir/map:
/sys/fs/bpf/ardur/process_lifecycle_events, root-owned
It rejects repository-controlled privileged paths when repository-root validation context is supplied, and it rejects any request to install or start a daemon in this scaffold slice. InspectDaemonCustodyPreflight adds the read-only on-disk inspection layer: symlink-aware realpath checks, owner/mode/type observations, and structured remediation text. The scaffold records the future daemon-boundary requirement that repo/mission config must not select privileged map paths; integration with mission config remains future work. For the future daemon path:
pinnedMapPathmust come from daemon-owned privileged config.- Repository / mission config must not control privileged map-path selection.
- Cgroup filtering must only be enabled after daemon-owned code has inserted at least one non-zero cgroup id into the allowlist map.
- Privileged daemon deployments should use:
- root-owned daemon config
- root-owned restrictive bpffs namespace/path
- explicit producer ownership/version checks before trusting samples
Concurrency contract
Correlatoris goroutine-safe and supports concurrent receipt registration and event correlation.- Race-safety is covered by
go test -race ./pkg/kernelcapture.
Current MVP claim boundary
Allowed claim after the gated smoke passes:
Ardur has a local Linux eBPF process-lifecycle proof with optional daemon-populated cgroup allowlist filtering, plus a no-mutation daemon custody preflight inspector and local JSON-line protocol contract scaffold for the future launch-wrapper-to-daemon boundary.
Not claimed yet:
- production daemon readiness
- daemon installation or startup
- socket server/listener implementation
- daemon-created per-session cgroups
- universal CLI capture
- file/network/privilege side-effect capture
- macOS/Windows kernel capture
- unprivileged/no-install eBPF support