ToolClad is the manifest format for AI tools — a single .clad.toml file that ties typed arguments, the command template, the output schema, and Cedar policy metadata together in one auditable contract. Three execution modes: oneshot CLI, interactive session via PTY, governed browser via CDP.
[tool]
name = "whois_lookup"
version = "1.0.0"
binary = "whois"
risk_tier = "low"
[tool.cedar]
resource = "Tool::WhoisLookup"
action = "execute_tool"
[args.target]
position = 1
required = true
type = "scope_target"
[command]
template = "whois {target}"
[output]
format = "text"
envelope = true
The model writes rm -rf / with the wrong glob. The sandbox tries to deny-list its way out. Allow-listing the call is the only safe escape.
The target field accepts a hostname, an IP, a CIDR — or a path traversal disguised as one. Errors surface at execve, not the call site.
Permissions live in three places: the wrapper, the runtime config, the agent prompt. None of them agree. Cedar in the manifest is the single source.
[tool] name = "nmap_scan" version = "1.0.0" binary = "nmap" description = "Network discovery scanner" timeout_seconds = 600 risk_tier = "high"[tool.cedar] resource = "Tool::NmapScan" action = "execute_tool"[args.target] position = 1 required = true type = "scope_target" [args.scan_type] type = "enum" allowed = ["service", "syn", "version"][command] exec = ["nmap", "-sT", "-sV", "{target}"] timeout_seconds = 600[output] format = "xml" parser = "builtin:xml" envelope = true [output.schema] type = "object"
every commit, every release, every call
Declare typed arguments, command template (or exec array), output parser, and Cedar resource/action in one human-diffable file. Lives next to the tool source.
$ $EDITOR tools/nmap_scan.clad.toml
Each row is a way contracts break. Each column is a section of .clad.toml. Filled cells are where the manifest carries the weight.
| threat ↓ · section → | [tool] | [args] | [command] | [output] | [tool.cedar] |
|---|---|---|---|---|---|
T-01Shell injection |
— |
✓ blocks |
✓ blocks |
— |
— |
T-02Untyped arguments |
— |
✓ blocks |
supports |
— |
— |
T-03Implicit policy |
supports |
— |
— |
— |
✓ blocks |
T-04Output reinterpretation |
— |
— |
— |
✓ blocks |
supports |
T-05Silent version change |
✓ blocks |
— |
— |
— |
supports |
One file alongside your tool. Validate it in CI. Sign it with SchemaPin. Now your contract is your artifact.
use toolclad::Manifest;
let m = Manifest::load("nmap_scan.clad.toml")?;
m.validate()?;
println!("✓ {} v{} — {} args",
m.tool.name, m.tool.version, m.args.len());
// Run it; ToolClad enforces types, timeout,
// process group isolation, and SHA-256 evidence.
let result = m.run(&["target=10.0.1.0/24"])?;
[tool]
name = "whois_lookup"
version = "1.0.0"
binary = "whois"
description = "WHOIS domain/IP registration lookup"
timeout_seconds = 30
risk_tier = "low"
[tool.cedar]
resource = "Tool::WhoisLookup"
action = "execute_tool"
[args.target]
position = 1
required = true
type = "scope_target"
[command]
template = "whois {target}"
[output]
format = "text"
envelope = true
Short answers. The full spec lives in TOOLCLAD_DESIGN_SPEC.md.
.clad.toml is the unit of trust. SchemaPin is what makes the manifest tamper-evident.[tool.cedar] resource/action pair. One file still parses; one decision rule still travels with it.oneshot (default CLI), session (PTY tools like msfconsole, psql) with per-interaction Cedar gating, and browser (CDP/Playwright) with URL scope enforcement. One file format, three backends.cargo install toolclad