Bundle 生命周期与引用协议
使用 Bundle 安装身份和资源授权,在生命周期中解析、保存并更新调用凭据,供业务运行使用。
Bundle 生命周期协议 6.0.0 以整个安装实例为操作边界,访问解析协议为 4.1.0。旧 4.0.0 继续支持属性查询,响应回传请求的协议版本;事件查询使用 4.1.0。
所有自定义插件统一使用这一机制,Module 作为 Bundle 资源接受显式属性授权。
安装身份与执行语义
installation 包含安装环境 environmentKey、applicationRuntimeId、workspaceId、
bundleInstallId、安装人 installedBy 和 Bundle 来源 bundle.environmentKey + bundle.bundleId。
安装环境与 Bundle 来源可以不同,所有定位均由 Runtime 提供。
installedBy 是安装目标环境中的正整数用户 ID,由 Runtime 从安装记录读取。
平台新建请求的所有操作和阶段均携带该字段;升级、配置和重试保持原安装人,DETACH 清理使用原安装快照。
它不是发布方 ownerUserId、当前操作人 requestedBy,也不是资源物化的 executionSubjectUserId。
SDK 使用 Option<i64> 兼容旧消息和历史快照中缺少安装人的情况,缺失时不得用其他用户替代。
业务插件需要以安装人创建初始管理员时,必须先确认该值存在;后续调用不能重复覆盖成员权限。
响应关联始终校验稳定安装身份,双方都提供安装人时必须一致。
生命周期请求、响应及安装、操作和资源输入对象忽略未知扩展字段,允许协议增加元数据。 已知字段类型、必填字段、枚举、版本、授权范围和响应身份关联仍必须校验。
operationType + stage 表达操作与资源执行时机:
| 字段 | 允许值 |
|---|---|
operation.operationType | INSTALL、UPGRADE、CONFIGURE、DETACH |
stage | BEFORE_RESOURCES、AFTER_RESOURCES |
operation.operationId、安装身份、插件身份和阶段由平台关联。插件按这些身份协调自己的业务状态,
不能另造安装记录或改变平台的操作结果。调用结果不确定时,Runtime 使用相同的操作身份和阶段
重复调用同一生命周期请求。插件必须安全处理重复执行并返回相应属性;已完成的业务变更不得重复创建资源。
executionSequence 是 Runtime 的正整数操作顺序,同一操作重试保持不变。
失败只保留当前 Bundle 安装实例的冲突保护,不占用其他 Bundle;DETACH 可以接管该安装已停止的失败操作。所有者的顺序校验必须覆盖实际业务写入。
详见失败与重试。
配置变更的参与范围
Module 属性、完整配置、网关域名和 Bundle 内 Agent/Timer 配置统一进入 CONFIGURE。
Runtime 先比较实际变化,再依据完整 Locator 和当前已验证安装资源账确定参与的 Bundle,随后读取这些 Bundle 的插件绑定。
没有插件的已安装 Module 仍有真实 CONFIGURE 所有者;无变化不生成操作,也不会扫描无关 Bundle 的插件声明。
Module 或插件资源缺少合法安装归属时明确报错。没有 Module 身份和插件声明的 Runtime 本地服务按原本地配置合同保存,不伪造 Bundle 安装。
instanceRefProperty 仍受保护,不能通过改变 Locator 或另一条配置入口改写。
Runtime 编译后的授权
Bundle 作者通过领域对象选择声明授权;插件执行边界使用 Runtime 根据确切 Module 归属编译的资源定位。
下面是与后续请求对应的 SDK 授权结构,用于插件验证输入和输出;它不是作者清单,不能复制到 baijimu.bundle.json。
{
"pluginId": "tenant-provider",
"protocolVersion": "6.0.0",
"order": 0,
"failurePolicy": "FAIL_FAST",
"configuration": {"schemaVersion": "1.0.0", "values": {}},
"resources": [{
"resource": "publisher/sales/MODULE/customer",
"readProperties": ["region"],
"writeProperties": ["tenantId"],
"credentialProperties": ["inventory"]
}]
}生命周期请求
{
"contractVersion": "6.0.0",
"pluginId": "tenant-provider",
"executionSequence": 1,
"installation": {
"environmentKey": "consumer",
"applicationRuntimeId": "runtime-a",
"workspaceId": 42,
"bundleInstallId": "installation-a",
"installedBy": 25,
"bundle": {"environmentKey": "publisher", "bundleId": "sales"}
},
"operation": {
"operationId": "operation-a",
"operationType": "INSTALL",
"currentVersion": null,
"targetVersion": "1.0.0"
},
"stage": "BEFORE_RESOURCES",
"configuration": {"schemaVersion": "1.0.0", "values": {}},
"resources": {
"publisher/sales/MODULE/customer": {
"action": "INSTALL",
"currentVersion": null,
"targetVersion": "1.0.0",
"properties": {"region": {"@type": "Data", "value": "north"}},
"references": {"inventory": {"@type": "ServiceReference", "serviceId": "inventory-a"}}
}
}
}configuration.schemaVersion 属于插件自己的业务配置合同,不等于生命周期版本。
resources 的键为 ResourceLocator,只包含获授权的资源和属性。
properties 的每项输入和输出都必须是 {"@type":"Data","value":...},不接受裸值。
Runtime 按 Module 的属性定义校验 value,再写回一次 Data 包装,不递归猜测业务对象内的 @type。
SDK 的 decode_data() 用于读取业务值,from_data_values() 用于把业务值编码为输出;
decode() 与 from_properties() 保留完整的类型包装。
引用始终是逻辑 ServiceReference、Map 或 List,不附带调用地址或密钥。
响应
成功响应使用完整 CModel envelope:外层 contractVersion: "1.0.0"、errorCode: "0" 和 data。
data 原样关联请求的 contractVersion、pluginId、installation、operation、stage、executionSequence,
并返回 outcome 与 resources。
outcome 为 NOT_APPLIED、APPLIED、VERIFIED、FAILED 或 UNKNOWN。
resources 按 ResourceLocator 返回获准写入的业务 Data 属性,例如业务租户编号,以及插件自己签发、
按安装隔离的业务服务 Token。敏感业务属性必须声明 isSensitive: true,并由当前插件的
writeProperties 显式授权;它不属于用于引用解析授权的 credentialProperties。
不能回写引用属性、平台引用解析返回的 access 地址与 Token,或其他身份的共享管理凭据。
业务凭据的签发、轮换、撤销和保存边界见插件自有业务凭据。
Runtime 校验输出范围并决定安装进度。UNKNOWN 不能当成功处理。不得只返回裸 data。
{
"contractVersion": "1.0.0",
"errorCode": "0",
"data": {
"contractVersion": "6.0.0",
"pluginId": "tenant-provider",
"executionSequence": 1,
"installation": {
"environmentKey": "consumer",
"applicationRuntimeId": "runtime-a",
"workspaceId": 42,
"bundleInstallId": "installation-a",
"installedBy": 25,
"bundle": {
"environmentKey": "publisher",
"bundleId": "sales"
}
},
"operation": {
"operationId": "operation-a",
"operationType": "INSTALL",
"currentVersion": null,
"targetVersion": "1.0.0"
},
"stage": "BEFORE_RESOURCES",
"outcome": "APPLIED",
"resources": {
"publisher/sales/MODULE/customer": {
"tenantId": {"@type": "Data", "value": "tenant-a"}
}
}
}
}日常属性更新
修改已安装 Module 的属性会由所属 Bundle 执行 CONFIGURE,currentVersion 与 targetVersion 相同。
Runtime 在同一事务中保存目标配置并冻结现有 Bundle 操作和两阶段授权;失败的事务不会留下半份操作。
同一 Bundle 保留完整资源与插件范围,继续使用现有操作认领、派发和 Runtime 内部提交流程。
执行期间,其他用户配置写入会被拒绝;过期的配置快照不能覆盖新值。
网络中断后的重试保持 operationId + stage,插件返回同一业务结果及获准写入的 Data 属性。
生命周期中解析访问
插件使用独立平台凭据调用 POST /bundle-plugin-manager/api/service-reference/resolve。
在登记的平台 API 基地址后追加这个完整路径;不要再使用 /bundle-plugin/** 或 /plugin/** 别名。
该接口属于插件管理服务,公网网关按服务级 /api/** 规则映射,由插件管理服务验证插件凭据。
API 基地址与完整路径
平台 API 基地址从目标环境登记或部署配置读取,只包含平台入口,不包含插件管理服务的路径。
例如平台入口为 https://api.example.com 时:
| 配置或请求 | 值 |
|---|---|
| 平台 API 基地址 | https://api.example.com |
| 引用解析完整路径 | /bundle-plugin-manager/api/service-reference/resolve |
| 最终 POST 地址 | https://api.example.com/bundle-plugin-manager/api/service-reference/resolve |
去掉基地址末尾的斜杠后,追加完整路径一次。不要在基地址中预先放入 bundle-plugin-manager/api,
也不要再追加 bundle-plugin;否则会重复服务前缀。示例域名仅用于说明,不能作为运行时默认地址。
迁移调用代码时,同时检查部署后实际生效的 API 基地址配置;只修改源代码或未被注入的配置项,不会修正实际请求。
请求与响应
请求包含:
{
"contractVersion": "4.1.0",
"installation": {
"environmentKey": "consumer",
"applicationRuntimeId": "runtime-a",
"workspaceId": 42,
"bundleInstallId": "installation-a",
"installedBy": 25,
"bundle": {"environmentKey": "publisher", "bundleId": "sales"}
},
"resource": "publisher/sales/MODULE/customer",
"propertyKey": "inventory"
}不传 pluginId、目标服务 ID、URL 或 Token。平台从认证身份和活动安装的当前属性解析授权目标。
成功 data 返回相同的 installation、resource、propertyKey,以及当前逻辑 reference、
access[serviceId] = {url, token} 和 expiresAt。
完整的引用响应示例:
{
"contractVersion": "1.0.0",
"errorCode": "0",
"data": {
"contractVersion": "4.1.0",
"installation": {
"environmentKey": "consumer",
"applicationRuntimeId": "runtime-a",
"workspaceId": 42,
"bundleInstallId": "installation-a",
"installedBy": 25,
"bundle": {
"environmentKey": "publisher",
"bundleId": "sales"
}
},
"resource": "publisher/sales/MODULE/customer",
"propertyKey": "inventory",
"reference": {
"@type": "ServiceReference",
"serviceId": "inventory-a"
},
"access": {
"inventory-a": {
"url": "https://runtime.example/service/inventory-a",
"token": "example-current-access-token"
}
},
"expiresAt": null
}
}ServiceReferenceMap.valueMap 的业务键和 ServiceReferenceList.valueList 的顺序以当前响应 reference 为准;相同 serviceId 共用一份 access。
生命周期解析成功后持久化所选目标的 URL、Token 与引用选择关系;任一目标未授权、安装失效或凭据撤销时,整次解析失败,不启用该候选配置。
生命周期更新访问凭据
Token 和调用地址由生命周期管理。插件在安装、升级、配置等相关生命周期中使用自己的插件身份解析引用,
校验完整安装身份、资源、属性或事件及有效期,将逻辑来源与 URL、Token 持久化为受保护的本地运行配置。
生命周期回调中的逻辑 references 仍不内联凭据;凭据来自插件单独调用的统一引用解析接口。
插件可以将解析结果交给实际业务 Owner 校验、保存和使用;业务 Owner 不冒用插件身份查询引用。
system-manage 遵循同一规则,渠道网关不承担引用查询。标准 AFTER_RESOURCES 阶段不隐式执行解析。
业务调用及其重试不调用引用解析接口,直接使用已经生效的本地配置。进程重启读取持久配置。 两次相关生命周期变更之间沿用同一配置,不因每次业务请求、失败重试或定时轮询重新领取 Token。
- 引用重绑、目标地址变更、Token 轮换及授权范围变化必须进入生命周期更新流程;影响已有引用时,负责变更的 Owner 必须触发相关消费者的配置生命周期,不能依赖消费者下次业务请求发现变化。
- URL、Token 与目标选择关系成组提交,使用生命周期的操作顺序和幂等身份防止旧回调覆盖新配置;只有校验、 持久化及启用完成后才能报告该阶段成功。初始化失败时不能启用业务入口。
- 更新失败不得清空仍然有效的已生效配置,也不得启用未完成的候选。已撤销、已移除或过期的凭据不能继续使用; 授权检查仍由被调用服务执行,不能伪造安装成功或恢复被撤销凭据。
- DETACH、资源移除和授权撤销通过生命周期停用或清理对应运行配置。只操作本安装、本资源的配置,保留其他来源。
expiresAt非空时,凭据 Owner 必须通过生命周期在过期前更新。未完成更新则停止使用过期凭据并报告更新失败, 不能让业务发送路径承担续期;expiresAt: null不代表允许使用已被撤销的 Token。- 仅将凭据保存在有访问控制的运行配置中,不写入源码、Bundle 内容、日志、普通业务载荷或公开响应。 请求级 Actor assertion 不持久化,不属于本规则允许保存的 ServiceReference Token。
失败操作保留的执行账用于生命周期恢复,不能仅因另一次升级失败就使此前有效的业务配置失效。 本节替代旧的“每次业务调用解析、禁止持久化访问凭据”规则;旧消费者须完成生命周期配置改造后才能符合本合同。
异步发布事件
Bundle 插件在相关生命周期中查询 Module 声明的事件,复用上述凭据解析接口及插件身份 Token;后台 Worker 使用保存的事件调用配置。
事件不是引用属性,不需要 eventPublisher 自引用,也不需要新增 publishEvents 开关。
平台根据插件身份、当前 Bundle 安装与绑定、模块归属及当前事件声明校验授权。
请求保持相同的 installation 和 resource,用 eventName 指定事件;eventName 与 propertyKey
必须二选一,不能同时传入,也不能由调用方指定目标服务、URL 或调用凭据:
{
"contractVersion": "4.1.0",
"installation": {
"environmentKey": "consumer",
"applicationRuntimeId": "runtime-a",
"workspaceId": 42,
"bundleInstallId": "installation-a",
"bundle": {"environmentKey": "publisher", "bundleId": "sales"}
},
"resource": "publisher/sales/MODULE/customer",
"eventName": "CustomerCreated"
}成功响应的 data 回传相同安装、资源和 eventName,提供
access[eventName] = {url, token} 与 expiresAt。url 已是完整事件方法地址,不能再追加事件名。
事件响应不包含 propertyKey 或逻辑属性 reference。原属性查询仍返回 reference 与按服务索引的 access。
事件凭据按以下流程使用:
- 生命周期中用插件身份 Token,按安装、Module Locator 和事件名请求解析接口。
- 校验安装、资源、事件名和有效期,将对应完整 URL、调用 Token 持久化并启用。插件身份 Token 只用于解析认证。
- 每次发送及业务重试读取本地事件配置,向保存的 URL 发送事件参数,使用调用 Token 作为 Bearer 凭据,不再次解析。
- 保持事件 ID 和业务幂等键不变;HTTP 与 CModel 业务结果都成功后确认 outbox。传输失败保留队列,按业务规则重试。
事件声明、引用或凭据变化由后续生命周期更新配置,解除或撤销则停用对应配置。 事件不存在、普通方法冒充事件、插件未绑定、资源不属于 Bundle、安装失效或授权撤销时,生命周期解析失败。 解析期间授权或当前模块发生变化时,由生命周期重试重新读取,未通过校验的候选不能写成已生效配置。
事件声明与订阅见模块事件与发布机制。 注册、Bundle 绑定及上线验收见Bundle 生命周期插件。
请求级 X-Runtime-Actor-Assertion 属于独立的Runtime 用户委托合同,不进入插件的安装关联或引用访问材料。