Verified tool launch
Run a native tool from a signed registry and prove that the process that started is the one both authorities admitted.
On this page
An agent host can start native tools from a publisher's signed registry and know which bytes ran, with which arguments, under which restrictions. A configured pathname never turns into open-ended execution authority.
Two parties decide each launch. The publisher signs the tool's identity, topology, and requested permissions; the operator picks the trusted signers and the resource ceiling this host allows. Chio admits the registry under that ceiling, retains the exact helper and executable, and watches the executable enter its confined environment. At dispatch the kernel binds the resulting launch record into the call's receipt.
The receipt reads confined because the launch record is bound at dispatch
The publisher's signed registry and the operator's resource ceiling meet at admission, and the host retains the exact helper, target, and resource descriptors. The parent observes the target exec under the compiled restrictions and records a FullyEnforced launch record. At dispatch the kernel binds that record into the receipt as native_launch; a verifier checks its digest and signer against pinned kernel keys. Verified tool launch.
The receipt reads “confined by Chio” only when a FullyEnforced launch record is bound as native_launch at dispatch, and a verifier checks that record against pinned kernel keys. A host-executed tool carries no launch reference, so its receipt reads “not confined by Chio” with the reason from its tool_origin. A launch in any other state, or a target that exited before dispatch, fails dispatch closed with a deny receipt.
One signed contract covers the bytes, the arguments, and the resources
A tool registration that holds only a name and a command leaves the launch open: which executable bytes run, which arguments they receive, which resources they can open, which account owns the process. Verified launch fixes all four in one reviewed contract and retains the objects that enforce it.
| Input | Selected by | Binding it carries |
|---|---|---|
| Signed manifest | Trusted publisher | Verified registry snapshot, selected server and tools, topology, requested permissions |
| Signed launch policy | Operator | Ceilings, helper and target, exact argv, execution identity, required enforcement |
| Public-key pins | Operator configuration | Publisher and policy signer, chosen before the evidence arrives |
| Retained descriptors | Host and cage compiler | Opened helper, executable, and resource identities |
For a repository reader launched as chio-tool-repo-reader --root /srv/larkfield/pricing, the contract holds those exact arguments, the reader's bytes, the helper, the execution identity, and the retained read resources. Moving the workspace or replacing the binary is a new contract, reviewed again. A publisher can describe what its tool needs; only the operator decides what this machine allows, and launch needs both.
The host executes what it retained, so a swapped file never runs
Native minimal and standard profiles require authenticated local topology for every tool in the selected registry; the brokered profile requires authenticated brokered topology and its matching preconnected descriptor. Remote, mixed, missing, or profile-confused topology refuses cage admission. The compiled profile commits to the authorization digest, and the sealed launch plan commits to the profile.
The host then keeps the admitted helper and target open and never reopens their pathnames. It verifies the running helper through procfs and authenticates the process relationship before passing resources; the helper checks the sealed canonical plan and every descriptor it receives. Each substitution meets its own binding:
- Target replaced after preparation. The retained executable identity runs, or the launch is refused.
- A different helper. The authenticated helper identity fails and the launch path is refused.
- An inherited resource swapped. The descriptor role and retained resource identity no longer match, and the resource set is refused.
- A connection to another broker. The original prepared socket and request commitment refuse the substitution.
- Discovery changed after signing. The publisher-signed manifest refuses the inconsistent tool list.
Rebuilding a target therefore means a new admitted identity. These bindings keep the observed launch attached to the reviewed contract; the operating-system restrictions still constrain whatever code runs inside the process.
A launch becomes FullyEnforced only when the parent sees the exec
The helper remaps the fixed descriptor table, closes unnamed descriptors, applies Landlock filesystem and network restrictions, installs the independent seccomp filter, and sets the execution identity and parent-death behavior. Its initial exec uses retained descriptor 255 while the child's descriptor ceiling is 192, and close-on-exec removes that descriptor in the transition, so the target cannot place an object in slot 255 and reuse the bootstrap exec. The Linux enforcement exercise checks this relationship.
A prepared record commits to the manifest, profile, plan, descriptor table, helper, target, and applied enforcement state. The parent then requires the kernel's exec event, compares the stopped post-exec image with the retained target, and checks the close-on-exec status channel. Only that conjunction produces FullyEnforced.
The recorded launch reports landlock_abi 6, with landlock_filesystem_status, landlock_network_status, and seccomp_status each fully_enforced on x86_64, status_eof_observed true, and a target_binding_digest equal to the target digest the verifier checks below. A malformed, partial, timed-out, or identity-mismatched path terminates and reaps the child. The host never relaunches the tool without its restrictions to keep it available.
In Rust: the prepared and verified enforcement records
pub struct EnforcementPrepared {
pub schema: String,
pub process_id: u32,
pub manifest_digest: String,
pub profile_digest: String,
pub plan_digest: String,
pub fd_table_digest: String,
pub helper_binding_digest: String,
pub target_binding_digest: String,
pub target_identity: FileIdentity,
pub applied_execution_identity: ExecutionIdentity,
pub nono_version: String,
pub nono_patch_version: String,
pub landlock_abi: u32,
pub landlock_filesystem_status: ObservedRulesetStatus,
pub landlock_network_status: ObservedRulesetStatus,
pub seccompiler_version: String,
pub seccomp_status: SeccompEnforcementStatus,
pub seccomp_architecture: SandboxArchitecture,
pub seccomp_filter_digest: String,
pub trace_session_digest: String,
pub prepared_at_unix_ms: u64,
}pub struct FullyEnforcedEvidence {
pub prepared: EnforcementPrepared,
pub exec_transition: ExecTransitionObserved,
pub status_eof_observed: bool,
}The prepared example verifies one native start with eleven checks
The signed-launch package assembles the inputs and initializes a CLI process host. Its prepare.py reviews the selected tool's MCP inventory, runs the reference provisioner with demonstration signing keys, cross-checks the independent public pins, and runs chio process init with a six-call read grant against a new directory. The selector then copies the original enforcement receipt's exact bytes from the store, read-only, and the verifier compares it with the signed policy.
Run it on supported Linux x86-64 with the chio CLI, the repository reader and static-PIE cage helper from the reference-runtime archive, Python 3, and a private, operator-owned rollback-anchor directory on a separate persistent device. Choose the policy signer from your provisioning result and the target digest from the executable you selected.
export READER=/usr/local/libexec/chio/chio-tool-repo-reader
export HELPER=/usr/local/libexec/chio/chio-cage-init
export ANCHOR=/mnt/larkfield-recovery/reader-anchor
python3 prepare.py --directory "$PWD/profile" \
--reader "$READER" --helper "$HELPER" --anchor-root "$ANCHOR"
python3 select-receipt.py --database profile/launch/cage-receipts.sqlite3 \
--output enforcement.json > enforcement.id
POLICY_PIN=$(python3 -c 'import json; print(json.load(open("profile/public-pins.json"))["cagePolicyPublicKey"])')
TARGET_SHA256=$(sha256sum "$READER" | cut -d ' ' -f 1)
chio receipt verify-native-start \
--signed-policy profile/launch/cage-launch-policy.json \
--enforcement enforcement.json --server-id workspace \
--trusted-policy-signer "$POLICY_PIN" \
--expected-receipt-id "$(cat enforcement.id)" \
--expected-target-sha256 "$TARGET_SHA256"{
"schema": "chio.native-start-verification.v1",
"receipt_id": "1014e47fd65adc0270d2c3d129a84159e4809e7ecc0a184b0390b0ea364abf8e",
"server_id": "workspace",
"target_sha256": "12e89693ace081c9078494f167f04b9583f4235d0213424770bcda00d246507f",
"checks": [
"policy_signer_pin",
"enforced_policy",
"admitted_policy_binding",
"signed_manifest",
"receipt_signature",
"receipt_semantics",
"receipt_context",
"execution_identity",
"helper_binding",
"target_binding",
"selected_receipt"
],
"unchecked": [
"live_pid_linkage",
"target_death",
"call_completion",
"terminal_outcome",
"log_inclusion"
]
}You should see provisioning and initialization exit 0, a launch reporting Landlock ABI 6 with filesystem, network, and syscall restrictions fully enforced, and the verifier exit 0. Its record authenticates the policy, manifest, receipt, execution identity, helper, and target, and lists five claims it leaves to other evidence: the live process, its death, call completion, the terminal outcome, and log inclusion.
Changing only --expected-target-sha256 made the verifier exit 1, and the signed-material preflight rejected a wrong manifest pin and a changed workspace argument. Repair the reviewed input when that happens; editing a pin to match the artifact under inspection removes the comparison. Setup retains every command's status and output, and an existing profile or failed setup stays in place for inspection. With several launches in one store, pass the original operation's receipt ID to the selector.
An embedding consumes each prepared launch once, after a final authority check
An embedding host already owns its verified registry, operator ceilings, and compiled cage. This function prepares the sealed contract, keeps its target and plan commitments, rechecks the original authority, and consumes the single-use preparation.
use chio_cage::{CompiledCage, CageLaunchOptions, EnforcedChild,
prepare_launch_with_options, launch_prepared};
use std::time::Duration;
/// The host has already admitted the live registry and retained exact helper,
/// target, argv, working directory and resource descriptors into CompiledCage.
pub fn launch_review_tool(
compiled: CompiledCage,
recheck_original_authority: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
) -> Result<(String, String, EnforcedChild), Box<dyn std::error::Error>> {
let options = CageLaunchOptions::new(Duration::from_secs(10))?;
let prepared = prepare_launch_with_options(compiled, options)?;
let target = prepared.evidence().target_binding_digest().to_owned();
let plan = prepared.evidence().plan_digest().to_owned();
assert!(prepared.evidence().exact_requirements_match());
// Preparing has started neither helper nor target. The authorized serving
// owner rechecks its actual operation immediately before consuming it.
recheck_original_authority()?;
let child = launch_prepared(prepared, options)?;
Ok((target, plan, child))
// The MCP connector owns child.take_stdio() and the protocol exchange.
// Retain child.try_wait()/wait() evidence for exit; preparation is not exit.
}The returned child belongs to the connector, which takes its stdio for the MCP exchange and retains its lifecycle record at exit. Authority withdrawn after preparation fails the final recheck and the preparation is dropped without a call to launch_prepared. Missing kernel enforcement returns a CageLaunchError that retains the failure record, and a tool that exits nonzero after an enforced exec leaves launch and exit evidence to diagnose separately. Download the function; it compiles on the reviewed API and launches on the supported Linux enforcement host.
Read more in Security profiles, which supplies the CLI inputs for governed host launch.
A failed launch, a failed tool, and a lost report leave different records
Cage enforcement records become signed receipt bodies with state-specific meaning: a rejection, a bootstrap failure, a fully enforced launch, and a terminal exit are separate events. The verifier checks signature, content hash, bindings, state, and timestamps before treating one as evidence, and a launch record bound to another operation's server and tool says nothing about this one.
- Restrictions not installed. The helper failed before any enforced launch; the host refuses dispatch and the operator repairs host prerequisites.
- Tool error after an enforced launch. The process crossed the launch boundary and failed in its own work, with its launch and exit records retained.
- Completion report lost. The target launched and its outcome stays unknown; recovery resolves it without a second dispatch.
A brokered native tool owns one invocation over its prepared connection. Preparation and initialization establish that path, a lost completion authorizes no second use, and cleanup terminates the original child owner without replacing the connection. Upgrading the tool or launcher changes the contract: re-run the allowed, denied, and interruption cases, and keep the old record as evidence about the old launch.
Read more in Verify security evidence for the verification order, and Supply chain for the provenance of admitted artifacts.