> ## Documentation Index
> Fetch the complete documentation index at: https://noorle.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How plugins run

> Noorle plugins are WASI Preview 2 components run on Wasmtime: the .npack archive, admission validation, tool discovery, the sandbox, and resource limits.

A Noorle plugin is a **WebAssembly component** targeting **WASI Preview 2**,
executed on **Wasmtime** inside the platform. Its exported functions become
tools that agents, gateways, and workflows can call.

## 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:

| Pattern                       | Role                                           | Required                                            |
| ----------------------------- | ---------------------------------------------- | --------------------------------------------------- |
| any `*.wasm`                  | the component                                  | **yes** — if several are present, the last one wins |
| `noorle.yaml` or `noorle.yml` | [configuration](/docs/build/plugins/configuration)  | no                                                  |
| any `*.wit`                   | WIT interface, read only for tool descriptions | no                                                  |
| `.env` or `env`               | environment variable defaults                  | no                                                  |

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:

1. **It must be a Component Model binary.** A core WebAssembly module is
   rejected.
2. **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.
3. **It must export at least one callable function.**
4. Binaries over **20 MB** produce a warning, not a rejection.

<Warning>
  Of `wasi:keyvalue`, only the `store` interface is implemented.
</Warning>

## 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 the `permissions` 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/input` and `/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.

See [Permissions](/docs/build/plugins/permissions) for the full grammar.

## Resource limits

Three independent stops bound a single tool call:

| Stop               | Default               | Bounds                                   |
| ------------------ | --------------------- | ---------------------------------------- |
| Wall-clock timeout | **30 s**              | clamped to `[1 s, 120 s]`                |
| Memory             | **128 MB**            | clamped to `[128 MB, 512 MB]`            |
| CPU fuel           | **200,000,000 units** | clamped to `[10,000,000, 1,000,000,000]` |

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
  default `Supervised` level, an `Act` call 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](/docs/run/platform/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](/docs/build/languages/rust).

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`, and `cancel`. It is available on agent and gateway
  surfaces, not in workflows, and is classified `Privileged`.

In production a build gets an `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.

<Note>
  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.
</Note>

## Next

* [When to build a plugin](/docs/build/plugins/when-to-build)
* [Quickstart](/docs/build/plugins/quickstart)
* [Project structure](/docs/build/plugins/project-structure)
