cosi-project/runtime  /  overview go 1.26 MPL-2.0
The engine under Talos & Omni — studied from the source

COSI Runtime

A small, opinionated Go framework for building operating systems and control planes as a graph of controllers reconciling typed resources against a central state broker. This walkthrough takes you from the mental model down to the engine internals — then out to real-world applications — culminating in the Talos source code, where COSI was born.

3primitives: resource · state · controller
~19klines of Go, zero magic
2controller flavours: reconcile & queue
1interface from local to gRPC
What "COSI" means.

COSI = Common Operating System Interface. It originated at Sidero Labs as the resource/controller engine inside Talos Linux and Omni. Think of it as "Kubernetes' resource-and-controller idea, extracted into a reusable library" — but lighter, in-process by default, strongly typed, and free of etcd, CRDs, or a scheduler.

#The one-diagram mental model

Everything in COSI is one of three things. Resources are the data. The State is the single broker that stores them and streams every change. Controllers are single-threaded loops that read some resources (inputs) and write others (outputs). The Runtime wires the graph together and decides who wakes up when.

State CoreState broker Get·List·Create·Update Destroy·Watch·WatchKind in-mem · bolt · gRPC Resources Metadata + typed Spec ns / type / id @ version stored in Controller A inputs → reconcile → outputs single goroutine QController B queue · per-item · concurrent finalizers + backoff watch events Runtime Engine dependency graph watch → dedup → route read-through cache owned-write enforcement restart / backoff metrics (expvar) orchestrates all controllers + state — engine observes & drives —
Hover any block. Controllers never call each other — they communicate only through resources in the State.

#The three design principles

🧩

Simple & structured

Impose just enough structure to make systems simple. Metadata is strict; the spec is opaque. One reconcile loop, one direction of data flow.

🛡️

No conflicts by design

Each resource has exactly one writer. Exclusive outputs are owned by a single controller; the runtime refuses to start if two controllers claim the same exclusive type.

🗺️

Graph as documentation

The inputs/outputs you declare are the architecture. The dependency graph is exportable and tells you exactly how your system is wired.

#Why it's interesting for your projects

If you are building anything that looks like "desired state in, real-world side-effects out, keep them reconciled" — a VM control plane, a device manager, a GitOps agent, a fleet reconciler — COSI gives you the reconciliation substrate for free, without adopting all of Kubernetes. The same state.State interface runs in-process for a unit test and over gRPC for a distributed deployment, with no change to your controllers. The closing chapters test that claim against two real codebases and reach an honest answer for each.

How to read this walkthrough — ten pages, three movements.

Pages 01–03 are the contract you program against (resources, state, controllers). Pages 04–05 are the machinery that makes it work (the engine and the gRPC transport). Pages 06–07 are the application: page 06 is greenfield recipes and a decision framework; page 07 is the source-grounded Talos case study — the canonical full application, where COSI was born. Read top-to-bottom for the full tour, or jump to 06 for the "should I use this?" answer, or to 07 for Talos as the ground truth.

#A 30-second taste

Here is a complete, runnable COSI system: an in-memory state, a runtime, one controller, and a write that triggers it. Everything else in this walkthrough is an elaboration of these few lines (this is essentially cmd/runtime/main.go).

main.gogo
// 1. a state broker: namespaced, in-memory, wrapped with convenience methods
st := state.WrapCore(namespaced.NewState(inmem.Build))

// 2. a controller runtime over that state
rt, err := runtime.NewRuntime(st, logging.DefaultLogger(), options.WithMetrics(true))

// 3. register a controller (declares its inputs & outputs)
err = rt.RegisterController(&conformance.IntToStrController{
    SourceNamespace: "default",
    TargetNamespace: "default",
})

// 4. run the engine; it watches state and wakes controllers on change
go rt.Run(ctx)

// 5. write a resource — the controller reconciles it into an output
st.Create(ctx, conformance.NewIntResource("default", "int-1", 42))
// → IntToStrController wakes, reads IntResource(42), writes StrResource("42")

Ready? Start with the data model — what exactly a resource is.