Skip to main content

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.json and friends land under /app/<app-id>/pkg/
  • mo/<hash>.mo joins 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:

GroupContents
installationCompiler-owned public identity, currently a 32-byte network_id
stable_memoryThe app's own active managed memory roots
app_callsExactly the functions its declared backend dependencies exposed
capabilitiesOnly 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.