Before you start
- An account on the Portal.
- A Rust toolchain. This walkthrough uses the Rust template; the steps after the build are identical in every language.
Plugins can be written in Rust, Python, JavaScript, TypeScript, or Go —
each has a working example and a guide under
Languages. Whatever the source language, what you
upload is a WASI Preview 2 component, and admission checks the same things —
see How plugins run.
1
Install the CLI
noorle --version does not resolve in a new shell, follow whatever the
installer printed about your PATH.2
Scaffold the project
wit/world.wit interface, a src/lib.rs implementing it, a
noorle.yaml, a Cargo.toml, and a build.sh the CLI calls. See
Project structure for what each file
does.3
Declare the tool in WIT
The exported functions in Return
wit/world.wit become your tools. The doc
comment above each one becomes the description a model reads, so write it
for the model.result<T, string> — that is how an error reaches the caller.4
Implement it
waki crate. reqwest does not compile to a
WASI component.5
Grant the permissions it needs
A plugin with no Scope hosts specifically — a wildcard grants more than you meant. Full
grammar in the configuration reference.
permissions block gets no network, no storage, and no
environment variables. Name the hosts you call:6
Build
dist/
as a .npack — a gzip-compressed tar holding the .wasm, your
noorle.yaml, and the .wit.7
Upload it
In the Portal, go to Plugins → New plugin, choose .npack archive,
and pick the file from
dist/.Plugin names are 2–50 characters. The component is validated before it is
stored; if validation fails you get the errors back and nothing is
written.On success the upload becomes the plugin’s active version in the same
commit, and its tools are available to bind.8
Bind it and call it
Attach the plugin to an agent or a gateway the same way you attach any
capability — see
Attaching capabilities.The wire name of your tool is
{namespace}_{tool}, where the namespace is
a short prefix the platform assigns to the capability. Read it off the
capability in the Portal or off the gateway’s tools/list response; it is
not derived from the plugin’s name.What happens on the first call
Plugin tools are classifiedAct. Where autonomy enforcement is on for your
account and the agent sits at its default Supervised level, an Act call
pauses for approval rather than running straight through — approve it in the
Playground, or add it to the agent’s auto-approve list.
Troubleshooting
Upload rejected: “Component exports no callable functions” — the binary is a core WebAssembly module, not a component, or nothing is exported from the world. Confirmcrate-type = ["cdylib"] in Cargo.toml and that export! is
present in src/lib.rs.
Upload rejected on imports — the component imports an interface outside
the nine allowed WASI prefixes. wasi:keyvalue/store is supported;
wasi:keyvalue/atomics, batch, and watch are not — those pass admission
and then fail at call time.
The tool runs but every outbound request fails — check the network.allow
list in noorle.yaml. An absent or empty list means no outbound access at
all. CIDR entries are rejected outright; use host patterns.
Calls time out — the default wall clock is 30 seconds, and CPU fuel
exhaustion is also reported as a timeout. You can raise the timeout in
noorle.yaml up to 120 seconds; fuel is not settable per plugin.