WASM Providers
Execution model
Section titled “Execution model”Every provider is a .wasm module compiled to wasm32-wasip2 and executed in a fresh wasmtime instance per call. There is no shared state between executions — the sandbox is created, the provider runs, the result is collected, and the sandbox is destroyed.
Sandbox guarantees
Section titled “Sandbox guarantees”A running provider has:
- No network access — all I/O goes through host imports
- No filesystem access — only WASM linear memory
- No syscalls — minimal WASI context (no env vars, no args, no preopens)
- No secrets — credentials are injected at the host transport layer
- Bounded CPU — fuel metering terminates runaway computation
- Bounded memory — allocation cap prevents memory exhaustion
- Bounded I/O — call budget limits host import invocations
- Bounded time — epoch-based wall-clock deadline (works even on tight loops)
Built-in providers
Section titled “Built-in providers”LatchGate v0.1 ships two supported providers: http_api and fs. The http_api provider covers the action class that handles the majority of agent traffic — REST APIs, webhooks, web reads, and SaaS integrations. The fs provider handles filesystem operations.
v0.1 — supported
Section titled “v0.1 — supported”| Provider | WASM module | I/O imports | Purpose |
|---|---|---|---|
http_api | http_api.wasm | io/http, io/log | HTTP requests (REST APIs, webhooks, SaaS integrations) |
fs | fs.wasm | io/fs, io/log | Filesystem operations (fs_read, fs_write, fs_delete) |
The http_api provider powers most of the built-in actions via template manifests. It is production-grade and requires no custom WASM code — actions are defined in YAML. The fs provider handles filesystem operations confined to the configured fs_root_path.
Planned for future releases
Section titled “Planned for future releases”The following providers exist in source under providers/ and are excluded from the v0.1 workspace build. Their WIT interfaces (wit/io-database.wit, wit/io-smtp.wit, wit/io-queue.wit, wit/io-storage.wit) are committed and previewable. No action manifests are shipped for them yet. They are not loaded by the runtime in v0.1.
| Provider | WIT interface | Status |
|---|---|---|
email | io/smtp | Implementation present; pending finalised transactional vs. notification email model |
database | io/database | Implementation present; SQL classifier needs replacement with a real parser before production |
queue | io/queue | Implementation present; AMQP fault model under review |
artifact_store | io/storage | Implementation present; bucket policy model being finalised |
Future releases will add these action classes once each provider’s validation surface is hardened. Until then, no action manifests are shipped for these providers.
Build the supported provider:
make providersHost I/O layer
Section titled “Host I/O layer”Providers communicate with external systems through host-implemented import functions defined in the WIT interface files. The host layer enforces:
- Sink validation — every target URL checked against the grant’s
allowed_sinksbefore executing - Credential injection — secrets read from the kernel store, injected into outgoing requests (e.g., Authorization header, connection string) without the provider ever seeing them
- SSRF protection — private IP blocking (loopback, RFC-1918, link-local, CGNAT, IPv6), DNS pinning (resolve-then-connect to prevent rebinding), redirect blocking, system proxy bypass
- Timeout and size limits — per-call response time and payload size enforcement
WIT interface contract
Section titled “WIT interface contract”package latchgate:provider@0.1.0;
world provider { import io-http; import io-fs; import io-smtp; import io-database; import io-queue; import io-storage; import io-log;
export execute: func(task-json: string) -> result<string, string>;}In v0.1, io/http, io/fs, and io/log are linked at instantiation. The other imports remain in the WIT package as forward-compatible declarations for future releases. A v0.1 provider that attempts to call an unlinked import fails at instantiation.
Writing a custom provider
Section titled “Writing a custom provider”- Create a Rust library targeting
wasm32-wasip2:
cargo new --lib my_providercd my_provider-
Add the WIT bindings and implement the
executeexport. The provider receives a JSON task string and returns a JSON result string (or error). -
Declare only the I/O imports your provider actually needs. The fewer imports, the smaller the attack surface.
-
Build:
cargo build --target wasm32-wasip2 --release-
Compute the SHA-256 digest and create a manifest YAML referencing it. Or use
make providerswhich handles digest computation automatically. -
Place the
.wasmintarget/providers/and the manifest indefinitions/manifests/.
For a complete walkthrough with code examples, see Custom Actions.
Provider lifecycle (per call)
Section titled “Provider lifecycle (per call)”1. Look up pre-compiled Component from cache by digest2. Create fresh Store with HostState and resource limits3. Create fresh Instance with only declared imports linked4. Configure fuel limit, memory limit, I/O call budget, epoch deadline5. Call provider's execute export6. Collect result (or timeout/fuel-exhaustion/memory error)7. Drop Instance + Store (sandbox destroyed)Concurrency
Section titled “Concurrency”WASM provider dispatch is bounded by a tokio::sync::Semaphore with MAX_CONCURRENT_EXECUTIONS permits (compile-time constant: 4). Excess requests wait for a permit.
For writing custom actions (YAML-only and WASM), see Custom Actions. For the system architecture and security invariants, see Architecture.