v0.7.87 — Audit iteration 3: 27 verifier-confirmed fixes (June 2026) A third adversarial-audit sweep over API auth, channels, the kernel scheduler/workflows, memory/knowledge, and request routing — each finding gated behind a 3-lens refute panel. Closes an unauthenticated-Owner auth bypass (spoofed
X-Forwarded-For), redirect-based SSRF in the A2A and cron-webhook clients, an RHP pre-shared-key serialization leak, MCP allowlist/file-read hardening, a ~24× cost-quota over-count, cron validation/timezone correctness, workflow run-leak + persistence races, knowledge-graph multi-hop traversal, and Unicode-aware session search. See the release notes for details.Clone →
cargo run --release -- start→ open the dashboard → talk to an agent. No API key required. When no provider key is found in the environment, RustyHand falls back to a deterministic mock driver and seeds four sample resources so every major dashboard page is interactive on first visit:
rustywelcome agent (chat-ready)demo-pipelineworkflow (2-step sample, click to run)- sample agent-spawn trigger
demo-daily-pingcron job (registered, disabled by default)A welcome modal on first visit lists all four with one-click navigation. The CLI startup banner and Docker entrypoint both announce demo mode as a feature instead of a missing-key warning. Set
ANTHROPIC_API_KEY(or a Kimi / DeepSeek / Zhipu / MiniMax / OpenRouter / Ollama key) and restart for real LLM responses, orRUSTYHAND_DISABLE_DEMO_MODE=1to force a hard fail.Highlights since v0.7.41:
- React dashboard — every subsystem is a page: agents, chat, workflows, automation (cron + triggers), channels, analytics, knowledge graph, skills, approvals, audit, and a full
config.tomleditor.- Autonomous-by-default agents — tuned to act without asking; grant any agent all 72 tools live via
PATCH /api/agents/{id}/config(no respawn).- 72 built-in tools — shell, web/news search, browser automation, RAG, knowledge-graph CRUD, file ops, image vision, and config management.
- Trust-by-default security with visible guardrails —
GET /api/securityreports the real runtime posture (exec mode, approval policy, per-channel gating); flip to allowlist/deny withRUSTYHAND_EXEC_MODE.- Everything persists — audit log (Merkle hash chain), workflows, and triggers all survive daemon restart and are replayed/validated on boot.
Origin
This project is based on OpenFang by RightNow-AI, modified and extended for custom use cases.
Table of Contents
- What is RustyHand?
- Installation
- Quick Start
- Telegram Setup
- Configuration
- CLI Reference
- Autonomous Templates
- 40 Pre-built Agent Templates
- Channel Adapters
- 7 LLM Providers
- Architecture
- API Endpoints
- Dashboard
- Security
- Deployment
- Docker Environment Variables
- Development
- Benchmarks
- MCP Integration (for AI Agents)
- How It Works — Data Flow
- License
What is RustyHand?
RustyHand is an open-source Agent Operating System — not a chatbot framework, not a Python wrapper around an LLM. It is a full operating system for autonomous agents, built from scratch in Rust.
Traditional agent frameworks wait for you to type something. RustyHand runs autonomous agents that work for you — on schedules, 24/7, building knowledge graphs, monitoring targets, generating leads, managing social media, and reporting results directly to your Telegram chat.
Telegram-First Autonomous Agent
Telegram is the primary interface for RustyHand agents. Your agent can:
The entire system compiles to a single ~32MB binary. One install, one command, your agents are live.
Installation
One-liner (Linux / macOS / WSL)
curl -fsSL https://raw.githubusercontent.com/ginkida/rustyhand/main/scripts/install.sh | sh
Environment variables:
RUSTY_HAND_INSTALL_DIR— custom install path (default:~/.rustyhand/bin)RUSTY_HAND_VERSION— pin a specific version tag
Windows (PowerShell)
irm https://raw.githubusercontent.com/ginkida/rustyhand/main/scripts/install.ps1 | iex
From source
git clone https://github.com/ginkida/rustyhand.git
cd rustyhand
cargo build --release -p rusty-hand-cli
# Binary: target/release/rustyhand (or rustyhand.exe on Windows)
Requires Rust 1.75+ (stable). The rust-toolchain.toml in the repo will auto-select the right toolchain.
Docker
docker compose up --build
# Dashboard at http://localhost:4200
Or run directly with env vars (no config.toml needed):
docker run -p 4200:4200 \
-e ANTHROPIC_API_KEY=your-key \
-e RUSTYHAND_API_KEY=my-secret-bearer-token \
-v rustyhand-data:/data \
ghcr.io/ginkida/rustyhand:latest
All configuration can be set via RUSTYHAND_* environment variables — see Docker Environment Variables.
Quick Start
Option Zero: Try it in 30 seconds, no API key
The fastest possible first run, no credentials, no configuration:
git clone https://github.com/ginkida/rustyhand
cd rustyhand
cargo run --release -- start
# In another tab: open http://localhost:4200
The dashboard banner will read "DEMO MODE — running on the deterministic
mock driver." Spawn an agent, send a message, watch the agent loop run,
session grow, audit log fill up. Every reply is [mock] <your message> —
unmistakably demo, but the full pipeline (sessions, persistence, workflows,
cron jobs) is real. Set ANTHROPIC_API_KEY (or any of 26 other supported
providers' env vars) and restart for real LLM responses.
Option A: Docker (fastest)
docker run -d --name rustyhand \
-p 4200:4200 \
-e ANTHROPIC_API_KEY=your-key \
-v rustyhand-data:/data \
ghcr.io/ginkida/rustyhand:latest
# Dashboard: http://localhost:4200
# API: http://localhost:4200/api/health
To secure the API with a bearer token:
docker run -d --name rustyhand \
-p 4200:4200 \
-e ANTHROPIC_API_KEY=your-key \
-e RUSTYHAND_API_KEY=my-secret-token \
-v rustyhand-data:/data \
ghcr.io/ginkida/rustyhand:latest
# Now all API calls require: -H "Authorization: Bearer my-secret-token"
See Docker Environment Variables for all options.
Option B: From binary
# 1. Initialize — creates ~/.rustyhand/ and walks you through provider setup
rustyhand init
# 2. Start the daemon (API + kernel)
rustyhand start
# Dashboard is live at http://localhost:4200
# 3. Chat with the default agent
rustyhand chat
# 4. Spawn a pre-built agent
rustyhand agent new coder
# 5. Send a one-shot message
rustyhand message researcher "What are the emerging trends in AI agent frameworks?"
# 6. Launch the interactive TUI dashboard
rustyhand tui
# 7. Run diagnostics
rustyhand doctor
Telegram Setup
Telegram is the primary channel for interacting with RustyHand agents. Setup takes 2 minutes:
1. Create a Telegram Bot
- Message @BotFather on Telegram
- Send
/newbot, follow prompts, get your bot token - Set the token:
export TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
2. Configure RustyHand
# ~/.rustyhand/config.toml
[channels.telegram]
bot_token_env = "TELEGRAM_BOT_TOKEN"
allowed_users = [] # Empty = allow anyone. Set [123456] for specific user IDs.
3. Start and chat
rustyhand start
# Open Telegram, message your bot
# /agents — list agents
# /agent assistant — select an agent
# Send text, photos, voice messages — the agent handles all of them
What your agent can do in Telegram
You: [send a voice message]
Agent: [auto-transcribes via Whisper, processes your request]
You: [send a photo]
Agent: [auto-describes the image, responds based on what it sees]
You: "Search for Rust 2024 edition changes"
Agent: ⚙️ web_search...
✅ web_search
Here are the key changes in Rust 2024...
Agent: ⚠️ Agent "coder" wants to execute:
`shell_exec: rm -rf /tmp/cache`
[✅ Approve] [❌ Reject] ⏱️ 60s
You: [click ✅ Approve]
Agent: Done! Cache cleared.
Autonomous mode
Agents with schedule_mode = "continuous" or "periodic" run in the background and push results to your Telegram chat automatically — no prompting needed.
# agent.toml
[schedule]
mode = "periodic"
cron = "0 9 * * *" # Every day at 9 AM
The agent wakes up, performs its task, and sends the result to the last Telegram chat it was used in.
Configuration
RustyHand can be configured in two ways:
- Config file (
~/.rustyhand/config.toml) — for binary installs - Environment variables (
RUSTYHAND_*) — for Docker, see Docker Environment Variables
API Authentication
When api_key is set, all endpoints (except /api/health) require a Bearer token:
# In config.toml:
api_key = "my-secret-token"
# Or via env var (Docker):
RUSTYHAND_API_KEY=my-secret-token
# Clients must include the header:
curl -H "Authorization: Bearer my-secret-token" http://localhost:4200/api/agents
Without api_key, the API is open (fine for local development).
Config file
Location: ~/.rustyhand/config.toml
# API server settings
api_key = "your-bearer-token" # Recommended for non-localhost access
api_listen = "127.0.0.1:4200" # HTTP bind address
[default_model]
provider = "anthropic" # anthropic, kimi, deepseek, zhipu, minimax, openrouter, ollama
model = "claude-sonnet-4-6" # Model identifier
api_key_env = "ANTHROPIC_API_KEY" # Env var holding the API key
# base_url = "https://api.anthropic.com" # Optional: override endpoint
[memory]
decay_rate = 0.05 # Memory confidence decay
# sqlite_path = "~/.rustyhand/data/rustyhand.db"
[network]
listen_addr = "127.0.0.1:4200" # RHP P2P listen address
# shared_secret = "" # Required for P2P authentication
# Session compaction (LLM-based context management)
[compaction]
threshold = 80 # Compact when messages exceed this count
keep_recent = 20 # Keep this many recent messages
max_summary_tokens = 1024
# Usage display in chat responses
# usage_footer = "Full" # Off, Tokens, Cost, Full
# Channel adapters (tokens via env vars)
[telegram]
bot_token_env = "TELEGRAM_BOT_TOKEN"
allowed_users = [] # Empty = allow all
[discord]
bot_token_env = "DISCORD_BOT_TOKEN"
# guild_ids = []
[slack]
bot_token_env = "SLACK_BOT_TOKEN"
app_token_env = "SLACK_APP_TOKEN"
# MCP server connections
[[mcp_servers]]
name = "filesystem"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
Environment variables
Copy .env.example to ~/.rustyhand/.env and fill in the keys you need:
# LLM providers — set ANY key and RustyHand auto-detects the provider.
# Priority order: Anthropic → Kimi → DeepSeek → Zhipu → MiniMax → OpenRouter.
ANTHROPIC_API_KEY=sk-ant-... # Claude Opus / Sonnet / Haiku (default)
KIMI_API_KEY=sk-kimi-... # Kimi Code — Anthropic-compat, 256K ctx
DEEPSEEK_API_KEY=sk-... # DeepSeek V4 Flash / V4 Pro (V3/R1 legacy, deprecated 2026-07-24)
ZHIPU_API_KEY=... # Zhipu GLM-4.6
MINIMAX_API_KEY=eyJ... # MiniMax M1 / M2.7 (1M context)
OPENROUTER_API_KEY=sk-or-... # Universal gateway (GPT/Gemini/Grok/etc.)
# Local LLM — no key needed, just run `ollama serve`
# (Base URL defaults to http://localhost:11434/v1 — override only if needed)
# Embedding-only upstreams (independent of LLM provider)
VOYAGE_API_KEY=pa-... # Voyage AI (voyage-3-lite, code, legal, ...)
# OPENAI_API_KEY can also be used for text-embedding-3-* — not for LLM completion.
# Channel tokens
TELEGRAM_BOT_TOKEN=123456:ABC-...
DISCORD_BOT_TOKEN=...
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
# Log level
RUST_LOG=info
# RUST_LOG=rusty_hand=debug # Debug RustyHand only
Manage config from the CLI
rustyhand config show # Print current config
rustyhand config edit # Open in $EDITOR
rustyhand config get default_model.provider # Read a key
rustyhand config set default_model.provider kimi # Switch provider
rustyhand config set-key kimi # Interactively save API key
rustyhand config test-key kimi # Verify connectivity
CLI Reference
Core commands
Agents
Channels
Models
Skills
Workflows & scheduling
Integrations (MCP)
Security & vault
Other
All list/status commands support --json for scripting.
Autonomous Templates
RustyHand ships autonomous templates as agent presets in the dashboard. They are not a separate runtime entity: each template creates a normal agent, and you can optionally attach a cron schedule during creation.
Each autonomous template bundles:
- Agent preset — model, profile, prompt, and capabilities
- Schedule defaults — suggested cron expression and trigger message
- Operational playbook — multi-phase prompt for recurring work
- Guardrails — approval and tool constraints where needed
Bundled Autonomous Templates
Use the dashboard to launch one: Agents → Templates or Create Agent → enable schedule.
40 Pre-built Agent Templates
Spawn any template with rustyhand agent new <name>:
Agent manifest format (agent.toml)
name = "hello-world"
version = "0.1.0"
description = "A friendly greeting agent"
author = "rusty-hand"
module = "builtin:chat"
[model]
provider = "anthropic"
model = "claude-sonnet-4-6"
max_tokens = 4096
temperature = 0.6
system_prompt = """Your system prompt here..."""
[resources]
max_llm_tokens_per_hour = 100000
[capabilities]
tools = ["file_read", "file_list", "web_fetch", "web_search", "memory_store", "memory_recall"]
network = ["*"]
memory_read = ["*"]
memory_write = ["self.*"]
agent_spawn = false
Channel Adapters
RustyHand ships three messaging adapters — the ones whose APIs work without a public webhook URL, which is what most users actually run:
- Telegram — long-polling Bot API (
@BotFathertoken). - Discord — Gateway WebSocket (Developer Portal bot token).
- Slack — Socket Mode (
xapp-app token +xoxb-bot token).
Each adapter supports per-channel model overrides, DM/group policies, rate limiting, and output formatting.
v0.7.4 and earlier shipped 38 adapters (Matrix, WhatsApp, Signal, Teams, IRC, ...). They were dropped in v0.7.5 — most were webhook-only and broken in typical localhost / home-Docker setups, and many were sprint fillers without real usage. Pin to v0.7.4 if you need them, or open an issue and we'll discuss a route.
Channel policies
Configure each channel under the [channels.*] table in config.toml:
[channels.telegram]
bot_token_env = "TELEGRAM_BOT_TOKEN"
allowed_users = [123456789] # Restrict to specific users
default_agent = "assistant" # Route inbound messages here
[channels.telegram.overrides]
dm_policy = "Respond" # Respond | AllowedOnly | Ignore
group_policy = "MentionOnly" # All | MentionOnly | CommandsOnly | Ignore
output_format = "TelegramHtml" # Markdown | TelegramHtml | SlackMrkdwn | PlainText
Zero-config Telegram on Docker
Since v0.7.10, the Docker entrypoint generates default_agent = "assistant"
under each [channels.*] section automatically (override with the
RUSTYHAND_{TELEGRAM,DISCORD,SLACK}_DEFAULT_AGENT env var, or set it to
an empty string to leave it blank). The bundled assistant manifest uses
provider = "anthropic", so a fresh container with ANTHROPIC_API_KEY
plus a bot token replies to the first message without any extra config or
rustyhand init step.
If the router can't resolve a target at message time (no
default_agent, no bindings, no direct routes), the bridge tries to
auto-route to a running agent first, then to spawn one of the bundled
meta-agents (assistant → coordinator → coder). The user only sees
a config-pointing error message when every fallback fails.
7 LLM Providers
RustyHand v0.7.0 ships with a deliberately lean set of 7 providers, driven by 2 wire protocols (Anthropic Messages API + OpenAI-compatible Chat Completions). Anthropic and Kimi Code are the two first-class coding providers:
Default auto-detect order: Anthropic → Kimi → DeepSeek → Zhipu → MiniMax → OpenRouter. Set whichever key you have; RustyHand picks the first one found.
Prefer Kimi? Set KIMI_API_KEY and auto-detect will route to the stable kimi-for-coding model ID on the Kimi Code endpoint (api.kimi.com/coding); Moonshot maps that ID to the current Kimi Code backend. Change anytime via rustyhand config set default_model.provider <name>.
v0.6.x shipped 27 providers (OpenAI, Gemini, Groq, xAI, Copilot, Mistral, Together, Fireworks, Perplexity, Cohere, AI21, Cerebras, SambaNova, HuggingFace, Replicate, vLLM, LM Studio, Moonshot, Qwen, Qianfan, Bedrock). They were removed in v0.7.0 — use
openrouterto reach any of those models through one gateway.
Features:
- Intelligent routing with task complexity scoring
- Automatic fallback between providers
- Per-model pricing and cost tracking
- Per-agent budget limits
Embedding providers
Vector embeddings power semantic memory recall. The catalog is independent of
the LLM provider list — OPENAI_API_KEY is still usable for text-embedding-3-*
even though OpenAI is not a first-class LLM provider in v0.7.0.
Auto-detected at boot (first available wins):
Configure explicitly in config.toml:
[memory]
embedding_provider = "voyage" # or "openai", "ollama", "<custom>"
embedding_api_key_env = "VOYAGE_API_KEY"
Or let RustyHand auto-detect: it probes Voyage → OpenAI → Ollama at boot and uses the first available provider. Falls back to text search (SQLite LIKE) when no embedding driver is found.
rustyhand models list # Browse all models
rustyhand models list --provider kimi # Filter by provider
rustyhand models set claude-sonnet # Set default model
Architecture
10 Rust crates with a modular kernel design:
rusty-hand-types Core types, traits, config, taint tracking, Ed25519 manifest signing
|
+-- rusty-hand-memory SQLite persistence, vector embeddings (Voyage/OpenAI/Ollama), session compaction
+-- rusty-hand-wire RHP P2P protocol (JSON-RPC over TCP, HMAC-SHA256 auth)
+-- rusty-hand-channels Telegram + Discord + Slack adapters with rate limiting
+-- rusty-hand-skills Skill system + ClawHub marketplace
+-- rusty-hand-extensions 25 MCP integrations, AES-256-GCM credential vault, OAuth2
|
+-- rusty-hand-runtime Agent loop, 2 LLM drivers (Anthropic + OpenAI-compat), 53+ tools, WASM sandbox, MCP, A2A
|
+-- rusty-hand-kernel Orchestration: lifecycle, scheduling, metering, RBAC, workflows
|
+-- rusty-hand-api Axum HTTP daemon, 120+ endpoints, WebSocket, SSE, OpenAI-compat
|
+-- rusty-hand-cli CLI binary + TUI dashboard (ratatui)
Key internals
API Endpoints
Default: http://127.0.0.1:4200. All endpoints return JSON. Authenticate with Authorization: Bearer <api_key> when api_key is set in config.
Health & status
Agents
Budget
Network & P2P
A2A (Agent-to-Agent)
OpenAI-compatible
Drop-in replacement for OpenAI API:
curl -X POST http://localhost:4200/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "researcher",
"messages": [{"role": "user", "content": "Analyze Q4 market trends"}],
"stream": true
}'
Full REST/WS/SSE endpoints cover agents, memory, workflows, channels, models, skills, sessions, approvals, triggers, crons, security, and more (120+ total).
Dashboard
The web dashboard is served at http://localhost:4200 when the daemon is running. Since v0.7.45 it's a React 18 control panel (industrial-rust palette, 15 pages); React + ReactDOM UMD bundles and the precompiled JSX are inlined into the binary at compile time, so the dashboard ships single-binary like before — no CDN, no node_modules at runtime.
Sections
Other niceties: ⌘K / Ctrl-K command palette, toast notifications, tweaks fab (theme dark/light, accent rust/copper/amber/forest/electric, density), AuthGate + LoginScreen for remote deployments, error boundary so a component crash shows a recovery card instead of a blank #root.
Maintaining the panel
JSX sources live in crates/rusty-hand-api/static/js/panel/src/*.jsx; compiled outputs sit one level up at static/js/panel/*.js. After editing JSX, recompile via:
cd crates/rusty-hand-api/static/js/panel/src && ./build.sh
The build needs Node + esbuild (npm i -g esbuild or use ESBUILD="npx --yes [email protected]" ./build.sh). The Rust build itself doesn't touch Node — it include_str!s the .js files. A build.rs checks JSX/JS mtimes and emits a cargo:warning= if you forget to recompile.
Tests pinning the panel contract:
tests/panel_dashboard_test.rs— 10 tests asserting HTML response, React inlined, every page component + endpoint wired, no Alpine residue, bundle size 100 KB ≤ x ≤ 1 MBtests/panel_jsx_smoke.rs— runs the compiled bundle under Node with a React shim and asserts every page component instantiates without throwing (catches JSX runtime errors that string-match tests miss)
Security
16 independent security layers — defense in depth, no single point of failure.
Deployment
Systemd
A service file is provided in deploy/rustyhand.service:
sudo cp deploy/rustyhand.service /etc/systemd/system/rustyhand.service
# Edit ExecStart path and user as needed
sudo systemctl daemon-reload
sudo systemctl enable --now rustyhand
The service includes security hardening: NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, and resource limits.
Docker Environment Variables
The Docker entrypoint generates config.toml from environment variables automatically — no config file needed. If you mount your own config.toml, env vars are ignored.
Set RUSTYHAND_FORCE_ENV_CONFIG=1 to always regenerate config from env vars (overrides mounted file).
Core
LLM Provider
LLM API Keys (pass through to agents)
Budget
Memory & Embeddings
Channels
Other
Example: full Docker run
docker run -d --name rustyhand \
-p 4200:4200 \
-e RUSTYHAND_API_KEY=my-secret-token \
-e RUSTYHAND_PROVIDER=anthropic \
-e RUSTYHAND_MODEL=claude-sonnet-4-6 \
-e RUSTYHAND_MODEL_KEY_ENV=ANTHROPIC_API_KEY \
-e ANTHROPIC_API_KEY=sk-ant-... \
-e RUSTYHAND_BUDGET_DAILY=5.0 \
-e TELEGRAM_BOT_TOKEN=123456:ABC... \
-v rustyhand-data:/data \
ghcr.io/ginkida/rustyhand:latest
Cross-compilation
Cross-compilation to aarch64-unknown-linux-gnu is supported via Cross.toml:
cross build --release --target aarch64-unknown-linux-gnu -p rusty-hand-cli
Development
Prerequisites
- Rust 1.75+ (stable) —
rust-toolchain.tomlauto-selects - Components:
rustfmt,clippy(included in toolchain)
Build & verify
# Compile all crates (use --lib if the daemon binary is locked)
cargo build --workspace --lib
# Run all tests (1,481 as of v0.7.10)
cargo test --workspace
# Lint — must be 0 warnings
cargo clippy --workspace --all-targets -- -D warnings
# Format check
cargo fmt --all -- --check
Release build
cargo build --release -p rusty-hand-cli
# Binary: target/release/rustyhand (~32 MB)
# LTO + single codegen unit + stripped symbols + opt-level 3
Project structure
rustyhand/
Cargo.toml # Workspace manifest (10 member crates)
Cargo.lock
rust-toolchain.toml # Rust stable + rustfmt + clippy
.env.example # Environment variable template
Dockerfile # Multi-stage build
docker-compose.yml
Cross.toml # Cross-compilation config
agents/ # 37 pre-built agent templates (agent.toml each)
deploy/ # systemd service, Docker scripts
scripts/ # install.sh, install.ps1
crates/
rusty-hand-types/ # Core types (config.rs is the master config struct)
rusty-hand-memory/ # SQLite + vector embeddings (Voyage AI, OpenAI, Ollama)
rusty-hand-runtime/ # Agent loop + LLM drivers + tools + sandbox
rusty-hand-wire/ # RHP P2P protocol
rusty-hand-api/ # Axum HTTP server + routes + dashboard
src/
server.rs # Router setup, middleware, AppState
routes.rs # All API endpoint handlers (~7600 LOC)
static/
index_body.html # Dashboard SPA (Alpine.js)
index_head.html # CSS + fonts
rusty-hand-kernel/ # Central kernel (~5300 LOC, 40+ fields)
rusty-hand-cli/ # CLI + TUI binary
rusty-hand-channels/ # Telegram + Discord + Slack adapters
rusty-hand-skills/ # Skill system + ClawHub + OpenClaw compat
rusty-hand-extensions/ # MCP + vault + OAuth2
Key files for contributors
Common gotchas
rustyhand.exemay be locked if the daemon is running — use--libflag or kill daemon first- New config fields need: struct field +
#[serde(default)]+Defaultimpl entry - New routes must be registered in
server.rsrouter AND implemented inroutes.rs - Dashboard tabs need both HTML in
index_body.htmland JS data/methods AgentLoopResultfield is.responsenot.response_text- CLI daemon command is
start(notdaemon)
Benchmarks
All data from official documentation and public repositories — April 2026.
MCP Integration (for AI Agents)
RustyHand exposes itself as an MCP server over stdio, giving any MCP-compatible AI agent (Claude Desktop, Cursor, Windsurf, Claude Code, etc.) full control over the agent OS.
Setup
Add to your MCP client config (e.g. claude_desktop_config.json):
{
"mcpServers": {
"rustyhand": {
"command": "rustyhand",
"args": ["mcp"]
}
}
}
That's it. The AI agent now has 30+ tools to manage the entire system.
Available MCP Tools
Plus: every running agent automatically becomes an MCP tool named rusty_hand_agent_{name}, so the AI agent can delegate tasks to specialized RustyHand agents.
Example: AI agent workflow via MCP
An AI agent (Claude, GPT, etc.) can autonomously:
rustyhand_agent_spawn— create a researcher agent with web accessrustyhand_agent_message— ask it to research a topicrustyhand_agent_spawn— create a writer agentrustyhand_agent_message— pass research results to the writerrustyhand_cron_create— schedule the researcher to update weeklyrustyhand_budget_status— check how much it all cost
Why an AI agent should use RustyHand
- Delegate work — spawn specialized agents instead of doing everything yourself
- Persistent memory — agents remember context across sessions (vector search + knowledge graph)
- 3 channels — reach users on Telegram, Discord, and Slack without building integrations
- Budget control — set spending limits so agents can't run up costs
- 7 LLM providers — Anthropic, Kimi, DeepSeek, Zhipu, MiniMax, OpenRouter, Ollama — plus OpenRouter gateway to any other model
- Autonomous scheduling — cron jobs run agents on schedule, no human needed
- 60 bundled skills — instant expertise in Kubernetes, AWS, PostgreSQL, Git, Python, etc.
- Approval gates — dangerous actions require human approval before executing
- Audit trail — every action is logged in a Merkle hash chain
REST API for Programmatic Access
When api_key is configured, add -H "Authorization: Bearer <token>" to all requests (except /api/health).
# Health check (always public)
curl http://localhost:4200/api/health
# List agents
curl http://localhost:4200/api/agents
# Spawn an agent
curl -X POST http://localhost:4200/api/agents \
-H "Content-Type: application/json" \
-d '{"manifest_toml": "name = \"my-agent\"\nmodule = \"builtin:chat\"\n[model]\nprovider = \"kimi\"\nmodel = \"kimi-for-coding\"\napi_key_env = \"KIMI_API_KEY\"\nsystem_prompt = \"You are a helpful assistant.\""}'
# Send a message (triggers LLM call, returns full response)
curl -X POST http://localhost:4200/api/agents/{id}/message \
-H "Content-Type: application/json" \
-d '{"message": "Hello, what can you do?"}'
# Stream a response (SSE)
curl -N -X POST http://localhost:4200/api/agents/{id}/message/stream \
-H "Content-Type: application/json" \
-d '{"message": "Write a haiku about Rust"}'
# OpenAI-compatible endpoint (drop-in replacement for any OpenAI client)
curl -X POST http://localhost:4200/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "coder", "messages": [{"role": "user", "content": "Fix this bug"}]}'
# Budget status
curl http://localhost:4200/api/budget
# Memory — store and recall
curl -X PUT http://localhost:4200/api/memory/agents/{id}/kv/project_name \
-H "Content-Type: application/json" \
-d '{"value": "rustyhand"}'
curl http://localhost:4200/api/memory/agents/{id}/kv/project_name
# With auth enabled:
curl -H "Authorization: Bearer my-secret-token" http://localhost:4200/api/agents
SDKs for Python and JavaScript are included in sdk/python/ and sdk/javascript/.
How It Works — Data Flow
User message (CLI / API / Telegram / Discord / ...)
|
v
[Channel Adapter] --- converts platform message to unified ChannelMessage
|
v
[Kernel Router] --- resolves target agent via bindings/broadcast rules
|
v
[Agent Registry] --- looks up AgentManifest + Session
|
v
[Agent Loop] (rusty-hand-runtime/src/agent_loop.rs)
|
+-- 1. Recall memories (vector similarity via Voyage/OpenAI/Ollama, or text LIKE)
+-- 2. Build system prompt (SOUL.md + USER.md + TOOLS.md + MEMORY.md + recalled context)
+-- 3. Call LLM (driver: Anthropic or OpenAI-compat)
| |-- retry on rate limit (3x, exponential backoff)
| |-- fallback to next provider on failure
| |-- model routing by complexity (simple/medium/complex)
+-- 4. If tool_use → execute tool → append result → goto 3 (max 50 iterations)
| |-- built-in: file_read, file_write, shell_exec, web_search, web_fetch,
| | memory_store, memory_recall, agent_send, agent_spawn, browser_*
| |-- MCP tools: GitHub, Notion, Slack, PostgreSQL, ... (25+ integrations)
| |-- skills: 60 prompt-only + Python/WASM/Node.js executable skills
+-- 5. Extract response text + reply directives
|
v
[Metering] --- record token usage + cost, check budget limits
|
v
[Session Save] --- persist messages to SQLite, append daily memory log
|
v
[Channel Adapter] --- format response for platform, send back
|
v
User receives response
Key Types
Extending RustyHand
Add a new LLM provider:
- Add base URL constant to
crates/rusty-hand-types/src/model_catalog.rs - Add match arm to
crates/rusty-hand-runtime/src/drivers/mod.rsprovider_defaults() - Most providers use the OpenAI-compatible driver — no new driver code needed
Add a new API endpoint:
- Add handler function in
crates/rusty-hand-api/src/routes.rs - Register route in
crates/rusty-hand-api/src/server.rsbuild_router() - Add request/response types in
crates/rusty-hand-api/src/types.rsif needed
Add a new config field:
- Add field with
#[serde(default)]to struct incrates/rusty-hand-types/src/config.rs - Add default value to the
Defaultimpl - Add to custom
Debugimpl (redact secrets)
Add a new built-in tool:
- Add
ToolDefinitiontobuiltin_tool_definitions()incrates/rusty-hand-runtime/src/tool_runner.rs - Add execution handler in the same file's
execute_tool()match
Add a new channel adapter:
- Create
crates/rusty-hand-channels/src/<name>.rs - Add
pub mod <name>tocrates/rusty-hand-channels/src/lib.rs - Wire into
crates/rusty-hand-api/src/channel_bridge.rs
License
MIT — use it however you want.
Links
Acknowledgments
RustyHand is a fork of OpenFang, originally built by Jaber at RightNow.