Skip to main content
The Cosmos SDK is built around the object-capability model (ocap) — a security model designed for systems that compose untrusted components. The threat model is explicit: a thriving ecosystem of Cosmos SDK modules will eventually include faulty or malicious ones. Ocap limits the damage any single module can do.

How it works

The model has two rules:
  1. An object can send a message to another object only if it holds a reference to it.
  2. An object can obtain a reference to another object only by receiving it through a message.
In practice: a module can only affect the state it has been explicitly handed access to. If the bank keeper was not passed to your module, your module cannot touch balances — full stop. There is no global registry to reach into. This makes security analysis local. You can audit what a module can do by looking at what references it was given at wiring time, without reading its implementation.

Pointer vs. value

Only pass what a module needs. If you pass a pointer, you grant write access. If you pass a value, you grant read access. This code violates the principle — passing a pointer to an external module grants it the ability to mutate the account:
Pass a copy instead:

Keeper interfaces

The most common place to apply ocap in SDK modules is at keeper boundaries. Instead of accepting a concrete keeper type from another module, define a narrow interface containing only the methods your module actually calls. For example, x/distribution needs to query balances and send coins, but it does not need the full bank keeper. It defines its own interface:
By convention these live in types/expected_keepers.go. The benefit is twofold: the interface documents exactly what cross-module access your module requires, and it makes the dependency easy to mock in tests.

Store isolation

Modules do not receive direct access to the global multistore. Instead, each module gets a store.KVStoreService scoped to its own prefix — it can only read and write within that namespace.
This means a bug or malicious call in one module’s keeper cannot read or corrupt another module’s state. The scoping is enforced at the store layer, not by convention.

Authority

Some operations — updating parameters, pausing a module, triggering emergency actions — should only be callable by governance or another trusted account. The SDK handles this with an explicit authority string stored in the keeper.
Message handlers check the caller against this address before proceeding:
The authority address is set at wiring time in app.go and cannot be changed at runtime. This is ocap applied to governance: privileged capability is a reference, and only the holder of that reference can exercise it. See simapp/app.go for how keeper dependencies and authorities are wired in a complete application. For background, see the Wikipedia article on object-capability model.