From 7ced86697c0d56d5f1dcec66b6e6f36dbaa10615 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 11:59:43 +0200 Subject: [PATCH 01/55] feat: add Codespaces support to who_am_i examples (pilot) Updates both Motoko and Rust who_am_i examples to use GitHub Codespaces instead of ICP Ninja for the one-click browser experience. devcontainer.json changes: - Image: marc0olo/icp-dev-env-motoko:dev / icp-dev-env-rust:dev - workspaceFolder: opens VS Code directly in the example directory - Port 8000 (icp-cli gateway) replaces port 4943 (dfx) - postStartCommand: icp network start -d (auto-starts on every resume) - postCreateCommand: mops install (Motoko only) - Adds stateful.runme extension for interactive README buttons README changes: - Replaces ICP Ninja badge with Open in GitHub Codespaces badge - Adds note that authentication uses production id.ai (PocketIC accepts mainnet signatures, so no local II needed) - Adds Runme action buttons: deploy, frontend, reset-deploy, info - Removes icp network stop (lifecycle handled by Codespace suspension) - Adds mainnet guidance section See dfinity/examples#1345 Co-Authored-By: Claude Sonnet 4.6 --- .../who_am_i/.devcontainer/devcontainer.json | 20 ++++--- motoko/who_am_i/README.md | 54 +++++++++++++------ rust/who_am_i/.devcontainer/devcontainer.json | 19 ++++--- rust/who_am_i/README.md | 48 +++++++++++------ 4 files changed, 97 insertions(+), 44 deletions(-) diff --git a/motoko/who_am_i/.devcontainer/devcontainer.json b/motoko/who_am_i/.devcontainer/devcontainer.json index ebb0b8bcc..7ccc8cad7 100644 --- a/motoko/who_am_i/.devcontainer/devcontainer.json +++ b/motoko/who_am_i/.devcontainer/devcontainer.json @@ -1,20 +1,26 @@ { - "name": "ICP Dev Environment", - "image": "ghcr.io/dfinity/icp-dev-env-slim:22", - "forwardPorts": [4943, 5173], + "name": "Who Am I? (Motoko)", + "image": "ghcr.io/marc0olo/icp-dev-env-motoko:dev", + "workspaceFolder": "/workspaces/examples/motoko/who_am_i", + "forwardPorts": [8000, 5173], "portsAttributes": { - "4943": { - "label": "dfx", + "8000": { + "label": "ICP local network", "onAutoForward": "ignore" }, "5173": { - "label": "vite", + "label": "Frontend", "onAutoForward": "openBrowser" } }, + "postCreateCommand": "mops install", + "postStartCommand": "icp network start -d", "customizations": { "vscode": { - "extensions": ["dfinity-foundation.vscode-motoko"] + "extensions": [ + "dfinity-foundation.vscode-motoko", + "stateful.runme" + ] } } } diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index f64514f10..f650cc90c 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -6,13 +6,42 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Every entity, such as a user or canister smart contract, has a principal identifier. Principals can be used for identification and authentication. Who am I? uses Internet Identity (II) for user authentication, then displays the principal identifier associated with that Internet Identity on the user interface. -## Deploying from ICP Ninja +## Try in browser -This example can be deployed directly from [ICP Ninja](https://icp.ninja), a browser-based IDE for ICP. To continue developing locally after deploying from ICP Ninja, see [BUILD.md](BUILD.md). +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=motoko%2Fwho_am_i%2F.devcontainer%2Fdevcontainer.json&ref=feat%2Fcodespaces) -[![Open in ICP Ninja](https://icp.ninja/assets/open.svg)](https://icp.ninja/i?g=https://github.com/dfinity/examples/motoko/who_am_i) +Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. -> **Note:** ICP Ninja currently uses `dfx` under the hood, which is why this example includes a `dfx.json` configuration file. `dfx` is the legacy CLI, being superseded by [icp-cli](https://cli.internetcomputer.org), which is what developers should use for local development. +> **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. + +## Local development + +The local ICP network starts automatically when this Codespace opens. After deploying, start the frontend dev server and open the preview URL. + +**Install dependencies** +```sh { name=install } +mops install +``` + +**Deploy** +```sh { name=deploy } +icp deploy +``` + +**Start frontend** +```sh { name=frontend } +npm run dev +``` + +**Reset & redeploy** *(wipes all canister state)* +```sh { name=reset-deploy } +icp deploy --mode reinstall -y +``` + +**Show canister info** +```sh { name=info } +icp environment +``` ## Build and deploy from the command line @@ -30,25 +59,20 @@ git clone https://github.com/dfinity/examples cd examples/motoko/who_am_i ``` -### Deployment +### Deploy -Start the local network: +Start the local network and deploy: ```bash icp network start -d -``` - -Deploy the canisters: - -```bash +mops install icp deploy +npm run dev ``` -Stop the local network when done: +## Ready to deploy on mainnet? -```bash -icp network stop -``` +Codespaces is ideal for learning and local experimentation. When you're ready for mainnet, [install icp-cli locally](https://cli.internetcomputer.org) and follow the [mainnet deployment guide](https://cli.internetcomputer.org/0.2/guides/deploying-to-mainnet.md). Mainnet requires ICP tokens and cycles — managing identities securely is much better from your own machine. ## Updating the Candid interface diff --git a/rust/who_am_i/.devcontainer/devcontainer.json b/rust/who_am_i/.devcontainer/devcontainer.json index ebb0b8bcc..823b1d131 100644 --- a/rust/who_am_i/.devcontainer/devcontainer.json +++ b/rust/who_am_i/.devcontainer/devcontainer.json @@ -1,20 +1,25 @@ { - "name": "ICP Dev Environment", - "image": "ghcr.io/dfinity/icp-dev-env-slim:22", - "forwardPorts": [4943, 5173], + "name": "Who Am I? (Rust)", + "image": "ghcr.io/marc0olo/icp-dev-env-rust:dev", + "workspaceFolder": "/workspaces/examples/rust/who_am_i", + "forwardPorts": [8000, 5173], "portsAttributes": { - "4943": { - "label": "dfx", + "8000": { + "label": "ICP local network", "onAutoForward": "ignore" }, "5173": { - "label": "vite", + "label": "Frontend", "onAutoForward": "openBrowser" } }, + "postStartCommand": "icp network start -d", "customizations": { "vscode": { - "extensions": ["dfinity-foundation.vscode-motoko"] + "extensions": [ + "rust-analyzer", + "stateful.runme" + ] } } } diff --git a/rust/who_am_i/README.md b/rust/who_am_i/README.md index 236aa318e..52cb1198a 100644 --- a/rust/who_am_i/README.md +++ b/rust/who_am_i/README.md @@ -6,13 +6,37 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Every entity, such as a user or canister smart contract, has a principal identifier. Principals can be used for identification and authentication. Who am I? uses Internet Identity (II) for user authentication, then displays the principal identifier associated with that Internet Identity on the user interface. -## Deploying from ICP Ninja +## Try in browser -This example can be deployed directly from [ICP Ninja](https://icp.ninja), a browser-based IDE for ICP. To continue developing locally after deploying from ICP Ninja, see [BUILD.md](BUILD.md). +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=rust%2Fwho_am_i%2F.devcontainer%2Fdevcontainer.json&ref=feat%2Fcodespaces) -[![Open in ICP Ninja](https://icp.ninja/assets/open.svg)](https://icp.ninja/i?g=https://github.com/dfinity/examples/rust/who_am_i) +Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. -> **Note:** ICP Ninja currently uses `dfx` under the hood, which is why this example includes a `dfx.json` configuration file. `dfx` is the legacy CLI, being superseded by [icp-cli](https://cli.internetcomputer.org), which is what developers should use for local development. +> **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. + +## Local development + +The local ICP network starts automatically when this Codespace opens. After deploying, start the frontend dev server and open the preview URL. + +**Deploy** +```sh { name=deploy } +icp deploy +``` + +**Start frontend** +```sh { name=frontend } +npm run dev +``` + +**Reset & redeploy** *(wipes all canister state)* +```sh { name=reset-deploy } +icp deploy --mode reinstall -y +``` + +**Show canister info** +```sh { name=info } +icp environment +``` ## Build and deploy from the command line @@ -30,25 +54,19 @@ git clone https://github.com/dfinity/examples cd examples/rust/who_am_i ``` -### Deployment +### Deploy -Start the local network: +Start the local network and deploy: ```bash icp network start -d -``` - -Deploy the canisters: - -```bash icp deploy +npm run dev ``` -Stop the local network when done: +## Ready to deploy on mainnet? -```bash -icp network stop -``` +Codespaces is ideal for learning and local experimentation. When you're ready for mainnet, [install icp-cli locally](https://cli.internetcomputer.org) and follow the [mainnet deployment guide](https://cli.internetcomputer.org/0.2/guides/deploying-to-mainnet.md). Mainnet requires ICP tokens and cycles — managing identities securely is much better from your own machine. ## Updating the Candid interface From 3f0a8fb8632e230f0c788b3b364ed6647850aaf0 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 12:23:30 +0200 Subject: [PATCH 02/55] fix: add postAttachCommand to open README with Runme buttons on attach Adds `"postAttachCommand": "code README.md"` to both who_am_i devcontainer configs so the README with Runme action buttons opens automatically every time a Codespace is attached. Also adds a resume note to the Motoko README pointing users to github.com/codespaces. Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/.devcontainer/devcontainer.json | 1 + motoko/who_am_i/README.md | 2 +- rust/who_am_i/.devcontainer/devcontainer.json | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/motoko/who_am_i/.devcontainer/devcontainer.json b/motoko/who_am_i/.devcontainer/devcontainer.json index 7ccc8cad7..e0f993d76 100644 --- a/motoko/who_am_i/.devcontainer/devcontainer.json +++ b/motoko/who_am_i/.devcontainer/devcontainer.json @@ -15,6 +15,7 @@ }, "postCreateCommand": "mops install", "postStartCommand": "icp network start -d", + "postAttachCommand": "code README.md", "customizations": { "vscode": { "extensions": [ diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index f650cc90c..842093f40 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -10,7 +10,7 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Eve [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=motoko%2Fwho_am_i%2F.devcontainer%2Fdevcontainer.json&ref=feat%2Fcodespaces) -Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. +Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. To resume an existing Codespace, go to [github.com/codespaces](https://github.com/codespaces). > **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. diff --git a/rust/who_am_i/.devcontainer/devcontainer.json b/rust/who_am_i/.devcontainer/devcontainer.json index 823b1d131..bc09904ef 100644 --- a/rust/who_am_i/.devcontainer/devcontainer.json +++ b/rust/who_am_i/.devcontainer/devcontainer.json @@ -14,6 +14,7 @@ } }, "postStartCommand": "icp network start -d", + "postAttachCommand": "code README.md", "customizations": { "vscode": { "extensions": [ From 10d68bdc19dc7c4d2ca4f5ee7df4024457941a56 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 12:27:26 +0200 Subject: [PATCH 03/55] docs: improve Codespace resume guidance in both who_am_i READMEs Clarifies that returning users should look for the "Open existing codespace" banner on the creation page, and adds the github.com/codespaces link to the Rust README (was already in Motoko). Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/README.md | 2 +- rust/who_am_i/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index 842093f40..63864a8ae 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -10,7 +10,7 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Eve [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=motoko%2Fwho_am_i%2F.devcontainer%2Fdevcontainer.json&ref=feat%2Fcodespaces) -Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. To resume an existing Codespace, go to [github.com/codespaces](https://github.com/codespaces). +Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. If you already have a Codespace for this example, the creation page will show an **"Open existing codespace"** option at the top — use that to resume. You can also browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). > **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. diff --git a/rust/who_am_i/README.md b/rust/who_am_i/README.md index 52cb1198a..f949ab2cb 100644 --- a/rust/who_am_i/README.md +++ b/rust/who_am_i/README.md @@ -10,7 +10,7 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Eve [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=rust%2Fwho_am_i%2F.devcontainer%2Fdevcontainer.json&ref=feat%2Fcodespaces) -Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. +Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. If you already have a Codespace for this example, the creation page will show an **"Open existing codespace"** option at the top — use that to resume. You can also browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). > **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. From ac9b17fa3dca241f1b4f4db41462c1c9c2b408a6 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 12:40:46 +0200 Subject: [PATCH 04/55] fix: use code -r to scope VS Code window to example subdirectory workspaceFolder doesn't scope the Explorer in Codespaces; code -r reopens the current window rooted at the example folder, fixing both the sidebar view and the terminal working directory. Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/.devcontainer/devcontainer.json | 2 +- rust/who_am_i/.devcontainer/devcontainer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/motoko/who_am_i/.devcontainer/devcontainer.json b/motoko/who_am_i/.devcontainer/devcontainer.json index e0f993d76..ae1fd9b71 100644 --- a/motoko/who_am_i/.devcontainer/devcontainer.json +++ b/motoko/who_am_i/.devcontainer/devcontainer.json @@ -15,7 +15,7 @@ }, "postCreateCommand": "mops install", "postStartCommand": "icp network start -d", - "postAttachCommand": "code README.md", + "postAttachCommand": "code -r /workspaces/examples/motoko/who_am_i", "customizations": { "vscode": { "extensions": [ diff --git a/rust/who_am_i/.devcontainer/devcontainer.json b/rust/who_am_i/.devcontainer/devcontainer.json index bc09904ef..817f9d24e 100644 --- a/rust/who_am_i/.devcontainer/devcontainer.json +++ b/rust/who_am_i/.devcontainer/devcontainer.json @@ -14,7 +14,7 @@ } }, "postStartCommand": "icp network start -d", - "postAttachCommand": "code README.md", + "postAttachCommand": "code -r /workspaces/examples/rust/who_am_i", "customizations": { "vscode": { "extensions": [ From f27b777b3a97545f3328323c4191714d82c6c44b Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 12:45:24 +0200 Subject: [PATCH 05/55] fix: move Codespaces devcontainers to repo root .devcontainer/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub Codespaces only discovers devcontainer.json under .devcontainer/ at the repo root — per-example subdirectory .devcontainer/ folders are silently ignored, causing the devcontainer_path URL param to be discarded. Adds root-level .devcontainer/motoko-who-am-i/ and /rust-who-am-i/ configs and updates the Codespaces badge URLs in both READMEs accordingly. The per-example .devcontainer/ files are kept for local dev container use. Co-Authored-By: Claude Sonnet 4.6 --- .../motoko-who-am-i/devcontainer.json | 27 +++++++++++++++++++ .devcontainer/rust-who-am-i/devcontainer.json | 26 ++++++++++++++++++ motoko/who_am_i/README.md | 2 +- rust/who_am_i/README.md | 2 +- 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 .devcontainer/motoko-who-am-i/devcontainer.json create mode 100644 .devcontainer/rust-who-am-i/devcontainer.json diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json new file mode 100644 index 000000000..ae1fd9b71 --- /dev/null +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -0,0 +1,27 @@ +{ + "name": "Who Am I? (Motoko)", + "image": "ghcr.io/marc0olo/icp-dev-env-motoko:dev", + "workspaceFolder": "/workspaces/examples/motoko/who_am_i", + "forwardPorts": [8000, 5173], + "portsAttributes": { + "8000": { + "label": "ICP local network", + "onAutoForward": "ignore" + }, + "5173": { + "label": "Frontend", + "onAutoForward": "openBrowser" + } + }, + "postCreateCommand": "mops install", + "postStartCommand": "icp network start -d", + "postAttachCommand": "code -r /workspaces/examples/motoko/who_am_i", + "customizations": { + "vscode": { + "extensions": [ + "dfinity-foundation.vscode-motoko", + "stateful.runme" + ] + } + } +} diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json new file mode 100644 index 000000000..817f9d24e --- /dev/null +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -0,0 +1,26 @@ +{ + "name": "Who Am I? (Rust)", + "image": "ghcr.io/marc0olo/icp-dev-env-rust:dev", + "workspaceFolder": "/workspaces/examples/rust/who_am_i", + "forwardPorts": [8000, 5173], + "portsAttributes": { + "8000": { + "label": "ICP local network", + "onAutoForward": "ignore" + }, + "5173": { + "label": "Frontend", + "onAutoForward": "openBrowser" + } + }, + "postStartCommand": "icp network start -d", + "postAttachCommand": "code -r /workspaces/examples/rust/who_am_i", + "customizations": { + "vscode": { + "extensions": [ + "rust-analyzer", + "stateful.runme" + ] + } + } +} diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index 63864a8ae..ebdd5693f 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -8,7 +8,7 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Eve ## Try in browser -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=motoko%2Fwho_am_i%2F.devcontainer%2Fdevcontainer.json&ref=feat%2Fcodespaces) +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=.devcontainer%2Fmotoko-who-am-i%2Fdevcontainer.json&ref=feat%2Fcodespaces) Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. If you already have a Codespace for this example, the creation page will show an **"Open existing codespace"** option at the top — use that to resume. You can also browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). diff --git a/rust/who_am_i/README.md b/rust/who_am_i/README.md index f949ab2cb..285bd9463 100644 --- a/rust/who_am_i/README.md +++ b/rust/who_am_i/README.md @@ -8,7 +8,7 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Eve ## Try in browser -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=rust%2Fwho_am_i%2F.devcontainer%2Fdevcontainer.json&ref=feat%2Fcodespaces) +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=.devcontainer%2Frust-who-am-i%2Fdevcontainer.json&ref=feat%2Fcodespaces) Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. If you already have a Codespace for this example, the creation page will show an **"Open existing codespace"** option at the top — use that to resume. You can also browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). From 0bb4e035de1756859d2b969a7d79aec11593f2b8 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 12:52:53 +0200 Subject: [PATCH 06/55] fix: replace code -r with code README.md, suppress noisy notifications - Removes code -r (caused double window reload since workspaceFolder now works correctly once devcontainer is properly discovered) - Opens README.md on attach so Runme buttons are immediately visible - Suppresses port 7865 (PocketIC internal port) auto-forward notification - Sets git.openRepositoryInParentFolders=always to avoid parent-repo prompt Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/motoko-who-am-i/devcontainer.json | 10 ++++++++-- .devcontainer/rust-who-am-i/devcontainer.json | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index ae1fd9b71..a27e1874d 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -11,17 +11,23 @@ "5173": { "label": "Frontend", "onAutoForward": "openBrowser" + }, + "7865": { + "onAutoForward": "ignore" } }, "postCreateCommand": "mops install", "postStartCommand": "icp network start -d", - "postAttachCommand": "code -r /workspaces/examples/motoko/who_am_i", + "postAttachCommand": "code README.md", "customizations": { "vscode": { "extensions": [ "dfinity-foundation.vscode-motoko", "stateful.runme" - ] + ], + "settings": { + "git.openRepositoryInParentFolders": "always" + } } } } diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 817f9d24e..87075f691 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -11,16 +11,22 @@ "5173": { "label": "Frontend", "onAutoForward": "openBrowser" + }, + "7865": { + "onAutoForward": "ignore" } }, "postStartCommand": "icp network start -d", - "postAttachCommand": "code -r /workspaces/examples/rust/who_am_i", + "postAttachCommand": "code README.md", "customizations": { "vscode": { "extensions": [ "rust-analyzer", "stateful.runme" - ] + ], + "settings": { + "git.openRepositoryInParentFolders": "always" + } } } } From ecef03c786accfb41c054fe32d55a3fc49a7b343 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 13:26:51 +0200 Subject: [PATCH 07/55] chore: remove per-example .devcontainer directories Root-level .devcontainer// configs handle both Codespaces and local dev container use. Per-example configs are redundant and create maintenance overhead across 46+ examples. Co-Authored-By: Claude Sonnet 4.6 --- .../who_am_i/.devcontainer/devcontainer.json | 27 ------------------- rust/who_am_i/.devcontainer/devcontainer.json | 26 ------------------ 2 files changed, 53 deletions(-) delete mode 100644 motoko/who_am_i/.devcontainer/devcontainer.json delete mode 100644 rust/who_am_i/.devcontainer/devcontainer.json diff --git a/motoko/who_am_i/.devcontainer/devcontainer.json b/motoko/who_am_i/.devcontainer/devcontainer.json deleted file mode 100644 index ae1fd9b71..000000000 --- a/motoko/who_am_i/.devcontainer/devcontainer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "Who Am I? (Motoko)", - "image": "ghcr.io/marc0olo/icp-dev-env-motoko:dev", - "workspaceFolder": "/workspaces/examples/motoko/who_am_i", - "forwardPorts": [8000, 5173], - "portsAttributes": { - "8000": { - "label": "ICP local network", - "onAutoForward": "ignore" - }, - "5173": { - "label": "Frontend", - "onAutoForward": "openBrowser" - } - }, - "postCreateCommand": "mops install", - "postStartCommand": "icp network start -d", - "postAttachCommand": "code -r /workspaces/examples/motoko/who_am_i", - "customizations": { - "vscode": { - "extensions": [ - "dfinity-foundation.vscode-motoko", - "stateful.runme" - ] - } - } -} diff --git a/rust/who_am_i/.devcontainer/devcontainer.json b/rust/who_am_i/.devcontainer/devcontainer.json deleted file mode 100644 index 817f9d24e..000000000 --- a/rust/who_am_i/.devcontainer/devcontainer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Who Am I? (Rust)", - "image": "ghcr.io/marc0olo/icp-dev-env-rust:dev", - "workspaceFolder": "/workspaces/examples/rust/who_am_i", - "forwardPorts": [8000, 5173], - "portsAttributes": { - "8000": { - "label": "ICP local network", - "onAutoForward": "ignore" - }, - "5173": { - "label": "Frontend", - "onAutoForward": "openBrowser" - } - }, - "postStartCommand": "icp network start -d", - "postAttachCommand": "code -r /workspaces/examples/rust/who_am_i", - "customizations": { - "vscode": { - "extensions": [ - "rust-analyzer", - "stateful.runme" - ] - } - } -} From 6256a86d95a6b2f3c70cd45e76cd7d96d32cf62f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:00:16 +0200 Subject: [PATCH 08/55] fix: Codespaces DX improvements for who_am_i examples - Fix ICP gateway rejecting API calls from Codespaces: override origin header in Vite proxy to localhost:8000 so the network launcher accepts requests from non-localhost forwarded domains - Add "Show URLs" Runme cell that constructs correct Codespaces-aware frontend and Candid UI URLs using $CODESPACE_NAME - Rename "Local development" section to "Codespace actions" and remove redundant "Install dependencies" cell (handled by postCreateCommand) - Set workbench.editorAssociations to open .md files in Runme directly, preventing the double README tab (preview + notebook) Co-Authored-By: Claude Sonnet 4.6 --- .../motoko-who-am-i/devcontainer.json | 5 ++++- .devcontainer/rust-who-am-i/devcontainer.json | 5 ++++- motoko/who_am_i/README.md | 22 +++++++++++++------ .../vite.config.js | 8 ++++++- rust/who_am_i/README.md | 4 ++-- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index a27e1874d..6a302ee95 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -26,7 +26,10 @@ "stateful.runme" ], "settings": { - "git.openRepositoryInParentFolders": "always" + "git.openRepositoryInParentFolders": "always", + "workbench.editorAssociations": { + "*.md": "runme" + } } } } diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 87075f691..748a1d55c 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -25,7 +25,10 @@ "stateful.runme" ], "settings": { - "git.openRepositoryInParentFolders": "always" + "git.openRepositoryInParentFolders": "always", + "workbench.editorAssociations": { + "*.md": "runme" + } } } } diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index ebdd5693f..d2fd0538c 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -14,14 +14,9 @@ Opens a pre-configured environment with the ICP toolchain installed and the loca > **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. -## Local development +## Codespace actions -The local ICP network starts automatically when this Codespace opens. After deploying, start the frontend dev server and open the preview URL. - -**Install dependencies** -```sh { name=install } -mops install -``` +The local ICP network starts automatically when this Codespace opens. Run each step below in order. **Deploy** ```sh { name=deploy } @@ -33,6 +28,19 @@ icp deploy npm run dev ``` +**Show URLs** *(after deploying)* +```sh { name=urls } +BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null || echo "not deployed") +FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null || echo "not deployed") +if [ -n "$CODESPACE_NAME" ]; then + echo "Frontend app: https://${CODESPACE_NAME}-5173.app.github.dev" + echo "Candid UI: https://${CODESPACE_NAME}-8000.app.github.dev/?id=${BACKEND_ID}" +else + echo "Frontend app: http://localhost:5173" + echo "Candid UI: http://localhost:8000/?id=${BACKEND_ID}" +fi +``` + **Reset & redeploy** *(wipes all canister state)* ```sh { name=reset-deploy } icp deploy --mode reinstall -y diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js b/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js index 41d03427c..9d0e17f6c 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js +++ b/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js @@ -24,7 +24,13 @@ function getDevServerConfig() { )}; SameSite=Lax;`, }, proxy: { - "/api": { target: "http://127.0.0.1:8000", changeOrigin: true }, + "/api": { + target: "http://127.0.0.1:8000", + changeOrigin: true, + // The ICP gateway rejects non-localhost Origin headers. Override it so + // requests proxied from Codespaces (or any non-localhost dev URL) are accepted. + headers: { origin: "http://localhost:8000" }, + }, }, }; } catch {} diff --git a/rust/who_am_i/README.md b/rust/who_am_i/README.md index 285bd9463..8ba283e45 100644 --- a/rust/who_am_i/README.md +++ b/rust/who_am_i/README.md @@ -14,9 +14,9 @@ Opens a pre-configured environment with the ICP toolchain installed and the loca > **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. -## Local development +## Codespace actions -The local ICP network starts automatically when this Codespace opens. After deploying, start the frontend dev server and open the preview URL. +The local ICP network starts automatically when this Codespace opens. Run each step below in order. **Deploy** ```sh { name=deploy } From 871cfe598842907751c9e9e1fbd98458531cecf9 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:32:20 +0200 Subject: [PATCH 09/55] fix: route agent to port 8000 in Codespaces, use ?canisterId= URLs - actor.js: detect .app.github.dev and route API calls directly to the port-8000 forwarded URL, bypassing the Vite proxy host check - README: reorder Codespace actions (Show URLs before dev server), use ?canisterId= query-param routing (works with Codespaces port forwarding), derive Candid UI URL from icp network status --json, remove icp environment cell Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/README.md | 24 ++++++++----------- .../src/actor.js | 14 ++++++++++- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index d2fd0538c..88f5ea4f3 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -23,22 +23,23 @@ The local ICP network starts automatically when this Codespace opens. Run each s icp deploy ``` -**Start frontend** -```sh { name=frontend } -npm run dev -``` - **Show URLs** *(after deploying)* ```sh { name=urls } BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null || echo "not deployed") FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null || echo "not deployed") +CANDID_UI_ID=$(icp network status --json | jq -r '.candid_ui_principal') if [ -n "$CODESPACE_NAME" ]; then - echo "Frontend app: https://${CODESPACE_NAME}-5173.app.github.dev" - echo "Candid UI: https://${CODESPACE_NAME}-8000.app.github.dev/?id=${BACKEND_ID}" + BASE="https://${CODESPACE_NAME}-8000.app.github.dev" else - echo "Frontend app: http://localhost:5173" - echo "Candid UI: http://localhost:8000/?id=${BACKEND_ID}" + BASE="http://localhost:8000" fi +echo "Frontend: ${BASE}/?canisterId=${FRONTEND_ID}" +echo "Candid UI: ${BASE}/?canisterId=${CANDID_UI_ID}&id=${BACKEND_ID}" +``` + +**Start dev server** *(optional — for frontend development)* +```sh { name=frontend } +npm run dev ``` **Reset & redeploy** *(wipes all canister state)* @@ -46,11 +47,6 @@ fi icp deploy --mode reinstall -y ``` -**Show canister info** -```sh { name=info } -icp environment -``` - ## Build and deploy from the command line ### Prerequisites diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js b/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js index becf24513..0536211a0 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js +++ b/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js @@ -13,8 +13,20 @@ if (!canisterId) { ); } +// In GitHub Codespaces the Vite dev server runs on port 5173, but the ICP +// gateway only accepts requests whose Host matches its own port (8000). Derive +// the port-8000 forwarded URL from the current hostname so API calls bypass +// the Vite proxy and reach the gateway directly. +function getNetworkHost() { + const { hostname, origin } = window.location; + if (hostname.endsWith(".app.github.dev")) { + return "https://" + hostname.replace(/-\d+\.app\.github\.dev$/, "-8000.app.github.dev"); + } + return origin; +} + const agentOptions = { - host: window.location.origin, + host: getNetworkHost(), rootKey: canisterEnv?.IC_ROOT_KEY, }; From 56c9881f8f624ea61777b838032f2538a063ec78 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:37:20 +0200 Subject: [PATCH 10/55] feat: auto-deploy and open frontend URL on Codespace attach Add postAttach.sh scripts for both who_am_i devcontainers: - deploys canisters on first attach (guarded by canister status check, skipped on reconnects) - prints Frontend + Candid UI URLs to terminal - opens frontend in browser via `code --open-url` - opens README in editor Update README to reflect automatic deploy/open behaviour; simplify Codespace actions section accordingly. Co-Authored-By: Claude Sonnet 4.6 --- .../motoko-who-am-i/devcontainer.json | 2 +- .devcontainer/motoko-who-am-i/postAttach.sh | 29 +++++++++++++++++++ .devcontainer/rust-who-am-i/devcontainer.json | 2 +- .devcontainer/rust-who-am-i/postAttach.sh | 29 +++++++++++++++++++ motoko/who_am_i/README.md | 21 +------------- 5 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 .devcontainer/motoko-who-am-i/postAttach.sh create mode 100644 .devcontainer/rust-who-am-i/postAttach.sh diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index 6a302ee95..cefc64f0c 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -18,7 +18,7 @@ }, "postCreateCommand": "mops install", "postStartCommand": "icp network start -d", - "postAttachCommand": "code README.md", + "postAttachCommand": "bash /workspaces/examples/.devcontainer/motoko-who-am-i/postAttach.sh", "customizations": { "vscode": { "extensions": [ diff --git a/.devcontainer/motoko-who-am-i/postAttach.sh b/.devcontainer/motoko-who-am-i/postAttach.sh new file mode 100644 index 000000000..007f77ca2 --- /dev/null +++ b/.devcontainer/motoko-who-am-i/postAttach.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Deploy if not already deployed (skipped on reconnects) +if ! icp canister status internet_identity_app_backend -i > /dev/null 2>&1; then + echo "Deploying canisters..." + icp deploy +fi + +# Build access URLs +BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) +FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) +CANDID_UI_ID=$(icp network status --json | jq -r '.candid_ui_principal' 2>/dev/null) + +if [ -n "$CODESPACE_NAME" ]; then + BASE="https://${CODESPACE_NAME}-8000.app.github.dev" +else + BASE="http://localhost:8000" +fi + +FRONTEND_URL="${BASE}/?canisterId=${FRONTEND_ID}" +CANDID_URL="${BASE}/?canisterId=${CANDID_UI_ID}&id=${BACKEND_ID}" + +echo "" +echo " Frontend: $FRONTEND_URL" +echo " Candid UI: $CANDID_URL" +echo "" + +code --open-url "$FRONTEND_URL" || true +code README.md diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 748a1d55c..4f37a8570 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -17,7 +17,7 @@ } }, "postStartCommand": "icp network start -d", - "postAttachCommand": "code README.md", + "postAttachCommand": "bash /workspaces/examples/.devcontainer/rust-who-am-i/postAttach.sh", "customizations": { "vscode": { "extensions": [ diff --git a/.devcontainer/rust-who-am-i/postAttach.sh b/.devcontainer/rust-who-am-i/postAttach.sh new file mode 100644 index 000000000..007f77ca2 --- /dev/null +++ b/.devcontainer/rust-who-am-i/postAttach.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Deploy if not already deployed (skipped on reconnects) +if ! icp canister status internet_identity_app_backend -i > /dev/null 2>&1; then + echo "Deploying canisters..." + icp deploy +fi + +# Build access URLs +BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) +FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) +CANDID_UI_ID=$(icp network status --json | jq -r '.candid_ui_principal' 2>/dev/null) + +if [ -n "$CODESPACE_NAME" ]; then + BASE="https://${CODESPACE_NAME}-8000.app.github.dev" +else + BASE="http://localhost:8000" +fi + +FRONTEND_URL="${BASE}/?canisterId=${FRONTEND_ID}" +CANDID_URL="${BASE}/?canisterId=${CANDID_UI_ID}&id=${BACKEND_ID}" + +echo "" +echo " Frontend: $FRONTEND_URL" +echo " Candid UI: $CANDID_URL" +echo "" + +code --open-url "$FRONTEND_URL" || true +code README.md diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index 88f5ea4f3..0006a8b00 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -16,26 +16,7 @@ Opens a pre-configured environment with the ICP toolchain installed and the loca ## Codespace actions -The local ICP network starts automatically when this Codespace opens. Run each step below in order. - -**Deploy** -```sh { name=deploy } -icp deploy -``` - -**Show URLs** *(after deploying)* -```sh { name=urls } -BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null || echo "not deployed") -FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null || echo "not deployed") -CANDID_UI_ID=$(icp network status --json | jq -r '.candid_ui_principal') -if [ -n "$CODESPACE_NAME" ]; then - BASE="https://${CODESPACE_NAME}-8000.app.github.dev" -else - BASE="http://localhost:8000" -fi -echo "Frontend: ${BASE}/?canisterId=${FRONTEND_ID}" -echo "Candid UI: ${BASE}/?canisterId=${CANDID_UI_ID}&id=${BACKEND_ID}" -``` +The local ICP network is started and canisters are deployed automatically when this Codespace opens. The frontend URL opens in your browser once deployment completes. **Start dev server** *(optional — for frontend development)* ```sh { name=frontend } From e3b5f6b8772774401c092ea591983ef049c65a65 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:37:54 +0200 Subject: [PATCH 11/55] chore: improve postAttach transparency with status messages Add clear echo output at each stage: network confirmation, deploy progress (with timing hint), deployment complete/skipped, URL building, and browser open notification. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/motoko-who-am-i/postAttach.sh | 14 ++++++++++++-- .devcontainer/rust-who-am-i/postAttach.sh | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.devcontainer/motoko-who-am-i/postAttach.sh b/.devcontainer/motoko-who-am-i/postAttach.sh index 007f77ca2..8f1ca2244 100644 --- a/.devcontainer/motoko-who-am-i/postAttach.sh +++ b/.devcontainer/motoko-who-am-i/postAttach.sh @@ -1,12 +1,21 @@ #!/bin/bash +echo "ICP local network is running on port 8000." +echo "" + # Deploy if not already deployed (skipped on reconnects) if ! icp canister status internet_identity_app_backend -i > /dev/null 2>&1; then - echo "Deploying canisters..." + echo "Deploying canisters (this takes ~30 seconds on first run)..." icp deploy + echo "" + echo "Deployment complete." +else + echo "Canisters already deployed." fi -# Build access URLs +echo "" +echo "Building access URLs..." + BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) CANDID_UI_ID=$(icp network status --json | jq -r '.candid_ui_principal' 2>/dev/null) @@ -24,6 +33,7 @@ echo "" echo " Frontend: $FRONTEND_URL" echo " Candid UI: $CANDID_URL" echo "" +echo "Opening frontend in browser..." code --open-url "$FRONTEND_URL" || true code README.md diff --git a/.devcontainer/rust-who-am-i/postAttach.sh b/.devcontainer/rust-who-am-i/postAttach.sh index 007f77ca2..22b044db4 100644 --- a/.devcontainer/rust-who-am-i/postAttach.sh +++ b/.devcontainer/rust-who-am-i/postAttach.sh @@ -1,12 +1,21 @@ #!/bin/bash +echo "ICP local network is running on port 8000." +echo "" + # Deploy if not already deployed (skipped on reconnects) if ! icp canister status internet_identity_app_backend -i > /dev/null 2>&1; then - echo "Deploying canisters..." + echo "Deploying canisters (this takes a while on first run — Rust compilation included)..." icp deploy + echo "" + echo "Deployment complete." +else + echo "Canisters already deployed." fi -# Build access URLs +echo "" +echo "Building access URLs..." + BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) CANDID_UI_ID=$(icp network status --json | jq -r '.candid_ui_principal' 2>/dev/null) @@ -24,6 +33,7 @@ echo "" echo " Frontend: $FRONTEND_URL" echo " Candid UI: $CANDID_URL" echo "" +echo "Opening frontend in browser..." code --open-url "$FRONTEND_URL" || true code README.md From f23750220e73b165cd3244b885b14bb4e3cc80cc Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:41:35 +0200 Subject: [PATCH 12/55] docs: re-add redeploy cell to Codespace actions (preserves state) Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index 0006a8b00..7b21f8354 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -18,6 +18,11 @@ Opens a pre-configured environment with the ICP toolchain installed and the loca The local ICP network is started and canisters are deployed automatically when this Codespace opens. The frontend URL opens in your browser once deployment completes. +**Redeploy** *(after code changes — preserves canister state)* +```sh { name=deploy } +icp deploy +``` + **Start dev server** *(optional — for frontend development)* ```sh { name=frontend } npm run dev From b25182f1771b66443da89606ce2797b7dbe4ec26 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:45:18 +0200 Subject: [PATCH 13/55] fix: suppress port 7863 notification (PocketIC internal port) Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/motoko-who-am-i/devcontainer.json | 3 +++ .devcontainer/rust-who-am-i/devcontainer.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index cefc64f0c..f4fa2cac1 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -12,6 +12,9 @@ "label": "Frontend", "onAutoForward": "openBrowser" }, + "7863": { + "onAutoForward": "ignore" + }, "7865": { "onAutoForward": "ignore" } diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 4f37a8570..1f477ec8d 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -12,6 +12,9 @@ "label": "Frontend", "onAutoForward": "openBrowser" }, + "7863": { + "onAutoForward": "ignore" + }, "7865": { "onAutoForward": "ignore" } From f750b043a64860f789aec379a9cc3298168e2044 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:47:48 +0200 Subject: [PATCH 14/55] fix: suppress port 7864 notification (Runme extension process) Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/motoko-who-am-i/devcontainer.json | 3 +++ .devcontainer/rust-who-am-i/devcontainer.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index f4fa2cac1..89669fd99 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -15,6 +15,9 @@ "7863": { "onAutoForward": "ignore" }, + "7864": { + "onAutoForward": "ignore" + }, "7865": { "onAutoForward": "ignore" } diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 1f477ec8d..9ce750d0c 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -15,6 +15,9 @@ "7863": { "onAutoForward": "ignore" }, + "7864": { + "onAutoForward": "ignore" + }, "7865": { "onAutoForward": "ignore" } From 3ca422119284eef029c6567d2fc2ea7a81057ab1 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:49:03 +0200 Subject: [PATCH 15/55] fix: remove code --open-url (browser blocks programmatic tabs) URLs are printed to the terminal and are clickable via Cmd/Ctrl+click. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/motoko-who-am-i/postAttach.sh | 3 --- .devcontainer/rust-who-am-i/postAttach.sh | 3 --- 2 files changed, 6 deletions(-) diff --git a/.devcontainer/motoko-who-am-i/postAttach.sh b/.devcontainer/motoko-who-am-i/postAttach.sh index 8f1ca2244..f47645bfe 100644 --- a/.devcontainer/motoko-who-am-i/postAttach.sh +++ b/.devcontainer/motoko-who-am-i/postAttach.sh @@ -33,7 +33,4 @@ echo "" echo " Frontend: $FRONTEND_URL" echo " Candid UI: $CANDID_URL" echo "" -echo "Opening frontend in browser..." - -code --open-url "$FRONTEND_URL" || true code README.md diff --git a/.devcontainer/rust-who-am-i/postAttach.sh b/.devcontainer/rust-who-am-i/postAttach.sh index 22b044db4..5eee2abd0 100644 --- a/.devcontainer/rust-who-am-i/postAttach.sh +++ b/.devcontainer/rust-who-am-i/postAttach.sh @@ -33,7 +33,4 @@ echo "" echo " Frontend: $FRONTEND_URL" echo " Candid UI: $CANDID_URL" echo "" -echo "Opening frontend in browser..." - -code --open-url "$FRONTEND_URL" || true code README.md From abccf6b5bdb5f814bcbbb6a7f952b5a7c6f7f785 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 14:53:06 +0200 Subject: [PATCH 16/55] fix: root key fetch and broken-pipe in Codespaces actor.js: create HttpAgent explicitly and call fetchRootKey() when accessed via the ICP gateway directly (no ic_env cookie). Extend isLocalNetwork() to cover .app.github.dev so Codespaces URLs are treated as local. Make createBackendActor async accordingly. App.jsx: await createBackendActor. postAttach.sh: capture icp network status --json into a variable before piping to jq to avoid the broken-pipe panic; add __Candid_UI fallback for candid_ui_principal. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/motoko-who-am-i/postAttach.sh | 8 ++++- .devcontainer/rust-who-am-i/postAttach.sh | 8 ++++- .../src/App.jsx | 2 +- .../src/actor.js | 32 +++++++++++++------ 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/.devcontainer/motoko-who-am-i/postAttach.sh b/.devcontainer/motoko-who-am-i/postAttach.sh index f47645bfe..242b9fb3e 100644 --- a/.devcontainer/motoko-who-am-i/postAttach.sh +++ b/.devcontainer/motoko-who-am-i/postAttach.sh @@ -18,7 +18,13 @@ echo "Building access URLs..." BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) -CANDID_UI_ID=$(icp network status --json | jq -r '.candid_ui_principal' 2>/dev/null) +# Capture JSON first to avoid broken-pipe panic in the icp-cli binary +NETWORK_JSON=$(icp network status --json 2>/dev/null) +CANDID_UI_ID=$(printf '%s' "$NETWORK_JSON" | jq -r '.candid_ui_principal // empty' 2>/dev/null) +# Fallback: some icp-cli versions expose the Candid UI as a named canister +if [ -z "$CANDID_UI_ID" ]; then + CANDID_UI_ID=$(icp canister status __Candid_UI -i 2>/dev/null || true) +fi if [ -n "$CODESPACE_NAME" ]; then BASE="https://${CODESPACE_NAME}-8000.app.github.dev" diff --git a/.devcontainer/rust-who-am-i/postAttach.sh b/.devcontainer/rust-who-am-i/postAttach.sh index 5eee2abd0..b4e80e3aa 100644 --- a/.devcontainer/rust-who-am-i/postAttach.sh +++ b/.devcontainer/rust-who-am-i/postAttach.sh @@ -18,7 +18,13 @@ echo "Building access URLs..." BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) -CANDID_UI_ID=$(icp network status --json | jq -r '.candid_ui_principal' 2>/dev/null) +# Capture JSON first to avoid broken-pipe panic in the icp-cli binary +NETWORK_JSON=$(icp network status --json 2>/dev/null) +CANDID_UI_ID=$(printf '%s' "$NETWORK_JSON" | jq -r '.candid_ui_principal // empty' 2>/dev/null) +# Fallback: some icp-cli versions expose the Candid UI as a named canister +if [ -z "$CANDID_UI_ID" ]; then + CANDID_UI_ID=$(icp canister status __Candid_UI -i 2>/dev/null || true) +fi if [ -n "$CODESPACE_NAME" ]; then BASE="https://${CODESPACE_NAME}-8000.app.github.dev" diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx b/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx index 08af5ce2e..fcd46c286 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx +++ b/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx @@ -21,7 +21,7 @@ const App = () => { const updateActor = async () => { const authClient = await AuthClient.create(); const identity = authClient.getIdentity(); - const actor = createBackendActor(identity); + const actor = await createBackendActor(identity); const isAuthenticated = await authClient.isAuthenticated(); setState((prev) => ({ diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js b/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js index 0536211a0..f877d2a6c 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js +++ b/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js @@ -1,4 +1,5 @@ import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; +import { HttpAgent } from "@icp-sdk/core/agent"; import { createActor } from "./bindings/internet_identity_app_backend"; const canisterEnv = safeGetCanisterEnv(); @@ -34,18 +35,31 @@ const agentOptions = { // II is always deployed on the local network (not the dev server). // REPLICA_PORT is injected by vite.config.js during `vite dev` since // window.location.port would be the dev server port, not the network port. -const isLocal = - window.location.hostname === "localhost" || - window.location.hostname === "127.0.0.1" || - window.location.hostname.endsWith(".localhost"); +// Local network: localhost variants and GitHub Codespaces port-forwarded URLs. +// Both require fetching the root key since they don't use the mainnet trust anchor. +function isLocalNetwork() { + const { hostname } = window.location; + return ( + hostname === "localhost" || + hostname === "127.0.0.1" || + hostname.endsWith(".localhost") || + hostname.endsWith(".app.github.dev") + ); +} + const II_CANISTER_ID = "uqzsh-gqaaa-aaaaq-qaada-cai"; const networkPort = process.env.REPLICA_PORT || window.location.port; -export const identityProviderUrl = isLocal +export const identityProviderUrl = isLocalNetwork() ? `http://${II_CANISTER_ID}.localhost:${networkPort}` : "https://id.ai"; -export function createBackendActor(identity) { - return createActor(canisterId, { - agentOptions: { ...agentOptions, identity }, - }); +export async function createBackendActor(identity) { + const agent = HttpAgent.createSync({ ...agentOptions, identity }); + // When the ic_env cookie is absent (frontend served from the ICP gateway + // directly rather than via Vite), rootKey is undefined and the agent would + // fall back to the mainnet trust anchor. Fetch the actual root key instead. + if (!canisterEnv?.IC_ROOT_KEY && isLocalNetwork()) { + await agent.fetchRootKey(); + } + return createActor(canisterId, { agent }); } From 986af95c83a90baf3fa48b8b570536d4de093416 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 15:00:07 +0200 Subject: [PATCH 17/55] fix: use echo to pipe JSON to jq (printf omits trailing newline) Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/motoko-who-am-i/postAttach.sh | 6 +----- .devcontainer/rust-who-am-i/postAttach.sh | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.devcontainer/motoko-who-am-i/postAttach.sh b/.devcontainer/motoko-who-am-i/postAttach.sh index 242b9fb3e..780907e51 100644 --- a/.devcontainer/motoko-who-am-i/postAttach.sh +++ b/.devcontainer/motoko-who-am-i/postAttach.sh @@ -20,11 +20,7 @@ BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) # Capture JSON first to avoid broken-pipe panic in the icp-cli binary NETWORK_JSON=$(icp network status --json 2>/dev/null) -CANDID_UI_ID=$(printf '%s' "$NETWORK_JSON" | jq -r '.candid_ui_principal // empty' 2>/dev/null) -# Fallback: some icp-cli versions expose the Candid UI as a named canister -if [ -z "$CANDID_UI_ID" ]; then - CANDID_UI_ID=$(icp canister status __Candid_UI -i 2>/dev/null || true) -fi +CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') if [ -n "$CODESPACE_NAME" ]; then BASE="https://${CODESPACE_NAME}-8000.app.github.dev" diff --git a/.devcontainer/rust-who-am-i/postAttach.sh b/.devcontainer/rust-who-am-i/postAttach.sh index b4e80e3aa..c0ed35f32 100644 --- a/.devcontainer/rust-who-am-i/postAttach.sh +++ b/.devcontainer/rust-who-am-i/postAttach.sh @@ -20,11 +20,7 @@ BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) # Capture JSON first to avoid broken-pipe panic in the icp-cli binary NETWORK_JSON=$(icp network status --json 2>/dev/null) -CANDID_UI_ID=$(printf '%s' "$NETWORK_JSON" | jq -r '.candid_ui_principal // empty' 2>/dev/null) -# Fallback: some icp-cli versions expose the Candid UI as a named canister -if [ -z "$CANDID_UI_ID" ]; then - CANDID_UI_ID=$(icp canister status __Candid_UI -i 2>/dev/null || true) -fi +CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') if [ -n "$CODESPACE_NAME" ]; then BASE="https://${CODESPACE_NAME}-8000.app.github.dev" From f2306c755cda72ecb44d10533922726ff4accd38 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 15:00:47 +0200 Subject: [PATCH 18/55] revert: remove fetchRootKey logic from actor.js The asset canister serves the ic_env cookie (containing the root key) when the frontend is loaded directly from the ICP gateway, so the agent already has the correct root key. The explicit fetchRootKey call and async createBackendActor were unnecessary. Co-Authored-By: Claude Sonnet 4.6 --- .../src/App.jsx | 2 +- .../src/actor.js | 32 ++++++------------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx b/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx index fcd46c286..08af5ce2e 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx +++ b/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx @@ -21,7 +21,7 @@ const App = () => { const updateActor = async () => { const authClient = await AuthClient.create(); const identity = authClient.getIdentity(); - const actor = await createBackendActor(identity); + const actor = createBackendActor(identity); const isAuthenticated = await authClient.isAuthenticated(); setState((prev) => ({ diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js b/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js index f877d2a6c..0536211a0 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js +++ b/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js @@ -1,5 +1,4 @@ import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; -import { HttpAgent } from "@icp-sdk/core/agent"; import { createActor } from "./bindings/internet_identity_app_backend"; const canisterEnv = safeGetCanisterEnv(); @@ -35,31 +34,18 @@ const agentOptions = { // II is always deployed on the local network (not the dev server). // REPLICA_PORT is injected by vite.config.js during `vite dev` since // window.location.port would be the dev server port, not the network port. -// Local network: localhost variants and GitHub Codespaces port-forwarded URLs. -// Both require fetching the root key since they don't use the mainnet trust anchor. -function isLocalNetwork() { - const { hostname } = window.location; - return ( - hostname === "localhost" || - hostname === "127.0.0.1" || - hostname.endsWith(".localhost") || - hostname.endsWith(".app.github.dev") - ); -} - +const isLocal = + window.location.hostname === "localhost" || + window.location.hostname === "127.0.0.1" || + window.location.hostname.endsWith(".localhost"); const II_CANISTER_ID = "uqzsh-gqaaa-aaaaq-qaada-cai"; const networkPort = process.env.REPLICA_PORT || window.location.port; -export const identityProviderUrl = isLocalNetwork() +export const identityProviderUrl = isLocal ? `http://${II_CANISTER_ID}.localhost:${networkPort}` : "https://id.ai"; -export async function createBackendActor(identity) { - const agent = HttpAgent.createSync({ ...agentOptions, identity }); - // When the ic_env cookie is absent (frontend served from the ICP gateway - // directly rather than via Vite), rootKey is undefined and the agent would - // fall back to the mainnet trust anchor. Fetch the actual root key instead. - if (!canisterEnv?.IC_ROOT_KEY && isLocalNetwork()) { - await agent.fetchRootKey(); - } - return createActor(canisterId, { agent }); +export function createBackendActor(identity) { + return createActor(canisterId, { + agentOptions: { ...agentOptions, identity }, + }); } From ebd5ee19c195dee55b1134aef751ae32ee92e059 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 15:21:38 +0200 Subject: [PATCH 19/55] fix: add *.app.github.dev to gateway domains for Codespaces support Allows the ICP HTTP gateway to accept requests with Codespaces forwarded Host headers. If the gateway doesn't support wildcards, we'll need a dynamic postStart.sh approach instead. Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/icp.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/motoko/who_am_i/icp.yaml b/motoko/who_am_i/icp.yaml index 6cf444682..87f9f7033 100644 --- a/motoko/who_am_i/icp.yaml +++ b/motoko/who_am_i/icp.yaml @@ -2,6 +2,10 @@ networks: - name: local mode: managed ii: true + gateway: + domains: + - localhost + - "*.app.github.dev" canisters: - name: internet_identity_app_backend From 1f73c78d6546a9d92a54f381840f45a5faeae628 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 15:27:54 +0200 Subject: [PATCH 20/55] revert: remove Codespaces host workarounds from actor.js and vite.config.js getNetworkHost() and the origin header override were workarounds for the gateway rejecting non-localhost Host headers. The root cause is now fixed via gateway.domains in icp.yaml, so these are no longer needed. Co-Authored-By: Claude Sonnet 4.6 --- .../internet_identity_app_frontend/src/actor.js | 14 +------------- .../internet_identity_app_frontend/vite.config.js | 8 +------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js b/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js index 0536211a0..becf24513 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js +++ b/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js @@ -13,20 +13,8 @@ if (!canisterId) { ); } -// In GitHub Codespaces the Vite dev server runs on port 5173, but the ICP -// gateway only accepts requests whose Host matches its own port (8000). Derive -// the port-8000 forwarded URL from the current hostname so API calls bypass -// the Vite proxy and reach the gateway directly. -function getNetworkHost() { - const { hostname, origin } = window.location; - if (hostname.endsWith(".app.github.dev")) { - return "https://" + hostname.replace(/-\d+\.app\.github\.dev$/, "-8000.app.github.dev"); - } - return origin; -} - const agentOptions = { - host: getNetworkHost(), + host: window.location.origin, rootKey: canisterEnv?.IC_ROOT_KEY, }; diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js b/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js index 9d0e17f6c..41d03427c 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js +++ b/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js @@ -24,13 +24,7 @@ function getDevServerConfig() { )}; SameSite=Lax;`, }, proxy: { - "/api": { - target: "http://127.0.0.1:8000", - changeOrigin: true, - // The ICP gateway rejects non-localhost Origin headers. Override it so - // requests proxied from Codespaces (or any non-localhost dev URL) are accepted. - headers: { origin: "http://localhost:8000" }, - }, + "/api": { target: "http://127.0.0.1:8000", changeOrigin: true }, }, }; } catch {} From 128441dc381b286ed293fe349a6aa93a9611f2a8 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 15:59:54 +0200 Subject: [PATCH 21/55] fix: inject Codespaces domain into icp.yaml via postStart.sh The wildcard *.app.github.dev caused the gateway to hang (PocketIC doesn't support wildcards). Replace with a postStart.sh script that injects the exact ${CODESPACE_NAME}-8000.app.github.dev domain into icp.yaml before starting the network. The modification is workspace- local and not committed. Co-Authored-By: Claude Sonnet 4.6 --- .../motoko-who-am-i/devcontainer.json | 2 +- .devcontainer/motoko-who-am-i/postStart.sh | 20 +++++++++++++++++++ .devcontainer/rust-who-am-i/devcontainer.json | 2 +- .devcontainer/rust-who-am-i/postStart.sh | 20 +++++++++++++++++++ motoko/who_am_i/icp.yaml | 4 ---- 5 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 .devcontainer/motoko-who-am-i/postStart.sh create mode 100644 .devcontainer/rust-who-am-i/postStart.sh diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index 89669fd99..3c753023f 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -23,7 +23,7 @@ } }, "postCreateCommand": "mops install", - "postStartCommand": "icp network start -d", + "postStartCommand": "bash /workspaces/examples/.devcontainer/motoko-who-am-i/postStart.sh", "postAttachCommand": "bash /workspaces/examples/.devcontainer/motoko-who-am-i/postAttach.sh", "customizations": { "vscode": { diff --git a/.devcontainer/motoko-who-am-i/postStart.sh b/.devcontainer/motoko-who-am-i/postStart.sh new file mode 100644 index 000000000..39104f1fc --- /dev/null +++ b/.devcontainer/motoko-who-am-i/postStart.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# In GitHub Codespaces, inject the forwarded domain into icp.yaml so the +# HTTP gateway accepts requests with that Host header. +if [ -n "$CODESPACE_NAME" ]; then + DOMAIN="${CODESPACE_NAME}-8000.app.github.dev" + node -e " + const fs = require('fs'); + const content = fs.readFileSync('icp.yaml', 'utf8'); + if (!content.includes('gateway:')) { + const updated = content.replace( + ' ii: true\n', + ' ii: true\n gateway:\n domains:\n - localhost\n - ${DOMAIN}\n' + ); + fs.writeFileSync('icp.yaml', updated); + } + " +fi + +icp network start -d diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 9ce750d0c..2368bb4d8 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -22,7 +22,7 @@ "onAutoForward": "ignore" } }, - "postStartCommand": "icp network start -d", + "postStartCommand": "bash /workspaces/examples/.devcontainer/rust-who-am-i/postStart.sh", "postAttachCommand": "bash /workspaces/examples/.devcontainer/rust-who-am-i/postAttach.sh", "customizations": { "vscode": { diff --git a/.devcontainer/rust-who-am-i/postStart.sh b/.devcontainer/rust-who-am-i/postStart.sh new file mode 100644 index 000000000..39104f1fc --- /dev/null +++ b/.devcontainer/rust-who-am-i/postStart.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# In GitHub Codespaces, inject the forwarded domain into icp.yaml so the +# HTTP gateway accepts requests with that Host header. +if [ -n "$CODESPACE_NAME" ]; then + DOMAIN="${CODESPACE_NAME}-8000.app.github.dev" + node -e " + const fs = require('fs'); + const content = fs.readFileSync('icp.yaml', 'utf8'); + if (!content.includes('gateway:')) { + const updated = content.replace( + ' ii: true\n', + ' ii: true\n gateway:\n domains:\n - localhost\n - ${DOMAIN}\n' + ); + fs.writeFileSync('icp.yaml', updated); + } + " +fi + +icp network start -d diff --git a/motoko/who_am_i/icp.yaml b/motoko/who_am_i/icp.yaml index 87f9f7033..6cf444682 100644 --- a/motoko/who_am_i/icp.yaml +++ b/motoko/who_am_i/icp.yaml @@ -2,10 +2,6 @@ networks: - name: local mode: managed ii: true - gateway: - domains: - - localhost - - "*.app.github.dev" canisters: - name: internet_identity_app_backend From e7a47f7f818c877acd04ca91177bfa56011f8a0d Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 18:44:33 +0200 Subject: [PATCH 22/55] chore: centralize devcontainer scripts and add generic CODESPACE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move postStart.sh and postAttach.sh to .devcontainer/scripts/ (shared by all examples); both devcontainer.json files now reference them directly - postStart.sh: generic gateway domain injection — handles both ii:true and non-ii icp.yaml layouts - postAttach.sh: fully generic via `icp project show` + jq — detects frontend vs backend canisters by recipe/sync type, shows correct URLs for each (frontend URL or Candid UI link) - Add .devcontainer/CODESPACE.md with Show URLs / Redeploy / Reset cells; symlinked into motoko/who_am_i and rust/who_am_i - Scope workbench.editorAssociations to CODESPACE.md only (not *.md) - Remove postCreateCommand (mops install) from motoko devcontainer - Use icp deploy --mode reinstall -y for reset instead of network restart Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/CODESPACE.md | 69 ++++++++++++++++++ .../motoko-who-am-i/devcontainer.json | 7 +- .devcontainer/motoko-who-am-i/postAttach.sh | 38 ---------- .devcontainer/rust-who-am-i/devcontainer.json | 6 +- .devcontainer/rust-who-am-i/postAttach.sh | 38 ---------- .devcontainer/rust-who-am-i/postStart.sh | 20 ------ .devcontainer/scripts/postAttach.sh | 71 +++++++++++++++++++ .../{motoko-who-am-i => scripts}/postStart.sh | 11 +-- motoko/who_am_i/CODESPACE.md | 1 + rust/who_am_i/CODESPACE.md | 1 + 10 files changed, 155 insertions(+), 107 deletions(-) create mode 100644 .devcontainer/CODESPACE.md delete mode 100644 .devcontainer/motoko-who-am-i/postAttach.sh delete mode 100644 .devcontainer/rust-who-am-i/postAttach.sh delete mode 100644 .devcontainer/rust-who-am-i/postStart.sh create mode 100755 .devcontainer/scripts/postAttach.sh rename .devcontainer/{motoko-who-am-i => scripts}/postStart.sh (55%) mode change 100644 => 100755 create mode 120000 motoko/who_am_i/CODESPACE.md create mode 120000 rust/who_am_i/CODESPACE.md diff --git a/.devcontainer/CODESPACE.md b/.devcontainer/CODESPACE.md new file mode 100644 index 000000000..4635cdea0 --- /dev/null +++ b/.devcontainer/CODESPACE.md @@ -0,0 +1,69 @@ +# ICP Codespace + +Canisters are deployed automatically when the Codespace starts. + +## Show URLs + +```bash {"name":"show-urls","interactive":false} +CANISTER_JSON=$(icp canister status --json 2>/dev/null) +NETWORK_JSON=$(icp network status --json 2>/dev/null) +CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') + +if [ -n "$CODESPACE_NAME" ]; then + BASE="https://${CODESPACE_NAME}-8000.app.github.dev" +else + BASE="http://localhost:8000" +fi + +FRONTEND_NAMES=$(icp project show 2>/dev/null | node -e ' + let data = ""; + process.stdin.on("data", c => data += c); + process.stdin.on("end", () => { + const lines = data.split("\n"); + const frontends = []; + let inCanisters = false, currentCanister = null; + for (const line of lines) { + if (/^\S/.test(line)) { + inCanisters = (line === "canisters:"); + currentCanister = null; + continue; + } + if (!inCanisters) continue; + const m = line.match(/^ ([a-z][a-z0-9_-]*):\s*$/); + if (m) { currentCanister = m[1]; continue; } + if (currentCanister && !frontends.includes(currentCanister)) { + if (line.includes("asset-canister") || /^\s+type:\s+assets\s*$/.test(line)) { + frontends.push(currentCanister); + } + } + } + process.stdout.write(frontends.join("\n") + (frontends.length ? "\n" : "")); + }); +') + +echo "$CANISTER_JSON" | while IFS= read -r entry; do + ID=$(echo "$entry" | jq -r '.id') + NAME=$(echo "$entry" | jq -r '.name') + if echo "$FRONTEND_NAMES" | grep -qx "$NAME"; then + echo "$NAME: ${BASE}/?canisterId=${ID}" + elif [ -n "$CANDID_UI_ID" ]; then + echo "$NAME (Candid UI): ${BASE}/?canisterId=${CANDID_UI_ID}&id=${ID}" + fi +done +``` + +## Redeploy + +Rebuilds and redeploys all canisters, preserving their state. + +```bash {"name":"redeploy"} +icp deploy +``` + +## Reset & Redeploy + +Reinstalls all canisters from scratch, wiping their state. The network keeps running. + +```bash {"name":"reset-and-redeploy"} +icp deploy --mode reinstall -y +``` diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index 3c753023f..4312fa5a1 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -22,9 +22,8 @@ "onAutoForward": "ignore" } }, - "postCreateCommand": "mops install", - "postStartCommand": "bash /workspaces/examples/.devcontainer/motoko-who-am-i/postStart.sh", - "postAttachCommand": "bash /workspaces/examples/.devcontainer/motoko-who-am-i/postAttach.sh", + "postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh", + "postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh", "customizations": { "vscode": { "extensions": [ @@ -34,7 +33,7 @@ "settings": { "git.openRepositoryInParentFolders": "always", "workbench.editorAssociations": { - "*.md": "runme" + "CODESPACE.md": "runme" } } } diff --git a/.devcontainer/motoko-who-am-i/postAttach.sh b/.devcontainer/motoko-who-am-i/postAttach.sh deleted file mode 100644 index 780907e51..000000000 --- a/.devcontainer/motoko-who-am-i/postAttach.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -echo "ICP local network is running on port 8000." -echo "" - -# Deploy if not already deployed (skipped on reconnects) -if ! icp canister status internet_identity_app_backend -i > /dev/null 2>&1; then - echo "Deploying canisters (this takes ~30 seconds on first run)..." - icp deploy - echo "" - echo "Deployment complete." -else - echo "Canisters already deployed." -fi - -echo "" -echo "Building access URLs..." - -BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) -FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) -# Capture JSON first to avoid broken-pipe panic in the icp-cli binary -NETWORK_JSON=$(icp network status --json 2>/dev/null) -CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') - -if [ -n "$CODESPACE_NAME" ]; then - BASE="https://${CODESPACE_NAME}-8000.app.github.dev" -else - BASE="http://localhost:8000" -fi - -FRONTEND_URL="${BASE}/?canisterId=${FRONTEND_ID}" -CANDID_URL="${BASE}/?canisterId=${CANDID_UI_ID}&id=${BACKEND_ID}" - -echo "" -echo " Frontend: $FRONTEND_URL" -echo " Candid UI: $CANDID_URL" -echo "" -code README.md diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 2368bb4d8..d985316a5 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -22,8 +22,8 @@ "onAutoForward": "ignore" } }, - "postStartCommand": "bash /workspaces/examples/.devcontainer/rust-who-am-i/postStart.sh", - "postAttachCommand": "bash /workspaces/examples/.devcontainer/rust-who-am-i/postAttach.sh", + "postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh", + "postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh", "customizations": { "vscode": { "extensions": [ @@ -33,7 +33,7 @@ "settings": { "git.openRepositoryInParentFolders": "always", "workbench.editorAssociations": { - "*.md": "runme" + "CODESPACE.md": "runme" } } } diff --git a/.devcontainer/rust-who-am-i/postAttach.sh b/.devcontainer/rust-who-am-i/postAttach.sh deleted file mode 100644 index c0ed35f32..000000000 --- a/.devcontainer/rust-who-am-i/postAttach.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -echo "ICP local network is running on port 8000." -echo "" - -# Deploy if not already deployed (skipped on reconnects) -if ! icp canister status internet_identity_app_backend -i > /dev/null 2>&1; then - echo "Deploying canisters (this takes a while on first run — Rust compilation included)..." - icp deploy - echo "" - echo "Deployment complete." -else - echo "Canisters already deployed." -fi - -echo "" -echo "Building access URLs..." - -BACKEND_ID=$(icp canister status internet_identity_app_backend -i 2>/dev/null) -FRONTEND_ID=$(icp canister status internet_identity_app_frontend -i 2>/dev/null) -# Capture JSON first to avoid broken-pipe panic in the icp-cli binary -NETWORK_JSON=$(icp network status --json 2>/dev/null) -CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') - -if [ -n "$CODESPACE_NAME" ]; then - BASE="https://${CODESPACE_NAME}-8000.app.github.dev" -else - BASE="http://localhost:8000" -fi - -FRONTEND_URL="${BASE}/?canisterId=${FRONTEND_ID}" -CANDID_URL="${BASE}/?canisterId=${CANDID_UI_ID}&id=${BACKEND_ID}" - -echo "" -echo " Frontend: $FRONTEND_URL" -echo " Candid UI: $CANDID_URL" -echo "" -code README.md diff --git a/.devcontainer/rust-who-am-i/postStart.sh b/.devcontainer/rust-who-am-i/postStart.sh deleted file mode 100644 index 39104f1fc..000000000 --- a/.devcontainer/rust-who-am-i/postStart.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# In GitHub Codespaces, inject the forwarded domain into icp.yaml so the -# HTTP gateway accepts requests with that Host header. -if [ -n "$CODESPACE_NAME" ]; then - DOMAIN="${CODESPACE_NAME}-8000.app.github.dev" - node -e " - const fs = require('fs'); - const content = fs.readFileSync('icp.yaml', 'utf8'); - if (!content.includes('gateway:')) { - const updated = content.replace( - ' ii: true\n', - ' ii: true\n gateway:\n domains:\n - localhost\n - ${DOMAIN}\n' - ); - fs.writeFileSync('icp.yaml', updated); - } - " -fi - -icp network start -d diff --git a/.devcontainer/scripts/postAttach.sh b/.devcontainer/scripts/postAttach.sh new file mode 100755 index 000000000..835751b6b --- /dev/null +++ b/.devcontainer/scripts/postAttach.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +echo "ICP local network is running on port 8000." +echo "" + +CANISTER_JSON=$(icp canister status --json 2>/dev/null) + +if [ -z "$CANISTER_JSON" ]; then + echo "Deploying canisters..." + icp deploy + echo "" + echo "Deployment complete." + CANISTER_JSON=$(icp canister status --json 2>/dev/null) +else + echo "Canisters already deployed." +fi + +echo "" +echo "Access URLs:" +echo "" + +NETWORK_JSON=$(icp network status --json 2>/dev/null) +CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') + +if [ -n "$CODESPACE_NAME" ]; then + BASE="https://${CODESPACE_NAME}-8000.app.github.dev" +else + BASE="http://localhost:8000" +fi + +# Detect frontend canisters from `icp project show` (recipes fully expanded). +# A canister is a frontend if its registry_recipe is the asset-canister, or it +# has a sync step with type: assets. Everything else gets a Candid UI link. +FRONTEND_NAMES=$(icp project show 2>/dev/null | node -e ' + let data = ""; + process.stdin.on("data", c => data += c); + process.stdin.on("end", () => { + const lines = data.split("\n"); + const frontends = []; + let inCanisters = false, currentCanister = null; + for (const line of lines) { + if (/^\S/.test(line)) { + inCanisters = (line === "canisters:"); + currentCanister = null; + continue; + } + if (!inCanisters) continue; + const m = line.match(/^ ([a-z][a-z0-9_-]*):\s*$/); + if (m) { currentCanister = m[1]; continue; } + if (currentCanister && !frontends.includes(currentCanister)) { + if (line.includes("asset-canister") || /^\s+type:\s+assets\s*$/.test(line)) { + frontends.push(currentCanister); + } + } + } + process.stdout.write(frontends.join("\n") + (frontends.length ? "\n" : "")); + }); +') + +echo "$CANISTER_JSON" | while IFS= read -r entry; do + ID=$(echo "$entry" | jq -r '.id') + NAME=$(echo "$entry" | jq -r '.name') + if echo "$FRONTEND_NAMES" | grep -qx "$NAME"; then + echo " $NAME: ${BASE}/?canisterId=${ID}" + elif [ -n "$CANDID_UI_ID" ]; then + echo " $NAME (Candid UI): ${BASE}/?canisterId=${CANDID_UI_ID}&id=${ID}" + fi +done + +echo "" +code CODESPACE.md diff --git a/.devcontainer/motoko-who-am-i/postStart.sh b/.devcontainer/scripts/postStart.sh old mode 100644 new mode 100755 similarity index 55% rename from .devcontainer/motoko-who-am-i/postStart.sh rename to .devcontainer/scripts/postStart.sh index 39104f1fc..1d629c659 --- a/.devcontainer/motoko-who-am-i/postStart.sh +++ b/.devcontainer/scripts/postStart.sh @@ -8,10 +8,13 @@ if [ -n "$CODESPACE_NAME" ]; then const fs = require('fs'); const content = fs.readFileSync('icp.yaml', 'utf8'); if (!content.includes('gateway:')) { - const updated = content.replace( - ' ii: true\n', - ' ii: true\n gateway:\n domains:\n - localhost\n - ${DOMAIN}\n' - ); + const gateway = ' gateway:\n domains:\n - localhost\n - ${DOMAIN}\n'; + let updated; + if (content.includes(' ii: true\n')) { + updated = content.replace(' ii: true\n', ' ii: true\n' + gateway); + } else { + updated = content.replace(' mode: managed\n', ' mode: managed\n' + gateway); + } fs.writeFileSync('icp.yaml', updated); } " diff --git a/motoko/who_am_i/CODESPACE.md b/motoko/who_am_i/CODESPACE.md new file mode 120000 index 000000000..17b02d2b4 --- /dev/null +++ b/motoko/who_am_i/CODESPACE.md @@ -0,0 +1 @@ +../../.devcontainer/CODESPACE.md \ No newline at end of file diff --git a/rust/who_am_i/CODESPACE.md b/rust/who_am_i/CODESPACE.md new file mode 120000 index 000000000..17b02d2b4 --- /dev/null +++ b/rust/who_am_i/CODESPACE.md @@ -0,0 +1 @@ +../../.devcontainer/CODESPACE.md \ No newline at end of file From 50b20cde37607465e48c984cc4f764294ab9ec3a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 21 May 2026 18:46:38 +0200 Subject: [PATCH 23/55] refactor: extract URL display to show-urls.sh Move URL detection logic from postAttach.sh and CODESPACE.md into a dedicated script. All three cells in CODESPACE.md are now one-liners. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/CODESPACE.md | 46 +------------------------ .devcontainer/scripts/postAttach.sh | 52 +---------------------------- .devcontainer/scripts/show-urls.sh | 47 ++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 96 deletions(-) create mode 100755 .devcontainer/scripts/show-urls.sh diff --git a/.devcontainer/CODESPACE.md b/.devcontainer/CODESPACE.md index 4635cdea0..2674e31b8 100644 --- a/.devcontainer/CODESPACE.md +++ b/.devcontainer/CODESPACE.md @@ -5,51 +5,7 @@ Canisters are deployed automatically when the Codespace starts. ## Show URLs ```bash {"name":"show-urls","interactive":false} -CANISTER_JSON=$(icp canister status --json 2>/dev/null) -NETWORK_JSON=$(icp network status --json 2>/dev/null) -CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') - -if [ -n "$CODESPACE_NAME" ]; then - BASE="https://${CODESPACE_NAME}-8000.app.github.dev" -else - BASE="http://localhost:8000" -fi - -FRONTEND_NAMES=$(icp project show 2>/dev/null | node -e ' - let data = ""; - process.stdin.on("data", c => data += c); - process.stdin.on("end", () => { - const lines = data.split("\n"); - const frontends = []; - let inCanisters = false, currentCanister = null; - for (const line of lines) { - if (/^\S/.test(line)) { - inCanisters = (line === "canisters:"); - currentCanister = null; - continue; - } - if (!inCanisters) continue; - const m = line.match(/^ ([a-z][a-z0-9_-]*):\s*$/); - if (m) { currentCanister = m[1]; continue; } - if (currentCanister && !frontends.includes(currentCanister)) { - if (line.includes("asset-canister") || /^\s+type:\s+assets\s*$/.test(line)) { - frontends.push(currentCanister); - } - } - } - process.stdout.write(frontends.join("\n") + (frontends.length ? "\n" : "")); - }); -') - -echo "$CANISTER_JSON" | while IFS= read -r entry; do - ID=$(echo "$entry" | jq -r '.id') - NAME=$(echo "$entry" | jq -r '.name') - if echo "$FRONTEND_NAMES" | grep -qx "$NAME"; then - echo "$NAME: ${BASE}/?canisterId=${ID}" - elif [ -n "$CANDID_UI_ID" ]; then - echo "$NAME (Candid UI): ${BASE}/?canisterId=${CANDID_UI_ID}&id=${ID}" - fi -done +bash /workspaces/examples/.devcontainer/scripts/show-urls.sh ``` ## Redeploy diff --git a/.devcontainer/scripts/postAttach.sh b/.devcontainer/scripts/postAttach.sh index 835751b6b..fff2b2c2b 100755 --- a/.devcontainer/scripts/postAttach.sh +++ b/.devcontainer/scripts/postAttach.sh @@ -10,62 +10,12 @@ if [ -z "$CANISTER_JSON" ]; then icp deploy echo "" echo "Deployment complete." - CANISTER_JSON=$(icp canister status --json 2>/dev/null) -else - echo "Canisters already deployed." fi echo "" echo "Access URLs:" echo "" - -NETWORK_JSON=$(icp network status --json 2>/dev/null) -CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') - -if [ -n "$CODESPACE_NAME" ]; then - BASE="https://${CODESPACE_NAME}-8000.app.github.dev" -else - BASE="http://localhost:8000" -fi - -# Detect frontend canisters from `icp project show` (recipes fully expanded). -# A canister is a frontend if its registry_recipe is the asset-canister, or it -# has a sync step with type: assets. Everything else gets a Candid UI link. -FRONTEND_NAMES=$(icp project show 2>/dev/null | node -e ' - let data = ""; - process.stdin.on("data", c => data += c); - process.stdin.on("end", () => { - const lines = data.split("\n"); - const frontends = []; - let inCanisters = false, currentCanister = null; - for (const line of lines) { - if (/^\S/.test(line)) { - inCanisters = (line === "canisters:"); - currentCanister = null; - continue; - } - if (!inCanisters) continue; - const m = line.match(/^ ([a-z][a-z0-9_-]*):\s*$/); - if (m) { currentCanister = m[1]; continue; } - if (currentCanister && !frontends.includes(currentCanister)) { - if (line.includes("asset-canister") || /^\s+type:\s+assets\s*$/.test(line)) { - frontends.push(currentCanister); - } - } - } - process.stdout.write(frontends.join("\n") + (frontends.length ? "\n" : "")); - }); -') - -echo "$CANISTER_JSON" | while IFS= read -r entry; do - ID=$(echo "$entry" | jq -r '.id') - NAME=$(echo "$entry" | jq -r '.name') - if echo "$FRONTEND_NAMES" | grep -qx "$NAME"; then - echo " $NAME: ${BASE}/?canisterId=${ID}" - elif [ -n "$CANDID_UI_ID" ]; then - echo " $NAME (Candid UI): ${BASE}/?canisterId=${CANDID_UI_ID}&id=${ID}" - fi -done +bash /workspaces/examples/.devcontainer/scripts/show-urls.sh echo "" code CODESPACE.md diff --git a/.devcontainer/scripts/show-urls.sh b/.devcontainer/scripts/show-urls.sh new file mode 100755 index 000000000..ddd4bb628 --- /dev/null +++ b/.devcontainer/scripts/show-urls.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +CANISTER_JSON=$(icp canister status --json 2>/dev/null) +NETWORK_JSON=$(icp network status --json 2>/dev/null) +CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""') + +if [ -n "$CODESPACE_NAME" ]; then + BASE="https://${CODESPACE_NAME}-8000.app.github.dev" +else + BASE="http://localhost:8000" +fi + +FRONTEND_NAMES=$(icp project show 2>/dev/null | node -e ' + let data = ""; + process.stdin.on("data", c => data += c); + process.stdin.on("end", () => { + const lines = data.split("\n"); + const frontends = []; + let inCanisters = false, currentCanister = null; + for (const line of lines) { + if (/^\S/.test(line)) { + inCanisters = (line === "canisters:"); + currentCanister = null; + continue; + } + if (!inCanisters) continue; + const m = line.match(/^ ([a-z][a-z0-9_-]*):\s*$/); + if (m) { currentCanister = m[1]; continue; } + if (currentCanister && !frontends.includes(currentCanister)) { + if (line.includes("asset-canister") || /^\s+type:\s+assets\s*$/.test(line)) { + frontends.push(currentCanister); + } + } + } + process.stdout.write(frontends.join("\n") + (frontends.length ? "\n" : "")); + }); +') + +echo "$CANISTER_JSON" | while IFS= read -r entry; do + ID=$(echo "$entry" | jq -r '.id') + NAME=$(echo "$entry" | jq -r '.name') + if echo "$FRONTEND_NAMES" | grep -qx "$NAME"; then + echo " $NAME: ${BASE}/?canisterId=${ID}" + elif [ -n "$CANDID_UI_ID" ]; then + echo " $NAME (Candid UI): ${BASE}/?canisterId=${CANDID_UI_ID}&id=${ID}" + fi +done From 37e2ef65ed95b0bb01a38d25a4f554a6d87337cf Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 05:51:56 +0200 Subject: [PATCH 24/55] docs: clean up Codespace section in who_am_i READMEs Remove incorrect "open existing codespace" claim, drop Codespace actions (moved to CODESPACE.md), and remove em-dashes. Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/README.md | 21 +-------------------- rust/who_am_i/README.md | 26 +------------------------- 2 files changed, 2 insertions(+), 45 deletions(-) diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index 7b21f8354..925a0d78d 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -10,29 +10,10 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Eve [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=.devcontainer%2Fmotoko-who-am-i%2Fdevcontainer.json&ref=feat%2Fcodespaces) -Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. If you already have a Codespace for this example, the creation page will show an **"Open existing codespace"** option at the top — use that to resume. You can also browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). +Opens a pre-configured environment with the ICP toolchain installed. The local network starts and canisters are deployed automatically. You can browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). > **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. -## Codespace actions - -The local ICP network is started and canisters are deployed automatically when this Codespace opens. The frontend URL opens in your browser once deployment completes. - -**Redeploy** *(after code changes — preserves canister state)* -```sh { name=deploy } -icp deploy -``` - -**Start dev server** *(optional — for frontend development)* -```sh { name=frontend } -npm run dev -``` - -**Reset & redeploy** *(wipes all canister state)* -```sh { name=reset-deploy } -icp deploy --mode reinstall -y -``` - ## Build and deploy from the command line ### Prerequisites diff --git a/rust/who_am_i/README.md b/rust/who_am_i/README.md index 8ba283e45..e15ae849d 100644 --- a/rust/who_am_i/README.md +++ b/rust/who_am_i/README.md @@ -10,34 +10,10 @@ Who am I? demonstrates how entities on the Internet Computer are identified. Eve [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=.devcontainer%2Frust-who-am-i%2Fdevcontainer.json&ref=feat%2Fcodespaces) -Opens a pre-configured environment with the ICP toolchain installed and the local network started automatically. If you already have a Codespace for this example, the creation page will show an **"Open existing codespace"** option at the top — use that to resume. You can also browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). +Opens a pre-configured environment with the ICP toolchain installed. The local network starts and canisters are deployed automatically. You can browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). > **Note:** Authentication uses production [Internet Identity](https://id.ai) rather than a local test instance. You will see your real principal identifier. -## Codespace actions - -The local ICP network starts automatically when this Codespace opens. Run each step below in order. - -**Deploy** -```sh { name=deploy } -icp deploy -``` - -**Start frontend** -```sh { name=frontend } -npm run dev -``` - -**Reset & redeploy** *(wipes all canister state)* -```sh { name=reset-deploy } -icp deploy --mode reinstall -y -``` - -**Show canister info** -```sh { name=info } -icp environment -``` - ## Build and deploy from the command line ### Prerequisites From 6b2f799bf3a864b441f6ec59e277020d860d74cb Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 05:59:08 +0200 Subject: [PATCH 25/55] chore: remove Runme, add startup editor suppression, document URL limitations - Remove stateful.runme extension from both devcontainer configs - Remove workbench.editorAssociations (no longer needed) - Add workbench.startupEditor: none to suppress auto-opening README.md - CODESPACE.md is now plain markdown: warns that icp deploy URLs do not work in Codespaces and explains the ?canisterId= limitation for non-SPA frontends Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/CODESPACE.md | 14 +++++++++----- .devcontainer/motoko-who-am-i/devcontainer.json | 7 ++----- .devcontainer/rust-who-am-i/devcontainer.json | 7 ++----- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.devcontainer/CODESPACE.md b/.devcontainer/CODESPACE.md index 2674e31b8..d5a18b9ce 100644 --- a/.devcontainer/CODESPACE.md +++ b/.devcontainer/CODESPACE.md @@ -1,18 +1,22 @@ # ICP Codespace -Canisters are deployed automatically when the Codespace starts. +The network starts and canisters are deployed automatically when this Codespace opens. Access URLs are printed in the terminal once deployment completes. -## Show URLs +## Access URLs -```bash {"name":"show-urls","interactive":false} +`icp deploy` prints URLs using `localhost:8000`, which do not work inside a Codespace. Run the following command to get the correct forwarded URLs: + +```bash bash /workspaces/examples/.devcontainer/scripts/show-urls.sh ``` +> **Note for non-SPA frontends:** Frontend URLs use a `?canisterId=` query parameter for routing. This works correctly for single-page apps (all navigation stays client-side). If your frontend uses real path-based navigation where clicking a link triggers a new browser request (e.g. navigating to `/page2`), the query parameter will be dropped and the gateway will not know which canister to serve. Subdomain-based routing is not available in Codespaces because GitHub's TLS certificate only covers one subdomain level, making `.-8000.app.github.dev` invalid. + ## Redeploy Rebuilds and redeploys all canisters, preserving their state. -```bash {"name":"redeploy"} +```bash icp deploy ``` @@ -20,6 +24,6 @@ icp deploy Reinstalls all canisters from scratch, wiping their state. The network keeps running. -```bash {"name":"reset-and-redeploy"} +```bash icp deploy --mode reinstall -y ``` diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index 4312fa5a1..82bc5a1be 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -27,14 +27,11 @@ "customizations": { "vscode": { "extensions": [ - "dfinity-foundation.vscode-motoko", - "stateful.runme" + "dfinity-foundation.vscode-motoko" ], "settings": { "git.openRepositoryInParentFolders": "always", - "workbench.editorAssociations": { - "CODESPACE.md": "runme" - } + "workbench.startupEditor": "none" } } } diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index d985316a5..1574e353c 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -27,14 +27,11 @@ "customizations": { "vscode": { "extensions": [ - "rust-analyzer", - "stateful.runme" + "rust-analyzer" ], "settings": { "git.openRepositoryInParentFolders": "always", - "workbench.editorAssociations": { - "CODESPACE.md": "runme" - } + "workbench.startupEditor": "none" } } } From 779dbfab92127768a05978a37d828496724d4d95 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 08:06:34 +0200 Subject: [PATCH 26/55] chore: improve Codespace UX for who_am_i examples - Open CODESPACE.md immediately on attach (before deploy) and in preview mode - Always run icp deploy on attach (idempotent, accurate for both first start and resume) - Add setup-in-progress disclaimer to CODESPACE.md - Move non-SPA note to bottom, rename Redeploy section to Deploy / Redeploy - Replace invalid workbench.commandPalette.showAskInChat with editorAssociations for markdown preview Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/CODESPACE.md | 12 ++++++++---- .devcontainer/motoko-who-am-i/devcontainer.json | 14 ++++---------- .devcontainer/rust-who-am-i/devcontainer.json | 14 ++++---------- .devcontainer/scripts/postAttach.sh | 17 +++-------------- 4 files changed, 19 insertions(+), 38 deletions(-) diff --git a/.devcontainer/CODESPACE.md b/.devcontainer/CODESPACE.md index d5a18b9ce..5e68b9dde 100644 --- a/.devcontainer/CODESPACE.md +++ b/.devcontainer/CODESPACE.md @@ -1,5 +1,7 @@ # ICP Codespace +> **Setup in progress:** The ICP local network is starting and canisters are being deployed automatically. Check the terminal panel for status. Access URLs will be printed once deployment is complete. + The network starts and canisters are deployed automatically when this Codespace opens. Access URLs are printed in the terminal once deployment completes. ## Access URLs @@ -10,11 +12,9 @@ The network starts and canisters are deployed automatically when this Codespace bash /workspaces/examples/.devcontainer/scripts/show-urls.sh ``` -> **Note for non-SPA frontends:** Frontend URLs use a `?canisterId=` query parameter for routing. This works correctly for single-page apps (all navigation stays client-side). If your frontend uses real path-based navigation where clicking a link triggers a new browser request (e.g. navigating to `/page2`), the query parameter will be dropped and the gateway will not know which canister to serve. Subdomain-based routing is not available in Codespaces because GitHub's TLS certificate only covers one subdomain level, making `.-8000.app.github.dev` invalid. - -## Redeploy +## Deploy / Redeploy -Rebuilds and redeploys all canisters, preserving their state. +Deploys or redeploys all canisters, preserving their state. This also runs automatically every time the Codespace starts. ```bash icp deploy @@ -27,3 +27,7 @@ Reinstalls all canisters from scratch, wiping their state. The network keeps run ```bash icp deploy --mode reinstall -y ``` + +## Note for non-SPA frontends + +Frontend URLs use a `?canisterId=` query parameter for routing. This works correctly for single-page apps (all navigation stays client-side). If your frontend uses real path-based navigation where clicking a link triggers a new browser request (e.g. navigating to `/page2`), the query parameter will be dropped and the gateway will not know which canister to serve. Subdomain-based routing is not available in Codespaces because GitHub's TLS certificate only covers one subdomain level, making `.-8000.app.github.dev` invalid. diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index 82bc5a1be..9254e4622 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -11,15 +11,6 @@ "5173": { "label": "Frontend", "onAutoForward": "openBrowser" - }, - "7863": { - "onAutoForward": "ignore" - }, - "7864": { - "onAutoForward": "ignore" - }, - "7865": { - "onAutoForward": "ignore" } }, "postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh", @@ -31,7 +22,10 @@ ], "settings": { "git.openRepositoryInParentFolders": "always", - "workbench.startupEditor": "none" + "workbench.startupEditor": "none", + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor" + } } } } diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 1574e353c..a84b26eab 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -11,15 +11,6 @@ "5173": { "label": "Frontend", "onAutoForward": "openBrowser" - }, - "7863": { - "onAutoForward": "ignore" - }, - "7864": { - "onAutoForward": "ignore" - }, - "7865": { - "onAutoForward": "ignore" } }, "postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh", @@ -31,7 +22,10 @@ ], "settings": { "git.openRepositoryInParentFolders": "always", - "workbench.startupEditor": "none" + "workbench.startupEditor": "none", + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor" + } } } } diff --git a/.devcontainer/scripts/postAttach.sh b/.devcontainer/scripts/postAttach.sh index fff2b2c2b..8aa6f2ae8 100755 --- a/.devcontainer/scripts/postAttach.sh +++ b/.devcontainer/scripts/postAttach.sh @@ -1,21 +1,10 @@ #!/bin/bash -echo "ICP local network is running on port 8000." -echo "" - -CANISTER_JSON=$(icp canister status --json 2>/dev/null) - -if [ -z "$CANISTER_JSON" ]; then - echo "Deploying canisters..." - icp deploy - echo "" - echo "Deployment complete." -fi +code CODESPACE.md +echo "Deploying canisters..." +icp deploy echo "" echo "Access URLs:" echo "" bash /workspaces/examples/.devcontainer/scripts/show-urls.sh - -echo "" -code CODESPACE.md From d4ab1f9352945df65ef032c368cc12ea9543fd17 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 08:13:55 +0200 Subject: [PATCH 27/55] chore: update port labels to ICP and Vite Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/motoko-who-am-i/devcontainer.json | 4 ++-- .devcontainer/rust-who-am-i/devcontainer.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.devcontainer/motoko-who-am-i/devcontainer.json b/.devcontainer/motoko-who-am-i/devcontainer.json index 9254e4622..3ff50e8d8 100644 --- a/.devcontainer/motoko-who-am-i/devcontainer.json +++ b/.devcontainer/motoko-who-am-i/devcontainer.json @@ -5,11 +5,11 @@ "forwardPorts": [8000, 5173], "portsAttributes": { "8000": { - "label": "ICP local network", + "label": "ICP", "onAutoForward": "ignore" }, "5173": { - "label": "Frontend", + "label": "Vite", "onAutoForward": "openBrowser" } }, diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index a84b26eab..28f7d667d 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -5,11 +5,11 @@ "forwardPorts": [8000, 5173], "portsAttributes": { "8000": { - "label": "ICP local network", + "label": "ICP", "onAutoForward": "ignore" }, "5173": { - "label": "Frontend", + "label": "Vite", "onAutoForward": "openBrowser" } }, From e8cc74044efab1a777025774920098726d764d1a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 08:22:23 +0200 Subject: [PATCH 28/55] feat: add root devcontainer for local multi-example development MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uses the combined Motoko + Rust image for contributors and explorers who clone the full repo. No lifecycle scripts — intended for local Dev Containers use, not Codespaces. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/devcontainer.json | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..10f129b7e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,31 @@ +{ + "name": "ICP Examples (Motoko + Rust)", + "image": "ghcr.io/marc0olo/icp-dev-env-all:dev", + "workspaceFolder": "/workspaces/examples", + "forwardPorts": [8000, 5173], + "portsAttributes": { + "8000": { + "label": "ICP", + "onAutoForward": "ignore" + }, + "5173": { + "label": "Vite", + "onAutoForward": "openBrowser" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "dfinity-foundation.vscode-motoko", + "rust-analyzer" + ], + "settings": { + "git.openRepositoryInParentFolders": "always", + "workbench.startupEditor": "none", + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor" + } + } + } + } +} From 57e1fb26b0b5753662ad2f85a441044f68ccc5da Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 08:24:11 +0200 Subject: [PATCH 29/55] fix: use fully qualified rust-lang.rust-analyzer extension ID Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/devcontainer.json | 2 +- .devcontainer/rust-who-am-i/devcontainer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 10f129b7e..efc0c7ef2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,7 +17,7 @@ "vscode": { "extensions": [ "dfinity-foundation.vscode-motoko", - "rust-analyzer" + "rust-lang.rust-analyzer" ], "settings": { "git.openRepositoryInParentFolders": "always", diff --git a/.devcontainer/rust-who-am-i/devcontainer.json b/.devcontainer/rust-who-am-i/devcontainer.json index 28f7d667d..ba2260fc5 100644 --- a/.devcontainer/rust-who-am-i/devcontainer.json +++ b/.devcontainer/rust-who-am-i/devcontainer.json @@ -18,7 +18,7 @@ "customizations": { "vscode": { "extensions": [ - "rust-analyzer" + "rust-lang.rust-analyzer" ], "settings": { "git.openRepositoryInParentFolders": "always", From 3da33785906a55c97a379c1b1be15083dec3720a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:12:03 +0200 Subject: [PATCH 30/55] docs: update README with icp-cli, Codespaces, and Dev Containers - Replace dfx install instructions with icp-cli - Document GitHub Codespaces (per-example badges) and root Dev Container - Remove Gitpod reference - Fix all docs URLs to docs.internetcomputer.org - Demote ICP Ninja to a brief mention Co-Authored-By: Claude Sonnet 4.6 --- README.md | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 191a5d00b..295126e58 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Internet Computer sample applications +# Internet Computer sample applications Get started building on ICP with the sample applications in this repository. From this repository, you can deploy, download, clone, fork, or share sample projects. @@ -14,7 +14,7 @@ Code samples are organized by programming language: - [Rust](https://github.com/dfinity/examples/tree/master/rust) - [C](https://github.com/dfinity/examples/tree/master/c) -Some examples include frontends written in a variety of frameworks, such as React, JavaScript, etc. +Some examples include frontends written in a variety of frameworks such as React, JavaScript, etc. Additional frontend samples can be found in the following folders: @@ -22,45 +22,45 @@ Additional frontend samples can be found in the following folders: - [HTML](https://github.com/dfinity/examples/tree/master/hosting) - [Unity](https://github.com/dfinity/examples/tree/master/native-apps) -## Deploying samples +## Try in browser -### ICP Ninja +Many examples include a GitHub Codespaces badge in their README. Clicking it opens a pre-configured environment with the ICP toolchain installed — the local network starts and canisters are deployed automatically. No local setup required. -You can open and deploy examples with [ICP Ninja](https://icp.ninja/), a web-based tool that allows you to create and manage Internet Computer projects without downloading any tools or setting up a local environment. +Browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). -To contribute an example that will be featured on ICP Ninja, check out the [NINJA_CONTRIBUTING.md](./NINJA_CONTRIBUTING.md) file. +## Local development -### GitHub Codespaces or Gitpod +### Dev Containers -This repo can be opened in a web-based developer environment such as [GitHub Codespaces](https://github.com/codespaces) or [Gitpod](https://www.gitpod.io/), allowing you to edit and deploy the sample projects without downloading any tools or setting up a local environment. +Open the repo root in [VS Code](https://code.visualstudio.com/) with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) to get a pre-configured environment with the full ICP toolchain for both Motoko and Rust. VS Code will prompt you to reopen in the container automatically. -[Get started with GitHub codespaces](https://internetcomputer.org/docs/current/developer-docs/developer-tools/ide/codespaces). +```bash +git clone https://github.com/dfinity/examples.git +``` -[Get started with Gitpod](https://internetcomputer.org/docs/current/developer-docs/developer-tools/ide/gitpod). +Then navigate into an example and follow its README to deploy. -### dfx +> **Note:** The per-example devcontainer configs are designed for GitHub Codespaces. For local Dev Container use, always open the repo root. -dfx is a command-line tool used to create, deploy. and manage projects on ICP. To download and use dfx with this examples repo, run the following commands locally (macOS/Linux systems): +### Command line -``` +Install [icp-cli](https://cli.internetcomputer.org), clone the repo, navigate into an example, and follow its README: + +```bash git clone https://github.com/dfinity/examples.git -cd examples -sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)" +cd examples// ``` -Then, navigate into the folder of the sample that you want to use and follow the project's README instructions to setup and deploy the sample code. - - -## Resources - -- [ICP Developer Docs](https://internetcomputer.org/docs/current/home) +### ICP Ninja -- [Overview of ICP](https://internetcomputer.org/docs/current/developer-docs/getting-started/overview-of-icp) +You can also open and deploy examples with [ICP Ninja](https://icp.ninja/), a web-based tool that requires no local setup. To contribute an example to ICP Ninja, see [NINJA_CONTRIBUTING.md](./NINJA_CONTRIBUTING.md). -- [Installing dfx](https://internetcomputer.org/docs/current/developer-docs/getting-started/install/) +## Resources -- [Developer tools](https://internetcomputer.org/docs/current/developer-docs/developer-tools/dev-tools-overview) +- [Quickstart](https://docs.internetcomputer.org/getting-started/quickstart) +- [Developer tools](https://docs.internetcomputer.org/developer-tools) +- [icp-cli](https://cli.internetcomputer.org) ## Security considerations and best practices -If you base your application on one of these examples, we recommend you familiarize yourself with and adhere to the [security best practices](https://internetcomputer.org/docs/current/references/security/) for developing on the Internet Computer. The examples provided here may not implement all the best practices. +If you base your application on one of these examples, we recommend you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/building-apps/security/overview) for developing on the Internet Computer. The examples provided here may not implement all the best practices. From f7e35421a5ac24eb96f4be846868672a891fc88e Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:13:53 +0200 Subject: [PATCH 31/55] docs: remove ICP Ninja section and fix security best practices URL Co-Authored-By: Claude Sonnet 4.6 --- README.md | 6 +----- motoko/who_am_i/README.md | 2 +- rust/who_am_i/README.md | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 295126e58..6d39e1b69 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,6 @@ git clone https://github.com/dfinity/examples.git cd examples// ``` -### ICP Ninja - -You can also open and deploy examples with [ICP Ninja](https://icp.ninja/), a web-based tool that requires no local setup. To contribute an example to ICP Ninja, see [NINJA_CONTRIBUTING.md](./NINJA_CONTRIBUTING.md). - ## Resources - [Quickstart](https://docs.internetcomputer.org/getting-started/quickstart) @@ -63,4 +59,4 @@ You can also open and deploy examples with [ICP Ninja](https://icp.ninja/), a we ## Security considerations and best practices -If you base your application on one of these examples, we recommend you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/building-apps/security/overview) for developing on the Internet Computer. The examples provided here may not implement all the best practices. +If you base your application on one of these examples, we recommend you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for developing on the Internet Computer. The examples provided here may not implement all the best practices. diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index 925a0d78d..1960529e4 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -57,4 +57,4 @@ $(mops toolchain bin moc) --idl $(mops sources) -o src/internet_identity_app_bac ## Security considerations and best practices -If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices. +If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for developing on ICP. This example may not implement all the best practices. diff --git a/rust/who_am_i/README.md b/rust/who_am_i/README.md index e15ae849d..a1dc13c25 100644 --- a/rust/who_am_i/README.md +++ b/rust/who_am_i/README.md @@ -57,4 +57,4 @@ candid-extractor target/wasm32-unknown-unknown/release/internet_identity_app_bac ## Security considerations and best practices -If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices. +If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for developing on ICP. This example may not implement all the best practices. From 638669e4299f45c9108a31bf78fd8db51cce0ce6 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:15:13 +0200 Subject: [PATCH 32/55] chore: remove NINJA_CONTRIBUTING.md Co-Authored-By: Claude Sonnet 4.6 --- NINJA_CONTRIBUTING.md | 151 ------------------------------------------ 1 file changed, 151 deletions(-) delete mode 100644 NINJA_CONTRIBUTING.md diff --git a/NINJA_CONTRIBUTING.md b/NINJA_CONTRIBUTING.md deleted file mode 100644 index 8c7a6e393..000000000 --- a/NINJA_CONTRIBUTING.md +++ /dev/null @@ -1,151 +0,0 @@ -# Contributing a project to ICP Ninja - -We recommend to build your example directly within ICP Ninja, such that it starts out with the correct tooling, structure and configs. -If you do that, your project will naturally be in the correct format and can be easily added to ICP Ninja. -Once the example is done, you can download the source files from ICP Ninja. -Alternatively, you can start with an existing Ninja project from this repo (see CODEOWNERS file for Ninja examples, e.g. `motoko/hello_world` or `rust/hello_world`) and modify it. - -Ideally, your project should have a frontend and backend. - -### Where to place your example -* `/motoko` for a Motoko project -* `/rust` for a Rust project -* `/hosting` for a frontend-only project - -### Compilation requirements -* Make sure the project compiles and runs with `dfx deploy` inside the `ghcr.io/dfinity/icp-dev-env-slim:22` container. -* Make sure there are no custom scripts for building or running the project, as ICP Ninja has no terminal! -* Make sure `npm run dev` works and the canister can be called through the browser (if applicable, this is if users download the project and run it locally). -* Make sure II login works (if applicable). -* If you use Rust, make sure the project has `ic_cdk::export_candid!();` in the `lib.rs` file, such that the Candid interface can be auto derived. -* If you use Motoko, use Mops as the package manager. - -## Preparing the PR -1. Add your project in the `CODEOWNERS` file, with your team as codeowner. -2. Add your project to the matrix in `.github/workflows/ninja_pr_checks.yml` to run PR tests. -3. Add a `README.md` file, copy the `BUILD.md` and `devcontainer.json` files. -4. Request review from the `@dfinity/ninja-devs` team if it is not added automatically. - -## Submit a PR to the ICP Ninja repo -1. Add your newly added project to `frontend/public/projects.json` -2. Bump the commit hash in `submodules/examples` to a commit hash after your PR has been merged into the examples repo. -3. Ask the Ninja team or AI to give you a beautiful image for your project - -## Templates - -### Recommended `dfx.json` for a Rust canister: - -```json -{ - "canisters": { - "backend": { - "candid": "backend/backend.did", - "type": "custom", - "shrink": true, - "gzip": true, - "wasm": "target/wasm32-unknown-unknown/release/backend.wasm", - "build": [ - "cargo build --target wasm32-unknown-unknown --release -p backend", - "candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/backend.did" - ], - "metadata": [ - { - "name": "candid:service" - } - ] - } - }, - "output_env_file": ".env" -} -``` - -### Recommended `dfx.json` for a Motoko canister: - -```json -{ - "canisters": { - "backend": { - "main": "backend/app.mo", - "type": "motoko", - "args": "--enhanced-orthogonal-persistence" - }, - }, - "output_env_file": ".env", - "defaults": { - "build": { - "packtool": "mops sources" - } - } -} - -``` - -### Recommended `dfx.json` for a frontend/asset canister: - -```json -{ - "canisters": { - "frontend": { - "dependencies": ["backend"], - "frontend": { - "entrypoint": "frontend/index.html" - }, - "source": ["frontend/dist"], - "type": "assets" - } - }, - "output_env_file": ".env" -} -``` - -### Recommended `package.json` -Make sure to install all packages necessary in the prebuild step of your `package.json`, e.g., with - -```js -"scripts": { - "prebuild": "npm i --include=dev && ...", - ... - }, -``` - -### Recommended `vite.config.js` - -```js -import react from '@vitejs/plugin-react'; -import { defineConfig } from 'vite'; -import { fileURLToPath, URL } from 'url'; -import environment from 'vite-plugin-environment'; - -export default defineConfig({ - base: './', - plugins: [react(), environment('all', { prefix: 'CANISTER_' }), environment('all', { prefix: 'DFX_' })], - envDir: '../', - define: { - 'process.env': process.env - }, - optimizeDeps: { - esbuildOptions: { - define: { - global: 'globalThis' - } - } - }, - resolve: { - alias: [ - { - find: 'declarations', - replacement: fileURLToPath(new URL('../src/declarations', import.meta.url)) - } - ] - }, - server: { - proxy: { - '/api': { - target: 'http://127.0.0.1:4943', - changeOrigin: true - } - }, - host: '127.0.0.1' - } -}); -``` From 89eff98184571e6882c273f92e1dbb46976fdad8 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:16:48 +0200 Subject: [PATCH 33/55] docs: refurbish ADDING_AN_EXAMPLE.md - Remove ICP Ninja reference (NINJA_CONTRIBUTING.md deleted) - Replace dfx references with icp-cli - Add Codespaces devcontainer guidance for new examples Co-Authored-By: Claude Sonnet 4.6 --- ADDING_AN_EXAMPLE.md | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/ADDING_AN_EXAMPLE.md b/ADDING_AN_EXAMPLE.md index 2aa0a0529..509dd68af 100644 --- a/ADDING_AN_EXAMPLE.md +++ b/ADDING_AN_EXAMPLE.md @@ -1,40 +1,47 @@ # How to add a new example -> For ICP Ninja: check [NINJA_CONTRIBUTING.md](./NINJA_CONTRIBUTING.md) for how to contribute a project to ICP Ninja. - Each example should be available in both Rust and Motoko variations, implementing the same Candid interface (and, ideally, semantics). -To illustrate the pattern, this repo now contains one such example, project `hello_world`: +To illustrate the pattern, this repo contains one such example, project `hello_world`: -`motoko/hello_world` -`rust/hello_world` +``` +motoko/hello_world +rust/hello_world +``` -When adding a new `dfx` generated project, make sure to delete its GitHub metadata files (`.gitignore`, `.git` etc). +When adding a new project, make sure to delete any generated GitHub metadata files (`.gitignore`, `.git` etc). Each project should include a language-specific README.md that also links to the corresponding README.md of its counterpart in another language, making it easy for language-curious readers to explore both implementations. -## CI +## Codespaces -Apart from the standard `dfx` material, each project should provide a `Makefile` used by GitHub Actions CI to run (very) basic tests. +To make the example available in GitHub Codespaces, add a devcontainer config under `.devcontainer/-/devcontainer.json` pointing to the appropriate image: + +- Motoko: `ghcr.io/dfinity/icp-dev-env-motoko` +- Rust: `ghcr.io/dfinity/icp-dev-env-rust` + +Add a Codespaces badge to the example's README pointing to the new devcontainer config. See the existing `who_am_i` examples for reference. + +## CI -For each example, there is a single CI file with four build actions to produce Darwin and Linux builds and tests of the Motoko/Rust, projects, such as: +Each project should provide a `Makefile` used by GitHub Actions CI to run basic tests. For each example, there is a single CI file: ``` .github/workflows/hello_world.yml ``` -Implementing the GitHub action will ensure it runs in CI and helps keep examples in sync with releases of `dfx`. +Implementing the GitHub action ensures it runs in CI and helps keep examples in sync with [icp-cli](https://cli.internetcomputer.org) releases. ## Documentation -For your new example to be included in the ICP developer documentation, make sure you update the `samples` submodule in the portal repository to point to the latest commit in this examples repository using the following command: +For your new example to be included in the ICP developer documentation, update the `samples` submodule in the portal repository to point to the latest commit: ```bash git submodule update --remote submodules/samples ``` -After you run this command, commit the changes to a new PR to have them merged into the portal repo. +After running this command, commit the changes to a new PR in the portal repo. -## Issues +## Notes -While this structure leads to some duplication (especially shared components like frontend code) it ensures that Motoko users can focus solely on Motoko-specific content, and likewise for Rust users. It also enables easily finding language-specific examples when a given use case is not easily supported in the other language. +While this structure leads to some duplication (especially shared frontend code) it ensures that Motoko users can focus solely on Motoko-specific content, and likewise for Rust users. It also enables easily finding language-specific examples when a given use case is not easily supported in the other language. From cd8c55ef7490531af50e02d9e3eda1ec218b69f7 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:17:28 +0200 Subject: [PATCH 34/55] docs: remove outdated portal submodule documentation section Co-Authored-By: Claude Sonnet 4.6 --- ADDING_AN_EXAMPLE.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ADDING_AN_EXAMPLE.md b/ADDING_AN_EXAMPLE.md index 509dd68af..da2fd12b1 100644 --- a/ADDING_AN_EXAMPLE.md +++ b/ADDING_AN_EXAMPLE.md @@ -32,16 +32,6 @@ Each project should provide a `Makefile` used by GitHub Actions CI to run basic Implementing the GitHub action ensures it runs in CI and helps keep examples in sync with [icp-cli](https://cli.internetcomputer.org) releases. -## Documentation - -For your new example to be included in the ICP developer documentation, update the `samples` submodule in the portal repository to point to the latest commit: - -```bash -git submodule update --remote submodules/samples -``` - -After running this command, commit the changes to a new PR in the portal repo. - ## Notes While this structure leads to some duplication (especially shared frontend code) it ensures that Motoko users can focus solely on Motoko-specific content, and likewise for Rust users. It also enables easily finding language-specific examples when a given use case is not easily supported in the other language. From d9680c123d3cc0b1a88dcfea92257272e9dd9eaf Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:18:09 +0200 Subject: [PATCH 35/55] docs: remove ICP Ninja reference and update example submission guidance Co-Authored-By: Claude Sonnet 4.6 --- .github/CONTRIBUTING.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ed0a23cf4..96cf519ac 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,7 +1,5 @@ # Contributing -> For ICP Ninja: check [NINJA_CONTRIBUTING.md](./../NINJA_CONTRIBUTING.md) for how to contribute a project to ICP Ninja. - Thank you for your interest in contributing to example apps for the Internet Computer. By participating in this project, you agree to abide by our [Code of Conduct](./CODE_OF_CONDUCT.md). @@ -116,4 +114,4 @@ To open a new issue: ### Submitting your own example -We're not accepting community examples at this time -- we have something better planned. +See [ADDING_AN_EXAMPLE.md](./../ADDING_AN_EXAMPLE.md) for guidelines on how to structure and submit a new example. From cc1b1ca5262726210dc30e02c9de4b48a5e4146f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:32:01 +0200 Subject: [PATCH 36/55] feat: add Codespaces support to hello_world examples - Add devcontainer configs for motoko-hello-world and rust-hello-world - Add CODESPACE.md symlinks - Replace ICP Ninja sections with Codespaces badge in both READMEs - Remove dfx.json, BUILD.md, and old .devcontainer from each example - Add hello_world.yml CI workflow using icp-cli - Remove hello_world entries from ninja_pr_checks.yml - Fix security best practices and icp-cli URLs Co-Authored-By: Claude Sonnet 4.6 --- .../motoko-hello-world/devcontainer.json | 32 +++++++++++++++ .../rust-hello-world/devcontainer.json | 32 +++++++++++++++ .github/workflows/hello_world.yml | 39 +++++++++++++++++++ .github/workflows/ninja_pr_checks.yml | 2 - .../.devcontainer/devcontainer.json | 20 ---------- motoko/hello_world/BUILD.md | 26 ------------- motoko/hello_world/CODESPACE.md | 1 + motoko/hello_world/README.md | 24 +++--------- motoko/hello_world/dfx.json | 23 ----------- .../.devcontainer/devcontainer.json | 20 ---------- rust/hello_world/BUILD.md | 26 ------------- rust/hello_world/CODESPACE.md | 1 + rust/hello_world/README.md | 25 +++--------- rust/hello_world/dfx.json | 29 -------------- 14 files changed, 117 insertions(+), 183 deletions(-) create mode 100644 .devcontainer/motoko-hello-world/devcontainer.json create mode 100644 .devcontainer/rust-hello-world/devcontainer.json create mode 100644 .github/workflows/hello_world.yml delete mode 100644 motoko/hello_world/.devcontainer/devcontainer.json delete mode 100644 motoko/hello_world/BUILD.md create mode 120000 motoko/hello_world/CODESPACE.md delete mode 100644 motoko/hello_world/dfx.json delete mode 100644 rust/hello_world/.devcontainer/devcontainer.json delete mode 100644 rust/hello_world/BUILD.md create mode 120000 rust/hello_world/CODESPACE.md delete mode 100644 rust/hello_world/dfx.json diff --git a/.devcontainer/motoko-hello-world/devcontainer.json b/.devcontainer/motoko-hello-world/devcontainer.json new file mode 100644 index 000000000..4566d6ece --- /dev/null +++ b/.devcontainer/motoko-hello-world/devcontainer.json @@ -0,0 +1,32 @@ +{ + "name": "Hello World (Motoko)", + "image": "ghcr.io/marc0olo/icp-dev-env-motoko:dev", + "workspaceFolder": "/workspaces/examples/motoko/hello_world", + "forwardPorts": [8000, 5173], + "portsAttributes": { + "8000": { + "label": "ICP", + "onAutoForward": "ignore" + }, + "5173": { + "label": "Vite", + "onAutoForward": "openBrowser" + } + }, + "postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh", + "postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh", + "customizations": { + "vscode": { + "extensions": [ + "dfinity-foundation.vscode-motoko" + ], + "settings": { + "git.openRepositoryInParentFolders": "always", + "workbench.startupEditor": "none", + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor" + } + } + } + } +} diff --git a/.devcontainer/rust-hello-world/devcontainer.json b/.devcontainer/rust-hello-world/devcontainer.json new file mode 100644 index 000000000..9676252c6 --- /dev/null +++ b/.devcontainer/rust-hello-world/devcontainer.json @@ -0,0 +1,32 @@ +{ + "name": "Hello World (Rust)", + "image": "ghcr.io/marc0olo/icp-dev-env-rust:dev", + "workspaceFolder": "/workspaces/examples/rust/hello_world", + "forwardPorts": [8000, 5173], + "portsAttributes": { + "8000": { + "label": "ICP", + "onAutoForward": "ignore" + }, + "5173": { + "label": "Vite", + "onAutoForward": "openBrowser" + } + }, + "postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh", + "postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh", + "customizations": { + "vscode": { + "extensions": [ + "rust-lang.rust-analyzer" + ], + "settings": { + "git.openRepositoryInParentFolders": "always", + "workbench.startupEditor": "none", + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor" + } + } + } + } +} diff --git a/.github/workflows/hello_world.yml b/.github/workflows/hello_world.yml new file mode 100644 index 000000000..cd874d931 --- /dev/null +++ b/.github/workflows/hello_world.yml @@ -0,0 +1,39 @@ +name: hello_world + +on: + push: + branches: + - master + pull_request: + paths: + - motoko/hello_world/** + - rust/hello_world/** + - .github/workflows/hello_world.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + motoko-hello_world: + runs-on: ubuntu-24.04 + container: ghcr.io/marc0olo/icp-dev-env-motoko:dev + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy + working-directory: motoko/hello_world + run: | + icp network start -d + mops install + icp deploy + + rust-hello_world: + runs-on: ubuntu-24.04 + container: ghcr.io/marc0olo/icp-dev-env-rust:dev + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy + working-directory: rust/hello_world + run: | + icp network start -d + icp deploy diff --git a/.github/workflows/ninja_pr_checks.yml b/.github/workflows/ninja_pr_checks.yml index a5a39e7f7..3a5b63952 100644 --- a/.github/workflows/ninja_pr_checks.yml +++ b/.github/workflows/ninja_pr_checks.yml @@ -41,7 +41,6 @@ jobs: ["EVM Block Explorer (Motoko)"]="motoko/evm_block_explorer" ["FileVault (Motoko)"]="motoko/filevault" ["Flying Ninja (Motoko)"]="motoko/flying_ninja" - ["Hello World (Motoko)"]="motoko/hello_world" ["LLM Chatbot (Motoko)"]="motoko/llm_chatbot" ["Query Stats (Motoko)"]="motoko/query_stats" ["Send HTTP Get (Motoko)"]="motoko/send_http_get" @@ -61,7 +60,6 @@ jobs: ["EVM Block Explorer (Rust)"]="rust/evm_block_explorer" ["Flying Ninja (Rust)"]="rust/flying_ninja" ["Guards (Rust)"]="rust/guards" - ["Hello World (Rust)"]="rust/hello_world" ["LLM Chatbot (Rust)"]="rust/llm_chatbot" ["Performance Counters (Rust)"]="rust/performance_counters" ["Periodic Tasks (Rust)"]="rust/periodic_tasks" diff --git a/motoko/hello_world/.devcontainer/devcontainer.json b/motoko/hello_world/.devcontainer/devcontainer.json deleted file mode 100644 index ebb0b8bcc..000000000 --- a/motoko/hello_world/.devcontainer/devcontainer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "ICP Dev Environment", - "image": "ghcr.io/dfinity/icp-dev-env-slim:22", - "forwardPorts": [4943, 5173], - "portsAttributes": { - "4943": { - "label": "dfx", - "onAutoForward": "ignore" - }, - "5173": { - "label": "vite", - "onAutoForward": "openBrowser" - } - }, - "customizations": { - "vscode": { - "extensions": ["dfinity-foundation.vscode-motoko"] - } - } -} diff --git a/motoko/hello_world/BUILD.md b/motoko/hello_world/BUILD.md deleted file mode 100644 index 8877d3ed2..000000000 --- a/motoko/hello_world/BUILD.md +++ /dev/null @@ -1,26 +0,0 @@ -# Continue building locally - -Projects deployed through ICP Ninja are temporary; they will only be live for 30 minutes before they are removed. To continue building locally, follow these steps. - -### 1. Install developer tools - -Install [Node.js](https://nodejs.org/en/download/) and [icp-cli](https://cli.internetcomputer.org): - -```bash -npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm -``` - -Then navigate into your project's directory that you downloaded from ICP Ninja. - -### 2. Deploy locally - -Start the local network and deploy the project: - -```bash -icp network start -d -icp deploy -``` - -## Additional examples - -Additional code examples and sample applications can be found in the [DFINITY examples repo](https://github.com/dfinity/examples). diff --git a/motoko/hello_world/CODESPACE.md b/motoko/hello_world/CODESPACE.md new file mode 120000 index 000000000..17b02d2b4 --- /dev/null +++ b/motoko/hello_world/CODESPACE.md @@ -0,0 +1 @@ +../../.devcontainer/CODESPACE.md \ No newline at end of file diff --git a/motoko/hello_world/README.md b/motoko/hello_world/README.md index 4afb8738b..64ebd885d 100644 --- a/motoko/hello_world/README.md +++ b/motoko/hello_world/README.md @@ -21,13 +21,11 @@ The `/backend` folder contains the Motoko canister, `app.mo`. The `/frontend` fo Edit the `mops.toml` file to add [Motoko dependencies](https://mops.one/) to the project. -## Deploying from ICP Ninja +## Try in browser -This example can be deployed directly from [ICP Ninja](https://icp.ninja), a browser-based IDE for ICP. To continue developing locally after deploying from ICP Ninja, see [BUILD.md](BUILD.md). +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=.devcontainer%2Fmotoko-hello-world%2Fdevcontainer.json&ref=feat%2Fcodespaces) -[![Open in ICP Ninja](https://icp.ninja/assets/open.svg)](https://icp.ninja/i?g=https://github.com/dfinity/examples/motoko/hello_world) - -> **Note:** ICP Ninja currently uses `dfx` under the hood, which is why this example includes a `dfx.json` configuration file. `dfx` is the legacy CLI, being superseded by [icp-cli](https://cli.internetcomputer.org), which is what developers should use for local development. +Opens a pre-configured environment with the ICP toolchain installed. The local network starts and canisters are deployed automatically. You can browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). ## Build and deploy from the command line @@ -47,24 +45,14 @@ cd examples/motoko/hello_world ### Deployment -Start the local network: +Start the local network and deploy: ```bash icp network start -d -``` - -Deploy the canisters: - -```bash +mops install icp deploy ``` -Stop the local network when done: - -```bash -icp network stop -``` - ## Updating the Candid interface The `backend/backend.did` file defines the backend canister's public interface. The frontend TypeScript bindings are auto-generated from this file during the frontend build. @@ -77,4 +65,4 @@ $(mops toolchain bin moc) --idl -o backend/backend.did backend/app.mo ## Security considerations and best practices -If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices. +If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for developing on ICP. This example may not implement all the best practices. diff --git a/motoko/hello_world/dfx.json b/motoko/hello_world/dfx.json deleted file mode 100644 index d073ee09f..000000000 --- a/motoko/hello_world/dfx.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "canisters": { - "backend": { - "main": "backend/app.mo", - "type": "motoko", - "args": "--enhanced-orthogonal-persistence" - }, - "frontend": { - "dependencies": ["backend"], - "frontend": { - "entrypoint": "frontend/index.html" - }, - "source": ["frontend/dist"], - "type": "assets" - } - }, - "output_env_file": ".env", - "defaults": { - "build": { - "packtool": "mops sources" - } - } -} diff --git a/rust/hello_world/.devcontainer/devcontainer.json b/rust/hello_world/.devcontainer/devcontainer.json deleted file mode 100644 index ebb0b8bcc..000000000 --- a/rust/hello_world/.devcontainer/devcontainer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "ICP Dev Environment", - "image": "ghcr.io/dfinity/icp-dev-env-slim:22", - "forwardPorts": [4943, 5173], - "portsAttributes": { - "4943": { - "label": "dfx", - "onAutoForward": "ignore" - }, - "5173": { - "label": "vite", - "onAutoForward": "openBrowser" - } - }, - "customizations": { - "vscode": { - "extensions": ["dfinity-foundation.vscode-motoko"] - } - } -} diff --git a/rust/hello_world/BUILD.md b/rust/hello_world/BUILD.md deleted file mode 100644 index ffb6557ec..000000000 --- a/rust/hello_world/BUILD.md +++ /dev/null @@ -1,26 +0,0 @@ -# Continue building locally - -Projects deployed through ICP Ninja are temporary; they will only be live for 30 minutes before they are removed. To continue building locally, follow these steps. - -### 1. Install developer tools - -Install [Node.js](https://nodejs.org/en/download/) and [icp-cli](https://cli.icp.build): - -```bash -npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm -``` - -Then navigate into your project's directory that you downloaded from ICP Ninja. - -### 2. Deploy locally - -Start the local network and deploy the project: - -```bash -icp network start -d -icp deploy -``` - -## Additional examples - -Additional code examples and sample applications can be found in the [DFINITY examples repo](https://github.com/dfinity/examples). diff --git a/rust/hello_world/CODESPACE.md b/rust/hello_world/CODESPACE.md new file mode 120000 index 000000000..17b02d2b4 --- /dev/null +++ b/rust/hello_world/CODESPACE.md @@ -0,0 +1 @@ +../../.devcontainer/CODESPACE.md \ No newline at end of file diff --git a/rust/hello_world/README.md b/rust/hello_world/README.md index 468adb75d..26d10c261 100644 --- a/rust/hello_world/README.md +++ b/rust/hello_world/README.md @@ -17,20 +17,18 @@ The frontend provides a simple form where users can enter their name and receive The `/backend` folder contains the Rust canister source code. The `/frontend` folder contains web assets for the application's user interface. The user interface is written with plain JavaScript, but any frontend framework can be used. -## Deploying from ICP Ninja +## Try in browser -This example can be deployed directly from [ICP Ninja](https://icp.ninja), a browser-based IDE for ICP. To continue developing locally after deploying from ICP Ninja, see [BUILD.md](BUILD.md). +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=.devcontainer%2Frust-hello-world%2Fdevcontainer.json&ref=feat%2Fcodespaces) -[![Open in ICP Ninja](https://icp.ninja/assets/open.svg)](https://icp.ninja/i?g=https://github.com/dfinity/examples/rust/hello_world) - -> **Note:** ICP Ninja currently uses `dfx` under the hood, which is why this example includes a `dfx.json` configuration file. `dfx` is the legacy CLI, being superseded by [icp-cli](https://cli.icp.build), which is what developers should use for local development. +Opens a pre-configured environment with the ICP toolchain installed. The local network starts and canisters are deployed automatically. You can browse all your Codespaces at [github.com/codespaces](https://github.com/codespaces). ## Build and deploy from the command line ### Prerequisites - [x] Install [Node.js](https://nodejs.org/en/download/) -- [x] Install [icp-cli](https://cli.icp.build): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` +- [x] Install [icp-cli](https://cli.internetcomputer.org): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` ### Install @@ -43,24 +41,13 @@ cd examples/rust/hello_world ### Deployment -Start the local network: +Start the local network and deploy: ```bash icp network start -d -``` - -Deploy the canisters: - -```bash icp deploy ``` -Stop the local network when done: - -```bash -icp network stop -``` - ## Updating the Candid interface The `backend/backend.did` file defines the backend canister's public interface. The frontend TypeScript bindings are auto-generated from this file during the frontend build. @@ -74,4 +61,4 @@ candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/ba ## Security considerations and best practices -If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices. +If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for developing on ICP. This example may not implement all the best practices. diff --git a/rust/hello_world/dfx.json b/rust/hello_world/dfx.json deleted file mode 100644 index 6ed5a89e5..000000000 --- a/rust/hello_world/dfx.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "canisters": { - "backend": { - "candid": "backend/backend.did", - "type": "custom", - "shrink": true, - "gzip": true, - "wasm": "target/wasm32-unknown-unknown/release/backend.wasm", - "build": [ - "cargo build --target wasm32-unknown-unknown --release -p backend", - "candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/backend.did" - ], - "metadata": [ - { - "name": "candid:service" - } - ] - }, - "frontend": { - "dependencies": ["backend"], - "frontend": { - "entrypoint": "frontend/index.html" - }, - "source": ["frontend/dist"], - "type": "assets" - } - }, - "output_env_file": ".env" -} From 04a43dc2cca7232ebfad4ccdb3857bfe258e86a9 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:38:38 +0200 Subject: [PATCH 37/55] feat: add Makefiles with tests and CI workflows for hello_world and who_am_i hello_world: tests default greeting, setGreeting/set_greeting + updated output who_am_i: tests whoami returns a principal and is deterministic Creates who_am_i.yml CI workflow; removes hello_world and who_am_i from ninja_pr_checks.yml to avoid duplicate runs. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/hello_world.yml | 6 ++-- .github/workflows/ninja_pr_checks.yml | 2 -- .github/workflows/who_am_i.yml | 41 +++++++++++++++++++++++++++ motoko/hello_world/Makefile | 15 ++++++++++ motoko/who_am_i/Makefile | 14 +++++++++ rust/hello_world/Makefile | 15 ++++++++++ rust/who_am_i/Makefile | 14 +++++++++ 7 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/who_am_i.yml create mode 100644 motoko/hello_world/Makefile create mode 100644 motoko/who_am_i/Makefile create mode 100644 rust/hello_world/Makefile create mode 100644 rust/who_am_i/Makefile diff --git a/.github/workflows/hello_world.yml b/.github/workflows/hello_world.yml index cd874d931..350a5d1b7 100644 --- a/.github/workflows/hello_world.yml +++ b/.github/workflows/hello_world.yml @@ -20,20 +20,22 @@ jobs: container: ghcr.io/marc0olo/icp-dev-env-motoko:dev steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - name: Deploy + - name: Deploy and test working-directory: motoko/hello_world run: | icp network start -d mops install icp deploy + make test rust-hello_world: runs-on: ubuntu-24.04 container: ghcr.io/marc0olo/icp-dev-env-rust:dev steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - name: Deploy + - name: Deploy and test working-directory: rust/hello_world run: | icp network start -d icp deploy + make test diff --git a/.github/workflows/ninja_pr_checks.yml b/.github/workflows/ninja_pr_checks.yml index 3a5b63952..d906d59ea 100644 --- a/.github/workflows/ninja_pr_checks.yml +++ b/.github/workflows/ninja_pr_checks.yml @@ -48,7 +48,6 @@ jobs: ["Superheroes (Motoko)"]="motoko/superheroes" ["Threshold ECDSA (Motoko)"]="motoko/threshold-ecdsa" ["Threshold Schnorr (Motoko)"]="motoko/threshold-schnorr" - ["Who Am I (Motoko)"]="motoko/who_am_i" ["Rust backend (Rust)"]="rust/backend_only" ["Rust backend Wasm64 (Rust)"]="rust/backend_wasm64" ["Basic Bitcoin (Rust)"]="rust/basic_bitcoin" @@ -70,7 +69,6 @@ jobs: ["SIMD (Rust)"]="rust/simd" ["Threshold ECDSA (Rust)"]="rust/threshold-ecdsa" ["Unit Testable Canister (Rust)"]="rust/unit_testable_rust_canister" - ["Who Am I (Rust)"]="rust/who_am_i" ["Photo Gallery (Rust)"]="rust/photo_gallery" ["Inter-canister calls (Rust)"]="rust/inter-canister-calls" ["X.509 (Rust)"]="rust/x509" diff --git a/.github/workflows/who_am_i.yml b/.github/workflows/who_am_i.yml new file mode 100644 index 000000000..2fb2ee409 --- /dev/null +++ b/.github/workflows/who_am_i.yml @@ -0,0 +1,41 @@ +name: who_am_i + +on: + push: + branches: + - master + pull_request: + paths: + - motoko/who_am_i/** + - rust/who_am_i/** + - .github/workflows/who_am_i.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + motoko-who_am_i: + runs-on: ubuntu-24.04 + container: ghcr.io/marc0olo/icp-dev-env-motoko:dev + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy and test + working-directory: motoko/who_am_i + run: | + icp network start -d + mops install + icp deploy + make test + + rust-who_am_i: + runs-on: ubuntu-24.04 + container: ghcr.io/marc0olo/icp-dev-env-rust:dev + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy and test + working-directory: rust/who_am_i + run: | + icp network start -d + icp deploy + make test diff --git a/motoko/hello_world/Makefile b/motoko/hello_world/Makefile new file mode 100644 index 000000000..7f538e890 --- /dev/null +++ b/motoko/hello_world/Makefile @@ -0,0 +1,15 @@ +.PHONY: test + +test: + @echo "--- Testing default greeting ---" + @result=$$(icp canister call backend greet '("World")') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'Hello, World!' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "--- Testing setGreeting ---" + @icp canister call backend setGreeting '("Hi, ")' + @result=$$(icp canister call backend greet '("Alice")') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'Hi, Alice!' && \ + echo "PASS" || (echo "FAIL" && exit 1) diff --git a/motoko/who_am_i/Makefile b/motoko/who_am_i/Makefile new file mode 100644 index 000000000..ebc17ba7a --- /dev/null +++ b/motoko/who_am_i/Makefile @@ -0,0 +1,14 @@ +.PHONY: test + +test: + @echo "--- Testing whoami returns a principal ---" + @result=$$(icp canister call internet_identity_app_backend whoami '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'principal' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "--- Testing whoami is deterministic ---" + @result1=$$(icp canister call internet_identity_app_backend whoami '()') && \ + result2=$$(icp canister call internet_identity_app_backend whoami '()') && \ + [ "$$result1" = "$$result2" ] && \ + echo "PASS: $$result1" || (echo "FAIL: $$result1 != $$result2" && exit 1) diff --git a/rust/hello_world/Makefile b/rust/hello_world/Makefile new file mode 100644 index 000000000..269592eca --- /dev/null +++ b/rust/hello_world/Makefile @@ -0,0 +1,15 @@ +.PHONY: test + +test: + @echo "--- Testing default greeting ---" + @result=$$(icp canister call backend greet '("World")') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'Hello, World!' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "--- Testing set_greeting ---" + @icp canister call backend set_greeting '("Hi, ")' + @result=$$(icp canister call backend greet '("Alice")') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'Hi, Alice!' && \ + echo "PASS" || (echo "FAIL" && exit 1) diff --git a/rust/who_am_i/Makefile b/rust/who_am_i/Makefile new file mode 100644 index 000000000..ebc17ba7a --- /dev/null +++ b/rust/who_am_i/Makefile @@ -0,0 +1,14 @@ +.PHONY: test + +test: + @echo "--- Testing whoami returns a principal ---" + @result=$$(icp canister call internet_identity_app_backend whoami '()') && \ + echo "$$result" && \ + echo "$$result" | grep -q 'principal' && \ + echo "PASS" || (echo "FAIL" && exit 1) + + @echo "--- Testing whoami is deterministic ---" + @result1=$$(icp canister call internet_identity_app_backend whoami '()') && \ + result2=$$(icp canister call internet_identity_app_backend whoami '()') && \ + [ "$$result1" = "$$result2" ] && \ + echo "PASS: $$result1" || (echo "FAIL: $$result1 != $$result2" && exit 1) From 1c50536746b71ceefcf79ffa1d769d4b8dec7bb9 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:53:36 +0200 Subject: [PATCH 38/55] docs: add workflow template, clarify npm run dev, update contributing guide - Add .github/workflow-template.yml as canonical starting point for new examples - Remove mops install from who_am_i CI (handled by icp deploy) - Separate npm run dev from deploy steps in who_am_i READMEs with explanation - Update ADDING_AN_EXAMPLE.md to reference template and Makefile pattern Co-Authored-By: Claude Sonnet 4.6 --- .github/workflow-template.yml | 35 +++++++++++++++++++++++++++++++ .github/workflows/hello_world.yml | 1 - .github/workflows/who_am_i.yml | 1 - ADDING_AN_EXAMPLE.md | 13 +++++++++--- motoko/hello_world/README.md | 1 - motoko/who_am_i/README.md | 6 +++++- rust/who_am_i/README.md | 5 +++++ 7 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 .github/workflow-template.yml diff --git a/.github/workflow-template.yml b/.github/workflow-template.yml new file mode 100644 index 000000000..e7b244c20 --- /dev/null +++ b/.github/workflow-template.yml @@ -0,0 +1,35 @@ +# Workflow template for ICP examples. +# Copy this file to .github/workflows/.yml and replace the placeholders. +# +# PLACEHOLDERS: +# e.g. hello_world +# motoko | rust +# ghcr.io/dfinity/icp-dev-env-motoko | ghcr.io/dfinity/icp-dev-env-rust + +name: + +on: + push: + branches: + - master + pull_request: + paths: + - //** + - .github/workflows/.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + -: + runs-on: ubuntu-24.04 + container: + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy and test + working-directory: / + run: | + icp network start -d + icp deploy + make test diff --git a/.github/workflows/hello_world.yml b/.github/workflows/hello_world.yml index 350a5d1b7..13d7bec8a 100644 --- a/.github/workflows/hello_world.yml +++ b/.github/workflows/hello_world.yml @@ -24,7 +24,6 @@ jobs: working-directory: motoko/hello_world run: | icp network start -d - mops install icp deploy make test diff --git a/.github/workflows/who_am_i.yml b/.github/workflows/who_am_i.yml index 2fb2ee409..ad845724a 100644 --- a/.github/workflows/who_am_i.yml +++ b/.github/workflows/who_am_i.yml @@ -24,7 +24,6 @@ jobs: working-directory: motoko/who_am_i run: | icp network start -d - mops install icp deploy make test diff --git a/ADDING_AN_EXAMPLE.md b/ADDING_AN_EXAMPLE.md index da2fd12b1..084af2565 100644 --- a/ADDING_AN_EXAMPLE.md +++ b/ADDING_AN_EXAMPLE.md @@ -24,13 +24,20 @@ Add a Codespaces badge to the example's README pointing to the new devcontainer ## CI -Each project should provide a `Makefile` used by GitHub Actions CI to run basic tests. For each example, there is a single CI file: +Each project should provide a `Makefile` with a `test` target that runs basic canister tests using `icp canister call`. Each example also needs a GitHub Actions workflow file at `.github/workflows/.yml`. + +Use the workflow template as a starting point: ``` -.github/workflows/hello_world.yml +.github/workflow-template.yml ``` -Implementing the GitHub action ensures it runs in CI and helps keep examples in sync with [icp-cli](https://cli.internetcomputer.org) releases. +Copy it, replace the placeholders, and add the appropriate container image: + +- Motoko: `ghcr.io/dfinity/icp-dev-env-motoko` +- Rust: `ghcr.io/dfinity/icp-dev-env-rust` + +See `hello_world` and `who_am_i` for reference implementations. Workflows run on Linux only using container images — no provision scripts needed. ## Notes diff --git a/motoko/hello_world/README.md b/motoko/hello_world/README.md index 64ebd885d..11965410f 100644 --- a/motoko/hello_world/README.md +++ b/motoko/hello_world/README.md @@ -49,7 +49,6 @@ Start the local network and deploy: ```bash icp network start -d -mops install icp deploy ``` diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index 1960529e4..57a12d0a2 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -36,8 +36,12 @@ Start the local network and deploy: ```bash icp network start -d -mops install icp deploy +``` + +The frontend is served by the asset canister. To run the Vite dev server with hot reload during frontend development: + +```bash npm run dev ``` diff --git a/rust/who_am_i/README.md b/rust/who_am_i/README.md index a1dc13c25..01c198486 100644 --- a/rust/who_am_i/README.md +++ b/rust/who_am_i/README.md @@ -37,6 +37,11 @@ Start the local network and deploy: ```bash icp network start -d icp deploy +``` + +The frontend is served by the asset canister. To run the Vite dev server with hot reload during frontend development: + +```bash npm run dev ``` From f21fbb4b357b4244393db05af7ca2a44648325b9 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 09:56:49 +0200 Subject: [PATCH 39/55] fix: handle icp.yaml without a network section in postStart.sh Fall back to appending a network block when neither ii: true nor mode: managed is present. Fixes gateway injection for examples like hello_world that have no network section in their icp.yaml. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postStart.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.devcontainer/scripts/postStart.sh b/.devcontainer/scripts/postStart.sh index 1d629c659..3ffcbcde9 100755 --- a/.devcontainer/scripts/postStart.sh +++ b/.devcontainer/scripts/postStart.sh @@ -12,8 +12,10 @@ if [ -n "$CODESPACE_NAME" ]; then let updated; if (content.includes(' ii: true\n')) { updated = content.replace(' ii: true\n', ' ii: true\n' + gateway); - } else { + } else if (content.includes(' mode: managed\n')) { updated = content.replace(' mode: managed\n', ' mode: managed\n' + gateway); + } else { + updated = content.trimEnd() + '\nnetwork:\n mode: managed\n' + gateway; } fs.writeFileSync('icp.yaml', updated); } From a53a79b11853bf175f5afaa59e3a0b55d4571e9a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 10:02:24 +0200 Subject: [PATCH 40/55] fix: use correct networks.local structure when appending to icp.yaml The icp.yaml schema expects 'networks' (plural) with named entries, not a top-level 'network' field. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postStart.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/scripts/postStart.sh b/.devcontainer/scripts/postStart.sh index 3ffcbcde9..3147e0d88 100755 --- a/.devcontainer/scripts/postStart.sh +++ b/.devcontainer/scripts/postStart.sh @@ -15,7 +15,7 @@ if [ -n "$CODESPACE_NAME" ]; then } else if (content.includes(' mode: managed\n')) { updated = content.replace(' mode: managed\n', ' mode: managed\n' + gateway); } else { - updated = content.trimEnd() + '\nnetwork:\n mode: managed\n' + gateway; + updated = content.trimEnd() + '\nnetworks:\n local:\n mode: managed\n' + gateway; } fs.writeFileSync('icp.yaml', updated); } From adde114524ba7b816325aac8f42d22bd61b22688 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 10:05:39 +0200 Subject: [PATCH 41/55] fix: networks is a sequence not a map in icp.yaml Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postStart.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/scripts/postStart.sh b/.devcontainer/scripts/postStart.sh index 3147e0d88..247ef8b2a 100755 --- a/.devcontainer/scripts/postStart.sh +++ b/.devcontainer/scripts/postStart.sh @@ -15,7 +15,7 @@ if [ -n "$CODESPACE_NAME" ]; then } else if (content.includes(' mode: managed\n')) { updated = content.replace(' mode: managed\n', ' mode: managed\n' + gateway); } else { - updated = content.trimEnd() + '\nnetworks:\n local:\n mode: managed\n' + gateway; + updated = content.trimEnd() + '\nnetworks:\n - name: local\n mode: managed\n' + gateway; } fs.writeFileSync('icp.yaml', updated); } From 5f21a7ea8bf1eba63073ad880d27787886bfad8f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 10:17:08 +0200 Subject: [PATCH 42/55] fix: add Vite devserver domain to gateway domains in postStart.sh Includes the port 5173 forwarded domain so the ICP gateway accepts requests proxied through the Vite dev server in Codespaces. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postStart.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/scripts/postStart.sh b/.devcontainer/scripts/postStart.sh index 247ef8b2a..165ff01c2 100755 --- a/.devcontainer/scripts/postStart.sh +++ b/.devcontainer/scripts/postStart.sh @@ -4,11 +4,12 @@ # HTTP gateway accepts requests with that Host header. if [ -n "$CODESPACE_NAME" ]; then DOMAIN="${CODESPACE_NAME}-8000.app.github.dev" + DEV_DOMAIN="${CODESPACE_NAME}-5173.app.github.dev" node -e " const fs = require('fs'); const content = fs.readFileSync('icp.yaml', 'utf8'); if (!content.includes('gateway:')) { - const gateway = ' gateway:\n domains:\n - localhost\n - ${DOMAIN}\n'; + const gateway = ' gateway:\n domains:\n - localhost\n - ${DOMAIN}\n - ${DEV_DOMAIN}\n'; let updated; if (content.includes(' ii: true\n')) { updated = content.replace(' ii: true\n', ' ii: true\n' + gateway); From af48b750b72194b443f1c9fee27a989ef776e074 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 10:47:34 +0200 Subject: [PATCH 43/55] fix: ensure network is running in postAttach.sh Adds a defensive icp network start -d before deploy so the Codespace recovers if postStart.sh failed or the network process didn't survive. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postAttach.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.devcontainer/scripts/postAttach.sh b/.devcontainer/scripts/postAttach.sh index 8aa6f2ae8..8c7954bf3 100755 --- a/.devcontainer/scripts/postAttach.sh +++ b/.devcontainer/scripts/postAttach.sh @@ -2,6 +2,8 @@ code CODESPACE.md +icp network start -d 2>/dev/null || true + echo "Deploying canisters..." icp deploy echo "" From 58533d0d76459cf6451e8708b7ec0dded53d182a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 11:06:40 +0200 Subject: [PATCH 44/55] fix: wait for ICP network to be ready before deploying icp network start -d returns immediately but the replica takes a moment to be ready. Poll localhost:8000/api/v2/status until it responds before running icp deploy. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postAttach.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.devcontainer/scripts/postAttach.sh b/.devcontainer/scripts/postAttach.sh index 8c7954bf3..91c7804fa 100755 --- a/.devcontainer/scripts/postAttach.sh +++ b/.devcontainer/scripts/postAttach.sh @@ -4,6 +4,10 @@ code CODESPACE.md icp network start -d 2>/dev/null || true +until curl -sf http://localhost:8000/api/v2/status >/dev/null 2>&1; do + sleep 1 +done + echo "Deploying canisters..." icp deploy echo "" From 608944c680a3328003daa1ff38f105d52b03e770 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 11:08:02 +0200 Subject: [PATCH 45/55] fix: remove redundant network start and polling from postAttach.sh postStartCommand completes before postAttachCommand runs, so the network is guaranteed ready by postStart.sh. The band-aid was masking errors. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postAttach.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.devcontainer/scripts/postAttach.sh b/.devcontainer/scripts/postAttach.sh index 91c7804fa..8aa6f2ae8 100755 --- a/.devcontainer/scripts/postAttach.sh +++ b/.devcontainer/scripts/postAttach.sh @@ -2,12 +2,6 @@ code CODESPACE.md -icp network start -d 2>/dev/null || true - -until curl -sf http://localhost:8000/api/v2/status >/dev/null 2>&1; do - sleep 1 -done - echo "Deploying canisters..." icp deploy echo "" From 6db978387048b29099c3007cef81d933adc7a8c9 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 11:08:20 +0200 Subject: [PATCH 46/55] fix: wait for network readiness in postStart.sh before returning icp network start -d may return before the replica is ready to accept connections. Poll /api/v2/status so postStartCommand only completes once the network is actually up, guaranteeing postAttach can deploy. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postStart.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.devcontainer/scripts/postStart.sh b/.devcontainer/scripts/postStart.sh index 165ff01c2..9edeea93b 100755 --- a/.devcontainer/scripts/postStart.sh +++ b/.devcontainer/scripts/postStart.sh @@ -24,3 +24,7 @@ if [ -n "$CODESPACE_NAME" ]; then fi icp network start -d + +until curl -sf http://localhost:8000/api/v2/status >/dev/null 2>&1; do + sleep 1 +done From f71ef432bee0e69036c3fe95abb905d8c61ae8fa Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 11:11:06 +0200 Subject: [PATCH 47/55] fix: add set -e to lifecycle scripts and remove unnecessary polling icp network start -d already blocks until the network is ready. set -e ensures failures are visible immediately rather than silently continuing to the next command. Co-Authored-By: Claude Sonnet 4.6 --- .devcontainer/scripts/postAttach.sh | 1 + .devcontainer/scripts/postStart.sh | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.devcontainer/scripts/postAttach.sh b/.devcontainer/scripts/postAttach.sh index 8aa6f2ae8..2f23ceec7 100755 --- a/.devcontainer/scripts/postAttach.sh +++ b/.devcontainer/scripts/postAttach.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e code CODESPACE.md diff --git a/.devcontainer/scripts/postStart.sh b/.devcontainer/scripts/postStart.sh index 9edeea93b..d51d228c8 100755 --- a/.devcontainer/scripts/postStart.sh +++ b/.devcontainer/scripts/postStart.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # In GitHub Codespaces, inject the forwarded domain into icp.yaml so the # HTTP gateway accepts requests with that Host header. @@ -24,7 +25,3 @@ if [ -n "$CODESPACE_NAME" ]; then fi icp network start -d - -until curl -sf http://localhost:8000/api/v2/status >/dev/null 2>&1; do - sleep 1 -done From 9a8f53dc89309a3d218fd8d7a19968058f6dba0c Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 12:10:02 +0200 Subject: [PATCH 48/55] refactor: rename canisters to backend/frontend in who_am_i examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename internet_identity_app_backend → backend and internet_identity_app_frontend → frontend in both Motoko and Rust who_am_i examples, including src directories, .did files, icp.yaml, Cargo.toml, Makefile, vite.config.js, actor.js, package.json, .gitignore, and README. Also removes ICP Ninja artifacts (dfx.json, BUILD.md) and simplifies vite.config.js by dropping the dfx fallback. Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/.gitignore | 2 +- motoko/who_am_i/BUILD.md | 26 ---------- motoko/who_am_i/Makefile | 6 +-- motoko/who_am_i/README.md | 4 +- motoko/who_am_i/dfx.json | 45 ---------------- motoko/who_am_i/icp.yaml | 14 ++--- motoko/who_am_i/package.json | 2 +- .../backend.did} | 0 .../main.mo | 0 .../index.css | 0 .../index.html | 0 .../who_am_i/src/frontend}/package.json | 2 +- .../public/favicon.ico | Bin .../src/App.jsx | 0 .../who_am_i/src/frontend}/src/actor.js | 8 +-- .../src/main.jsx | 0 .../vite.config.js | 48 +++--------------- rust/who_am_i/.gitignore | 2 +- rust/who_am_i/BUILD.md | 26 ---------- rust/who_am_i/Cargo.lock | 2 +- rust/who_am_i/Cargo.toml | 2 +- rust/who_am_i/Makefile | 6 +-- rust/who_am_i/README.md | 6 +-- rust/who_am_i/dfx.json | 41 --------------- rust/who_am_i/icp.yaml | 14 ++--- rust/who_am_i/package.json | 4 +- .../Cargo.toml | 2 +- .../backend.did} | 0 .../src/lib.rs | 0 .../index.css | 0 .../index.html | 0 .../who_am_i/src/frontend}/package.json | 2 +- .../public/favicon.ico | Bin .../src/App.jsx | 0 .../who_am_i/src/frontend}/src/actor.js | 8 +-- .../src/main.jsx | 0 .../tsconfig.json | 0 .../vite.config.js | 48 +++--------------- 38 files changed, 55 insertions(+), 265 deletions(-) delete mode 100644 motoko/who_am_i/BUILD.md delete mode 100644 motoko/who_am_i/dfx.json rename motoko/who_am_i/src/{internet_identity_app_backend/internet_identity_app_backend.did => backend/backend.did} (100%) rename motoko/who_am_i/src/{internet_identity_app_backend => backend}/main.mo (100%) rename motoko/who_am_i/src/{internet_identity_app_frontend => frontend}/index.css (100%) rename motoko/who_am_i/src/{internet_identity_app_frontend => frontend}/index.html (100%) rename {rust/who_am_i/src/internet_identity_app_frontend => motoko/who_am_i/src/frontend}/package.json (91%) rename motoko/who_am_i/src/{internet_identity_app_frontend => frontend}/public/favicon.ico (100%) rename motoko/who_am_i/src/{internet_identity_app_frontend => frontend}/src/App.jsx (100%) rename {rust/who_am_i/src/internet_identity_app_frontend => motoko/who_am_i/src/frontend}/src/actor.js (77%) rename motoko/who_am_i/src/{internet_identity_app_frontend => frontend}/src/main.jsx (100%) rename motoko/who_am_i/src/{internet_identity_app_frontend => frontend}/vite.config.js (50%) delete mode 100644 rust/who_am_i/BUILD.md delete mode 100644 rust/who_am_i/dfx.json rename rust/who_am_i/src/{internet_identity_app_backend => backend}/Cargo.toml (85%) rename rust/who_am_i/src/{internet_identity_app_backend/internet_identity_app_backend.did => backend/backend.did} (100%) rename rust/who_am_i/src/{internet_identity_app_backend => backend}/src/lib.rs (100%) rename rust/who_am_i/src/{internet_identity_app_frontend => frontend}/index.css (100%) rename rust/who_am_i/src/{internet_identity_app_frontend => frontend}/index.html (100%) rename {motoko/who_am_i/src/internet_identity_app_frontend => rust/who_am_i/src/frontend}/package.json (91%) rename rust/who_am_i/src/{internet_identity_app_frontend => frontend}/public/favicon.ico (100%) rename rust/who_am_i/src/{internet_identity_app_frontend => frontend}/src/App.jsx (100%) rename {motoko/who_am_i/src/internet_identity_app_frontend => rust/who_am_i/src/frontend}/src/actor.js (77%) rename rust/who_am_i/src/{internet_identity_app_frontend => frontend}/src/main.jsx (100%) rename rust/who_am_i/src/{internet_identity_app_frontend => frontend}/tsconfig.json (100%) rename rust/who_am_i/src/{internet_identity_app_frontend => frontend}/vite.config.js (50%) diff --git a/motoko/who_am_i/.gitignore b/motoko/who_am_i/.gitignore index 2a822d5c2..fcacb2b70 100644 --- a/motoko/who_am_i/.gitignore +++ b/motoko/who_am_i/.gitignore @@ -1 +1 @@ -src/internet_identity_app_frontend/src/bindings/ +src/frontend/src/bindings/ diff --git a/motoko/who_am_i/BUILD.md b/motoko/who_am_i/BUILD.md deleted file mode 100644 index 8877d3ed2..000000000 --- a/motoko/who_am_i/BUILD.md +++ /dev/null @@ -1,26 +0,0 @@ -# Continue building locally - -Projects deployed through ICP Ninja are temporary; they will only be live for 30 minutes before they are removed. To continue building locally, follow these steps. - -### 1. Install developer tools - -Install [Node.js](https://nodejs.org/en/download/) and [icp-cli](https://cli.internetcomputer.org): - -```bash -npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm -``` - -Then navigate into your project's directory that you downloaded from ICP Ninja. - -### 2. Deploy locally - -Start the local network and deploy the project: - -```bash -icp network start -d -icp deploy -``` - -## Additional examples - -Additional code examples and sample applications can be found in the [DFINITY examples repo](https://github.com/dfinity/examples). diff --git a/motoko/who_am_i/Makefile b/motoko/who_am_i/Makefile index ebc17ba7a..e5eba77f5 100644 --- a/motoko/who_am_i/Makefile +++ b/motoko/who_am_i/Makefile @@ -2,13 +2,13 @@ test: @echo "--- Testing whoami returns a principal ---" - @result=$$(icp canister call internet_identity_app_backend whoami '()') && \ + @result=$$(icp canister call backend whoami '()') && \ echo "$$result" && \ echo "$$result" | grep -q 'principal' && \ echo "PASS" || (echo "FAIL" && exit 1) @echo "--- Testing whoami is deterministic ---" - @result1=$$(icp canister call internet_identity_app_backend whoami '()') && \ - result2=$$(icp canister call internet_identity_app_backend whoami '()') && \ + @result1=$$(icp canister call backend whoami '()') && \ + result2=$$(icp canister call backend whoami '()') && \ [ "$$result1" = "$$result2" ] && \ echo "PASS: $$result1" || (echo "FAIL: $$result1 != $$result2" && exit 1) diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index 57a12d0a2..e57ee3d64 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -51,12 +51,12 @@ Codespaces is ideal for learning and local experimentation. When you're ready fo ## Updating the Candid interface -The `src/internet_identity_app_backend/internet_identity_app_backend.did` file defines the backend canister's public interface. The frontend TypeScript bindings are auto-generated from this file during the frontend build. +The `src/backend/backend.did` file defines the backend canister's public interface. The frontend TypeScript bindings are auto-generated from this file during the frontend build. If you modify the backend's public API, regenerate the `.did` file: ```bash -$(mops toolchain bin moc) --idl $(mops sources) -o src/internet_identity_app_backend/internet_identity_app_backend.did src/internet_identity_app_backend/main.mo +$(mops toolchain bin moc) --idl $(mops sources) -o src/backend/backend.did src/backend/main.mo ``` ## Security considerations and best practices diff --git a/motoko/who_am_i/dfx.json b/motoko/who_am_i/dfx.json deleted file mode 100644 index e7ba2df8b..000000000 --- a/motoko/who_am_i/dfx.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "canisters": { - "internet_identity_app_backend": { - "main": "src/internet_identity_app_backend/main.mo", - "type": "motoko" - }, - "internet_identity_app_frontend": { - "dependencies": ["internet_identity_app_backend"], - "internet_identity_app_frontend": { - "entrypoint": "src/internet_identity_app_frontend/index.html" - }, - "source": ["src/internet_identity_app_frontend/dist"], - "type": "assets" - }, - "internet_identity": { - "candid": "https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity.did", - "type": "custom", - "specified_id": "rdmx6-jaaaa-aaaaa-aaadq-cai", - "remote": { - "id": { - "ic": "rdmx6-jaaaa-aaaaa-aaadq-cai" - } - }, - "wasm": "https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity_production.wasm.gz" - }, - "internet_identity_frontend": { - "candid": "https://raw.githubusercontent.com/dfinity/internet-identity/refs/heads/main/src/internet_identity_frontend/internet_identity_frontend.did", - "type": "custom", - "specified_id": "uqzsh-gqaaa-aaaaq-qaada-cai", - "remote": { - "id": { - "ic": "uqzsh-gqaaa-aaaaq-qaada-cai" - } - }, - "wasm": "https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity_frontend.wasm.gz", - "init_arg": "(record { fetch_root_key = opt true; dev_csp = opt true; backend_canister_id = principal \"rdmx6-jaaaa-aaaaa-aaadq-cai\"; analytics_config = null; related_origins = opt vec { \"http://uqzsh-gqaaa-aaaaq-qaada-cai.localhost:4943\" }; backend_origin = \"http://rdmx6-jaaaa-aaaaa-aaadq-cai.localhost:4943\"; captcha_config = opt record { max_unsolved_captchas = 50 : nat64; captcha_trigger = variant { Static = variant { CaptchaDisabled } } }})" - } - }, - "output_env_file": ".env", - "defaults": { - "build": { - "packtool": "mops sources" - } - } -} diff --git a/motoko/who_am_i/icp.yaml b/motoko/who_am_i/icp.yaml index 6cf444682..820044595 100644 --- a/motoko/who_am_i/icp.yaml +++ b/motoko/who_am_i/icp.yaml @@ -4,18 +4,18 @@ networks: ii: true canisters: - - name: internet_identity_app_backend + - name: backend recipe: type: "@dfinity/motoko@v4.1.0" configuration: - main: src/internet_identity_app_backend/main.mo - candid: src/internet_identity_app_backend/internet_identity_app_backend.did + main: src/backend/main.mo + candid: src/backend/backend.did - - name: internet_identity_app_frontend + - name: frontend recipe: type: "@dfinity/asset-canister@v2.1.0" configuration: - dir: src/internet_identity_app_frontend/dist + dir: src/frontend/dist build: - - npm install --prefix src/internet_identity_app_frontend - - npm run build --prefix src/internet_identity_app_frontend + - npm install --prefix src/frontend + - npm run build --prefix src/frontend diff --git a/motoko/who_am_i/package.json b/motoko/who_am_i/package.json index 7684efc81..969610713 100644 --- a/motoko/who_am_i/package.json +++ b/motoko/who_am_i/package.json @@ -7,6 +7,6 @@ }, "type": "module", "workspaces": [ - "src/internet_identity_app_frontend" + "src/frontend" ] } diff --git a/motoko/who_am_i/src/internet_identity_app_backend/internet_identity_app_backend.did b/motoko/who_am_i/src/backend/backend.did similarity index 100% rename from motoko/who_am_i/src/internet_identity_app_backend/internet_identity_app_backend.did rename to motoko/who_am_i/src/backend/backend.did diff --git a/motoko/who_am_i/src/internet_identity_app_backend/main.mo b/motoko/who_am_i/src/backend/main.mo similarity index 100% rename from motoko/who_am_i/src/internet_identity_app_backend/main.mo rename to motoko/who_am_i/src/backend/main.mo diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/index.css b/motoko/who_am_i/src/frontend/index.css similarity index 100% rename from motoko/who_am_i/src/internet_identity_app_frontend/index.css rename to motoko/who_am_i/src/frontend/index.css diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/index.html b/motoko/who_am_i/src/frontend/index.html similarity index 100% rename from motoko/who_am_i/src/internet_identity_app_frontend/index.html rename to motoko/who_am_i/src/frontend/index.html diff --git a/rust/who_am_i/src/internet_identity_app_frontend/package.json b/motoko/who_am_i/src/frontend/package.json similarity index 91% rename from rust/who_am_i/src/internet_identity_app_frontend/package.json rename to motoko/who_am_i/src/frontend/package.json index 49a3f1ace..412b87e6d 100644 --- a/rust/who_am_i/src/internet_identity_app_frontend/package.json +++ b/motoko/who_am_i/src/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "internet_identity_app_frontend", + "name": "frontend", "private": true, "type": "module", "scripts": { diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/public/favicon.ico b/motoko/who_am_i/src/frontend/public/favicon.ico similarity index 100% rename from motoko/who_am_i/src/internet_identity_app_frontend/public/favicon.ico rename to motoko/who_am_i/src/frontend/public/favicon.ico diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx b/motoko/who_am_i/src/frontend/src/App.jsx similarity index 100% rename from motoko/who_am_i/src/internet_identity_app_frontend/src/App.jsx rename to motoko/who_am_i/src/frontend/src/App.jsx diff --git a/rust/who_am_i/src/internet_identity_app_frontend/src/actor.js b/motoko/who_am_i/src/frontend/src/actor.js similarity index 77% rename from rust/who_am_i/src/internet_identity_app_frontend/src/actor.js rename to motoko/who_am_i/src/frontend/src/actor.js index becf24513..1e4ac04ad 100644 --- a/rust/who_am_i/src/internet_identity_app_frontend/src/actor.js +++ b/motoko/who_am_i/src/frontend/src/actor.js @@ -1,15 +1,15 @@ import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; -import { createActor } from "./bindings/internet_identity_app_backend"; +import { createActor } from "./bindings/backend"; const canisterEnv = safeGetCanisterEnv(); const canisterId = - canisterEnv?.["PUBLIC_CANISTER_ID:internet_identity_app_backend"] ?? - process.env.CANISTER_ID_INTERNET_IDENTITY_APP_BACKEND; + canisterEnv?.["PUBLIC_CANISTER_ID:backend"] ?? + process.env.CANISTER_ID_BACKEND; if (!canisterId) { throw new Error( - "Canister ID for 'internet_identity_app_backend' not found. Run 'icp deploy' or 'dfx deploy' first." + "Canister ID for 'backend' not found. Run 'icp deploy' first." ); } diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/main.jsx b/motoko/who_am_i/src/frontend/src/main.jsx similarity index 100% rename from motoko/who_am_i/src/internet_identity_app_frontend/src/main.jsx rename to motoko/who_am_i/src/frontend/src/main.jsx diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js b/motoko/who_am_i/src/frontend/vite.config.js similarity index 50% rename from motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js rename to motoko/who_am_i/src/frontend/vite.config.js index 41d03427c..8bb840ac3 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/vite.config.js +++ b/motoko/who_am_i/src/frontend/vite.config.js @@ -4,9 +4,8 @@ import react from "@vitejs/plugin-react"; import { icpBindgen } from "@icp-sdk/bindgen/plugins/vite"; function getDevServerConfig() { - // Try icp-cli first try { - const canisterId = execSync("icp canister status internet_identity_app_backend -e local -i", { + const canisterId = execSync("icp canister status backend -e local -i", { encoding: "utf-8", stdio: "pipe", }).trim(); @@ -20,7 +19,7 @@ function getDevServerConfig() { replicaPort: "8000", headers: { "Set-Cookie": `ic_env=${encodeURIComponent( - `ic_root_key=${networkStatus.root_key}&PUBLIC_CANISTER_ID:internet_identity_app_backend=${canisterId}` + `ic_root_key=${networkStatus.root_key}&PUBLIC_CANISTER_ID:backend=${canisterId}` )}; SameSite=Lax;`, }, proxy: { @@ -29,46 +28,12 @@ function getDevServerConfig() { }; } catch {} - // Try dfx - try { - const pingResult = JSON.parse( - execSync("dfx ping", { encoding: "utf-8", stdio: "pipe" }) - ); - const rootKeyHex = Buffer.from(pingResult.root_key).toString("hex"); - const canisterId = execSync( - "dfx canister id internet_identity_app_backend", - { - encoding: "utf-8", - stdio: "pipe", - } - ).trim(); - return { - replicaPort: "4943", - headers: { - "Set-Cookie": `ic_env=${encodeURIComponent( - `ic_root_key=${rootKeyHex}&PUBLIC_CANISTER_ID:internet_identity_app_backend=${canisterId}` - )}; SameSite=Lax;`, - }, - proxy: { - "/api": { - target: "http://127.0.0.1:4943", - changeOrigin: true, - }, - }, - host: "127.0.0.1", - }; - } catch {} - throw new Error( - "No local network running. Start with:\n icp network start -d && icp deploy\nor:\n dfx start --background && dfx deploy" + "No local network running. Start with:\n icp network start -d && icp deploy" ); } export default defineConfig(({ command, mode }) => { - // dfx generates ../../.env with CANISTER_ID_* vars on deploy. Bake them into - // the bundle so actor.js can fall back to them when the ic_env cookie does not - // contain canister IDs (dfx does not inject PUBLIC_CANISTER_ID:* env vars - // into the asset canister, unlike icp-cli). const env = loadEnv(mode, "../..", ["CANISTER_"]); const devConfig = command === "serve" ? getDevServerConfig() : undefined; @@ -77,14 +42,13 @@ export default defineConfig(({ command, mode }) => { plugins: [ react(), icpBindgen({ - didFile: - "../internet_identity_app_backend/internet_identity_app_backend.did", + didFile: "../backend/backend.did", outDir: "./src/bindings", }), ], define: { - "process.env.CANISTER_ID_INTERNET_IDENTITY_APP_BACKEND": JSON.stringify( - env.CANISTER_ID_INTERNET_IDENTITY_APP_BACKEND + "process.env.CANISTER_ID_BACKEND": JSON.stringify( + env.CANISTER_ID_BACKEND ), "process.env.REPLICA_PORT": JSON.stringify(devConfig?.replicaPort ?? ""), }, diff --git a/rust/who_am_i/.gitignore b/rust/who_am_i/.gitignore index 2a822d5c2..fcacb2b70 100644 --- a/rust/who_am_i/.gitignore +++ b/rust/who_am_i/.gitignore @@ -1 +1 @@ -src/internet_identity_app_frontend/src/bindings/ +src/frontend/src/bindings/ diff --git a/rust/who_am_i/BUILD.md b/rust/who_am_i/BUILD.md deleted file mode 100644 index 8877d3ed2..000000000 --- a/rust/who_am_i/BUILD.md +++ /dev/null @@ -1,26 +0,0 @@ -# Continue building locally - -Projects deployed through ICP Ninja are temporary; they will only be live for 30 minutes before they are removed. To continue building locally, follow these steps. - -### 1. Install developer tools - -Install [Node.js](https://nodejs.org/en/download/) and [icp-cli](https://cli.internetcomputer.org): - -```bash -npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm -``` - -Then navigate into your project's directory that you downloaded from ICP Ninja. - -### 2. Deploy locally - -Start the local network and deploy the project: - -```bash -icp network start -d -icp deploy -``` - -## Additional examples - -Additional code examples and sample applications can be found in the [DFINITY examples repo](https://github.com/dfinity/examples). diff --git a/rust/who_am_i/Cargo.lock b/rust/who_am_i/Cargo.lock index b830c16d5..bc1209943 100644 --- a/rust/who_am_i/Cargo.lock +++ b/rust/who_am_i/Cargo.lock @@ -298,7 +298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "internet_identity_app_backend" +name = "backend" version = "0.1.0" dependencies = [ "candid", diff --git a/rust/who_am_i/Cargo.toml b/rust/who_am_i/Cargo.toml index b72184e38..4c16df46e 100644 --- a/rust/who_am_i/Cargo.toml +++ b/rust/who_am_i/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["src/internet_identity_app_backend"] +members = ["src/backend"] resolver = "2" diff --git a/rust/who_am_i/Makefile b/rust/who_am_i/Makefile index ebc17ba7a..e5eba77f5 100644 --- a/rust/who_am_i/Makefile +++ b/rust/who_am_i/Makefile @@ -2,13 +2,13 @@ test: @echo "--- Testing whoami returns a principal ---" - @result=$$(icp canister call internet_identity_app_backend whoami '()') && \ + @result=$$(icp canister call backend whoami '()') && \ echo "$$result" && \ echo "$$result" | grep -q 'principal' && \ echo "PASS" || (echo "FAIL" && exit 1) @echo "--- Testing whoami is deterministic ---" - @result1=$$(icp canister call internet_identity_app_backend whoami '()') && \ - result2=$$(icp canister call internet_identity_app_backend whoami '()') && \ + @result1=$$(icp canister call backend whoami '()') && \ + result2=$$(icp canister call backend whoami '()') && \ [ "$$result1" = "$$result2" ] && \ echo "PASS: $$result1" || (echo "FAIL: $$result1 != $$result2" && exit 1) diff --git a/rust/who_am_i/README.md b/rust/who_am_i/README.md index 01c198486..a9ee448b9 100644 --- a/rust/who_am_i/README.md +++ b/rust/who_am_i/README.md @@ -51,13 +51,13 @@ Codespaces is ideal for learning and local experimentation. When you're ready fo ## Updating the Candid interface -The `src/internet_identity_app_backend/internet_identity_app_backend.did` file defines the backend canister's public interface. The frontend TypeScript bindings are auto-generated from this file during the frontend build. +The `src/backend/backend.did` file defines the backend canister's public interface. The frontend TypeScript bindings are auto-generated from this file during the frontend build. If you modify the backend's public API, rebuild the canister and regenerate the `.did` file: ```bash -icp build internet_identity_app_backend -candid-extractor target/wasm32-unknown-unknown/release/internet_identity_app_backend.wasm > src/internet_identity_app_backend/internet_identity_app_backend.did +icp build backend +candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > src/backend/backend.did ``` ## Security considerations and best practices diff --git a/rust/who_am_i/dfx.json b/rust/who_am_i/dfx.json deleted file mode 100644 index a4c690961..000000000 --- a/rust/who_am_i/dfx.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "canisters": { - "internet_identity_app_backend": { - "candid": "src/internet_identity_app_backend/internet_identity_app_backend.did", - "package": "internet_identity_app_backend", - "type": "rust" - }, - "internet_identity_app_frontend": { - "dependencies": ["internet_identity_app_backend"], - "internet_identity_app_frontend": { - "entrypoint": "src/internet_identity_app_frontend/index.html" - }, - "source": ["src/internet_identity_app_frontend/dist"], - "type": "assets" - }, - "internet_identity": { - "candid": "https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity.did", - "type": "custom", - "specified_id": "rdmx6-jaaaa-aaaaa-aaadq-cai", - "remote": { - "id": { - "ic": "rdmx6-jaaaa-aaaaa-aaadq-cai" - } - }, - "wasm": "https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity_production.wasm.gz" - }, - "internet_identity_frontend": { - "candid": "https://raw.githubusercontent.com/dfinity/internet-identity/refs/heads/main/src/internet_identity_frontend/internet_identity_frontend.did", - "type": "custom", - "specified_id": "uqzsh-gqaaa-aaaaq-qaada-cai", - "remote": { - "id": { - "ic": "uqzsh-gqaaa-aaaaq-qaada-cai" - } - }, - "wasm": "https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity_frontend.wasm.gz", - "init_arg": "(record { fetch_root_key = opt true; dev_csp = opt true; backend_canister_id = principal \"rdmx6-jaaaa-aaaaa-aaadq-cai\"; analytics_config = null; related_origins = opt vec { \"http://uqzsh-gqaaa-aaaaq-qaada-cai.localhost:4943\" }; backend_origin = \"http://rdmx6-jaaaa-aaaaa-aaadq-cai.localhost:4943\"; captcha_config = opt record { max_unsolved_captchas = 50 : nat64; captcha_trigger = variant { Static = variant { CaptchaDisabled } } }})" - } - }, - "output_env_file": ".env" -} \ No newline at end of file diff --git a/rust/who_am_i/icp.yaml b/rust/who_am_i/icp.yaml index e354013ab..bae2f48bc 100644 --- a/rust/who_am_i/icp.yaml +++ b/rust/who_am_i/icp.yaml @@ -4,18 +4,18 @@ networks: ii: true canisters: - - name: internet_identity_app_backend + - name: backend recipe: type: "@dfinity/rust@v3.2.0" configuration: - package: internet_identity_app_backend - candid: src/internet_identity_app_backend/internet_identity_app_backend.did + package: backend + candid: src/backend/backend.did - - name: internet_identity_app_frontend + - name: frontend recipe: type: "@dfinity/asset-canister@v2.1.0" configuration: - dir: src/internet_identity_app_frontend/dist + dir: src/frontend/dist build: - - npm install --prefix src/internet_identity_app_frontend - - npm run build --prefix src/internet_identity_app_frontend + - npm install --prefix src/frontend + - npm run build --prefix src/frontend diff --git a/rust/who_am_i/package.json b/rust/who_am_i/package.json index 3544e9035..2d7d15ddb 100644 --- a/rust/who_am_i/package.json +++ b/rust/who_am_i/package.json @@ -3,7 +3,7 @@ "node": ">=16.0.0", "npm": ">=7.0.0" }, - "name": "internet_identity_app", + "name": "who-am-i", "scripts": { "build": "npm run build --workspaces --if-present", "prebuild": "npm run prebuild --workspaces --if-present", @@ -13,6 +13,6 @@ }, "type": "module", "workspaces": [ - "src/internet_identity_app_frontend" + "src/frontend" ] } diff --git a/rust/who_am_i/src/internet_identity_app_backend/Cargo.toml b/rust/who_am_i/src/backend/Cargo.toml similarity index 85% rename from rust/who_am_i/src/internet_identity_app_backend/Cargo.toml rename to rust/who_am_i/src/backend/Cargo.toml index ceda7f950..734a31d6b 100644 --- a/rust/who_am_i/src/internet_identity_app_backend/Cargo.toml +++ b/rust/who_am_i/src/backend/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "internet_identity_app_backend" +name = "backend" version = "0.1.0" edition = "2021" diff --git a/rust/who_am_i/src/internet_identity_app_backend/internet_identity_app_backend.did b/rust/who_am_i/src/backend/backend.did similarity index 100% rename from rust/who_am_i/src/internet_identity_app_backend/internet_identity_app_backend.did rename to rust/who_am_i/src/backend/backend.did diff --git a/rust/who_am_i/src/internet_identity_app_backend/src/lib.rs b/rust/who_am_i/src/backend/src/lib.rs similarity index 100% rename from rust/who_am_i/src/internet_identity_app_backend/src/lib.rs rename to rust/who_am_i/src/backend/src/lib.rs diff --git a/rust/who_am_i/src/internet_identity_app_frontend/index.css b/rust/who_am_i/src/frontend/index.css similarity index 100% rename from rust/who_am_i/src/internet_identity_app_frontend/index.css rename to rust/who_am_i/src/frontend/index.css diff --git a/rust/who_am_i/src/internet_identity_app_frontend/index.html b/rust/who_am_i/src/frontend/index.html similarity index 100% rename from rust/who_am_i/src/internet_identity_app_frontend/index.html rename to rust/who_am_i/src/frontend/index.html diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/package.json b/rust/who_am_i/src/frontend/package.json similarity index 91% rename from motoko/who_am_i/src/internet_identity_app_frontend/package.json rename to rust/who_am_i/src/frontend/package.json index 49a3f1ace..412b87e6d 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/package.json +++ b/rust/who_am_i/src/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "internet_identity_app_frontend", + "name": "frontend", "private": true, "type": "module", "scripts": { diff --git a/rust/who_am_i/src/internet_identity_app_frontend/public/favicon.ico b/rust/who_am_i/src/frontend/public/favicon.ico similarity index 100% rename from rust/who_am_i/src/internet_identity_app_frontend/public/favicon.ico rename to rust/who_am_i/src/frontend/public/favicon.ico diff --git a/rust/who_am_i/src/internet_identity_app_frontend/src/App.jsx b/rust/who_am_i/src/frontend/src/App.jsx similarity index 100% rename from rust/who_am_i/src/internet_identity_app_frontend/src/App.jsx rename to rust/who_am_i/src/frontend/src/App.jsx diff --git a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js b/rust/who_am_i/src/frontend/src/actor.js similarity index 77% rename from motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js rename to rust/who_am_i/src/frontend/src/actor.js index becf24513..1e4ac04ad 100644 --- a/motoko/who_am_i/src/internet_identity_app_frontend/src/actor.js +++ b/rust/who_am_i/src/frontend/src/actor.js @@ -1,15 +1,15 @@ import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; -import { createActor } from "./bindings/internet_identity_app_backend"; +import { createActor } from "./bindings/backend"; const canisterEnv = safeGetCanisterEnv(); const canisterId = - canisterEnv?.["PUBLIC_CANISTER_ID:internet_identity_app_backend"] ?? - process.env.CANISTER_ID_INTERNET_IDENTITY_APP_BACKEND; + canisterEnv?.["PUBLIC_CANISTER_ID:backend"] ?? + process.env.CANISTER_ID_BACKEND; if (!canisterId) { throw new Error( - "Canister ID for 'internet_identity_app_backend' not found. Run 'icp deploy' or 'dfx deploy' first." + "Canister ID for 'backend' not found. Run 'icp deploy' first." ); } diff --git a/rust/who_am_i/src/internet_identity_app_frontend/src/main.jsx b/rust/who_am_i/src/frontend/src/main.jsx similarity index 100% rename from rust/who_am_i/src/internet_identity_app_frontend/src/main.jsx rename to rust/who_am_i/src/frontend/src/main.jsx diff --git a/rust/who_am_i/src/internet_identity_app_frontend/tsconfig.json b/rust/who_am_i/src/frontend/tsconfig.json similarity index 100% rename from rust/who_am_i/src/internet_identity_app_frontend/tsconfig.json rename to rust/who_am_i/src/frontend/tsconfig.json diff --git a/rust/who_am_i/src/internet_identity_app_frontend/vite.config.js b/rust/who_am_i/src/frontend/vite.config.js similarity index 50% rename from rust/who_am_i/src/internet_identity_app_frontend/vite.config.js rename to rust/who_am_i/src/frontend/vite.config.js index 41d03427c..8bb840ac3 100644 --- a/rust/who_am_i/src/internet_identity_app_frontend/vite.config.js +++ b/rust/who_am_i/src/frontend/vite.config.js @@ -4,9 +4,8 @@ import react from "@vitejs/plugin-react"; import { icpBindgen } from "@icp-sdk/bindgen/plugins/vite"; function getDevServerConfig() { - // Try icp-cli first try { - const canisterId = execSync("icp canister status internet_identity_app_backend -e local -i", { + const canisterId = execSync("icp canister status backend -e local -i", { encoding: "utf-8", stdio: "pipe", }).trim(); @@ -20,7 +19,7 @@ function getDevServerConfig() { replicaPort: "8000", headers: { "Set-Cookie": `ic_env=${encodeURIComponent( - `ic_root_key=${networkStatus.root_key}&PUBLIC_CANISTER_ID:internet_identity_app_backend=${canisterId}` + `ic_root_key=${networkStatus.root_key}&PUBLIC_CANISTER_ID:backend=${canisterId}` )}; SameSite=Lax;`, }, proxy: { @@ -29,46 +28,12 @@ function getDevServerConfig() { }; } catch {} - // Try dfx - try { - const pingResult = JSON.parse( - execSync("dfx ping", { encoding: "utf-8", stdio: "pipe" }) - ); - const rootKeyHex = Buffer.from(pingResult.root_key).toString("hex"); - const canisterId = execSync( - "dfx canister id internet_identity_app_backend", - { - encoding: "utf-8", - stdio: "pipe", - } - ).trim(); - return { - replicaPort: "4943", - headers: { - "Set-Cookie": `ic_env=${encodeURIComponent( - `ic_root_key=${rootKeyHex}&PUBLIC_CANISTER_ID:internet_identity_app_backend=${canisterId}` - )}; SameSite=Lax;`, - }, - proxy: { - "/api": { - target: "http://127.0.0.1:4943", - changeOrigin: true, - }, - }, - host: "127.0.0.1", - }; - } catch {} - throw new Error( - "No local network running. Start with:\n icp network start -d && icp deploy\nor:\n dfx start --background && dfx deploy" + "No local network running. Start with:\n icp network start -d && icp deploy" ); } export default defineConfig(({ command, mode }) => { - // dfx generates ../../.env with CANISTER_ID_* vars on deploy. Bake them into - // the bundle so actor.js can fall back to them when the ic_env cookie does not - // contain canister IDs (dfx does not inject PUBLIC_CANISTER_ID:* env vars - // into the asset canister, unlike icp-cli). const env = loadEnv(mode, "../..", ["CANISTER_"]); const devConfig = command === "serve" ? getDevServerConfig() : undefined; @@ -77,14 +42,13 @@ export default defineConfig(({ command, mode }) => { plugins: [ react(), icpBindgen({ - didFile: - "../internet_identity_app_backend/internet_identity_app_backend.did", + didFile: "../backend/backend.did", outDir: "./src/bindings", }), ], define: { - "process.env.CANISTER_ID_INTERNET_IDENTITY_APP_BACKEND": JSON.stringify( - env.CANISTER_ID_INTERNET_IDENTITY_APP_BACKEND + "process.env.CANISTER_ID_BACKEND": JSON.stringify( + env.CANISTER_ID_BACKEND ), "process.env.REPLICA_PORT": JSON.stringify(devConfig?.replicaPort ?? ""), }, From b907833c0151301bb828a975031db253153b23eb Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 12:57:44 +0200 Subject: [PATCH 49/55] chore: bump moc/core, use --default-persistent-actors, pin recipe fix branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - moc 1.5.1 → 1.8.2, core 2.4.0 → 2.5.0 - Add --default-persistent-actors to moc args so the persistent keyword is no longer needed in main.mo - Pin Motoko recipe to fix/motoko-mops-moc-args branch until dfinity/icp-cli-recipes#26 merges and a stable release is cut Co-Authored-By: Claude Sonnet 4.6 --- motoko/who_am_i/icp.yaml | 2 +- motoko/who_am_i/mops.toml | 6 +++--- motoko/who_am_i/src/backend/main.mo | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/motoko/who_am_i/icp.yaml b/motoko/who_am_i/icp.yaml index 820044595..3a01d0623 100644 --- a/motoko/who_am_i/icp.yaml +++ b/motoko/who_am_i/icp.yaml @@ -6,7 +6,7 @@ networks: canisters: - name: backend recipe: - type: "@dfinity/motoko@v4.1.0" + type: https://raw.githubusercontent.com/dfinity/icp-cli-recipes/refs/heads/fix/motoko-mops-moc-args/recipes/motoko/recipe.hbs configuration: main: src/backend/main.mo candid: src/backend/backend.did diff --git a/motoko/who_am_i/mops.toml b/motoko/who_am_i/mops.toml index dc89d0891..2fbc4f292 100644 --- a/motoko/who_am_i/mops.toml +++ b/motoko/who_am_i/mops.toml @@ -1,11 +1,11 @@ [toolchain] -moc = "1.5.1" +moc = "1.8.2" [dependencies] -core = "2.4.0" +core = "2.5.0" [moc] # M0236: use context dot notation (e.g. x.toText() instead of Nat.toText(x)) # M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically) # M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate) -args = ["-W=M0236,M0237,M0223"] +args = ["--default-persistent-actors", "-W=M0236,M0237,M0223"] diff --git a/motoko/who_am_i/src/backend/main.mo b/motoko/who_am_i/src/backend/main.mo index f011aecd3..70015e177 100644 --- a/motoko/who_am_i/src/backend/main.mo +++ b/motoko/who_am_i/src/backend/main.mo @@ -1,6 +1,6 @@ import Principal "mo:core/Principal"; -persistent actor Whoami { +actor Whoami { public query (message) func whoami() : async Principal { message.caller; }; From 985dacdcd8b7cbb1b161e0b515239a5b7515ff5f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 13:01:58 +0200 Subject: [PATCH 50/55] docs: add AGENTS.md and CLAUDE.md with example structure guidelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AGENTS.md is the primary source of agent instructions covering: canonical example layout, icp.yaml / mops.toml / Cargo.toml patterns, Makefile requirements, devcontainer and CI workflow templates, README structure, dfx→icp-cli migration checklist, and pending items (images, Motoko recipe version). CLAUDE.md delegates to AGENTS.md. Co-Authored-By: Claude Sonnet 4.6 --- AGENTS.md | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 3 + 2 files changed, 366 insertions(+) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..f5eb5180f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,363 @@ +# Agent Instructions + +Guidelines for AI agents (Claude, Codex, Cursor, Copilot, etc.) working in this repository. + +## Repository overview + +This repo contains canonical ICP examples, each available in both Motoko and Rust. Every example lives under two sibling directories: + +``` +motoko// +rust// +``` + +Both implement the same Candid interface so readers can compare language implementations side by side. + +--- + +## Toolchain + +- **Always use `icp-cli`** for all ICP operations. Never use `dfx`. +- CLI docs: https://cli.internetcomputer.org +- ICP developer docs: https://docs.internetcomputer.org + +--- + +## Canonical example structure + +Follow the `hello_world` layout. New examples and migrations should use this pattern: + +``` +// +├── icp.yaml # canister definitions (icp-cli project file) +├── Makefile # must contain a `test` target +├── README.md +├── CODESPACE.md # symlink or copy from .devcontainer/CODESPACE.md +├── package.json # npm workspaces root pointing to frontend/ +├── mops.toml # Motoko only +├── Cargo.toml # Rust only (workspace) +├── rust-toolchain.toml # Rust only +├── backend/ +│ ├── app.mo or lib.rs # canister entry point +│ └── backend.did # Candid interface (source of truth) +└── frontend/ + ├── index.html + ├── package.json + ├── vite.config.js + ├── src/ + │ ├── actor.js # icp-sdk actor wiring + │ ├── App.jsx + │ └── main.jsx + └── dist/ # build output (gitignored, rebuilt by icp deploy) +``` + +> **Note:** `who_am_i` uses `src/backend/` and `src/frontend/` for historical reasons. +> New examples and migrations should use the flat `backend/` / `frontend/` layout above. + +### What NOT to include + +- `dfx.json` — dfx project file, not used with icp-cli +- `BUILD.md` — ICP Ninja artifact +- `.dfx/` — dfx state directory +- `src/bindings/` — auto-generated by the bindgen Vite plugin, must be gitignored +- Committed `dist/` output — built by `icp deploy`; never commit pre-built assets + +--- + +## icp.yaml + +### Motoko + +```yaml +networks: # omit if no Internet Identity needed + - name: local + mode: managed + ii: true + +canisters: + - name: backend + recipe: + type: "@dfinity/motoko@v4.1.0" # see pending items below + configuration: + main: backend/app.mo + candid: backend/backend.did + + - name: frontend + recipe: + type: "@dfinity/asset-canister@v2.1.0" + configuration: + dir: frontend/dist + build: + - npm install --prefix frontend + - npm run build --prefix frontend +``` + +### Rust + +```yaml +canisters: + - name: backend + recipe: + type: "@dfinity/rust@v3.2.0" + configuration: + package: backend + candid: backend/backend.did + + - name: frontend + recipe: + type: "@dfinity/asset-canister@v2.1.0" + configuration: + dir: frontend/dist + build: + - npm install --prefix frontend + - npm run build --prefix frontend +``` + +**Canister names are always `backend` and `frontend`.** Never use names like `_backend`, `internet_identity_app_backend`, etc. + +--- + +## mops.toml (Motoko) + +```toml +[toolchain] +moc = "1.5.1" # bump to latest stable; who_am_i uses 1.8.2 + +[dependencies] +core = "2.4.0" # replaces the old base library + +[moc] +# M0236: use context dot notation +# M0237: redundant explicit implicit arguments +# M0223: redundant type instantiation +args = ["-W=M0236,M0237,M0223"] +``` + +If your example uses `persistent actor`, add `--default-persistent-actors` to `args` (see `who_am_i` for reference) or use the `persistent` keyword explicitly. + +The `[moc] args` section requires a Motoko recipe that passes moc args from `mops.toml`. Until `@dfinity/motoko` gets this support in a stable release, `who_am_i` pins a fix-branch URL. Track: https://github.com/dfinity/icp-cli-recipes/pull/26 + +--- + +## Cargo.toml (Rust) + +Root workspace: +```toml +[workspace] +members = ["backend"] +resolver = "2" +``` + +`backend/Cargo.toml`: +```toml +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +candid = "0.10" +ic-cdk = "0.20" +``` + +--- + +## Makefile + +Every example must have a `Makefile` with a `test` target that exercises the deployed canister via `icp canister call`: + +```makefile +.PHONY: test + +test: + @echo "--- Testing ---" + @result=$$(icp canister call backend '') && \ + echo "$$result" && \ + echo "$$result" | grep -q '' && \ + echo "PASS" || (echo "FAIL" && exit 1) +``` + +- Tests must call the `backend` canister by that name. +- Use `grep -q` to assert on output content. +- Each assertion is a separate echo block for clarity. + +--- + +## Devcontainer config + +Add a file at `.devcontainer/-/devcontainer.json`: + +```json +{ + "name": " ()", + "image": "ghcr.io/dfinity/icp-dev-env-:", + "workspaceFolder": "/workspaces/examples//", + "forwardPorts": [8000, 5173], + "portsAttributes": { + "8000": { "label": "ICP", "onAutoForward": "ignore" }, + "5173": { "label": "Vite", "onAutoForward": "openBrowser" } + }, + "postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh", + "postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh", + "customizations": { + "vscode": { + "extensions": ["dfinity-foundation.vscode-motoko"], + "settings": { + "git.openRepositoryInParentFolders": "always", + "workbench.startupEditor": "none", + "workbench.editorAssociations": { "*.md": "vscode.markdown.preview.editor" } + } + } + } +} +``` + +- Motoko extension: `dfinity-foundation.vscode-motoko` +- Rust extension: `rust-lang.rust-analyzer` +- The `postStart` and `postAttach` scripts are shared across all examples — do not create per-example scripts. +- `forwardPorts` and `portsAttributes` are only needed for examples with a frontend. + +Also add a Codespaces badge to the example's README pointing to the new devcontainer config: + +```markdown +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dfinity/examples?devcontainer_path=.devcontainer%2F-%2Fdevcontainer.json&ref=master) +``` + +--- + +## CI workflow + +Copy `.github/workflow-template.yml` to `.github/workflows/.yml` and fill in the placeholders. A single workflow file covers both language variants: + +```yaml +name: + +on: + push: + branches: [master] + pull_request: + paths: + - motoko//** + - rust//** + - .github/workflows/.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + motoko-: + runs-on: ubuntu-24.04 + container: ghcr.io/dfinity/icp-dev-env-motoko: + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy and test + working-directory: motoko/ + run: | + icp network start -d + icp deploy + make test + + rust-: + runs-on: ubuntu-24.04 + container: ghcr.io/dfinity/icp-dev-env-rust: + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy and test + working-directory: rust/ + run: | + icp network start -d + icp deploy + make test +``` + +- Linux only, no macOS runners. +- No provision scripts — toolchain comes from the container image. +- Always include the `concurrency` block to cancel superseded runs. +- Pin the `actions/checkout` SHA and annotate it with the version tag. + +--- + +## README structure + +Each example's README should follow this structure: + +```markdown +# + +[View this sample's code on GitHub]() + +## Overview +<2-3 sentences describing what the example demonstrates> + +## Try in browser + + +## Build and deploy from the command line + +### Prerequisites +- [ ] Install Node.js +- [ ] Install icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` + +### Install + + +### Deploy + + +## Updating the Candid interface + + +## Security considerations and best practices + +``` + +- Codespaces badge URL must include `ref=master` (or the appropriate branch). +- Security best practices URL: `https://docs.internetcomputer.org/guides/security/overview` +- Each README links to its counterpart in the other language. + +--- + +## Pending items (do not resolve prematurely) + +### Container images +All devcontainer configs and CI workflows currently reference `ghcr.io/marc0olo/icp-dev-env-*:dev` (a personal fork used while images are being prepared for the official dfinity org). When images are published at `ghcr.io/dfinity/icp-dev-env-*`, update every occurrence in: +- `.devcontainer/*/devcontainer.json` +- `.github/workflows/*.yml` + +Also note: `icp-dev-env-all` is a new combined Motoko+Rust image (for the root devcontainer) that does not yet exist in the dfinity org. + +### Motoko recipe version +`motoko/who_am_i/icp.yaml` pins a raw GitHub URL to pick up `[moc] args` support before it ships in a stable release. Once https://github.com/dfinity/icp-cli-recipes/pull/26 merges and a new `@dfinity/motoko` version is released, update: +- `motoko/who_am_i/icp.yaml` +- `motoko/hello_world/icp.yaml` + +--- + +## dfx → icp-cli migration checklist + +When migrating an existing example: + +- [ ] Replace `dfx.json` with `icp.yaml` using the canonical structure above +- [ ] Rename canisters to `backend` and `frontend` +- [ ] Rename `src//` to `backend/` (or `src/backend/` if keeping the `src/` layout) +- [ ] Rename `src//` to `frontend/` (or `src/frontend/`) +- [ ] Rename `.did` file to `backend.did` +- [ ] Update `Cargo.toml` package name to `backend` (Rust) +- [ ] Update `Cargo.lock` package name entry (Rust) +- [ ] Update workspace member path in root `Cargo.toml` (Rust) +- [ ] Update `vite.config.js`: canister name, `didFile` path, env var names, remove dfx fallback +- [ ] Update `actor.js`: import path, `PUBLIC_CANISTER_ID:backend`, `CANISTER_ID_BACKEND` +- [ ] Update root `package.json` workspace path to `frontend/` +- [ ] Update `.gitignore` bindings path to `frontend/src/bindings/` +- [ ] Update `mops.toml` to current toolchain versions (Motoko) +- [ ] Delete `dfx.json`, `BUILD.md`, `.dfx/`, `.env` (dfx-generated) +- [ ] Add `Makefile` with `test` target +- [ ] Add devcontainer config under `.devcontainer/-/` +- [ ] Add CI workflow under `.github/workflows/.yml` +- [ ] Add Codespaces badge to README +- [ ] Update README deploy instructions to use `icp-cli` +- [ ] Update README Candid regeneration command to use `icp-cli` / `mops` toolchain diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..bdc9873c0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# Claude Code Instructions + +Read and follow all guidelines in [AGENTS.md](AGENTS.md) before making any changes to this repository. From 5d98d080ed5a8d10d53f1b235196ca2b48118f03 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 13:06:27 +0200 Subject: [PATCH 51/55] docs: instruct agents to fetch ICP skills before working Adds a Skills section at the top of AGENTS.md pointing to the live skills registry at skills.internetcomputer.org. Lists the relevant skills (icp-cli, icp-cli/dfx-migration, motoko, mops-cli, internet-identity, asset-canister) and establishes that skills take precedence over general knowledge where they overlap. Co-Authored-By: Claude Sonnet 4.6 --- AGENTS.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index f5eb5180f..23c37f00a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,27 @@ Guidelines for AI agents (Claude, Codex, Cursor, Copilot, etc.) working in this repository. +## Skills — fetch before working + +ICP skills are live reference documents maintained by DFINITY. Always fetch the relevant skills **before** making changes — do not rely on cached or training-data versions. + +**Index:** `https://skills.internetcomputer.org/.well-known/skills/index.json` + +Fetch the skill content at its `url` field. Skills relevant to this repo: + +| Task | Skill to fetch | +|------|---------------| +| Any ICP project work, `icp.yaml`, canister lifecycle | `icp-cli` | +| Migrating an example from dfx to icp-cli | `icp-cli` + its `references/dfx-migration.md` file | +| Motoko canister code | `motoko` | +| `mops.toml`, toolchain pinning, moc flags | `mops-cli` | +| Internet Identity integration | `internet-identity` | +| Frontend asset canister | `asset-canister` | + +Skills take precedence over general knowledge when both cover the same topic. + +--- + ## Repository overview This repo contains canonical ICP examples, each available in both Motoko and Rust. Every example lives under two sibling directories: From 9b30706db38ed8f90c0b4a818341edb7ee97a707 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 13:10:55 +0200 Subject: [PATCH 52/55] fix: add paths filter to ninja_pr_checks workflow Without a paths filter, changing the workflow file (e.g. removing hello_world and who_am_i which now use icp-cli) triggers the run_all_examples self-trigger, causing all ninja examples to run on unrelated PRs. Enumerating the exact ninja-managed paths ensures the workflow only fires when relevant example code or the workflow file itself changes. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ninja_pr_checks.yml | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.github/workflows/ninja_pr_checks.yml b/.github/workflows/ninja_pr_checks.yml index d906d59ea..ee4a3b735 100644 --- a/.github/workflows/ninja_pr_checks.yml +++ b/.github/workflows/ninja_pr_checks.yml @@ -4,6 +4,53 @@ on: pull_request: branches: - master + paths: + - 'hosting/**' + - 'motoko/backend_only/**' + - 'motoko/canister_logs/**' + - 'motoko/classes/**' + - 'motoko/basic_bitcoin/**' + - 'motoko/daily_planner/**' + - 'motoko/evm_block_explorer/**' + - 'motoko/filevault/**' + - 'motoko/flying_ninja/**' + - 'motoko/llm_chatbot/**' + - 'motoko/query_stats/**' + - 'motoko/send_http_get/**' + - 'motoko/send_http_post/**' + - 'motoko/superheroes/**' + - 'motoko/threshold-ecdsa/**' + - 'motoko/threshold-schnorr/**' + - 'motoko/tokenmania/**' + - 'motoko/nft-creator/**' + - 'rust/backend_only/**' + - 'rust/backend_wasm64/**' + - 'rust/basic_bitcoin/**' + - 'rust/canister-info/**' + - 'rust/canister_logs/**' + - 'rust/basic_ethereum/**' + - 'rust/candid_type_generation/**' + - 'rust/daily_planner/**' + - 'rust/evm_block_explorer/**' + - 'rust/flying_ninja/**' + - 'rust/guards/**' + - 'rust/llm_chatbot/**' + - 'rust/performance_counters/**' + - 'rust/periodic_tasks/**' + - 'rust/qrcode/**' + - 'rust/query_stats/**' + - 'rust/send_http_get/**' + - 'rust/send_http_post/**' + - 'rust/simd/**' + - 'rust/threshold-ecdsa/**' + - 'rust/tokenmania/**' + - 'rust/unit_testable_rust_canister/**' + - 'rust/photo_gallery/**' + - 'rust/inter-canister-calls/**' + - 'rust/x509/**' + - 'rust/receiving-icp/**' + - 'rust/exchange-rates/**' + - '.github/workflows/ninja_pr_checks.yml' concurrency: group: ninja-pr-checks-${{ github.workflow }}-${{ github.ref }} From cc1cdd4bf019101192800a8dd75f3d039e6d5310 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 26 May 2026 13:36:43 +0200 Subject: [PATCH 53/55] fix: pin Motoko recipe to immutable commit SHA Both Motoko examples now point to the specific commit of the fix branch rather than the branch name, which is mutable. Swap to a stable @dfinity/motoko release tag once dfinity/icp-cli-recipes#26 ships. Co-Authored-By: Claude Sonnet 4.6 --- AGENTS.md | 12 +++++++++--- motoko/hello_world/icp.yaml | 2 +- motoko/who_am_i/icp.yaml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 23c37f00a..d7ea80675 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -352,9 +352,15 @@ All devcontainer configs and CI workflows currently reference `ghcr.io/marc0olo/ Also note: `icp-dev-env-all` is a new combined Motoko+Rust image (for the root devcontainer) that does not yet exist in the dfinity org. ### Motoko recipe version -`motoko/who_am_i/icp.yaml` pins a raw GitHub URL to pick up `[moc] args` support before it ships in a stable release. Once https://github.com/dfinity/icp-cli-recipes/pull/26 merges and a new `@dfinity/motoko` version is released, update: -- `motoko/who_am_i/icp.yaml` -- `motoko/hello_world/icp.yaml` +Both `motoko/who_am_i/icp.yaml` and `motoko/hello_world/icp.yaml` pin a specific commit SHA of the Motoko recipe to pick up `[moc] args` support from `mops.toml` before it ships in a stable release: + +```yaml +type: https://raw.githubusercontent.com/dfinity/icp-cli-recipes/bc9581d9258d2d7feb15ab4ae8d04baf923b985f/recipes/motoko/recipe.hbs +``` + +Tracked in: https://github.com/dfinity/icp-cli-recipes/pull/26 + +Once that PR merges and a new `@dfinity/motoko` version is released, replace the raw URL in both files with the versioned tag (e.g. `@dfinity/motoko@vX.Y.Z`). --- diff --git a/motoko/hello_world/icp.yaml b/motoko/hello_world/icp.yaml index a4edd3a0e..580b3e389 100644 --- a/motoko/hello_world/icp.yaml +++ b/motoko/hello_world/icp.yaml @@ -1,7 +1,7 @@ canisters: - name: backend recipe: - type: "@dfinity/motoko@v4.1.0" + type: https://raw.githubusercontent.com/dfinity/icp-cli-recipes/bc9581d9258d2d7feb15ab4ae8d04baf923b985f/recipes/motoko/recipe.hbs configuration: main: backend/app.mo candid: backend/backend.did diff --git a/motoko/who_am_i/icp.yaml b/motoko/who_am_i/icp.yaml index 3a01d0623..5bb732fa8 100644 --- a/motoko/who_am_i/icp.yaml +++ b/motoko/who_am_i/icp.yaml @@ -6,7 +6,7 @@ networks: canisters: - name: backend recipe: - type: https://raw.githubusercontent.com/dfinity/icp-cli-recipes/refs/heads/fix/motoko-mops-moc-args/recipes/motoko/recipe.hbs + type: https://raw.githubusercontent.com/dfinity/icp-cli-recipes/bc9581d9258d2d7feb15ab4ae8d04baf923b985f/recipes/motoko/recipe.hbs configuration: main: src/backend/main.mo candid: src/backend/backend.did From d19e1c63e6456a86bdc76b4342d48d4270e24949 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 26 May 2026 13:45:36 +0200 Subject: [PATCH 54/55] refactor: adopt new Motoko recipe format (v5 schema) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new recipe only needs `name` in icp.yaml. Move `main`, `candid`, and per-canister args into mops.toml under [canisters.]. - hello_world: bump moc 1.5.1→1.8.2, core 2.4.0→2.5.0, add --default-persistent-actors, add [canisters.backend], drop `persistent` keyword from app.mo - who_am_i: add [canisters.backend] to existing mops.toml - Both READMEs: update .did regeneration command to `mops build backend --idl` - AGENTS.md: update icp.yaml and mops.toml reference examples Co-Authored-By: Claude Sonnet 4.6 --- AGENTS.md | 19 ++++++++++--------- motoko/hello_world/README.md | 4 ++-- motoko/hello_world/backend/app.mo | 2 +- motoko/hello_world/icp.yaml | 3 +-- motoko/hello_world/mops.toml | 10 +++++++--- motoko/who_am_i/README.md | 2 +- motoko/who_am_i/icp.yaml | 3 +-- motoko/who_am_i/mops.toml | 4 ++++ 8 files changed, 27 insertions(+), 20 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d7ea80675..549b11622 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -98,10 +98,9 @@ networks: # omit if no Internet Identity needed canisters: - name: backend recipe: - type: "@dfinity/motoko@v4.1.0" # see pending items below + type: "@dfinity/motoko@vX.Y.Z" # see pending items; currently pinned to a commit SHA configuration: - main: backend/app.mo - candid: backend/backend.did + name: backend # must match [canisters.backend] key in mops.toml - name: frontend recipe: @@ -142,21 +141,23 @@ canisters: ```toml [toolchain] -moc = "1.5.1" # bump to latest stable; who_am_i uses 1.8.2 +moc = "1.8.2" [dependencies] -core = "2.4.0" # replaces the old base library +core = "2.5.0" [moc] # M0236: use context dot notation # M0237: redundant explicit implicit arguments # M0223: redundant type instantiation -args = ["-W=M0236,M0237,M0223"] -``` +args = ["--default-persistent-actors", "-W=M0236,M0237,M0223"] -If your example uses `persistent actor`, add `--default-persistent-actors` to `args` (see `who_am_i` for reference) or use the `persistent` keyword explicitly. +[canisters.backend] +main = "backend/app.mo" +candid = "backend/backend.did" +``` -The `[moc] args` section requires a Motoko recipe that passes moc args from `mops.toml`. Until `@dfinity/motoko` gets this support in a stable release, `who_am_i` pins a fix-branch URL. Track: https://github.com/dfinity/icp-cli-recipes/pull/26 +`[canisters.]` replaces the `main`, `candid`, and `args` fields that were previously in `icp.yaml`. The `name` key must match the `name` in the recipe configuration. `--default-persistent-actors` makes all actors persistent by default, so the `persistent` keyword is not needed in source files. --- diff --git a/motoko/hello_world/README.md b/motoko/hello_world/README.md index 11965410f..c369722e6 100644 --- a/motoko/hello_world/README.md +++ b/motoko/hello_world/README.md @@ -56,10 +56,10 @@ icp deploy The `backend/backend.did` file defines the backend canister's public interface. The frontend TypeScript bindings are auto-generated from this file during the frontend build. -If you modify the backend's public API, regenerate the `.did` file using the Motoko compiler: +If you modify the backend's public API, regenerate the `.did` file: ```bash -$(mops toolchain bin moc) --idl -o backend/backend.did backend/app.mo +mops build backend --idl ``` ## Security considerations and best practices diff --git a/motoko/hello_world/backend/app.mo b/motoko/hello_world/backend/app.mo index 7bbd4f6f3..6e434246b 100644 --- a/motoko/hello_world/backend/app.mo +++ b/motoko/hello_world/backend/app.mo @@ -1,4 +1,4 @@ -persistent actor HelloWorld { +actor HelloWorld { // We store the greeting in a stable variable such that it gets persisted over canister upgrades. var greeting : Text = "Hello, "; diff --git a/motoko/hello_world/icp.yaml b/motoko/hello_world/icp.yaml index 580b3e389..3c1670447 100644 --- a/motoko/hello_world/icp.yaml +++ b/motoko/hello_world/icp.yaml @@ -3,8 +3,7 @@ canisters: recipe: type: https://raw.githubusercontent.com/dfinity/icp-cli-recipes/bc9581d9258d2d7feb15ab4ae8d04baf923b985f/recipes/motoko/recipe.hbs configuration: - main: backend/app.mo - candid: backend/backend.did + name: backend - name: frontend recipe: diff --git a/motoko/hello_world/mops.toml b/motoko/hello_world/mops.toml index dc89d0891..49573d4b5 100644 --- a/motoko/hello_world/mops.toml +++ b/motoko/hello_world/mops.toml @@ -1,11 +1,15 @@ [toolchain] -moc = "1.5.1" +moc = "1.8.2" [dependencies] -core = "2.4.0" +core = "2.5.0" [moc] # M0236: use context dot notation (e.g. x.toText() instead of Nat.toText(x)) # M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically) # M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate) -args = ["-W=M0236,M0237,M0223"] +args = ["--default-persistent-actors", "-W=M0236,M0237,M0223"] + +[canisters.backend] +main = "backend/app.mo" +candid = "backend/backend.did" diff --git a/motoko/who_am_i/README.md b/motoko/who_am_i/README.md index e57ee3d64..ab0df3a0d 100644 --- a/motoko/who_am_i/README.md +++ b/motoko/who_am_i/README.md @@ -56,7 +56,7 @@ The `src/backend/backend.did` file defines the backend canister's public interfa If you modify the backend's public API, regenerate the `.did` file: ```bash -$(mops toolchain bin moc) --idl $(mops sources) -o src/backend/backend.did src/backend/main.mo +mops build backend --idl ``` ## Security considerations and best practices diff --git a/motoko/who_am_i/icp.yaml b/motoko/who_am_i/icp.yaml index 5bb732fa8..4cd8aef9c 100644 --- a/motoko/who_am_i/icp.yaml +++ b/motoko/who_am_i/icp.yaml @@ -8,8 +8,7 @@ canisters: recipe: type: https://raw.githubusercontent.com/dfinity/icp-cli-recipes/bc9581d9258d2d7feb15ab4ae8d04baf923b985f/recipes/motoko/recipe.hbs configuration: - main: src/backend/main.mo - candid: src/backend/backend.did + name: backend - name: frontend recipe: diff --git a/motoko/who_am_i/mops.toml b/motoko/who_am_i/mops.toml index 2fbc4f292..85ea333c4 100644 --- a/motoko/who_am_i/mops.toml +++ b/motoko/who_am_i/mops.toml @@ -9,3 +9,7 @@ core = "2.5.0" # M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically) # M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate) args = ["--default-persistent-actors", "-W=M0236,M0237,M0223"] + +[canisters.backend] +main = "src/backend/main.mo" +candid = "src/backend/backend.did" From f3f3bb42a9e21c3b3305a1365d31b8b38bbcfcb6 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 26 May 2026 13:50:10 +0200 Subject: [PATCH 55/55] fix: remove deleted examples from ninja_pr_checks paths filter tokenmania, nft-creator (Motoko) and tokenmania (Rust) were removed from master; drop their path entries to keep the filter in sync. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ninja_pr_checks.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ninja_pr_checks.yml b/.github/workflows/ninja_pr_checks.yml index ee4a3b735..b4bc032a7 100644 --- a/.github/workflows/ninja_pr_checks.yml +++ b/.github/workflows/ninja_pr_checks.yml @@ -21,8 +21,6 @@ on: - 'motoko/superheroes/**' - 'motoko/threshold-ecdsa/**' - 'motoko/threshold-schnorr/**' - - 'motoko/tokenmania/**' - - 'motoko/nft-creator/**' - 'rust/backend_only/**' - 'rust/backend_wasm64/**' - 'rust/basic_bitcoin/**' @@ -43,7 +41,6 @@ on: - 'rust/send_http_post/**' - 'rust/simd/**' - 'rust/threshold-ecdsa/**' - - 'rust/tokenmania/**' - 'rust/unit_testable_rust_canister/**' - 'rust/photo_gallery/**' - 'rust/inter-canister-calls/**'