mohameduk/Ubag_protocol
GitHub: mohameduk/Ubag_protocol
UBAG 是一个在 Web 边缘为自主 AI Agent 提供加密身份认证与请求路由的开放参考实现,让站点能自动区分人类和 Agent 并直接提供结构化数据。
Stars: 0 | Forks: 0
# UBAG Web 层
### 通用行为授权网关 — Web 层
[](https://github.com/mohameduk/Ubag_protocol/actions/workflows/ci.yml)
[](LICENSE)
[](#项目状态)
**Web 边缘的 Agent 身份与路由。开放参考实现。**
当自主 Agent 访问网站时,UBAG 会验证*它是谁*并进行相应的路由:
人类路由至普通站点,已认证的 Agent 路由至纯净的 JSON-LD,未知的
bot 路由至加密挑战。MCP 标准化了 Agent 与工具的通信方式 — 但它在
Web 层留下了一个空白:在没有人类参与的情况下如何确认 Agent 身份。这就是填补这一空白的层。
## 问题所在
当自主 MCP Agent 如今访问网站时:
- 网站无法验证*它是谁*
- 它像 2005 年的任何 bot 一样抓取原始 HTML
- 未知的 Agent 会被 Cloudflare 拦截 — 包括合法的 Agent
- 没有人类在循环中点击“允许”
MCP 的 OAuth 2.1 规范是为**人类委托**认证(浏览器 → 重定向 → 用户点击允许)而构建的。它不涵盖**自主 Agent 身份** — 即在没有人类参与循环时 Agent 携带的凭证。
UBAG 填补了这一空白。
## 工作原理
对启用了 UBAG 的站点的每一个请求都会通过一个 3 分支矩阵进行路由:
```
Incoming request
│
▼
┌───────────────────┐
│ UBAG middleware │
└───────────────────┘
│
├── Has valid UBAG credential? ──► Branch B: Clean JSON-LD data
│ Structured. No HTML parsing.
│
├── Looks human? ───────────────► Branch A: Transparent proxy
│ Origin server, untouched.
│
└── Unknown agent? ─────────────► Branch C: Sandbox + challenge
Sign a nonce with your Ed25519 key.
Prove identity → get credentialed.
```
**分支 B 是核心洞见。** Agent 无需抓取 50 个页面来了解一家企业,UBAG 直接提供一个结构化的 JSON-LD 响应 — 产品、价格、政策、联系方式。一次请求,无需 HTML 解析,提取错误大幅减少。
## 安全模型
UBAG 是**非对称的 — 身份路径中没有共享密钥:**
- **Agent 身份 = Ed25519 密钥对。** Agent 的身份是其公钥的 SHA-256 指纹(`ubag:…`)。为了进入,Agent 使用其*私钥*对站点的 nonce 进行签名;站点使用*公钥*进行验证。只有密钥持有者才能通过 — 仅仅知道共享密钥永远无法确立 Agent *到底是谁*。
- **凭证 = ES256 JWT**,由颁发者的 EC P-256 私钥签名,并且任何站点都可以使用颁发者的*公钥*进行验证 — 通过 `/.well-known/jwks.json` 自动作为 JWKS 提供。任何站点都不需要密钥来验证凭证 — 这与 OAuth / OIDC 模型相同,正是这种模型允许一个凭证在独立的站点间通用。
- **支持所有权证明。** 每个凭证通过 `cnf` 声明绑定到 Agent 的密钥,因此验证者可以要求持有者证明它仍然持有匹配的私钥。
- **一个服务端 HMAC**,仅用于让站点确认它颁发了给定的 nonce **而无需存储状态**(服务器向*自身*签名)。它*不是*身份证明的一部分。
Python 和 Node SDK 共享相同的传输格式(原始 Ed25519 + ES256),因此其中一个产生的签名或凭证在另一个中可以进行逐字节验证。这种互操作性已通过两个包中的测试进行了覆盖。
## 安装
尚未发布到 PyPI / npm。请从本仓库安装:
```
# Python (FastAPI / Starlette)
git clone https://github.com/mohameduk/Ubag_protocol
cd Ubag_protocol/ubag-python
pip install -e ".[fastapi]"
# Node (Express)
cd ../ubag-node
npm install
```
## 快速开始 — Python (FastAPI)
```
from fastapi import FastAPI
from ubag import UBAGMiddleware, generate_issuer_keypair
# 你的站点是其自身的 credential issuer。生成一次并持久化这些凭证
# (或者通过单独传递 issuer_public_key 来运行 verify-only)。
ISSUER_PRIVATE, ISSUER_PUBLIC = generate_issuer_keypair() # EC P-256 (ES256)
app = FastAPI()
app.add_middleware(
UBAGMiddleware,
origin="https://yoursite.com", # Branch A proxy target
issuer_key=ISSUER_PRIVATE, # mints + verifies agent credentials
server_secret="a-random-32+char-string-for-nonce-stamping",
site_meta={"name": "My Store", "type": "Store", "description": "We sell widgets"},
)
```
`issuer_key` 也可以来自 `UBAG_ISSUER_KEY` 环境变量;仅验证部署可以单独传递 `issuer_public_key`(或 `UBAG_ISSUER_PUBLIC`)。
你的站点现在将:
- ✅ 向已认证的 MCP Agent 提供纯净的 JSON-LD(分支 B)
- ✅ 将人类透明地代理到你的源站(分支 A)
- ✅ 使用 Ed25519 nonce 挑战对未知 Agent 进行沙盒隔离(分支 C)
- ✅ 提供 `yoursite.com/.well-known/ubag.json` 用于 Agent 发现
- ✅ 在每次访问时调用可选的 `audit_fn(branch, request, response)`
## 快速开始 — Node (Express)
```
const express = require('express');
const { ubag, generateIssuerKeypair } = require('ubag');
const { privateKey: ISSUER_PRIVATE } = generateIssuerKeypair(); // EC P-256 (ES256)
const app = express();
app.use(ubag({
origin: 'https://yoursite.com',
issuerKey: ISSUER_PRIVATE,
serverSecret: 'a-random-32+char-string-for-nonce-stamping',
siteMeta: { name: 'My Store', type: 'Store', description: 'We sell widgets' },
}));
```
## 查看运行效果(60 秒)
可运行的端到端演示会在进程内启动一个 UBAG 站点,并引导一个 Agent 完成整个握手过程 — *被拦截 → 受到挑战 → 对 nonce 签名 → 获得凭证 → 被提供 JSON-LD* — 然后打印出另一个站点将用于验证该凭证的 JWKS:
```
# Python
cd ubag-python && pip install -e ".[fastapi]" && python ../examples/demo.py
# Node
npm install --prefix ubag-node && node examples/demo.js
```
## 致 MCP Agent 开发者
如果你正在构建一个访问网站的 MCP Agent,请获取一个 UBAG 凭证:
```
from ubag import AgentCredential
# 你的 agent 的身份就是它的 Ed25519 keypair。生成一次;持久化 agent.export()。
agent = AgentCredential.generate(owner="you@email.com")
# 当 UBAG 站点向你发起 challenge (HTTP 429) 时,请对 nonce 进行签名并将其发回:
# challenge = resp.json()["ubag_challenge"] # nested in the 429 body: nonce, timestamp, stamp
# solution = agent.solve_challenge(challenge) # signs the nonce with your private key
# r = httpx.post(f"{site}/ubag/verify", json=solution)
# agent.set_credential(r.json()["credential"])
# 一旦获取 credential,它将随每个请求一起发送:
headers = agent.headers()
# {"X-UBAG-Credential": "eyJ..."}
```
启用了 UBAG 的站点会识别你的 Agent,并直接提供结构化数据而非 HTML。你的 Agent 能获得更好的数据;网站所有者也能获得可见性和控制权。Node SDK 暴露了相同的 `AgentCredential` API。
## 发现:`/.well-known/ubag.json`
每个启用了 UBAG 的站点都会在 `/.well-known/ubag.json` 提供一份发现文档
(保留 `/agents.json` 作为旧版别名)。这是刻意**不**命名为
`agents.json` 的 — 该文件名已被不相关的规范(Wildcard 的
OpenAPI 风格 `agents.json`、Google/Microsoft/HF 的 ARD 等)占用;`ubag.json` 使
UBAG 的身份/路由文档保持零冲突且明确无歧义。
```
{
"ubag_version": "1.0",
"host": "yoursite.com",
"credential_endpoint": "https://yoursite.com/ubag/verify",
"branches": {
"B-AGENT": { "description": "Authorized MCP agents — clean JSON-LD",
"requires": "X-UBAG-Credential header with valid JWT",
"content_type": "application/ld+json" },
"A-HUMAN": { "description": "Human browsers — transparently proxied to origin",
"requires": "None" },
"C-SANDBOX": { "description": "Unknown agents — Ed25519 nonce challenge",
"requires": "None — solve challenge to get credentialed",
"challenge_endpoint": "/ubag/verify" }
},
"discovery": {
"ubag_json": "https://yoursite.com/.well-known/ubag.json",
"verify_endpoint": "https://yoursite.com/ubag/verify",
"jwks_endpoint": "https://yoursite.com/.well-known/jwks.json"
}
}
```
就像 `robots.txt` 一样,但可被机器执行 — Agent 会在发起请求前获取它。
## 为什么不直接使用 Cloudflare?
| | Cloudflare | AWS WAF | UBAG |
|---|---|---|---|
| 阻止未知 bot | ✅ | ✅ | 对它们进行挑战 |
| 为 Agent 提供结构化数据 | Markdown(AI 功能) | ❌ | ✅ JSON-LD |
| 加密 Agent 身份 / 凭证 | ❌ | ❌ | ✅ |
| 自主 Agent 支持(无浏览器) | ❌ | ❌ | ✅ |
| 开源 | ❌ | ❌ | ✅ |
| 供应商锁定 | Cloudflare | AWS | 无 |
Cloudflare 和 AWS 会阻止或过滤 bot。UBAG 则是**提升**它们:一个未知的 Agent 可以解决挑战、获得凭证并变得已授权 — 没有任何合法 Agent 会被永久阻止。*(竞争对手列反映了撰写本文时的一般功能;请对照当前的供应商文档进行验证。)*
## MCP 集成
UBAG 是对 MCP 的补充,而不是替代它:
- **MCP OAuth 2.1** — 人类委托认证(用户在浏览器中点击允许)
- **UBAG 凭证** — 自主 Agent 身份(无人类在循环中)
```
MCP Agent
│
├── Talking to MCP servers? ──► MCP OAuth 2.1
│
└── Visiting websites? ────────► UBAG credential
```
UBAG 凭证只需颁发一次,并由站点的 middleware 在进程内进行验证 — 无需重定向,无需浏览器流程 — 并且可在任何启用了 UBAG 的站点上工作。
## 仓库布局
```
ubag-python/ Python middleware — FastAPI / Starlette today (v0.2.0)
ubag-node/ Node middleware — Express today (v0.2.0)
```
这两个包都实现了完整的协议(路由、凭证、挑战、密钥、
ubag.json)并共享一个可交叉验证的传输格式。
## 运行测试
```
# Python
cd ubag-python && pip install -e ".[dev]" && pytest
# Node
cd ubag-node && npm install && npm test
```
## 项目状态
**目前已实现**
- [x] 分支 A — 人类透明代理
- [x] 分支 B — Agent JSON-LD 结构化数据
- [x] 分支 C — 沙盒 + Ed25519 nonce 挑战
- [x] 非对称加密 — Ed25519 Agent 身份 + ES256/JWKS 凭证,无共享密钥
- [x] `ubag.json` 发现 — 在每个 UBAG 站点上提供(别名:`/agents.json`)
- [x] 审计钩子 — 在每次请求时调用 `audit_fn` 回调
- [x] Python SDK (FastAPI/Starlette) + Node SDK (Express),已实现跨 SDK 验证
**计划中 / 尚未构建**
- [ ] 发布到 PyPI (`pip install ubag`) 和 npm (`npm install ubag`)
- [ ] Django / Flask / Next.js middleware 适配器
- [ ] 托管凭证注册表 / 颁发者位于 `ubagprotocol.com/credential`
- [ ] WordPress 插件
- [ ] Docker 参考部署(一键自托管)
- [ ] 正式规范文档 (`docs/spec/…`)
- [ ] 面向站点所有者的支付/收入分成层
## 联系方式
由 Mohamed Ben Hadj Hmida 构建
[ubagprotocol.com](https://ubagprotocol.com) · [github.com/mohameduk/Ubag_protocol](https://github.com/mohameduk/Ubag_protocol)
MIT 许可证。
标签:AI代理, GNU通用公共许可证, MITM代理, Node.js, Python, Web网关, 无后门, 路由控制, 逆向工具