Submitted:
28 October 2026
Posted:
28 October 2026
You are already at the latest version
Abstract
Linux servers are usually defended by tools that observe kernel activity but leave the response to a human, or by static policies that cannot recognise a hijacked legitimate binary. We present Kernel Borderlands (KB), a kernel-level runtime defence that closes the loop from observation to enforcement. An eBPF sensor scores process behaviour and enforces five graded containment levels, the first four reversible, through BPF-LSM hooks, behind authorisation gates that prevent it from acting against PID 1, critical daemons or its own components. A Go control plane keeps a SHA-256 hash-chained audit log and detects beaconing; a Ray-based agent swarm implements a containment policy trained by Proximal Policy Optimisation and quorum confirmation of actions; and a Rust watchdog verifies BPF bytecode and gates the boot sequence. On 908 held-out samples derived from ADFA-LD, the containment policy reaches 97.1–100% accuracy on every attack class with no under-containment, and the Jury policy has no false negatives. In a live capture, the control plane issued a throttle within about 1 ms of each of six escalations to BORDERLANDS. With all hooks loaded, KB adds 38 ns per system call and 26 µs per process creation, with no measurable overhead on a parallel compile or a CPU-bound workload.
Keywords:
eBPF
; BPF-LSM
; runtime containment
; intrusion detection
; reinforcement learning
; multi-agent systems
1. Introduction
When a Linux server is compromised, the attacker’s actions eventually become kernel operations: a process is executed, a credential changes, a sensitive file is opened, a socket connects to a remote host, or a memory region becomes writable and executable. The extended Berkeley Packet Filter (eBPF) lets verified programs observe these operations at Ring 0 with low overhead [1], and since Linux 5.7 BPF-LSM programs can also deny them [2].
Observation, however, is not defence. A runtime defence must also decide how strongly to respond, enforce that decision where the attacker cannot simply ignore it, prove afterwards that its record of what it did has not been altered, and protect itself from being blinded or turned against the host. Existing tools usually provide one or two of these properties: eBPF monitors alert but rarely respond, enforcement tools respond with a fixed kill-or-deny rule, and almost none keep a tamper-evident record of their own actions.
- Transitions, not events.
A single execve() is not an attack, and neither is a single anonymous mmap() or a single change of user ID. The same execve() after a privilege change, inside an unexpected process lineage and after a burst of writable-and-executable mappings very likely is. KB therefore treats the transition between behavioural states over time as its unit of analysis: each event updates one of six weighted behavioural dimensions of its process, the composite score is smoothed, and rule chains move the process through the states SAFE, OBSERVED, SUSPICIOUS, BORDERLANDS and COMPROMISED.
- Problem statement.
From this view we derive five problems that existing tools do not solve together.
- Observability is not response. Seeing an attack is of little use unless the system can choose and apply a proportionate response inside the kernel, quickly and, where possible, reversibly.
- Telemetry must never delay enforcement. A burst of events, for example from a build job that spawns thousands of short-lived processes, must not stall the path that carries containment orders. We hit exactly this failure during development: on one shared socket a telemetry burst filled the send buffer, the sensor misread EAGAIN as a dead peer, and containment delivery went down with it.
- The defence must not be turnable against itself. The containment pipeline must refuse to act on PID 1, kernel threads, critical daemons, organisation-critical workloads or its own watchdog, even if an upstream decision asks it to.
- Decisions must be provable. Every enforcement action, policy reload and agent decision must be recorded so that later modification or deletion is detectable.
- The decision-makers need supervision. Once agents may recommend containment, a stuck or misbehaving agent must be detected and restarted, and the decision layer should not start until the layers beneath it have been verified.
Kernel Borderlands (KB) implements the full observe–score–decide–enforce–audit loop for Linux server workloads. Its name reflects the central idea: each process lives in a behavioural zone (SAFE, SUSPICIOUS or BORDERLANDS), and the defence acts at the border between normal and hostile behaviour rather than on individual events. The contributions of this paper are:
- An architecture of five cooperating subsystems connected only through root-owned Unix sockets, with telemetry and control on separate channels (§Section 4).
- Five graded containment levels enforced by BPF-LSM, guarded by two ordered authorisation gates (CPM, CWP) that no decision layer can bypass (§Section 6).
- A unified formulation of every learned and statistical decision procedure: behaviour scoring, the reinforcement-learned Containment and Jury policies, quorum consensus, agent oversight, beaconing detection and the audit chain (§Section 5).
- An evaluation combining offline policy results, a live attack capture, a run-time overhead benchmark, the test suites, and a source-level security audit, together with the defects found by testing on real hosts (§Section 14–§Section 16).
- An open-source implementation packaged for Ubuntu and Arch Linux, with its kernel requirements, portability model and a test case study (§Section 11, §Section 14.4).
Section 2 reviews related work and Section 3 states the threat model. Section 4, Section 5, Section 6, Section 7, Section 8 and Section 9 describe the architecture, the mathematical formulation and each subsystem; Section 10, Section 11, Section 12 and Section 13 cover implementation, deployment, the end-to-end path and the operator interfaces. Section 14 evaluates the system, and Section 15, Section 16, Section 17 and Section 18 discuss the results, the audit, the limitations and future work.
2. Background and Related Work
- eBPF and BPF-LSM.
The BSD Packet Filter [3] introduced an in-kernel virtual machine for packet filtering. eBPF generalised it with maps, helper functions and a verifier that proves termination and memory safety before a program is loaded [1]; programs attach to tracepoints, kprobes, XDP [4] and, through KRSI, LSM hooks [2]. BTF and CO-RE let one compiled object run unchanged across kernel versions [5].
- Access control and intrusion detection.
SELinux [6] and AppArmor enforce static policy through the LSM framework [7]; an exploited server that stays within its permitted operations does not violate that policy. Forrest et al. showed that system-call sequences form a “sense of self” for a process [8], which started a long line of host-based anomaly detection [9]. Network datasets such as NSL-KDD have well-known flaws [10,11], and ADFA-LD [12] provides Linux system-call traces of modern attacks.
- eBPF security tools.
Falco [13] and Tracee [14] evaluate rules and signatures over kernel events and focus on detection. Tetragon [15] adds in-kernel enforcement, but its response is rule-triggered and binary. Table 1 compares these approaches with KB.
- Learning-based response and trust.
Choosing a response under uncertainty is a sequential decision problem suited to reinforcement learning [16]; KB uses PPO [17] with RLlib [18] on Ray [19]. Agreement among agents relates to the Byzantine generals problem [20]. Hash-chained logs [21,22] make tampering detectable, and TPM measured boot [23,24] anchors trust below software, which matters because a software watchdog on a compromised kernel faces the confused-deputy problem [25].
- Gaps in existing approaches.
Taken together, the survey leaves eight gaps that KB is designed to close: (L1) signature tools miss novel and obfuscated attacks; (L2) static access-control policy cannot adapt when a permitted binary is hijacked; (L3) user-space monitors can be killed or blinded by a privileged attacker; (L4) most eBPF tools stop at an alert, leaving a human in the critical path; (L5) where enforcement exists it is all-or-nothing, so a false positive on a production database is as damaging as the attack; (L6) nothing prevents a containment pipeline from being steered against PID 1, systemd or the defence itself; (L7) plain logs can be edited after the fact; and (L8) systems that automate response rarely supervise the automation itself.
3. Threat Model and Design Goals
We assume an attacker who has gained code execution on the host, for example through a vulnerable network service, and who may escalate to root. The attacker may try to stay within a process’s permitted system calls, spread activity across many resources to evade rate limits, flood the telemetry channel, or attack the defence itself. A full kernel compromise, in which the attacker can rewrite kernel memory, is beyond any same-host software defence and is out of scope.
From this model we derive five goals. G1 enforcement must happen in the kernel; G2 responses must be proportionate, and reversible below the final level; G3 no decision path may contain PID 1, kernel threads, critical daemons or KB’s own components; G4 every action must be recorded in a log whose alteration is detectable; and G5 the decision layer must not start until the layers beneath it have been verified. Table 2 maps the STRIDE categories to the mechanisms that address them.
4. System Architecture
KB consists of five subsystems (Figure 1). kb-core (C/eBPF) is the only Ring-0 component. kb-control-plane (kbd, Go) ingests telemetry, keeps state, audits and exposes APIs. kb-aads (Python/Ray) is the agent swarm. kb-checker (Rust) is the integrity watchdog, and kb-op provides the operator interfaces.
Telemetry flows one way, from the sensor’s user-space bridge to kbd over kbd.sock; containment, sensitive-path, rule and workload pushes return over a separate kbct.sock (Table 3). The split fixes a real availability bug: on a single multiplexed socket, a telemetry burst filled the send buffer, write() returned EAGAIN, and the sensor treated this as a dead peer, taking containment delivery down with it. All sockets are root-owned Unix domain sockets with mode 0660 and group kb; the internal data path has no TCP listener.
- Wire contract.
Messages between the C sensor and the Go control plane are packed little-endian frames: a 4-byte length prefix, a 4-byte header (magic 0x4B42, wire version 3, message type) and a fixed payload. Table 4 lists the message types. Because the two sides are compiled separately, sizes and type codes are locked by byte-exact tests on both sides. New types are additive: a receiver ignores a type it does not know, which is how the containment acknowledgement (type 10) was introduced without changing the wire version.
- Architectural principles.
Three principles shape the design. First, each layer talks only to its neighbours, which limits the blast radius of a failed component. Second, enforcement authority sits in the control plane and the sensor: the swarm is advisory, and disabling it leaves deterministic enforcement by the policy engine running. Third, the watchdog does not trust any component’s report about itself; it reads the BPF programs and the kernel containment map directly and compares the map with the control plane’s state.
5. Mathematical Formulation
- Behaviour score and zones.
Each process has six behavioural dimensions (process, syscall, privilege, file, network, memory). An event e sets the dimension it belongs to, , where the value depends on the event’s flags:
The syscall dimension is not set by individual events. Every ten ring-buffer polls the sensor reads the per-process system-call counters from a kernel map, computes the distribution of the calls made since the previous scan, and normalises its Shannon entropy by the entropy of 512 equally likely calls:
and a smoothed value raises a high-entropy evidence flag. The composite score is
with in the order above, and it is smoothed by an exponential moving average
The zone is SAFE if , SUSPICIOUS if and BORDERLANDS otherwise, with defaults , that can be overridden per process name. In parallel, a rule engine tracks evidence flags and ordered sequences (for example an RWX mapping followed by a write to process memory within 30 s; all 19 rules are listed in Appendix C) and moves a process from SAFE through OBSERVED, SUSPICIOUS and BORDERLANDS to COMPROMISED.
Figure 2.
Rule-driven behaviour states. The dashed IOC rule, which would jump directly to COMPROMISED, is designed but cannot yet fire (Section 17)
Figure 2.
Rule-driven behaviour states. The dashed IOC rule, which would jump directly to COMPROMISED, is designed but cannot yet fire (Section 17)

- Containment policy.
Containment is a single-step decision over the observation with action (NONE to TERMINATE). Against the labelled target level t the reward is
so under-containment costs 0.5 per level of error and over-containment 0.3 (Figure 3). The policy is trained with PPO, which maximises the clipped surrogate
with . Serving is deterministic, .
- Jury policy and quorum.
The Jury reuses o with a binary target and rewards (TP), (FP), (TN) and (FN). Without a full observation it falls back to a threshold on a confidence : CONTAIN if , otherwise ALLOW. For votes from a freshly spawned Jury pool, containment proceeds iff
and Q is false for . All weights are currently , giving a simple majority.
- Beaconing and slow exfiltration.
For each (PID, address, port) flow, the last inter-connection intervals are binned into seconds, and
is the Shannon entropy [26] of the bin distribution, with a sentinel 3.0 below ten samples. Connection counts per 60 s bucket feed Welford’s running mean and variance [27] and a one-sided CUSUM [28] with slack :
flagged when after at least five buckets. A beacon alert requires both and the CUSUM flag and is debounced to one per flow per five minutes, so neither regular low-volume traffic nor a single burst triggers it alone. Figure 4 shows why the threshold separates beacons from interactive traffic: a fixed-interval beacon puts every interval in one bin (), while timing spread over all five bins reaches the maximum .
- Severity and courthouse oversight.
A score s maps to CRITICAL (), HIGH (), MEDIUM () or LOW, and is raised one tier if the process matches a registered critical workload; severity routes alerts but never decides containment. For a registered agent with error count e, status and uptimes read s apart, the verdict is terminate if the agent is unregistered, restart if , and healthy otherwise.
- Audit chain.
Each audit row commits to its predecessor,
so altering or deleting a row changes every later hash, which recomputation detects [21].
6. kb-Core: Sensor and Enforcement
- Hooks and portability.
Tracepoints and kprobes on process exec and exit, system calls, commit_creds, openat, connect/bind, mmap/mprotect, process_vm_writev and security_capable, plus TLS uprobes, write one unified event record into a 1 MB ring buffer. The sensor is built with CO-RE: kernel fields are read only through BPF_CORE_READ(), and libbpf relocates them at load time, so one object runs on any kernel from 5.10 with BTF and CONFIG_BPF_LSM. Loading is all-or-nothing and depends on the kernel’s actual features rather than its version number, which matters because enterprise kernels backport eBPF features: if BTF, a relocation or a program type is missing, the load fails and the sensor exits before attaching any program. Table 5 lists the attachment points. Section 11 gives the CO-RE build, the kernel requirements and a compatibility matrix for 16 distributions.
- Graded containment.
Five LSM programs (file_open, bprm_check_security, socket_connect, socket_bind, file_mprotect) consult contained_pids_map and deny the operation, returning -EPERM from the exec hook and -EACCES from the others (Table 6). Level 1 is applied through cgroup v2 limits [29]. Level 4 applies every block the hooks can enforce, and kbd then sends SIGKILL through a pidfd. It does so only if the process passes kbd’s own copy of the CPM and CWP checks and the sensor acknowledges that it wrote level 4 to the map; a refusal, or no acknowledgement within 3 s, means no kill. In file_open, the full-quarantine check runs before bpf_d_path(), so a path-resolution failure cannot bypass it. Listing shows the hook with its comments removed. The key is zero-padded because map lookups compare all 64 bytes, and bpf_d_path() does not clear the tail.
- Authorisation gates.
Any decision layer can be wrong, so two user-space gates run in a fixed order before the kernel map is written (Algorithms Section 6 and Section 6). Releasing containment is never gated. The Critical Process Module (CPM) has a compiled-in floor that includes systemd, kbd and kb-checker, so KB cannot be turned against its own watchdog. Critical Workload Protection (CWP) lets an organisation register workloads by path or by SHA-256 hash; a HASH-tier binary that no longer matches its digest is logged as “Identity Verification Failed” and becomes eligible for containment, because it has shown itself not to be the workload it claims to be.
...
Algorithm 1: CPM classification (cpm_classify)
1: if readlink(/proc/pid/exe) fails then return REJECT_KTHREAD
2: if pid then return REJECT_PID1
3: if pid ∈ protected_pids then return REJECT_PID
4: path ← readlink(/proc/pid/exe)
5: if path ∈ protected_exec_paths then return REJECT_EXEC
6: return ALLOW
...
...
Algorithm 2: CWP classification (cwp_classify), run only after CPM allows
1: tier ← lookup(workloads, pid), else by resolved path; if none return ALLOW
2: if tier = PATH then return REJECT_PATH
3: if path or registry lookup fails then return REJECT_HASH {fail closed}
4: if SHA256(file) ≠ expected then log “Identity Verification Failed”; return ALLOW
5: return REJECT_HASH
...
- Safety of the gates.
Let be the set of processes that CPM protects: PID 1, kernel threads, registered PIDs and processes whose resolved executable is on the protected list, which includes systemd, kbd and kb-checker. Let be the CWP-registered workloads whose identity check passes (a PATH-tier match, or a HASH-tier binary whose SHA-256 equals its registered digest).
Proposition 1.
For any sequence of containment requests from any decision path (operator, policy engine or swarm), no process in is written to the containment map at a level above zero, and none is killed by a TERMINATE decision.
Proof
(Proof sketch). The containment map is written only by the sensor’s command handler, and every request with level passes Algorithm Section 6 and then Algorithm Section 6 before the write; a rejection returns without writing. Algorithm Section 6 rejects every member of , including when /proc/pid/exe cannot be resolved. Algorithm Section 6 rejects every member of , and fails closed when the path or registry lookup fails. Requests with only delete entries. TERMINATE’s SIGKILL is sent only after the sensor acknowledges a successful level-4 write, and through a pidfd opened before the request, so it reaches only a process that passed both gates. No other code path writes the map, so the property holds whatever the decision layer requests. It assumes an uncompromised kernel and sensor, which is the threat model of Section 3. □
- Rate limiting.
A token bucket (capacity 100, refill one token per 10 ms) is kept per 〈tgid, start_time, resource〉 in a 10,240-entry LRU map (Algorithm Section 6). The start time stops a reused PID inheriting an exhausted bucket, and per-resource keys stop an attacker from spreading activity to stay under a flat per-PID limit. Tier-1 events (credential changes, capability probes, TLS plaintext) are never throttled, and dropped events are reported as a summary count.
- TERMINATE and the sensor acknowledgement.
Levels 1–3 are enforced entirely by the hooks. Level 4 adds a SIGKILL sent by kbd, and because a kill cannot be undone it is guarded twice. Before sending level 4, kbd pins the target with pidfd_open and applies its own copy of the CPM and CWP rules: PID 1, kernel threads, the protected-executable list (the sensor’s floor plus the packaged /usr/bin and /usr/sbin paths) and the loaded workload registry, failing closed when it cannot classify the process. It then registers for the sensor’s reply and sends the command. The sensor answers every containment command with a CONTAINMENT_ACK that reports what its own gates and the map write did (Table 7, Figure 5 and Figure 6). kbd kills, through the pinned pidfd, only on applied; a refusal, an unknown code or no reply within 3 s means no kill, and a PID whose request went unanswered is locked out for 30 s so that a late reply cannot answer a newer request. Every non-applied decision is written to the audit log as CONTAINMENT_REFUSED_BY_SENSOR. A sensor that predates the acknowledgement never replies, so TERMINATE is refused until the sensor is upgraded.
...
Algorithm 3: Token-bucket throttle (in eBPF)
1: key tgid, start_time, resource〉; b ← lookup or insert
2: b.tokens
3: if b.dropped and b.tokens then emit DROPPED(b.dropped); b.dropped
4: if b.tokens then b.tokens; return ALLOW
5: b.dropped; return THROTTLE
...
7. kb-Control-Plane
kbd keeps per-process state in an in-memory L1 sync.Map, authoritative for reads, backed by an L2 SQLite database in WAL mode written by a single goroutine through a 4096-entry channel, so ingestion never blocks on disk I/O. On a process exit both layers are flushed, so a reused PID inherits nothing. An upsert preserves any active containment level; an earlier version reset it to NONE on every telemetry update.
- Audit log.
Every enforcement action, policy reload and agent decision is appended to the chain of Eq. 10. Verification is exposed through an HTTP route, a gRPC method and kbctl audit verify. Our review found that VerifyChain did not check rows.Err() after iterating, so an I/O error mid-scan looked like a valid, shorter chain. It now returns the error, with a fault-injecting regression test.
...
Algorithm 4: Audit chain append and verification (internal/audit)
Log(action, subject, actor, reason):
1: lock; now; actionsubjectactorreasonprev
2: ; insert row with (prev, h); prev
Verify():
3: prev ← “genesis”; count
4: for each row in id order do
5: if scan error then return (false, count, error)
6: if row.hash then return (false, count, “chain broken”)
7: prev ← row.hash; count ← count
8: if the cursor reports an error then return (false, count, error) {the fixed defect}
9: return (true, count, none)
...
...
Algorithm 5: Beacon detector, per flow (internal/detection)
1: window[(pid, address, port)] (create if absent)
2: if w has a previous timestamp then append the interval to w (keep the last 100)
3: if the event falls in the current 60 s bucket then count ← count
4: else fold count into Welford ; ; open a new bucket
5: entropy of the intervals over five bins (3.0 if fewer than 10 samples)
6: cusum ← (buckets ) ∧ () ∧ ()
7: return beacon cusum {alerts debounced to one per flow per 5 min}
...
- Policy and workloads.
ReloadPolicy swaps a new policy engine under an RWMutex without restarting. The critical-workload registry can be Ed25519-signed; when a public key is configured, a missing or invalid signature is rejected and the last good registry kept. Automatic NAMESPACE containment on a beacon alert is opt-in per process, because a statistical heuristic should not gain enforcement authority by default. Thresholds and enforcement options are set per process name (for example, nginx uses 55/85 instead of 40/75), and paths listed in the policy only add to a compiled-in floor of sensitive paths and can never remove it. The shipped defaults are listed in Appendix D.
- Internal structure and interfaces.
kbd is organised as Go packages for socket I/O and wire decoding (ipc), two-layer storage (store), per-process policy (policy), the audit chain (audit), beacon detection (detection), translation of decisions into sensor commands (enforcement), a client for the watchdog (checkerclient) and the orchestrating object with its APIs (controlplane). Table 8 lists the two external interfaces. Both are reachable only locally: gRPC on the root-owned kba.sock, and HTTP bound to loopback with a CORS allow-list. State-changing HTTP routes require a bearer token compared in constant time; if no token is configured they refuse every request rather than run unauthenticated.
8. kb-Aads: The Agent Swarm
The swarm is a set of Ray actors sharing a common base class. The Patroller dispatches a PID to a Hunter pool when its score first crosses 40. The Judge opens a consensus round, spawns a fresh Jury pool and gates the Executor on Eq. 7 (Algorithm Section 8); a crashed juror is treated as an absent vote, not a veto. The Containment agent chooses the level with the trained policy, and the militia applies CGROUP → SECCOMP → NAMESPACE → TERMINATE strictly in order, halting at the first failure. The Healer is deliberately rule-based: it restores a process only when it is SAFE, scores below 20 and has been contained for 60 ticks, because no ground truth for restore outcomes exists. A SwarmRegistry lets the Judge locate agents and apply the courthouse verdict. On a test host the swarm started nine agents with no errors. Table 9 summarises the roles.
- Integration status.
Every role in Table 9 is implemented and covered by the 68 swarm tests, but release 1.0.0 does not yet connect them into a live decision loop. The Patroller hands a flagged PID to the Hunter, whose investigation is still a stub, and no live code path yet calls the Judge’s consensus round, the Containment policy or the militia; the orchestrator leaves the coupling of a quorum decision with a containment level as an open integration step. In the released system, live enforcement therefore comes from the control plane’s policy engine and from operators, and the swarm’s contribution is the trained policies evaluated in Section 14.
...
Algorithm 6: Judge consensus round
1: pool ← spawn n Jury actors (n from configuration); register each
2: results ← gather(vote(alert) for each juror, return_exceptions)
3: votes ← results that are not exceptions {crash ⇒ absent vote}
4: if votes is empty then return {round skipped}
5: if then Executor.quarantine(alert)
...
...
Algorithm 7: Militia escalation (command_squad)
1: stages ← [CGROUP, SECCOMP, NAMESPACE, TERMINATE] truncated to the target level
2: for each stage do
3: spawn one squad member for the stage and register it
4: result ← await member.execute(pid, reason) {stages never race}
5: if not result.success then break {halt; no retry}
6: return the stage reports
...
...
Algorithm 8: Courthouse check of an agent (assess_severity)
1: registry.lookup(agent); if none then return TERMINATE
2: .status(); if.status ≠ active or.errors then return RESTART
3: wait s; .status()
4: if .uptime .uptime then return RESTART {agent is not advancing}
5: return HEALTHY
...
- Training data.
No eBPF-captured attack corpus existed, so all six of ADFA-LD’s attack families [12] were mapped to five KB scenarios and target levels, with benign data from ADFA-LD Normal traces and live /proc telemetry (Table 10, Figure 7). Each row carries the five wire-level features of o. For a trace of length n with syscall diversity and longest run of a repeated call r, each statistic is standardised against the mean and standard deviation of the ADFA-LD Normal traces, and
where are computed on the two halves of the trace. The zone feature is , with a per-scenario floor so that a labelled attack is never SAFE. The rows are split 70/15/15 into training, validation and test sets, stratified by class. Training used RLlib PPO with class-balanced sampling (category first, then row), so the rare attack classes receive equal attention.
9. kb-Checker and the Boot Sequence
kb-checker holds no persistent state and opens no network listener. It hashes the JIT-compiled BPF programs, reconciles the kernel containment map with the control-plane database and heals differences, requires the exec event of a spawned /bin/true within 3 s, and flags hooks averaging over 500 ns. When it cannot recover cleanly it escalates from SIGKILL of the sensor, to detaching BPF links, to an iptables lockdown that keeps only loopback.
In the boot design, order is a security property enforced with plain systemd dependencies (Figure 8). kbd binds its sockets and signals readiness (Type=notify); the watchdog requires it, takes a PID-file lock and runs blocking audits; and the swarm requires the watchdog, so a failed audit prevents the decision layer from starting (goal G5). Release 1.0.0 relaxes this while the watchdog’s recovery path is repaired: kb-checker is installed but not started by kbctl up, and the swarm unit, which is installed but not enabled, depends only on kbd. The boot gate is therefore part of the design but not enforced by the packaged units (Section 17).
Table 11.
Audit loops of kb-checker.
| Check | Period | Method |
|---|---|---|
| BPF bytecode integrity | 60 s | SHA-256 of each loaded program obtained through bpf_obj_get_info_by_fd |
| Control-plane health | 5 s | gRPC health check against kbd |
| Swarm health | 30 s | status query of the agent swarm |
| Sensor liveness | 60 s | spawn /bin/true and require its exec event within 3 s |
| Containment-map integrity | 60 s | compare the kernel map with the control plane’s state and heal differences |
| Hook latency | 60 s | warn when a program’s average run time exceeds 500 ns |
- Fallback ladder and failure handling.
Escalation is suppressed for a 15 s start-up grace period, which removed false lockdowns on cold boot. After that, an integrity violation first stops the sensor through systemctl; only if that fails does the watchdog kill the sensor process (pkill -9), remove the pinned BPF objects under /sys/fs/bpf, and set the iptables policies to drop all traffic except loopback, so an operator at the console can still intervene. Two other failures are handled by construction. If the sensor cannot load or attach its programs, for example because bpf is missing from the active LSM list, it exits before attaching anything and systemd restarts it; the host stays up but unprotected, and the failure is visible in the unit status. The watchdog orders itself after the sensor with Wants= rather than Requires=: a real boot test showed that Requires= made a sensor restart issued by the watchdog’s own recovery also stop the watchdog, which then misread its own termination as a failed recovery and escalated to a false lockdown.
10. Implementation
Table 12 summarises the implementation, and Appendix E reproduces the code behind the main mechanisms. The sensor is compiled with clang -target bpf; bpftool generates a skeleton that the user-space loader links against libbpf. The bridge reconnects to kbd with exponential back-off between 100 ms and 5 s, so a control-plane restart never requires reloading the hooks. Wire messages are packed little-endian structures whose sizes are locked by byte-exact tests on both the C and Go sides, because a silent mismatch between the two once dropped every containment command. The KernelBorderlands gRPC service offers 13 RPCs, including process and zone queries, event and alert streams, SetContainment, SubmitAgentDecision, audit verification and export, zone override, and policy and workload reload. Generative AI assistance used during development, evaluation and writing is described in the Acknowledgments.
11. Deployment, Portability and Requirements
KB is meant to run on the Linux servers an organisation already has, not on a specially built kernel. This section describes how one sensor binary runs across kernels, which kernel features it needs, how BPF-LSM is enabled, which distributions are supported, and how the packaged release is installed.
11.1. Software Stack
Table 13 lists the software each subsystem needs. Everything is free and open source, and a deployment needs no licences or extra hardware, since KB runs on the server it protects.
11.2. Compile Once, Run Everywhere
An eBPF program reads kernel structures such as task_struct and cred directly, and their layout changes between kernel versions and vendor builds. KB uses CO-RE [5] so that one compiled object runs on every supported kernel without per-host compilation or a kernel module.
- One header for all kernels. The build generates vmlinux.h from the build host’s BTF (bpftool btf dump file /sys/kernel/btf/vmlinux format c) instead of using version-specific kernel headers.
- Relocatable field accesses. Kernel fields are read only through BPF_CORE_READ(): the parent’s thread-group ID (task->real_parent->tgid) and the new credentials in commit_creds (uid, gid, euid, cap_effective). Clang (-g -O2 -target bpf) records each access as a relocation instead of a fixed offset, and -g keeps the BTF that relocation needs.
- Fix-up at load time. When the sensor starts, libbpf matches the relocations against the running kernel’s /sys/kernel/btf/vmlinux and patches the correct offsets into the bytecode before the verifier sees it.
- All-or-nothing load. If BTF is missing, a relocation cannot be resolved, or the kernel rejects a program or map type, the skeleton load fails and the sensor exits before attaching any program. If loading succeeds but attaching fails, for example because bpf is not in the active LSM list, the sensor also exits. A host is therefore never left half-instrumented.
Between load and attach the sensor fills its maps (sensitive paths, the CPM floor and the CWP registry) and registers itself as protected, so no hook runs before protection is in place.
Table 14.
Kernel features required by the sensor and the control plane.
| Feature | Used for | Since Linux |
|---|---|---|
| LRU hash map | rate limiter, per-process state | 4.10 |
| BTF (CONFIG_DEBUG_INFO_BTF) | CO-RE relocation | 5.4 |
| bpf_probe_read_kernel/_user | reading kernel and user memory | 5.5 |
| BPF-LSM (CONFIG_BPF_LSM, bpf in lsm=) | the five enforcement hooks | 5.7 |
| BPF ring buffer | the single 1 MB event stream | 5.8 |
| bpf_d_path | path of the opened file in lsm/file_open | 5.10 |
| pidfd_open, pidfd_send_signal | the TERMINATE kill in kbd | 5.3 |
| cgroup v2 | level 1 (CGROUP) throttling | 4.5 |
| Minimum kernel for the full feature set | 5.10 | |
11.3. Enabling BPF-LSM
Telemetry needs only eBPF and BTF, but enforcement needs the BPF Linux Security Module. Two conditions must hold: the kernel is built with CONFIG_BPF_LSM=y, and bpf appears in the list of active LSMs. Many distributions build the option but leave bpf out of the default list, so it must be added to the lsm= kernel parameter (Listing ).
11.4. Distribution Compatibility
Table 15 lists the distributions KB targets. Native distributions were tested and deployed with the packaged release and meet every requirement. Target distributions ship stock kernels with the required features but have not been validated end to end. Verify marks vendor kernels whose BTF or BPF-LSM configuration depends on the exact build; their version numbers understate their eBPF features because enterprise kernels backport them, so the checks of Listing are authoritative. “Boot param” means that BPF-LSM is compiled in but bpf must be added to lsm=.
11.5. Packaging and Service Units
Release 1.0.0 is built from one install target into an Ubuntu .deb and an Arch Linux package, published with SHA-256 checksums and with field manuals for both distributions. Installation provisions the kb group, the operator account and the configuration under /etc/kb, but starts nothing; kbctl up starts the stack. Table 16 lists the systemd units as shipped.
Figure 9 shows how the units, sockets and local network endpoints fit together on one host. All internal sockets live under /run/kb/; the only TCP listeners are bound to loopback, apart from the operator’s SSH port.
12. End-to-End Containment Path
Figure 10 follows one malicious process from its first kernel event to an enforced and audited containment. The steps cross every trust boundary in the system, and each boundary has a check that does not depend on the layer above it: the verifier for the hooks, the gates for the map write, the LSM hook for the operation, and the hash chain for the record. Steps 6–7 show the designed swarm path; in release 1.0.0 the policy engine or an operator issues the containment request directly (Section 8), as in the live capture of Section 14.
13. Operator Interfaces and Packaging
kbctl is a Go gRPC CLI for isolation and restore, zone override, audit verification and export, policy reload, workload signing, and starting or stopping the stack. kb-tui is a Rust terminal console reached through a dedicated sshd instance on port 2222 with a forced command, so authentication is handled by OpenSSH rather than custom code. The web dashboard requires a bearer token for isolate (level 3, reversible), kill (level 4) and restore, and kb-mcp exposes a read-only statistics tool (event rate and number of active processes) to LLM tooling over the Model Context Protocol. KB is distributed as an Ubuntu .deb and an Arch Linux package built from one install target; the packages provision the kb group and the operator account but start nothing until an operator runs kbctl up.
Table 17.
Operator interfaces
| Interface | Functions |
|---|---|
| kbctl (Go, gRPC) | process isolate –level (default NAMESPACE), zone override, audit verify|export, policy reload, workload reload|keygen|sign, stats; stack control up|down|status; dashboard up|down|status|url|open; ssh up|down|status|connect|add-key; tui |
| kb-tui (Rust, ratatui) | four views: Processes, Alerts, Agent Activity and Query Console, with confirmed containment actions; reached over sshd@kb-operator on port 2222 with a forced command, and every session is recorded in the audit chain |
| kb-dashboard (React, TypeScript) | Processes, Zone Topology, Alerts, Telemetry, Services, Containment, Consensus Log, Rogue Management, Console and Settings; Isolate and a two-step Kill are separate actions |
| kb-mcp (Go) | Model Context Protocol server over stdio with one read-only tool, kb.get_statistics |
14. Evaluation
14.1. Containment and Jury Policies
The first PPO run reached 97.8% overall test accuracy but only 60.0% on CGROUP and 70.4% on SECCOMP. The cause was in the features, not the model: two attack scenarios shared one engineered event-type code and zone floor. Giving each scenario its own code lifted every attack class to 97.1–100% and cut under-containment from 1.1% to 0.0% (Figure 11). Overall accuracy fell to 84.4% because the benign class, 87% of the test set, fell from 100% to 82.2%. We accept this trade deliberately: a false positive costs a review or a restore, while a false negative costs a compromise, which is exactly the asymmetry of Eq. 5.
The Jury collapses the same labels to CONTAIN/ALLOW and uses the same splits. It reached 100.0% validation and test accuracy with zero false negatives (Table 18). Training is not seeded, so figures vary slightly between runs (an earlier run scored 99.8% and 99.4%), but zero false negatives held in every run observed. The binary task is easier because the attack classes that the five-way policy must separate collapse into one label.
14.2. Live Detection and Response
We captured telemetry from a live run on an Ubuntu virtual machine during a controlled attack simulation: 188 rounds at a 3 s interval, each touching the file, memory and network dimensions of one long-lived process. The capture holds 10,440 sensor events (7,703 hook events), 489 zone transitions and 409 audit rows. The attack process escalated SAFE → SUSPICIOUS → BORDERLANDS six times, and no other process reached BORDERLANDS. Each time, the control plane issued a CGROUP throttle within about 1 ms of the BORDERLANDS audit record, and the sensor logged it as applied 0.53–1.90 s later, with a mean of 1.35 s (Figure 12). These delays come from journal timestamps and so include logging latency.
Privilege-change events dominate the hook stream (51.4%), and the sensor flagged 3,563 hook events (46%) as sensitive (Figure 13). The ring buffer reported overload 1,863 times at 101–270 events/s, with 40,761 events dropped in total, which is the load that the rate limiter and the Tier-1 exemption are designed for. Half of the watchdog’s 855 log lines are health checks and a quarter are recovery actions, reflecting ten sensor restarts during the capture. These are feasibility traces rather than a detection-rate benchmark: one attack script was used, the benign baseline is short, and watchdog-forced restarts leave gaps in the sensor stream.
14.3. Functional Scenarios
Table 19 summarises the live scenarios run against a running sensor and control plane. The three attack chains exercise the rule engine end to end, and the gate scenarios confirm that protection holds even while the telemetry channel is saturated.
14.4. Test Case Study
Testing ran at four levels because each level caught defects the others missed: unit tests (Go with in-memory SQLite, pytest with injectable fake Jury and militia classes so that consensus logic runs without a Ray cluster, cargo test, and C tests for the behaviour engine); byte-exact wire tests on both sides of the C/Go boundary; live-sensor scripts that drive real hooks (Table 19) and verify CPM and CWP against a running sensor; and boot tests of the unit chain. Table 20, Table 21 and Table 22 list representative cases by area, each backed by a named automated test or live script in the repository; all pass on release 1.0.0.
14.5. Test Suites and Implementation Size
Testing ran at four levels, because each level caught defects the others missed: unit tests, byte-exact wire-contract tests, live-sensor scenarios and end-to-end boot tests of the unit chain (Figure 14). The control plane has 164 Go tests and the swarm 68 pytest tests, with injectable fake actors so consensus logic is testable without a Ray cluster. The live script triggers all event types and three multi-stage attack chains against a running sensor; separate scripts verify CPM and CWP against it. The hand-written implementation, excluding generated code and vendored libbpf, is about 22,000 lines.
14.6. Run-Time Overhead
We measured overhead on a workstation with an Intel Core Ultra 9 275HX (24 threads) running Linux 7.2 with BTF, BPF-LSM and kprobes enabled. The installed kbd and sensor services ran with all 18 sensor programs loaded: 5 BPF-LSM hooks, 2 kprobes, 2 uprobes and 9 tracepoints, including raw_syscalls/sys_enter. Each workload ran five times, alternating between KB stopped and KB running, with the CPU governor fixed to performance and the state database reset before each KB run; we report medians (Table 23, Figure 15).
The added cost is a roughly fixed amount per hooked operation: 38 ns on every system call, from the sys_enter hook that feeds the syscall-entropy dimension, a few hundred nanoseconds for file, network and memory operations that have their own hooks, and 26 s per process creation. In relative terms this is large for a null system call but disappears in real work: a parallel compile and a CPU-bound benchmark both changed by less than 1%, within run-to-run noise. Resident memory was about 28 MB for kbd and 12 MB for the sensor. The micro-benchmarks are single-process floods, so the sensor’s token bucket (burst 100, refilled at 100 per second per process, start time and resource) dropped and counted 150,000–190,000 events per run, which is its designed behaviour. On an earlier 4-vCPU virtual machine where the LSM and kprobe programs could not be loaded, the absolute per-operation costs were about twice as high, and a compile slowed by 14%; the difference between hosts shows that overhead should be measured on the deployment hardware. Aggregating sys_enter counts in a kernel map instead of per event would remove most of the remaining per-syscall cost.
Every individual run is listed in Appendix F.
14.7. Threats to Validity
The policy results come from ADFA-LD traces mapped to KB’s features, not from KB’s own sensor, so they show that the feature design and reward separate the attack classes, not that the deployed system detects those attacks at the same rate. The test split shares the generation process of the training data, and each of the four attack levels has only 25–35 test samples, so per-level accuracy has wide uncertainty. The live capture used a single attack script on one virtual machine, and the full-hook overhead was measured on a single host and kernel. A labelled eBPF corpus and an independent benchmark are needed before the detection rate can be stated.
15. Discussion
- Cost per event.
Every kernel-side operation is constant time and bounded by the verifier (Table 24), so the per-event cost does not grow with load (measured in §Section 14), and all kernel maps are sized at load time so a hostile process cannot exhaust them. User-space state grows with the number of live processes and is flushed on exit; only the audit log grows without bound, by design.
- Memory footprint.
Table 25 lists the fixed and growing structures. All kernel-side memory is fixed when the maps are created, so a hostile process cannot exhaust it, and the LRU maps age out stale entries. User-space state scales with live processes and is flushed on exit. Only two structures grow without bound: the audit log, which is append-only so that history cannot be lost, and the per-flow detector windows, which still need time-based eviction for idle flows.
- Design choices.
Several deliberate choices trade generality for assurance. Unix sockets instead of TCP keep the internal data path off the network. SQLite in WAL mode with a single writer needs no listener and removes lock contention, while the L1 map absorbs reads. systemd dependencies make the boot gate exactly as trustworthy as systemd itself and easy for operators to inspect. The CPM and CWP checks run in the sensor’s user-space bridge rather than in eBPF, because hashing a binary and resolving /proc/pid/exe cannot be done safely in the kernel; the cost is a few microseconds per containment decision, which is rare compared with telemetry. Containment is a single-step decision because that is how the control plane asks the question (which level for this process now?), and it keeps training stable on a small dataset. The Healer stays rule-based, and beacon-triggered containment stays opt-in, because neither has ground truth that would justify giving a learned or statistical rule enforcement authority.
- Learned decisions under a deterministic floor.
The swarm is advisory: CPM and CWP sit below every decision path, the policy engine can enforce without the swarm, and the reward shape encodes the asymmetry between a false positive and a missed attack. This layering means that a mistaken or compromised decision layer can at worst act against an unprotected process: the Healer or an operator can lift levels 1–3, and every action, including a TERMINATE kill, is recorded in the audit log.
- Lessons.
Four lessons from building KB generalise beyond it. Live tests find what unit tests cannot: the most serious defects, namely mismatched wire constants that silently dropped every containment command, a socket design that let telemetry bursts kill the control path, and a missing readiness notification that froze the boot chain, all passed their unit tests and were found only on a running system. Order checks deliberately and fail closed: checking full quarantine before resolving a path, rejecting a HASH-tier containment when the path cannot be resolved, and surfacing a cursor error decide whether a security property actually holds. Look at per-class metrics: a 97.8%-accurate model was failing on two of five classes, and the fix was in feature engineering, not in the learning algorithm. Claims decay: re-checking earlier audit findings, and this paper’s own claims, against the current source repeatedly found statements that had become out of date.
16. Security Audit and Defects Found
We re-verified every previously reported open defect directly against current source rather than carrying old claims forward. Most were already fixed: an unauthenticated HTTP API with wildcard CORS (now a constant-time bearer token and an origin allow-list), the telemetry update that reset containment, the LSM fail-open ordering, a custom SSH server without throttling (replaced by OpenSSH), a hard-coded dashboard host, unvalidated containment RPCs and three Jury consensus defects. Only the rows.Err() gap in the audit verifier was genuinely open, and it is now fixed. Audit findings decay as code moves and must be rechecked at every revision.
Integration testing on real hosts found defects that every unit test had passed (Table 26). Several would have disabled a security property while reporting success.
17. Limitations and Future Work
We state the limitations plainly, because a security system is defined as much by what it does not do as by what it does.
- Training data. The policies are trained on ADFA-LD, a public stand-in: it covers no memory-exploit scenario, and there is no mapping yet from the sensor’s live event stream to the training features. An attack lab that records labelled eBPF telemetry is the next step, followed by a detection-rate benchmark.
- Swarm integration. The swarm’s roles are implemented and tested, but in release 1.0.0 no live path yet connects a flagged process to the consensus round, the Containment policy and the militia (Section 8). The Hunter is a stub planned as a fine-tuned, tool-calling language model; the Healer needs restore-outcome labels before it can be learned; and the courthouse reaches only the restart verdict until peer-drift and boundary-violation detectors exist.
- Watchdog and boot gate. The watchdog’s recovery path still causes sensor restarts under load, so release 1.0.0 keeps it down and the swarm unit no longer requires it; the boot gate of Section 9 is therefore not enforced by the packaged units until the escalation logic is hardened.
- Kill under load. The sensor’s acknowledgement shares the telemetry socket, so under a telemetry flood it can be lost; TERMINATE then fails closed and leaves the process contained but alive. Only TERMINATE waits for the acknowledgement; for levels 1–3 a sensor refusal is audited but not returned to the caller.
- Overhead. Overhead was measured on one workstation and kernel with synthetic workloads; it should be repeated on server hardware with a production-like service.
- Detection inputs. The beacon detector uses connection frequency as a proxy for byte volume, because connect() telemetry carries no payload size; detector windows and the rate limiter keep state in memory, and idle flows are not yet evicted.
- Behaviour rules. Seven of the 19 shipped rules need evidence flags or sequence steps the sensor does not yet produce (shell spawn, ptrace, anonymous executable mappings, exec from /tmp, connect bursts), so they cannot fire (Appendix C).
- Coverage and scope. Operator additions to the CPM executable list have a receiver in the sensor but no sender yet, so the floor is compiled in. kb-mcp has not been audited. All policy is host-local, with no fleet management.
- Trust domain. Any same-host defence shares the kernel’s trust domain; a full kernel compromise is beyond it.
Future work follows directly from these limits:
- an attack lab that captures labelled eBPF telemetry from scripted attacks in isolated virtual machines, so the policies are retrained on KB’s own observation surface;
- a live decision loop that couples the Jury’s quorum with the Containment policy and the militia, with the Hunter built as a tool-calling agent using kb-mcp as its interface;
- restore-outcome labels for a learned Healer, and drift and boundary detectors for the full courthouse;
- multi-step escalation episodes in which the policy observes the effect of each stage;
- byte-level exfiltration telemetry from sendmsg/write accounting;
- server benchmarks (for example web, database and kernel-build workloads) and a hardened watchdog that restores the boot gate;
- fleet operation with a signed central policy store and aggregated audit chains; and
- moving the last line of defence to an out-of-band hardware attestation module, which is being pursued as separate work.
18. Conclusion
Kernel Borderlands shows that a Linux runtime defence can sit where attacks actually happen, inside the kernel, while keeping every automated decision proportionate, gated, auditable and supervised. Its contribution is the combination: graded BPF-LSM enforcement behind authorisation gates that the decision layer cannot bypass, a hash-chained control plane, a swarm whose containment and jury decisions are learned and checked by quorum, and a watchdog that gates the boot sequence. The policies reach 97–100% accuracy on every attack class with no under-containment and no Jury false negatives on held-out data, and the live capture shows the complete loop from kernel event to enforced, audited containment.
Author Contributions
P.S.R.V.K. led the project, designed the system architecture, implemented the kernel subsystem (kb-core: the eBPF sensor, BPF-LSM enforcement and the CPM and CWP gates) and the integrity watchdog (kb-checker), wrote the documentation, and drafted the manuscript. T.K. implemented the control-plane subsystem (kb-control-plane: the kbd daemon with its gRPC and IPC interfaces, storage, process lifecycle, HTTP and SSE API, enforcement and audit chain). K.K. implemented the agent-swarm subsystem (kb-aads: the Ray agents, consensus, dataset pipeline and reinforcement-learning training) and carried out the attack simulation and adversarial testing. R.Y.K. implemented the operator-interface subsystem (kb-op: the command-line tool, the SSH terminal console, the web dashboard and the MCP server). Each author contributed the manuscript sections on their own subsystem. All authors contributed to testing, reviewed the manuscript, and approved the final version.
Funding
This research received no external funding.
Institutional Review Board Statement
Not applicable.
Informed Consent Statement
Not applicable.
Data Availability Statement
ADFA-LD is publicly available from UNSW Canberra. The labelled training, validation and test splits used in this study are in the code repository (https://github.com/pardhuvarmax/kernel-borderlands, directory scripts/dataset/output). The source code, release packages and field manuals are in the same repository. Other data are available from the corresponding author on reasonable request.
Acknowledgments
We thank the maintainers of the Linux kernel, libbpf, Ray and RLlib, and the authors of the ADFA-LD dataset, on whose work this project builds. The research, architecture, design and core implementation are the authors’ own work. Under the authors’ direction, generative AI (Anthropic Claude, via Claude Code) was used for module-level and repository-wide testing including edge cases, the run-time benchmark, field documentation from the authors’ data, minor bug fixes and selected implementation tasks recorded in the project’s change log, and editing and typesetting this manuscript. The authors reviewed and verified all AI-assisted output and take full responsibility for the content of this publication.
Conflicts of Interest
The authors declare no conflicts of interest.
Abbreviations
The following abbreviations are used in this manuscript:
| BTF | BPF Type Format |
| CO-RE | Compile Once, Run Everywhere |
| CPM | Critical Process Module |
| CUSUM | Cumulative sum (control chart) |
| CWP | Critical Workload Protection |
| eBPF | extended Berkeley Packet Filter |
| EMA | Exponential moving average |
| JJE | Judge–Jury–Executor |
| LRU | Least recently used |
| LSM | Linux Security Module |
| MCP | Model Context Protocol |
| PPO | Proximal Policy Optimisation |
| RPC | Remote procedure call |
| SSE | Server-sent events |
| STRIDE | Spoofing, Tampering, Repudiation, Information disclosure, |
| Denial of service, Elevation of privilege | |
| WAL | Write-ahead logging |
Appendix A. Training Configuration
Both policies were trained with the same configuration (Table A1), taken from the training scripts in the repository. Training ran on CPU; other settings are RLlib defaults.
Table A1.
Training configuration of the Containment and Jury policies.
| Setting | Value |
|---|---|
| Algorithm | PPO, RLlib (new API stack), PyTorch |
| Learning rate | |
| Training batch / mini-batch | 2,000 / 256 samples |
| Iterations / epochs per iteration | 40 / 10 |
| Episode | single step; reward of Eq. 5 (Containment) |
| or TP/FP/TN/FN rewards (Jury) | |
| Observation | score, zone, root flag, score delta, event type |
| Sampling | class-balanced: category first, then row |
| Data split | 4,205 / 898 / 908 rows (70/15/15, stratified, seed 42) |
| Training seed | not seeded |
Appendix B. Reproducing the Results
All artefacts are in the public repository (https://github.com/pardhuvarmax/kernel-borderlands), and this paper describes release 1.0.0.
- Packages. The dist/ directory contains the Ubuntu package kernel-borderlands_1.0.0_amd64.deb, the Arch Linux package kernel-borderlands-1.0.0-1-x86_64.pkg.tar.zst, a SHA256SUMS file and field manuals for both distributions. Install with apt install ./<file>.deb or pacman -U <file>.pkg.tar.zst, then run sudo kbctl up.
- Build from source.make in kb-core/ regenerates vmlinux.h and builds the sensor; go build ./... in kb-control-plane/ builds the control plane.
- Data. The labelled training, validation and test splits are in scripts/dataset/output/; the dataset pipeline and training scripts are in kb-aads/.
- Tests.go test ./... in kb-control-plane/ (164 tests), pytest in kb-aads/ (68 tests), cargo test in kb-checker/, and kb-core/tests/test_all_hooks.sh against a running sensor.
- Kernel check. Run the commands of Listing before loading the sensor.
Appendix C. Behaviour Rules and Evidence Flags
The behaviour engine moves a process between states when a rule’s conditions hold (Section 5). Release 1.0.0 ships 19 rules in rules.yaml, compiled by kbd and pushed to the sensor when it connects (Table A2). A rule fires when all its required flags are set, at least the stated number of its optional flags are set, and, where given, its sequence occurs in order within the window. The sensor currently sets 13 of the defined evidence flags (Table A3); seven rules depend on flags or sequence steps that are defined but not yet produced, so they cannot fire. They are kept because they encode the intended detections and will start working as the corresponding telemetry is added.
Table A2.
Behaviour rules shipped in release 1.0.0 (states abbreviated: OBS observed, SUSP suspicious, BORD borderlands, COMP compromised).
Table A2.
Behaviour rules shipped in release 1.0.0 (states abbreviated: OBS observed, SUSP suspicious, BORD borderlands, COMP compromised).
| Rule | Transition | Conditions (flags; optional flags; sequence) | Window | Can fire |
|---|---|---|---|---|
| known_ioc_sequence | SAFE→COMP | OUTBOUND_CONNECT, RWX_MAPPING; EXEC_FROM_TMP, EXEC_FROM_PROC (); seq. EXEC → OUTBOUND_CONNECT → RWX_MAP | 30 s | no |
| reverse_shell_compromised | BORD→COMP | OUTBOUND_CONNECT, SPAWNED_SHELL; seq. OUTBOUND_CONNECT → EXEC_SHELL | 60 s | no |
| injection_ptrace_compromised | BORD→COMP | RWX_MAPPING, PTRACE_USED; seq. RWX_MAP → PTRACE | 30 s | no |
| injection_proc_mem_compromised | BORD→COMP | RWX_MAPPING, PROC_MEM_WRITE; seq. RWX_MAP → PROC_MEM_WRITE | 30 s | yes |
| escalation_exfil_compromised | BORD→COMP | PRIVILEGE_GAINED, OUTBOUND_CONNECT; SHADOW_ACCESS, PASSWD_ACCESS, SSH_KEY_ACCESS () | 120 s | yes |
| rwx_memory_abuse | SUSP→BORD | RWX_MAPPING, PROC_MEM_WRITE, PROCESS_VM_WRITE () | – | yes |
| ptrace_injection_borderlands | SUSP→BORD | PTRACE_USED | – | no |
| c2_port_connection | SUSP→BORD | OUTBOUND_CONNECT, C2_CANDIDATE_PORT | – | yes |
| proc_mem_write_borderlands | SUSP→BORD | PROC_MEM_WRITE | – | yes |
| rapid_connect_burst_borderlands | SUSP→BORD | RAPID_CONNECT_BURST | – | no |
| multi_anomaly_suspicious | OBS→SUSP | EXEC_FROM_TMP, EXEC_FROM_PROC, SPAWNED_SHELL, PRIVILEGE_GAINED, ROOT_ACHIEVED, RWX_MAPPING, ANON_EXEC, WX_TRANSITION, OUTBOUND_CONNECT, NONSTANDARD_PORT, SHADOW_ACCESS, SSH_KEY_ACCESS, HIGH_SYSCALL_ENTROPY () | – | yes |
| privilege_plus_network_suspicious | OBS→SUSP | PRIVILEGE_GAINED, OUTBOUND_CONNECT | 60 s | yes |
| cred_file_access_suspicious | OBS→SUSP | SHADOW_ACCESS, PASSWD_ACCESS, SUDOERS_ACCESS, SSH_KEY_ACCESS () | – | yes |
| anon_exec_mapping_suspicious | OBS→SUSP | ANON_EXEC | – | no |
| shell_spawn_suspicious | OBS→SUSP | SPAWNED_SHELL | – | no |
| unexpected_outbound_connection | SAFE→OBS | OUTBOUND_CONNECT | – | yes |
| privilege_change_observed | SAFE→OBS | PRIVILEGE_GAINED, ROOT_ACHIEVED, CAP_GAINED () | – | yes |
| high_syscall_entropy_observed | SAFE→OBS | HIGH_SYSCALL_ENTROPY | – | yes |
| first_anomaly_observed | SAFE→OBS | any flag | – | yes |
Table A3.
Evidence flags by group; flags marked * are set by the release 1.0.0 sensor.
| Group | Flags |
|---|---|
| Execution | EXEC_FROM_TMP, EXEC_FROM_PROC, SPAWNED_SHELL, ANOMALOUS_PARENT, RAPID_EXEC_BURST, EXECVEAT |
| Privilege | PRIVILEGE_GAINED, ROOT_ACHIEVED, CAP_GAINED, SETUID_ABUSE |
| Memory | RWX_MAPPING, ANON_EXEC, WX_TRANSITION, LARGE_ANON_EXEC, PROC_MEM_WRITE, PROCESS_VM_WRITE |
| Files | SHADOW_ACCESS, PASSWD_ACCESS, SUDOERS_ACCESS, SSH_KEY_ACCESS, CRON_WRITE, BINARY_PATH_WRITE |
| Network | OUTBOUND_CONNECT, NONSTANDARD_PORT, C2_CANDIDATE_PORT, BIND_LISTENER, DNS_TUNNEL_SUSPECT, RAPID_CONNECT_BURST |
| System calls | HIGH_SYSCALL_ENTROPY, PTRACE_USED, SECCOMP_BYPASS_ATTEMPT, UNUSUAL_SYSCALL_SEQ |
Appendix D. Default Configuration
The package installs its configuration under /etc/kb. Listings – show the shipped defaults with comments removed. The workload registry ships empty; its documented example registers a database by path and a secrets manager by hash. Note that the default policy enables automatic termination for postgres, which operators should review now that TERMINATE kills.
Appendix E. Selected Source Listings
The listings below are taken from release 1.0.0 with comments removed; elisions are marked. They show the code behind the main mechanisms: the in-kernel rate limiter (Listing ), an enforcement hook (Listing ), the sensor’s gated command handler (Listing ), the control plane’s containment path with the TERMINATE kill (Listing ), the audit chain (Listing ), the Jury round (Listing ) and the watchdog’s liveness check (Listing ). In the rate limiter, setting last_time to the current time after a refill discards the fractional part of the elapsed interval, so the effective refill rate is slightly below 100 tokens per second.
Appendix F. Raw Run-Time Overhead Data
Table A4 gives every run behind the medians of Table 23. Baseline and KB runs alternated five times on the same host (Intel Core Ultra 9 275HX, 24 logical CPUs, Linux 7.2.11 with the performance governor, active LSMs capability,landlock,lockdown,yama,bpf), with the sensor and kbd built from commit 450ff3e and their SHA-256 digests recorded. Resident memory stayed between 27.3 and 29.9 MB for kbd and 12.0 and 12.5 MB for the sensor. Idle CPU over 30 s was 0.3% of one core for each process in the first three runs, and 0.8–1.2% (kbd) and 5.0–8.4% (sensor) in the last two runs. The rate limiter dropped between 151,579 and 189,881 events per run during the floods.
Table A4.
All five alternating runs (base: no KB; KB: all 18 programs loaded)
| Benchmark | Run 1 | Run 2 | Run 3 | Run 4 | Run 5 | Median | |
|---|---|---|---|---|---|---|---|
| Null system call (ns) | base | 72.4 | 65.7 | 65.4 | 67.1 | 76.2 | 67.1 |
| KB | 98.9 | 106.5 | 105.2 | 105.3 | 99.4 | 105.2 | |
| open/close (ns) | base | 424 | 431 | 511 | 422 | 425 | 425 |
| KB | 624 | 762 | 611 | 617 | 633 | 624 | |
| socket connect (ns) | base | 1293 | 1312 | 1447 | 1380 | 1387 | 1380 |
| KB | 1670 | 1565 | 1647 | 1701 | 1796 | 1670 | |
| mmap/mprotect (ns) | base | 959 | 1189 | 930 | 901 | 978 | 959 |
| KB | 1107 | 1109 | 1075 | 1154 | 1232 | 1109 | |
| fork+exec (s) | base | 225.4 | 222.4 | 220.2 | 230.9 | 234.5 | 225.4 |
| KB | 243.7 | 251.7 | 248.0 | 259.8 | 266.2 | 251.7 | |
| compile, make -j4 (s) | base | 0.999 | 0.983 | 1.082 | 1.007 | 1.124 | 1.007 |
| KB | 0.987 | 1.010 | 0.988 | 1.012 | 1.087 | 1.010 | |
| sysbench cpu (events/s) | base | 15,013 | 15,588 | 15,737 | 15,698 | 14,530 | 15,588 |
| KB | 15,698 | 15,153 | 15,633 | 15,473 | 14,762 | 15,473 |
Appendix G. Wire Structures
Listing gives the packed C structures behind the message types of Table 4, taken from kb-core/userspace/bridge/ with comments removed. Every frame is preceded by a 4-byte little-endian length. The Go side decodes the same layouts field by field, and tests on both sides fix the sizes: 128 bytes for kb_wire_process_state, 40 bytes for kb_wire_zone_transition, 76 bytes (a 72-byte payload after the header) for the containment command, and 16 bytes for the acknowledgement.
References
- Gregg, B. BPF Performance Tools: Linux System and Application Observability; Addison-Wesley: Boston, 2019. [Google Scholar]
- Singh, K. Kernel Runtime Security Instrumentation (KRSI) / BPF LSM. Linux kernel documentation. 2020. Available online: https://docs.kernel.org/bpf/prog_lsm.html.
- McCanne, S.; Jacobson, V. The BSD Packet Filter: A New Architecture for User-level Packet Capture. In Proceedings of the Proceedings of the USENIX Winter 1993 Conference, 1993; pp. 259–270. [Google Scholar]
- Høiland-Jørgensen, T.; Brouer, J.D.; Borkmann, D.; Fastabend, J.; Herbert, T.; Ahern, D.; Miller, D. The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel. In Proceedings of the Proceedings of the 14th International Conference on Emerging Networking EXperiments and Technologies (CoNEXT), 2018; pp. 54–66. [Google Scholar] [CrossRef]
- libb. libbpf and BPF CO-RE (Compile Once – Run Everywhere); pf, Ed.; 2026; Available online: https://github.com/libbpf/libbpf (accessed on 27 October 2026).
- Loscocco, P.; Smalley, S. Integrating Flexible Support for Security Policies into the Linux Operating System. In Proceedings of the Proceedings of the USENIX Annual Technical Conference, FREENIX Track, 2001; pp. 29–42. [Google Scholar]
- Wright, C.; Cowan, C.; Smalley, S.; Morris, J.; Kroah-Hartman, G. Linux Security Modules: General Security Support for the Linux Kernel. In Proceedings of the Proceedings of the 11th USENIX Security Symposium, 2002; pp. 17–31. [Google Scholar]
- Forrest, S.; Hofmeyr, S.A.; Somayaji, A.; Longstaff, T.A. A Sense of Self for Unix Processes. In Proceedings of the Proceedings of the IEEE Symposium on Security and Privacy, 1996; pp. 120–128. [Google Scholar] [CrossRef]
- Liao, H.J.; Lin, C.H.R.; Lin, Y.C.; Tung, K.Y. Intrusion Detection System: A Comprehensive Review. J. Netw. Comput. Appl. 2013, 36, 16–24. [Google Scholar] [CrossRef]
- Tavallaee, M.; Bagheri, E.; Lu, W.; Ghorbani, A.A. A Detailed Analysis of the KDD CUP 99 Data Set. In Proceedings of the Proceedings of the IEEE Symposium on Computational Intelligence for Security and Defense Applications, 2009; pp. 1–6. [Google Scholar] [CrossRef]
- McHugh, J. Testing Intrusion Detection Systems: A Critique of the 1998 and 1999 DARPA Intrusion Detection System Evaluations as Performed by Lincoln Laboratory. ACM Trans. Inf. Syst. Secur. 2000, 3, 262–294. [Google Scholar] [CrossRef]
- Creech, G.; Hu, J. Generation of a New IDS Test Dataset: Time to Retire the KDD Collection. In Proceedings of the Proceedings of the IEEE Wireless Communications and Networking Conference (WCNC), 2013; pp. 4487–4492. [Google Scholar] [CrossRef]
- The Falco Project. Falco: Cloud Native Runtime Security. 2026. Available online: https://falco.org/ (accessed on 27 October 2026).
- Aqua Security. Tracee: Runtime Security and Forensics Using eBPF. 2026. Available online: https://github.com/aquasecurity/tracee (accessed on 27 October 2026).
- Cilium (Ed.) Tetragon: eBPF-based Security Observability and Runtime Enforcement; 2026; Available online: https://tetragon.io/ (accessed on 27 October 2026).
- Nguyen, T.T.; Reddi, V.J. Deep Reinforcement Learning for Cyber Security. IEEE Trans. Neural Netw. Learn. Syst. 2023, 34, 3779–3795. [Google Scholar] [CrossRef] [PubMed]
- Schulman, J.; Wolski, F.; Dhariwal, P.; Radford, A.; Klimov, O. Proximal Policy Optimization Algorithms. arXiv 2017, arXiv:1707.06347. [Google Scholar]
- Liang, E.; Liaw, R.; Nishihara, R.; Moritz, P.; Fox, R.; Goldberg, K.; Gonzalez, J.; Jordan, M.; Stoica, I. RLlib: Abstractions for Distributed Reinforcement Learning. In Proceedings of the Proceedings of the 35th International Conference on Machine Learning (ICML), 2018; pp. 3053–3062. [Google Scholar]
- Moritz, P.; Nishihara, R.; Wang, S.; Tumanov, A.; Liaw, R.; Liang, E.; Elibol, M.; Yang, Z.; Paul, W.; Jordan, M.I.; et al. Ray: A Distributed Framework for Emerging AI Applications. In Proceedings of the Proceedings of the 13th USENIX Symposium on Operating Systems Design and Implementation (OSDI), 2018; pp. 561–577. [Google Scholar]
- Lamport, L.; Shostak, R.; Pease, M. The Byzantine Generals Problem. ACM Trans. Program. Lang. Syst. 1982, 4, 382–401. [Google Scholar] [CrossRef]
- Schneier, B.; Kelsey, J. Secure Audit Logs to Support Computer Forensics. ACM Trans. Inf. Syst. Secur. 1999, 2, 159–176. [Google Scholar] [CrossRef]
- Crosby, S.A.; Wallach, D.S. Efficient Data Structures for Tamper-Evident Logging. In Proceedings of the Proceedings of the 18th USENIX Security Symposium, 2009; pp. 317–334. [Google Scholar]
- Trusted Computing Group. Trusted Platform Module Library, Family 2.0, Level 00, Revision 1.59; Technical report; Trusted Computing Group, 2019. [Google Scholar]
- Sailer, R.; Zhang, X.; Jaeger, T.; van Doorn, L. Design and Implementation of a TCG-based Integrity Measurement Architecture. In Proceedings of the Proceedings of the 13th USENIX Security Symposium, 2004; pp. 223–238. [Google Scholar]
- Hardy, N. The Confused Deputy (or Why Capabilities Might Have Been Invented). ACM SIGOPS Oper. Syst. Rev. 1988, 22, 36–38. [Google Scholar] [CrossRef]
- Shannon, C.E. A Mathematical Theory of Communication. Bell Syst. Tech. J. 1948, 27, 379–423. [Google Scholar] [CrossRef]
- Welford, B.P. Note on a Method for Calculating Corrected Sums of Squares and Products. Technometrics 1962, 4, 419–420. [Google Scholar] [CrossRef]
- Page, E.S. Continuous Inspection Schemes. Biometrika 1954, 41, 100–115. [Google Scholar] [CrossRef]
- Kerrisk, M. The Linux Programming Interface; No Starch Press: San Francisco, 2010. [Google Scholar]
Figure 1.
KB dataflow. Solid arrows carry telemetry and commands over root-owned Unix sockets; dashed arrows are the watchdog’s audits. Telemetry and control use separate sockets so that a telemetry burst cannot block an enforcement order
Figure 1.
KB dataflow. Solid arrows carry telemetry and commands over root-owned Unix sockets; dashed arrows are the watchdog’s audits. Telemetry and control use separate sockets so that a telemetry burst cannot block an enforcement order

Figure 3.
Reward of Eq. 5. Choosing too weak a level is penalised more steeply than choosing too strong a level
Figure 3.
Reward of Eq. 5. Choosing too weak a level is penalised more steeply than choosing too strong a level

Figure 4.
Entropy of Eq. 8 for intervals spread evenly over 1 to 5 bins. Only timing concentrated in at most two bins falls below the alert threshold
Figure 4.
Entropy of Eq. 8 for intervals spread evenly over 1 to 5 bins. Only timing concentrated in at most two bins falls below the alert threshold

Figure 5.
The sensor’s handling of a containment command. Every outcome, including refusals and a failed map write, is reported back to kbd in a containment acknowledgement (dashed).
Figure 5.
The sensor’s handling of a containment command. Every outcome, including refusals and a failed map write, is reported back to kbd in a containment acknowledgement (dashed).

Figure 6.
The TERMINATE path. The kill in step 7 happens only after both kbd’s own gate (step 1) and the sensor’s gates (step 4) allow it and the sensor confirms the map write (step 6).
Figure 6.
The TERMINATE path. The kill in step 7 happens only after both kbd’s own gate (step 1) and the sensor’s gates (step 4) allow it and the sensor confirms the map write (step 6).

Figure 7.
Class distribution of the train, validation and test splits by target level. Benign rows make up 87–88% of each split.
Figure 7.
Class distribution of the train, validation and test splits by target level. Benign rows make up 87–88% of each split.

Figure 8.
Representative boot timeline of the boot design. Each phase starts only after its After=/Requires= dependency is satisfied
Figure 8.
Representative boot timeline of the boot design. Each phase starts only after its After=/Requires= dependency is satisfied

Figure 9.
Deployment of release 1.0.0 on one host. Solid boxes are units started by kbctl up; dashed boxes are installed but not started. Unix sockets are shown in teal and loopback or operator endpoints in amber.
Figure 9.
Deployment of release 1.0.0 on one host. Solid boxes are units started by kbctl up; dashed boxes are installed but not started. Unix sockets are shown in teal and loopback or operator endpoints in amber.

Figure 10.
End-to-end containment path. (1) exec, credential, file, network and memory events; (2) rate-limited ring buffer; (3) ProcessState over kbd.sock; (4) score and zone reach BORDERLANDS; (5) hash-chained audit append; (6) alert over kba.sock; (7) quorum reached, SetContainment; (8) command over kbct.sock; (9) CPM then CWP gates; (10) containment map written; (11) the next blocked operation is denied. Steps 1–5 run without the swarm, and the policy engine can issue step 8 directly.
Figure 10.
End-to-end containment path. (1) exec, credential, file, network and memory events; (2) rate-limited ring buffer; (3) ProcessState over kbd.sock; (4) score and zone reach BORDERLANDS; (5) hash-chained audit append; (6) alert over kba.sock; (7) quorum reached, SetContainment; (8) command over kbct.sock; (9) CPM then CWP gates; (10) containment map written; (11) the next blocked operation is denied. Steps 1–5 run without the swarm, and the policy engine can issue step 8 directly.

Figure 11.
Containment policy accuracy per level on 908 held-out samples. Attack classes rise to 97.1–100%; under-containment falls from 1.1% to 0.0%
Figure 11.
Containment policy accuracy per level on 908 held-out samples. Attack classes rise to 97.1–100%; under-containment falls from 1.1% to 0.0%

Figure 12.
Delay from each BORDERLANDS transition to the containment action logged by the sensor

Figure 13.
Composition of the live capture. Top: hook events by type (7,703). Bottom: watchdog log lines by tag (855). *File access, network connect/bind, mmap, TLS plaintext. †Performance, map audit, integrity, warnings, liveness.
Figure 13.
Composition of the live capture. Top: hook events by type (7,703). Bottom: watchdog log lines by tag (855). *File access, network connect/bind, mmap, TLS plaintext. †Performance, map audit, integrity, warnings, liveness.

Figure 14.
Test coverage (top) and implementation size (bottom). “live scenarios” counts the scenarios of the live-sensor script; generated and vendored code excluded
Figure 14.
Test coverage (top) and implementation size (bottom). “live scenarios” counts the scenarios of the live-sensor script; generated and vendored code excluded

Figure 15.
Overhead by workload with all 18 sensor programs loaded. The cost is a fixed amount per hooked operation, so it matters only for operation-intensive code
Figure 15.
Overhead by workload with all 18 sensor programs loaded. The cost is a fixed amount per hooked operation, so it matters only for operation-intensive code

Table 1.
Capabilities of Linux runtime-defence approaches.
| Capability | MAC | auditd | Falco | Tracee | Tetragon | KB |
|---|---|---|---|---|---|---|
| Kernel vantage point | part. | |||||
| Behavioural sequences | – | – | rules | rules | ||
| In-kernel enforcement | – | – | – | |||
| Graded containment levels | – | – | – | – | – | 5 |
| Learned decision policy | – | – | – | – | – | PPO |
| Quorum before acting | – | – | – | – | – | |
| Self-protection gates | part. | – | – | – | – | |
| Tamper-evident audit | – | part. | – | – | – | |
| Boot-gating watchdog | – | – | – | – | – |
Table 2.
STRIDE analysis of attacks against KB itself.
| Threat | Example | Mitigation |
|---|---|---|
| Spoofing | Rename a process to look like a protected daemon | Gates resolve /proc/pid/exe; HASH tier checks SHA-256 |
| Tampering | Edit audit rows to hide an action | Hash chain; verification detects any change |
| Repudiation | Operator denies an isolation | Every RPC, HTTP action and SSH session is audited |
| Disclosure | Read the process list over HTTP | Loopback bind, bearer token, origin allow-list |
| Denial of service | Flood telemetry to stall containment | Per-resource token bucket; separate control socket |
| Elevation | Use KB against PID 1 or its watchdog | CPM floor and ordered CWP gate |
Table 3.
Socket topology. All are Unix domain sockets under /run/kb/.
| Socket | Direction | Carries |
|---|---|---|
| kbd.sock | sensor → kbd | ProcessState (128 B), ZoneTransition (40 B), flow timestamps |
| kbct.sock | kbd→ sensor | containment (72 B payload), paths, rules, workloads |
| kba.sock | clients ↔ kbd | gRPC service (13 RPCs) for swarm and operators |
| kbc.sock | clients ↔ checker | watchdog diagnostics |
Table 4.
Message types on the sensor/control-plane boundary.
| Type | Message | Direction (socket) | Contents |
|---|---|---|---|
| 1 | PROCESS_STATE | sensor → kbd (kbd.sock) | 128-byte state: identity, six dimension scores, composite, EMA, entropy, zone |
| 2 | ZONE_TRANSITION | sensor → kbd (kbd.sock) | 40 bytes: PID, from/to zone, score, start time |
| 3 | rules | kbd → sensor (kbct.sock) | compiled behaviour rules pushed at connect time |
| 4 | PROCESS_EXIT | sensor → kbd (kbd.sock) | PID, exit time and code; flushes per-PID state |
| 5 | CONTAINMENT_CMD | kbd → sensor (kbct.sock) | 72-byte payload: PID, level 0–4, 64-byte reason |
| 6 | SENSITIVE_PATHS | kbd → sensor (kbct.sock) | policy paths added to the compiled-in floor |
| 7 | CPM_PROTECTED_EXEC | kbd → sensor (kbct.sock) | receiver only; no sender yet (floor is compiled in) |
| 8 | CWP_WORKLOADS | kbd → sensor (kbct.sock) | path, identity tier and SHA-256 per workload |
| 9 | NET_FLOW | sensor → kbd (kbd.sock) | PID, address, port, timestamp for beacon detection |
| 10 | CONTAINMENT_ACK | sensor → kbd (kbd.sock) | PID, level and the gates’ decision (Table 7) |
Table 5.
kb-core attachment points.
| Attachment | Type | Purpose |
|---|---|---|
| sched_process_exec/exit | telemetry | lifecycle; protected-PID registration |
| raw_syscalls/sys_enter | telemetry | per-process syscall counts (entropy) |
| kprobe/commit_creds | telemetry | credential changes (never throttled) |
| sys_enter_openat | telemetry | file access, sensitive paths |
| connect / bind | telemetry | outbound flows, listening sockets |
| mmap / mprotect | telemetry | anonymous and RWX memory |
| process_vm_writev | telemetry | cross-process memory writes |
| kprobe/security_capable | telemetry | capability probes |
| uprobe TLS write | telemetry | plaintext (OpenSSL, GnuTLS, NSS, Go) |
| lsm/file_open | enforce | level 2: sensitive paths; ≥3: all |
| lsm/bprm_check_security | enforce | level ≥2: deny exec |
| lsm/socket_connect, _bind | enforce | level ≥2: deny IPv4/IPv6 |
| lsm/file_mprotect | enforce | level ≥3: deny PROT_EXEC |
Table 6.
Containment levels and what each one blocks.
| Lvl | Name | Throttle | Sens. files | exec/net | PROT_EXEC | Kill |
|---|---|---|---|---|---|---|
| 0 | NONE | – | – | – | – | – |
| 1 | CGROUP | – | – | – | – | |
| 2 | SECCOMP | – | – | |||
| 3 | NAMESPACE | – | ||||
| 4 | TERMINATE |
Table 7.
Decision codes in the containment acknowledgement.
| Code | Decision | Meaning |
|---|---|---|
| 0 | applied | map entry written (level ) or removed (level 0) |
| 1 | CPM kernel thread | /proc/pid/exe could not be resolved |
| 2 | CPM PID 1 | target is init |
| 3 | CPM protected PID | PID registered in protected_pids_map |
| 4 | CPM protected executable | executable on the protected list |
| 5 | CWP path | registered PATH-tier workload |
| 6 | CWP hash | registered HASH-tier workload whose binary still matches |
| 7 | map update failed | gates passed but the BPF map write or delete failed |
Table 8.
External interfaces of kbd.
| Interface | Operations |
|---|---|
| gRPC KernelBorderlands (13 RPCs) | GetProcessState, ListZone, SetContainment, StreamEvents, StreamAlerts, SubmitAgentDecision, GetSystemStats, VerifyAuditChain, ExportAuditLog, OverrideZone, ReloadPolicy, ReloadWorkloads, RecordSSHSession |
| HTTP/SSE (dashboard) | /api/processes, /api/alerts, /api/logs, /api/audit/verify, /api/services, /api/events (SSE), /api/metrics, /api/agents, /api/policy; token-protected: /api/isolate (level 3), /api/kill (level 4), /api/restore, /api/policy/reload |
Table 9.
Agent roles in kb-aads.
| Agent | Behaviour |
|---|---|
| Patroller | tracks per-PID scores; hands a PID to a Hunter the first time it crosses the suspicious threshold (40 by default) and re-arms when the score falls back below it |
| Hunter | investigation of flagged processes; a stub whose evidence-gathering steps are not yet implemented |
| Judge | opens a consensus round, spawns a fresh Jury pool (5 by default), calls the Executor on quorum, and applies the courthouse check to other agents |
| Jury | votes CONTAIN or ALLOW with the trained policy given a full observation, otherwise with the 0.75 confidence threshold |
| Executor | submits a quorum decision to kbd over gRPC on kba.sock; it never writes the map |
| Containment | chooses the level 0–4 with the trained PPO policy |
| Militia | a squad lead spawns one member per stage and applies CGROUP to TERMINATE in order, halting at the first failure |
| Healer | restores a process only when it is SAFE, scores below 20 and has been contained for 60 ticks |
| Relays, registry | long-lived actors that route messages to a pool; the registry maps agent IDs to actor handles so the Judge can reach dynamically spawned agents |
Table 10.
ADFA-LD mapping and labelled rows (6,011 total; split 4,205 / 898 / 908).
| ADFA-LD family | KB scenario | Target level | Rows |
|---|---|---|---|
| Normal + live /proc | normal | 0 NONE | 5,265 |
| Hydra-FTP | credential_access | 1 CGROUP | 162 |
| Hydra-SSH | lateral_movement | 2 SECCOMP | 176 |
| Adduser | privilege_escalation | 3 NAMESPACE | 91 |
| Java-Meterpreter | process_injection | 3 NAMESPACE | 124 |
| Meterpreter, Web-Shell | reverse_shell | 4 TERMINATE | 193 |
Table 12.
Implementation by subsystem (non-blank lines of hand-written source and tests; generated code and vendored libraries excluded).
Table 12.
Implementation by subsystem (non-blank lines of hand-written source and tests; generated code and vendored libraries excluded).
| Subsystem | Language | Main components | kLOC |
|---|---|---|---|
| kb-core | C, eBPF | unified sensor, LSM hooks, gates, behaviour engine | 6.3 |
| kb-control-plane | Go | ingestion, L1/L2 store, policy, audit, detection, APIs | 8.2 |
| kb-aads | Python | Ray actors, JJE consensus, PPO training and inference | 2.4 |
| kb-checker | Rust | integrity audits, map self-heal, boot gate, fallbacks | 1.1 |
| kb-op | Go, Rust, TS | kbctl, kb-tui, dashboard, kb-mcp | 4.2 |
| Total | 22.1 |
Table 13.
Software stack by subsystem.
| Component | Requirement |
|---|---|
| Kernel | Linux 5.10 or later with CONFIG_DEBUG_INFO_BTF=y, CONFIG_BPF_LSM=y and bpf in the active LSM list (Table 14) |
| eBPF toolchain | clang/LLVM, bpftool, vendored libbpf, libelf and zlib |
| Control plane | Go 1.25, gRPC and Protocol Buffers, SQLite 3 in WAL mode |
| Agent swarm | Python 3 with Ray and RLlib (≥2.9) and PyTorch (≥2.0), in a virtual environment |
| Watchdog and console | Rust (edition 2021); kb-tui uses ratatui and a dedicated OpenSSH sshd |
| Dashboard | React 19 and TypeScript, built with Node.js and served by kbopd |
| Service management | systemd (Type=notify, After=/Requires=/Wants=) |
| Containment primitives | BPF-LSM hooks, cgroup v2 (level 1), pidfd signalling (level 4) |
Table 15.
Linux distribution compatibility
| Distribution | Kernel | BTF | BPF-LSM | Status | Notes |
|---|---|---|---|---|---|
| Ubuntu 24.04 LTS | 6.8 | yes | boot param | Native | primary development platform |
| Ubuntu 22.04 LTS | 5.15/6.8 | yes | boot param | Native | .deb package |
| Arch Linux | latest | yes | yes | Native | tested and deployed with the Arch package |
| Debian 12 | 6.1 | yes | boot param | Target | same set-up as Ubuntu |
| Linux Mint 21/22 | 5.15/6.8 | yes | boot param | Target | Ubuntu LTS base |
| Kali (rolling) | 6.x | yes | boot param | Target | Debian base |
| Fedora 40+ | 6.8+ | yes | check list | Target | SELinux alongside BPF-LSM |
| openSUSE Tumbleweed | latest | yes | check list | Target | rolling release |
| RHEL 9+ | 5.14+bp | yes | vendor | Verify | backported eBPF features |
| CentOS Stream 9 | 5.14+bp | yes | vendor | Verify | upstream of RHEL 9 |
| Rocky / Alma 9+ | 5.14+bp | yes | vendor | Verify | RHEL-compatible |
| Oracle Linux 9+ | UEK 5.15+ | yes | vendor | Verify | prefer UEK with BTF |
| Amazon Linux 2023 | 6.1+ | yes | check | Verify | verify kernel configuration |
| Alpine | 6.x LTS | check | check | Experimental | musl user space needs static builds |
| NixOS | config. | config. | config. | Experimental | set bpf in kernel parameters |
| RHEL 7 / Ubuntu 18.04 | 3.10/4.15 | no | no | Unsupported | no BTF, ring buffer or BPF-LSM |
Table 16.
Service units in release 1.0.0
| Unit | Dependencies | Role and status |
|---|---|---|
| kbd.service | After=local-fs | control plane; Type=notify; started by kbctl up |
| kb-sensor.service | Requires=kbd | eBPF sensor; restarted by systemd on failure |
| kb-checker.service | Requires=kbd, Wants=kb-sensor | watchdog; installed, kept down while its recovery path is repaired |
| kbagents.service | Requires=kbd | swarm launcher; installed, not enabled |
| kbopd.service | Requires=kbd | serves the dashboard |
| sshd@kb-operator | After=network | operator SSH on port 2222 with kb-tui as forced command |
Table 18.
Jury policy on the 908-sample test set.
| Pred. C | Pred. A | Metric | Value | |
|---|---|---|---|---|
| Actual CONTAIN | 117 | 0 | Accuracy | 100.0% |
| Actual ALLOW | 0 | 791 | Recall (C) | 100.0% |
| FN rate | 0.0% |
Table 19.
Live functional scenarios; all passed.
| Scenario | Observed behaviour |
|---|---|
| All telemetry types (exec, exit, creds, files, connect, bind, mmap, mprotect) | expected events, flags and sensitive-path marks |
| Chain A: privilege change + credential access | SAFE → OBSERVED → SUSPICIOUS |
| Chain B: memory injection | SUSPICIOUS → BORDERLANDS → COMPROMISED |
| Chain C: C2 connection to port 4444 | SAFE → SUSPICIOUS → BORDERLANDS |
| Contain PID 1, kbd or kb-checker | refused by CPM |
| Contain a protected PID while kbd.sock is flooded | refused; control path unaffected |
| CWP PATH tier / modified HASH-tier binary | refused / “Identity Verification Failed”, allowed |
| Level 2 process runs curl; level 3 calls mprotect(PROT_EXEC) | denied in both cases (EPERM on exec, EACCES on connect and mprotect) |
| Tampered audit row; I/O error during verification | chain reported broken; error surfaced |
| Rigid 10 s beacon vs. human-like timing | flagged; high entropy ignored |
Table 20.
Test cases: containment, gates and the kill path.
| ID | Input | Expected and observed |
|---|---|---|
| C1 | contain PID 1, or kbd’s own PID | refused before any command is sent |
| C2 | contain a process named systemd or kbd; TERMINATE on kb-checker | refused by kbd’s name floor; refused by its executable list |
| C3 | contain a protected PID while kbd.sock is flooded (live) | refused; control path unaffected |
| C4 | CWP PATH-tier workload; modified HASH-tier binary (live) | refused; “Identity Verification Failed” and allowed |
| C5 | routine telemetry for a contained PID | containment level preserved (regression test) |
| C6 | restore (level 0) on a protected PID | allowed: release is never gated |
| C7 | dashboard Isolate | sent as NAMESPACE, never TERMINATE |
| C8 | TERMINATE refused by kbd’s gate | nothing sent, nothing killed |
| C9 | TERMINATE refused by the sensor, or no acknowledgement | no kill; unanswered PID locked out |
| C10 | acknowledgement for a different PID | ignored |
| C11 | levels 1–3 | no kill and no wait for an acknowledgement |
| C12 | real pidfd against a test child process | child killed; PID 1 refused |
Table 21.
Test cases: audit chain, API security, workloads and detection.
| ID | Input | Expected and observed |
|---|---|---|
| A1 | modify one row’s hash, then verify | chain reported broken |
| A2 | fault-injecting driver fails the cursor mid-scan | error surfaced, not a shorter valid chain |
| A3 | state-changing HTTP route with no server token configured | 503: fails closed |
| A4 | wrong or missing bearer token | 401 |
| A5 | request from an unknown origin | CORS header not reflected |
| A6 | tampered, unsigned or wrong-key workload registry | rejected; previous registry kept |
| A7 | HASH-tier workload with the hash omitted | digest computed locally from the binary |
| A8 | rigid beacon vs. varied, human-like timing | low entropy flagged; high entropy ignored |
| A9 | low entropy without a volume shift | no beacon verdict: both signals required |
| A10 | sustained rigid beacon | one alert per flow per five minutes |
| A11 | BORDERLANDS alert for a CWP workload | severity raised one tier with owner metadata |
Table 22.
Test cases: agent swarm.
| ID | Input | Expected and observed |
|---|---|---|
| S1 | Jury confidence exactly 0.75; just above | ALLOW (strict threshold); CONTAIN |
| S2 | tied votes; empty vote list | no quorum; false without raising |
| S3 | two of three jurors crash, survivor votes CONTAIN | round completes, quarantine executed |
| S4 | all jurors crash | round skipped without raising |
| S5 | militia target level 3; NAMESPACE stage fails at target 4 | three stages in order; halts before TERMINATE |
| S6 | agent uptime does not advance in 3 s | courthouse verdict RESTART |
| S7 | Healer with a missing field | keep contained |
| S8 | Jury with a full observation and the trained checkpoint | policy used; threshold fallback without it |
| S9 | Patroller score crossing the threshold twice | escalated once until the score drops |
Table 23.
Median cost per operation, all hooks loaded (5 runs each).
| Workload | KB off | KB on | Added | Change |
|---|---|---|---|---|
| null system call (getppid) | 67.1 ns | 105.2 ns | 38 ns | +57% |
| open + close | 425 ns | 624 ns | 199 ns | +47% |
| UDP socket + connect | 1.38 s | 1.67 s | 290 ns | +21% |
| mmap/mprotect | 959 ns | 1,109 ns | 150 ns | +16% |
| fork + exec /bin/true | 225 s | 252 s | 26 s | +11.7% |
| Compile (make -j4) | 1.007 s | 1.010 s | – | +0.4% |
| sysbench cpu, 4 threads | 15,588/s | 15,473/s | – | −0.7% |
Table 24.
Cost of the main operations (P processes, A audit rows, n jurors).
| Operation | Cost | Note |
|---|---|---|
| Telemetry hook, token bucket | map lookups, ring-buffer reserve | |
| LSM enforcement hook | file_open adds path resolution | |
| Score update | six dimensions | |
| L1 upsert / L2 write | / | L2 is off the hot path |
| Audit append / verify | / | one SHA-256 per row |
| Beacon observe | ≤100 intervals per flow | |
| CWP HASH tier | only on a containment decision | |
| Jury round | parallel; latency of slowest juror |
Table 25.
Memory footprint of the main data structures (P processes, A audit rows, F active flows, intervals)
Table 25.
Memory footprint of the main data structures (P processes, A audit rows, F active flows, intervals)
| Structure | Size | Growth |
|---|---|---|
| kb_events ring buffer | 1 MB | fixed; shared by all hooks |
| rate-limiter LRU map | 10,240 entries | fixed; LRU eviction |
| per-process system-call counts | 655,360 entries | fixed (10,240 processes × 64) |
| contained_pids_map | 1,024 entries | fixed |
| protected_pids_map | 2,048 entries | fixed |
| protected_workloads_map | 8,192 entries | fixed |
| protected executable and workload paths | 64 entries each | fixed; 64-byte keys |
| user-space scoring table | 10,240 slots | fixed |
| L1 sync.Map and L2 process rows | bounded by live processes; flushed on exit | |
| L2 audit log | append-only by design | |
| beacon-detector windows | at most 100 intervals per flow; no idle eviction yet |
Table 26.
Defects found by live and real-host testing.
| Defect | Fix |
|---|---|
| Go and C disagreed on the containment message type (3 vs. 5); every command was dropped while the audit log recorded success | Constants aligned; wire contract locked by tests |
| Telemetry burst on a shared socket killed the control path | Separate kbd.sock and kbct.sock; SIGPIPE ignored |
| Unconditional sensitive-path block broke sudo, su and login host-wide | Block only for contained processes |
| bpf_d_path() left the key tail unzeroed, so exact matches never hit | Explicit zero padding of the 64-byte key |
| kbd never sent sd_notify; the unit chain stayed “activating” | Poll sockets, then notify READY |
| Watchdog locked the host down on every cold boot | 15 s start-up grace period |
| Unscored PID’s zero-value uid=0 read as root | Empty process name means not found |
| Packaged operator sshd lacked the capabilities for privilege separation, and the operator account was locked | Minimal capability set; account unlocked without a usable password |
Disclaimer/Publisher’s Note: The statements, opinions and data contained in all publications are solely those of the individual author(s) and contributor(s) and not of MDPI and/or the editor(s). MDPI and/or the editor(s) disclaim responsibility for any injury to people or property resulting from any ideas, methods, instructions or products referred to in the content. |
© 2026 by the authors. Licensee MDPI, Basel, Switzerland. This article is an open access article distributed under the terms and conditions of the Creative Commons Attribution (CC BY) license (http://creativecommons.org/licenses/by/4.0/).
Copyright: This open access article is published under a Creative Commons CC BY 4.0 license, which permit the free download, distribution, and reuse, provided that the author and preprint are cited in any reuse.