Governance decides that a chain will change. The x/upgrade module is where that decision becomes a deterministic, chain-wide event with a name, a height, and a migration every node runs in lockstep.
The 2026-06-27 cert lesson on the x/gov module ended at a specific moment: a software-upgrade proposal passes the tally and the change is ratified by the validator set. That lesson stopped there, because what the passed proposal does is a different module's job. Today covers that module. The x/upgrade module is the on-chain machinery that turns a governance decision into a coordinated chain halt, a binary swap, and a schema migration that every node executes at the same block height.
For the Interchain Dev credential this is core territory: the difference between the governance of an upgrade and the execution of one, what an upgrade handler is and when it runs, and why store migrations must be deterministic. Today's Ops and Dev lessons covered the operational half; this covers the on-chain half that makes those operations meaningful.
tome_refs: []. The vault's 44-tome corpus has no Cosmos-SDK or Interchain reference literature, so there is no book-tier source to ground citations against. The material is grounded in the Cosmos SDK's own module documentation and the arc of prior Interchain cert lessons. The gap is logged as a tome-acquisition target.
The x/upgrade module has a small surface and one job: hold a plan, and act on it at a height. The plan carries a name, a target height, and optional info (a JSON string that by convention tells operators where to find the new binary). There is at most one active plan at a time; scheduling a new upgrade sets or replaces it.
The plan enters the store through governance. A MsgSoftwareUpgrade message, wrapped in a proposal, is what the validator set votes on. When the proposal passes, the module's handler writes the plan into the upgrade store. From that moment the chain is armed: it will halt at the plan height whether or not any operator has staged a binary. The upgrade is a consensus commitment, not an operational suggestion.
The module acts in the BeginBlocker, before any transactions. At block H it compares H against the plan height. If H is below the plan height, nothing happens. If H equals the plan height and a matching handler is registered, it runs the handler and clears the plan. If H equals the plan height and no matching handler is registered, it panics with UPGRADE REQUIRED.
The upgrade handler is a named function the application registers with the upgrade keeper. Registration ties a handler to a plan name: when the chain upgrades to v2.1.0, the module looks for a handler registered under the exact string v2.1.0.
First, the handler name and the plan name must match exactly. A binary registering a handler named v2.1.0 crosses cleanly if the plan is named v2.1.0, and halts with UPGRADE REQUIRED if the plan is named anything else. The handler name is the assertion "this binary knows how to become the chain after this named upgrade."
Second, the handler runs exactly once, in-consensus, at the upgrade height. It is not a startup script. It executes inside the BeginBlocker at block H, so its effects are part of the app-hash every validator must agree on. If the handler read the wall clock, hit the network, or iterated a Go map in unspecified order, validators would compute different post-upgrade states and the chain would fail to finalize H. Determinism is the difference between an upgrade and a chain split.
Store migrations change a chain's data schema without corrupting state. Each module carries a consensus version, a monotonic integer. When a module's stored layout changes between releases, the module bumps its consensus version and registers a migration function that transforms state from the old version to the new.
The upgrade handler drives this through the module version map: a record of which consensus version each module was at before the upgrade. The handler passes that map to the migration runner, which compares each module's stored version against the version the new binary declares and runs the registered migration for every gap. A module that went from consensus version 2 to 4 has its 2-to-3 and 3-to-4 migrations run in sequence.
A module with no schema change has no version gap, so the runner runs nothing for it. Most upgrades migrate only one or two modules; the rest pass through untouched. The version map is what lets the runner tell the difference cheaply.
Governance passed a MsgSoftwareUpgrade naming v2.1.0 at height 18,500,000. The store holds the plan; the chain is armed. Operators stage the binary via today's Bash pipeline. The binary registers a handler named v2.1.0 wiring two migrations: x/staking moved from consensus version 4 to 5 (a new validator-record field), and a new x/nftmarket module was added at version 1.
At block 18,500,000 the BeginBlocker fires. On a staged validator the v2.1.0 handler matches the plan and calls the migration runner with the version map. It sees x/staking at 4 in the map but 5 in the binary, so it runs the 4-to-5 migration and populates the new field with its default. It sees x/nftmarket absent from the map but present in the binary, so it stands up its store. Every other module shows no gap and is skipped. The handler returns, the plan clears, and the chain finalizes the block under v2.1.0.
On an unstaged validator the old binary has no v2.1.0 handler. The BeginBlocker hits the schema fence and panics with UPGRADE REQUIRED: v2.1.0. That node halts until Cosmovisor swaps a staged binary or an operator acts. The 2026-07-04 state-export cert lesson is the recovery path if the migration proves buggy on mainnet.
The three lessons are one upgrade from three angles. This cert lesson is the on-chain mechanism: x/upgrade stores the plan, panics at the schema fence, and runs the handler and migrations in-consensus at height H. The Ops lesson is the operational response: Cosmovisor reads the halt and swaps the binary. The Dev lesson is the preparation: a Bash pipeline stages and checksums the binary. Governance arms the upgrade, the schema fence enforces it, Cosmovisor executes it, and the staging pipeline makes execution safe.
v3 at height H has passed. No operator has staged the v3 binary. What happens at height H?UPGRADE REQUIRED: v3. The upgrade is a consensus commitment; staging affects recovery speed, not whether the halt occurs.The x/upgrade module is the bridge between a governance vote and a running chain that has become something new. Governance arms the plan; the BeginBlocker enforces it at the schema fence; the handler runs the store migrations in-consensus so the schema change is itself a deterministic event. For the exam, hold three things: the halt is a consensus commitment that staging cannot cancel, the handler name must match the plan name exactly, and migrations run at height because a schema change to a replicated state machine must be as deterministic as the blocks around it.