eddielebelle/ringclaw
GitHub: eddielebelle/ringclaw
一个通过任务级权限分离和凭证隔离来从架构上限制 prompt 注入爆炸半径的 LLM agent 研究原型框架。
Stars: 0 | Forks: 0
# RingClaw
一个权限分离的 LLM agent 框架,建立在一个令人不安的前提之上:**任何浏览开放互联网的模型,迟早都会被它读取的内容所劫持。**
就像 Openclaw,只是更安全了……
研究原型。
## 前提
浏览型 agent 的工作是读取陌生人编写的文本,而 LLM 的核心能力是遵循文本中的指令。这两者是同一套机制。没有任何模型更新、系统提示词或注入分类器可以干净地将它们分开——每一项试图*识别*攻击的防御,都是在和一个可以针对冻结目标自由迭代的攻击者对赌。学术文献也得出了相同的结论:[*AI Agents May Always Fall for Prompt Injections*](https://arxiv.org/abs/2605.17634)(Abdelnabi & Bagdasarian, 2026)。把这场对赌视为已经输了:一旦你的 agent 读取了受攻击者影响的内容,就假定攻击者正在选择它的下一步行动。
RingClaw 正是基于这一假设,并提出了仅剩的一个问题:**被劫持的模型实际能做什么?** 目标是答案是*除了它已经被授权做的事情之外什么也做不了*——攻击者继承的是在他们获得控制权之前就已选定的范围,而不是你账户的密钥。
## 由前提推导出的结论
如果假定 worker 一旦接触到不可信内容就会被入侵,那么就会得出四条设计规则:
1. **安全性不能依赖于模型。** 模型只生成 tool-call JSON;而工具才接触外部世界。每一项结构性防御——主机白名单、凭证管控、收件人检查——都在工具边界处强制执行,因此无论模型是否诚实,它都成立。
2. **没有任何组件持有致命的三要素。** 私密数据、不可信暴露面、外部通信——数据窃取需要全部三者([Willison's framing](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/))。每项任务都被主分类器拆分为 `public` 和 `sensitive` 两个层级,它们作为没有桥梁的独立 agent 运行:读取陌生人网页的 worker 永远不会持有你的凭证,而持有凭证的 worker 永远不会读取陌生人的网页。
3. **范围在暴露前锁定。** 主控者在 worker 读取哪怕一个字节的不可信内容*之前*,就定义好每个 worker 的精简任务和主机白名单,并且用户批准该计划。被劫持的 worker 无法扩大其自身的范围,而被劫持的主控者也无法在批准后对其进行扩展。
4. **凭证绝不进入模型上下文。** 凭证库仅在工具边界处释放 secret,并 URL 限制为它们所属的主机。一个被完全攻破的 worker 无法泄露从未出现在其对话历史中的内容。
现有的浏览器代理(OpenClaw、Claude Computer Use)接受了同样的前提,但在**外部**对其进行遏制——OS 沙箱、通道访问控制、部署端白名单。这些方法很有效,RingClaw 可以与它们组合使用;它增加的维度是**在 agent 层进行内部的、针对单个任务的权限分离**,从而在结构上使能力与已批准的任务相匹配,而不是与沙箱允许的范围相匹配。
最接近的设计血统是 [CaMeL](https://github.com/google-research/camel-prompt-injection)([Debenedetti et al., *Defeating Prompt Injections by Design*](https://arxiv.org/abs/2503.18813))——通过构造来击败注入,使用特权规划器和隔离的 worker,而不是通过检测。RingClaw 将同样的直觉应用于长生命周期的个人代理:由操作者定义的信任根、凭证库、作为明确用户契约的计划批准,以及在相同机制下的周期性(cron)任务。
## 隔离带来的是实用性,而不仅仅是安全性
对隔离防御的标准反对意见是,它们会给 agent 增加过重的负担导致其毫无用处——Abdelnabi & Bagdasarian 直接指出:“一种隔离了哪怕单一数据(例如一封关键的电子邮件)的防御,可能会使任务无法完成。”当你在任务中途隔离*数据*时,这确实成立。RingClaw 的押注在于,当你拆解*任务*时,情况就会发生逆转:以正确的粒度进行拆分,限制波及范围的举动也会让每一个部分更容易执行。
其机制是完全一样的。主控者交给每个 worker 一份简短的**精简任务**——目标、相关的控制句柄、主机范围,仅此而已。从安全性的角度来解读,这是被劫持的 worker 无法逃避的契约。从实用性的角度来解读,这是一项小巧、明确的工作,没有上千轮对话的历史记录,也没有相互冲突的目标——这正是那些小型、廉价、本地模型停止幻觉并开始完成任务的机制。拆解的质量成为了同时左右两者的唯一调节旋钮:太粗略你就会重新造出单体 agent,太细致 worker 就会在碎片上停滞不前。
实际结果是,敏感的工作流不再需要前沿模型。在 Abdelnabi & Bagdasarian 自己的配套数据集中的 100 个委派场景中,RingClaw 下的本地 gemma 级别模型在严格实用性上得分 90/100,安全违规为 0/100,收件人信任关卡拦截了全部六次外部发送尝试。测试框架已包含在此 repo 中——使用 [`examples/flow_separation/`](examples/flow_separation/README.md) 进行复现。按次调用进行模型路由正是为了这种拆分而存在的:在规划质量至关重要的地方使用前沿主控者,而在其他所有地方使用本地 worker。
## 公开邀请:尝试攻破它
像“被劫持的 worker 无法离开其范围”这样的声明,其价值只取决于它能经受住什么考验,因此这个 repo 提供的不仅是用来欣赏它的工具,更包含了用来攻击它的工具。
[`examples/flow_separation/`](examples/flow_separation/README.md) 测试框架是一个完全植入检测设备的模拟工作环境——agent 采取的每一个动作都会被记录在审计日志中,评分器会标记任何漏网的外部发送,并且其场景格式让你很容易编写自己的对抗性变体(将你的 payload 植入到线程正文中,将该流程标记为未授权,看看有什么能蒙混过关)。
真正重要的目标是**结构性的底线**,而不是模型:威胁模型已经假定 worker 可能会被越狱,因此如果一次成功的越狱仍然停留在其范围内,说明系统正按预期工作。真正的攻破是指公开的 worker 接触到了凭证或敏感主机、范围在计划批准后被扩大,或者凭证库在不匹配的 URL 上释放了凭证。已知的遗留发现已记录在 [`SECURITY.md`](SECURITY.md) 中,免得你白白浪费一晚上的时间去重新发现它们;如果是超越该列表的任何内容,我们希望能收到反馈——对于任何可利用的内容请私下联系(**wrh9ppyzo@mozmail.com**),或者作为 issue 提交给社区。这里没有漏洞赏金(单人研究项目);我们能提供的是鸣谢,以及快速、坦诚的回复。
有关设计理念和威胁模型,请参阅 [`DESIGN.md`](DESIGN.md)。
有关各组件的参考,请参阅 [`ARCHITECTURE.md`](ARCHITECTURE.md)。
有关范围、假设和已知的遗留发现,请参阅 [`SECURITY.md`](SECURITY.md)。
## 状态
**研究原型。** RingClaw *限制了 prompt 注入的波及范围*;它不能消除注入,不能使 agent 变得“无法黑客攻击”,也不具备生产或企业级可用性。其价值在于架构和坦诚的威胁模型——请参阅 [`SECURITY.md`](SECURITY.md) 了解哪些在范围内、哪些不在范围内,以及已记录的遗留发现。
已通过真实的 LLM(本地使用 Ollama gemma4-agent,更高质量的运行使用 Anthropic Claude)和真实的 Telegram bot 频道进行了端到端验证。2230 项测试通过。已实现的防御:
- 由用户进行计划批准作为信任契约(手动 / 低于阈值的自动模式,在可配置的波及级别之上实行 2FA 门控)
- 在敏感任务调度时重新检查主机信任根
- 在批准时锁定每个任务的 worker 范围;主控者之后无法扩展它
- Sanitiser(确定性的按类型解析器;模式:passthrough / parse_strict)
- 轨迹监视器(独立的小型 LLM 裁决器;接受 / 拒绝 / surface_to_user)
- 动作监视器(worker 循环内部的按轮次思维漂移关卡)
- 针对敏感 `type()` 动作的收件人信任根,作用于 email 字段
- 计划任务,带有冻结的计划模板,默认 10 天过期,撤销时实行协作-中止终止,先前触发的 prompt 注入防御框架
## 快速开始
需要 Python ≥ 3.12 和 [`uv`](https://docs.astral.sh/uv/)。
```
git clone && cd ringclaw
uv sync
```
选择一个后端:
* **Ollama**(本地,免费,默认)。内置配置使用 `gemma4-agent:default`,这是基于公开的 Gemma 权重的一个轻量级自定义构建版本——根据 [`models/README.md`](models/README.md) 构建它(只需一个 `ollama create`),或者通过 `--model` 指向你已有的任何支持工具调用的模型。
* **Anthropic**(`uv sync --extra anthropic`,然后设置 `ANTHROPIC_API_KEY`)。
进行交互式设置(将写入 `$XDG_CONFIG_HOME/ringclaw/config.toml`):
```
uv run ringclaw setup
```
运行一个一次性任务:
```
uv run ringclaw run "summarise https://example.com"
```
或者启动 Telegram bot(需要 `telegram` 扩展和一个 bot token):
```
uv sync --extra telegram
export TELEGRAM_BOT_TOKEN=… # from @BotFather
uv run ringclaw telegram
```
## 桌面仪表盘 (GUI)
一个双窗格的仪表盘:左侧是聊天和计划的批准/修改/拒绝,右侧是 agent 的**实时浏览器**,带有查看与接管功能(你直接处理登录/CAPTCHA;agent 看不到你输入的内容)。还包括内存审查、MCP 添加、surfaces 和配置编辑器。
将其作为本地 Web 应用运行(仅限 localhost,token 门控):
```
uv sync --extra gui
uv run ringclaw gui # prints http://127.0.0.1:8787/?token=… — open it
```
如果想要原生的桌面应用,请构建 Tauri 外壳。它使用 PyInstaller 将 Python 后端打包为**侧载应用**,因此最终结果是完全自包含的(目标机器上不需要单独安装 RingClaw)。只需一条命令(在执行 [`desktop/README.md`](desktop/README.md) 中的一次性前置要求——Rust,`cargo install tauri-cli`,Tauri 的 Linux 库——之后):
```
./desktop/build.sh # bundles the sidecar + builds the installer
# → desktop/src-tauri/target/release/bundle/ (.deb / .AppImage / …)
```
浏览/支付任务还需要 Playwright 的浏览器:
`uv run playwright install chromium`。
## 一图看懂架构
```
┌─────────────────────────────────────────────────────────────────┐
│ User (Channel: CLI / Telegram) │
└────────────────────────────┬────────────────────────────────────┘
│ task
▼
┌─────────────────────────────────────────────────────────────────┐
│ Master classifier │
│ • Sees raw user task EXACTLY ONCE │
│ • Produces a Plan: tier per item + distilled_task per item + │
│ per-item host allowlist (sensitive only) │
│ • Fail-safe: ambiguous tasks default to refuse rather than │
│ widening trust │
└────────────────────────────┬────────────────────────────────────┘
│ plan
▼
┌─────────────────────────────────────────────────────────────────┐
│ Plan-approval gate (the trust contract) │
│ • Shows the user the plan in plain language │
│ • User approves / rejects; promotes unrecognised hosts │
│ for this run only (with explicit warning) │
│ • Optional TOTP 2FA gate above a configurable blast level │
└──────────────┬────────────────────────────────┬─────────────────┘
│ approved │ approved
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ Public worker │ │ Sensitive worker │
│ • Tool surface: │ │ • Tool surface: │
│ narrow / openclaw / │ │ SensitiveSurface │
│ computer_use / │ │ • Host allowlist │
│ anthropic_rl │ │ locked at dispatch │
│ • Blocklist on │ │ • URL-gated vault │
│ sensitive hosts │ │ release │
│ • Sees attacker pages │ │ • Recipient trust │
│ │ │ root for type() │
└───────────┬───────────┘ └───────────┬───────────┘
│ tool results │ tool results
│ (sanitised) │ (sanitised)
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ Defense-in-depth components │
│ • Sanitiser (parse_strict: deterministic type parsers) │
│ • Trajectory monitor (post-turn judge, separate LLM) │
│ • Action monitor (per-turn thinking-drift gate) │
│ • Plan-approval re-prompt on host promotion / drift │
└─────────────────────────────────────────────────────────┘
```
Public worker 和 sensitive worker 不共享环境、surface、后端或对话历史。主控者永远看不到 worker 的输出,除非它们通过了 sanitiser。主控者侧的第一层(tier-1)内存与 worker 是隔离的。
## 子系统
### 通道(用户 ↔ agent)
* **CLI** —— 一次性执行,打印最终答案。
* **Telegram** —— 长轮询 bot,受白名单限制,重启后通过 tier-2 保持持久会话。
通道仅用于入站;计划任务的外部传递通过相同的通道适配器进行。
### 工具面
worker 可以调用的一组工具。根据操作者配置逐层选择。
* **`narrow`** —— 极简的 `navigate`、`click_text`、`type`、`screenshot`、`wait`。防御性最强;能力最弱。
* **`openclaw`** —— OpenClaw 风格的坐标 / DOM 工具。能力更高,攻击面更广。
* **`computer_use`** —— Anthropic Computer Use API。
* **`anthropic_rl`** —— Anthropic 专门构建的浏览器 RL 工具。
* **`SensitiveSurface`** —— 仅用于敏感层;具有主机白名单限制,通过凭证库进行表单填充,并对 email 字段进行收件人信任检查。
### 环境
* `NullEnv` —— 无浏览器;用于纯 LLM 任务。
* `PlaywrightEnv` —— 通过 Playwright 使用真实浏览器,按 `--browser-profile` 提供持久化配置,带有用于路由的主机映射,配置目录权限为 0700。
### 凭证库
内存中的凭证存储,预先从 `--vault-file` 或在主机提升阶段通过 `bw-helper` 风格的 secretRef.exec 填充。URL 门控释放:sensitive worker 仅在位于绑定到这些凭证的主机上时,才能通过 `SensitiveSurface` 检索凭证。凭证绝不会以文本形式进入 agent 上下文。
### Sanitiser
由操作者配置驱动,包含两种模式:
* `passthrough`(默认)—— 工具输出原封不动地流向主控者。
* `parse_strict` —— 按字段类型的确定性解析器(decimal、带白名单的 url、bool、short_text 等)。任何无法干净解析的内容都会被丢弃。宁缺毋滥(偏向于假阴性)。
### 轨迹与动作监视器
* **轨迹监视器** —— 轮次后的 LLM 裁决器,输出范围很窄(接受 / 拒绝 / surface_to_user)。捕捉那些通过了结构性过滤器的偏离目标的动作。
* **动作监视器** —— worker循环内部的按轮次思维漂移关卡。累积违规次数;在达到配置的限制时停止 worker。
两者默认均为 `disabled`;在操作者配置中更改模式以启用。
## 计划任务
计划批准契约在时间上的延伸。`CronTemplate` 是相同的计划批准时刻,被冻结成一个可重新触发的工件,默认在 10 天后过期。重新触发不会再次提示;过期机制强制人类以已知的频率重新参与,从而防止范围漂移在不知不觉中累积。
在 [`src/ringclaw/cron.py`](src/ringclaw/cron.py) 中实现。完整的设计说明见 [`DESIGN.md` § Scheduled tasks](DESIGN.md)。
### 批准周期性任务
```
uv run ringclaw cron approve morning-brief \
--task "Summarise overnight email and reply with the highlights." \
--tier public \
--cron "0 7 * * *" --tz "Europe/London" \
--deliver-channel telegram --deliver-to "telegram:123456789"
```
运行调度程序(长期运行):
```
uv run ringclaw cron scheduler --interval 30
```
检查:
```
ringclaw cron list # tabular status
ringclaw cron show # full template
ringclaw cron diagnose [] # audit + remediation hints
ringclaw cron tick # dry-run scheduler pass
ringclaw cron revoke # atomic purge
```
### 触发时的关卡(按顺序,默认全部开启)
1. **签名验证** —— 模板主体哈希后与存储的 `approval_signature` 匹配。任何外部编辑都会被拒绝。
2. **过期检查** —— `template.expires_at` 必须是在未来时间。
3. **分类器波及上限检查** —— 在冻结的 distilled_task 上运行分类器,如果提议的最大波及范围超过 `--blast-ceiling` 则拒绝。在批准时也会运行。可通过 `--no-blast-check` 选择退出。
4. **协作中止检查(worker 运行前)** —— 重新读取注册表,以捕获在准备阶段发生的撤销操作。
5. Worker 在根据操作者配置全新构建的实例上运行冻结的任务。
6. **协作中止检查(worker 运行后)** —— 在交付前再次检查。如果在触发过程中被撤销,则结果为 `killed`:不交付,不进行 tier-2 写入。
7. 交付至 `template.deliver_channel`。
每次拒绝都会向网络发送相同的通用消息 `"Scheduled job didn't run today."`;具体原因只会出现在审计日志中。`ringclaw cron diagnose` 是面向操作者的详细视图。
### 带有 Prompt 注入防御的先前触发上下文
每次触发都会在 worker 运行之前,将带有 `cron:` 键的 tier-2 状态读取到 `prior_turns` 列表中。前一次触发的 `final_text` 可能携带攻击者的 payload(前一次触发读取了攻击者内容并被越狱)。为了防止持久的入侵:
* 先前内容进入**用户消息**,永远不进入系统提示词(信任降级)。
* 包裹在 `[BEGIN_UNTRUSTED nonce=] / [END_UNTRUSTED nonce=]` 标记中——攻击者内容无法知道每次触发的随机数,因此无法伪造关闭标记来逃逸出该框架。
* 攻击者内容中的字面标记前缀 token 在包含之前会被清除掉。
* 截断为最后 3 次触发 × 每次最多 200 个字符。
## CLI 参考
```
ringclaw run One-shot task from CLI
ringclaw telegram Long-polling Telegram bot
ringclaw setup Interactive operator-config wizard
ringclaw login --profile NAME One-time headed-browser login
ringclaw surface add --profile NAME
Catalogue-driven headed login
for a known surface (gmail,
proton, amazon, etc.)
ringclaw cron approve --task TEXT
--cron EXPR | --at ISODT | --every SECONDS
[--tier TIER] [--blast-ceiling LEVEL]
[--deliver-channel CHAN] [--deliver-to TARGET]
[--expires-in DAYS] [--no-blast-check] [--force]
ringclaw cron list
ringclaw cron show
ringclaw cron revoke [--force]
ringclaw cron diagnose []
ringclaw cron tick Dry-run scheduler pass
ringclaw cron scheduler [--interval SECONDS]
[--once | --max-iterations N]
[--dry-run] [--stub-worker]
[--no-blast-check]
```
所有子命令都接受 `--config PATH` 来覆盖操作者配置的存放位置。
## 配置
操作者配置是一个 TOML 文件,位于 `$XDG_CONFIG_HOME/ringclaw/config.toml`(`~/.config/ringclaw/`)。`ringclaw setup` 会写入一个可用的入门配置;其模式(schema)在 [`src/ringclaw/operator_config.py`](src/ringclaw/operator_config.py) 中有内联的文档说明。
关键部分:
```
[defaults] # runtime knobs the CLI inherits
backend = "ollama"
model = "gemma4-agent:default"
think = true
[telegram] # inbound channel
token_env = "TELEGRAM_BOT_TOKEN"
allow_from = ["123456789"]
[hosts."mail.google.com"] # trust universe
classifications = ["user-email"]
credentials = ["gmail_login"]
[friction.user-email]
classification = "user-email"
default_blast = "high"
[tiers.public]
tool_surface = "narrow"
[tiers.sensitive]
tool_surface = "SensitiveSurface"
allowed_hosts = ["mail.google.com"]
credentials_eligible = ["gmail_login"]
[modes] # defense-in-depth knobs
plan_approval = "manual" # manual | auto_if_blast_below
auto_approve_threshold = "low"
sanitiser = "passthrough" # passthrough | parse_strict
trajectory_monitor = "disabled" # disabled | log | enforce
action_monitor = "disabled"
two_factor_threshold = "off" # off | low | medium | high
[two_factor]
secret_env = "RINGCLAW_TOTP_SECRET"
```
## 存储布局
遵循 XDG 规范,且按用户隔离:
```
$XDG_CONFIG_HOME/ringclaw/
├── config.toml # operator config
├── catalogue/*.toml # user-extended surface catalogue
└── channels/*.toml # user-extended channel catalogue
$XDG_DATA_HOME/ringclaw/
├── profiles// # persistent browser profiles (0700)
├── sessions.json # surface-login manifest
└── cron/
├── jobs.toml # approved cron templates (0700/0600)
└── audit.jsonl # cron lifecycle log
$XDG_STATE_HOME/ringclaw/
├── sessions/ # tier-2 per-(channel, sender)
│ ├── telegram/.json
│ └── cron/.json
└── cron/locks/.lock # in-flight fire locks
```
## 威胁模型
详细表格见 [`DESIGN.md` § Defenses by attack class](DESIGN.md)。
要点:
* **通过页面内容对 worker 进行 Prompt 注入** —— 由每个任务的主机白名单(由环境强制执行)和 sanitiser 进行防御。被越狱的 worker 仍然无法脱离其作用域。
* **通过受攻击者影响的工具输出对主控者进行 Prompt 注入** —— 由在敏感调度时重新检查主机信任根来防御。即使主控者提出了带有攻击者提供的主机的敏感任务,该主机也无法通过信任根检查。
* **通过错误主机进行凭证窃取** —— URL 门控的凭证库释放;凭证永远不会进入 agent 上下文。
* **通过植入的链接/表单进行跨主机导航** —— 表面级别的动作后 URL 检查,拒绝超出范围的导航。
* **包含会使得主控者产生偏见的提示词的 worker 输出** —— 处于 `parse_strict` 模式下的 sanitiser 会丢弃自由格式的文本;轨迹监视器会捕捉偏离目标的行动计划。
* **计划任务的重放/篡改** —— `approval_signature` 提供完整性(对规范化后的主体进行 sha256 校验)。文件系统权限(目录 0700,文件 0600)是真实性防御。信任假设详见 cron 模块的 docstring。
**不在此范围内(Out of scope)**的威胁:
* 合法主机的失陷(bank.example 本身就被攻击者控制)——与密码管理器一样;爱莫能助。
* 文件系统级别的攻击者(对 `$XDG_DATA_HOME` 有写入权限)——被视为“他们已经控制了该账户”。
* 侧信道(时间、下游效应)。
## 开发
```
uv sync --all-extras # install everything
uv run playwright install chromium # for browser/payment tests
uv run pytest -q # 2230 tests
uv run pytest -q -k cron # cron subsystem only
uv run ruff check src/ # lint
```
项目布局:
```
src/ringclaw/
├── cli.py # all ringclaw subcommands
├── orchestrator.py # RingClaw class, plan dispatch
├── master/ # classifier
├── public/ # PublicAgent, AgentLoop, envs, surfaces, LLMs
├── sensitive/ # SensitiveSurface
├── plan_approval.py # the trust-contract gate
├── sanitiser.py # parse_strict implementation
├── trajectory_monitor.py # post-turn judge
├── vault.py # credential store
├── host_trust.py # host trust root
├── recipient_trust.py # email recipient trust root
├── catalogue.py # surface catalogue (gmail, proton, …)
├── channel_catalogue.py # channel catalogue (telegram, …)
├── sessions.py # per-profile login manifest
├── cron.py # scheduled tasks: templates, registry,
│ # audit, scheduler, executor
├── channels/ # CLI, Telegram channel adapters
├── tier2.py # per-session memory
├── operator_config.py # TOML schema
└── configs/ # shipped default config
```
## 指引
* [`DESIGN.md`](DESIGN.md) —— 设计理念 + 威胁模型
* [`ARCHITECTURE.md`](ARCHITECTURE.md) —— 各组件参考
* [`SECURITY.md`](SECURITY.md) —— 范围、假设、遗留发现,以及如何报告漏洞
## 许可证
Apache License 2.0 —— 详见 [`LICENSE`](LICENSE)。版权所有 © 2026 Owain Squad Ltd.
标签:AI安全, AI风险缓解, Chat Copilot, LLM代理, 提示注入防御, 权限隔离, 沙箱架构, 源代码安全, 特征检测, 逆向工具