The Kernel Contract
A cognet is versioned against the kernel exactly like a binary is versioned against syscalls. The kernel evolves freely underneath; the contract is one shape, and it is the only thing a cognet ever holds.
Read this page before the others. Everything that follows is a consequence of it.
The declarations live in the Cognet API — this page is the argument for why they are shaped that way.
The privilege inversion
Axon already treats model-authored code as untrusted. The capsule can request; only the kernel can take.
Cognets extend the same inversion one ring further in. The cognet is the cognition, and the kernel protects the user's resources from it with equal severity. Not because a cognet is malicious — you wrote it — but because a boundary that only holds for well-behaved code isn't a boundary.
| Ring | Component | Holds |
|---|---|---|
| 0 | kernel | engine, scheduler, the capsule constructor, the session log |
| 3 | cognet | the ABI, and nothing else |
| 3 | capsule | model-authored code, policy-gated |
The cognet and the capsule are both unprivileged, for different reasons and through different doors.
The whole surface
type KernelAbi = {
output(type, data): Promise<void> // emission — unmediated
engine: KernelEngines // inference, by role
run(code): Promise<AxonRunResult> // execution — mediated, never rejects
scope(): AxonScope // what the capsule can execute
base(): Promise<string> // the user's rendered identity
emit(type, data): void // cognet:* telemetry only
fault(input): Promise<void> // report a grammar violation
store: KernelStore // private state + read-only history
knowledge: KernelKnowledge // long-term material
wake(): Promise<number> // continuous cognets only
clock(): KernelClock // a snapshot of the rhythm
}
That is it. There is no escape hatch. Full declarations:
kernel, kernel.engine,
kernel.store.
Two verbs touch the world
output() and run() are the cognet's entire vocabulary for acting, and the distinction
between them is the one that matters here.
output() is a self-contained emission. The cognet already holds the full content and
nothing external can fail it, so it is unmediated at the call site — writing to your own
stdout cannot itself harm anything. The kernel commits it and forwards it, but never
refuses it.
run() is a mediated request. The cognet does not know the outcome until the capsule
responds, so it is policy-gated. It never rejects either: success and failure are both
ordinary values on a stable result the cognet reads to decide what to do next.
Both are committed by the kernel on the cognet's behalf. It never reads or writes the log directly.
What a cognet cannot do
The absences are the design. None of these are enforced by convention — there is no API to call.
No filesystem, no network, no shell. Not restricted: absent. The only path to any of
them is asking the kernel to run code in a capsule the kernel alone constructs, under the
agent's policy. store and knowledge are mediated doors, not a filesystem — a name that
resolves outside the store throws.
No writing to the log. There is no commit verb. The cognet emits and requests; the
kernel writes. A cognet cannot forge history, backdate a decision, or record something it
did not do. fault() is the shape this takes when a cognet needs a system fact recorded:
it describes the fault and the kernel writes it.
No reading kernel telemetry. store.session.get() returns entries only — the same
envelopes stimuli arrive as. Kernel spans and error machinery aren't filtered out, they're
unreachable: the store reads a projection already classified by type namespace before the
cognet asks.
No configuration. A cognet cannot read the agent's config, paths, deploy settings, engine choice, or module list. It does not know what kind of world it is in — which is precisely what makes it portable to a different one.
No model identity. A cognet names a role and receives a handle. It never learns which provider or which model filled it, in either direction.
No clock of its own. Wakes are scheduled by the kernel. A continuous cognet may ask for
one with wake(); an invocation cognet cannot decide to run at all.
No thread or session concept. There is no id to address anything by. One cognet instance is exactly one continuous stream. Multiple conversations are multiple Axon instances — a host concern this ABI has no opinion on.
No identity. A cognet has no name it can act on, no owner it can query, no credentials. Identity belongs to the perimeter, not the mind inside it.
No cancellation to thread. No mediated verb takes a signal. The kernel cancels its
own work unconditionally, because threading a leash by hand meant one missing argument made
an operation unkillable.
What the kernel will not do
The boundary runs both ways. The kernel has no opinion on cognition.
No grammar. No context assembly. No prompt format. No loop strategy, no stop condition, no compression, no memory policy, no retrieval. It guards the user's base identity and reports the capsule's executable scope; the cognet alone decides how either enters model context.
This is why there is no semantic search on knowledge and never will be — choosing how a
mind recalls is a memory policy. It is why engine:done carries spoke / acted /
yielded as signals rather than a verdict: whether a turn is over is the one thing a
loop is for.
The AIR format that zero uses is zero's choice, not the platform's. Another cognet can
use a different grammar, or no grammar at all. Nothing above this line has an opinion.
Wake scope
Process-lifetime things are ambient. Wake-scoped things arrive as arguments:
loop(async ({ stimuli, signal, stop }) => { /* ... */ })
The ABI itself carries nothing per-wake. It is one process-lifetime object, and it is the
only thing a cognet ever holds. See loop.
There is deliberately no push or delta channel. A temporally-extended emission — streaming
speech, a progressive result — is expressed through output() itself using the chunking
standard: correlated entries, closed by a final marker. Chunks are ordinary committed
entries, so they reach every observer through the same commit pipeline as any other fact.
Versioning
Every compiled cognet carries the ABI it targets. Normally it declares nothing: a cognet publishes as source and is compiled by the consumer against the kernel it will actually run on, so the compile step stamps in the ABI it built against.
A cognet that must refuse a kernel it hasn't been validated against pins it:
export default defineCognet({
abi: "11",
mode: { kind: "invocation" },
})
axon prepare verifies a pin against the kernel this Axon provides and fails loudly on a
mismatch, naming both versions. A cognet pinned to an older ABI never half-loads.
The versioned surface is more than the function shapes. A cognet is a prebuilt bundle: it compiled against a snapshot of every payload type the contract references — the output events, the engine call and event types, the cognet event map, the entry wire. A breaking change to any of those is a breaking change to the ABI and bumps the number, even though no signature moved. Otherwise a stale bundle loads cleanly under a matching version and misreads events at runtime.
Additive changes — new event types, new optional fields — are compatible, like new syscalls.
The kernel is free to change anything beneath this contract. The contract itself changes only with the ABI number.