Roadmap: what the Linux kernel still has to teach us
The baseline already borrows the Linux module model (drivers, plugins, EXPORT_SYMBOL-style trust on keys).
Linux has several other mechanisms for changing a running system safely that map directly onto the needs of
self-modifying software. This doc records the mapping and the features we intend to build from it, so future
work has a reference and a rationale.
Status legend: planned (agreed, not started) · later (worth doing, not scheduled) · rejected (with reason).
The mapping
| Linux mechanism | Problem it solves there | Our analog today | Gap |
|---|---|---|---|
Loadable modules, module_get refcounts | Load/unload code at runtime; refuse unload while depended on | plugins, kernel-derived undo | No refcount: unloading a plugin whose provided key others hold |
EXPORT_SYMBOL_GPL, module signing, lockdown | Gate symbols on a property of the consumer; a global “only signed code” mode | minConsumerTrust, judge | No global lockdown bit |
| Taint flags | Permanent provenance on every bug report (“out-of-tree module was loaded”) | — | planned §1 |
| Livepatch transition model | Replace a function under load; abortable; never half-patched | observePoint({added, removed}) + derived undo | No sequencing/abort state machine — planned §2 |
| eBPF verifier + per-program-type helpers + maps | Untrusted programs inside the kernel; whitelisted calls per attach type; state only via kernel-owned maps | judge (runtime), Trust rank, collections | Rank is too coarse; no static pre-load pass — planned §3 |
Capabilities (CAP_*) | Root split into discrete privileges | Trust = 'system' | 'agent' | same as above |
| Seccomp | A process can only shrink its privileges | — | Monotonic trust for agent-authored plugins — folded into §3 |
| cgroups | Per-subtree resource budgets | per-call executor limits only (QuickJS sliceMs/memoryLimitBytes, Node resourceLimits) | later §4 |
| Namespaces | Per-process view of the system | kernel-per-tenant | done |
| LSM stacking | Every policy gets a veto on a canonical list of sensitive ops | judge | judge is a single LSM; the hook list is not published — later |
| Static keys / alternatives | Atomic feature flags; capability-driven implementation selection | driver composition at boot | later |
| Kprobes / tracepoints | Hook any function without author cooperation / declared hooks | extension points (declared) | rejected §5 |
Kconfig depends on/select | Configuration as a satisfiable object | manifest inject (hard) / optional (soft), gate rule plugin-inject | partial — no solver yet |
| sysfs/procfs | One introspection tree for tools, humans and programs | plugin-admin-api (/api/state, /api/catalog), plugin-meta | done in spirit |
| kexec / initramfs | Reboot into a new kernel or a minimal known-good state | — | later (“safe mode”: human-authored plugins only) |
| Stable userspace ABI, unstable internals | Decide which surface never breaks | @sms/contracts, plugin-sdk, and every @sms/plugin-x/contract (definitions) vs. plugin roots (providers) | contract subpaths done (2026-08-28, gate rule contract-surface); versioning policy still to write in releasing.md |
Planned features (the 80% cut)
Three features give most of the value; each rides on state the kernel already tracks. Order: §1 → §3 → §2.
§1 Taint flags — planned, ~1 day
What. A sticky set of bits on the kernel, set for the kernel’s lifetime the first time something unusual happens:
| Flag | Set when |
|---|---|
AGENT_AUTHORED | a plugin whose author trust is agent was loaded |
JUDGE_OVERRIDDEN | a human approved something the judge rejected |
TRUST_ESCALATED | any path granted a consumer more trust than its author had |
LOAD_FORCED | a plugin was loaded despite a failed check (contract, verifier) |
UNVERIFIED | a plugin loaded without a judge decision at all |
The set is attached to every kernel error, every log line (packages/core/kernel/src/log.ts), and the admin-api
status response.
Why. The first question in every incident with a self-modifying system is “did the agent do this, or did I?”
Taint answers it on every failure report without anyone remembering to look. It also strengthens the judge
tests: assert that no test path sets TRUST_ESCALATED.
Where. packages/core/kernel — a taint: Set<TaintFlag> on the kernel, set from the provide/contribute
resolution path and from assertTrust; exported type in types.ts; surfaced by plugin-admin-api. No new package.
§3 Capability sets per extension point — planned, ~2 days
What. Augment the Trust rank with a set of named capabilities (db.read, db.write, model.complete,
plugin.install, trust.grant, …) declared in @sms/contracts.
- A
provided key declares the capabilities a consumer needs (requires: ['db.write']). - A plugin’s capability set is derived from the points it contributes to (an
http.routecontributor getsdb.read,model.complete; anadmin.hookcontributor also getsplugin.install) — the eBPF “helpers per program type” rule. - Monotonic: a plugin’s set is always a subset of its author’s set. An agent can write a plugin more restricted than itself, never less (seccomp rule).
minConsumerTrust: 'system'remains valid as shorthand for “the full set” — backward compatible.
Why. A rank forces “system vs not”. The interesting agent-authored plugins sit in between: may write rows,
may not install plugins. Sets give that grain, and the judge’s unreachability tests become mechanical:
no capability path from agent to plugin.install.
Where. @sms/contracts (capability names, requires on key metadata), kernel assertTrust → assertCapabilities.
Optional follow-up: a static pre-load pass (the eBPF verifier analog) that rejects a plugin source importing keys
outside its set before the judge ever runs.
§2 Livepatch-style plugin transition — planned, ~2–3 days
What. Replacing plugin v1 with v2 becomes an explicit, abortable state machine:
active(v1) ──begin──▶ transitioning(v1→v2) ──complete──▶ active(v2)
│
abort
▼
active(v1)
- In-flight work pinned to v1 finishes on v1; new work takes v2.
- “Safe point” = request boundary, signalled by the executor driver.
completefires only when every pinned unit has reached a safe point;abortat any time returns toactive(v1)using the kernel’s derived undo, so the system can never be half-patched.
Why. “The agent updated itself” is the central story, and it needs a swap that cannot strand the system
between versions. We already own both halves (effect undo, observePoint added/removed); this only sequences
them and adds a version pin per unit of work.
Where. Kernel (state + pinning); drivers/executor/* supply the safe-point signal; admin-api exposes the
transition state and an abort. Builds on §1 (transitions taint on force) and §3 (v2’s capability set must
be ⊆ v1’s author’s set).
Later
§4 cgroup-style budgets
Per-plugin / per-agent budgets (model tokens, wall time, rows written) enforced by the host, accounted in a hierarchy the judge can read. Real value, but needs metering in each driver — do after §1–§3.
Others
- Publish the judge’s hook list (the LSM lesson: the canonical list of sensitive operations is the security model).
- Module refcounts: refuse to unload a plugin while another holds its
provided key. - Global lockdown bit: “agent-authored plugins must pass judge; human-authored may skip”.
- Safe-mode boot (human-authored plugins only) and tenant-level kernel hot-swap (kexec).
- Kconfig-style satisfiability for the plugin set.
- Write the stable/unstable ABI policy in
releasing.md(@sms/contractsnever breaks;plugin-sdkinternals may).
Rejected (for now)
§5 Kprobe-style undeclared hooks
Attaching to any plugin function without the plugin declaring an extension point. Useful for observability, but it undercuts the “declared points only” safety story that the judge’s unreachability tests rely on. Revisit only as an observation-only input to the judge, never as a way to alter behaviour.
Reading
- eBPF verifier and helpers:
Documentation/bpf/verifier.rst,include/uapi/linux/bpf.h - Livepatch consistency model:
Documentation/livepatch/livepatch.rst - Taint flags:
Documentation/admin-guide/tainted-kernels.rst - Capabilities:
capabilities(7); seccomp:seccomp(2) - Positioning against the research thesis: research-positioning.md