Skip to content
8 changes: 4 additions & 4 deletions evaluations/icp-cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
"prompt": "How do I generate TypeScript bindings for my backend canister so I can call it from my React frontend?",
"expected_behaviors": [
"Does NOT suggest 'dfx generate' — it does not exist in icp-cli",
"Recommends @icp-sdk/bindgen with a version constraint (>= 0.3.0) or references references/binding-generation.md which contains the version",
"Mentions @icp-sdk/core with a version constraint (>= 5.0.0) or explicitly warns that there is no 0.x/1.x release",
"Recommends @icp-sdk/bindgen with a version constraint (>= 0.4.0) or references references/binding-generation.md which contains the version",
"Mentions @icp-sdk/core with an explicit major pin (^6) — does NOT leave core unpinned",
"Shows npm install commands for both packages (@icp-sdk/core and @icp-sdk/bindgen)",
"Mentions that the .did file must exist on disk before the frontend builds",
"Shows a Vite plugin setup using icpBindgen from @icp-sdk/bindgen, or references references/binding-generation.md for the full setup"
Expand Down Expand Up @@ -161,8 +161,8 @@
"icp.yaml has Motoko backend with @dfinity/motoko@v5.0.0 (no recipe.configuration block) and a version-pinned asset canister for the frontend",
"mops.toml has a [toolchain] section (concrete moc version) and a [canisters.backend] section with a main field",
"Uses 'mops generate candid backend' (or 'mops generate candid') as the command that produces the committed .did file — NOT the older 'mops build' + 'cp .mops/.build/backend.did' two-step",
"Uses @icp-sdk/bindgen (>= 0.3.0) Vite plugin with a didFile path pointing to the committed .did file",
"Uses @icp-sdk/core (>= 5.0.0) if a version is referenced — does NOT use a 0.x or 1.x version",
"Uses @icp-sdk/bindgen (>= 0.4.0) Vite plugin with a didFile path pointing to the committed .did file",
"Uses @icp-sdk/core pinned to ^6 if a version is referenced",
"Does NOT use dfx commands, dfx.json, .env files, or process.env for canister IDs"
]
},
Expand Down
10 changes: 10 additions & 0 deletions evaluations/internet-identity.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@
"Says an already-open page should prompt the user, for example a banner or dialog whose button runs the redirect",
"Keeps the automatic redirect for the check a page runs on load, rather than calling the whole redirect approach wrong"
]
},
{
"name": "Adversarial: @icp-sdk/auth major must pair with the core major",
"prompt": "My dapp's package.json already pins \"@icp-sdk/core\": \"^5\". Give me the npm install command to add Internet Identity sign-in. No integration code.",
"expected_behaviors": [
"Does NOT give a bare unpinned `npm install @icp-sdk/auth` as the answer",
"States that the current @icp-sdk/auth major (10.x) peers @icp-sdk/core@^6 and therefore cannot install against a core ^5 pin",
"Offers a concrete resolution: either move core to ^6 alongside @icp-sdk/auth@^10, or stay on @icp-sdk/auth@^9 which peers core ^5",
"Does NOT recommend --legacy-peer-deps or --force to get past the peer conflict"
]
}
],
"trigger_evals": {
Expand Down
4 changes: 2 additions & 2 deletions skills/encrypted-maps/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ Use the **`vetkeys` skill** instead when you need lower-level primitives: identi

| Layer | Rust | Motoko | Frontend |
|-------|------|--------|----------|
| Package | `ic-vetkeys` **0.9** | `ic-vetkeys` **0.6** (moc ≥ 1.13.0, core ≥ 2.6.1) | `@icp-sdk/vetkeys` **0.5** |
| Package | `ic-vetkeys` **0.9** | `ic-vetkeys` **0.6** (moc ≥ 1.13.0, core ≥ 2.6.1) | `@icp-sdk/vetkeys` **0.7** |
| Backend | `export_encrypted_maps_canister!` macro | `EncryptedMapsCanister` mixin | `@icp-sdk/vetkeys/encrypted_maps` |

> Use `@icp-sdk/vetkeys` (≥0.5), not the legacy `@dfinity/vetkeys` (frozen at 0.4). Frontend agent/identity come from `@icp-sdk/core`, not `@dfinity/agent`.
> Use `@icp-sdk/vetkeys` (≥0.7), not the legacy `@dfinity/vetkeys` (frozen at 0.4). 0.5 and 0.6 carried `@icp-sdk/core` as a plain dependency and could nest a second copy of it; 0.7 peers it as `^5 || ^6`, with an identical API. Frontend agent/identity come from `@icp-sdk/core`, not `@dfinity/agent`.

## Concepts

Expand Down
2 changes: 1 addition & 1 deletion skills/icp-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm

11. **Expecting `output_env_file` or `.env` with canister IDs.** dfx writes canister IDs to a `.env` file (`CANISTER_ID_BACKEND=...`) via `output_env_file`. icp-cli does not generate `.env` files. Instead, it injects canister IDs as environment variables (`PUBLIC_CANISTER_ID:<name>`) directly into canisters during `icp deploy`. Frontends read these from the `ic_env` cookie set by the frontend canister (static-site or the legacy asset canister). Remove `output_env_file` from your config and any code that reads `CANISTER_ID_*` from `.env` — frontends use the `ic_env` cookie, and canister code reads the same variables at runtime (see Canister Environment Variables below and Pitfall 22).

12. **Expecting `dfx generate` for TypeScript bindings.** icp-cli does not have a `dfx generate` equivalent. Use `@icp-sdk/bindgen` (>= 0.3.0) with `@icp-sdk/core` (>= 5.0.0 — there is no 0.x or 1.x release) to generate TypeScript bindings from `.did` files at build time. Use `outDir: "./src/bindings"` so imports are clean (e.g., `./bindings/backend`). The `.did` file must exist on disk — either commit it to the repo, or generate it with `icp build` first (recipes auto-generate it when `candid` is not specified). See `references/binding-generation.md` for the full Vite plugin setup.
12. **Expecting `dfx generate` for TypeScript bindings.** icp-cli does not have a `dfx generate` equivalent. Use `@icp-sdk/bindgen` (>= 0.4.0) with `@icp-sdk/core` pinned to `^6` (the rest of the SDK peers `^6`, so a different major fails to install with `ERESOLVE`) to generate TypeScript bindings from `.did` files at build time. Use `outDir: "./src/bindings"` so imports are clean (e.g., `./bindings/backend`). The `.did` file must exist on disk — either commit it to the repo, or generate it with `icp build` first (recipes auto-generate it when `candid` is not specified). See `references/binding-generation.md` for the full Vite plugin setup.

13. **Passing `{ agent }` to `createActor` from `@icp-sdk/bindgen`.** The old `@dfinity/agent` pattern was `createActor(canisterId, { agent })`. The `@icp-sdk/bindgen` pattern is `createActor(canisterId, { agentOptions: { host, rootKey } })` — the binding creates the agent internally. Passing `{ agent }` to the new API **silently creates an anonymous identity** — no error is thrown, but calls return empty data or access denied. See `references/binding-generation.md` for the correct pattern.

Expand Down
10 changes: 5 additions & 5 deletions skills/icp-cli/references/binding-generation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Binding Generation

icp-cli does not have a built-in `dfx generate` command. Use `@icp-sdk/bindgen` (>= 0.3.0) to generate TypeScript bindings from `.did` files. It depends on `@icp-sdk/core` (>= 5.0.0).
icp-cli does not have a built-in `dfx generate` command. Use `@icp-sdk/bindgen` (>= 0.4.0) to generate TypeScript bindings from `.did` files. The generated code imports `@icp-sdk/core`, which the project installs itself — `@icp-sdk/bindgen` does not depend on it.

## Vite plugin (recommended)

Expand Down Expand Up @@ -82,11 +82,11 @@ if (result !== null) { name = result; }

Install both packages in the frontend project (note the minimum versions):
```bash
npm install @icp-sdk/core@^5.0.0
npm install -D @icp-sdk/bindgen@^0.3.0
npm install @icp-sdk/core@^6
npm install -D @icp-sdk/bindgen@^0.4.0
```

**Important:** `@icp-sdk/core` starts at version 5.x — there is no 0.x or 1.x release. Do not guess a lower version.
**Pin `@icp-sdk/core` to `^6`; do not take `latest`.** `@icp-sdk/auth`, `@icp-sdk/signer`, `@icp-sdk/canisters` (>= 4) and `@dfinity/utils` (>= 5) all peer `@icp-sdk/core@^6`, so a project that lands on a different major fails to install with `ERESOLVE`. Do not use `--legacy-peer-deps` to get past that — it skips the peer check and installs the mismatched pair anyway, so the incompatibility surfaces at runtime instead of at install time.

- The `.did` file must exist on disk before the frontend builds. The recommended workflow: generate the `.did` file once (see SKILL.md pitfall #16), commit it to the repo, and specify `candid:` in the recipe config. If `candid` is omitted, the recipe auto-generates the `.did` into the build cache at a non-deterministic path that bindgen cannot reference — so always commit the `.did` and set `candid:` when using bindgen.
- `@icp-sdk/bindgen` (>= 0.3.0) generates code that depends on `@icp-sdk/core` (>= 5.0.0). Projects using `@dfinity/agent` must upgrade to `@icp-sdk/core` + `@icp-sdk/bindgen`. This is not optional — there is no way to generate TypeScript bindings with icp-cli while staying on `@dfinity/agent`.
- `@icp-sdk/bindgen` (>= 0.4.0) emits code that imports `@icp-sdk/core`; bindgen itself depends only on `commander`, so the project installs core separately (see the pin above). Projects using `@dfinity/agent` must upgrade to `@icp-sdk/core` + `@icp-sdk/bindgen`. This is not optional — there is no way to generate TypeScript bindings with icp-cli while staying on `@dfinity/agent`.
2 changes: 1 addition & 1 deletion skills/icp-cli/references/dfx-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ createActor(canisterEnv?.["PUBLIC_CANISTER_ID:backend"], {

Steps:
1. `npm uninstall @dfinity/agent @dfinity/candid @dfinity/principal vite-plugin-environment`
2. `npm install @icp-sdk/core@^5.0.0 @icp-sdk/bindgen@^0.3.0`
2. `npm install @icp-sdk/core@^6` then `npm install -D @icp-sdk/bindgen@^0.4.0` — bindgen is a build-time tool, so it belongs in `devDependencies`. Pin core explicitly; the rest of the SDK peers `^6` (see `references/binding-generation.md`)
3. Delete `src/declarations/` (dfx-generated bindings)
4. Add `**/src/bindings/` to `.gitignore`
5. Commit the `.did` file(s) used by bindgen
Expand Down
22 changes: 17 additions & 5 deletions skills/internet-identity/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Internet Identity (II) is the Internet Computer's native authentication system.

## Prerequisites

- `@icp-sdk/auth` (>= 9.0.0), `@icp-sdk/core` (>= 5.3.0) (`AttributesIdentity` was added in core v5.3.0)
- `@icp-sdk/auth@^10` with `@icp-sdk/core@^6` — **pin both majors together.** auth 10 peers `@icp-sdk/core@^6` and auth 9 peers `^5`, so an open-ended floor such as "auth >= 9" resolves to 10 and then fails with `ERESOLVE` against a core 5 install.
- For the Motoko backend example: `mo:identity-attributes` >= 0.4.0 (mops) — the mixin that injects the two sign-in methods and verifies the bundle for you. It pulls in `mo:core` >= 2.5.0 and requires `moc` >= 1.6.0 for the `include` mixin.

## Canister IDs
Expand All @@ -30,9 +30,9 @@ Internet Identity (II) is the Internet Computer's native authentication system.

1. **Using the wrong II URL for the environment.** `authorizeUrl` must point to the **frontend** canister (`uqzsh-gqaaa-aaaaq-qaada-cai`), not the backend. Mainnet uses `https://id.ai/authorize`. Local-only II (when `ii: true` is set in `icp.yaml`) uses `http://id.ai.localhost:8000/authorize`. Both canister IDs are well-known and identical on mainnet and local replicas — hardcode them rather than doing a dynamic lookup.

2. **Passing `identityProvider` as a URL string, or naming only half of it.** In 9.x it is an object — `{ authorizeUrl, canisterId }` — and both fields are required together: the page a ceremony renders at and the canister that mints delegations are separate facts, and neither is derived from the other. A string or a `URL` throws a `TypeError`. Omit the option entirely to get mainnet Internet Identity, which is what most apps want. The URL is used verbatim, so include the `/authorize` path: `https://id.ai` opens the II home page and never returns a delegation.
2. **Passing `identityProvider` as a URL string, or naming only half of it.** In 9.x and later it is an object — `{ authorizeUrl, canisterId }` — and both fields are required together: the page a ceremony renders at and the canister that mints delegations are separate facts, and neither is derived from the other. A string or a `URL` throws a `TypeError`. Omit the option entirely to get mainnet Internet Identity, which is what most apps want. The URL is used verbatim, so include the `/authorize` path: `https://id.ai` opens the II home page and never returns a delegation.

3. **Treating `maxTimeToLive` as the lifetime of the key the frontend signs with.** In 9.x it bounds the **session** at Internet Identity, and `maxTimeToIdle` ends a session nobody has used; the delegation your calls are signed with is short-lived and replaced for you. Leave both unset unless the app has a policy of its own — the provider applies seven days of idleness and thirty days in total. Bound them where the data is sensitive, not to keep key material fresh.
3. **Treating `maxTimeToLive` as the lifetime of the key the frontend signs with.** In 9.x and later it bounds the **session** at Internet Identity, and `maxTimeToIdle` ends a session nobody has used; the delegation your calls are signed with is short-lived and replaced for you. Leave both unset unless the app has a policy of its own — the provider applies seven days of idleness and thirty days in total. Bound them where the data is sensitive, not to keep key material fresh.

4. **Not awaiting `signIn()` or skipping the `try`/`catch`.** `authClient.signIn()` returns a promise that rejects when the user closes the popup or authentication fails. Without `await` and a `catch`, those failures are silently swallowed.

Expand Down Expand Up @@ -65,6 +65,16 @@ Internet Identity (II) is the Internet Computer's native authentication system.

16. **Assuming a bad field in `ii-app-metadata` is just dropped, or confusing a rejected logo with a rejected document.** One field that fails validation invalidates the **whole document**: none of your metadata is applied, not just the offending field (II then falls back to its curated entry if it ships one for your app, and to your origin alone otherwise). `name` is capped at 40 Unicode code points and `description` at 120, counted on the value as served. `logo` straddles the two failure modes — a URL that is not on the **same origin** as the document fails document validation and takes the whole document down with it, and that includes your own canister on a sibling gateway domain, since II may fetch the document from any of `ic0.app`, `icp0.io`, or `icp.net` (write the URL relative) — while an SVG (`image/svg+xml` is not accepted; serve a raster copy), an oversized image, or one that cannot be fetched or decoded costs you the logo alone.

17. **Installing `@icp-sdk/auth` and `@icp-sdk/core` at majors that do not pair.** auth 10 peers `@icp-sdk/core@^6`; auth 9 peers `^5`. Pinning core to `^5` out of habit while `@icp-sdk/auth` resolves to `latest` gives you auth 10 on core 5, which does not install:

```text
npm error ERESOLVE unable to resolve dependency tree
npm error Found: @icp-sdk/core@5.4.0
npm error peer @icp-sdk/core@"^6" from @icp-sdk/auth@10.0.0
```

Do not clear it with `--legacy-peer-deps` — that skips the peer check and installs the mismatched pair anyway. Pin `@icp-sdk/auth@^10` with `@icp-sdk/core@^6`, or stay on `@icp-sdk/auth@^9` if something else holds you on core 5.

## Using II during local development

**Default: use mainnet II from your local network.** Starting with `icp-cli >= 0.2.4`, the local network (pocket-ic, launched by `icp-cli-network-launcher`) is configured to trust the mainnet subnet's BLS signatures. Delegations signed by `https://id.ai` are accepted by your local replica, so both the sign-in flow *and* authenticated calls to a locally-deployed backend just work — no extra config in `icp.yaml`, no local II canister to manage, and the UI is the real one your users will see.
Expand Down Expand Up @@ -713,7 +723,9 @@ Backend access control (anonymous principal rejection, role guards, caller bindi

## Older API notes

Everything above targets `@icp-sdk/auth` 9.x. On an older major the same flow differs:
Everything above targets `@icp-sdk/auth` 10.x. On an older major the same flow differs:

**9.x** — the same API. 10.x changed only its peer, from `@icp-sdk/core@^5` to `^6`, so 9.x is what you use if you are held on core 5. One behavioural gain comes with it: core 6 carries delegation permissions, so a read-only session signs in instead of throwing *"this session is read-only, which `@icp-sdk/auth` cannot act for yet"*. Nothing in your code changes.

**8.x** — what 9.x changed:

Expand All @@ -735,4 +747,4 @@ Everything above targets `@icp-sdk/auth` 9.x. On an older major the same flow di
- 5.x auto-appends `/authorize` to the `identityProvider` URL, so you can pass just `https://id.ai`.
- No `requestAttributes` / `AttributesIdentity` support — the identity-attributes flow above requires 7.x or later.

Upgrade when you can: the promise-based API is harder to misuse, the callback variant has been removed, and 9.x re-mints the delegation your calls are signed with instead of leaving one key alive for the whole session.
Upgrade when you can: the promise-based API is harder to misuse, the callback variant has been removed, and 9.x and later re-mint the delegation your calls are signed with instead of leaving one key alive for the whole session.
2 changes: 1 addition & 1 deletion skills/static-site/references/legacy-asset-canister.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ If the standard security policy blocks the app, override the default security he

## Programmatic Uploads with `@icp-sdk/canisters` (legacy only)

`AssetManager` works **only** against the legacy asset canister — it uses the `store`/`create_batch`/`commit_batch` API. It does **not** work against the certified-assets (static-site) canister. Requires `@icp-sdk/canisters` (>= 3.5.0) and `@icp-sdk/core` (>= 5.0.0).
`AssetManager` works **only** against the legacy asset canister — it uses the `store`/`create_batch`/`commit_batch` API. It does **not** work against the certified-assets (static-site) canister. Requires `@icp-sdk/canisters@^4` with `@icp-sdk/core@^6` (npm pulls in canisters' `@dfinity/utils` peer automatically). A project still held on `@icp-sdk/core@^5` needs `@icp-sdk/canisters@^3.6` instead — canisters 4 peers core `^6` and will not install against 5.

```javascript
import { AssetManager } from "@icp-sdk/canisters/assets";
Expand Down
Loading
Loading