Files
optimclaw/wit/tool.wit
T
Illia PolosukhinandClaude Opus 4.6 2e62d71567 feat: Add NEAR key management with transaction signing and policy engine
Implements hybrid-custody NEAR key management where the agent holds scoped
function-call keys for routine operations while high-value operations require
explicit user approval through the existing channel approval flow.

Core infrastructure:
- Ed25519 key generation/import via ed25519-dalek (not near-crypto)
- AES-256-GCM encrypted storage via existing SecretsStore
- Hand-rolled borsh-serializable NEAR transaction types
- NEP-413 intent signing and MPC chain signature support
- Configurable policy engine with transaction analysis pipeline
- Daily spend tracking with automatic midnight UTC reset
- Encrypted backup/restore with Argon2id KDF
- CLI subcommands: generate, import, list, info, remove, export, policy, backup, restore
- NEAR ed25519 secret key leak detection (Critical/Block)
- WASM sign-payload host function (keys never enter WASM memory)
- KeyManager wired into AgentDeps for agent-wide access

Security invariants: private keys never reach the LLM or WASM boundary,
signing happens in host Rust code with Zeroize on drop, every transaction
is analyzed before signing, most-restrictive policy rule wins.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-02-09 16:24:47 -08:00

174 lines
6.0 KiB
Plaintext

// WASM Tool Sandbox Interface
//
// Defines the contract between sandboxed tools and the host runtime.
// Tools export the `tool` interface; the host provides the `host` interface.
//
// Security Model:
// - WASM tools are untrusted and run in a sandbox
// - All capabilities are opt-in (default: no access)
// - Secrets are NEVER exposed to WASM; credentials are injected at host boundary
// - All outputs are scanned for secret leakage before returning to WASM
package near:agent;
/// Host-provided capabilities for sandboxed tools.
///
/// These are the only ways a sandboxed tool can interact with the outside world.
/// The set is intentionally minimal to reduce attack surface.
interface host {
/// Log levels for structured logging.
enum log-level {
trace,
debug,
info,
warn,
error,
}
/// Emit a log message.
///
/// Messages are collected and emitted after execution completes.
/// Rate-limited to 1000 entries per execution, 4KB per message.
log: func(level: log-level, message: string);
/// Get the current timestamp in milliseconds since Unix epoch.
now-millis: func() -> u64;
/// Read a file from the workspace (if capability granted).
///
/// Path must be relative (no leading /) and cannot contain "..".
/// Returns None if the file doesn't exist or capability not granted.
workspace-read: func(path: string) -> option<string>;
// ==================== HTTP Capability ====================
/// Response from an HTTP request.
record http-response {
/// HTTP status code.
status: u16,
/// Response headers as JSON object string.
headers-json: string,
/// Response body bytes.
body: list<u8>,
}
/// Make an HTTP request (if capability granted).
///
/// Security:
/// - Only allowed endpoints (host/path patterns) can be accessed
/// - Credentials are injected by the host; WASM never sees them
/// - Response is scanned for leaked secrets before returning
/// - Rate-limited per tool
///
/// Returns Err with error message if:
/// - Endpoint not in allowlist
/// - Rate limit exceeded
/// - Request/response size limit exceeded
/// - Network error
/// - Timeout
/// - Secret leak detected in response
http-request: func(
method: string,
url: string,
headers-json: string,
body: option<list<u8>>
) -> result<http-response, string>;
// ==================== Tool Invocation Capability ====================
/// Invoke another tool by alias (if capability granted).
///
/// Security:
/// - WASM calls tools by alias, not real name (indirection layer)
/// - Only aliased tools can be invoked
/// - Rate-limited per tool
/// - Output is scanned for leaked secrets before returning
///
/// Returns the tool output as JSON string, or Err with error message.
tool-invoke: func(alias: string, params-json: string) -> result<string, string>;
// ==================== Secrets Capability ====================
/// Check if a secret exists (if capability granted).
///
/// Security:
/// - WASM can only check existence, NEVER read values
/// - Only allowed secret names can be checked
/// - Actual credentials are injected by host during HTTP requests
///
/// Returns true if the secret exists and is accessible to this tool.
secret-exists: func(name: string) -> bool;
// ==================== Signing Capability ====================
/// Result of a payload signing request.
record sign-result {
/// Base64-encoded signature bytes (set on success).
signature: option<string>,
/// Error message (set on failure).
error: option<string>,
/// True if user approval is needed before signing can proceed.
approval-pending: bool,
}
/// Sign a payload using a NEAR key managed by the host (if capability granted).
///
/// Security:
/// - Private keys NEVER enter WASM memory; signing happens in host code only
/// - Only key labels declared in the tool's signing capability can be used
/// - Rate-limited per execution
/// - Subject to the host's transaction policy (may require user approval)
///
/// The payload should be base64-encoded bytes to sign.
/// The context-json is optional metadata about what's being signed (for policy display).
///
/// Returns sign-result with either signature or error/approval-pending.
sign-payload: func(key-label: string, payload: string, context-json: string) -> sign-result;
}
/// Tool interface that sandboxed tools must implement.
interface tool {
/// Request payload for tool execution.
record request {
/// JSON-encoded parameters matching the tool's schema.
params: string,
/// Optional JSON-encoded job context for stateful operations.
context: option<string>,
}
/// Response from tool execution.
record response {
/// JSON-encoded output on success.
output: option<string>,
/// Error message on failure.
error: option<string>,
}
/// Execute the tool with the given request.
///
/// This is the main entry point. The tool should:
/// 1. Parse params as JSON according to its schema
/// 2. Perform the operation
/// 3. Return a response with either result or error set
execute: func(req: request) -> response;
/// Get the JSON Schema for this tool's parameters.
///
/// Must return a valid JSON Schema object describing the expected
/// structure of the `params` field in requests.
schema: func() -> string;
/// Get a human-readable description of what this tool does.
///
/// Used by the LLM to understand when to invoke the tool.
description: func() -> string;
}
/// World definition for sandboxed tools.
///
/// Tools import host capabilities and export the tool interface.
world sandboxed-tool {
import host;
export tool;
}