Provisioning
The operator-owned neutron-provision system creates and reinstalls whole
Neutron canisters through one pipeline with two target adapters and the same
target-neutral package bytes. It is distinct from the hosted browser
Dispenser flow.
Configuration is desired state
A config is an archive-only declaration: an artifact set, and the fleet of canisters to produce from it.
It has 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 — a separation that exists so a provisioning run cannot be influenced by app-supplied build logic.
Two pin regimes
| Target | Requirement |
|---|---|
| Local PocketIC (inline set) | A kernel path and an ordered app-path list. No hashes, byte lengths, ids, or versions to maintain. |
| IC / external release sets | Complete {path, sha256, bytes, id, version} pins, every one verified. |
Loading a path-only local config keeps only its relative declarations. Only a local reinstall resolves and reads each archive, deriving its exact bytes, digest, package identity, version, and kernel-or-app role. That derived digest becomes an internal input to the compile cache, the operation fingerprint, and the deployment receipt.
Unknown artifact-set fields, duplicate ids or paths, archive drift during an operation, and symlink escapes all fail closed.
One session per config
Every writable config derives exactly one private session file, even when several configs share the repository-wide PocketIC supervisor.
It holds runtime attachment data, the sole ordered fleet mapping (index zero is the primary node), permanent IC creation or adoption evidence, the latest verified deployment, and at most one active operation.
Canister identity lives here — not in a tracked mapping directory.
Operations
There are exactly six commands. Their availability and mutation semantics differ by target.
| Operation | Local PocketIC | IC |
|---|---|---|
serve | Start or attach the supervisor | Rejected |
reinstall | Always executes, destructive | Plan-only unless an execute flag is passed |
authorize PRINCIPAL | Fleet-wide | Rejected |
status | Verify the live supervisor and session binding, then print the recorded fleet | Validate and print the local journal only |
create | Rejected — use serve, then reinstall | Paid, resumable |
adopt CANISTER_ID | Rejected | Live-verify an existing Neutron; --execute records the adoption without mutating it |
IC create and reinstall are read-only plans by default. Remote mutation
requires --execute; --yes is accepted only with --execute for those two
commands. adopt is also plan-only by default, but its execution writes only a
local verified receipt. It rejects --yes and never mutates the canister.
:::note What status actually checks
On a local target, status verifies the supervisor process identity, instance
availability, auto-progress, topology digest, gateway root key, and the
config-to-session binding — then prints the recorded fleet from the session.
It does not call each node canister.
authorize is the command that inspects every node: it checks each one's
status, module hash, and controllers.
On an IC target, status intentionally makes no live IC query. Use a new
create/reinstall plan, adopt, or the separate deployed-Kernel observation
check when current network state must be proved.
:::
The local flow
Several configs can attach their own sessions to the live supervisor rather than starting competing servers.
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 — which
is the intended behaviour: authorization is desired state, and a temporary grant
is explicitly temporary.
Local fixtures
Fixture lifecycle is provisioner-owned and profile-specific. minimal provides
the Application and trust-root infrastructure, Internet Identity, threshold-key
test subnet, and a pinned local update-source asset canister whose ID is recorded
in the session.
full_protocol_fixtures adds deterministic ledger/index fixtures, persistent
loopback-only Bitcoin Core regtest and Foundry Anvil processes started before
PocketIC, plus pinned BTC-checker, ckBTC-minter, EVM-RPC, and ckETH-minter
canisters funded through their native deposit paths. Its native-chain fixture
funding applies to the fleet's primary node.
None of this reintroduces a separate workspace deployment command. A Neutron reinstall only binds the recorded update-source origin.
Production creation
The IC adapter:
- validates pinned packages, certified subnet evidence, and the deployment identity, then compiles the complete actor in a read-only plan;
- shows the full spend, controller, and evidence boundary;
- on execution, pays the CMC and selects an exact target subnet;
- creates the canister and establishes its final controller set;
- routes chunked management calls with the child as effective canister ID;
- installs the already-planned actor, seeds certified assets, and binds runtime config;
- authorizes the deployment identity;
- verifies the fresh Neutron and records final deployment evidence.
The final management controller set is the deployment identity, configured
backup controllers, and the Neutron itself. Kernel authorization is separate:
the fresh Kernel authorizes only the deployment identity. An Internet Identity
principal obtained later in the browser must be added by an already-authorized
identity; the PocketIC-only authorize command does not perform that production
step.
Creation evidence is permanent in the session. A completed create never
creates a second canister for that session. The same session can later reset the
already-paid canister without another CMC payment or creation.
Adoption and destructive reinstall
adopt CANISTER_ID is the only way to establish a session source for an
existing IC Neutron. The plan live-verifies the exact current V25 runtime,
module, package inventory, subnet evidence, controllers, settings, and deployer
authorization. --execute repeats the proof under the operation locks and
writes a private adoption receipt; it sends no ICP and changes nothing on the
canister.
An IC reinstall must derive its canister ID from permanent creation or adoption
evidence. It has no canister-ID override. Execution preserves the canister ID,
subnet, controllers, settings, remaining cycles apart from execution costs, and
the original running/stopped state, but deliberately erases all Kernel state,
app data, browser authorizations, certified assets, and snapshots. It then
installs and verifies the complete configured package set and authorizes only
the deployment identity.
Changing desired-state config is permitted only through a fresh reinstall plan when no operation is active. An active operation must resume with the exact config, package, compiler, controller, and evidence fingerprints already bound to its journal.
Interruption recovery
Before the first irreversible IC call, the provisioner fsyncs the exact binary
payload under .neutron/provision/ and records its digest in the active session
operation. On a retry it reconciles ambiguous ledger, CMC, canister-creation,
chunk upload, install, snapshot, module, and runtime results instead of rebuilding
from mutable source or repeating a confirmed effect.
Crash recovery uses a temporary binary payload. A completed operation may remain marked active while payload cleanup is pending; rerunning the executing command finishes that cleanup. Completed operations do not retain archive JSON bundles.
Do not delete an active IC session or payload after an ambiguous response. Rerun
the same command with --execute and the exact same inputs. The journal first
records the verified deployment, then removes the payload, then clears the
active operation, so a crash between those steps is recoverable.
Runtime binding
Once the final canister id is known, the provisioner writes the certified closed
ten-field /system/runtime-config.json, containing the exact isolated-frame
origin template and the update-source-origin override. PocketIC binds the exact
session-recorded fixture origin. IC requires null, meaning the Kernel derives
each package's verified https://<source-principal>.icp0.io origin from its
manifest rather than applying a global override.
The kernel validates that record before initialising browser network clients or frames, and frame URL construction fails closed unless the result matches the certified template.
Network identity
The assembler compiles a trusted immutable installation.network_id into the
actor:
- Production derives it from the compiler-pinned IC mainnet root-key SPKI DER.
- Local derives it from the exact status root key of the attached pinned PocketIC instance.
Runtime config, manifests, and app scripts cannot supply it. Every fleet node gets the same network identity but its own canister-bound runtime config.
The current provisioner and in-product compiler require the current V25 stable installation context. A prior stable signature that does not own that context is rejected with a clean-reinstall requirement. There is no legacy assembler acceptance path. The provisioner itself has no state-preserving upgrade command; its reinstall operations are destructive.
Certified asset restoration
Package preparation rewrites kernel files to root and package paths, app files
to /app/<id>/, and shared modules to /mo/<sha256>.mo. Provisioning clears
the new actor's static namespace and restores the complete file set.
Identical files resolving to one final content-addressed path are deduplicated; conflicting bytes for one path fail. Uploads may be scheduled concurrently, but each update call carries exactly one file operation or one subsequent chunk.
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 path that runs an app-owned build script during provisioning