What the platform stores
Plugins are uploaded either as a bare.wasm component (with an optional
.wit file) or as a .npack archive — a gzip-compressed tar.
Archive entries are matched by filename pattern, not by fixed path:
Directories are skipped; anything else is logged and ignored. Extraction is
capped at 256 MB total decompressed across all entries, enforced
incrementally — the tar header’s declared size is not trusted.
The plugin’s name comes from
metadata.name in noorle.yaml, falling back to
the archive filename minus .npack, then to the WASM file’s stem.
Admission validation
Every component is validated before it is stored. Four checks:- It must be a Component Model binary. A core WebAssembly module is rejected.
- Every import must sit under one of nine WASI prefixes:
wasi:clocks/,wasi:random/,wasi:cli/,wasi:sockets/,wasi:io/,wasi:filesystem/,wasi:http/,wasi:config/,wasi:keyvalue/. Anything else is rejected. - It must export at least one callable function.
- Binaries over 20 MB produce a warning, not a rejection.
How tools are discovered
Tool discovery does not read the.wit file. The platform walks the
component’s exported functions using Wasmtime’s component type reflection and
generates a JSON Schema for each from the WIT types.
The .wit file, when you ship one, supplies the descriptions — the doc
comment above an exported function becomes the tool description a model reads.
If the file fails to parse, the upload still succeeds; you just get tools with
no descriptions.
Tool names
Each capability carries a namespace, and the wire name of a tool is{namespace}_{tool}.
The namespace for a plugin is assigned by the platform — a short random
lowercase string, unique within your account. It is not derived from the
plugin’s name, and there is no field to set it.
The tool half is derived from the component’s export path, not from the
function name alone. A function exported directly from the world contributes
only its own name; a function exported through a WIT interface contributes
the package and interface names as well, joined with _. Each part is
lowercased, and every character outside a–z, 0–9, and - becomes _ —
so hyphens survive but dots, colons, slashes, and spaces do not. A name that
would exceed 128 characters collapses to the function name alone.
Because the namespace is assigned and the tool half depends on how your world
is structured, read the final name off the capability in the Portal, or off
a gateway’s tools/list response, rather than predicting it.
A tool whose namespaced name would collide with a reserved platform tool is
dropped from the catalog and logged.
The sandbox
The component runs with no ambient authority. What it can reach is exactly what thepermissions block in noorle.yaml grants — and with no
permissions block at all, the platform applies a default policy whose every
field is empty: no network, no storage, no environment variables.
- Network — an allowlist of hosts, enforced by a wrapper WASI state built from the policy. An empty allowlist means no outbound access. CIDR ranges are rejected at validation — use host patterns. Outbound request bodies are scanned for secret patterns as they stream; findings are logged, not blocked.
- Storage — not a real filesystem. Three object-store-backed mounts,
granted individually:
/workspace/inputand/workspace/output, both session-scoped, and/workspace/home, which is agent-scoped and materialised only for agent callers. - Environment — only the keys you list are visible; nothing from the host environment leaks in.
- Credentials — declared in
noorle.yaml, stored encrypted, and injected into outbound HTTP at the host boundary. Ciphertext never enters guest memory.
Resource limits
Three independent stops bound a single tool call:
Values are identical in every environment. A plugin may request a lower
timeout or a different memory ceiling in
noorle.yaml; the request is
clamped to these bounds. Fuel is not settable from noorle.yaml — it comes
from platform configuration plus an optional per-account override
(wasm_cpu_fuel_limit, wasm_memory_limit_mb), itself clamped to the same
bounds.
Alongside fuel there is an epoch deadline computed from the timeout in 10 ms
ticks, and an outer wall clock. Fuel exhaustion is reported to the caller as
a timeout, not as a distinct error.
Outbound HTTP is separately capped at 100 requests per execution. That
number comes from platform configuration and is the same for every plugin.
Caching
Compiled components are cached in-process keyed by content hash, so two capabilities sharing identical WASM compile and pre-instantiate once. A second, byte-weighted cache (256 MB per pod) holds plugin bytes keyed by WASM hash, so a warm plugin skips the object-store fetch.Where plugin calls sit in the platform
- Risk tier. Every plugin tool is classified
Act— kind-wide, with no per-tool override. Where autonomy enforcement is on and the agent sits at its defaultSupervisedlevel, anActcall pauses for approval unless it has been auto-approved. - Surfaces. Plugins carry no exposure restriction, so they are listed on agent, gateway, and workflow surfaces alike.
- Billing. Plugin execution is metered and lands in your account’s usage, like any other capability call — see Usage and limits.
Generating a plugin
You do not have to write the code. Plugin Builder takes a natural-language requirement, runs a coding agent inside a build sandbox, and feeds the resulting archive through the same upload, validation, and registration path as a hand-built plugin. It scaffolds from the Rust template, so a generated plugin is a Rust project. Writing the plugin yourself gives you the choice of five languages — see Languages. Two ways in:- Portal — Plugins → New plugin → Build with AI.
- As a capability — bind Plugin Builder to an agent and it gets three
tools:
build,status, andcancel. It is available on agent and gateway surfaces, not in workflows, and is classifiedPrivileged.
md sandbox, up to 1800 seconds, and up to
50 agent turns. Builds are asynchronous: build returns an id, status
reports progress, and the new tool appears on the capability when it
completes.
Generated plugins land as ordinary versions of an ordinary capability — same
validation, same permissions model, same version history. Review the generated
noorle.yaml before activating; the network permissions are the part worth
reading.