How it works
The model has two rules:- An object can send a message to another object only if it holds a reference to it.
- An object can obtain a reference to another object only by receiving it through a message.
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: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:
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 astore.KVStoreService scoped to its own prefix — it can only read and write within that namespace.
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 explicitauthority string stored in the keeper.
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.