Tool Binding, Handler, and Manifest
This guide explains how a Touba tool executes real code without putting that code in the .tb file.
Overview
source .tb
declares a tool
|
v
AIR
carries requirements
|
v
runtime
verifies the manifest
|
v
handler
executes the real effect
|
v
trace
observes authorization, refusal, or success
Declare the Tool
Shape:
tool Name
takes input as InputType
gives OutputType
requires capability
Example:
tool SearchDatasets
takes request as DatasetSearchRequest
gives DatasetSearchResult
requires network
This declaration does not grant network access. It says that the tool requires the network capability.
Tool capabilities
V1 has a closed list: network, filesystem_read, filesystem_write,
process, external_api, secret_read, human_approval, memory_read
and memory_write. Other names produce UnknownToolCapability.
process covers host process operations, such as invoking Docker.
Separate ReadLogs and RestartService into distinct tools so uses can
allow one without the other. Grants are admission checks, not an OS sandbox.
The human_approval capability alone creates no pause: put human.approve
before the protected effect.
Generate the Manifest
Shape:
touba compile program.tb --out program.air.json
touba manifest generate program.air.json --out touba.manifest.json
In a Touba project, the recommended workflow is:
touba prepare --mode plan
touba prepare --mode write
touba prepare --mode check
prepare produces or verifies the project manifest and handler stubs from AIR. The commands touba compile, touba manifest generate, and touba handler stub remain useful for understanding each transformation separately.
Example:
touba compile search.tb --out search.air.json
touba manifest generate search.air.json --out touba.manifest.json
The manifest is a reviewable template. It connects a tool to a handler and declares granted capabilities, secrets, and limits.
Create a Handler
Shape:
touba handler stub program.air.json \
--tool-ref tool:module.name/ToolName \
--handler-root handlers
Example:
touba handler stub search.air.json \
--tool-ref tool:examples.search/SearchDatasets \
--handler-root handlers
The generated file is a skeleton. The developer adds business code to it.
Write the Handler with the SDK
Shape:
await defineTool({
toolRef: "tool:module.name/ToolName",
inputSignature: [{ name: "request", type_ref: "type:module.name/Input" }],
outputType: "type:module.name/Output",
requiredGrants: ["network"],
run: async ({ request, context }) => {
return await runHostCode(request, context);
},
});
Example:
import { defineTool } from "@touba/sdk";
await defineTool({
toolRef: "tool:examples.search/SearchDatasets",
inputSignature: [{ name: "request", type_ref: "type:examples.search/SearchRequest" }],
outputType: "type:examples.search/SearchResult",
requiredGrants: ["network"],
run: async ({ request }) => {
return await searchDatasets(request);
},
});
The SDK handles the process protocol on the handler side. The runtime remains the primary authority for grants, secrets, scopes, limits, and payloads.
Run with an Explicit Manifest
Shape:
touba run program.air.json \
--manifest touba.manifest.json \
--inputs-json '{"input":{"field":"value"}}' \
--trace-out trace.json
Example:
touba run search.air.json \
--manifest touba.manifest.json \
--inputs-json '{"request":{"query":"parks"}}' \
--trace-out trace.json
The runtime refuses before the handler if the manifest does not satisfy the AIR contract. By default, standard output displays show messages. Use --output result to display the returned value, or --output trace to write the JSON trace to standard output.
Even if the manifest was generated by touba prepare, it must be supplied explicitly to the runtime with --manifest.
Verify the Handler Supply Chain
For a strict run, add a supply-chain lock coherent with AIR, manifest, handler, SDK, declared handler_package identity, handler package files, and declared dependency artifacts:
touba run search.air.json \
--manifest touba.manifest.json \
--supply-chain-lock touba.lock.json \
--require-supply-chain-lock \
--inputs-json '{"request":{"query":"parks"}}'
The lock grants no runtime capability. It refuses drift before handler launch.
Observable Refusals
The runtime can refuse when:
- no binding exists for the tool;
- the required grant is missing;
- a required secret is missing;
- the input payload does not match the signature;
- the output payload does not match
gives; - the handler exceeds its timeout;
- the payload exceeds the declared limit.
Local handler diagnostics
Unhandled exceptions retain a bounded public diagnostic, without arbitrary messages or stacks. To locate an exception in a prepared development project:
touba run --project . --profile dev --handler-diagnostics diagnostic-local.jsonl
Add your program's required inputs and providers. The destination must be new.
On macOS/Linux, it is exclusively created with mode 0600, without overwriting
an existing file or following a final symlink. Records contain runtime failure
codes, run/call identifiers, exception classes and, with the SDK, a line and
column within a handler file argument (handler_arg_index). Calls into a
different file may not yield a position.
No exception message, raw stack, stderr, payload or secret value is recorded.
Limits are 4 KiB per private response, 128 events and 64 KiB per file. A warning
reports incomplete diagnostics; malformed private data is discarded. This is
not evidence of business effects, and is not exported into traces, server APIs
or SSE. Reproduce the problem in a focused business test; use
ToubaHandlerError for a deliberately safe public diagnostic.
The option requires an explicit dev run plan, resolved through --project
or supplied with --run-plan. Production, required locks, hardened profiles
and Windows are refused. Ambient environment variables and .env do not
activate it. Do not publish the local diagnostic file.
Runtime Scope
Tool binding provides an explicit path from source contract to host effect: declared tool, manifest binding, handler, runtime checks, and trace.
Required platform isolation is configured per binding. Complete permission systems, automatic handler package installation, vulnerability auditing, and dependency graph resolution extend the published static registry. The runtime does not discover handlers automatically.