Security model
Scope
A package contains frontend assets, Motoko modules, and a manifest. The reviewed browser install flow validates package content, assembles one actor for the kernel and selected installed apps, compiles it, stages code/assets and backend call reservations, dispatches the upgrade, verifies the resulting runtime, and commits an install journal. Unknown update outcomes have explicit recovery and replay paths; an interrupted deployment should not be described as though no staged or journaled state can ever remain.
Third-party app Motoko code is not isolated by a canister boundary after installation. Everything below follows from that.
Threat model
What the implementation actually does
- All installed Motoko apps are assembled into one generated actor.
- An accepted package with id
kernelreplaces the operating shell and root package metadata; the ordinary-app static exception applies solely by that id. - App manifests can define authorized-principal query/update functions, internal functions, exact public-ingress routes, private memory declarations, declared backend dependencies, and controlled generated resources. Ordinary apps cannot inject actor-self, foreign-memory, or raw-module references.
- Every ordinary app wrapper that exists checks membership in the kernel's authorization set. The single exception is a synchronous handler used exclusively by paid canister public-ingress routes that opts into the cycles argument — and that opt-in emits no ordinary wrapper at all.
- Only reviewed kernel methods may use
allow: "unauthorized". Ordinary apps are rejected and must use the brokered public-ingress dispatcher. - Kernel methods that store assets or install code are ordinary wrapped methods, protected by the same generated assert.
- In the reviewed public-repository install flow, responses use a fixed Candid query interface and are checked against the IC certificate/Merkle witness, link-pinned manifest digest, and package hashes. This authenticates fetched bytes, not a publisher identity, and does not describe local/provisioning inputs.
- Tile, tray, and ordinary-background frontends use sandboxed credentialless iframes. A background can instead receive one of two disclosed dedicated origin modes.
What the design intends
- A malicious package author should not bypass generated wrapper policy by directly creating actors, making raw inter-canister calls, attaching cycles, modifying certified data, or reading/writing stable memory outside the managed model.
- A malicious app should not receive another app's memory, or use an internal provider function, unless the exact dependency appeared in the install dialog.
- A malicious frontend should not access the kernel frontend's browser credentials or signing identity merely because it lives in the same canister.
Authorized principals are not controllers
The backend stores a flat set of non-anonymous authorized principals. It has no human identity, role, team, or per-user data model, and it cannot prove that all principals in that set belong to one person. Each authorized principal can invoke the ordinary owner-facing wrappers, so treating an instance as single-owner is a product/deployment assumption, not an enforced one-human invariant.
IC controllers are a separate authority set maintained by the management canister. Merely being authorized does not make a principal a controller, and merely being a controller does not automatically enter the flat authorization set. A live controller can, however, call the reviewed recovery method to add an authorized principal—and can bypass Neutron entirely by replacing or deleting the canister. The Settings access snapshot reports the two sets separately.
Bootstrap activation
Initial activation is a one-time bearer-token transfer into the authorization set. A current controller arms the canister with a 32-byte SHA-256 token hash; that same atomic message removes the arming caller from Neutron's authorization set if present. Any non-anonymous principal that later presents the raw 32-byte token is added to the authorization set, while the hash and setter are cleared and the token is permanently marked consumed in the same non-awaiting update.
Possession of the raw token is therefore sufficient until it is consumed. The flow does not transfer IC controller authority, and controllers retain their independent recovery and management powers.
The kernel is openly replaceable
A package with id kernel is a kernel replacement, and anyone can author one.
Installing it means choosing a new operating-system implementation for your own
canister.
Kernel packages are therefore evaluated as explicit OS replacement requests, not as ordinary apps that normal app rules should confine. The reviewed install UI requires an authorized principal and displays the replacement at level 4; the package channel itself is not closed or publisher-authenticated.
What is not claimed
This section is as important as the rest of the page.
The browser is in the trusted computing base. Compilation, package validation, install review, and every consent decision happen in the kernel frontend. A compromised browser compromises the instance.
A controller can do anything. IC controllers can replace code, change settings, stop, or delete the canister entirely outside Neutron's rules. No Neutron mechanism constrains a controller.
A kernel replacement can do anything. All app-facing guarantees are guarantees of the running kernel. Install a different kernel and you have different guarantees.
Certified does not mean confidential. A correctly verified certified
response authenticates the selected request/response binding. It does not make
public bytes secret.
Committed asset paths — the registry, install provenance, /mo/**, /pkg/**,
/app/<id>/pkg/** — are public by design. Anyone may read or probe them,
and delivered bytes are visible to clients, gateways, and intermediary caches.
A controller can also replace the code to disclose other canister state.
Neutron does not automatically encrypt app canister state. Stable Store and connection records are plaintext to replicated execution. Chain-key assertions originate in app backend code before the kernel hashes them, and HTTPS outcall requests, replies, and credentials are processed during replicated execution. Threshold signing and app namespaces are not secret canister computation.
Host-operator visibility is a deployment/platform property, not an app
capability guarantee. The stock provisioner accepts either a 13-node
application-family subnet or a 7-node subnet whose Registry record has
sev_enabled = true; it verifies the certified Registry record and topology.
That proves what the Registry declared at the observed version. The canister
does not itself perform hardware attestation, and Neutron does not turn that flag
into an unconditional claim that a host, controller, side channel, remote HTTPS
peer, or leaking app cannot observe data. If confidentiality depends on SEV,
verify the recorded deployment evidence and the IC platform assumptions
separately.
vetKeys isolation has named limits. The reviewed kernel derives the app and installation namespace, keeps private derivation in a source-bound browser broker, and exposes only public material to Motoko. This is not a claim against an active controller, a kernel replacement, a compromised browser, an app deliberately leaking its own recovered key, or failure of the IC's threshold cryptography assumptions.
Package authenticity is not verified. Canister or DAO signatures for packages are not implemented. In a certified repository flow, certification and digests prove the checked byte provenance and integrity—not publisher identity, legality, security review, or quality. Provider and publisher names and HTTPS links are plain-text, visibly unverified claims.
App metadata is hostile content. Descriptions, tool metadata, tile titles, and tray labels remain untrusted even after structural sanitisation. Discovery explicitly marks them as untrusted. Normalisation and control-character checks reduce particular parsing/display tricks; they do not prevent all visual spoofing or make the text true.
Payment proves transport, not trust. For a paid public-ingress route,
attached cycles prove only that the immediate caller is canister-mediated. They
are not an assertion of the caller's identity, owner, app, or trustworthiness,
and required_cycles is a floor, not a price cap or execution bound.
Development-stage gates
Two areas retain explicit release qualifications:
HTTP POST update routes rely on an HTTP gateway honoring upgrade = true.
The canister repeats admission in the update path, but production qualification
still has to cover the deployed gateway's upgrade behavior; the source does not
justify a universal deployed-gateway guarantee.
Certified Assets now has a source-owned PocketIC/gateway/Chromium qualification runner, including hostile Range fail-closure and same-Wasm upgrade persistence. A candidate binding is not a result: only a current source-bound pass receipt is qualification evidence, and the bounded run does not establish cost, proof size, allocator behavior, or upgrade safety at the 100,000-entry production ceiling.
Chain-key assertion signing V1 cannot encode or sign a raw chain transaction, and its install grant is not Neutron's one-shot value-moving consent. An external verifier may still treat a signed assertion as high-impact authority, so apps must constrain assertion semantics themselves. Any future typed transaction adapter needs its own transaction-shaped, one-shot authorized-principal confirmation immediately before signing.
Telemetry is accounting, not authority
The kernel meters app updates, public-ingress broker and handler messages, HTTP POST messages, and scheduled callbacks, keeping per-installation lifetime totals and rolling daily buckets.
Three things about it:
- It never authorizes, refuses, or slows an operation.
- Apps cannot read or alter it and receive no counter handle.
- It is deliberately low-side and not billing-grade — it omits variable byte fees, response-callback bases, shared timer dispatch, storage, and compute allocation. Exact arithmetic on incomplete inputs is still incomplete.
The counters contain no payload, destination, or per-call log.
Further reading
- Static checks — the Motoko policy
- Capabilities — the broker inventory
- Frame isolation — the browser boundary
- Consent — what the user is actually shown
- Open questions — known gaps