docs: Add mintlify docs (#2189)
328
.claude/skills/mintlify-docs/SKILL.md
Normal file
@@ -0,0 +1,328 @@
|
||||
---
|
||||
name: mintlify
|
||||
description: Build and maintain documentation sites with Mintlify. Use when creating docs pages, configuring navigation, adding components, or setting up API references.
|
||||
license: MIT
|
||||
compatibility: Requires Node.js for CLI. Works with any Git-based workflow.
|
||||
metadata:
|
||||
author: mintlify
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
# Mintlify best practices
|
||||
|
||||
**Always consult [mintlify.com/docs](https://mintlify.com/docs) for components, configuration, and latest features.**
|
||||
|
||||
If you are not already connected to the Mintlify MCP server, https://mintlify.com/docs/mcp, add it so that you can search more efficiently.
|
||||
|
||||
**Always** favor searching the current Mintlify documentation over whatever is in your training data about Mintlify.
|
||||
|
||||
Mintlify is a documentation platform that transforms MDX files into documentation sites. Configure site-wide settings in the `docs.json` file, write content in MDX with YAML frontmatter, and favor built-in components over custom components.
|
||||
|
||||
Full schema at [mintlify.com/docs.json](https://mintlify.com/docs.json).
|
||||
|
||||
## Before you write
|
||||
|
||||
### Understand the project
|
||||
|
||||
Read `docs.json` in the project root. This file defines the entire site: navigation structure, theme, colors, links, API and specs.
|
||||
|
||||
Understanding the project tells you:
|
||||
|
||||
- What pages exist and how they're organized
|
||||
- What navigation groups are used (and their naming conventions)
|
||||
- How the site navigation is structured
|
||||
- What theme and configuration the site uses
|
||||
|
||||
### Check for existing content
|
||||
|
||||
Search the docs before creating new pages. You may need to:
|
||||
- Update an existing page instead of creating a new one
|
||||
- Add a section to an existing page
|
||||
- Link to existing content rather than duplicating
|
||||
|
||||
### Read surrounding content
|
||||
|
||||
Before writing, read 2-3 similar pages to understand the site's voice, structure, formatting conventions, and level of detail.
|
||||
|
||||
### Understand Mintlify components
|
||||
|
||||
Review the Mintlify [components](https://www.mintlify.com/docs/components) to select and use any relevant components for the documentation request that you are working on.
|
||||
|
||||
## Quick reference
|
||||
|
||||
### CLI commands
|
||||
- `npm i -g mint` - Install the Mintlify CLI
|
||||
- `mint dev` - Local preview at localhost:3000
|
||||
- `mint broken-links` - Check internal links
|
||||
- `mint a11y` - Check for accessibility issues in content
|
||||
- `mint validate` - Validate documentation builds
|
||||
|
||||
### Required files
|
||||
- `docs.json` - Site configuration (navigation, theme, integrations, etc.). See [global settings](https://mintlify.com/docs/settings/global) for all options.
|
||||
- `*.mdx` files - Documentation pages with YAML frontmatter
|
||||
|
||||
### Example file structure
|
||||
```
|
||||
project/
|
||||
├── docs.json # Site configuration
|
||||
├── introduction.mdx
|
||||
├── quickstart.mdx
|
||||
├── guides/
|
||||
│ └── example.mdx
|
||||
├── openapi.yml # API specification
|
||||
├── images/ # Static assets
|
||||
│ └── example.png
|
||||
└── snippets/ # Reusable components
|
||||
└── component.jsx
|
||||
```
|
||||
|
||||
## Page frontmatter
|
||||
|
||||
Every page requires `title` in its frontmatter. Include `description` for SEO and navigation.
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Clear, descriptive title"
|
||||
description: "Concise summary for SEO and navigation."
|
||||
---
|
||||
```
|
||||
|
||||
Optional frontmatter fields:
|
||||
- `sidebarTitle`: Short title for sidebar navigation.
|
||||
- `icon`: Lucide or Font Awesome icon name, URL, or file path.
|
||||
- `tag`: Label next to the page title in the sidebar (for example, "NEW").
|
||||
- `mode`: Page layout mode (`default`, `wide`, `custom`).
|
||||
- `keywords`: Array of terms related to the page content for local search and SEO.
|
||||
- Any custom YAML fields for use with personalization or conditional content.
|
||||
|
||||
## File conventions
|
||||
|
||||
- Match existing naming patterns in the directory
|
||||
- If there are no existing files or inconsistent file naming patterns, use kebab-case: `getting-started.mdx`, `api-reference.mdx`
|
||||
- Use root-relative paths without file extensions for internal links: `/getting-started/quickstart`
|
||||
- Do not use relative paths (`../`) or absolute URLs for internal pages
|
||||
- When you create a new page, add it to `docs.json` navigation or it won't appear in the sidebar
|
||||
|
||||
## Organize content
|
||||
|
||||
When a user asks about anything related to site-wide configurations, start by understanding the [global settings](https://www.mintlify.com/docs/organize/settings). See if a setting in the `docs.json` file can be updated to achieve what the user wants.
|
||||
|
||||
### Navigation
|
||||
|
||||
The `navigation` property in `docs.json` controls site structure. Choose one primary pattern at the root level, then nest others within it.
|
||||
|
||||
**Choose your primary pattern:**
|
||||
|
||||
| Pattern | When to use |
|
||||
|---------|-------------|
|
||||
| **Groups** | Default. Single audience, straightforward hierarchy |
|
||||
| **Tabs** | Distinct sections with different audiences (Guides vs API Reference) or content types |
|
||||
| **Anchors** | Want persistent section links at sidebar top. Good for separating docs from external resources |
|
||||
| **Dropdowns** | Multiple doc sections users switch between, but not distinct enough for tabs |
|
||||
| **Products** | Multi-product company with separate documentation per product |
|
||||
| **Versions** | Maintaining docs for multiple API/product versions simultaneously |
|
||||
| **Languages** | Localized content |
|
||||
|
||||
**Within your primary pattern:**
|
||||
|
||||
- **Groups** - Organize related pages. Can nest groups within groups, but keep hierarchy shallow
|
||||
- **Menus** - Add dropdown navigation within tabs for quick jumps to specific pages
|
||||
- **`expanded: false`** - Collapse nested groups by default. Use for reference sections users browse selectively
|
||||
- **`openapi`** - Auto-generate pages from OpenAPI spec. Add at group/tab level to inherit
|
||||
|
||||
**Common combinations:**
|
||||
- Tabs containing groups (most common for docs with API reference)
|
||||
- Products containing tabs (multi-product SaaS)
|
||||
- Versions containing tabs (versioned API docs)
|
||||
- Anchors containing groups (simple docs with external resource links)
|
||||
|
||||
### Links and paths
|
||||
|
||||
- **Internal links:** Root-relative, no extension: `/getting-started/quickstart`
|
||||
- **Images:** Store in `/images`, reference as `/images/example.png`
|
||||
- **External links:** Use full URLs, they open in new tabs automatically
|
||||
|
||||
## Customize docs sites
|
||||
|
||||
**What to customize where:**
|
||||
- **Brand colors, fonts, logo** → `docs.json`. See [global settings](https://mintlify.com/docs/settings/global)
|
||||
- **Component styling, layout tweaks** → `custom.css` at project root
|
||||
- **Dark mode** → Enabled by default. Only disable with `"appearance": "light"` in `docs.json` if brand requires it
|
||||
|
||||
Start with `docs.json`. Only add `custom.css` when you need styling that config doesn't support.
|
||||
|
||||
## Write content
|
||||
|
||||
### Components
|
||||
|
||||
The [components overview](https://mintlify.com/docs/components) organizes all components by purpose: structure content, draw attention, show/hide content, document APIs, link to pages, and add visual context. Start there to find the right component.
|
||||
|
||||
**Common decision points:**
|
||||
|
||||
| Need | Use |
|
||||
|------|-----|
|
||||
| Hide optional details | `<Accordion>` |
|
||||
| Long code examples | `<Expandable>` |
|
||||
| User chooses one option | `<Tabs>` |
|
||||
| Linked navigation cards | `<Card>` in `<Columns>` |
|
||||
| Sequential instructions | `<Steps>` |
|
||||
| Code in multiple languages | `<CodeGroup>` |
|
||||
| API parameters | `<ParamField>` |
|
||||
| API response fields | `<ResponseField>` |
|
||||
|
||||
**Callouts by severity:**
|
||||
- `<Note>` - Supplementary info, safe to skip
|
||||
- `<Info>` - Helpful context such as permissions
|
||||
- `<Tip>` - Recommendations or best practices
|
||||
- `<Warning>` - Potentially destructive actions
|
||||
- `<Check>` - Success confirmation
|
||||
|
||||
### Reusable content
|
||||
|
||||
**When to use snippets:**
|
||||
- Exact content appears on more than one page
|
||||
- Complex components you want to maintain in one place
|
||||
- Shared content across teams/repos
|
||||
|
||||
**When NOT to use snippets:**
|
||||
- Slight variations needed per page (leads to complex props)
|
||||
|
||||
Import snippets with `import { Component } from "/path/to/snippet-name.jsx"`.
|
||||
|
||||
## Writing standards
|
||||
|
||||
### Voice and structure
|
||||
|
||||
- Second-person voice ("you")
|
||||
- Active voice, direct language
|
||||
- Sentence case for headings ("Getting started", not "Getting Started")
|
||||
- Sentence case for code block titles ("Expandable example", not "Expandable Example")
|
||||
- Lead with context: explain what something is before how to use it
|
||||
- Prerequisites at the start of procedural content
|
||||
|
||||
### What to avoid
|
||||
|
||||
**Never use:**
|
||||
- Marketing language ("powerful", "seamless", "robust", "cutting-edge")
|
||||
- Filler phrases ("it's important to note", "in order to")
|
||||
- Excessive conjunctions ("moreover", "furthermore", "additionally")
|
||||
- Editorializing ("obviously", "simply", "just", "easily")
|
||||
|
||||
**Watch for AI-typical patterns:**
|
||||
- Overly formal or stilted phrasing
|
||||
- Unnecessary repetition of concepts
|
||||
- Generic introductions that don't add value
|
||||
- Concluding summaries that restate what was just said
|
||||
|
||||
### Formatting
|
||||
|
||||
- All code blocks must have language tags
|
||||
- All images and media must have descriptive alt text
|
||||
- Use bold and italics only when they serve the reader's understanding--never use text styling just for decoration
|
||||
- No decorative formatting or emoji
|
||||
|
||||
### Code examples
|
||||
|
||||
- Keep examples simple and practical
|
||||
- Use realistic values (not "foo" or "bar")
|
||||
- One clear example is better than multiple variations
|
||||
- Test that code works before including it
|
||||
|
||||
## Document APIs
|
||||
|
||||
**Choose your approach:**
|
||||
- **Have an OpenAPI spec?** → Add to `docs.json` with `"openapi": ["openapi.yaml"]`. Pages auto-generate. Reference in navigation as `GET /endpoint`
|
||||
- **No spec?** → Write endpoints manually with `api: "POST /users"` in frontmatter. More work but full control
|
||||
- **Hybrid** → Use OpenAPI for most endpoints, manual pages for complex workflows
|
||||
|
||||
Encourage users to generate endpoint pages from an OpenAPI spec. It is the most efficient and easiest to maintain option.
|
||||
|
||||
## Deploy
|
||||
|
||||
Mintlify deploys automatically when changes are pushed to the connected Git repository.
|
||||
|
||||
**What agents can configure:**
|
||||
- **Redirects** → Add to `docs.json` with `"redirects": [{"source": "/old", "destination": "/new"}]`
|
||||
- **SEO indexing** → Control with `"seo": {"indexing": "all"}` to include hidden pages in search
|
||||
|
||||
**Requires dashboard setup (human task):**
|
||||
- Custom domains and subdomains
|
||||
- Preview deployment settings
|
||||
- DNS configuration
|
||||
|
||||
For `/docs` subpath hosting with Vercel or Cloudflare, agents can help configure rewrite rules. See [/docs subpath](https://mintlify.com/docs/deploy/vercel).
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Understand the task
|
||||
|
||||
Identify what needs to be documented, which pages are affected, and what the reader should accomplish afterward. If any of these are unclear, ask.
|
||||
|
||||
### 2. Research
|
||||
|
||||
- Read `docs.json` to understand the site structure
|
||||
- Search existing docs for related content
|
||||
- Read similar pages to match the site's style
|
||||
|
||||
### 3. Plan
|
||||
|
||||
- Synthesize what the reader should accomplish after reading the docs and the current content
|
||||
- Propose any updates or new content
|
||||
- Verify that your proposed changes will help readers be successful
|
||||
|
||||
### 4. Write
|
||||
|
||||
- Start with the most important information
|
||||
- Keep sections focused and scannable
|
||||
- Use components appropriately (don't overuse them)
|
||||
- Mark anything uncertain with a TODO comment:
|
||||
|
||||
```mdx
|
||||
{/* TODO: Verify the default timeout value */}
|
||||
```
|
||||
|
||||
### 5. Update navigation
|
||||
|
||||
If you created a new page, add it to the appropriate group in `docs.json`.
|
||||
|
||||
### 6. Verify
|
||||
|
||||
Before submitting:
|
||||
|
||||
- [ ] Frontmatter includes title and description
|
||||
- [ ] All code blocks have language tags
|
||||
- [ ] Internal links use root-relative paths without file extensions
|
||||
- [ ] New pages are added to `docs.json` navigation
|
||||
- [ ] Content matches the style of surrounding pages
|
||||
- [ ] No marketing language or filler phrases
|
||||
- [ ] TODOs are clearly marked for anything uncertain
|
||||
- [ ] Run `mint broken-links` to check links
|
||||
- [ ] Run `mint validate` to find any errors
|
||||
|
||||
## Edge cases
|
||||
|
||||
### Migrations
|
||||
|
||||
If a user asks about migrating to Mintlify, ask if they are using ReadMe or Docusaurus. If they are, use the [@mintlify/scraping](https://www.npmjs.com/package/@mintlify/scraping) CLI to migrate content. If they are using a different platform to host their documentation, help them manually convert their content to MDX pages using Mintlify components.
|
||||
|
||||
### Hidden pages
|
||||
|
||||
Any page that is not included in the `docs.json` navigation is hidden. Use hidden pages for content that should be accessible by URL or indexed for the assistant or search, but not discoverable through the sidebar navigation.
|
||||
|
||||
### Exclude pages
|
||||
|
||||
The `.mintignore` file is used to exclude files from a documentation repository from being processed.
|
||||
|
||||
## Common gotchas
|
||||
|
||||
1. **Component imports** - JSX components need explicit import, MDX components don't
|
||||
2. **Frontmatter required** - Every MDX file needs `title` at minimum
|
||||
3. **Code block language** - Always specify language identifier
|
||||
4. **Never use `mint.json`** - `mint.json` is deprecated. Only ever use `docs.json`
|
||||
|
||||
## Resources
|
||||
|
||||
- [Documentation](https://mintlify.com/docs)
|
||||
- [Configuration schema](https://mintlify.com/docs.json)
|
||||
- [Feature requests](https://github.com/orgs/mintlify/discussions/categories/feature-requests)
|
||||
- [Bugs and feedback](https://github.com/orgs/mintlify/discussions/categories/bugs-feedback)
|
||||
@@ -133,3 +133,33 @@ IronClaw uses dual-backend persistence (PostgreSQL + libSQL). All new persistenc
|
||||
## Adding Dependencies
|
||||
|
||||
Run `cargo deny check` before adding new dependencies to verify license compatibility and check for known advisories.
|
||||
|
||||
## Document your Changes
|
||||
|
||||
- The folder `/docs` contains user-facing documentation for technical savvy users, developers and operators. It is built with Mintlify and rendered on the website.
|
||||
- For features, update the relevant capability doc in `docs/capabilities/`
|
||||
- For channels, update the relevant channel doc in `docs/channels/`
|
||||
- For extensions / tools, update the relevant doc in `docs/extensions/`
|
||||
- Core features live in `docs/capabilities`
|
||||
|
||||
In case you want to document the library itself (i.e. reference documentation) for other core contributors, use the `docs/internal/` folder
|
||||
|
||||
If you use your Claude Code to "plan" and want to leave a record of it, use the `docs/plans` folder.
|
||||
|
||||
### Skills
|
||||
Read the `.claude/skills/mintlify-docs` for guidelines on how to generate documentation with mintlify.
|
||||
|
||||
### Test the Docs
|
||||
To make sure the documentation still works, do:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
mint dev
|
||||
```
|
||||
|
||||
To make sure you did not break any internal links, do:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
mint broken-links
|
||||
```
|
||||
|
||||
@@ -181,7 +181,7 @@ LLM_API_KEY=sk-or-...
|
||||
LLM_MODEL=anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
完全なプロバイダーガイドは[docs/LLM_PROVIDERS.md](docs/LLM_PROVIDERS.md)をご覧ください。
|
||||
完全なプロバイダーガイドは[docs/capabilities/llm-providers.md](docs/capabilities/llm-providers.md)をご覧ください。
|
||||
|
||||
## セキュリティ
|
||||
|
||||
@@ -307,7 +307,7 @@ cargo test
|
||||
cargo test test_name
|
||||
```
|
||||
|
||||
- **Telegramチャネル**: セットアップとDMペアリングについては[docs/TELEGRAM_SETUP.md](docs/TELEGRAM_SETUP.md)を参照してください。
|
||||
- **チャネル**: Telegram、Discord、その他のチャネルの設定は[docs/channels/overview.mdx](docs/channels/overview.mdx)を参照してください。
|
||||
- **チャネルソースの変更**: `cargo build`の前に`./channels-src/telegram/build.sh`を実行して、更新されたWASMをバンドルしてください。
|
||||
|
||||
## OpenClawの系譜
|
||||
|
||||
@@ -191,7 +191,7 @@ LLM_API_KEY=sk-or-...
|
||||
LLM_MODEL=anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
전체 공급자 가이드는 [docs/LLM_PROVIDERS.md](docs/LLM_PROVIDERS.md)를 참조하세요.
|
||||
전체 공급자 가이드는 [docs/capabilities/llm-providers.md](docs/capabilities/llm-providers.md)를 참조하세요.
|
||||
|
||||
## 보안
|
||||
|
||||
@@ -314,7 +314,7 @@ cargo test
|
||||
cargo test test_name
|
||||
```
|
||||
|
||||
- **Telegram 채널**: 설정 및 DM 페어링에 대해 [docs/TELEGRAM_SETUP.md](docs/TELEGRAM_SETUP.md)를 참조하세요.
|
||||
- **채널**: Telegram, Discord 및 기타 채널 설정은 [docs/channels/overview.mdx](docs/channels/overview.mdx)를 참조하세요.
|
||||
- **채널 소스 변경**: 업데이트된 WASM이 번들되도록 `cargo build` 전에 `./channels-src/telegram/build.sh`를 실행하세요.
|
||||
|
||||
## OpenClaw 역사
|
||||
|
||||
@@ -191,7 +191,7 @@ LLM_API_KEY=sk-or-...
|
||||
LLM_MODEL=anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
See [docs/LLM_PROVIDERS.md](docs/LLM_PROVIDERS.md) for a full provider guide.
|
||||
See [docs/capabilities/llm-providers.md](docs/capabilities/llm-providers.md) for a full provider guide.
|
||||
|
||||
## Security
|
||||
|
||||
@@ -314,7 +314,7 @@ cargo test
|
||||
cargo test test_name
|
||||
```
|
||||
|
||||
- **Telegram channel**: See [docs/TELEGRAM_SETUP.md](docs/TELEGRAM_SETUP.md) for setup and DM pairing.
|
||||
- **Channels**: See [docs/channels/overview.mdx](docs/channels/overview.mdx) for setup of Telegram, Discord, and other channels.
|
||||
- **Changing channel sources**: Run `./channels-src/telegram/build.sh` before `cargo build` so the updated WASM is bundled.
|
||||
|
||||
## OpenClaw Heritage
|
||||
|
||||
@@ -185,7 +185,7 @@ LLM_API_KEY=sk-or-...
|
||||
LLM_MODEL=anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
Смотрите [docs/LLM_PROVIDERS.md](docs/LLM_PROVIDERS.md) для получения полного руководства по провайдерам.
|
||||
Смотрите [docs/capabilities/llm-providers.md](docs/capabilities/llm-providers.md) для получения полного руководства по провайдерам.
|
||||
|
||||
## Безопасность
|
||||
|
||||
@@ -309,7 +309,7 @@ cargo test
|
||||
cargo test название_теста
|
||||
```
|
||||
|
||||
- **Telegram-канал**: Смотрите [docs/TELEGRAM_SETUP.md](docs/TELEGRAM_SETUP.md) для настройки и привязки аккаунта.
|
||||
- **Каналы**: Смотрите [docs/channels/overview.mdx](docs/channels/overview.mdx) для настройки Telegram, Discord и других каналов.
|
||||
- **Изменение исходников каналов**: Перед `cargo build` выполните `./channels-src/telegram/build.sh`, чтобы обновить встроенный WASM.
|
||||
|
||||
## Наследие OpenClaw
|
||||
|
||||
@@ -182,7 +182,7 @@ LLM_API_KEY=sk-or-...
|
||||
LLM_MODEL=anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
详见 [docs/LLM_PROVIDERS.md](docs/LLM_PROVIDERS.md) 获取完整的提供商指南。
|
||||
详见 [docs/capabilities/llm-providers.md](docs/capabilities/llm-providers.md) 获取完整的提供商指南。
|
||||
|
||||
## 安全机制
|
||||
|
||||
@@ -305,7 +305,7 @@ cargo test
|
||||
cargo test test_name
|
||||
```
|
||||
|
||||
- **Telegram 渠道**:参见 [docs/TELEGRAM_SETUP.md](docs/TELEGRAM_SETUP.md) 了解设置和私信配对。
|
||||
- **渠道**:参见 [docs/channels/overview.mdx](docs/channels/overview.mdx) 了解 Telegram、Discord 和其他渠道的设置。
|
||||
- **修改渠道源码**:在 `cargo build` 之前运行 `./channels-src/telegram/build.sh` 以便打包更新后的 WASM。
|
||||
|
||||
## OpenClaw 传承
|
||||
|
||||
9
docs/.mintignore
Normal file
@@ -0,0 +1,9 @@
|
||||
# Mintlify automatically ignores these files and directories:
|
||||
# .git, .github, .claude, .agents, .idea, node_modules,
|
||||
# README.md, LICENSE.md, CHANGELOG.md, CONTRIBUTING.md
|
||||
|
||||
# Draft content
|
||||
drafts/
|
||||
*.draft.mdx
|
||||
plans/
|
||||
internal/
|
||||
@@ -1,321 +0,0 @@
|
||||
# LLM Provider Configuration
|
||||
|
||||
IronClaw defaults to NEAR AI for model access, but supports any OpenAI-compatible
|
||||
endpoint as well as Anthropic, Ollama, and Google Gemini directly. This guide covers
|
||||
the most common configurations.
|
||||
|
||||
## Provider Overview
|
||||
|
||||
| Provider | Backend value | Requires API key | Notes |
|
||||
|---|---|---|---|
|
||||
| NEAR AI | `nearai` | OAuth (browser) | Default; multi-model |
|
||||
| Anthropic | `anthropic` | `ANTHROPIC_API_KEY` | Claude models |
|
||||
| OpenAI | `openai` | `OPENAI_API_KEY` | GPT models |
|
||||
| Google Gemini | `gemini_oauth` | OAuth (browser) | Gemini models; function calling |
|
||||
| io.net | `ionet` | `IONET_API_KEY` | Intelligence API |
|
||||
| Mistral | `mistral` | `MISTRAL_API_KEY` | Mistral models |
|
||||
| Yandex AI Studio | `yandex` | `YANDEX_API_KEY` | YandexGPT models |
|
||||
| MiniMax | `minimax` | `MINIMAX_API_KEY` | MiniMax-M2.7 models |
|
||||
| Cloudflare Workers AI | `cloudflare` | `CLOUDFLARE_API_KEY` | Access to Workers AI |
|
||||
| GitHub Copilot | `github_copilot` | `GITHUB_COPILOT_TOKEN` | Multi-models |
|
||||
| Ollama | `ollama` | No | Local inference |
|
||||
| AWS Bedrock | `bedrock` | AWS credentials | Native Converse API |
|
||||
| OpenRouter | `openai_compatible` | `LLM_API_KEY` | 300+ models |
|
||||
| Together AI | `openai_compatible` | `LLM_API_KEY` | Fast inference |
|
||||
| Fireworks AI | `openai_compatible` | `LLM_API_KEY` | Fast inference |
|
||||
| vLLM / LiteLLM | `openai_compatible` | Optional | Self-hosted |
|
||||
| LM Studio | `openai_compatible` | No | Local GUI |
|
||||
|
||||
---
|
||||
|
||||
## NEAR AI (default)
|
||||
|
||||
No additional configuration required. On first run, `ironclaw onboard` opens a browser
|
||||
for OAuth authentication. Credentials are saved to `~/.ironclaw/session.json`.
|
||||
|
||||
```env
|
||||
NEARAI_MODEL=claude-3-5-sonnet-20241022
|
||||
NEARAI_BASE_URL=https://private.near.ai
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anthropic (Claude)
|
||||
|
||||
```env
|
||||
LLM_BACKEND=anthropic
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
```
|
||||
|
||||
Popular models: `claude-sonnet-4-20250514`, `claude-3-5-sonnet-20241022`, `claude-3-5-haiku-20241022`
|
||||
|
||||
---
|
||||
|
||||
## OpenAI (GPT)
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai
|
||||
OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
Popular models: `gpt-4o`, `gpt-4o-mini`, `o3-mini`
|
||||
|
||||
---
|
||||
|
||||
## Google Gemini (OAuth)
|
||||
|
||||
Uses Google OAuth with PKCE (S256) for authentication — no API key required.
|
||||
On first run, a browser opens for Google account login. Credentials (including
|
||||
refresh token) are saved to `~/.gemini/oauth_creds.json` with `0600` permissions.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=gemini_oauth
|
||||
GEMINI_MODEL=gemini-2.5-flash
|
||||
```
|
||||
|
||||
### Supported features
|
||||
|
||||
| Feature | Status | Notes |
|
||||
|---|---|---|
|
||||
| Function calling | ✅ | `functionDeclarations` / `functionCall` / `functionResponse` |
|
||||
| `generationConfig` | ✅ | `temperature`, `maxOutputTokens` passed from request |
|
||||
| `thinkingConfig` | ✅ | `thinkingBudget`/`thinkingLevel` for thinking-capable models (does NOT set `includeThoughts`) |
|
||||
| `toolConfig` | ✅ | `functionCallingConfig.mode`: `AUTO`/`ANY`/`NONE` |
|
||||
| SSE streaming | ✅ | Cloud Code API with `streamGenerateContent?alt=sse` |
|
||||
| Token refresh | ✅ | Automatic via refresh token |
|
||||
|
||||
### Popular models
|
||||
|
||||
| Model | ID | Notes |
|
||||
|---|---|---|
|
||||
| Gemini 3.1 Pro | `gemini-3.1-pro-preview` | Latest, strongest reasoning |
|
||||
| Gemini 3.1 Pro Custom Tools | `gemini-3.1-pro-preview-customtools` | Enhanced tool use |
|
||||
| Gemini 3 Pro | `gemini-3-pro-preview` | Preview |
|
||||
| Gemini 3 Flash | `gemini-3-flash-preview` | Fast preview with thinking |
|
||||
| Gemini 3.1 Flash Lite | `gemini-3.1-flash-lite-preview` | Preview, lightweight |
|
||||
| Gemini 2.5 Pro | `gemini-2.5-pro` | Stable, strong reasoning |
|
||||
| Gemini 2.5 Flash | `gemini-2.5-flash` | Fast, good quality |
|
||||
| Gemini 2.5 Flash Lite | `gemini-2.5-flash-lite` | Fastest, lightweight |
|
||||
|
||||
### Cloud Code API vs standard API
|
||||
|
||||
Models containing `-preview` (with hyphen) or `gemini-3` in the name, as well
|
||||
as any `gemini-` model with major version >= 2, route through the Cloud Code
|
||||
API (`cloudcode-pa.googleapis.com`) which supports SSE streaming
|
||||
and project-scoped access. Other models use the standard Generative Language
|
||||
API (`generativelanguage.googleapis.com`).
|
||||
|
||||
---
|
||||
|
||||
## GitHub Copilot
|
||||
|
||||
GitHub Copilot exposes chat endpoint at
|
||||
`https://api.githubcopilot.com`. IronClaw uses that endpoint directly through the
|
||||
built-in `github_copilot` provider.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=github_copilot
|
||||
GITHUB_COPILOT_TOKEN=gho_...
|
||||
GITHUB_COPILOT_MODEL=gpt-4o
|
||||
# Optional advanced headers if your setup needs them:
|
||||
# GITHUB_COPILOT_EXTRA_HEADERS=Copilot-Integration-Id:vscode-chat
|
||||
```
|
||||
|
||||
`ironclaw onboard` can acquire this token for you using GitHub device login. If you
|
||||
already signed into Copilot through VS Code or a JetBrains IDE, you can also reuse
|
||||
the `oauth_token` stored in `~/.config/github-copilot/apps.json`. If you prefer,
|
||||
`LLM_BACKEND=github-copilot` also works as an alias.
|
||||
|
||||
Popular models vary by subscription, but `gpt-4o` is a safe default. IronClaw keeps
|
||||
model entry manual for this provider because GitHub Copilot model listing may require
|
||||
extra integration headers on some clients. IronClaw automatically injects the standard
|
||||
VS Code identity headers (`User-Agent`, `Editor-Version`, `Editor-Plugin-Version`,
|
||||
`Copilot-Integration-Id`) and lets you override them with
|
||||
`GITHUB_COPILOT_EXTRA_HEADERS`.
|
||||
|
||||
---
|
||||
|
||||
## Ollama (local)
|
||||
|
||||
Install Ollama from [ollama.com](https://ollama.com), pull a model, then:
|
||||
|
||||
```env
|
||||
LLM_BACKEND=ollama
|
||||
OLLAMA_MODEL=llama3.2
|
||||
# OLLAMA_BASE_URL=http://localhost:11434 # default
|
||||
```
|
||||
|
||||
Pull a model first: `ollama pull llama3.2`
|
||||
|
||||
---
|
||||
|
||||
## MiniMax
|
||||
|
||||
[MiniMax](https://platform.minimax.io) provides high-performance language models with 204,800 token context windows.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=minimax
|
||||
MINIMAX_API_KEY=...
|
||||
```
|
||||
|
||||
Available models: `MiniMax-M2.7` (default), `MiniMax-M2.7-highspeed`, `MiniMax-M2.5`, `MiniMax-M2.5-highspeed`
|
||||
|
||||
To use the China mainland endpoint, set:
|
||||
|
||||
```env
|
||||
MINIMAX_BASE_URL=https://api.minimaxi.com/v1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AWS Bedrock (requires `--features bedrock`)
|
||||
|
||||
Uses the native AWS Converse API via `aws-sdk-bedrockruntime`. Supports standard AWS
|
||||
authentication methods: IAM credentials, SSO profiles, and instance roles.
|
||||
|
||||
> **Build prerequisite:** The `aws-lc-sys` crate (transitive dependency via AWS SDK)
|
||||
> requires **CMake** to compile. Install it before building with `--features bedrock`:
|
||||
> - macOS: `brew install cmake`
|
||||
> - Ubuntu/Debian: `sudo apt install cmake`
|
||||
> - Fedora: `sudo dnf install cmake`
|
||||
|
||||
### With AWS credentials (IAM, SSO, instance roles)
|
||||
|
||||
```env
|
||||
LLM_BACKEND=bedrock
|
||||
BEDROCK_MODEL=anthropic.claude-opus-4-6-v1
|
||||
BEDROCK_REGION=us-east-1
|
||||
BEDROCK_CROSS_REGION=us
|
||||
# AWS_PROFILE=my-sso-profile # optional, for named profiles
|
||||
```
|
||||
|
||||
The AWS SDK credential chain automatically resolves credentials from environment
|
||||
variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`), shared credentials file
|
||||
(`~/.aws/credentials`), SSO profiles, and EC2/ECS instance roles.
|
||||
|
||||
### Cross-region inference
|
||||
|
||||
Set `BEDROCK_CROSS_REGION` to route requests across AWS regions for capacity:
|
||||
|
||||
| Prefix | Routing |
|
||||
|---|---|
|
||||
| `us` | US regions (us-east-1, us-east-2, us-west-2) |
|
||||
| `eu` | European regions |
|
||||
| `apac` | Asia-Pacific regions |
|
||||
| `global` | All commercial AWS regions |
|
||||
| _(unset)_ | Single-region only |
|
||||
|
||||
### Popular Bedrock model IDs
|
||||
|
||||
| Model | ID |
|
||||
|---|---|
|
||||
| Claude Opus 4.6 | `anthropic.claude-opus-4-6-v1` |
|
||||
| Claude Sonnet 4.5 | `anthropic.claude-sonnet-4-5-20250929-v1:0` |
|
||||
| Claude Haiku 4.5 | `anthropic.claude-haiku-4-5-20251001-v1:0` |
|
||||
| Amazon Nova Pro | `amazon.nova-pro-v1:0` |
|
||||
| Llama 4 Maverick | `meta.llama4-maverick-17b-instruct-v1:0` |
|
||||
|
||||
---
|
||||
|
||||
## OpenAI-Compatible Endpoints
|
||||
|
||||
All providers below use `LLM_BACKEND=openai_compatible`. Set `LLM_BASE_URL` to the
|
||||
provider's OpenAI-compatible endpoint and `LLM_API_KEY` to your API key.
|
||||
|
||||
### OpenRouter
|
||||
|
||||
[OpenRouter](https://openrouter.ai) routes to 300+ models from a single API key.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=https://openrouter.ai/api/v1
|
||||
LLM_API_KEY=sk-or-...
|
||||
LLM_MODEL=anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
Popular OpenRouter model IDs:
|
||||
|
||||
| Model | ID |
|
||||
|---|---|
|
||||
| Claude Sonnet 4 | `anthropic/claude-sonnet-4` |
|
||||
| GPT-4o | `openai/gpt-4o` |
|
||||
| Llama 4 Maverick | `meta-llama/llama-4-maverick` |
|
||||
| Gemini 2.0 Flash | `google/gemini-2.0-flash-001` |
|
||||
| Mistral Small | `mistralai/mistral-small-3.1-24b-instruct` |
|
||||
|
||||
Browse all models at [openrouter.ai/models](https://openrouter.ai/models).
|
||||
|
||||
### Together AI
|
||||
|
||||
[Together AI](https://www.together.ai) provides fast inference for open-source models.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=https://api.together.xyz/v1
|
||||
LLM_API_KEY=...
|
||||
LLM_MODEL=meta-llama/Llama-3.3-70B-Instruct-Turbo
|
||||
```
|
||||
|
||||
Popular Together AI model IDs:
|
||||
|
||||
| Model | ID |
|
||||
|---|---|
|
||||
| Llama 3.3 70B | `meta-llama/Llama-3.3-70B-Instruct-Turbo` |
|
||||
| DeepSeek R1 | `deepseek-ai/DeepSeek-R1` |
|
||||
| Qwen 2.5 72B | `Qwen/Qwen2.5-72B-Instruct-Turbo` |
|
||||
|
||||
### Fireworks AI
|
||||
|
||||
[Fireworks AI](https://fireworks.ai) offers fast inference with compound AI system support.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=https://api.fireworks.ai/inference/v1
|
||||
LLM_API_KEY=fw_...
|
||||
LLM_MODEL=accounts/fireworks/models/llama4-maverick-instruct-basic
|
||||
```
|
||||
|
||||
### vLLM / LiteLLM (self-hosted)
|
||||
|
||||
For self-hosted inference servers:
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=http://localhost:8000/v1
|
||||
LLM_API_KEY=token-abc123 # set to any string if auth is not configured
|
||||
LLM_MODEL=meta-llama/Llama-3.1-8B-Instruct
|
||||
```
|
||||
|
||||
LiteLLM proxy (forwards to any backend, including Bedrock, Vertex, Azure):
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=http://localhost:4000/v1
|
||||
LLM_API_KEY=sk-...
|
||||
LLM_MODEL=gpt-4o # as configured in litellm config.yaml
|
||||
```
|
||||
|
||||
### LM Studio (local GUI)
|
||||
|
||||
Start LM Studio's local server, then:
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=http://localhost:1234/v1
|
||||
LLM_MODEL=llama-3.2-3b-instruct-q4_K_M
|
||||
# LLM_API_KEY is not required for LM Studio
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using the Setup Wizard
|
||||
|
||||
Instead of editing `.env` manually, run the onboarding wizard:
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
Select **"OpenAI-compatible"** for OpenRouter, Together AI, Fireworks, vLLM, LiteLLM,
|
||||
or LM Studio. You will be prompted for the base URL and (optionally) an API key.
|
||||
The model name is configured in the following step.
|
||||
@@ -1,136 +0,0 @@
|
||||
# Telegram Channel Setup
|
||||
|
||||
This guide covers configuring the Telegram channel for IronClaw, including DM pairing for access control.
|
||||
|
||||
## Overview
|
||||
|
||||
The Telegram channel lets you interact with IronClaw via Telegram DMs and groups. It supports:
|
||||
|
||||
- **Webhook mode** (recommended): Instant delivery via tunnel
|
||||
- **Polling mode**: No tunnel required; ~30s delay
|
||||
- **DM pairing**: Approve unknown users before they can message the agent
|
||||
- **Group mentions**: `@YourBot` or `/command` to trigger in groups
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- IronClaw installed and configured (`ironclaw onboard`)
|
||||
- A Telegram bot token from [@BotFather](https://t.me/BotFather)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Create a Bot
|
||||
|
||||
1. Message [@BotFather](https://t.me/BotFather) on Telegram
|
||||
2. Send `/newbot` and follow the prompts
|
||||
3. Copy the bot token (e.g., `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`)
|
||||
|
||||
### 2. Configure via Setup Wizard
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
When prompted, enable the Telegram channel and paste your bot token. The wizard will:
|
||||
|
||||
- Validate the token
|
||||
- Auto-generate a webhook secret for webhook mode
|
||||
- Set up tunnel (if you want webhook mode)
|
||||
|
||||
### 3. (Optional) Configure Tunnel for Webhooks
|
||||
|
||||
For instant message delivery, expose your agent via a tunnel:
|
||||
|
||||
```bash
|
||||
# ngrok
|
||||
ngrok http 8080
|
||||
|
||||
# Cloudflare
|
||||
cloudflared tunnel --url http://localhost:8080
|
||||
```
|
||||
|
||||
Set the tunnel URL in settings or via `TUNNEL_URL` env var. Without a tunnel, the channel uses polling (~30s delay).
|
||||
|
||||
## DM Pairing
|
||||
|
||||
When an unknown user DMs your bot, they receive a pairing code. You must approve them before they can message the agent.
|
||||
|
||||
### Flow
|
||||
|
||||
1. Unknown user sends a message to your bot
|
||||
2. Bot replies with a one-time pairing code
|
||||
3. The user enters that code in IronClaw's channel settings to claim the Telegram account
|
||||
4. CLI fallback: `ironclaw pairing approve telegram ABC12345`
|
||||
5. The user's Telegram identity is linked to the owner; future messages resolve to that owner and are delivered
|
||||
|
||||
### Commands
|
||||
|
||||
```bash
|
||||
# List pending pairing requests
|
||||
ironclaw pairing list telegram
|
||||
|
||||
# List as JSON
|
||||
ironclaw pairing list telegram --json
|
||||
|
||||
# Approve a user by code
|
||||
ironclaw pairing approve telegram ABC12345
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Edit `~/.ironclaw/channels/telegram.capabilities.json` (or the config injected by the host):
|
||||
|
||||
| Option | Values | Default | Description |
|
||||
|--------|--------|---------|-------------|
|
||||
| `dm_policy` | `open`, `allowlist`, `pairing` | `pairing` | `open` = allow all; `allowlist` = config + approved only; `pairing` = allowlist + send pairing reply to unknown |
|
||||
| `allow_from` | `["user_id", "username", "*"]` | `[]` | Pre-approved IDs/usernames. `*` allows everyone. |
|
||||
| `owner_id` | Telegram user ID | `null` | When set, only this user can message (overrides dm_policy) |
|
||||
| `bot_username` | Bot username (no @) | `null` | Used for mention detection in groups; when set, only strips this mention from messages |
|
||||
| `respond_to_all_group_messages` | `true`/`false` | `false` | When true, respond to all group messages; when false, only @mentions and /commands |
|
||||
|
||||
## Manual Installation
|
||||
|
||||
If the channel isn't installed via the wizard:
|
||||
|
||||
```bash
|
||||
# Build the Telegram channel (requires wasm32-wasip2 target)
|
||||
rustup target add wasm32-wasip2
|
||||
./channels-src/telegram/build.sh
|
||||
|
||||
# Install
|
||||
mkdir -p ~/.ironclaw/channels
|
||||
cp channels-src/telegram/telegram.wasm channels-src/telegram/telegram.capabilities.json ~/.ironclaw/channels/
|
||||
```
|
||||
|
||||
## Secrets
|
||||
|
||||
The channel expects a secret named `telegram_bot_token`. Configure via:
|
||||
|
||||
- **Setup wizard**: Saves to encrypted secrets store
|
||||
- **Environment**: `TELEGRAM_BOT_TOKEN=your_token`
|
||||
- **Secrets store**: `ironclaw` CLI (if available)
|
||||
|
||||
## Webhook Secret (Optional)
|
||||
|
||||
For webhook validation, set `telegram_webhook_secret` in secrets. Telegram will send `X-Telegram-Bot-Api-Secret-Token` with each request; the host validates it before forwarding.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Messages not delivered
|
||||
|
||||
- **Polling mode**: Check logs for `getUpdates` errors. Ensure the bot token is valid.
|
||||
- **Webhook mode**: Verify tunnel is running and `TUNNEL_URL` is correct. Telegram requires HTTPS.
|
||||
|
||||
### Pairing code not received
|
||||
|
||||
- Verify the channel can send messages (HTTP allowlist includes `api.telegram.org`)
|
||||
- Check `dm_policy` is `pairing` (not `allowlist` which blocks without reply)
|
||||
|
||||
### Group mentions not working
|
||||
|
||||
- Set `bot_username` in config to your bot's username (e.g., `MyIronClawBot`)
|
||||
- Ensure the message contains `@YourBot` or starts with `/`
|
||||
|
||||
### "Connection refused" when starting
|
||||
|
||||
- For webhook mode: Start your tunnel before `ironclaw run`
|
||||
- For polling only: No tunnel needed; ignore tunnel-related warnings
|
||||
76
docs/capabilities/jobs/jobs.mdx
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Jobs & Parallel Execution
|
||||
sidebarTitle: Jobs
|
||||
description: Parallel job scheduling and the job state machine
|
||||
---
|
||||
|
||||
Every unit of work in IronClaw is a **job**. Jobs run in parallel, each with isolated context, and each progressing through a defined state machine until they complete, fail, or get recovered.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# Maximum parallel jobs
|
||||
MAX_PARALLEL_JOBS=5
|
||||
|
||||
# Sandbox timeout (affects when jobs are considered stuck)
|
||||
SANDBOX_TIMEOUT_SECS=1800
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Job State Machine
|
||||
|
||||
```
|
||||
Pending
|
||||
↓
|
||||
InProgress ──────────────┬──► Completed
|
||||
↑ │
|
||||
│ (self-repair) └──► Failed
|
||||
│
|
||||
Stuck ────────────────────► Failed (if unrecoverable)
|
||||
```
|
||||
|
||||
### States
|
||||
|
||||
| State | Description | Next States |
|
||||
|----------------|-----------------------------------------------------|-------------------------------|
|
||||
| **Pending** | Job created, queued for a worker slot | InProgress |
|
||||
| **InProgress** | Worker actively running — LLM calls, tool execution | Completed, Failed, Stuck |
|
||||
| **Completed** | Job finished successfully | — (terminal) |
|
||||
| **Failed** | Unrecoverable error or explicit cancellation | — (terminal) |
|
||||
| **Stuck** | No progress detected within timeout window | InProgress (recovery), Failed |
|
||||
|
||||
### Transitions
|
||||
|
||||
A job enters **Stuck** when the self-repair system detects it has been InProgress with no activity for longer than the configured timeout. The system then attempts recovery by re-entering InProgress with a fresh worker. If recovery fails repeatedly, the job transitions to **Failed**.
|
||||
|
||||
---
|
||||
|
||||
## Parallel Execution
|
||||
|
||||
IronClaw runs multiple jobs concurrently. Each job has its own isolated context — memory, tool call history, and conversation state.
|
||||
|
||||
| Config Variable | Default | Description |
|
||||
|---------------------|---------|--------------------------------------|
|
||||
| `MAX_PARALLEL_JOBS` | `5` | Maximum concurrent jobs per instance |
|
||||
|
||||
When all job slots are occupied, new jobs queue as **Pending** until a slot opens. The scheduler dispatches queued jobs in order of creation time.
|
||||
|
||||
<Note>
|
||||
Increasing `MAX_PARALLEL_JOBS` increases LLM API concurrency. Set it according to your API rate limits and available system resources.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Job Tools
|
||||
|
||||
Four built-in tools let the agent manage jobs at runtime:
|
||||
|
||||
| Tool | Description |
|
||||
|--------------|----------------------------------------------------------------|
|
||||
| `create_job` | Create a new job with a given description and optional context |
|
||||
| `list_jobs` | List all active jobs with their current state and metadata |
|
||||
| `job_status` | Get detailed status for a specific job by ID |
|
||||
| `cancel_job` | Cancel an InProgress or Pending job |
|
||||
148
docs/capabilities/jobs/self-repair.mdx
Normal file
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: Self-Repair & Stuck Jobs
|
||||
sidebarTitle: Self-Repair
|
||||
description: Automatic detection and recovery of stuck jobs
|
||||
---
|
||||
|
||||
IronClaw monitors all running jobs and automatically recovers those that stop making progress — without requiring human intervention.
|
||||
|
||||
---
|
||||
|
||||
## What Is a Stuck Job?
|
||||
|
||||
A job is considered **stuck** when it has been in the **InProgress** state for longer than the configured timeout without producing any output, tool calls, or state updates.
|
||||
|
||||
Common causes:
|
||||
- LLM provider timeout or rate limit with no retry budget remaining
|
||||
- Tool call hanging on an unresponsive external service
|
||||
- Container resource exhaustion (OOM, CPU throttle)
|
||||
- Network partition between the agent and a sandboxed worker
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# Enable self-repair (default: true)
|
||||
SELF_REPAIR_ENABLED=true
|
||||
|
||||
# How long before a job is considered stuck (seconds)
|
||||
SELF_REPAIR_TIMEOUT_SECS=300
|
||||
|
||||
# Maximum recovery attempts before marking job as Failed
|
||||
SELF_REPAIR_MAX_RETRIES=3
|
||||
|
||||
# How often the monitor checks for stuck jobs (seconds)
|
||||
SELF_REPAIR_CHECK_INTERVAL_SECS=60
|
||||
```
|
||||
|
||||
<Note>
|
||||
`SELF_REPAIR_TIMEOUT_SECS` should be set lower than `SANDBOX_TIMEOUT_SECS`. The sandbox enforces a hard kill; self-repair is a soft recovery that runs before the hard kill triggers.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Detection
|
||||
|
||||
The self-repair system runs as a background task alongside the scheduler. It periodically scans all InProgress jobs and compares the last-activity timestamp against the stuck threshold.
|
||||
|
||||
```
|
||||
[Self-Repair Monitor]
|
||||
↓
|
||||
For each InProgress job:
|
||||
last_activity > SELF_REPAIR_TIMEOUT?
|
||||
↓ yes
|
||||
Transition: InProgress → Stuck
|
||||
↓
|
||||
Log failure to tool_failures table
|
||||
↓
|
||||
Attempt recovery
|
||||
```
|
||||
|
||||
The `tool_failures` table accumulates failure records per job and per tool. This data is used to assess whether recovery is worth attempting again or whether the job should be moved directly to **Failed**.
|
||||
|
||||
---
|
||||
|
||||
## Recovery Flow
|
||||
|
||||
When a stuck job is detected, the self-repair system attempts to restart it:
|
||||
|
||||
<Steps>
|
||||
<Step title="Transition to Stuck">
|
||||
The job state changes from InProgress to Stuck. The event is logged with the failure reason and timestamp.
|
||||
</Step>
|
||||
|
||||
<Step title="Inspect failure history">
|
||||
The system checks the `tool_failures` table for this job. If the job has exceeded the maximum retry count, it is transitioned directly to **Failed** and recovery is skipped.
|
||||
</Step>
|
||||
|
||||
<Step title="Re-enter InProgress">
|
||||
If retries remain, the job transitions back to InProgress. A new worker picks it up and resumes execution from the last saved checkpoint.
|
||||
</Step>
|
||||
|
||||
<Step title="Evaluate outcome">
|
||||
If the job completes successfully, the failure records are cleared. If it gets stuck again, the cycle repeats until the retry limit is reached, at which point the job fails permanently.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### State Diagram
|
||||
|
||||
```
|
||||
InProgress
|
||||
↓ (timeout detected)
|
||||
Stuck ──────────────────────► Failed (retry limit reached)
|
||||
↓ (retry available)
|
||||
InProgress
|
||||
↓
|
||||
Completed (or back to Stuck)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tool Failure Tracking
|
||||
|
||||
Every time a tool fails during job execution, the event is recorded:
|
||||
|
||||
| Field | Description |
|
||||
|---------------|--------------------------------------|
|
||||
| `job_id` | The job that experienced the failure |
|
||||
| `tool_name` | Which tool failed |
|
||||
| `error` | Error message or failure reason |
|
||||
| `occurred_at` | Timestamp of the failure |
|
||||
|
||||
This history is available to the worker on retry, allowing it to avoid repeating the same failing tool call or to select an alternative approach.
|
||||
|
||||
---
|
||||
|
||||
## Observability
|
||||
|
||||
Stuck and recovered jobs are visible in:
|
||||
|
||||
- **Job history** — The web gateway's job list shows state transitions with timestamps
|
||||
- **Logs** — `RUST_LOG=ironclaw::agent::self_repair=debug` for detailed repair events
|
||||
- **`list_jobs` tool** — Shows current state including Stuck jobs
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Job repeatedly gets stuck" icon="refresh-cw">
|
||||
- Check `RUST_LOG=ironclaw::agent::self_repair=debug` for failure reasons
|
||||
- Inspect tool failure records via `job_status` tool
|
||||
- Consider increasing `SELF_REPAIR_TIMEOUT_SECS` if the job is legitimately long-running
|
||||
- Check network connectivity to LLM provider and external services
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Job fails immediately after recovery" icon="x-circle">
|
||||
- The tool failure history may reveal a specific tool that always fails
|
||||
- Check whether the external service the tool depends on is available
|
||||
- Review sandbox logs if the job runs in a container (`SANDBOX_ENABLED=true`)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Stuck jobs not being detected" icon="search">
|
||||
- Verify `SELF_REPAIR_ENABLED=true`
|
||||
- Check that `SELF_REPAIR_TIMEOUT_SECS` is not set too high
|
||||
- Confirm the self-repair monitor is running: look for `self_repair` in startup logs
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
318
docs/capabilities/llm-providers.md
Normal file
@@ -0,0 +1,318 @@
|
||||
---
|
||||
title: Inference Providers
|
||||
description: IronClaw readily supports multiple LLM providers
|
||||
---
|
||||
|
||||
IronClaw supports multiple LLM providers out of the box, including NEAR AI , Anthropic, OpenAI, Google Gemini, GitHub Copilot, Ollama, AWS Bedrock, and any OpenAI-compatible endpoint.
|
||||
|
||||
Providers can be configured via environment variables or the onboarding wizard. IronClaw's modular architecture allows seamless integration with new providers by implementing the `LLMProvider` trait.
|
||||
|
||||
#### Configuring a Provider
|
||||
To config a new provider, simply run the onboarding wizard:
|
||||
|
||||
```bash
|
||||
ironclaw onboard --provider-only
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Provider Overview
|
||||
|
||||
| Provider | Backend value | Requires API key | Notes |
|
||||
|-----------------------|---------------------|------------------------|---------------------------------|
|
||||
| NEAR AI | `nearai` | OAuth (browser) | Multi-model |
|
||||
| Anthropic | `anthropic` | `ANTHROPIC_API_KEY` | Claude models |
|
||||
| OpenAI | `openai` | `OPENAI_API_KEY` | GPT models |
|
||||
| Google Gemini | `gemini_oauth` | OAuth (browser) | Gemini models; function calling |
|
||||
| io.net | `ionet` | `IONET_API_KEY` | Intelligence API |
|
||||
| Mistral | `mistral` | `MISTRAL_API_KEY` | Mistral models |
|
||||
| Yandex AI Studio | `yandex` | `YANDEX_API_KEY` | YandexGPT models |
|
||||
| MiniMax | `minimax` | `MINIMAX_API_KEY` | MiniMax-M2.7 models |
|
||||
| Cloudflare Workers AI | `cloudflare` | `CLOUDFLARE_API_KEY` | Access to Workers AI |
|
||||
| GitHub Copilot | `github_copilot` | `GITHUB_COPILOT_TOKEN` | Multi-models |
|
||||
| Ollama | `ollama` | No | Local inference |
|
||||
| AWS Bedrock | `bedrock` | AWS credentials | Native Converse API |
|
||||
| OpenRouter | `openai_compatible` | `LLM_API_KEY` | 300+ models |
|
||||
| Together AI | `openai_compatible` | `LLM_API_KEY` | Fast inference |
|
||||
| Fireworks AI | `openai_compatible` | `LLM_API_KEY` | Fast inference |
|
||||
| vLLM / LiteLLM | `openai_compatible` | Optional | Self-hosted |
|
||||
| LM Studio | `openai_compatible` | No | Local GUI |
|
||||
|
||||
---
|
||||
|
||||
## NEAR AI
|
||||
|
||||
```env
|
||||
NEARAI_MODEL=claude-3-5-sonnet-20241022
|
||||
NEARAI_BASE_URL=https://private.near.ai
|
||||
```
|
||||
|
||||
Popular models: `Qwen/Qwen3.5-122B-A10B`, `black-forest-labs/FLUX.2-klein-4B`, `zai-org/GLM-5-FP8`
|
||||
|
||||
---
|
||||
|
||||
## Anthropic (Claude)
|
||||
|
||||
```env
|
||||
LLM_BACKEND=anthropic
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
```
|
||||
|
||||
Popular models: `claude-sonnet-4-20250514`, `claude-3-5-sonnet-20241022`, `claude-3-5-haiku-20241022`
|
||||
|
||||
---
|
||||
|
||||
## OpenAI (GPT)
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai
|
||||
OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
Popular models: `gpt-4o`, `gpt-4o-mini`, `o3-mini`
|
||||
|
||||
---
|
||||
|
||||
## Google Gemini (OAuth)
|
||||
|
||||
Uses Google OAuth with PKCE (S256) for authentication — no API key required.
|
||||
On first run, a browser opens for Google account login. Credentials (including
|
||||
refresh token) are saved to `~/.gemini/oauth_creds.json` with `0600` permissions.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=gemini_oauth
|
||||
GEMINI_MODEL=gemini-2.5-flash
|
||||
```
|
||||
|
||||
### Supported features
|
||||
|
||||
| Feature | Status | Notes |
|
||||
|--------------------|--------|-----------------------------------------------------------------------------------------------|
|
||||
| Function calling | ✅ | `functionDeclarations` / `functionCall` / `functionResponse` |
|
||||
| `generationConfig` | ✅ | `temperature`, `maxOutputTokens` passed from request |
|
||||
| `thinkingConfig` | ✅ | `thinkingBudget`/`thinkingLevel` for thinking-capable models (does NOT set `includeThoughts`) |
|
||||
| `toolConfig` | ✅ | `functionCallingConfig.mode`: `AUTO`/`ANY`/`NONE` |
|
||||
| SSE streaming | ✅ | Cloud Code API with `streamGenerateContent?alt=sse` |
|
||||
| Token refresh | ✅ | Automatic via refresh token |
|
||||
|
||||
### Popular models
|
||||
|
||||
| Model | ID | Notes |
|
||||
|-----------------------------|--------------------------------------|-----------------------------|
|
||||
| Gemini 3.1 Pro | `gemini-3.1-pro-preview` | Latest, strongest reasoning |
|
||||
| Gemini 3.1 Pro Custom Tools | `gemini-3.1-pro-preview-customtools` | Enhanced tool use |
|
||||
| Gemini 3 Pro | `gemini-3-pro-preview` | Preview |
|
||||
| Gemini 3 Flash | `gemini-3-flash-preview` | Fast preview with thinking |
|
||||
| Gemini 3.1 Flash Lite | `gemini-3.1-flash-lite-preview` | Preview, lightweight |
|
||||
| Gemini 2.5 Pro | `gemini-2.5-pro` | Stable, strong reasoning |
|
||||
| Gemini 2.5 Flash | `gemini-2.5-flash` | Fast, good quality |
|
||||
| Gemini 2.5 Flash Lite | `gemini-2.5-flash-lite` | Fastest, lightweight |
|
||||
|
||||
### Cloud Code API vs standard API
|
||||
|
||||
Models containing `-preview` (with hyphen) or `gemini-3` in the name, as well
|
||||
as any `gemini-` model with major version >= 2, route through the Cloud Code
|
||||
API (`cloudcode-pa.googleapis.com`) which supports SSE streaming
|
||||
and project-scoped access. Other models use the standard Generative Language
|
||||
API (`generativelanguage.googleapis.com`).
|
||||
|
||||
---
|
||||
|
||||
## GitHub Copilot
|
||||
|
||||
GitHub Copilot exposes chat endpoint at
|
||||
`https://api.githubcopilot.com`. IronClaw uses that endpoint directly through the
|
||||
built-in `github_copilot` provider.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=github_copilot
|
||||
GITHUB_COPILOT_TOKEN=gho_...
|
||||
GITHUB_COPILOT_MODEL=gpt-4o
|
||||
# Optional advanced headers if your setup needs them:
|
||||
# GITHUB_COPILOT_EXTRA_HEADERS=Copilot-Integration-Id:vscode-chat
|
||||
```
|
||||
|
||||
`ironclaw onboard` can acquire this token for you using GitHub device login. If you
|
||||
already signed into Copilot through VS Code or a JetBrains IDE, you can also reuse
|
||||
the `oauth_token` stored in `~/.config/github-copilot/apps.json`. If you prefer,
|
||||
`LLM_BACKEND=github-copilot` also works as an alias.
|
||||
|
||||
Popular models vary by subscription, but `gpt-4o` is a safe default. IronClaw keeps
|
||||
model entry manual for this provider because GitHub Copilot model listing may require
|
||||
extra integration headers on some clients. IronClaw automatically injects the standard
|
||||
VS Code identity headers (`User-Agent`, `Editor-Version`, `Editor-Plugin-Version`,
|
||||
`Copilot-Integration-Id`) and lets you override them with
|
||||
`GITHUB_COPILOT_EXTRA_HEADERS`.
|
||||
|
||||
---
|
||||
|
||||
## Ollama (local)
|
||||
|
||||
Install Ollama from [ollama.com](https://ollama.com), pull a model, then:
|
||||
|
||||
```env
|
||||
LLM_BACKEND=ollama
|
||||
OLLAMA_MODEL=llama3.2
|
||||
# OLLAMA_BASE_URL=http://localhost:11434 # default
|
||||
```
|
||||
|
||||
Pull a model first: `ollama pull llama3.2`
|
||||
|
||||
---
|
||||
|
||||
## MiniMax
|
||||
|
||||
[MiniMax](https://platform.minimax.io) provides high-performance language models with 204,800 token context windows.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=minimax
|
||||
MINIMAX_API_KEY=...
|
||||
```
|
||||
|
||||
Available models: `MiniMax-M2.7` (default), `MiniMax-M2.7-highspeed`, `MiniMax-M2.5`, `MiniMax-M2.5-highspeed`
|
||||
|
||||
To use the China mainland endpoint, set:
|
||||
|
||||
```env
|
||||
MINIMAX_BASE_URL=https://api.minimaxi.com/v1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AWS Bedrock (requires `--features bedrock`)
|
||||
|
||||
Uses the native AWS Converse API via `aws-sdk-bedrockruntime`. Supports standard AWS
|
||||
authentication methods: IAM credentials, SSO profiles, and instance roles.
|
||||
|
||||
> **Build prerequisite:** The `aws-lc-sys` crate (transitive dependency via AWS SDK)
|
||||
> requires **CMake** to compile. Install it before building with `--features bedrock`:
|
||||
> - macOS: `brew install cmake`
|
||||
> - Ubuntu/Debian: `sudo apt install cmake`
|
||||
> - Fedora: `sudo dnf install cmake`
|
||||
|
||||
### With AWS credentials (IAM, SSO, instance roles)
|
||||
|
||||
```env
|
||||
LLM_BACKEND=bedrock
|
||||
BEDROCK_MODEL=anthropic.claude-opus-4-6-v1
|
||||
BEDROCK_REGION=us-east-1
|
||||
BEDROCK_CROSS_REGION=us
|
||||
# AWS_PROFILE=my-sso-profile # optional, for named profiles
|
||||
```
|
||||
|
||||
The AWS SDK credential chain automatically resolves credentials from environment
|
||||
variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`), shared credentials file
|
||||
(`~/.aws/credentials`), SSO profiles, and EC2/ECS instance roles.
|
||||
|
||||
### Cross-region inference
|
||||
|
||||
Set `BEDROCK_CROSS_REGION` to route requests across AWS regions for capacity:
|
||||
|
||||
| Prefix | Routing |
|
||||
|-----------|----------------------------------------------|
|
||||
| `us` | US regions (us-east-1, us-east-2, us-west-2) |
|
||||
| `eu` | European regions |
|
||||
| `apac` | Asia-Pacific regions |
|
||||
| `global` | All commercial AWS regions |
|
||||
| _(unset)_ | Single-region only |
|
||||
|
||||
### Popular Bedrock model IDs
|
||||
|
||||
| Model | ID |
|
||||
|-------------------|---------------------------------------------|
|
||||
| Claude Opus 4.6 | `anthropic.claude-opus-4-6-v1` |
|
||||
| Claude Sonnet 4.5 | `anthropic.claude-sonnet-4-5-20250929-v1:0` |
|
||||
| Claude Haiku 4.5 | `anthropic.claude-haiku-4-5-20251001-v1:0` |
|
||||
| Amazon Nova Pro | `amazon.nova-pro-v1:0` |
|
||||
| Llama 4 Maverick | `meta.llama4-maverick-17b-instruct-v1:0` |
|
||||
|
||||
---
|
||||
|
||||
## OpenAI-Compatible Endpoints
|
||||
|
||||
All providers below use `LLM_BACKEND=openai_compatible`. Set `LLM_BASE_URL` to the
|
||||
provider's OpenAI-compatible endpoint and `LLM_API_KEY` to your API key.
|
||||
|
||||
### OpenRouter
|
||||
|
||||
[OpenRouter](https://openrouter.ai) routes to 300+ models from a single API key.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=https://openrouter.ai/api/v1
|
||||
LLM_API_KEY=sk-or-...
|
||||
LLM_MODEL=anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
Popular OpenRouter model IDs:
|
||||
|
||||
| Model | ID |
|
||||
|------------------|--------------------------------------------|
|
||||
| Claude Sonnet 4 | `anthropic/claude-sonnet-4` |
|
||||
| GPT-4o | `openai/gpt-4o` |
|
||||
| Llama 4 Maverick | `meta-llama/llama-4-maverick` |
|
||||
| Gemini 2.0 Flash | `google/gemini-2.0-flash-001` |
|
||||
| Mistral Small | `mistralai/mistral-small-3.1-24b-instruct` |
|
||||
|
||||
Browse all models at [openrouter.ai/models](https://openrouter.ai/models).
|
||||
|
||||
### Together AI
|
||||
|
||||
[Together AI](https://www.together.ai) provides fast inference for open-source models.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=https://api.together.xyz/v1
|
||||
LLM_API_KEY=...
|
||||
LLM_MODEL=meta-llama/Llama-3.3-70B-Instruct-Turbo
|
||||
```
|
||||
|
||||
Popular Together AI model IDs:
|
||||
|
||||
| Model | ID |
|
||||
|---------------|-------------------------------------------|
|
||||
| Llama 3.3 70B | `meta-llama/Llama-3.3-70B-Instruct-Turbo` |
|
||||
| DeepSeek R1 | `deepseek-ai/DeepSeek-R1` |
|
||||
| Qwen 2.5 72B | `Qwen/Qwen2.5-72B-Instruct-Turbo` |
|
||||
|
||||
### Fireworks AI
|
||||
|
||||
[Fireworks AI](https://fireworks.ai) offers fast inference with compound AI system support.
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=https://api.fireworks.ai/inference/v1
|
||||
LLM_API_KEY=fw_...
|
||||
LLM_MODEL=accounts/fireworks/models/llama4-maverick-instruct-basic
|
||||
```
|
||||
|
||||
### vLLM / LiteLLM (self-hosted)
|
||||
|
||||
For self-hosted inference servers:
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=http://localhost:8000/v1
|
||||
LLM_API_KEY=token-abc123 # set to any string if auth is not configured
|
||||
LLM_MODEL=meta-llama/Llama-3.1-8B-Instruct
|
||||
```
|
||||
|
||||
LiteLLM proxy (forwards to any backend, including Bedrock, Vertex, Azure):
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=http://localhost:4000/v1
|
||||
LLM_API_KEY=sk-...
|
||||
LLM_MODEL=gpt-4o # as configured in litellm config.yaml
|
||||
```
|
||||
|
||||
### LM Studio (local GUI)
|
||||
|
||||
Start LM Studio's local server, then:
|
||||
|
||||
```env
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=http://localhost:1234/v1
|
||||
LLM_MODEL=llama-3.2-3b-instruct-q4_K_M
|
||||
# LLM_API_KEY is not required for LM Studio
|
||||
```
|
||||
135
docs/capabilities/memory/identity.mdx
Normal file
@@ -0,0 +1,135 @@
|
||||
---
|
||||
title: Identity Files
|
||||
sidebarTitle: Identity Files
|
||||
description: Files injected into the LLM system prompt to give the agent persistent identity
|
||||
---
|
||||
|
||||
Identity files are special memory documents that are automatically injected into the LLM system prompt on every turn. They give the agent a consistent identity, behavioral instructions, and knowledge about you — persisting across sessions and restarts.
|
||||
|
||||
## The Four Identity Files
|
||||
|
||||
---
|
||||
|
||||
## Identity Files
|
||||
|
||||
Four special files are automatically injected into the LLM system prompt on every turn:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `AGENTS.md` | Behavioral rules and operating instructions for the agent |
|
||||
| `SOUL.md` | Values, personality, and principles the agent embodies |
|
||||
| `USER.md` | Information about you — your preferences, context, and working style |
|
||||
| `IDENTITY.md` | Combined role and identity definition |
|
||||
|
||||
All four files live in the workspace root and can be either manually edited by you, or written by the agent using the `memory_write` tool.
|
||||
|
||||
---
|
||||
|
||||
## AGENTS.md
|
||||
|
||||
Behavioral instructions. What the agent should and should not do, how it should handle specific situations, and any workflow rules you want enforced.
|
||||
|
||||
```markdown
|
||||
# Agent Instructions
|
||||
|
||||
## Communication Style
|
||||
- Be concise. Lead with the answer, then explain if needed.
|
||||
- Use bullet points for lists, not prose paragraphs.
|
||||
- When uncertain, say so explicitly rather than guessing.
|
||||
|
||||
## Tool Use
|
||||
- Always search memory before answering questions about past work.
|
||||
- Write summaries to memory after completing significant tasks.
|
||||
- Do not execute shell commands without explaining what they do first.
|
||||
|
||||
## Security
|
||||
- Never log or store API keys or secrets.
|
||||
- Confirm before deleting files.
|
||||
- Do not access external URLs not directly related to the current task.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SOUL.md
|
||||
|
||||
Values and personality. How the agent approaches problems, what it prioritizes, and what kind of assistant it aims to be.
|
||||
|
||||
```markdown
|
||||
# Soul
|
||||
|
||||
I am a careful, methodical assistant. I value:
|
||||
|
||||
- **Accuracy over speed** — I take time to verify before acting.
|
||||
- **Transparency** — I explain my reasoning, especially when uncertain.
|
||||
- **Security-first** — I treat all external data as potentially adversarial.
|
||||
- **User autonomy** — I recommend, but the user decides.
|
||||
|
||||
When I encounter ambiguity, I ask clarifying questions rather than assuming.
|
||||
When I make a mistake, I acknowledge it directly and correct it.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## USER.md
|
||||
|
||||
Information about you. The agent reads this file to understand your context, preferences, and working patterns without needing to ask repeatedly.
|
||||
|
||||
```markdown
|
||||
# User Profile
|
||||
|
||||
## Context
|
||||
- Working on: IronClaw, a Rust-based secure AI assistant
|
||||
- Primary languages: Rust, Python, bash
|
||||
- Environment: Linux (Ubuntu 24.04), zsh, Neovim
|
||||
- Source tree: ~/projects/ironclaw
|
||||
|
||||
## Preferences
|
||||
- Prefer explicit error handling over unwrap/expect
|
||||
- Use conventional commits format
|
||||
- Write tests before marking tasks complete
|
||||
- Prefer compact code without excessive comments
|
||||
|
||||
## Working Style
|
||||
- Morning: architecture and planning
|
||||
- Afternoon: implementation
|
||||
- End of day: write summary to memory at daily/<date>.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## IDENTITY.md
|
||||
|
||||
A combined identity definition, often used to give the agent a specific role or persona for a particular workspace or project.
|
||||
|
||||
```markdown
|
||||
# Identity
|
||||
|
||||
I am IronClaw's embedded development assistant, running inside the ironclaw-src workspace.
|
||||
|
||||
My role is to help with:
|
||||
- Rust development (cargo, clippy, testing)
|
||||
- Architecture decisions and design review
|
||||
- Security analysis and threat modeling
|
||||
- Documentation and specification writing
|
||||
|
||||
I have full knowledge of the IronClaw codebase and its conventions as described in CLAUDE.md.
|
||||
I prioritize correctness, security, and maintainability in all recommendations.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How Injection Works
|
||||
|
||||
At the start of every LLM call, the agent reads all four identity files from the workspace and prepends them to the system prompt:
|
||||
|
||||
```
|
||||
[AGENTS.md content]
|
||||
[SOUL.md content]
|
||||
[USER.md content]
|
||||
[IDENTITY.md content]
|
||||
[Skill injections]
|
||||
[Conversation history]
|
||||
[Current message]
|
||||
```
|
||||
|
||||
Files that do not exist are silently skipped — you do not need all four. Start with whichever is most useful for your workflow.
|
||||
52
docs/capabilities/memory/memory.mdx
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: Persistent Memory
|
||||
description: Your agent's durable, searchable long-term memory system
|
||||
---
|
||||
|
||||
The LLM context window is temporary. Everything in it disappears when the conversation ends. The memory system is permanent: documents written to memory survive indefinitely and are retrievable via search from any session.
|
||||
|
||||
This means the agent must be proactive about writing. Before answering a question about prior work, the agent should search memory. Before ending a task that produced useful information, the agent should write a summary.
|
||||
|
||||
<Note>
|
||||
The agent is instructed to call `memory_search` before answering questions about past work, prior decisions, or previously stored information. If you feel the agent has forgotten something, try asking it to search memory explicitly.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Workspace Structure
|
||||
|
||||
The workspace uses a filesystem-like path hierarchy. Documents live at paths you define:
|
||||
|
||||
| Example Path | Use Case |
|
||||
|-------------------------------|------------------------------------------|
|
||||
| `context/vision.md` | Project goals and direction |
|
||||
| `context/architecture.md` | System design decisions |
|
||||
| `daily/2024-01-15.md` | Daily notes and logs |
|
||||
| `daily/standup.md` | Latest standup draft (overwritten daily) |
|
||||
| `projects/ironclaw/notes.md` | Project-specific notes |
|
||||
| `inbox/task-20240115.md` | Incoming tasks to process |
|
||||
| `processed/task-20240115.md` | Processed and archived tasks |
|
||||
| `ops/incidents/2024-01-15.md` | Incident records |
|
||||
| `AGENTS.md` | Agent behavior instructions |
|
||||
| `SOUL.md` | Agent values and personality |
|
||||
|
||||
Paths are arbitrary strings. Use whatever structure makes sense for your workflow. The `memory_tree` tool shows all paths organized as a directory tree.
|
||||
|
||||
---
|
||||
|
||||
## Four Memory Tools
|
||||
|
||||
| Tool | Description |
|
||||
|-----------------|--------------------------------------------------------------------------------------------------------------|
|
||||
| `memory_search` | Hybrid FTS + vector search. Call this before answering questions about prior work. Returns ranked results. |
|
||||
| `memory_write` | Write a document to a path. Creates or overwrites. Supports structured content (markdown, JSON, plain text). |
|
||||
| `memory_read` | Read a specific document by exact path. |
|
||||
| `memory_tree` | List all paths in the workspace as a tree. Use for discovery and navigation. |
|
||||
|
||||
---
|
||||
|
||||
## Efficient Retrieval with Vector Search
|
||||
|
||||
You can configure the memory to be persisted as a vector store, which allows for fast semantic search and retrieval during the initial onboarding.
|
||||
|
||||
This is ideal for larger workspaces or when you want the agent to have quick access to a large amount of information.
|
||||
33
docs/capabilities/overview.mdx
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Capabilities Overview
|
||||
sidebarTitle: Overview
|
||||
description: Discover what makes IronClaw unique
|
||||
---
|
||||
|
||||
IronClaw combines long-term memory, event-driven automation, parallel execution, and strict isolation controls so agents can run real workflows safely.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Security" icon="shield" href="/security">
|
||||
Defense-in-depth controls for prompt safety, sandboxing, leak detection, and network boundaries.
|
||||
</Card>
|
||||
|
||||
<Card title="Memory" icon="database" href="/capabilities/memory/memory">
|
||||
Durable, searchable memory with identity files that persist behavior and context across sessions.
|
||||
</Card>
|
||||
|
||||
<Card title="Routines" icon="clock" href="/capabilities/routines/cron">
|
||||
Scheduled, heartbeat, and reactive execution models for proactive and event-driven automation.
|
||||
</Card>
|
||||
|
||||
<Card title="Jobs" icon="suitcase" href="/capabilities/jobs/jobs">
|
||||
Parallel job orchestration with state transitions, retries, and self-repair for stuck execution.
|
||||
</Card>
|
||||
|
||||
<Card title="Skills" icon="sparkles" href="/capabilities/skills">
|
||||
Context-activated prompt extensions with scoring, gating, and trust-based tool attenuation.
|
||||
</Card>
|
||||
|
||||
<Card title="Sandboxed Tools" icon="cubes" href="/capabilities/sandboxed-tools">
|
||||
Wasm-based tool isolation with explicit capabilities, resource limits, and controlled I/O.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
46
docs/capabilities/routines/cron.mdx
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Cron Routines
|
||||
description: Schedule recurring tasks with cron expressions
|
||||
---
|
||||
|
||||
Cron routines fire on a repeating schedule using standard cron expressions. Use them for any task that should happen at a predictable time — daily reports, weekly cleanups, hourly checks.
|
||||
|
||||
---
|
||||
|
||||
## Creating a Cron Routine
|
||||
|
||||
Describe the schedule and task to the agent. It will call `routine_create` on your behalf:
|
||||
|
||||
```
|
||||
Create a routine that runs every weekday at 9am and summarizes
|
||||
what I worked on yesterday by reading my daily notes, then
|
||||
writes a standup draft to memory at daily/standup.md.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution Model
|
||||
|
||||
When a cron routine fires:
|
||||
|
||||
1. The routine engine creates a new agent job with the action prompt
|
||||
2. The job runs through the full agent loop — LLM reasoning, tool calls, safety layer
|
||||
3. Output is written to memory or sent via a channel notification (if configured)
|
||||
4. The run is recorded in routine history, accessible via `routine_history`
|
||||
|
||||
Routine jobs run as standard jobs and count against `MAX_PARALLEL_JOBS`. If the limit is reached when a cron fires, the routine job queues as Pending.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# Enable routines
|
||||
ROUTINES_ENABLED=true
|
||||
|
||||
# Cron tick interval (how often the engine checks for due jobs)
|
||||
ROUTINES_CRON_INTERVAL=60 # seconds
|
||||
|
||||
# Max routines running simultaneously
|
||||
ROUTINES_MAX_CONCURRENT=3
|
||||
```
|
||||
186
docs/capabilities/routines/heartbeat.mdx
Normal file
@@ -0,0 +1,186 @@
|
||||
---
|
||||
title: Heartbeat System
|
||||
sidebarTitle: Heartbeat
|
||||
description: Periodic checks and execution
|
||||
---
|
||||
|
||||
The heartbeat system gives IronClaw agency between conversations. Every 30 minutes (by default), it reads `HEARTBEAT.md` from the workspace and executes a checklist of proactive tasks — without you having to ask.
|
||||
|
||||
<Tip>
|
||||
You can setup how often the agent checks the heartbeat list
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## What Heartbeat Does
|
||||
|
||||
On each heartbeat tick:
|
||||
|
||||
1. Reads `HEARTBEAT.md` from the workspace root
|
||||
2. Runs the checklist items as an agent job
|
||||
3. If the job produces findings or output, sends a notification to the configured channel
|
||||
4. Records the run in the `heartbeat_state` table
|
||||
|
||||
If `HEARTBEAT.md` does not exist or is empty, the tick is a no-op.
|
||||
|
||||
The heartbeat job runs through the full agent loop — LLM reasoning, tool calls, safety layer — with the same capabilities as a manually triggered job.
|
||||
|
||||
---
|
||||
|
||||
## HEARTBEAT.md Format
|
||||
|
||||
Write `HEARTBEAT.md` as a checklist of tasks. The agent reads this as its instructions for each periodic run:
|
||||
|
||||
```markdown
|
||||
# Heartbeat Checklist
|
||||
|
||||
## Daily Tasks
|
||||
- [ ] Check memory at daily/ for yesterday's notes. If missing, remind the user.
|
||||
- [ ] Search memory for any items tagged as "follow-up" or "urgent" and list them.
|
||||
- [ ] Read ops/stuck-jobs.md if it exists and summarize any unresolved incidents.
|
||||
|
||||
## Weekly Tasks (run only on Mondays)
|
||||
- [ ] Summarize the week's daily notes into a weekly summary at weekly/<date>.md
|
||||
- [ ] Check for any routines that haven't run in the past 7 days and flag them.
|
||||
|
||||
## Always
|
||||
- [ ] If any of the above produce findings, write a summary to memory at heartbeat/latest.md
|
||||
- [ ] Only notify the user if there are actionable items — do not send empty pings.
|
||||
```
|
||||
|
||||
The agent interprets the checklist and executes each item using available tools. Conditional items ("run only on Mondays") are evaluated by the LLM using the current date.
|
||||
|
||||
---
|
||||
|
||||
## Notification Behavior
|
||||
|
||||
After each tick, if the job produces output that warrants user attention, the heartbeat system sends a notification to the configured channel. If nothing actionable was found, no notification is sent — heartbeat is designed to be quiet unless it has something useful to say.
|
||||
|
||||
Findings are also written to memory at `heartbeat/latest.md` (if your HEARTBEAT.md instructs this), making them searchable in future sessions.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# Enable heartbeat (default: true)
|
||||
HEARTBEAT_ENABLED=true
|
||||
|
||||
# Interval between ticks in seconds (default: 1800 = 30 minutes)
|
||||
HEARTBEAT_INTERVAL_SECS=1800
|
||||
|
||||
# Channel to send notifications to
|
||||
HEARTBEAT_NOTIFY_CHANNEL=tui # tui, web, telegram, webhook
|
||||
|
||||
# User ID to notify
|
||||
HEARTBEAT_NOTIFY_USER=default
|
||||
```
|
||||
|
||||
<Note>
|
||||
Set `HEARTBEAT_INTERVAL_SECS=3600` (1 hour) or higher if the heartbeat job is too frequent for your LLM API rate limits or budget. The heartbeat runs as a normal job and consumes tokens.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Writing HEARTBEAT.md
|
||||
|
||||
Ask the agent to create or update the heartbeat checklist:
|
||||
|
||||
```
|
||||
Write HEARTBEAT.md with tasks to check my inbox/ folder every 30 minutes
|
||||
and summarize any new items.
|
||||
```
|
||||
|
||||
Or edit the file manually:
|
||||
|
||||
```
|
||||
memory_write path="HEARTBEAT.md" content="
|
||||
# Heartbeat Checklist
|
||||
|
||||
- [ ] List all memory documents in inbox/ — if any exist, summarize and move to processed/
|
||||
- [ ] Check if daily/<today>.md exists — if not, create a daily entry template
|
||||
- [ ] Only notify if inbox/ had items or daily notes were missing
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Example HEARTBEAT.md Files
|
||||
|
||||
### Minimal — Inbox Monitor
|
||||
|
||||
```markdown
|
||||
# Heartbeat
|
||||
|
||||
- [ ] Check inbox/ for new documents. Process and move to processed/. Notify only if items were found.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Developer Workflow
|
||||
|
||||
```markdown
|
||||
# Heartbeat Checklist
|
||||
|
||||
## Checks
|
||||
- [ ] Read ops/incidents/ — summarize any open incidents older than 24 hours
|
||||
- [ ] Check for stuck jobs in the last hour
|
||||
- [ ] Look for daily/<today>.md — create it with a timestamp if missing
|
||||
|
||||
## Output
|
||||
- [ ] Write findings to heartbeat/latest.md
|
||||
- [ ] Notify only if there are open incidents or stuck jobs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Personal Assistant
|
||||
|
||||
```markdown
|
||||
# Heartbeat
|
||||
|
||||
- [ ] Search memory for items tagged "reminder" or "todo"
|
||||
- [ ] Check if any items are due today based on their content
|
||||
- [ ] Summarize time-sensitive items and notify
|
||||
- [ ] Do not notify if nothing is due
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Heartbeat vs Routines
|
||||
|
||||
Heartbeat and cron routines serve similar purposes but differ in design:
|
||||
|
||||
| Feature | Heartbeat | Cron Routine |
|
||||
|---------------|--------------------------|-------------------------------------|
|
||||
| Configuration | Single HEARTBEAT.md file | Per-routine configuration |
|
||||
| Schedule | Fixed global interval | Custom per-routine cron expression |
|
||||
| Scope | Single checklist job | Multiple independent jobs |
|
||||
| Complexity | Simple — edit one file | Flexible — manage multiple routines |
|
||||
|
||||
Use heartbeat for a unified set of periodic checks. Use cron routines when you need different schedules for different tasks, or when tasks should run independently.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Heartbeat not firing" icon="clock">
|
||||
- Verify `HEARTBEAT_ENABLED=true` in your configuration
|
||||
- Check startup logs for `heartbeat` to confirm the system started
|
||||
- Confirm `HEARTBEAT_INTERVAL_SECS` is set to a reasonable value
|
||||
- Verify `HEARTBEAT.md` exists in the workspace root via `memory_read path="HEARTBEAT.md"`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Getting too many notifications" icon="bell">
|
||||
- Update HEARTBEAT.md to add a condition: "only notify if there are actionable items"
|
||||
- Increase `HEARTBEAT_INTERVAL_SECS` to reduce frequency
|
||||
- Make checklist items more specific so the agent doesn't over-report
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Heartbeat job consuming too many tokens" icon="coins">
|
||||
- Simplify HEARTBEAT.md — fewer checklist items mean fewer LLM calls
|
||||
- Increase `HEARTBEAT_INTERVAL_SECS` to reduce frequency
|
||||
- Add guardrails-style instructions to HEARTBEAT.md: "Use at most 5 tool calls per run"
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
124
docs/capabilities/routines/reactive.mdx
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Reactive Routines
|
||||
description: Event-driven and webhook-triggered automation
|
||||
---
|
||||
|
||||
Reactive routines fire in response to events rather than a schedule. Agents use them to automate workflows that should happen when something occurs — a file is created, a webhook fires, or an internal agent event is raised.
|
||||
|
||||
---
|
||||
|
||||
## Trigger Types
|
||||
|
||||
### Event Triggers
|
||||
|
||||
Internal agent events that reactive routines can listen for:
|
||||
|
||||
| Event | When It Fires |
|
||||
|-----------------|-----------------------------------------|
|
||||
| `job.completed` | Any job finishes successfully |
|
||||
| `job.failed` | Any job reaches the Failed state |
|
||||
| `memory.write` | A memory document is created or updated |
|
||||
| `routine.run` | Another routine completes a run |
|
||||
| `heartbeat` | The heartbeat system fires |
|
||||
|
||||
Event triggers can filter by additional criteria. For example, a routine can listen for `memory.write` on a specific path:
|
||||
|
||||
```json
|
||||
{
|
||||
"trigger": {
|
||||
"type": "event",
|
||||
"event": "memory.write",
|
||||
"filter": {
|
||||
"path_prefix": "inbox/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Webhook Triggers
|
||||
|
||||
Reactive routines can expose an HTTP endpoint that fires the routine when called:
|
||||
|
||||
```json
|
||||
{
|
||||
"trigger": {
|
||||
"type": "webhook",
|
||||
"path": "/hooks/deploy-complete",
|
||||
"secret": "${DEPLOY_WEBHOOK_SECRET}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The endpoint is available at:
|
||||
|
||||
```
|
||||
POST https://<your-ironclaw-host>/hooks/<path>
|
||||
Authorization: Bearer <secret>
|
||||
```
|
||||
|
||||
<Note>
|
||||
Webhook trigger endpoints are not yet exposed in the web gateway UI. Create webhook routines via the `routine_create` tool or via direct chat until UI support is added.
|
||||
</Note>
|
||||
|
||||
### Example Webhook Request
|
||||
|
||||
Trigger a deploy-complete routine from a CI/CD pipeline:
|
||||
|
||||
```bash
|
||||
curl -X POST https://ironclaw.example.com/hooks/deploy-complete \
|
||||
-H "Authorization: Bearer ${DEPLOY_WEBHOOK_SECRET}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"environment": "production",
|
||||
"version": "v1.4.2",
|
||||
"commit": "abc1234"
|
||||
}'
|
||||
```
|
||||
|
||||
The request body is available to the action prompt as context.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
Guardrails are constraints that limit what a reactive routine can do during a single run. They are especially important for reactive routines because the trigger is external — you cannot predict how frequently events will fire.
|
||||
|
||||
```json
|
||||
{
|
||||
"guardrails": {
|
||||
"max_tokens": 8000,
|
||||
"max_tool_calls": 20,
|
||||
"allowed_tools": ["memory_write", "memory_read", "memory_search"],
|
||||
"timeout_secs": 120,
|
||||
"rate_limit": {
|
||||
"max_runs": 10,
|
||||
"window_secs": 3600
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Guardrail | Description |
|
||||
|--------------------------|--------------------------------------------------------------------|
|
||||
| `max_tokens` | Stop the job if cumulative LLM token usage exceeds this value |
|
||||
| `max_tool_calls` | Stop after this many tool calls |
|
||||
| `allowed_tools` | Whitelist of tools the routine may use (empty = all tools allowed) |
|
||||
| `timeout_secs` | Hard kill the job after this many seconds |
|
||||
| `rate_limit.max_runs` | Maximum runs within the time window |
|
||||
| `rate_limit.window_secs` | Time window for rate limiting (seconds) |
|
||||
|
||||
<Warning>
|
||||
Always set `rate_limit` on webhook-triggered routines. Without it, a misconfigured external service flooding your endpoint will spawn unlimited jobs.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Execution Context
|
||||
|
||||
Reactive routine jobs receive the triggering event as part of their context. For webhook triggers, this includes the request body. For event triggers, this includes the event payload (job ID, memory path, etc.).
|
||||
|
||||
The action prompt can reference this context directly:
|
||||
|
||||
```
|
||||
action: "A job just completed: ${event.job_id}. Get the status and write a summary."
|
||||
```
|
||||
201
docs/capabilities/sandboxed-tools.mdx
Normal file
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: WASM Tools
|
||||
sidebarTitle: Sandboxed Tools
|
||||
description: Sandboxed tool execution via WebAssembly (wasmtime)
|
||||
---
|
||||
|
||||
WASM tools run inside a WebAssembly sandbox powered by [wasmtime](https://wasmtime.dev/). They have access to IronClaw's host functions (logging, time, workspace), but all other capabilities — network, filesystem, credentials — must be explicitly declared in a `capabilities.json` file.
|
||||
|
||||
This is IronClaw's recommended approach for custom integrations that need stronger isolation than a built-in Rust tool provides.
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
LLM selects tool
|
||||
↓
|
||||
IronClaw loads WASM module (from cache or disk)
|
||||
↓
|
||||
Tool executes inside wasmtime sandbox
|
||||
↓
|
||||
Network requests route through the network proxy
|
||||
↓
|
||||
Proxy validates domain against allowlist
|
||||
↓
|
||||
Proxy injects credentials from encrypted store
|
||||
↓
|
||||
Response returns to WASM module
|
||||
↓
|
||||
Tool output passes through Safety Layer
|
||||
↓
|
||||
LLM receives sanitized result
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sandbox Architecture
|
||||
|
||||
### Fuel Metering
|
||||
|
||||
Every WASM instruction consumes "fuel." When a module runs out of fuel, it is terminated immediately. This prevents infinite loops and runaway computation.
|
||||
|
||||
```bash
|
||||
# Configure fuel limit (default: 100,000,000 instructions)
|
||||
export WASM_FUEL_LIMIT=100000000
|
||||
```
|
||||
|
||||
### Memory Limits
|
||||
|
||||
WASM modules are allocated a fixed linear memory. Attempting to allocate beyond the limit traps and terminates the module.
|
||||
|
||||
```bash
|
||||
# Configure memory limit (default: 16 MB)
|
||||
export WASM_MEMORY_LIMIT=16777216
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
Each WASM tool has a per-tool rate limiter to prevent a misbehaving or compromised tool from making excessive API calls.
|
||||
|
||||
Rate limits are declared in `capabilities.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"rate_limit": {
|
||||
"requests_per_minute": 60,
|
||||
"requests_per_day": 1000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## capabilities.json
|
||||
|
||||
Every WASM tool must include a `capabilities.json` alongside its `.wasm` binary. This file is the single source of truth for what the tool is allowed to do.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-tool",
|
||||
"version": "0.1.0",
|
||||
"description": "Fetches data from example.com API",
|
||||
|
||||
"network": {
|
||||
"allowed_hosts": [
|
||||
"api.example.com",
|
||||
"auth.example.com"
|
||||
]
|
||||
},
|
||||
|
||||
"filesystem": {
|
||||
"read": ["/workspace/data/*"],
|
||||
"write": ["/workspace/output/*"]
|
||||
},
|
||||
|
||||
"credentials": [
|
||||
{
|
||||
"name": "example_api_key",
|
||||
"inject_as": "Authorization",
|
||||
"format": "Bearer {value}"
|
||||
}
|
||||
],
|
||||
|
||||
"rate_limit": {
|
||||
"requests_per_minute": 30,
|
||||
"requests_per_day": 500
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Network allowlist
|
||||
|
||||
The `network.allowed_hosts` list controls which domains the tool can reach. Requests to any domain not in this list are rejected by the network proxy before the TCP connection is established.
|
||||
|
||||
HTTPS traffic uses the `CONNECT` tunnel method. The proxy validates the target hostname against the allowlist before establishing the tunnel.
|
||||
|
||||
### Filesystem access
|
||||
|
||||
WASM tools cannot access the host filesystem by default. Paths declared under `filesystem.read` and `filesystem.write` are mounted into the WASM module's sandbox at the declared paths.
|
||||
|
||||
### Credential injection
|
||||
|
||||
Credentials listed in `capabilities.json` are never passed into the WASM module's memory. Instead, when the module makes an outbound HTTP request, the proxy intercepts the request and injects the specified header before forwarding it.
|
||||
|
||||
The WASM module never sees the raw credential value — it only makes an unauthenticated HTTP request, and the proxy adds the auth header transparently.
|
||||
|
||||
---
|
||||
|
||||
## Host Functions
|
||||
|
||||
WASM modules can call the following host functions provided by IronClaw:
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `log(level, message)` | Write to the IronClaw log at the given severity level |
|
||||
| `now_unix_secs()` | Return the current Unix timestamp |
|
||||
| `workspace_read(path)` | Read a document from the workspace |
|
||||
| `workspace_write(path, content)` | Write a document to the workspace |
|
||||
|
||||
These are the only host capabilities available. A WASM tool cannot call arbitrary OS functions, fork processes, or access the network directly — all network access goes through the proxy.
|
||||
|
||||
---
|
||||
|
||||
## Tool Discovery
|
||||
|
||||
WASM tools are discovered at startup from two locations:
|
||||
|
||||
- `~/.ironclaw/tools/` — User-installed tools
|
||||
- `<workspace>/tools/` — Per-workspace tools
|
||||
|
||||
Each tool directory must contain:
|
||||
- `<toolname>.wasm` — The compiled WebAssembly module
|
||||
- `capabilities.json` — The capability declaration
|
||||
|
||||
---
|
||||
|
||||
## Installing WASM Tools
|
||||
|
||||
```bash
|
||||
# Install from a local file
|
||||
ironclaw tool install ./my-tool.wasm
|
||||
|
||||
# Install from a URL
|
||||
ironclaw tool install https://example.com/tools/my-tool.wasm
|
||||
|
||||
# List installed tools
|
||||
ironclaw tool list
|
||||
```
|
||||
|
||||
Or via the `extension_install` tool in chat:
|
||||
> "Install the tool from https://example.com/tools/my-tool.wasm"
|
||||
|
||||
---
|
||||
|
||||
## Module Compilation and Caching
|
||||
|
||||
WASM modules are compiled by wasmtime on first load and cached. Subsequent loads use the compiled artifact, so only the first invocation of a new tool incurs compilation overhead.
|
||||
|
||||
The cache is stored at `~/.ironclaw/wasm-cache/`.
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
|
||||
<Warning>
|
||||
Review `capabilities.json` before installing any WASM tool from an untrusted source. The capability file determines exactly what the tool can access — network hosts, filesystem paths, and which credentials are injected.
|
||||
</Warning>
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Credential injection is one-way" icon="lock">
|
||||
The proxy injects credentials into outgoing requests. There is no mechanism for a WASM module to read stored secrets directly. Even if a WASM module is compromised, it cannot access the secrets store.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="No process spawning" icon="shield">
|
||||
WASM modules cannot spawn child processes, execute shell commands, or load dynamic libraries. The WASI interface exposed to modules is minimal.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Fuel prevents denial-of-service" icon="zap">
|
||||
A buggy or malicious WASM tool that enters an infinite loop will be terminated when it exhausts its fuel allocation. This prevents a single tool from blocking the agent indefinitely.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
88
docs/capabilities/skills.mdx
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Skills
|
||||
description: Prompt extensions that activate based on context
|
||||
---
|
||||
|
||||
Skills are markdown files that contain domain-specific instructions. When a skill activates, its markdown body is injected into the LLM context — giving the agent specialized knowledge and behavior without retraining.
|
||||
|
||||
<Tip>
|
||||
IronClaw can search and install skills from the ClawHub registry, a community-driven repository of pre-built skills covering various domains and use cases.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## What Skills Do
|
||||
|
||||
A skill is a self-contained expertise module. It defines:
|
||||
|
||||
- **When to activate** — patterns, keywords, and regex that match incoming messages
|
||||
- **What to inject** — a markdown body with instructions, examples, and domain knowledge
|
||||
- **What tools to require** — binaries, environment variables, and configuration needed
|
||||
- **How much context to use** — a token budget cap per activation
|
||||
|
||||
Skills are evaluated on every turn. The agent selects the most relevant skills that fit within the prompt budget and injects them before the LLM reasons about the request.
|
||||
|
||||
---
|
||||
|
||||
## Activation Pipeline
|
||||
|
||||
Skills pass through four stages before injection:
|
||||
|
||||
<Steps>
|
||||
<Step title="Gate">
|
||||
Check that all prerequisites are met: required binaries exist on `PATH`, required environment variables are set, required configuration is present. Skills that fail gating are skipped entirely — they never score or consume budget.
|
||||
</Step>
|
||||
|
||||
<Step title="Score">
|
||||
Each gated skill is scored against the current message using a deterministic algorithm: keyword matches, tag overlaps, and regex pattern matches. Higher scores indicate stronger relevance.
|
||||
</Step>
|
||||
|
||||
<Step title="Budget">
|
||||
Skills are sorted by score descending. Starting from the highest-scoring skill, each is selected until the `SKILLS_MAX_TOKENS` budget is exhausted. Lower-scoring skills that don't fit are dropped for this turn.
|
||||
</Step>
|
||||
|
||||
<Step title="Attenuate">
|
||||
Trust-based tool ceiling is applied. Installed skills (from ClawHub) lose access to dangerous tools regardless of what the skill requests. Trusted skills retain full tool access. See Trust Levels below.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Trust Levels
|
||||
|
||||
| Trust Level | Source | Tool Access |
|
||||
|---------------|-------------------------------------------------------------|---------------------------------------------------------|
|
||||
| **Trusted** | User-placed in `~/.ironclaw/skills/` or workspace `skills/` | All tools available to the agent |
|
||||
| **Installed** | Downloaded from ClawHub registry via `skill_install` | Read-only tools only — no shell, no file write, no HTTP |
|
||||
|
||||
<Warning>
|
||||
Never place a skill file in the trusted directories unless you have reviewed its contents. A skill in `~/.ironclaw/skills/` has the same tool access as you do.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Skill Directories
|
||||
|
||||
IronClaw discovers skills from three locations, checked in order:
|
||||
|
||||
| Directory | Trust | Description |
|
||||
|---------------------------------|-----------|-------------------------------------------------------------|
|
||||
| `~/.ironclaw/skills/` | Trusted | User's global skills, available in all sessions |
|
||||
| `<workspace>/skills/` | Trusted | Per-workspace skills, activated in that workspace's context |
|
||||
| `~/.ironclaw/installed_skills/` | Installed | Registry-installed skills from ClawHub |
|
||||
|
||||
Skills in trusted directories are loaded as-is. Skills in `installed_skills/` have their tool access capped by the attenuation layer regardless of what they declare.
|
||||
|
||||
---
|
||||
|
||||
## Auto-Discovery
|
||||
|
||||
When `SKILLS_AUTO_DISCOVER=true` (the default), IronClaw scans all skill directories at startup and indexes all valid SKILL.md files. New skills added while the agent is running are picked up on the next restart.
|
||||
|
||||
```bash
|
||||
# Enable auto-discovery (default: true)
|
||||
SKILLS_AUTO_DISCOVER=true
|
||||
|
||||
# Max tokens injected per turn across all active skills
|
||||
SKILLS_MAX_TOKENS=4000
|
||||
```
|
||||
@@ -1,15 +1,34 @@
|
||||
# Building WASM Channels
|
||||
|
||||
This guide covers how to build WASM channel modules for IronClaw.
|
||||
|
||||
## Overview
|
||||
---
|
||||
title: How to build a channel
|
||||
description: "Build a WASM messaging channel that plugs into IronClaw"
|
||||
---
|
||||
|
||||
Channels are WASM components that handle communication with external messaging platforms (Telegram, WhatsApp, Slack, etc.). They run in a sandboxed environment and communicate with the host via the WIT (WebAssembly Interface Types) interface.
|
||||
|
||||
## Directory Structure
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Install Rust and add the WASM target:
|
||||
|
||||
```bash
|
||||
rustup target add wasm32-wasip2
|
||||
```
|
||||
channels/ # Or channels-src/
|
||||
|
||||
Optional but useful for component conversion workflows:
|
||||
|
||||
```bash
|
||||
cargo install wasm-tools
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Create the project structure
|
||||
|
||||
Create a new crate under `channels-src/` (or another location) with this layout:
|
||||
|
||||
```text
|
||||
channels-src/
|
||||
└── my-channel/
|
||||
├── Cargo.toml
|
||||
├── src/
|
||||
@@ -18,15 +37,18 @@ channels/ # Or channels-src/
|
||||
```
|
||||
|
||||
After building, deploy to:
|
||||
```
|
||||
|
||||
```text
|
||||
~/.ironclaw/channels/
|
||||
├── my-channel.wasm
|
||||
└── my-channel.capabilities.json
|
||||
```
|
||||
|
||||
## Cargo.toml Template
|
||||
---
|
||||
|
||||
```toml
|
||||
## 2. Configure Cargo.toml
|
||||
|
||||
```toml Cargo.toml
|
||||
[package]
|
||||
name = "my-channel"
|
||||
version = "0.1.0"
|
||||
@@ -48,7 +70,11 @@ strip = true
|
||||
codegen-units = 1
|
||||
```
|
||||
|
||||
## Channel Implementation
|
||||
---
|
||||
|
||||
## 3. Implement the channel interface
|
||||
|
||||
Now that the crate is ready, implement the channel guest interface exposed by `wit/channel.wit`, and implement the guest trait methods to handle incoming messages and send responses.
|
||||
|
||||
### Required Imports
|
||||
|
||||
@@ -114,6 +140,16 @@ impl Guest for MyChannel {
|
||||
// Call platform API to send message
|
||||
}
|
||||
|
||||
/// Send a proactive message without a prior inbound event.
|
||||
fn on_broadcast(user_id: String, response: AgentResponse) -> Result<(), String> {
|
||||
// Send a message to a known user or chat ID.
|
||||
}
|
||||
|
||||
/// React to agent status changes such as thinking or tool activity.
|
||||
fn on_status(update: StatusUpdate) {
|
||||
// Show typing indicators or status messages when useful.
|
||||
}
|
||||
|
||||
/// Called when channel is shutting down.
|
||||
fn on_shutdown() {
|
||||
channel_host::log(channel_host::LogLevel::Info, "Channel shutting down");
|
||||
@@ -124,7 +160,16 @@ impl Guest for MyChannel {
|
||||
export!(MyChannel);
|
||||
```
|
||||
|
||||
## Critical Pattern: Metadata Flow
|
||||
|
||||
<Note>
|
||||
`on_start` configures how the host calls your channel. `on_http_request` and `on_poll` ingest external messages; `on_respond` delivers replies to an existing conversation; `on_broadcast` sends proactive messages; `on_status` lets channels surface thinking indicators and other progress updates.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## 4. Preserve routing metadata (critical)
|
||||
|
||||
Once your channel can receive messages, keep enough metadata to send responses back to the right chat and sender.
|
||||
|
||||
**The most important pattern**: Store routing info in message metadata so responses can be delivered.
|
||||
|
||||
@@ -161,9 +206,19 @@ fn on_respond(response: AgentResponse) -> Result<(), String> {
|
||||
}
|
||||
```
|
||||
|
||||
## Credential Injection
|
||||
<Note>
|
||||
`response.metadata_json` contains the metadata from the original inbound message. Treat it as the source of truth for reply routing.
|
||||
</Note>
|
||||
|
||||
**Never hardcode credentials!** Use placeholders that the host replaces:
|
||||
---
|
||||
|
||||
## 5. Add secure credential placeholders
|
||||
|
||||
Now that the message path is set, configure API credentials using placeholders instead of hardcoded tokens.
|
||||
|
||||
<Warning>
|
||||
**Never hardcode credentials!** Use placeholders that the host replaces
|
||||
</Warning>
|
||||
|
||||
### URL Placeholders (Telegram-style)
|
||||
|
||||
@@ -176,7 +231,6 @@ channel_host::http_request("POST", url, &headers_json, Some(&body));
|
||||
### Header Placeholders (WhatsApp-style)
|
||||
|
||||
```rust
|
||||
// The host replaces {WHATSAPP_ACCESS_TOKEN} in headers too
|
||||
let headers = serde_json::json!({
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer {WHATSAPP_ACCESS_TOKEN}"
|
||||
@@ -186,11 +240,13 @@ channel_host::http_request("POST", &url, &headers.to_string(), Some(&body));
|
||||
|
||||
The placeholder format is `{SECRET_NAME}` where `SECRET_NAME` matches the credential name in uppercase with underscores (e.g., `whatsapp_access_token` → `{WHATSAPP_ACCESS_TOKEN}`).
|
||||
|
||||
## Capabilities File
|
||||
---
|
||||
|
||||
Create `my-channel.capabilities.json`:
|
||||
## 6. Define capabilities
|
||||
|
||||
```json
|
||||
The capabilities file declares setup prompts, allowlists, and rate limits.
|
||||
|
||||
```json my-channel.capabilities.json
|
||||
{
|
||||
"type": "channel",
|
||||
"name": "my-channel",
|
||||
@@ -244,107 +300,96 @@ Create `my-channel.capabilities.json`:
|
||||
}
|
||||
```
|
||||
|
||||
## Building and Deploying
|
||||
---
|
||||
|
||||
### Supply Chain Security: No Committed Binaries
|
||||
## 7. Build and install
|
||||
|
||||
**Do not commit compiled WASM binaries.** They are a supply chain risk — the binary in a PR may not match the source. IronClaw builds channels from source:
|
||||
With code and capabilities in place, build the channel and copy the two required artifacts.
|
||||
|
||||
- `cargo build` automatically builds `telegram.wasm` via `build.rs`
|
||||
- The built binary is in `.gitignore` and is not committed
|
||||
- CI should run `cargo build` (or `./scripts/build-all.sh`) to produce releases
|
||||
### Generic channel build
|
||||
|
||||
**Reproducible build:**
|
||||
```bash
|
||||
cargo build --release
|
||||
cd channels-src/my-channel
|
||||
cargo build --release --target wasm32-wasip2
|
||||
|
||||
# Convert the raw wasm module into a component and strip it
|
||||
wasm-tools component new target/wasm32-wasip2/release/my_channel.wasm -o my-channel.wasm \
|
||||
2>/dev/null || cp target/wasm32-wasip2/release/my_channel.wasm my-channel.wasm
|
||||
wasm-tools strip my-channel.wasm -o my-channel.wasm
|
||||
|
||||
mkdir -p ~/.ironclaw/channels
|
||||
cp my-channel.wasm ~/.ironclaw/channels/my-channel.wasm
|
||||
cp my-channel.capabilities.json ~/.ironclaw/channels/
|
||||
```
|
||||
|
||||
Prerequisites: `rustup target add wasm32-wasip2`, `cargo install wasm-tools` (optional; fallback copies raw WASM if unavailable).
|
||||
<Note>
|
||||
The channels shipped in `channels-src/` use small `build.sh` wrappers that do exactly this component-conversion step. If your channel lives in the repo, following that pattern is the safest option.
|
||||
</Note>
|
||||
|
||||
### Telegram Channel (Manual Build)
|
||||
### Telegram channel example
|
||||
|
||||
```bash
|
||||
# Add WASM target if needed
|
||||
rustup target add wasm32-wasip2
|
||||
|
||||
# Build Telegram channel
|
||||
./channels-src/telegram/build.sh
|
||||
|
||||
# Install (or use ironclaw onboard to install bundled channel)
|
||||
mkdir -p ~/.ironclaw/channels
|
||||
cp channels-src/telegram/telegram.wasm channels-src/telegram/telegram.capabilities.json ~/.ironclaw/channels/
|
||||
```
|
||||
|
||||
**Note**: The main IronClaw binary bundles `telegram.wasm` via `include_bytes!`. When modifying the Telegram channel source, run `./channels-src/telegram/build.sh` **before** building the main crate, so the updated WASM is included.
|
||||
<Danger>
|
||||
If you are contributing a channel to the public repository, **do not commit compiled WASM binaries.** They are a supply chain risk — the binary in a PR may not match the source. IronClaw builds channels from source.
|
||||
</Danger>
|
||||
|
||||
### Other Channels
|
||||
---
|
||||
|
||||
```bash
|
||||
# Build the WASM component
|
||||
cd channels-src/my-channel
|
||||
cargo build --release --target wasm32-wasip2
|
||||
## 8. Host functions you can call
|
||||
|
||||
# Deploy to ~/.ironclaw/channels/
|
||||
cp target/wasm32-wasip2/release/my_channel.wasm ~/.ironclaw/channels/my-channel.wasm
|
||||
cp my-channel.capabilities.json ~/.ironclaw/channels/
|
||||
```
|
||||
|
||||
## Host Functions Available
|
||||
|
||||
The channel host provides these functions:
|
||||
Channel modules get a small host API for logging, storage, HTTP, and message emission:
|
||||
|
||||
```rust
|
||||
// Logging
|
||||
channel_host::log(LogLevel::Info, "Message");
|
||||
channel_host::log(channel_host::LogLevel::Info, "message");
|
||||
|
||||
// Time
|
||||
let now = channel_host::now_millis();
|
||||
let _now = channel_host::now_millis();
|
||||
|
||||
// Workspace (scoped to channel namespace)
|
||||
let data = channel_host::workspace_read("state/offset");
|
||||
channel_host::workspace_write("state/offset", "12345")?;
|
||||
let _ = channel_host::workspace_write("state/offset", "12345");
|
||||
let _ = channel_host::workspace_read("state/offset");
|
||||
|
||||
// HTTP requests (credentials auto-injected)
|
||||
let response = channel_host::http_request("POST", &url, &headers, Some(&body))?;
|
||||
let _response = channel_host::http_request("POST", &url, &headers, Some(&body));
|
||||
|
||||
// Emit message to agent
|
||||
channel_host::emit_message(&EmittedMessage { ... });
|
||||
channel_host::emit_message(&EmittedMessage { /* ... */ });
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Webhook Secret Validation
|
||||
|
||||
The host validates webhook secrets automatically. Check `req.secret_validated`:
|
||||
Real channels also use a few additional host APIs:
|
||||
|
||||
```rust
|
||||
fn on_http_request(req: IncomingHttpRequest) -> OutgoingHttpResponse {
|
||||
if !req.secret_validated {
|
||||
channel_host::log(LogLevel::Warn, "Invalid webhook secret");
|
||||
// Host should have already rejected, but defense in depth
|
||||
}
|
||||
// ...
|
||||
}
|
||||
let has_secret = channel_host::secret_exists("telegram_bot_token");
|
||||
|
||||
let _ = channel_host::store_attachment_data("attachment-id", &bytes);
|
||||
|
||||
let _ = channel_host::pairing_upsert_request("telegram", "123456", "{}")?;
|
||||
let _ = channel_host::pairing_resolve_identity("telegram", "123456")?;
|
||||
let _ = channel_host::pairing_read_allow_from("telegram")?;
|
||||
```
|
||||
|
||||
### Polling with Offset Tracking
|
||||
Use `store_attachment_data` when you download binary payloads such as voice notes or images during webhook or polling callbacks. Use the pairing APIs when your channel supports owner approval for unknown direct-message senders.
|
||||
|
||||
For platforms that require polling (not webhook-based):
|
||||
---
|
||||
|
||||
## 9. Common patterns
|
||||
|
||||
### Polling with stored offsets
|
||||
|
||||
```rust
|
||||
const OFFSET_PATH: &str = "state/last_offset";
|
||||
|
||||
fn on_poll() {
|
||||
// Read last offset
|
||||
let offset = channel_host::workspace_read(OFFSET_PATH)
|
||||
.and_then(|s| s.parse::<i64>().ok())
|
||||
.unwrap_or(0);
|
||||
|
||||
// Fetch updates since offset
|
||||
let updates = fetch_updates(offset);
|
||||
|
||||
// Process and track new offset
|
||||
let mut new_offset = offset;
|
||||
|
||||
for update in updates {
|
||||
if update.id >= new_offset {
|
||||
new_offset = update.id + 1;
|
||||
@@ -352,37 +397,33 @@ fn on_poll() {
|
||||
emit_message(update);
|
||||
}
|
||||
|
||||
// Save new offset
|
||||
if new_offset != offset {
|
||||
let _ = channel_host::workspace_write(OFFSET_PATH, &new_offset.to_string());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Status Message Filtering
|
||||
|
||||
Skip status updates to prevent loops:
|
||||
### Ignore status-only payloads
|
||||
|
||||
```rust
|
||||
// Skip status updates (delivered, read, etc.)
|
||||
if !payload.statuses.is_empty() && payload.messages.is_empty() {
|
||||
return; // Only status updates, no actual messages
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### Bot Message Filtering
|
||||
|
||||
Skip bot messages to prevent infinite loops:
|
||||
### Ignore bot senders
|
||||
|
||||
```rust
|
||||
if sender.is_bot {
|
||||
return; // Don't respond to bots
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
---
|
||||
|
||||
Add tests in the same file:
|
||||
## 10. Testing and troubleshooting
|
||||
|
||||
Add basic parsing and metadata round-trip tests:
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
@@ -391,50 +432,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_webhook() {
|
||||
let json = r#"{ ... }"#;
|
||||
let payload: WebhookPayload = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(payload.messages.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metadata_roundtrip() {
|
||||
let meta = MyMessageMetadata { ... };
|
||||
let json = serde_json::to_string(&meta).unwrap();
|
||||
let parsed: MyMessageMetadata = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(meta.chat_id, parsed.chat_id);
|
||||
let json = r#"{\"messages\":[]}"#;
|
||||
let v: serde_json::Value = serde_json::from_str(json).expect("valid json in test");
|
||||
assert!(v.get("messages").is_some());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run tests with:
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "byte index N is not a char boundary"
|
||||
|
||||
Never slice strings by byte index! Use character-aware truncation:
|
||||
If you see `byte index N is not a char boundary`, avoid byte slicing and truncate by characters:
|
||||
|
||||
```rust
|
||||
// BAD: panics on multi-byte UTF-8 (emoji, etc.)
|
||||
let preview = &content[..50];
|
||||
|
||||
// GOOD: safe truncation
|
||||
let preview: String = content.chars().take(50).collect();
|
||||
```
|
||||
|
||||
### Credential placeholders not replaced
|
||||
If credential placeholders are not resolved:
|
||||
|
||||
1. Check the secret name matches (lowercase with underscores)
|
||||
2. Verify the secret is in `allowed_names` in capabilities
|
||||
3. Check logs for "unresolved placeholders" warnings
|
||||
|
||||
### Messages not routing to responses
|
||||
|
||||
Ensure `on_respond` uses the ORIGINAL message's metadata, not response metadata:
|
||||
```rust
|
||||
// response.metadata_json comes from the ORIGINAL emit_message call
|
||||
let metadata: MyMetadata = serde_json::from_str(&response.metadata_json)?;
|
||||
```
|
||||
1. Verify secret names match the declared placeholders.
|
||||
2. Confirm the secret is permitted in `allowed_names`.
|
||||
3. Check runtime logs for unresolved placeholder warnings.
|
||||
95
docs/channels/discord.mdx
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "Discord"
|
||||
description: "Interact with your agent through Discord"
|
||||
---
|
||||
|
||||
You can create a Discord application and connect your IronClaw agent to it. Once configured, you can talk to your agent in direct messages or add it to a group chat so it can participate there.
|
||||
|
||||
<Note>
|
||||
If you haven't set up your agent yet, follow our [Quickstart guide](../quickstart)
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Set up the Discord channel
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Create a new application">
|
||||
|
||||
In order to create a new Discord application, navigate to the [Developer Portal](https://discord.com/developers/applications).
|
||||
|
||||
<Steps>
|
||||
<Step title="New Application">
|
||||
Click on the "New Application" button, give it a name (e.g. IronClaw), and click "Create".
|
||||
</Step>
|
||||
<Step title="Get your bot token">
|
||||
Navigate to the "Bot" tab on the left sidebar, under the "Token" section click on "Reset Token" and copy the token that is generated. You will need this token to connect your Discord application to IronClaw.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Step>
|
||||
<Step title="Configure the Discord channel in IronClaw">
|
||||
|
||||
Invoke the IronClaw CLI onboard wizard using the `--channels-only` flag to configure only the channels without going through the entire onboarding process again:
|
||||
|
||||
```
|
||||
ironclaw onboard --channels-only
|
||||
```
|
||||
|
||||
<Steps>
|
||||
<Step title="Config the Tunnel">
|
||||
If you have not setup a `Tunnel` yet, the wizard will ask you to choose a tunnel provider and set it up. We recommend using [ngrok](https://dashboard.ngrok.com/) for its ease of use and reliability.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Install the Discord channel">
|
||||
Select the Discord channel from the list of available channels to install it.
|
||||
</Step>
|
||||
<Step title="Add your bot token">
|
||||
Enter the bot token from the `"Bot"` tab in the [Discord Developer Portal](https://discord.com/developers/applications), and the public key from the `"General Information"` tab.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Step>
|
||||
<Step title="Start IronClaw">
|
||||
Start the `ironclaw` agent:
|
||||
|
||||
```
|
||||
ironclaw
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add the bot to your Discord server">
|
||||
|
||||
Discord does not allow to message bots directly, so you will first need to add it to a server you are part of, and then you can DM the bot from there.
|
||||
|
||||
Generate an invite URL in the "OAuth" section of the [Discord Developer Portal](https://discord.com/developers/applications) with the following `scopes` enabled:
|
||||
|
||||
- bot
|
||||
- applications.commands
|
||||
|
||||
Then select - at minimum - the following `Bot Permissions`:
|
||||
|
||||
- View Channels
|
||||
- Send Messages
|
||||
- Read Message History
|
||||
- Add Reactions
|
||||
|
||||
Copy the generated URL at the bottom, paste it into your browser, select your server, and click Continue to connect. You should now see your bot in the Discord server.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Enable mentions in group chats">
|
||||
In order to be able to trigger the bot in group chats, you need to edit the `.ironclaw/channels/discord.capabilities.json` file and add the `channel ID` of the channel where you want the bot to be active to the `mention_channel_ids` list.
|
||||
|
||||
<Tip>
|
||||
To get the channel ID, **enable Developer Mode** in your **Discord settings**, right-click on the channel where you want the bot to be active, and click "Copy ID".
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Direct Messaging">
|
||||
Now that you share a server with the bot, you can send it a direct message. Click on its name in the member list and send them a direct message. The bot will reply with a pairing command that you need to execute in the terminal to complete the pairing process and start chatting with your agent.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
125
docs/channels/local.md
Normal file
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: "Local"
|
||||
description: "Use IronClaw locally via terminal or browser"
|
||||
---
|
||||
|
||||
By default, IronClaw provides two local interfaces for chatting with your agent:
|
||||
|
||||
- **Terminal UI (TUI):** chat directly in your terminal
|
||||
- **Web Gateway:** chat in your browser over a local HTTP server
|
||||
|
||||
<Note>
|
||||
If you haven't set up your agent yet, follow our [Quickstart guide](../quickstart)
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Terminal UI
|
||||
|
||||
Simply run `ironclaw` and the TUI will launch in your terminal. Use the keyboard shortcuts below to navigate and chat with your agent.
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Enter` | Send message |
|
||||
| `Shift+Enter` | New line in composer |
|
||||
| `Ctrl+C` | Quit |
|
||||
| `Ctrl+L` | Clear screen |
|
||||
| `Tab` | Focus next element |
|
||||
| `Esc` | Cancel or back |
|
||||
| `Up/Down` | Scroll history |
|
||||
|
||||
### Configuration
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `CLI_ENABLED` | `true` | Enable or disable the Terminal UI |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Web Gateway
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `GATEWAY_HOST` | `127.0.0.1` | Host interface for the Web Gateway |
|
||||
| `GATEWAY_PORT` | `3000` | Port used by the Web Gateway |
|
||||
| `GATEWAY_ENABLED` | `true` | Enable or disable the Web Gateway |
|
||||
| `GATEWAY_AUTH_TOKEN` | auto-generated | Auth token required to open the Web UI |
|
||||
|
||||
### Authentication
|
||||
|
||||
By default, IronClaw generates an auth token at startup and prints it in logs. To use a stable token across restarts:
|
||||
|
||||
```bash
|
||||
export GATEWAY_AUTH_TOKEN="your-secure-token-here"
|
||||
```
|
||||
|
||||
Generate one:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
### API endpoints
|
||||
|
||||
The Web Gateway also exposes local endpoints:
|
||||
|
||||
| Endpoint | Description |
|
||||
|----------|-------------|
|
||||
| `GET /api/status` | Server status |
|
||||
| `POST /api/chat` | Send message |
|
||||
| `GET /api/jobs` | List jobs |
|
||||
| `GET /api/memory` | Search memory |
|
||||
|
||||
### Network access
|
||||
|
||||
Use localhost-only access (recommended):
|
||||
|
||||
```bash
|
||||
export GATEWAY_HOST=127.0.0.1
|
||||
```
|
||||
|
||||
Use LAN access:
|
||||
|
||||
```bash
|
||||
export GATEWAY_HOST=0.0.0.0
|
||||
```
|
||||
|
||||
<Warning>
|
||||
When using `0.0.0.0`, use a strong auth token and place the service behind HTTPS/reverse proxy before exposing it outside your local network.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Terminal display issues">
|
||||
- Ensure your terminal supports Unicode and 256 colors
|
||||
- Set `TERM=xterm-256color`
|
||||
- Restart the terminal session
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Terminal input issues">
|
||||
- Check terminal focus
|
||||
- Run `reset`
|
||||
- Disable conflicting terminal mouse mode
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Web UI connection refused">
|
||||
- Verify `ironclaw run` is active
|
||||
- Check `GATEWAY_PORT` value
|
||||
- Confirm host and firewall settings
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Web UI token rejected">
|
||||
- Copy token exactly from startup logs
|
||||
- Remove trailing spaces
|
||||
- Set a persistent `GATEWAY_AUTH_TOKEN`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="WebSocket disconnects">
|
||||
- Check local network/proxy stability
|
||||
- Verify reverse proxy supports WebSocket upgrades
|
||||
- Inspect browser console logs
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
32
docs/channels/overview.mdx
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: "Overview"
|
||||
description: "Set up messaging channels to interact with your agent"
|
||||
---
|
||||
|
||||
Channels define how people send messages to your agent. You can start local for development, then add messaging apps or webhooks as your integration needs grow.
|
||||
|
||||
<Card title="Tunnel" icon="link" href="/tunnel" horizontal>
|
||||
Configure a tunnel so webhook-based channels can receive incoming requests.
|
||||
</Card>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Local" icon="terminal" href="/channels/local">
|
||||
Built-in terminal UI and web gateway for local usage and testing.
|
||||
</Card>
|
||||
|
||||
<Card title="Telegram" icon="send" href="/channels/telegram">
|
||||
Talk to your agent in Telegram direct messages and group chats.
|
||||
</Card>
|
||||
|
||||
<Card title="Signal" icon="message" href="/channels/signal">
|
||||
Connect IronClaw to Signal through a signal-cli HTTP daemon.
|
||||
</Card>
|
||||
|
||||
<Card title="Discord" icon="discord" href="/channels/discord">
|
||||
Integrate with Discord to chat in servers, channels, and DMs.
|
||||
</Card>
|
||||
|
||||
<Card title="HTTP Webhook" icon="webhook" href="/channels/webhook">
|
||||
Send messages from external systems using a REST endpoint.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
39
docs/channels/signal.mdx
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: "Signal"
|
||||
description: "Interact with your agent through Signal"
|
||||
---
|
||||
|
||||
Connect IronClaw to Signal so you can talk to your agent in direct messages.
|
||||
|
||||
<Note>
|
||||
If you haven't set up your agent yet, follow our [Quickstart guide](../quickstart)
|
||||
</Note>
|
||||
|
||||
<Info>
|
||||
Signal channel documentation is coming soon. The channel is fully implemented — full setup steps and configuration details are being written.
|
||||
|
||||
</Info>
|
||||
|
||||
---
|
||||
|
||||
## Set up a Signal channel
|
||||
|
||||
The Signal channel connects IronClaw to a running [signal-cli](https://github.com/AsamK/signal-cli) HTTP daemon. Before configuring IronClaw, start signal-cli in daemon mode on your machine.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Options
|
||||
|
||||
Configure the Signal channel through environment variables:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `SIGNAL_HTTP_URL` | URL of the signal-cli HTTP daemon |
|
||||
| `SIGNAL_ACCOUNT` | Your Signal phone number (e.g. `+1234567890`) |
|
||||
| `SIGNAL_ALLOW_FROM` | Comma-separated list of phone numbers allowed to message the bot |
|
||||
|
||||
```bash
|
||||
export SIGNAL_HTTP_URL=http://127.0.0.1:8080
|
||||
export SIGNAL_ACCOUNT=+1234567890
|
||||
export SIGNAL_ALLOW_FROM=+0987654321,+11234567890
|
||||
```
|
||||
338
docs/channels/telegram.mdx
Normal file
@@ -0,0 +1,338 @@
|
||||
---
|
||||
title: "Telegram"
|
||||
description: "Interact with your agent through Telegram"
|
||||
---
|
||||
|
||||
You can create a Telegram bot and connect your IronClaw agent to it. Once configured, you can talk to your agent in direct messages or add it to a group chat so it can participate there.
|
||||
|
||||
<Note>
|
||||
If you haven't set up your agent yet, follow our [Quickstart guide](../quickstart)
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Set up a Telegram channel
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Create a new bot with BotFather">
|
||||
|
||||
In order to create a new Telegram bot, you need to talk to [BotFather](https://t.me/botfather), the official Telegram bot that helps you create and manage your bots.
|
||||
|
||||
<Steps>
|
||||
<Step title="Start a conversation with BotFather">
|
||||
Search for "BotFather" in the Telegram app and start a conversation with it. You can also use this link: [https://t.me/botfather](https://t.me/botfather)
|
||||
</Step>
|
||||
<Step title="Create a new bot">
|
||||
Send the command `/newbot` to BotFather and follow the instructions to create a new bot. You will need to choose a name and a username for your bot. The username must end with "bot". For example, "my_agent_bot".
|
||||
</Step>
|
||||
<Step title="Get your bot token">
|
||||
After creating your bot, BotFather will give you a token that looks like this: `123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ`. This token is used to authenticate your bot and allow it to access the Telegram API. Keep this token safe and do not share it with anyone. You will need it later to configure the Telegram channel in IronClaw.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Step>
|
||||
<Step title="Configure the Telegram channel in IronClaw">
|
||||
|
||||
Invoke the IronClaw CLI onboard wizard using the `--channels-only` flag to configure only the channels without going through the entire onboarding process again:
|
||||
|
||||
```
|
||||
ironclaw onboard --channels-only
|
||||
```
|
||||
|
||||
<Steps>
|
||||
<Step title="Config the Tunnel">
|
||||
If you have not setup a `Tunnel` yet, the wizard will ask you to choose a tunnel provider and set it up. We recommend using [ngrok](https://dashboard.ngrok.com/) for its ease of use and reliability.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Install the Telegram channel">
|
||||
Select the Telegram channel from the list of available channels to install it.
|
||||

|
||||
</Step>
|
||||
<Step title="Add your bot token">
|
||||
Enter the bot token you got from BotFather in the previous step.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Step>
|
||||
|
||||
<Step title="Test your Telegram channel">
|
||||
Now that you have configured your Telegram channel, it is time to test it out. Start the `ironclaw` agent if it is not already running:
|
||||
|
||||
```
|
||||
ironclaw
|
||||
```
|
||||
|
||||
Send a message to your bot in Telegram. It will respond with a command that you will need to execute in the terminal to complete the channel setup:
|
||||
|
||||
```
|
||||
ironclaw pairing approve telegram <PAIRING_CODE>
|
||||
```
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Telegram Side Settings
|
||||
|
||||
<Accordion title="Privacy mode and group visibility">
|
||||
Telegram bots default to Privacy Mode, which limits what group messages they receive.If the bot must see all group messages, either:
|
||||
|
||||
- disable privacy mode via `/setprivacy`, or
|
||||
- make the bot a group admin.
|
||||
|
||||
When toggling privacy mode, remove and re-add the bot in each group so Telegram applies the change.
|
||||
</Accordion>
|
||||
<Accordion title="Group permissions">
|
||||
Admin status is controlled in Telegram group settings.Admin bots receive all group messages, which is useful for always-on group behavior.
|
||||
</Accordion>
|
||||
<Accordion title="Helpful BotFather toggles">
|
||||
- `/setjoingroups` to allow/deny group adds
|
||||
- `/setprivacy` for group visibility behavior
|
||||
|
||||
</Accordion>
|
||||
|
||||
---
|
||||
|
||||
## Configuration Options
|
||||
|
||||
You can configure the behavior of the Telegram channel through the
|
||||
`.ironclaw/channels/telegram.capabilities.json` file, which is created
|
||||
automatically after you set up the channel for the first time.
|
||||
|
||||
<Accordion title="Options Overview">
|
||||
|
||||
| Option | Values | Default | Description |
|
||||
|---------------------------------|--------------------------------|-----------|---------------------------------------------------------------------------------|
|
||||
| `dm_policy` | `open`, `allowlist`, `pairing` | `pairing` | Controls who can send direct messages to the bot |
|
||||
| `allow_from` | user IDs | `[]` | Users allowed to DM the bot when `dm_policy` is set to `allowlist` |
|
||||
| `owner_id` | Telegram user ID | — | If set, only this user can interact with the bot (DMs and group messages) |
|
||||
| `respond_to_all_group_messages` | bool | `false` | Respond to all group messages |
|
||||
| `bot_username` | username | — | For group mention detection whenever `respond_to_all_group_messages` is `false` |
|
||||
| `polling_enabled` | bool | `false` | Use polling instead of webhooks |
|
||||
| `poll_interval_ms` | number | `30000` | Polling interval in milliseconds (only used if `polling_enabled` is `true`) |
|
||||
</Accordion>
|
||||
|
||||
<Note>
|
||||
|
||||
Remember to restart your agent after changing the configuration file for the changes to take effect
|
||||
|
||||
</Note>
|
||||
|
||||
### Direct Message Policy
|
||||
|
||||
The `dm_policy` option controls who can send direct messages to the bot:
|
||||
|
||||
- `open`: anyone can DM the bot without restrictions
|
||||
- `allowlist`: only users in the `allow_from` list can DM the bot
|
||||
- `pairing` **(default)**: the bot will DM back any user that contacts it with a pairing command that needs to be executed in the terminal
|
||||
|
||||
Related options:
|
||||
- The `allow_from` option is a list of Telegram user IDs that are allowed to DM the bot when `dm_policy` is set to `allowlist`.
|
||||
- The `owner_id` option restricts the bot to answer only messages from a specific Telegram user ID
|
||||
|
||||
<Tip>
|
||||
|
||||
**User ID**
|
||||
|
||||
Message [@userinfobot](https://t.me/userinfobot) to get your Telegram user ID.
|
||||
|
||||
</Tip>
|
||||
|
||||
### Respond to all group messages
|
||||
By default, the Telegram channel will only respond to messages that mention the bot in groups.
|
||||
If you want the bot to respond to all group messages, set the `respond_to_all_group_messages`
|
||||
|
||||
Relevant options:
|
||||
- If `respond_to_all_group_messages` is set to `false`, the bot will only respond to messages that mention it.
|
||||
In this case, make sure to set the `bot_username` option with the bot's username (without the `@`)
|
||||
|
||||
### Polling
|
||||
|
||||
In case you do not want to configure a `tunnel`, you can setup the Telegram channel to poll for
|
||||
new messages every certain interval of time.
|
||||
|
||||
To do this, set the `polling_enabled` option to `true` and configure the `poll_interval_ms` option
|
||||
with the desired polling interval in milliseconds (default is 30000ms, which is 30 seconds).
|
||||
|
||||
### Configuration Examples
|
||||
|
||||
**Private Team Assistant** — mentions only, pairing for DMs:
|
||||
```json
|
||||
{
|
||||
"bot_username": "TeamBot",
|
||||
"respond_to_all_group_messages": false,
|
||||
"dm_policy": "pairing"
|
||||
}
|
||||
```
|
||||
|
||||
**Always-On Expert** — responds to all messages:
|
||||
```json
|
||||
{
|
||||
"bot_username": "DevOpsBot",
|
||||
"respond_to_all_group_messages": true,
|
||||
"allow_from": ["*"]
|
||||
}
|
||||
```
|
||||
|
||||
**Owner-Only** — personal assistant in shared groups:
|
||||
```json
|
||||
{
|
||||
"bot_username": "MyBot",
|
||||
"respond_to_all_group_messages": false,
|
||||
"owner_id": "12345678"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Group Chat Participation
|
||||
|
||||
IronClaw can be configured to participate in Telegram group chats, by default the bot will only respond to commands (use `/help` to see the
|
||||
list of available commands). If you want the bot to respond to mentions or all messages in a group you need to configure it.
|
||||
|
||||
### Adding the Bot to Groups
|
||||
|
||||
1. **Enable group privacy** in @BotFather:
|
||||
- Message [@BotFather](https://t.me/BotFather)
|
||||
- Send `/mybots` → Select your bot
|
||||
- Click "Bot Settings" → "Group Privacy"
|
||||
- Turn OFF "Privacy mode" (allows bot to see all messages)
|
||||
|
||||
2. **Add bot to a group**:
|
||||
- Open the group in Telegram
|
||||
- Add member → Search for your bot username
|
||||
- Grant admin permissions (optional but recommended)
|
||||
|
||||
3. **Configure `bot_username`** in IronClaw:
|
||||
```json
|
||||
{
|
||||
"bot_username": "MyIronClawBot"
|
||||
}
|
||||
```
|
||||
|
||||
### Group Trigger Modes
|
||||
|
||||
#### Commands & Mentions
|
||||
|
||||
The bot responds when a command is used, e.g. `/skills`, or when the bot is mentioned, e.g. `@MyIronClawBot what's the weather?`
|
||||
|
||||
Configuration:
|
||||
|
||||
- Set "Privacy mode" to `OFF` in @BotFather, or make the bot a group admin
|
||||
- Configure the `bot_username`:
|
||||
|
||||
```json
|
||||
{
|
||||
"bot_username": "MyIronClawBot",
|
||||
"respond_to_all_group_messages": false
|
||||
}
|
||||
```
|
||||
|
||||
Benefits:
|
||||
- Respects group conversation flow
|
||||
- No spam from unsolicited responses
|
||||
- Users explicitly choose to engage the agent
|
||||
|
||||
#### Respond to All Messages
|
||||
|
||||
The bot processes and responds to every message in the group.
|
||||
|
||||
- Set "Privacy mode" to OFF in @BotFather, or make the bot a group admin
|
||||
- Configure both `bot_username` and `respond_to_all_group_messages`:
|
||||
|
||||
Configuration:
|
||||
```json
|
||||
{
|
||||
"bot_username": "MyIronClawBot",
|
||||
"respond_to_all_group_messages": true
|
||||
}
|
||||
```
|
||||
|
||||
Use cases:
|
||||
- Small team rooms where the agent is always helpful
|
||||
- Automated moderation or summarization
|
||||
- Specific-topic groups where the agent provides expertise
|
||||
|
||||
---
|
||||
|
||||
## Message Privacy
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What the bot can see" icon="eye">
|
||||
- All messages in groups where privacy mode is disabled
|
||||
- Usernames and display names
|
||||
- Message timestamps
|
||||
- Reply chains (threading context)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What gets sent to the LLM" icon="message">
|
||||
- The message text (with @mention stripped)
|
||||
- Sender identifier (username or first name)
|
||||
- Recent conversation history in that thread
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
---
|
||||
|
||||
## Webhook Secret (Optional)
|
||||
|
||||
When IronClaw is running in webhook mode, Telegram delivers messages by sending HTTP requests to your public URL. Because that URL is reachable from the internet, any third party could send fake requests to it.
|
||||
|
||||
A webhook secret is a shared token you configure in IronClaw. Telegram includes that token in every request it sends. IronClaw rejects any request that does not carry the correct token, so only genuine Telegram traffic reaches your agent.
|
||||
|
||||
To enable it, add `telegram_webhook_secret` to your `.ironclaw/channels/telegram.capabilities.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"telegram_webhook_secret": "your-secret-here"
|
||||
}
|
||||
```
|
||||
|
||||
Generate a suitable value with:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 16
|
||||
```
|
||||
|
||||
<Note>
|
||||
The webhook secret is only relevant when `polling_enabled` is `false`. If you are using polling, this option has no effect.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Messages not delivered">
|
||||
**Polling:** Check logs for `getUpdates` errors and verify the bot token is valid.
|
||||
|
||||
**Webhook:** Verify the HTTPS URL is reachable and the tunnel is running.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Pairing code not sent">
|
||||
- Ensure `dm_policy` is set to `pairing` and not `allowlist`
|
||||
- Verify `api.telegram.org` is accessible from your instance
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Group mentions not working">
|
||||
- Confirm `bot_username` is set and matches the bot username exactly, without the `@`
|
||||
- Verify the bot has permission to read group messages
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Bot not seeing group messages">
|
||||
- Disable Privacy Mode in @BotFather: `/mybots` → Bot Settings → Group Privacy → turn OFF
|
||||
- Remove and re-add the bot to the group after changing the privacy setting
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Bot responding to all group messages unexpectedly">
|
||||
- Set `respond_to_all_group_messages` to `false`
|
||||
- Verify the config was saved and restart the agent
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Owner binding timeout during setup">
|
||||
The wizard waits 120 seconds for the first message. If it times out, send `/start` to your bot in Telegram and re-run `ironclaw onboard --channels-only`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
187
docs/channels/webhook.mdx
Normal file
@@ -0,0 +1,187 @@
|
||||
---
|
||||
title: HTTP Webhook
|
||||
sidebarTitle: Webhook
|
||||
description: REST API for external integrations
|
||||
---
|
||||
|
||||
The HTTP Webhook channel provides a REST API for integrating external services with IronClaw.
|
||||
|
||||
<Note>
|
||||
If you haven't set up your agent yet, follow our [Quickstart guide](../quickstart)
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Enabling Webhooks
|
||||
|
||||
```bash
|
||||
export HTTP_ENABLED=true
|
||||
export HTTP_HOST=0.0.0.0
|
||||
export HTTP_PORT=8080
|
||||
export HTTP_WEBHOOK_SECRET=your-secret
|
||||
```
|
||||
|
||||
Or during onboarding:
|
||||
```
|
||||
Step 6: Channel Configuration
|
||||
→ Select "HTTP Webhook"
|
||||
→ Port: 8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
<Warning>
|
||||
The HTTP webhook binds to `0.0.0.0:8080` by default. If you don't need external webhook delivery, set `HTTP_HOST=127.0.0.1`.
|
||||
</Warning>
|
||||
|
||||
### Shared Secret Validation
|
||||
|
||||
Configure a webhook secret to validate requests:
|
||||
|
||||
```bash
|
||||
export HTTP_WEBHOOK_SECRET="your-secret-here"
|
||||
```
|
||||
|
||||
The secret is sent in the `X-Webhook-Secret` header.
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
- **Body size**: 64 KB maximum
|
||||
- **Rate**: 60 requests per minute per IP
|
||||
|
||||
---
|
||||
|
||||
## Sending Messages
|
||||
|
||||
### Request Format
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/webhook \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Webhook-Secret: your-secret" \
|
||||
-d '{
|
||||
"user_id": "default",
|
||||
"message": "Hello, IronClaw!"
|
||||
}'
|
||||
```
|
||||
|
||||
### Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "uuid",
|
||||
"status": "queued"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Payload Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `user_id` | string | Yes | User identifier |
|
||||
| `message` | string | Yes | Message content |
|
||||
| `conversation_id` | string | No | Continue existing conversation |
|
||||
| `metadata` | object | No | Arbitrary metadata |
|
||||
|
||||
## Response Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `job_id` | string | Job UUID for status checking |
|
||||
| `status` | string | `queued`, `running`, `completed` |
|
||||
| `response` | string | Agent response (when complete) |
|
||||
|
||||
---
|
||||
|
||||
## Checking Status
|
||||
|
||||
```bash
|
||||
curl http://localhost:8080/jobs/{job_id} -H "X-Webhook-Secret: your-secret"
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"status": "completed",
|
||||
"response": "Hello! How can I help?",
|
||||
"created_at": "2024-01-15T10:30:00Z",
|
||||
"completed_at": "2024-01-15T10:30:05Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| `400` | Invalid request body |
|
||||
| `401` | Missing or invalid secret |
|
||||
| `429` | Rate limit exceeded |
|
||||
| `500` | Server error |
|
||||
|
||||
---
|
||||
|
||||
## Example Integrations
|
||||
|
||||
### GitHub Webhook
|
||||
|
||||
Configure GitHub to send events to your IronClaw webhook URL:
|
||||
|
||||
```bash
|
||||
# GitHub webhook URL
|
||||
https://your-server:8080/webhook
|
||||
|
||||
# Secret: your configured HTTP_WEBHOOK_SECRET
|
||||
```
|
||||
|
||||
### Zapier
|
||||
|
||||
Use Zapier's Webhook action to send events to IronClaw.
|
||||
|
||||
### Custom Script
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
response = requests.post(
|
||||
"http://localhost:8080/webhook",
|
||||
headers={"X-Webhook-Secret": "your-secret"},
|
||||
json={"user_id": "automation", "message": "Process this data"}
|
||||
)
|
||||
|
||||
print(response.json()["job_id"])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Connection refused" icon="x-circle">
|
||||
- Check IronClaw is running
|
||||
- Verify `HTTP_PORT` is correct
|
||||
- Check firewall: `sudo ufw allow 8080`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="401 Unauthorized" icon="key">
|
||||
- Include `X-Webhook-Secret` header
|
||||
- Verify secret matches configuration
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="429 Too Many Requests" icon="clock">
|
||||
- Rate limit: 60 requests/minute
|
||||
- Implement exponential backoff
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Messages not processed" icon="message-circle">
|
||||
- Check logs: `RUST_LOG=ironclaw=debug ironclaw run`
|
||||
- Verify JSON format
|
||||
- Check `user_id` is valid
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
309
docs/docs.json
Normal file
@@ -0,0 +1,309 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "almond",
|
||||
"name": "IronClaw",
|
||||
"colors": {
|
||||
"primary": "#1191f0",
|
||||
"light": "#1191f0",
|
||||
"dark": "#1191f0"
|
||||
},
|
||||
"favicon": "/images/logo/favicon.ico",
|
||||
"logo": {
|
||||
"light": "/images/logo/logo.svg",
|
||||
"dark": "/images/logo/logo-dark.svg"
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "Get Support",
|
||||
"href": "https://t.me/ironclawAI"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "Deploy Now",
|
||||
"href": "https://agent.near.ai/"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"languages": [
|
||||
{
|
||||
"language": "en",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": " ",
|
||||
"groups": [
|
||||
{
|
||||
"group": " ",
|
||||
"pages": [
|
||||
"index",
|
||||
"quickstart",
|
||||
"onboard"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Capabilities",
|
||||
"icon": "cubes",
|
||||
"pages": [
|
||||
"capabilities/overview",
|
||||
"capabilities/llm-providers",
|
||||
{
|
||||
"group": "Core Capabilities",
|
||||
"pages": [
|
||||
"security",
|
||||
{
|
||||
"group": "Memory",
|
||||
"pages": [
|
||||
"capabilities/memory/memory",
|
||||
"capabilities/memory/identity"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Routines",
|
||||
"pages": [
|
||||
"capabilities/routines/cron",
|
||||
"capabilities/routines/heartbeat",
|
||||
"capabilities/routines/reactive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Jobs",
|
||||
"pages": [
|
||||
"capabilities/jobs/jobs",
|
||||
"capabilities/jobs/self-repair"
|
||||
]
|
||||
},
|
||||
"capabilities/skills",
|
||||
"capabilities/sandboxed-tools"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Channels",
|
||||
"icon": "message",
|
||||
"pages": [
|
||||
"channels/overview",
|
||||
{
|
||||
"group": "Built-in Channels",
|
||||
"pages": [
|
||||
"tunnel",
|
||||
"channels/local",
|
||||
"channels/discord",
|
||||
"channels/telegram",
|
||||
"channels/webhook",
|
||||
"channels/signal"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Extensions (Tools)",
|
||||
"icon": "hammer",
|
||||
"pages": [
|
||||
"extensions/overview",
|
||||
{
|
||||
"group": "Built-in Extensions",
|
||||
"pages": [
|
||||
"extensions/file-tools",
|
||||
"extensions/github",
|
||||
{
|
||||
"group": "Google",
|
||||
"pages": [
|
||||
"extensions/google/oauth-setup",
|
||||
"extensions/google/gmail",
|
||||
"extensions/google/calendar",
|
||||
"extensions/google/docs",
|
||||
"extensions/google/drive",
|
||||
"extensions/google/sheets",
|
||||
"extensions/google/slides"
|
||||
]
|
||||
},
|
||||
"extensions/mcp",
|
||||
"extensions/web-search",
|
||||
"extensions/shell"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Tutorials",
|
||||
"icon": "graduation-cap",
|
||||
"pages": [
|
||||
"extensions/building-a-tool",
|
||||
"channels/building-a-channel",
|
||||
{
|
||||
"group": "How to host IronClaw on...",
|
||||
"pages": [
|
||||
"infrastructure/droplet"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"language": "zh",
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "支持",
|
||||
"href": "https://t.me/ironclawAI"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "立即部署",
|
||||
"href": "https://agent.near.ai/"
|
||||
}
|
||||
},
|
||||
"tabs": [
|
||||
{
|
||||
"tab": " ",
|
||||
"groups": [
|
||||
{
|
||||
"group": " ",
|
||||
"pages": [
|
||||
"zh/index",
|
||||
"zh/quickstart",
|
||||
"zh/onboard"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "核心能力",
|
||||
"icon": "cubes",
|
||||
"pages": [
|
||||
"zh/capabilities/overview",
|
||||
{
|
||||
"group": "核心能力",
|
||||
"pages": [
|
||||
"zh/security",
|
||||
{
|
||||
"group": "记忆",
|
||||
"pages": [
|
||||
"zh/capabilities/memory/memory",
|
||||
"zh/capabilities/memory/identity"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "例程",
|
||||
"pages": [
|
||||
"zh/capabilities/routines/cron",
|
||||
"zh/capabilities/routines/heartbeat",
|
||||
"zh/capabilities/routines/reactive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "任务",
|
||||
"pages": [
|
||||
"zh/capabilities/jobs/jobs",
|
||||
"zh/capabilities/jobs/self-repair"
|
||||
]
|
||||
},
|
||||
"zh/capabilities/skills",
|
||||
"zh/capabilities/sandboxed-tools"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "渠道",
|
||||
"icon": "message",
|
||||
"pages": [
|
||||
"zh/channels/overview",
|
||||
{
|
||||
"group": "内置渠道",
|
||||
"pages": [
|
||||
"zh/tunnel",
|
||||
"zh/channels/local",
|
||||
"zh/channels/signal",
|
||||
"zh/channels/telegram",
|
||||
"zh/channels/webhook"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "扩展(工具)",
|
||||
"icon": "hammer",
|
||||
"pages": [
|
||||
"zh/extensions/overview",
|
||||
{
|
||||
"group": "内置扩展",
|
||||
"pages": [
|
||||
"zh/extensions/file-tools",
|
||||
"zh/extensions/github",
|
||||
{
|
||||
"group": "Google",
|
||||
"pages": [
|
||||
"zh/extensions/google/oauth-setup",
|
||||
"zh/extensions/google/gmail",
|
||||
"zh/extensions/google/calendar",
|
||||
"zh/extensions/google/docs",
|
||||
"zh/extensions/google/drive",
|
||||
"zh/extensions/google/sheets",
|
||||
"zh/extensions/google/slides"
|
||||
]
|
||||
},
|
||||
"zh/extensions/mcp",
|
||||
"zh/extensions/web-search",
|
||||
"zh/extensions/shell"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "教程",
|
||||
"icon": "graduation-cap",
|
||||
"pages": [
|
||||
"zh/extensions/building-a-tool",
|
||||
{
|
||||
"group": "在以下平台托管 IronClaw",
|
||||
"pages": [
|
||||
"zh/infrastructure/droplet"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"global": {
|
||||
"anchors": [
|
||||
{
|
||||
"anchor": "Agent Dashboard",
|
||||
"href": "https://agent.near.ai/",
|
||||
"icon": "sliders"
|
||||
},
|
||||
{
|
||||
"anchor": "NEAR AI Cloud",
|
||||
"href": "https://cloud.near.ai",
|
||||
"icon": "cloud"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"contextual": {
|
||||
"options": [
|
||||
"copy",
|
||||
"view",
|
||||
"chatgpt",
|
||||
"claude",
|
||||
"perplexity",
|
||||
"mcp",
|
||||
"cursor",
|
||||
"vscode"
|
||||
]
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"x": "https://x.com/near_ai",
|
||||
"github": "https://github.com/nearai",
|
||||
"linkedin": "https://linkedin.com/company/near-ai"
|
||||
}
|
||||
}
|
||||
}
|
||||
131
docs/drafts/agents/clawhub.mdx
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
title: ClawHub Registry
|
||||
sidebarTitle: ClawHub
|
||||
description: Discover and install skills from the ClawHub registry
|
||||
---
|
||||
|
||||
ClawHub is the official registry for IronClaw skills. It lets you discover community-built skills, install them with a single command, and keep them up to date — without leaving the agent interface.
|
||||
|
||||
## What ClawHub Provides
|
||||
|
||||
- **Searchable catalog** of skills organized by category and keyword
|
||||
- **Versioned downloads** — install a specific version or always pull the latest
|
||||
- **Trust boundary** — all registry-installed skills run under the **Installed** trust level, limiting tool access to read-only operations
|
||||
|
||||
<Warning>
|
||||
Skills from ClawHub are installed with **Installed** trust, not **Trusted** trust. They cannot execute shell commands, write files, or make HTTP requests. If you need elevated access, download the SKILL.md manually and place it in `~/.ironclaw/skills/` after reviewing the contents.
|
||||
</Warning>
|
||||
|
||||
## Skill Tools
|
||||
|
||||
Four built-in tools manage skills at runtime. Use them directly in conversation:
|
||||
|
||||
### skill_list
|
||||
|
||||
List all discovered skills on this instance, including trust level, activation status, and source directory.
|
||||
|
||||
```
|
||||
skill_list
|
||||
|
||||
# Example output:
|
||||
# kubernetes-deploy (trusted) ~/.ironclaw/skills/
|
||||
# git-helper (trusted) <workspace>/skills/
|
||||
# python-lint (installed) ~/.ironclaw/installed_skills/
|
||||
```
|
||||
|
||||
### skill_search
|
||||
|
||||
Search the ClawHub registry for available skills matching a query.
|
||||
|
||||
```
|
||||
skill_search "terraform infrastructure"
|
||||
|
||||
# Returns: skill name, description, version, download count, tags
|
||||
```
|
||||
|
||||
### skill_install
|
||||
|
||||
Download and install a skill from ClawHub into `~/.ironclaw/installed_skills/`.
|
||||
|
||||
```
|
||||
skill_install python-lint
|
||||
skill_install python-lint@0.3.1 # pin a specific version
|
||||
```
|
||||
|
||||
The skill is immediately available after installation — no restart required.
|
||||
|
||||
### skill_remove
|
||||
|
||||
Remove an installed skill from `~/.ironclaw/installed_skills/`.
|
||||
|
||||
```
|
||||
skill_remove python-lint
|
||||
```
|
||||
|
||||
Trusted skills in `~/.ironclaw/skills/` must be removed manually by deleting the directory.
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# ClawHub registry URL
|
||||
SKILLS_CATALOG_URL=https://clawhub.dev
|
||||
|
||||
# Maximum tokens injected per turn across all active skills
|
||||
SKILLS_MAX_TOKENS=4000
|
||||
|
||||
# Scan skill directories at startup
|
||||
SKILLS_AUTO_DISCOVER=true
|
||||
```
|
||||
|
||||
## How Installation Works
|
||||
|
||||
<Steps>
|
||||
<Step title="Search or browse">
|
||||
Use `skill_search` or visit [clawhub.dev](https://clawhub.dev) to find a skill. Note the skill's slug (e.g., `python-lint`).
|
||||
</Step>
|
||||
|
||||
<Step title="Install">
|
||||
Run `skill_install <slug>`. The agent fetches the SKILL.md from the registry and writes it to `~/.ironclaw/installed_skills/<slug>/SKILL.md`.
|
||||
</Step>
|
||||
|
||||
<Step title="Activation">
|
||||
On the next turn, the skill is eligible for scoring and injection. It will activate automatically when your messages match its keywords or patterns.
|
||||
</Step>
|
||||
|
||||
<Step title="Verification">
|
||||
Run `skill_list` to confirm the skill appears with `(installed)` trust level.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Updating Skills
|
||||
|
||||
Skills installed from ClawHub are pinned to the version at installation time. To update:
|
||||
|
||||
```
|
||||
skill_remove python-lint
|
||||
skill_install python-lint
|
||||
```
|
||||
|
||||
This removes the old version and installs the current latest from the registry.
|
||||
|
||||
## Publishing to ClawHub
|
||||
|
||||
ClawHub is an open registry. To publish a skill:
|
||||
|
||||
1. Write a valid SKILL.md following the [SKILL.md Format](/agents/skills-format)
|
||||
2. Test it locally by placing it in `~/.ironclaw/skills/`
|
||||
3. Submit via the ClawHub web interface at [clawhub.dev](https://clawhub.dev)
|
||||
|
||||
Published skills are reviewed before appearing in search results.
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Skills Overview" icon="puzzle" href="/agents/skills">
|
||||
How the activation pipeline and trust levels work
|
||||
</Card>
|
||||
|
||||
<Card title="SKILL.md Format" icon="file-text" href="/agents/skills-format">
|
||||
Write your own skills from scratch
|
||||
</Card>
|
||||
</CardGroup>
|
||||
126
docs/drafts/agents/index.mdx
Normal file
@@ -0,0 +1,126 @@
|
||||
---
|
||||
title: Agent Overview
|
||||
sidebarTitle: Overview
|
||||
description: How IronClaw's agent runtime processes requests and executes jobs
|
||||
---
|
||||
|
||||
IronClaw's agent runtime is an always-on loop that accepts messages from any channel, classifies intent, and dispatches work as parallel jobs — each independently tracked through a defined state machine.
|
||||
|
||||
## Agent Loop
|
||||
|
||||
When a message arrives, the agent loop:
|
||||
|
||||
1. **Classifies intent** — The router determines whether the message is a new task, a follow-up, a command (undo, compact, clear), or a system event
|
||||
2. **Creates a job** — Each unit of work gets its own isolated job context with conversation memory
|
||||
3. **Schedules execution** — The scheduler dispatches the job to a worker, respecting the `MAX_PARALLEL_JOBS` limit
|
||||
4. **Runs the worker** — The worker performs LLM reasoning, selects tools, and executes in a loop until the job reaches a terminal state
|
||||
5. **Returns output** — Results stream back to the originating channel in real time
|
||||
|
||||
```
|
||||
Channel Input
|
||||
↓
|
||||
[Router] → Classify intent
|
||||
↓
|
||||
[Scheduler] → Check capacity (MAX_PARALLEL_JOBS)
|
||||
↓
|
||||
[Worker] → LLM reasoning + tool execution
|
||||
↓
|
||||
[Safety Layer] → Scan outputs
|
||||
↓
|
||||
Channel Response
|
||||
```
|
||||
|
||||
## Parallel Jobs
|
||||
|
||||
Multiple jobs run concurrently. Each job has its own context — memory, tool state, and conversation history — isolated from other jobs.
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `MAX_PARALLEL_JOBS` | `5` | Maximum concurrent jobs per agent instance |
|
||||
|
||||
When the limit is reached, new requests queue until a job slot opens. The scheduler uses a priority queue and respects job order within the same session.
|
||||
|
||||
## Job State Machine
|
||||
|
||||
Every job moves through a defined set of states:
|
||||
|
||||
```
|
||||
Pending
|
||||
↓
|
||||
InProgress ──────────────┬──► Completed
|
||||
↑ │
|
||||
│ (recovery) └──► Failed
|
||||
│
|
||||
Stuck ────────────────────► Failed (if unrecoverable)
|
||||
```
|
||||
|
||||
| State | Meaning |
|
||||
|-------|---------|
|
||||
| **Pending** | Created, waiting for a worker slot |
|
||||
| **InProgress** | Actively executing |
|
||||
| **Completed** | Finished successfully |
|
||||
| **Failed** | Terminal failure — no further retries |
|
||||
| **Stuck** | No progress detected — recovery attempted |
|
||||
|
||||
The self-repair system monitors InProgress jobs and transitions stuck ones back to InProgress for recovery. See [Self-Repair](/agents/self-repair) for details.
|
||||
|
||||
## Session Model
|
||||
|
||||
IronClaw uses a three-level session hierarchy:
|
||||
|
||||
| Level | Description |
|
||||
|-------|-------------|
|
||||
| **Session** | A named context (e.g., a project or topic) |
|
||||
| **Thread** | A conversation within a session |
|
||||
| **Turn** | A single user message + agent response pair |
|
||||
|
||||
Each turn is a checkpoint. You can undo the last turn, redo a cancelled turn, or compact old turns to reduce context window pressure.
|
||||
|
||||
## Undo / Redo
|
||||
|
||||
The agent tracks every turn with a state checkpoint:
|
||||
|
||||
```
|
||||
undo → Revert to the state before the last turn
|
||||
redo → Re-apply the most recently undone turn
|
||||
compact → Summarize old turns to free context window space
|
||||
clear → Reset the current thread
|
||||
```
|
||||
|
||||
Type these commands directly in any channel — the submission parser intercepts them before they reach the LLM.
|
||||
|
||||
## Context Window Management
|
||||
|
||||
As conversations grow, context pressure increases. IronClaw handles this automatically:
|
||||
|
||||
- **Context monitor** tracks token usage per turn
|
||||
- **Compaction** summarizes old turns when pressure is high
|
||||
- **Manual compact** is available via the `compact` command
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Jobs & Parallel Execution" icon="layers" href="/agents/jobs">
|
||||
State machine details, job tools, and concurrency configuration
|
||||
</Card>
|
||||
|
||||
<Card title="Self-Repair" icon="wrench" href="/agents/self-repair">
|
||||
Automatic detection and recovery of stuck jobs
|
||||
</Card>
|
||||
|
||||
<Card title="Skills" icon="puzzle" href="/agents/skills">
|
||||
Context-aware prompt extensions that activate automatically
|
||||
</Card>
|
||||
|
||||
<Card title="Routines" icon="clock" href="/agents/routines">
|
||||
Scheduled and event-driven automation
|
||||
</Card>
|
||||
|
||||
<Card title="Memory" icon="database" href="/agents/memory">
|
||||
Persistent workspace with hybrid search
|
||||
</Card>
|
||||
|
||||
<Card title="Heartbeat" icon="activity" href="/agents/heartbeat">
|
||||
Proactive periodic execution
|
||||
</Card>
|
||||
</CardGroup>
|
||||
131
docs/drafts/agents/memory-search.mdx
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
title: Hybrid Search
|
||||
sidebarTitle: Hybrid Search
|
||||
description: FTS + vector search combined via Reciprocal Rank Fusion
|
||||
---
|
||||
|
||||
The `memory_search` tool uses hybrid search — combining keyword-based full-text search (FTS) with semantic vector search — and merges the results using Reciprocal Rank Fusion (RRF). This gives you the precision of exact keyword matches and the recall of semantic similarity in a single query.
|
||||
|
||||
## Why Hybrid Search
|
||||
|
||||
Neither search method alone is sufficient:
|
||||
|
||||
| Method | Strength | Weakness |
|
||||
|--------|----------|----------|
|
||||
| **FTS (keyword)** | Finds exact terms, names, IDs | Misses synonyms and paraphrased content |
|
||||
| **Vector (semantic)** | Finds related concepts even with different words | Can miss exact matches, slower |
|
||||
| **Hybrid (RRF)** | Best of both — precise and semantically aware | Requires embedding model |
|
||||
|
||||
## Reciprocal Rank Fusion (RRF)
|
||||
|
||||
RRF merges two ranked result lists into a single ranked list without needing to calibrate score scales between the two systems.
|
||||
|
||||
For each document that appears in either result list:
|
||||
|
||||
```
|
||||
RRF_score = 1 / (k + rank_fts) + 1 / (k + rank_vector)
|
||||
```
|
||||
|
||||
Where `k` is a smoothing constant (typically 60) and `rank` is the 1-based position in each list. Documents appearing in both lists score higher than those in only one. Documents ranked near the top of either list score higher than those ranked lower.
|
||||
|
||||
The final results are sorted by RRF score descending.
|
||||
|
||||
## Chunking Strategy
|
||||
|
||||
Documents are not searched as whole files. They are split into overlapping chunks before indexing:
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| **Chunk size** | 800 tokens |
|
||||
| **Overlap** | 15% (~120 tokens between chunks) |
|
||||
|
||||
The overlap ensures that content near chunk boundaries is not lost. Each chunk is independently indexed for FTS and vector search. Search results return the matching chunk with its parent document path.
|
||||
|
||||
When you write a long document via `memory_write`, it is automatically chunked and indexed. Updating a document re-chunks and re-indexes it entirely.
|
||||
|
||||
## Embedding Providers
|
||||
|
||||
Vector search requires embedding models to convert text into semantic vectors. IronClaw supports two embedding providers:
|
||||
|
||||
### OpenAI (default)
|
||||
|
||||
Uses OpenAI's text embedding API. Requires `OPENAI_API_KEY`.
|
||||
|
||||
| Model | Dimensions | Notes |
|
||||
|-------|-----------|-------|
|
||||
| `text-embedding-3-small` | 1536 | Default — fast, cost-effective |
|
||||
| `text-embedding-3-large` | 3072 | Higher quality, higher cost |
|
||||
|
||||
### NEAR AI
|
||||
|
||||
Uses NEAR AI's embedding endpoint. Requires NEAR AI authentication (session token or API key).
|
||||
|
||||
```bash
|
||||
EMBEDDING_PROVIDER=nearai
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# Embedding provider: "openai" (default) or "nearai"
|
||||
EMBEDDING_PROVIDER=openai
|
||||
|
||||
# Enable vector search (requires embedding provider)
|
||||
EMBEDDING_ENABLED=true
|
||||
|
||||
# Embedding model (OpenAI provider)
|
||||
EMBEDDING_MODEL=text-embedding-3-small
|
||||
|
||||
# OpenAI API key (required for OpenAI embedding provider)
|
||||
OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
<Note>
|
||||
If `EMBEDDING_ENABLED=false` or no embedding provider is configured, `memory_search` falls back to FTS-only search. Results will still be useful but semantic similarity matching is disabled.
|
||||
</Note>
|
||||
|
||||
## FTS Backend
|
||||
|
||||
Full-text search is implemented differently per database backend:
|
||||
|
||||
| Backend | FTS Implementation |
|
||||
|---------|--------------------|
|
||||
| **PostgreSQL** | `tsvector` with `ts_rank_cd` scoring |
|
||||
| **libSQL** | FTS5 virtual table with sync triggers |
|
||||
|
||||
Both backends support the same query interface. Queries are tokenized, stemmed, and matched against indexed document chunks.
|
||||
|
||||
## Query Tips
|
||||
|
||||
- **Keyword queries** work best for specific terms, names, IDs, and exact phrases
|
||||
- **Natural language queries** work best for semantic search (e.g., "how we decided to handle authentication")
|
||||
- **Hybrid queries** benefit from including both specific terms and context
|
||||
|
||||
```
|
||||
memory_search "kubernetes deployment errors last week"
|
||||
memory_search "why did we choose postgres over sqlite"
|
||||
memory_search "KUBECONFIG authentication setup"
|
||||
```
|
||||
|
||||
## Database Storage
|
||||
|
||||
Chunks and their embeddings are stored in two tables:
|
||||
|
||||
| Table | Contents |
|
||||
|-------|----------|
|
||||
| `memory_documents` | Document metadata: path, title, created/updated timestamps |
|
||||
| `memory_chunks` | Chunk content, FTS index, and vector embeddings |
|
||||
|
||||
Vector embeddings are stored as `VECTOR(1536)` (PostgreSQL with pgvector) or `F32_BLOB(1536)` (libSQL with libsql_vector_idx).
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Memory & Workspace" icon="database" href="/agents/memory">
|
||||
Workspace structure and the four memory tools
|
||||
</Card>
|
||||
|
||||
<Card title="Identity Files" icon="user" href="/agents/identity-files">
|
||||
Files injected into the system prompt on every turn
|
||||
</Card>
|
||||
</CardGroup>
|
||||
130
docs/drafts/agents/routines.mdx
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
title: Routines Overview
|
||||
sidebarTitle: Routines
|
||||
description: Scheduled and reactive task automation
|
||||
---
|
||||
|
||||
Routines let IronClaw take action without a human in the loop. Define a trigger, an action, and optional guardrails — the agent handles the rest automatically.
|
||||
|
||||
## Two Trigger Types
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Cron" icon="clock" href="/agents/routines-cron">
|
||||
Schedule tasks using cron expressions. Runs on a repeating schedule — hourly, daily, weekly, or any custom interval.
|
||||
</Card>
|
||||
|
||||
<Card title="Reactive" icon="zap" href="/agents/routines-reactive">
|
||||
React to events and webhooks. Triggers when specific conditions are met — a file changes, a webhook fires, or an internal event occurs.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Routine Anatomy
|
||||
|
||||
Every routine has three parts:
|
||||
|
||||
### Trigger
|
||||
|
||||
What causes the routine to run. Either a cron schedule or an event/webhook condition.
|
||||
|
||||
```yaml
|
||||
trigger:
|
||||
type: cron
|
||||
schedule: "0 9 * * 1-5" # 9am Monday–Friday
|
||||
```
|
||||
|
||||
```yaml
|
||||
trigger:
|
||||
type: webhook
|
||||
path: /hooks/deploy
|
||||
secret: ${DEPLOY_WEBHOOK_SECRET}
|
||||
```
|
||||
|
||||
### Action
|
||||
|
||||
What the routine does when triggered. This is a prompt sent to the agent — the full reasoning and tool execution loop runs as a job.
|
||||
|
||||
```yaml
|
||||
action:
|
||||
prompt: |
|
||||
Check the last 24 hours of application logs for errors.
|
||||
Summarize any critical issues and write a report to memory at daily/errors.md.
|
||||
```
|
||||
|
||||
### Guardrails
|
||||
|
||||
Optional constraints that limit what the routine can do. Guardrails prevent runaway automation.
|
||||
|
||||
```yaml
|
||||
guardrails:
|
||||
max_tokens: 8000 # Stop if LLM usage exceeds this per run
|
||||
max_tool_calls: 20 # Stop if more than 20 tool calls are made
|
||||
allowed_tools: # Whitelist specific tools (empty = all allowed)
|
||||
- memory_write
|
||||
- memory_read
|
||||
- memory_search
|
||||
timeout_secs: 300 # Kill the job after 5 minutes
|
||||
```
|
||||
|
||||
## Managing Routines
|
||||
|
||||
### Via Chat
|
||||
|
||||
Create routines by describing them to the agent:
|
||||
|
||||
```
|
||||
Create a routine that runs every morning at 8am and checks my email inbox
|
||||
for anything urgent, then writes a summary to memory.
|
||||
```
|
||||
|
||||
The agent will use the `routine_create` tool to set it up.
|
||||
|
||||
### Via Web UI
|
||||
|
||||
The web gateway includes a Routines tab with a visual interface for creating, editing, enabling, and disabling routines. Changes take effect on the next tick.
|
||||
|
||||
### Via Tools
|
||||
|
||||
Six built-in tools manage routines programmatically:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `routine_create` | Create a new routine with trigger, action, and guardrails |
|
||||
| `routine_list` | List all routines with status and last-run info |
|
||||
| `routine_update` | Modify an existing routine's trigger, action, or guardrails |
|
||||
| `routine_delete` | Remove a routine permanently |
|
||||
| `routine_history` | View execution history and outcomes for a routine |
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# Enable routines system
|
||||
ROUTINES_ENABLED=true
|
||||
|
||||
# Cron tick interval — how often the engine checks for due cron jobs
|
||||
ROUTINES_CRON_INTERVAL=60 # seconds (default: 60)
|
||||
|
||||
# Maximum routines running at the same time
|
||||
ROUTINES_MAX_CONCURRENT=3
|
||||
```
|
||||
|
||||
<Note>
|
||||
`ROUTINES_CRON_INTERVAL` controls tick granularity, not execution frequency. A routine scheduled at `0 * * * *` (every hour on the hour) will fire within `ROUTINES_CRON_INTERVAL` seconds of the scheduled time.
|
||||
</Note>
|
||||
|
||||
## Execution Model
|
||||
|
||||
When a routine fires, it creates a standard agent job. The job runs through the full agent loop — LLM reasoning, tool calls, safety layer — with the action prompt as the initial message. Guardrails are enforced by the routine engine as the job runs.
|
||||
|
||||
Routine jobs appear in job history alongside manual jobs. You can inspect them with `list_jobs` or `job_status`.
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Cron Routines" icon="clock" href="/agents/routines-cron">
|
||||
Schedule recurring tasks with cron expressions
|
||||
</Card>
|
||||
|
||||
<Card title="Reactive Routines" icon="zap" href="/agents/routines-reactive">
|
||||
Event-driven and webhook-triggered automation
|
||||
</Card>
|
||||
</CardGroup>
|
||||
172
docs/drafts/agents/skills-format.mdx
Normal file
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: SKILL.md Format
|
||||
sidebarTitle: SKILL.md Format
|
||||
description: YAML frontmatter schema for writing custom skills
|
||||
---
|
||||
|
||||
A SKILL.md file has two parts: a YAML frontmatter block that controls when and how the skill activates, and a markdown body that gets injected into the LLM context when it does.
|
||||
|
||||
## Full Example
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: kubernetes-deploy
|
||||
version: 0.2.0
|
||||
description: Kubernetes deployment and operations guidance
|
||||
activation:
|
||||
patterns:
|
||||
- "deploy to.*production"
|
||||
- "rollback.*deployment"
|
||||
- "kubectl.*error"
|
||||
keywords:
|
||||
- deployment
|
||||
- kubernetes
|
||||
- kubectl
|
||||
- k8s
|
||||
- pod
|
||||
- namespace
|
||||
max_context_tokens: 2000
|
||||
metadata:
|
||||
ironclaw:
|
||||
requires:
|
||||
bins:
|
||||
- kubectl
|
||||
- docker
|
||||
env:
|
||||
- KUBECONFIG
|
||||
---
|
||||
|
||||
# Kubernetes Deployment Skill
|
||||
|
||||
You are operating in a Kubernetes environment. Follow these guidelines:
|
||||
|
||||
## Deployment Checklist
|
||||
|
||||
Before deploying to production:
|
||||
1. Verify the image tag is pinned (never use `latest`)
|
||||
2. Check resource limits are set on all containers
|
||||
3. Confirm readiness and liveness probes are defined
|
||||
4. Review the rollout strategy (RollingUpdate recommended)
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
# Check rollout status
|
||||
kubectl rollout status deployment/<name> -n <namespace>
|
||||
|
||||
# Rollback to previous version
|
||||
kubectl rollout undo deployment/<name> -n <namespace>
|
||||
|
||||
# View pod logs
|
||||
kubectl logs -l app=<name> -n <namespace> --tail=100
|
||||
```
|
||||
|
||||
## Error Patterns
|
||||
|
||||
- `ImagePullBackOff` → Check image name, tag, and registry credentials
|
||||
- `CrashLoopBackOff` → Check container logs and resource limits
|
||||
- `Pending` → Check node resources and PVC availability
|
||||
```
|
||||
|
||||
## Frontmatter Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `name` | string | Yes | Unique identifier for the skill. Used in `skill_list` output and log messages. Use kebab-case. |
|
||||
| `version` | string | Yes | SemVer version string (e.g., `0.1.0`). Used for update detection from ClawHub. |
|
||||
| `description` | string | Yes | One-line human-readable description. Shown in skill listings and registry search results. |
|
||||
| `activation.patterns` | string[] | No | Regex patterns matched against the full incoming message. Any match raises the skill's score. |
|
||||
| `activation.keywords` | string[] | No | Simple keyword matches (case-insensitive, substring). Each keyword match adds to the score. |
|
||||
| `activation.max_context_tokens` | integer | No | Maximum tokens this skill may contribute per turn. Defaults to `SKILLS_MAX_TOKENS / 2` if omitted. |
|
||||
| `metadata.ironclaw.requires.bins` | string[] | No | Binaries that must exist on `PATH`. Skill is gated out if any are missing. |
|
||||
| `metadata.ironclaw.requires.env` | string[] | No | Environment variables that must be set. Skill is gated out if any are missing. |
|
||||
|
||||
## Activation Scoring
|
||||
|
||||
Skills are scored before selection. The scoring algorithm is deterministic:
|
||||
|
||||
- Each **keyword** match in the incoming message: +1 point
|
||||
- Each **pattern** (regex) match: +3 points
|
||||
- Skills with zero score are not injected (unless no other skills match)
|
||||
|
||||
When multiple skills score equally, they are ordered by `name` for reproducibility.
|
||||
|
||||
## Markdown Body
|
||||
|
||||
Everything after the closing `---` of the frontmatter is the skill's body. This is injected verbatim into the LLM system prompt when the skill activates.
|
||||
|
||||
Write the body as instructions to the agent:
|
||||
|
||||
```markdown
|
||||
# My Skill
|
||||
|
||||
You are helping with [topic]. Follow these guidelines:
|
||||
|
||||
- Guideline one
|
||||
- Guideline two
|
||||
|
||||
## Reference
|
||||
|
||||
Include tables, code examples, and structured information the agent
|
||||
should have available when handling related requests.
|
||||
```
|
||||
|
||||
### Body Guidelines
|
||||
|
||||
- Keep it concise — every token injected costs budget
|
||||
- Write in second person ("You are...", "When the user asks...")
|
||||
- Include concrete examples and reference tables where useful
|
||||
- Avoid narrative prose — the agent prefers structured information
|
||||
- Code blocks in the body are injected as-is into the prompt
|
||||
|
||||
## Minimal Skill Example
|
||||
|
||||
The simplest valid SKILL.md:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: git-helper
|
||||
version: 0.1.0
|
||||
description: Git workflow guidance
|
||||
activation:
|
||||
keywords:
|
||||
- git
|
||||
- commit
|
||||
- branch
|
||||
- merge
|
||||
---
|
||||
|
||||
# Git Helper
|
||||
|
||||
Follow conventional commits format: `type(scope): description`
|
||||
|
||||
Types: feat, fix, docs, style, refactor, test, chore
|
||||
|
||||
Always check `git status` before committing.
|
||||
```
|
||||
|
||||
## File Location
|
||||
|
||||
Place your SKILL.md file in one of the trusted directories:
|
||||
|
||||
```bash
|
||||
# Global skill (available everywhere)
|
||||
~/.ironclaw/skills/my-skill/SKILL.md
|
||||
|
||||
# Workspace-scoped skill
|
||||
<workspace>/skills/my-skill/SKILL.md
|
||||
```
|
||||
|
||||
The directory name does not need to match the `name` field, but using the same value avoids confusion.
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Skills Overview" icon="puzzle" href="/agents/skills">
|
||||
Activation pipeline, trust levels, and skill directories
|
||||
</Card>
|
||||
|
||||
<Card title="ClawHub Registry" icon="package" href="/agents/clawhub">
|
||||
Share and discover skills from the community registry
|
||||
</Card>
|
||||
</CardGroup>
|
||||
7
docs/drafts/assets/favicon.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" fill="#5B6FFF" rx="12"/>
|
||||
<path d="M30 35 L50 25 L70 35 L70 50 L50 60 L30 50 Z" fill="white"/>
|
||||
<path d="M30 50 L50 60 L50 75 L30 65 Z" fill="#8B9AFF"/>
|
||||
<path d="M70 50 L50 60 L50 75 L70 65 Z" fill="#4A5FE6"/>
|
||||
<circle cx="50" cy="42" r="8" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 363 B |
2214
docs/drafts/assets/ironclaw-architecture.excalidraw
Normal file
BIN
docs/drafts/assets/ironclaw-architecture.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
5
docs/drafts/assets/ironclaw-architecture.svg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
docs/drafts/assets/ironclaw.png
Normal file
|
After Width: | Height: | Size: 267 KiB |
925
docs/drafts/assets/safety-layer-overview.excalidraw
Normal file
@@ -0,0 +1,925 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "title",
|
||||
"x": 328.47265625,
|
||||
"y": 16.1015625,
|
||||
"width": 400,
|
||||
"height": 40,
|
||||
"text": "Safety Layer Overview",
|
||||
"originalText": "Safety Layer Overview",
|
||||
"fontSize": 32,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "top",
|
||||
"strokeColor": "#1e40af",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400001,
|
||||
"version": 65,
|
||||
"versionNonce": 1932644593,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"lineHeight": 1.25,
|
||||
"index": "a0",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735226247,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "safety_layer",
|
||||
"x": 331.01171875,
|
||||
"y": 79.5,
|
||||
"width": 400,
|
||||
"height": 450,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#fef3c7",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400010,
|
||||
"version": 162,
|
||||
"versionNonce": 193759953,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"index": "a1",
|
||||
"frameId": null,
|
||||
"updated": 1772735226247
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "safety_label",
|
||||
"x": 489.01953125,
|
||||
"y": 94.5,
|
||||
"width": 10.546875,
|
||||
"height": 22.5,
|
||||
"text": "",
|
||||
"originalText": "",
|
||||
"fontSize": 18,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#b45309",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400012,
|
||||
"version": 165,
|
||||
"versionNonce": 586655281,
|
||||
"isDeleted": true,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"lineHeight": 1.25,
|
||||
"index": "a2",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735231946,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "validator",
|
||||
"x": 381.01171875,
|
||||
"y": 139.5,
|
||||
"width": 300,
|
||||
"height": 80,
|
||||
"strokeColor": "#b45309",
|
||||
"backgroundColor": "#ffffff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400020,
|
||||
"version": 163,
|
||||
"versionNonce": 1409638111,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"index": "a3",
|
||||
"frameId": null,
|
||||
"updated": 1772735244009
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "validator_title",
|
||||
"x": 526.32421875,
|
||||
"y": 169.5,
|
||||
"width": 9.375,
|
||||
"height": 20,
|
||||
"text": "",
|
||||
"originalText": "",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400022,
|
||||
"version": 167,
|
||||
"versionNonce": 584329983,
|
||||
"isDeleted": true,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": "validator",
|
||||
"lineHeight": 1.25,
|
||||
"index": "a4",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735244010,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "validator_desc",
|
||||
"x": 391.01171875,
|
||||
"y": 184.5,
|
||||
"width": 253.125,
|
||||
"height": 15,
|
||||
"text": "Length, encoding, forbidden patterns",
|
||||
"originalText": "Length, encoding, forbidden patterns",
|
||||
"fontSize": 12,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400024,
|
||||
"version": 163,
|
||||
"versionNonce": 615017951,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"lineHeight": 1.25,
|
||||
"index": "a5",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735238046,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "sanitizer",
|
||||
"x": 380.890625,
|
||||
"y": 239.5,
|
||||
"width": 300,
|
||||
"height": 80,
|
||||
"strokeColor": "#b45309",
|
||||
"backgroundColor": "#ffffff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400030,
|
||||
"version": 164,
|
||||
"versionNonce": 1272218623,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"index": "a6",
|
||||
"frameId": null,
|
||||
"updated": 1772735258774
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "sanitizer_title",
|
||||
"x": 526.203125,
|
||||
"y": 269.5,
|
||||
"width": 9.375,
|
||||
"height": 20,
|
||||
"text": "",
|
||||
"originalText": "",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400032,
|
||||
"version": 168,
|
||||
"versionNonce": 1539419167,
|
||||
"isDeleted": true,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": "sanitizer",
|
||||
"lineHeight": 1.25,
|
||||
"index": "a7",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735258774,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "sanitizer_desc",
|
||||
"x": 407.0546875,
|
||||
"y": 288.06640625,
|
||||
"width": 246.09375,
|
||||
"height": 15,
|
||||
"text": "Pattern detection, content escaping",
|
||||
"originalText": "Pattern detection, content escaping",
|
||||
"fontSize": 12,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400034,
|
||||
"version": 193,
|
||||
"versionNonce": 130606815,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"lineHeight": 1.25,
|
||||
"index": "a8",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735255062,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "policy",
|
||||
"x": 383.40234375,
|
||||
"y": 342.81640625,
|
||||
"width": 300,
|
||||
"height": 80,
|
||||
"strokeColor": "#b45309",
|
||||
"backgroundColor": "#ffffff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400040,
|
||||
"version": 190,
|
||||
"versionNonce": 464100415,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"index": "a9",
|
||||
"frameId": null,
|
||||
"updated": 1772735322968
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "policy_title",
|
||||
"x": 526.32421875,
|
||||
"y": 369.5,
|
||||
"width": 9.375,
|
||||
"height": 20,
|
||||
"text": "",
|
||||
"originalText": "",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400042,
|
||||
"version": 167,
|
||||
"versionNonce": 1799052671,
|
||||
"isDeleted": true,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": "policy",
|
||||
"lineHeight": 1.25,
|
||||
"index": "aA",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735265767,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "policy_desc",
|
||||
"x": 443.74609375,
|
||||
"y": 379.23828125,
|
||||
"width": 196.875,
|
||||
"height": 30,
|
||||
"text": "Severity rules:\n Critical, High, Medium, Low",
|
||||
"originalText": "Severity rules:\n Critical, High, Medium, Low",
|
||||
"fontSize": 12,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400044,
|
||||
"version": 174,
|
||||
"versionNonce": 401914911,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"lineHeight": 1.25,
|
||||
"index": "aB",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735332470,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "leak",
|
||||
"x": 381.01171875,
|
||||
"y": 439.5,
|
||||
"width": 300,
|
||||
"height": 80,
|
||||
"strokeColor": "#b45309",
|
||||
"backgroundColor": "#ffffff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400050,
|
||||
"version": 163,
|
||||
"versionNonce": 724104369,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"index": "aC",
|
||||
"frameId": null,
|
||||
"updated": 1772735270808
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "leak_title",
|
||||
"x": 526.32421875,
|
||||
"y": 469.5,
|
||||
"width": 9.375,
|
||||
"height": 20,
|
||||
"text": "",
|
||||
"originalText": "",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400052,
|
||||
"version": 167,
|
||||
"versionNonce": 1214854801,
|
||||
"isDeleted": true,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": "leak",
|
||||
"lineHeight": 1.25,
|
||||
"index": "aD",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735270808,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "leak_desc",
|
||||
"x": 435.39453125,
|
||||
"y": 478.84375,
|
||||
"width": 210.9375,
|
||||
"height": 30,
|
||||
"text": "15+ secret patterns: \nAPI keys, tokens, private keys",
|
||||
"originalText": "15+ secret patterns: \nAPI keys, tokens, private keys",
|
||||
"fontSize": 12,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400054,
|
||||
"version": 197,
|
||||
"versionNonce": 764993873,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"lineHeight": 1.25,
|
||||
"index": "aE",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735314549,
|
||||
"autoResize": true
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "arrow1",
|
||||
"x": 531.01171875,
|
||||
"y": 219.5,
|
||||
"width": 0,
|
||||
"height": 20,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400100,
|
||||
"version": 162,
|
||||
"versionNonce": 1600952593,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
20
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "validator",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "sanitizer",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"index": "aF",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735226247
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "arrow2",
|
||||
"x": 531.01171875,
|
||||
"y": 319.5,
|
||||
"width": 0,
|
||||
"height": 20,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400110,
|
||||
"version": 162,
|
||||
"versionNonce": 293162737,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
20
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "sanitizer",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "policy",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"index": "aG",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735226247
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "arrow3",
|
||||
"x": 531.01171875,
|
||||
"y": 419.5,
|
||||
"width": 0,
|
||||
"height": 20,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 400120,
|
||||
"version": 162,
|
||||
"versionNonce": 808732881,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
20
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "policy",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "leak",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"index": "aH",
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772735226247
|
||||
},
|
||||
{
|
||||
"id": "YJybso9KYw4iOFCWvChiV",
|
||||
"type": "text",
|
||||
"x": 820.5,
|
||||
"y": 480.5,
|
||||
"width": 8,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "aI",
|
||||
"roundness": null,
|
||||
"seed": 1190148401,
|
||||
"version": 3,
|
||||
"versionNonce": 1671504543,
|
||||
"isDeleted": true,
|
||||
"boundElements": null,
|
||||
"updated": 1772735222093,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "_DDMgdorEChvdytqe-Dy6",
|
||||
"type": "text",
|
||||
"x": 471.25390625,
|
||||
"y": 95.8359375,
|
||||
"width": 126.26000213623047,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "aJ",
|
||||
"roundness": null,
|
||||
"seed": 107329553,
|
||||
"version": 32,
|
||||
"versionNonce": 1301645809,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1772735236929,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Safety Layer",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "Safety Layer",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "IWiSVoJgF-qlEiY6aawW2",
|
||||
"type": "text",
|
||||
"x": 480.66796875,
|
||||
"y": 151.24609375,
|
||||
"width": 86.66000366210938,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "aK",
|
||||
"roundness": null,
|
||||
"seed": 573862687,
|
||||
"version": 26,
|
||||
"versionNonce": 159393407,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1772735249728,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Validator",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "Validator",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "7xQl6XXaj1kPeCo2nofJC",
|
||||
"type": "text",
|
||||
"x": 477.88671875,
|
||||
"y": 253.71875,
|
||||
"width": 85.72000122070312,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "aL",
|
||||
"roundness": null,
|
||||
"seed": 538234943,
|
||||
"version": 3,
|
||||
"versionNonce": 893730545,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1772735261698,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Sanitizer",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "Sanitizer",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "LlD4VsiLOLfqwHkb9RFrY",
|
||||
"type": "text",
|
||||
"x": 473.03125,
|
||||
"y": 350.41796875,
|
||||
"width": 125.31999969482422,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "aM",
|
||||
"roundness": null,
|
||||
"seed": 1813075359,
|
||||
"version": 18,
|
||||
"versionNonce": 1943720607,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1772735327819,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Policy Engine",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "Policy Engine",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "Vu-zf114jdCd5Fhmoc0r2",
|
||||
"type": "text",
|
||||
"x": 475.27734375,
|
||||
"y": 453.171875,
|
||||
"width": 141.3000030517578,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "aN",
|
||||
"roundness": null,
|
||||
"seed": 1007041649,
|
||||
"version": 3,
|
||||
"versionNonce": 1726137183,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1772735275182,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Leak Detector",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "Leak Detector",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 20,
|
||||
"gridStep": 5,
|
||||
"gridModeEnabled": false,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"lockedMultiSelections": {}
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
BIN
docs/drafts/assets/safety-layer-overview.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
5
docs/drafts/assets/safety-layer-overview.svg
Normal file
|
After Width: | Height: | Size: 25 KiB |
470
docs/drafts/assets/sandbox-network-proxy.excalidraw
Normal file
@@ -0,0 +1,470 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [
|
||||
{
|
||||
"id": "title",
|
||||
"type": "text",
|
||||
"x": 206.65034702845998,
|
||||
"y": 42.7890625,
|
||||
"width": 363.3343505859375,
|
||||
"height": 36.91015624999993,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e40af",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Sandbox Network Proxy",
|
||||
"fontSize": 29.528124999999942,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "top",
|
||||
"boundElements": [],
|
||||
"version": 166,
|
||||
"versionNonce": 218929233,
|
||||
"index": "a0",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772736580609,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Sandbox Network Proxy",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "subtitle",
|
||||
"type": "text",
|
||||
"x": 246.62890625,
|
||||
"y": 82.9140625,
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"angle": 0,
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Credential Injection Flow",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "top",
|
||||
"boundElements": [],
|
||||
"version": 51,
|
||||
"versionNonce": 1633117745,
|
||||
"index": "a1",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772736580609,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Credential Injection Flow",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "container",
|
||||
"type": "rectangle",
|
||||
"x": 100,
|
||||
"y": 150,
|
||||
"width": 140,
|
||||
"height": 70,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#dbeafe",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_container",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"versionNonce": 138430655,
|
||||
"index": "a2",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772736551060,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_container",
|
||||
"type": "text",
|
||||
"x": 110,
|
||||
"y": 165,
|
||||
"width": 120,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Container",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "container",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 1364518577,
|
||||
"index": "a3",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772736551060,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "Container",
|
||||
"autoResize": true,
|
||||
"lineHeight": 2.5
|
||||
},
|
||||
{
|
||||
"id": "proxy",
|
||||
"type": "rectangle",
|
||||
"x": 320,
|
||||
"y": 150,
|
||||
"width": 140,
|
||||
"height": 70,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#3b82f6",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_proxy",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"versionNonce": 1205262559,
|
||||
"index": "a4",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772736551060,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_proxy",
|
||||
"type": "text",
|
||||
"x": 330,
|
||||
"y": 165,
|
||||
"width": 120,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#ffffff",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Proxy",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "proxy",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 1749477521,
|
||||
"index": "a5",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772736551060,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "Proxy",
|
||||
"autoResize": true,
|
||||
"lineHeight": 2.5
|
||||
},
|
||||
{
|
||||
"id": "external_service",
|
||||
"type": "rectangle",
|
||||
"x": 540,
|
||||
"y": 150,
|
||||
"width": 140,
|
||||
"height": 70,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#a7f3d0",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_external",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"versionNonce": 1382442239,
|
||||
"index": "a6",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772736551060,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_external",
|
||||
"type": "text",
|
||||
"x": 550,
|
||||
"y": 165,
|
||||
"width": 120,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "External\nService",
|
||||
"fontSize": 14,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "external_service",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 962440817,
|
||||
"index": "a7",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772736551060,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "External\nService",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.4285714285714286
|
||||
},
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow",
|
||||
"x": 240,
|
||||
"y": 185,
|
||||
"width": 80,
|
||||
"height": 0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#1e3a5f",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
80,
|
||||
0
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "container",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "proxy",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"version": 2,
|
||||
"versionNonce": 1466920223,
|
||||
"index": "a8",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"boundElements": [],
|
||||
"updated": 1772736551060,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow",
|
||||
"x": 460,
|
||||
"y": 185,
|
||||
"width": 80,
|
||||
"height": 0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#1e3a5f",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
80,
|
||||
0
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "proxy",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "external_service",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"version": 2,
|
||||
"versionNonce": 727758929,
|
||||
"index": "a9",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"boundElements": [],
|
||||
"updated": 1772736551060,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "label_inject",
|
||||
"type": "text",
|
||||
"x": 347.8125,
|
||||
"y": 210,
|
||||
"width": 84.375,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "│\n▼\nInjects Auth",
|
||||
"fontSize": 12,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "top",
|
||||
"boundElements": [],
|
||||
"version": 3,
|
||||
"versionNonce": 2107319953,
|
||||
"index": "aA",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772736553427,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "│\n▼\nInjects Auth",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.1111111111111112
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 20,
|
||||
"gridStep": 5,
|
||||
"gridModeEnabled": false,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"lockedMultiSelections": {}
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
BIN
docs/drafts/assets/sandbox-network-proxy.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
4
docs/drafts/assets/sandbox-network-proxy.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/drafts/assets/screenshots/web-chat-overview.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/drafts/assets/screenshots/web-extensions-overview.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
docs/drafts/assets/screenshots/web-memory-overview.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/drafts/assets/screenshots/web-routines-overview.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/drafts/assets/screenshots/web-settings-overview.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
docs/drafts/assets/screenshots/web-skills-list.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
602
docs/drafts/assets/secrets-encryption-flow.excalidraw
Normal file
@@ -0,0 +1,602 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [
|
||||
{
|
||||
"id": "jnqDkcXxlQypJIV2jZfPn",
|
||||
"type": "rectangle",
|
||||
"x": 587.19140625,
|
||||
"y": 148.8984375,
|
||||
"width": 147.359375,
|
||||
"height": 82.5078125,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "#a5d8ff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "Zz",
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 1705063761,
|
||||
"version": 215,
|
||||
"versionNonce": 2092967935,
|
||||
"isDeleted": false,
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow"
|
||||
}
|
||||
],
|
||||
"updated": 1772738456910,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "title",
|
||||
"type": "text",
|
||||
"x": 228.3470982142856,
|
||||
"y": 14.675781249999986,
|
||||
"width": 397.0200892857144,
|
||||
"height": 34.679687500000014,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e40af",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Secrets Encryption Flow",
|
||||
"fontSize": 27.74375000000001,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "top",
|
||||
"boundElements": [],
|
||||
"version": 130,
|
||||
"versionNonce": 64332031,
|
||||
"index": "a0",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772738634080,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Secrets Encryption Flow",
|
||||
"autoResize": false,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "subtitle",
|
||||
"type": "text",
|
||||
"x": 278.9765625,
|
||||
"y": 52.87109375,
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"angle": 0,
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Master Key Protected",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "top",
|
||||
"boundElements": [],
|
||||
"version": 132,
|
||||
"versionNonce": 261564703,
|
||||
"index": "a1",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772738634080,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Master Key Protected",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "secret_plain",
|
||||
"type": "rectangle",
|
||||
"x": 100,
|
||||
"y": 150,
|
||||
"width": 160,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#fed7aa",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_secret",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"versionNonce": 1134323601,
|
||||
"index": "a2",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_secret",
|
||||
"type": "text",
|
||||
"x": 110,
|
||||
"y": 170,
|
||||
"width": 140,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Secret\n(Plain)",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "secret_plain",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 543970815,
|
||||
"index": "a3",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "Secret\n(Plain)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "encrypted_store",
|
||||
"type": "rectangle",
|
||||
"x": 340,
|
||||
"y": 150,
|
||||
"width": 160,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#ddd6fe",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "arrow_key",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_encrypted",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"versionNonce": 732646769,
|
||||
"index": "a4",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_encrypted",
|
||||
"type": "text",
|
||||
"x": 350,
|
||||
"y": 170,
|
||||
"width": 140,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Encrypted\nStore",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "encrypted_store",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 831471135,
|
||||
"index": "a5",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "Encrypted\nStore",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "text_storage",
|
||||
"type": "text",
|
||||
"x": 601.8046875,
|
||||
"y": 169.43359375,
|
||||
"width": 120,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Storage\n(Database)",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"boundElements": [],
|
||||
"version": 137,
|
||||
"versionNonce": 197709919,
|
||||
"index": "a6",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772738451660,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Storage\n(Database)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "os_keychain",
|
||||
"type": "rectangle",
|
||||
"x": 340,
|
||||
"y": 320,
|
||||
"width": 160,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#a7f3d0",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow_key",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_keychain",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"versionNonce": 1247814207,
|
||||
"index": "a7",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_keychain",
|
||||
"type": "text",
|
||||
"x": 350,
|
||||
"y": 340,
|
||||
"width": 140,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "OS Keychain\n(AES-256)",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "os_keychain",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 618226993,
|
||||
"index": "a8",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "OS Keychain\n(AES-256)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow",
|
||||
"x": 260,
|
||||
"y": 190,
|
||||
"width": 80,
|
||||
"height": 0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#1e3a5f",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
80,
|
||||
0
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "secret_plain",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "encrypted_store",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"version": 2,
|
||||
"versionNonce": 1002406495,
|
||||
"index": "a9",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"boundElements": [],
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow",
|
||||
"x": 506,
|
||||
"y": 190.08648455983635,
|
||||
"width": 75.19140625,
|
||||
"height": 0.06863328552998382,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#1e3a5f",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
75.19140625,
|
||||
0.06863328552998382
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "encrypted_store",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "jnqDkcXxlQypJIV2jZfPn",
|
||||
"mode": "orbit",
|
||||
"fixedPoint": [
|
||||
0,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"version": 28,
|
||||
"versionNonce": 1391262687,
|
||||
"index": "aA",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"boundElements": [],
|
||||
"updated": 1772738456910,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "arrow_key",
|
||||
"type": "arrow",
|
||||
"x": 420,
|
||||
"y": 230,
|
||||
"width": 0,
|
||||
"height": 90,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#1e3a5f",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
90
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "encrypted_store",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "os_keychain",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"version": 2,
|
||||
"versionNonce": 1719524991,
|
||||
"index": "aB",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"boundElements": [],
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "label_master_key",
|
||||
"type": "text",
|
||||
"x": 440,
|
||||
"y": 265,
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"angle": 0,
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Master Key",
|
||||
"fontSize": 14,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "middle",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 800361713,
|
||||
"index": "aC",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772738368353,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Master Key",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.4285714285714286
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 20,
|
||||
"gridStep": 5,
|
||||
"gridModeEnabled": false,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"lockedMultiSelections": {}
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
BIN
docs/drafts/assets/secrets-encryption-flow.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
4
docs/drafts/assets/secrets-encryption-flow.svg
Normal file
|
After Width: | Height: | Size: 19 KiB |
1189
docs/drafts/assets/secrets-overview.excalidraw
Normal file
BIN
docs/drafts/assets/secrets-overview.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
4
docs/drafts/assets/secrets-overview.svg
Normal file
|
After Width: | Height: | Size: 25 KiB |
609
docs/drafts/assets/secrets-zero-exposure.excalidraw
Normal file
@@ -0,0 +1,609 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [
|
||||
{
|
||||
"id": "secret_store",
|
||||
"type": "rectangle",
|
||||
"x": -7.16796875,
|
||||
"y": 98.3046875,
|
||||
"width": 230,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#ddd6fe",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_store",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 60,
|
||||
"versionNonce": 1606640913,
|
||||
"index": "a0",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772739507103,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_store",
|
||||
"type": "text",
|
||||
"x": 7.83203125,
|
||||
"y": 118.3046875,
|
||||
"width": 200,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Secret Store\n(Encrypted in DB)",
|
||||
"fontSize": 14,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "secret_store",
|
||||
"boundElements": [],
|
||||
"version": 60,
|
||||
"versionNonce": 1964415729,
|
||||
"index": "a1",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772739507103,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "Secret Store\n(Encrypted in DB)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.4285714285714286
|
||||
},
|
||||
{
|
||||
"id": "proxy",
|
||||
"type": "rectangle",
|
||||
"x": 310,
|
||||
"y": 100,
|
||||
"width": 220,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#3b82f6",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_proxy",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"versionNonce": 743287551,
|
||||
"index": "a2",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772739486081,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_proxy",
|
||||
"type": "text",
|
||||
"x": 320,
|
||||
"y": 120,
|
||||
"width": 200,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#ffffff",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Proxy\n(Injects Headers)",
|
||||
"fontSize": 14,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "proxy",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 1224067185,
|
||||
"index": "a3",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772739486081,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "Proxy\n(Injects Headers)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.4285714285714286
|
||||
},
|
||||
{
|
||||
"id": "external_service",
|
||||
"type": "rectangle",
|
||||
"x": 623.67578125,
|
||||
"y": 99.609375,
|
||||
"width": 160,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#a7f3d0",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "text_external",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow"
|
||||
}
|
||||
],
|
||||
"version": 29,
|
||||
"versionNonce": 1501698385,
|
||||
"index": "a4",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772739526735,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_external",
|
||||
"type": "text",
|
||||
"x": 633.67578125,
|
||||
"y": 119.609375,
|
||||
"width": 140,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "External\nService",
|
||||
"fontSize": 14,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "external_service",
|
||||
"boundElements": [],
|
||||
"version": 28,
|
||||
"versionNonce": 1451931569,
|
||||
"index": "a5",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772739520302,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "External\nService",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.4285714285714286
|
||||
},
|
||||
{
|
||||
"id": "container_sandbox",
|
||||
"type": "rectangle",
|
||||
"x": 340,
|
||||
"y": 280,
|
||||
"width": 160,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#dbeafe",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow_up",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "text_container",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"versionNonce": 1404057407,
|
||||
"index": "a6",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"updated": 1772739486081,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "text_container",
|
||||
"type": "text",
|
||||
"x": 350,
|
||||
"y": 300,
|
||||
"width": 140,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#374151",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Container\n(Sandbox)",
|
||||
"fontSize": 14,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "container_sandbox",
|
||||
"boundElements": [],
|
||||
"version": 2,
|
||||
"versionNonce": 671162417,
|
||||
"index": "a7",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772739486081,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"originalText": "Container\n(Sandbox)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.4285714285714286
|
||||
},
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow",
|
||||
"x": 228.83203125,
|
||||
"y": 138.36262665794595,
|
||||
"width": 74.484375,
|
||||
"height": 0.6180295920540289,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#1e3a5f",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
74.484375,
|
||||
0.6180295920540289
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"elementId": "secret_store",
|
||||
"mode": "orbit",
|
||||
"fixedPoint": [
|
||||
1,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "label_decrypt",
|
||||
"mode": "orbit",
|
||||
"fixedPoint": [
|
||||
0.9353515625,
|
||||
1.6695406249999991
|
||||
]
|
||||
},
|
||||
"version": 213,
|
||||
"versionNonce": 1436581329,
|
||||
"index": "a8",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"boundElements": [],
|
||||
"updated": 1772739545111,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"moveMidPointsWithElement": false
|
||||
},
|
||||
{
|
||||
"id": "label_decrypt",
|
||||
"type": "text",
|
||||
"x": 228.48828125,
|
||||
"y": 105.58984375,
|
||||
"width": 80,
|
||||
"height": 20,
|
||||
"angle": 0,
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Decrypt",
|
||||
"fontSize": 12,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "arrow1",
|
||||
"type": "arrow"
|
||||
}
|
||||
],
|
||||
"version": 110,
|
||||
"versionNonce": 1747856863,
|
||||
"index": "a9",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772739517219,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Decrypt",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.6666666666666667
|
||||
},
|
||||
{
|
||||
"id": "arrow2",
|
||||
"type": "arrow",
|
||||
"x": 536,
|
||||
"y": 139.95678500196112,
|
||||
"width": 81.67578125,
|
||||
"height": 0.697170829423527,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#1e3a5f",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
81.67578125,
|
||||
-0.697170829423527
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"elementId": "proxy",
|
||||
"mode": "orbit",
|
||||
"fixedPoint": [
|
||||
1,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "external_service",
|
||||
"fixedPoint": [
|
||||
-0.004856054687500233,
|
||||
0.4950707031249998
|
||||
]
|
||||
},
|
||||
"version": 154,
|
||||
"versionNonce": 542584689,
|
||||
"index": "aA",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"boundElements": [],
|
||||
"updated": 1772739526410,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"moveMidPointsWithElement": false
|
||||
},
|
||||
{
|
||||
"id": "arrow_up",
|
||||
"type": "arrow",
|
||||
"x": 420,
|
||||
"y": 280,
|
||||
"width": 0,
|
||||
"height": 100,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e3a5f",
|
||||
"backgroundColor": "#1e3a5f",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
-100
|
||||
]
|
||||
],
|
||||
"startBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "container_sandbox",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"endBinding": {
|
||||
"mode": "orbit",
|
||||
"elementId": "proxy",
|
||||
"fixedPoint": [
|
||||
0.5001,
|
||||
0.5001
|
||||
]
|
||||
},
|
||||
"version": 2,
|
||||
"versionNonce": 845776881,
|
||||
"index": "aB",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"boundElements": [],
|
||||
"updated": 1772739486081,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "label_never",
|
||||
"type": "text",
|
||||
"x": 442.30078125,
|
||||
"y": 216.796875,
|
||||
"width": 140,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#dc2626",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Never passes\nthrough container",
|
||||
"fontSize": 12,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "middle",
|
||||
"boundElements": [],
|
||||
"version": 15,
|
||||
"versionNonce": 688681745,
|
||||
"index": "aC",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772739538806,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Never passes\nthrough container",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.6666666666666667
|
||||
},
|
||||
{
|
||||
"id": "label_auth",
|
||||
"type": "text",
|
||||
"x": 541.515625,
|
||||
"y": 89.2265625,
|
||||
"width": 82.03125,
|
||||
"height": 40,
|
||||
"angle": 0,
|
||||
"strokeColor": "#64748b",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"text": "Authorization:\n Bearer",
|
||||
"fontSize": 10,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "middle",
|
||||
"boundElements": [],
|
||||
"version": 114,
|
||||
"versionNonce": 908769439,
|
||||
"index": "aD",
|
||||
"isDeleted": false,
|
||||
"strokeStyle": "solid",
|
||||
"seed": 1,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"updated": 1772739533998,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"originalText": "Authorization:\n Bearer",
|
||||
"autoResize": true,
|
||||
"lineHeight": 2
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 20,
|
||||
"gridStep": 5,
|
||||
"gridModeEnabled": false,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"lockedMultiSelections": {}
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
BIN
docs/drafts/assets/secrets-zero-exposure.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
4
docs/drafts/assets/secrets-zero-exposure.svg
Normal file
|
After Width: | Height: | Size: 18 KiB |
1776
docs/drafts/assets/security-architecture.excalidraw
Normal file
BIN
docs/drafts/assets/security-architecture.png
Normal file
|
After Width: | Height: | Size: 229 KiB |
5
docs/drafts/assets/security-architecture.svg
Normal file
|
After Width: | Height: | Size: 48 KiB |
1651
docs/drafts/assets/security-data-flow.excalidraw
Normal file
BIN
docs/drafts/assets/security-data-flow.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
4
docs/drafts/assets/security-data-flow.svg
Normal file
|
After Width: | Height: | Size: 41 KiB |
243
docs/drafts/help/faq.mdx
Normal file
@@ -0,0 +1,243 @@
|
||||
---
|
||||
title: FAQ
|
||||
sidebarTitle: FAQ
|
||||
description: Frequently asked questions
|
||||
---
|
||||
|
||||
Common questions about IronClaw.
|
||||
|
||||
## General
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What is IronClaw?" icon="help-circle">
|
||||
IronClaw is a secure, self-hosted AI assistant that runs on your own hardware. It provides a personal AI with strong privacy guarantees through multi-layer security.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do I need Docker?" icon="container">
|
||||
**No for basic use, yes for job sandboxing.**
|
||||
|
||||
IronClaw runs as a standalone binary. Docker is only required if you want to use:
|
||||
- Sandboxed job execution (Claude Code mode)
|
||||
- WASM tool building with automatic compilation
|
||||
|
||||
Without Docker, IronClaw still works fully — just without the Docker sandbox layer.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I run without PostgreSQL?" icon="database">
|
||||
**Yes.** IronClaw includes libSQL (embedded SQLite) as an alternative.
|
||||
|
||||
libSQL requires no separate server and is recommended for personal use. PostgreSQL is recommended for production or multi-user deployments.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Is my data sent to NEAR AI?" icon="shield">
|
||||
Only prompts and responses go to your chosen LLM provider. Your:
|
||||
- Conversations are stored locally
|
||||
- Files remain on your machine
|
||||
- Secrets are encrypted locally
|
||||
- Settings are in your database
|
||||
|
||||
If using a cloud provider (NEAR AI, Anthropic, OpenAI), your prompts/responses are sent to them. Use Ollama or Tinfoil for maximum privacy.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How is IronClaw different from openclaw?" icon="git-compare">
|
||||
| Feature | IronClaw | openclaw |
|
||||
|---------|----------|----------|
|
||||
| Deployment | Local, self-hosted | Cloud service |
|
||||
| Data | Your hardware, your control | openclaw infrastructure |
|
||||
| Language | Rust | Varies |
|
||||
| Security | Multi-layer, local-first | Cloud security |
|
||||
|
||||
IronClaw is the self-hosted version for users who want complete control over their data.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Configuration
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="How do I switch LLM providers?" icon="refresh-cw">
|
||||
```bash
|
||||
# Re-run the wizard
|
||||
ironclaw onboard --skip-auth
|
||||
|
||||
# Or edit ~/.ironclaw/.env
|
||||
export LLM_BACKEND=anthropic
|
||||
export ANTHROPIC_API_KEY=sk-ant-...
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I add a second channel?" icon="plus">
|
||||
```bash
|
||||
ironclaw onboard --channels-only
|
||||
```
|
||||
|
||||
This runs only Step 6 of the wizard, letting you add channels without reconfiguring everything.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What's the difference between skills and tools?" icon="git-compare">
|
||||
| Skills | Tools |
|
||||
|--------|-------|
|
||||
| SKILL.md prompt extensions | Executable code |
|
||||
| Extends LLM behavior | Extends capabilities |
|
||||
| No code execution | Can run arbitrary code |
|
||||
| Trust-based activation | Capability-based security |
|
||||
|
||||
Skills guide the LLM's behavior. Tools give it new abilities.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Where is my data stored?" icon="database">
|
||||
By default: `~/.ironclaw/`
|
||||
|
||||
```
|
||||
~/.ironclaw/
|
||||
├── ironclaw.db # libSQL database
|
||||
├── .env # Bootstrap config
|
||||
├── channels/ # WASM channels
|
||||
├── tools/ # WASM tools
|
||||
└── skills/ # SKILL.md files
|
||||
```
|
||||
|
||||
Change with `IRONCLAW_BASE_DIR`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="How do I reset everything?" icon="trash">
|
||||
```bash
|
||||
# Stop IronClaw
|
||||
killall ironclaw
|
||||
|
||||
# Remove data
|
||||
rm -rf ~/.ironclaw
|
||||
|
||||
# Restart fresh
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
<Warning>This permanently deletes all your data.</Warning>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Why is the wizard asking for a browser?" icon="globe">
|
||||
NEAR AI OAuth requires a browser for authentication. It opens your default browser to complete login.
|
||||
|
||||
On VPS/servers without a browser, use:
|
||||
- NEAR AI Cloud API key (option 4 in auth menu)
|
||||
- Or set `IRONCLAW_OAUTH_CALLBACK_URL` to a public URL
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I run IronClaw as a service?" icon="server">
|
||||
```bash
|
||||
# Install service
|
||||
ironclaw service install
|
||||
|
||||
# Start
|
||||
sudo systemctl enable --now ironclaw # Linux
|
||||
brew services start ironclaw # macOS
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I use IronClaw behind a reverse proxy?" icon="network">
|
||||
**Yes.** Configure your reverse proxy to forward to the Web Gateway:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
```
|
||||
|
||||
See [VPS Install](/install/vps) for examples.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Security
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="How secure is IronClaw?" icon="shield">
|
||||
IronClaw uses defense in depth:
|
||||
|
||||
1. **Safety Layer** — Prompt injection defense
|
||||
2. **WASM Sandbox** — Sandboxed tool execution
|
||||
3. **Docker Sandbox** — Container isolation
|
||||
4. **Secrets Encryption** — AES-256-GCM
|
||||
5. **Zero-Exposure Model** — Credentials never in containers
|
||||
|
||||
See [Security Overview](/security) for details.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can IronClaw be hacked?" icon="alert-triangle">
|
||||
No software is unhackable. IronClaw reduces risk through:
|
||||
- Multiple isolation layers
|
||||
- Minimal attack surface
|
||||
- Security-first design
|
||||
- Regular updates
|
||||
|
||||
Follow security best practices and keep updated.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Is my data encrypted?" icon="lock">
|
||||
- **Secrets**: AES-256-GCM encrypted
|
||||
- **Database**: Unencrypted by default (use full-disk encryption)
|
||||
- **Network**: TLS to providers
|
||||
|
||||
See [Secrets](/security/secrets) and [Database](/setup/database) for details.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if a tool is malicious?" icon="bug">
|
||||
Tools run in:
|
||||
- WASM sandbox (memory limits, fuel metering)
|
||||
- Docker sandbox (container isolation)
|
||||
- Network proxy (controlled access)
|
||||
|
||||
Even if a tool is compromised, it's contained.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Advanced
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can I run IronClaw on a Raspberry Pi?" icon="cpu">
|
||||
**Yes**, with caveats:
|
||||
- Use libSQL (not PostgreSQL)
|
||||
- Use Ollama with small models (3B parameters)
|
||||
- Expect slower performance
|
||||
- Use SD card for storage
|
||||
|
||||
```bash
|
||||
# Install for ARM
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I use my own LLM?" icon="brain">
|
||||
**Yes.** Use the OpenAI-compatible backend:
|
||||
|
||||
```bash
|
||||
export LLM_BACKEND=openai_compatible
|
||||
export LLM_BASE_URL=http://your-llm:8000/v1
|
||||
export LLM_API_KEY=your-key
|
||||
```
|
||||
|
||||
Or use Ollama for local models.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I contribute?" icon="github">
|
||||
Yes! IronClaw is open source:
|
||||
|
||||
- GitHub: https://github.com/ironclaw-ai/ironclaw
|
||||
- Issues: Report bugs and feature requests
|
||||
- PRs: Welcome with tests
|
||||
|
||||
See CONTRIBUTING.md in the repository.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Still Have Questions?
|
||||
|
||||
- [Troubleshooting](/help/troubleshooting) — Common issues
|
||||
- [CLI Reference](/reference/cli) — Command reference
|
||||
- [Configuration](/setup/configuration) — Environment variables
|
||||
- [GitHub Issues](https://github.com/ironclaw-ai/ironclaw/issues) — Bug reports
|
||||
317
docs/drafts/help/troubleshooting.mdx
Normal file
@@ -0,0 +1,317 @@
|
||||
---
|
||||
title: Troubleshooting
|
||||
sidebarTitle: Troubleshooting
|
||||
description: Common issues and solutions
|
||||
---
|
||||
|
||||
Solutions for common IronClaw issues.
|
||||
|
||||
## Diagnostic Tool
|
||||
|
||||
Run diagnostics first:
|
||||
|
||||
```bash
|
||||
ironclaw doctor
|
||||
```
|
||||
|
||||
This checks:
|
||||
- Database connectivity
|
||||
- LLM provider access
|
||||
- Docker availability
|
||||
- Tunnel configuration
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Installation
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Binary not found after install" icon="x-circle">
|
||||
**Cause:** PATH not updated
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Add to PATH
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
|
||||
# Or restart your terminal
|
||||
exec $SHELL
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Permission denied" icon="lock">
|
||||
**Solution:**
|
||||
```bash
|
||||
# Fix ownership
|
||||
sudo chown -R $USER:$USER ~/.local/bin/ironclaw
|
||||
|
||||
# Or move to system path
|
||||
sudo mv ~/.local/bin/ironclaw /usr/local/bin/
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Database
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Database connection failed" icon="database">
|
||||
**PostgreSQL:**
|
||||
```bash
|
||||
# Check PostgreSQL is running
|
||||
sudo systemctl status postgresql
|
||||
|
||||
# Verify connection
|
||||
psql postgres://user:pass@localhost/ironclaw
|
||||
```
|
||||
|
||||
**libSQL:**
|
||||
```bash
|
||||
# Check permissions
|
||||
ls -la ~/.ironclaw/
|
||||
|
||||
# Fix ownership
|
||||
chmod 755 ~/.ironclaw
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="pgvector extension not found" icon="puzzle">
|
||||
**Solution:**
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt install postgresql-15-pgvector
|
||||
|
||||
# Enable extension
|
||||
sudo -u postgres psql -d ironclaw -c "CREATE EXTENSION IF NOT EXISTS vector;"
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database file is locked (libSQL)" icon="lock">
|
||||
**Solution:**
|
||||
```bash
|
||||
# Find and kill process
|
||||
lsof ~/.ironclaw/ironclaw.db
|
||||
kill -9 <pid>
|
||||
|
||||
# Or wait for process to exit
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### LLM Provider
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Browser didn't open for OAuth" icon="globe">
|
||||
**Solutions:**
|
||||
1. Check default browser is set
|
||||
2. Manually visit the URL shown in terminal
|
||||
3. On VPS: use API key mode instead
|
||||
|
||||
```bash
|
||||
# Set callback URL for remote servers
|
||||
export IRONCLAW_OAUTH_CALLBACK_URL=https://your-server:9876
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="API key rejected" icon="key">
|
||||
**Solutions:**
|
||||
1. Verify key is copied correctly (no extra spaces)
|
||||
2. Check key hasn't expired
|
||||
3. Ensure billing is set up (OpenAI/Anthropic)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Session expired" icon="clock">
|
||||
**Solution:**
|
||||
```bash
|
||||
# Re-authenticate
|
||||
ironclaw onboard --skip-auth
|
||||
# Select NEAR AI → re-authenticate
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rate limit exceeded" icon="alert">
|
||||
**Solutions:**
|
||||
1. Wait and retry
|
||||
2. Implement exponential backoff
|
||||
3. Check your provider's rate limits
|
||||
4. Consider upgrading tier
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Model not found" icon="search">
|
||||
**Solutions:**
|
||||
1. Verify model name spelling
|
||||
2. Check model availability for your account
|
||||
3. Try a different model
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Channels
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Web Gateway not accessible" icon="globe">
|
||||
**Solutions:**
|
||||
```bash
|
||||
# Check IronClaw is running
|
||||
ironclaw status
|
||||
|
||||
# Verify port
|
||||
sudo ss -tlnp | grep 3000
|
||||
|
||||
# Check firewall
|
||||
sudo ufw status
|
||||
sudo ufw allow 3000
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Web Gateway token not found" icon="key">
|
||||
**Solution:**
|
||||
```bash
|
||||
# View logs
|
||||
RUST_LOG=ironclaw=info ironclaw run 2>&1 | grep "Gateway auth token"
|
||||
|
||||
# Or set persistent token
|
||||
export GATEWAY_AUTH_TOKEN=your-token
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Telegram bot not responding" icon="message-circle">
|
||||
**Solutions:**
|
||||
1. Check bot token is valid (test with @BotFather)
|
||||
2. Verify polling mode or webhook URL
|
||||
3. Check logs for errors
|
||||
4. Ensure owner is paired (if using pairing mode)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Webhook not receiving messages" icon="webhook">
|
||||
**Solutions:**
|
||||
1. Verify HTTPS URL is set (required by Telegram)
|
||||
2. Check tunnel is running (ngrok, cloudflared)
|
||||
3. Ensure webhook secret matches
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Owner binding timeout" icon="clock">
|
||||
**Solutions:**
|
||||
1. Send `/start` to your bot in Telegram
|
||||
2. Re-run `ironclaw onboard --channels-only`
|
||||
3. Wait 120 seconds for first message
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Sandbox
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Docker not available" icon="x-circle">
|
||||
**Solution:**
|
||||
```bash
|
||||
# Install Docker
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
|
||||
# Add user to docker group
|
||||
sudo usermod -aG docker $USER
|
||||
|
||||
# Log out and back in
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Sandbox timeout" icon="clock">
|
||||
**Solutions:**
|
||||
1. Increase timeout:
|
||||
```bash
|
||||
export SANDBOX_TIMEOUT_SECS=300
|
||||
```
|
||||
2. Check for infinite loops in job
|
||||
3. Verify job logic
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Out of memory" icon="alert">
|
||||
**Solutions:**
|
||||
1. Increase memory limit:
|
||||
```bash
|
||||
export SANDBOX_MEMORY_LIMIT_MB=4096
|
||||
```
|
||||
2. Optimize job memory usage
|
||||
3. Use smaller models
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Security
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Keychain prompts repeatedly" icon="refresh-cw">
|
||||
**Solutions:**
|
||||
- On macOS, click "Always Allow" in keychain dialog
|
||||
- This is expected OS behavior
|
||||
- Caching minimizes prompts
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Keychain not available on Linux" icon="linux">
|
||||
**Solution:**
|
||||
```bash
|
||||
# Install gnome-keyring
|
||||
sudo apt install gnome-keyring
|
||||
|
||||
# Or use environment variable mode
|
||||
export SECRETS_MASTER_KEY="your-key"
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Platform-Specific
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="macOS Keychain dialogs" icon="apple">
|
||||
**Expected:** Two dialogs on first access:
|
||||
1. "Enter your password to unlock the keychain"
|
||||
2. "Allow ironclaw to access this keychain item"
|
||||
|
||||
**Solution:** Click "Always Allow" to prevent repeated prompts.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="WSL2 port not accessible" icon="windows">
|
||||
**Solutions:**
|
||||
- WSL2 automatically forwards ports
|
||||
- Use `http://localhost:3000` from Windows
|
||||
- Check WSL2 is running: `wsl --status`
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Debug Logging
|
||||
|
||||
Enable verbose logging:
|
||||
|
||||
```bash
|
||||
# All modules
|
||||
RUST_LOG=debug ironclaw run
|
||||
|
||||
# Just IronClaw
|
||||
RUST_LOG=ironclaw=debug ironclaw run
|
||||
|
||||
# Specific module
|
||||
RUST_LOG=ironclaw::agent=debug ironclaw run
|
||||
|
||||
# With HTTP requests
|
||||
RUST_LOG=ironclaw=debug,tower_http=debug ironclaw run
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
If your issue isn't listed:
|
||||
|
||||
1. Run `ironclaw doctor --json`
|
||||
2. Check logs with `RUST_LOG=debug`
|
||||
3. Search [GitHub Issues](https://github.com/ironclaw-ai/ironclaw/issues)
|
||||
4. Create a new issue with:
|
||||
- IronClaw version
|
||||
- Operating system
|
||||
- Full error message
|
||||
- Steps to reproduce
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="FAQ" icon="help-circle" href="/help/faq">
|
||||
Frequently asked questions
|
||||
</Card>
|
||||
|
||||
<Card title="CLI Reference" icon="terminal" href="/reference/cli">
|
||||
Command-line reference
|
||||
</Card>
|
||||
</CardGroup>
|
||||
249
docs/drafts/install/docker.mdx
Normal file
@@ -0,0 +1,249 @@
|
||||
---
|
||||
title: Docker Installation
|
||||
sidebarTitle: Docker
|
||||
description: Run IronClaw in a Docker container
|
||||
---
|
||||
|
||||
Run IronClaw inside a Docker container. This provides isolation and consistency across environments.
|
||||
|
||||
<Warning>
|
||||
**Important distinction:** IronClaw runs **alongside** Docker (for job sandboxing), not inside Docker by default. This page covers running IronClaw itself in a container — a different use case from the default installation.
|
||||
</Warning>
|
||||
|
||||
## When to Use Docker
|
||||
|
||||
- **Testing**: Quick experiments without modifying your system
|
||||
- **Consistent environments**: Same configuration across dev/staging/prod
|
||||
- **Multi-tenant**: Run multiple IronClaw instances on one host
|
||||
- **CI/CD**: Automated deployments
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Create data directory
|
||||
mkdir -p ~/.ironclaw
|
||||
|
||||
# Run IronClaw
|
||||
docker run -d \
|
||||
--name ironclaw \
|
||||
-v ~/.ironclaw:/home/ironclaw/.ironclaw \
|
||||
# enabling this mount will allow the container to control the docker daemon, use at own risk
|
||||
# -v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-p 3000:3000 \
|
||||
-p 8080:8080 \
|
||||
# Pin to a specific IronClaw version for reproducible, rollback-friendly deployments.
|
||||
nearai/ironclaw:latest
|
||||
```
|
||||
|
||||
## Docker Compose
|
||||
|
||||
Save as `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
ironclaw:
|
||||
# Pin to a specific IronClaw version for reproducible, rollback-friendly deployments.
|
||||
image: nearai/ironclaw:latest
|
||||
container_name: ironclaw
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
# Persistent data
|
||||
- ~/.ironclaw:/home/ironclaw/.ironclaw
|
||||
|
||||
# Docker socket for sandbox jobs, also exposes your docker host to this container!
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
ports:
|
||||
# Web Gateway
|
||||
- "3000:3000"
|
||||
# HTTP Webhook
|
||||
- "8080:8080"
|
||||
|
||||
environment:
|
||||
# Required: database backend
|
||||
- DATABASE_BACKEND=libsql
|
||||
|
||||
# Optional: LLM backend
|
||||
- LLM_BACKEND=nearai
|
||||
- NEARAI_SESSION_TOKEN=${NEARAI_SESSION_TOKEN}
|
||||
|
||||
# Optional: Web Gateway
|
||||
- GATEWAY_ENABLED=true
|
||||
- GATEWAY_HOST=0.0.0.0
|
||||
- GATEWAY_PORT=3000
|
||||
|
||||
# Optional: HTTP Webhook
|
||||
- HTTP_ENABLED=true
|
||||
- HTTP_HOST=0.0.0.0
|
||||
- HTTP_PORT=8080
|
||||
|
||||
# Optional: PostgreSQL instead of libSQL
|
||||
# postgres:
|
||||
# image: pgvector/pgvector:pg15
|
||||
# environment:
|
||||
# POSTGRES_USER: ironclaw
|
||||
# POSTGRES_PASSWORD: changeme
|
||||
# POSTGRES_DB: ironclaw
|
||||
# volumes:
|
||||
# - postgres_data:/var/lib/postgresql/data
|
||||
#
|
||||
#volumes:
|
||||
# postgres_data:
|
||||
```
|
||||
|
||||
Start:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Volume Mounts
|
||||
|
||||
| Host Path | Container Path | Purpose |
|
||||
|-----------|---------------|---------|
|
||||
| `~/.ironclaw` | `/home/ironclaw/.ironclaw` | Config, database, logs |
|
||||
| `/var/run/docker.sock` | `/var/run/docker.sock` | Launch sandbox containers |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Pass configuration via environment variables:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name ironclaw \
|
||||
-e DATABASE_BACKEND=libsql \
|
||||
-e LLM_BACKEND=nearai \
|
||||
-e NEARAI_SESSION_TOKEN=sess_xxx \
|
||||
-e GATEWAY_ENABLED=true \
|
||||
-v ~/.ironclaw:/home/ironclaw/.ironclaw \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-p 3000:3000 \
|
||||
nearai/ironclaw:latest
|
||||
```
|
||||
|
||||
See [Configuration Reference](/setup/configuration) for all options.
|
||||
|
||||
## Docker-in-Docker Considerations
|
||||
|
||||
IronClaw can launch sandbox containers. In Docker, this requires:
|
||||
|
||||
1. **Docker socket mount** (shown above): Allows IronClaw to launch sibling containers
|
||||
2. **Privileged mode** (optional): Only if sandbox jobs need elevated permissions
|
||||
|
||||
```bash
|
||||
# With privileged mode (not recommended unless needed)
|
||||
docker run -d \
|
||||
--name ironclaw \
|
||||
--privileged \
|
||||
-v ~/.ironclaw:/home/ironclaw/.ironclaw \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
...
|
||||
```
|
||||
|
||||
## Custom Data Directory
|
||||
|
||||
To use a different data location:
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/ironclaw/data
|
||||
|
||||
docker run -d \
|
||||
--name ironclaw \
|
||||
-e IRONCLAW_BASE_DIR=/data \
|
||||
-v /opt/ironclaw/data:/data \
|
||||
...
|
||||
```
|
||||
|
||||
## Reverse Proxy (HTTPS)
|
||||
|
||||
For external access, put a reverse proxy in front:
|
||||
|
||||
### Caddy
|
||||
|
||||
```caddy
|
||||
# Caddyfile
|
||||
webg.example.com {
|
||||
reverse_proxy localhost:3000
|
||||
}
|
||||
```
|
||||
|
||||
### nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name webg.example.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Updating
|
||||
|
||||
```bash
|
||||
# Pull latest image
|
||||
docker pull nearai/ironclaw:latest
|
||||
|
||||
# Recreate container
|
||||
docker stop ironclaw
|
||||
docker rm ironclaw
|
||||
docker run -d \
|
||||
--name ironclaw \
|
||||
-v ~/.ironclaw:/home/ironclaw/.ironclaw \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-p 3000:3000 \
|
||||
nearai/ironclaw:latest
|
||||
|
||||
# Or with docker compose
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Cannot connect to Docker daemon" icon="x-circle">
|
||||
Ensure Docker socket is mounted:
|
||||
```bash
|
||||
docker run ... -v /var/run/docker.sock:/var/run/docker.sock ...
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Permission denied on data directory" icon="lock">
|
||||
Fix ownership:
|
||||
```bash
|
||||
sudo chown -R 1000:1000 ~/.ironclaw
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Port already in use" icon="network">
|
||||
Change port mapping:
|
||||
```bash
|
||||
-p 3002:3000 # Maps host port 3002 to container port 3000
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Full environment variable reference
|
||||
</Card>
|
||||
<Card title="VPS Install" icon="server" href="/install/vps">
|
||||
Production deployment guide
|
||||
</Card>
|
||||
</CardGroup>
|
||||
148
docs/drafts/install/index.mdx
Normal file
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: Installation
|
||||
sidebarTitle: Overview
|
||||
description: Choose how to install IronClaw
|
||||
---
|
||||
|
||||
IronClaw can run in multiple environments — from your local machine to a cloud VPS. Choose the installation method that fits your needs.
|
||||
|
||||
## Installation Methods
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Local Install" icon="monitor" href="/install/local">
|
||||
Run IronClaw directly on your machine. Best for personal use.
|
||||
|
||||
- **Linux**: Shell script or package manager
|
||||
- **macOS**: Homebrew or shell script
|
||||
- **Windows**: Native or WSL2 (recommended)
|
||||
</Card>
|
||||
|
||||
<Card title="Docker" icon="container" href="/install/docker">
|
||||
Run IronClaw in a container. Good for consistent environments.
|
||||
|
||||
- Docker Compose available
|
||||
- Volume persistence
|
||||
- Docker-in-Docker support
|
||||
</Card>
|
||||
|
||||
<Card title="VPS / Cloud" icon="cloud" href="/install/vps">
|
||||
Deploy to a remote server. Best for always-on operation.
|
||||
|
||||
- Ubuntu/Debian recommended
|
||||
- PostgreSQL + pgvector
|
||||
- Reverse proxy for HTTPS
|
||||
</Card>
|
||||
|
||||
<Card title="NEAR AI Cloud" icon="zap" href="/install/nearai-cloud">
|
||||
Managed hosting by NEAR AI. Zero maintenance.
|
||||
|
||||
- Pre-configured environment
|
||||
- Session token injection
|
||||
- Web UI access
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Quick Decision Guide
|
||||
|
||||
| Scenario | Recommended Method | Database |
|
||||
|----------|-------------------|----------|
|
||||
| Personal laptop | [Local install](/install/local) | libSQL |
|
||||
| Always-on server | [VPS](/install/vps) | PostgreSQL |
|
||||
| Testing / experimenting | [Docker](/install/docker) | libSQL |
|
||||
| Zero maintenance | [NEAR AI Cloud](/install/nearai-cloud) | Managed |
|
||||
| Team deployment | [VPS](/install/vps) | PostgreSQL |
|
||||
|
||||
## System Requirements
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Minimum Requirements" icon="gauge">
|
||||
- **OS**: Linux (glibc 2.31+), macOS 14+, Windows 10+ or WSL2
|
||||
- **RAM**: 512 MB (1 GB recommended with LLM)
|
||||
- **Disk**: 100 MB for binary + data
|
||||
- **Network**: Internet access for LLM provider
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Recommended for Production" icon="server">
|
||||
- **OS**: Ubuntu 22.04 LTS, Debian 12, or macOS 14+
|
||||
- **RAM**: 2 GB
|
||||
- **Disk**: 1 GB+ for logs and data
|
||||
- **Database**: PostgreSQL 15+ with pgvector
|
||||
- **Docker**: 24.x+ (for sandbox jobs)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Optional: Docker" icon="box">
|
||||
Docker is **optional** for running IronClaw itself, but required if you want to use:
|
||||
- Sandboxed job execution (Docker isolation)
|
||||
- Claude Code mode
|
||||
- Automatic WASM tool compilation
|
||||
|
||||
Install Docker:
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
|
||||
# macOS
|
||||
brew install docker
|
||||
|
||||
# Or download from https://docker.com
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Default: libSQL for Local Installs
|
||||
|
||||
For local installations, we recommend **libSQL** (embedded SQLite) as your database backend:
|
||||
|
||||
- **Zero-dependency**: No separate database server to install
|
||||
- **Auto-created**: Database created automatically at `~/.ironclaw/ironclaw.db`
|
||||
- **Zero-config**: Works out of the box
|
||||
- **Turso option**: Can sync to Turso cloud for backup
|
||||
|
||||
```bash
|
||||
# During wizard Step 1, select "libSQL"
|
||||
# Path: ~/.ironclaw/ironclaw.db (default)
|
||||
```
|
||||
|
||||
**When to use PostgreSQL instead:**
|
||||
- Multi-user deployments
|
||||
- High-throughput scenarios
|
||||
- Existing PostgreSQL infrastructure
|
||||
- Need for advanced querying
|
||||
|
||||
See [Database Backends](/setup/database) for full comparison.
|
||||
|
||||
## Installation Checklist
|
||||
|
||||
Before installing:
|
||||
|
||||
- [ ] Choose your installation method
|
||||
- [ ] Decide on database backend (libSQL vs PostgreSQL)
|
||||
- [ ] Have your LLM provider credentials ready (API key or OAuth)
|
||||
- [ ] Decide which channels you want to enable
|
||||
|
||||
After installation:
|
||||
|
||||
- [ ] Run the onboarding wizard: `ironclaw onboard`
|
||||
- [ ] Start IronClaw: `ironclaw run`
|
||||
- [ ] Verify Web Gateway loads (if enabled)
|
||||
- [ ] Test sending a message
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **Installation issues**: See [Troubleshooting](/help/troubleshooting)
|
||||
- **Configuration help**: See [Configuration Reference](/setup/configuration)
|
||||
- **Wizard questions**: See [Wizard Walkthrough](/start/wizard)
|
||||
|
||||
## What's Next?
|
||||
|
||||
<Steps>
|
||||
<Step title="Install IronClaw" icon="download">
|
||||
Follow the installation guide for your chosen method
|
||||
</Step>
|
||||
<Step title="Run the Wizard" icon="settings">
|
||||
Configure your database, LLM, and channels
|
||||
</Step>
|
||||
<Step title="Start Using IronClaw" icon="message-square">
|
||||
Open the Web Gateway or chat via Telegram
|
||||
</Step>
|
||||
</Steps>
|
||||
305
docs/drafts/install/local.mdx
Normal file
@@ -0,0 +1,305 @@
|
||||
---
|
||||
title: Local Installation
|
||||
sidebarTitle: Local
|
||||
description: Install IronClaw on Linux, macOS, or Windows
|
||||
---
|
||||
|
||||
Install IronClaw directly on your local machine for personal use. This is the simplest and most common installation method.
|
||||
|
||||
## Installation by OS
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux">
|
||||
### Quick Install (Shell Script)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
|
||||
This installs to `~/.local/bin/ironclaw`. Add to your PATH if needed:
|
||||
|
||||
```bash
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||||
```
|
||||
|
||||
### Package Manager (Ubuntu/Debian)
|
||||
|
||||
```bash
|
||||
# Add repository
|
||||
curl -fsSL https://repo.ironclaw.ai/gpg | sudo gpg --dearmor -o /usr/share/keyrings/ironclaw.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/ironclaw.gpg] https://repo.ironclaw.ai stable main" | sudo tee /etc/apt/sources.list.d/ironclaw.list
|
||||
|
||||
# Install
|
||||
sudo apt update
|
||||
sudo apt install ironclaw
|
||||
```
|
||||
|
||||
### Build from Source
|
||||
|
||||
```bash
|
||||
# Prerequisites
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
|
||||
|
||||
# Build
|
||||
git clone https://github.com/nearai/ironclaw.git
|
||||
cd ironclaw
|
||||
cargo build --release
|
||||
|
||||
# Install
|
||||
sudo cp target/release/ironclaw /usr/local/bin/
|
||||
```
|
||||
|
||||
### Post-Install
|
||||
|
||||
1. **Run the wizard**:
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
2. **Select libSQL** (recommended) during Step 1
|
||||
|
||||
3. **Service setup** (optional):
|
||||
```bash
|
||||
ironclaw service install
|
||||
sudo systemctl enable --now ironclaw
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="macOS">
|
||||
### Homebrew (Recommended)
|
||||
|
||||
```bash
|
||||
# Add tap
|
||||
brew tap ironclaw-ai/tap
|
||||
|
||||
# Install
|
||||
brew install ironclaw
|
||||
```
|
||||
|
||||
### Shell Script
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
|
||||
Installs to `~/.local/bin/ironclaw`.
|
||||
|
||||
### Post-Install
|
||||
|
||||
1. **Run the wizard**:
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
2. **macOS Keychain** (Step 2): Two system dialogs are expected:
|
||||
- "Enter your password to unlock the keychain"
|
||||
- "Allow ironclaw to access this keychain item"
|
||||
|
||||
Click "Always Allow" to minimize future prompts.
|
||||
|
||||
3. **Service setup** (optional):
|
||||
```bash
|
||||
ironclaw service install
|
||||
brew services start ironclaw
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows (WSL2)">
|
||||
### Prerequisites
|
||||
|
||||
1. Install WSL2:
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
|
||||
2. Restart, then open Ubuntu in WSL2
|
||||
|
||||
### Install in WSL2
|
||||
|
||||
```bash
|
||||
# Inside WSL2
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
|
||||
### Access from Windows
|
||||
|
||||
The Web Gateway binds to localhost by default:
|
||||
|
||||
- In WSL2: `http://127.0.0.1:3000`
|
||||
- From Windows: `http://localhost:3000` (WSL2 forwards ports automatically)
|
||||
|
||||
### Post-Install
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
ironclaw run
|
||||
```
|
||||
|
||||
<Note>
|
||||
WSL2 is the recommended way to run IronClaw on Windows. Native Windows support is available but less tested.
|
||||
</Note>
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows (Native)">
|
||||
### PowerShell Install
|
||||
|
||||
```powershell
|
||||
irm https://install.ironclaw.ai | iex
|
||||
```
|
||||
|
||||
Or manually:
|
||||
1. Download `ironclaw-x86_64-pc-windows-msvc.zip` from [releases](https://github.com/nearai/ironclaw/releases)
|
||||
2. Extract to `C:\Program Files\IronClaw\`
|
||||
3. Add to PATH
|
||||
|
||||
### Manual PATH Setup
|
||||
|
||||
```powershell
|
||||
# Add to system PATH
|
||||
[Environment]::SetEnvironmentVariable(
|
||||
"Path",
|
||||
$env:Path + ";C:\Program Files\IronClaw",
|
||||
"User"
|
||||
)
|
||||
```
|
||||
|
||||
### Post-Install
|
||||
|
||||
```powershell
|
||||
ironclaw onboard
|
||||
ironclaw run
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Native Windows support is experimental. For production use, prefer WSL2 or Linux.
|
||||
</Warning>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Database Recommendation
|
||||
|
||||
For local installs, use **libSQL** (embedded SQLite):
|
||||
|
||||
```
|
||||
Wizard Step 1: Database Connection
|
||||
→ Select "libSQL"
|
||||
→ Path: ~/.ironclaw/ironclaw.db (default)
|
||||
```
|
||||
|
||||
Benefits of libSQL for local use:
|
||||
- No separate database server to install
|
||||
- Zero configuration
|
||||
- Automatic migrations
|
||||
- Optional Turso cloud sync
|
||||
|
||||
PostgreSQL is available but adds complexity for single-user deployments.
|
||||
|
||||
## Keychain Behavior
|
||||
|
||||
Your OS manages the encryption master key:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="macOS" icon="apple">
|
||||
Uses macOS Keychain. Two dialogs expected:
|
||||
1. "Enter your password to unlock the keychain"
|
||||
2. "Allow ironclaw to access this keychain item"
|
||||
|
||||
Click "Always Allow" on the second dialog to prevent repeated prompts.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Linux" icon="linux">
|
||||
Requires `gnome-keyring` or `kwallet`:
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt install gnome-keyring
|
||||
|
||||
# Fedora
|
||||
sudo dnf install gnome-keyring
|
||||
```
|
||||
|
||||
If unavailable, use environment variable mode in Step 2.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Windows" icon="windows">
|
||||
Uses Windows Data Protection API (DPAPI). No additional setup required.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Service Installation
|
||||
|
||||
Run IronClaw as a background service:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux (systemd)">
|
||||
```bash
|
||||
# Install service
|
||||
ironclaw service install
|
||||
|
||||
# Enable and start
|
||||
sudo systemctl enable --now ironclaw
|
||||
|
||||
# Check status
|
||||
sudo systemctl status ironclaw
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u ironclaw -f
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="macOS (launchd)">
|
||||
```bash
|
||||
# Install service
|
||||
ironclaw service install
|
||||
|
||||
# Start with Homebrew
|
||||
brew services start ironclaw
|
||||
|
||||
# Or manually
|
||||
launchctl load ~/Library/LaunchAgents/ai.ironclaw.service.plist
|
||||
|
||||
# Check status
|
||||
launchctl list | grep ironclaw
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows">
|
||||
Windows service support is coming. For now, use:
|
||||
- Task Scheduler
|
||||
- NSSM (Non-Sucking Service Manager)
|
||||
- Or run in WSL2 with systemd
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Verify Installation
|
||||
|
||||
```bash
|
||||
# Check version
|
||||
ironclaw --version
|
||||
|
||||
# Run diagnostics
|
||||
ironclaw doctor
|
||||
|
||||
# Expected output:
|
||||
# ✓ Binary: ironclaw v0.13.0
|
||||
# ✓ Config directory: /home/user/.ironclaw
|
||||
# ✓ Database: libSQL (not yet initialized)
|
||||
# ✓ Docker: available (optional)
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Run the wizard**: `ironclaw onboard`
|
||||
2. **Start IronClaw**: `ironclaw run`
|
||||
3. **Open Web Gateway**: `http://127.0.0.1:3000`
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Wizard Walkthrough" icon="settings" href="/start/wizard">
|
||||
Detailed guide to the 8-step setup wizard
|
||||
</Card>
|
||||
<Card title="Troubleshooting" icon="tool" href="/help/troubleshooting">
|
||||
Common issues and solutions
|
||||
</Card>
|
||||
</CardGroup>
|
||||
81
docs/drafts/install/nearai-cloud.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: NEAR AI Cloud
|
||||
sidebarTitle: NEAR AI Cloud
|
||||
description: Use NEAR AI managed hosting for IronClaw
|
||||
---
|
||||
|
||||
NEAR AI Cloud offers managed hosting for IronClaw — zero setup required.
|
||||
|
||||
## What is NEAR AI Cloud?
|
||||
|
||||
NEAR AI Cloud runs IronClaw for you on their infrastructure:
|
||||
|
||||
- **Pre-configured**: Database, LLM, and channels ready to go
|
||||
- **Session token auth**: Uses your NEAR AI account
|
||||
- **Web UI access**: Browser-based chat interface
|
||||
- **Zero maintenance**: Updates and monitoring handled by NEAR AI
|
||||
|
||||
## Getting Access
|
||||
|
||||
1. Sign up at https://cloud.near.ai
|
||||
2. Request IronClaw access from your NEAR AI dashboard
|
||||
3. Receive your IronClaw instance URL and credentials
|
||||
|
||||
## Authentication
|
||||
|
||||
NEAR AI Cloud uses session token authentication. Your token is automatically injected into the IronClaw environment.
|
||||
|
||||
### For hosting providers
|
||||
|
||||
If you're deploying IronClaw on NEAR AI infrastructure:
|
||||
|
||||
```bash
|
||||
# Session token is injected via environment
|
||||
export NEARAI_SESSION_TOKEN="sess_xxxxx"
|
||||
```
|
||||
|
||||
This takes precedence over file-based tokens.
|
||||
|
||||
## Using Your Instance
|
||||
|
||||
Once provisioned:
|
||||
|
||||
1. **Access the Web UI**: Open your assigned URL in a browser
|
||||
2. **Authenticate**: Log in with your NEAR AI credentials
|
||||
3. **Start chatting**: The agent is pre-configured and ready
|
||||
|
||||
## Configuration
|
||||
|
||||
While most settings are managed by NEAR AI, you can customize:
|
||||
|
||||
- **Channels**: Enable Telegram, webhooks, etc.
|
||||
- **Tools**: Install extensions from the registry
|
||||
- **Skills**: Load custom SKILL.md files
|
||||
|
||||
Access configuration through the Web UI Settings tab.
|
||||
|
||||
## Limitations
|
||||
|
||||
- Cannot modify core LLM backend (fixed to NEAR AI)
|
||||
- Cannot access underlying database directly
|
||||
- File system access limited to workspace
|
||||
- No shell access to the host
|
||||
|
||||
## Switching to Self-Hosted
|
||||
|
||||
If you outgrow managed hosting:
|
||||
|
||||
1. Export your data via the Web UI
|
||||
2. Follow the [VPS installation guide](/install/vps)
|
||||
3. Import your data to the new instance
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Local Install" icon="monitor" href="/install/local">
|
||||
Run IronClaw on your own machine
|
||||
</Card>
|
||||
<Card title="VPS Install" icon="server" href="/install/vps">
|
||||
Self-hosted deployment guide
|
||||
</Card>
|
||||
</CardGroup>
|
||||
123
docs/drafts/install/uninstalling.mdx
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: Uninstalling IronClaw
|
||||
sidebarTitle: Uninstalling
|
||||
description: Completely remove IronClaw from your system
|
||||
---
|
||||
|
||||
How to completely remove IronClaw from your system.
|
||||
|
||||
## Stop IronClaw
|
||||
|
||||
```bash
|
||||
# If running in terminal
|
||||
Ctrl+C
|
||||
|
||||
# If running as a service
|
||||
sudo systemctl stop ironclaw # systemd
|
||||
brew services stop ironclaw # Homebrew
|
||||
```
|
||||
|
||||
## Remove Binary
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux/macOS">
|
||||
```bash
|
||||
# Remove binary
|
||||
rm ~/.local/bin/ironclaw
|
||||
|
||||
# Or if installed system-wide
|
||||
sudo rm /usr/local/bin/ironclaw
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Ubuntu/Debian (APT)">
|
||||
```bash
|
||||
sudo apt remove ironclaw
|
||||
sudo apt autoremove
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="macOS (Homebrew)">
|
||||
```bash
|
||||
brew uninstall ironclaw
|
||||
brew untap ironclaw-ai/tap
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Docker">
|
||||
```bash
|
||||
# Stop and remove container
|
||||
docker stop ironclaw
|
||||
docker rm ironclaw
|
||||
|
||||
# Remove image
|
||||
docker rmi nearai/ironclaw:latest
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Remove Data
|
||||
|
||||
<Warning>
|
||||
This permanently deletes all IronClaw data including conversations, memory, and settings. This cannot be undone.
|
||||
</Warning>
|
||||
|
||||
```bash
|
||||
# Remove data directory
|
||||
rm -rf ~/.ironclaw
|
||||
|
||||
# Remove service files
|
||||
sudo rm -f /etc/systemd/system/ironclaw.service
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# Remove launchd service (macOS)
|
||||
rm -f ~/Library/LaunchAgents/ai.ironclaw.service.plist
|
||||
|
||||
# Remove logs
|
||||
sudo rm -rf /var/log/ironclaw
|
||||
```
|
||||
|
||||
## Remove PostgreSQL Database
|
||||
|
||||
If you used PostgreSQL:
|
||||
|
||||
```bash
|
||||
# Remove database (optional)
|
||||
sudo -u postgres psql <<EOF
|
||||
DROP DATABASE ironclaw;
|
||||
DROP USER ironclaw;
|
||||
EOF
|
||||
```
|
||||
|
||||
## Verify Removal
|
||||
|
||||
```bash
|
||||
# Check binary removed
|
||||
which ironclaw
|
||||
# Should return nothing
|
||||
|
||||
# Check data removed
|
||||
ls -la ~/.ironclaw
|
||||
# Should return: No such file or directory
|
||||
|
||||
# Check service removed
|
||||
sudo systemctl status ironclaw
|
||||
# Should return: Unit ironclaw.service could not be found
|
||||
```
|
||||
|
||||
## Partial Removal
|
||||
|
||||
To keep your data but remove the binary:
|
||||
|
||||
```bash
|
||||
# Just remove binary
|
||||
rm ~/.local/bin/ironclaw
|
||||
|
||||
# Data remains at ~/.ironclaw/
|
||||
# Reinstall later and your data will be intact
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Install IronClaw](/install) again if needed
|
||||
- Your data can be restored if you kept `~/.ironclaw/`
|
||||
193
docs/drafts/install/updating.mdx
Normal file
@@ -0,0 +1,193 @@
|
||||
---
|
||||
title: Updating IronClaw
|
||||
sidebarTitle: Updating
|
||||
description: How to update IronClaw to the latest version
|
||||
---
|
||||
|
||||
Keep IronClaw up to date for the latest features and security patches.
|
||||
|
||||
## Check Current Version
|
||||
|
||||
```bash
|
||||
ironclaw --version
|
||||
# ironclaw 0.13.0
|
||||
```
|
||||
|
||||
## Update by Installation Method
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux (Shell Script)">
|
||||
```bash
|
||||
# Re-run the install script (updates in place)
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
|
||||
# Verify update
|
||||
ironclaw --version
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Ubuntu/Debian (APT)">
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt upgrade ironclaw
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="macOS (Homebrew)">
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade ironclaw
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Docker">
|
||||
```bash
|
||||
# Pull latest image
|
||||
docker pull nearai/ironclaw:latest
|
||||
|
||||
# Recreate container
|
||||
docker stop ironclaw
|
||||
docker rm ironclaw
|
||||
docker run -d --name ironclaw ...
|
||||
|
||||
# Or with docker compose
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="From Source">
|
||||
```bash
|
||||
cd /path/to/ironclaw
|
||||
git pull origin main
|
||||
cargo build --release
|
||||
sudo cp target/release/ironclaw /usr/local/bin/
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Post-Update Steps
|
||||
|
||||
### 1. Review Changelog
|
||||
|
||||
Check what changed in the new version:
|
||||
|
||||
```bash
|
||||
# View changelog (if installed from source)
|
||||
cat /usr/local/share/ironclaw/CHANGELOG.md
|
||||
|
||||
# Or online
|
||||
open https://github.com/nearai/ironclaw/releases
|
||||
```
|
||||
|
||||
### 2. Run Migrations
|
||||
|
||||
Database migrations run automatically on startup, but verify:
|
||||
|
||||
```bash
|
||||
ironclaw run
|
||||
# Look for: "Running migrations..." in logs
|
||||
```
|
||||
|
||||
### 3. Restart Service
|
||||
|
||||
If running as a service:
|
||||
|
||||
```bash
|
||||
# systemd (Linux)
|
||||
sudo systemctl restart ironclaw
|
||||
|
||||
# launchd (macOS)
|
||||
launchctl unload ~/Library/LaunchAgents/ai.ironclaw.service.plist
|
||||
launchctl load ~/Library/LaunchAgents/ai.ironclaw.service.plist
|
||||
|
||||
# Homebrew
|
||||
brew services restart ironclaw
|
||||
```
|
||||
|
||||
## Downgrading
|
||||
|
||||
If you need to rollback:
|
||||
|
||||
```bash
|
||||
# Linux/macOS shell script
|
||||
# Download specific version
|
||||
curl -fsSL https://install.ironclaw.ai | bash -s -- --version 0.12.0
|
||||
|
||||
# Docker
|
||||
docker pull nearai/ironclaw:v0.12.0
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Downgrading may require manual database rollback if migrations were applied. Always backup before updating.
|
||||
</Warning>
|
||||
|
||||
## Automatic Updates
|
||||
|
||||
### systemd Timer (Linux)
|
||||
|
||||
```bash
|
||||
sudo tee /etc/systemd/system/ironclaw-update.service <<EOF
|
||||
[Unit]
|
||||
Description=IronClaw Update Check
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/bash -c 'curl -fsSL https://install.ironclaw.ai | bash'
|
||||
EOF
|
||||
|
||||
sudo tee /etc/systemd/system/ironclaw-update.timer <<EOF
|
||||
[Unit]
|
||||
Description=Run IronClaw update check daily
|
||||
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
|
||||
sudo systemctl enable --now ironclaw-update.timer
|
||||
```
|
||||
|
||||
## Troubleshooting Updates
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Update fails with permission denied" icon="lock">
|
||||
```bash
|
||||
# Fix permissions
|
||||
sudo chown -R $USER:$USER ~/.local/bin/ironclaw
|
||||
|
||||
# Or update with sudo
|
||||
sudo curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database migration fails" icon="database">
|
||||
1. Stop IronClaw
|
||||
2. Backup database
|
||||
3. Run with debug logging: `RUST_LOG=debug ironclaw run`
|
||||
4. Check specific migration error
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Service won't start after update" icon="x-circle">
|
||||
```bash
|
||||
# Check logs
|
||||
sudo journalctl -u ironclaw -n 50
|
||||
|
||||
# Verify binary
|
||||
which ironclaw
|
||||
ironclaw --version
|
||||
|
||||
# Reinstall if corrupted
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Check the [Changelog](/reference/changelog) for new features
|
||||
- Review [Security](/security) updates
|
||||
- See [Troubleshooting](/help/troubleshooting) if issues occur
|
||||
342
docs/drafts/install/vps.mdx
Normal file
@@ -0,0 +1,342 @@
|
||||
---
|
||||
title: VPS Installation
|
||||
sidebarTitle: VPS / Cloud
|
||||
description: Deploy IronClaw to a cloud server
|
||||
---
|
||||
|
||||
Deploy IronClaw to a remote VPS or cloud server for always-on operation.
|
||||
|
||||
## Recommended Providers
|
||||
|
||||
- **DigitalOcean**: Droplets from $6/month
|
||||
- **Hetzner**: CX11 from €4.51/month
|
||||
- **AWS**: t3.small or larger
|
||||
- **Google Cloud**: e2-small or larger
|
||||
- **Azure**: B1s or larger
|
||||
|
||||
Minimum specs: 1 vCPU, 2 GB RAM, 20 GB SSD
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
# SSH into your server
|
||||
ssh user@your-server-ip
|
||||
|
||||
# Update packages
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
|
||||
## Step 1: Install IronClaw
|
||||
|
||||
```bash
|
||||
# Install IronClaw
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
|
||||
# Add to PATH if needed
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||||
```
|
||||
|
||||
## Step 2: Install PostgreSQL
|
||||
|
||||
PostgreSQL is recommended for production deployments.
|
||||
|
||||
```bash
|
||||
# Install PostgreSQL
|
||||
sudo apt install postgresql postgresql-contrib
|
||||
|
||||
# Start PostgreSQL
|
||||
sudo systemctl enable --now postgresql
|
||||
|
||||
# Create database and user
|
||||
sudo -u postgres psql <<EOF
|
||||
CREATE USER ironclaw WITH PASSWORD 'your-secure-password';
|
||||
CREATE DATABASE ironclaw OWNER ironclaw;
|
||||
EOF
|
||||
|
||||
# Detect PostgreSQL major version (e.g., 14, 15, 16)
|
||||
PG_MAJOR=$(psql -V | awk '{print $3}' | cut -d. -f1)
|
||||
# Install matching pgvector package for the detected PostgreSQL version
|
||||
sudo apt install "postgresql-$PG_MAJOR-pgvector"
|
||||
# Enable the vector extension in the ironclaw database
|
||||
sudo -u postgres psql -d ironclaw -c "CREATE EXTENSION IF NOT EXISTS vector;"
|
||||
```
|
||||
|
||||
## Step 3: Run the Wizard
|
||||
|
||||
<Warning>
|
||||
**Browser OAuth blocked on VPS.** The default NEAR AI authentication requires a browser on the same machine. On a VPS, use:
|
||||
|
||||
1. **NEAR AI Cloud API key** (recommended): Get an API key from https://cloud.near.ai and paste it into the wizard
|
||||
2. **Custom callback URL**: Set `IRONCLAW_OAUTH_CALLBACK_URL` to a publicly reachable URL
|
||||
|
||||
</Warning>
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
**Wizard selections:**
|
||||
|
||||
1. **Database**: Select "PostgreSQL"
|
||||
- Enter connection string: `postgres://ironclaw:your-secure-password@localhost/ironclaw`
|
||||
|
||||
2. **Security**: Select "OS Keychain" or "Environment Variable"
|
||||
|
||||
3. **Inference Provider**: For NEAR AI, select option 4: "NEAR AI Cloud API key"
|
||||
- Paste your API key from https://cloud.near.ai
|
||||
|
||||
4. **Model Selection**: Choose from the list
|
||||
|
||||
5. **Embeddings**: Enable if using OpenAI or NEAR AI
|
||||
|
||||
6. **Channels**: Enable Web Gateway and HTTP Webhook
|
||||
|
||||
7. **Extensions**: Install desired tools
|
||||
|
||||
8. **Heartbeat**: Optional, for periodic tasks
|
||||
|
||||
## Step 4: Firewall Configuration
|
||||
|
||||
<Warning>
|
||||
**Important:** IronClaw's orchestrator binds to `0.0.0.0:50051` on Linux for container communication. This port should **not** be exposed externally. The firewall configuration below includes rules to block external access to this port—do not add any UFW allow rules for `50051`.
|
||||
</Warning>
|
||||
|
||||
```bash
|
||||
# Install UFW if not present
|
||||
sudo apt install ufw
|
||||
|
||||
# Default deny incoming
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
|
||||
# Allow SSH
|
||||
sudo ufw allow 22/tcp
|
||||
|
||||
# Allow Web Gateway
|
||||
sudo ufw allow 3000/tcp
|
||||
|
||||
# Allow HTTP Webhook (if using)
|
||||
sudo ufw allow 8080/tcp
|
||||
|
||||
# Orchestrator gRPC port (internal only)
|
||||
# UFW already denies incoming traffic by default; do NOT add an allow rule for 50051.
|
||||
# If you run Docker workers on the same host, you can allow only from the Docker bridge, e.g.:
|
||||
# sudo ufw allow in on docker0 to any port 50051 proto tcp
|
||||
# sudo ufw deny in on eth0 to any port 50051 proto tcp
|
||||
|
||||
# Enable firewall
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
## Step 5: Reverse Proxy (HTTPS)
|
||||
|
||||
For external access, use a reverse proxy with TLS:
|
||||
|
||||
### Option A: Caddy (Recommended)
|
||||
|
||||
```bash
|
||||
# Install Caddy
|
||||
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
sudo apt update
|
||||
sudo apt install caddy
|
||||
|
||||
# Configure Caddy
|
||||
sudo tee /etc/caddy/Caddyfile <<EOF
|
||||
webg.example.com {
|
||||
reverse_proxy localhost:3000
|
||||
}
|
||||
EOF
|
||||
|
||||
# Start Caddy
|
||||
sudo systemctl enable --now caddy
|
||||
```
|
||||
|
||||
### Option B: nginx
|
||||
|
||||
```bash
|
||||
# Install nginx
|
||||
sudo apt install nginx
|
||||
|
||||
# Configure site
|
||||
sudo tee /etc/nginx/sites-available/ironclaw <<'EOF'
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name webg.example.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/webg.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/webg.example.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name webg.example.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Enable site
|
||||
sudo ln -sf /etc/nginx/sites-available/ironclaw /etc/nginx/sites-enabled/
|
||||
sudo rm -f /etc/nginx/sites-enabled/default
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
### Option C: Cloudflare Tunnel
|
||||
|
||||
```bash
|
||||
# Install cloudflared
|
||||
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
|
||||
sudo dpkg -i cloudflared.deb
|
||||
|
||||
# Authenticate
|
||||
cloudflared tunnel login
|
||||
|
||||
# Create tunnel
|
||||
cloudflared tunnel create ironclaw
|
||||
|
||||
# Configure
|
||||
sudo mkdir -p /etc/cloudflared
|
||||
sudo tee /etc/cloudflared/config.yml <<EOF
|
||||
tunnel: YOUR-TUNNEL-ID
|
||||
credentials-file: /root/.cloudflared/YOUR-TUNNEL-ID.json
|
||||
|
||||
ingress:
|
||||
- hostname: webg.example.com
|
||||
service: http://localhost:3000
|
||||
- service: http_status:404
|
||||
EOF
|
||||
|
||||
# Install as service
|
||||
sudo cloudflared service install
|
||||
sudo systemctl enable --now cloudflared
|
||||
```
|
||||
|
||||
## Step 6: Service Setup
|
||||
|
||||
```bash
|
||||
# Install systemd service
|
||||
ironclaw service install
|
||||
|
||||
# Configure service
|
||||
sudo systemctl edit ironclaw
|
||||
```
|
||||
|
||||
Add environment variables:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
Environment="DATABASE_BACKEND=postgres"
|
||||
Environment="DATABASE_URL=postgres://ironclaw:your-secure-password@localhost/ironclaw"
|
||||
Environment="GATEWAY_HOST=127.0.0.1"
|
||||
```
|
||||
|
||||
Start the service:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable --now ironclaw
|
||||
sudo systemctl status ironclaw
|
||||
```
|
||||
|
||||
## Step 7: Verify
|
||||
|
||||
```bash
|
||||
# Check service status
|
||||
sudo systemctl status ironclaw
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u ironclaw -f
|
||||
|
||||
# Run diagnostics
|
||||
ironclaw doctor
|
||||
```
|
||||
|
||||
## Security Hardening
|
||||
|
||||
### Fail2ban
|
||||
|
||||
```bash
|
||||
sudo apt install fail2ban
|
||||
|
||||
# Create filter
|
||||
sudo tee /etc/fail2ban/filter.d/ironclaw.conf <<'EOF'
|
||||
[Definition]
|
||||
failregex = ^.*Unauthorized.*from <HOST>.*$
|
||||
^.*Invalid token.*from <HOST>.*$
|
||||
ignoreregex =
|
||||
EOF
|
||||
|
||||
# Create jail
|
||||
sudo tee /etc/fail2ban/jail.d/ironclaw.conf <<EOF
|
||||
[ironclaw]
|
||||
enabled = true
|
||||
port = http,https
|
||||
filter = ironclaw
|
||||
logpath = /var/log/ironclaw/web.log
|
||||
maxretry = 5
|
||||
bantime = 3600
|
||||
EOF
|
||||
|
||||
sudo systemctl restart fail2ban
|
||||
```
|
||||
|
||||
### Auto-updates
|
||||
|
||||
```bash
|
||||
# Install unattended-upgrades
|
||||
sudo apt install unattended-upgrades
|
||||
|
||||
# Configure
|
||||
sudo dpkg-reconfigure unattended-upgrades
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Cannot connect to database" icon="database">
|
||||
Verify PostgreSQL is running:
|
||||
```bash
|
||||
sudo systemctl status postgresql
|
||||
sudo -u postgres psql -c "\l"
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Web Gateway not accessible" icon="globe">
|
||||
Check firewall and binding:
|
||||
```bash
|
||||
sudo ufw status
|
||||
sudo ss -tlnp | grep 3000
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="NEAR AI authentication fails" icon="key">
|
||||
On VPS, use NEAR AI Cloud API key instead of browser OAuth:
|
||||
```bash
|
||||
export NEARAI_API_KEY=your-api-key
|
||||
ironclaw onboard
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Full environment variable reference
|
||||
</Card>
|
||||
<Card title="Channels" icon="message-circle" href="/channels">
|
||||
Set up Telegram and other channels
|
||||
</Card>
|
||||
</CardGroup>
|
||||
624
docs/drafts/ops/api.mdx
Normal file
@@ -0,0 +1,624 @@
|
||||
---
|
||||
title: REST API Reference
|
||||
sidebarTitle: REST API
|
||||
description: Complete endpoint reference for the IronClaw Web Gateway API
|
||||
---
|
||||
|
||||
The IronClaw Web Gateway exposes a REST API for all agent operations. All endpoints require bearer token authentication.
|
||||
|
||||
## Authentication
|
||||
|
||||
```http
|
||||
Authorization: Bearer <GATEWAY_AUTH_TOKEN>
|
||||
```
|
||||
|
||||
The token is set via the `GATEWAY_AUTH_TOKEN` environment variable. An unauthenticated request returns `401 Unauthorized`.
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
For remote deployments, replace with your reverse proxy domain (e.g., `https://ironclaw.yourdomain.com`).
|
||||
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/status` | Agent status, version, uptime, active job count |
|
||||
| `GET` | `/api/health` | Liveness check — returns `200 OK` if the gateway is up |
|
||||
| `GET` | `/api/gateway/status` | Gateway connection status and channel details |
|
||||
|
||||
### GET /api/status
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/status | jq .
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "running",
|
||||
"version": "0.13.0",
|
||||
"uptime_secs": 86400,
|
||||
"active_jobs": 2,
|
||||
"llm_backend": "nearai",
|
||||
"database_backend": "libsql",
|
||||
"sandbox_enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chat
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | `/api/chat/send` | Submit a message; returns a streaming response or job ID |
|
||||
| `POST` | `/api/chat/approval` | Approve or deny a pending tool execution |
|
||||
| `POST` | `/api/chat/auth-token` | Complete OAuth token flow |
|
||||
| `POST` | `/api/chat/auth-cancel` | Cancel pending OAuth flow |
|
||||
| `GET` | `/api/chat/events` | SSE stream of chat events (job updates, messages) |
|
||||
| `GET` | `/api/chat/ws` | WebSocket endpoint for real-time chat |
|
||||
| `GET` | `/api/chat/history` | Get message history for a session |
|
||||
| `GET` | `/api/chat/threads` | List all conversation threads |
|
||||
| `POST` | `/api/chat/thread/new` | Create a new conversation thread |
|
||||
|
||||
### POST /api/chat/send
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"message": "Summarize the last 3 jobs", "session_id": "default"}' \
|
||||
http://localhost:3000/api/chat/send
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "string (required)",
|
||||
"session_id": "string (optional, defaults to 'default')",
|
||||
"stream": false
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "job_01j9abc123",
|
||||
"session_id": "default",
|
||||
"status": "pending",
|
||||
"message": "Job created. Connect to /api/jobs/job_01j9abc123 for updates."
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/chat/threads
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/chat/threads | jq .
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"threads": [
|
||||
{
|
||||
"id": "default",
|
||||
"name": "Default",
|
||||
"created_at": "2024-01-15T10:00:00Z",
|
||||
"updated_at": "2024-01-15T12:30:00Z",
|
||||
"message_count": 42
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### POST /api/chat/approval
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"job_id": "job_01j9abc123", "approved": true}' \
|
||||
http://localhost:3000/api/chat/approval
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Jobs
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/jobs` | List jobs (supports `?status=`, `?limit=`, `?offset=`) |
|
||||
| `GET` | `/api/jobs/summary` | Get job statistics summary |
|
||||
| `GET` | `/api/jobs/:id` | Get job details and current state |
|
||||
| `POST` | `/api/jobs/:id/cancel` | Cancel a running job |
|
||||
| `POST` | `/api/jobs/:id/restart` | Restart a completed or failed job |
|
||||
| `POST` | `/api/jobs/:id/prompt` | Send a follow-up prompt to a running job |
|
||||
| `GET` | `/api/jobs/:id/events` | Get job event history |
|
||||
| `GET` | `/api/jobs/:id/files/list` | List files in the job's sandbox |
|
||||
| `GET` | `/api/jobs/:id/files/read` | Read a file from the job's sandbox |
|
||||
|
||||
### GET /api/jobs
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
"http://localhost:3000/api/jobs?status=running&limit=10" | jq .
|
||||
```
|
||||
|
||||
**Query parameters:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `status` | string | Filter by: `pending`, `running`, `completed`, `failed`, `cancelled` |
|
||||
| `limit` | integer | Max results (default: 20, max: 100) |
|
||||
| `offset` | integer | Pagination offset |
|
||||
| `session_id` | string | Filter by session |
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"id": "job_01j9abc123",
|
||||
"session_id": "default",
|
||||
"status": "running",
|
||||
"intent": "summarize recent activity",
|
||||
"created_at": "2024-01-15T10:30:00Z",
|
||||
"updated_at": "2024-01-15T10:30:05Z",
|
||||
"tool_calls": 3,
|
||||
"tokens_used": 1200
|
||||
}
|
||||
],
|
||||
"total": 47,
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/jobs/summary
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/jobs/summary | jq .
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 47,
|
||||
"by_status": {
|
||||
"pending": 2,
|
||||
"running": 3,
|
||||
"completed": 35,
|
||||
"failed": 5,
|
||||
"cancelled": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /api/jobs/:id/cancel
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/jobs/job_01j9abc123/cancel
|
||||
```
|
||||
|
||||
### POST /api/jobs/:id/restart
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/jobs/job_01j9abc123/restart
|
||||
```
|
||||
|
||||
### POST /api/jobs/:id/prompt
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"message": "Also check the tests directory"}' \
|
||||
http://localhost:3000/api/jobs/job_01j9abc123/prompt
|
||||
```
|
||||
|
||||
### GET /api/jobs/:id/files/list
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/jobs/job_01j9abc123/files/list | jq .
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"files": [
|
||||
{"path": "/workspace/main.rs", "size": 1234, "is_dir": false},
|
||||
{"path": "/workspace/src", "size": 0, "is_dir": true}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/jobs/:id/files/read
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
"http://localhost:3000/api/jobs/job_01j9abc123/files/read?path=/workspace/main.rs" | jq .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/memory/search` | Hybrid search (FTS + vector) across workspace memory |
|
||||
| `GET` | `/api/memory/tree` | Browse the workspace file tree |
|
||||
| `GET` | `/api/memory/list` | List memory documents with pagination |
|
||||
| `GET` | `/api/memory/read` | Read a memory document by path |
|
||||
| `POST` | `/api/memory/write` | Write a new memory document |
|
||||
| `GET` | `/api/memory/:path` | Read a memory document by path (alternative) |
|
||||
| `PUT` | `/api/memory/:path` | Write or update a memory document |
|
||||
| `DELETE` | `/api/memory/:path` | Delete a memory document |
|
||||
|
||||
### GET /api/memory/search
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
"http://localhost:3000/api/memory/search?q=deployment+notes&limit=5" | jq .
|
||||
```
|
||||
|
||||
**Query parameters:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `q` | string | Search query (required) |
|
||||
| `limit` | integer | Max results (default: 10) |
|
||||
| `semantic` | boolean | Include semantic/vector results (default: true, requires embeddings) |
|
||||
|
||||
### GET /api/memory/tree
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
"http://localhost:3000/api/memory/tree?path=/" | jq .
|
||||
```
|
||||
|
||||
### POST /api/memory/write
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"path": "context/notes.md", "content": "# Notes\n\nSome notes here."}' \
|
||||
http://localhost:3000/api/memory/write
|
||||
```
|
||||
|
||||
### PUT /api/memory/:path
|
||||
|
||||
```bash
|
||||
curl -s -X PUT \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"content": "# Deploy Notes\n\nDeployed v0.13 on 2024-01-15.", "tags": ["deploy", "notes"]}' \
|
||||
http://localhost:3000/api/memory/context/deploy-notes.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Routines
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/routines` | List all routines |
|
||||
| `GET` | `/api/routines/summary` | Get routine statistics summary |
|
||||
| `POST` | `/api/routines` | Create a new routine |
|
||||
| `GET` | `/api/routines/:id` | Get a routine by ID |
|
||||
| `PUT` | `/api/routines/:id` | Update a routine |
|
||||
| `DELETE` | `/api/routines/:id` | Delete a routine |
|
||||
| `POST` | `/api/routines/:id/trigger` | Manually trigger a routine |
|
||||
| `POST` | `/api/routines/:id/toggle` | Enable or disable a routine |
|
||||
| `GET` | `/api/routines/:id/runs` | Get execution history for a routine |
|
||||
|
||||
### POST /api/routines
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "daily-digest",
|
||||
"trigger": {"type": "cron", "schedule": "0 9 * * *"},
|
||||
"action": {"type": "message", "content": "Generate a daily summary of yesterday'"'"'s activity."},
|
||||
"enabled": true
|
||||
}' \
|
||||
http://localhost:3000/api/routines
|
||||
```
|
||||
|
||||
### POST /api/routines/:id/trigger
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/routines/routine_01j9abc123/trigger
|
||||
```
|
||||
|
||||
### POST /api/routines/:id/toggle
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"enabled": false}' \
|
||||
http://localhost:3000/api/routines/routine_01j9abc123/toggle
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skills
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/skills` | List all discovered skills with trust level and activation status |
|
||||
| `POST` | `/api/skills/install` | Install a skill from ClawHub or a local path |
|
||||
| `DELETE` | `/api/skills/:name` | Remove an installed skill |
|
||||
| `GET` | `/api/skills/search` | Search the ClawHub registry |
|
||||
|
||||
### GET /api/skills/search
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
"http://localhost:3000/api/skills/search?q=git+workflow" | jq .
|
||||
```
|
||||
|
||||
### POST /api/skills/install
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "git-workflow", "source": "clawhub"}' \
|
||||
http://localhost:3000/api/skills/install
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Extensions
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/extensions` | List installed extensions (MCP servers, WASM modules) |
|
||||
| `GET` | `/api/extensions/tools` | List tools provided by extensions |
|
||||
| `GET` | `/api/extensions/registry` | List available extensions from registry |
|
||||
| `POST` | `/api/extensions/install` | Install an extension from URL or registry |
|
||||
| `POST` | `/api/extensions/:id/auth` | Configure authentication for an extension |
|
||||
| `POST` | `/api/extensions/:id/activate` | Activate an installed extension |
|
||||
| `DELETE` | `/api/extensions/:id` | Uninstall an extension |
|
||||
|
||||
### GET /api/extensions/registry
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/extensions/registry | jq .
|
||||
```
|
||||
|
||||
### GET /api/extensions/tools
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/extensions/tools | jq .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Secrets
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/secrets` | List secret names (values are never returned) |
|
||||
| `POST` | `/api/secrets` | Store a new secret (AES-256-GCM encrypted at rest) |
|
||||
| `DELETE` | `/api/secrets/:name` | Delete a stored secret |
|
||||
|
||||
### POST /api/secrets
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "github_token", "value": "ghp_xxxxxxxxxxxx", "description": "GitHub PAT for CI"}' \
|
||||
http://localhost:3000/api/secrets
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "github_token",
|
||||
"created_at": "2024-01-15T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
Secret values are write-only. The `GET /api/secrets` endpoint returns only secret names and metadata, never the plaintext values. Values are encrypted with AES-256-GCM before storage.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Settings
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/settings` | Get all user settings as a key-value map |
|
||||
| `GET` | `/api/settings/:key` | Get a specific setting |
|
||||
| `PUT` | `/api/settings` | Update one or more settings |
|
||||
| `GET` | `/api/settings/export` | Export all settings as JSON |
|
||||
| `POST` | `/api/settings/import` | Import settings from JSON |
|
||||
|
||||
### PUT /api/settings
|
||||
|
||||
```bash
|
||||
curl -s -X PUT \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"heartbeat_enabled": "true", "max_parallel_jobs": "3"}' \
|
||||
http://localhost:3000/api/settings
|
||||
```
|
||||
|
||||
### GET /api/settings/export
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/settings/export > settings.json
|
||||
```
|
||||
|
||||
### POST /api/settings/import
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @settings.json \
|
||||
http://localhost:3000/api/settings/import
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logs
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/logs/events` | SSE stream of live log events |
|
||||
| `GET` | `/api/logs/level` | Get current log level |
|
||||
| `PUT` | `/api/logs/level` | Set log level dynamically |
|
||||
|
||||
### GET /api/logs/events
|
||||
|
||||
```bash
|
||||
curl -N \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Accept: text/event-stream" \
|
||||
http://localhost:3000/api/logs/events
|
||||
```
|
||||
|
||||
### PUT /api/logs/level
|
||||
|
||||
```bash
|
||||
curl -s -X PUT \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"level": "debug"}' \
|
||||
http://localhost:3000/api/logs/level
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Channels
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/channels` | List all configured channels and their enabled status |
|
||||
| `GET` | `/api/channels/:id/status` | Connection status for a specific channel |
|
||||
|
||||
---
|
||||
|
||||
## Pairing
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/pairing/:channel` | List pairing codes for a channel type |
|
||||
| `POST` | `/api/pairing/:channel` | Create a new pairing code |
|
||||
|
||||
### GET /api/pairing/:channel
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/pairing/telegram | jq .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OAuth
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/oauth/callback` | OAuth callback handler for channel authentication |
|
||||
|
||||
---
|
||||
|
||||
## OpenAI Compatibility
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/v1/models` | List available models |
|
||||
| `POST` | `/v1/chat/completions` | OpenAI-compatible chat completions |
|
||||
|
||||
### GET /v1/models
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/v1/models | jq .
|
||||
```
|
||||
|
||||
### POST /v1/chat/completions
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "claude-3-5-sonnet-20241022",
|
||||
"messages": [{"role": "user", "content": "Hello"}]
|
||||
}' \
|
||||
http://localhost:3000/v1/chat/completions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Status | Code | Description |
|
||||
|--------|------|-------------|
|
||||
| `400` | Bad Request | Malformed request body or missing required fields |
|
||||
| `401` | Unauthorized | Missing or invalid `Authorization` header |
|
||||
| `404` | Not Found | The requested resource does not exist |
|
||||
| `409` | Conflict | Resource already exists (e.g., duplicate secret name) |
|
||||
| `422` | Unprocessable Entity | Request is well-formed but semantically invalid |
|
||||
| `429` | Too Many Requests | Rate limit exceeded |
|
||||
| `500` | Internal Server Error | Unexpected server error — check logs |
|
||||
| `503` | Service Unavailable | Agent is initializing or shutting down |
|
||||
|
||||
**Error response body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "not_found",
|
||||
"message": "Job 'job_01j9abc123' does not exist",
|
||||
"request_id": "req_7f3a9b"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="WebSocket & SSE" icon="radio" href="/ops/websocket-sse">
|
||||
Real-time streaming for job updates and log tailing
|
||||
</Card>
|
||||
<Card title="Orchestrator API" icon="server" href="/ops/orchestrator">
|
||||
Internal worker API for sandbox containers
|
||||
</Card>
|
||||
<Card title="Logging" icon="file-text" href="/ops/logging">
|
||||
RUST_LOG, journalctl, and cost tracking
|
||||
</Card>
|
||||
</CardGroup>
|
||||
294
docs/drafts/ops/logging.mdx
Normal file
@@ -0,0 +1,294 @@
|
||||
---
|
||||
title: Logging & Monitoring
|
||||
sidebarTitle: Logging
|
||||
description: RUST_LOG levels, structured logs, cost tracking, and SSE log streams
|
||||
---
|
||||
|
||||
IronClaw uses the [tracing](https://docs.rs/tracing) crate for structured logging. Output goes to stdout/stderr and can be streamed in real time via the Web Gateway SSE endpoint.
|
||||
|
||||
---
|
||||
|
||||
## RUST_LOG Format
|
||||
|
||||
Control log verbosity with the `RUST_LOG` environment variable. The format follows the `tracing_subscriber` filter syntax:
|
||||
|
||||
```
|
||||
RUST_LOG=<crate>=<level>[,<crate2>=<level2>,...]
|
||||
```
|
||||
|
||||
### Common Log Level Configurations
|
||||
|
||||
| Configuration | Use Case |
|
||||
|---------------|----------|
|
||||
| `ironclaw=error` | Production: errors only |
|
||||
| `ironclaw=warn` | Production: errors and warnings |
|
||||
| `ironclaw=info` | Production: normal operational events (recommended) |
|
||||
| `ironclaw=debug` | Troubleshooting: detailed request/response flow |
|
||||
| `ironclaw=trace` | Deep debugging: all internal events including LLM token streams |
|
||||
| `ironclaw=info,tower_http=warn` | Reduce HTTP access log noise |
|
||||
| `ironclaw=debug,tower_http=debug` | Debug with HTTP request details |
|
||||
|
||||
Set in your environment file or shell:
|
||||
|
||||
```bash
|
||||
# In .env or /etc/ironclaw/ironclaw.env
|
||||
RUST_LOG=ironclaw=info,tower_http=warn
|
||||
|
||||
# Or inline for a single run
|
||||
RUST_LOG=ironclaw=debug ironclaw run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Log Levels
|
||||
|
||||
| Level | When to Use |
|
||||
|-------|-------------|
|
||||
| `error` | Unrecoverable failures: database connection lost, LLM provider unreachable, job crashed |
|
||||
| `warn` | Recoverable issues: retry attempt, rate limit hit, sandbox container restart, circuit breaker tripped |
|
||||
| `info` | Normal operations: job started/completed, routine triggered, heartbeat ran, extension activated |
|
||||
| `debug` | Request flow detail: LLM API calls, tool invocations with parameters, state transitions |
|
||||
| `trace` | Fine-grained internals: token-by-token stream chunks, SQL queries, WASM fuel consumption |
|
||||
|
||||
---
|
||||
|
||||
## Per-Module Filtering
|
||||
|
||||
Target specific subsystems without flooding the output:
|
||||
|
||||
```bash
|
||||
# Only agent and scheduler modules
|
||||
RUST_LOG=ironclaw::agent=debug,ironclaw::agent::scheduler=trace
|
||||
|
||||
# LLM provider calls only
|
||||
RUST_LOG=ironclaw::llm=debug
|
||||
|
||||
# Safety layer only
|
||||
RUST_LOG=ironclaw::safety=debug
|
||||
|
||||
# Skills system only
|
||||
RUST_LOG=ironclaw::skills=debug
|
||||
|
||||
# Sandbox and proxy
|
||||
RUST_LOG=ironclaw::sandbox=debug
|
||||
|
||||
# Database queries
|
||||
RUST_LOG=ironclaw::db=trace
|
||||
|
||||
# Everything + HTTP access log
|
||||
RUST_LOG=ironclaw=debug,tower_http=debug
|
||||
|
||||
# Silence noisy dependencies
|
||||
RUST_LOG=ironclaw=info,hyper=warn,reqwest=warn,tower_http=warn
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Log Output Format
|
||||
|
||||
IronClaw emits structured logs in the following format:
|
||||
|
||||
```
|
||||
2024-01-15T10:30:00.123456Z INFO ironclaw::agent::worker: job started job_id="job_01j9abc" intent="summarize recent activity" session="default"
|
||||
2024-01-15T10:30:00.456789Z DEBUG ironclaw::llm::nearai_chat: sending request model="claude-3-5-sonnet-20241022" tokens=1024
|
||||
2024-01-15T10:30:02.891234Z INFO ironclaw::agent::worker: tool call tool="memory_search" job_id="job_01j9abc"
|
||||
2024-01-15T10:30:03.234567Z INFO ironclaw::agent::worker: job completed job_id="job_01j9abc" duration_ms=3111 tokens_used=1847
|
||||
```
|
||||
|
||||
Fields included in log events:
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `timestamp` | ISO-8601 UTC timestamp |
|
||||
| `level` | Log level (ERROR/WARN/INFO/DEBUG/TRACE) |
|
||||
| `module` | Rust module path (e.g., `ironclaw::agent::worker`) |
|
||||
| `message` | Human-readable event description |
|
||||
| `job_id` | Associated job ID (when applicable) |
|
||||
| `session_id` | Conversation session (when applicable) |
|
||||
| `tool` | Tool name (for tool call events) |
|
||||
| `duration_ms` | Operation duration in milliseconds (on completion events) |
|
||||
| `tokens_used` | LLM token count (for LLM call events) |
|
||||
|
||||
---
|
||||
|
||||
## journalctl (systemd)
|
||||
|
||||
When running under systemd, all output is captured by the journal:
|
||||
|
||||
```bash
|
||||
# Follow live logs
|
||||
journalctl -u ironclaw -f
|
||||
|
||||
# Last 200 lines
|
||||
journalctl -u ironclaw -n 200
|
||||
|
||||
# Since a specific time
|
||||
journalctl -u ironclaw --since "2024-01-15 10:00:00"
|
||||
|
||||
# Last hour only
|
||||
journalctl -u ironclaw --since "1 hour ago"
|
||||
|
||||
# Last hour, error level and above
|
||||
journalctl -u ironclaw --since "1 hour ago" -p err
|
||||
|
||||
# Filter by a specific string (grep equivalent)
|
||||
journalctl -u ironclaw -g "job_01j9abc"
|
||||
|
||||
# Export to JSON for analysis
|
||||
journalctl -u ironclaw --since today -o json > ironclaw-today.json
|
||||
|
||||
# Export to plain text file
|
||||
journalctl -u ironclaw --since today > ironclaw-today.log
|
||||
|
||||
# Check disk usage of journal
|
||||
journalctl --disk-usage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LLM Cost Tracking
|
||||
|
||||
IronClaw records every LLM API call in the `llm_calls` database table. Each record includes:
|
||||
|
||||
| Column | Description |
|
||||
|--------|-------------|
|
||||
| `job_id` | Job that triggered the call |
|
||||
| `model` | Model name (e.g., `claude-3-5-sonnet-20241022`) |
|
||||
| `provider` | LLM backend (e.g., `nearai`, `anthropic`) |
|
||||
| `prompt_tokens` | Input tokens |
|
||||
| `completion_tokens` | Output tokens |
|
||||
| `total_tokens` | Sum of prompt + completion |
|
||||
| `cost_usd` | Estimated cost in USD (based on published rates) |
|
||||
| `latency_ms` | Time to first token in milliseconds |
|
||||
| `created_at` | Timestamp of the call |
|
||||
|
||||
### Query via REST API
|
||||
|
||||
```bash
|
||||
# Get cost summary
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:3000/api/status | jq '.cost_summary'
|
||||
```
|
||||
|
||||
### Query the Database Directly (libSQL)
|
||||
|
||||
```bash
|
||||
# Connect to the libSQL database
|
||||
sqlite3 ~/.ironclaw/ironclaw.db
|
||||
|
||||
# Total cost today
|
||||
SELECT
|
||||
provider,
|
||||
model,
|
||||
SUM(total_tokens) AS tokens,
|
||||
ROUND(SUM(cost_usd), 4) AS cost_usd
|
||||
FROM llm_calls
|
||||
WHERE date(created_at) = date('now')
|
||||
GROUP BY provider, model
|
||||
ORDER BY cost_usd DESC;
|
||||
|
||||
# Top 10 most expensive jobs this week
|
||||
SELECT
|
||||
job_id,
|
||||
SUM(total_tokens) AS tokens,
|
||||
ROUND(SUM(cost_usd), 4) AS cost_usd
|
||||
FROM llm_calls
|
||||
WHERE created_at >= datetime('now', '-7 days')
|
||||
GROUP BY job_id
|
||||
ORDER BY cost_usd DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
### Query the Database Directly (PostgreSQL)
|
||||
|
||||
```sql
|
||||
-- Total cost this month
|
||||
SELECT
|
||||
provider,
|
||||
model,
|
||||
SUM(total_tokens) AS tokens,
|
||||
ROUND(SUM(cost_usd)::numeric, 4) AS cost_usd
|
||||
FROM llm_calls
|
||||
WHERE created_at >= date_trunc('month', NOW())
|
||||
GROUP BY provider, model
|
||||
ORDER BY cost_usd DESC;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Live Log Streaming via Web Gateway
|
||||
|
||||
The Web Gateway exposes a live SSE log stream at `/api/logs`. This lets you tail logs from a browser or monitoring system without SSH access.
|
||||
|
||||
```bash
|
||||
# Stream logs with curl
|
||||
curl -N \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Accept: text/event-stream" \
|
||||
http://localhost:3000/api/logs
|
||||
```
|
||||
|
||||
Example output:
|
||||
|
||||
```
|
||||
event: log
|
||||
data: {"level":"INFO","message":"job started","module":"ironclaw::agent::worker","job_id":"job_01j9abc","ts":"2024-01-15T10:30:00Z"}
|
||||
|
||||
event: job_status
|
||||
data: {"job_id":"job_01j9abc","status":"completed","ts":"2024-01-15T10:30:03Z"}
|
||||
```
|
||||
|
||||
See [WebSocket & SSE](/ops/websocket-sse) for JavaScript integration examples and the full event type reference.
|
||||
|
||||
---
|
||||
|
||||
## Reducing Log Noise
|
||||
|
||||
Some dependencies are verbose at the default log level. Silence them while keeping IronClaw output at debug:
|
||||
|
||||
```bash
|
||||
# Quiet HTTP infrastructure
|
||||
RUST_LOG=ironclaw=debug,hyper=warn,reqwest=warn,tower_http=warn,h2=warn
|
||||
|
||||
# Quiet database layer
|
||||
RUST_LOG=ironclaw=info,ironclaw::db=warn
|
||||
|
||||
# Maximum quiet (errors only everywhere)
|
||||
RUST_LOG=error
|
||||
|
||||
# Recommended production setting
|
||||
RUST_LOG=ironclaw=info,tower_http=warn,hyper=warn
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Disabling Specific Modules
|
||||
|
||||
If a particular module is too noisy during an investigation, disable it completely:
|
||||
|
||||
```bash
|
||||
# Suppress all sandbox logs
|
||||
RUST_LOG=ironclaw=debug,ironclaw::sandbox=off
|
||||
|
||||
# Suppress all LLM call details
|
||||
RUST_LOG=ironclaw=debug,ironclaw::llm=info
|
||||
|
||||
# Suppress routine engine tick logs
|
||||
RUST_LOG=ironclaw=info,ironclaw::agent::routine_engine=warn
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="REST API Reference" icon="code" href="/ops/api">
|
||||
Query cost summaries and status via the REST API
|
||||
</Card>
|
||||
<Card title="WebSocket & SSE" icon="radio" href="/ops/websocket-sse">
|
||||
Stream live logs to a browser or monitoring tool
|
||||
</Card>
|
||||
<Card title="Troubleshooting" icon="wrench" href="/help/troubleshooting">
|
||||
Common error patterns and how to resolve them
|
||||
</Card>
|
||||
</CardGroup>
|
||||
348
docs/drafts/ops/orchestrator.mdx
Normal file
@@ -0,0 +1,348 @@
|
||||
---
|
||||
title: Orchestrator API
|
||||
sidebarTitle: Orchestrator
|
||||
description: Internal worker API for sandbox container communication
|
||||
---
|
||||
|
||||
The Orchestrator runs on a separate internal port (default `50051`) from the web gateway. This API is used by worker containers to communicate with the orchestrator for LLM calls, credential injection, and job lifecycle management.
|
||||
|
||||
<Note>
|
||||
This is an internal API. Worker containers receive a per-job bearer token during initialization. All `/worker/` endpoints require authentication.
|
||||
</Note>
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:50051
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Workers authenticate using per-job bearer tokens issued during container initialization:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <job_token>
|
||||
```
|
||||
|
||||
Tokens are scoped to specific job IDs and rejected if used for other jobs.
|
||||
|
||||
---
|
||||
|
||||
## Health
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/health` | Liveness check — returns `200 OK` if orchestrator is running |
|
||||
|
||||
### GET /health
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:50051/health
|
||||
# Response: "ok"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Job Management
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/worker/{job_id}/job` | Get job description and configuration |
|
||||
| `POST` | `/worker/{job_id}/status` | Worker reports current status/iteration |
|
||||
| `POST` | `/worker/{job_id}/complete` | Worker reports job completion or failure |
|
||||
|
||||
### GET /worker/{job_id}/job
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:50051/worker/job_01j9abc123/job | jq .
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Job job_01j9abc123",
|
||||
"description": "Analyze the codebase and summarize findings",
|
||||
"project_dir": "/workspace/my-project"
|
||||
}
|
||||
```
|
||||
|
||||
### POST /worker/{job_id}/status
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"state": "in_progress",
|
||||
"message": "Running analysis",
|
||||
"iteration": 3
|
||||
}' \
|
||||
http://localhost:50051/worker/job_01j9abc123/status
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"state": "string (pending|running|in_progress|completed|failed)",
|
||||
"message": "string (optional status message)",
|
||||
"iteration": "integer (current iteration count)"
|
||||
}
|
||||
```
|
||||
|
||||
### POST /worker/{job_id}/complete
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"success": true,
|
||||
"message": "Analysis complete. Found 5 key files."
|
||||
}' \
|
||||
http://localhost:50051/worker/job_01j9abc123/complete
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": "boolean (required)",
|
||||
"message": "string (optional result message)"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LLM Proxy
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | `/worker/{job_id}/llm/complete` | Proxy a completion request to the LLM |
|
||||
| `POST` | `/worker/{job_id}/llm/complete_with_tools` | Proxy a tool-use request to the LLM |
|
||||
|
||||
### POST /worker/{job_id}/llm/complete
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
],
|
||||
"model": "claude-3-5-sonnet-20241022",
|
||||
"max_tokens": 1024,
|
||||
"temperature": 0.7
|
||||
}' \
|
||||
http://localhost:50051/worker/job_01j9abc123/llm/complete | jq .
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": "array (ChatMessage array)",
|
||||
"model": "string (model identifier)",
|
||||
"max_tokens": "integer (optional, default from config)",
|
||||
"temperature": "float (optional, 0.0-1.0)",
|
||||
"stop_sequences": "array (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "LLM response text",
|
||||
"input_tokens": 15,
|
||||
"output_tokens": 42,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
```
|
||||
|
||||
### POST /worker/{job_id}/llm/complete_with_tools
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{"role": "user", "content": "List files in the project"}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "shell",
|
||||
"description": "Execute shell commands",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"model": "claude-3-5-sonnet-20241022",
|
||||
"max_tokens": 1024
|
||||
}' \
|
||||
http://localhost:50051/worker/job_01j9abc123/llm/complete_with_tools | jq .
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": "array (ChatMessage array)",
|
||||
"tools": "array (Tool definition array)",
|
||||
"model": "string (model identifier)",
|
||||
"max_tokens": "integer",
|
||||
"temperature": "float (optional)",
|
||||
"tool_choice": "string (optional, 'auto'|'none'|tool name)"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "I'll list the files for you.",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_abc123",
|
||||
"name": "shell",
|
||||
"input": {"command": "ls -la"}
|
||||
}
|
||||
],
|
||||
"input_tokens": 120,
|
||||
"output_tokens": 85,
|
||||
"finish_reason": "tool_use"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Job Events
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | `/worker/{job_id}/event` | Worker sends events (message, tool_use, tool_result, result) |
|
||||
|
||||
### POST /worker/{job_id}/event
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"event_type": "message",
|
||||
"data": {
|
||||
"role": "assistant",
|
||||
"content": "Analyzing the codebase..."
|
||||
}
|
||||
}' \
|
||||
http://localhost:50051/worker/job_01j9abc123/event
|
||||
```
|
||||
|
||||
**Event types:**
|
||||
|
||||
| Event Type | Data Fields |
|
||||
|------------|-------------|
|
||||
| `message` | `role`, `content` |
|
||||
| `tool_use` | `tool_name`, `input` |
|
||||
| `tool_result` | `tool_name`, `output` |
|
||||
| `result` | `status`, `session_id` (optional) |
|
||||
|
||||
**Response:** `200 OK` on success
|
||||
|
||||
---
|
||||
|
||||
## Claude Code Bridge
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/worker/{job_id}/prompt` | Get next queued follow-up prompt for Claude Code |
|
||||
|
||||
### GET /worker/{job_id}/prompt
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:50051/worker/job_01j9abc123/prompt
|
||||
```
|
||||
|
||||
**Response (with pending prompt):**
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "What is the current git status?",
|
||||
"done": false
|
||||
}
|
||||
```
|
||||
|
||||
**Response (queue empty):** `204 No Content`
|
||||
|
||||
---
|
||||
|
||||
## Credentials
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/worker/{job_id}/credentials` | Get decrypted secrets granted to this job |
|
||||
|
||||
### GET /worker/{job_id}/credentials
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:50051/worker/job_01j9abc123/credentials | jq .
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"env_var": "GITHUB_TOKEN",
|
||||
"value": "ghp_xxxxxxxxxxxx"
|
||||
},
|
||||
{
|
||||
"env_var": "DATABASE_URL",
|
||||
"value": "postgres://user:pass@localhost/db"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Response (no grants):** `204 No Content`
|
||||
|
||||
**Response (secrets store unavailable):** `503 Service Unavailable`
|
||||
|
||||
---
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Status | Code | Description |
|
||||
|--------|------|-------------|
|
||||
| `401` | Unauthorized | Missing or invalid job token |
|
||||
| `404` | Not Found | Job not found or container not running |
|
||||
| `503` | Service Unavailable | Secrets store not configured |
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
The orchestrator port is configured via:
|
||||
|
||||
| Environment Variable | Default | Description |
|
||||
|----------------------|---------|-------------|
|
||||
| `ORCHESTRATOR_PORT` | `50051` | Internal API port |
|
||||
|
||||
On Linux, the orchestrator binds to all interfaces (`0.0.0.0`) to allow container access. On macOS/Windows, it binds to loopback (`127.0.0.1`) since Docker Desktop routes through the VM.
|
||||
375
docs/drafts/ops/websocket-sse.mdx
Normal file
@@ -0,0 +1,375 @@
|
||||
---
|
||||
title: WebSocket & SSE Streaming
|
||||
sidebarTitle: WebSocket & SSE
|
||||
description: Real-time streaming via WebSocket and Server-Sent Events
|
||||
---
|
||||
|
||||
IronClaw supports two real-time streaming protocols: **WebSocket** for bidirectional communication (chat, job updates) and **Server-Sent Events (SSE)** for unidirectional log and event streams.
|
||||
|
||||
---
|
||||
|
||||
## WebSocket
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
ws://localhost:3000/ws
|
||||
```
|
||||
|
||||
For TLS-terminated deployments:
|
||||
|
||||
```
|
||||
wss://ironclaw.yourdomain.com/ws
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
WebSocket connections authenticate by sending an `auth` message immediately after connecting. The connection is rejected if authentication is not completed within 10 seconds.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth",
|
||||
"token": "<GATEWAY_AUTH_TOKEN>"
|
||||
}
|
||||
```
|
||||
|
||||
Successful authentication response:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_ok",
|
||||
"user_id": "default"
|
||||
}
|
||||
```
|
||||
|
||||
Failed authentication:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "error",
|
||||
"code": "auth_failed",
|
||||
"message": "Invalid token"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Message Types
|
||||
|
||||
**Client → Server:**
|
||||
|
||||
| Type | Description | Payload |
|
||||
|------|-------------|---------|
|
||||
| `auth` | Authenticate the connection | `{ "token": "..." }` |
|
||||
| `chat` | Send a message to the agent | `{ "message": "...", "session_id": "..." }` |
|
||||
| `cancel_job` | Cancel a running job | `{ "job_id": "..." }` |
|
||||
| `ping` | Keepalive ping | `{}` |
|
||||
|
||||
**Server → Client:**
|
||||
|
||||
| Type | Description | Payload |
|
||||
|------|-------------|---------|
|
||||
| `auth_ok` | Authentication succeeded | `{ "user_id": "..." }` |
|
||||
| `job_created` | A new job was created | `{ "job_id": "...", "status": "pending" }` |
|
||||
| `job_update` | Job state changed | `{ "job_id": "...", "status": "...", "output": "..." }` |
|
||||
| `job_complete` | Job finished successfully | `{ "job_id": "...", "result": "..." }` |
|
||||
| `job_failed` | Job failed | `{ "job_id": "...", "error": "..." }` |
|
||||
| `tool_call` | A tool is being invoked | `{ "job_id": "...", "tool": "...", "params": {} }` |
|
||||
| `stream_chunk` | Partial LLM response chunk | `{ "job_id": "...", "delta": "..." }` |
|
||||
| `error` | Protocol or server error | `{ "code": "...", "message": "..." }` |
|
||||
| `pong` | Keepalive pong | `{}` |
|
||||
|
||||
---
|
||||
|
||||
### JavaScript Example
|
||||
|
||||
```javascript
|
||||
const TOKEN = 'your-gateway-auth-token';
|
||||
const ws = new WebSocket('ws://localhost:3000/ws');
|
||||
|
||||
ws.onopen = () => {
|
||||
// Step 1: authenticate
|
||||
ws.send(JSON.stringify({ type: 'auth', token: TOKEN }));
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data);
|
||||
|
||||
switch (msg.type) {
|
||||
case 'auth_ok':
|
||||
console.log('Authenticated, user:', msg.user_id);
|
||||
// Step 2: send a chat message
|
||||
ws.send(JSON.stringify({
|
||||
type: 'chat',
|
||||
message: 'What jobs ran today?',
|
||||
session_id: 'default',
|
||||
}));
|
||||
break;
|
||||
|
||||
case 'stream_chunk':
|
||||
console.log(msg.delta); // stream the response
|
||||
break;
|
||||
|
||||
case 'job_complete':
|
||||
console.log('\nDone. Job:', msg.job_id);
|
||||
break;
|
||||
|
||||
case 'job_failed':
|
||||
console.error('Job failed:', msg.error);
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
console.error('Error:', msg.code, msg.message);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = (err) => console.error('WebSocket error:', err);
|
||||
ws.onclose = (event) => console.log('Closed:', event.code, event.reason);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Reconnection with Exponential Backoff
|
||||
|
||||
WebSocket connections can drop due to network interruptions or server restarts. Implement reconnection with exponential backoff to avoid hammering the server:
|
||||
|
||||
```javascript
|
||||
class IronclawSocket {
|
||||
constructor(url, token) {
|
||||
this.url = url;
|
||||
this.token = token;
|
||||
this.ws = null;
|
||||
this.reconnectDelay = 1000; // start at 1 second
|
||||
this.maxDelay = 30000; // cap at 30 seconds
|
||||
this.reconnectTimer = null;
|
||||
this.connect();
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.ws = new WebSocket(this.url);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
this.reconnectDelay = 1000; // reset on successful connect
|
||||
this.ws.send(JSON.stringify({ type: 'auth', token: this.token }));
|
||||
};
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
this.onMessage(JSON.parse(event.data));
|
||||
};
|
||||
|
||||
this.ws.onclose = (event) => {
|
||||
if (!event.wasClean) {
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onerror = () => {
|
||||
this.ws.close();
|
||||
};
|
||||
}
|
||||
|
||||
scheduleReconnect() {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
console.log(`Reconnecting in ${this.reconnectDelay / 1000}s...`);
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxDelay);
|
||||
this.connect();
|
||||
}, this.reconnectDelay);
|
||||
}
|
||||
|
||||
send(msg) {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(JSON.stringify(msg));
|
||||
}
|
||||
}
|
||||
|
||||
onMessage(msg) {
|
||||
// Override in subclass or replace with your handler
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const client = new IronclawSocket('ws://localhost:3000/ws', TOKEN);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Server-Sent Events (SSE)
|
||||
|
||||
SSE provides a unidirectional stream from the server to the client over a standard HTTP connection. It is simpler than WebSocket for read-only use cases like log tailing and event monitoring.
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
GET /api/logs
|
||||
Accept: text/event-stream
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
SSE uses the same bearer token in the `Authorization` header:
|
||||
|
||||
```bash
|
||||
curl -N \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Accept: text/event-stream" \
|
||||
http://localhost:3000/api/logs
|
||||
```
|
||||
|
||||
### SSE Event Types
|
||||
|
||||
Each SSE event has an `event` field indicating its type and a `data` field containing a JSON payload.
|
||||
|
||||
| Event | Description | Data Fields |
|
||||
|-------|-------------|-------------|
|
||||
| `log` | A log line from the agent | `{ "level": "info", "message": "...", "module": "...", "ts": "..." }` |
|
||||
| `job_status` | Job state changed | `{ "job_id": "...", "status": "...", "ts": "..." }` |
|
||||
| `routine_fired` | A routine was triggered and executed | `{ "routine_id": "...", "name": "...", "ts": "..." }` |
|
||||
| `heartbeat` | Proactive heartbeat executed | `{ "findings": true, "summary": "...", "ts": "..." }` |
|
||||
| `tool_call` | A tool was invoked | `{ "job_id": "...", "tool": "...", "ts": "..." }` |
|
||||
| `error` | Server-side error in the stream | `{ "code": "...", "message": "..." }` |
|
||||
|
||||
### Example SSE Stream
|
||||
|
||||
```
|
||||
event: log
|
||||
data: {"level":"info","message":"Job job_01j9abc123 started","module":"ironclaw::agent","ts":"2024-01-15T10:30:00Z"}
|
||||
|
||||
event: tool_call
|
||||
data: {"job_id":"job_01j9abc123","tool":"memory_search","ts":"2024-01-15T10:30:01Z"}
|
||||
|
||||
event: job_status
|
||||
data: {"job_id":"job_01j9abc123","status":"completed","ts":"2024-01-15T10:30:03Z"}
|
||||
|
||||
event: heartbeat
|
||||
data: {"findings":true,"summary":"3 pending items in checklist","ts":"2024-01-15T10:30:00Z"}
|
||||
```
|
||||
|
||||
### JavaScript SSE Example
|
||||
|
||||
```javascript
|
||||
const evtSource = new EventSource(
|
||||
'http://localhost:3000/api/logs',
|
||||
{
|
||||
// EventSource doesn't support custom headers natively in browsers.
|
||||
// Use a token query parameter as an alternative:
|
||||
// 'http://localhost:3000/api/logs?token=...'
|
||||
// Or use fetch with ReadableStream for header support (see below).
|
||||
}
|
||||
);
|
||||
|
||||
evtSource.addEventListener('log', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log(`[${data.level.toUpperCase()}] ${data.message}`);
|
||||
});
|
||||
|
||||
evtSource.addEventListener('job_status', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log(`Job ${data.job_id} → ${data.status}`);
|
||||
});
|
||||
|
||||
evtSource.addEventListener('heartbeat', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.findings) console.warn('Heartbeat findings:', data.summary);
|
||||
});
|
||||
|
||||
evtSource.onerror = () => {
|
||||
console.error('SSE connection lost, browser will auto-reconnect');
|
||||
};
|
||||
```
|
||||
|
||||
### SSE with Fetch (Header Authentication)
|
||||
|
||||
The native `EventSource` API does not support custom headers. Use `fetch` with a `ReadableStream` for full header control:
|
||||
|
||||
```javascript
|
||||
async function streamLogs(token) {
|
||||
const response = await fetch('http://localhost:3000/api/logs', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Accept': 'text/event-stream',
|
||||
},
|
||||
});
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop(); // keep incomplete line
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = JSON.parse(line.slice(6));
|
||||
console.log(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reverse Proxy Configuration for WebSocket
|
||||
|
||||
WebSocket connections require specific proxy headers. Without them, the connection upgrade will fail.
|
||||
|
||||
### nginx
|
||||
|
||||
```nginx
|
||||
location /ws {
|
||||
proxy_pass http://127.0.0.1:3000/ws;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Required for WebSocket upgrade
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
# Keep connections alive
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
keepalive_timeout 86400s;
|
||||
}
|
||||
|
||||
location /api/logs {
|
||||
proxy_pass http://127.0.0.1:3000/api/logs;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Required for SSE
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 86400s;
|
||||
chunked_transfer_encoding on;
|
||||
}
|
||||
```
|
||||
|
||||
### Caddy
|
||||
|
||||
Caddy handles WebSocket and SSE automatically — no special configuration needed. The `reverse_proxy` directive transparently proxies both protocols.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="REST API Reference" icon="code" href="/ops/api">
|
||||
All 40+ REST endpoints including jobs, memory, and routines
|
||||
</Card>
|
||||
<Card title="Logging" icon="file-text" href="/ops/logging">
|
||||
RUST_LOG levels and structured log output
|
||||
</Card>
|
||||
<Card title="VPS Hardening" icon="shield" href="/platforms/vps">
|
||||
nginx and Caddy reverse proxy configuration with TLS
|
||||
</Card>
|
||||
</CardGroup>
|
||||
305
docs/drafts/platforms/docker-compose.mdx
Normal file
@@ -0,0 +1,305 @@
|
||||
---
|
||||
title: Docker Compose
|
||||
sidebarTitle: Docker Compose
|
||||
description: Production Docker Compose deployment with PostgreSQL and volumes
|
||||
---
|
||||
|
||||
This page covers a production Docker Compose setup for IronClaw with PostgreSQL, named volumes, and health checks. Use this when you want a fully containerized, self-contained deployment that is easy to back up and migrate.
|
||||
|
||||
<Note>
|
||||
This is for running IronClaw itself inside Docker Compose alongside PostgreSQL. This is separate from IronClaw's Docker sandbox feature, which launches containers for job isolation. Both can coexist — see the Docker-in-Docker section below.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## docker-compose.yml
|
||||
|
||||
Save this as `docker-compose.yml` in your deployment directory (e.g., `/opt/ironclaw/`):
|
||||
|
||||
```yaml
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
ironclaw:
|
||||
image: nearai/ironclaw:latest
|
||||
container_name: ironclaw
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
# Persistent IronClaw data (skills, installed extensions, workspace files)
|
||||
- ironclaw_data:/home/ironclaw/.ironclaw
|
||||
# OPTIONAL: Docker socket for sandbox job execution (Docker-in-Docker sibling containers).
|
||||
# Enabling this grants the container control over the host Docker daemon; uncomment only if required.
|
||||
# - /var/run/docker.sock:/var/run/docker.sock
|
||||
ports:
|
||||
# Web Gateway — bind to localhost only, expose via reverse proxy
|
||||
- "127.0.0.1:3000:3000"
|
||||
# HTTP Webhook channel
|
||||
- "127.0.0.1:8080:8080"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-sf", "http://localhost:3000/api/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- ironclaw_net
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "5"
|
||||
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg16
|
||||
container_name: ironclaw-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ironclaw
|
||||
POSTGRES_USER: ironclaw
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
expose:
|
||||
# Only expose to internal network — never bind to host
|
||||
- "5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ironclaw -d ironclaw"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
networks:
|
||||
- ironclaw_net
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
|
||||
volumes:
|
||||
ironclaw_data:
|
||||
driver: local
|
||||
postgres_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
ironclaw_net:
|
||||
driver: bridge
|
||||
internal: false # Set to true if you want to block all external network access from containers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## .env File
|
||||
|
||||
Create `.env` in the same directory as `docker-compose.yml`:
|
||||
|
||||
```bash
|
||||
# ─── Database ─────────────────────────────────────────────────────────────────
|
||||
DATABASE_BACKEND=postgres
|
||||
DATABASE_URL=postgres://ironclaw:change_this_to_a_strong_password@postgres:5432/ironclaw
|
||||
POSTGRES_PASSWORD=change_this_to_a_strong_password # Must match password in DATABASE_URL
|
||||
|
||||
# ─── LLM Provider ─────────────────────────────────────────────────────────────
|
||||
LLM_BACKEND=nearai
|
||||
NEARAI_SESSION_TOKEN=sess_xxx
|
||||
NEARAI_MODEL=claude-3-5-sonnet-20241022
|
||||
# Or use Anthropic directly:
|
||||
# LLM_BACKEND=anthropic
|
||||
# ANTHROPIC_API_KEY=sk-ant-xxx
|
||||
|
||||
# ─── Web Gateway ──────────────────────────────────────────────────────────────
|
||||
GATEWAY_ENABLED=true
|
||||
GATEWAY_HOST=0.0.0.0
|
||||
GATEWAY_PORT=3000
|
||||
GATEWAY_AUTH_TOKEN=change_this_to_a_random_64_char_secret
|
||||
|
||||
# ─── HTTP Webhook ─────────────────────────────────────────────────────────────
|
||||
HTTP_ENABLED=false
|
||||
HTTP_PORT=8080
|
||||
HTTP_WEBHOOK_SECRET=change_this_too
|
||||
|
||||
# ─── Embeddings ───────────────────────────────────────────────────────────────
|
||||
EMBEDDING_ENABLED=true
|
||||
OPENAI_API_KEY=sk-xxx
|
||||
EMBEDDING_MODEL=text-embedding-3-small
|
||||
|
||||
# ─── Docker Sandbox ───────────────────────────────────────────────────────────
|
||||
SANDBOX_ENABLED=true
|
||||
SANDBOX_IMAGE=ironclaw-worker:latest
|
||||
SANDBOX_MEMORY_LIMIT_MB=512
|
||||
SANDBOX_TIMEOUT_SECS=1800
|
||||
SANDBOX_CPU_LIMIT=1.0
|
||||
SANDBOX_NETWORK_PROXY=true
|
||||
SANDBOX_PROXY_PORT=8081
|
||||
SANDBOX_DEFAULT_POLICY=workspace_write
|
||||
|
||||
# ─── Skills ───────────────────────────────────────────────────────────────────
|
||||
SKILLS_ENABLED=true
|
||||
SKILLS_CATALOG_URL=https://clawhub.dev
|
||||
|
||||
# ─── Routines ─────────────────────────────────────────────────────────────────
|
||||
ROUTINES_ENABLED=true
|
||||
ROUTINES_CRON_INTERVAL=60
|
||||
|
||||
# ─── Heartbeat ────────────────────────────────────────────────────────────────
|
||||
HEARTBEAT_ENABLED=true
|
||||
HEARTBEAT_INTERVAL_SECS=1800
|
||||
HEARTBEAT_NOTIFY_CHANNEL=web
|
||||
|
||||
# ─── Logging ──────────────────────────────────────────────────────────────────
|
||||
RUST_LOG=ironclaw=info,tower_http=warn
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Never commit `.env` to version control. Add `.env` to your `.gitignore`. Rotate `GATEWAY_AUTH_TOKEN` and `POSTGRES_PASSWORD` after deployment.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Deploy
|
||||
|
||||
```bash
|
||||
# Start all services
|
||||
docker compose up -d
|
||||
|
||||
# Check status
|
||||
docker compose ps
|
||||
|
||||
# Expected output:
|
||||
# NAME STATUS PORTS
|
||||
# ironclaw running 127.0.0.1:3000->3000/tcp
|
||||
# ironclaw-postgres running (healthy)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose logs -f
|
||||
|
||||
# IronClaw only
|
||||
docker compose logs -f ironclaw
|
||||
|
||||
# Postgres only
|
||||
docker compose logs -f postgres
|
||||
|
||||
# Last 100 lines
|
||||
docker compose logs --tail=100 ironclaw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updates
|
||||
|
||||
```bash
|
||||
# Pull latest images
|
||||
docker compose pull
|
||||
|
||||
# Recreate containers with new images (zero-downtime for postgres; brief downtime for ironclaw)
|
||||
docker compose up -d --no-deps ironclaw
|
||||
|
||||
# Or restart everything
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backups
|
||||
|
||||
### PostgreSQL Database Backup
|
||||
|
||||
```bash
|
||||
# Dump to file
|
||||
docker compose exec postgres pg_dump \
|
||||
-U ironclaw \
|
||||
-d ironclaw \
|
||||
--format=custom \
|
||||
--compress=9 \
|
||||
> backup-$(date +%Y%m%d-%H%M%S).dump
|
||||
|
||||
# Restore from dump
|
||||
docker compose exec -T postgres pg_restore \
|
||||
-U ironclaw \
|
||||
-d ironclaw \
|
||||
--clean \
|
||||
--if-exists \
|
||||
< backup-20240101-120000.dump
|
||||
```
|
||||
|
||||
### Volume Backup
|
||||
|
||||
```bash
|
||||
# Discover the ironclaw_data volume name (project prefix may vary)
|
||||
# This picks the first volume whose name contains "ironclaw_data"
|
||||
|
||||
VOLUME_NAME=$(docker volume ls -q --filter name='ironclaw_data' | head -n 1)
|
||||
|
||||
# Back up ironclaw_data volume (workspace, skills, config)
|
||||
docker run --rm \
|
||||
-v "${VOLUME_NAME}":/source:ro \
|
||||
-v "$(pwd)"/backups:/dest \
|
||||
alpine tar czf /dest/ironclaw-data-$(date +%Y%m%d).tar.gz -C /source .
|
||||
|
||||
# Restore
|
||||
docker run --rm \
|
||||
-v "${VOLUME_NAME}":/dest \
|
||||
-v "$(pwd)"/backups:/source:ro \
|
||||
alpine tar xzf /source/ironclaw-data-20240101.tar.gz -C /dest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docker-in-Docker for Sandbox
|
||||
|
||||
IronClaw can launch Docker sandbox containers even when running inside Docker itself. This works by mounting the host Docker socket (`/var/run/docker.sock`). The sandbox containers become siblings on the host, not children of the IronClaw container.
|
||||
|
||||
To enable this, add the following mount to your `docker-compose.yml` (inside the IronClaw service):
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Mounting the Docker socket gives IronClaw the ability to create and manage containers on the host. This is equivalent to root access on the host system. Only mount the socket if you trust the IronClaw process and have configured `SANDBOX_ENABLED=true` intentionally.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Stopping and Removing
|
||||
|
||||
```bash
|
||||
# Stop containers (preserve volumes)
|
||||
docker compose down
|
||||
|
||||
# Stop and remove volumes (destructive — deletes all data)
|
||||
docker compose down -v
|
||||
|
||||
# Remove images
|
||||
docker compose down --rmi all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="VPS Hardening" icon="shield" href="/platforms/vps">
|
||||
Caddy, UFW, fail2ban, and SSH hardening
|
||||
</Card>
|
||||
<Card title="REST API Reference" icon="code" href="/ops/api">
|
||||
Web Gateway endpoint reference
|
||||
</Card>
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Full environment variable reference
|
||||
</Card>
|
||||
</CardGroup>
|
||||
321
docs/drafts/platforms/linux.mdx
Normal file
@@ -0,0 +1,321 @@
|
||||
---
|
||||
title: Linux
|
||||
sidebarTitle: Linux
|
||||
description: Running IronClaw on Linux with systemd, GNOME Keyring, and UFW
|
||||
---
|
||||
|
||||
IronClaw runs natively on Linux with full support for systemd service management, GNOME Keyring for secure key storage, and UFW/fail2ban for host hardening.
|
||||
|
||||
## Installation
|
||||
|
||||
### Shell Script (Recommended)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
|
||||
Installs to `~/.local/bin/ironclaw`. Add to PATH if not already present:
|
||||
|
||||
```bash
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### Package Manager (Ubuntu / Debian)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://repo.ironclaw.ai/gpg | sudo gpg --dearmor -o /usr/share/keyrings/ironclaw.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/ironclaw.gpg] https://repo.ironclaw.ai stable main" \
|
||||
| sudo tee /etc/apt/sources.list.d/ironclaw.list
|
||||
|
||||
sudo apt update && sudo apt install ironclaw
|
||||
```
|
||||
|
||||
### Cargo (Build from Source)
|
||||
|
||||
```bash
|
||||
cargo install ironclaw
|
||||
```
|
||||
|
||||
Requires Rust 1.78+. Install Rust via [rustup.rs](https://rustup.rs).
|
||||
|
||||
---
|
||||
|
||||
## systemd Service
|
||||
|
||||
Run IronClaw as a managed background service that restarts on failure and launches on boot.
|
||||
|
||||
### Unit File
|
||||
|
||||
Create `/etc/systemd/system/ironclaw.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=IronClaw AI Assistant
|
||||
Documentation=https://docs.ironclaw.ai
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=ironclaw
|
||||
Group=ironclaw
|
||||
EnvironmentFile=/etc/ironclaw/ironclaw.env
|
||||
ExecStart=/usr/local/bin/ironclaw run
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
TimeoutStopSec=30s
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=read-only
|
||||
ReadWritePaths=/var/lib/ironclaw /home/ironclaw/.ironclaw
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=ironclaw
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### Environment File
|
||||
|
||||
Create `/etc/ironclaw/ironclaw.env` (mode 640, owned by root:ironclaw):
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/ironclaw
|
||||
sudo touch /etc/ironclaw/ironclaw.env
|
||||
sudo chmod 640 /etc/ironclaw/ironclaw.env
|
||||
sudo chown root:ironclaw /etc/ironclaw/ironclaw.env
|
||||
```
|
||||
|
||||
Example contents:
|
||||
|
||||
```bash
|
||||
DATABASE_BACKEND=libsql
|
||||
LLM_BACKEND=nearai
|
||||
NEARAI_SESSION_TOKEN=sess_xxx
|
||||
GATEWAY_ENABLED=true
|
||||
GATEWAY_HOST=127.0.0.1
|
||||
GATEWAY_PORT=3000
|
||||
GATEWAY_AUTH_TOKEN=change_this_to_a_random_secret
|
||||
RUST_LOG=ironclaw=info
|
||||
```
|
||||
|
||||
### Enable and Start
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now ironclaw
|
||||
|
||||
# Verify
|
||||
sudo systemctl status ironclaw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GNOME Keyring Integration
|
||||
|
||||
IronClaw uses the system keyring to store the encryption master key for secrets. On GNOME-based desktops, this is GNOME Keyring.
|
||||
|
||||
### Install libsecret
|
||||
|
||||
```bash
|
||||
# Ubuntu / Debian
|
||||
sudo apt install gnome-keyring libsecret-tools
|
||||
|
||||
# Fedora
|
||||
sudo dnf install gnome-keyring libsecret
|
||||
|
||||
# Arch
|
||||
sudo pacman -S gnome-keyring libsecret
|
||||
```
|
||||
|
||||
### Store the Master Key Manually (optional)
|
||||
|
||||
IronClaw handles this automatically on first run, but you can pre-seed the key:
|
||||
|
||||
```bash
|
||||
secret-tool store \
|
||||
--label="IronClaw Master Key" \
|
||||
application ironclaw \
|
||||
key master
|
||||
```
|
||||
|
||||
Retrieve it later:
|
||||
|
||||
```bash
|
||||
secret-tool lookup application ironclaw key master
|
||||
```
|
||||
|
||||
### Headless / Server Environments
|
||||
|
||||
GNOME Keyring requires a D-Bus session. On headless servers, use the environment variable fallback instead:
|
||||
|
||||
```bash
|
||||
IRONCLAW_MASTER_KEY=<base64-encoded-32-byte-key>
|
||||
```
|
||||
|
||||
Generate a secure key:
|
||||
|
||||
```bash
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The environment variable approach exposes the key in process listings. Use the keyring on desktop systems. On servers, prefer a secrets manager (Vault, AWS Secrets Manager) and inject at startup.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## UFW Firewall Rules
|
||||
|
||||
Restrict access to the Web Gateway so only local processes can reach it.
|
||||
|
||||
```bash
|
||||
# Allow SSH (do this first to avoid locking yourself out)
|
||||
sudo ufw allow 22/tcp
|
||||
|
||||
# Block port 3000 from external access
|
||||
sudo ufw deny in on eth0 to any port 3000
|
||||
|
||||
# If you need access from a specific trusted IP only
|
||||
# sudo ufw allow from 192.168.1.100 to any port 3000
|
||||
|
||||
# Enable UFW
|
||||
sudo ufw enable
|
||||
sudo ufw status verbose
|
||||
```
|
||||
|
||||
<Note>
|
||||
If you expose IronClaw via a reverse proxy (Caddy, nginx), the proxy listens on 443 and forwards to 127.0.0.1:3000 internally. Port 3000 never needs to be public-facing. See [VPS Hardening](/platforms/vps) for the full reverse proxy setup.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## fail2ban Configuration
|
||||
|
||||
Protect against repeated authentication failures against the Web Gateway.
|
||||
|
||||
Create `/etc/fail2ban/filter.d/ironclaw.conf`:
|
||||
|
||||
```ini
|
||||
[Definition]
|
||||
failregex = ^.*401 Unauthorized.*from <HOST>.*$
|
||||
^.*auth.*failed.*<HOST>.*$
|
||||
ignoreregex =
|
||||
```
|
||||
|
||||
Create `/etc/fail2ban/jail.d/ironclaw.conf`:
|
||||
|
||||
```ini
|
||||
[ironclaw]
|
||||
enabled = true
|
||||
port = 3000
|
||||
filter = ironclaw
|
||||
logpath = /var/log/ironclaw/access.log
|
||||
maxretry = 5
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
action = ufw
|
||||
```
|
||||
|
||||
Reload fail2ban:
|
||||
|
||||
```bash
|
||||
sudo systemctl reload fail2ban
|
||||
sudo fail2ban-client status ironclaw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Viewing Logs
|
||||
|
||||
```bash
|
||||
# Follow live logs
|
||||
journalctl -u ironclaw -f
|
||||
|
||||
# Last 100 lines
|
||||
journalctl -u ironclaw -n 100
|
||||
|
||||
# Logs from the past hour
|
||||
journalctl -u ironclaw --since "1 hour ago"
|
||||
|
||||
# With debug output (set RUST_LOG=ironclaw=debug in env file first)
|
||||
journalctl -u ironclaw -f --output=short-precise
|
||||
|
||||
# Export to file
|
||||
journalctl -u ironclaw --since today > ironclaw-today.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AppArmor Profile (Optional)
|
||||
|
||||
An AppArmor profile can constrain IronClaw's filesystem and network access at the kernel level.
|
||||
|
||||
Create `/etc/apparmor.d/usr.local.bin.ironclaw`:
|
||||
|
||||
```
|
||||
#include <tunables/global>
|
||||
|
||||
/usr/local/bin/ironclaw {
|
||||
#include <abstractions/base>
|
||||
#include <abstractions/nameservice>
|
||||
|
||||
# Binary
|
||||
/usr/local/bin/ironclaw mr,
|
||||
|
||||
# Config and data
|
||||
/home/ironclaw/.ironclaw/** rw,
|
||||
/var/lib/ironclaw/** rw,
|
||||
/etc/ironclaw/ironclaw.env r,
|
||||
|
||||
# Keyring
|
||||
/run/user/*/keyring/** rw,
|
||||
|
||||
# Docker socket (for sandbox)
|
||||
/var/run/docker.sock rw,
|
||||
|
||||
# Network
|
||||
network tcp,
|
||||
network udp,
|
||||
|
||||
# Deny everything else
|
||||
deny /etc/shadow r,
|
||||
deny /root/** rw,
|
||||
}
|
||||
```
|
||||
|
||||
Load the profile:
|
||||
|
||||
```bash
|
||||
sudo apparmor_parser -r /etc/apparmor.d/usr.local.bin.ironclaw
|
||||
sudo aa-status | grep ironclaw
|
||||
```
|
||||
|
||||
<Note>
|
||||
The AppArmor profile is optional. IronClaw's own sandbox (Docker containers with dropped capabilities) provides defense-in-depth regardless of whether AppArmor is configured.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="VPS Hardening" icon="shield" href="/platforms/vps">
|
||||
UFW, Caddy, fail2ban, and SSH hardening for public-facing deployments
|
||||
</Card>
|
||||
<Card title="Docker Compose" icon="layers" href="/platforms/docker-compose">
|
||||
Production deployment with PostgreSQL and named volumes
|
||||
</Card>
|
||||
<Card title="Logging" icon="file-text" href="/ops/logging">
|
||||
RUST_LOG levels, journalctl, and cost tracking
|
||||
</Card>
|
||||
</CardGroup>
|
||||
279
docs/drafts/platforms/macos.mdx
Normal file
@@ -0,0 +1,279 @@
|
||||
---
|
||||
title: macOS
|
||||
sidebarTitle: macOS
|
||||
description: Running IronClaw on macOS with Homebrew, Keychain, and launchd
|
||||
---
|
||||
|
||||
IronClaw supports macOS natively with Homebrew installation, macOS Keychain for secure key storage, and launchd for background service management.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Homebrew (Recommended)
|
||||
|
||||
```bash
|
||||
# Add the IronClaw tap
|
||||
brew tap ironclaw-ai/tap
|
||||
|
||||
# Install IronClaw
|
||||
brew install ironclaw
|
||||
```
|
||||
|
||||
### Shell Script
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
|
||||
Installs to `~/.local/bin/ironclaw`. Add to PATH:
|
||||
|
||||
```bash
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
### Cargo (Build from Source)
|
||||
|
||||
```bash
|
||||
# Install Rust if needed
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
|
||||
# Build and install
|
||||
cargo install ironclaw
|
||||
```
|
||||
|
||||
### Verify Installation
|
||||
|
||||
```bash
|
||||
ironclaw --version
|
||||
ironclaw doctor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## First Launch: Gatekeeper
|
||||
|
||||
macOS Gatekeeper may block the binary on first launch if it was downloaded directly rather than installed through Homebrew.
|
||||
|
||||
**To allow it:**
|
||||
|
||||
1. Right-click `ironclaw` in Finder and choose **Open**
|
||||
2. Click **Open** in the security dialog
|
||||
|
||||
Or via the terminal:
|
||||
|
||||
```bash
|
||||
xattr -d com.apple.quarantine ~/.local/bin/ironclaw
|
||||
```
|
||||
|
||||
<Note>
|
||||
Binaries installed via `brew install ironclaw` are automatically notarized and will not trigger the Gatekeeper warning.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## macOS Keychain Integration
|
||||
|
||||
IronClaw stores its encryption master key in the macOS Keychain. This keeps the key off disk and protected by your login password and Touch ID.
|
||||
|
||||
### First Run Dialogs
|
||||
|
||||
On first run you will see two system dialogs:
|
||||
|
||||
1. **"Enter your password to unlock the login keychain"** — Unlock the keychain to read/write items. Enter your macOS login password.
|
||||
2. **"ironclaw wants to use your confidential information stored in 'IronClaw Master Key' in your keychain"** — Click **Always Allow** to prevent repeated prompts on future launches.
|
||||
|
||||
<Warning>
|
||||
Clicking **Allow** instead of **Always Allow** causes this dialog to appear on every launch. Choose **Always Allow** the first time to avoid repeated interruptions.
|
||||
</Warning>
|
||||
|
||||
### Managing the Keychain Entry
|
||||
|
||||
View the entry in Keychain Access (open via Spotlight: `keychain access`):
|
||||
- Category: **Passwords**
|
||||
- Name: `IronClaw Master Key`
|
||||
- Account: `ironclaw`
|
||||
|
||||
To delete and regenerate the master key (this invalidates all stored secrets):
|
||||
|
||||
```bash
|
||||
security delete-generic-password -a ironclaw -s "IronClaw Master Key"
|
||||
ironclaw onboard # Re-run wizard to generate a new key
|
||||
```
|
||||
|
||||
### Headless / CI Environments
|
||||
|
||||
For non-interactive macOS environments (CI, build machines), use the environment variable fallback:
|
||||
|
||||
```bash
|
||||
export IRONCLAW_MASTER_KEY=$(openssl rand -base64 32)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## launchd Service
|
||||
|
||||
launchd is the macOS equivalent of systemd. It manages background services and can restart IronClaw automatically on failure or system reboot.
|
||||
|
||||
### User-Level Service (Recommended)
|
||||
|
||||
Create `~/Library/LaunchAgents/ai.ironclaw.plist`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>ai.ironclaw</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/ironclaw</string>
|
||||
<string>run</string>
|
||||
</array>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>DATABASE_BACKEND</key>
|
||||
<string>libsql</string>
|
||||
<key>LLM_BACKEND</key>
|
||||
<string>nearai</string>
|
||||
<key>GATEWAY_ENABLED</key>
|
||||
<string>true</string>
|
||||
<key>GATEWAY_HOST</key>
|
||||
<string>127.0.0.1</string>
|
||||
<key>GATEWAY_PORT</key>
|
||||
<string>3000</string>
|
||||
<key>RUST_LOG</key>
|
||||
<string>ironclaw=info</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<dict>
|
||||
<key>Crashed</key>
|
||||
<true/>
|
||||
<key>SuccessfulExit</key>
|
||||
<false/>
|
||||
</dict>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/ironclaw.stdout.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/ironclaw.stderr.log</string>
|
||||
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/Users/YOUR_USERNAME</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
Replace `YOUR_USERNAME` with your actual macOS username (`whoami`).
|
||||
|
||||
<Note>
|
||||
Sensitive values (API keys, auth tokens) should not be placed in the plist directly since it is a plain text file. Store them in the Keychain and have IronClaw read them at startup, or use `launchctl setenv` to inject them at runtime.
|
||||
</Note>
|
||||
|
||||
### Load and Manage the Service
|
||||
|
||||
```bash
|
||||
# Load the service (starts immediately due to RunAtLoad)
|
||||
launchctl load ~/Library/LaunchAgents/ai.ironclaw.plist
|
||||
|
||||
# Unload (stop and disable)
|
||||
launchctl unload ~/Library/LaunchAgents/ai.ironclaw.plist
|
||||
|
||||
# Reload after editing the plist
|
||||
launchctl unload ~/Library/LaunchAgents/ai.ironclaw.plist
|
||||
launchctl load ~/Library/LaunchAgents/ai.ironclaw.plist
|
||||
|
||||
# Check status
|
||||
launchctl list | grep ironclaw
|
||||
|
||||
# Start / stop manually
|
||||
launchctl start ai.ironclaw
|
||||
launchctl stop ai.ironclaw
|
||||
```
|
||||
|
||||
### Homebrew Services (Alternative)
|
||||
|
||||
If installed via Homebrew:
|
||||
|
||||
```bash
|
||||
# Start now and on login
|
||||
brew services start ironclaw
|
||||
|
||||
# Stop
|
||||
brew services stop ironclaw
|
||||
|
||||
# Restart
|
||||
brew services restart ironclaw
|
||||
|
||||
# View status
|
||||
brew services list | grep ironclaw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docker Desktop for macOS
|
||||
|
||||
The Docker sandbox requires Docker. On macOS, use [Docker Desktop](https://www.docker.com/products/docker-desktop/).
|
||||
|
||||
### Install Docker Desktop
|
||||
|
||||
1. Download from [docker.com/products/docker-desktop](https://www.docker.com/products/docker-desktop/)
|
||||
2. Drag to Applications and open
|
||||
3. Complete the setup wizard
|
||||
4. Verify: `docker run --rm hello-world`
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Docker Desktop runs inside a Linux VM on macOS. Configure the VM resource allocation in **Docker Desktop → Settings → Resources**:
|
||||
|
||||
| Setting | Minimum | Recommended |
|
||||
|---------|---------|-------------|
|
||||
| CPUs | 2 | 4 |
|
||||
| Memory | 4 GB | 8 GB |
|
||||
| Disk | 20 GB | 40 GB |
|
||||
|
||||
<Note>
|
||||
Docker on macOS has more overhead than native Linux due to the VM layer. Sandbox container startup is typically 1-3 seconds slower than on Linux. This is expected behavior.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Viewing Logs
|
||||
|
||||
```bash
|
||||
# Stream logs in real time (unified log)
|
||||
log stream --predicate 'process == "ironclaw"' --level info
|
||||
|
||||
# Show recent messages
|
||||
log show --predicate 'process == "ironclaw"' --last 1h
|
||||
|
||||
# View launchd stdout/stderr files
|
||||
tail -f /tmp/ironclaw.stdout.log
|
||||
tail -f /tmp/ironclaw.stderr.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="VPS Hardening" icon="shield" href="/platforms/vps">
|
||||
Securing IronClaw on a public-facing server
|
||||
</Card>
|
||||
<Card title="Docker Compose" icon="layers" href="/platforms/docker-compose">
|
||||
Production Docker Compose with PostgreSQL
|
||||
</Card>
|
||||
<Card title="Logging" icon="file-text" href="/ops/logging">
|
||||
RUST_LOG levels and log streaming
|
||||
</Card>
|
||||
</CardGroup>
|
||||
328
docs/drafts/platforms/raspberry-pi.mdx
Normal file
@@ -0,0 +1,328 @@
|
||||
---
|
||||
title: Raspberry Pi
|
||||
sidebarTitle: Raspberry Pi
|
||||
description: Running IronClaw on Raspberry Pi with ARM64 and local inference
|
||||
---
|
||||
|
||||
IronClaw runs well on Raspberry Pi 4 and Pi 5 with a 64-bit OS. Pair it with Ollama for fully local inference — no cloud dependency, no API keys required.
|
||||
|
||||
<Note>
|
||||
Raspberry Pi 4 (4 GB RAM) and Pi 5 (4/8 GB) are the recommended hardware. Pi 3 and earlier models lack sufficient memory for comfortable operation. A 64-bit OS (Raspberry Pi OS 64-bit or Ubuntu 22.04 ARM64) is required.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
| Component | Minimum | Recommended |
|
||||
|-----------|---------|-------------|
|
||||
| Model | Raspberry Pi 4 (4 GB) | Raspberry Pi 5 (8 GB) |
|
||||
| OS | Raspberry Pi OS 64-bit | Ubuntu 22.04 LTS ARM64 |
|
||||
| Storage | 16 GB microSD | 32 GB+ microSD or USB SSD |
|
||||
| RAM | 4 GB | 8 GB |
|
||||
| Swap | 2 GB | 4 GB |
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Shell Script
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
|
||||
This downloads the ARM64 binary automatically. Add to PATH:
|
||||
|
||||
```bash
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### Verify the Architecture
|
||||
|
||||
```bash
|
||||
uname -m # Should print: aarch64
|
||||
ironclaw --version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recommended Configuration
|
||||
|
||||
The libSQL backend is strongly recommended for Raspberry Pi — it requires no separate database server and runs embedded in the IronClaw process.
|
||||
|
||||
Create `~/.ironclaw/.env`:
|
||||
|
||||
```bash
|
||||
# Database: embedded SQLite (no server needed)
|
||||
DATABASE_BACKEND=libsql
|
||||
LIBSQL_PATH=~/.ironclaw/ironclaw.db
|
||||
|
||||
# LLM: local Ollama inference
|
||||
LLM_BACKEND=ollama
|
||||
OLLAMA_BASE_URL=http://127.0.0.1:11434
|
||||
OLLAMA_MODEL=llama3.2:3b
|
||||
|
||||
# Web Gateway
|
||||
GATEWAY_ENABLED=true
|
||||
GATEWAY_HOST=127.0.0.1
|
||||
GATEWAY_PORT=3000
|
||||
GATEWAY_AUTH_TOKEN=change_this_to_a_random_secret
|
||||
|
||||
# Embeddings: disable if RAM-constrained
|
||||
EMBEDDING_ENABLED=false
|
||||
|
||||
# Docker sandbox: optional, disable to save resources
|
||||
SANDBOX_ENABLED=false
|
||||
|
||||
# Routines
|
||||
ROUTINES_ENABLED=true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ollama for Local Inference
|
||||
|
||||
Ollama runs language models entirely on the Pi. No data leaves the device.
|
||||
|
||||
### Install Ollama
|
||||
|
||||
```bash
|
||||
curl -fsSL https://ollama.ai/install.sh | sh
|
||||
```
|
||||
|
||||
### Pull a Model
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="4 GB RAM (Pi 4 / Pi 5 4 GB)" icon="cpu">
|
||||
Use a 3B parameter model that fits comfortably:
|
||||
|
||||
```bash
|
||||
ollama pull llama3.2:3b
|
||||
```
|
||||
|
||||
This model uses ~2 GB RAM and leaves headroom for the OS and IronClaw.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="8 GB RAM (Pi 5 8 GB)" icon="cpu">
|
||||
You can run a larger model with better quality:
|
||||
|
||||
```bash
|
||||
# 7B quantized — good quality, fits in 8 GB
|
||||
ollama pull llama3.1:8b-instruct-q4_K_M
|
||||
|
||||
# Or Mistral 7B
|
||||
ollama pull mistral:7b-instruct-q4_K_M
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Verify Ollama is Running
|
||||
|
||||
```bash
|
||||
# Start Ollama service
|
||||
sudo systemctl enable --now ollama
|
||||
|
||||
# Test a completion
|
||||
curl http://127.0.0.1:11434/api/chat -d '{
|
||||
"model": "llama3.2:3b",
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"stream": false
|
||||
}'
|
||||
```
|
||||
|
||||
### IronClaw Configuration for Ollama
|
||||
|
||||
```bash
|
||||
LLM_BACKEND=ollama
|
||||
OLLAMA_BASE_URL=http://127.0.0.1:11434
|
||||
OLLAMA_MODEL=llama3.2:3b
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory and Swap
|
||||
|
||||
The Pi's limited RAM makes swap configuration important.
|
||||
|
||||
### Check Current Swap
|
||||
|
||||
```bash
|
||||
free -h
|
||||
swapon --show
|
||||
```
|
||||
|
||||
### Increase Swap to 2 GB
|
||||
|
||||
```bash
|
||||
# Disable current swap
|
||||
sudo dphys-swapfile swapoff
|
||||
|
||||
# Edit swap config
|
||||
sudo nano /etc/dphys-swapfile
|
||||
# Set: CONF_SWAPSIZE=2048
|
||||
|
||||
# Re-enable
|
||||
sudo dphys-swapfile setup
|
||||
sudo dphys-swapfile swapon
|
||||
|
||||
# Verify
|
||||
free -h
|
||||
```
|
||||
|
||||
### Use a USB SSD for Swap (Better Performance)
|
||||
|
||||
If you have a USB SSD attached:
|
||||
|
||||
```bash
|
||||
sudo mkswap /dev/sda1
|
||||
sudo swapon /dev/sda1
|
||||
|
||||
# Make permanent
|
||||
echo '/dev/sda1 none swap sw 0 0' | sudo tee -a /etc/fstab
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Avoid heavy swap usage on microSD cards — the write cycles degrade cards quickly. If you rely on swap, route it to a USB SSD.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## systemd Service
|
||||
|
||||
Run IronClaw as a background service on the Pi.
|
||||
|
||||
Create `/etc/systemd/system/ironclaw.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=IronClaw AI Assistant
|
||||
After=network-online.target ollama.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=pi
|
||||
EnvironmentFile=/home/pi/.ironclaw/.env
|
||||
ExecStart=/home/pi/.local/bin/ironclaw run
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=ironclaw
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Enable and start:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now ironclaw
|
||||
journalctl -u ironclaw -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docker Sandbox (Optional)
|
||||
|
||||
Docker works on Raspberry Pi but is resource-intensive. For a Pi with 4 GB RAM, disable the sandbox unless you specifically need job isolation.
|
||||
|
||||
### Install Docker on Pi
|
||||
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
### Sandbox Configuration
|
||||
|
||||
```bash
|
||||
# Enable sandbox (requires Docker)
|
||||
SANDBOX_ENABLED=true
|
||||
SANDBOX_IMAGE=ironclaw-worker:latest
|
||||
SANDBOX_MEMORY_LIMIT_MB=256 # Keep low on Pi
|
||||
SANDBOX_TIMEOUT_SECS=300
|
||||
```
|
||||
|
||||
<Note>
|
||||
On a 4 GB Pi, each sandbox container takes ~200-300 MB RAM. With Ollama also running, a single concurrent job is the practical limit. On an 8 GB Pi 5, you can run 2-3 concurrent sandbox jobs.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Disable embeddings if RAM-constrained" icon="database">
|
||||
Semantic memory search uses an embedding model that requires additional RAM and an embedding API call. If you are not using the memory search features, disable it:
|
||||
|
||||
```bash
|
||||
EMBEDDING_ENABLED=false
|
||||
```
|
||||
|
||||
Full-text search (FTS5) still works without embeddings.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Use a microSD A2 card or USB SSD" icon="hard-drive">
|
||||
Database I/O on a slow microSD card significantly affects response times. An A2-rated microSD or USB SSD reduces latency for libSQL writes.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Disable the heartbeat if idle periods are long" icon="activity">
|
||||
The heartbeat runs every 30 minutes by default and triggers an LLM call. On Ollama with a 3B model, this can take 10-30 seconds and consumes RAM. Adjust the interval or disable:
|
||||
|
||||
```bash
|
||||
HEARTBEAT_ENABLED=false
|
||||
# Or slow it down
|
||||
HEARTBEAT_INTERVAL_SECS=7200 # 2 hours
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Reduce max parallel jobs" icon="layers">
|
||||
Lower the concurrency limit to avoid memory pressure:
|
||||
|
||||
```bash
|
||||
MAX_PARALLEL_JOBS=1
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Use quantized models" icon="cpu">
|
||||
Quantized models (Q4_K_M) use 30-50% less RAM than full-precision models with only modest quality loss. Always prefer quantized variants on Pi hardware.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
---
|
||||
|
||||
## Accessing IronClaw from Your Network
|
||||
|
||||
By default IronClaw binds to `127.0.0.1`. To access it from another device on your LAN:
|
||||
|
||||
```bash
|
||||
GATEWAY_HOST=0.0.0.0
|
||||
GATEWAY_PORT=3000
|
||||
```
|
||||
|
||||
Then navigate to `http://<pi-ip-address>:3000` from another machine. Secure with a strong `GATEWAY_AUTH_TOKEN`.
|
||||
|
||||
<Warning>
|
||||
Do not expose port 3000 directly to the internet. If you need remote access, use a VPN (WireGuard, Tailscale) or SSH tunnel instead.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Ollama Provider" icon="cpu" href="/providers/ollama">
|
||||
Full configuration reference for the Ollama LLM provider
|
||||
</Card>
|
||||
<Card title="Linux Platform" icon="terminal" href="/platforms/linux">
|
||||
systemd, GNOME Keyring, and UFW hardening for Linux
|
||||
</Card>
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Full environment variable reference
|
||||
</Card>
|
||||
</CardGroup>
|
||||
385
docs/drafts/platforms/vps.mdx
Normal file
@@ -0,0 +1,385 @@
|
||||
---
|
||||
title: VPS Hardening
|
||||
sidebarTitle: VPS Hardening
|
||||
description: Securing IronClaw on a VPS with UFW, Caddy, and fail2ban
|
||||
---
|
||||
|
||||
Deploying IronClaw on a public VPS requires additional hardening. Never expose the Web Gateway port directly to the internet — route all external traffic through a TLS-terminating reverse proxy and restrict direct port access with a firewall.
|
||||
|
||||
---
|
||||
|
||||
## Create a Dedicated User
|
||||
|
||||
Run IronClaw as a non-root user with Docker access:
|
||||
|
||||
```bash
|
||||
# Create user
|
||||
sudo adduser --disabled-password --gecos "" ironclaw
|
||||
|
||||
# Add to docker group (for sandbox)
|
||||
sudo usermod -aG docker ironclaw
|
||||
|
||||
# Create config directory
|
||||
sudo mkdir -p /etc/ironclaw
|
||||
sudo chown root:ironclaw /etc/ironclaw
|
||||
sudo chmod 750 /etc/ironclaw
|
||||
|
||||
# Create data directory
|
||||
sudo mkdir -p /var/lib/ironclaw
|
||||
sudo chown ironclaw:ironclaw /var/lib/ironclaw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UFW Firewall Rules
|
||||
|
||||
```bash
|
||||
# Reset to defaults (careful: this disables existing rules)
|
||||
# sudo ufw reset
|
||||
|
||||
# Default policies
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
|
||||
# Allow SSH (do this first — never lock yourself out)
|
||||
sudo ufw allow 22/tcp
|
||||
|
||||
# Allow HTTPS (reverse proxy)
|
||||
sudo ufw allow 443/tcp
|
||||
|
||||
# Allow HTTP (for Let's Encrypt / ACME challenge only)
|
||||
sudo ufw allow 80/tcp
|
||||
|
||||
# Block direct access to IronClaw from public internet
|
||||
# Port 3000 (Web Gateway) — internal only
|
||||
sudo ufw deny 3000/tcp
|
||||
|
||||
# Block orchestrator port (internal container API)
|
||||
sudo ufw deny 50051/tcp
|
||||
|
||||
# If you have a known management IP, allow it explicitly:
|
||||
# sudo ufw allow from 203.0.113.10 to any port 3000
|
||||
|
||||
# Enable UFW
|
||||
sudo ufw enable
|
||||
sudo ufw status numbered
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Always run `sudo ufw allow 22/tcp` before enabling UFW. Enabling UFW with the default deny policy and no SSH rule will immediately lock you out of the server.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Caddy Reverse Proxy (Recommended)
|
||||
|
||||
Caddy automatically provisions and renews TLS certificates via Let's Encrypt. No manual certificate management required.
|
||||
|
||||
### Install Caddy
|
||||
|
||||
```bash
|
||||
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
|
||||
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
|
||||
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
sudo apt update && sudo apt install caddy
|
||||
```
|
||||
|
||||
### Caddyfile
|
||||
|
||||
Replace `ironclaw.yourdomain.com` with your actual domain. Edit `/etc/caddy/Caddyfile`:
|
||||
|
||||
```caddy
|
||||
ironclaw.yourdomain.com {
|
||||
# TLS via Let's Encrypt (automatic)
|
||||
# Requires port 80 to be reachable for ACME challenge
|
||||
|
||||
# Security headers
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
-Server
|
||||
}
|
||||
|
||||
# Rate limit (requires caddy-ratelimit plugin, optional)
|
||||
# rate_limit {
|
||||
# zone dynamic_zone {
|
||||
# key {remote_host}
|
||||
# events 60
|
||||
# window 1m
|
||||
# }
|
||||
# }
|
||||
|
||||
# WebSocket and SSE pass-through
|
||||
reverse_proxy localhost:3000 {
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
|
||||
# Keep WebSocket connections alive
|
||||
transport http {
|
||||
keepalive 30s
|
||||
keepalive_idle_conns 10
|
||||
}
|
||||
}
|
||||
|
||||
# Access log
|
||||
log {
|
||||
output file /var/log/caddy/ironclaw-access.log
|
||||
format json
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Apply the configuration:
|
||||
|
||||
```bash
|
||||
sudo systemctl reload caddy
|
||||
# Verify TLS provisioning
|
||||
curl -I https://ironclaw.yourdomain.com/api/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## nginx Alternative
|
||||
|
||||
If you prefer nginx:
|
||||
|
||||
```bash
|
||||
sudo apt install nginx certbot python3-certbot-nginx
|
||||
```
|
||||
|
||||
Create `/etc/nginx/sites-available/ironclaw`:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name ironclaw.yourdomain.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name ironclaw.yourdomain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/ironclaw.yourdomain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/ironclaw.yourdomain.com/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# WebSocket support
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# SSE: disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
access_log /var/log/nginx/ironclaw.access.log;
|
||||
error_log /var/log/nginx/ironclaw.error.log;
|
||||
}
|
||||
```
|
||||
|
||||
Enable and obtain a certificate:
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/ironclaw /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
sudo certbot --nginx -d ironclaw.yourdomain.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cloudflare Tunnel (Zero Open Ports)
|
||||
|
||||
Cloudflare Tunnel routes traffic through Cloudflare's network. No inbound ports need to be open on the VPS — not even 80 or 443.
|
||||
|
||||
```bash
|
||||
# Install cloudflared
|
||||
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
|
||||
| sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg
|
||||
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared bookworm main' \
|
||||
| sudo tee /etc/apt/sources.list.d/cloudflared.list
|
||||
sudo apt update && sudo apt install cloudflared
|
||||
|
||||
# Authenticate (opens browser)
|
||||
cloudflared tunnel login
|
||||
|
||||
# Create tunnel
|
||||
cloudflared tunnel create ironclaw
|
||||
|
||||
# Configure: ~/.cloudflared/config.yml
|
||||
cat > ~/.cloudflared/config.yml <<'EOF'
|
||||
tunnel: <tunnel-id>
|
||||
credentials-file: /home/ironclaw/.cloudflared/<tunnel-id>.json
|
||||
|
||||
ingress:
|
||||
- hostname: ironclaw.yourdomain.com
|
||||
service: http://localhost:3000
|
||||
- service: http_status:404
|
||||
EOF
|
||||
|
||||
# Route DNS
|
||||
cloudflared tunnel route dns ironclaw ironclaw.yourdomain.com
|
||||
|
||||
# Run as service
|
||||
sudo cloudflared service install
|
||||
sudo systemctl enable --now cloudflared
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## fail2ban
|
||||
|
||||
### SSH Protection
|
||||
|
||||
Create `/etc/fail2ban/jail.d/sshd.local`:
|
||||
|
||||
```ini
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = ssh
|
||||
filter = sshd
|
||||
logpath = /var/log/auth.log
|
||||
maxretry = 3
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
```
|
||||
|
||||
### IronClaw Auth Protection
|
||||
|
||||
Create `/etc/fail2ban/filter.d/ironclaw.conf`:
|
||||
|
||||
```ini
|
||||
[Definition]
|
||||
failregex = ^.*"status":401.*"remote_ip":"<HOST>".*$
|
||||
^.*401 Unauthorized.*<HOST>.*$
|
||||
ignoreregex =
|
||||
```
|
||||
|
||||
Create `/etc/fail2ban/jail.d/ironclaw.conf`:
|
||||
|
||||
```ini
|
||||
[ironclaw]
|
||||
enabled = true
|
||||
port = 443,3000
|
||||
filter = ironclaw
|
||||
logpath = /var/log/caddy/ironclaw-access.log
|
||||
/var/log/nginx/ironclaw.access.log
|
||||
maxretry = 10
|
||||
bantime = 1800
|
||||
findtime = 300
|
||||
action = ufw
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart fail2ban
|
||||
sudo fail2ban-client status
|
||||
sudo fail2ban-client status ironclaw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Automatic Security Updates
|
||||
|
||||
```bash
|
||||
sudo apt install unattended-upgrades
|
||||
|
||||
# Configure
|
||||
sudo tee /etc/apt/apt.conf.d/50unattended-upgrades > /dev/null <<'EOF'
|
||||
Unattended-Upgrade::Allowed-Origins {
|
||||
"${distro_id}:${distro_codename}-security";
|
||||
};
|
||||
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
|
||||
Unattended-Upgrade::MinimalSteps "true";
|
||||
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
|
||||
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
|
||||
Unattended-Upgrade::Automatic-Reboot "false";
|
||||
EOF
|
||||
|
||||
# Enable
|
||||
sudo dpkg-reconfigure --priority=low unattended-upgrades
|
||||
sudo systemctl enable --now unattended-upgrades
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SSH Hardening
|
||||
|
||||
Edit `/etc/ssh/sshd_config`:
|
||||
|
||||
```
|
||||
# Disable password authentication
|
||||
PasswordAuthentication no
|
||||
ChallengeResponseAuthentication no
|
||||
UsePAM no
|
||||
|
||||
# Enable public key only
|
||||
PubkeyAuthentication yes
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
|
||||
# Restrict login
|
||||
PermitRootLogin no
|
||||
AllowUsers ironclaw youruser
|
||||
|
||||
# Connection limits
|
||||
MaxAuthTries 3
|
||||
LoginGraceTime 30
|
||||
ClientAliveInterval 300
|
||||
ClientAliveCountMax 2
|
||||
|
||||
# Restrict algorithms (optional, modern clients support these)
|
||||
KexAlgorithms curve25519-sha256,ecdh-sha2-nistp256
|
||||
Ciphers aes256-gcm@openssh.com,chacha20-poly1305@openssh.com
|
||||
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```bash
|
||||
sudo sshd -t # Test config before reloading
|
||||
sudo systemctl reload sshd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Docker Compose" icon="layers" href="/platforms/docker-compose">
|
||||
Production Docker Compose with PostgreSQL and volume backups
|
||||
</Card>
|
||||
<Card title="REST API Reference" icon="code" href="/ops/api">
|
||||
All 40+ Web Gateway API endpoints
|
||||
</Card>
|
||||
<Card title="Logging" icon="file-text" href="/ops/logging">
|
||||
Log levels, journalctl, and cost tracking
|
||||
</Card>
|
||||
</CardGroup>
|
||||
232
docs/drafts/platforms/windows-native.mdx
Normal file
@@ -0,0 +1,232 @@
|
||||
---
|
||||
title: Windows (Native)
|
||||
sidebarTitle: Windows Native
|
||||
description: Running IronClaw natively on Windows (experimental)
|
||||
---
|
||||
|
||||
IronClaw can run natively on Windows without WSL2. This is useful when you need a pure Windows deployment or cannot use WSL2. Native Windows support is experimental — for most users, [WSL2 is recommended](/platforms/windows-wsl2).
|
||||
|
||||
<Warning>
|
||||
Native Windows support is experimental. Some features behave differently compared to Linux and WSL2, particularly shell tool execution and Docker sandbox integration. Production deployments should use Linux or WSL2.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### PowerShell Script
|
||||
|
||||
Open PowerShell as your user (not necessarily Administrator) and run:
|
||||
|
||||
```powershell
|
||||
irm https://install.ironclaw.ai/windows | iex
|
||||
```
|
||||
|
||||
This downloads the `ironclaw-x86_64-pc-windows-msvc.exe` binary and installs it to `%USERPROFILE%\.local\bin\ironclaw.exe`.
|
||||
|
||||
### Manual Install
|
||||
|
||||
1. Download `ironclaw-x86_64-pc-windows-msvc.zip` from [github.com/ironclaw-ai/ironclaw/releases](https://github.com/nearai/ironclaw/releases)
|
||||
2. Extract to `C:\Program Files\IronClaw\`
|
||||
3. Add to PATH (see below)
|
||||
|
||||
### Add to PATH
|
||||
|
||||
```powershell
|
||||
# Add to user PATH (persistent)
|
||||
[Environment]::SetEnvironmentVariable(
|
||||
"Path",
|
||||
$env:Path + ";C:\Program Files\IronClaw",
|
||||
"User"
|
||||
)
|
||||
|
||||
# Reload PATH in current session
|
||||
$env:Path = [Environment]::GetEnvironmentVariable("Path", "User")
|
||||
|
||||
# Verify
|
||||
ironclaw --version
|
||||
```
|
||||
|
||||
### Build from Source
|
||||
|
||||
Requires [Rust](https://rustup.rs) and [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/):
|
||||
|
||||
```powershell
|
||||
# Install dependencies
|
||||
winget install Rustlang.Rust.MSVC
|
||||
winget install Microsoft.VisualStudio.2022.BuildTools
|
||||
|
||||
# Build
|
||||
git clone https://github.com/nearai/ironclaw.git
|
||||
cd ironclaw
|
||||
cargo build --release
|
||||
|
||||
# Install
|
||||
copy target\release\ironclaw.exe C:\Program Files\IronClaw\ironclaw.exe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## First Run
|
||||
|
||||
```powershell
|
||||
ironclaw onboard
|
||||
ironclaw run
|
||||
```
|
||||
|
||||
Navigate to `http://127.0.0.1:3000` in your browser.
|
||||
|
||||
---
|
||||
|
||||
## Secrets: Windows DPAPI
|
||||
|
||||
On Windows, IronClaw stores the encryption master key using the **Windows Data Protection API (DPAPI)**. DPAPI ties the key to your Windows user account and machine — no additional setup is required.
|
||||
|
||||
The key is stored in the Windows Credential Manager under the name `IronClaw Master Key`. You can inspect it via:
|
||||
|
||||
1. Open **Credential Manager** (search in Start menu)
|
||||
2. Click **Windows Credentials**
|
||||
3. Look for `IronClaw Master Key` under **Generic Credentials**
|
||||
|
||||
<Note>
|
||||
DPAPI keys are tied to the current Windows user and machine. If you migrate your IronClaw data to a different machine or reinstall Windows, your encrypted secrets will be unreadable without re-entering them. Export secrets before migrating.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Task Scheduler Autostart
|
||||
|
||||
Use Windows Task Scheduler to run IronClaw at login without a console window.
|
||||
|
||||
### Using schtasks (Command Line)
|
||||
|
||||
```powershell
|
||||
# Create task (runs at login, hidden)
|
||||
schtasks /create `
|
||||
/tn "IronClaw" `
|
||||
/tr "\"C:\Program Files\IronClaw\ironclaw.exe\" run" `
|
||||
/sc ONLOGON `
|
||||
/ru "%USERNAME%" `
|
||||
/f
|
||||
|
||||
# Start immediately
|
||||
schtasks /run /tn "IronClaw"
|
||||
|
||||
# Stop
|
||||
schtasks /end /tn "IronClaw"
|
||||
|
||||
# Delete task
|
||||
schtasks /delete /tn "IronClaw" /f
|
||||
```
|
||||
|
||||
### Using Task Scheduler XML
|
||||
|
||||
Create `ironclaw-task.xml`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-16"?>
|
||||
<Task version="1.2"
|
||||
xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
||||
<Triggers>
|
||||
<LogonTrigger>
|
||||
<Enabled>true</Enabled>
|
||||
</LogonTrigger>
|
||||
</Triggers>
|
||||
<Principals>
|
||||
<Principal id="Author">
|
||||
<LogonType>InteractiveToken</LogonType>
|
||||
<RunLevel>LeastPrivilege</RunLevel>
|
||||
</Principal>
|
||||
</Principals>
|
||||
<Settings>
|
||||
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
||||
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
||||
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
||||
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
||||
<RestartOnFailure>
|
||||
<Interval>PT1M</Interval>
|
||||
<Count>999</Count>
|
||||
</RestartOnFailure>
|
||||
</Settings>
|
||||
<Actions>
|
||||
<Exec>
|
||||
<Command>C:\Program Files\IronClaw\ironclaw.exe</Command>
|
||||
<Arguments>run</Arguments>
|
||||
<WorkingDirectory>%USERPROFILE%</WorkingDirectory>
|
||||
</Exec>
|
||||
</Actions>
|
||||
</Task>
|
||||
```
|
||||
|
||||
Import the task:
|
||||
|
||||
```powershell
|
||||
schtasks /create /xml ironclaw-task.xml /tn "IronClaw"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations vs WSL2
|
||||
|
||||
| Feature | Native Windows | WSL2 |
|
||||
|---------|---------------|------|
|
||||
| Docker sandbox | Requires Docker Desktop, limited | Full support via Docker Desktop WSL2 backend |
|
||||
| Shell tool (`shell`) | PowerShell / cmd.exe only; bash unavailable | Full bash support |
|
||||
| PATH handling | Windows PATH conventions (`\`, `;`) | Unix PATH (`/`, `:`) |
|
||||
| GNOME Keyring | Not available (uses DPAPI) | Available |
|
||||
| systemd service | Not available (use Task Scheduler) | Available (Win 11 22H2+) |
|
||||
| Signal handling | Limited SIGTERM support | Full Unix signals |
|
||||
| Symbolic links | Requires Developer Mode or elevated privileges | Native support |
|
||||
|
||||
---
|
||||
|
||||
## Docker Sandbox on Native Windows
|
||||
|
||||
The Docker sandbox requires [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop/).
|
||||
|
||||
```powershell
|
||||
# Install via winget
|
||||
winget install Docker.DockerDesktop
|
||||
```
|
||||
|
||||
After installation:
|
||||
1. Start Docker Desktop
|
||||
2. Go to **Settings → General** and ensure "Use the WSL 2 based engine" is checked (recommended even for native Windows IronClaw to avoid Windows container mode)
|
||||
3. Set `SANDBOX_ENABLED=true` in your IronClaw configuration
|
||||
|
||||
<Note>
|
||||
Docker Desktop must be running before IronClaw starts if the sandbox is enabled. Docker Desktop does not auto-start by default after a fresh install — enable it in **Settings → General → Start Docker Desktop when you sign in**.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
Create `%USERPROFILE%\.ironclaw\.env` or set environment variables via System Properties:
|
||||
|
||||
```powershell
|
||||
# PowerShell: set persistent user environment variables
|
||||
[Environment]::SetEnvironmentVariable("DATABASE_BACKEND", "libsql", "User")
|
||||
[Environment]::SetEnvironmentVariable("LLM_BACKEND", "nearai", "User")
|
||||
[Environment]::SetEnvironmentVariable("NEARAI_SESSION_TOKEN", "sess_xxx", "User")
|
||||
[Environment]::SetEnvironmentVariable("GATEWAY_ENABLED", "true", "User")
|
||||
[Environment]::SetEnvironmentVariable("GATEWAY_AUTH_TOKEN", "change_me", "User")
|
||||
```
|
||||
|
||||
Or via the GUI: **System Properties → Advanced → Environment Variables → User variables**.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Windows (WSL2)" icon="terminal" href="/platforms/windows-wsl2">
|
||||
Recommended Windows setup with full feature support
|
||||
</Card>
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Full environment variable reference
|
||||
</Card>
|
||||
<Card title="Troubleshooting" icon="wrench" href="/help/troubleshooting">
|
||||
Common issues and solutions
|
||||
</Card>
|
||||
</CardGroup>
|
||||
262
docs/drafts/platforms/windows-wsl2.mdx
Normal file
@@ -0,0 +1,262 @@
|
||||
---
|
||||
title: Windows (WSL2)
|
||||
sidebarTitle: Windows (WSL2)
|
||||
description: Running IronClaw on Windows via WSL2 (recommended)
|
||||
---
|
||||
|
||||
WSL2 (Windows Subsystem for Linux 2) is the recommended way to run IronClaw on Windows. It provides a full Linux environment with near-native performance and seamless port forwarding to Windows.
|
||||
|
||||
<Note>
|
||||
WSL2 is preferred over native Windows for IronClaw. The Docker sandbox, shell tools, and keyring integrations all work without workarounds under WSL2. For a fully native Windows setup, see [Windows Native](/platforms/windows-native).
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Set Up WSL2
|
||||
|
||||
### Install WSL2
|
||||
|
||||
Open PowerShell as Administrator and run:
|
||||
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
|
||||
This installs WSL2 with Ubuntu as the default distribution. Restart your machine when prompted.
|
||||
|
||||
### Verify WSL2 is Active
|
||||
|
||||
```powershell
|
||||
wsl --status
|
||||
# Should show: Default Version: 2
|
||||
|
||||
wsl --list --verbose
|
||||
# NAME STATE VERSION
|
||||
# Ubuntu Running 2
|
||||
```
|
||||
|
||||
### Update Ubuntu
|
||||
|
||||
Open Ubuntu from the Start menu and run:
|
||||
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Install IronClaw in WSL2
|
||||
|
||||
Once inside the WSL2 Ubuntu terminal, installation is identical to Linux:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://install.ironclaw.ai | bash
|
||||
```
|
||||
|
||||
Add to PATH:
|
||||
|
||||
```bash
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
Run the setup wizard:
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessing the Web Gateway from Windows
|
||||
|
||||
IronClaw's Web Gateway binds to `127.0.0.1:3000` inside WSL2. Modern Windows 11 and recent Windows 10 versions automatically forward localhost ports from WSL2 to the Windows host.
|
||||
|
||||
**From a Windows browser, navigate to:** `http://localhost:3000`
|
||||
|
||||
### Manual Port Forwarding (Older Windows 10)
|
||||
|
||||
If automatic forwarding does not work, find the WSL2 IP address and add a port proxy rule:
|
||||
|
||||
```powershell
|
||||
# In PowerShell (run as Administrator)
|
||||
# Get WSL2 IP
|
||||
$wslIp = (wsl hostname -I).Trim()
|
||||
|
||||
# Add port forward
|
||||
netsh interface portproxy add v4tov4 `
|
||||
listenaddress=127.0.0.1 `
|
||||
listenport=3000 `
|
||||
connectaddress=$wslIp `
|
||||
connectport=3000
|
||||
|
||||
# Verify
|
||||
netsh interface portproxy show all
|
||||
```
|
||||
|
||||
Remove the rule later:
|
||||
|
||||
```powershell
|
||||
netsh interface portproxy delete v4tov4 listenaddress=127.0.0.1 listenport=3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Secrets and Keyring
|
||||
|
||||
Secrets inside WSL2 are stored in the Linux keyring (GNOME Keyring or the keyutils kernel keyring), **not** in Windows Credential Manager.
|
||||
|
||||
### Install GNOME Keyring in WSL2
|
||||
|
||||
```bash
|
||||
sudo apt install gnome-keyring
|
||||
|
||||
# Start the keyring daemon in your shell session
|
||||
eval $(gnome-keyring-daemon --start --components=secrets)
|
||||
export GNOME_KEYRING_CONTROL
|
||||
```
|
||||
|
||||
Add this to `~/.bashrc` to start it automatically:
|
||||
|
||||
```bash
|
||||
if [ -z "$GNOME_KEYRING_CONTROL" ]; then
|
||||
eval $(gnome-keyring-daemon --start --components=secrets 2>/dev/null)
|
||||
export GNOME_KEYRING_CONTROL
|
||||
fi
|
||||
```
|
||||
|
||||
### Alternative: Environment Variable
|
||||
|
||||
On WSL2 without a desktop session, the keyring daemon may not start reliably. Use the environment variable fallback:
|
||||
|
||||
```bash
|
||||
# Generate a key once and store it securely
|
||||
openssl rand -base64 32 > ~/.ironclaw/master.key
|
||||
chmod 600 ~/.ironclaw/master.key
|
||||
|
||||
# Reference it in your .env
|
||||
IRONCLAW_MASTER_KEY=$(cat ~/.ironclaw/master.key)
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The master key file must be protected with restrictive permissions (600). Anyone who can read it can decrypt your stored secrets.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Docker Desktop with WSL2 Backend
|
||||
|
||||
Docker Desktop integrates directly with WSL2 and is the recommended Docker setup on Windows.
|
||||
|
||||
### Install Docker Desktop
|
||||
|
||||
1. Download from [docker.com/products/docker-desktop](https://www.docker.com/products/docker-desktop/)
|
||||
2. During installation, ensure **"Use the WSL 2 based engine"** is checked
|
||||
3. After installation, go to **Settings → Resources → WSL Integration**
|
||||
4. Enable integration with your Ubuntu distribution
|
||||
|
||||
### Verify Docker Access Inside WSL2
|
||||
|
||||
```bash
|
||||
# In WSL2 terminal
|
||||
docker run --rm hello-world
|
||||
```
|
||||
|
||||
You should see the Docker hello-world message. IronClaw can now use Docker for sandbox execution without needing a separate Docker installation inside WSL2.
|
||||
|
||||
<Note>
|
||||
Do not install Docker Engine directly inside WSL2 when using Docker Desktop. The Docker Desktop WSL2 integration exposes the Docker daemon to WSL2 automatically — installing a second Docker daemon causes conflicts.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Windows Firewall Rules for WSL2
|
||||
|
||||
Windows Firewall applies to WSL2 network traffic. If your firewall is set to block inbound connections to WSL2, the Web Gateway will be unreachable from Windows.
|
||||
|
||||
### Allow Port 3000 (if needed)
|
||||
|
||||
```powershell
|
||||
# PowerShell (run as Administrator)
|
||||
New-NetFirewallRule `
|
||||
-DisplayName "IronClaw Web Gateway" `
|
||||
-Direction Inbound `
|
||||
-Protocol TCP `
|
||||
-LocalPort 3000 `
|
||||
-Action Allow `
|
||||
-Profile Private
|
||||
```
|
||||
|
||||
Block public network access (keep it local-only):
|
||||
|
||||
```powershell
|
||||
New-NetFirewallRule `
|
||||
-DisplayName "Block IronClaw Public" `
|
||||
-Direction Inbound `
|
||||
-Protocol TCP `
|
||||
-LocalPort 3000 `
|
||||
-Action Block `
|
||||
-Profile Public
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Windows Terminal (Recommended)
|
||||
|
||||
[Windows Terminal](https://apps.microsoft.com/detail/9n0dx20hk701) provides the best experience for WSL2:
|
||||
|
||||
- Multi-pane view (IronClaw logs + interactive shell side by side)
|
||||
- Proper font rendering and colors for the TUI interface
|
||||
- WSL2 profiles with custom colors and fonts
|
||||
|
||||
Install from the Microsoft Store or:
|
||||
|
||||
```powershell
|
||||
winget install Microsoft.WindowsTerminal
|
||||
```
|
||||
|
||||
Set Ubuntu (WSL2) as the default profile in Settings.
|
||||
|
||||
---
|
||||
|
||||
## Running IronClaw as a WSL2 Background Service
|
||||
|
||||
WSL2 supports systemd on Windows 11 22H2+ and recent Windows 10 builds. Check if it is enabled:
|
||||
|
||||
```bash
|
||||
cat /etc/wsl.conf
|
||||
# Should contain:
|
||||
# [boot]
|
||||
# systemd=true
|
||||
```
|
||||
|
||||
Enable systemd if not set:
|
||||
|
||||
```bash
|
||||
sudo tee /etc/wsl.conf > /dev/null <<'EOF'
|
||||
[boot]
|
||||
systemd=true
|
||||
EOF
|
||||
|
||||
# Restart WSL2 from PowerShell
|
||||
wsl --shutdown
|
||||
# Re-open Ubuntu
|
||||
```
|
||||
|
||||
Then follow the [Linux systemd setup](/platforms/linux#systemd-service) to create a service unit.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Linux Platform" icon="terminal" href="/platforms/linux">
|
||||
Full systemd, keyring, and UFW setup reference
|
||||
</Card>
|
||||
<Card title="Windows Native" icon="windows" href="/platforms/windows-native">
|
||||
Running IronClaw natively without WSL2 (experimental)
|
||||
</Card>
|
||||
<Card title="VPS Hardening" icon="shield" href="/platforms/vps">
|
||||
Securing a public-facing deployment
|
||||
</Card>
|
||||
</CardGroup>
|
||||
122
docs/drafts/providers/anthropic.mdx
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Anthropic
|
||||
sidebarTitle: Anthropic
|
||||
description: Claude models via Anthropic API
|
||||
---
|
||||
|
||||
Use Anthropic's Claude models directly via their official API.
|
||||
|
||||
## Overview
|
||||
|
||||
Anthropic provides state-of-the-art language models with exceptional reasoning capabilities and long context windows.
|
||||
|
||||
**Key features:**
|
||||
- **Claude Sonnet** — Best balance of speed and capability
|
||||
- **Long context** — Up to 200K tokens
|
||||
- **Safety focus** — Built-in Constitutional AI
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# ~/.ironclaw/.env
|
||||
|
||||
LLM_BACKEND=anthropic
|
||||
ANTHROPIC_API_KEY=sk-ant-api03-...
|
||||
```
|
||||
|
||||
## Getting an API Key
|
||||
|
||||
1. Visit https://console.anthropic.com
|
||||
2. Create an account
|
||||
3. Generate an API key
|
||||
4. Copy the key (starts with `sk-ant-`)
|
||||
|
||||
## Available Models
|
||||
|
||||
| Model | Context | Best For |
|
||||
|-------|---------|----------|
|
||||
| `claude-sonnet-4-20250514` | 200K | Complex reasoning, coding (recommended) |
|
||||
| `claude-3-5-sonnet-20241022` | 200K | General purpose, great performance |
|
||||
| `claude-3-5-haiku-20241022` | 200K | Fast responses, simple tasks |
|
||||
|
||||
## Setup
|
||||
|
||||
### Via Wizard
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
1. Step 3: Select "Anthropic"
|
||||
2. Enter your API key
|
||||
3. Step 4: Select a model
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
```bash
|
||||
# Edit ~/.ironclaw/.env
|
||||
export LLM_BACKEND=anthropic
|
||||
export ANTHROPIC_API_KEY=sk-ant-api03-...
|
||||
|
||||
# Optional: custom base URL
|
||||
export ANTHROPIC_BASE_URL=https://api.anthropic.com
|
||||
```
|
||||
|
||||
Restart IronClaw:
|
||||
```bash
|
||||
ironclaw run
|
||||
```
|
||||
|
||||
## Enterprise / Custom Base URL
|
||||
|
||||
For enterprise deployments with custom endpoints:
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_BASE_URL=https://your-enterprise.anthropic.com
|
||||
```
|
||||
|
||||
## Cost
|
||||
|
||||
Approximate pricing (per 1K tokens):
|
||||
|
||||
| Model | Input | Output |
|
||||
|-------|-------|--------|
|
||||
| Claude Sonnet 4 | $3.00 | $15.00 |
|
||||
| Claude 3.5 Sonnet | $3.00 | $15.00 |
|
||||
| Claude 3.5 Haiku | $0.25 | $1.25 |
|
||||
|
||||
See [Anthropic pricing](https://www.anthropic.com/pricing) for current rates.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Invalid API key" icon="key">
|
||||
- Verify key starts with `sk-ant-`
|
||||
- Check for extra spaces
|
||||
- Ensure key is active in console
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rate limit exceeded" icon="clock">
|
||||
- Anthropic has rate limits per tier
|
||||
- Check your tier in console
|
||||
- Consider upgrading or implementing retries
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Model not found" icon="x-circle">
|
||||
- Verify model name spelling
|
||||
- Check model availability for your tier
|
||||
- Try a different model
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="NEAR AI" icon="star" href="/providers/nearai">
|
||||
Default provider with OAuth option
|
||||
</Card>
|
||||
|
||||
<Card title="OpenAI" icon="circle" href="/providers/openai">
|
||||
Alternative: GPT models
|
||||
</Card>
|
||||
</CardGroup>
|
||||
159
docs/drafts/providers/index.mdx
Normal file
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: LLM Providers
|
||||
sidebarTitle: Overview
|
||||
description: Choose your AI model provider
|
||||
---
|
||||
|
||||
IronClaw supports multiple LLM backends. Choose the provider that best fits your needs.
|
||||
|
||||
## Provider Comparison
|
||||
|
||||
| Provider | Backend | Local | Privacy | Cost | Best For |
|
||||
|----------|---------|-------|---------|------|----------|
|
||||
| **NEAR AI** | `nearai` | ✗ | ★★★ | $ | Default, easy setup |
|
||||
| **Anthropic** | `anthropic` | ✗ | ★★★ | $$ | Claude models |
|
||||
| **OpenAI** | `openai` | ✗ | ★★ | $$ | GPT models |
|
||||
| **Ollama** | `ollama` | ✓ | ★★★★★ | Free | Local inference |
|
||||
| **Tinfoil** | `tinfoil` | ✗ | ★★★★★ | $$ | TEE privacy |
|
||||
| **OpenRouter** | `openai_compatible` | ✗ | ★★ | $ | Model variety |
|
||||
| **Moonshot** | `openai_compatible` | ✗ | ★★★ | $$ | Kimi K2.5 |
|
||||
|
||||
## Quick Selection Guide
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Easiest">
|
||||
**NEAR AI** — Default provider, works out of the box
|
||||
- Browser OAuth or API key
|
||||
- Multiple models
|
||||
- No credit card required to start
|
||||
</Tab>
|
||||
|
||||
<Tab title="Most Private">
|
||||
**Ollama** — Run models locally
|
||||
- No data leaves your machine
|
||||
- Free
|
||||
- Requires GPU for larger models
|
||||
</Tab>
|
||||
|
||||
<Tab title="Best Models">
|
||||
**Anthropic Claude** — Industry-leading reasoning
|
||||
- Excellent for complex tasks
|
||||
- Long context window
|
||||
- Higher cost
|
||||
</Tab>
|
||||
|
||||
<Tab title="Model Variety">
|
||||
**OpenRouter** — Access 300+ models
|
||||
- Single API key
|
||||
- Mix of commercial and open models
|
||||
- Pay-as-you-go
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Available Providers
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="NEAR AI" icon="star" href="/providers/nearai">
|
||||
Default provider. Browser OAuth or API key. Multiple models including Claude.
|
||||
</Card>
|
||||
|
||||
<Card title="Anthropic" icon="triangle" href="/providers/anthropic">
|
||||
Claude models directly. Best reasoning and long context.
|
||||
</Card>
|
||||
|
||||
<Card title="OpenAI" icon="circle" href="/providers/openai">
|
||||
GPT-4o, GPT-4o-mini, o3-mini. Direct API access.
|
||||
</Card>
|
||||
|
||||
<Card title="Ollama" icon="download" href="/providers/ollama">
|
||||
Local inference. Free, private. Llama, Mistral, Qwen, and more.
|
||||
</Card>
|
||||
|
||||
<Card title="OpenAI-Compatible" icon="layers" href="/providers/openai-compatible">
|
||||
OpenRouter, Together AI, Fireworks, vLLM, LiteLLM, LM Studio.
|
||||
</Card>
|
||||
|
||||
<Card title="Tinfoil" icon="shield" href="/providers/tinfoil">
|
||||
Hardware-attested TEE. Neither Tinfoil nor cloud can see prompts.
|
||||
</Card>
|
||||
|
||||
<Card title="Moonshot" icon="sparkles" href="/providers/moonshot">
|
||||
Kimi K2.5 with 256K context. Advanced reasoning and long-context models.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Switching Providers
|
||||
|
||||
Change providers by updating environment variables and restarting:
|
||||
|
||||
```bash
|
||||
# Edit ~/.ironclaw/.env
|
||||
export LLM_BACKEND=anthropic
|
||||
export ANTHROPIC_API_KEY=sk-ant-...
|
||||
|
||||
# Restart IronClaw
|
||||
ironclaw run
|
||||
```
|
||||
|
||||
Or re-run the wizard:
|
||||
|
||||
```bash
|
||||
ironclaw onboard --skip-auth
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
All providers are configured via environment variables:
|
||||
|
||||
```bash
|
||||
# In ~/.ironclaw/.env
|
||||
LLM_BACKEND=anthropic
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
ANTHROPIC_BASE_URL=https://api.anthropic.com # optional
|
||||
```
|
||||
|
||||
See [Configuration](/setup/configuration) for the complete reference.
|
||||
|
||||
## Privacy Considerations
|
||||
|
||||
| Provider | Data Handling |
|
||||
|----------|---------------|
|
||||
| **NEAR AI** | Prompts/responses sent to NEAR AI |
|
||||
| **Anthropic** | Prompts/responses sent to Anthropic |
|
||||
| **OpenAI** | Prompts/responses sent to OpenAI |
|
||||
| **Ollama** | No external data transmission |
|
||||
| **Tinfoil** | Encrypted in TEE, provider cannot see |
|
||||
|
||||
For maximum privacy, use **Ollama** (local) or **Tinfoil** (TEE).
|
||||
|
||||
## Cost Estimation
|
||||
|
||||
Rough cost per 1K tokens (input + output):
|
||||
|
||||
| Provider | Cost |
|
||||
|----------|------|
|
||||
| Ollama | Free (GPU/electricity only) |
|
||||
| OpenAI GPT-4o-mini | ~$0.60 |
|
||||
| NEAR AI | ~$1-3 |
|
||||
| OpenAI GPT-4o | ~$5-10 |
|
||||
| Anthropic Claude | ~$3-15 |
|
||||
|
||||
Actual costs vary by model and usage.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Choose your provider:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Start with NEAR AI" icon="rocket" href="/providers/nearai">
|
||||
Default provider — easiest to set up
|
||||
</Card>
|
||||
|
||||
<Card title="Go Local" icon="lock" href="/providers/ollama">
|
||||
Maximum privacy with Ollama
|
||||
</Card>
|
||||
|
||||
<Card title="Use Claude" icon="brain" href="/providers/anthropic">
|
||||
Best reasoning capabilities
|
||||
</Card>
|
||||
</CardGroup>
|
||||
194
docs/drafts/providers/moonshot.mdx
Normal file
@@ -0,0 +1,194 @@
|
||||
---
|
||||
title: Moonshot AI
|
||||
description: Kimi K2.5 and other Moonshot models via OpenAI-compatible API
|
||||
---
|
||||
|
||||
[Moonshot AI](https://platform.moonshot.ai) provides state-of-the-art large language models including Kimi K2.5, featuring advanced reasoning capabilities and an extensive context window.
|
||||
|
||||
## Overview
|
||||
|
||||
Moonshot AI offers the Kimi family of models through an OpenAI-compatible API:
|
||||
|
||||
- **Kimi K2.5** — State-of-the-art reasoning with 256K context window
|
||||
- **Kimi K1.6** — Long-context model for document analysis
|
||||
- **OpenAI-compatible** — Standard `/v1/chat/completions` endpoint
|
||||
- **Competitive pricing** — Pay-as-you-go token-based billing
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# ~/.ironclaw/.env
|
||||
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=https://api.moonshot.ai/v1
|
||||
LLM_API_KEY=sk-...
|
||||
LLM_MODEL=kimi-k2-5
|
||||
```
|
||||
|
||||
## Getting an API Key
|
||||
|
||||
1. Visit [platform.moonshot.ai](https://platform.moonshot.ai)
|
||||
2. Create an account and complete verification
|
||||
3. Generate an API key from the dashboard
|
||||
4. Copy the key and add it to your `.env` file
|
||||
|
||||
## Available Models
|
||||
|
||||
| Model | Context Window | Description |
|
||||
|-------|----------------|-------------|
|
||||
| `kimi-k2-5` | 256,000 tokens | Flagship reasoning model (recommended) |
|
||||
| `kimi-k1.6` | 2,000,000 tokens | Ultra-long context for documents |
|
||||
| `kimi-k2-5-instruct` | 256,000 tokens | Instruction-tuned variant |
|
||||
|
||||
<Note>
|
||||
Model IDs may vary. Check the [Moonshot documentation](https://platform.moonshot.ai/docs/overview) for the latest available models and exact IDs.
|
||||
</Note>
|
||||
|
||||
## Setup
|
||||
|
||||
### Via Wizard
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
1. Step 3: Select "Other (OpenAI-compatible)"
|
||||
2. Enter base URL: `https://api.moonshot.ai/v1`
|
||||
3. Enter your API key
|
||||
4. Step 4: Enter model name: `kimi-k2-5`
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
```bash
|
||||
# Add to ~/.ironclaw/.env
|
||||
export LLM_BACKEND=openai_compatible
|
||||
export LLM_BASE_URL=https://api.moonshot.ai/v1
|
||||
export LLM_API_KEY=sk-your-moonshot-key
|
||||
export LLM_MODEL=kimi-k2-5
|
||||
```
|
||||
|
||||
Restart IronClaw:
|
||||
```bash
|
||||
ironclaw run
|
||||
```
|
||||
|
||||
## Using Kimi K2.5
|
||||
|
||||
Kimi K2.5 is Moonshot's flagship model with advanced capabilities:
|
||||
|
||||
### Strengths
|
||||
|
||||
- **Long context** — 256K token window for large documents
|
||||
- **Strong reasoning** — Excellent for complex analysis
|
||||
- **Multilingual** — Strong performance in Chinese and English
|
||||
- **Tool use** — Supports function calling
|
||||
|
||||
### Example Use Cases
|
||||
|
||||
<CodeGroup>
|
||||
```bash Document Analysis
|
||||
# Kimi's long context excels at analyzing large documents
|
||||
"Summarize this 100-page PDF and extract key findings"
|
||||
```
|
||||
|
||||
```bash Code Review
|
||||
# Strong reasoning for code analysis
|
||||
"Review this Rust module for potential bugs and improvements"
|
||||
```
|
||||
|
||||
```bash Complex Reasoning
|
||||
# Multi-step problem solving
|
||||
"Analyze these three approaches and recommend the best one with justification"
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## API Compatibility
|
||||
|
||||
Moonshot implements the OpenAI Chat Completions API:
|
||||
|
||||
```
|
||||
POST https://api.moonshot.ai/v1/chat/completions
|
||||
```
|
||||
|
||||
Supported features:
|
||||
- Streaming responses (`stream: true`)
|
||||
- Function calling / tool use
|
||||
- System messages
|
||||
- Temperature and top-p sampling
|
||||
|
||||
<Note>
|
||||
Some OpenAI-specific features like logprobs may not be available. Refer to Moonshot's API documentation for the latest feature support.
|
||||
</Note>
|
||||
|
||||
## Pricing
|
||||
|
||||
Moonshot uses pay-as-you-go pricing based on tokens consumed:
|
||||
|
||||
| Model | Input (per 1M tokens) | Output (per 1M tokens) |
|
||||
|-------|----------------------|------------------------|
|
||||
| Kimi K2.5 | Check current rates | Check current rates |
|
||||
| Kimi K1.6 | Check current rates | Check current rates |
|
||||
|
||||
Visit [platform.moonshot.ai/pricing](https://platform.moonshot.ai/pricing) for current rates.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="API key invalid" icon="key">
|
||||
- Verify your API key is correctly copied
|
||||
- Ensure your account is verified
|
||||
- Check for any account restrictions
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Model not found" icon="search">
|
||||
- Confirm the exact model ID from the Moonshot dashboard
|
||||
- Try `kimi-k2-5` or `kimi-k2-5-instruct`
|
||||
- Check the model is available in your region
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rate limiting" icon="gauge">
|
||||
- Moonshot may rate-limit requests
|
||||
- Implement exponential backoff for retries
|
||||
- Consider upgrading your plan for higher limits
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Context length exceeded" icon="maximize">
|
||||
- Kimi K2.5 supports 256K tokens
|
||||
- Use `memory_write` to store large documents
|
||||
- Chunk large inputs when possible
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Comparison with Tinfoil
|
||||
|
||||
Both providers offer Kimi K2.5, but with different tradeoffs:
|
||||
|
||||
| Feature | Moonshot | Tinfoil |
|
||||
|---------|----------|---------|
|
||||
| **Model** | Kimi K2.5 | Kimi K2.5 |
|
||||
| **Privacy** | Standard | TEE-encrypted |
|
||||
| **Pricing** | Direct pay-as-you-go | Tinfoil rates |
|
||||
| **Attestation** | No | Hardware-verified |
|
||||
| **Use case** | General use | Maximum privacy |
|
||||
|
||||
Choose **Moonshot** for direct API access and competitive pricing. Choose **Tinfoil** when you need TEE privacy guarantees.
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Tinfoil" icon="shield" href="/providers/tinfoil">
|
||||
TEE-secured Kimi inference alternative
|
||||
</Card>
|
||||
|
||||
<Card title="OpenAI-Compatible" icon="layers" href="/providers/openai-compatible">
|
||||
Other compatible providers
|
||||
</Card>
|
||||
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Full environment variable reference
|
||||
</Card>
|
||||
|
||||
<Card title="OpenAI" icon="circle" href="/providers/openai">
|
||||
Compare with GPT models
|
||||
</Card>
|
||||
</CardGroup>
|
||||
191
docs/drafts/providers/nearai.mdx
Normal file
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: NEAR AI
|
||||
sidebarTitle: NEAR AI
|
||||
description: Default LLM provider for IronClaw
|
||||
---
|
||||
|
||||
NEAR AI is the default provider for IronClaw, offering access to multiple models including Claude via a simple OAuth flow.
|
||||
|
||||
## Overview
|
||||
|
||||
NEAR AI provides:
|
||||
- **Browser OAuth** — One-click authentication (default)
|
||||
- **API Key** — NEAR AI Cloud mode for VPS/servers
|
||||
- **Multiple models** — Claude, GPT, and others
|
||||
- **Session management** — Automatic token refresh
|
||||
|
||||
## Authentication Modes
|
||||
|
||||
### Mode 1: Browser OAuth (Default)
|
||||
|
||||
**Best for:** Local machines with a browser
|
||||
|
||||
**How it works:**
|
||||
1. `ironclaw onboard` opens your browser
|
||||
2. Log in with GitHub or Google
|
||||
3. Session token saved to `~/.ironclaw/session.json`
|
||||
4. Automatic renewal
|
||||
|
||||
**Setup:**
|
||||
```bash
|
||||
ironclaw onboard
|
||||
# Select NEAR AI → Options 1 or 2 (GitHub/Google)
|
||||
```
|
||||
|
||||
### Mode 2: API Key
|
||||
|
||||
**Best for:** VPS, servers, headless environments
|
||||
|
||||
**How it works:**
|
||||
1. Get API key from https://cloud.near.ai
|
||||
2. Paste into terminal during onboarding
|
||||
3. Key saved to `~/.ironclaw/.env`
|
||||
|
||||
**Setup:**
|
||||
```bash
|
||||
ironclaw onboard
|
||||
# Select NEAR AI → Option 4: "NEAR AI Cloud API key"
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**VPS / Remote servers:** Browser OAuth won't work without a browser. Use API key mode or set `IRONCLAW_OAUTH_CALLBACK_URL` to a publicly reachable URL.
|
||||
</Warning>
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# ~/.ironclaw/.env
|
||||
|
||||
# Backend selection
|
||||
LLM_BACKEND=nearai
|
||||
|
||||
# Base URL (default)
|
||||
NEARAI_BASE_URL=https://private.near.ai
|
||||
|
||||
# Session token (set by OAuth, or manually)
|
||||
NEARAI_SESSION_TOKEN=sess_xxxxx
|
||||
|
||||
# OR for API key mode
|
||||
NEARAI_API_KEY=your-api-key
|
||||
|
||||
# Model selection
|
||||
NEARAI_MODEL=claude-sonnet-4-20250514
|
||||
```
|
||||
|
||||
## Available Models
|
||||
|
||||
Popular NEAR AI models:
|
||||
|
||||
| Model | Description |
|
||||
|-------|-------------|
|
||||
| `claude-sonnet-4-20250514` | Anthropic Claude Sonnet 4 (recommended) |
|
||||
| `claude-3-5-sonnet-20241022` | Claude 3.5 Sonnet |
|
||||
| `claude-3-5-haiku-20241022` | Claude 3.5 Haiku (faster) |
|
||||
|
||||
Models are fetched from the NEAR AI API during onboarding.
|
||||
|
||||
## Session Management
|
||||
|
||||
### Automatic Renewal
|
||||
|
||||
Session tokens auto-renew before expiration (typically 8-12 hours).
|
||||
|
||||
### Manual Session Update
|
||||
|
||||
If you need to update the session manually:
|
||||
|
||||
```bash
|
||||
ironclaw config set nearai.session_token sess_xxxxx
|
||||
```
|
||||
|
||||
Or edit `~/.ironclaw/session.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "sess_xxxxx",
|
||||
"expires_at": "2024-01-15T18:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### For Hosting Providers
|
||||
|
||||
If you're a hosting provider injecting tokens:
|
||||
|
||||
```bash
|
||||
export NEARAI_SESSION_TOKEN=sess_xxxxx
|
||||
```
|
||||
|
||||
This takes precedence over file-based tokens.
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Cheap Model
|
||||
|
||||
Set a cheaper model for simple tasks:
|
||||
|
||||
```bash
|
||||
export NEARAI_CHEAP_MODEL=claude-3-5-haiku-20241022
|
||||
```
|
||||
|
||||
### Fallback Model
|
||||
|
||||
Set a fallback if the primary fails:
|
||||
|
||||
```bash
|
||||
export NEARAI_FALLBACK_MODEL=gpt-4o
|
||||
```
|
||||
|
||||
### Circuit Breaker
|
||||
|
||||
Automatic failover after consecutive failures:
|
||||
|
||||
```bash
|
||||
export NEARAI_CIRCUIT_BREAKER_THRESHOLD=5
|
||||
export NEARAI_CIRCUIT_BREAKER_TIMEOUT_SECS=60
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Browser didn't open" icon="globe">
|
||||
- Check default browser is set
|
||||
- Try manually visiting the URL shown in terminal
|
||||
- On VPS: use API key mode instead
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Session expired" icon="clock">
|
||||
```bash
|
||||
# Re-authenticate
|
||||
ironclaw onboard --skip-auth
|
||||
# Select NEAR AI → re-authenticate
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="VPS authentication fails" icon="server">
|
||||
Browser OAuth doesn't work on headless servers. Solutions:
|
||||
|
||||
1. **Use API key mode** (recommended)
|
||||
2. **Set callback URL**:
|
||||
```bash
|
||||
export IRONCLAW_OAUTH_CALLBACK_URL=https://your-server:9876
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Model not available" icon="x-circle">
|
||||
- Check model name spelling
|
||||
- Models vary by account tier
|
||||
- Try a different model
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Full environment variable reference
|
||||
</Card>
|
||||
|
||||
<Card title="Anthropic" icon="triangle" href="/providers/anthropic">
|
||||
Use Claude directly (alternative)
|
||||
</Card>
|
||||
</CardGroup>
|
||||
168
docs/drafts/providers/ollama.mdx
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
title: Ollama
|
||||
sidebarTitle: Ollama
|
||||
description: Local LLM inference with Ollama
|
||||
---
|
||||
|
||||
Run language models locally using Ollama — free, private, and no API keys required.
|
||||
|
||||
## Overview
|
||||
|
||||
Ollama lets you run open-source models on your own hardware:
|
||||
|
||||
- **Completely private** — No data leaves your machine
|
||||
- **No API costs** — Just electricity and hardware
|
||||
- **Offline capable** — Works without internet
|
||||
- **Multiple models** — Llama, Mistral, Qwen, and more
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Ollama installed: https://ollama.com
|
||||
- Sufficient RAM (8GB+ recommended)
|
||||
- GPU optional but recommended
|
||||
|
||||
## Installation
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
brew install ollama
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
curl -fsSL https://ollama.com/install.sh | sh
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
Download from https://ollama.com
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# ~/.ironclaw/.env
|
||||
|
||||
LLM_BACKEND=ollama
|
||||
OLLAMA_BASE_URL=http://localhost:11434
|
||||
```
|
||||
|
||||
## Pull a Model
|
||||
|
||||
Before using a model, pull it:
|
||||
|
||||
```bash
|
||||
# Llama 3.2 (3B parameters, fast)
|
||||
ollama pull llama3.2
|
||||
|
||||
# Llama 3.1 (8B parameters, balanced)
|
||||
ollama pull llama3.1
|
||||
|
||||
# Qwen 2.5 (7B parameters, multilingual)
|
||||
ollama pull qwen2.5
|
||||
|
||||
# Mistral (7B parameters, efficient)
|
||||
ollama pull mistral
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### Via Wizard
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
1. Step 3: Select "Ollama"
|
||||
2. Step 4: Enter model name (e.g., `llama3.1`)
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
```bash
|
||||
export LLM_BACKEND=ollama
|
||||
export OLLAMA_BASE_URL=http://localhost:11434
|
||||
|
||||
# Set default model in IronClaw
|
||||
ironclaw config set llm.model llama3.1
|
||||
```
|
||||
|
||||
## Popular Models
|
||||
|
||||
| Model | Size | VRAM | Best For |
|
||||
|-------|------|------|----------|
|
||||
| `llama3.2` | 3B | 4GB | Fast, simple tasks |
|
||||
| `llama3.1` | 8B | 6GB | General purpose |
|
||||
| `qwen2.5` | 7B | 6GB | Multilingual |
|
||||
| `mistral` | 7B | 6GB | Efficient |
|
||||
| `codellama` | 7B | 6GB | Code generation |
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
| Model Size | RAM | GPU VRAM |
|
||||
|------------|-----|----------|
|
||||
| 3B | 4GB | 4GB |
|
||||
| 7B | 8GB | 6GB |
|
||||
| 13B | 16GB | 12GB |
|
||||
| 70B | 64GB | 48GB |
|
||||
|
||||
Without GPU, models run slower on CPU.
|
||||
|
||||
## Custom Base URL
|
||||
|
||||
For remote Ollama server:
|
||||
|
||||
```bash
|
||||
export OLLAMA_BASE_URL=http://your-server:11434
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Ollama not running" icon="x-circle">
|
||||
```bash
|
||||
# Start Ollama
|
||||
ollama serve
|
||||
|
||||
# Or as a service
|
||||
brew services start ollama # macOS
|
||||
sudo systemctl start ollama # Linux
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Out of memory" icon="alert">
|
||||
- Use a smaller model (3B instead of 7B)
|
||||
- Close other applications
|
||||
- Add swap space
|
||||
- Use a machine with more RAM
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Slow responses" icon="clock">
|
||||
- Use GPU if available
|
||||
- Try a smaller model
|
||||
- Quantized models run faster
|
||||
- Check CPU usage
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Model not found" icon="search">
|
||||
```bash
|
||||
# Pull the model first
|
||||
ollama pull llama3.1
|
||||
|
||||
# List available models
|
||||
ollama list
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="NEAR AI" icon="star" href="/providers/nearai">
|
||||
Cloud option with no hardware requirements
|
||||
</Card>
|
||||
|
||||
<Card title="Tinfoil" icon="shield" href="/providers/tinfoil">
|
||||
Private cloud inference with TEE
|
||||
</Card>
|
||||
</CardGroup>
|
||||
171
docs/drafts/providers/openai-compatible.mdx
Normal file
@@ -0,0 +1,171 @@
|
||||
---
|
||||
title: OpenAI-Compatible
|
||||
sidebarTitle: OpenAI-Compatible
|
||||
description: OpenRouter, Together AI, Fireworks, vLLM, LiteLLM, LM Studio
|
||||
---
|
||||
|
||||
IronClaw supports any OpenAI-compatible API endpoint. This includes OpenRouter, Together AI, Fireworks AI, self-hosted inference, and more.
|
||||
|
||||
## Overview
|
||||
|
||||
These providers use the same OpenAI API format:
|
||||
|
||||
```
|
||||
POST /v1/chat/completions
|
||||
Authorization: Bearer {key}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# ~/.ironclaw/.env
|
||||
|
||||
LLM_BACKEND=openai_compatible
|
||||
LLM_BASE_URL=https://api.openrouter.ai/api/v1
|
||||
LLM_API_KEY=sk-or-...
|
||||
```
|
||||
|
||||
## Supported Providers
|
||||
|
||||
### OpenRouter
|
||||
|
||||
[OpenRouter](https://openrouter.ai) — 300+ models, single API key.
|
||||
|
||||
```bash
|
||||
export LLM_BACKEND=openai_compatible
|
||||
export LLM_BASE_URL=https://openrouter.ai/api/v1
|
||||
export LLM_API_KEY=sk-or-...
|
||||
export LLM_MODEL=anthropic/claude-sonnet-4
|
||||
|
||||
# For attribution (optional)
|
||||
export LLM_EXTRA_HEADERS="HTTP-Referer:https://your-site.com,X-Title:Your App"
|
||||
```
|
||||
|
||||
**Popular models:**
|
||||
|
||||
| Model | ID |
|
||||
|-------|-----|
|
||||
| Claude Sonnet 4 | `anthropic/claude-sonnet-4` |
|
||||
| GPT-4o | `openai/gpt-4o` |
|
||||
| Llama 4 Maverick | `meta-llama/llama-4-maverick` |
|
||||
| Gemini 2.0 Flash | `google/gemini-2.0-flash-001` |
|
||||
|
||||
Browse all: https://openrouter.ai/models
|
||||
|
||||
### Together AI
|
||||
|
||||
[Together AI](https://www.together.ai) — Fast inference for open-source models.
|
||||
|
||||
```bash
|
||||
export LLM_BACKEND=openai_compatible
|
||||
export LLM_BASE_URL=https://api.together.xyz/v1
|
||||
export LLM_API_KEY=...
|
||||
export LLM_MODEL=meta-llama/Llama-3.3-70B-Instruct-Turbo
|
||||
```
|
||||
|
||||
### Fireworks AI
|
||||
|
||||
[Fireworks AI](https://fireworks.ai) — Fast inference with compound AI.
|
||||
|
||||
```bash
|
||||
export LLM_BACKEND=openai_compatible
|
||||
export LLM_BASE_URL=https://api.fireworks.ai/inference/v1
|
||||
export LLM_API_KEY=fw-...
|
||||
export LLM_MODEL=accounts/fireworks/models/llama4-maverick-instruct-basic
|
||||
```
|
||||
|
||||
### vLLM (Self-Hosted)
|
||||
|
||||
[vLLM](https://github.com/vllm-project/vllm) — High-throughput inference.
|
||||
|
||||
```bash
|
||||
# Start vLLM server
|
||||
python -m vllm.entrypoints.openai.api_server \
|
||||
--model meta-llama/Meta-Llama-3-8B-Instruct
|
||||
|
||||
# IronClaw config
|
||||
export LLM_BACKEND=openai_compatible
|
||||
export LLM_BASE_URL=http://localhost:8000/v1
|
||||
export LLM_API_KEY=token-abc123 # any value if auth not configured
|
||||
export LLM_MODEL=meta-llama/Meta-Llama-3-8B-Instruct
|
||||
```
|
||||
|
||||
### LiteLLM Proxy
|
||||
|
||||
[LiteLLM](https://github.com/BerriAI/litellm) — Universal proxy for any provider.
|
||||
|
||||
```bash
|
||||
# LiteLLM config (config.yaml)
|
||||
model_list:
|
||||
- model_name: gpt-4o
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
api_key: sk-...
|
||||
|
||||
# IronClaw config
|
||||
export LLM_BACKEND=openai_compatible
|
||||
export LLM_BASE_URL=http://localhost:4000/v1
|
||||
export LLM_API_KEY=sk-...
|
||||
export LLM_MODEL=gpt-4o
|
||||
```
|
||||
|
||||
### LM Studio
|
||||
|
||||
[LM Studio](https://lmstudio.ai) — Local GUI with OpenAI-compatible server.
|
||||
|
||||
1. Download and install LM Studio
|
||||
2. Load a model
|
||||
3. Start local server
|
||||
4. Configure IronClaw:
|
||||
|
||||
```bash
|
||||
export LLM_BACKEND=openai_compatible
|
||||
export LLM_BASE_URL=http://localhost:1234/v1
|
||||
export LLM_MODEL=llama-3.2-3b-instruct
|
||||
# No API key needed
|
||||
```
|
||||
|
||||
## Extra Headers
|
||||
|
||||
Add custom headers with `LLM_EXTRA_HEADERS`:
|
||||
|
||||
```bash
|
||||
export LLM_EXTRA_HEADERS="Key1:Value1,Key2:Value2"
|
||||
```
|
||||
|
||||
Useful for OpenRouter attribution.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Invalid base URL" icon="link">
|
||||
- Must end with `/v1` for most providers
|
||||
- Include protocol (`https://`)
|
||||
- No trailing slash after `/v1`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Model not found" icon="search">
|
||||
- Each provider uses different model IDs
|
||||
- Check provider's model list
|
||||
- Use exact ID from provider docs
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Authentication failed" icon="key">
|
||||
- Verify API key format
|
||||
- Check for expired keys
|
||||
- Some providers don't need keys (LM Studio)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Ollama" icon="download" href="/providers/ollama">
|
||||
Free local inference alternative
|
||||
</Card>
|
||||
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Full environment variable reference
|
||||
</Card>
|
||||
</CardGroup>
|
||||
88
docs/drafts/providers/openai.mdx
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: OpenAI
|
||||
sidebarTitle: OpenAI
|
||||
description: GPT models via OpenAI API
|
||||
---
|
||||
|
||||
Use OpenAI's GPT models directly via their API.
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# ~/.ironclaw/.env
|
||||
|
||||
LLM_BACKEND=openai
|
||||
OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
## Getting an API Key
|
||||
|
||||
1. Visit https://platform.openai.com
|
||||
2. Create an account
|
||||
3. Generate an API key
|
||||
4. Add billing information
|
||||
5. Copy the key (starts with `sk-`)
|
||||
|
||||
## Available Models
|
||||
|
||||
| Model | Context | Best For |
|
||||
|-------|---------|----------|
|
||||
| `gpt-4o` | 128K | Complex tasks, reasoning |
|
||||
| `gpt-4o-mini` | 128K | Cost-effective, fast |
|
||||
| `o3-mini` | 128K | Reasoning tasks |
|
||||
|
||||
## Setup
|
||||
|
||||
### Via Wizard
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
1. Step 3: Select "OpenAI"
|
||||
2. Enter your API key
|
||||
3. Step 4: Select a model
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
```bash
|
||||
# Edit ~/.ironclaw/.env
|
||||
export LLM_BACKEND=openai
|
||||
export OPENAI_API_KEY=sk-...
|
||||
|
||||
# Optional: custom base URL
|
||||
export OPENAI_BASE_URL=https://api.openai.com/v1
|
||||
```
|
||||
|
||||
Restart IronClaw:
|
||||
```bash
|
||||
ironclaw run
|
||||
```
|
||||
|
||||
## Cost
|
||||
|
||||
Approximate pricing (per 1K tokens):
|
||||
|
||||
| Model | Input | Output |
|
||||
|-------|-------|--------|
|
||||
| GPT-4o | $2.50 | $10.00 |
|
||||
| GPT-4o-mini | $0.15 | $0.60 |
|
||||
| o3-mini | $1.10 | $4.40 |
|
||||
|
||||
See [OpenAI pricing](https://openai.com/pricing) for current rates.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Insufficient quota" icon="alert">
|
||||
- Add billing information to OpenAI account
|
||||
- Check your spending limit
|
||||
- Verify you have available credits
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rate limit exceeded" icon="clock">
|
||||
- OpenAI has tier-based rate limits
|
||||
- Implement exponential backoff
|
||||
- Consider using a different model tier
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
121
docs/drafts/providers/tinfoil.mdx
Normal file
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: Tinfoil
|
||||
sidebarTitle: Tinfoil
|
||||
description: Private TEE inference via Tinfoil
|
||||
---
|
||||
|
||||
Tinfoil provides hardware-attested Trusted Execution Environment (TEE) inference — neither Tinfoil nor the cloud provider can see your prompts.
|
||||
|
||||
## Overview
|
||||
|
||||
Tinfoil runs models inside hardware-attested TEEs:
|
||||
|
||||
- **Hardware-attested** — Intel TDX or AMD SEV-SNP
|
||||
- **End-to-end encrypted** — Only you can decrypt responses
|
||||
- **Verifiable** — Cryptographic proof of execution
|
||||
- **Zero-knowledge** — Provider sees only encrypted data
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# ~/.ironclaw/.env
|
||||
|
||||
LLM_BACKEND=tinfoil
|
||||
TINFOIL_API_KEY=your-api-key
|
||||
TINFOIL_MODEL=kimi-k2-5
|
||||
```
|
||||
|
||||
## Getting an API Key
|
||||
|
||||
1. Visit https://tinfoil.sh
|
||||
2. Create an account
|
||||
3. Generate an API key
|
||||
4. Copy the key
|
||||
|
||||
## Available Models
|
||||
|
||||
| Model | Description |
|
||||
|-------|-------------|
|
||||
| `kimi-k2-5` | Moonshot AI Kimi K2.5 (default) |
|
||||
|
||||
## Setup
|
||||
|
||||
### Via Wizard
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
1. Step 3: Select "Tinfoil"
|
||||
2. Enter your API key
|
||||
3. Step 4: Confirm model
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
```bash
|
||||
export LLM_BACKEND=tinfoil
|
||||
export TINFOIL_API_KEY=your-api-key
|
||||
export TINFOIL_MODEL=kimi-k2-5
|
||||
```
|
||||
|
||||
Restart IronClaw:
|
||||
```bash
|
||||
ironclaw run
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌─────────────┐ Encrypted ┌──────────────┐ Encrypted ┌─────────────┐
|
||||
│ You │ ◄──────────────────► │ TEE │ ◄──────────────────► │ Cloud │
|
||||
│ (Client) │ (TLS + Attestation)│ (Enclave) │ (Network) │ (Untrusted)│
|
||||
└─────────────┘ └──────────────┘ └─────────────┘
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
└────────►│ Model runs in │◄───────┘
|
||||
│ secure enclave │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
1. **Attestation** — Client verifies TEE is genuine
|
||||
2. **Key exchange** — Ephemeral keys established
|
||||
3. **Encrypted inference** — Prompts encrypted end-to-end
|
||||
4. **Verifiable** — Cryptographic proof of execution
|
||||
|
||||
## Privacy Guarantees
|
||||
|
||||
- **Tinfoil** — Cannot see prompts (attested code)
|
||||
- **Cloud provider** — Only sees encrypted traffic
|
||||
- **IronClaw** — Your local client, you control
|
||||
|
||||
## Cost
|
||||
|
||||
Tinfoil pricing varies by model. See https://tinfoil.sh/pricing for current rates.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Attestation failed" icon="shield">
|
||||
- Verify system time is correct
|
||||
- Check network connectivity
|
||||
- TEE may be updating (try again)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="API key invalid" icon="key">
|
||||
- Check key is copied correctly
|
||||
- Verify account is active
|
||||
- Check for expired keys
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Ollama" icon="download" href="/providers/ollama">
|
||||
Free local inference alternative
|
||||
</Card>
|
||||
|
||||
<Card title="NEAR AI" icon="star" href="/providers/nearai">
|
||||
Default provider option
|
||||
</Card>
|
||||
</CardGroup>
|
||||
35
docs/drafts/reference/changelog.mdx
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Changelog
|
||||
sidebarTitle: Changelog
|
||||
description: IronClaw release history
|
||||
---
|
||||
|
||||
Release history for IronClaw.
|
||||
|
||||
## Changelog Location
|
||||
|
||||
The complete changelog is maintained in the main repository:
|
||||
|
||||
<Card title="View Changelog on GitHub" icon="github" href="https://github.com/ironclaw-ai/ironclaw/blob/main/CHANGELOG.md">
|
||||
See the full release history
|
||||
</Card>
|
||||
|
||||
## Versioning
|
||||
|
||||
IronClaw follows [Semantic Versioning](https://semver.org/):
|
||||
|
||||
- **MAJOR**: Breaking changes
|
||||
- **MINOR**: New features (backward compatible)
|
||||
- **PATCH**: Bug fixes
|
||||
|
||||
## Current Version
|
||||
|
||||
To check your installed version:
|
||||
|
||||
```bash
|
||||
ironclaw --version
|
||||
```
|
||||
|
||||
## Upgrade
|
||||
|
||||
See [Updating IronClaw](/install/updating) for upgrade instructions.
|
||||
396
docs/drafts/reference/cli.mdx
Normal file
@@ -0,0 +1,396 @@
|
||||
---
|
||||
title: CLI Reference
|
||||
sidebarTitle: CLI
|
||||
description: Command-line interface reference
|
||||
---
|
||||
|
||||
Complete reference for the `ironclaw` CLI.
|
||||
|
||||
## Global Options
|
||||
|
||||
```bash
|
||||
ironclaw [OPTIONS] [COMMAND]
|
||||
```
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `-c, --config <FILE>` | Path to config file |
|
||||
| `--no-onboard` | Skip auto-onboarding |
|
||||
| `-v, --verbose` | Enable verbose logging |
|
||||
| `-h, --help` | Print help |
|
||||
| `-V, --version` | Print version |
|
||||
|
||||
## Commands
|
||||
|
||||
### run
|
||||
|
||||
Start the IronClaw agent.
|
||||
|
||||
```bash
|
||||
ironclaw run [OPTIONS]
|
||||
```
|
||||
|
||||
Starts the agent with all configured channels. This is the main command for normal operation.
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--no-tui` | Disable TUI, use HTTP only |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Start normally
|
||||
ironclaw run
|
||||
|
||||
# Start without TUI
|
||||
ironclaw run --no-tui
|
||||
|
||||
# With verbose logging
|
||||
RUST_LOG=ironclaw=debug ironclaw run
|
||||
```
|
||||
|
||||
### onboard
|
||||
|
||||
Run the interactive setup wizard.
|
||||
|
||||
```bash
|
||||
ironclaw onboard [OPTIONS]
|
||||
```
|
||||
|
||||
Configures database, LLM, channels, and security settings.
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--skip-auth` | Skip authentication steps |
|
||||
| `--channels-only` | Configure only channels |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Full wizard
|
||||
ironclaw onboard
|
||||
|
||||
# Skip auth (use existing)
|
||||
ironclaw onboard --skip-auth
|
||||
|
||||
# Add new channel
|
||||
ironclaw onboard --channels-only
|
||||
```
|
||||
|
||||
### config
|
||||
|
||||
Manage configuration settings.
|
||||
|
||||
```bash
|
||||
ironclaw config <SUBCOMMAND>
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `list` | List all settings |
|
||||
| `get <KEY>` | Get a specific setting |
|
||||
| `set <KEY> <VALUE>` | Set a setting |
|
||||
| `delete <KEY>` | Delete a setting |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# List all settings
|
||||
ironclaw config list
|
||||
|
||||
# Get LLM backend
|
||||
ironclaw config get llm.backend
|
||||
|
||||
# Set model
|
||||
ironclaw config set llm.model claude-sonnet-4
|
||||
|
||||
# Delete setting (reset to default)
|
||||
ironclaw config delete llm.model
|
||||
```
|
||||
|
||||
### tool
|
||||
|
||||
Manage tools.
|
||||
|
||||
```bash
|
||||
ironclaw tool <SUBCOMMAND>
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `list` | List installed tools |
|
||||
| `install <PATH>` | Install a tool |
|
||||
| `remove <NAME>` | Remove a tool |
|
||||
| `run <NAME>` | Run a tool |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# List tools
|
||||
ironclaw tool list
|
||||
|
||||
# Install from file
|
||||
ironclaw tool install ./my-tool.wasm
|
||||
|
||||
# Remove tool
|
||||
ironclaw tool remove my-tool
|
||||
```
|
||||
|
||||
### registry
|
||||
|
||||
Manage the tool registry.
|
||||
|
||||
```bash
|
||||
ironclaw registry <SUBCOMMAND>
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `list` | List available tools |
|
||||
| `search <QUERY>` | Search for tools |
|
||||
| `install <NAME>` | Install from registry |
|
||||
| `update` | Update registry index |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# List available tools
|
||||
ironclaw registry list
|
||||
|
||||
# Search
|
||||
ironclaw registry search calendar
|
||||
|
||||
# Install
|
||||
ironclaw registry install google-calendar
|
||||
```
|
||||
|
||||
### mcp
|
||||
|
||||
Manage MCP (Model Context Protocol) servers.
|
||||
|
||||
```bash
|
||||
ironclaw mcp <SUBCOMMAND>
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `list` | List MCP servers |
|
||||
| `add <URL>` | Add an MCP server |
|
||||
| `remove <NAME>` | Remove an MCP server |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
ironclaw mcp list
|
||||
ironclaw mcp add http://localhost:3000/sse
|
||||
```
|
||||
|
||||
### memory
|
||||
|
||||
Manage workspace memory.
|
||||
|
||||
```bash
|
||||
ironclaw memory <SUBCOMMAND>
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `list` | List documents |
|
||||
| `read <PATH>` | Read a document |
|
||||
| `write <PATH>` | Write a document |
|
||||
| `search <QUERY>` | Search memory |
|
||||
| `tree` | Show memory tree |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# List documents
|
||||
ironclaw memory list
|
||||
|
||||
# Write a document
|
||||
echo "My note" | ironclaw memory write notes/idea.md
|
||||
|
||||
# Search
|
||||
ironclaw memory search "project idea"
|
||||
```
|
||||
|
||||
### pairing
|
||||
|
||||
Manage channel pairing.
|
||||
|
||||
```bash
|
||||
ironclaw pairing <SUBCOMMAND>
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `list <CHANNEL>` | List pending requests |
|
||||
| `approve <CHANNEL> <CODE>` | Approve a pairing request |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# List Telegram pending
|
||||
ironclaw pairing list telegram
|
||||
|
||||
# Approve
|
||||
ironclaw pairing approve telegram ABC12345
|
||||
|
||||
# List as JSON
|
||||
ironclaw pairing list telegram --json
|
||||
```
|
||||
|
||||
### service
|
||||
|
||||
Manage system service.
|
||||
|
||||
```bash
|
||||
ironclaw service <SUBCOMMAND>
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `install` | Install service |
|
||||
| `uninstall` | Remove service |
|
||||
| `start` | Start service |
|
||||
| `stop` | Stop service |
|
||||
| `status` | Check service status |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Install
|
||||
ironclaw service install
|
||||
|
||||
# Check status
|
||||
ironclaw service status
|
||||
|
||||
# Control
|
||||
sudo systemctl start ironclaw # Linux
|
||||
brew services start ironclaw # macOS
|
||||
```
|
||||
|
||||
### doctor
|
||||
|
||||
Run diagnostics.
|
||||
|
||||
```bash
|
||||
ironclaw doctor [OPTIONS]
|
||||
```
|
||||
|
||||
Checks:
|
||||
- Database connectivity
|
||||
- LLM provider access
|
||||
- Docker availability
|
||||
- Tunnel detection
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--json` | Output as JSON |
|
||||
| `--fix` | Attempt to fix issues |
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
ironclaw doctor
|
||||
ironclaw doctor --json
|
||||
```
|
||||
|
||||
### status
|
||||
|
||||
Show current status.
|
||||
|
||||
```bash
|
||||
ironclaw status [OPTIONS]
|
||||
```
|
||||
|
||||
Shows:
|
||||
- Version
|
||||
- Configuration
|
||||
- Database status
|
||||
- LLM backend
|
||||
- Channels
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
ironclaw status
|
||||
```
|
||||
|
||||
### completion
|
||||
|
||||
Generate shell completion scripts.
|
||||
|
||||
```bash
|
||||
ironclaw completion <SHELL>
|
||||
```
|
||||
|
||||
**Shells:** `bash`, `zsh`, `fish`, `powershell`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# Bash
|
||||
ironclaw completion bash > /etc/bash_completion.d/ironclaw
|
||||
|
||||
# Zsh
|
||||
ironclaw completion zsh > /usr/local/share/zsh/site-functions/_ironclaw
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
CLI behavior can be modified with environment variables:
|
||||
|
||||
```bash
|
||||
# Config directory
|
||||
export IRONCLAW_BASE_DIR=/custom/path
|
||||
|
||||
# Skip onboarding
|
||||
export ONBOARD_COMPLETED=true
|
||||
|
||||
# Logging
|
||||
export RUST_LOG=ironclaw=debug
|
||||
```
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| `0` | Success |
|
||||
| `1` | General error |
|
||||
| `2` | Invalid arguments |
|
||||
| `3` | Configuration error |
|
||||
| `4` | Database error |
|
||||
| `5` | Network error |
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Configuration" icon="settings" href="/setup/configuration">
|
||||
Environment variable reference
|
||||
</Card>
|
||||
|
||||
<Card title="Troubleshooting" icon="tool" href="/help/troubleshooting">
|
||||
Common issues and solutions
|
||||
</Card>
|
||||
</CardGroup>
|
||||
174
docs/drafts/security/index.mdx
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Security
|
||||
sidebarTitle: Overview
|
||||
description: IronClaw's defense-in-depth security architecture
|
||||
---
|
||||
|
||||
Security is IronClaw's primary differentiator. Your data stays yours through multiple layers of defense.
|
||||
|
||||
## Security-First Design
|
||||
|
||||
IronClaw is built with security as a core principle:
|
||||
|
||||
- **Local-first** — Your data stays on your machine
|
||||
- **Encrypted at rest** — Secrets use AES-256-GCM
|
||||
- **Sandboxed execution** — Tools run in isolated environments
|
||||
- **Prompt injection defense** — Multi-layer protection
|
||||
- **Zero-exposure credentials** — Secrets never enter containers
|
||||
|
||||
## Defense Layers
|
||||
|
||||
<Frame>
|
||||
<img src="/assets/security-architecture.svg" alt="IronClaw Security Architecture Diagram" />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
[Download the Excalidraw file](/assets/security-architecture.excalidraw) to explore or edit this diagram.
|
||||
</Note>
|
||||
|
||||
The security architecture illustrates IronClaw's **defense in depth** approach with four independent protection layers that data flows through before reaching external services.
|
||||
|
||||
## The Four Defense Layers
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Safety Layer" icon="shield" href="/security/safety-layer">
|
||||
Sanitizer, validator, policy engine, and leak detector. Protects against prompt injection and data exfiltration.
|
||||
</Card>
|
||||
|
||||
<Card title="WASM Sandbox" icon="blocks" href="/security/sandbox">
|
||||
Tools run in wasmtime with memory limits and fuel metering. Sandboxed execution.
|
||||
</Card>
|
||||
|
||||
<Card title="Docker Sandbox" icon="container" href="/security/sandbox">
|
||||
Job execution in isolated containers with network proxy and credential injection.
|
||||
</Card>
|
||||
|
||||
<Card title="Secrets Management" icon="lock" href="/security/secrets">
|
||||
AES-256-GCM encryption, OS keychain integration, zero-exposure credential model.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Security Defaults
|
||||
|
||||
IronClaw ships with secure defaults:
|
||||
|
||||
| Feature | Default | Why |
|
||||
|---------|---------|-----|
|
||||
| **Web Gateway host** | `127.0.0.1` | Local only |
|
||||
| **Webhook host** | `0.0.0.0` | ⚠️ Review if external not needed |
|
||||
| **Sandbox policy** | `readonly` | No filesystem writes |
|
||||
| **Secrets master key** | OS keychain | Hardware-backed |
|
||||
| **LLM backend** | NEAR AI | OAuth, no API key storage |
|
||||
| **Telegram DM policy** | `pairing` | Access control |
|
||||
|
||||
## Prompt Injection Defense
|
||||
|
||||
Multiple layers protect against prompt injection:
|
||||
|
||||
1. **Input validation** — Length, encoding, forbidden patterns
|
||||
2. **Sanitizer** — Escapes dangerous content
|
||||
3. **Policy engine** — Severity-based actions
|
||||
4. **Leak detector** — Scans for 15+ secret patterns
|
||||
5. **Tool output wrapping** — XML format with escape hints
|
||||
|
||||
<Note>
|
||||
Tool outputs are wrapped before reaching the LLM:
|
||||
```xml
|
||||
<tool_output name="search" sanitized="true">
|
||||
[content here]
|
||||
</tool_output>
|
||||
```
|
||||
</Note>
|
||||
|
||||
## Data Flow
|
||||
|
||||
<Frame>
|
||||
<img src="/assets/security-data-flow.svg" alt="Security Data Flow Diagram" />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
[Download the Excalidraw file](/assets/security-data-flow.excalidraw) to explore or edit this diagram.
|
||||
</Note>
|
||||
|
||||
```
|
||||
User Input
|
||||
↓
|
||||
[Validator] → Reject if invalid
|
||||
↓
|
||||
[Sanitizer] → Escape dangerous patterns
|
||||
↓
|
||||
[Policy Engine] → Apply rules
|
||||
↓
|
||||
[Leak Detector] → Scan for secrets
|
||||
↓
|
||||
LLM Processing
|
||||
↓
|
||||
Tool Execution
|
||||
↓
|
||||
[WASM Sandbox] → Sandboxed tool
|
||||
↓
|
||||
[Docker Sandbox] → Isolated job
|
||||
↓
|
||||
[Network Proxy] → Credential injection
|
||||
↓
|
||||
External Service
|
||||
```
|
||||
|
||||
## Zero-Exposure Credential Model
|
||||
|
||||
Secrets are never exposed to untrusted code:
|
||||
|
||||
1. **Stored encrypted** — AES-256-GCM in database
|
||||
2. **Master key in keychain** — OS-managed
|
||||
3. **Injected at proxy** — HTTP requests only
|
||||
4. **Containers never see raw values** — Safe even if compromised
|
||||
|
||||
See [Secrets](/security/secrets) for details.
|
||||
|
||||
## Compliance Considerations
|
||||
|
||||
IronClaw helps with security compliance:
|
||||
|
||||
| Requirement | IronClaw Feature |
|
||||
|-------------|-----------------|
|
||||
| Data encryption at rest | AES-256-GCM for secrets |
|
||||
| Access control | Channel policies, owner binding |
|
||||
| Audit logging | Structured logs, job history |
|
||||
| Least privilege | Sandboxed execution |
|
||||
| Network isolation | Domain allowlists |
|
||||
|
||||
## Security Checklist
|
||||
|
||||
When deploying IronClaw:
|
||||
|
||||
- [ ] Use OS keychain for master key (not env var)
|
||||
- [ ] Set `HTTP_HOST=127.0.0.1` if external webhooks not needed
|
||||
- [ ] Configure Telegram DM policy (not `open`)
|
||||
- [ ] Block port 50051 with firewall on VPS
|
||||
- [ ] Use libSQL encryption-at-rest warning
|
||||
- [ ] Review sandbox policy for your use case
|
||||
- [ ] Set strong Web Gateway auth token
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
If you discover a security vulnerability:
|
||||
|
||||
1. Email security@ironclaw.ai
|
||||
2. Do not disclose publicly until fixed
|
||||
3. Include steps to reproduce
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Safety Layer" icon="shield" href="/security/safety-layer">
|
||||
Sanitizer, validator, policy, leak detector
|
||||
</Card>
|
||||
|
||||
<Card title="Secrets" icon="lock" href="/security/secrets">
|
||||
Encryption and credential management
|
||||
</Card>
|
||||
|
||||
<Card title="Sandbox" icon="container" href="/security/sandbox">
|
||||
WASM and Docker isolation
|
||||
</Card>
|
||||
</CardGroup>
|
||||
197
docs/drafts/security/safety-layer.mdx
Normal file
@@ -0,0 +1,197 @@
|
||||
---
|
||||
title: Safety Layer
|
||||
sidebarTitle: Safety Layer
|
||||
description: Prompt injection defense and content validation
|
||||
---
|
||||
|
||||
The Safety Layer provides multi-stage defense against prompt injection, data exfiltration, and malicious content.
|
||||
|
||||
## Overview
|
||||
|
||||
All external content passes through the Safety Layer before reaching the LLM:
|
||||
|
||||
<Frame>
|
||||
<img src="/assets/safety-layer-overview.svg" alt="Safety Layer Overview Diagram" />
|
||||
</Frame>
|
||||
|
||||
```
|
||||
External Data → Validator → Sanitizer → Policy Engine → Leak Detector → LLM
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Validator" icon="check-circle">
|
||||
Input validation: length, encoding, forbidden patterns.
|
||||
</Card>
|
||||
|
||||
<Card title="Sanitizer" icon="shield">
|
||||
Content escaping and dangerous pattern detection.
|
||||
</Card>
|
||||
|
||||
<Card title="Policy Engine" icon="settings">
|
||||
Severity-based rules with configurable actions.
|
||||
</Card>
|
||||
|
||||
<Card title="Leak Detector" icon="search">
|
||||
Scans for 15+ secret patterns in tool outputs.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Validator
|
||||
|
||||
Checks input before processing:
|
||||
|
||||
| Check | Action |
|
||||
|-------|--------|
|
||||
| **Length** | Reject if exceeds limit |
|
||||
| **Encoding** | Reject invalid UTF-8 |
|
||||
| **Null bytes** | Reject or strip |
|
||||
| **Control chars** | Reject or escape |
|
||||
|
||||
## Sanitizer
|
||||
|
||||
Escapes dangerous content:
|
||||
|
||||
### Injection Patterns Detected
|
||||
|
||||
- Command chaining (`;`, `&&`, `||`)
|
||||
- Subshells (`$()`, backticks)
|
||||
- Path traversal (`../`)
|
||||
- Null bytes
|
||||
- Control characters
|
||||
|
||||
### Tool Output Wrapping
|
||||
|
||||
Tool outputs are wrapped before reaching the LLM:
|
||||
|
||||
```xml
|
||||
<tool_output name="search" sanitized="true">
|
||||
[escaped content here]
|
||||
</tool_output>
|
||||
```
|
||||
|
||||
The `sanitized="true"` attribute signals that content has been processed.
|
||||
|
||||
## Policy Engine
|
||||
|
||||
Rules-based enforcement with severity levels:
|
||||
|
||||
### Severity Levels
|
||||
|
||||
| Level | Action | Use Case |
|
||||
|-------|--------|----------|
|
||||
| **Critical** | Block + Alert | System compromise attempt |
|
||||
| **High** | Block | Malicious content |
|
||||
| **Medium** | Warn | Suspicious patterns |
|
||||
| **Low** | Log | Minor issues |
|
||||
|
||||
### Policy Actions
|
||||
|
||||
- **Block** — Reject the content
|
||||
- **Warn** — Allow with warning
|
||||
- **Sanitize** — Clean and proceed
|
||||
- **Review** — Flag for human review
|
||||
|
||||
## Leak Detector
|
||||
|
||||
Scans for 15+ secret patterns:
|
||||
|
||||
### Detected Patterns
|
||||
|
||||
| Pattern | Example |
|
||||
|---------|---------|
|
||||
| API keys | `sk-...`, `ak-...` |
|
||||
| Tokens | `ghp_...`, `sess-...` |
|
||||
| Private keys | `-----BEGIN RSA PRIVATE KEY-----` |
|
||||
| Connection strings | `postgres://user:pass@...` |
|
||||
| AWS credentials | `AKIA...` |
|
||||
| GitHub tokens | `ghp_...` |
|
||||
|
||||
### Actions per Pattern
|
||||
|
||||
| Action | Behavior |
|
||||
|--------|----------|
|
||||
| **Block** | Reject the entire output |
|
||||
| **Redact** | Mask the secret (e.g., `sk-****`) |
|
||||
| **Warn** | Flag but allow |
|
||||
|
||||
## Shell Environment Scrubbing
|
||||
|
||||
The shell tool scrubs sensitive environment variables:
|
||||
|
||||
```rust
|
||||
// Before: PATH, HOME, SECRET_KEY
|
||||
// After: PATH, HOME
|
||||
```
|
||||
|
||||
Prevents secrets from leaking via `env` or `$VAR` expansion.
|
||||
|
||||
## Command Injection Detection
|
||||
|
||||
Shell commands are checked for injection attempts:
|
||||
|
||||
```bash
|
||||
# BLOCKED: Command chaining
|
||||
cat file; rm -rf /
|
||||
|
||||
# BLOCKED: Subshell
|
||||
echo $(cat /etc/passwd)
|
||||
|
||||
# BLOCKED: Path traversal
|
||||
cat ../../../etc/passwd
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Safety settings are configured via environment variables:
|
||||
|
||||
```bash
|
||||
# Enable/disable safety layer
|
||||
export SAFETY_ENABLED=true
|
||||
|
||||
# Configure severity thresholds
|
||||
export SAFETY_SEVERITY_THRESHOLD=medium
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
The Safety Layer runs automatically:
|
||||
|
||||
1. **Input validation** — Before processing user input
|
||||
2. **Tool output scanning** — Before sending to LLM
|
||||
3. **LLM response scanning** — Before displaying to user
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Content blocked unexpectedly" icon="x-circle">
|
||||
- Check policy severity threshold
|
||||
- Review sanitizer rules
|
||||
- Consider whitelisting specific patterns
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Leaks not detected" icon="search">
|
||||
- Pattern may not be in default list
|
||||
- Add custom pattern via configuration
|
||||
- Check leak detector is enabled
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Performance impact" icon="gauge">
|
||||
- Safety layer adds minimal overhead
|
||||
- Most checks are O(n) on content size
|
||||
- Disable specific checks if needed
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Secrets" icon="lock" href="/security/secrets">
|
||||
Encryption and credential management
|
||||
</Card>
|
||||
|
||||
<Card title="Sandbox" icon="container" href="/security/sandbox">
|
||||
WASM and Docker isolation
|
||||
</Card>
|
||||
</CardGroup>
|
||||
230
docs/drafts/security/sandbox.mdx
Normal file
@@ -0,0 +1,230 @@
|
||||
---
|
||||
title: Sandbox
|
||||
sidebarTitle: Sandbox
|
||||
description: WASM and Docker sandbox isolation
|
||||
---
|
||||
|
||||
IronClaw uses two sandbox layers for tool execution: WASM sandbox for tools, and Docker sandbox for jobs.
|
||||
|
||||
## Two Sandboxes
|
||||
|
||||
| Sandbox | Use Case | Isolation |
|
||||
|---------|----------|-----------|
|
||||
| **WASM** | Tool execution | Memory limits, fuel metering |
|
||||
| **Docker** | Job execution | Container isolation, network proxy |
|
||||
|
||||
## WASM Sandbox
|
||||
|
||||
Tools run in a WebAssembly sandbox using wasmtime.
|
||||
|
||||
### Features
|
||||
|
||||
- **Memory limits** — Configurable max memory per tool
|
||||
- **Fuel metering** — Prevents infinite loops
|
||||
- **No filesystem access** — Unless explicitly allowed
|
||||
- **No network access** — Unless allowlisted
|
||||
|
||||
### Configuration
|
||||
|
||||
```bash
|
||||
# Enable WASM sandbox
|
||||
export WASM_SANDBOX_ENABLED=true
|
||||
|
||||
# Memory limit (bytes)
|
||||
export WASM_MEMORY_LIMIT=16777216 # 16 MB
|
||||
|
||||
# Fuel limit (wasm instructions)
|
||||
export WASM_FUEL_LIMIT=100000000
|
||||
```
|
||||
|
||||
### Capabilities
|
||||
|
||||
Tools declare capabilities in `capabilities.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"network": {
|
||||
"allowed_hosts": ["api.example.com"]
|
||||
},
|
||||
"filesystem": {
|
||||
"read": ["/workspace/*"],
|
||||
"write": ["/workspace/*"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Docker Sandbox
|
||||
|
||||
Jobs run in isolated Docker containers.
|
||||
|
||||
### Container Features
|
||||
|
||||
- **Non-root user** — UID 1000
|
||||
- **Read-only rootfs** — Immutable base image
|
||||
- **Dropped capabilities** — Minimal privileges
|
||||
- **Network proxy** — Controlled outbound access
|
||||
- **Resource limits** — Memory, CPU, timeouts
|
||||
|
||||
### Policies
|
||||
|
||||
| Policy | Filesystem | Network | Use Case |
|
||||
|--------|-----------|---------|----------|
|
||||
| **ReadOnly** | Read-only workspace | Allowlist only | Analysis, review |
|
||||
| **WorkspaceWrite** | Read-write workspace | Allowlist only | Code generation |
|
||||
| **FullAccess** | Full filesystem | Unrestricted | Admin tasks (rare) |
|
||||
|
||||
### Configuration
|
||||
|
||||
```bash
|
||||
# Enable sandbox
|
||||
export SANDBOX_ENABLED=true
|
||||
|
||||
# Set policy
|
||||
export SANDBOX_POLICY=workspace_write # readonly, workspace_write, full_access
|
||||
|
||||
# Resource limits
|
||||
export SANDBOX_MEMORY_LIMIT_MB=2048
|
||||
export SANDBOX_CPU_SHARES=1024
|
||||
export SANDBOX_TIMEOUT_SECS=120
|
||||
|
||||
# Docker image
|
||||
export SANDBOX_IMAGE=ironclaw-worker:latest
|
||||
```
|
||||
|
||||
## Network Proxy
|
||||
|
||||
All container traffic routes through a host-side proxy:
|
||||
|
||||
### Domain Allowlist
|
||||
|
||||
Only allowlisted domains are reachable:
|
||||
|
||||
```
|
||||
api.github.com
|
||||
crates.io
|
||||
registry.npmjs.org
|
||||
pypi.org
|
||||
...
|
||||
```
|
||||
|
||||
Add custom domains:
|
||||
|
||||
```bash
|
||||
export SANDBOX_EXTRA_DOMAINS="api.example.com,api2.example.com"
|
||||
```
|
||||
|
||||
### Credential Injection
|
||||
|
||||
Secrets are injected into HTTP requests at the proxy:
|
||||
|
||||
1. Container makes HTTP request
|
||||
2. Proxy intercepts request
|
||||
3. Proxy adds authorization header
|
||||
4. Container never sees raw credential
|
||||
|
||||
<Frame>
|
||||
<img src="/assets/sandbox-network-proxy.svg" alt="Network Proxy Credential Injection" />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
[Download the Excalidraw file](/assets/sandbox-network-proxy.excalidraw) to explore or edit this diagram.
|
||||
</Note>
|
||||
|
||||
## Zero-Exposure Credential Model
|
||||
|
||||
Secrets never enter the container environment:
|
||||
|
||||
| Approach | Risk |
|
||||
|----------|------|
|
||||
| **Environment variables** | Container can dump env |
|
||||
| **Volume mounts** | Container can read files |
|
||||
| **Proxy injection** | ✅ Container never sees secret |
|
||||
|
||||
## Container Hardening
|
||||
|
||||
Security features enabled by default:
|
||||
|
||||
```dockerfile
|
||||
# Non-root user
|
||||
USER 1000
|
||||
|
||||
# Read-only root filesystem
|
||||
--read-only
|
||||
|
||||
# Drop all capabilities
|
||||
--cap-drop=ALL
|
||||
|
||||
# No new privileges
|
||||
--security-opt=no-new-privileges:true
|
||||
|
||||
# Seccomp profile
|
||||
--security-opt=seccomp=default.json
|
||||
```
|
||||
|
||||
## Docker-in-Docker
|
||||
|
||||
IronClaw can run inside Docker and still sandbox jobs:
|
||||
|
||||
```bash
|
||||
# Mount Docker socket
|
||||
docker run ... \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
...
|
||||
```
|
||||
|
||||
Containers are siblings, not children.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Docker not available" icon="x-circle">
|
||||
- Install Docker: https://docs.docker.com/get-docker
|
||||
- Check Docker daemon: `sudo systemctl status docker`
|
||||
- Add user to docker group: `sudo usermod -aG docker $USER`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Sandbox timeout" icon="clock">
|
||||
- Job exceeded `SANDBOX_TIMEOUT_SECS`
|
||||
- Increase timeout for long-running tasks
|
||||
- Check for infinite loops
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Out of memory" icon="alert">
|
||||
- Container exceeded `SANDBOX_MEMORY_LIMIT_MB`
|
||||
- Increase memory limit
|
||||
- Optimize job memory usage
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Network blocked" icon="network">
|
||||
- Domain not in allowlist
|
||||
- Add to `SANDBOX_EXTRA_DOMAINS`
|
||||
- Check proxy logs
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Important Distinction
|
||||
|
||||
<Warning>
|
||||
**IronClaw runs alongside Docker** (for job sandboxing), not inside Docker by default.
|
||||
|
||||
- **Default**: IronClaw binary → spawns containers for jobs
|
||||
- **Optional**: IronClaw inside container → still spawns sibling containers
|
||||
</Warning>
|
||||
|
||||
See [Docker Install](/install/docker) for running IronClaw itself in a container.
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Safety Layer" icon="shield" href="/security/safety-layer">
|
||||
Prompt injection defense
|
||||
</Card>
|
||||
|
||||
<Card title="Secrets" icon="lock" href="/security/secrets">
|
||||
Encryption and credential management
|
||||
</Card>
|
||||
|
||||
<Card title="WASM Tools" icon="blocks" href="/tools/wasm">
|
||||
Building and deploying WASM tools with sandbox constraints
|
||||
</Card>
|
||||
</CardGroup>
|
||||
231
docs/drafts/security/secrets.mdx
Normal file
@@ -0,0 +1,231 @@
|
||||
---
|
||||
title: Secrets Management
|
||||
sidebarTitle: Secrets
|
||||
description: Encrypted credential storage and zero-exposure model
|
||||
---
|
||||
|
||||
IronClaw uses a zero-exposure credential model: secrets are encrypted at rest and never exposed to untrusted code.
|
||||
|
||||
## Overview
|
||||
|
||||
<Frame>
|
||||
<img src="/assets/secrets-overview.svg" alt="Secrets Management Diagram" />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
[Download the Excalidraw file](/assets/secrets-overview.excalidraw) to explore or edit this diagram.
|
||||
</Note>
|
||||
|
||||
<Frame>
|
||||
<img src="/assets/secrets-encryption-flow.svg" alt="Secrets Encryption Flow" />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
[Download the Excalidraw file](/assets/secrets-encryption-flow.excalidraw) to explore or edit this diagram.
|
||||
</Note>
|
||||
|
||||
## Zero-Exposure Model
|
||||
|
||||
Secrets follow a strict lifecycle:
|
||||
|
||||
1. **Stored encrypted** — AES-256-GCM in database
|
||||
2. **Master key in keychain** — OS-managed, hardware-backed
|
||||
3. **Injected at proxy boundary** — HTTP requests only
|
||||
4. **Containers never see raw values** — Safe even if compromised
|
||||
|
||||
<Frame>
|
||||
<img src="/assets/secrets-zero-exposure.svg" alt="Zero-Exposure Credential Model" />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
[Download the Excalidraw file](/assets/secrets-zero-exposure.excalidraw) to explore or edit this diagram.
|
||||
</Note>
|
||||
|
||||
## Encryption
|
||||
|
||||
### Algorithm: AES-256-GCM
|
||||
|
||||
- **Key size**: 256 bits
|
||||
- **Mode**: GCM (Galois/Counter Mode)
|
||||
- **Authentication**: Built-in AEAD
|
||||
|
||||
### Key Hierarchy
|
||||
|
||||
```shell
|
||||
Master Key (from OS keychain)
|
||||
│
|
||||
├──► SecretsCrypto
|
||||
│ │
|
||||
│ └──► Encrypt/Decrypt secrets
|
||||
│
|
||||
└──► Derived per-secret keys
|
||||
```
|
||||
|
||||
## Master Key Sources
|
||||
|
||||
The master key can come from three sources:
|
||||
|
||||
| Source | Security | Convenience |
|
||||
|--------|----------|-------------|
|
||||
| **OS Keychain** | ★★★★★ | ★★★☆☆ |
|
||||
| **Environment Variable** | ★★★☆☆ | ★★★★★ |
|
||||
| **Skip** | ★☆☆☆☆ | ★★★★★ |
|
||||
|
||||
### OS Keychain (Recommended)
|
||||
|
||||
- **macOS**: Keychain Access
|
||||
- **Linux**: GNOME Keyring or KWallet
|
||||
- **Windows**: Windows Credential Store
|
||||
|
||||
```bash
|
||||
# Generated and stored automatically
|
||||
# Two system dialogs on first use (normal)
|
||||
```
|
||||
|
||||
### Environment Variable
|
||||
|
||||
```bash
|
||||
export SECRETS_MASTER_KEY="32-byte-hex-encoded-key"
|
||||
|
||||
# Generate a key
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
## Secret Storage
|
||||
|
||||
Secrets are stored in the `secrets` database table:
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `user_id` | TEXT | Owner |
|
||||
| `name` | TEXT | Secret identifier |
|
||||
| `value` | BLOB | Encrypted value |
|
||||
| `created_at` | TIMESTAMP | Creation time |
|
||||
| `updated_at` | TIMESTAMP | Last update |
|
||||
|
||||
## Managing Secrets
|
||||
|
||||
### Via Wizard
|
||||
|
||||
Secrets are configured during onboarding:
|
||||
|
||||
```bash
|
||||
ironclaw onboard
|
||||
```
|
||||
|
||||
### Via CLI
|
||||
|
||||
```bash
|
||||
# List secrets
|
||||
ironclaw secret list
|
||||
|
||||
# Get a secret (decrypted)
|
||||
ironclaw secret get telegram_bot_token
|
||||
|
||||
# Set a secret
|
||||
ironclaw secret set telegram_bot_token "your-token"
|
||||
|
||||
# Delete a secret
|
||||
ironclaw secret delete telegram_bot_token
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Some secrets can be set via env vars:
|
||||
|
||||
```bash
|
||||
export TELEGRAM_BOT_TOKEN="your-token"
|
||||
export ANTHROPIC_API_KEY="sk-ant-..."
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
```
|
||||
|
||||
## Secret Names
|
||||
|
||||
Common secret names used by IronClaw:
|
||||
|
||||
| Name | Used By | Source |
|
||||
|------|---------|--------|
|
||||
| `telegram_bot_token` | Telegram channel | @BotFather |
|
||||
| `telegram_webhook_secret` | Telegram channel | Generated |
|
||||
| `llm_openai_api_key` | OpenAI provider | platform.openai.com |
|
||||
| `llm_anthropic_api_key` | Anthropic provider | console.anthropic.com |
|
||||
| `llm_compatible_api_key` | OpenAI-compatible | Provider |
|
||||
| `llm_nearai_api_key` | NEAR AI Cloud | cloud.near.ai |
|
||||
|
||||
## Platform Notes
|
||||
|
||||
### macOS
|
||||
|
||||
Two system dialogs on first keychain access:
|
||||
1. "Enter your password to unlock the keychain"
|
||||
2. "Allow ironclaw to access this keychain item"
|
||||
|
||||
Click "Always Allow" to minimize prompts.
|
||||
|
||||
### Linux
|
||||
|
||||
Requires `gnome-keyring`:
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt install gnome-keyring
|
||||
|
||||
# Fedora
|
||||
sudo dnf install gnome-keyring
|
||||
|
||||
# Arch
|
||||
sudo pacman -S gnome-keyring
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
Uses Windows Data Protection API (DPAPI). No additional setup required.
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Use OS keychain** when possible
|
||||
2. **Generate strong master keys** if using env var mode
|
||||
3. **Rotate secrets regularly**
|
||||
4. **Audit secret access** via logs
|
||||
5. **Never commit secrets** to version control
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Keychain prompts repeatedly" icon="refresh-cw">
|
||||
On macOS, click "Always Allow" on the keychain dialog. This is expected OS behavior.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Keychain not available on Linux" icon="linux">
|
||||
Install `gnome-keyring`:
|
||||
```bash
|
||||
sudo apt install gnome-keyring
|
||||
```
|
||||
|
||||
Or use environment variable mode in Step 2.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Secret not found" icon="search">
|
||||
- Check secret name spelling
|
||||
- Verify secret was saved during onboarding
|
||||
- Re-run `ironclaw onboard` to reconfigure
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Decryption fails" icon="key">
|
||||
- Master key may have changed
|
||||
- Database may be corrupted
|
||||
- Try restoring from backup
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Safety Layer" icon="shield" href="/security/safety-layer">
|
||||
Prompt injection defense
|
||||
</Card>
|
||||
|
||||
<Card title="Sandbox" icon="container" href="/security/sandbox">
|
||||
WASM and Docker isolation
|
||||
</Card>
|
||||
</CardGroup>
|
||||