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:

  • rusty welcome agent (chat-ready)
  • demo-pipeline workflow (2-step sample, click to run)
  • sample agent-spawn trigger
  • demo-daily-ping cron 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, or RUSTYHAND_DISABLE_DEMO_MODE=1 to 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.toml editor.
  • 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 guardrailsGET /api/security reports the real runtime posture (exec mode, approval policy, per-channel gating); flip to allowlist/deny with RUSTYHAND_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?

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:

Capability How it works
See photos Auto-describes images via vision API
Hear voice Auto-transcribes voice messages via Whisper
Receive files Downloads documents, forwards to agent
Send files/photos/voice Sends generated content back to chat
Ask permission Inline keyboard buttons (Approve/Reject) pushed automatically
Show progress Real-time tool-use updates: "⚙️ web_search..." → "✅ Done"
Report autonomously Background tasks push results to your chat without prompting
72 built-in tools Shell, web/news search, browser (wait, JS exec, scroll, download), RAG, knowledge graph, file ops, image vision
Markdown formatting Bold, italic, code blocks render natively in Telegram
Reply threading Responses reply to the user's message for clean conversation flow
Sticker/GIF/Location Agent understands stickers, animations, and shared locations

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

  1. Message @BotFather on Telegram
  2. Send /newbot, follow prompts, get your bot token
  3. 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

Command Description
rustyhand init Initialize ~/.rustyhand/ and default config
rustyhand start Start the daemon (API server + kernel)
rustyhand stop Stop the running daemon
rustyhand status [--json] Show kernel status
rustyhand health [--json] Quick daemon health check
rustyhand doctor [--repair] Run diagnostic checks
rustyhand tools [--json] List all 72 built-in tools, grouped by category (offline)
rustyhand tui Launch interactive TUI dashboard
rustyhand dashboard Open web dashboard in browser
rustyhand chat [agent] Quick chat with an agent
rustyhand message <agent> <text> Send a one-shot message
rustyhand logs [--follow] [--lines N] Tail the log file
rustyhand reset [--confirm] Reset local config and state

Agents

Command Description
rustyhand agent new [template] Spawn from a template (interactive picker if omitted)
rustyhand agent templates [--json] List the bundled agent templates (offline)
rustyhand agent spawn <manifest.toml> Spawn from a manifest file
rustyhand agent list [--json] List running agents
rustyhand agent chat <id> Interactive chat with an agent by ID
rustyhand agent kill <id> Kill an agent

Channels

Command Description
rustyhand channel list List configured channels and status
rustyhand channel setup [name] Interactive channel setup wizard
rustyhand channel test <name> Send a test message
rustyhand channel enable <name> Enable a channel
rustyhand channel disable <name> Disable a channel

Models

Command Description
rustyhand models list [--provider X] Browse available models
rustyhand models aliases Show model shorthand names
rustyhand models providers List providers and their auth status
rustyhand models set [model] Set the default model

Skills

Command Description
rustyhand skill install <source> Install from ClawHub, local path, or git URL
rustyhand skill list List installed skills
rustyhand skill search <query> Search ClawHub marketplace
rustyhand skill remove <name> Remove a skill
rustyhand skill create Scaffold a new skill

Workflows & scheduling

Command Description
rustyhand workflow list List workflows
rustyhand workflow create <file.json> Create from JSON
rustyhand workflow run <id> <input> Run a workflow
rustyhand trigger list [--agent-id X] List event triggers
rustyhand trigger create <agent-id> <pattern-json> Create a trigger
rustyhand cron list List scheduled jobs

Integrations (MCP)

Command Description
rustyhand add <name> [--key TOKEN] Install an integration (e.g., github, notion)
rustyhand remove <name> Remove an integration
rustyhand integrations [query] List / search integrations

Security & vault

Command Description
rustyhand vault init Initialize the credential vault (AES-256-GCM)
rustyhand vault set <key> Store a credential
rustyhand vault list List stored keys (values hidden)
rustyhand vault remove <key> Remove a credential
rustyhand security audit View the audit trail
rustyhand security rbac Manage access control

Other

Command Description
rustyhand mcp Start MCP server over stdio
rustyhand sessions [agent] List conversation sessions
rustyhand approvals list List pending approval requests
rustyhand qr Generate device pairing QR code
rustyhand onboard Interactive onboarding wizard
rustyhand completion <shell> Generate shell completions (bash/zsh/fish/powershell)
rustyhand new skill|integration Scaffold a new skill or integration

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

Template What It Does
GitHub Monitor Monitors repositories, runs tests, detects regressions, and files issues on a schedule.
Web Researcher Runs recurring research sweeps, cross-references sources, and produces structured reports.
Content Clipper Processes long-form video into short clips with captions and packaging.
Lead Generator Discovers and enriches qualified leads on a recurring schedule.
Intel Collector Monitors targets, detects changes, and updates a living knowledge base.
Predictor Collects signals, updates forecasts, and tracks prediction accuracy.
Twitter Manager Creates, schedules, and reviews social content with approval controls.
Web Browser Executes recurring browser automation tasks with strict purchase approval gates.

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>:

Template Description
analyst Data analysis and reporting
api-monitor API endpoint monitoring
architect System design and architecture
assistant General-purpose assistant
capability-builder Meta-agent — writes new skills at runtime via privileged skill_install tool
ci-monitor CI/CD pipeline monitoring
code-reviewer Code review and feedback
coder Software development
coordinator Meta-agent — delegates work across other agents via agent_send
customer-support Customer support
dag-monitor DAG/workflow monitoring
data-scientist Data science and ML
db-reporter Database reporting
debugger Bug investigation
devops-lead DevOps and infrastructure
diagnostic Meta-agent — read-only observability (self-history, metrics, audit log)
doc-writer Documentation
email-assistant Email drafting and management
health-tracker Health and fitness tracking
hello-world Starter agent for new users
home-automation Smart home control
legal-assistant Legal document review
log-analyzer Log analysis and alerting
meeting-assistant Meeting notes and follow-ups
ops Operations management
orchestrator Multi-agent orchestration
personal-finance Financial tracking
planner Project planning
recruiter Recruiting and screening
researcher Research and analysis
sales-assistant Sales support
security-auditor Security analysis
slack-notifier Slack notification automation
social-media Social media management
test-engineer Testing and QA
translator Multi-language translation
travel-planner Travel planning
tutor Education and tutoring
weekly-digest Weekly summary reports
writer Content writing

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 (@BotFather token).
  • 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 (assistantcoordinatorcoder). 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:

Provider Env var Role
Anthropic (default) ANTHROPIC_API_KEY Claude Opus/Sonnet/Haiku — best-in-class tool use + extended thinking
Kimi (Moonshot) KIMI_API_KEY Kimi Code — Anthropic-compatible, 256K ctx, vision, reasoning
DeepSeek DEEPSEEK_API_KEY V4 Flash + V4 Pro — fast & reasoning (V3/R1 deprecated 2026-07-24). Also exposes an Anthropic-compatible endpoint at /anthropic
Zhipu GLM ZHIPU_API_KEY GLM-4.6 — Chinese frontier
MiniMax MINIMAX_API_KEY M1/M2 — 1M context
OpenRouter OPENROUTER_API_KEY Universal gateway — any model via one key
Ollama (no key) Local on localhost:11434

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 openrouter to 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):

Provider Models Key required
Voyage AI voyage-3, voyage-3-lite, voyage-code-3, voyage-finance-2, voyage-law-2, voyage-multilingual-2 VOYAGE_API_KEY
OpenAI (embedding-only) text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002 OPENAI_API_KEY
Ollama nomic-embed-text, all-MiniLM-L6-v2, mxbai-embed-large No
Any OpenAI-compat endpoint whatever the server exposes provider-specific

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

Concept Implementation
Agent loop rusty-hand-runtime — iterative LLM call → tool execution → response cycle
Kernel RustyHandKernel struct (40+ fields) — central orchestration for all subsystems
AppState Bridges kernel to HTTP routes via Arc<RustyHandKernel> in Axum state
Sandbox WASM (wasmtime) with fuel metering + epoch interruption + watchdog thread
Memory SQLite + vector embeddings (Voyage AI, OpenAI, Ollama) for semantic search + knowledge graph
Metering Per-agent token/cost tracking with budget enforcement and alerts
P2P RHP (RustyHand Protocol) — JSON-RPC over TCP, Ed25519 identity, nonce-based auth
A2A Agent-to-Agent protocol for cross-instance agent communication

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

Endpoint Method Description
/api/health GET Health check
/api/status GET Full kernel status

Agents

Endpoint Method Description
/api/agents GET List all agents
/api/agents POST Spawn a new agent
/api/agents/{id} GET Agent details
/api/agents/{id} DELETE Kill an agent
/api/agents/{id}/message POST Send message (triggers LLM)

Budget

Endpoint Method Description
/api/budget GET Global budget status
/api/budget PUT Update budget settings
/api/budget/agents GET Per-agent cost ranking
/api/budget/agents/{id} GET Single agent budget detail

Network & P2P

Endpoint Method Description
/api/network/status GET RHP network status
/api/peers GET Connected peers

A2A (Agent-to-Agent)

Endpoint Method Description
/api/a2a/agents GET External A2A agents
/api/a2a/discover POST Discover agent at URL
/api/a2a/send POST Send task to external agent
/api/a2a/tasks/{id}/status GET Check task status

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

Section What you see
Overview Live tiles (running agents, cost, audit entries, errors), recent activity, approvals waiting, demo-seed shortcuts, providers, audit chain head
Agents Spawn (template or custom), kill / restart, drawer with Info / Config / Identity / Activity tabs editing the agent's manifest live
Chat WebSocket streaming with HTTP fallback, markdown rendering, tool-trace cards, sessions list per agent
Workflows Visual step builder with HTML5 drag-and-drop reorder, fan-out / collect / conditional / loop modes, runs history, run-with-JSON-input
Automation Cron jobs (3 schedule × 3 action variants), triggers (9 pattern variants), toggle / run-now / delete
Channels Configure modal generated from /api/channels field metadata, test / reload / disconnect
Skills Custom install via inline editor, ClawHub browse + search + install, uninstall
Analytics Cost, requests, cache hit-rate, p95 latency, top agents, provider state, CSV export
Knowledge Live graph viz + mini-cypher query (source:foo relation:works_at depth:3 → POST /api/knowledge/query)
Memory Sessions list with label edit + delete, export to markdown, full backup / restore via /api/memory/{export,import}
Approvals Pending decisions with one-click approve / reject
Audit log Recent + chain-verify + JSON export
MCP servers Configured + connected MCP bridges from /api/mcp/servers
Network RHP P2P status + known peers (/api/network/status, /api/peers)
Bindings Agent → channel/trigger bindings (/api/bindings)
Settings Provider key set / delete / test, demo-mode info, build info

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 MB
  • tests/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.

# System Description
1 WASM Dual-Metered Sandbox Tool code runs in WebAssembly with fuel metering + epoch interruption. Watchdog kills runaway code.
2 Merkle Hash-Chain Audit Trail Every action cryptographically linked. Tamper with one entry and the chain breaks.
3 Taint Tracking Information flow labels propagate through execution — secrets tracked from source to sink.
4 Ed25519 Signed Manifests Agent identity and capabilities are cryptographically signed.
5 SSRF Protection Blocks private IPs, cloud metadata endpoints, DNS rebinding.
6 Secret Zeroization Zeroizing<String> auto-wipes API keys from memory when no longer needed.
7 RHP Mutual Auth HMAC-SHA256 nonce-based, constant-time verification for P2P.
8 Capability Gates Role-based access control — agents declare tools, kernel enforces.
9 Security Headers CSP, X-Frame-Options, HSTS, X-Content-Type-Options on every response.
10 Health Redaction Public health check returns minimal info. Full diagnostics require auth.
11 Subprocess Sandbox env_clear() + selective passthrough. Process tree isolation with cross-platform kill.
12 Prompt Injection Scanner Detects override attempts, data exfiltration patterns, shell injection in skills.
13 Loop Guard SHA256-based tool call loop detection with circuit breaker.
14 Session Repair 7-phase message history validation and automatic recovery.
15 Path Traversal Prevention Canonicalization with symlink escape prevention.
16 GCRA Rate Limiter Cost-aware token bucket rate limiting with per-IP tracking.

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

Env var Default Description
RUSTYHAND_API_KEY (none) Bearer auth token. When set, all API endpoints require Authorization: Bearer <token> header. Strongly recommended for non-local access.
RUSTYHAND_API_LISTEN 0.0.0.0:4200 HTTP bind address
RUSTYHAND_LOG_LEVEL info Log level: trace, debug, info, warn, error

LLM Provider

Env var Default Description
RUSTYHAND_PROVIDER anthropic LLM provider: anthropic, kimi, deepseek, zhipu, minimax, openrouter, ollama
RUSTYHAND_MODEL claude-sonnet-4-6 Model identifier
RUSTYHAND_MODEL_KEY_ENV ANTHROPIC_API_KEY Which env var holds the LLM API key
RUSTYHAND_MODEL_BASE_URL (auto) Override provider API endpoint
RUSTYHAND_FALLBACK_PROVIDER (none) Fallback provider if primary fails
RUSTYHAND_FALLBACK_MODEL (none) Fallback model
RUSTYHAND_FALLBACK_KEY_ENV (none) Env var for fallback API key

LLM API Keys (pass through to agents)

Env var Provider
`ANTHROPI