Background
ICP Ninja currently serves two roles for the examples repo:
- A browser-based IDE that deploys examples to mainnet for 20-30 minutes ("try in browser")
- A launch point linked from the developer-docs repo via commit-pinned URLs
This issue proposes replacing that setup with GitHub Codespaces — a mature, zero-infrastructure cloud IDE that provides a full, pre-configured development environment at the click of a button, without short-lived mainnet deployments.
Related: dfinity/developer-docs#44 (which proposes deeper ICP Ninja integration in docs — the Codespaces approach is the alternative to consider).
Why Codespaces
- No infrastructure to maintain — DFINITY doesn't need to run canister deployments or pay cycles for demos
- Full dev environment — developers can actually modify and learn from examples, not just watch them run
- Free tier — 120 core-hours/month per GitHub user at no cost
- VS Code — familiar editor, no proprietary tooling to learn
- One-click per example — deep-link URL opens the exact example with the right toolchain pre-installed
- Gitpod is no longer a viable alternative — Gitpod Classic shut down October 2025; the company rebranded to "Ona" and pivoted to AI agents
Architecture
Local network lifecycle
icp network start binds to port 8000 by default. The -d flag runs it as a background daemon (required — foreground mode blocks the terminal).
When a Codespace is suspended (idle timeout or manual stop), the network daemon is killed but the filesystem is preserved. State is ephemeral (PocketIC is in-memory), so every Codespace session starts fresh. There is no need to hook into Codespace close events — icp network stop is effectively irrelevant in this environment.
postStartCommand in devcontainer.json handles automatic network start on every Codespace open and resume:
"postStartCommand": "icp network start -d"
Two images (managed in icp-dev-env)
| Tool |
icp-dev-env-motoko |
icp-dev-env-rust |
| Node.js LTS |
✓ |
✓ |
| pnpm |
✓ |
✓ |
| icp-cli |
✓ |
✓ |
| ic-wasm |
✓ |
✓ |
| moc |
✓ |
— |
| mops |
✓ |
— |
| Rust toolchain + wasm32 target |
— |
✓ |
Node.js is required in both images because icp-cli and ic-wasm are npm packages. ic-wasm is needed in both because it is used by icp-cli build recipes for both languages.
Per-example devcontainer.json
All 46 existing devcontainer.json files are currently byte-for-byte identical — there is no per-example customization today. Under the new approach they gain a meaningful difference: a workspaceFolder that points VS Code directly into the example's subdirectory, enabling per-example Codespaces deep-links.
Motoko template:
{
"name": "<Example Name> (Motoko)",
"image": "ghcr.io/dfinity/icp-dev-env-motoko:1",
"workspaceFolder": "/workspaces/examples/motoko/<example-name>",
"forwardPorts": [8000],
"portsAttributes": {
"8000": { "label": "ICP local network", "onAutoForward": "ignore" }
},
"postCreateCommand": "mops install",
"postStartCommand": "icp network start -d",
"customizations": {
"vscode": {
"extensions": ["dfinity-foundation.vscode-motoko", "stateful.runme"]
}
}
}
Rust template:
{
"name": "<Example Name> (Rust)",
"image": "ghcr.io/dfinity/icp-dev-env-rust:1",
"workspaceFolder": "/workspaces/examples/rust/<example-name>",
"forwardPorts": [8000],
"portsAttributes": {
"8000": { "label": "ICP local network", "onAutoForward": "ignore" }
},
"postStartCommand": "icp network start -d",
"customizations": {
"vscode": {
"extensions": ["rust-analyzer", "stateful.runme"]
}
}
}
Add port 5173 to forwardPorts and portsAttributes for examples that include a Vite frontend.
"Open in Codespaces" URL
The Codespaces deep-link URL with devcontainer_path and ref is a direct drop-in for the current ICP Ninja URL pattern:
|
Example |
| ICP Ninja |
https://icp.ninja/i?g=https://github.com/dfinity/examples/tree/{sha}/rust/send_http_get |
| Codespaces |
https://codespaces.new/dfinity/examples?devcontainer_path=rust%2Fsend_http_get%2F.devcontainer%2Fdevcontainer.json&ref={sha} |
The ref parameter accepts a full commit SHA, so the developer-docs submodule bump workflow can update Codespaces URLs with the same SHA it already uses for ICP Ninja links. No automation change needed — just a URL pattern change.
Note: Codespaces Prebuilds are branch-based, not commit-based. When developer-docs pins to a specific SHA that isn't the current master HEAD, the prebuild won't apply and startup will be slightly slower. This is a minor trade-off.
Runme action buttons
The Runme VS Code extension (pre-installed via devcontainer.json) turns README code blocks into clickable buttons. No custom extension or .vscode/tasks.json needed.
Standard button set for all examples:
## Local development
> The local ICP network starts automatically when this Codespace opens.
**Check network**
```sh { name=network-status }
icp network status
```
**Build**
```sh { name=build }
icp build
```
**Deploy** *(first run installs; subsequent runs upgrade and preserve stable state)*
```sh { name=deploy }
icp deploy
```
**Reset & redeploy** *(wipes all canister state — fresh install)*
```sh { name=reset-deploy }
icp deploy --mode reinstall -y
```
**Show canister info**
```sh { name=info }
icp environment
```
Add a Start frontend dev server button (npm run dev or pnpm dev) for examples with a frontend.
Key notes:
icp deploy (default --mode auto) is idempotent and state-preserving — it installs on first run and upgrades on subsequent runs without losing stable variable state
--mode reinstall -y is the explicit "wipe and reset" option; -y skips the Candid compatibility prompt that would otherwise block the button
- No "Stop network" or "Start network" buttons — the lifecycle is handled automatically
Mainnet guidance
Developers should not manage mainnet identities in Codespaces. If the Codespace is deleted, identities (and any associated cycles/canister ownership) are lost with no warning. Every README should include:
## Ready to deploy on mainnet?
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.
Checklist
Prerequisite: icp-dev-env repo
This repo (examples)
developer-docs repo
Infrastructure
Open questions
- Should
dfx.json be removed immediately or kept during a transition window while ICP Ninja still serves existing links?
- Are there examples where
icp network start -d should not be the postStartCommand (e.g., examples that only target mainnet)?
- Should the Codespaces badge replace the ICP Ninja badge in READMEs, or sit alongside it during a transition period?
Background
ICP Ninja currently serves two roles for the examples repo:
This issue proposes replacing that setup with GitHub Codespaces — a mature, zero-infrastructure cloud IDE that provides a full, pre-configured development environment at the click of a button, without short-lived mainnet deployments.
Related: dfinity/developer-docs#44 (which proposes deeper ICP Ninja integration in docs — the Codespaces approach is the alternative to consider).
Why Codespaces
Architecture
Local network lifecycle
icp network startbinds to port 8000 by default. The-dflag runs it as a background daemon (required — foreground mode blocks the terminal).When a Codespace is suspended (idle timeout or manual stop), the network daemon is killed but the filesystem is preserved. State is ephemeral (PocketIC is in-memory), so every Codespace session starts fresh. There is no need to hook into Codespace close events —
icp network stopis effectively irrelevant in this environment.postStartCommandindevcontainer.jsonhandles automatic network start on every Codespace open and resume:Two images (managed in icp-dev-env)
icp-dev-env-motokoicp-dev-env-rustNode.js is required in both images because
icp-cliandic-wasmare npm packages.ic-wasmis needed in both because it is used by icp-cli build recipes for both languages.Per-example
devcontainer.jsonAll 46 existing
devcontainer.jsonfiles are currently byte-for-byte identical — there is no per-example customization today. Under the new approach they gain a meaningful difference: aworkspaceFolderthat points VS Code directly into the example's subdirectory, enabling per-example Codespaces deep-links.Motoko template:
{ "name": "<Example Name> (Motoko)", "image": "ghcr.io/dfinity/icp-dev-env-motoko:1", "workspaceFolder": "/workspaces/examples/motoko/<example-name>", "forwardPorts": [8000], "portsAttributes": { "8000": { "label": "ICP local network", "onAutoForward": "ignore" } }, "postCreateCommand": "mops install", "postStartCommand": "icp network start -d", "customizations": { "vscode": { "extensions": ["dfinity-foundation.vscode-motoko", "stateful.runme"] } } }Rust template:
{ "name": "<Example Name> (Rust)", "image": "ghcr.io/dfinity/icp-dev-env-rust:1", "workspaceFolder": "/workspaces/examples/rust/<example-name>", "forwardPorts": [8000], "portsAttributes": { "8000": { "label": "ICP local network", "onAutoForward": "ignore" } }, "postStartCommand": "icp network start -d", "customizations": { "vscode": { "extensions": ["rust-analyzer", "stateful.runme"] } } }Add port
5173toforwardPortsandportsAttributesfor examples that include a Vite frontend."Open in Codespaces" URL
The Codespaces deep-link URL with
devcontainer_pathandrefis a direct drop-in for the current ICP Ninja URL pattern:https://icp.ninja/i?g=https://github.com/dfinity/examples/tree/{sha}/rust/send_http_gethttps://codespaces.new/dfinity/examples?devcontainer_path=rust%2Fsend_http_get%2F.devcontainer%2Fdevcontainer.json&ref={sha}The
refparameter accepts a full commit SHA, so the developer-docs submodule bump workflow can update Codespaces URLs with the same SHA it already uses for ICP Ninja links. No automation change needed — just a URL pattern change.Runme action buttons
The Runme VS Code extension (pre-installed via
devcontainer.json) turns README code blocks into clickable buttons. No custom extension or.vscode/tasks.jsonneeded.Standard button set for all examples:
Add a Start frontend dev server button (
npm run devorpnpm dev) for examples with a frontend.Key notes:
icp deploy(default--mode auto) is idempotent and state-preserving — it installs on first run and upgrades on subsequent runs without losing stable variable state--mode reinstall -yis the explicit "wipe and reset" option;-yskips the Candid compatibility prompt that would otherwise block the buttonMainnet guidance
Developers should not manage mainnet identities in Codespaces. If the Codespace is deleted, identities (and any associated cycles/canister ownership) are lost with no warning. Every README should include:
Checklist
Prerequisite:
icp-dev-envrepoghcr.io/dfinity/icp-dev-env-motoko:1with the toolchain aboveghcr.io/dfinity/icp-dev-env-rust:1with the toolchain aboveThis repo (
examples).icp/to.gitignorerepo-wide (cached network state is currently being tracked unintentionally)devcontainer.jsonfiles: new image, port 8000 (not 4943),workspaceFolder,postStartCommand, correct extensions per language,postCreateCommand: "mops install"for Motoko onlyicp.yamlto Motoko examples that only havedfx.json(prerequisite for icp-cli to work — part of ongoing Motoko migration)dfx.jsonfrom examples or keep temporarily for ICP Ninja backward compatibilitydeveloper-docsrepoInfrastructure
masterbranch to reduce cold-start timeOpen questions
dfx.jsonbe removed immediately or kept during a transition window while ICP Ninja still serves existing links?icp network start -dshould not be thepostStartCommand(e.g., examples that only target mainnet)?