Skip to content

Security model

Tools are the part of an agent most likely to be untrusted — third-party code, generated code, code you’d rather not fully audit. maiden runs every tool inside a WebAssembly sandbox with no ambient authority, and mediates the few things a tool can do at the host boundary.

A tool is a WASI-P2 component. It exports three functions maiden calls (describe, schema, execute) and it may import only what maiden’s contract grants it. It cannot open files, spawn processes, or reach the network on its own — a fresh WASM instance has no capabilities until the host links them in, and maiden links in only a narrow, gated host interface.

The host enforces three things on every execute call:

Each tool runs under an epoch-based deadline derived from its timeout_ms policy. A background ticker advances the engine’s epoch; a tool that runs past its deadline traps and the call returns an error. A runaway or infinite-loop tool cannot hang the agent.

The tool’s WASM store is capped at its memory_mb policy via store limits. A tool that tries to allocate past the ceiling is denied the growth rather than being allowed to exhaust host memory.

This is the important one. A tool never holds a secret and never has an open door to the network. When a tool wants to make an HTTP request, it calls the host’s http-fetch with:

  • the URL, plus an optional method, headers and body, and
  • a credential name (never a value).

The host then, in order:

  1. Extracts the URL’s host and checks it against the tool’s http_allow allowlist (exact match, or a single-label *.example.com wildcard). A host that isn’t on the list is refused before any network call happens.
  2. Requires https (plain http only for loopback) — a request the host is about to authenticate must not go out in the clear.
  3. Checks the credential name against the tool’s secrets list. A tool may only spend a credential its own policy declared; naming some other MAIDEN_SECRET_* that happens to be in the process environment is refused.
  4. Resolves that name from MAIDEN_SECRET_<NAME> and attaches it as an Authorization: Bearer header.
  5. Sends the request with redirects disabled. A redirect is a second request, and following one would let an allowlisted host hand the tool a Location pointing anywhere. The 3xx returns to the tool as an ordinary response instead.

The request is bounded by the tool’s timeout_ms — the same budget the rest of its execution spends from — and the response body is capped at 2 MB.

The secret value never crosses into the WASM instance. The tool’s code only ever sees a name like TFL_TOKEN; the mapping from name to value lives entirely in the host. A compromised or malicious tool cannot exfiltrate a credential it was never given, and cannot reach a host it wasn’t allowlisted for.

Passing a secret value into tool code means the secret is only as safe as the least-trusted line in that tool. Passing a name inverts it: the tool declares which credential it needs (and agent.toml must grant it via secrets), but the host holds the value and decides when to attach it. The blast radius of a bad tool shrinks from “can leak my API keys” to “can make allowlisted requests that the host chose to authenticate.”

The allowlist is by host name, not by address. If a name on a tool’s allowlist resolves to an address inside your network, the tool reaches it — allowlisting a host is an explicit grant, so grant hosts you mean. maiden does not resolve-and-check addresses, and does not re-resolve between the check and the request.

The allowlist also says nothing about what a tool sends to a host it is allowed to reach. A tool that legitimately talks to api.example.com can send whatever it likes there, including data it was given as input. The boundary bounds reach and credential custody, not intent.

Compiled components are cached by path and modification time, so repeated tool calls don’t recompile. Editing a tool’s .wasm on disk invalidates its cache entry, so the next call picks up the new version — useful during development, and never a way to bypass the policy, which is applied fresh on every call.