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

# Plugin quickstart

> Scaffold a plugin with the Noorle CLI, build it to a WASI Preview 2 component, upload the archive in the Portal, and call the tool.

Scaffold a plugin, build it to a WebAssembly component, and get it into your
account.

## Before you start

* An account on the [Portal](https://platform.noorle.com).
* A Rust toolchain. This walkthrough uses the Rust template; the steps after
  the build are identical in every language.

<Note>
  Plugins can be written in **Rust, Python, JavaScript, TypeScript, or Go** —
  each has a working example and a guide under
  [Languages](/docs/build/languages/rust). Whatever the source language, what you
  upload is a WASI Preview 2 component, and admission checks the same things —
  see [How plugins run](/docs/build/plugins/overview#admission-validation).
</Note>

<Steps>
  <Step title="Install the CLI">
    ```bash theme={null}
    curl -fsSL cli.noorle.dev | sh
    noorle --version
    ```

    If `noorle --version` does not resolve in a new shell, follow whatever the
    installer printed about your `PATH`.
  </Step>

  <Step title="Scaffold the project">
    ```bash theme={null}
    noorle plugin init my-plugin --template rust
    cd my-plugin
    ```

    You get a `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](/docs/build/plugins/project-structure) for what each file
    does.
  </Step>

  <Step title="Declare the tool in WIT">
    The exported functions in `wit/world.wit` become your tools. The doc
    comment above each one becomes the description a model reads, so write it
    for the model.

    ```wit theme={null}
    package example:weather;

    world weather-component {
        enum unit { metric, imperial }

        record weather-response {
            location: string,
            temperature: f64,
            humidity: option<u32>,
            weather-conditions: list<string>,
        }

        /// Check the current weather for a location
        export check-weather: func(location: string, unit: unit) -> result<weather-response, string>;
    }
    ```

    Return `result<T, string>` — that is how an error reaches the caller.
  </Step>

  <Step title="Implement it">
    ```rust theme={null}
    #![allow(unsafe_op_in_unsafe_fn)]
    wit_bindgen::generate!({ world: "weather-component", path: "./wit" });

    struct WeatherComponent;

    impl Guest for WeatherComponent {
        fn check_weather(location: String, unit: Unit) -> Result<WeatherResponse, String> {
            // ...
        }
    }

    export!(WeatherComponent);
    ```

    For outbound HTTP use the `waki` crate. `reqwest` does not compile to a
    WASI component.
  </Step>

  <Step title="Grant the permissions it needs">
    A plugin with no `permissions` block gets **no network, no storage, and no
    environment variables**. Name the hosts you call:

    ```yaml theme={null}
    schema_version: "1.0"
    metadata:
      name: my-plugin
      description: "Fetches current weather for a location"
    permissions:
      network:
        allow:
          - host: "api.openweathermap.org"
      environment:
        allow:
          - key: OPENWEATHER_API_KEY
    ```

    Scope hosts specifically — a wildcard grants more than you meant. Full
    grammar in the [configuration reference](/docs/build/plugins/configuration).
  </Step>

  <Step title="Build">
    ```bash theme={null}
    noorle plugin build
    ```

    This compiles the component and packages it. The archive lands in `dist/`
    as a `.npack` — a gzip-compressed tar holding the `.wasm`, your
    `noorle.yaml`, and the `.wit`.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Bind it and call it">
    Attach the plugin to an agent or a gateway the same way you attach any
    capability — see
    [Attaching capabilities](/docs/run/agents/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.
  </Step>
</Steps>

## What happens on the first call

Plugin tools are classified `Act`. 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. Confirm `crate-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.

## Next

* [Project structure](/docs/build/plugins/project-structure)
* [Permissions](/docs/build/plugins/permissions)
* [Publishing and versions](/docs/build/plugins/publishing)
