百积木文档
开发指南后端应用开发

环境配置与服务鉴权

使用环境配置、Provider 绑定和 consumer token 隔离后端部署环境。

同一个 Artifact 应能够部署到多个环境。所有环境差异都通过 Hosted Service Environment 管理,但必须区分 两类配置:应用配置由部署解析后提供给业务进程;运行治理配置由 Hosted Service、OpenResty 和运行载体消费, 不进入业务代码。完整责任划分见架构与资源边界。

Resource Binding 的通用边界、当前公开能力和申请流程见 基础能力与 Resource Binding。数据库 Profile 和 Object Storage Profile 是当前 明确支持的 Hosted Service Provider 绑定;不要因为某项基础能力在 Runtime 中可见,就假设 Hosted Service 已支持该绑定类型。

环境配置

应用配置用于改变同一 Artifact 在目标 Environment 中的业务运行参数。适合放入应用配置的内容包括:

  • 日志级别、功能开关和外部服务地址
  • 业务运行参数
  • 数据库 Profile 或 Allocation 的绑定引用
  • Object Storage Profile 的绑定引用
  • 第三方凭据 Provider 的绑定引用

敏感值不要直接写入源码、Artifact 元数据、公开文档或普通日志。配置变更应形成可审计的修订,并在部署前确认解析结果。

实例数量、扩缩容策略、CPU/内存限制、通用请求限流和并发保护属于运行治理配置。它们可以在产品界面上归属 目标 Environment,但必须使用 Hosted Service 定义的类型化字段、独立校验和版本化修订,并受 Slot 当前容量 权益约束;不能把 replicaCount、rateLimit 等自定义普通配置键注入 Rust 进程后由业务代码执行。Rust 进程 不得读取或决定副本数,也不应重复实现平台已经承诺的通用流量治理。

当前是否支持某个治理项以及准确参数,以工作区当前 Runtime 方法定义和 Slot 目录为准。方法定义中尚未提供的 治理能力不能通过普通 Environment 配置绕过;平台能力正式提供并完成运行态验证前,文档也不能宣称该治理已生效。

使用前通过当前 CLI 帮助确认参数:

baijimu hosted-service env --help
baijimu hosted-service config-provider --help
baijimu hosted-service config-binding --help

Provider registry 是动态目录。新增 object-storage Provider 不要求修改 Hosted Service contract、Hosted Service Component 或 CLI;环境先安装对象存储 Package 并注册 Provider,项目再用 config-binding 保存自己的 profileRef。部署解析出的对象 API 地址和 Token 只进入目标 Environment,底层对象存储 AccessKey 不进入 Hosted Service 配置。

完整示例见 Hosted Service 对象存储与签名上传。

绑定数据库即生成默认配置

数据库 Profile 提供连接配置的来源。创建 Environment 数据库绑定时,平台同时生成 DATABASE_URL,来源为该绑定的 databaseUrl 字段;不需要再编写变量映射文件,也不需要复制连接密码。 绑定与默认配置在同一个事务、同一份环境修订中提交。随后部署,业务进程即可读取 DATABASE_URL。 已有运行中的进程需要重新部署才能使用新修订。

baijimu workspace hosted-service config-binding create \
  --workspace-id WORKSPACE_ID --environment-id ENVIRONMENT_ID \
  --name orders --provider-key DATABASE_PROVIDER_KEY --profile-ref DATABASE_PROFILE_REF

同一环境绑定第二个数据库时,在绑定命令中指定变量前缀:

baijimu workspace hosted-service config-binding create \
  --workspace-id WORKSPACE_ID --environment-id ENVIRONMENT_ID \
  --name analytics --provider-key DATABASE_PROVIDER_KEY --profile-ref ANALYTICS_PROFILE_REF \
  --variable-prefix ANALYTICS

这会生成 ANALYTICS_DATABASE_URL。前缀只接受大写字母、数字和下划线,不能以数字开头或以下划线结尾。 省略前缀始终选择 DATABASE_URL,不会根据绑定顺序猜测主数据库。 若该变量已经指向相同绑定和字段,重复请求不会重复添加;若来自其他数据库、固定值或 Secret,整次绑定会报错, 保留原配置。相同绑定参数的重复请求返回原绑定;修改资源引用或其他绑定属性应使用 update。

默认规则由 Hosted Service 的版本化能力配置定义,当前数据库能力声明 DATABASE_URL ← databaseUrl。 配置中只保存资源引用,部署时再由 Provider 受控解析凭据并通过 Secret 通道注入。 历史绑定不会在发布时自动改写;对已有绑定再次提交相同 create 参数,可按相同冲突规则生成默认配置。 其他能力仍按各自配置合同选择字段。

自定义变量名称与来源

环境配置使用一个明确的变量列表。每个名称在环境内只能定义一次,来源可以是固定值、环境内资源绑定的字段、 Environment Secret,或资源绑定的调用凭据。Provider 返回哪些字段,不决定应用获得哪些环境变量。

数据库绑定已自动创建默认变量。需要添加其他变量或自定义名称时,可读取当前环境配置,保留已有变量,再提交完整配置文件。例如:

{
  "schemaVersion": "1.0.0",
  "variables": [
    { "name": "LOG_LEVEL", "source": { "kind": "literal", "value": "info" } },
    { "name": "DATABASE_URL", "source": { "kind": "resource", "binding": "orders", "field": "databaseUrl" } },
    { "name": "API_KEY", "source": { "kind": "secret", "key": "EXTERNAL_API_KEY" } }
  ],
  "applicationConfig": { "featureEnabled": true }
}
baijimu workspace hosted-service env update --workspace-id WORKSPACE_ID \
  --environment-id ENVIRONMENT_ID --config-file ./environment.json

env update --config-file 替换完整环境配置;必须保留仍需使用的数据库变量,省略会删除该变量。

applicationConfig 是应用自己的不透明配置,作为 HOSTED_SERVICE_CONFIG_JSON 提供给进程;它的字段不会自动 变成环境变量。设置为 null 表示不提供这份应用配置。固定环境变量使用字符串。

资源字段只支持顶层字符串、数字、布尔值;嵌套对象、数组和空值不能隐式展开。同一个字段同时出现在 Provider 的 values 和 env 中会被视为歧义。变量只通过当前环境的绑定名称访问资源,不能填写任意 profileRef。

资源字段、Secret 和绑定凭据都通过 Secret 通道注入。绑定凭据使用 { "kind": "bindingCredential", "binding": "workflow" } 作为来源,不再自动生成 WORKFLOW_TOKEN。 数据库默认变量在绑定时已写入配置。除此之外,没有声明的字段和 Secret 不注入;重复变量名、缺少绑定或字段、引用不存在的 Secret 都会报错。

绑定名由使用方定义,在环境内唯一。primary 没有特殊意义,同一个环境可以绑定多个数据库。配置引用中的 绑定名和迁移目标都必须有效;被当前配置或未完成迁移引用的绑定不能直接改名、停用或删除。

Environment Secret

每个 Project Environment 拥有独立的 Secret snapshot。Secret 只在所属 Environment 的配置和部署流程中可用, 不能跨 Environment 读取或复用。新增 Environment 后平台会自动创建对应 snapshot,不需要修改后端 Artifact、 额外登记密钥或调整组件版本。数据库 Profile 凭据仍由 Database Provider 根据绑定在部署时解析,不复制进 Environment Secret。

先读取当前键名和 Environment Revision。该操作只返回元数据,不返回任何 Secret 值:

baijimu workspace hosted-service env secret-get \
  --workspace-id WORKSPACE_ID \
  --environment-id ENVIRONMENT_ID

更新使用显式 patch,并携带读取到的 version:

baijimu workspace hosted-service env secret-update \
  --workspace-id WORKSPACE_ID \
  --environment-id ENVIRONMENT_ID \
  --expected-version ENVIRONMENT_REVISION \
  --upserts-file ./secrets.json \
  --remove-secret-key RETIRED_TOKEN \
  --change-reason "rotate provider credential"

--upserts-file 只写入或替换文件中出现的键,--remove-secret-key 只删除明确指定的键,其他现有键保持 不变。并发期间 Environment Revision 已变化时,服务端拒绝旧版本 patch;重新读取元数据并核对变更后再提交, 不要覆盖未知的新修订。

把历史普通配置迁移为 Secret 时,在同一次命令中传入 --remove-config-key NAME,并在 upsert 文件中提供 同名 Secret。普通配置删除、加密 Secret snapshot 和统一 Environment Revision 在同一个事务中提交;不能 把 --remove-config-key 当作普通配置删除接口。空 patch、隐式清空、写入与删除同一个键、删除不存在的键, 都会失败关闭。--remove-config-key 将同名固定变量原子改为 Secret 来源;Secret 存储键与变量名称属于不同 用途,只有显式声明的来源决定取哪个值,写入 Secret 不会覆盖同名固定变量。

Secret 值不会通过查询、更新响应或管理界面回显。敏感 JSON 应通过权限受控的临时文件或标准输入边界提供, 避免直接写在命令行、Shell 历史、项目 Git、Artifact、普通配置或日志中。更新只改变 Environment 配置源事实; 已有业务进程仍需通过受支持的重新部署或重启流程取得新 snapshot。

读取部署后的公开 Endpoint

Endpoint 的端口是 Runtime Revision 的平台事实,不属于业务配置。业务 Artifact 只声明 Endpoint 的数量、 稳定名称、协议和通用鉴权模式;部署时平台分配端口,并把每个端口拆分为业务进程环境变量。后端不需要解析 JSON 或根据 Project ID、环境 ID、域名格式和监听端口自行推导启动端口。

每个 Endpoint 都注入一个 APP_ENDPOINT_<NORMALIZED_NAME>_PORT,其中名称转换规则为:小写字母转为大写, 连字符 - 转为下划线 _。例如:

APP_ENDPOINT_API_PORT=20000
APP_ENDPOINT_MANAGEMENT_PORT=20001

端口只在当前 Runtime Revision 内有效,示例值不固定。业务进程必须在固定的 127.0.0.1 上,使用对应 Endpoint 的命名端口变量启动 listener;不得使用或硬编码 APP_BIND_HOST、APP_PORT、PORT、BIND_ADDR、 listenPort 示例值或 proxyListenPort。平台不会注入这些变量,也不提供 APP_PORT 兼容注入。

Runtime 仍可能提供 RUNTIME_ENDPOINTS_JSON 作为 Endpoint 元数据,供确实需要读取 baseUrl 等公开信息的 适配器使用;它不是业务监听端口契约,业务启动不得依赖它解析端口。

固定健康检查协议

业务进程必须在每个声明的 HTTP Endpoint listener 上提供:

GET /healthz

进程完成初始化并能够接收业务流量后返回 2xx;未就绪时返回非 2xx。/healthz 是 Hosted Service 的固定 协议常量,不属于 hosted-service.toml、Artifact Endpoint 元数据或 Environment 配置。平台不会读取或覆盖健康路径, 并拒绝任何健康路径覆盖字段。健康探针不执行 Endpoint consumer token 鉴权, 因此 handler 只能返回就绪状态,不应输出配置、凭据或业务数据。

当多个 Project Environment 共用一个 Slot Runtime 时,每个业务进程仍只会收到属于自身 Deployment 的 Endpoint, 并保留模块定义中的逻辑名称(例如 api)。Slot 父进程使用的命名空间前缀、访问令牌和其他 内部聚合字段不会进入业务进程的 RUNTIME_ENDPOINTS_JSON;若适配器读取该可选元数据,只能依赖下表定义的七个公开字段, 监听端口仍必须从对应的命名环境变量读取。

每个 Endpoint 对象包含以下字段:

字段类型含义
namestringEndpoint 的稳定名称,例如 api 或 management
protocolstringEndpoint 协议;当前为 http
listenPortintegerRuntime 为该 Endpoint 动态分配的业务进程监听端口
routeHoststring平台为该 Endpoint 分配的路由主机名
baseUrlstring调用方可使用的完整公开基础 URL
authModestring该 Endpoint 当前采用的 Hosted Service 鉴权模式
primaryboolean是否为当前服务的主 Endpoint

示例结构如下;实际值由当前部署动态生成:

[
  {
    "name": "api",
    "protocol": "http",
    "listenPort": 20000,
    "routeHost": "example.app.baijimu.com.cn",
    "baseUrl": "https://example.app.baijimu.com.cn",
    "authMode": "none",
    "primary": true
  },
  {
    "name": "management",
    "protocol": "http",
    "listenPort": 20001,
    "routeHost": "example-management.app.baijimu.com.cn",
    "baseUrl": "https://example-management.app.baijimu.com.cn",
    "authMode": "token",
    "primary": false
  }
]

服务需要回传业务 API 地址时,应先按稳定语义选择 name == "api" 的 Endpoint;要求主入口时再同时校验 primary == true,并使用该对象的 baseUrl。缺少匹配项或存在多个匹配项应视为部署契约错误并停止启动, 不要猜测地址、退回旧属性值或从 routeHost 重新拼接 URL。

平台还会注入 RUNTIME_ENVIRONMENT_ID、RUNTIME_DEPLOYMENT_ID、RUNTIME_DEFAULT_USER_ID 和 RUNTIME_DEFAULT_WORKSPACE_ID,用于标识当前业务进程的部署与默认调用上下文。这些值和 RUNTIME_ENDPOINTS_JSON 都是平台拥有的运行时事实:即使环境普通配置、配置绑定或 Secret 中出现同名键, 最终注入值也以当前 Deployment 为准。命名 Endpoint 端口变量同样是平台拥有的运行时事实,业务进程必须为 自己声明的每个 Endpoint 读取对应命名端口,并在 127.0.0.1 上启动 HTTP listener。一个进程声明多个 Endpoint 时应在同一进程内绑定多个 listener,Runtime 仍只启动一个 OpenResty。listenPort 不能替代 baseUrl: 业务监听地址、Runtime 内部代理端口和公网调用地址是三个不同概念,只有 baseUrl 是调用方应保存和使用的稳定地址。

业务、管理与 Bundle 管理器生命周期入口

先按Bundle 管理器接入推荐架构确定协议由谁承载。 采用 Adapter 0.2.0 架构时,平台公开生命周期入口位于 Application Runtime 中 Adapter Bundle 自带的 Gateway。 网关通过 Adapter Module 验证 caller Token,再使用 Hosted Consumer 授权调用独立 Adapter 后端; Adapter 处理生命周期协议并调用业务 Hosted Service 的业务回调。Bundle 安装交付网关与标准模块方法, 后端进程、握手调度、凭据存储和数据库仍由独立 Hosted Service 承担,安装不会创建后端。 业务后端只实现业务回调,无需重复实现平台管理器协议。 以下 Endpoint 清单、listener 与登记步骤用于后端直接实现管理器协议;采用 Adapter Gateway 时, 管理器登记使用网关完整执行地址,Adapter 后端 Endpoint 保持 Hosted Token 保护,业务项目保留自己的业务 Endpoint。

设计后端时,先区分三类调用方,再声明 Endpoint。多租户服务如果需要随 Bundle 安装开通租户、 升级或配置时同步业务配置、卸载时解除绑定,必须提供供平台管理器登记服务调用的生命周期接口。 多租户本身不要求接入管理器;不参与这些操作的服务无需声明生命周期入口。

入口示例调用方职责与鉴权
api应用、模块、工作流、业务用户执行业务请求,由业务后端识别租户、校验业务权限并隔离数据。
management服务运营人员、租户管理员管理业务配置、账号或配额;分别校验租户内管理权限和跨租户权限。
lifecycle百积木管理器登记服务执行获授权的 Bundle 生命周期操作,校验平台调用管理器的独立凭据和完整安装归属。

这三个名称是设计示例,不是平台保留名称,也不会自动生成 handler 或授予权限。 推荐将平台生命周期入口与业务入口、人工管理入口分开声明,便于独立路由和限制接口范围。 生命周期接口也可以与管理接口共用 listener,但必须按路由分别认证、授权,不能让管理器凭据获得所有管理能力。 管理器可以由业务服务直接实现,也可以由独立适配服务实现;后者只在自己的项目中声明执行入口。

在 Hosted Service 声明 Bundle 管理器的监听入口

下面是同一进程提供三类入口的完整清单示例。业务 api 使用 auth_mode = "none",由业务后端校验租户 token 和业务权限。后端从校验后的凭据确定租户,并按该租户隔离读写。 人工管理入口先通过 Hosted Service 入口鉴权,再校验管理员权限。生命周期入口校验平台调用 Bundle 管理器的专用 Bearer token。

contract_version = "3.0.0"

[service]
entrypoint = "target/release/example-service"

[[service.endpoints]]
name = "api"
protocol = "http"
auth_mode = "none"
primary = true
required = true

[[service.endpoints]]
name = "management"
protocol = "http"
auth_mode = "token"
required = true

[[service.endpoints]]
name = "lifecycle"
protocol = "http"
auth_mode = "none"
required = true

业务进程分别读取 APP_ENDPOINT_API_PORT、APP_ENDPOINT_MANAGEMENT_PORT 和 APP_ENDPOINT_LIFECYCLE_PORT,在 127.0.0.1 启动对应 listener;每个 listener 都实现固定的 GET /healthz。不要硬编码端口,也不要为声明之外的入口猜测端口或回退到业务端口。 这是业务应用自己的监听入口,不是 Hosted Runtime 的运维或热更新管理端口。

lifecycle 示例使用 auth_mode = "none",只表示 Hosted Service 不校验 consumer token。 生命周期 handler 必须先验证 Authorization: Bearer <平台调用管理器的独立凭据>,再解析和执行请求; 缺失或错误凭据必须拒绝。该凭据通过受控服务端配置提供,不得复用业务租户 Token、人工管理 Token 或管理器调用平台的凭据。不要把这个示例直接改成 token 并复用管理密钥,否则会把两种独立认证合同混在一起。 声明多个端口不会自动建立网络隔离;实际可达范围以部署后的路由和访问策略为准。

从 Endpoint 到 Bundle 管理器登记

完整首次接入和验收以首次接入流程与验收为准。声明 listener 不会自动注册管理器;普通后端无需注册。 直接承载管理器协议时,按以下顺序衔接部署与握手:

  1. 实现当前管理器协议及认证,准备数据库和加密密钥。
  2. 用 baijimu workspace lifecycle-plugin register 申请一次性授权,通过部署 Secret 及变量绑定交付。
  3. 部署或重启后,从 RUNTIME_ENDPOINTS_JSON 选择实际承载协议的 Endpoint,将正式 HTTPS baseUrl 与真实 POST 路径组成执行地址。 不使用本机监听地址、动态端口或人工管理页面;不要根据 primary 标记猜测哪个入口实现了协议。
  4. 管理器持久化交换状态并完成握手,登记地址与阶段,建立双向凭据;CLI 的 complete-enrollment 只回查结果。
  5. 移除一次性引导配置,保留身份和数据库密钥;验证无引导重启、轮换和鉴权,再进行业务 Bundle 联调。

采用 Runtime Gateway 的 Adapter 时,执行地址来自正式网关记录,按 Adapter 接入顺序准备调用链,不能套用直连 Endpoint 地址。 地址属于管理器登记目录,凭据属于受控存储,都不写入 Bundle。 AI 交付时报告实际 Endpoint、执行 URL、管理器身份、接入及业务验收结果,不输出凭据;单独 /healthz 通过不能证明管理器已接入。

Endpoint 鉴权

Hosted Service 支持两类边界:

  • authMode=token:由环境级 consumer 和绑定到当前 Endpoint 的 token 控制;Hosted Service 不解释 consumer 或 Endpoint 的业务用途。
  • authMode=none:Hosted Service 不校验平台服务 token,业务应用必须自己实现完整鉴权。

Endpoint 鉴权只支持 none 和 token。Endpoint 名称仅用于稳定路由,不会把 management、primary 或其他名称转换成不同鉴权语义。

选择 none 不代表 Endpoint 自动安全,也不代表可以信任前端传入的用户身份。

管理 consumer 和 token 前先读取当前命令:

baijimu hosted-service consumer --help
baijimu hosted-service token --help
baijimu hosted-service auth-config --help

token list 同时返回数字 id 和公开 tokenId。撤销时必须传 tokenId(例如 hsvc_...),数字 id 只是平台内部记录编号,不是撤销契约:

baijimu workspace hosted-service token list WORKSPACE_ID --environment-id ENVIRONMENT_ID
baijimu workspace hosted-service token revoke WORKSPACE_ID \
  --environment-id ENVIRONMENT_ID \
  --token-id TOKEN_ID

删除 consumer 会在同一事务中撤销它的全部 token,并软删除 consumer;该操作必须显式确认:

baijimu workspace hosted-service consumer delete WORKSPACE_ID \
  --consumer-id CONSUMER_ID \
  --yes

撤销不存在的 tokenId、删除不存在的 consumer,或操作不属于当前工作区的资源都会失败关闭,不会返回伪成功。

token 创建后只在安全位置保存。不要把它:

  • 写入平台应用 accessUrl
  • 提交到项目 Git
  • 返回给浏览器端长期保存
  • 输出到构建或部署日志

Hosted Service Endpoint token 保护入站服务访问。后端调用 Bundle 资源时,使用生命周期交付的 ServiceReference 与对应资源的正式方法合同。 资源身份、调用凭据与业务用户委托分别校验,当前资源由各资源 Owner 提供。 旧 Bundle Capability 的全局 Token 和 Hosted 受管绑定已经退役。

接收 Runtime 调用上下文

后端作为模块方法的 HTTP 目标时,Runtime 不会自动向 Rust 服务注入 SDK 对象或完整上下文 JSON。模块必须 在 methodBody 中显式选择稳定上下文字段并映射到自己的 Header、Path、Query 或 Body;Rust 服务再在 HTTP 入口把映射结果转换成类型化请求上下文,并对必需字段失败关闭。

准确字段、出现条件、expression: "system.<字段名>" 映射,以及可直接使用的 Rust/Axum 提取示例见 模块调用上下文。自定义上下文 Header 不是 Endpoint 凭据;服务鉴权和业务用户授权仍需分别校验。

业务用户身份

服务 token 只回答“哪个调用方可以访问这个 Endpoint”,不自动回答“当前业务用户是谁、能操作哪些数据”。

平台应用调用后端应用时,应由受控网关传递已验证的工作区和用户上下文;后端应用不能把普通请求参数中的 userId 当成可信身份。管理员代办能力必须单独定义权限和审计。

接收 X-Baijimu-User-Assertion 的后端应用按获取平台用户断言公钥配置和刷新签发环境的 JWKS,再验证断言及业务授权。

后端同时作为模块方法目标,并按逻辑引用解析出的地址和凭据调用其他 Runtime 服务时,使用 Runtime 用户委托传递当前已验证用户:

  • 入站请求存在 X-Runtime-Actor-Assertion 时,只在当前请求内保存和转发;
  • 出站调用同时携带目标引用的 Bearer token 和该 assertion;
  • 后端不签名、不解析、不记录 assertion,跨服务的下一跳轮换由 Runtime 完成;
  • assertion 只对引用解析返回的 url 有效,不能作为 Hosted Service 普通公开 Endpoint 的鉴权方式。

本页内容