Voter Verification Guide
This guide explains how to independently verify that a proposed canister upgrade in the OHSHII-LAUNCHER DAO was built from the claimed source code. NEVER trust proposal text alone - always verify before voting.
Security Warning
Section titled “Security Warning”CRITICAL: A malicious proposer can write anything in the proposal title and description. The ONLY way to know if a proposal is legitimate is to:
- Clone the source repository
- Checkout the exact commit referenced in the proposal
- Review the code changes
- Build with the sandboxed Docker script
- Compare your locally computed hash with the proposal’s hash
If the hashes don’t match, DO NOT vote for the proposal.
A matching hash is only half the review
Section titled “A matching hash is only half the review”A reproducible build proves the WASM is the source the proposal declares. It does not prove that source is safe. A backdoor genuinely present in the declared commit — an extra principal accepted by is_admin(), say — reproduces perfectly: your hash matches, and no artifact-level scan can see it, because it adds no method and no export.
Step 3 above is what catches that, and it is the step people skip because it used to be the awkward one. It no longer is.
Governance records the (repository, commit) pair every canister is currently running, written only after an upgrade’s module hash is confirmed on-chain. That gives the review a second endpoint: the proposal says where the code is going, this says where it is.
icp canister call xnsdw-yaaaa-aaaal-qsqua-cai get_deployed_source '(principal "<target_canister_id>")' -n icThe proposal view links the comparison for you when both sides are on GitHub. Otherwise, compare locally — note that the proposer’s commit normally lives in their own fork, since proposals are made before (or instead of) an upstream merge:
git clone <deployed_repo_url> && cd <repo>git remote add proposed <proposal_repo_url>git fetch proposedgit diff <deployed_commit> proposed/<proposal_commit>If get_deployed_source returns null, the baseline is not recorded — the canister was last upgraded before governance began storing provenance. That is not a clean bill of health: review the full proposed source rather than a delta.
Read the diff before voting. A green hash with an unread diff is not verification.
Embedded (nested) wasms: the backend carries other canisters’ code
Section titled “Embedded (nested) wasms: the backend carries other canisters’ code”The backend module embeds three other wasm modules as data blobs
(include_bytes! in src/backend/src/lib.rs): the SONS governance wasm it
installs on every campaign canister, and DFINITY’s ICRC ledger + index wasms it
installs for every created token. These blobs never appear in the backend’s
export section or candid metadata — reviewing “the backend’s methods” says
nothing about them. Three rules make them verifiable:
- The full reproducible rebuild covers them transitively — and it is the
ONLY check that does. The proposal’s WASM hash is a SHA-256 over every
byte of the module, embedded blobs included, and
docker-build.shrebuilds the SONS blob from source before building the backend. A matching hash therefore proves the embedded SONS wasm is exactly what the declared commit’ssrc/sons_governance/produces. Conversely: any weaker check (scanning the uploaded wasm’s exports, candid metadata, a partial build) proves nothing about the embedded blobs. - Source review must include
src/sons_governance/(and every crate the backend embeds), not just the backend crate. A hand-editedis_admin()in the SONS source reproduces perfectly and passes every artifact scan — only reading the diff catches it. And never trust a binary committed in the tree: a.wasm.gzblob inside a commit is unreviewable by definition; trust comes from the pipeline rebuilding it from source (SONS) or from a pinned official hash (DFINITY blobs, next point). - The DFINITY blobs are pinned.
scripts/check_pinned_artifacts.py(run automatically bydocker-build.sh, and runnable standalone with just Python 3) verifiesicrc1_wasm/ic-icrc1-ledger.wasm.gzandic-icrc1-index.wasm.gzagainst hashes that were verified byte-identical to DFINITY’s official distribution (theledger-suite-icrc-2025-01-21GitHub release anddownload.dfinity.systemsat the wasm’s owngit_commit_id, respectively — the provenance trail is in the script’sPINStable). Any change to these blobs must arrive with an updated pin pointing at an official release.
As defence-in-depth, scripts/check_no_dev_admin.py (and the on-chain gate in
both governance canisters) also performs a nested scan: it walks the full
module bytes, finds embedded wasm modules (gzipped or raw), and fails if any of
them exports a dev_* method. Like the outer export scan, this catches the
lazy-malicious build — it does not catch deliberately obfuscated blobs or
source-level backdoors, which only rules 1–2 cover.
Prerequisites
Section titled “Prerequisites”Before you begin, ensure you have:
- Git installed
- Docker installed and running (for backend verification)
- dfx installed (for frontend evidence verification) - Install dfx
- icp-cli (optional) — interchangeable with dfx for every ON-CHAIN hash
check in this guide (
npm install -g @icp-sdk/icp-cli). Frontend evidence nuance (verified against the official docs, July 2026): the STAGING flow is documented with icp-cli (icp canister call … propose_commit_batch, needsPreparepermission — see Managing an SNS → Stage the asset update), but icp-cli has no equivalent of the permissionless VOTER-side evidence computation (dfx deploy --compute-evidence), and the official docs’ alternative (re-staging from the same source and comparing evidence) requiresPreparepermission on the asset canister. THIS repo therefore ships a dfx-free equivalent:tools/compute-evidence(wraps the sameic_asset::compute_evidencelibrary call dfx uses — read-only, anonymous, no permissions; see the Frontend section below). - Node.js and npm installed (for frontend verification)
- Terminal/command line access
- ~20GB disk space for Docker build cache (first build only)
Quick Verification
Section titled “Quick Verification”For Backend Proposals (UpgradeCanister)
Section titled “For Backend Proposals (UpgradeCanister)”Use the one-click verification script:
# 1. Clone the repository and checkout the proposal's commitgit clone https://github.com/ohshii-labs/ohshii-launchercd ohshii-launchergit checkout <git_commit_hash_from_proposal>
# 2. Inspect the Dockerfile (IMPORTANT: read it before running)cat Dockerfile
# 3. Run the verification helper./Deploy/verify-proposal.sh <canister_name>The script outputs the .wasm.gz SHA-256 hash. Compare it with the proposal’s WASM Hash.
It also runs a content check the hash cannot provide: it parses the wasm
export section of the artefact it just built and fails if any local-test
(dev_*) method is compiled in (scripts/check_no_dev_admin.py). Those methods
exist only in the local test environment’s builds and must never reach mainnet;
a hash match alone would not notice them, because a contaminated build compared
against itself still matches. (If python3 is unavailable the scan is skipped
with a visible warning — install it rather than voting on an unscanned build.) This scan does not replace the source review
described above — a backdoor written into the declared source adds no export, so
Step 3 remains the only defence against that class.
Valid canister names: ohshii_launcher_backend, dao_storage, pool_manager, ohshii_governance, sons_governance
For Frontend Proposals (AssetUpgrade)
Section titled “For Frontend Proposals (AssetUpgrade)”The verify-proposal.sh script does NOT work for frontend. Use dfx with --compute-evidence:
# 1. Clone the repository and checkout the proposal's commitgit clone https://github.com/ohshii-labs/ohshii-launchercd ohshii-launchergit checkout <git_commit_hash_from_proposal>
# 2. Install dependenciesnpm ci
# 3. Compute the evidence hash locally (requires dfx + network access)dfx deploy ohshii_launcher_frontend --network ic --compute-evidenceThis builds the frontend locally, connects to the IC to determine what batch operations would be needed, and computes the Evidence Hash. Compare it with the proposal’s Evidence field. If they match, the proposed assets are built from that source code.
dfx-free alternative (same evidence, no permissions needed): this repo
ships a tiny standalone tool wrapping the very library call dfx uses
(ic_asset::compute_evidence — read-only queries against the live canister,
anonymous caller). Unlike dfx it does NOT run the npm build for you — build
first:
npm cinpm run build:appcargo run --release --manifest-path tools/compute-evidence/Cargo.toml -- <frontend_canister_id> ./distThe printed hash is the evidence — compare it with the proposal’s Evidence field. Remember: evidence is a DELTA against the LIVE target canister’s current state, so always point the tool at the real canister the proposal targets (a test-environment canister yields a different hash by construction).
Concurrency (what if a second frontend proposal is attempted?): the
asset canister hard-serializes frontend upgrades — while a proposed batch
exists, create_batch fails with “Batch N is already proposed. Delete or
execute it to propose another” (verified in the upstream ic-certified-assets
source), so a second, different frontend update cannot even be STAGED until
the pending one is committed or deleted. Proposed batches never expire
(only un-proposed, inactive batches do, after ~5 minutes).
Cleanup is AUTOMATIC: when an AssetUpgrade (or dapp-batch asset step)
proposal ends in any non-executed terminal state — rejected, expired,
veto-upheld, or execution-failed — the governance canister schedules a
delete_batch on the target and its timer retries it with backoff until the
asset canister is unblocked (an executed proposal consumed its batch, so
there is nothing to delete). Batches staged but never attached to a proposal
are discovered and removed the same way (abandoned-session probe). Watch
progress via the public get_pending_asset_cleanups query on
ohshii_governance; a persistent failure surfaces as an
asset_batch_cleanup_gave_up event in the governance event log.
Manual fallback (only if the automatic queue gave up): the guardian or an
admin can run guardian_delete_asset_batch(<frontend_id>, <N>) on
ohshii_governance, or any Prepare/Commit-permission principal can
unblock staging directly:
icp canister call <frontend_canister_id> delete_batch '(record { batch_id = <N> : nat })' -n icNeither path will ever delete a batch that a still-live proposal references.
This lock is also what keeps the evidence baseline stable for the whole vote: no other frontend change can land under a pending proposal.
Why this verifies a not-yet-executed proposal: the live state is only
the COMMON BASELINE. The proposer’s staged batch (not committed — the
canister still serves the old frontend) and your locally computed
operations are both diffs from that same unchanged state, so the two
evidence hashes match exactly when — and only when — the bytes the proposer
staged equal the bytes your build of the declared commit produces. If even
one staged file differs from the commit’s output, its content sha256 inside
the operations changes and the evidence no longer matches. (Prerequisite,
same as with dfx: the frontend build must be deterministic — same commit +
npm ci + same Node version.)
Requires: dfx installed (dfx path only), internet access to IC mainnet. See “Frontend (Asset) Canister Upgrades” below for full details.
Optional: Source Interface Integrity (no toolchain)
Section titled “Optional: Source Interface Integrity (no toolchain)”Hash verification proves the WASM was built from a given commit. As an additional, dependency-free check you can confirm that commit’s public interfaces are internally consistent — that every Candid method has a matching implementation and every hand-written cross-canister type still matches its interface:
# At the proposal's commit (only Python 3 required — no dfx, no network)npm run check:mirrors # expect: 0 error(s)This is the same gate the reproducible build runs before producing any WASM. See “Interface-integrity checks” in the Docker Deploy Guide for what it verifies and why a mismatch matters.
Detailed Verification Steps
Section titled “Detailed Verification Steps”Step 1: Clone the Repository
Section titled “Step 1: Clone the Repository”git clone https://github.com/ohshii-labs/ohshii-launchercd ohshii-launcherStep 2: Checkout the Exact Commit
Section titled “Step 2: Checkout the Exact Commit”The proposal displays a git_commit_hash. Checkout that exact commit:
git checkout <git_commit_hash>Important: If the commit hash doesn’t exist in the repository, the proposal may be fraudulent.
Step 3: Inspect the Dockerfile
Section titled “Step 3: Inspect the Dockerfile”Before running any build, read the Dockerfile to ensure it doesn’t contain malicious commands:
cat DockerfileLook for:
- Standard
rust:latestor similar base image cargo buildcommands- No suspicious
curl,wget, or external downloads - No hardcoded private keys or backdoors
Step 4: Run the Reproducible Build
Section titled “Step 4: Run the Reproducible Build”The Docker build produces deterministic WASM files:
./Deploy/docker-build.shThis script:
- Builds all 5 canisters in an isolated Docker container
- Optimizes WASMs with
ic-wasm - Creates deterministic
.wasm.gzfiles withgzip -9 -n - Outputs hashes to
Deploy/WASM_HASHES.txt
Build time: ~5-10 minutes on first run (downloads dependencies), ~2-3 minutes on subsequent runs.
Step 5: Verify the Hash
Section titled “Step 5: Verify the Hash”Option A: Use the verification helper
./Deploy/verify-proposal.sh <canister_name>Valid canister names:
ohshii_launcher_backenddao_storagepool_managerohshii_governancesons_governance
Option B: Manual verification
# The hash is already in WASM_HASHES.txtcat Deploy/WASM_HASHES.txt | grep <canister_name>
# Or calculate manuallyshasum -a 256 Deploy/wasm/<canister_name>_docker.wasm.gzStep 6: Compare with Proposal
Section titled “Step 6: Compare with Proposal”Open the proposal in the OHSHII-LAUNCHER governance interface. Compare:
| Proposal Type | Compare your hash with… |
|---|---|
| Backend Upgrade | WASM Hash field |
| Frontend (Asset) Upgrade | Content Hash field |
MATCH = The proposed WASM was built from the claimed source code. Safe to vote.
MISMATCH = The proposed WASM is different. DO NOT VOTE for this proposal.
Understanding the Hashes
Section titled “Understanding the Hashes”Raw vs Gzipped WASM
Section titled “Raw vs Gzipped WASM”- Raw
.wasm: Uncompressed WebAssembly module - Gzipped
.wasm.gz: Compressed withgzip -9 -n
DAO proposals use .wasm.gz because:
- Smaller size = lower storage costs
- Faster upload to the Internet Computer
- The
-nflag removes timestamps for reproducibility
Why -n Flag Matters
Section titled “Why -n Flag Matters”Without the -n flag, gzip embeds the file modification timestamp:
# WITHOUT -n: different hash every timegzip -9 -k file.wasmshasum -a 256 file.wasm.gz # Hash A at 10:00 AMrm file.wasm.gzgzip -9 -k file.wasmshasum -a 256 file.wasm.gz # Hash B at 10:01 AM (DIFFERENT!)
# WITH -n: same hash alwaysgzip -9 -n -k file.wasmshasum -a 256 file.wasm.gz # Hash Crm file.wasm.gzgzip -9 -n -k file.wasmshasum -a 256 file.wasm.gz # Hash C (SAME!)Hash Types in Proposals
Section titled “Hash Types in Proposals”| Hash Type | Used For | Description | How to Verify |
|---|---|---|---|
| WASM Hash | Backend upgrades | SHA-256 of the .wasm.gz module | ./Deploy/verify-proposal.sh <canister> |
| Deployed module hash | Any installed canister | SHA-256 of the bytes actually installed (.wasm.gz after a governance execution; raw .wasm after a direct admin deploy) | dfx canister info <id> --network ic or icp canister status <id> -n ic — or ./scripts/verify-hash.sh ic (checks both encodings) |
| Evidence | Frontend upgrades | Deterministic hash over all batch operations | dfx deploy ohshii_launcher_frontend --network ic --compute-evidence (dfx, or the repo’s dfx-free tools/compute-evidence; icp-cli itself covers only the Prepare-permission staging flow) |
| Content Hash | Frontend upgrades | SHA-256 of individual uploaded files | shasum -a 256 <file> (simpler alternative) |
Backend Canister Upgrades
Section titled “Backend Canister Upgrades”Backend upgrades use install_chunked_code to deploy compiled Rust canisters.
Verification Flow
Section titled “Verification Flow”- Proposer builds WASM with Docker
- Proposer uploads chunks to proposal storage
- Proposal stores
wasm_module_hash(SHA-256 of.wasm.gz) - You verify by rebuilding and comparing hashes
- At execution, backend recalculates hash from stored chunks (TOCTOU protection),
installs them, and on-chain re-verifies that the deployed module hash equals
the proposal’s WASM Hash (
verify_installationin ohshii_governance) - You can independently confirm the same thing with
dfx canister infooricp canister status(see below)
Post-Execution Verification
Section titled “Post-Execution Verification”Which hash to expect — this depends on WHO installed the canister:
| Deploy path | Bytes installed | Deployed module hash equals |
|---|---|---|
| Governance proposal execution (the DAO path this guide is about) | the proposal’s .wasm.gz chunks | the proposal’s WASM Hash (= SHA-256 of Deploy/wasm/<name>_docker.wasm.gz) |
Direct admin deploy (Deploy/docker-deploy.sh / Deploy/icp-deploy.sh) | the raw .wasm | SHA-256 of Deploy/wasm/<name>_docker.wasm |
Read the deployed module hash with either CLI (public information — no
identity or controller rights needed; both print it with a 0x prefix):
dfx canister info <canister_id> --network ic --identity anonymousicp canister status <canister_id> -n icCompute the local hashes to compare against:
shasum -a 256 Deploy/wasm/<canister_name>_docker.wasm.gzshasum -a 256 Deploy/wasm/<canister_name>_docker.wasmOr let the repo do the whole comparison (read-only; it checks BOTH encodings and tells you which deploy path the on-chain hash corresponds to):
./scripts/verify-hash.sh icTiming matters: these on-chain checks are meaningful only AFTER the
proposal has executed, and only if your local build is the proposed commit.
While the vote is still open, the chain still runs the previous version and
a mismatch is expected — during the vote the only valid check is
verify-proposal.sh against the proposal’s WASM Hash.
Frontend (Asset) Canister Upgrades
Section titled “Frontend (Asset) Canister Upgrades”Frontend upgrades use the IC asset canister’s batch upload system (the same used by all SNS DAOs on the Internet Computer). This is a fundamentally different process from backend upgrades.
Important: The verify-proposal.sh script does NOT work for frontend. Use dfx deploy --compute-evidence instead.
Reference: This verification process follows the official DFINITY documentation: SNS Asset Canisters
How Asset Canister Updates Work (Background)
Section titled “How Asset Canister Updates Work (Background)”Unlike backend canisters (where you upload a single .wasm.gz file), frontend assets (HTML, CSS, JS)
are stored inside the asset canister. All asset canisters use the same standard assetstorage.wasm.gz
code provided by dfx — so module_hash is the same for every asset canister. What differs is the
content stored inside.
The update process works like this:
- Proposer (with
Preparepermission) runsdfx deploy ohshii_launcher_frontend --network ic --by-proposal - dfx builds the frontend, uploads asset chunks to the canister, and locks the batch
- The asset canister computes an evidence hash via
compute_evidence()— a deterministic hash over ALL batch operations (file keys, content types, encodings, SHA-256 of each file) - The proposal stores:
batch_id,evidence,git_repo_url,git_commit_hash - Voters verify the evidence by rebuilding locally with
--compute-evidence - If approved, governance calls
commit_proposed_batch()which re-validates the evidence (TOCTOU protection)
What You See in a Frontend Proposal
Section titled “What You See in a Frontend Proposal”When you view an AssetUpgrade proposal in the governance interface:
| Field | Example | What It Means |
|---|---|---|
| Evidence | e3b0c44298fc1c... | Deterministic hash over all batch operations (you CAN verify this!) |
| Batch ID | 42 | Identifies the locked batch on the asset canister |
| Git Repo URL | https://github.com/ohshii-labs/ohshii-launcher | Source repository |
| Git Commit | f1e2d3c4b5a6... | Exact commit the frontend was built from |
| Build Instructions | npm ci && npm run build | How to reproduce the build |
Step-by-Step Frontend Verification
Section titled “Step-by-Step Frontend Verification”Step 1: Clone and Checkout
Section titled “Step 1: Clone and Checkout”git clone https://github.com/ohshii-labs/ohshii-launchercd ohshii-launchergit checkout <git_commit_hash_from_proposal>Step 2: Install Dependencies
Section titled “Step 2: Install Dependencies”npm ci
npm ciuses the exact versions frompackage-lock.json, which maximizes reproducibility.
Step 3: Compute Evidence Locally
Section titled “Step 3: Compute Evidence Locally”dfx deploy ohshii_launcher_frontend --network ic --compute-evidenceWhat this command does:
- Builds the frontend locally (dfx automatically runs
npm run buildfor asset canisters — you do NOT need to run it manually) - Connects to the IC mainnet read-only to fetch the current state of the asset canister (it does NOT deploy or upload anything)
- Determines what batch operations would be needed to go from the current state to the new version
- Computes a deterministic hash over those operations (the evidence)
- Displays the evidence hash and exits
Why --network ic? The evidence depends on the DIFF between the current canister state and the proposed new state. dfx must read the current state from IC mainnet. Without --network ic, dfx would try a local network where the canister doesn’t exist (causing the “no Wasm module” error). This is a read-only operation — nothing is deployed or modified on mainnet.
Example output:
Computed evidence: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855Step 4: Compare Evidence with the Proposal
Section titled “Step 4: Compare Evidence with the Proposal”| Your Computed Evidence | Proposal’s Evidence | Result |
|---|---|---|
e3b0c44298fc1c... | e3b0c44298fc1c... | MATCH - The proposed assets match the source code |
e3b0c44298fc1c... | 7a8b9c0d1e2f... | MISMATCH - Do NOT vote! |
Understanding the Evidence Hash
Section titled “Understanding the Evidence Hash”The evidence hash is NOT a simple file hash. It is a deterministic hash computed by the
asset canister’s compute_evidence() function over the entire set of batch operations:
Evidence = Hash( CreateAsset { key: "/index.html", content_type: "text/html", ... }, SetAssetContent { key: "/index.html", sha256: <file_hash>, chunks: [...] }, SetAssetContent { key: "/assets/index-abc123.js", sha256: <file_hash>, chunks: [...] }, DeleteAsset { key: "/old-file.css" }, ... all operations needed to go from current state → new state)This means:
- Same source code + same current canister state = same evidence (deterministic)
- The evidence covers ALL files, not just one — it is a comprehensive proof
- It also includes deletions of old files and metadata changes
- The proposer cannot influence this computation — it is calculated by the IC
Why --network ic is Required (and Safe)
Section titled “Why --network ic is Required (and Safe)”The --compute-evidence flag connects to the IC mainnet because the evidence depends on the
difference between the current canister state and the proposed state. Two identical builds
would produce different evidence if the canister’s current state is different.
This is a read-only operation. Despite the dfx deploy command name, the --compute-evidence
flag prevents any deployment. It only:
- Reads the current canister state from IC (query call)
- Compares it with your locally built assets
- Computes and displays the evidence hash
Without --network ic, dfx defaults to a local network where the canister doesn’t exist,
causing the error: "canister contains no Wasm module".
You need:
- dfx installed (install guide)
- Internet access to the IC mainnet (read-only)
- Node.js + npm installed (dfx runs
npm run buildautomatically)
Alternative: Content Hash Verification (If You Don’t Have dfx)
Section titled “Alternative: Content Hash Verification (If You Don’t Have dfx)”If you don’t have dfx installed, you can do a simpler verification by building manually and checking individual file hashes:
# 1. Install dependencies and build the frontend manuallynpm cinpm run build
# 2. Hash the specific file mentioned in the proposal's Build Instructionsshasum -a 256 dist/index.htmlCompare this with the Content Hash if shown in the proposal. This verifies individual files but does NOT cover the full batch (deletions, metadata changes, etc.).
Reproducibility
Section titled “Reproducibility”The build is designed to be reproducible — the same commit should produce the same evidence hash:
- Timestamps are derived from the git commit (not
Date.now()), so they are identical for the same commit - Dependencies are locked via
package-lock.jsonand installed withnpm ci - Vite uses
strictRequires: truefor deterministic CommonJS handling
| Factor | Backend (Docker) | Frontend (dfx) |
|---|---|---|
| Build reproducibility | Identical (Docker isolates everything) | Should match if same Node.js major version |
| Verification tool | verify-proposal.sh | dfx deploy --compute-evidence |
Tips if evidence doesn’t match:
- Use
npm cibefore running dfx (notnpm install) - dfx runs
npm run buildautomatically — do NOT run it separately - Use the same Node.js major version as the proposer (check
node --version) - Run
git statusto ensure no local modifications - Run
git log -1 --format=%Hto verify you’re on the exact commit
If evidence doesn’t match:
- Check you’re on the correct git commit
- Check your Node.js version matches the proposer’s
- Review the source code manually — if the code looks legitimate, the mismatch may be due to build tool differences, not malicious intent
- Ask the proposer to specify their Node.js version in the Build Instructions
Common Frontend Verification Issues
Section titled “Common Frontend Verification Issues””Command not found: dfx”
Section titled “”Command not found: dfx””Install dfx: sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"
”My evidence doesn’t match!”
Section titled “”My evidence doesn’t match!””Possible causes:
- Wrong commit: Verify you checked out the exact commit from the proposal
- Different Node.js version: Use
node --versionand compare with the proposer’s - Modified files: Run
git statusto ensure no local changes - Stale npm cache: Try
rm -rf node_modules && npm ci - Different canister state: If the canister was updated between proposal creation and your verification, evidence will differ (this is rare during voting period)
“The build instructions are suspicious”
Section titled ““The build instructions are suspicious””DO NOT vote if the build instructions contain:
curlorwgetdownloading external files- Commands that modify system files
- Obfuscated or encoded commands
- Anything you don’t understand
Report suspicious proposals to the community.
”I don’t have dfx and can’t install it”
Section titled “”I don’t have dfx and can’t install it””Use the simpler Content Hash method described above, and also:
- Review the source code at the git commit for suspicious changes
- Verify the build instructions are standard (
npm ci && npm run build) - Check that the proposer is a known, trusted community member
Manual Verification (Without Helper Script)
Section titled “Manual Verification (Without Helper Script)”If you prefer to do each step manually:
# 1. Build with Docker./Deploy/docker-build.sh
# 2. The .wasm.gz files are already created in Deploy/wasm/ls -la Deploy/wasm/*.wasm.gz
# 3. Calculate hash manuallyshasum -a 256 Deploy/wasm/ohshii_launcher_backend_docker.wasm.gz
# 4. If you need to regenerate gzip for some reason:rm Deploy/wasm/ohshii_launcher_backend_docker.wasm.gzgzip -9 -n -k Deploy/wasm/ohshii_launcher_backend_docker.wasmshasum -a 256 Deploy/wasm/ohshii_launcher_backend_docker.wasm.gzTroubleshooting
Section titled “Troubleshooting”Docker Not Running
Section titled “Docker Not Running”Error: Docker is not running. Please start Docker Desktop.Start Docker Desktop and try again.
Hash Mismatch
Section titled “Hash Mismatch”Possible causes:
- Wrong commit: Double-check you checked out the exact commit from the proposal
- Modified local files: Ensure
git statusshows no uncommitted changes - Different Rust toolchain: The Dockerfile should use a consistent toolchain
- Malicious proposal: The proposer may have uploaded different code than claimed
WASM File Not Found
Section titled “WASM File Not Found”Error: WASM file not foundRun ./Deploy/docker-build.sh first to generate the WASM files.
Slow Build
Section titled “Slow Build”First builds download all dependencies (~5-10 minutes). Subsequent builds use Docker cache (~2-3 minutes).
Q: Can I verify backend proposals without Docker?
No. Docker ensures the build environment is identical across all machines. Without it, Rust compiler differences could produce different binaries.
Q: Can I verify the Evidence Hash for frontend proposals?
Yes! Run dfx deploy ohshii_launcher_frontend --network ic --compute-evidence. This builds the frontend locally and computes the evidence hash without uploading anything. Compare it with the proposal’s Evidence field. This is the official DFINITY-recommended approach for asset canister verification (same process used by all SNS DAOs on the IC).
Q: Why does --compute-evidence need network access?
The evidence hash depends on the difference between the current canister state and the proposed new state. dfx must read the current asset canister state from the IC to compute what batch operations would be needed, then hash those operations.
Q: What if I disagree with the code changes?
That’s your prerogative as a voter! Verification confirms the WASM matches the code. Whether the code is good is a separate judgment call.
Q: Can the proposer fake the git commit hash?
Technically, they could reference a commit in a forked repository. Always verify the URL is the official ohshii-labs/ohshii-launcher. Always verify the hash of your own local build matches the hash shown in the proposal before voting.
Q: Why do proposals use .wasm.gz instead of raw .wasm?
Smaller file size reduces storage costs and upload time. The IC decompresses it automatically during installation.
Q: What’s TOCTOU protection?
Time-of-Check-Time-of-Use. It prevents tampering between when voters verify and when the code is executed. The backend recalculates hashes at execution time.
Q: How do I know the Dockerfile isn’t malicious?
Read it! It’s a few dozen lines. Look for standard Rust build commands. Report anything suspicious.
Getting Help
Section titled “Getting Help”- GitHub Issues: https://github.com/ohshii-labs/ohshii-launcher/issues
- Discord: [OHSHII Labs Discord]
- Documentation: See
Deploy/DOCKER_DEPLOY_GUIDE.mdfor deployment details
Summary Checklist
Section titled “Summary Checklist”Backend Proposals (UpgradeCanister)
Section titled “Backend Proposals (UpgradeCanister)”- Clone the correct repository (
ohshii-labs/ohshii-launcher) - Checkout the exact commit hash from the proposal
- Read the Dockerfile before running it
- Run
./Deploy/verify-proposal.sh <canister_name>(or./Deploy/docker-build.sh+ manual hash) - Compare your
.wasm.gzhash with the proposal’s WASM Hash - MATCH = Safe to vote | MISMATCH = Do not vote
Frontend Proposals (AssetUpgrade)
Section titled “Frontend Proposals (AssetUpgrade)”- Clone the correct repository (
ohshii-labs/ohshii-launcher) - Checkout the exact commit hash from the proposal
- Run
npm cito install dependencies - Run
dfx deploy ohshii_launcher_frontend --network ic --compute-evidence - Compare your computed evidence with the proposal’s Evidence field
- MATCH = Safe to vote | MISMATCH = Investigate (may be Node.js version difference)
Remember: Trust but verify. Your vote protects the entire community.