Docker Deploy Guide
This guide explains how to deploy OhShii Launcher canisters using Docker for reproducible, verifiable builds suitable for DAO governance.
Why Docker Builds for DAO?
Section titled “Why Docker Builds for DAO?”- Reproducible: Same source code → same WASM hash, regardless of who builds it
- Verifiable: Community members can verify deployed WASMs match the source code
- Transparent: No local environment dependencies that could differ between developers
- Trustless: DAO voters can independently verify what they’re approving
This guide covers the full Docker deployment workflow, .dockerignore optimization, environment variables, and troubleshooting.
Prerequisites
Section titled “Prerequisites”- Docker Desktop installed and running
- dfx installed and identity configured
- Git repository up to date
- expect command for automated password entry
- Script will offer to install if missing:
brew install expect - Required for
docker-deploy.shautomated script
- Script will offer to install if missing:
- didc (Candid compiler) - optional, script auto-downloads if missing
- Used to generate TypeScript declarations for dynamic canisters
- Auto-downloaded to
/tmp/didcbydocker-build.shif not found
Quick Start (Recommended)
Section titled “Quick Start (Recommended)”Single Command Build + Deploy (Backend + Frontend)
Section titled “Single Command Build + Deploy (Backend + Frontend)”cd /path/to/dev-ohshii-launcher./docker-deploy.sh --buildThis single script handles everything:
- ✓ Checks all prerequisites (Docker, dfx, Node.js, npm, expect)
- ✓ Installs npm dependencies if needed
- ✓ Builds backend WASMs with Docker (with
--buildflag) - ✓ Builds frontend with Vite
- ✓ Deploys all 5 canisters (4 backend + 1 frontend) to IC mainnet
- ✓ Verifies backend hashes automatically
That’s it! The script handles the complete deployment pipeline.
Script Reference
Section titled “Script Reference”docker-deploy.sh - Full Deployment Pipeline
Section titled “docker-deploy.sh - Full Deployment Pipeline”What it does:
- Checks prerequisites (Docker, dfx, Node.js, npm, expect)
- Optionally builds WASMs with Docker (if
--buildflag or WASMs missing) - Builds frontend with
npm run build - Deploys all 4 backend canisters using Docker-built WASMs
- Deploys frontend canister
- Verifies hashes match expected values
Usage:
./Deploy/docker-deploy.sh # Use existing WASMs (skips Docker build if files exist)./Deploy/docker-deploy.sh --build # Force Docker rebuild (use after code changes!)The script switches to the project root automatically, so you can run it from the project root or from Deploy/. Backend deployment verification accepts both “Upgraded code” and “Upgrading code for canister” (IC output) as success.
Important: Always use --build after modifying Rust code!
docker-build.sh - Build Only (No Deploy)
Section titled “docker-build.sh - Build Only (No Deploy)”What it does:
- Builds all Rust canisters inside Docker (reproducible)
- Optimizes WASMs with
ic-wasm shrink - Adds Candid metadata to WASMs
- Creates deterministic
.wasm.gzfiles withgzip -9 -n(for DAO proposals) - Generates TypeScript declarations for
sons_governancewithdidc - Creates
WASM_HASHES.txtwith SHA-256 hashes (both raw and gzipped)
Usage:
./docker-build.sh # Build WASMs only, no deployOutput files in Deploy/wasm/:
ohshii_launcher_backend_docker.wasm # Raw WASMohshii_launcher_backend_docker.wasm.gz # Deterministic gzip (for DAO proposals)dao_storage_docker.wasmdao_storage_docker.wasm.gzpool_manager_docker.wasmpool_manager_docker.wasm.gzohshii_governance_docker.wasmohshii_governance_docker.wasm.gzsons_governance_docker.wasmsons_governance_docker.wasm.gzOutput files in Deploy/:
WASM_HASHES.txt # Contains both raw and .wasm.gz hashesFor DAO voters: The .wasm.gz hashes in WASM_HASHES.txt are what should be compared with the proposal’s WASM Hash or Content Hash.
quick-deploy.sh - Deploy Only (No Build)
Section titled “quick-deploy.sh - Deploy Only (No Build)”What it does:
- Deploys existing
*_docker.wasmfiles to IC mainnet - Does NOT build (assumes WASMs already exist)
- Useful for re-deploying without rebuilding
Usage:
./quick-deploy.sh # Deploy existing WASMsInterface-safety gate (atomic storage ↔ backend wave)
Section titled “Interface-safety gate (atomic storage ↔ backend wave)”docker-deploy.sh and quick-deploy.sh run a committed pre-flight gate —
scripts/check_live_subtype.sh — before installing any canister. It is part
of the repo (any authorized deployer who clones gets it; it is not a local-only
helper).
What it protects. A change to an interface-sensitive dao_storage method
(one that pairs atomically with ohshii_launcher_backend on the wire) is a hard
atomic-deploy pair. Installing such a change non-atomically — backend without
storage, or storage without backend — leaves the live pair on incompatible wires
and traps the affected flows. The gate compares the methods you are about to ship
against the live on-chain candid:service and:
- passes if they match (no wire change), or
- requires the wave to co-install
dao_storage+ohshii_launcher_backend(+pool_manager) and an explicit operator attestation that no ingress can hit a half-installed pair, otherwise it refuses the deploy (fail-closed).
Read identity. The live candid:service is public, read-only metadata, so the
gate fetches it with the unencrypted default dfx identity — NOT your deploy
identity (which is a password-protected PEM that can’t be decrypted
non-interactively). This is a per-invocation override; your deploy still installs
with the protected identity.
Environment variables.
| Var | Effect |
|---|---|
OHSHII_WAVE_CONFIRMED=1 | Operator attests ingress is quiesced — required to ship a detected wire change in a co-membered wave. |
OHSHII_READ_IDENTITY=<name> | dfx identity used for the read-only metadata fetch (default: default). |
OHSHII_SKIP_LIVE_SUBTYPE=1 | Emergency hotfix override — bypasses the gate, logs loudly. Use only with manual sign-off. |
OHSHII_NETWORK=<net> | Network for the metadata query (default: ic). |
Exit codes: 0 safe to proceed · 1 unsafe wave (wire changed without
co-membership / attestation) · 2 tooling error (cannot verify → fail-closed).
When you add a new storage↔backend wire, add its method name to
SENSITIVE_METHODS in scripts/check_live_subtype.sh.
Common Workflows
Section titled “Common Workflows”After Code Changes (Most Common)
Section titled “After Code Changes (Most Common)”./docker-deploy.sh --buildFirst Time Setup
Section titled “First Time Setup”# 1. Create password file (one-time)cd Deployecho 'DFX_IDENTITY_PASSWORD="your-password"' > .env.deploy
# 2. Build and deploy./docker-deploy.sh --buildRebuild Only (No Deploy)
Section titled “Rebuild Only (No Deploy)”./docker-build.shRedeploy Same Code
Section titled “Redeploy Same Code”./docker-deploy.sh # Uses existing WASMsManual Build Process (Advanced)
Section titled “Manual Build Process (Advanced)”If you want to build separately before deploying:
Step 1: Navigate to Project Root
Section titled “Step 1: Navigate to Project Root”cd /path/to/dev-ohshii-launcherStep 2: Run Docker Build
Section titled “Step 2: Run Docker Build”./docker-build.shWhat this does:
- Builds
sons_governancecanister first (standalone) - Optimizes with
ic-wasm(shrink + Candid metadata) - Copies governance WASM to
src/backend/governance_wasm/and gzips it - Generates TypeScript/JavaScript declarations with
didcforsons_governancesrc/declarations/sons_governance/sons_governance.did.jssrc/declarations/sons_governance/sons_governance.did.d.ts- Automatically downloads
didcif not found
- Builds remaining canisters (
ohshii_launcher_backendembeds the fresh governance WASM) - Optimizes all output WASMs with
ic-wasm - Generates
WASM_HASHES.txtwith SHA-256 hashes
Build time: ~5-10 minutes (depending on hardware)
Output files in project root:
ohshii_launcher_backend_docker.wasmdao_storage_docker.wasmpool_manager_docker.wasmohshii_governance_docker.wasmsons_governance_docker.wasmWASM_HASHES.txtStep 3: Review WASM Hashes
Section titled “Step 3: Review WASM Hashes”cat WASM_HASHES.txtExample output:
# WASM Hashes - Fri Feb 14 16:30:00 CET 2026
# Main canisters (deployed via docker-deploy.sh or dfx)abc123...def ohshii_launcher_backend_docker.wasm456789...abc dao_storage_docker.wasmdef012...345 pool_manager_docker.wasm678901...bcd ohshii_governance_docker.wasm
# Dynamic canister (embedded in backend)234567...890 sons_governance_docker.wasm
# Embedded governance WASM (gzipped, used by include_bytes! in backend)890123...cde src/backend/governance_wasm/sons_governance.wasm.gzSave this file - it’s your proof of what was built and deployed.
Deployment Options
Section titled “Deployment Options”Option A: Automated Script (Recommended - All-in-One)
Section titled “Option A: Automated Script (Recommended - All-in-One)”Uses docker-deploy.sh for automated build + deployment of ALL canisters (backend + frontend) with hash verification.
Setup (One-Time)
Section titled “Setup (One-Time)”Create .env.deploy in Deploy directory:
cd Deployecho 'DFX_IDENTITY_PASSWORD="your-password-here"' > .env.deploySecurity: .env.deploy is already in .gitignore
Deploy (Build + Deploy Everything)
Section titled “Deploy (Build + Deploy Everything)”./docker-deploy.shWhat it does:
- Checks prerequisites (Docker, dfx, Node.js, npm, expect)
- Offers to install
expectif missing
- Offers to install
- Installs npm dependencies (if
node_modules/not present) - Builds backend WASMs with Docker (if not present or use
--buildto force rebuild)- Generates
WASM_HASHES.txt
- Generates
- Builds frontend with Vite (
npm run build)- Creates
dist/directory
- Creates
- Deploys all 5 canisters to IC mainnet:
- 4 backend canisters with Docker-built WASMs
- 1 frontend canister with Vite-built assets
- Automatically enters password using
expect - Auto-confirms breaking changes with
--yesflag - Verifies hash of each backend canister using
dfx canister info
- Reports complete deployment summary
Options:
./docker-deploy.sh # Use existing WASMs or build if missing./docker-deploy.sh --build # Force rebuild + deployExpected output:
========================================OHSHII-LAUNCHER - Build & Deploy to IC========================================
Checking prerequisites...✓ Docker is running✓ dfx is installed✓ Node.js is installed (v20.11.0)✓ npm is installed (10.2.4)✓ expect is installed✓ Identity password configured
✓ npm dependencies already installed
Using existing Docker-built backend WASMs (use --build to rebuild)
Building frontend with Vite...✓ Frontend build completed
========================================DEPLOYMENT CONFIRMATION========================================
⚠️ WARNING: This will upgrade all canisters on IC mainnet.
Pre-deployment checklist: ✓ Backend Docker build completed ✓ Frontend build completed ✓ WASM hashes generated ✓ Identity configured
Canisters to deploy: • ohshii_launcher_backend (backend) • dao_storage (backend) • pool_manager (backend) • ohshii_governance (backend) • ohshii_launcher_frontend (frontend)
Deploy to IC mainnet? (yes/no): yes
========================================Starting deployment to IC mainnet...========================================
1/4 Deploying ohshii_launcher_backend... [OK] Hash verified: abc123...def
2/4 Deploying dao_storage... [OK] Hash verified: 456789...abc
3/4 Deploying pool_manager... [OK] Hash verified: def012...345
4/4 Deploying ohshii_governance... [OK] Hash verified: 678901...bcd
========================================Backend deployment summary:✓ All backend canisters deployed and verified!========================================
========================================5/5 Deploying frontend...========================================
✓ Frontend deployed successfully
========================================DEPLOYMENT SUMMARY========================================✓ All canisters deployed successfully! • Backend canisters: Verified ✓ • Frontend canister: Deployed ✓========================================Option B: Manual Deploy with Hash Verification
Section titled “Option B: Manual Deploy with Hash Verification”For more control or if expect is not available.
Deploy Each Canister
Section titled “Deploy Each Canister”# Token Launcher Backenddfx canister install ohshii_launcher_backend \ --wasm ./ohshii_launcher_backend_docker.wasm \ --mode upgrade \ --network ic
# DAO Storagedfx canister install dao_storage \ --wasm ./dao_storage_docker.wasm \ --mode upgrade \ --network ic
# Pool Managerdfx canister install pool_manager \ --wasm ./pool_manager_docker.wasm \ --mode upgrade \ --network ic
# OHSHII Governancedfx canister install ohshii_governance \ --wasm ./ohshii_governance_docker.wasm \ --mode upgrade \ --network icNote: sons_governance is NOT deployed directly - it’s embedded in ohshii_launcher_backend and deployed dynamically when LGEs are created.
Verify Deployed Hashes
Section titled “Verify Deployed Hashes”After each deployment, verify the WASM hash:
# Get deployed canister info (includes module_hash)dfx canister info ohshii_launcher_backend --network ic
# Compare with local WASM hashshasum -a 256 ohshii_launcher_backend_docker.wasmOr use the verification script:
./verify-canister-hash.sh ohshii_launcher_backend ohshii_launcher_backend_docker.wasm./verify-canister-hash.sh dao_storage dao_storage_docker.wasm./verify-canister-hash.sh pool_manager pool_manager_docker.wasm./verify-canister-hash.sh ohshii_governance ohshii_governance_docker.wasmPost-Deployment Verification
Section titled “Post-Deployment Verification”1. Check Canister Versions
Section titled “1. Check Canister Versions”Verify the new version is deployed:
dfx canister call ohshii_launcher_backend get_version --network ic# Expected: ("1.0.0")
dfx canister call dao_storage get_version --network ic# Expected: ("1.0.0")
dfx canister call pool_manager get_version --network ic# Expected: ("1.0.0")
dfx canister call ohshii_governance get_version --network ic# Expected: ("1.0.0")2. Verify Canister Status
Section titled “2. Verify Canister Status”dfx canister status ohshii_launcher_backend --network icdfx canister status dao_storage --network icdfx canister status pool_manager --network icdfx canister status ohshii_governance --network icCheck:
- Status: Running
- Memory allocation: Not excessive
- Module hash: Matches
WASM_HASHES.txt - Controllers: Correct (should include NNS, ohshii_governance, and admin principals)
3. Test Basic Functionality
Section titled “3. Test Basic Functionality”# Test a read-only query to ensure canister is respondingdfx canister call dao_storage get_campaigns_count --network icFor DAO Governance Proposals
Section titled “For DAO Governance Proposals”When creating an upgrade proposal, provide:
1. Source Code Reference
Section titled “1. Source Code Reference”GitHub Commit: abc123def456... (full 40-char hash)Repository: https://github.com/your-org/ohshii-launcherBranch: main2. Build Instructions
Section titled “2. Build Instructions”docker build -t ohshii-builder .# Extract WASMs as documented in DOCKER_DEPLOY_GUIDE.md3. WASM Hashes
Section titled “3. WASM Hashes”Paste the .wasm.gz hashes from WASM_HASHES.txt(These are what voters will compare with their local build)4. Verification Steps for Voters
Section titled “4. Verification Steps for Voters”Voters can verify the proposal using the one-click helper script:
# 1. Clone repository at proposal's commitgit clone https://github.com/ohshii-labs/ohshii-launchercd ohshii-launchergit checkout <git_commit_hash>
# 2. Run reproducible Docker build./Deploy/docker-build.sh
# 3. Verify hash with one-click script./Deploy/verify-proposal.sh <canister_name>Or verify manually by comparing the .wasm.gz hash from Deploy/WASM_HASHES.txt with the proposal’s WASM Hash.
See VOTER_VERIFICATION_GUIDE.md for detailed voter verification instructions.
Reproducibility Testing
Section titled “Reproducibility Testing”Any community member can verify the build:
# Clone repositorygit clone https://github.com/ohshii-labs/ohshii-launchercd ohshii-launcher
# Checkout exact commitgit checkout abc123def456
# Build with Docker./Deploy/docker-build.sh
# Verify hashes with the helper script./Deploy/verify-proposal.sh --all
# Or compare hashes manuallycat Deploy/WASM_HASHES.txt# The .wasm.gz hashes should be IDENTICAL to the proposal hashesImportant: The .wasm.gz files are created with gzip -9 -n which strips timestamps from the gzip header, ensuring identical hashes across different machines and build times.
Docker Build Performance
Section titled “Docker Build Performance”Why Docker Reinstalls Dependencies?
Section titled “Why Docker Reinstalls Dependencies?”Docker uses layer caching. Each RUN command creates a layer:
RUN rustup target add wasm32-unknown-unknown # Layer 1 (cached)RUN cargo install ic-wasm # Layer 2 (cached)COPY . . # Layer 3 (invalidated when code changes)RUN cargo build ... # Layer 4 (rebuilds because Layer 3 changed)When you change source code:
- The
COPY . .layer is invalidated - All subsequent layers must rebuild
However, if only source code changed and Docker layers 1-2 are cached:
rustupandic-wasm installare SKIPPED (cached)- Only
cargo buildruns
.dockerignore Optimization
Section titled “.dockerignore Optimization”The .dockerignore file excludes unnecessary files from Docker context:
| Excluded | Reason | Size Saved |
|---|---|---|
target/ | Rust build artifacts | ~6GB |
node_modules/ | JS dependencies | ~500MB |
.dfx/ | DFX local state | ~100MB |
.git/ | Git history | ~50MB |
Before .dockerignore: ~7.4GB transferred (70+ seconds) After .dockerignore: ~100MB transferred (few seconds)
Build Times
Section titled “Build Times”| Scenario | Time |
|---|---|
| First build (no cache) | ~10-15 min |
| Code change (cache layers 1-2) | ~3-5 min |
| No code change (full cache) | ~30 sec |
Troubleshooting
Section titled “Troubleshooting”Build Fails with “Docker not running”
Section titled “Build Fails with “Docker not running””Solution:
# Start Docker Desktop, then retry./docker-build.shBuild Fails with Memory Error
Section titled “Build Fails with Memory Error”Solution: Increase Docker memory allocation:
- Open Docker Desktop
- Settings → Resources → Memory
- Increase to at least 4GB
- Apply & Restart
Deploy Fails with “Module hash mismatch”
Section titled “Deploy Fails with “Module hash mismatch””Cause: Trying to deploy a WASM that doesn’t match what dfx expects
Solution: Use --mode upgrade and ensure you’re deploying the Docker-built WASM:
dfx canister install <canister_name> \ --wasm ./<canister_name>_docker.wasm \ --mode upgrade \ --network icHash Verification Fails
Section titled “Hash Verification Fails”Cause: Deployed canister doesn’t match local WASM
Verification Logic:
- “Module hash … is already installed” → Hash verified from output ✓
- “Upgraded code for canister” → Deploy successful, hash = local WASM ✓
- “error” / “failed” → Deploy failed ❌
Solution if verification fails:
- Verify you deployed the correct
*_docker.wasmfile - Rebuild with
./docker-build.shto ensure fresh build - Redeploy with correct WASM file
Manual verification (if needed):
# Get deployed hashdfx canister info <canister_name> --network ic | grep "Module hash"
# Get local WASM hashshasum -a 256 <canister_name>_docker.wasmBoth should match (deployed hash has 0x prefix, local doesn’t).
Breaking Changes Warning
Section titled “Breaking Changes Warning”What it means: dfx detected that the new Candid interface has breaking changes compared to the deployed version.
Common causes:
- Added/removed public methods
- Changed method signatures
- Changed return types
Solution: The script automatically accepts breaking changes with --yes flag. To review changes manually, use:
dfx canister install <canister_name> --wasm <file>_docker.wasm --mode upgrade --network ic# (without --yes flag to see warnings)Frontend Deploy Overwrites Backend WASMs
Section titled “Frontend Deploy Overwrites Backend WASMs”Problem: dfx deploy ohshii_launcher_frontend rebuilds all dependencies (backend canisters) with local cargo build, overwriting the Docker WASMs.
Cause: In dfx.json, frontend has dependencies: [backend canisters]. dfx deploy rebuilds them.
Solution: Use dfx canister install instead of dfx deploy for the frontend:
dfx canister install ohshii_launcher_frontend --mode upgrade --network icThe docker-deploy.sh script handles this automatically.
Gzip Only Needed for sons_governance
Section titled “Gzip Only Needed for sons_governance”Note: Gzip compression is only needed for sons_governance.wasm because it’s embedded in the backend using include_bytes!("sons_governance.wasm.gz").
For all other canisters (ohshii_launcher_backend, dao_storage, pool_manager, ohshii_governance), we deploy the raw WASM files directly. The hash shown in the dashboard is the hash of the raw WASM file.
”Password required” but expect not working
Section titled “”Password required” but expect not working”Solution: Deploy manually without the script:
dfx canister install <canister_name> \ --wasm ./<canister_name>_docker.wasm \ --mode upgrade \ --network ic# Enter password when promptedCanister Architecture Notes
Section titled “Canister Architecture Notes”sons_governance (Special Case)
Section titled “sons_governance (Special Case)”The sons_governance canister is embedded in ohshii_launcher_backend:
const GOVERNANCE_WASM: &[u8] = include_bytes!("../governance_wasm/sons_governance.wasm.gz");Why?
- Governance canisters are created dynamically per LGE campaign
- Each LGE gets its own governance canister instance
- The backend deploys governance canisters using the embedded WASM
TypeScript Declarations:
Since sons_governance is NOT in dfx.json (it’s dynamic), the build script uses didc to generate TypeScript/JavaScript declarations:
# Automatically done by docker-build.sh: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.tsVerification: The governance WASM hash is part of the backend’s WASM hash. When you verify the backend WASM hash, you’re also verifying the embedded governance WASM.
To update governance (and all canisters):
./docker-build.sh# Then deploy with docker-deploy.sh or dfx deploy --network icVersion Management
Section titled “Version Management”Before Deploying a New Version
Section titled “Before Deploying a New Version”-
Update version constants in each modified canister’s
lib.rs:const CANISTER_VERSION: &str = "1.0.0"; // Increment version -
Commit version changes:
Terminal window git add .git commit -m "Bump version to 1.1.0"git push -
Tag the release:
Terminal window git tag v1.1.0git push origin v1.1.0 -
Build with Docker:
Terminal window ./docker-build.sh -
Deploy and verify hashes
-
Publish WASM_HASHES.txt to DAO:
- Create GitHub release with tag
- Attach
WASM_HASHES.txt - Include commit hash and build instructions
Security Best Practices
Section titled “Security Best Practices”-
Always use Docker builds for production deployments
- Never use local
cargo buildfor mainnet - Never use
dfx buildfor mainnet
- Never use local
-
Verify hashes before and after deployment
- Before: Check
WASM_HASHES.txtis correct - After: Verify deployed hash matches local hash
- Before: Check
-
Keep .env.deploy secure
- Never commit to git
- Use strong passwords
- Rotate passwords periodically
-
Document every deployment
- Git commit hash
- WASM hashes
- Deployment timestamp
- Who deployed
-
Test on testnet first (if available)
- Deploy to testnet
- Run integration tests
- Only then deploy to mainnet
Quick Reference
Section titled “Quick Reference”Full Deployment Flow (All-in-One)
Section titled “Full Deployment Flow (All-in-One)”# 1. Navigate to projectcd /path/to/dev-ohshii-launcher
# 2. Build + Deploy with single command./docker-deploy.sh
# Script automatically:# - Checks prerequisites# - Builds WASMs (if needed)# - Deploys all canisters# - Verifies hashes
# 3. Verify versions (post-deployment)dfx canister call ohshii_launcher_backend get_version --network icdfx canister call dao_storage get_version --network icdfx canister call pool_manager get_version --network icdfx canister call ohshii_governance get_version --network icForce Rebuild + Deploy
Section titled “Force Rebuild + Deploy”# Force rebuild even if WASMs exist./docker-deploy.sh --build
# Or manually:./docker-build.sh # 1. Build./docker-deploy.sh # 2. DeployEmergency Rollback
Section titled “Emergency Rollback”If deployment fails or issues are discovered:
# Get the previous WASM from backup or rebuild from previous commitgit checkout <previous-commit>./docker-build.sh
# Redeploy previous version./docker-deploy.shAdditional Resources
Section titled “Additional Resources”- Dockerfile: Build configuration for reproducible builds
- docker-build.sh: Automated build script (builds all canisters, produces .wasm and .wasm.gz)
- docker-deploy.sh: Automated deployment script
- verify-proposal.sh: One-click voter verification helper
- VOTER_VERIFICATION_GUIDE.md: Detailed guide for voters verifying DAO proposals
- WASM_HASHES.txt: Generated hash file (contains both raw and .wasm.gz hashes)
- README.md: Full project documentation
Last Updated: February 2026
Canister Version: 1.0.0