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:
| Target | Pin 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 sets | Complete {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:
| Operation | Local PocketIC | IC |
|---|---|---|
serve | ✅ start/attach the supervisor | rejected |
reinstall | ✅ always executes, destructive | ✅ requires explicit execute flag |
authorize PRINCIPAL | ✅ fleet-wide | rejected |
status | ✅ supervisor + session binding | ✅ |
create | rejected — serve, then reinstall | ✅ paid, resumable |
adopt CANISTER_ID | rejected | ✅ 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
- Attach to the one repository-wide checksum-pinned PocketIC process, its persisted instance, its fixtures, and the fixed browser gateway.
- Resolve and read every declared archive once, deriving exact identity.
- Compile the complete actor once (using the verified compile cache).
- 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.
- Clear the new actor's static namespace and restore the complete configured file set — one file operation per update call.
- Apply a separately canister-bound runtime configuration to each node.
- Authorize the deterministic developer identity plus each target's configured principals, fleet-wide.
- Fund provision-owned fixtures according to fleet policy.
- 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