Skip to main content

Consent

Consent is where the security model meets a human. Neutron's approach has one organising rule:

Kernel-attested facts and app-authored text are different types, and only kernel facts can determine grouping, risk level, warning text, or the approval action.

App rationale is displayed, separately, labelled App-provided explanation — unverified. It has no enforcement role and cannot change a heading, a warning, or a button.

Install disclosure

At install, the flow validates the manifest, re-checks the app id for URL safety, verifies every Motoko module against its content hash, and then derives an immutable structured disclosure snapshot from the canonical capability plan.

Note from the plan — not assembled from independent registry fields and not guessed from source code. One projection, one fingerprint, one decision.

The four severity levels

Every rendered permission fact carries a level from 1 to 4. Several capabilities are conditional—the same declaration discloses at a higher level once it crosses a materially riskier threshold.

LevelFacts
1A reviewed kernel-only public method; a function whose only injected resources are caller, the read-only canister principal, or public_ingress_cycles
2Persistent background storage · dedicated resident origin · preapproved self calls · background UI requests · app dependencies · exposing an internal function to consumers · randomness · a query public-ingress route · backend_calls with zero cycles and exact-only scopes · HTTPS outcalls without POST · an Ethereum provider without eth_sendTransaction · a function receiving stable memory
3Stable Store · HTTP routes · Certified Assets · agent entrypoints · vetKeys · scheduled tasks · an update public-ingress route · backend_calls with cycles above zero or a principal/method scope · HTTPS outcalls with POST · eth_sendTransaction · any connection permission · a function receiving task capabilities
4Replacing the kernel · replacing kernel memory · retiring a memory root · chain-key signing · a function receiving actor-self

Chain-key signing sits in level 4, the same band as replacing the operating system. That is deliberate: a threshold signature is authority a verifier elsewhere may act on, and V1 cannot constrain what an assertion is later taken to mean.

The conditional pairs are where a diff matters most. Adding POST to an existing HTTPS endpoint, or widening a backend_calls scope from exact to principal, moves the same declaration from level 2 to level 3.

What is rejected rather than disclosed

Some malformed requests never reach a dialog. Foreign memory (a memory_<id> that is not one of the app's active roots), raw module resources, the reserved app_caller argument, and any unrecognised resource fail the capability-plan build. There is no permission variant for them — the install is rejected outright.

Likewise, only the kernel package may request the actor-self resource, and only the kernel package can produce the kernel-memory-replacement disclosure. An ordinary app declaring either is rejected at plan build, not shown at level 4.

Capability-specific disclosures

For backend_calls, the kernel shows the allowed reservation scope kinds and their meanings, maximum concurrency, persistence and revocation rules, and states that installation grants no destination. For vetkeys, it states that no key is created on install, that lifecycle changes require kernel consent, that derivation spends cycles, that compatible updates inherit access, that disabling cannot erase a browser-held key, and that an app can disclose keys from its own slots.

Dialog behaviour

The dialog shows the app id, package size, the structured snapshot, and compile status.

  • The snapshot is fixed for one decision.
  • All exact facts remain reachable in the scrollable dialog.
  • The accept button is disabled until compilation finishes.
  • A changed package requires a new snapshot and a new decision.

Updates show a diff

For an update, the install flow validates the complete previous and target plans for the same app and presents a deterministic exact diff of added, removed, and changed entries, with both configurations and fingerprints.

The generic diff deliberately does not guess whether a changed capability is broader or narrower. That classification is capability-specific policy, not something a structural comparison should assert.

Call approval

For a one-shot frontend canister call, the dialog shows the kernel-attested caller (app, role, exact endpoint), destination canister, method, and a type-aware rendering of the arguments. After approval, the authenticated browser identity—not the Neutron canister backend—signs and dispatches the call.

This path is separate from Motoko backend_calls: backend calls execute from the Neutron canister and require a persistent reservation plus the declared cycle and concurrency ceilings. One-shot frontend approval does not create such a reservation.

Rejection rejects the app's request. The management canister is never an eligible target. If the destination is the Neutron canister itself, the trusted registry must resolve the method as the live source app's own non-internal method: kernel methods, other apps' methods, and Agent Mode self-calls are denied.

Rendering arguments safely

Arbitrary Candid Text is valid call data, so consent does not sanitise or rewrite arguments. It renders them as canonical, type-aware data:

  • Strings and keys are quoted, so text is visually distinct from JSON primitives.
  • Visually dangerous or invisible code points are escaped and exposed — control, bidi, zero-width, default-ignorable — without mutating the call.
  • Candid blob / vec nat8 leaves are never expanded into JSON, hex, base64, or byte arrays. For each binary value the dialog shows the Candid-derived field or index path, the exact byte length, and a transient SHA-256 digest of the immutable bytes.

Dispatch uses the retained, validated request. Binary leaves are copied before review; the displayed length and transient digest describe those immutable copies, which are the bytes used for encoding after approval.

Grants

GrantScopeLifetime
One-callA single cross-app tool callImmediate
SessionAn exact endpoint + tool pairKernel memory; gone on reload
Persistent reservationAn outbound canister-call scopeSurvives compatible upgrades; revocable in Settings
Agent ModeOne exact app version + entrypointSession-only

Cross-app tool listing and invocation require approval. The dialog shows caller app and role, exact endpoint, tool, and JSON arguments, and offers allow-once, allow-for-session, or reject. An app may request a session grant explicitly.

Session grants live in kernel memory and disappear on page reload. They are never serialised into the durable capability registry.

Pending dialogs are bound to the exact endpoint session and are rejected if that frame closes or reloads. Because live-Candid preflight and reservation lookup are asynchronous, the kernel rechecks the exact endpoint object and session after each pre-consent await and again before applying an approved batch.

Batched reservation requests

backend_calls.request opens one dialog for an array of reserve and release actions, and may include one same-app method to run after approval. The kernel validates every scope, records the source-bound context, and validates that method's complete normalised arguments against live Candid before creating the consent request.

Only then can it apply the visible reservation batch atomically. If a post-call was included, the kernel revalidates and runs that exact same-app call afterward. The call is a separate operation: if it fails, the already-applied reservation batch remains. This lets an app replace a complete selection—a set of ledger canisters, say—without separate reservation prompts.

The dialog states explicitly that a persistent reservation may authorize repeated future calls with different app-chosen arguments.

workspace.open_tile is the single app-navigation path, and it has narrow no-dialog shortcuts:

  • A currently focused tile with transient user activation may open or reuse any installed tile.
  • A focused open tray with transient user activation may open or reuse only a tile of the same app.

Everything else — backgrounds, hidden tiles, unfocused callers — goes through a once-only Open Tile dialog. Approval opens one tile; it never creates a session grant that would let the caller open more UI later. Stale endpoint sessions fail closed.

Focusing existing UI grants no tool or backend capability. The optional view argument is a bounded navigation token delivered only over the target tile's private port; it carries no arguments and no authority, and target apps must never perform persistent, destructive, signed, or backend work from it.

Presentation modes

The kernel has one browser-local presentation mode shared by Settings and every kernel-owned prompt.

normal is the strict default: the essential consequence and choice, at low cognitive load. Expanded app details are consequence-oriented — concise kernel-written copy for material external, public, signing, key, data, connection, and autonomous background access, plus live capability switches, persistent grants with revoke actions, and a non-authoritative source status. A capability that only uses compiler-enforced namespaces is omitted or reduced to one plain-language control.

developer adds exact permissions, identifiers, hashes, plan fingerprints, API and delivery policy, quota and audit detail, operation counters, internal methods and paths, memory schemas, and raw usage breakdowns.

note

This mode is presentation-only. It must never change authorization, permission enforcement, risk classification, or the material consequences required for informed consent. Switching mode while a row is open replaces what is rendered, nothing else.

Some things no dialog can establish, and the disclosure says so:

  • Protocol and handler names do not attest semantics.
  • Attached cycles prove canister-mediated transport, not caller trustworthiness.
  • A repository certificate proves byte provenance, not publisher identity or quality.
  • Installed-app descriptions and tool metadata remain hostile prompt content even after sanitisation.