Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 mechanismProblem it solves thereOur analog todayGap
Loadable modules, module_get refcountsLoad/unload code at runtime; refuse unload while depended onplugins, kernel-derived undoNo refcount: unloading a plugin whose provided key others hold
EXPORT_SYMBOL_GPL, module signing, lockdownGate symbols on a property of the consumer; a global “only signed code” modeminConsumerTrust, judgeNo global lockdown bit
Taint flagsPermanent provenance on every bug report (“out-of-tree module was loaded”)planned §1
Livepatch transition modelReplace a function under load; abortable; never half-patchedobservePoint({added, removed}) + derived undoNo sequencing/abort state machine — planned §2
eBPF verifier + per-program-type helpers + mapsUntrusted programs inside the kernel; whitelisted calls per attach type; state only via kernel-owned mapsjudge (runtime), Trust rank, collectionsRank is too coarse; no static pre-load pass — planned §3
Capabilities (CAP_*)Root split into discrete privilegesTrust = 'system' | 'agent'same as above
SeccompA process can only shrink its privilegesMonotonic trust for agent-authored plugins — folded into §3
cgroupsPer-subtree resource budgetsper-call executor limits only (QuickJS sliceMs/memoryLimitBytes, Node resourceLimits)later §4
NamespacesPer-process view of the systemkernel-per-tenantdone
LSM stackingEvery policy gets a veto on a canonical list of sensitive opsjudgejudge is a single LSM; the hook list is not published — later
Static keys / alternativesAtomic feature flags; capability-driven implementation selectiondriver composition at bootlater
Kprobes / tracepointsHook any function without author cooperation / declared hooksextension points (declared)rejected §5
Kconfig depends on/selectConfiguration as a satisfiable objectmanifest inject (hard) / optional (soft), gate rule plugin-injectpartial — no solver yet
sysfs/procfsOne introspection tree for tools, humans and programsplugin-admin-api (/api/state, /api/catalog), plugin-metadone in spirit
kexec / initramfsReboot into a new kernel or a minimal known-good statelater (“safe mode”: human-authored plugins only)
Stable userspace ABI, unstable internalsDecide 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:

FlagSet when
AGENT_AUTHOREDa plugin whose author trust is agent was loaded
JUDGE_OVERRIDDENa human approved something the judge rejected
TRUST_ESCALATEDany path granted a consumer more trust than its author had
LOAD_FORCEDa plugin was loaded despite a failed check (contract, verifier)
UNVERIFIEDa 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.route contributor gets db.read, model.complete; an admin.hook contributor also gets plugin.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 assertTrustassertCapabilities. 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.
  • complete fires only when every pinned unit has reached a safe point; abort at any time returns to active(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/contracts never breaks; plugin-sdk internals 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