Applications
A Neutron app is a .neutron package installed into a canister. It is not a
separate canister and not a hosted web app you link to.
What an app contributes
my_app.v0.1.0.neutron
├── neutron.json the required manifest: identity, functions, memory, capabilities, UI
├── neutron.lock.json managed-memory lineage lock, present only when memory is declared
├── web/… optional frontend assets, served under /app/<id>/
└── mo/<sha256>.mo content-addressed Motoko modules
When installed:
web/**is served under/app/<app-id>/neutron.jsonand friends land under/app/<app-id>/pkg/mo/<hash>.mojoins the shared/mo/module namespace- the manifest joins the next combined actor compile
The backend
The backend is a plain Motoko module, not an actor. It exposes a class named
Init. Ordinary annotated query and update methods normally become actor
methods in the generated wrapper. Internal, dependency-export, and scheduled
handlers use private generated paths; public-ingress declarations add protocol
dispatch, and handlers receiving public-ingress cycles are route-only.
import Memory "./memory/my_app/v1";
module {
public type AppBackendEnvironment = {
stable_memory : { my_app : Memory.Mem };
};
public class Init(env : AppBackendEnvironment) {
let mem = env.stable_memory.my_app;
public func /*update*/set_name(name : Text) : Text {
let previous = mem.name;
mem.name := name;
previous;
};
};
}
The optional constructor argument is the app-wide environment. It is one anonymous record containing only:
| Group | Contents |
|---|---|
installation | Compiler-owned public identity, currently a 32-byte network_id |
stable_memory | The app's own active managed memory roots |
app_calls | Exactly the functions its declared backend dependencies exposed |
capabilities | Only the broker leaves it explicitly selected |
Empty groups are omitted; an app with no backend resources receives Init()
with no arguments. The app defines the record's type locally and narrowly —
Motoko's structural typing means it simply ignores fields it does not name.
Declared invocation-scoped resources, such as the caller or a task capability,
are injected separately as exact method arguments.
There is no ambient authority. No actor reference, no kernel handle, no cycle
primitive, no system capability, no foreign memory. See
Capabilities.
The frontend surfaces
An app may occupy up to three kinds of browser surface. All are sandboxed
iframes. Tiles, trays, and ordinary backgrounds have opaque origins; a
background may instead receive a separately declared dedicated origin, in which
case its sandbox includes allow-same-origin under an installation-bound host.
Tiles
Windows in the workspace. An app declares zero to 32 tile definitions; each launch creates a new tile instance in one of the workspaces. Tiles are disposable, non-resident, and opaque-origin. Their workspace records and layout persist locally, and visited frames may remain mounted but hidden for the shell session.
"tiles": [
{ "id": "main", "title": "Notes", "path": "index.html", "icon": "static/icon.png" }
]
Omit tiles and the app has no tile. Backend-only or background-only packages
are valid; every tile that should appear in the launcher must be declared.
A resident background
At most one hidden iframe that stays mounted while the workspace shell is mounted — even when tiles switch, close, or the workspace changes. This is where long-lived app state, model state, or coordination logic belongs.
Backgrounds are the only surface eligible for a dedicated origin (either credentialless-ephemeral or persistent), and only when explicitly declared and approved. Ordinary backgrounds remain opaque and credentialless like tiles.
A tray icon
At most one top-right icon, requiring a background. The kernel owns the button, badge presentation, popover chrome, placement, and close behaviour; the app supplies an untrusted title, icon, and page, and may set the badge value to null or an integer from 0 through 9999.
The tray page is mounted only while the popover is open and destroyed on close, always opaque and credentialless. It never inherits a background's dedicated origin. Persistent state belongs in the background, not the tray.
Endpoints
The kernel names every live frontend surface. These ids are kernel-attested — an app cannot claim one:
app:<appId>:background
app:<appId>:tile:<tileId>:instance:<instanceId>
app:<appId>:tray:instance:<instanceId>
Endpoints expose tools, call each other, and reach kernel tools over the message bus.
Reference apps
Two first-party examples define the expected shape:
- Hello — the minimal package: one update method, one memory root, one tile. Copy this to start a project.
- Kitchen Sink — the feature reference: a navigable workbench, a companion tile with shared state, a resident process, a tray with a badge, typed calls, preapproved self calls, live endpoint tools, HTTPS outcalls, chain-key assertions, and schema display. It is also the living reference for the design system.