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

# noorle.yaml reference

> The keys the platform reads from a plugin's noorle.yaml — schema_version, metadata, permissions, resources, and credentials.

`noorle.yaml` carries a plugin's metadata, its permission policy, and its
credential declarations. It is optional in the archive; a plugin without one
runs under a policy with **no network, no storage, and no environment access**.

## Shape

```yaml theme={null}
schema_version: "1.0"

metadata:
  name: "weather"
  description: "Fetches current weather for a location"
  author: "Acme Corp"
  license: "Apache-2.0"
  homepage: "https://github.com/acme/weather-plugin"
  tags: ["weather", "api"]

permissions:
  network:
    allow:
      - host: "api.openweathermap.org"
        path_prefix: "/data/2.5/"
        methods: ["GET"]

  storage:
    allow:
      - uri: "fs://output"
        access: [read, write]

  environment:
    allow:
      - key: "OPENWEATHER_API_KEY"

  resources:
    limits:
      memory: "256Mi"
      timeout: "10s"

credentials:
  - name: openweather_key
    description: "OpenWeatherMap API key"
    injection:
      type: url_query
      param_name: appid
    host_patterns: ["api.openweathermap.org"]
    required: true
```

<Warning>
  Two shapes that look right and are not:

  * The filesystem key is **`storage`**, not `filesystem`.
  * **`resources` sits inside `permissions`**, not beside it. A top-level
    `resources:` block is silently ignored, and your plugin runs on platform
    defaults.
</Warning>

## `schema_version`

**Required.** Must be exactly the string `"1.0"`. Any other value fails
validation.

## `metadata`

Optional. Every field inside it is optional.

| Key           | Type            | Notes                                                                                                |
| ------------- | --------------- | ---------------------------------------------------------------------------------------------------- |
| `name`        | string          | Overrides the name derived from the archive filename. Cannot be empty and cannot contain `/` or `\`. |
| `description` | string          | Shown in plugin listings.                                                                            |
| `author`      | string          |                                                                                                      |
| `license`     | string          | SPDX identifier.                                                                                     |
| `homepage`    | string          | Must start with `http://` or `https://`.                                                             |
| `tags`        | list of strings |                                                                                                      |

There is **no `version` key**. Versions are integers the platform assigns on
each upload — see [Publishing and versions](/docs/build/plugins/publishing).

## `permissions.network`

An allowlist of outbound hosts. Absent or empty means **no outbound access**.

```yaml theme={null}
permissions:
  network:
    allow:
      - host: "api.example.com"
      - host: "*.github.com"
      - host: "api.stripe.com"
        path_prefix: "/v1/charges"
        methods: ["POST"]
```

| Key           | Type                      | Effect                                                                                 |
| ------------- | ------------------------- | -------------------------------------------------------------------------------------- |
| `host`        | string                    | Hostname; `*.domain.com` wildcards are supported.                                      |
| `path_prefix` | string, optional          | Only requests whose path starts with this prefix are allowed. Omitted means all paths. |
| `methods`     | list of strings, optional | HTTP method allowlist. Omitted means all methods.                                      |

**CIDR entries are rejected at validation.** `- cidr: "10.0.0.0/8"` fails the
upload with an explicit error; use host patterns.

## `permissions.storage`

Grants access to the platform's virtual mounts. This is **not a host
filesystem** — each mount is object-store-backed.

```yaml theme={null}
permissions:
  storage:
    allow:
      - uri: "fs://input"
        access: [read]
      - uri: "fs://output"
        access: [read, write]
```

| URI           | Guest path          | Scoped to                  | The mount permits |
| ------------- | ------------------- | -------------------------- | ----------------- |
| `fs://input`  | `/workspace/input`  | the current session        | read              |
| `fs://output` | `/workspace/output` | the current session        | read, write       |
| `fs://home`   | `/workspace/home`   | the agent, across sessions | read, write       |

`access` is **required** on every entry — omit it and the manifest fails to
parse. It declares intent rather than setting policy: each mount's
permissions are fixed to the column above, so listing `[read, write]` on
`fs://input` does not make it writable, and listing `[read]` on `fs://output`
does not make it read-only. Write down the access your code actually uses.

Granting a mount in the policy is necessary but not sufficient — each one also
has a runtime precondition:

* `fs://input` and `fs://output` are materialised only when the call carries a
  session. A caller without one — an anonymous request on a public gateway,
  for instance — sees neither directory.
* `fs://home` is materialised only when the caller is an agent. The policy may
  grant it on any surface; on a gateway or workflow surface it is skipped
  silently and the guest sees no `/workspace/home`.

`fs://working` is a retired name and is ignored with a warning.

## `permissions.environment`

```yaml theme={null}
permissions:
  environment:
    allow:
      - key: "API_BASE_URL"
      - key: "LOG_LEVEL"
```

Allow-only. Only the keys listed here are visible to `std::env::var`, and only
if a value exists for them — set values in the archive's `.env` file or in the
Portal, per version. Nothing from the host environment reaches the guest.

For secrets, prefer `credentials` below: an environment value is readable by
your code, while a credential is injected outside the sandbox and never enters
guest memory.

## `permissions.resources.limits`

```yaml theme={null}
permissions:
  resources:
    limits:
      memory: "256Mi"
      timeout: "10s"
```

| Key       | Type                                                                  | Effect                                                     |
| --------- | --------------------------------------------------------------------- | ---------------------------------------------------------- |
| `memory`  | string with a `Ki` / `Mi` / `Gi` suffix, or an unquoted integer in MB | Clamped to `[128 MB, 512 MB]`. Mind the units — see below. |
| `timeout` | duration string                                                       | Clamped to `[1 s, 120 s]`. Default when unset: 30 s.       |

<Warning>
  **Quote a memory value only when it carries a suffix.** `memory: "256Mi"` and
  an unquoted `memory: 256` both ask for 256 MB. A quoted, suffix-less
  `memory: "256"` asks for 256 **bytes** — which then clamps up to the 128 MB
  floor, and is almost certainly not what you meant.
</Warning>

Both are requests, not guarantees — a value outside the platform's bounds is
clamped, not rejected. An account-level override, if one is configured, is
applied last and wins.

**CPU fuel is not settable here.** It comes from platform configuration
(200,000,000 units by default) plus an optional per-account override.

**Outbound HTTP volume is not settable here either.** Every plugin gets the
platform's cap of **100 outbound HTTP requests per execution**.

## `credentials`

Declares secrets your plugin needs and where the platform should inject them
into outbound HTTP. Values are stored encrypted and injected at the host
boundary — the guest never sees them.

```yaml theme={null}
credentials:
  - name: github_token
    description: "GitHub personal access token"
    injection:
      type: bearer
    host_patterns: ["api.github.com"]
    path_prefix: "/repos/"
    required: true
```

| Key             | Type            | Notes                                                                                                                                               |
| --------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`          | string          | **Required.** Lowercase letters, digits, and underscores only (`^[a-z0-9_]+$`); unique within the plugin. This is the slot the account admin fills. |
| `description`   | string          | Shown in the Portal next to the input.                                                                                                              |
| `injection`     | object          | **Required.** See below.                                                                                                                            |
| `host_patterns` | list of strings | Hosts this credential applies to. Empty means every host the network policy already allows.                                                         |
| `path_prefix`   | string          | Only inject when the request path starts with this.                                                                                                 |
| `methods`       | list of strings | Only inject on these HTTP methods.                                                                                                                  |
| `required`      | bool            | When true, an execution with the secret unset fails before the component runs.                                                                      |

### Injection types

| `type`       | Extra keys    | Result                                                           |
| ------------ | ------------- | ---------------------------------------------------------------- |
| `bearer`     | —             | `Authorization: Bearer <value>`                                  |
| `basic_auth` | —             | `Authorization: Basic base64(<value>)`; the value is `user:pass` |
| `header`     | `header_name` | `<header_name>: <value>`                                         |
| `url_query`  | `param_name`  | Query parameter, replacing any existing one                      |
| `url_path`   | `placeholder` | Replaces `{placeholder}` in the path with the URL-encoded value  |
| `json_body`  | `field`       | Buffers the JSON body, sets `field`, re-serialises               |
| `form_body`  | `field`       | Same for `application/x-www-form-urlencoded`                     |

## Editing configuration after upload

You do not have to rebuild to change policy. In the Portal, open the plugin,
go to **Versions**, and use **Edit configuration** on a version. The drawer
covers allowed network hosts, environment variables and their values, the
timeout and memory limits, and any credential slots the version declares.

Keep `noorle.yaml` as the source of truth for a version's policy — it is the
copy you can review, diff, and re-upload.
