给编码 Agent 的运行时主机护栏——按每个动作授权它的安装、脚本与网络请求,而不是全有或全无。
English | 简体中文
你把 Agent 放进自主模式拉依赖、跑脚本——但容器是全有或全无的,关掉它换效率后,主机就彻底裸奔了。AgentGate 在每个触碰主机的动作发生的瞬间拦下来,带着 Agent 自己的意图问你一句:放行还是拒绝。
当你让一个编码 Agent 写代码、拉依赖、跑脚本时,你把信任交给了它,但责任还在你身上——而你和主机之间没有一个带作用域的检查点。容器能做隔离,可它是全有或全无的,开发者为了 Agent 的效率往往把它关掉;就算开着,它也无法区分「这次安装没问题」和「那个网络请求是在外传数据」。
这不是一个静态依赖扫描器。Miasma 这类供应链蠕虫专门盯着 AI 编码 Agent——它瘫痪过 72+ 个仓库(含微软的 Azure Functions Action),而它的载荷只在安装 / 执行那一刻才暴露,静态分析在安装前读包根本看不到。AgentGate 是运行时、按动作的护栏:在安装、脚本、egress 发生的当下逐个授权,让供应链载荷在执行点被拦下,而不是等 72 个仓库挂掉后才发现。
这正是 @simonw 反复讨论的「Agent 跑 shell 命令时信任与控制的取舍」,也是那些把自主性拉满、却不带任何主机护栏的编码 Agent harness(如 affaan-m/ECC)缺的那一块——AgentGate 跟它们互补,而非竞争。
编码 Agent 跑在护栏后面:它拉起的已配置 shim 列表(npm、pip、curl、wget、……)上的子进程被 PATH shim 截获——自定义二进制和直接 shell spawn 不受门控;通用 exec 插桩在路线图上——每个网络请求被注入的 HTTP(S) 代理重定向到本地。两条路径都汇到一个 unix-socket broker / Gate Engine,由它按 policy.yaml 做首条匹配即生效的裁决——必要时弹出 [a]llow / [d]eny / [A]lways 提示。放行的动作才 exec 真实二进制并继续,拒绝的从不落地;每一笔裁决都追加进 JSONL 审计日志,事后用 agentgate audit 回看。
需要 Go 1.24+(Linux 或 macOS)。从冷启动到第一个授权提示,三条命令:
go install github.com/SuperMarioYL/agentgate@latest # 1. 安装单文件二进制
agentgate init # 2. 在当前目录落一份默认 policy.yaml
agentgate run -- claude --autonomous "加个图表库并接好" # 3. 把你的 Agent 跑在护栏后面第一个触碰主机的动作就会暂停,并显示 Agent 自己的意图:
┌─ AgentGate · action paused ──────────────────
│ agent : claude-code
│ action : exec
│ target : npm install chalk
│ intent : agent wants to install npm package: chalk
└──────────────────────────────────────────────
[a]llow / [d]eny / [A]lways ?
按 a 放行一次、d 拒绝、A 永久放行(会把规则写回 policy.yaml,稳态下几乎不再打扰你)。事后用 agentgate audit 查看每个被门控动作的 JSONL 审计流:
agentgate audit
# ✓ 13:20:26 exec allow npm install chalk
# ✗ 13:20:26 net_egress deny telemetry.unknown-host.example
# 只看「被拦下了什么」——按决策 / 动作 / 时间过滤(v0.3.0)
agentgate audit --decision deny
agentgate audit --action net_egress --since 2h
agentgate audit --decision deny --json # 原始 JSONL,便于管道处理
--since接受 RFC3339 时间戳、日期(2026-06-19)或「多久之前」的时长(2h、30m)。
拦截方式可移植、无需 ptrace / libpcap:通过 PATH shim 把每个被拦的命令转发给一个 unix-socket broker,由它持有门控决策;网络 egress 则通过注入
HTTP(S)_PROXY的本地重定向代理逐个主机门控。完整走查见examples/claude-code-session.md。
Agent 的 npm install 被暂停等待批准,安装后脚本对未声明主机的 egress 被红字拦下,最后 agentgate audit 打出完整轨迹:
该 GIF 由
docs/demo.tape经 vhs 在 CI 中渲染(见.github/workflows/demo.yml)。仓库另附录制好的docs/demo.cast,可本地用asciinema play docs/demo.cast回放。
策略是有序、首条匹配即生效的规则列表。每条规则有一个 match(action + target_glob)和一个 decision(allow / deny / ask);任何规则都没命中的动作落到 default。
default: ask # 没有规则命中时的兜底决策
rules:
# exec —— Agent 拉起的安装与脚本
- match:
action: exec
target_glob: "*install*"
decision: ask # 每次安装都浮现出来,让你看清拉了什么
# fs_write —— 仅 check / dry-run(本版尚未运行时强制,见下方安全说明)
- match:
action: fs_write
target_glob: "$PWD/**"
decision: allow
scope: "$PWD" # 记录期望写入范围,供 agentgate check 解析
# net_egress —— 放行常用 registry,门控其余一切
- match:
action: net_egress
target_glob: "registry.npmjs.org"
decision: allow
- match:
action: net_egress
decision: deny # 未声明的主机 -> 拦截Glob 语义:* 匹配单个路径 / 主机段(filepath.Match 语义),** 跨段匹配(如 $PWD/**);带后缀的 ** 模式(如 /proj/**.env)要求 target 以该后缀结尾,不会把后缀当作中间子串去命中(即 /proj/.env.backup/passwd 不会被 /proj/**.env 误放)。不带通配的裸 host token 按主机边界匹配——命中整个 target,或 host:port 的 host 部分(如 registry.npmjs.org 命中 registry.npmjs.org:443),但不会误放 github.com.evil.com 或 evilgithub.com 这类伪造主机。以点开头的 token(如 .github.com)匹配整棵子域树(api.github.com),但不含裸顶级域 github.com 本身。agentgate init 会落一份内置的合理默认策略,可直接编辑。
⚠️ 安全说明 —— 各动作面的强制现状(务必读)。 AgentGate 在运行时强制两个面:exec(Agent 拉起的子进程,经 PATH shim + broker 逐个裁决)与net_egress(经本地 HTTP(S) 重定向代理按主机门控)。fs_write目前是 check / dry-run 专用:策略引擎与agentgate check --action fs_write会解析写入规则,但本版尚未在运行时拦截 Agent 的真实写操作(运行时写入插桩 —— Linux ptrace/eBPF、macOS LD_PRELOAD/sandbox-exec —— 已列入 v0.7.0+ 路线图)。因此agentgate init的默认策略不再附带一条会让人误以为写入被真正拦下的兜底deny fs_write。请用agentgate check验证写入规则,但不要指望它们在真实运行中生效——目前只有exec与net_egress是运行时强制的。
写完策略,想知道「Agent 真要做某个动作时会怎样」?agentgate check 把一个假想动作丢给策略,打印决策(allow / deny / ask)与命中原因——不跑任何子进程、不发起任何 egress、不写审计日志。
agentgate check --action exec -- npm install left-pad
# action : exec
# target : npm install left-pad
# intent : agent wants to install npm package: left-pad
# decision: ask (matched a rule)
agentgate check --action net_egress github.com.evil.com:443
# decision: deny (no rule matched, fell through to default)
agentgate check --action fs_write /etc/passwd
# decision: deny (matched an allow rule but the path escapes its scope)--action 取 exec(默认)/ fs_write / net_egress,--policy 指定要检查的策略文件。
按了几次 [A]lways 之后,规则会悄悄写回 policy.yaml——一道运行时门控只有在你能看清它到底会放行什么时才值得信任。agentgate policy 按首条匹配即生效的顺序打印全部生效规则(含 --always 追加的那些)的动作、目标 glob、决策与 scope,最后一行是无规则命中时的默认决策:
agentgate policy
# # effective policy (policy.yaml) — first match wins, top to bottom
# # ACTION TARGET DECISION SCOPE
# 1 net_egress registry.npmjs.org allow -
# 2 fs_write /proj/** allow /proj
# 3 exec npm install* allow -
# * any any ask -加 --explain 则只解析一个假想动作,告诉你它会命中哪条规则(复用与 agentgate check 相同的无副作用解析器):
agentgate policy --explain --action exec "npm install chalk"
# decision: allow (matched a rule)
# matched : action=exec target=npm install* -> allow看清规则只是一半——如果发现某个 [A]lways 授权得太宽,你还得能把它收回来。在 v0.5.0 之前,这意味着手动去改 policy.yaml;现在 agentgate policy rm 让「撤销一次错误授权」和「当初做出授权」一样简单。按上面列出的 1 起始序号删,或按 --action + --target 精确匹配删;被删的规则和更新后的生效表都会打印出来:
# 按序号删(就是 agentgate policy 打印的 # 列)
agentgate policy rm 3
# removed rule #3: action=exec target=npm install* -> allow
# (随后重新打印生效规则表)
# 或按 动作 + 目标 glob 精确匹配删
agentgate policy rm --action net_egress --target "registry.npmjs.org"序号越界、默认行(*)或匹配不到规则时,命令会给出清晰错误并以非零码退出,且不改动策略文件。删掉规则后,它此前自动放行的同类动作会重新回到提示/默认决策——授权是真正被收回了,不是只从表里消失。(本版只做删除:改写某条规则或调整顺序仍是删掉再加,或手动编辑 policy.yaml。)
v0.4.0 修复:早先
[A]lways放行一条 exec 时,持久化的是完整命令行原文(如npm install left-pad)。它不含通配,于是下一次npm install chalk命中不了、又来打扰你,--always形同虚设。现在 exec 的[A]lways会按「二进制 + 子命令」生成可复用 glob(npm install*),既覆盖同类后续安装,又不会过宽到放行pip install。
从零写策略容易漏。examples/policies/supply-chain.yaml 收录了针对真实供应链攻击行为的即用规则——每条都标注了它挡的是哪种攻击,且全部 glob-正确(不含 | 伪替代,见下):
| 配方 | 挡住的攻击 |
|---|---|
deny exec *curl* / deny exec *wget* |
装后脚本 `curl http://evil / wget … |
ask exec *npm install* / *pip install* |
每次依赖安装都浮现,看清拉了什么包(typosquat / 投毒) |
deny net_egress 兜底 + registry 白名单 |
载荷向未声明主机外泄数据 / 回连 C2 |
deny exec *chmod +x* 等 |
装后脚本给自己提权 / 落可执行文件 |
复制其中一条到你的 policy.yaml,再用 agentgate check 确认它确实拦下对应命令:
cp examples/policies/supply-chain.yaml policy.yaml # 或把某几条粘进你现有的策略
agentgate check --action exec -- curl http://evil.example
# decision: deny (matched a rule)为什么不能用
*curl*|*sh*一条搞定? glob 匹配器没有|替代语义——filepath.Match把|当字面字符。所以*curl*|*sh*只会命中「命令里恰好含curl … | … sh这段字面」的情况,curl http://evil(无字面竖线)和wget … | sh(无curl)都会漏过。cookbook 里一律拆成独立规则(*curl*、*wget*各一条),别再把|当替代用。
agentgate run 的常用开关:
| 选项 | 类型 | 默认值 | 含义 |
|---|---|---|---|
--policy |
string | ./policy.yaml(或 $AGENTGATE_POLICY) |
使用的策略文件 |
--audit |
string | .agentgate/audit.jsonl(或 $AGENTGATE_AUDIT) |
追加式 JSONL 审计日志路径 |
--agent |
string | claude-code |
被包裹 Agent 的标识,会带进提示与审计 |
--no-net |
bool | false |
关闭网络 egress 门控(仅门控 exec / fs) |
--always |
bool | true |
把 [A]lways 选择持久化写回策略文件 |
--enforce |
bool | false |
无人值守模式:不弹任何提示,每个 ask 直接 deny(默认拒绝),适配 CI |
CI 流水线里没有操作员可以回答提示。agentgate run --enforce 用空 prompter 启动引擎——每个 ask 直接落到 deny(默认拒绝),整个运行永不等待 TTY:
agentgate run --enforce -- npm ci
# agentgate: --enforce (headless): no prompts, ask resolves to deny (deny-by-default)只有被策略显式 allow 的动作才放行;其余一律拦下并落审计。该模式下 --always 持久化自动关闭(没有操作员可以选 [A]lways)。
诚实的定位——容器在隔离上比我们成熟得多;AgentGate 解决的是另一个问题:按动作、带意图、运行时。
| 维度 | AgentGate | 容器 / 一次性 VM | 静态依赖扫描器 |
|---|---|---|---|
| 按动作逐个授权 | ✓ | ✗(全有或全无) | ✗ |
| 携带 Agent 意图 | ✓ | ✗ | ✗ |
| 运行时拦截载荷 | ✓ | 部分(边界内不区分动作) | ✗(安装前读包,错过运行时载荷) |
| 成熟的进程隔离 | 部分(spawn + egress 边界) | ✓ | — |
| 安装时不被关掉换效率 | ✓ | ✗(常因影响效率被禁用) | — |
- m1 —— wrap & gate exec:包裹 Agent,拦截已配置 shim 列表(npm、pip、curl、wget、……)上的子进程——自定义二进制和直接 shell spawn 不受门控;通用 exec 插桩在路线图上——带意图提示 allow/deny。
- m2 —— scope fs & net:
policy.yaml按主机门控 egress 并写入 JSONL 审计;fs_write规则可由agentgate check解析(check / dry-run 专用,运行时强制见下方 v0.7.0+ 路线图)。 - m3 —— DSL & 演示:
allow/deny/askDSL +--always持久化、agentgate init默认策略、60 秒 asciinema 演示、双语 README。 - m4 —— 写策略 & 审策略:
agentgate check对任意动作做 dry-run;egress 按主机边界匹配,堵住伪造主机绕过;.hosttoken 把规则限定在子域树内。 - m5 —— CI 与排障:
agentgate run --enforce无人值守默认拒绝模式(CI 不再卡在 TTY 提示);agentgate audit支持按--decision/--action/--since过滤与--json输出;修复符号链接越界写入与**路径 glob 子串过宽匹配两处沙箱缺陷。 - m6 —— 看策略 & 可复用 always:
agentgate policy按生效顺序打印全部规则(含--always追加项)+--explain解析单个动作;修复 exec 的[A]lways持久化命令行原文导致下一次同类安装仍被打扰的缺陷(改为「二进制 + 子命令」可复用 glob)。 - m7 —— 撤策略(闭合 看→改 回路):
agentgate policy rm <序号>/--action --target从 CLI 撤销一条持久化规则,让收回一次过宽的[A]lways授权和当初做出它一样简单——不必再手改policy.yaml。 - m8 —— 供应链策略 cookbook + 安全实话:附带
examples/policies/supply-chain.yaml(针对真实供应链行为的即用 policy 配方,见下方 Cookbook);同时修正四处安全表述/失效开放缺陷 ——fs_write不再宣称运行时强制(改为 check/dry-run 专用)、broker 不可达时 shim 失败即拒(不再 ungated 放行)、示例里失效的*curl*|*sh*规则改为 glob-正确的独立规则、net 代理绑定失败时报错退出(不再静默放行 egress)。 - 运行时 fs_write 强制(v0.7.0+):Linux ptrace/eBPF、macOS LD_PRELOAD/sandbox-exec 写入插桩,让
fs_write从 check-only 升级为真正的运行时拦截。 - 更多 harness 的开箱适配与 README 安全章节集成(ECC / openfang,v0.7.0)。
- 团队共享策略 / 审计仪表盘(v2+ 探索,非当前论点)。
推送仓库后可设置 GitHub topics:
gh repo edit --add-topic agent --add-topic coding-agent --add-topic security --add-topic sandbox
AgentGate 免费、MIT 许可、单文件二进制的开源软件——没有付费墙,没有托管层。欢迎通过 issue 反馈问题或提交 PR。
AgentGate — a runtime per-action host gate for your Coding Agent. It pauses each
install / script / egress with the agent's own intent, instead of all-or-nothing
containers. After the Miasma worm, your agent needs a seatbelt.
https://github.com/SuperMarioYL/agentgate
MIT © 2026 SuperMarioYL
