Skip to content

Plugin Sandbox & Loading

Document ID: PLG-004
File Path: docs/08-plugin-sdk/sandbox.md
Version: 1.1.0
Status: Draft
Owner: AI Platform Team
Last Updated: 2026-07-16


This document defines how plugin code is loaded and isolated. Plugins extend a privileged platform with third-party code, so isolation is mandatory: a plugin must not be able to reach anything it was not granted, nor destabilize the host.

It builds on the Tool Runtime sandbox model (which isolates tool executions) and extends isolation to the other capability kinds.


RiskConsequence without isolation
Malicious pluginHost compromise, data theft
Buggy pluginCrash or resource exhaustion of a core service
Over-reachAccess to ungranted secrets/data
Supply-chain tamperingTrojaned capability runs with platform trust

Isolation contains all four. Permission grants (Permissions) define what is allowed; the sandbox enforces it.


Different capability kinds run in different places, so isolation differs:

KindHostIsolation
toolTool RuntimePer-execution sandbox (native→microVM)
providerLLM GatewayWASM/process adapter; egress allowlist
memory_backendMemory EngineWASM/process adapter; scoped store access
workflow_activityTool RuntimePer-execution sandbox
policyPolicy EnginePure, sandboxed evaluation (no I/O)

Tool and activity capabilities reuse the Tool Runtime’s isolation backends verbatim; this document focuses on how the other kinds are contained.


ModelDescriptionUsed for
In-process (native)Linked/dynamically loaded into the hostFirst-party, fully trusted only
WASMPlugin compiled to WebAssembly, run in an embedded VMDefault for verified plugins
Out-of-processPlugin runs as a separate process/service, host calls over IPC/gRPCHeavy or untrusted capabilities
Sandboxed (gVisor/microVM)Strong OS/HW isolationCommunity/untrusted code

WASM is the default loading model: capability-based, deterministic, memory-safe, language-portable, and cheap to instantiate. Native in-process loading is reserved for first-party plugins.

Implemented today (wovyr-plugin’s runtime module): the WASM loader (WasiCapabilityRuntime, behind the wasi cargo feature) and the container loader (ContainerCapabilityRuntime — Docker/Podman, gVisor via runsc; always compiled, ECO-303). Both speak the same capability ABI (request JSON on stdin → response JSON on stdout, WOVYR_SECRET_* env injection); a capability picks its loader via the manifest’s sandbox field (wasm vs container/gvisor), and each loader refuses the other’s capabilities fail-closed — a gvisor capability is refused by a plain-Docker runtime rather than demoted. The out-of-process (IPC/gRPC) and microVM models remain future work.


WASM plugins interact with the host only through an explicit, capability-gated host interface — there is no ambient syscall access.

WASM plugin
│ imports (host functions, gated by grants)
Host shim ──► net.egress(host, req) [requires net:egress grant]
──► secret.read(ref) [requires secret:read grant]
──► memory.query(q) [requires memory:read grant]
──► log/metric (always)

Each host function checks the plugin’s grants before acting (Permissions §7). An ungranted import call is denied and audited.


Plugin execution is bounded like any other untrusted workload:

ResourceEnforced via
CPU / fuelWASM fuel metering or cgroup quota
MemoryWASM linear-memory cap / cgroup memory.max
TimeExecution timeout → cancel
EgressNetwork allowlist (granted hosts only)
ConcurrencyPer-plugin and per-tenant caps

Limits mirror the Tool Runtime resource enforcement.


  • A plugin’s failure (panic, OOM, timeout) is contained to its sandbox and surfaced as a capability error — the host service stays healthy.
  • Disabling or uninstalling a plugin tears down its sandboxes and unregisters its host imports.
  • Crashing plugins are subject to circuit-breaking: repeated failures auto-disable the capability and alert operators.

  • Plugin instances and their state are tenant-scoped; one tenant’s plugin invocation cannot observe another’s.
  • Sandboxes are never reused across tenants (consistent with Tool Runtime §8).

Trust classDefault loading model
First-partyIn-process / native
VerifiedWASM
Community / untrustedOut-of-process + gVisor/microVM

Tenant policy may require a stronger model than the default, never weaker.


RequirementTarget
WASM instantiation< 2 ms p95
Host-call permission check< 1 ms
Failure containment100% (no host crash from plugin fault)
Cross-tenant leakage0 (hard)



VersionDateDescription
1.1.02026-07-16§4: noted the implemented loading models — the WASM loader (wasi feature) and the new container/gVisor loader (ContainerCapabilityRuntime, ECO-303) with their shared stdin/stdout ABI and fail-closed loader routing
1.0.02026-06-27Initial Plugin Sandbox & Loading specification