backend/- Source code for the backendbackend/main/- Package registry canisterbackend/storage/- Storage canisters
frontend/- Source code for the package registry frontend (mops.one)cli/- Source code for themopscommand line toolcli-builder/- Docker base image for reproducible Mops CLI buildscli-release/- Mops CLI builds and frontendcli-release/frontend/- Frontend for the Mops CLI (cli.mops.one)cli-release/versions/- Mops CLI versions
docs/- Mops documentation (docs.mops.one)blog/- Mops blog (blog.mops.one)ui-kit/- Mops UI Kit with shared UI componentsbench/- Dogfood formops benchcommandtest/- Dogfood formops testcommand
Every canister deploy — local, staging and mainnet — runs on icp-cli (config in icp.yaml). Install the same versions CI pins:
npm install -g @icp-sdk/icp-cli@1.2.0 @icp-sdk/ic-wasm@0.11.1npm start - starts local replica and dev server
To be able to install/publish packages locally:
- Install
tsxorbunglobally
npm install -g tsx
- Add
mops-localalias to your shell (~/.zshrc,~/.bashrc)
alias mops-local="tsx /<path-to-local-mops>/cli/environments/nodejs/cli.ts"or
alias mops-local="bun /<path-to-local-mops>/cli/environments/nodejs/cli.ts"- Point the CLI at your local registry
MOPS_NETWORK=local alone is not enough: the built-in local endpoint hardcodes the staging canister id, while the local replica allocates a fresh one at create time. Pass the deployed one explicitly:
export MOPS_REGISTRY_HOST="http://127.0.0.1:4943"
export MOPS_REGISTRY_CANISTER_ID="$(jq -r .main .icp/cache/mappings/local.ids.json)"Now you can install/publish packages locally like this mops-local add <pkg>
To work against the staging registry instead, export MOPS_NETWORK=staging (no overrides needed), or set it per command: MOPS_NETWORK=staging mops-local add <pkg>.
See Environment Variables in the documentation for details.
npm run deploy-staging # main + assets, on the staging canisters
npm run deploy-ic # every canister the ic environment declares
npm run deploy-ic blog # or just oneBoth import the mops identity by name. The canister IDs come from
.icp/data/mappings/<environment>.ids.json, which is committed — icp-cli keeps
mainnet IDs there precisely so a fresh clone knows the canisters already exist,
and only .icp/cache/ (local networks, downloads) is ignored. Deploys pass
--no-create, so a missing mapping fails instead of quietly creating a second
canister.
Adding a mainnet canister means icp canister link <name> <id> -e ic once, then
committing the updated mapping.
Always deploy through the npm scripts, not icp deploy directly. They set
MOPS_FRONTEND_NETWORK from the same value they pass to -e, which is what the
frontend build reads. Deliberately not icp-cli's own ICP_ENVIRONMENT: -e
wins for icp but an exported ICP_ENVIRONMENT would still reach vite, so the
two could silently disagree and bake local replica IDs into a mainnet bundle.
A raw icp deploy assets leaves it unset and fails loudly instead.
docs and cli are deployed by release.yml on a CLI release, through
.github/actions/deploy-canister. Neither builds the frontend, so the raw form
is safe for them. To roll one back, check out the previous release commit and
run what that action runs:
icp deploy cli -e ic --identity mops --no-create --yes