Documentation Index Fetch the complete documentation index at: https://mintlify.com/nearai/ironclaw/llms.txt
Use this file to discover all available pages before exploring further.
IronClaw’s WASM tool system allows you to build custom tools in Rust that compile to WebAssembly and run in a sandboxed environment with strict security controls.
Location : src/tools/wasm/
Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ WASM Tool Execution │
│ │
│ WASM Tool ──▶ Host Function ──▶ Allowlist ──▶ Credential ──▶ Execute │
│ (untrusted) (boundary) Validator Injector Request │
│ │ │
│ ▼ │
│ ◀────── Leak Detector ◀────── Response │
│ (sanitized, no secrets) │
└─────────────────────────────────────────────────────────────────────────────┘
Key principles :
Compile once, instantiate fresh : Tools are validated and compiled at registration time. Each execution creates a fresh instance (NEAR pattern).
Fuel metering : CPU usage is limited via Wasmtime’s fuel system.
Memory limits : Memory growth is bounded via ResourceLimiter (default 10MB).
Capability-based security : Features are opt-in via Capabilities.
Credential isolation : Tools never see raw credentials, only placeholder names.
Location : src/tools/wasm/mod.rs:1-75
Security Constraints
Threat Mitigation CPU exhaustion Fuel metering Memory exhaustion ResourceLimiter, 10MB default Infinite loops Epoch interruption + tokio timeout Filesystem access No WASI FS, only host workspace_read Network access Allowlisted endpoints only Credential exposure Injection at host boundary only Secret exfiltration Leak detector scans all outputs Log spam Max 1000 entries, 4KB per message Path traversal Validate paths (no .., no / prefix) Trap recovery Discard instance, never reuse WASM tampering BLAKE3 hash verification on load
Location : src/tools/wasm/mod.rs:32-50
WIT Interface
WASM tools implement the sandboxed-tool world defined in wit/tool.wit:
package near : agent @ 0.2.0;
world sandboxed-tool {
import host ;
export tool ;
}
interface host {
enum log-level { trace , debug , info , warn , error }
// Logging
log : func ( level : log-level , message : string );
// Time access
now-millis : func () -> u64 ;
// Workspace read access
workspace-read : func ( path : string ) -> option < string >;
// HTTP requests (with allowlist validation)
http-request : func (
method : string ,
url : string ,
headers-json : string ,
body : option < list < u8 >>,
timeout-ms : option < u32 >,
) -> result < http-response , string >;
record http-response {
status : u16 ,
headers-json : string ,
body : list < u8 >,
}
// Secret existence check (not the value)
secret-exists : func ( name : string ) -> bool ;
// Invoke other tools (with permission)
tool-invoke : func (
name : string ,
params-json : string ,
) -> result < string , string >;
}
interface tool {
// Tool metadata
name : func () -> string ;
description : func () -> string ;
parameters-schema : func () -> string ;
// Main execution function
execute : func ( params-json : string ) -> result < string , string >;
}
Location : wit/tool.wit
Capabilities System
Capabilities define what a WASM tool is allowed to do. They are declared in a <tool-name>.capabilities.json file.
Location : src/tools/wasm/capabilities.rs
HTTP Capability
pub struct HttpCapability {
pub allowlist : Vec < EndpointPattern >,
pub rate_limit : Option < RateLimitConfig >,
pub timeout_secs : Option < u64 >,
pub credentials : HashMap < String , CredentialMapping >,
}
pub struct EndpointPattern {
pub host : String ,
pub path_prefix : Option < String >,
pub methods : Vec < String >,
}
Example (github-tool.capabilities.json):
{
"capabilities" : {
"http" : {
"allowlist" : [
{
"host" : "api.github.com" ,
"path_prefix" : "/" ,
"methods" : [ "GET" , "POST" ]
}
],
"credentials" : {
"github_token" : {
"secret_name" : "github_token" ,
"location" : { "type" : "bearer" },
"host_patterns" : [ "api.github.com" ]
}
},
"rate_limit" : {
"requests_per_minute" : 60 ,
"requests_per_hour" : 3600
}
},
"secrets" : {
"allowed_names" : [ "github_token" , "github_*" ]
}
}
}
Location : src/tools/wasm/capabilities_schema.rs:28-80
Workspace Capability
pub struct WorkspaceCapability {
pub allowed_paths : Vec < String >,
pub read_only : bool ,
}
Example:
{
"workspace" : {
"allowed_paths" : [ "docs/" , "*.md" ],
"read_only" : true
}
}
Secrets Capability
pub struct SecretsCapability {
pub allowed_names : Vec < String >,
}
Example:
{
"secrets" : {
"allowed_names" : [ "github_token" , "github_*" ]
}
}
pub struct ToolInvokeCapability {
pub allowed_tools : Vec < String >,
}
Example:
{
"tool_invoke" : {
"allowed_tools" : [ "http" , "memory_read" ]
}
}
Resource Limits
pub struct ResourceLimits {
pub memory_bytes : u64 ,
pub fuel_limit : u64 ,
pub timeout_ms : u64 ,
}
impl Default for ResourceLimits {
fn default () -> Self {
Self {
memory_bytes : DEFAULT_MEMORY_LIMIT , // 10 MB
fuel_limit : DEFAULT_FUEL_LIMIT , // 100M ops
timeout_ms : DEFAULT_TIMEOUT , // 30 seconds
}
}
}
Location : src/tools/wasm/limits.rs:10-42
Constants :
pub const DEFAULT_MEMORY_LIMIT : u64 = 10 * 1024 * 1024 ; // 10 MB
pub const DEFAULT_FUEL_LIMIT : u64 = 100_000_000 ; // 100M operations
pub const DEFAULT_TIMEOUT : u64 = 30_000 ; // 30 seconds
Location : src/tools/wasm/limits.rs:7-9
Credential Injection
WASM tools never see raw credentials. The host injects them at request time.
Location : src/tools/wasm/credential_injector.rs
Credential Mapping
pub struct CredentialMapping {
pub secret_name : String ,
pub location : CredentialLocation ,
pub host_patterns : Vec < String >,
}
pub enum CredentialLocation {
Bearer , // Authorization: Bearer <token>
Header { name : String }, // Custom-Header: <token>
QueryParam { name : String }, // ?api_key=<token>
UrlPlaceholder { placeholder : String }, // https://api.com/{TOKEN}/data
}
Example injection locations :
{
"credentials" : {
"github_token" : {
"secret_name" : "github_token" ,
"location" : { "type" : "bearer" },
"host_patterns" : [ "api.github.com" ]
},
"telegram_token" : {
"secret_name" : "telegram_bot_token" ,
"location" : {
"type" : "url_placeholder" ,
"placeholder" : "BOT_TOKEN"
},
"host_patterns" : [ "api.telegram.org" ]
},
"custom_api_key" : {
"secret_name" : "api_key" ,
"location" : {
"type" : "query_param" ,
"name" : "api_key"
},
"host_patterns" : [ "api.example.com" ]
}
}
}
Location : src/tools/wasm/credential_injector.rs:28-150
Placeholder Substitution
Tools can reference credentials by placeholder in URLs and headers:
// In WASM tool code:
let url = "https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage" ;
let headers = json! ({
"Authorization" : "Bearer {GITHUB_TOKEN}"
});
// Host substitutes before making request
let injected_url = "https://api.telegram.org/bot1234567890:ABC.../sendMessage" ;
let injected_headers = { "Authorization" : "Bearer ghp_abc123..." };
Location : src/tools/wasm/wrapper.rs:129-145
Leak Detection
All WASM tool outputs are scanned for credential leakage:
let leak_detector = LeakDetector :: new ();
for cred in & self . credentials {
leak_detector . add_secret ( & cred . secret_value);
}
let sanitized = leak_detector . scan ( & output ) ? ;
Location : src/tools/wasm/wrapper.rs:300-320
Secrets are replaced with [REDACTED:SECRET_NAME] before returning to the LLM.
1. Create Project
cd tools-src
cargo new --lib my-tool
cd my-tool
[ package ]
name = "my-tool"
version = "0.1.0"
edition = "2021"
[ lib ]
crate-type = [ "cdylib" ]
[ dependencies ]
wit-bindgen = "0.28"
serde = { version = "1.0" , features = [ "derive" ] }
serde_json = "1.0"
[ profile . release ]
opt-level = "z"
lto = true
codegen-units = 1
strip = true
//! My custom WASM tool.
wit_bindgen :: generate! ({
world : "sandboxed-tool" ,
path : "../../wit/tool.wit" ,
});
use serde :: { Deserialize , Serialize };
struct MyTool ;
#[derive( Deserialize )]
struct Params {
input : String ,
}
#[derive( Serialize )]
struct Output {
result : String ,
}
impl exports :: near :: agent :: tool :: Guest for MyTool {
fn name () -> String {
"my_tool" . to_string ()
}
fn description () -> String {
"Does something useful" . to_string ()
}
fn parameters_schema () -> String {
serde_json :: json! ({
"type" : "object" ,
"properties" : {
"input" : {
"type" : "string" ,
"description" : "Input text"
}
},
"required" : [ "input" ]
}) . to_string ()
}
fn execute ( params_json : String ) -> Result < String , String > {
let params : Params = serde_json :: from_str ( & params_json )
. map_err ( | e | format! ( "Invalid params: {}" , e )) ? ;
// Use host functions
near :: agent :: host :: log (
near :: agent :: host :: LogLevel :: Info ,
format! ( "Processing: {}" , params . input),
);
// Make HTTP request (if allowed)
let response = near :: agent :: host :: http_request (
"GET" . to_string (),
"https://api.github.com/zen" . to_string (),
"{}" . to_string (),
None ,
Some ( 5000 ),
) . map_err ( | e | format! ( "HTTP error: {}" , e )) ? ;
let result = String :: from_utf8_lossy ( & response . body) . to_string ();
let output = Output { result };
Ok ( serde_json :: to_string ( & output ) . unwrap ())
}
}
export! ( MyTool );
4. Create Capabilities File
my-tool.capabilities.json:
{
"http" : {
"allowlist" : [
{
"host" : "api.github.com" ,
"path_prefix" : "/" ,
"methods" : [ "GET" ]
}
],
"rate_limit" : {
"requests_per_minute" : 60 ,
"requests_per_hour" : 1000
}
}
}
5. Build
cargo build --target wasm32-wasip2 --release
Output: target/wasm32-wasip2/release/my_tool.wasm
6. Install
ironclaw tool install target/wasm32-wasip2/release/my_tool.wasm \
--capabilities my-tool.capabilities.json
Runtime APIs
Location : src/tools/wasm/runtime.rs
pub struct WasmToolRuntime {
engine : Engine ,
linker : Arc < Linker < StoreData >>,
}
impl WasmToolRuntime {
pub fn new ( config : WasmRuntimeConfig ) -> Result < Self , WasmError >;
pub async fn prepare (
& self ,
name : & str ,
wasm_bytes : & [ u8 ],
limits : Option < ResourceLimits >,
) -> Result < PreparedModule , WasmError >;
}
Location : src/tools/wasm/runtime.rs:52-150
Location : src/tools/wasm/wrapper.rs
pub struct WasmToolWrapper {
runtime : Arc < WasmToolRuntime >,
module : PreparedModule ,
capabilities : Capabilities ,
description_override : Option < String >,
schema_override : Option < serde_json :: Value >,
secrets_store : Option < Arc < dyn SecretsStore >>,
oauth_refresh : Option < OAuthRefreshConfig >,
}
impl WasmToolWrapper {
pub fn new (
runtime : Arc < WasmToolRuntime >,
module : PreparedModule ,
capabilities : Capabilities ,
) -> Self ;
pub fn with_description ( mut self , desc : impl Into < String >) -> Self ;
pub fn with_schema ( mut self , schema : serde_json :: Value ) -> Self ;
pub fn with_secrets_store ( mut self , store : Arc < dyn SecretsStore >) -> Self ;
pub fn with_oauth_refresh ( mut self , config : OAuthRefreshConfig ) -> Self ;
}
Location : src/tools/wasm/wrapper.rs:67-98
Storage
WASM tools can be stored in a database with integrity verification.
Location : src/tools/wasm/storage.rs
pub async fn store_tool (
& self ,
params : StoreToolParams <' _ >,
) -> Result < StoredWasmTool , WasmStorageError >;
pub struct StoreToolParams <' a > {
pub user_id : & ' a str ,
pub name : & ' a str ,
pub description : & ' a str ,
pub wasm_binary : & ' a [ u8 ],
pub parameters_schema : serde_json :: Value ,
pub trust_level : TrustLevel ,
pub capabilities : Option < StoredCapabilities >,
}
pub async fn get_with_binary (
& self ,
user_id : & str ,
name : & str ,
) -> Result < StoredWasmToolWithBinary , WasmStorageError >;
Integrity verification :
BLAKE3 hash computed on store
Hash verified on load
Prevents WASM tampering
Location : src/tools/wasm/storage.rs:120-280
Discover WASM tools in the tools-src/ directory:
Location : src/tools/wasm/loader.rs
pub fn discover_tools ( tools_dir : & Path ) -> Vec < DiscoveredTool >;
pub struct DiscoveredTool {
pub name : String ,
pub wasm_path : PathBuf ,
pub capabilities_path : Option < PathBuf >,
}
Example :
let tools = discover_tools ( Path :: new ( "tools-src" ));
for tool in tools {
println! ( "Found: {} at {:?}" , tool . name, tool . wasm_path);
}
Location : src/tools/wasm/loader.rs:28-150
OAuth Authentication
WASM tools can declare OAuth requirements in capabilities:
{
"auth" : {
"secret_name" : "slack_bot_token" ,
"display_name" : "Slack" ,
"oauth" : {
"authorization_url" : "https://slack.com/oauth/v2/authorize" ,
"token_url" : "https://slack.com/api/oauth.v2.access" ,
"client_id_env" : "SLACK_OAUTH_CLIENT_ID" ,
"client_secret_env" : "SLACK_OAUTH_CLIENT_SECRET" ,
"scopes" : [ "chat:write" , "channels:read" ],
"use_pkce" : false
},
"env_var" : "SLACK_BOT_TOKEN"
}
}
Auth flow priority :
Check env_var - if set in environment, use it directly
Check oauth - if configured, open browser for OAuth flow
Fall back to instructions + manual token entry
Location : See src/tools/README.md:49-103
Examples
Real-world WASM tools in tools-src/:
GitHub (tools-src/github/) - Repository management, issues, PRs, workflows
Gmail (tools-src/gmail/) - Search, read, send emails
Slack (tools-src/slack/) - Post messages, read channels
Telegram (tools-src/telegram/) - Send messages, manage conversations
Google Calendar (tools-src/google-calendar/) - Manage events
Google Drive (tools-src/google-drive/) - File management
Google Sheets (tools-src/google-sheets/) - Spreadsheet operations
Google Docs (tools-src/google-docs/) - Document editing
Best Practices
Minimize allowlist scope : Only allow the specific hosts/paths your tool needs
Use credential injection : Never hardcode secrets
Validate inputs : Check parameter lengths and formats
Handle errors gracefully : Return descriptive error messages
Log appropriately : Use host log() for debugging
Optimize binary size : Use opt-level = "z" and strip = true
Test thoroughly : Test with and without credentials
Document schema : Provide clear descriptions for all parameters
Next Steps
MCP Integration Learn about MCP server integration
Building Tools Use the software builder to create tools