Skip to content

Sandboxing & Permissions

WASM nodes execute inside Wasmtime rather than as native plugins. That provides memory isolation and lets Flow-Like meter execution and gate host functions. It does not make arbitrary third-party code trustworthy, so Flow-Like also shows a consent prompt before running sideloaded packages.

Conceptual view of a third-party WASM node contained in a sandbox, with approved paths to network, scoped storage, and configured models

BoundaryCurrent behavior
Linear memoryA module cannot directly address the host process’s memory
FilesystemNo host directory is preopened as a general-purpose filesystem; storage access uses Flow-Like host functions
Host functionsVariables, cache, storage, streaming, models, OAuth, A2UI, functions, and network operations are capability-checked
CPU workWasmtime fuel metering bounds instruction use
Wall timeEpoch interruption enforces the configured timeout
MemoryStore limits apply the package memory tier
Catalog identityA placed WASM node must resolve to an installed package ID

WASM execution is not deterministic by default. Nodes can access time and randomness, and permitted nodes can call networks, storage, models, and other stateful host services.

Each node exports its own permission labels. The loader converts those labels to runtime capabilities. The desktop loader for installed packages then layers the package’s memory and timeout limits and its allowed_hosts list on top. The manifest cannot add or remove capabilities.

PermissionProtected capability
network:httpHTTP host access
network:websocketWebSocket access
network:tcpTCP sockets
network:udpUDP sockets
network:dnsDNS lookups
storage:readRead through Flow-Like storage host functions
storage:writeWrite and delete through storage host functions
database:readRead from wired database and SQL session pins
database:writeModify rows through wired database pins
variablesRead and write flow variables
cacheRead and write execution cache
streamingEmit streaming output
modelsInvoke configured model host functions
a2uiUse A2UI host functions
oauthRequest configured OAuth tokens
functionsCall functions or subflows

There are no current storage:node or storage:user node-permission labels. Storage scope is represented by the FlowPath values provided to the node and the credentials behind the host service. The sandbox gates the node, user, upload, and cache directories on one storage capability, so the store lists all four for a package whose nodes declare storage:read or storage:write.

A node with no declared permissions receives none of the protected Flow-Like capabilities in this table. It can still read its input pins, write outputs, log, access runtime metadata, and use baseline facilities supplied by its ABI.

Core-module host functions check their capability before performing a protected operation. Component Model nodes also receive WASI interfaces. In the current linker, any network capability enables the WASI networking context, while specific Flow-Like network host functions still check their capability.

The desktop loader for installed packages copies the manifest’s allowed_hosts into each node’s execution configuration. The list is checked for WebSocket connects and for WASI sockets, where the destination IP address is compared with it. A non-empty list also keeps the standard wasi:http interface from being linked. The Flow-Like HTTP host function does not consult the list today, and the server executor applies neither allowed_hosts nor the manifest’s resource tiers. Do not describe allowed_hosts as a complete execution-time allowlist. Apply network egress restrictions at the executor or cluster boundary when destinations must be constrained.

In the Rust SDK, permissions are attached to the node definition:

fn get_node(&self) -> NodeDefinition {
let mut node = NodeDefinition::new(
"fetch_data",
"Fetch Data",
"Downloads data from an API",
"Integrations/HTTP",
);
// Add pins...
node.add_permission(NodePermission::NetworkHttp);
node
}

Python and TypeScript SDKs export the same serialized labels:

permissions = ["network:http"]
node.addPermission("network:http");

Do not request capabilities “just in case.” The UI displays the union of permissions used by each package’s nodes in the board.

Resource tiers, the host allowlist, and OAuth scopes remain in flow-like.toml. Capability flags are not authored there; the registry derives the store’s capability listing from the node definitions. See the manifest reference for the exact division between manifest settings and node permissions.

When the UI detects sideloaded WASM packages without saved consent, it shows:

  • package IDs;
  • permissions aggregated by package;
  • Run once;
  • Trust for this board;
  • Always trust, which remembers each package ID across boards.

The choices are stored in browser/local app storage under wasm-consent-board-* and wasm-consent-package-* keys. Consent is a local UX decision; it does not grant extra runtime capabilities.

The current dialog does not expose an event-specific trust button.

Trust is keyed by package ID, not package version. Updating a package under the same ID does not automatically ask for consent again, so review package updates before installing them.

The package manifest selects memory and timeout tiers. The runtime also applies fuel and structural Wasmtime limits. Exact defaults and presets live in packages/wasm/src/limits.rs.

Resource limits reduce the impact of runaway code, but they are not a billing or abuse-prevention policy by themselves. A permitted node can still perform expensive network or model operations before its local execution limit is reached.

  • Give each node a stable name and an accurate permission list.
  • Request storage write only when the node actually writes or deletes data.
  • Treat OAuth tokens and model inputs as sensitive.
  • Batch small host calls where possible.
  • Validate URLs and untrusted response data.
  • Avoid logging secrets or entire credential-bearing payloads.
  • Test denial paths: a missing permission should fail safely.
  • Keep package memory and timeout tiers as small as practical.
  • Run untrusted packages in a dedicated executor environment.
  • Keep executor environment variables free of unrelated secrets.
  • Restrict outbound network access with container or Kubernetes policy when destination control matters.
  • Give executor storage credentials only the scope required for execution.
  • Pin and review package versions.
  • Monitor timeout, fuel, memory, network, model, and storage failures.

Can a WASM node access an arbitrary host directory?

No host directory is preopened for general file access. Nodes use Flow-Like storage host functions and FlowPath values when granted storage permissions.

Does approving a package bypass the sandbox?

No. Consent allows execution to proceed; runtime capabilities still come from the node definition.

Does no-permission mean fully deterministic pure computation?

No. It means no protected Flow-Like capabilities. Baseline ABI facilities, logging, runtime metadata, time, or randomness may still be available.

Can trust be revoked?

Yes. Remove the relevant wasm-consent-board-* or wasm-consent-package-* entry from local storage, or clear the application’s local data.