JosephTLucas/wasm_af

GitHub: JosephTLucas/wasm_af

一个基于 WebAssembly 组件模型和 OPA 策略引擎构建的安全优先型 AI 代理编排框架,通过结构化沙箱实现零信任执行。

Stars: 7 | Forks: 1

# WASM_AF — WebAssembly Agent Framework **一个安全优先的 AI 代理编排框架,基于 [WebAssembly Component Model](https://component-model.bytecodealliance.org/) 和 [wasmtime](https://wasmtime.dev/) 构建。** WASM_AF 利用 WebAssembly 的沙箱化和 ephemeral(随用随弃)特性,创建了一个零信任的 AI 代理运行时。代理是 WASM 组件,默认被隔离,依据策略获得能力,并在任务完成后销毁。 ![Demo](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/90bb237bd9195914.gif) ## 快速开始 ``` export LLM_API_KEY="your-key-here" export LLM_BASE_URL="https://api.openai.com/v1" cd examples/wasmclaw LLM_MODE=api make reply-all-demo ``` 前置条件:[Rust](https://rustup.rs/)(包含 `wasm32-wasip2` 目标)、[NATS server](https://nats.io/)(或自带 NATS 的 [wash](https://wasmcloud.com/docs/installation/))、[jq](https://jqlang.github.io/jq/)。 对于 API 推理,请在 repo 根目录下的 `.env` 文件(已 gitignore)中设置 `LLM_API_KEY` 和 `LLM_BASE_URL`,或者在 shell 中 export 它们。 ## 为什么选择 WASM + AI Agents? 大多数代理框架通过**约定**来强制安全性:仔细配置你的工具,不向不需要的代理传递凭据,通过应用层检查限制网络访问。而这个 WebAssembly 框架通过**结构构建**来强制安全性。WASM 组件**无法**接触文件系统、网络或环境,除非由编排器显式授予访问权限。 ## 架构 ``` sequenceDiagram participant Client participant Orchestrator participant OPA participant NATS participant Agent as Agent (WASM) Client->>Orchestrator: POST /message (or /tasks) Orchestrator->>Orchestrator: build DAG of steps Orchestrator->>NATS: persist task state loop for each ready step (parallel) Orchestrator->>Orchestrator: compute context_taint from ancestor outputs Orchestrator->>OPA: evaluate step (capabilities + context_taint) OPA-->>Orchestrator: allowed host functions, limits, approval Orchestrator->>Agent: instantiate with selective Linker Agent-->>Orchestrator: execute() result Note right of Agent: instance dropped immediately Orchestrator->>Orchestrator: store result + propagate taint labels Orchestrator->>NATS: write result + audit entry end Orchestrator-->>Client: task result ``` 编排器是一个嵌入 [wasmtime](https://wasmtime.dev/) Component Model 运行时的单一 Rust 二进制文件。对于每个任务步骤,它会组合一个仅包含 OPA 许可的 host 接口的 `Linker`,实例化代理组件,调用其 `execute` 导出函数,读取结果,然后 drop 该实例。 **结构性的能力缺失**:如果 OPA 不允许某一步骤使用 `host-exec`,该接口就不会被添加到 Linker 中。组件将无法实例化 —— 该能力在结构上是不存在的,而不仅仅是在运行时被拒绝。 NATS JetStream KV 提供任务状态持久化和不可变的审计追踪。 ## 核心原则 **策略驱动的能力授予。** [Open Policy Agent (OPA)](https://www.openpolicyagent.org/) 评估能力授予并控制执行的每一步。来自 Rego 的结构化决策(`allowed_hosts`、`max_memory_pages`、`timeout_sec`、`host_functions`、`config`、`allowed_paths`、`requires_approval`)控制哪些 WIT 接口被链接到每个组件实例。策略输入包含 `context_taint` —— 来自祖先步骤的来源标签集 —— 因此 Rego 规则可以基于数据来源拒绝执行或要求审批。默认拒绝;如果没有 `OPA_POLICY`,编排器将无法启动。 **实例级作用域。** 每个组件都有自己的 `Linker` 和 `Store`。当多个 url-fetch 实例并行运行时,每个实例都仅限于一个特定域。 **无代理间通信。** 代理之间互不通信。编排器调解所有数据流,将中间结果存储在 NATS KV 中,并将上下文从祖先步骤传递给其依赖项。 **WIT 类型的 host 接口。** LLM 推理、shell 执行、邮件发送和 KV 存储都是 WIT 接口,仅链接到需要它们的组件。一个不导入 `host-llm` 的组件无法调用它 —— 该导入在其 world 中不存在。凭据(API keys, SMTP)存在于 Rust 闭包中;它们永远不会进入 WASM 内存。 **Ephemeral 生命周期。** 每个组件在单个函数作用域内经历 实例化 → `call_execute` → 销毁。一个不存在代理或运行时是无法被利用的。底层编译好的 `Component` 在首次加载后会在内存中缓存,因此后续调用的实例化成本接近于零 —— 磁盘 I/O 和 JIT 编译在每个进程的生命周期内只发生一次。 **自带代理。** 外部 WASM 组件可以通过 `POST /agents` 上传到运行中的编排器。它们会根据 WIT world 定义进行验证,存储在磁盘上,并以 `capability: "untrusted"` 注册。BYOA Rego 策略层(`policies/byoa.rego`)应用严格的沙箱默认值 —— 无 host 函数、无网络、4 MiB 内存、10 秒超时、强制审批。 **人机交互审批关卡。** 当 OPA 策略对某一步骤返回 `requires_approval: true` 时,编排器会暂停该步骤而不是执行它。DAG 的其他分支继续运行。只有在通过 HTTP API 显式 approve 或 reject 后,执行才会恢复。 ``` # 在你的 policy.rego 中 — 批准是按 agent 类型 opt-in 的: requires_approval if { input.step.agent_type == "email-send" } approval_reason := "email delivery requires human approval" if { input.step.agent_type == "email-send" } ``` **污点追踪。** 数据来源标签(`web`、`external`、`untrusted`)随步骤输出一起在 DAG 中传播。当像 `url-fetch` 或 `web-search` 这样的代理从外部源生成数据时,该输出会被标记上污点。该污点会流向每一个消费该数据的下游步骤。OPA 策略可以根据步骤上下文中存在哪些污点标签,拒绝执行、要求审批或应用其他控制。受信任的平台代理可以*解密*污点 —— 一个通过 LLM 处理原始 web 内容的 `summarizer` 会从其输出中剥离 `web` 标签,因此下游步骤看到的是干净的数据。BYOA 代理始终被标记为 `untrusted` 污点,并且永远无法解密污点。 ``` # 阻止 web 来源的数据到达 shell 执行: allow if { input.step.agent_type == "shell" not _context_has_web_taint ... } # 当 web 污染数据流向 LLM 时要求批准: requires_approval if { "web" in input.context_taint "llm_complete" in input.agent.host_functions not _is_declassifier } ``` ## API ### 健康检查 | Method | Path | Description | |---|---|---| | `GET` | `/healthz` | 当编排器就绪时返回 `200 OK` | ### 任务生命周期 | Method | Path | Description | |---|---|---| | `POST` | `/message` | 同步聊天端点 (提交 + 轮询 + 返回响应) | | `POST` | `/tasks` | 提交新任务 (返回 `task_id`) | | `GET` | `/tasks/{id}` | 获取任务状态 (计划、步骤状态、结果) | ### 审批关卡 | Method | Path | Description | |---|---|---| | `GET` | `/tasks/{id}/approvals` | 列出等待审批的步骤 | | `POST` | `/tasks/{id}/steps/{stepId}/approve` | 批准一个步骤 (body: `{"approved_by": "alice"}`) | | `POST` | `/tasks/{id}/steps/{stepId}/reject` | 拒绝一个步骤 (body: `{"rejected_by": "bob", "reason": "..."}`) | ### 自带代理 (BYOA) | Method | Path | Description | |---|---|---| | `POST` | `/agents` | 上传 `.wasm` 组件 + 元数据 (multipart form) | | `DELETE` | `/agents/{name}` | 移除外部代理 (平台代理受保护) | | `GET` | `/agents` | 列出所有代理 (包含 `external` 标志) | 外部代理在运行时注册,并自动获得 `capability: "untrusted"` 且无任何 host 函数。 ``` curl -X POST localhost:8080/agents \ -F 'meta={"name":"my-agent","context_key":"my_agent_result"}' \ -F 'wasm=@my_agent.wasm' curl localhost:8080/agents | jq . curl -X DELETE localhost:8080/agents/my-agent ``` 完整指南请参见 [docs/creating-an-agent.md](docs/creating-an-agent.md)。 ## 项目结构 ``` wasm_af/ ├── Cargo.toml # Rust workspace (orchestrator crates) ├── Makefile # build, test, demo │ ├── wit/ │ └── agent.wit # WIT interface definitions (the agent contract) │ ├── docs/ │ └── creating-an-agent.md # guide: platform agents + BYOA walkthrough │ ├── crates/ │ ├── orchestrator/ # the framework — Rust binary (axum + wasmtime) │ │ ├── src/ │ │ │ ├── main.rs # HTTP server, env config, startup │ │ │ ├── engine.rs # wasmtime Component loading, selective Linker │ │ │ ├── host/mod.rs # WIT Host trait impls (llm, kv, exec, etc.) │ │ │ ├── policy.rs # OPA evaluator (regorus, evaluates Rego per step) │ │ │ ├── scheduler.rs # DAG scheduler, parallel dispatch, splice │ │ │ ├── registry.rs # agent registry (thread-safe, mutable at runtime) │ │ │ └── api.rs # HTTP handlers (message, submit, get, approve, reject, BYOA) │ │ └── Cargo.toml │ │ │ ├── dag/ # DAG: dependency graph, ready-set, ancestors, splice │ │ └── src/lib.rs │ │ │ └── taskstate/ # NATS JetStream KV: task state, audit log, payloads │ └── src/ │ ├── lib.rs │ ├── store.rs # KV store implementation │ └── types.rs # shared types (Status, TaskState, etc.) │ ├── policies/ # reusable OPA policy modules │ └── byoa.rego # untrusted-agent sandbox tier │ ├── runtimes/ # WASI sandbox runtimes (downloaded, not checked in) │ ├── build.sh # downloads Python WASM (SHA256-verified) │ └── python.wasm # CPython 3.12 for wasm32-wasi (gitignored) │ ├── components/ # Rust workspace — WASM agent components (wit-bindgen) │ ├── .cargo/config.toml # default target: wasm32-wasip2 │ └── agents/ │ ├── router/ # LLM-based skill router (classifies → skill + params) │ ├── shell/ # host command execution via host-exec │ ├── sandbox-exec/ # sandboxed code execution via host-sandbox │ ├── file-ops/ # WASI std::fs (no host functions) │ ├── email-send/ # host fn email delivery via host-email │ ├── email-read/ # config-injected inbox reader via host-config │ ├── memory/ # conversation history via host-kv │ ├── responder/ # LLM response generation via host-llm │ ├── url-fetch/ # URL fetching via wasi:http │ ├── web-search/ # Brave Search API via wasi:http │ └── summarizer/ # LLM summarization via host-llm │ └── examples/ ├── pii-pipeline/ # BYOA demo: Python agent in a multi-agent pipeline ├── prompt-injection/ # security demo: injection fails structurally └── wasmclaw/ # personal AI assistant with two-tier execution ├── lib/setup.sh # shared infra: build, NATS, orchestrator, cleanup ├── run.sh # main demo (skills, security, approval, taint tracking) ├── reply-all-demo.sh # parallel DAG demo (jailbreak + approval) ├── taint-demo.sh # taint tracking demo (deny, approval gate, declassification) ├── agents.json # agent registry (includes output_taint, declassifies) ├── policy.rego # step policy (capability gates + taint-aware rules) ├── submit.rego # submission policy ├── jailbreak.rego # prompt injection / jailbreak detection rules ├── data.json # allowlists, feature flags, taint gates, jailbreak patterns ├── *_test.rego # 92 OPA tests (opa test .) └── Makefile ``` ## 运行演示 ### Wasmclaw (个人 AI 助理) ``` cd examples/wasmclaw make demo # mock LLM (deterministic, no deps) LLM_MODE=api make demo # OpenAI-compatible API (needs LLM_API_KEY + LLM_BASE_URL) make reply-all-demo # parallel DAG: jailbreak + approval (interactive Y/n) make taint-demo # taint tracking: deny, approval gate, declassification # (needs LLM_API_KEY + LLM_BASE_URL + BRAVE_API_KEY in .env) ``` ### PII Pipeline (自带代理) ``` cd examples/pii-pipeline make demo # mock LLM (deterministic, no deps) LLM_MODE=api make demo # OpenAI-compatible API (needs LLM_API_KEY + LLM_BASE_URL) ``` ### Prompt Injection ``` cd examples/prompt-injection && make demo # requires Ollama (pulls model automatically) ``` ## 配置 ### Orchestrator | Variable | Default | Description | |---|---|---| | `LISTEN_ADDR` | `0.0.0.0:8080` | HTTP server 监听地址 (接受 `:PORT` 或 `HOST:PORT`) | | `WASM_DIR` | `./components/target/wasm32-wasip2/release` | 包含已编译 `.wasm` 组件的目录 | | `NATS_URL` | `nats://127.0.0.1:4222` | NATS server 地址 | | `OPA_POLICY` | — | `.rego` 文件或目录的路径 (**必需**) | | `OPA_DATA` | — | JSON 数据文件的路径 (在启动时填充 OPA 数据存储) | | `AGENT_REGISTRY_FILE` | — | JSON 代理注册表文件的路径 (必需) | | `AGENT_REGISTRY` | — | 内联 JSON 代理注册表 (优先于文件) | | `LLM_MODE` | `mock` | `mock` 用于确定性路由,`api` 用于远程推理 (任何 OpenAI 兼容端点),`real` 用于本地 Ollama | | `LLM_BASE_URL` | — | OpenAI 兼容的 API base URL | | `LLM_API_KEY` | — | LLM 端点的 API key (`LLM_MODE=api` 时必需) | | `LLM_MODEL` | `gpt-4o-mini` | LLM 端点的模型名称 | | `LLM_TEMPERATURE` | — | 默认采样温度 | | `LLM_TIMEOUT_SEC` | `120` | LLM API 调用的 HTTP 客户端超时时间 | | `PLUGIN_TIMEOUT_SEC` | `30` | 每次组件调用的最大挂钟时间(秒) | | `PLUGIN_MAX_MEMORY_PAGES` | `256` | 每个组件的最大 WASM 内存页数 (每页 64 KiB) | | `SHELL_ALLOWED_COMMANDS` | `ls,cat,pwd,...` | 逗号分隔的命令二进制白名单 | | `SHELL_ALLOWED_PATHS` | `/tmp/wasmclaw` | 用于 shell 参数限制的逗号分隔路径基前缀 | | `SHELL_TIMEOUT_SEC` | `10` | 每次 shell 命令执行的最大挂钟时间(秒) | | `SANDBOX_RUNTIMES_DIR` | `./runtimes` | 包含 WASI 运行时 `.wasm` 文件的目录 | | `SANDBOX_TIMEOUT_SEC` | `30` | 每次沙箱代码执行的最大挂钟时间(秒) | | `SANDBOX_ALLOWED_LANGUAGES` | `python` | 用于 sandbox-exec 的逗号分隔语言白名单 | | `SANDBOX_ALLOWED_PATHS` | `/tmp/wasmclaw` | 挂载到沙箱实例的逗号分隔 host 路径 | | `EMAIL_ALLOWED_DOMAINS` | `example.com,partner-corp.com` | 逗号分隔的收件人域白名单 | | `APPROVAL_WEBHOOK_URL` | — | 用于 POST 审批事件的 URL | | `APPROVAL_TIMEOUT_SEC` | `0` | N 秒后自动拒绝步骤 (0 = 无超时) | ### API Mode 当 `LLM_MODE=api` 时,编排器连接到任何 OpenAI 兼容的推理端点。在你的环境或 `.env` 文件中设置 `LLM_BASE_URL` 和 `LLM_API_KEY`。端点上可用的任何模型都可以通过 `LLM_MODEL` 选择。 ## WIT 接口定义 代理契约定义在 `wit/agent.wit` 中。每个代理组件实现 `execute` 导出,并可以导入任何 host 接口的子集: | Interface | Functions | Purpose | |---|---|---| | `host-llm` | `llm-complete` | LLM 推理 (凭据在 host 闭包中) | | `host-kv` | `kv-get`, `kv-put` | NATS JetStream KV (按代理类型划分命名空间) | | `host-exec` | `exec-command` | Host 命令执行 (白名单限制) | | `host-sandbox` | `sandbox-exec` | 沙箱代码执行 (WASI 运行时) | | `host-email` | `send-email` | 邮件发送 (域名白名单) | | `host-config` | `get-config` | 只读配置 (始终可用) | 代理仅导入它们需要的接口。编排器的 `Linker` 仅提供 OPA 允许的内容。不匹配 = 实例化失败 = 结构性拒绝。 共享的 `kv-pair` 类型带有一个可选的 `taint` 字段。编排器使用从 DAG 计算出的来源标签填充它;代理可以检查它以做出污点感知的决策(例如,注释 LLM prompts),或者完全忽略它。 ``` record kv-pair { key: string, val: string, taint: option>, } ``` ## 许可证 Apache License 2.0 — 详见 [LICENSE](LICENSE)。
标签:AI安全, AI工具, Chat Copilot, FTP漏洞扫描, Lerna, NATS, OPA, Rust, Wasmtime, WebAssembly, XSS注入, 人工智能体, 可视化界面, 安全编排, 微服务安全, 沙箱隔离, 策略执行, 组件模型, 结构化提示词, 网络安全审计, 网络流量审计, 能力访问控制, 运行时防护, 通知系统, 零信任架构, 靶场