io-harness 0.17.0

An embeddable agent runtime for Rust: any task, any provider, in your own process. Run commands, edit files and search a repository under a layered permission boundary on files, commands and network; gate the result on the project's own test command in any language, or on nothing at all; and keep a full SQLite trace of every step, refusal and budget draw. With an execution sandbox, contained sub-agents, an MCP client, and durable resume for unattended runs.
Documentation

IO Harness

crates.io docs.rs CI License: Apache-2.0 MSRV 1.88

An embeddable agent runtime for Rust. Any task, any provider, in your process — with a permission boundary, a sandbox, and a durable trace you own.

You hand it a contract: the task, the workspace it may touch, and what it may read, write, run and dial. It runs the loop — observe, reason, act, check, stop — and hands back an outcome, with every step, refusal and budget draw in a SQLite trace you can read afterwards. The agent can run the project's own toolchain, so the language the project is written in is not the harness's business.

Quickstart

[dependencies]
io-harness = "0.17"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
use io_harness::{run_with, ApproveAll, OpenRouter, Policy, Store, TaskContract, Verification};

#[tokio::main]
async fn main() -> io_harness::Result<()> {
    let provider = OpenRouter::from_env()?; // OPENROUTER_API_KEY + OPENROUTER_MODEL
    let store = Store::memory()?;

    let contract = TaskContract::workspace(
        "the test suite is failing; find out why and fix it",
        "/path/to/repo",
        // The project's own command decides whether the work is done. Nothing
        // here is Rust-specific — `go test ./...` or `pytest` reads the same.
        Verification::Command { argv: vec!["npm".into(), "test".into()], expect_exit: 0 },
    );

    // src/ is writable, secrets/ is refused outright and never reaches a human,
    // and the agent may run the test runner and nothing that publishes.
    let policy = Policy::default()
        .layer("app")
        .allow_read("*")
        .allow_write("src/*")
        .deny_read("secrets/*")
        .allow_exec("npm*")
        .deny_exec("npm publish*");

    let result = run_with(&contract, &provider, &store, &policy, &ApproveAll).await?;
    println!("{:?}", result.outcome);
    Ok(())
}

Requires Rust 1.88 or later. The floor comes from rmcp, which publishes no rust-version of its own, so cargo cannot catch it at resolve time — on 1.87 the build fails inside that dependency rather than here.

What it does

The loop. A [TaskContract] names the goal, the subject, and — optionally — the criterion. Workspace mode gives the agent grep, find, read_file, write_file and edit_file across a repository root. Single-file mode edits one file.

Commands, under the same boundary as everything else. The agent runs the project's own build, tests, linter or package manager through an exec tool taking a fixed argv and never a shell string. Every call is checked against the policy on the program and on the whole argv, so allow_exec("cargo test*") beside deny_exec("cargo publish*") means what it reads. A command runs with your process's privileges and is not sandboxed — that bound is stated in full in the command execution guide, because it is the widest thing the crate grants.

Verification in any language, or none. A criterion can be the project's own test command in whatever language it is written, or nothing at all when the task has no checkable criterion — "work out why the deploy fails" is a run, not a gate. What a passing gate does and does not prove is stated exactly in the verification guide; it is narrower than it reads. The harness also ships a table of what each ecosystem's commands conventionally are, so the agent does not spend turns discovering that this is a pnpm workspace — see language support.

A permission boundary. Layered, deny-first rules decide what the agent may read, write, execute, and connect to. Anything marked ask goes to an approver that can approve, deny, or defer past the end of the process and resume on a human decision later. Every refusal and decision is in the trace, attributed to the rule and the layer that produced it.

Budgets and stop conditions. Steps, wall-clock time, and token spend are capped. A tree of agents draws from one shared ledger no spawned contract can raise.

Agent composition. A root run can spawn contained sub-agents over a shared workspace, nested, many at once. A child inherits its parent's policy and can only narrow it — never grant itself what the parent lacks.

An execution sandbox. Model-produced code runs in an ephemeral sandbox with an isolated workdir, resource caps that kill rather than throttle, and network denied by default. Native backends on macOS and Linux over a portable floor that runs everywhere.

Durable, unattended runs. After every completed step the trace, the budget draw, and a checkpoint commit in one transaction. A crash resumes the whole tree where it stopped: completed steps are not re-run, the budget is not double-charged, and an irreversible action already taken is not taken twice.

Providers, with fallback. OpenRouter, Anthropic, and OpenAI behind one trait, over the crate's own HTTP+SSE client. A provider that is down or rate-limited falls back to the next configured one; failures are classified so a caller can tell a retryable transport error from a terminal one.

Extensibility, in-process and out. Implement the Tool trait for something your program already does, or point the harness at MCP servers over stdio or streamable HTTP. Skills are markdown instruction files that shape how the agent approaches a class of task, with no Rust at all.

Context that stays relevant. Each turn is assembled to fit a stated share of the token budget: superseded observations are compacted, and an observation a later write invalidated is re-read rather than trusted. Durable memory keyed to the workspace survives between runs.

Observation and replay. Register an observer and be called as the run happens — steps, tool calls, approvals, refusals, spend draws, retries, fallbacks, outcomes — instead of polling the store. A recorded provider replays a case so it runs identically twice.

Documents and images, behind opt-in features: spreadsheets, Word, PowerPoint text, PDF, and barcode decoding, each gated on the real path the model named; and image passthrough to any provider whose model accepts one.

Git, as fixed-argv built-ins: status, diff, log, add, and commit, so a run ends as a reviewable commit rather than a working tree someone has to reconstruct. The model supplies paths and a message, never a subcommand or a flag, so push, fetch, reset and rebase are unreachable by construction.

Guides

Guide What it covers
Permissions and approval Layered rules, what asks and what is refused, deferring past process exit
Command execution Running a project's own toolchain, checked on the whole argv — and the bound that is not there
Language support Toolchain detection, a criterion in any language, migrating off the Rust-specific gates
Verification The criteria, execution-based gates, and exactly what a pass proves
Agent composition Sub-agents, inherit-and-narrow containment, the shared ledger
Execution sandbox Backends per platform, resource caps, the portable floor
Durable runs Checkpoints, resume, approvals that survive a restart
MCP and network egress Stdio and HTTP servers, Act::Net, what the policy does not govern
Tools and skills The Tool trait, the toolbox, skill discovery and its boundary
Context and memory Per-turn assembly, compaction, invalidation, durable memory
Resilience Failure classification, retry, provider fallback, stall detection
Observability and replay Observers, events, outcome records, deterministic replay
Documents Spreadsheets, Word, PowerPoint, PDF, barcodes — and what was cut
Images and git Image passthrough and the fixed-argv git built-ins

docs/CAPABILITIES.md indexes them. docs/CONTRACT.md is the public contract: what is stable, what may change, and the limits that hold today.

Feature flags

Everything below is off by default. The default build compiles no optional dependency at all.

Feature What it adds
media Image passthrough to providers that accept images
documents Umbrella over the five below
xlsx Spreadsheet read, generate, and preserving single-cell edit
docx Word read and generate (no in-place edit, deliberately)
pptx PowerPoint text extraction (read-only, no writer)
pdf PDF generate, extract text, watermark, fill AcroForm fields
barcode Barcode and QR decoding from an image

Platform support

Platform Sandbox containment
macOS Native, sandbox-exec
Linux Native, namespaces and rlimits
Windows Portable floor only — the Job Object backend is designed, not implemented, and only the wall clock is enforced

The full suite runs on all three in CI.

Stability

The crate is pre-1.0 and stays pre-1.0 until its owner says otherwise. A minor release may break the public contract — SemVer permits it below 1.0, and this project uses it. What you can rely on is not that a break will not happen, but that when it does it is marked in CHANGELOG.md with a migration note saying what to write instead, and that a renamed or removed item goes through a deprecation cycle rather than vanishing between two releases.

docs/CONTRACT.md states the whole of it.

Part of initorigin

IO Harness is one of the initorigin products:

Product What it is Status
io-harness The Rust agent harness (the center product) Released
io-cli Terminal app on io-harness In development
io-studio Desktop coding studio on io-harness In development

io-harness is the only public repository today, which is why it is the only one linked. io-cli and io-studio open when they release.

Contributing

Read CONTRIBUTING.md. Work branches from develop, lands via PR, and every user-facing change updates CHANGELOG.md. Releases follow docs/RELEASE_PROCESS.md.

Security

Report vulnerabilities per SECURITY.md.

License

Apache-2.0. Copyright 2026 Aakash Pawar (InitOrigin). See LICENSE and NOTICE.