Can my eBPF tool actually run here, and if not, exactly what needs to change?
kfeatures is a pure-Go library that answers this question.
It probes kernel capabilities at runtime and returns actionable diagnostics: not just unsupported, but why and how to fix it.
if err := kfeatures.Check(kfeatures.FeatureBPFLSM, kfeatures.FeatureBTF); err != nil {
var fe *kfeatures.FeatureError
if errors.As(err, &fe) {
log.Fatalf("%s - %s", fe.Feature, fe.Reason)
// Output: BPF LSM - CONFIG_BPF_LSM=y but 'bpf' not in active LSM list; add lsm=...,bpf to kernel boot params
}
}The same answers are available from the CLI for CI/CD gating (semantic exit codes), and from --mcp mode for AI agents (JSON-RPC over stdio, every subcommand exposed as an MCP tool with a discoverable input schema). See CLI.
cilium/ebpf/features answers: "Does this kernel support program type X?"
bpftool feature probe answers: "What BPF features does this kernel have?" (CLI only, not embeddable in Go)
Neither tells you whether your tool can actually run. For example, BPF LSM requires three things simultaneously: CONFIG_BPF_LSM=y in the kernel config, bpf in the active LSM boot parameter list, and the LSM program type supported by the running kernel. cilium/ebpf/features can only check the last one. bpftool can check the first and last, but not the second. Neither provides remediation guidance.
| Capability | cilium/ebpf/features |
bpftool feature probe |
kfeatures |
|---|---|---|---|
| BPF program type probes | ✓ | ✓ | ✓ |
| BPF map type / helper probes | ✓ | ✓ | ✓ † |
BTF availability (/sys/kernel/btf/vmlinux) |
✗ | ✗ * | ✓ |
Kernel config parsing (any CONFIG_*, =y/=m) |
✗ | ✓ | ✓ |
Active LSM list (/sys/kernel/security/lsm) |
✗ | ✗ | ✓ |
| BPF LSM enabled (config + boot params + program type) | ✗ | ✗ | ✓ |
| IMA detection (LSM list + securityfs directory) | ✗ | ✗ | ✓ |
| IMA active measurement (runtime policy detection) | ✗ | ✗ | ✓ |
| Process capabilities (CAP_BPF, CAP_SYS_ADMIN, CAP_PERFMON) | ✗ | ✗ | ✓ |
| Unprivileged BPF status | ✗ | ✓ | ✓ |
| Mount-state gates (bpffs/tracefs/custom paths via superblock magic) | ✗ | ✗ | ✓ |
ELF requirement extraction (parse .o, derive requirements) |
✗ | ✗ | ✓ |
| Composite feature validation | ✗ | ✗ | ✓ |
| Actionable diagnostics (remediation steps) | ✗ | ✗ | ✓ |
| Selective probing (minimize overhead) | ✓ ‡ | ✗ § | ✓ |
| Pure Go, no CGO | ✓ | ✗ | ✓ |
| Usable as a Go library | ✓ | ✗ | ✓ |
* bpftool checks CONFIG_DEBUG_INFO_BTF in the kernel config but does not verify /sys/kernel/btf/vmlinux exists.
† Exposed in kfeatures as parameterized requirements (RequireMapType, RequireProgramHelper) consumed by Check(...).
‡ cilium/ebpf/features is per-function: callers invoke individual probe functions on demand.
§ bpftool feature probe runs the full probe set on every invocation.
Other Go projects (libbpfgo, Tetragon, Falco libs) have some feature detection built in, but none is a standalone reusable library. They are either CGO-dependent, tightly coupled to their parent project, or written in C/C++.
go get github.com/leodido/kfeaturesValidate that required kernel features are available:
import (
"errors"
"log"
"github.com/leodido/kfeatures"
)
if err := kfeatures.Check(kfeatures.FeatureBPFLSM, kfeatures.FeatureBTF); err != nil {
var fe *kfeatures.FeatureError
if errors.As(err, &fe) {
log.Fatalf("kernel not ready: %s - %s", fe.Feature, fe.Reason)
}
}Combine Feature enums with parameterized workload requirements:
import (
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/asm"
"github.com/leodido/kfeatures"
)
err := kfeatures.Check(
kfeatures.FeatureBTF,
kfeatures.RequireProgramType(ebpf.XDP),
kfeatures.RequireMapType(ebpf.Hash),
kfeatures.RequireProgramHelper(ebpf.XDP, asm.FnMapLookupElem),
)Gate on a filesystem mounted at an arbitrary path with the expected superblock magic. Useful when bpffs lives somewhere other than /sys/fs/bpf:
import (
"github.com/leodido/kfeatures"
"golang.org/x/sys/unix"
)
err := kfeatures.Check(
kfeatures.RequireMount("/run/bpf", unix.BPF_FS_MAGIC),
)Magic constants come from golang.org/x/sys/unix (e.g. unix.BPF_FS_MAGIC, unix.TRACEFS_MAGIC, unix.CGROUP2_SUPER_MAGIC).
FeatureGroup packages a set of requirements as a single value you can pass anywhere a Requirement is accepted:
var TracingTool = kfeatures.FeatureGroup{
kfeatures.FeatureBTF,
kfeatures.FeatureKprobeMulti,
kfeatures.RequireProgramType(ebpf.Kprobe),
}
if err := kfeatures.Check(TracingTool); err != nil {
log.Fatal(err)
}Point FromELF at an eBPF .o and get back a FeatureGroup describing its program types, map types, and helper-per-program requirements (directly consumable by Check):
reqs, err := kfeatures.FromELF("./bpf/probe.o")
if err != nil {
log.Fatal(err)
}
if err := kfeatures.Check(reqs); err != nil {
log.Fatalf("kernel cannot run probe.o: %v", err)
}Output is deterministic (deduplicated, stably ordered). Unknown ELF kinds fail closed.
Check returns the diagnosis for the first failing feature. To inspect any feature against a single probe snapshot, call Diagnose directly:
sf, _ := kfeatures.Probe()
if !sf.BPFLSMEnabled.Supported {
fmt.Println(sf.Diagnose(kfeatures.FeatureBPFLSM))
// CONFIG_BPF_LSM=y but 'bpf' not in active LSM list; add lsm=...,bpf to kernel boot params
}Probe all features for diagnostics and reporting:
sf, err := kfeatures.Probe()
if err != nil {
log.Fatal(err)
}
fmt.Println(sf)Sample output (truncated):
Kernel: 6.1.0-generic
Program Types:
LSM: yes
kprobe: yes
kprobe.multi: yes
Core:
BTF: yes
Security Subsystems:
BPF LSM enabled: yes
IMA enabled: no
IMA directory: yes
Active LSMs: lockdown, capability, yama, apparmor, bpf
Filesystems:
tracefs: yes
bpffs: yes
Individual fields are typed and inspectable programmatically (see SystemFeatures).
Probe only what you need:
sf, err := kfeatures.ProbeWith(
kfeatures.WithProgramTypes(ebpf.LSM, ebpf.Kprobe),
kfeatures.WithSecuritySubsystems(),
kfeatures.WithCapabilities(),
)WithX options select probe scope. They do not define requirements; use Check(...) for gating.
A CLI is included for operator diagnostics, CI/CD gating, and AI-agent integration:
go install github.com/leodido/kfeatures/cmd/kfeatures@latestkfeatures probe # probe all features
kfeatures check --require bpf-lsm,btf,cap-bpf # exit 0 if met, 1 otherwise
kfeatures probe --json # JSON output
kfeatures config # display kernel configkfeatures check exits 0 when all requirements are met and 1 when any are missing. Drop it into a Helm chart pre-install hook, an init container, or a CI job. With --json the verdict is a parse-friendly object on stdout:
$ kfeatures check --require bpf-lsm,btf --json
{
"ok": false,
"feature": "bpf-lsm",
"reason": "CONFIG_BPF_LSM=y but 'bpf' not in active LSM list; add lsm=...,bpf to kernel boot params"
}
$ echo $?
1Invocation errors (missing required flag, unknown flag, invalid value, unknown subcommand) emit a structured JSON envelope on stderr and exit with a semantic code so wrappers can distinguish "the user invoked us wrong" from "the kernel is missing a feature":
| Exit code | Category | Example |
|---|---|---|
0 |
OK | check passed |
1 |
Runtime | FeatureError, probe failure, missing kernel config |
10 |
Input | missing_required_flag: required flag not provided |
11 |
Input | invalid_flag_value: wrong type or unknown enum |
12 |
Input | unknown_flag |
14 |
Input | unknown_command |
$ kfeatures check --require bogus
{"error":"invalid_flag_value","exit_code":11,"flag":"require","got":"bogus","expected":"feature","command":"kfeatures check","message":"invalid argument \"bogus\" for \"-r, --require\" flag: unknown feature: \"bogus\" (available: …)"}
$ echo $?
11Codes follow the structcli/exitcode categories: input errors 10–19 are agent-fixable, runtime errors 1–9 are operator-fixable.
kfeatures is built to be driven by LLM agents and code-generation tooling without --help scraping.
--jsonschema dumps a JSON Schema describing a command's flags. Use =tree to walk the entire subtree:
$ kfeatures check --jsonschema | jq '.title, .properties | keys'
"kfeatures check"
[
"json",
"require"
]
$ kfeatures --jsonschema=tree | jq 'map(.title) | map(select(test("^kfeatures( probe| check| config| version)?$")))'
[
"kfeatures",
"kfeatures check",
"kfeatures config",
"kfeatures probe",
"kfeatures version"
](--jsonschema=tree walks every node, including cobra-generated help and completion leaves; filter with jq to the ones you care about.)
--mcp turns kfeatures into a Model Context Protocol server over stdio. Each runnable leaf command becomes an MCP tool whose input schema mirrors the cobra flag set; agents introspect via tools/list and invoke via tools/call:
Tools exposed: probe, check, config. The server stays alive across business-outcome errors (a failing check does not terminate the session), and invocation errors flow through the same structured envelope as the CLI. Pure stdlib JSON-RPC inside structcli; no extra heavy SDK dependency.
| Category | Features |
|---|---|
| Program types | LSM, kprobe, kprobe.multi, tracepoint, fentry |
| Core | BTF availability (CO-RE) |
| Security | BPF LSM enabled, IMA enabled, IMA active measurement, active LSM list |
| Capabilities and runtime gates | CAP_BPF, CAP_SYS_ADMIN, CAP_PERFMON, unprivileged BPF disabled, BPF stats enabled |
| Syscalls | bpf(), perf_event_open() |
| JIT | enabled, hardened, kallsyms, memory limit, CONFIG_BPF_JIT_ALWAYS_ON |
| Filesystems | tracefs, debugfs, securityfs, bpffs (gated tracefs/bpffs checks verify the filesystem is mounted with the expected superblock magic) |
| Custom mount gates | any path + superblock magic via RequireMount |
| Namespaces | initial user namespace, initial PID namespace |
| Parameterized workload requirements | program type, map type, helper-per-program-type via requirement items |
| ELF-derived requirements | program/map types and helper-per-program requirements via FromELF |
| Mitigation context | Spectre v1/v2 vulnerability status |
| Kernel config | CONFIG_BPF_LSM, CONFIG_IMA, CONFIG_DEBUG_INFO_BTF, CONFIG_FPROBE, any CONFIG_* |
Pre-1.0. The public API may change between minor versions; breaking changes are
called out explicitly in CHANGELOG.md. The FromELF contract
(signature, determinism, fail-closed semantics) is frozen; see
CONTRIBUTING.md.
- Linux for runtime probing/checking (uses Linux-specific syscalls and sysfs).
FromELFis parser-only and works on any platform.- Some probes require
CAP_BPForCAP_SYS_ADMIN.
See CONTRIBUTING.md for the API model, the feature-addition checklist, and the development workflow.