Secure Tool Execution (Rust Sandbox)
Relevant source files
The following files were used as context for generating this wiki page: - [ARCHITECTURE.md](https://github.com/Alonso-pixel/loom/blob/HEAD/ARCHITECTURE.md) - [ROADMAP.md](https://github.com/Alonso-pixel/loom/blob/HEAD/ROADMAP.md) - [core/src/lib.rs](https://github.com/Alonso-pixel/loom/blob/HEAD/core/src/lib.rs) - [core/src/tools/README.md](https://github.com/Alonso-pixel/loom/blob/HEAD/core/src/tools/README.md) - [core/src/cognitive/llm/tool_orchestrator.rs](https://github.com/Alonso-pixel/loom/blob/HEAD/core/src/cognitive/llm/tool_orchestrator.rs) - [core/tests/filesystem_tool_test.rs](https://github.com/Alonso-pixel/loom/blob/HEAD/core/tests/filesystem_tool_test.rs) - [loom-py/src/loom/bridge/README.md](https://github.com/Alonso-pixel/loom/blob/HEAD/loom-py/src/loom/bridge/README.md)Loom's architecture is founded on a "Brain/Hand Separation" principle, which isolates the AI's cognitive functions from its execution capabilities. The "Brain," typically a Python agent, handles high-level reasoning, prompt engineering, and LLM calls. This allows for rapid iteration on AI logic. The "Hands," however, are implemented within the Rust Core runtime. This component is responsible for the secure, stable, and performant execution of all tools and system interactions.
This separation is a deliberate design choice to enhance security and stability. While the Python "Brain" decides what to do, the Rust "Hands" control how it's done, enforcing security constraints within a sandboxed environment. All tool invocations from Python agents are forwarded via a gRPC bridge to the Rust Core, which manages execution through a centralized ToolRegistry. This ensures that potentially destructive or sensitive operations are handled by a robust, low-level system rather than the more dynamic and high-level agent code.
Sources: ARCHITECTURE.md, ROADMAP.md
Architecture and Data Flow
The tool execution system is designed to provide a unified interface for various types of tools while enforcing strict security boundaries. The flow begins with a Python agent and ends with execution inside the Rust Core.
The diagram below illustrates the high-level data flow for a tool call.
Core Components
| Component | Responsibility | Source File |
|---|---|---|
Tool Trait | Defines a common interface (name, description, parameters_schema, execute) for all tools, ensuring they can be managed by the registry. | core/src/tools/README.md |
ToolRegistry | A central, thread-safe registry that holds all available tools. It is responsible for looking up a tool by its name and dispatching the execution call with the provided parameters. | core/src/lib.rs:113-193 |
ToolOrchestrator | A higher-level component within the cognitive module that receives tool-use requests from the LLM, parses them, and invokes the ToolRegistry to execute the calls. | core/src/cognitive/llm/tool_orchestrator.rs |
McpManager | Manages the lifecycle of external tool servers that conform to the Model Context Protocol (MCP), allowing Loom to integrate with a wider ecosystem of tools. | core/src/tools/README.md |
Sources: core/src/tools/README.md, core/src/lib.rs, core/src/cognitive/llm/tool_orchestrator.rs
The Tool Trait
All tools, whether native or adapted from MCP, must implement the Tool trait. This provides a consistent contract for the ToolRegistry.
#[async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn parameters_schema(&self) -> serde_json::Value;
async fn execute(&self, params: serde_json::Value) -> ToolResult<serde_json::Value>;
}
Sources: core/src/tools/README.md
Tool Types
Loom supports two primary types of tools, both managed under the same ToolRegistry.
Native Tools
Native tools are implemented in Rust and compiled directly into the Loom Core runtime. They provide core functionalities for interacting with the file system, executing shell commands, and accessing web resources. These tools are initialized and registered when the Loom struct is created.
The following native tools are registered by default:
| Tool Name | Description |
|---|---|
fs:read_file | Read a file from the agent's sandboxed workspace. |
fs:write_file | Write a file to the agent's sandboxed workspace. |
fs:list_dir | List the contents of a directory within the workspace. |
fs:delete | Delete a file or directory within the workspace. |
system:shell | Execute a shell command from a pre-approved allowlist. |
git:* | A suite of tools for Git operations like clone, checkout, etc. |
weather:get | Get weather data. |
web:search | Search the web using the Brave Search API. Requires BRAVE_API_KEY. |
Sources: core/src/lib.rs:129-200, core/src/tools/README.md
MCP (Model Context Protocol) Tools
MCP allows Loom to integrate with external, standalone tool servers. The McpManager is responsible for spawning and managing these server processes based on configuration. It then creates an McpToolAdapter for each external tool, which implements the Tool trait and allows the ToolRegistry to treat it just like a native tool.
MCP servers can be configured via environment variables or a configuration file.
// Example configuration for an MCP server
McpServerConfig {
name: "filesystem".to_string(),
command: "npx".to_string(),
args: vec![
"-y".to_string(),
"@modelcontextprotocol/server-filesystem".to_string(),
"/tmp".to_string(),
],
env: None,
cwd: None,
protocol_version: None,
}
Sources: core/examples/mcp_integration.rs:35-47, core/src/tools/README.md
End-to-End Execution Flow
The sequence diagram below details the journey of a tool call from the Python agent to its execution in Rust and back.
Security Mechanisms
The Rust sandbox enforces several security measures to protect the host system.
Workspace Isolation
Filesystem tools (fs:*) are strictly confined to a designated workspace directory. Any attempt to access paths outside this directory (e.g., using ../) is blocked. This prevents an agent from reading sensitive system files or writing to arbitrary locations.
The following test demonstrates path traversal being blocked:
#[tokio::test]
async fn test_path_traversal_blocked() {
let workspace = create_temp_workspace();
let write_tool = WriteFileTool::new(workspace.path().to_path_buf());
// Try to write outside workspace
let result = write_tool
.call(json!({
"path": "../../../etc/passwd",
"content": "hacked"
}))
.await;
assert!(result.is_err());
}
Sources: core/tests/filesystem_tool_test.rs:125-139, core/src/tools/README.md
Shell Command Allowlist
The system:shell tool does not permit arbitrary command execution. Instead, it maintains an allowlist of over 60 pre-approved, generally safe, read-only commands. This gives agents useful inspection capabilities without the risk of executing malicious or destructive scripts.
A partial list of allowed commands includes:
- File/System Info:
ls,pwd,cat,grep,wc,df,ps,uname - Networking:
ping,curl,wget,nslookup - Development:
git,python,node,cargo
Sources: core/src/lib.rs:153-193, core/src/tools/README.md
Human-in-the-Loop Approval
The system is designed to support human-in-the-loop (HITL) approval for sensitive operations. While the core mechanism is in the Rust sandbox, the approval flow is managed by the higher-level Python SDK, which can prompt the user for confirmation before proceeding with a destructive tool call.
Sources: core/src/tools/README.md
Conclusion
Loom's secure tool execution model is a cornerstone of its architecture. By separating the fast-iterating Python "Brain" from the secure and stable Rust "Hands," Loom provides a powerful yet safe environment for AI agents. The ToolRegistry acts as a unified dispatcher, while security mechanisms like workspace isolation and command allowlisting ensure that agents can perform useful tasks without compromising the underlying system. This sandboxed approach is critical for building long-running, autonomous agents that can be trusted to operate on a user's machine or a server.