Installable tool binaries
rex::workflow discovers typed tool modules at runtime. Give the CLI a directory with
--tool-dir DIRECTORY or REX_TOOL_DIR; an import of tools.NAME looks for the executable
rex-tool-NAME in that directory. Tools that are not imported are neither inspected nor started.
When neither setting is present, the CLI searches beside its own executable.
Installing a tool means copying its executable into that directory. Removing it makes the module
unavailable on the next workflow compilation. rex does not link to tool crates, so this
does not require recompiling the workflow host.
Binary contract
Every tool is a separate Rust crate named rex-tool-NAME and its binary implements two commands:
rex-tool-NAME manifest
rex-tool-NAME execute FUNCTION [JSON]
If the JSON argument to execute is omitted, the binary reads it from standard input. Arguments
may be an object keyed by the documented parameter names or a positional array. A successful
command writes only JSON to standard output; diagnostics go to standard error and failures return
a nonzero status.
manifest returns protocol version 1, the exact Rex module ID, and a stable TypeBundle:
{
"protocolVersion": 1,
"module": "tools.example",
"typeBundle": {
"docs": "Module documentation.",
"values": {
"ping": [{
"scheme": { "type": {
"kind": "fun",
"params": [{ "kind": "builtin", "name": "String" }],
"ret": { "kind": "builtin", "name": "String" }
} },
"params": ["message"],
"docs": "Function documentation."
}]
},
"adts": []
},
"defaults": []
}
The bundle contains every public function scheme, Rex-facing parameter name, module/function/type
documentation, and every ADT owned by the module. defaults preserves concrete host-backed
Default instances used by record construction and update. Protocol version 1 requires each
function to have one non-overloaded, concrete type scheme because JSON cannot represent unresolved
type variables.
For execute, the shared runner uses json_to_rex for every argument, invokes the ordinary typed
module handler, and uses rex_to_json for its result. The generated handler therefore performs the
normal FromRex conversions before entering Rust and IntoRex conversion on return. The proxy in
The rex::workflow proxy uses the same two JSON conversion functions, so direct binary calls and calls from a
workflow have identical representations.
Authoring a tool
A tool crate depends on rex, defines an ordinary Rust-backed module, and gives
that module factory to the shared command runner:
use rex::{
engine::{EngineError, Module},
workflow::state::State,
};
fn module() -> Result<Module<State>, EngineError> {
// Usually generated by #[rex::module] and #[rex::export].
api::rex_module()
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
rex::workflow::tool_protocol::run_tool_cli(
module,
|| rex::workflow::tool_protocol::default_tool_state(
"example",
"REX_TOOL_EXAMPLE_IMAGE",
"rex-tool-example:local",
),
)
.await
}
The bundled examples are the six workspace crates rex-tool-ffmpeg, rex-tool-gnuplot,
rex-tool-graphviz, rex-tool-imagemagick, rex-tool-poppler, and rex-tool-qpdf. Their Rust
types derive Rex; exported async functions receive State, convert typed requests into OCI jobs,
and return typed results.
Use State::store and the helpers in rex::storage for content-addressed blobs and trees. Use
State::execute_tool with a ToolExecutionPlan containing the tool’s trusted command. A tool can
instead construct an OciJob and invoke an OciJobExecutor such as
DockerOciJobExecutor; the public job boundary provides the same CAS staging, output validation,
limits, and isolation controls.
default_tool_state opens the filesystem CAS named by REX_STORE and configures the Docker OCI
backend for the tool name, image variable, and development default supplied by that tool crate.
Embedding hosts with a different deployment model may supply a different state factory to
run_tool_cli.
Trust boundary
An installed tool binary is native code and is therefore trusted to the same degree as the Rex workflow host. The directory must not be writable by untrusted workflow authors. Rex source cannot select an arbitrary binary: the requested module ID determines one filename, and the binary’s manifest must declare that exact module and a supported protocol version.
The native binary is only the typed protocol adapter. External media, plotting, document, or scientific programs should still run through the OCI executor rather than directly on the host. Workflow source controls typed function arguments, not image references, mounts, executable paths, or Docker options.