Substrate

Portable tool execution service that reduces the burden on agent applications by managing environment complexity and eliminating repetitive tool implementation

GitHub

Substrate is an environment like a filesystem, with a worker that reads from an agent application in the background. Intuitively, Substrate's SDK intercepts your tool calls, writes them to the broker, which is often just a path in your filesystem. The worker will be listening on the broker, relaying the tool call events to the server for execution.

One tool call through Substrate Inside your agent process the Substrate SDK intercepts a tool call and appends it as an event to the broker, a directory on disk. In the Substrate service a worker pulls the event and submits it to the server, which resolves policy and workspace, executes the call in a sandbox, and persists sessions, effects, and artifacts. The result returns to the in-process call; a direct HTTP mode can skip the broker entirely. Your agent process Substrate service result direct HTTP Agent app your code Substrate SDK Broker a directory on disk Worker Substrate server resolves policy + workspace Sandbox isolated, resolved workspace Sessions Effect ledger Artifacts intercepts the tool call append tool-call event worker pulls events submit call execute under policy persist 1 2 3 4 5 6 call / event async pull return / alternate 1 step order
One tool call, end to end — ① the SDK intercepts the call inside your process, ② appends it as an event to the broker (just a directory on disk), ③ the worker pulls it, ④ submits it to the server, ⑤ which executes it in a sandbox under the resolved policy, ⑥ and persists sessions, effects, and artifacts. The result rides the same path back to your code — or skip the broker entirely with direct HTTP.
from pathlib import Path
from substrate import Environment


def open_session(root: Path, *, timeout_ms: int = 600_000):
    """Provision the environment once; it outlives any agent."""
    env = Environment.create(
        workspace={"kind": "existing", "root": str(root)},
        worker={"kind": "managed", "id": "synth-worker"},
        policy={
            "readRoots": ["/workspace"],
            "writeRoots": ["/workspace"],
            "network": {"enabled": False},
            "maxDurationMs": timeout_ms,
            "maxOutputBytes": 100_000,
        },
        submitTimeoutMs=timeout_ms,
    )
    return env.create_session()


def run(session, agent):
    """Drive an agent against an existing session."""
    while not agent.done:
        call = agent.next_tool_call()
        result = session.submit_tool(call.name, **call.arguments)
        agent.observe(result)
    return session.effects()  # what the agent applied


# Provisioned once; agents come and go against it.
session = open_session(Path("/data/run-42"))
effects = run(session, agent)

This project is primarily about separating tool execution from an agent application. This buys three things I find important: First, you do not have to redefine and reimplement tools for every language or project. Second, you can separate application control state from environment state. Third, you can separate the application lifecycle from the environment lifecycle. The tried and true metaphor for this concept makes it a bit more clear:

A waiter carries your order back to the kitchen, but they don't cook it at your table, and they don't drag the stove, the pantry, and the half-finished sauces out into the dining room. They hold only the order ticket, table four, salmon. That is the control state, it routes the waiter. The kitchen holds everything else: the ingredients, the equipment, the mise en place that took all morning to set up. Substrate is a kitchen that cooks every dish instantly, and can be dropped in the new restaurant you're opening tomorrow that is a completely different cousine with a waitstaff that rides around on unicycles.


WorkloadRequestsThroughputMedianP95
GET /health baseline5006,861 rps0.130 ms0.207 ms
Sequential Write250200 rps4.963 ms6.256 ms
Sequential Read2503,968 rps0.215 ms0.370 ms
Sequential Glob over 250 files1002,538 rps0.390 ms0.433 ms
Concurrent Write, 8 envs, 32 clients800320 rps97.273 ms141.030 ms

Measured on localhost against a release-built Substrate HTTP host. The run created real environments and sessions, then executed actual tool calls against temporary workspaces.