Skip to content

Replace ICP Ninja / short-lived mainnet deployments with GitHub Codespaces #1345

@marc0olo

Description

@marc0olo

Background

ICP Ninja currently serves two roles for the examples repo:

  1. A browser-based IDE that deploys examples to mainnet for 20-30 minutes ("try in browser")
  2. 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

  • Remove dfx/dfxvm from the Docker image
  • Publish ghcr.io/dfinity/icp-dev-env-motoko:1 with the toolchain above
  • Publish ghcr.io/dfinity/icp-dev-env-rust:1 with the toolchain above

This repo (examples)

  • Add .icp/ to .gitignore repo-wide (cached network state is currently being tracked unintentionally)
  • Update all 46 devcontainer.json files: new image, port 8000 (not 4943), workspaceFolder, postStartCommand, correct extensions per language, postCreateCommand: "mops install" for Motoko only
  • Update all Rust example READMEs: Codespaces badge, Runme blocks, remove dfx references, add mainnet guidance section
  • Update all Motoko example READMEs: same as above
  • Add icp.yaml to Motoko examples that only have dfx.json (prerequisite for icp-cli to work — part of ongoing Motoko migration)
  • Decide: remove dfx.json from examples or keep temporarily for ICP Ninja backward compatibility

developer-docs repo

Infrastructure

  • Configure Codespaces Prebuilds on this repo for the master branch to reduce cold-start time
  • Decide on ICP Ninja deprecation / transition timeline

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions