* feat(engine): LLM council via per-call model override in CodeAct Extend `llm_query()` and `llm_query_batched()` with a `model=` (and `models=` for the batched variant) keyword so CodeAct can route individual sub-queries to specific LLMs. The "LLM council" pattern becomes a skill — the agent broadcasts the same prompt across a parallel array of models and synthesizes the responses — with no new tool, dispatch path, or capability boundary. - Add `model: Option<String>` to `LlmCallConfig`; thread it through `LlmBridgeAdapter` onto `CompletionRequest.model` / `ToolCompletionRequest.model` so providers that honor per-request overrides (NEAR AI, Anthropic OAuth, GitHub Copilot, Bedrock) pick it up. Other providers fall back to their configured model. - `handle_llm_query` extracts a `model` arg; `handle_llm_query_batched` accepts either `model="..."` (broadcast) or `models=[...]` (parallel array, length-validated against `prompts`). - `__llm_complete__` host fn extracts `model` from explicit_config so the Python orchestrator can also forward it. - Update CodeAct preamble docs so the agent sees the new parameters. - Add `skills/llm-council/SKILL.md` with the council pattern, recommended NEAR AI model line-ups, and a synthesis example. Tests: 5 new scripting tests (model kwarg forwarding, default `None`, `models=` broadcast, single-`model=` broadcast, length-mismatch error) and 2 new bridge tests (config.model → CompletionRequest.model on both the no-tools and with-tools paths). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(engine): address llm-council review feedback Address three review comments on the LLM council PR: 1. Test coverage for the orchestrator entry point. Add two tests driving `handle_llm_complete` directly with explicit_config containing `model` (and a control case without it). Closes the "test through the caller, not just the helper" gap — the previous tests only exercised `handle_llm_query`, leaving the parallel `__llm_complete__` host fn path unverified. 2. Loud failure on non-string entries in `models=[...]`. Previously `models=[1, 2]` was silently coerced via `monty_to_string` to `["1", "2"]`. Now returns `TypeError` with the offending value, matching the existing length-mismatch handling style. 3. `None` slots in `models=[...]` are no longer backfilled by the singular `model=` kwarg. Each slot is authoritative: a `None` means "no override for this prompt" (use the configured default). Mixing the two would have been surprising — the docs now spell out the contract explicitly. Add a regression test that passes both `models=[None, "gpt-4o"]` and `model="claude-..."` and asserts the None slot stays None. Also update `skills/llm-council/SKILL.md` to default to a 4-model council of `anthropic/claude-opus-4-6`, `google/gemini-3-pro`, `zai-org/GLM-latest`, `openai/gpt-5.4`. Per-call provider errors already flow through the existing `Ok(Err(e))` arm as `"Error: ..."` strings — the batch never fails as a whole, so unavailable models just surface in their own slot. Tests: 4 new (2 orchestrator, 2 scripting), all 346 engine unit tests pass, zero clippy warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(engine): strict optional-string parsing for llm_query model= kwarg Address Copilot review on PR #2320. Four comments, all valid: 1 & 2. `model` and `single_model` were extracted via `extract_string_arg`, which calls `monty_to_string` — that coerces `MontyObject::None` to the literal string "None" and stringifies non-string values (ints become "1", etc.). So `llm_query(prompt="hi", model=None)` would silently route every call to a bogus model ID called "None". Add a strict `extract_optional_string_kwarg` helper that returns `Ok(None)` for missing/`None`, `Ok(Some(s))` for strings, and a `TypeError` for anything else. Use it in both `handle_llm_query` and `handle_llm_query_batched` for the `model=` kwarg. Regression tests cover: `model=None` → no override, `model=<int>` → TypeError, and the same two cases on the batched path. 3. The `models=` list-type error message said "list of strings" but we accept `None` entries. Updated to "list of str or None". 4. SKILL.md claimed the batched call "never raises". It does — for argument validation errors (wrong types, length mismatch). Clarified that per-model failures return as `"Error: ..."` strings, but argument validation still raises. Tests: 4 new regression tests, all 4687 main-crate and 358 engine tests pass, zero clippy warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(engine): positional args for llm_query_batched + correct provider docs Address two review comments from serrrfirat on PR #2320. 1. `llm_query_batched` silently dropped positional `context`/`model`/ `models` args. The documented signature is `llm_query_batched(prompts, context=None, model=None, models=None)`, but the extractors were hardcoded to kwargs only (`&[]` for args). A call like `llm_query_batched(prompts, None, "gpt-4o")` routed to the default model — silent contract violation. Thread the real `args` slice into each extractor with the documented positional indices: context=1, model=2, models=3. Positional `MontyObject::None` at any of those slots now correctly means "no override". Added 3 regression tests: - `llm_query_batched_honors_positional_context_and_model` - `llm_query_batched_honors_positional_models_list` - `llm_query_batched_positional_none_for_models_is_no_override` 2. SKILL.md claimed Bedrock honors per-request model overrides, but `bedrock.rs::complete()` unconditionally uses `self.current_model_id()` and ignores `request.model`. Also, the default 4-model prefixed lineup (`anthropic/...`, `google/...`, `openai/...`) only works on aggregator backends like NEAR AI — a direct Anthropic OAuth or Copilot provider honors `set_model` but can only switch between models within its own vendor. Rewrite the SKILL.md preamble with a provider capability table (dropping Bedrock from "honors it" and adding cross-vendor routing as a separate column), and add per-backend default lineups: NEAR AI (prefixed cross-vendor), Anthropic OAuth (Anthropic tiers only), Copilot (Copilot-exposed models). For backends that don't honor `model=` at all (Bedrock, raw OpenAI/Ollama/Tinfoil), the skill now instructs the agent to tell the user and fall back to a single-model answer. Tests: 3 new regression tests, all 4687 main-crate and 361 engine tests pass, zero clippy warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
IronClaw
Your secure personal AI assistant, always on your side
English | 简体中文 | Русский | 日本語 | 한국어
Philosophy • Features • Installation • Configuration • Security • Architecture
Philosophy
IronClaw is built on a simple principle: your AI assistant should work for you, not against you.
In a world where AI systems are increasingly opaque about data handling and aligned with corporate interests, IronClaw takes a different approach:
- Your data stays yours - All information is stored locally, encrypted, and never leaves your control
- Transparency by design - Open source, auditable, no hidden telemetry or data harvesting
- Self-expanding capabilities - Build new tools on the fly without waiting for vendor updates
- Defense in depth - Multiple security layers protect against prompt injection and data exfiltration
IronClaw is the AI assistant you can actually trust with your personal and professional life.
Features
Security First
- WASM Sandbox - Untrusted tools run in isolated WebAssembly containers with capability-based permissions
- Credential Protection - Secrets are never exposed to tools; injected at the host boundary with leak detection
- Prompt Injection Defense - Pattern detection, content sanitization, and policy enforcement
- Endpoint Allowlisting - HTTP requests only to explicitly approved hosts and paths
Always Available
- Multi-channel - REPL, HTTP webhooks, WASM channels (Telegram, Slack), and web gateway
- Docker Sandbox - Isolated container execution with per-job tokens and orchestrator/worker pattern
- Web Gateway - Browser UI with real-time SSE/WebSocket streaming
- Routines - Cron schedules, event triggers, webhook handlers for background automation
- Heartbeat System - Proactive background execution for monitoring and maintenance tasks
- Parallel Jobs - Handle multiple requests concurrently with isolated contexts
- Self-repair - Automatic detection and recovery of stuck operations
Self-Expanding
- Dynamic Tool Building - Describe what you need, and IronClaw builds it as a WASM tool
- MCP Protocol - Connect to Model Context Protocol servers for additional capabilities
- Plugin Architecture - Drop in new WASM tools and channels without restarting
Persistent Memory
- Hybrid Search - Full-text + vector search using Reciprocal Rank Fusion
- Workspace Filesystem - Flexible path-based storage for notes, logs, and context
- Identity Files - Maintain consistent personality and preferences across sessions
Installation
Prerequisites
- Rust 1.85+
- PostgreSQL 15+ with pgvector extension
- NEAR AI account (authentication handled via setup wizard)
Download or Build
Visit Releases page to see the latest updates.
Install via Windows Installer (Windows)
Download the Windows Installer and run it.
Install via powershell script (Windows)
irm https://github.com/nearai/ironclaw/releases/latest/download/ironclaw-installer.ps1 | iex
Install via shell script (macOS, Linux, Windows/WSL)
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/nearai/ironclaw/releases/latest/download/ironclaw-installer.sh | sh
Install via Homebrew (macOS/Linux)
brew install ironclaw
Compile the source code (Cargo on Windows, Linux, macOS)
Install it with cargo, just make sure you have Rust installed on your computer.
# Clone the repository
git clone https://github.com/nearai/ironclaw.git
cd ironclaw
# Build
cargo build --release
# Run tests
cargo test
For full release (after modifying channel sources), run ./scripts/build-all.sh to rebuild channels first.
Database Setup
# Create database
createdb ironclaw
# Enable pgvector
psql ironclaw -c "CREATE EXTENSION IF NOT EXISTS vector;"
Configuration
Run the setup wizard to configure IronClaw:
ironclaw onboard
The wizard handles database connection, NEAR AI authentication (via browser OAuth),
and secrets encryption (using your system keychain). Settings are persisted in the
connected database; bootstrap variables (e.g. DATABASE_URL, LLM_BACKEND) are
written to ~/.ironclaw/.env so they are available before the database connects.
Alternative LLM Providers
IronClaw defaults to NEAR AI but supports many LLM providers out of the box. Built-in providers include Anthropic, OpenAI, GitHub Copilot, Google Gemini, MiniMax, Mistral, and Ollama (local). OpenAI-compatible services like OpenRouter (300+ models), Together AI, Fireworks AI, and self-hosted servers (vLLM, LiteLLM) are also supported.
Select your provider in the wizard, or set environment variables directly:
# Example: MiniMax (built-in, 204K context)
LLM_BACKEND=minimax
MINIMAX_API_KEY=...
# Example: OpenAI-compatible endpoint
LLM_BACKEND=openai_compatible
LLM_BASE_URL=https://openrouter.ai/api/v1
LLM_API_KEY=sk-or-...
LLM_MODEL=anthropic/claude-sonnet-4
See docs/capabilities/llm-providers.md for a full provider guide.
Security
IronClaw implements defense in depth to protect your data and prevent misuse.
WASM Sandbox
All untrusted tools run in isolated WebAssembly containers:
- Capability-based permissions - Explicit opt-in for HTTP, secrets, tool invocation
- Endpoint allowlisting - HTTP requests only to approved hosts/paths
- Credential injection - Secrets injected at host boundary, never exposed to WASM code
- Leak detection - Scans requests and responses for secret exfiltration attempts
- Rate limiting - Per-tool request limits to prevent abuse
- Resource limits - Memory, CPU, and execution time constraints
WASM ──► Allowlist ──► Leak Scan ──► Credential ──► Execute ──► Leak Scan ──► WASM
Validator (request) Injector Request (response)
Prompt Injection Defense
External content passes through multiple security layers:
- Pattern-based detection of injection attempts
- Content sanitization and escaping
- Policy rules with severity levels (Block/Warn/Review/Sanitize)
- Tool output wrapping for safe LLM context injection
Data Protection
- All data stored locally in your PostgreSQL database
- Secrets encrypted with AES-256-GCM
- No telemetry, analytics, or data sharing
- Full audit log of all tool executions
Architecture
┌────────────────────────────────────────────────────────────────┐
│ Channels │
│ ┌──────┐ ┌──────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ REPL │ │ HTTP │ │WASM Channels│ │ Web Gateway │ │
│ └──┬───┘ └──┬───┘ └──────┬──────┘ │ (SSE + WS) │ │
│ │ │ │ └──────┬──────┘ │
│ └─────────┴──────────────┴────────────────┘ │
│ │ │
│ ┌─────────▼─────────┐ │
│ │ Agent Loop │ Intent routing │
│ └────┬──────────┬───┘ │
│ │ │ │
│ ┌──────────▼────┐ ┌──▼───────────────┐ │
│ │ Scheduler │ │ Routines Engine │ │
│ │(parallel jobs)│ │(cron, event, wh) │ │
│ └──────┬────────┘ └────────┬─────────┘ │
│ │ │ │
│ ┌─────────────┼────────────────────┘ │
│ │ │ │
│ ┌───▼─────┐ ┌────▼────────────────┐ │
│ │ Local │ │ Orchestrator │ │
│ │Workers │ │ ┌───────────────┐ │ │
│ │(in-proc)│ │ │ Docker Sandbox│ │ │
│ └───┬─────┘ │ │ Containers │ │ │
│ │ │ │ ┌───────────┐ │ │ │
│ │ │ │ │Worker / CC│ │ │ │
│ │ │ │ └───────────┘ │ │ │
│ │ │ └───────────────┘ │ │
│ │ └─────────┬───────────┘ │
│ └──────────────────┤ │
│ │ │
│ ┌───────────▼──────────┐ │
│ │ Tool Registry │ │
│ │ Built-in, MCP, WASM │ │
│ └──────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
Core Components
| Component | Purpose |
|---|---|
| Agent Loop | Main message handling and job coordination |
| Router | Classifies user intent (command, query, task) |
| Scheduler | Manages parallel job execution with priorities |
| Worker | Executes jobs with LLM reasoning and tool calls |
| Orchestrator | Container lifecycle, LLM proxying, per-job auth |
| Web Gateway | Browser UI with chat, memory, jobs, logs, extensions, routines |
| Routines Engine | Scheduled (cron) and reactive (event, webhook) background tasks |
| Workspace | Persistent memory with hybrid search |
| Safety Layer | Prompt injection defense and content sanitization |
Usage
# First-time setup (configures database, auth, etc.)
ironclaw onboard
# Start interactive REPL
cargo run
# With debug logging
RUST_LOG=ironclaw=debug cargo run
Development
# Format code
cargo fmt
# Lint
cargo clippy --all --benches --tests --examples --all-features
# Run tests
createdb ironclaw_test
cargo test
# Run specific test
cargo test test_name
- Channels: See docs/channels/overview.mdx for setup of Telegram, Discord, and other channels.
- Changing channel sources: Run
./channels-src/telegram/build.shbeforecargo buildso the updated WASM is bundled.
OpenClaw Heritage
IronClaw is a Rust reimplementation inspired by OpenClaw. See FEATURE_PARITY.md for the complete tracking matrix.
Key differences:
- Rust vs TypeScript - Native performance, memory safety, single binary
- WASM sandbox vs Docker - Lightweight, capability-based security
- PostgreSQL vs SQLite - Production-ready persistence
- Security-first design - Multiple defense layers, credential protection
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.
