本服务依赖的网关契约
这三份 YAML 文档描述了 Cloud Agent 调用的网关 HTTP API,也划清了两个仓库之间的边界:Cloud Agent 只通过 HTTP 和网关打交道,从不导入网关的模块,也从不持有网关的签名密钥。
文档 |
网关中的路由 |
Cloud Agent 中的调用方 |
|---|---|---|
|
||
|
||
|
这个目录里还有一份 Cloud Agent 自己公开 API 的 JSON 快照,见下文。
这些文档是什么,不是什么
YAML 契约是对照网关源代码手写的,记录的是双方约定的接口,不依赖任何一套正在运行的部署。里面只收录本服务用到的端点,并不是完整的网关 API 参考。
应用运行时不会加载这些文档,也不会拿 OpenAPI 去校验每个响应。契约是否一致,靠调用方的测试和代码评审来把关。运行时的客户端能兼容新增的可选字段之类的变化;线上契约测试仍会把文档里没声明的新增内容标出来,提醒更新文档。
来源 commit
每份文档的 info.x-source 记录了编写时依据的仓库、分支、源文件,以及能确定时的 commit。这些只是出处,不代表你的网关部署跑的就是那个版本。
2026-09-10 的一次路由检查记录显示,三个路由都在网关的 origin/main 上:
文档 |
检查过的路由 |
|---|---|
|
|
|
授权的签发、续期、撤销和用量路由 |
|
|
这次检查只是历史记录,不能说明当前的部署已经就绪。网关路由有变化时,要把契约和合并后的源代码对一遍,并跑相关的调用方测试和线上契约测试。本地测试读不到网关仓库里的文件,也就没法替你验证。
Agent 的准入也写在 internal-lookups.openapi.yaml 里,不是第四份契约。客户端调用的是 /internal/users/{user_id}/agent-access,而不是用户状态查询接口。Profile 用现有的用户状态端点,仅仅是为了显示所有者当前的邮箱;其中的角色和状态字段从不用于授权 Agent 访问。接入旧版网关之前,请先读 Agent 准入策略,了解部署策略、上线要求,以及出错时一律拒绝访问的行为。
这些文档没有覆盖的内容
光看响应的 schema,看不出授权的时间规则,也看不出一次拒绝具体意味着什么。下面这些依赖需要和网关保持一致:
grants.py里的GATEWAY_MAX_TTL_S = 900和GATEWAY_RENEWAL_STEP_S = 300,对应网关授权的最长 TTL 和默认 TTL,契约在x-constants下把它们记为MAX_GRANT_TTL_S和DEFAULT_GRANT_TTL_S。请求的 TTL 超过最大值会被截断,续期时按续期步长重新计算过期时间;所以两边的值一旦对不上,可能没有任何调用报错,有效授权的寿命却悄悄变短了。cloud_agent/workers/attempts.py里的DEAD_GRANT_REFUSALS包含unknown_grant、grant_not_live和subject_unavailable。worker 靠错误类型来区分"授权已失效"和"网关配置或认证出了问题"。只看 HTTP 状态码是不够的:同样是 404,可能是授权失效了,也可能是内部 API 根本没配置。
test_grant_constants_match_contract.py 会拿客户端的常量和 YAML 文档对比。test_grant_lifecycle.py 会结合客户端的续期节奏检查这些常量,并锁定授权失效时的错误类型。可选的线上授权生命周期测试会检查已部署网关的实际时间规则;本地测试没法确认网关真的用的是这些值。
这里唯一属于我们自己的文档
agent-control-plane.openapi.json 记录 Cloud Agent 对外提供的 API。它由 create_app().openapi() 生成,按键排序后提交,方便评审 API 的改动。
tests/unit/test_api_surface_contract.py 会拿实际提供的 schema 和这份快照对比,不一致就失败;它只检查,不会重新生成文件。有意改 API 时,用开发者指南里的命令重新生成,看一遍 diff,和实现代码一起提交。
怎样检查
在仓库根目录跑本地的文档检查和快照检查:
uv run pytest -q \
tests/unit/test_contracts.py \
tests/unit/test_grant_constants_match_contract.py \
tests/unit/test_api_surface_contract.py
契约检查会把 YAML 文档当作 OpenAPI 校验,确认 x-pinned-by.consumer 指向的测试文件确实存在,并检查 gateway.py 和 grants.py 里出现的每个 /internal/ 和 /v1/ 路径都写进了某份文档。CI 会跑这些检查。不过它们只能保证本仓库内部前后一致,保证不了和线上网关一致。
要检查一套部署,先设置 AGENT_CONTRACT_GATEWAY_URL 和 AGENT_CONTRACT_DISPATCH_TOKEN,然后运行:
uv run pytest -q -m external \
tests/external/test_gateway_access_contract.py \
tests/external/test_gateway_contracts_live.py
这些探测会用随机命名、实际不存在的账号,检查 JWKS 密钥和带类型的拒绝响应。授权生命周期这个用例默认跳过,除非用 AGENT_CONTRACT_SUBJECT 指定一个专门用于测试的账号。设置了这个变量,就表示你同意测试真的签发、续期和撤销一个授权;测试结束清理时会把它撤销。更全面的部署检查见冒烟测试。