This is the Cosmos SDK example application — a reference implementation showing developers how to build a Cosmos SDK module from scratch and wire it into a chain. It lives at github.com/cosmos/example.
The primary audience is Cosmos SDK developers learning module development. The docs are tutorial-style and are published to the Cosmos SDK documentation site.
| Branch | Purpose |
|---|---|
main |
Complete, fully-featured counter module with all bells and whistles |
tutorial/start |
Blank template — users follow tutorial docs to build the module from scratch |
Rule: main always contains the finished module. tutorial/start contains the starting scaffolding users begin from. The tutorials in docs/ walk users from tutorial/start → main.
Critical: All docs live in /docs/ and are published to the Cosmos SDK docs site. Any change to a doc file here must also be reflected on the Cosmos docs site. Always flag this when making doc changes.
| File | Content |
|---|---|
tutorial-00-prerequisites.md |
Prerequisites: Go, Make, Docker, Git; repo layout overview |
tutorial-01-quickstart.md |
Fast path: build, install, run chain, send a tx |
tutorial-02-build-a-module.md |
Step-by-step: build a minimal counter module from scratch |
tutorial-03-counter-walkthrough.md |
Walk through full module (main branch) — params, fees, auth, errors, telemetry, sim |
tutorial-04-run-and-test.md |
Running a local chain, localnet, CLI reference, all test layers |
The custom module is x/counter. The full build pattern is:
proto files → make proto-gen → generated types → keeper → msg/query servers → module.go → app.go wiring
proto/example/counter/v1/— Protobuf definitions (tx, query, state, genesis)x/counter/types/— Generated + hand-written types (keys, codec, expected keepers)x/counter/keeper/— Keeper (state), MsgServer (txs), QueryServer (queries), telemetry, errorsx/counter/module.go— AppModule implementation (genesis, services, simulation, block hooks)x/counter/autocli.go— Auto-generates CLI from protoapp.go— Wires counter module into the chain
- State:
count(uint64) +params(MaxAddValue, AddCost) - Messages:
MsgAdd,MsgUpdateParams(governance-gated) - Queries:
Count,Params - Validation: MaxAddValue limit, overflow protection
- Fees: AddCost charged via bank keeper
- Authority: governance module controls param updates
- Errors: named sentinel errors with codes
- Telemetry: OpenTelemetry Int64Counter metric
- Simulation: simsx weighted operations + randomized genesis
- Tests: unit (keeper, msg_server, query_server), E2E, simulation
Docs in docs/ are kept in sync with the Cosmos docs site repo (cosmos/docs) via GitHub Actions + a transform script.
example repo |
Docs site |
|---|---|
docs/prerequisites.md |
sdk/next/tutorials/example/prerequisites.mdx |
docs/quickstart.md |
sdk/next/tutorials/example/quickstart.mdx |
docs/build-a-module.md |
sdk/next/tutorials/example/build-a-module.mdx |
docs/counter-walkthrough.md |
sdk/next/tutorials/example/counter-walkthrough.mdx |
docs/run-and-test.md |
sdk/next/tutorials/example/run-and-test.mdx |
example repo |
Docs site (Mintlify) | |
|---|---|---|
| Title | # H1 heading |
YAML frontmatter title: + noindex: true |
| Docs links | https://docs.cosmos.network/sdk/next/... |
sdk/next/... (relative) |
| File extension | .md |
.mdx |
transform.py exists in two places and must be kept in sync:
cosmos/example:scripts/docs-sync/transform.py— used for manual local syncs (both directions)cosmos/docs:scripts/docs-sync/transform.py— used by thedocs-sync-to-example.ymlworkflow
If you change the transform logic, update both copies. The docs repo copy is intentional: the workflow must not copy and execute an unverified script from an external repo, since it has access to EXAMPLE_REPO_TOKEN.
The docs-sync.yml workflow in cosmos/example runs transform.py from the example repo itself — this is safe because it only has access to DOCS_REPO_TOKEN (write on cosmos/docs), not any token that could be used to write back to cosmos/example.
scripts/docs-sync/transform.py handles all conversion in both directions:
# example → docs site
python3 scripts/docs-sync/transform.py --direction to-mintlify --input docs/ --output-dir /path/to/docs-site/sdk/next/tutorials/example/
# docs site → example
python3 scripts/docs-sync/transform.py --direction to-example --input /path/to/docs-site/sdk/next/tutorials/example/ --output-dir docs/
# Run tests
python3 scripts/docs-sync/test_transform.pyexample→ docs site:.github/workflows/docs-sync.yml— triggers on push tomainwhendocs/**changes, opens a PR oncosmos/docs. Requires secretDOCS_REPO_TOKEN.- docs site →
example:.github/workflows/docs-sync-to-example.ymlin the docs repo — triggers on push tomainwhensdk/next/tutorials/example/**changes, opens a PR oncosmos/example. Requires secretEXAMPLE_REPO_TOKEN.
Both actions check !contains(github.event.head_commit.message, '[docs-sync]') and tag sync commits with [docs-sync] to prevent infinite loops.
- In
cosmos/example: secretDOCS_REPO_TOKEN— fine-grained PAT withcontents:write+pull-requests:writeoncosmos/docs - In
cosmos/docs: secretEXAMPLE_REPO_TOKEN— fine-grained PAT withcontents:write+pull-requests:writeoncosmos/example
The tutorial/start branch is not manually maintained — it is generated automatically from main via GitHub Actions, and can also be run manually.
.github/workflows/update-tutorial-branch.yml triggers on every push to main (except [tutorial-sync] and [docs-sync] commits). It runs scripts/create-tutorial-branch.sh and force-pushes the result to tutorial/start. No secrets required — uses the default GITHUB_TOKEN.
bash scripts/create-tutorial-branch.sh
git push -f origin tutorial/startscripts/create-tutorial-branch.sh lives on main and:
- Creates/resets the
tutorial/startbranch from current HEAD usinggit checkout -B - Removes all counter module source files (
x/counter/, proto files, generated.pb.go, tests) - Adds
.gitkeepplaceholders sox/counter/andproto/example/counter/v1/exist on clone - Strips counter wiring from
app.go(imports, keeper field, store key, module registration, etc.) using cross-platformperl -i - Includes
docs/so tutorial content is available on both branches - Verifies
go build ./...still passes - Commits with
[tutorial-sync]tag to prevent the workflow from re-triggering itself
Rule: Never hand-edit tutorial/start. All changes flow from main through this script.
Counter module integration points in app.go:
maccPerms— counter module account entry (for fee collection)ExampleApp.CounterKeeperfield- Counter store key in store keys
NewKeeper(...)with bank keeper + authorityModuleManagerregistration- Genesis, export, begin/end blocker order lists
go test ./x/counter/... # Unit tests (fast, no chain)
go test -v -run TestE2ETestSuite ./tests/... # E2E (starts in-process network)
make test-sim-full # Simulation (randomized txs)
make lint # Lintmake build # Compile binary
make install # Install exampled binary
make start # Build + start single-node chain (Chain ID: demo, accounts: alice/bob, denom: stake)
make proto-gen # Regenerate proto (requires Docker)
make localnet-init # Initialize multi-validator localnet
make localnet-start # Start localnetAfter every change to this repo, update CHANGELOG.md. Add an entry under ## [Unreleased] describing what changed and why. When changes are committed, move the unreleased items into a dated entry.
- No em dashes. Use a period, comma, or colon instead.
- Binary:
exampled - Chain ID (dev):
demo - Accounts:
alice,bob - Denom:
stake - Module name:
counter - Default params:
add_cost = 100stake,max_add_value = 100
github.com/cosmos/cosmos-sdk v0.54.0-rc.1cosmossdk.io/core v1.1.0cosmossdk.io/collections v1.4.0github.com/cometbft/cometbft v0.39.0-beta.2- Go 1.25.7