Skip to main content

Package updates

An app may opt into update checks by naming a canonical canister principal in its manifest:

{ "update_source": "<canister-principal>" }

Omitting it means manual updates only.

What update_source means

caution

The field identifies a download location. It is not a trusted publisher, it does not attest anything, and it grants the app no authority.

Certification proves that the named source canister served particular bytes. It does not prove who authored, audited, or approved them. A source controller can replace or reconfigure the canister, and a Commit publisher on the reference asset canister can publish package bytes. Both are therefore operational trust roots for what that source advertises, even though neither can silently install an update into a Neutron.

A later package may change or remove it. The kernel displays that transition during review and commits it with the rest of the package — a source change is part of what you are approving.

The field is committed package state, not a Kernel-wide default. An already installed app does not acquire a newly added source merely because the Kernel or starter changed; it needs an owner-reviewed package update that contains the field.

How a check works

Trusted kernel UI constructs the request; the app has no say in it.

  • The kernel builds only the verified icp0.io origin, plus fixed /repo/v1/releases/<id>.json and digest-addressed package paths.
  • It relies on that gateway to require HTTP response-certification v2.
  • It rejects redirects and origin changes, omits credentials, referrers, and bodies, and bounds streaming reads.
  • It validates the exact record, size, digest, and inner package identity.
  • Raw gateway origins are never accepted.

On PocketIC, a certified runtime-config override is used only when its embedded canister ID matches the package's update_source. A production-pinned manifest is not transparently redirected to an unrelated local fixture.

There is a subtle CORS rule worth knowing: when CORS exposes either proof header to JavaScript, the client also requires the complete v2 envelope. It accepts both headers being hidden only on the fixed verified gateway origin.

Privacy

Checks disclose the requested app ids plus unavoidable network metadata. They do not disclose installed versions, principals, cookies, or unrelated inventory.

They run only while an authorized user opens or refreshes Settings. There is no background polling.

Requests are grouped by source, sorted, and issued in waves of at most 20. One failure does not discard successful results for other apps or sources. A source that advertises a lower version is shown as behind; an equal version is current only when its digest agrees with the installed package provenance. Missing digest evidence is unverifiable, and an equal-version digest change is treated as equivocation.

Why per-app assets

The design uses one certified release record per app rather than a single catalog. That means a check for app A reveals interest in app A — not in your whole installed set — and it lets independent publishers operate independent sources without a shared index anyone has to trust.

Applying an update

An update is not a shortcut. Package bytes cross every normal boundary:

  1. bounded decoding and manifest validation
  2. the compiler and the static Motoko policy
  3. capability and dependency review, including the exact plan diff
  4. the managed-memory planner and stable-signature check
  5. the deployment journal, activation verification, and atomic commit
  6. recovery, if anything is interrupted

The release-version rule still applies: strictly higher, no equal, no downgrade.

Immediately before preparation the Kernel refetches every selected release and requires the same release-record digest, app ID, version, size, and package digest observed during the check. It also requires the installed baseline and source to remain unchanged. The owner may update one row or review all verified available rows as one batch; a failed member is not silently dropped.

If preparation or compilation fails, deployment has not begun. After a checked journal exists, that journal is authoritative: Neutron reconciles an active runtime to commit, waits while activation may still arrive, and exposes the persistent Settings recovery panel when owner action is safe. A deployment failure requires recovery and then a fresh source check before another batch.

Skipped releases

If a user skips releases, managed memory still composes one migration path from their installed schema version to the target. Your package must carry every intermediate schema and one unique path from each supported start version.

That is why the packager includes every declared memory root even when your current app module no longer imports it.

The reference source

A reference certified update source exists in the repository. Its design is instructive:

  • It pins the standard asset canister.
  • Its publisher is a non-controller principal with only the commit role — publishing does not confer canister control.
  • It provides atomic package and release publication, and public verification.
  • Publishing uses authenticated Candid calls, not public ingress.

That separation — publisher authority is narrower than controller authority — is the property to copy if you run your own source.

The separation limits damage but does not make the publisher untrusted: Commit can still publish assets. The source therefore enforces one distinct, non-controller Commit publisher operationally, and publication must be serialized because the stock asset canister has no generation compare-and-swap.

Publishing and starter updates

Routine SushiOS publication is driven by the closed production catalog:

npm run updates:publish

The command does not build apps or run their release tests. It loads already built .neutron archives, requires each inner manifest to name the catalog's source, derives ID/version/size/digest from the actual bytes, and publishes up to 20 selected packages. Before committing it rejects downgrades and equal-version/different-digest state, verifies any existing certified paths, uploads missing digest-addressed packages, and stages every changed release pointer in one atomic commit_batch. It then refetches and verifies every public release and package and emits a machine-readable receipt.

An exact same-version/same-digest publication is a verified no-op. Run the same command a second time and retain the resulting no-op receipt as postflight. Old digest-addressed packages remain present; routine publication has no garbage collector.

Publishing changes discovery for existing Neutrons. It does not install the update, alter an already running Neutron, or change the Dispenser starter. If future Dispenser-created Neutrons should begin with the same bytes, publish and verify them first, update the tracked starter selection, then stage one new revision:

npm --workspace dispenser run production:starter:set

Do not rerun that command merely as verification: even identical input creates a new starter revision. Query starter() and compare the committed metadata with the production receipt instead.

Publication recovery and rollback

  • Validation or upload failure before commit_batch exposes no new release; the script attempts to delete the staging batch.
  • A rejected commit_batch is atomic.
  • A lost or failed post-commit verification is ambiguous. Freeze the release lane and rerun the same command with the same archive bytes; an identical commit reconciles as a verified no-op. Do not substitute different bytes at the same version.
  • Release pointers are monotonic. To restore older behavior, publish that code as a new, strictly higher version; never move a pointer backward.
  • Before upgrading or resynchronizing the source asset canister, preserve receipts and package inputs. Its ordinary asset sync contains only managed static assets and can remove published paths that are absent from the sync directory. Verify or republish afterward, then restore the one-publisher permission invariant.

Settings

The Installed Apps table carries the update control in its overview row. There is no separate updates section, and no global usage ranking or chart.

The source and integrity status shown alongside it is install provenance — a public certified but non-authoritative record of where exact bytes came from, not an endorsement.