Icp-cli announcements and feedback discussion

nice, I think that is a good first shot! :slight_smile: I see that you already referenced the docs site we added to gh-pages.

I think we can work together on your repo for now. I am happy to start contributing there and provide updates. but I think that won’t happen before we come out of beta, we’re still making a lot of changes which also need to be reflected in the docs properly first.

please offer any and all PRs, in that case. I’m sure there are many other skills that we would like our agents to have. off the top of my head for problems that I have right now: authenticating using II when using agent-browser, writing integration tests with pic, assessing code for known security vulnerabilities (reentrancy, etc.)

edit: sorry, I’m getting off topic :slight_smile:

I totally forgot that we also have install scripts available. You can find the command in the release description: Releases · dfinity/icp-cli · GitHub

icp-cli v0.1.0-beta.5 Released

We’re excited to announce the release of icp-cli v0.1.0-beta.5 with several important improvements for ICP developers.

Highlights since beta.3

Windows Support
icp-cli now runs natively on Windows, expanding our platform coverage to macOS, Linux, and Windows.

See it in action: icp-cli setup and demo (Windows) - asciinema.org

Windows limitations:

  • Local networks require Docker Desktop
  • Motoko canisters require WSL — the Motoko compiler doesn’t run natively on Windows. Install icp-cli inside WSL and follow the macOS/Linux instructions instead.
  • Rust canisters work natively on Windows without WSL

Large WASM Support
You can now install canisters with WASM modules larger than 2MB. The CLI handles chunked uploads automatically.

Improved Argument Handling

  • icp canister call and icp canister install now accept arguments from files (Candid or hex format)
  • Hex-encoded arguments are supported directly
  • Configure install_args in icp.yaml to point to argument files

Safety Controls for Controller Changes
The CLI now warns and prompts for confirmation when removing yourself from a canister’s controllers, helping prevent accidental lockouts. Use -f/--force to skip prompts in scripts.

Token & Cycles Improvements

  • New human-friendly amount formats: 1_000, 1k, 1.5m, 1_234.5b, 4T
  • icp token transfer now accepts AccountIdentifier hex strings for ICP ledger transfers
  • New icp identity account-id command to display ledger account identifiers
  • New icp cycles transfer command (replaces icp token cycles transfer)

Network Naming Changes (Breaking)

  • The implicit “mainnet” network is now called “ic”
  • The --mainnet and --ic flags have been removed, use -n ic or -e ic instead
  • You can now override the implicit local network and environment

Other Improvements

  • icp canister metadata <canister> <section> fetches metadata from canisters
  • icp canister status now shows the canister name
  • IC network options (ii, nns, subnets, artificial-delay-ms) can be configured in the manifest
  • Docker networks now bind to 127.0.0.1 for better security

Documentation

Check out the documentation: icp-cli Documentation | ICP CLI

Full Changelog

See the complete changelog and download binaries:

Feedback

We’d love to hear your feedback! Please report issues or suggestions at https://github.com/dfinity/icp-cli/issues

Updated icp-skills/icp-cli for the new release: changelog.

npx skills add https://github.com/jorgenbuilder/icp-skills --skill icp-cli

Hey there,

we are discussing the dfx ledger approve command right now and I am wondering whether anybody here is actively using dfx to handle “advanced” actions like these.

How useful and important would it be to support icrc2 and other token relation operations/queries (icrc10, icrc103, icrc106, …) directly from the CLI rather than creating code snippets / scripts with existing libraries?

I am happy about any feedback in that regard, thanks! :slight_smile:

Introducing icp-cli v0.1.0 - The First Official Release

We are thrilled to announce the first official release of icp-cli - a modern, fast, and developer-friendly command-line tool for building and deploying applications on the Internet Computer.

After months of beta testing and invaluable community feedback, icp-cli v0.1.0 is here with powerful new features and the easiest installation experience yet!

Quick Install via npm

Getting started is now as simple as:

npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm

That’s it! The CLI automatically downloads the appropriate binary for your platform (macOS, Linux, or Windows).

What’s New Since Beta

Canister Snapshot Management

Full snapshot lifecycle management is now built-in:

# Create a snapshot of your canister's state
icp canister snapshot create my_canister

# List all snapshots
icp canister snapshot list my_canister

# Restore from a snapshot
icp canister snapshot restore my_canister <snapshot_id>

# Delete a snapshot
icp canister snapshot delete my_canister <snapshot_id>

Note: Canister migration is actively being worked on and will be supported soon.

Proxy Call Support

Route canister calls through a proxy canister with the new --proxy flag:

# Forward a call through a proxy canister
icp canister call my_canister my_method --proxy <PROXY_CANISTER_ID>

# Include cycles with the proxied call
icp canister call my_canister my_method --proxy <PROXY_CANISTER_ID> --cycles 1000000

HSM Identity Support

For enhanced security, icp-cli now supports Hardware Security Module (HSM) identities for signing transactions.

Enhanced Identity Management

  • icp identity export - Export your identity’s PEM file
  • icp identity rename - Rename existing identities
  • icp identity delete - Remove identities you no longer need

Canister Settings

  • Added log_visibility configuration to control who can access canister logs

Versioned Documentation

Our documentation now supports versioning, so you can always find docs matching your installed version at https://cli.icp.build which currently redirects to gh pages.

Call to Action

Test and Provide Feedback

We want to hear from you! Try out icp-cli and let us know:

  • What works well?
  • What could be improved?
  • What features would you like to see next?

Report issues and share feedback: GitHub Issues

Help Us Get to Homebrew (:star: required)

We’d love to offer icp-cli as an official Homebrew package, but Homebrew requires repositories to have 75+ GitHub stars for inclusion in their core formulas.

Please star these repositories to help us reach this milestone:

Every star counts and brings us closer to brew install icp-cli!

Resources

Thank You

A huge thank you to everyone who tested the beta releases and provided feedback. Your contributions have been instrumental in shaping this release.

We’re excited to see what you’ll build with icp-cli!


Happy building on the Internet Computer!

Starred both, really love icp-cli Documentation | ICP CLI . Will play around with it this weekend.

Great job!

I’ve starred both. Moving forward, I will be switching to icp-cli for future projects as well as for the examples included in existing libraries.

Hi ICP team & community :waving_hand:,

Huge congrats on the recent releases — the runtime canister ID injection via ic_env cookie served by the asset canister in production is excellent DX. Browser code automatically discovers PUBLIC_CANISTER_ID:<name>=<id> mappings without any hard-coding.

The local pain point In production: seamless — asset canister sets the ic_env cookie on HTML responses → agents/libs read it at runtime. Locally (e.g., vite dev + local replica with icp-cli), there’s still no built-in, zero-friction equivalent. Devs typically resort to:

  • Manually parsing .icp/cache/mappings/local.ids.json (generated by icp-cli after deploy)
  • Injecting via process.env (if using build-time env output)
  • Custom scripts, Vite plugins, or dev-server middleware

This creates extra steps during fast local iteration, especially in React/Vite/TanStack Query setups (common in the ecosystem).

Approaches we’ve explored / would love feedback on

  • A simple CLI helper (e.g. icp env set-local or icp dev env) that reads/updates local mappings after icp deploy and optionally injects a mock ic_env cookie or header for the dev server.
  • SDK/JS-agent utility like safeGetCanisterEnv() (experimental in js-sdk?) with clear fallbacks: cookie → env var → .icp/cache/mappings/local.ids.json → import.meta.
  • Lightweight Vite plugin/middleware to auto-set the cookie/header when local mappings are detected.

Two quick questions for the team/community:

  1. Is relying on the ic_env cookie (or similar runtime injection) the recommended pattern for local dev as well — even if it needs a little glue code today?
  2. Are there plans (or active work) for first-class icp-cli/SDK helpers to auto-populate/serve these mappings locally after deploy (e.g., cookie injection or env helper)?

This is a minimal template that I’m working on using icp-cli + ic-reactor!

Thanks for the great platform — loving the direction toward less hard-coding and better build/deploy separation! :rocket:

Hey Behrad, thanks for the feedback!

There is actually a WIP PR regarding bindings and setting the ic_env cookie automatically correctly depending on the environment.

It is not yet merged because it requires the icp network status command to be extended.

I am also working on making all examples working properly with icp-cli and I think the vite.config needs some additional tweak which I didn’t cover because I got my backend already deployed when testing it.

In this branch, I think icp deploy would currently fail in the updated frontend example when running vite build (or npm run build) if the backend is not deployed yet. This should also be covered.

Please have a look at this PR and let me know what you think. I assume that would generally solve the problem you mentioned, right?

See docs: add canister discovery, binding generation, and frontend development docs by marc0olo · Pull Request #338 · dfinity/icp-cli · GitHub

Hey @marc0olo, thanks for the quick response and for sharing the PR — super exciting to see this in progress! :raising_hands:

I took a look at #338, and yes, this looks like it would solve exactly the pain point I described:

  • The new docs on canister discovery + ic_env cookie mechanism are spot-on.
  • Updating vite.config.ts to pull from icp network status --json (and handling root_key properly) should make local dev feel much closer to production without manual .json parsing or env var hacks.
  • The example README rewrites explaining the cookie flow will be a big help for the community.

A couple quick thoughts/questions after skimming:

  1. Once the icp network status extension lands (for api_url etc.), will the dev server automatically set/inject the full ic_env cookie (with PUBLIC_CANISTER_ID mappings) via some proxy/middleware in the Vite example? Or will it still rely on build-time injection + fallbacks?
  2. Any rough ETA on the network status update / PR merge? No pressure — just curious so I can plan whether to wait or prototype a temp Vite middleware in the meantime.
  3. The PR mentions bindings generation — would love if the docs also touched on how safeGetCanisterEnv() (from @icp-sdk/core) fits as the recommended way to read the cookie with fallbacks (cookie → env → file?).

This direction is awesome — less hard-coding, better runtime discovery, and cleaner local/prod parity. Happy to test the branch locally once the blocker is cleared, or share feedback on the docs/examples if helpful.

Thanks again for driving this — looking forward to it landing! :rocket:

On the main branch this PR is now merged and the documentation around canister discovery and binding generation is added.

So in general, the asset canister always sets the ic_env cookie automatically. For the devserver this needs to be configured properly, see Local Development | ICP CLI. What’s important here is to deploy the backend first if you want to fetch the canister id from the backend via CLI when starting the devserver.

The proxy target for the devserver is currently hardcoded in the example and will be updated once the icp network status command is extended. Please also note that this command currently only works with managed networks. Once it supports both, connected and managed networks and returns api_url, this configuration will be fully flexible and should work for any environment.

I guess we should be able to include it in the next release.

If I am informed correctly, the asset canister will always provide this cookie - so you should be able to rely on it. But you do not have to use it, you can always hardcode or read from any source that you prefer. But again, for devservers you need to configure those to set the cookie accordingly.

Whether to use getCanisterEnv or safeGetCanisterEnv for reading the cookie depends on whether you want to throw an error or not if it isn’t present.

icp-cli Walkthrough: Templates, Recipes, and Dev Q&A

Hi everyone,

The icp-cli is now out of beta and ready for prime time. To ensure everyone is up to speed with the new command-line experience, we’re hosting a casual, terminal-first hangout session on February 19th.

This isn’t a marketing presentation. We’re skipping the slides to focus on the actual developer workflow.

What we’ll cover:

  • Initial Setup: A grounded look at the icp command structure and how to get running.
  • Templates & Recipes: How to leverage built-in patterns to skip the boilerplate.
  • Standard Workflows: Best practices for canister management and deployment.
  • Dev-to-Dev Dialogue: An open floor to discuss the “hard things.” We want your honest feedback on the tool and how we can better support your development process.

Whether you’ve been building on the IC for years or are just getting started with the new CLI, you’re welcome to join and ask questions.

Event Details:

We’re looking forward to hearing your thoughts and seeing what you’re building. See you there!

I’m trying to deploy a Rust canister using the new icp CLI (icp deploy todo), but the build step fails every time with this error:

Building canisters:
[todo] ✘ Failed to build canister: command 'mv target/wasm32-unknown-unknown/release/todo.wasm "$ICP_WASM_OUTPUT_PATH"' failed with status code 1                                                                           

 ----- Failed to build canister 'todo' -----
Error: 'command 'mv target/wasm32-unknown-unknown/release/todo.wasm "$ICP_WASM_OUTPUT_PATH"' failed with status code 1'
[todo] Build output:
[todo] Building: step 1 of 3 (script):
[todo] cargo build --package todo --target wasm32-unknown-unknown --release:
[todo] mv target/wasm32-unknown-unknown/release/todo.wasm "$ICP_WASM_OUTPUT_PATH":
[todo] >     Finished `release` profile [optimized] target(s) in 0.17s
[todo] > mv: rename target/wasm32-unknown-unknown/release/todo.wasm to /var/folders/zd/ldhmvy0j2ldc1t0l38xphk7h0000gn/T/.tmpo4fCk8/out.wasm: No such file or directory

Error: Canister(s) ["todo"] failed to build.

Structure:

Cargo.toml:

[package]
name = "todo"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
candid = { workspace = true }
ic-cdk = { workspace = true }
ic-cdk-macros = { workspace = true }
# ... (other typical deps like ic-stable-structures, serde, etc.)

canister.yaml (in backend/todo/):

name: todo

recipe:
  type: "@dfinity/rust"
  configuration:
    package: todo
    shrink: true
    candid: backend/todo/api/spec.did

The cargo build succeeds fine.

After running cargo build --target wasm32-unknown-unknown --release, I see the wasm file here:

target/wasm32-unknown-unknown/release/todo.wasm

Hi @b3hr4d,
I ran into something similar the other day…

The recipe will look for the target directory to be at the same level as it.
If you are building a cargo workspace the target directory will be at the level of Cargo.toml with the workspace definition not the one with your package definition.

You can get around this by using the recipe directly in icp.yaml instead of in a separate canister.yaml file.

I was thinking we should fix the recipe to handle this case but we didn’t get to it yet.

Hi @raymondk,

Thank you so much — that explanation nailed it exactly!

Moving the recipe config directly into the root icp.yaml like this fixed it immediately:

canisters:
  - name: todo
    recipe:
      type: "@dfinity/rust"
      configuration:
        package: todo
        shrink: true
        candid: backend/todo/api/spec.did

  # ... other canisters like frontend if any

After that, icp deploy todo (and full icp deploy) worked perfectly — no more mv error.

Super helpful tip, and yeah, it would be awesome if the recipe could eventually handle workspace layouts more gracefully (maybe by detecting the nearest workspace root or something). Until then, this is a clean workaround.

Thanks again for the quick and spot-on help! :raising_hands:

I’ve been trying to deploy my canister using the icp-cli.

When installing the canister, I need to pass an argument.

With dfx this was done by using the --argument flag like so:

dfx deploy my_canister --argument "(\"$MY_ENV_VAR\")"

I was browsing through the cli documentation, and it seems I have to use the init_args.

However, I’m having problems referencing environment variables in the init_args in my icp.yaml.

I also tested setting them under “canisters” > “settings” > “environment_variables”.

This is what I tried:

canisters:
  - name: my_canister
    recipe:
      type: "@dfinity/motoko@v4.0.0"
      configuration:
        main: src/backend/main.mo
        args:
    settings:
      environment_variables:
        MY_ENV_VAR: "abcde"
    init_args: "(\"$MY_ENV_VAR\")"

Unfortunately, the value isn’t picked up. Neither through the “settings” > “enverionment_variables” nor through my local .env file.

Can you tell me how I can do it correctly?

Hi @mattigrthr ,

The canister environment variables are not the same as the your operating system environment variables.

I would suggest you take a look at the docs here for some examples:

The short of it is that you would set the init args to the actual value you want to use. If you have different environments that need different values you would define those in your icp.yaml.

Let me know if that helps!

I ran into the same issue around cargo workspaces and instead solved it by moving the target directory into the canister’s directory. This can be achieved by creating a .cargo/config.toml in the canister’s directory with

[build]
target-dir = “target”

Good to know though that you can also solve it by moving the canister config up. Not sure which solution I prefer, guess it’s a matter of taste