在上一节中,你已经看到区块和交易是如何被处理的。但区块链真正的应用逻辑究竟在哪里? 在 Cosmos SDK 中,模块定义了这部分逻辑。 模块是 Cosmos SDK 应用的基础构件。每个模块都封装了一块特定功能,例如账户、代币转账、验证者管理、治理,或你自定义的任意逻辑。 如果你想查看一个完整可运行的模块,请参考构建模块教程系列。

为什么需要模块

一个区块链应用需要管理许多彼此独立的关注点(账户、余额、验证者管理等)。Cosmos SDK 不会把所有逻辑都放进一个单体状态机中,而是将应用拆分为多个模块。SDK 提供了一个基础层,使这些模块能够协同运作,组成一条统一的区块链。 每个模块拥有自己的一部分状态,定义自己的消息和查询,实现自己的业务规则,并在需要时接入区块生命周期和创世流程。这样可以让应用结构更清晰、组合性更强,也更容易推理。同时,它还能将不同模块之间的安全关注点隔离开来,从而构建出更安全的系统。

模块定义了什么

模块是一个自包含的状态与逻辑单元。 从高层看,一个模块会定义:
  • 状态:包含模块数据的 KVStore 命名空间
  • 消息:模块允许执行的操作
  • 查询:对模块状态的只读访问
  • MsgServer:负责校验、应用业务逻辑,并委托给 keeper
  • Keeper:状态访问层,也是访问存储的唯一受认可路径
  • 参数:由治理控制并存储在链上的配置

状态

状态是持久化在链上的数据,也就是交易可以读取或写入的一切内容。当交易执行时,模块会对这些存储数据应用状态转换或确定性更新。 每个模块都拥有自己那一部分区块链状态。例如:
  • x/auth 模块存储账户元数据。
  • x/bank 模块存储账户余额。
模块之间不会直接共享存储。每个模块在 multistore 中都有自己独立的键值存储命名空间。例如,bank 模块的存储中可能包含如下条目:
// x/bank store (conceptual)
balances | cosmos1abc...xyz | uatom  →  1000000
balances | cosmos1def...uvw | uatom  →   500000
键中编码了命名空间、地址和币种。值则是编码后的金额。其他模块都不能直接读取或写入这些条目,只有模块的 keeper 可以。要进一步了解状态如何存储和访问,请参阅Store。

消息

正如你在交易、消息与查询一节中学到的,每个模块通过消息(sdk.Msg)来定义它允许执行的操作。 消息定义在模块的 tx.proto 文件中的 service Msg 代码块下,并由该模块的 MsgServer 实现。下面是一个来自 bank 模块的简化示例:
// Message type definition
message MsgSend {
  string from_address = 1;
  string to_address   = 2;
  repeated cosmos.base.v1beta1.Coin amount = 3;
}

// Msg service — groups all messages for this module
service Msg {
  rpc Send(MsgSend) returns (MsgSendResponse);
}
在 Cosmos SDK 中,service Msg 代码块被称为 Msg service。protobuf 编译器会基于它生成一个 MsgServer 接口,由模块使用 Go 实现。BaseApp 会根据每条传入消息的 type URL(例如 /cosmos.bank.v1beta1.MsgSend)将其路由到正确的实现。 上面的 MsgSend 就是一个消息类型定义示例。它表示一个将代币从一个账户转移到另一个账户的请求。当它被包含进一笔交易并执行时:
  1. 检查发送方余额。
  2. 从 from_address 中扣除金额。
  3. 将金额记入 to_address。

查询

模块通过定义在 query.proto 中的查询服务,向外暴露对其状态的只读访问。查询不会修改状态,也不会经过区块执行流程。例如:
// query.proto
rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse);
在这个例子中,调用方提供一个地址和币种;查询会从 x/bank keeper 读取余额并返回,而不会修改状态。

业务逻辑

消息定义的是意图,而 MsgServer 和 Keeper 会协同工作来执行该意图,并将状态转换应用到模块中。 从概念上看,业务逻辑分布在两个层次中:
  • MsgServer 处理面向交易的逻辑:它会校验输入、应用消息级别的业务规则,并在需要时检查授权。完成这些后,它会将状态转换委托给 keeper。
  • Keeper 拥有模块状态:它定义存储结构,并提供唯一被授权的读写路径。消息处理器、区块钩子和治理提案都会通过 keeper 方法来修改状态。所有状态变更都必须经过 keeper,不能直接访问底层存储。

消息执行(MsgServer)

每个模块都会实现一个 MsgServer,当消息被路由到该模块时,它会由 BaseApp 的消息路由器调用。 MsgServer 负责:
  • 在需要时检查授权
  • 委托给 keeper 方法,由 keeper 校验输入、执行业务规则并完成状态转换
  • 返回响应
下面的示例来自最小 counter 模块教程,它会带你构建一个允许用户递增共享计数器的模块。参见从零构建一个模块教程。 下面是 counter 模块中的 Add 处理器,也就是处理计数器递增请求的 MsgServer 方法:
func (m msgServer) Add(ctx context.Context, request *types.MsgAddRequest) (*types.MsgAddResponse, error) {
    newCount, err := m.AddCount(ctx, request.GetAdd())
    if err != nil {
        return nil, err
    }

    return &types.MsgAddResponse{UpdatedCount: newCount}, nil
}
在这个最小教程中,Add 不需要权限控制,并且会直接委托给 keeper。处理器本身几乎不包含业务逻辑。这样可以让 msg_server.go 专注于请求处理,而 keeper 则负责实际的状态转换。 完整的 counter 模块示例则在这个最小结构之上增加了更丰富的模式。对于像 MsgUpdateParams 这样的特权消息,MsgServer 会在继续之前,将调用者与已存储的 authority 进行比对。参见完整 Counter 模块详解。
func (m msgServer) UpdateParams(ctx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
    if m.authority != msg.Authority {
        return nil, sdkerrors.Wrapf(
            govtypes.ErrInvalidSigner,
            "invalid authority; expected %s, got %s",
            m.authority,
            msg.Authority,
        )
    }

    if err := m.SetParams(ctx, msg.Params); err != nil {
        return nil, err
    }

    return &types.MsgUpdateParamsResponse{}, nil
}

Keeper

模块的 Keeper 是它的状态访问层。它拥有模块的 KVStore,并提供带类型的方法来读取和写入状态。存储字段是不导出的,因此 keeper 包之外的任何代码都不能直接访问它们。 MsgServer 和 QueryServer 都会嵌入 keeper。最佳实践是将业务逻辑实现为 keeper 方法,而不是写在 MsgServer 中,这样无论调用方是消息处理器、区块钩子还是治理提案,都能应用同一套规则。 下面的 keeper 示例来自最小 counter 模块示例。它只持有一个状态项,展示了一个最小但有用的 keeper 结构。参见教程中的步骤 5:Keeper。
type Keeper struct {
    Schema  collections.Schema
    counter collections.Item[uint64]
}

func (k *Keeper) AddCount(ctx context.Context, amount uint64) (uint64, error) {
    count, err := k.GetCount(ctx)
    if err != nil {
        return 0, err
    }
    newCount := count + amount
    return newCount, k.counter.Set(ctx, newCount)
}
counter 使用的是 collections.Item[uint64],它是一个带类型的单值条目,底层由模块的 KV 存储通过 Collections API 支撑。AddCount 会读取当前值,将其递增后再写回。所有状态访问都必须经过 keeper:keeper 包之外的任何代码都不能直接触达 counter。关于 keeper 如何从收到的上下文中打开自己的存储,请参阅执行上下文。

跨模块访问

默认情况下,模块彼此隔离。每个模块都拥有自己的状态,而对这部分状态的直接访问会通过模块的 keeper 受到限制。其他模块不能任意修改另一个模块的存储。 相反,模块之间通过显式定义的 keeper 接口进行交互。 例如:
  • staking 模块调用 bank keeper 上的方法来转移代币。
  • governance 模块调用其他模块上的参数更新方法。
每个模块都会定义一个 expected_keepers.go 文件,用来声明它依赖其他模块提供的接口。这使跨模块依赖关系变得明确:一个模块只能调用另一个模块选择对外暴露的方法。 这种设计让依赖关系更易于审计,并能防止意外或不安全的跨模块状态修改。 如果你想查看 expected keepers 和跨模块手续费收集的实际用法,请参阅完整 Counter 模块详解中的Expected keepers and fee collection。

参数

大多数模块都会暴露一个 Params 结构体:它是一组存储在链上的配置值,用于控制模块行为。与普通状态不同,参数被刻意设计为稳定配置:它们只会通过治理发生变化,而不会被用户交易修改。示例包括最小治理押金、验证者最大数量,或 mint 模块的通胀边界。参数通常存储在一个单独的键下(一般是一个 collections.Item[Params]),并通过提交治理提案来更新。 要更新参数,治理提案会提交一条 MsgUpdateParams 消息。MsgServer 会先检查调用方是否为指定的 authority 地址(通常是治理模块账户),然后才会把新值写入存储。关于这种模式的代码示例,请参阅消息执行(MsgServer)。 authority 地址会在 app.go 中构造 keeper 时设置。 对于链级共识参数,由 x/consensus 模块统一管理;它还支持 AuthorityParams,使治理能够在链上更新 authority 地址,而无需软件升级。 如果你想查看参数在实际模块中的实现,请参阅完整 Counter 模块详解中的参数与 authority。

区块钩子

模块可以通过在 module.go 的 AppModule 结构体中实现可选的钩子接口,在区块生命周期的特定阶段执行逻辑:
  • HasBeginBlocker — 在每个区块开始时运行逻辑
  • HasEndBlocker — 在每个区块结束时运行逻辑
  • HasPreBlocker — 在 BeginBlock 之前运行逻辑,用于共识参数变更
钩子是可选的,模块只应实现自己需要的钩子。这些钩子会在区块执行期间由 BaseApp 中的 ModuleManager 按配置顺序调用各个已注册模块的钩子。关于应用接线侧的内容,参见 app.go 中的 Module Manager。 模块通过在 module.go 的 AppModule 结构体上定义对应方法来实现钩子:
func (a AppModule) PreBlock(ctx context.Context) (appmodule.ResponsePreBlock, error) {
    // runs before BeginBlock; used for logic that must take effect before block execution
    return &sdk.ResponsePreBlock{}, nil
}

func (a AppModule) BeginBlock(ctx context.Context) error {
    // runs at the start of each block
    return nil
}

func (a AppModule) EndBlock(ctx context.Context) error {
    // runs at the end of each block
    return nil
}
定义这些方法后,模块就会启用对应的区块钩子。模块还需要在 app.go 中注册到 ModuleManager,这样这些钩子才会被调用。

创世初始化

模块定义了链启动时其状态如何初始化。 每个模块都会实现:
  • DefaultGenesis:返回模块的默认创世状态
  • ValidateGenesis:在链启动前校验创世状态
  • InitGenesis:在链启动时将创世状态写入模块存储
  • ExportGenesis:读取模块当前状态并将其序列化为创世数据
在 InitChain 期间,BaseApp 会调用每个模块的 InitGenesis,使用 genesis.json 填充其状态。 关于这在区块生命周期中的位置,参见 InitChain(仅创世)。 关于创世实现的演练,参见“构建模块”教程中的 第 2 步:Proto 文件 和 第 8 步:module.go。

内置模块与自定义模块

Cosmos SDK 自带一组大多数链都会使用的核心模块。点击任意模块了解更多:
模块功能
x/auth账户、身份验证与交易签名
x/bank代币余额与转账
x/staking验证者集合与委托
x/gov链上治理与提案
x/distribution质押奖励分发
x/slashing验证者惩罚执行
x/mint代币发行
x/evidence提交与处理验证者不当行为
x/upgrade协调链升级
x/authz委托消息授权
x/feegrant账户间的手续费额度
x/consensus链上管理 CometBFT 共识参数
以上只是可用模块中的一部分。应用可以包含这些模块中的任意子集,也可以定义全新的自定义模块。完整列表参见 模块列表。 Cosmos Enterprise 为要求更高的生产网络提供了额外的强化模块:
模块功能
Permissioned Consensus由链上权威管理的许可型验证者集合,替代基于代币的质押
Multi-sig具备可配置投票策略的链上多签账户与集体决策
Cosmos SDK 区块链本质上是将一组模块组装成单一应用。正是这种能够将模块和应用逻辑自定义到链底层细节的能力,使 Cosmos SDK 既灵活又强大。

模块剖析(高层视角)

模块位于 SDK 应用的 x/ 目录下:
x/
├── auth/        # Accounts and authentication
├── bank/        # Token balances and transfers
├── poa/         # Proof-of-authority validator management
├── gov/         # Governance system
└── mymodule/    # Your custom module
x/ 下的每个子目录都是一个自包含模块。 应用将多个模块组合在一起,形成完整的区块链。 在一个模块内部,你通常会看到如下结构:
x/mymodule/
├── keeper/
│   ├── keeper.go        # Keeper struct and state access methods
│   ├── msg_server.go    # MsgServer implementation
│   └── query_server.go  # QueryServer implementation
├── types/
│   ├── expected_keepers.go  # Interfaces for other modules' keepers
│   ├── keys.go              # Store key definitions
│   └── *.pb.go              # Generated from proto definitions
└── module.go            # AppModule implementation and hook registration
Proto 文件单独放在仓库根目录,而不是 x/ 内部:
proto/myapp/mymodule/v1/
├── tx.proto       # Message definitions
├── query.proto    # Query service definitions
├── state.proto    # On-chain state types
└── genesis.proto  # Genesis state definition
  • keeper/:包含 Keeper 结构体(状态访问)以及 MsgServer 和 QueryServer 接口的实现。
  • types/:定义模块的公共类型,包括生成的 protobuf 结构体、存储键,以及 expected_keepers.go 中声明本模块对其他模块需求的接口。
  • module.go:将模块连接到应用,并注册创世处理器、区块钩子以及消息和查询服务。
  • .proto 文件:定义消息、查询、状态模式和创世状态。Go 代码由这些文件生成,并在整个模块中使用。
查看 模块教程,了解如何从零开始构建一个模块。

模块所处的上下文

把你目前学到的内容串起来:
  • 账户 授权交易。
  • 交易 携带消息和执行约束。
  • 区块 对交易排序并定义提交边界。
  • 模块 定义执行的业务规则。
  • MsgServer 校验消息并编排状态转换。
  • Keeper 对模块状态执行受控读写。
  • 状态 持久化执行的确定性结果。
Account → Transaction → Message → Module → MsgServer → Keeper → State
在下一节 状态、存储与创世 中,你将更深入地了解模块状态如何存储、创世如何初始化它,以及应用如何提交确定性的状态转换。 在编写模块之前,请先阅读 模块设计注意事项,了解关于状态结构、消息接口、模块间依赖和升级规划的指导。准备开始构建时,请按照 构建模块教程 进行逐步演练。
In the previous section, you saw how blocks and transactions are processed. But where does the actual application logic of a blockchain live? In the Cosmos SDK, modules define that logic. Modules are the fundamental building blocks of a Cosmos SDK application. Each module encapsulates a specific piece of functionality, such as accounts, token transfers, validator management, governance, or any custom logic you define. To see a complete working module, follow the Build a module tutorial series.

Why modules exist

A blockchain application needs to manage many independent concerns (accounts, balances, validator management, etc). Instead of placing all logic in a single monolithic state machine, the Cosmos SDK divides the application into modules. The SDK provides a base layer that allows these modules to operate together as a cohesive blockchain. Each module owns a slice of state, defines its messages and queries, implements its business rules, and hooks into the block lifecycle and genesis as needed. This keeps the application organized, composable, and easier to reason about. It also separates safety concerns between modules, creating a more secure system.

What a module defines

A module is a self-contained unit of state and logic. At a high level, a module defines:
  • State: a KVStore namespace that contains the module’s data
  • Messages: the actions the module allows
  • Queries: read-only access to the module’s state
  • MsgServer: validates, applies business logic, and delegates to the keeper
  • Keeper: the state access layer: the only sanctioned path to the store
  • Params: governance-controlled configuration stored on-chain

State

State is the data persisted on the chain: everything that transactions can read from or write to. When a transaction is executed, modules apply state transitions or deterministic updates to this stored data. Each module owns its own part of the blockchain state. For example:
  • The x/auth module stores account metadata.
  • The x/bank module stores account balances.
Modules do not share storage directly. Each module has its own key-value store namespace located in a multistore. For example, the bank module’s store might contain entries like:
// x/bank store (conceptual)
balances | cosmos1abc...xyz | uatom  →  1000000
balances | cosmos1def...uvw | uatom  →   500000
The key encodes the namespace, address, and denomination. The value is the encoded amount. No other module can read or write these entries directly, only the module’s keeper can. To learn more about how state is stored and accessed, see Store.

Messages

As you learned in the Transactions, Messages, and Queries section, each module defines the actions it allows via messages (sdk.Msg). Messages are defined in the module’s tx.proto file under a service Msg block, and implemented by that module’s MsgServer. Here is a simplified example from the bank module:
// Message type definition
message MsgSend {
  string from_address = 1;
  string to_address   = 2;
  repeated cosmos.base.v1beta1.Coin amount = 3;
}

// Msg service — groups all messages for this module
service Msg {
  rpc Send(MsgSend) returns (MsgSendResponse);
}
The service Msg block is referred to as the Msg service in the Cosmos SDK. The protobuf compiler generates a MsgServer interface from it, which the module implements in Go. BaseApp routes each incoming message by its type URL (for example, /cosmos.bank.v1beta1.MsgSend) to the correct implementation. MsgSend above is an example of a message type definition. It represents a request to transfer tokens from one account to another. When included in a transaction and executed:
  1. The sender’s balance is checked.
  2. The amount is deducted from from_address.
  3. The amount is credited to to_address.

Queries

Modules expose read-only access to their state through query services, defined in query.proto. Queries do not modify state and do not go through block execution. For example:
// query.proto
rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse);
In this case, the caller provides an address and denomination; the query reads the balance from the x/bank keeper and returns it without modifying state.

Business logic

While messages define intent, the MsgServer and Keeper work together to execute that intent and apply state transitions to the module. Business logic is conceptually split across two layers:
  • The MsgServer handles the transaction-facing logic: it validates inputs, applies message-level business rules, and where required checks authorization. Once satisfied, it delegates state transitions to the keeper.
  • The Keeper owns the module’s state: it defines the storage schema and provides the only authorized path for reading and writing it. Message handlers, block hooks, and governance proposals all go through keeper methods to make state changes. All state changes must go through the keeper, nothing accesses the store directly.

Message execution (MsgServer)

Each module implements a MsgServer, which is invoked by BaseApp’s message router when a message is routed to that module. The MsgServer is responsible for:
  • Checking authorization when required
  • Delegating to keeper methods that validate inputs, enforce business rules, and perform state transitions
  • Returning a response
The following example is from the minimal counter module tutorial example, which walks you through building a module that lets users increment a shared counter. See the Build a Module from Scratch tutorial. The following is the counter module’s Add handler which is the MsgServer method that processes a request to increment the counter:
func (m msgServer) Add(ctx context.Context, request *types.MsgAddRequest) (*types.MsgAddResponse, error) {
    newCount, err := m.AddCount(ctx, request.GetAdd())
    if err != nil {
        return nil, err
    }

    return &types.MsgAddResponse{UpdatedCount: newCount}, nil
}
In the minimal tutorial, Add is permissionless and delegates directly to the keeper. The handler itself contains almost no business logic. That keeps msg_server.go focused on request handling, while the keeper owns the actual state transition. The full counter module example adds richer patterns on top of that minimal shape. For privileged messages like MsgUpdateParams, the MsgServer checks the caller against the stored authority before proceeding. See the Full Counter Module Walkthrough.
func (m msgServer) UpdateParams(ctx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
    if m.authority != msg.Authority {
        return nil, sdkerrors.Wrapf(
            govtypes.ErrInvalidSigner,
            "invalid authority; expected %s, got %s",
            m.authority,
            msg.Authority,
        )
    }

    if err := m.SetParams(ctx, msg.Params); err != nil {
        return nil, err
    }

    return &types.MsgUpdateParamsResponse{}, nil
}

Keeper

A module’s Keeper is its state access layer. It owns the module’s KVStore and provides typed methods for reading and writing state. The store fields are unexported, so nothing outside the keeper package can access them directly. The MsgServer and QueryServer both embed the keeper. It is best practice for business logic to be implemented in keeper methods rather than the MsgServer, so the same rules apply whether the caller is a message handler, a block hook, or a governance proposal. The following keeper example is from the minimal counter module example. It holds a single state item and shows the smallest useful keeper shape. See Step 5: Keeper in the tutorial.
type Keeper struct {
    Schema  collections.Schema
    counter collections.Item[uint64]
}

func (k *Keeper) AddCount(ctx context.Context, amount uint64) (uint64, error) {
    count, err := k.GetCount(ctx)
    if err != nil {
        return 0, err
    }
    newCount := count + amount
    return newCount, k.counter.Set(ctx, newCount)
}
counter uses collections.Item[uint64], a typed single-value entry backed by the module’s KV store using the Collections API. AddCount reads the current value, increments it, and writes it back. All state access goes through the keeper: nothing outside the keeper package can reach counter directly. For details on how the keeper opens its store from the context it receives, see Execution Context.

Inter-module access

Modules are isolated by default. Each module owns its state, and direct access to that state is restricted through the module’s keeper. Other modules cannot arbitrarily mutate another module’s storage. Instead, modules interact through explicitly defined keeper interfaces. For example:
  • The staking module calls methods on the bank keeper to transfer tokens.
  • The governance module calls parameter update methods on other modules.
Each module defines an expected_keepers.go file that declares the interfaces it requires from other modules. This makes cross-module dependencies explicit: a module can only call methods the other module has chosen to expose. This design keeps dependencies auditable and prevents accidental or unsafe cross-module state mutation. To see expected keepers and cross-module fee collection in practice, see Expected keepers and fee collection in the Full Counter Module Walkthrough.

Params

Most modules expose a Params struct: a set of configuration values stored on-chain that control the module’s behavior. Unlike regular state, params are intentionally stable: they only change through governance, not through user transactions. Examples include the minimum governance deposit, the maximum number of validators, or mint module inflation bounds. Params are stored under a single key (typically a collections.Item[Params]) and updated by submitting a governance proposal. To update params, a governance proposal submits a MsgUpdateParams message. The MsgServer checks that the caller is the designated authority address (usually the governance module account) before writing the new values to the store. See Message execution (MsgServer) for a code example of this pattern. The authority address is set at keeper construction time in app.go. For chain-level consensus parameters, the x/consensus module manages them centrally; it also supports AuthorityParams, which lets governance update the authority address on-chain without a software upgrade. To see params implemented in a working module, see Params and authority in the Full Counter Module Walkthrough.

Block hooks

Modules may execute logic at specific points in the block lifecycle by implementing optional hook interfaces in the AppModule struct in module.go:
  • HasBeginBlocker — runs logic at the start of each block
  • HasEndBlocker — runs logic at the end of each block
  • HasPreBlocker — runs logic before BeginBlock, used for consensus parameter changes
Hooks are optional, and modules should only implement the hooks they need. These hooks are invoked during block execution by the ModuleManager in BaseApp, which calls each registered module’s hooks in a configured order. For the application wiring side, see Module Manager in app.go. A module implements a hook by defining the corresponding method on its AppModule struct in module.go:
func (a AppModule) PreBlock(ctx context.Context) (appmodule.ResponsePreBlock, error) {
    // runs before BeginBlock; used for logic that must take effect before block execution
    return &sdk.ResponsePreBlock{}, nil
}

func (a AppModule) BeginBlock(ctx context.Context) error {
    // runs at the start of each block
    return nil
}

func (a AppModule) EndBlock(ctx context.Context) error {
    // runs at the end of each block
    return nil
}
Defining these methods opts the module into the corresponding block hooks. The module also needs to be registered with the ModuleManager in app.go for the hooks to be called.

Genesis initialization

Modules define how their state is initialized when the chain starts. Each module implements:
  • DefaultGenesis: returns the module’s default genesis state
  • ValidateGenesis: validates the genesis state before the chain starts
  • InitGenesis: writes the genesis state into the module’s store at chain start
  • ExportGenesis: reads the module’s current state and serializes it as genesis data
During InitChain, BaseApp calls each module’s InitGenesis to populate its state from genesis.json. For where this happens in the block lifecycle, see InitChain (genesis only). For a walkthrough of genesis implementation, see Step 2: Proto files and Step 8: module.go in the Build a Module tutorial.

Built-in and custom modules

The Cosmos SDK ships with a set of core modules that most chains use. Click any module to learn more:
ModuleWhat it does
x/authAccounts, authentication, and transaction signing
x/bankToken balances and transfers
x/stakingValidator set and delegations
x/govOn-chain governance and proposals
x/distributionStaking reward distribution
x/slashingValidator penalty enforcement
x/mintToken issuance
x/evidenceSubmission and handling of validator misbehavior
x/upgradeCoordinated chain upgrades
x/authzDelegated message authorization
x/feegrantFee allowances between accounts
x/consensusOn-chain management of CometBFT consensus params
The above are just a few of the modules available. Applications can include any subset of these modules and can define entirely new custom modules. For the full list, see List of Modules. Cosmos Enterprise provides additional hardened modules for production networks with more demanding requirements:
ModuleWhat it does
Permissioned ConsensusPermissioned validator set managed by an on-chain authority, replacing token-based staking
Multi-sigOn-chain multisig accounts and collective decision-making with configurable voting policies
A Cosmos SDK blockchain is ultimately a collection of modules assembled into a single application. This customization of modules and application logic down to the lowest levels of a chain is what makes the Cosmos SDK so flexible and powerful.

Anatomy of a module (high-level)

Modules live under the x/ directory of an SDK application:
x/
├── auth/        # Accounts and authentication
├── bank/        # Token balances and transfers
├── poa/         # Proof-of-authority validator management
├── gov/         # Governance system
└── mymodule/    # Your custom module
Each subdirectory under x/ is a self-contained module. An application composes multiple modules together to form a complete blockchain. Inside a module, you will typically see a structure like this:
x/mymodule/
├── keeper/
│   ├── keeper.go        # Keeper struct and state access methods
│   ├── msg_server.go    # MsgServer implementation
│   └── query_server.go  # QueryServer implementation
├── types/
│   ├── expected_keepers.go  # Interfaces for other modules' keepers
│   ├── keys.go              # Store key definitions
│   └── *.pb.go              # Generated from proto definitions
└── module.go            # AppModule implementation and hook registration
Proto files live separately at the repository root, not inside x/:
proto/myapp/mymodule/v1/
├── tx.proto       # Message definitions
├── query.proto    # Query service definitions
├── state.proto    # On-chain state types
└── genesis.proto  # Genesis state definition
  • keeper/: Contains the Keeper struct (state access) and implementations of the MsgServer and QueryServer interfaces.
  • types/: Defines the module’s public types: generated protobuf structs, store keys, and the expected_keepers.go interfaces that declare what this module needs from other modules.
  • module.go: Connects the module to the application and registers genesis handlers, block hooks, and message and query services.
  • .proto files: Define messages, queries, state schemas, and genesis state. Go code is generated from these files and used throughout the module.
Check out the Module Tutorial to learn how to build a module from scratch.

Modules in context

Putting everything together you’ve learned so far:
  • Accounts authorize transactions.
  • Transactions carry messages and execution constraints.
  • Blocks order transactions and define commit boundaries.
  • Modules define the business rules of execution.
  • MsgServer validates messages and orchestrates state transitions.
  • Keeper performs controlled reads and writes to module state.
  • State persists the deterministic result of execution.
Account → Transaction → Message → Module → MsgServer → Keeper → State
In the next section, State, Storage, and Genesis, you will look more closely at how module state is stored, how genesis initializes it, and how the application commits deterministic state transitions. Before writing a module, review Module Design Considerations for guidance on state structure, message surface, inter-module dependencies, and upgrade planning. When you are ready to build, follow the Build a Module tutorial for a step-by-step walkthrough.