# How OmniRoute's Skills System Works: Architecture and Sandbox Isolation Explained

> Explore OmniRoute's skills system for secure, reusable code execution. Learn about its registry architecture and Docker sandbox isolation for safe, untrusted code.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: architecture
- Published: 2026-07-25

---

**The skills system in OmniRoute provides a secure, extensible framework for executing reusable capabilities like file I/O and HTTP requests, using a registry-based architecture and Docker-based sandbox isolation to ensure untrusted code runs with strict resource limits and filesystem separation.**

The **OmniRoute** repository (`diegosouzapw/OmniRoute`) implements a powerful **skills system** that allows the router to expose reusable capabilities to downstream clients while maintaining rigorous security boundaries. This system combines a flexible handler registry with containerized execution environments to safely process both trusted operations and untrusted code.

## Core Architecture of the OmniRoute Skills System

### The Skill Registry (src/lib/skills/registry.ts)

At the heart of the system lies the **Skill Registry**, a centralized map that associates string identifiers with their corresponding handler functions. This registry serves as the lookup mechanism for the A2A server and MCP server when clients invoke capabilities via the `skills_execute` tool.

The registry is implemented as a simple object that exports a `skillRegistry` constant:

```typescript
// src/lib/skills/registry.ts (excerpt)
import { builtinSkills } from "./builtins";
export const skillRegistry = {
  ...builtinSkills,
  // custom skills can be added here at runtime
};

```

When a request arrives containing a `skill` field, OmniRoute resolves the appropriate `SkillHandler` (type defined in [`src/lib/skills/types.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/types.ts)) from this registry and validates the input payload before execution.

### Built-in Skill Handlers (src/lib/skills/builtins.ts)

The built-in skill set covers common operational patterns through async handler functions. Each handler receives a validated input payload and a **request-context object** containing the API-key identifier, enabling per-caller isolation.

The default capabilities include:

- **file_read**: Validates relative workspace paths, enforces size limits, and reads content from the skill workspace
- **file_write**: Validates paths and writes content atomically while respecting size constraints
- **http_request**: Sanitizes headers, caps allowed methods, and streams responses with configurable byte limits via `safeOutboundFetch`
- **web_search**: Delegates to the internal search engine through `executeWebSearch`
- **web_fetch**: Wraps generic fetch operations with safety checks

All handlers access the `context.apiKeyId` property, which is hashed using **SHA-256** to generate a per-API-key workspace directory via `getWorkspaceRoot`. This ensures filesystem operations remain strictly scoped to the caller's namespace.

## How Sandbox Isolation Works in OmniRoute

### The SandboxRunner Singleton (src/lib/skills/sandbox.ts)

For operations requiring execution of untrusted code, OmniRoute employs the **SandboxRunner**, a process-wide singleton that manages containerized execution environments. The runner is exported as `sandboxRunner` and instantiated via `SandboxRunner.getInstance()`:

```typescript
// src/lib/skills/sandbox.ts (excerpt)
export const sandboxRunner = SandboxRunner.getInstance();

```

The runner tracks container lifecycles through an internal `runningContainers` map and exposes methods including `run()`, `kill()`, `killAll()`, `isRunning()`, and `getRunningCount()` for execution management and monitoring.

### ContainerProvider Abstraction (src/lib/skills/containerProvider.ts)

The sandbox system abstracts container runtime details through the **ContainerProvider** interface defined in [`src/lib/skills/containerProvider.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/containerProvider.ts). This abstraction supports multiple backends including Docker and Podman, allowing the runner to construct proper execution commands without hardcoding runtime-specific logic.

The provider handles the construction of `docker run` (or equivalent) commands and implements provider-specific kill mechanisms for graceful or forceful container termination.

### Resource Constraints and Configuration

Every sandbox execution receives a `SandboxConfig` object that enforces strict resource boundaries. The configuration interface supports:

- **cpuLimit**: CPU percentage caps (e.g., `200` for 200% of one CPU)
- **memoryLimit**: Memory allocation in megabytes
- **timeout**: Execution deadline in milliseconds
- **networkEnabled**: Boolean flag controlling network access
- **readOnly**: Flag mounting the filesystem as read-only

Default limits are defined in [`sandbox.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/sandbox.ts) as `DEFAULT_CONFIG`, while individual skill handlers can override these values per-invocation using the `sandboxConfig` helper.

### Execution Lifecycle

When a skill requests sandbox execution, the runner executes the following sequence (lines 73-115 in [`sandbox.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/sandbox.ts)):

1. Generates a UUID using `randomUUID()` as the container identifier
2. Calls the provider's `buildRun` method to construct the execution command and arguments
3. Spawns the process using `child_process.spawn`, piping `stdout` and `stderr`
4. Sets a timeout; if expiration occurs, invokes `kill()` to send `SIGTERM` and execute provider-specific cleanup
5. Resolves a `SandboxResult` containing exit code, captured output, duration, and a `killed` flag

## Workspace Isolation and Security Boundaries

Beyond containerization, OmniRoute implements **workspace isolation** at the filesystem level. The `resolveWorkspacePath` function in [`builtins.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/builtins.ts) ensures each API key receives a dedicated workspace directory derived from the SHA-256 hash of the `apiKeyId`.

This design guarantees that:

- Skills cannot traverse outside their assigned workspace
- File read/write operations are scoped to the specific caller
- Multiple clients executing simultaneously remain completely isolated at the storage layer

## Practical Examples

### Invoking a Built-in Skill

To execute a skill programmatically from client code:

```typescript
import { skillRegistry } from "./src/lib/skills/registry";

async function readFileDemo(apiKeyId: string) {
  const handler = skillRegistry["file_read"];
  const result = await handler(
    { path: "notes/todo.txt", encoding: "utf8" },
    { apiKeyId }
  );
  console.log(result.content);
}

```

*The `file_read` handler implementation resides in [`src/lib/skills/builtins.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/builtins.ts) (lines 98-124).*

### Running Custom Commands in a Sandbox

For untrusted code execution, invoke the sandbox runner directly with custom constraints:

```typescript
import { sandboxRunner, type SandboxConfig } from "./src/lib/skills/sandbox";

async function runLs() {
  const cfg: SandboxConfig = {
    cpuLimit: 200,          // 200% of one CPU
    memoryLimit: 128,       // MB
    timeout: 5000,          // ms
    networkEnabled: false,
    readOnly: true,
  };
  const result = await sandboxRunner.run(
    "alpine:3.20",                 // image (allowed by default)
    ["ls", "-l", "/workspace"],   // command inside the container
    {},                           // extra env vars
    cfg
  );
  console.log(result.stdout);
}

```

*The `run` method is defined in [`sandbox.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/sandbox.ts) (lines 67-115).*

### Registering Custom Skills at Runtime

Extend the system by adding handlers to the registry dynamically:

```typescript
import { skillRegistry } from "./src/lib/skills/registry";

skillRegistry["echo"] = async (input, context) => ({
  success: true,
  echoed: input.message,
  context: context.apiKeyId,
});

```

*Custom skills become immediately available to all clients through the same `skillRegistry` object.*

## Request Flow: From Client to Container

The complete execution path demonstrates how the skills system integrates with sandbox isolation:

1. **Client request** includes `skill: "http_request"` and the required payload
2. **Route handler** extracts the skill name and performs a lookup in `skillRegistry`
3. The selected handler validates inputs and determines execution mode (direct or sandbox)
4. If **sandbox mode** is active (`defaultMode === "sandbox"` or user-specified), the handler invokes `sandboxRunner.run(image, cmd, env, sandboxConfig)`
5. The **ContainerProvider** executes the container with strict resource caps and workspace mounting
6. Upon completion, the `SandboxResult` is formatted into the standard skill response structure (`{ success, ... }`) and returned to the client

## Summary

- The **skills system in OmniRoute** centers on a registry pattern mapping skill names to handler functions in [`src/lib/skills/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/registry.ts), supporting both built-in and dynamically registered capabilities.
- **Sandbox isolation** is achieved through the `SandboxRunner` singleton in [`src/lib/skills/sandbox.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/sandbox.ts), which spawns containerized environments with configurable CPU, memory, timeout, and network restrictions.
- **Workspace isolation** uses SHA-256 hashed API keys to create per-caller filesystem boundaries, preventing unauthorized file access across different clients.
- The **ContainerProvider** abstraction in [`src/lib/skills/containerProvider.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/containerProvider.ts) enables support for multiple container runtimes (Docker, Podman) without modifying core execution logic.
- All built-in skills in [`src/lib/skills/builtins.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/builtins.ts) receive a `SkillContext` object containing the hashed API key, enabling consistent security boundaries across file operations, HTTP requests, and custom code execution.

## Frequently Asked Questions

### What is the skills system in OmniRoute?

The skills system in OmniRoute is a modular framework that exposes reusable capabilities—such as file I/O, HTTP requests, and code execution—to downstream clients through a centralized registry. It allows both built-in operations and custom handlers to be registered at runtime, providing a consistent interface for capability invocation across A2A and MCP protocols.

### How does OmniRoute achieve sandbox isolation?

OmniRoute achieves sandbox isolation through the **SandboxRunner** class, which spawns lightweight containers using Docker or Podman via the **ContainerProvider** abstraction. Each container runs with strict resource limits defined by `SandboxConfig`, including CPU percentage caps, memory limits, network restrictions, and timeout enforcement. The runner manages container lifecycles, automatically kills processes exceeding timeouts, and ensures filesystem access is restricted to per-API-key workspace directories.

### What resource limits can be configured for sandboxes?

According to the `SandboxConfig` interface in [`src/lib/skills/sandbox.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/sandbox.ts), administrators can configure `cpuLimit` (percentage of CPU), `memoryLimit` (megabytes), `timeout` (milliseconds), `networkEnabled` (boolean), and `readOnly` (boolean). These defaults can be overridden per-invocation, allowing fine-grained control over execution environments based on the specific requirements of each skill.

### How are files isolated between different API keys?

Files are isolated through a workspace naming scheme that generates unique directories based on the SHA-256 hash of each `apiKeyId`. The `getWorkspaceRoot` function in [`src/lib/skills/builtins.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/skills/builtins.ts) resolves these paths, ensuring that handlers can only access files within their specific hashed workspace directory. This cryptographic separation guarantees that no skill can read or write files belonging to a different API key, even when executing concurrently on the same host.