The requirements
The threat model names three assets, and each one is a requirement on the kernel:
Everything below argues these three. What CTRLRun does not defend against is listed in the
threat model under Out of scope and is not argued here.
The trust boundary
Untrusted: agent reasoning, model outputs, tool outputs, retry logic, other agents, and every token or header a caller presents. Trusted: the CTRLRun process, the policy file, the state store, and the approver’s shell. Data crosses the boundary in one place: the proposed action with its arguments, and whatever identity the caller presents. Nothing the agent asserts is trusted. Everything it proposes is normalized, checked against the policy, and checked against authority before anything is reserved. Inside the boundary the kernel trusts its own store, and the receipt chain (R3) is what lets an operator check that trust afterwards. The boundary has an edge the model states plainly: a compromised host, process or Python environment is inside it, and so is an administrator with write access to the policy or the store. The kernel does not defend against itself.Design principles, applied
Saltzer and Schroeder’s principles, each with where it shows in the design and where a regression would be caught.Implementation weaknesses, countered
The common ways a sound design goes wrong in code, what the kernel does about each, and where a regression would be caught.The evidence
- The suite. Every specification’s acceptance section has tests; the README’s tests badge carries the live count. CI runs it on four Python versions with a Postgres service, and a separate step proves the Postgres tests will actually run rather than skip.
- Mutation tables. For every MUST a change touches, the guard is removed, its test is confirmed red, and the guard is restored, with the table in the pull request. CONTRIBUTING.md, Tests first, lists the four shapes of a false green the tables are checked against.
- Fuzzing. The canonicalizer and the policy parser run under Atheris on every pull request
and weekly, asserting the invariants in
fuzz/properties.py. - Static analysis. CodeQL,
ruffwith the bugbear rules, andmypy --strict. - Coverage. Measured in CI and held at the floors CONTRIBUTING.md states.
ctrlrun verify. The eleven guarantees, replayed against the operator’s own policy and grants in a scratch instance of the configured store backend, with fake executors and no network.- Review. Every change is reviewed by a person who did not write it. A change to authorization, identity, delegation, the gateway, an adapter or the store gets the deeper review CONTRIBUTING.md describes before its pull request opens.
- Provenance. Releases carry signed SLSA provenance and PyPI attestations, and CONTRIBUTING.md says how to rebuild a release and compare it.
What this argument does not cover
- Everything under Out of scope in the threat model: a compromised host, process or
environment; an administrator with write access; a compromised remote or identity provider;
a wrong approver; an executor that raises
NotExecutedwhen it did execute. - No external review yet. Every review so far ran inside the project, by sessions that did not write the code under review. How this is built says so, and will carry the report if an external review happens.
- The argument is for the kernel. An adapter or a gateway deployment adds a boundary of its
own. The adapter conformance kit checks an adapter’s behaviour;
ctrlrun verifychecks only the kernel guarantees the configured policy, grants and store backend can exercise.
Next
- Threat model: what is defended and what is not, in full.
- What verify guarantees: the eleven guarantees, replayed against your own configuration.
- Get started · Why.