Versioning
Packed release integers
App releases use semantic major.minor.patch labels in filenames and UI, but
the manifest and Candid wire carry one Nat:
packed = major * 10_000 + minor * 100 + patch
minor and patch are each 0–99.
| Semantic | Packed |
|---|---|
0.1.0 | 100 |
0.1.1 | 101 |
0.2.0 | 200 |
1.0.0 | 10000 |
The supported floor is 0.1.0; lower packed values are rejected.
Where each form appears:
| Uses the packed integer | Uses the semantic label |
|---|---|
neutron.json version | Package filenames |
Dependency min_version | Install and Settings UI |
| Install inventories | Human-facing release notes |
| Repository manifests |
The upgrade policy
Owner-facing browser installs are release upgrades. Replacing an installed app requires a strictly higher packed version. Equal versions and downgrades are rejected before compilation even starts, and the kernel independently rejects a target below the committed version.
This is enforced in two places on purpose — the browser check gives a fast, clear error, and the canister check means a client that skipped it still cannot land a downgrade.
The trusted local whole-set path is different: it performs a destructive reinstall rather than an incremental browser update and may explicitly permit an equal-version redeploy. It still rejects downgrades when preserving an installed set.
An app distributed to users must increment its release version for every update. There is no in-place republish.
Independent version lanes
Several things in a Neutron package are versioned, and they are deliberately not coupled:
| Lane | Form | Changes when |
|---|---|---|
| App release | Packed integer, min 100 | You ship anything |
| Memory schema version | Positive integer per memory root | The persistent type changes |
| Capability API version | Positive integer per capability | The capability's contract changes |
Manifest format | Currently 3 | The manifest contract changes |
Lockfile format | Currently 2 | The lineage lock contract changes |
Note that the manifest is format 3 while the managed-memory lock remains format 2 — that is correct, not a mismatch.
Capability API versions are per capability. The authored Certified Assets
declaration and its backend interface are API 2, while the derived
certified_read_routes capability-plan entry is API 1. HTTP POST handler routes
are also API 1. Publication mounts derive exact-Neutron-host GET/HEAD;
portable immutable and mutable blob mounts derive canister-gateway GET.
Version compatibility for dependencies
A consumer declares min_version for each provider it depends on. The compiler
accepts a provider at or above that version, provided every requested
function is still exposed and still type-compatible.
The obligation this places on a provider is worth stating plainly:
Later provider releases must preserve exported functions compatibly. Breaking a signature means introducing a new function name, not changing the old one.
A provider cannot be uninstalled while a consumer still declares it.
Skipped releases
A package that supports installation over several older schema versions must preserve each supported installed schema identity and carry schemas and migration edges that form exactly one path from that version to the target. Edges may jump schema versions; they do not have to correspond to every skipped app release. A multi-edge path is composed into one migration plan.
See Managed memory.
update_source
An app may name a canonical, non-anonymous, non-management canister principal as its update source. A later package may change or remove it; the kernel displays that transition during review and commits it with the rest of the package.
The field identifies a download location. It is not a trusted publisher, not an attestation, and not a capability grant. See Updates.