Skip to main content

Kernel frontend

The kernel frontend is the UI a distro inherits from the kernel: a tiling workspace shell in the spirit of a keyboard-driven window manager. A distro is free to replace it, since it ships in the kernel package like anything else.

Bootstrap order

The entry point is deliberately sequenced. It synchronously captures and removes any repository setup handoff from the URL fragment, registers a hash listener, then loads and validates the certified /system/runtime-config.json before dynamically importing the main application.

That ordering guarantees fragment data and runtime authority are resolved before Internet Identity, agents, frames, update clients, or React are evaluated.

The closed runtime record carries the exact canister-specific isolated-frame origin template and, when configured, an update-source origin. Frame URL construction fails closed unless the result matches that certified template — or the exact kernel origin for an intentionally opaque unprefixed frame.

The root mounts the workspace shell and auth flow alongside kernel-owned request, install, connection, and agent dialogs, repository/install-offer controllers, and the toast viewport. Visible state lives in Zustand stores.

The workspace shell

Mounted only after login. It loads the app registry through certified HTTP and renders the workspace view plus a top toolbar: launcher button, workspace switcher, app tray icons, and a pinned trusted Kernel tray item.

Workspaces

  • Starts with three empty workspaces; appends one more when every exposed workspace is occupied, up to 20.
  • Never appends while any exposed workspace is empty.
  • State persists in versioned localStorage.

Each workspace holds a nullable binary split tree, its open tile instances, and a focused tile id. Opening a tile always creates a new instance: the first fills an empty workspace, later ones split the focused tile to the right.

Lazy mounting, and why inactive tiles are disconnected

The active workspace mounts first. Another mounts lazily on first visit and then stays in a hidden, aria-hidden, inert layer for the rest of the session — so returning reveals the existing iframe and its in-memory state rather than reloading everything.

But inactive tile frames are disconnected from the message bus, so they cannot act as undeclared resident processes. The kernel retains only the latest bounded same-app state invalidation per topic and replays it on reconnect, and a retained frame records which revision it received, so ordinary reconnects do not trigger redundant queries.

If an install replaces a hidden frame, its new document is deferred until that workspace becomes active. Authorization loss discards the visited set even if the next session uses the same principal.

Gestures

Split ratios clamp to 0.15..0.85. Pointer behaviour follows a Hyprland-style model:

  • drag a split gap to resize;
  • drag the tile header grip to move;
  • hold a layout modifier over a tile body to move, or over a corner to resize;
  • drop over another tile to insert on that side;
  • drop on a workspace button to transfer the tile and switch there.

Because iframe content consumes pointer events, the workspace renders a parent-owned hit layer over iframe content while a modifier is held or a drag is active.

At viewports up to 900px the workspace switches to its mobile layout and disables these move, resize, and split-gap pointer gestures.

Keyboard commands use browser-delivered Meta/Super/OS/Hyper states — switch workspace, move the focused tile to a workspace, close the focused tile.

Launcher

Opens from the top-left button — which is the reliable path once focus is inside an app iframe, because the parent page stops receiving keyboard events. Global shortcuts also work when focus is in the parent page, and are ignored while focus is in an input, textarea, select, or contenteditable element.

The launcher flattens registry entries into tile entries, excluding the kernel entry, and exposes system actions for installing a package and resetting the current workspace. Install offers equal File and URL buttons; both run the same review and install flow and open the installed package's first tile on success.

Settings

Trusted kernel UI — not an app, tile, iframe, route, or message-bus endpoint. Available only to the authenticated owner.

Opening Settings does not unmount the workspace: its surface becomes hidden, aria-hidden, and inert while every tile iframe stays mounted and resident backgrounds keep running. Back, Escape, a workspace button, or opening the launcher returns; closing restores focus to the Kernel tray button.

Installed Apps

All per-app operational information lives in one table — there is no separate instructions, usage, or updates section. Columns: App, Cycles used, Update, Version, Details, Uninstall.

The cycles cell joins telemetry by exact app id and installation uid and shows a low-side 13-node estimate formatted to four decimal places in TC.

Each row has a collapsed details control whose content depends on the presentation mode.

App uninstall is disabled unless registry ids and versions match the running actor. The kernel row can never be uninstalled. Candid Nat values are normalised to bigint; unsafe JavaScript numbers are rejected before formatting.

note

Settings never fetches or renders arbitrary manifest JSON. That keeps the view bounded and avoids treating third-party manifest text as trusted markup. All displayed app metadata remains untrusted plain text.

Access & recovery

A disclosure collapsed by default that does not call the management canister until opened. It lists equivalent authorized owner principals separately from IC controllers, validates and canonicalises principal text, and supports adding or removing either authority behind a destructive confirmation.

App-isolated keys

Also collapsed by default, loading its snapshot only when opened. Groups slots by app and shows lifecycle manager, state, current and legacy generations, shortened public fingerprints, key name, timestamps, last use, lifetime derivations, and approximate cycle spend — with enable, disable, rotate, legacy retirement, holder transfer, and permanent retirement actions.

The bounded audit view shows coarse action, outcome, app, slot, actor, generation, and time fields and never key or transport material.

The Kernel tray item

Uses the same kernel-owned popover chrome as app tray items, but its body is trusted React rather than an iframe. It shows the canister, cycle balance, and current heap as Memory, with a capacity bar comparing heap against the configured Wasm-memory limit, plus Open Settings and Logout.

It deliberately does not substitute expanded Wasm allocation or stable memory for displayed usage, and does not repeat the signed-in principal — that identity is marked in Settings' authorized-principals list. App tray icons may scroll when space is constrained; the Kernel item stays pinned at the right edge.

App registry shape

/system/apps.json is a strict format-3 structural projection plus the exact canonical capability plan. Strict normalisation rejects unsafe metadata rather than adopting a legacy row. Every function records its logical name; methods with a public Candid method also record the exact derived candid_name, whose relationship is verified. Internal functions and paid public-ingress handlers with invocation-scoped cycle access have no candid_name.

Install dialogs and progress

Compilation starts before the approval dialog resolves. The dialog shows a compiling state and disables acceptance until the store has a compiled Wasm size. Each attempt has an id, so a rejected install cannot have a late compile result update the current dialog.

Launcher and Settings share one kernel-owned uninstall confirmation and one global app-operation state. The confirmation lists owned memory roots when known. A verified uninstall performs message-bus cleanup, removes tiles from all workspaces, updates the registry, and unmounts the resident.