eunomia-bpf

Activity

Stars and mentions · Last 30 days, Oct 8 – Nov 6

GitHub stars37
Mentions3
02468Oct 8Oct 22Nov 6
UTC days · repositories eunomia-bpf owns3 repositories

Top repositories

eunomia-bpf’s repositories ranked by pull request authors, as on trending.

Loading

Contributes to

Repositories outside eunomia-bpf’s account where eunomia-bpf opened or reviewed pull requests in the past 365 days.

Who noticed eunomia-bpf

Everyone who starred or forked eunomia-bpf’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

4posts
Nov 71 post
  1. ActPlane: Pushing Agent Harness Enforcement Down to Kernel eBPF You wrote a rule in CLAUDE.md: "do not run git push." The agent complied: it never called the git tool. But it wrote a Python script containing subprocess.run(["git", "push"]), executed the script, and pushed your code to the remote. The prompt constraint was never violated. This scenario exposes a structural problem: agent constraints and agent side effects live on different planes. Prompt constraints operate at the reasoning layer, tool guards at the API layer, sandboxes at the container layer, yet every side effect an agent produces ultimately passes through the OS kernel. Every exec, every file open, every network connect: no matter what path the agent takes, the kernel sees it. If constraints aren't enforced at this layer, the agent can route around them without being malicious; it's simply trying to finish the task you gave it. ActPlane is built on this insight. It installs a policy engine in the kernel via eBPF, observing and enforcing agent constraints at the syscall layer. When a rule matches, it fires deterministically, with no dependency on the agent "remembering" anything. But ActPlane does more than block: when a constraint triggers, the agent receives human-readable feedback explaining why it was stopped and what to do instead. The agent reads the reason, takes a different path, and continues the task. This is the core difference between a harness and a sandbox: a sandbox gives you a wall and Permission denied; a harness gives you a rule and a recovery path. <!-- more --> What We Actually Want Before dissecting the problem, consider a few constraints that agents genuinely need in day-to-day work. None of them can be stated clearly enough in a prompt, enforced by an MCP gateway, or expressed by a container sandbox. Preventing an agent from running git push sounds like an ordinary sandbox rule, until you realize that the coverage of "prevent" depends on the layer doing the checking. The agent might call git directly, write a shell script that calls it, or generate a Python program with nested subprocess calls. The tool API layer can only answer "did the agent call the git tool," but the real question is "did any process in this agent's process tree execute the git binary." Those two questions have very different coverage. Now consider a temporal constraint: after modifying specs/*, the agent must run protoc before committing, or receive a reminder. Editing spec files is normal work; we don't want to block that. But if the agent edits a spec and goes straight to commit, it skipped a step. The goal is not to prevent the commit outright, but to remind the agent so it can decide whether to run code generation first. "Tests must pass before committing" follows similar logic but adds dynamic invalidation: every time the agent modifies a file under src/, previous test results expire. The check isn't "have tests ever passed," but "have tests passed since the last source modification." Run tests, then change one line? The status resets. Finally, a mandatory mediation constraint: the production database prod.db may only be accessed through the migration tool; the agent cannot open it directly. No matter how the agent reaches the file-open call, if its process ancestor chain hasn't executed migrate, the operation is blocked. What matters isn't whether the agent holds some abstract permission, but which path it took: pass through the designated gate program and you're allowed in; bypass it and you're stopped. Together, these four constraints involve process lineage tracking, temporal ordering, dynamic invalidation, and mandatory mediation, all beyond the scope of static allow/deny rules. To see why meeting them requires a kernel-level approach, consider where the existing three constraint layers each fall short. Three Layers, Three Blind Spots | Approach | What it does | What it misses | |----------|-------------|----------------| | Prompt constraints (CLAUDE.md, AGENTS.md) | Tell the agent what to do and not do | Probabilistic: agents forget or route around them in long contexts | | Tool-layer guards (MCP gateway, AgentSpec) | Intercept and authorize at the tool API | Bypassed when agents shell out, link SDKs, or spawn subprocesses | | Sandboxes (containers, VMs, E2B, Daytona) | Isolate the execution environment | All-or-nothing: can't express "file A only via script A" or "run tests before commit" | CLAUDE.md, AGENTS.md, and system prompts are the most common constraint mechanism today: natural-language instructions that depend on the model "remembering and obeying" during inference. The trouble is with "remembering." When conversations exceed tens of thousands of tokens, early constraints get diluted in attention by subsequent user instructions and tool outputs. The model hasn't technically forgotten the rule, but the rule's weight in decision-making drops as context expands. In an empirical study of 64 repositories, agents averaged dozens of tool calls per task and conversation contexts grew very large. The earlier a constraint is set and the longer the conversation, the lower the probability of effective compliance. Even harder to defend against is indirect violation: the agent is told "don't delete files," so it writes a Makefile target containing rm -rf and calls make clean. From the agent's perspective it just ran a build command; each decision point is locally reasonable, yet the behavior chain violates the original policy. Prompt constraints govern what the agent intends; actual behavior is outside their jurisdiction. What if we push down one layer and intercept at the tool call API? MCP gateways, AgentSpec, and Progent are far more reliable than prompts: if the agent calls file_write("/etc/passwd", ...), the tool layer rejects it outright, checking the operation itself rather than intent. But nearly every agent framework provides a shell tool, because agents need to run builds, execute tests, and call third-party CLIs. Once the agent has bash, it can write a Python script whose internals call subprocess.run(["curl", "-X", "POST", ...]) to send data to an external server. The tool layer sees python script.py and knows nothing about the subprocesses spawned inside; the call chain can be three or four layers deep, but the tool layer sees only the first. Less obviously, agent-generated code can directly link database drivers or HTTP client libraries, producing side effects at runtime that never pass through any tool call path. The tool layer's vision stops at the registered entry point; side effects from code execution are outside its view. Pushing down one more layer, we reach full environment isolation. Containers, VMs, E2B, and Daytona are the most reliable security boundary available today. For preventing agent escape to the host, they are the right answer. But the constraints agents actually need are richer than "can or can't access a resource." "After modifying proto files, the agent must run protoc before committing" is a temporal constraint; sandboxes have no concept of time and know only which resources are accessible at the current instant. "Sensitive data read from the database must not be written to logs" requires tracking data flow; sandboxes operate at process granularity and don't know what a process read or wrote internally. And the same git commit should have different policies depending on whether tests just ran, a distinction sandboxes can't make because they lack historical context. A frequently underestimated problem compounds all of this: feedback quality. When a sandbox rejects an operation, the agent receives only Permission denied or EPERM, with no indication of why it was rejected or how to satisfy the constraint. The typical result is the agent retrying three to five times and then abandoning the task, or worse, trying another path to bypass the rejection and introducing new problems. Sandboxes answer "can this process access this resource," but agents need richer questions answered: under what conditions, in what order, and based on what data-flow history is this operation permitted? The Path All Side Effects Must Travel All three layers' blind spots point in the same direction: the constraint mechanism must sit on the path every operation travels through. AgentSight (2025) used eBPF to capture both intent-layer and action-layer agent data, identifying the "semantic gap" between the two. ActPlane builds enforcement on that observability foundation. No matter what tool the agent uses, what script it writes, or how many layers of subprocesses it spawns, all side effects ultimately pass through OS kernel syscalls. ActPlane installs lightweight eBPF programs in the kernel, hooking the process lifecycle (sched_process_fork, sched_process_exec, sched_process_exit), file operations (sys_enter_openat, sys_enter_unlinkat, sys_enter_renameat2), and network connections (sys_enter_connect), then makes decisions based on labeled information-flow policies. Regardless of what path an agent takes, these syscalls are unavoidable. Return to the opening scenario: the agent wrote a Python script that spawned a subprocess calling git push. The tool layer saw only python script.py, but ActPlane sees all syscalls across the entire process tree, including that git call three layers deep. Because constraints attach directly to the process tree, "the agent's entire subprocess tree cannot touch git" is a single rule; there is no need to guard every possible tool entry point. ActPlane goes beyond pushing tool-layer guards down to the kernel. It introduces two capabilities that neither tool layers nor sandboxes have: data-flow tracking and temporal reasoning. Labels propagate across fork/exec and file read/write boundaries, making "data read from A must not flow to B" an expressible policy. The since clause lets rules update dynamically on the event timeline, turning "have tests passed since the last source modification" into a predicate that invalidates and rebuilds as new events occur. The next two sections unpack these mechanisms, but first: ActPlane is not just a deeper sandbox. Harness, Not Just a Sandbox A sandbox draws an isolation boundary: everything inside is allowed, everything outside is forbidden. For untrusted code this is the right model: you don't trust it, so you lock it in a cage. But an agent isn't untrusted code; it's your collaborator, and you want it to complete the task while following certain constraints along the way. Many of these constraints have nothing to do with security permissions, yet they're exactly the kind of rules agents need when operating autonomously in real codebases. "Run tests before committing" is an engineering workflow. "Access prod.db through the migration tool" is an operational standard. "Don't mix independent tasks in one commit" is a work habit. Sandboxes can't express them because the semantics go beyond resource access. A harness, however, also subsumes sandbox capabilities: when the agent runs an untrusted command, you can write a rule confining the entire subtree to read-only, no-network, or a specific directory. In ActPlane this is just a subset of the rules, coexisting with workflow rules in the same policy file. The feedback loop is the most critical part of harness design. When a rule fires, ActPlane delivers the reason to the agent through the framework's hook system: 🚫 KILLED: process 'git' (pid 4213, ppid 4210): /usr/bin/git effect: kill reason: no git under the agent; use the review workflow The agent reads the reason, understands the constraint, and takes a different path to complete the task. It doesn't need to "remember" that it can't use git beforehand; it can try, get told why not and what to do instead, and self-correct. This produces an interesting architectural pattern: the agent's reasoning remains probabilistic (that's what makes LLMs useful), while critical constraints are enforced deterministically by the kernel. Deterministic constraints and probabilistic decisions, joined by a feedback loop, yield an architecture that is both flexible and controllable. Core Mechanism: Label Propagation ActPlane's policies are not static allow/deny lists. They are labeled information-flow policies: processes and files get labels, labels propagate automatically along fork/exec edges and file read/write edges, and rules make decisions based on those labels. The academic roots trace to CamQuery (CCS 2018) and CamFlow (SoCC 2017), which implemented cross-channel taint propagation and enforcement on an in-kernel provenance graph. ActPlane brings the same idea to the modern eBPF/BPF-LSM substrate, requiring no custom kernel module, and targeting cooperative-but-forgetful AI agents rather than remote adversaries. An example makes this concrete. Say we want to constrain Claude Code's entire process tree. First, declare a label source: source AGENT = exec "claude", meaning whenever a process executes a binary named claude, it gets the AGENT label. Labels aren't generated by default; only behaviors explicitly declared as sources produce them, so the system tracks only the data flows you care about. Similarly, source SCHEMA_CHANGED = file "src/protocol/**/*.proto" declares that when these files are written, the writer gets the SCHEMA_CHANGED label. Once a label is created, it propagates automatically along two dimensions. Along process lineage: a parent process with the AGENT label forks a child, and the child inherits it. The child execs a new program and inherits it too. The agent starts bash, bash starts Python, Python calls subprocess to start git; every layer carries the AGENT label. Labels don't get lost at process boundaries. Along file I/O: a process with the AGENT label writes to file f, and f gets the AGENT label too. Later, a process that didn't originally have that label reads f and also acquires AGENT. When data crosses process boundaries through files, labels follow the data. With labels in place, rules are straightforward. kill exec "git" "push" if AGENT means any process with the AGENT label that tries to execute git push gets terminated. Revisiting the opening scenario: Claude Code (AGENT) → bash (inherits AGENT) → python script.py (inherits AGENT) → subprocess: git push (inherits AGENT) ← rule fires here No matter how deep the nesting or how many process boundaries are crossed, labels propagate along lineage and the rule fires at the final exec call. The checkpoint has moved from the tool entry down to the kernel's exec call, covering every path the agent might take. File-direction label propagation solves a different class of problems. Suppose an agent exports sensitive data from a production database to data.csv: the process gets a DB_SENSITIVE label, and the file does too. A later step reads data.csv, acquires DB_SENSITIVE, then tries to send the content over the network. At that point block connect if DB_SENSITIVE blocks the connection. The data flow crossed both process and file boundaries, but labels followed it throughout. Kernel-level tracking operates at syscall granularity: ActPlane knows process P wrote to file F, but not what content was written. This means over-tainting can occur: a process reads one line from a sensitive file, then writes completely unrelated data to another file, and the second file still gets marked sensitive. In the academic literature this is called label creep. In practice three factors mitigate it. Different agent tasks typically run in different process trees, so labels are naturally isolated. Rule matching checks operation-type-plus-label combinations (block connect if DB_SENSITIVE fires only on network connections, not every file write). And only explicitly declared sources produce labels in the first place. This is a deliberate tradeoff: better to over-label than to miss real data flows. Temporal Constraints: The since Clause Label propagation answers "who did it" and "where did the data come from." But many constraints also involve temporal ordering, such as "run tests before committing" and "run protoc after changing specs"; these require knowing whether event X happened after event Y. ActPlane uses the since clause to reason over the event timeline: "after X happens and before Y, Z is not allowed." Combining labels with temporal logic yields expressive policy files. The following four rules demonstrate four distinct constraint patterns: # actplane.yaml version: 1 policy: | source AGENT = exec "claude" # Track when protocol schema files are modified source SCHEMA_CHANGED = file "src/protocol/**/*.proto" rule no-git-branch: kill exec "git" "branch" if AGENT kill exec "git" "worktree" if AGENT because "This workspace forbids creating git branches or worktrees. Use other git commands, or ask the user to manage branches." rule regenerate-after-schema: notify exec "git" "commit" if SCHEMA_CHANGED unless after exec "protoc" since write "src/protocol/**" because "Protocol schema changed: generated code may be stale. Run `make proto` to regenerate, then commit." rule test-before-commit: block exec "git" "commit" if AGENT unless after exec "pnpm" "test" since write "src/**" because "Source files changed since last test run. Run `pnpm test:changed`, then commit." rule mediate-proddb: block open file "**/prod.db" unless lineage-includes exec "**/migrate" because "prod.db is reachable only through the migration tool. Run `./migrate` to access it." no-git-branch is the simplest: any process in the agent's tree that tries git branch or git worktree is terminated immediately, with no conditions and no temporal logic. The agent receives the because content and knows to use other git commands or ask the user to handle branches. regenerate-after-schema is a cross-event conditional rule using the notify effect. Its unless clause asks a specific temporal question: since the last write to the protocol directory, has protoc been executed? If yes, the commit proceeds; if no, the agent gets a reminder. The key is the since clause's dynamic nature: whenever the protocol directory is written again, the "already ran protoc" state resets and must be re-established, forming a predicate that updates dynamically on the event timeline rather than a one-time static check. test-before-commit has similar semantics but is stricter, using the block effect: have tests passed since the last write to src/**? If not, the commit is blocked before it executes. Every source modification resets the test status, so changing even one line after a passing test run means running them again. mediate-proddb takes a different approach: instead of relying on labels, it uses lineage-includes to check process ancestry. Any process wanting to open prod.db must have migrate somewhere in its ancestor chain. This rule expresses "the only legitimate access path": the agent directly calling open("prod.db") gets blocked, but calling ./migrate, which internally opens the file, succeeds. What matters isn't whether the agent holds some abstract permission, but whether its lineage passed through the designated gate. Traditional sandboxes have no concept of "time" or "path"; they know only the current instant's state. By maintaining both an event timeline for temporal reasoning and process lineage for path checking, ActPlane can express workflow constraints and mandatory mediation, not just access control. Agent Integration ActPlane delivers rule match reasons to agents through framework hook systems; the kernel remains the sole authority for observation and enforcement. Hooks relay match events into the agent's decision context. Claude Code integration is configured via .claude/settings.local.json with PostToolUse and PostToolUseFailure hooks: { "hooks": { "PostToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "actplane feedback-hook" }] }], "PostToolUseFailure": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "actplane feedback-hook" }] }] } } Codex integration via .codex/hooks.json: { "hooks": { "PostToolUse": [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "actplane feedback-hook" }] }] } } actplane feedback-hook checks whether any new rule match events have occurred since its last invocation and, if so, forwards the because content to the hook output. Because the agent runs this hook after every tool call, any triggered rule's reason enters the next decision's context. The integration requires no modifications to the agent framework itself. Runtime Architecture ActPlane's runtime splits into kernel space and user space: actplane.yaml ─▶ compiler (Rust) ─▶ .rodata config ─▶ eBPF kernel engine policy: | parse + lower DSL (set_global) propagate labels, match rules, matches ◀─────── ring buffer (in-process, via aya) ◀─── emit on match only The kernel part (bpf/ directory) maintains a per-node label set for processes, files, and network endpoints, executes propagation, evaluates compiled rules, and emits events to user space via ring buffer only when rules match. Unmatched operations produce zero user-space overhead, which matters because an active agent can trigger hundreds of file operations and process creations per second. If every operation notified user space for a decision, latency would be unacceptable. Label propagation and rule matching complete entirely in kernel space; user space participates only when a rule fires. The user-space part is the actplane Rust binary. eBPF programs are precompiled into CO-RE (Compile Once, Run Everywhere) format and embedded in the binary; installation needs no clang, llvm, libbpf, or compilation toolchain. The deployment path is cargo install actplane → actplane init to generate a starter config → actplane check to validate rules → sudo actplane run <command> to execute the agent under the harness. eBPF programs are checked by the kernel verifier, guaranteeing they won't crash the kernel or loop infinitely. At runtime the binary loads precompiled eBPF objects in-process via aya, parses actplane.yaml, compiles the DSL into kernel configuration (written into .rodata), seeds the target process's lineage, and listens on the ring buffer. Compared with Cilium Tetragon, whose matchBinaries + followChildren can propagate a lineage flag along fork/exec (the closest open-source feature to ActPlane's lineage tracking), ActPlane additionally propagates labels across file and network edges and provides semantic feedback to the agent. On permissions: actplane run and actplane watch require root or CAP_BPF + CAP_SYS_ADMIN to load the eBPF engine, but once loaded the target command drops back to the current user. actplane check requires no privileges; it only performs static rule validation. Use Cases and Limitations Kernel-level constraints prove most valuable when multiple agents from different vendors collaborate. When Claude Code calls Codex and Codex calls a custom tool chain, each vendor's framework-level guards know only their own registered tools: Claude Code's hooks don't know Codex's permission config, and vice versa. Framework-level guards assume "I know what paths the agent will use to operate the system," and cross-vendor calls break that assumption. OS-level rules, by contrast, propagate along process lineage regardless of which vendor's runtime is underneath, so a single rule governs the entire cross-vendor execution tree. CI/CD environments impose stricter requirements: agents in build pipelines can't push code, can't modify CI config, and must pass tests before producing build artifacts, all temporal constraints that since clauses are designed to express. In deployments involving sensitive data, agents also need data-flow policies like "data read from prod.db must not flow to the network." Traditional sandboxes can't track cross-process data flow at this granularity; label propagation can. ActPlane has clear boundaries. Because it is built on eBPF, it runs only on Linux 5.8+ with BTF support (/sys/kernel/btf/vmlinux), leaving macOS and Windows development scenarios uncovered, although most production deployments are on Linux. Loading eBPF programs requires root or CAP_BPF + CAP_SYS_ADMIN, which some shared servers and cloud containers won't grant. Kernel-level tracking reaches only syscall granularity, so in-process memory operations and encryption/decryption are out of scope. Block mode depends on BPF-LSM, which not all distributions enable by default. Can the Agent Recover and Keep Working? Blocking the nested git push from the opening example is only half the job. If the agent sees an unexplained EPERM, retries the same action, and abandons the task, the policy is safe but the harness is not useful. The paper evaluates policies collected from an empirical study across both coding tasks and safety benchmarks, including generated scripts and nested subprocesses that bypass tool-call interception. Across evaluated configurations, ActPlane improves policy compliance with 1.9% to 8.4% overhead. The experiments track what happens after a rule fires: actionable feedback gives the agent a reason and a recovery path, allowing it to revise its plan instead of treating enforcement as an opaque failure. With semantic feedback, recovery rate after a detected violation reaches 97.7%; without feedback, only 31.4%. A Kernel Checkpoint the Agent Can Understand Agent reasoning remains useful because it can find unexpected ways to complete a task. Production policy needs the opposite property at critical checkpoints: every path to a protected side effect must reach the same decision. ActPlane places those decisions at the kernel boundary, then returns enough context for the agent to revise its plan. The larger idea is a feedback loop in which flexible reasoning proposes actions, deterministic enforcement judges their effects, and the agent continues after learning why one path was rejected. --- > Paper: arXiv:2606.25189 > > GitHub: eunomia-bpf/ActPlane, MIT License > > ActPlane is an open-source project from the eunomia-bpf community. Built on AgentSight's eBPF observability foundation.

  2. Can an AI Agent Tune the Linux Scheduler? Inside SchedCP and sched-agent Linux makes scheduling decisions based on runnable tasks, wakeups, priorities, and accumulated CPU time. Applications evaluate those decisions in different terms: a kernel build cares about when the last compiler process finishes, an online service watches P99 latency and throughput, and a batch pipeline may be dominated by a single long job among dozens of short ones. The kernel supplies mechanisms and measurements; the workload defines what counts as a good result. This mismatch (the paper calls it a "semantic gap") means the default EEVDF scheduler applies one-size-fits-all policies to workloads that need different tradeoffs. The SchedCP paper asks whether an LLM agent can bridge that gap without unrestricted control of the machine. The core insight is architectural: decouple "what to optimize" (the AI's domain of semantic reasoning) from "how to observe and act" (the system's domain of safe execution). SchedCP implements this separation as an MCP server exposing workload analysis, a scheduler policy repository, and an execution verifier; sched-agent uses those operations to observe a workload, choose or generate a policy, test it, and learn from the result. The paper reports promising case studies (up to 1.79x kernel-build speedup and 13x reduction in generation time and cost) while presenting the evaluation as an initial study rather than a production-readiness claim. <!-- more --> Why a naive prompt fails: several problems in one request The paper begins with a deliberately simple request: Claude Code starts from an empty directory with full shell access and is asked to "write a FIFO scheduler in eBPF." Only one of three attempts produces a working scheduler. Another stops at pseudocode after six minutes; the third builds a scheduler tracer instead after eight minutes. The successful attempt takes 33 minutes, 221 LLM API calls, more than 15 iterations, and about $6. Even then, the resulting policy performs worse than EEVDF on some workloads; the agent required root access with no fallback mechanism when experiments crashed the system. That prompt quietly combines several jobs. The agent must learn the sched_ext interface, write eBPF code that passes the kernel verifier, arrange privileged loading, choose a benchmark, decide which metric matters, and interpret the measurement. A successful compilation settles only one part of the task. Useful tuning also depends on whether the policy improves the intended workload, keeps overhead and starvation under control, and can be reverted when results degrade. The paper identifies three critical challenges: Performance (the AI scheduler must outperform existing ones), Safety (no crashes, lockups, or starvation, with minimal privileges), and Efficiency (the 33-minute, $6 generation cost is impractical). SchedCP addresses these challenges by decomposing the problem into two stages: goal inference and policy synthesis. Goal inference analyzes commands, source structure, process behavior, and performance data to produce a workload description with an objective and constraints. Policy synthesis then searches for an existing scheduler in a repository, modifies or composes known components when appropriate, and generates a new policy only when reuse cannot express the required behavior. This ordering matters: selecting a measured policy from a repository is cheaper and easier to validate than writing new kernel code for every workload. The SchedCP control plane: a safe interface between Agent and kernel Rather than giving the agent a general root shell, SchedCP presents a stable systems interface governed by four design principles: (1) decoupling and role separation for future-proofing, (2) safety-first interface design that treats the AI as a potentially non-cautious actor and prevents catastrophic failures by default, (3) adaptive context provisioning that starts with minimal summaries and progressively reveals detail to manage token costs, and (4) composable tool architecture following Unix philosophy so agents can construct novel workflows. The paper groups the interface into three services. The Workload Analysis Engine provides tiered access: cost-effective API endpoints with pre-processed summaries (CPU load, memory usage), then secure sandbox access to perf, top, file reads, application builds, and dynamically attachable eBPF probes when deeper investigation is needed. The Scheduler Policy Repository stores executable eBPF programs with metadata (natural language descriptions, target workloads, historical performance metrics), enabling semantic search so the agent can find relevant schedulers or composable primitives before writing new code. The Execution Verifier validates all AI-generated code and configurations before deployment. !SchedCP control-plane design In the paper's design, verification progresses through multiple stages: (1) the kernel's eBPF verifier ensures memory safety and termination, (2) scheduler-specific static analysis detects logic flaws like starvation and unfairness that the standard verifier misses, and (3) dynamic validation in a secure micro-VM tests correctness and performance. Policies that pass receive signed deployment tokens for monitored canary runs with circuit breakers to revert if performance degrades, eliminating the agent's need for root access. The public SchedCP repository already exposes concrete MCP operations: list_schedulers, run_scheduler, stop_scheduler, get_execution_status, create_and_verify_scheduler, system_monitor, and workload management. The implementation includes custom scheduler compilation using clang with BPF targets and a 10-second kernel verification step. The full micro-VM, signed-token, and circuit-breaker path described in the paper is not yet visible as an end-to-end implementation, so that portion should be read as proposed architecture. This boundary redefines responsibilities. The agent reasons about workload intent and explores alternatives; deterministic software owns scheduler loading, measurement, and policy checks. The generated policy runs natively through Linux sched_ext, so there is no model inference latency in the scheduler hot path, unlike traditional ML approaches that would add unacceptable overhead. The paper notes the framework is implemented in approximately 4,000 lines of Rust and 6,000 lines of Python (including tests). sched-agent: in-context reinforcement learning for schedulers Building on SchedCP, the paper introduces sched-agent, a multi-agent system implementing in-context reinforcement learning (ICRL) for scheduler optimization. Using Claude Code's subagent architecture, it decomposes the task into four specialized roles with separate context windows and customized prompts. The framework integrates with container orchestrators (Kubernetes, Docker) to automatically trigger optimization when applications deploy. The Observation Agent builds workload profiles by querying the Workload Analysis Engine strategically, starting with high-level summaries from process names and commands, then requesting deeper profiling (perf stat, top) only when initial signals are ambiguous. It manages cost-precision tradeoffs explicitly. For a kernel compilation, it produces a profile like: "CPU-intensive parallel compilation with short-lived processes, inter-process dependencies, targeting makespan minimization." The Planning Agent transforms workload profiles into optimization strategies via the Scheduler Policy Repository, following a decision hierarchy: first configure existing schedulers, then generate patches, and finally compose new schedulers from primitives only when simpler options are insufficient. The Execution Agent synthesizes code artifacts, submits them to the Execution Verifier, and interprets results to refine code or fix logic issues. The Learning Agent completes the ICRL loop by analyzing deployment outcomes (e.g., "45% makespan reduction"), enabling in-session adaptation while updating the repository with refined metrics, deployment contexts, and documented antipatterns for future searches. The key insight is that this loop improves through session context and stored experience without retraining a scheduler model for each workload. A scheduler name alone says little: scx_rusty can be excellent for one workload and poor for another, and the same workload can change as its concurrency or data distribution shifts. By preserving the connection between workload description, policy, and measured result, the repository enables evidence-based policy reuse rather than static recommendations. Preliminary evaluation: four research questions The evaluation addresses four research questions: RQ1 (configuring existing schedulers), RQ2 (generating new schedulers for specific workloads), RQ3 (cost and efficiency of generation), and RQ4 (iterative refinement improvements). Two machines are used: an 86-core Intel Xeon 6787P with 758 GB RAM on Linux 6.14, and an 8-core Intel Core Ultra 7 258V with 30 GB on Linux 6.13. Claude Code with Opus 4 drives the agent; each case runs three times and results are averaged. The paper explicitly notes that a complete benchmark suite is future work, and all experiments successfully created working custom configurations or eBPF programs. Scheduler Configuration (RQ1): For kernel compilation (tinyconfig, make -j 172 on Linux 6.14 source), SchedCP initially selects scx_rusty, reducing average time from EEVDF's 13.57 seconds to 8.31 seconds (1.63x speedup). Iterative refinement then identifies scx_layered for an additional 16% gain, reaching 7.60 seconds; 1.79x total improvement over EEVDF. A pre-trained RL-based scheduler shows no improvement (13.79 seconds), likely because it requires costly hardware/workload-specific retraining. Learning from Feedback: The schbench experiment demonstrates how feedback corrects a poor initial choice. With EEVDF: 910 requests per second, 40.3 ms P99 latency. The agent's first selection (scx_bpfland) underperforms: 741 requests per second (0.81x), 46.1 ms P99 (0.87x). After three refinement iterations, it identifies scx_rusty as superior: 1,452 requests per second (1.60x) and 19.1 ms P99 latency (2.11x improvement over EEVDF). New Scheduler Synthesis (RQ2): Eight diverse batch workloads (file compression, video transcoding, software testing, data analytics) each launch 40 parallel tasks with a long-tail distribution: 39 short jobs, one long job. sched-agent correctly identifies the optimization goal and workload pattern, implementing Longest Job First (LJF) scheduling to achieve 20% average latency reduction. Claude Opus successfully classifies all 8 workloads at $0.15 per analysis; Claude Sonnet fails this classification step. Cost Reduction (RQ3): With SchedCP's reuse and tooling, generation efficiency improves 13x (from 33 minutes to 2.5 minutes), with $0.45 synthesis cost per workload versus $6 for naive generation. What these cases establish and what remains The experiments demonstrate a control loop completing three kinds of work under the reported conditions: selecting a scheduler that improves a known workload, recovering after measurement exposes a poor selection, and generating a simple policy for a recognizable workload pattern. They also illustrate why the system boundary matters: the agent can vary with the model and prompt, while validation, deployment, measurement, and stored policy history remain explicit parts of surrounding infrastructure that do not change with each model update. The current evidence covers two machines, a small set of workloads, three-run averages, and Claude Code with Opus 4. Broader claims about production reliability would require longer deployments, mixed and changing workloads, more hardware and kernel versions, failure injection, and measurements of the safety mechanisms the architecture describes. The public repository labels the project experimental and notes that its OS-optimization benchmark is still under construction. SchedCP's most useful contribution at this stage is a concrete division of labor. An AI agent translates workload evidence into a scheduling hypothesis; Linux sched_ext runs the resulting policy with kernel-level safety guarantees; a control plane decides which experiments are admissible and whether their measured results deserve another iteration. That is a more inspectable path to scheduler optimization than asking a general coding agent to produce privileged kernel code and treating successful compilation as success. The paper frames this as a step toward an "Agentic OS" (systems that can drive their own optimization) while being clear that the current results are preliminary. References - Yusheng Zheng, Yanpeng Hu, Wei Zhang, and Andi Quinn. "Towards Agentic OS: An LLM Agent Framework for Linux Schedulers." arXiv:2509.01245v4, September 2025. <arxiv.org> - SchedCP official repository: <eunomia-bpf/schedcp> - Linux sched_ext documentation: <docs.kernel.org> - Model Context Protocol: <modelcontextprotocol.io>

  3. Can a language model run in Linux eBPF? Linux eBPF programs usually observe or influence work done elsewhere: trace a syscall, filter a packet, or choose a scheduling policy. qwen3-ebpf asks a different question. Could a verified BPF program do the numerical work of a real language model, rather than merely watch a model server? The experiment runs the 28 decoder layers of Qwen3-0.6B in Linux eBPF, scores its 151,936-token vocabulary, and chooses the next token inside the kernel. It also keeps a KV cache for subsequent tokens. This is a working forward pass, not a useful replacement for a CPU or GPU inference engine: one measured token still takes around 1.2 seconds on the test host, and C remains responsible for loading the model, text handling, and dispatching BPF operators. <!-- more --> Start with one token A language model does not receive text directly. A tokenizer first turns text into integer IDs. The model looks up an embedding for each ID, transforms that vector through a stack of decoder layers, then assigns a score, or logit, to every possible next ID. Taking the highest score gives a next token; decoding its ID produces text. Here is the division of work in this prototype: flowchart TD A["C: tokenize text, load BF16 weights"] --> B["C: embed token and dispatch BPF programs"] B --> C subgraph L["One eBPF decoder layer, repeated 28 times"] C["RMSNorm · qwen3_norm"] --> D["Q/K/V matrices · qwen3_batch"] D --> E["Q/K norm and RoPE · qwen3_norm + qwen3_rope"] E --> F["Causal attention · qwen3_attention ↔ KV map"] F --> G["Output projection and residual · qwen3_batch + qwen3_vector"] G --> H["MLP · qwen3_norm + qwen3_batch + qwen3_silu + qwen3_vector"] end H -- "next layer" --> C H -- "after layer 28" --> I["Final norm, vocabulary projection and argmax · qwen3_norm + qwen3_batch"] I --> J["C: decode token ID to text"] Each box is a bounded operator, not a stage of one giant BPF program. C invokes them in sequence; the attention program writes and reads the BPF KV map. The matrix program processes up to 128 rows per invocation, so one box can still mean many kernel entries. The C driver runs BPF socket-filter programs through bpf_prog_test_run_opts. They are test-run entry points, not filters attached to an interface, and the project does not install a persistent model service. BPF performs matrix-vector products, RMSNorm, SiLU, vector operations, RoPE rotation, causal attention, and the final argmax. C supplies an embedding, the model weights and RoPE trigonometric values, then invokes the programs in the order required by the model. For a longer prompt, every input position traverses the 28 layers. When generating another token, BPF-written K/V vectors let attention reuse previous positions instead of recomputing them. That is what “in-kernel inference” means here: the forward arithmetic and token choice run in verified BPF programs. It does not mean that a text prompt enters the kernel and a finished sentence emerges from one call. Why the computation is split into pieces An eBPF program must pass the Linux verifier, which reasons about execution paths and memory accesses before the program can run. The implementation makes its work bounded and moves intermediate state through BPF maps. It uses fixed-point integers rather than the original BF16 floating-point arithmetic: Q16 activations, Q24 matrix weights, and Q20 normalization scales. SiLU and attention softmax use integer approximations; C computes the trigonometric inputs while BPF rotates the RoPE vectors. The important loop is bpf_loop. One matrix invocation computes up to 128 rows. Other invocations cover all 24 query/key normalization heads, all 16 query heads for a bounded attention-history chunk, or 256 past positions. New K/V pairs are written by the attention BPF program into its KV map. These changes cut repeated user/kernel transitions without pretending that all 28 layers have become one enormous verified program. A one-token run now makes about 4,169 bpf calls, versus tens of thousands in earlier, smaller-batch versions; that count is an implementation measurement, not a throughput claim. Two failed attempts explain the boundary. An early monolithic RMSNorm exceeded the verifier's instruction-processing budget on the test kernel; restructuring it around bounded callbacks made it load. Putting BF16-to-Q24 conversion directly in a 3,072-column BPF loop hit a verifier complexity error. Those are outcomes of particular programs on Linux 6.17 arm64, not proof that every larger BPF program is impossible. Where the weights live The official model is a roughly 1.5 GB BF16 Safetensors file mapped read-only by C. The default inference path copies the active matrix rows into a memory-mapped BPF work map after converting them to Q24. Preconverting every matrix to four-byte Q24 created a roughly 3.0 GB file and ran slower in exploratory trials on a host with about 4.6 GiB available memory. Doubling the weight footprint did not remove the bottleneck. We also built a full-model optional arena path. It copies only the current 128-row BF16 batch into a roughly 2 MiB BPF arena. A 65,536-entry lookup table in that arena maps each BF16 bit pattern to the same Q24 integer used by the default driver; BPF then reads those weights and computes the dot product. This moves weight conversion for the active matrix batch out of C without trying to keep the whole model in kernel-accessible memory. On token ID 0 and a two-token continuation of Hello, world!, it returned the same token IDs and byte-identical full-vocabulary logits as the default path. The arena smoke test also matched 128 real Q-projection rows exactly. The change did not establish a speedup. Six interleaved one-token measurements were 1.227/1.190/1.251 seconds for the default path and 1.220/1.228/1.234 seconds for the arena path. The reported timer starts after model and BPF setup, and this small same-host sample cannot establish throughput or tail latency. Arena addresses a data-access boundary; it neither compresses weights nor eliminates the remaining operator calls. Would four-bit weights solve it? INT4 is attractive because two weights fit in a byte rather than one BF16 weight taking two bytes. A tested group-scaled INT4 representation packed the matrices used by this model into 316,616,704 bytes, including scales, and its BPF operator passed verifier and row tests. In one single-token comparison its forward pass took 0.956 seconds versus 1.243 seconds for Q24, excluding 1.928 seconds spent prepacking. That is a useful performance signal, but accuracy failed the more important test: the default path's next token for input ID 0 was 9, while this INT4 version chose 284; the full-vocabulary logit mean absolute error was 1.837. Other tested prompts also changed their next IDs. Even smaller quantization groups or leaving the vocabulary projection at higher precision did not restore that example. So INT4 is a research path, not the default model. A calibrated or mixed-precision scheme might work, but it needs whole-model token and logit comparisons across varied prompts, memory and preprocessing measurements, and a fair latency distribution. A smaller weight file by itself does not show that the output is still the intended model. What is still outside the kernel? The exact tokenizer reads a large tokenizer.json, splits text with a regular expression, applies byte-level BPE, and reconstructs bytes for output. C handles these text and I/O tasks; moving them into BPF would require a separate bounded implementation and would leave the expensive matrix work unchanged. C also decides which operator runs next and supplies the next matrix batch. Because the entire model is not resident in BPF-accessible memory, a single long-running kernel invocation could not simply advance through all of its layers using the present design. The current KV map is another limit. Its layout reserves about 224 KiB per position; allocating all 40,960 configured positions would require roughly 8.75 GiB for KV alone. Attention can scan history in bounded chunks, but neither long-context memory use nor generation quality across arbitrary contexts has been established. The evidence supports a narrow but concrete conclusion. On the test kernel, verifier-accepted BPF programs execute all 28 layers and choose real-model tokens; for Hello, world!, the first generated ID is 1096 ( This), and the top five logits match the official BF16 reference. The reference comparison covers a small set of inputs, and the fixed-point full-vocabulary error is nonzero. The open engineering question is not whether a transformer can be expressed in eBPF. It is whether better weight formats, fewer boundaries, and bounded memory can make that expression useful for a workload where running in the kernel actually matters. The source, commands, and measurements are available for that next test.

  4. bpftime#407 bpftime现已支持Rocm!

    6 likes · 466 views

Achievements

Awards earned by the repositories eunomia-bpf owns. Select an award to embed its badge.

No awards yet

Repositories earn awards by reaching the top three in a completed trending day or week.

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.