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.
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
- 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.
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.
Requires: dfx installed, internet access to IC mainnet. See “Frontend (Asset) Canister Upgrades” below for full details.
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> |
| Evidence | Frontend upgrades | Deterministic hash over all batch operations | dfx deploy ohshii_launcher_frontend --network ic --compute-evidence |
| 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)
dfx canister infoconfirms deployed module hash
Post-Execution Verification
Section titled “Post-Execution Verification”After a proposal is executed, verify the deployed canister:
dfx canister info <canister_name> --network icThe output shows the installed module hash. It should match 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.