Governance Canister Build
Overview
Section titled “Overview”sons_governance is the per-campaign governance canister. It is not a fixed
canister listed in dfx.json: the launcher backend creates one instance for each
LGE campaign at runtime and installs the governance module into it.
- Source:
src/sons_governance/ - Candid interface:
src/sons_governance/sons_governance.did - Client declarations:
src/declarations/sons_governance/ - Embedded module:
src/backend/governance_wasm/sons_governance.wasm.gz, compiled into the backend withinclude_bytes!(src/backend/src/lib.rs)
Because the backend carries the governance module inside its own module, the governance module has to be built before the backend, and the backend’s hash covers the governance module too.
How the reproducible build produces it
Section titled “How the reproducible build produces it”./Deploy/docker-build.sh runs the Dockerfile, which builds the governance
module first and then the backend that embeds it:
The relevant outputs in Deploy/wasm/:
| File | What it is |
|---|---|
sons_governance_docker.wasm | the raw governance module |
sons_governance_docker.wasm.gz | the compressed module a SONS upgrade proposal carries |
sons_governance_embedded_in_backend.wasm.gz | the exact blob compiled into the backend |
The last two are the same bytes: the Dockerfile compares them during the build.
Deploy/WASM_HASHES.txt lists the embedded blob as embedded_backend_gz.
How the backend installs it
Section titled “How the backend installs it”The backend installs the governance module in chunks
(src/backend/src/sons_install.rs): it uploads the embedded .wasm.gz in pieces of
at most 1 MiB into the new canister’s own chunk store, calls
install_chunked_code, and clears the store afterwards. The platform completes the
install only if the chunks reassemble to the declared hash. The chunks are slices
of the embedded blob, so:
- a governance canister created by the backend reports the
embedded_backend_gzhash as its module hash; - a governance canister later upgraded by one of its own proposals reports that proposal’s WASM Hash instead.
Provenance of the committed embed
Section titled “Provenance of the committed embed”The embedded .wasm.gz is a build output committed to the repository, so that
builds other than the Docker build can compile the backend. Next to it sits a
sidecar, sons_governance.wasm.gz.source, written by the build that produced the
embed. It records:
- which build produced it (
localordocker); - the SHA-256 of the embed and of the module inside it;
- the build recipe (the same as Dockerfile STEP 1);
- the SHA-256 of every input that can change the module: the files of every local
crate
sons_governancedepends on, files they include, the rootCargo.toml,Cargo.lock,rust-toolchain.toml, and the build recipes.
scripts/check_sons_embed_fresh.py recomputes all of it and reports whether the
committed embed still matches the current source. Anyone can run it at a commit
with Python 3:
python3 scripts/check_sons_embed_fresh.pyExit 0 means the embed matches its recorded inputs; exit 1 (STALE) means an
input changed after the embed was built; exit 2 means the sidecar is missing or
unreadable.
This check is a consistency aid, not the proof a voter relies on. The release
artefact is the Docker build, which rebuilds the embed from source, so a matching
backend hash proves the embedded governance module is exactly what the declared
commit’s src/sons_governance/ produces. See the
Voter Verification Guide for the full procedure.
Verifying the governance hashes
Section titled “Verifying the governance hashes”After ./Deploy/docker-build.sh (or the minimal docker buildx path in the
Voter Verification Guide):
shasum -a 256 Deploy/wasm/sons_governance_docker.wasmshasum -a 256 Deploy/wasm/sons_governance_docker.wasm.gzshasum -a 256 Deploy/wasm/sons_governance_embedded_in_backend.wasm.gz./Deploy/verify-proposal.sh ohshii_launcher_backend also prints the embedded
blob’s hash and checks that those bytes are inside the backend module.
To read the module hash of a live campaign governance canister (public, no identity needed):
dfx canister info <governance_canister_id> --network ic --identity anonymousClient bindings
Section titled “Client bindings”sons_governance is not in dfx.json, so dfx generate does not cover it. Its
JavaScript and TypeScript bindings are generated from the .did with didc:
didc bind src/sons_governance/sons_governance.did -t js > src/declarations/sons_governance/sons_governance.did.jsdidc bind src/sons_governance/sons_governance.did -t ts > src/declarations/sons_governance/sons_governance.did.d.tsAn integrator can generate bindings for any language from
src/sons_governance/sons_governance.did with the standard Candid tooling.
Related documentation
Section titled “Related documentation”- Voter Verification Guide: verifying a proposal’s WASM hash and source diff
- Architecture: canister layout and governance scope