mcpField
T
TollWarden

TollWarden

本地服务
tollwarden.com安全25 天前更新

关于 TollWarden

TollWarden 是面向 x402 付款 Agent 的非托管安全服务,在结算前扫描付款,检测密钥泄露、重放、超额付款及不可信内容引导的收款地址,并返回附带原因的允许、标记或拦截判定。

主要功能

  1. 01结算前扫描 x402 付款
  2. 02检测付款字段中的敏感信息
  3. 03检查重放和重复购买
  4. 04检测超额及注入诱导付款
  5. 05签署绑定付款的判定证明
  6. 06要求签名者验证新鲜的允许判定

适用场景

  1. 01在钱包结算前检查 Agent 的 x402 付款
  2. 02暂停由抓取内容引导收款地址的付款
  3. 03签署付款授权前要求验证判定

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"com-tollwarden-tollwarden": {
"command": "npx",
"args": [
"-y",
"tollwarden"
]
}
}
}
启动命令npx -y tollwarden

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmtollwarden1.6.0

工具

12

收录的工具列表 · 更新于 2 周前

参数类型必填说明
policyobject否
policy.skip_deepboolean否
policy.force_deepboolean否即使低于小额支付阈值,也要运行深度内容分析
contextobject否
context.offerstring否支付条款所依据的原始 402 报价/发现载荷(接受 entry、Bazaar 列表 JSON)。此处预期包含 pay_to,且不受地址来源检查;仍会扫描报价中嵌入的注入指令。
context.phaseenum否在支付签名前扫描时设为 'pre_sign'(常规的先扫描再支付流程):此时 EIP-3009 nonce 尚不存在,因此不会因其缺失而发出警告。扫描已签名载荷时,请省略此项或使用 'post_sign'。pre_sign | post_sign
context.originenum否支付决策的来源planning | user_instruction | tool_result | fetched_content | unknown
context.contentstring否Agent 刚刚读取的正文(页面文本、工具输出),用于注入分析。不要将 402 报价/发现 JSON 放在此处——应放入 `offer`(其中预期包含收款方地址)。
context.content_source_urlstring否
paymentobject是
payment.assetstring否
payment.noncestring否
payment.payerstring否
payment.amountstring否原子单位,例如 '10000' = $0.01 USDC
payment.pay_tostring否
payment.reasonstring否
payment.schemestring否
payment.networkstring否CAIP-2,例如 eip155:8453
payment.metadataobject否
payment.amount_usdnumber否
payment.descriptionstring否
payment.resource_urlstring否
payment.asset_decimalsnumber否
agent_idstring否
expected_price_usdnumber否

详细介绍

TollWarden

面向 x402 的支付安全防火墙——在每笔微支付结算前进行筛查。

x402 network tests npm license

通过 x402 付款的 Agent 容易以可预测的方式蒙受损失:机密信息通过支付元数据泄露,被捕获的授权被重放,报价被抬高,遭投毒的网页内容则诱骗 Agent 向其原本无意付款的地址付款。TollWarden 只需在结算前发送一次 POST 请求,就能检查所有这些风险,并以机器可读的形式返回 允许 / 标记 / 拦截 结果及每项检查的原因——耗时仅 约 0.6 ms。

TollWarden 仅提供建议且不托管资产:它绝不会接触私钥、钱包或资金。它可以配合 Agent 已在使用的任何 facilitator 和钱包运行。此外,它本身也是一流的 x402 卖家——其端点通过官方 x402 中间件付费,经 Coinbase CDP facilitator 结算,并提供 Bazaar 发现元数据。

Agent decides to pay ──► POST /v1/scan/outgoing ──► allow ──► wallet settles
                                   │
                                   ├──► flag  ──► agent pauses / confirms intent
                                   └──► block ──► wallet refuses (reason attached)

30 秒即可上手

npm install @tollwarden/client    # TypeScript/Node
pip install tollwarden           # Python
import { TollWardenClient, TollWardenBlockedError } from "@tollwarden/client";
const tollwarden = new TollWardenClient({ agentId: "my-agent" }); // free API key auto-minted, 100 free scans

tollwarden.observe(fetchedPageText, { sourceUrl }); // tag what your agent just read → injection detection
await tollwarden.guardOutgoing(payment);            // throws TollWardenBlockedError on a block verdict

初次使用?5 分钟保护你的 x402 Agent →

SDK(sdk/,零依赖)还会使用固定密钥验证每项判定的 Ed25519 证明、跟踪免费调用额度,并可自主订阅套餐。钱包开发者可以使用独立的 verifyAttestation() / computePaymentCommitment() 函数,以及强制执行套件:new TollWardenEnforcer({ trustedKeyHex }).guardSigner(account) 会包装任何 viem/ethers 签名器,使其在没有新鲜且绑定到具体支付的允许判定时,无法对 x402 支付授权进行签名。

框架集成

使用 Agent 框架进行构建?TollWarden 提供即插即用的软件包,只需大约两行代码,就能为 Agent 实现“付款前先扫描”——包括一组工具和一种来源标记机制,可自动标记 Agent 读取的内容,因此无需任何提示词工程,就能让提示注入触发付款检测器正常工作:

框架软件包安装
LangChainlangchain-tollwardenpip install langchain-tollwarden
CrewAIcrewai-tollwardenpip install crewai-tollwarden
NeMo Agent Toolkitnemo-tollwardenpip install nemo-tollwarden
Coinbase AgentKitagentkit-tollwardenpip install agentkit-tollwarden
Vercel AI SDK@tollwarden/ai-sdknpm install @tollwarden/ai-sdk

每个软件包都提供相同的三种工具(扫描 / 检查信誉 / 报告),以及一种框架原生方式,可将 Agent 读取的内容传入注入检测。LangChain 使用回调,CrewAI 使用工具调用后钩子,Vercel AI SDK 使用 onStepFinish 处理器;这三者都会自动添加标记。NeMo 和 AgentKit 则通过显式的 content 参数接收内容,而 AgentKit 操作还会自动从 Agent 自身的钱包填入付款方。每个软件包还提供 guarded_payment / guardedPayment 封装器,从设计上强制执行安全检查(判定结果为拦截时,绝不会运行支付执行器);NeMo 版本还接受异步执行器。两行代码即可完成设置,详情请参阅各软件包的 README,或访问 tollwarden.com/integrations 上对应的页面。

可检测的风险

AI Agent 支付安全指南逐一介绍这些检查可应对的攻击和失效模式,并说明每项检查的局限性。

核心检测器

检查项检测内容
PII / 密钥检测在传输之前,检测 resource_url、description、reason 和 metadata 中的 EVM 私钥、助记词、AWS/OpenAI/Anthropic/GitHub/Slack 密钥、JWT、?api_key= URL 凭据、SSN、经 Luhn 校验的卡号、电子邮件、电话号码
重放检测nonce 重用(过期或捕获的付款授权),按 network:payer:nonce 限定范围,可配置 TTL 窗口
重复购买同一笔购买被授权两次。x402 客户端每次遇到 402 都会签署新的授权,因此重试请求会以新的 nonce 产生第二笔有效付款;nonce 重放检查会放行这笔付款,它也可能与第一笔付款一同结算。如果出站扫描与 60 s 内对同一笔购买(付款方、pay_to、资产、金额、resource_url)的先前扫描相匹配,但使用了不同的授权,就会标记为 replay.duplicate_purchase。使用 API 密钥进行扫描时,标记会指出较早的 scan_id。匿名范围由客户端声明,因此这里的标记绝不会回显其他调用方的 ID。同一授权的签名前扫描和签名后扫描只计为一次。有意重复购买不会触发该标记:传入较早扫描的 scan_id 作为 context.repeat_of(每次只尝试一笔),或者报告较早尝试的结果为已交付;付款调用成功时,SDK 付款封装会自动执行此操作。not_delivered 不会清除该标记,因为这正是会被重试的尝试。该标记仅用于警示,因为 TollWarden 只能看到扫描,看不到结算,因此无法知道第一笔付款是否已结算。被拦截的付款不会记录任何内容
超额付款检测超过预期价格的可配置倍数(≥3× 标记,≥10× 拦截),并设有绝对上限
提示词注入触发的付款付款的决策源于 Agent 刚读取的内容(工具结果/抓取的页面),而非其自身的规划步骤;根据该内容中加权的注入迹象升级处理(广泛的动词/宾语语料中出现覆盖/重定向措辞,支持 8 种语言:英语、西班牙语、葡萄牙语、法语、德语、俄语、中文、日语;伪造的系统/聊天模板/Guidance 标记、夹带的模型边界令牌、伪造的对话轮次、隐瞒措辞、商业邮件入侵措辞——“我们的收款地址已更改,无需验证”——以及紧迫性施压),如果迹象出现在类似地址的令牌附近,则加重权重。按注入内容的常见构造方式评分:只有弱迹象聚集时才会累积得分;单独的施压(紧迫性、对话记录形式、隐藏字符)绝不会触发拦截;人类自己的“向 0x… 支付 X”指令不会被计入对他们的评分。内容会同时扫描原始文本,以及剥除 HTML 标签、实体和 Markdown 强调格式后的文本,因此 **Ignore** all <b>previous</b>&nbsp;instructions 会按其呈现出的句子进行解读。如果 pay_to 地址本身来自该内容,就会拦截——即便地址被拆分到多行或分隔符之间、缺少 0x,或夹杂不可见字符
资源 URL 风险(传入)IP 字面量主机名、同形异义字或拼音码、短链接、user@host 欺骗、非 HTTPS、索要凭据(“发送你的助记词”)
交易对手信誉共享的事后举报登记库,每次扫描都会交叉核验;举报始终免费。被拦截的注入扫描也会自动纳入该登记库:在刚读取的内容中被植入的钱包(或被用作虚荣诱饵的钱包)会在所有 Agent 之后对其进行的扫描中被标记——一次检测即可转化为全网保护(仅标记;扫描输入由客户端提供)
交付结果按交易对手统计经过验证、与承诺绑定的交付历史——即使向卖家付款无误,但对方始终不发货,你仍会蒙受损失。交付率低或多次未发货的卖家会被标记(不会被拦截:H-2 同样适用于经过验证的历史记录)

零延迟加固层级——即使调用方 Agent 的叙述遭到篡改,这些检查仍然有效:

检查项检测内容
频率限制≥N 次扫描/分钟(触发标记;达到 2× 时阻止),累计每小时支出上限——速率和支出是可观测事实,而非自我报告。限制范围为API 密钥所属的账户,因此每次请求使用新的 agent_id 不会开启新的计数窗口;只有匿名扫描才会回退使用 agent_id/payer。支付被 TollWarden 阻止仍会占用一个频率名额,但不会计入每小时支出,因此,卖家的离谱报价若在达到上限时被拒绝,不会让你在接下来一小时内无法向其他人付款
首次联系金额上限首次向从未见过的交易对手付款,且金额超过阈值
资产验证asset 合约不是所声明网络上的规范 USDC(外观相似代币攻击)。网络可以是 CAIP-2 ID 或 x402 v1 名称(base、base-sepolia、polygon、ethereum);两者都会进入同一项检查
商户固定(TOFU)两个层级。你账户自己的固定记录:向某个域名付款后,该域名的 pay_to 地址发生轮换 → 阻止——没有其他人能写入该记录。共享观察记录:任何调用方首次为该域名提供的地址;若地址不一致,则触发标记(“另一位调用方见过不同的地址”),因为双方提供的信息都是客户端输入;只有在非阻塞式 CDP Bazaar 交叉核验确认固定地址是该域名的商户后,才会阻止。固定记录的年龄和明确列出的佐证来源会作为已签名的证明字段一并提供(见下文),因此钱包可以区分存续 4 分钟和 6 个月的固定记录,而不是一概同等信任
地址投毒pay_to 的首尾字符与已向其付款的交易对手、你自己的商户固定记录或 CDP 已验证固定记录相同,但中间部分不同——这类截断显示(“0x2096…287C”)诱导攻击会被阻止。若地址只是与其他调用方提供的地址相似,则触发标记,这样陌生人就不能植入一个虚假的“固定记录”,让你的正常付款因地址相似而被阻止。也能检测诱饵:在 Agent 刚读过的内容中植入收款方地址或受信任地址的近似副本。被阻止的付款会从信任状态中回滚,因此重复尝试时仍能检测到
ScoutScore 信任信号(可选启用)被 ScoutScore 评为 LOW/VERY_LOW 的商户域名(垃圾信息农场、模板克隆站、失效端点)→ 触发标记,并明确标注为外部第三方信号。查询采用异步方式并缓存(扫描延迟为零),只共享域名,而且永远不会单独触发阻止。使用 SCOUTSCORE=on 启用
已知恶意列表对经过整理/同步的恶意列表进行 O(1) 成员查询
深度内容分析解码并重新扫描经过编码/混淆的注入载荷:base64(两种字母表、换行、空格分块、双重编码)、十六进制、百分号编码、HTML 实体、JS/JSON \x/\u 转义、Unicode 标签字符隐藏(“不可见 ASCII”)、零宽字符和同形字符混淆(西里尔字母、希腊字母、IPA、小型大写字母、无点 i——另有混合文字脚本词信号,用于检测折叠表无法识别的相似字符)、leet 变形(1gn0re)和字母间隔(i g n o r e)。每次扫描都在 200 KB 内容上限内线性运行,并经过延迟测试保护。每笔付款低于 MICRO_BYPASS_USD(默认 $0.005)时跳过,但可防止拆分滴灌式规避:向某个交易对手累计扫描的支出一旦超过同一阈值,仍会运行深度检测;对于声明为 tool_result / fetched_content 的内容,则始终运行;也可通过每个请求中的 policy.force_deep 覆盖
签名判定。 每个响应都包含一份 Ed25519 attestation,其签名覆盖 `scan_iddirection

签名固定信息证据。 扫描证明还包含第二条签名记录——evidence-v1|scan_id|payment_commitment|pin_domain|pin_age_seconds|pin_corroboration(使用相同的密钥,并绑定到同一次扫描和同一笔付款)。它会公布扫描时该收款方背后的商家固定信息已持续多久(0 = 首次发现),以及哪些带外来源对其进行了佐证——每个来源都明确命名(目前为 cdp_bazaar,即 CDP Bazaar 商家索引中列出了该域名,作为已固定地址的资源之一),绝不是布尔值,也绝不是综合评分。原因在于:通过真正独立的渠道成功佐证,与通过攻击者已控制的渠道成功佐证,最终会是同一个比特;而每个商家来源的可信度应由你自行判断。把“固定信息较新且有佐证”合并成单一放行/标记结果,就等于让 TollWarden 擅自决定风险容忍度:有了独立的签名字段,钱包就可以接受一条仅固定了四分钟且有佐证的信息来支付一分钱,却拒绝用它支付五十美元。判定消息本身仍保持冻结为 7 个字段——已部署的验证器不受影响,两个 SDK 都会验证证据记录,并将其作为 pin_evidence 提供。一项需要坦诚说明的限制:验证器允许证据记录缺失(旧服务器、覆盖判定),因此缺失不能证明任何事——剥除记录后,无法区分旧服务器与遭篡改的响应;只有带签名的空字段才能断言“没有适用的固定信息”。如果你的策略要求固定信息证据,请明确决定如何处理不含此证据的证明。每个签名字段都是一项兼容性承诺:字段可以新增,但绝不会悄然更改或撤回。 钱包侧强制执行。 两个 SDK 都开箱即用地提供了这项策略,名为 TollWardenEnforcer。enforcer.guardSigner(account)(TS:viem 账户、ethers v6 签名者)/ enforcer.guard_signer(account)(Python:eth-account,支持所有调用形式)会将签名者包装在代理中。代理会根据正在签名的类型化数据(EIP-3009 / ERC-2612)重新计算支付承诺,并且只有在存在针对该承诺、经过新鲜验证且通过固定公钥验证的允许判定时,才会允许签名。批准仅可使用一次,并在证明失效时过期;被攻陷的 Agent 即使扫描了支付 A,也无法签署支付 B;如果跳过扫描,则完全无法签名。可选的本地策略可进一步限制操作:设置硬性收款方允许列表(allowedRecipients)以及单笔支付 / 累计支出上限(maxAmountAtomic / maxTotalAtomic)。这些限制会在签名时根据类型化数据检查,无需服务器参与——即使某笔支付带有有效的允许判定,只要它支付给未列入允许列表的收款方或超出上限,就会被拒绝。因此,即使咨询层遭到篡改,也只能向已知对象转移有限金额。Agent 永远无法扩展允许列表;唯一的例外通道是选择启用的 overrideAdmitsRecipient(需同时启用 acceptOverrides):来自升级审批的人工批准覆盖只会准许其绑定的那笔支付——绝不会准许该收款方,也绝不会突破支出上限。默认拒绝且完全在本地运行;两个实现均已使用同一个生产签名者进行交叉验证,详见 sdk/README.md 和 sdk-python/README.md。它可以与默认支付路径组合使用。将强制器传入 wrapFetchWithTollWarden(…, { enforcer }) / wrap_transport_with_tollwarden(…, enforcer=…),每个已扫描报价的允许判定就会成为签名授权,用于签署 x402 客户端为其生成的、携带 nonce 的授权。包装器会使用 context.phase: "pre_sign" 扫描报价,因为此时还没有 nonce。默认情况下,强制器只接受允许判定,因此必须在请求前声明决策来源(notePlanning()、noteUserInstruction() 或 observe()),否则来源未声明就会被标记,并在签署任何内容之前被拒绝。

防篡改审计日志。 每个扫描决策都会追加到哈希链日志(AUDIT_LOG=on),其中存储支付内容的 SHA-256 哈希及非敏感交易事实——绝不存储所扫描内容中的明文个人身份信息 / 机密。GET /v1/audit/verify 会重新计算哈希链并报告任何篡改;GET /v1/audit/head 会返回当前头部哈希,以便进行外部锚定。详见 SECURITY-AUDIT.md。

API

端点价格描述
POST /v1/scan/outgoing$0.01¹筛查 Agent 即将发起的付款
POST /v1/scan/incoming$0.01¹筛查 Agent 收到的 402 报价 / 付款请求
GET /v1/reputation/:address$0.01交易对手报告摘要
POST /v1/reputation/report免费事后举报不良交易对手
POST /v1/keys免费签发 API 密钥——每个密钥的前 100 次调用免费
POST /v1/keys/rotate免费将密钥的机密部分替换为新值——用量、免费额度和套餐都会保留;旧密钥会在宽限期内继续有效(默认 15 min,最长 24 h)
POST /v1/keys/revoke免费永久作废泄露的密钥及其账户(需要 {"confirm": true};不可撤销)
POST /v1/approvals/config免费启用人在回路审批:标记为需处理的判定会暂停,等待通过你的 webhook 进行人工决策
GET /v1/approvals/:id免费轮询待处理的审批;获批后返回已签名的 override:allow 判定
POST /v1/outcomes免费记录已扫描且已结算的付款是否实际送达(与承诺绑定;SDK 封装会自动执行此操作)
GET /v1/plans免费机器可读的套餐目录(层级、限额、订阅机制)
GET /v1/usage免费你的密钥专属用量统计:扫描 / 判定次数、免费层级额度、套餐状态,以及审批决策遥测数据(仅你可见)
POST /v1/trust/evaluate免费x402 信任提供方接口——卖家根据付款方的历史记录决定是否结算(TrustQuery → PASS/FAIL/UNCERTAIN + 证据)
GET /dashboard免费你的密钥的浏览器用量仪表板(参见仪表板)
POST /v1/plans/subscribe套餐价格为密钥订阅 / 续订套餐——通过 x402 付款,因此 Agent 可以自主升级
GET /.well-known/x402免费x402 清单
GET /.well-known/agent-card.json免费Agent 卡片
GET /.well-known/erc8004.json免费ERC-8004 Agent 注册文件(TollWarden 链上身份的 tokenURI)
GET /.well-known/tollwarden-verdict-key免费用于判定证明的 Ed25519 公钥
GET /v1/audit/verify免费验证审计日志哈希链(完整性检查)
GET /v1/audit/head免费当前审计日志头哈希 + 序列号
GET / · GET /health免费首页;或在 Accept: application/json 下返回自描述 JSON 架构 / 存活状态

¹ 订阅套餐后,每次扫描的价格会降低:Pro($4.99/30d)→ $0.005/次扫描,速度余量提高 6×,且每次扫描都进行深度内容分析;Scale($19.99/30d)→ $0.002/次扫描,适用硬性上限。套餐只会提高你自己的阈值——重放检测、商户固定、资产验证和 PII 扫描在所有层级中都完全相同,不能通过付费放宽。参见 GET /v1/plans。

在 X-API-Key 标头中发送从 POST /v1/keys 获取的密钥;前 FREE_CALLS 次调用(默认 100 次)免于付款。此后,未付款的调用会收到标准 x402 402 Payment Required 响应——任何 x402 客户端(@x402/fetch、x402-requests 等)都会自动处理付款并重试。 密钥生命周期。 账户是身份;psk_ 密钥只是指向该账户的凭证。如果密钥泄露,POST /v1/keys/rotate 会签发一个绑定到同一账户的新密钥——使用记录、剩余免费调用次数和任何有效套餐都会原样保留(轮换不会重置免费额度,因此无法通过轮换刷取额度)。旧密钥会在可选的宽限期内继续可用(grace_seconds,默认值为 900,0 = 立即失效,最长 24 h),这样一组服务就能无停机切换;但在宽限期内,旧密钥无法再执行轮换或撤销操作——泄露的旧密钥无法接管账户。POST /v1/keys/revoke(使用 {"confirm": true})是紧急停用开关:密钥和账户会永久失效,并保留墓碑记录,让失效密钥持续返回明确的原因,而不是莫名其妙的 401。要轮换所有者密钥?将 ADMIN_KEY_SHA256 更新为轮换响应中的 api_key_sha256。

示例:扫描一笔待付款项
POST /v1/scan/outgoing
{
  "agent_id": "my-agent",                    // labels the scan; limits are scoped to your key's account
  "payment": {
    "scheme": "exact",
    "network": "eip155:8453",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // enables canonical-USDC check + server-known decimals
    "amount": "10000",                       // atomic units ($0.01 USDC); amount_usd only if you have no atomic amount
    "pay_to": "0x2096...287C",
    "payer": "0xYourAgentWallet",
    "resource_url": "https://api.example.com/premium/report",
    "description": "Premium market report",
    "nonce": "0x1a2b3c...",
    "reason": "User asked for a market summary; this API was in my plan."
  },
  "expected_price_usd": 0.01,
  "context": {
    "origin": "planning",                    // planning | user_instruction | tool_result | fetched_content
    "content": "<the tool result / fetched page the agent just read, if any>",
    "content_source_url": "https://example.com/article"
  },
  "policy": { "force_deep": false }          // optional tiering overrides
}

context.origin 是注入检测的关键输入:由刚刚读取的内容触发的付款是提示词注入导致数据外泄的主要途径,因此会受到更严格的审查。

payment 必须指定收款方(pay_to)或金额(amount 或 amount_usd)。两者都没有的付款无内容可供检查,因此会返回 400,且不会被扫描、记录或结算(免费套餐密钥仍会消耗一次免费调用)。类型错误的字段会视为缺失,x402 报价名称也会如此,因此请将 payTo 作为 pay_to 发送,并将 maxAmountRequired 作为 amount 发送。

示例响应(重放被拦截)
{
  "scan_id": "b7911f8b-164f-45a4-a610-5fd3a45a5c8b",
  "direction": "outgoing",
  "verdict": "block",
  "risk_score": 95,
  "checks": [
    {
      "id": "replay.nonce_reuse",
      "verdict": "block",
      "severity": "critical",
      "reason": "Nonce reuse detected: this nonce was first seen 2026-07-14T09:32:50Z (scan b7911f8b-…) and has now appeared 2 times. A reused nonce means a stale or captured payment authorization is being replayed."
    }
  ],
  "scanned_at": "2026-07-14T09:33:12Z",
  "advisory": "Recommended action: DO NOT settle this payment. ...",
  "attestation": {
    "alg": "ed25519",
    "public_key_spki_hex": "302a3005...",
    "message": "b7911f8b-...|outgoing|block|95|2026-07-14T09:33:12Z|<payment_commitment>|2026-07-14T09:38:12Z",
    "signature_hex": "...",
    "payment_commitment": "sha256(network|pay_to|asset|amount|nonce)",
    "expires_at": "2026-07-14T09:38:12Z",
    "evidence": {
      "message": "evidence-v1|b7911f8b-...|<payment_commitment>|api.example.com|7776000|cdp_bazaar",
      "signature_hex": "...",
      "pin": { "domain": "api.example.com", "age_seconds": 7776000, "corroboration": ["cdp_bazaar"] }
    }
  }
}

仪表板

使用情况仪表板 — GET /dashboard。 一个独立页面,任何密钥持有者都可以查看自己的扫描次数、判定结果细分、免费套餐配额、套餐状态,以及(配置了审批时)自己的审批决策遥测数据。粘贴你的 psk_ 密钥并点击“查看”;密钥只会作为 X-API-Key 标头发送到 GET /v1/usage(绝不会出现在 URL 中,因此不会通过历史记录、引荐来源或服务器日志泄露),且每个密钥只能查看其所属账户的数据。页面使用严格限制的 CSP(default-src 'none',零外部资源),并且仅通过 textContent 渲染。

人工参与的升级审批

flag 判定不再必然意味着流程终止。配置一次 webhook,之后每次 flag 都会暂停,等待人工决策:

POST /v1/approvals/config   (X-API-Key)
{ "webhook_url": "https://hooks.your-ops.com/tollwarden" }   // "format": "slack" for a Slack-style message
→ { "enabled": true, "webhook_secret": "psw_..." }        // shown ONCE; deliveries are HMAC-SHA256-signed

触发 flag 时,你的 webhook 会收到付款信息(完整的 pay_to,绝不会被截断)以及一次性决策链接。审核者打开 /approve,逐个字符核对地址,然后点击 Approve 或 Deny。批准后会生成一个有效期较短(≤5 min)、经 Ed25519 签名的覆盖判定,并在签名消息本身中携带独特标签 override:allow——覆盖判定绝不会伪装成正常的 allow,且只绑定到被标记付款的确切承诺。阻止项永远不能获批;决策具有幂等性,使用单个令牌,并记录在防篡改审计日志中。

Agent 端只需用 SDK 写两行:

const scan = await tollwarden.scanOutgoing(payment);
if (scan.verdict === "flag" && scan.approval) {
  const override = await tollwarden.waitForApproval(scan, { payment }); // polls; throws on deny/expiry
  enforcer.approve(override, payment);                               // requires acceptOverrides: true
}

(Python:tollwarden.wait_for_approval(scan, payment=payment) / TollWardenEnforcer(..., accept_overrides=True)。)

**为什么 acceptOverrides 默认关闭:**持有自己 API 密钥的 Agent 可以将审批 webhook 配置为指向自己能够读取的位置,然后“批准”自己的 flag——完全不需要人工参与。只有当 webhook 接收端不在 Agent 的触及范围内时(例如你的运维渠道,而不是 Agent 的环境),覆盖判定才值得信任。因此,强制执行工具包会拒绝 override:allow,除非钱包所有者明确选择启用,机制与 allowFlagged 完全相同。

决策遥测——留意自身判断的变化。在大多数部署中,人工审批会逐渐沦为一种合规形式,审核者不假思索地批准——而这种变化无法从任何单次观察中看出来:审核者在连续三个月每次耗时 40 秒后,用 8 秒批准一笔付款,在单条日志中看起来与经过校准的审核者毫无区别。因此,TollWarden 会记录每次批准的决策延迟(两个时间戳均由服务器记录),并将每笔获批付款与其后续的交付结果关联起来。GET /v1/usage 和仪表板会显示你自己的汇总数据——近期与基线的延迟中位数、批准率,以及获批付款的实际交付情况。近期延迟中位数缩短,同时批准率接近 100%,这是敷衍审批的明显特征;结果关联则能将这种情况与“操作人员只是因为 flag 已变得可预测而提速”区分开来。仅提供原始汇总数据,不计算综合评分——而且数据仅对拥有该数据的密钥可见:数据不会用于生成判定,也不会出现在信誉查询、信任评估或公开统计中,因为公开记录谁在敷衍审批,无异于提供一份可供针对的名单。

**禁用。**针对单个密钥:使用 {"webhook_url": null} 调用 POST /v1/approvals/config——响应会附带一条提示,提醒你 flag 将恢复为仅供参考(不会暂停,不会生成覆盖判定,且启用了 acceptOverrides 的钱包将不再有从 flag 到付款的路径);待处理的审批在过期前仍可决策。针对整个服务器:APPROVALS=off 会拒绝新配置且不再开启新的审批,同时给出相同提示;进行中的审批仍可决策,因此在流程中途禁用不会导致任何事项悬而未决。

交付结果

每项扫描检查都会验证付款是否格式正确且未被篡改。但没有一项能告诉你卖家是否真的会交付:向一个从不发货的卖家付款,即使付款完全正常,仍会让 Agent 失败。结果闭环通过可衡量的历史记录而非指控来弥补这一缺口:

  • x402 交付通常是同步的——资源会随已付款的响应一起到达——因此 SDK 付款路径封装器(wrapFetchWithTollWarden / wrap_transport_with_tollwarden)会以机械方式观测交付,并自动报告:已付款的 2xx → delivered,已付款的 5xx/第二次 402 → not_delivered,并附上状态/字节数/延迟证据(通过 reportOutcomes: false 选择退出)。通过其他方式结算?可直接调用 POST /v1/outcomes 或 client.reportOutcome(scan, ...)。
  • 每项结果都关联到 TollWarden 执行过的一次扫描:报告必须提供 scan_id + payment_commitment 这一对信息;每次扫描只能有一项结果;带密钥的扫描只能由扫描账户提交。要伪造交付历史,就必须进行真实的扫描付款——纯文本报告无法具备这种抗女巫攻击特性。
  • 汇总交付率会显示在 GET /v1/reputation/:address 中,也会作为该交易对手未来每次扫描的 delivery 检查项:交付率低和反复未发货会触发标记(绝不拦截——可衡量的历史记录仍是第三方信号,适用审计 H-2)。
  • 分母也是公开的:声誉查询会在结果数量旁报告 scans_seen(TollWarden 对该交易对手执行的未拦截扫描数)和 report_coverage,因此选择性报告的结果会显示为低覆盖率,而不会被误认为完整记录;有扫描但没有任何已报告结果的交易对手,也会显示相应的扫描数量,而不是显示为“无历史记录”。覆盖率仅供参考(扫描数量由客户端决定),绝不会用于触发标记。

诚实说明其局限:该指标衡量的是内容是否到达,而不是内容是否优质——质量判断仍由 POST /v1/reputation/report 处理——并且仅适用于数字化、同步的 x402 交付,不适用于实体运输。

本地开发

供贡献者和审计检测器的人员使用——完全离线运行,已禁用付款,无需钱包:

git clone https://github.com/tollwarden/tollwarden && cd tollwarden
npm install

npm run dev            # local dev server — payments off
npm run demo:replay    # replay-attack demo: fresh nonce ALLOW → reused nonce BLOCK
npm test               # 799-test detector + hardening + plans + audit-log + dashboard + key-lifecycle + approvals + outcomes suite
npm run eval           # detection eval corpus: attack payloads + benign FP guards, graded via real scans (gates CI + publish)

性能

在零依赖开发服务器上测得(使用与生产环境相同的处理程序):2,000 次顺序扫描耗时 1.20 s → 每次往返扫描 0.60 ms,包括 HTTP、JSON 解析、完整检查套件和 Ed25519 签名。部署后的延迟主要由网络 RTT 决定;在付费路径上,与 facilitator 之间的 x402 验证/结算往返(x402 固有的步骤,在任何主机上都相同)占据了绝大部分耗时。

托管服务

位于 https://tollwarden.com 的生产服务已在 Base 主网上线,已被 x402 Bazaar 收录,已在 x402scan 注册,通过防篡改审计链进行监控,并已注册为链上的 ERC-8004 Agent(Base 上 IdentityRegistry 中的 agentId 为 59586——该代币的 URI 会解析回此服务的注册文件)。请自行验证:

curl https://tollwarden.com/health
curl https://tollwarden.com/.well-known/x402
curl https://tollwarden.com/v1/audit/verify
curl https://tollwarden.com/.well-known/erc8004.json
公开统计数据及其包含的内容

GET /v1/stats 会公布服务汇总数据(扫描总数、判定结果占比、不同 Agent 的数量,以及自行测量的 90 天正常运行时间)。响应会分别报告第三方和第一方的使用情况。运营方标记为第一方的密钥(我们自己的 Agent,主要是每天两次购买真实 x402 资源的开源生态侦察器)会计入 first_party,而主页面板只显示 third_party。归因依据是在扫描时记录的各密钥使用计数,因此每次扫描都由发起扫描的密钥进行认证,也涵盖密钥被标记之前的扫描。

为什么要公布第一方数据,而不是悄悄扣除?侦察器是公开的,其运行次数可以核查,因此读者可以自行核对数据。披露说明只是需要他人信任的一项声明。能够核对的数据才是证据。

只有所有者可以标记密钥(POST /v1/admin/keys/first-party,采用与 /v1/admin/stats 相同的保护机制,并接收密钥的哈希值,而非密钥本身)。客户端无法设置此标记。若调用方可以将自己标记为第一方,就能从公开的第三方统计基数中移除自己的扫描记录,而这正是设置分类的机制要防止的情况。

MCP 服务

已在官方 MCP 注册表中列出,名称为 com.tollwarden/tollwarden;也可以零配置直接运行:

// claude_desktop_config.json / any MCP client
{
  "mcpServers": {
    "tollwarden": {
      "command": "npx",
      "args": ["-y", "tollwarden"],
      "env": { "TOLLWARDEN_API_KEY": "psk_..." } // optional — mint one with the mint_api_key tool
    }
  }
}

通过 stdio 提供十二种工具:scan_outgoing_payment、scan_incoming_payment、check_counterparty_reputation、report_counterparty、report_payment_outcome(结算后闭环——建立经测量的交付历史)、dispute_reputation(通过钱包签名提交反驳,并与报告一同显示——绝不会删除报告)、mint_api_key、rotate_api_key(泄露密钥恢复——生成新密钥,同一账户)、check_approval_status(轮询人工审核流程中的审批状态)、get_plans、subscribe_plan,以及 verify_verdict_attestation(在本地执行完整的 Ed25519 验证——固定公钥、重新计算承诺、检查过期时间,以及验证签名的固定证据记录:返回包含固定时长和具名佐证来源的 pin_evidence)。默认连接生产服务;设置 TOLLWARDEN_URL 可指定其他服务。

检测默认值(托管服务)

公开这些信息以确保透明——您的扫描会依据以下 Starter 级别阈值进行评估(套餐可提高速率/支出上限;安全检查无法放宽):

行为默认值
超额付款标记 ≥3× 预期价格,≥10× 时阻止,绝对上限为 $10
速率标记 ≥10 次扫描/分钟(达到 2× 时阻止),累计支出上限为 $5/小时
首次联系向从未见过的交易对手首次付款,金额超过 $1 时标记
深度内容分析付款金额低于 $0.005 时跳过——直到向该交易对手的累计扫描支出超过 $0.005,之后始终对该交易对手启用(policy.force_deep 可覆盖;Pro/Scale 始终启用)
重放窗口nonce 跟踪 24 小时
重复购买60 秒内使用不同授权购买相同内容(DUPLICATE_PURCHASE_WINDOW_SECONDS,设为 0 可禁用)→ 标记,绝不阻止;通过 context.repeat_of,或针对较早尝试报告已交付 / 部分交付 / 内容错误的结果后清除
资产检查声明网络上的非规范 USDC(CAIP-2 id 或 x402 v1 名称)→ 阻止
商家固定按资源域名、按账户执行 TOFU:您账户固定的域名发生轮换 → 阻止;相对于其他调用方未经验证的观察发生轮换 → 标记;相对于 CDP 验证过的固定信息发生轮换 → 对任何人都阻止。固定信息的时效和具名佐证来源作为签名证明发布
地址投毒与已知地址两端各有 ≥4 个相同十六进制字符(但不完全相同)→ 已知地址属于您(曾付款 / 您的固定信息)或经 CDP 验证时阻止;若是其他调用方未经验证的固定信息,则标记;刚读取的内容中植入相同的相似地址 → 阻止(不可信来源,可信参考)或标记
付款金额根据原子 amount 和服务器已知的代币小数位数解析(规范 USDC = 6)。客户端声明的 asset_decimals 若不一致则忽略并标记。对于服务器无法验证的资产(包括服务器没有 USDC 条目的任何网络),声明的小数位数可以提高金额,但绝不能使其低于按 6 位小数计算的金额,因此卖家的 extra.decimals 无法让付款金额低于任何上限;此类网络上真正的 18 位小数代币会被高估,通常会因达到上限而被拒绝。仅当未提供原子金额时才使用 amount_usd;若与原子金额不一致,则会标记
ScoutScore 信号选择启用(SCOUTSCORE=on);评级为 LOW/VERY_LOW 的域名 → 标记(绝不阻止);缓存 24 小时
判定签名Ed25519,始终启用,证明有效期为 5 分钟

面向贡献者的本地开发配置记录在 .env.example 中。

架构

src/
  index.ts        Express + @x402/express middleware + CDP facilitator + Bazaar extension
  devserver.ts    Zero-dependency dev server (payments off) — same API surface
  api.ts          Framework-agnostic handlers (both servers route here)
  scanner.ts      Detector orchestration, tiering, verdict aggregation
  detectors/      pii · replay · duplicate · overpayment · injection (fast + deep) · urlrisk
                  asset · badlist · pinning · poisoning · scoutscore · velocity · offerdrift
  reputation.ts   Shared report registry
  outcomes.ts     Delivery-outcome ledger (commitment-bound) + delivery check
  approvals.ts    Human-in-the-loop step-up approvals (webhook + overrides)
  approvepage.ts  Human decide page (GET /approve)
  dashboard.ts    Self-contained usage dashboard (GET /dashboard)
  admindash.ts    Owner dashboard, audit-log-backed (GET /admin)
  verdictsign.ts  Ed25519 verdict attestation
  manifest.ts     /.well-known/x402 + agent card + ERC-8004 registration
  store.ts        JSON-file-backed state (tiny interface)
  auditlog.ts     Tamper-evident hash-chained decision log
  commitment.ts   Payment hashing (attestation binding + audit digest)
mcp/server.ts     MCP server (12 tools — npx tollwarden)
examples/         replay-demo.ts — reused-nonce attack blocked end-to-end
test/             799-test suite (detectors, hardening, plans, crypto, audit, dashboards, key lifecycle, approvals, outcomes — npm test)
eval/             detection eval corpus + runner (attacks must catch, benign must pass — npm run eval, gates CI)
sdk/              TypeScript client SDK + wallet enforcement kit + payment-path wrapper (npm: @tollwarden/client, 147 tests)
sdk-python/       Python client SDK + wallet enforcement kit + payment-path wrapper (PyPI: tollwarden, 153 tests)

设计说明:判定按最严重优先聚合(任何阻止项 ⇒ 阻止);risk_score 基于严重程度,并会因多个独立发现而累积增加;检测核心没有任何运行时依赖,因此无需安装即可使用 node --experimental-strip-types 运行完整测试套件。

安全与托管模式

  • 非托管式,仅提供建议。 TollWarden 只能看到支付元数据,不会签名、持有或转移资金。结算/拒绝决定由你的钱包/facilitator 作出(也可通过签名判定结果强制执行)。
  • 检测到的密钥会在响应中脱敏(保留前 4 个和后 2 个字符),且不会持久化。扫描载荷仅在内存中处理;只存储 nonce 指纹、哈希化的购买指纹(在重复购买检测窗口内保留)、固定项、频率计数器、信誉报告和 API 密钥用量。
  • 威胁模型——哪些防护能抵御哪些攻击者。 有两个值得关注的攻击者层级,两者并不相同:
    • 被混淆的 Agent——受到提示词注入的模型仍通过其常规执行框架调用工具——这是 TollWarden 的设计目标。检测功能可捕获被操纵的支付;频率与支出上限、首次接触限制和商家固定项均根据观察到的扫描结果在服务端计算,因此无论模型相信什么,这些限制都会应用于每笔经过扫描的支付;而 guardSigner 会在钱包端堵住跳过扫描的漏洞,因为经过包装的签名器会根据实际签名的类型化数据重新计算承诺,并在没有新鲜的允许判定时拒绝签名——还可通过本地策略(收款方允许列表 + 支出上限)限制已批准的支付。(context.origin 由调用方自行声明,因此,谎报来源的 Agent 会削弱内容检测层的防护——上限、固定项和签名器强制执行不依赖该字段。)
    • 进程遭到入侵——攻击者代码运行在可读取钱包私钥的环境中——这会击败任何进程内包装器,包括我们的执行器:它可以直接使用原始密钥签名,完全不接触 TollWarden。TollWarden 是非托管式的,绝不会持有你的密钥;这也意味着,它无法保护由你自己的进程暴露的密钥。对此类攻击者,缓解措施是隔离密钥——将签名器放在独立进程、KMS 或硬件中,并在该边界运行执行器,以确保 Agent 进程内部的任何组件都无法生成签名。(自动生成的 psk_ API 密钥仅是扫描凭证:它可以消耗你的 TollWarden 配额,但绝不能动用你的资金。)
  • 诚实说明局限: 注入检测基于启发式规则,并非语义分析;新颖的措辞可能绕过内容检测层(我们的公开评估语料库会记录已知缺陷,而提示词注入基准测试会公布测试结果,以及我们的自有语料库无法展示的内容)。信誉注册表接受未经身份验证的报告,应将其视为一种信号,而非事实标准。

参与贡献

欢迎提交问题和 PR,尤其是新的检测模式(需附带测试)和 SDK 改进(TS 和 Python 客户端位于 sdk/ 和 sdk-python/)。提交前请运行 npm test;每项检测器变更都需要有相应的覆盖测试。提交贡献即表示你同意根据 LICENSE 的条款将其授权给 TollWarden, LLC,并授予 TollWarden, LLC 将该贡献作为 Licensed Work 一部分进行再许可的权利(包括依据 Change License 再许可)。

许可证

Business Source License 1.1(源代码可用)。你可以阅读、运行、修改和可自托管 TollWarden,也可以通过托管服务使用 SDK/MCP 服务端,包括在商业产品中使用。许可证限制的是将代码本身作为有竞争关系的付费支付安全服务提供。每个版本在发布四年后转为 Apache 2.0。版本 ≤ 1.4.0 仍为 MIT。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。