Skip to main content

neutron-provision

The repository's general-purpose system for creating and reinstalling whole Neutron canisters.

It has one pipeline with two target adapters: a persistent local PocketIC fleet, and paid creation/reinstall on the IC. Both consume the same target-neutral package bytes.

Configuration is desired state

A provisioning config is an archive-only declaration of what should exist. It names an artifact set — either inline or external — and an IC target or a named local fleet.

It contains no workspace path, no build command, and no hook. The provisioner never builds a workspace and never executes an app-owned script. Producing the archives is the trusted package/release workflow's job.

Two pin regimes exist:

TargetPin requirements
Local PocketIC (inline set)A kernel path and an ordered app-path list. Developers do not maintain hashes, byte lengths, ids, or versions.
IC / external release setsComplete {path, sha256, bytes, id, version} pins, all verified.

Loading a path-only local config keeps only the relative declarations. Only a local reinstall actually resolves and reads each archive, deriving its exact bytes, digest, identity, version, and kernel-or-app role.

One session per config

Every config path derives one session path, even when several local configs share the repository-wide PocketIC supervisor. Once written, that session can hold:

  • runtime attachment data
  • the ordered local fleet mapping (index zero is the default node)
  • permanent IC creation evidence, where applicable
  • the latest verified deployment
  • at most one active operation

Canister identity lives here, not in a separate mapping directory. Session files are private state, not tracked configuration.

The command surface

Six operations exist. Their availability differs by target:

OperationLocal PocketICIC
serve✅ start/attach the supervisorrejected
reinstall✅ always executes, destructive✅ requires explicit execute flag
authorize PRINCIPAL✅ fleet-widerejected
status✅ supervisor + session binding
createrejected — serve, then reinstall✅ paid, resumable
adopt CANISTER_IDrejected✅ adopt an existing canister

IC mutations are read-only by default and require an explicit execute flag — the plan is always available without spending anything.

npm run provision -- CONFIG.ndeploy.json COMMAND

adopt CANISTER_ID, create, and IC reinstall accept --execute; create and IC reinstall also accept --yes.

What a local reinstall actually does

  1. Attach to the one repository-wide checksum-pinned PocketIC process, its persisted instance, its fixtures, and the fixed browser gateway.
  2. Resolve and read every declared archive once, deriving exact identity.
  3. Compile the complete actor once (using the verified compile cache).
  4. Reconcile any ambiguous interrupted install, then install or destructively reinstall every node in the ordered fleet. A browser-installed module change does not block the explicitly destructive reinstall.
  5. Clear the new actor's static namespace and restore the complete configured file set — one file operation per update call.
  6. Apply a separately canister-bound runtime configuration to each node.
  7. Authorize the deterministic developer identity plus each target's configured principals, fleet-wide.
  8. Fund provision-owned fixtures according to fleet policy.
  9. Verify, then report every labeled browser URL.

The separate authorize command adds and verifies a temporary principal fleet-wide without changing the deployment receipt — the next full reinstall resets each node to the developer plus the configured desired-state list.

Local fixtures

PocketIC fixture lifecycle is provisioner-owned. The minimal profile provides the application, lean NNS trust-root, Internet Identity, and test-threshold-key subnets plus the local update source. full_protocol_fixtures adds the other system subnets, ledger/index fixtures, and native chain services.

For that full profile, persistent loopback-only Bitcoin Core regtest and Foundry Anvil processes start before PocketIC; the provisioner then installs pinned BTC-checker, ckBTC-minter, EVM-RPC, and ckETH-minter canisters and funds Neutron through their native deposit paths. The provision-owned update-source asset canister is installed or reused and recorded in the session.

None of this reintroduces a separate workspace deployment command.

Production creation

The IC adapter pays the CMC, selects an exact target subnet, routes chunked management calls with an effective canister id, seeds certified package assets, and verifies the fresh Neutron.

Creation evidence is permanent in the session, so the same session can later reset an already-paid canister without another CMC payment or creation. Crash recovery uses a temporary binary payload; a completed operation may remain marked active while payload cleanup is pending, and rerunning the executing command finishes that cleanup.

See Provisioning for the operator view.

Runtime binding

Once the final canister id is known, the provisioner writes the certified closed ten-field /system/runtime-config.json. It includes the exact isolated-frame origin template and the explicit provision-owned local update-source origin — or null for an unconfigured IC source.

The kernel validates that record before initialising browser network clients or frames. Frame URL construction fails closed unless the result matches the certified template.

Deliberately unsupported

  • Per-app local CLI install, uninstall, upgrade, or unchanged no-op
  • Offline local compilation (only the attached provisioner has the verified PocketIC root context)
  • Any provisioning path that runs an app-owned build script