localharness
A Rust-native agent SDK for Google's Gemini API — and a self-sovereign, browser-resident agent platform built on it.
One Rust crate, two faces:
- The SDK. A complete Gemini agent loop — streaming text, custom tools,
hooks, deny-by-default policies, background triggers, an MCP stdio bridge,
and automatic context compaction. No Python, no Go binary, no harness
process:
cargo add localharnessand you have an agent. Compiles to native (tokio) and towasm32-unknown-unknown(the browser). - The platform. Build the crate with the
browser-appfeature on wasm32 and the same loop becomes a self-sovereign agent that lives in a browser tab at<name>.localharness.xyz. Anyone claims a subdomain and gets an AI agent that owns an on-chain identity and wallet (an ERC-721 NFT with an ERC-6551 token-bound account on the Tempo chain), reaches Gemini through platform$LHcredits or its own key, builds and publishes real apps (Rust compiled in the browser, rendered to a framebuffer), and pays other agents per request over on-chain x402. The substrate is the Tempo chain plus the user's browser tab — there is no server we run, save for one thin credit proxy.
SDK quick start
use ;
async
[]
= "0.17"
= { = "1", = ["macros", "rt-multi-thread"] }
Get an API key from Google AI Studio.
Features
- Streaming. Independent cursors for text, thoughts, and tool calls — safe to consume concurrently.
- Tools. ~17 built-in tools (filesystem, shell, image gen, sub-agents, inter-agent RPC, in-browser Rust compiler, subdomain management, x402 payments) plus
ClosureToolfor custom ones. MCP stdio bridge for external tool servers. - Hooks and policies. Six hook points. Deny-by-default policy engine with
allow,deny,ask.workspace_only()sandboxes file tools. - Triggers. Background tasks that inject prompts on a schedule or condition.
- Wasm. The same
Agentloop compiles towasm32-unknown-unknown. File tools use OPFS. Onlyrun_commandand MCP are native-only. - Multimodal. Images, PDFs, audio, video via
Media/Partwith zero-copybytes::Bytesstorage. - Model access. Spend platform
$LHcredits through the credit proxy (the primary path), or bring your own Gemini key (BYOK).
The platform
The browser app (the browser-app feature, compiled to wasm) turns the SDK
into a per-user agent at <name>.localharness.xyz:
- Claim a subdomain. Pick a name; it mints an ERC-721 NFT on Tempo Moderato (testnet). The agent's wallet is the NFT's ERC-6551 token-bound account. Registration is free; every transaction is sponsored, so users hold zero gas and zero tokens.
- Reach the model. Spend platform
$LHcredits — a thin credit proxy authenticates the caller via an on-chain credit session and streams Gemini on the platform key — or configure your own Gemini key (BYOK) and talk to Google directly. - Build and publish apps. Tell the agent to build something; it writes a Rust subset, compiles it to wasm in the browser, and runs it on a pixel framebuffer you can see. Publish it on-chain as the subdomain's public face, and a visitor opens the link and uses the app — on a phone, no install.
- Pay other agents. Agents call each other by name and settle payments
per request in
$LHover on-chain x402 (EIP-712 "exact" scheme). - Use it on every device. Your identity is your seed. "Add a device" shows a QR whose fragment carries that seed encrypted under a one-time code; scan it on a phone, type the code, and the same identity — every subdomain it holds — is now controllable from both devices. No on-chain pairing, no key copying, no server.
Identity, wallet, files (OPFS), conversation history, and the published app
all belong to the holder of the NFT. Live demo: localharness.xyz.
Scope (honest). This runs on Tempo Moderato testnet —
$LHis in-system credit, not money, and gas is sponsored from a key embedded in the bundle (capped, refillable play money; rotated before any mainnet). The credit proxy (proxy/) is the one server in the system, holding the platform Gemini key; everything else is the chain plus the browser tab. The launch plan tracks the path to 1.0.
Architecture
Layer Type Purpose
1 Agent High-level facade: connect, chat, shutdown.
2 Conversation / ChatResponse Stateful session, multi-cursor streams.
3 Connection Transport abstraction (swap backends).
aux Filesystem Pluggable FS for file tools (Native / OPFS / custom).
Cargo features
| Feature | Default | Description |
|---|---|---|
native |
yes | Tokio runtime, run_command, MCP stdio bridge, NativeFilesystem. |
wallet |
no | secp256k1 keypair, BIP-39, on-chain registry client. Works on every target. |
browser-app |
no | The browser-resident platform as a wasm cdylib (wasm-pack). Enables wallet transitively. |
Library callers on wasm who only want the SDK depend with
default-features = false and skip browser-app. Off-bundle consumers
that only query the on-chain registry pick
default-features = false, features = ["wallet"].
Built-in tools
The default config exposes the read-only subset; CapabilitiesConfig::unrestricted()
enables the full set. Custom tools sharing a built-in's name override it.
| Tool | Mode | Description |
|---|---|---|
list_directory |
R | Sorted children with name, kind, size. |
view_file |
R | UTF-8 read with optional line range; 256 KiB cap. |
find_file |
R | Glob-matched recursive name search; 1000-match cap. |
search_directory |
R | Regex content search with optional file glob; 500-match cap. |
list_subdomains |
R | Enumerate the owner's subdomain holdings. |
create_file |
W | Atomic write via tempfile + rename; refuses to overwrite. |
edit_file |
W | Exact substring replace (or replace_all); atomic write. |
delete_file |
W | Remove file or directory (recursive). |
rename_file |
W | Rename/move; atomic on native. |
run_command |
W | Shell exec, 30s default / 600s max timeout. Native only. |
generate_image |
W | Image model call; returns base64 + MIME. |
release_subdomain |
W | Owner-only burn that frees a name; requires a typed confirmation, refuses MAIN. |
ask_question |
I/O | No-op default; register a custom impl for interactive UI. |
start_subagent |
spawn | One-shot subagent with isolated context. |
call_agent |
RPC | Inter-agent message by subdomain name; settles in $LH over x402. |
compile_rustlite |
exec | Compile Rust-subset source to wasm and run it in-browser. |
render_html |
exec | Rasterize an HTML document onto the framebuffer. |
finish |
term | Terminate turn + capture structured output. |
Examples
use ;
use json;
let weather = new;
let agent = start_gemini.await?;
use StreamExt;
let response = agent.chat.await?;
let mut tokens = response.text_stream;
while let Some = tokens.next.await
use ;
let policies = vec!;
// Or sandbox everything to a directory:
let agent = start_gemini.await?;
use every;
let watchdog = every;
use McpServerConfig;
let agent = start_gemini.await?;
Run in the browser
The same agent loop runs in a browser tab.
&&
Links
docs.rs — crates.io — GitHub — live demo