四类凭据
| 用途 | 私密部分谁保管 | 对方需要什么 |
|---|
| 清单签名 | 开发者离线保管发布私钥 | 平台登记的发布者公钥 |
|---|
| 主控调用插件的请求签名 | 客户主控保管安装私钥 | 客户管理员交给你的安装公钥 |
|---|
| 插件的响应签名 | 开发者服务保管响应私钥 | 清单里声明的 provider_response_key |
|---|
| Webhook 消息认证 | 双方各自保管订阅 secret | 按 key ID 选用的 HMAC secret |
|---|
安装公钥只能由客户管理员经可信渠道交给你:主控插件详情页“安装签名公钥”给出公钥和 key ID。不要信任请求里随附的公钥,也不要向客户索要安装私钥。
服务商插件:先过沙箱契约
第三方可接入的类型为 dns、sms、mail、icp、payment;notify 可以声明但主控暂不启用,cdn 只限官方。主控调用 POST {base_url}/cdnca-provider/v1/{kind}/{action},请求体最大 256 KiB,响应最大 1 MiB,超时 15 秒,只连公网 HTTPS,不走代理、不跟随重定向。
POST /cdnca-provider/v1/dns/verify HTTP/1.1
Host: provider.example.com
Content-Type: application/json
X-Cdnca-Provider-Installation: <installation-id>
X-Cdnca-Provider-Timestamp: <unix-seconds>
X-Cdnca-Provider-Nonce: <32-hex>
X-Cdnca-Provider-Signature: <base64url-ed25519>
{"protocol":"cdnca.provider/v1","sandbox":true}
{"protocol":"cdnca.provider/v1","ok":true,"data":{}}
- 签名错误、时间差超过 300 秒或 nonce 重复:返回 HTTP 401。
- 其他业务失败:HTTP 200 加协议错误信封,例如 {"protocol":"cdnca.provider/v1","ok":false,"error":{"code":"unavailable"}}。
- 写操作的 idempotency_key 要持久保存,同一个键重复到达返回第一次的结果;网络层 nonce 每次都换,业务幂等键在同一操作的重试中保持不变。
- sandbox=true 只用沙箱数据:不发真实短信邮件、不改客户 DNS、不扣款。
协议参考实现和契约测试随开发源码提供,可通过“联系我们”申请与主控版本匹配的提供商协议示例。本地契约测试用你自己生成的沙箱安装身份,把客户端指向你的测试服务后跑完整契约,检查签名、篡改、时间窗、重复 nonce、幂等及各类型业务用例。客户安装时主控会对你的服务再跑一次安装契约,全部通过才能启用,只让健康检查返回 200 不够。签名消息格式、各动作字段和沙箱固定值见服务商接口指南。
带页面的插件:来源、SDK 与后端验签
- 部署页面的 HTTPS 域名;有后端时再准备 API 域名。
- 卖家站“页面来源”→“登记来源”,按页面提示把 cdnca-plugin-origin=<令牌> 原样放到该来源的 /.well-known/cdnca-plugin-origin.txt,点“验证”。origin 和 api_base_url 所属来源都要验证。
- 清单 spec.entrypoint 写 ui_page 入口,权限加 platform.ui_pages.provide,签名并上传(不带任何文件或包)。
{
"type": "ui_page",
"origin": "https://ui.example.com",
"api_base_url": "https://api.example.com/v1",
"pages": [
{
"id": "reports",
"path": "/reports",
"mount": "menu",
"menu_parent": "monitor",
"role": "user",
"title": {"zh-CN": "报表"}
}
]
}
来源必须是公网 HTTPS、默认端口、不跟随重定向;同一来源只属于一个发布者。页面在隔离 iframe 里运行,拿不到主控的 Cookie、存储和父页面 DOM。SDK 的 connect() 完成握手后只有 context()、navigate() 和 request() 三个能力;request 只转发到你自己的 api_base_url,每用户每插件每分钟最多 120 次。直接单独打开页面拿不到主控上下文,要在已安装并挂载的主控里测试。
后端用安装公钥验证 X-Cdnca-Plugin-Context(base64url JSON,60 秒有效,含 nonce)、X-Cdnca-Plugin-Key-Id 与 X-Cdnca-Plugin-Signature,并记住 nonce 防重放;多副本服务要用共享存储保存 nonce,还要检查上下文里的租户、插件与业务资源。SDK 源码、后端验签示例和固定向量都在 ui_page SDK 页公开。
Webhook:先收测试事件
订阅由客户在目标租户里创建:管理操作要求主控登录会话和 plugin_webhooks.manage,查询要求 plugin_webhooks.read;API Key 不能代替。最小测试订阅只选 io.cdnca.webhook.v1.test,不填安装授权 ID。目标必须是公网 HTTPS 域名、默认 443 端口,不能带查询串或片段,不能是 IP。
CDNCA_CONTROL_ORIGIN='https://control.example.com'
CDNCA_TENANT_SCOPE_ID='<tenant-scope-uuid>'
CDNCA_SUBSCRIPTIONS="${CDNCA_CONTROL_ORIGIN}/v1/tenant-scopes/${CDNCA_TENANT_SCOPE_ID}/plugin-webhook-subscriptions"
# CDNCA_CONTROL_ACCESS_TOKEN comes from the customer's own session
umask 077
curl --fail-with-body --silent --show-error \
-X POST "$CDNCA_SUBSCRIPTIONS" \
-H "Authorization: Bearer ${CDNCA_CONTROL_ACCESS_TOKEN:?}" \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: <unique-key-for-this-create>" \
--data-binary '{"name":"test","target_url":"https://hooks.example.com/cdnca/hooks","event_types":["io.cdnca.webhook.v1.test"]}' \
--output webhook-receipt.json
成功返回 201,回执含 subscription 和一次性 secret;把 secret 转存到接收服务的密钥存储,不要把回执写进普通日志。然后对 .../{subscription_id}/test-deliveries 发 POST(空对象正文、新的 Idempotency-Key),返回 202 只表示任务已接受,要再查 .../deliveries 确认状态为 delivered,并核对接收端已持久记录。
- 校验 POST、路径、Content-Type 和必需请求头。
- 对收到的原始正文算 SHA-256,与 X-CDNCA-Webhook-Content-Sha256 比较。
- 按 X-CDNCA-Webhook-Key-Id 选 secret,严格 base64url 解码为 32 字节后计算 HMAC-SHA256;签名形如 v2=<无填充 base64url>。
- 时间戳与本地时钟相差不超过 300 秒(含边界)。
- 按事件 schema 解析,绑定事件 ID、租户和订阅;delivery ID 去重,事件 ID 做业务幂等。
- 先可靠落盘或提交事务,再返回 2xx。
cdnca.webhook.signature/v2
POST
<canonical-escaped-request-path>
<X-CDNCA-Webhook-Timestamp>
<X-CDNCA-Webhook-Event-Id>
<X-CDNCA-Webhook-Delivery-Id>
<X-CDNCA-Webhook-Key-Id>
<lowercase-hex-sha256-of-raw-body>
自动投递最多 5 次尝试;自动重试保留 delivery ID 并增加 attempt,显式回放保留 event ID、生成新 delivery ID。单条回放 POST .../deliveries/{delivery_id}/replays,按时间段回放 POST .../delivery-replays(最长 31 天,每批最多 100 条)。轮换与撤销需要 Idempotency-Key 和当前订阅的 If-Match,缺版本得 428,版本过期得 412,回读后再判断。真实领域事件要绑定已启用的安装授权且客户已批准对应 events.*.read 权限;订阅只接收创建之后的新事件。参考接收器示例可通过“联系我们”申请(需 Go 1.27,Unix 文件锁,Windows 请在 WSL 或 Linux 里运行)。