apomesh-tool-runner
apomesh-tool-runner is the isolation boundary made executable: a
one-shot binary the per-OS ToolSandbox adapters spawn to run exactly one
tool invocation inside the OS sandbox. It is the third link in the
worker → sandbox → runner execution chain — the point at which tool code
finally runs, in its own short-lived process that shares nothing with the
worker. Touch it when the subprocess wire contract, the runner’s tool
registry, or its resource-limit / env hygiene changes.
Position in the workspace
Section titled “Position in the workspace”- Layer: worker / tool execution. See Workers for the sandbox isolation chain the runner sits at the end of.
- Depends on:
apomesh-substrate — the one
workspace dependency. Through that facade the runner reaches the
ToolInput/ToolOutput/ToolRegistrytypes, the substrate-shipped tools it registers, and therunner_exitexit-code contract (defined inapomesh-substrate-types). The rest are third-party:clap,libc,tokio(current-thread only),tokio-util,serde. - Consumed by: no crate links it — it is a binary-only crate. It is
located and spawned at runtime, not compiled in, by
apomesh-substrate-sandbox’s
locate_tool_runner. The two binaries ship side by side (see Usage).
What it owns
Section titled “What it owns”- The subprocess wire contract — a 4-byte big-endian length prefix
followed by a JSON
ToolInputon stdin, and the same framing carrying aToolOutputon stdout when dispatch succeeds. This framing is locked across both sandbox adapters;apomesh-substrate-sandboxspeaks the parent half. - The transient credential channel — the inbound
ToolInputcarries aToolContextwhosesecretsmap holds the per-tenantForwardedCredentialvalues the orchestrator resolved and the worker forwarded. Each registered tool reads its credential from there at dispatch — never from process env. See The credential plane. - The one-shot lifecycle — one process per tool dispatch. It builds a
single-threaded Tokio runtime to host the async
Tool::dispatch, runs it once, writes the frame, and exits. The sandbox re-spawns the runner for every dispatch. - The runner’s tool registry —
build_registryregisters the substrate-shipped, credential-less production tools (the nativeweb_searchproviders, the wrapped client search, andweb_fetch); each resolves its credential per dispatch fromToolContext.secrets. A tool the registry lacks surfaces to the worker astool not found, not a crash.--features test-toolsadditionally registers reflective in-process tools the sandbox integration tests use. - Resource-limit + error hygiene — an optional
APOMESH_MEMORY_LIMIT_BYTESis applied as anRLIMIT_AScap (baseline usage plus budget) before the first allocation; failures travel via distinct exit codes, not parsed stderr, so the parent maps them onto itsSandboxErrorvocabulary.
It does not own the sandbox policy or the spawn mechanics (those live in
apomesh-substrate-sandbox),
nor the tool implementations themselves (those live in the substrate tool
plane, reached through the apomesh-substrate facade). The runner is a host,
not the policy or the capability.
Public surface
Section titled “Public surface”This is a binary — its surface is its invocation contract, not an API.
- CLI — one flag:
apomesh-tool-runner --tool-id <ID>. The<ID>must match a tool registered at startup; an unknown tool exits distinctly. - stdin — 4-byte BE length +
ToolInputJSON (a 64 MiB frame cap guards against a desynced writer). TheToolInputcarries the toolargsand the optionalToolContext(invokingagent_ref+ the resolvedsecrets). - stdout — on success, 4-byte BE length +
ToolOutputJSON (the toolresultplus an optionalcostthe worker turns into aCostEvent). - Exit codes — the failure classification (
BAD_CLI_ARGS,RLIMIT_FAILED,TOOL_NOT_FOUND,INPUT_PARSE_FAILED,TOOL_ERROR,OUTPUT_WRITE_FAILED) is the single source of truth inapomesh-substrate-types(runner_exit), shared with the sandbox-side classifier so the cross-process contract cannot drift. On aTOOL_ERRORexit the runner serializes theToolErroras JSON to stderr for the parent to attach. - Cancellation — the runner is killed, not signalled: the sandbox aborts
a dispatch by
SIGKILLon the child, so the in-process cancel token is passed fresh and never fires.
Source: main.rs.
The runner is never invoked by hand — the sandbox locates and spawns it per
dispatch. locate_tool_runner
resolves the binary in order:
- the
APOMESH_TOOL_RUNNERenvironment variable (explicit override), - an
apomesh-tool-runneronPATH, - a sibling of the current executable — the canonical layout when the worker and runner ship in the same artifact.
That third rule is why the deployment ships them together: the worker
Dockerfile
builds both binaries (cargo build --release --bin apomesh-worker --bin apomesh-tool-runner) and copies each into /usr/local/bin/, so the worker
finds the runner as its sibling with no configuration. In a dev checkout the
same holds — both land in target/debug/.
Related
Section titled “Related”- Reference: apomesh-worker (drives the dispatch and selects the sandbox), apomesh-substrate-sandbox (locates and spawns this runner), apomesh-substrate-tools (the tool implementations the runner hosts).
- Concepts: The credential plane (how a resolved secret reaches the tool over the transient channel).