历史 · 应用、服务身份与凭据
重构前档案,仅供追溯,不作为新版本执行指令
历史档案 · 2026-09-11 重构前快照。 本文中的“当前”“已冻结”和实施顺序属于旧基线。新版本以现行总览和实施计划为准。
Developer App 是 OceanWay API 产品中的业务容器。所有机器调用都必须归属明确的 App、Environment 和 Service Account;API Key 只是 Credential,不是用户、钱包或独立权限主体。登录后管理界面统一位于 console.oceanway.tech/ai,ai.oceanway.tech 公开 Developer Center 不读取或修改这些资源。
正式资源层级
Organization / Personal Space
└── Developer App
└── Environment
└── Service Account
└── Credential| 对象 | 职责 | 不负责 |
|---|---|---|
| Organization / Personal Space | 资源所有权、成员关系和付款上下文 | 保存应用运行密钥 |
| Developer App | 产品接入边界、授权范围和用量聚合 | 充当执行身份或钱包 |
| Environment | 隔离开发、测试、生产配置与风险 | 复制 Organization 成员体系 |
| Service Account | 非交互式 Principal、Scope 和策略主体 | 保存明文 Secret、拥有独立余额 |
| Credential | 证明 Service Account 身份 | 单独决定权限、价格或付款方 |
Workspace 与可选 Project 是 App 或 Service Account 的授权绑定,不插入上述身份父子层级。Billing Account 由 Personal Space 或 Organization 的付款规则解析;App、Environment、Service Account 和 Credential 只能拥有预算与限额,不能拥有子钱包。
唯一 Command Owner
Developer Access Domain 是四层资源的唯一 Command Owner:
DeveloperAccess.create/update/disable/deleteApp
DeveloperAccess.create/update/disableEnvironment
DeveloperAccess.create/update/disableServiceAccount
DeveloperAccess.issue/rotate/revokeCredential这个边界独立于部署方式。首期可以在共享后端中实现为模块化领域服务,但必须满足:
- Console AI BFF 只校验页面请求、调用领域 Command 和组合读模型,不直接写领域表;
- Console 顶层安全/治理页面可以发起停用或撤销 Command,不复制 Credential 状态;
- Public API Edge 只校验 Credential 和读取不可变 Access Snapshot,不创建或修改四层资源;
- Developer Center 只读取公开内容,不接收任何客户资源 Command;
- Text/Media Gateway 不认识 App 层级,不保存 Developer Credential;
- 所有状态变化使用同一事务、并发约束、审计和 Outbox 事件。
Developer 是逻辑产品与领域,不是第二客户后台。Customer Identity、Organization、Membership、Workspace、Project、Billing Account 和 Audit 继续由共享客户控制面拥有。
个人默认空间与默认应用
每个个人用户首次进入 Console AI 专业空间时,Developer Access Domain 以幂等方式确保存在:
Personal Space
└── 默认 Developer App
└── 默认 Environment
└── 默认 Service Account这个默认链路用于降低第一次调用门槛,但底层仍使用完整资源模型:
- 用户可以直接创建第一枚 Credential,而不必手工创建四层资源;
- UI 可以把默认层级折叠为“我的应用”,不能在数据库中继续把 Key 直接绑定 User;
- 默认对象有稳定 ID、审计和生命周期,不通过名称识别;
- 用户创建第二个 App、加入企业或启用高级策略后,再展开完整层级;
- 个人默认资源始终属于 Personal Space,不会因用户加入企业而自动转为企业资产。
企业用户必须在当前 Organization 上下文中创建 App。员工离职只撤销 Membership 和其交互式访问,Organization 拥有的 App、Service Account、Credential、Run 与用量保持有效,除非企业策略明确停用或轮换。
Developer App
Developer App 表示一个客户应用或集成,例如网站后端、内部自动化、移动应用服务端或数据处理任务。
建议核心字段:
developerAppId
ownerType = personal_space | organization
ownerId
workspaceId?
projectId?
name / description
status
allowedOfferingIds[]?
defaultBudgetPolicyId?
createdBy / createdAt / updatedAt规则:
- App 只能属于一个 Personal Space 或 Organization;
- 企业 App 的管理权限来自 Membership 和 Developer 角色,不来自任意持有 Key 的用户;
- App 可以绑定一个 Workspace 和可选 Project,以获得 Asset、Agent、MCP 和 Project Context;
- 绑定只扩大到明确授权的资源,不等于 App 可以读取整个 Organization;
- App 停用后拒绝新请求,但历史 Run、Usage 和 Audit 保持可查询;
- App 删除必须先处理活跃 Credential、Webhook、保留策略和不可变账务记录,不能级联擦除历史事实。
Environment
Environment 将同一 App 的配置、凭据、Webhook、限额和运行历史隔离。典型显示名可以是开发、测试和生产,但系统使用稳定 environmentId,不把名称写成固定权限规则。
environmentId
developerAppId
name / slug
riskClass
status
allowedOfferingIds[]?
rateLimitPolicyId?
budgetPolicyId?
dataPolicyId?
createdAt / updatedAt环境规则:
- Credential、Webhook Endpoint 和 Run 必须固定一个 Environment;
- 不允许把测试环境 Key 静默提升为生产环境 Key;
- Environment 的模型、区域或数据策略只能收窄 App 与 Organization 策略;
- 生产环境的高风险变更可以要求再认证、双人审批或延迟生效;
- 删除环境前必须撤销 Credential,并保留历史 Run 和计费解释所需快照。
Service Account
Service Account 是非交互式权限主体。它唯一归属一个 Environment,并继承 App 与所有者上下文。
serviceAccountId
principalId
environmentId
tenantKind = organization | personal_space
tenantId
name
status
scopes[]
allowedOfferingIds[]?
workspaceBindings[]
projectBindings[]
assetPermissions[]
agentDeploymentPermissions[]
mcpConnectionGrants[]
rateLimitPolicyId?
budgetPolicyId?
createdBy / createdAt / disabledAt?Service Account 不登录 Console,不持有 Customer Session,也不能成为 Organization 成员。Customer User 通过 Console AI 专业空间管理它;Public API 根据 Credential 解析它。
principalId 是 Principal Registry 中 kind=service_account 的稳定 Principal ID,与 serviceAccountId 组成不可变的一对一绑定。二者属于不同命名空间:同一 Principal 不能绑定多个 Service Account,同一 Service Account 也不能更换、复用或借用另一个资源的 Principal。停用或删除 Service Account 只改变资源生命周期并撤销 Credential,不回收其 Principal ID;历史 Run、授权和计量继续引用原 Principal。创建、读取和准入必须验证 Principal Registry 的 Kind、Tenant 与 Service Account 归属完全一致,禁止把 Customer User、Workload Principal 或另一个 Tenant 的 Principal 绑定进来。
生产自动化必须使用 Service Account Credential,不能依赖某位员工的个人交互式 Session 或个人 Key。Service Account 权限采用最小授权:未声明的 Scope、Offering、Workspace、Project、Asset、Agent 或 MCP Tool 默认拒绝。
Credential 与 API Key
第一阶段客户 Developer Credential 类型为 API Key;未来正式发布的 OAuth Client 或客户侧短期 Token 只有经独立 ADR 批准后,才可以扩展同一开发者凭据抽象。OceanWay 内部 Workload Principal/Credential 属于独立信任边界,不复用 Developer Credential 的类型、Audience、Secret 或轮换记录。
developerCredentialId
serviceAccountId
type = api_key
prefix / lastFour
secretHash
status
expiresAt?
notBefore?
ipAllowlist[]?
rateLimitOverrideId?
createdBy / createdAt
lastUsedAt?
rotatedFromId?
revokedAt?安全规则:
- Secret 使用密码学安全随机源生成,只在创建响应中完整返回一次。
- 服务端只持久化校验所需 Hash、前缀和尾号,不保存可逆密文。
- 列表、详情、审计、日志和支持工具永不回显完整 Secret。
- 轮换创建新 Credential,通过
rotatedFromId建立关系;旧 Key 在明确宽限期后撤销。 - Credential 泄漏只需撤销 Credential,不改变 Service Account、App、钱包或历史 Run 身份。
- Developer Key 只在
api.oceanway.techEdge 鉴权,不转发给 Text/Media Gateway 或 Provider。
身份类型与登录边界
| Principal | 认证或委托方式 | 可以做什么 |
|---|---|---|
| Customer User | OceanWay Customer Identity / Console Host-only Session | 登录 Console AI、提交有权执行的 Developer Access Command |
| Service Account | DeveloperCredential;Playground 由短期 Grant 委托 | 作为唯一 Public API 执行主体,不登录任何客户 UI |
| OceanWay Workload Principal | 短期 Workload Credential 或 mTLS assertion | OceanWay 服务间调用和私有网关访问 |
| OceanWay Workforce | 独立 Workforce Identity | 进入内部 Admin 和运维读模型 |
DeveloperCredential 与 Playground Execution Grant 都是认证或授权证据,不是 Principal。Playground 不创建第五类持久主体:Customer User 是业务发起人,目标 Service Account 是唯一 API 执行主体,Grant 只证明该用户在受限条件下可以委托该 Service Account 执行。
Customer Session、DeveloperCredential、Playground Execution Grant、Workload Credential 和 Workforce Session 必须使用不同 Audience。Developer Center 不需要 Customer Session;Console Session 不能被 Public API、私有网关或 Admin 接受。Workload Identity 是内部信任体系的统称;实现中必须区分稳定 Workload Principal 与它使用的短期 Workload Credential。
Playground Execution Grant
Playground 不创建、读取或复用长期 Developer Credential。浏览器携带 Console Session 调用 Console AI BFF;BFF 从 Developer Access Domain 取得目标 App、Environment 与 Service Account 的 Access Snapshot,再向 Authorization/Policy Token Issuer 申请短期、受限的 Execution Grant,并由服务端调用 Public API Edge。BFF 只能申请和持有 Grant,不能自行签发。
Grant 必须绑定 Customer User Actor、目标 Service Account、App、Environment、允许的 Offering/操作、有效期、使用约束和审计身份。它不是持久 Credential,不能执行四层资源命令,也不得暴露给浏览器 JavaScript、本地存储、URL 或客户端日志。API Edge 接受 Grant Audience,但仍不接受 Customer Session。
授权解析
每个 Public API 请求按固定顺序计算有效权限。正式客户程序集成使用 Developer Credential;Console Playground 使用短期受限 Grant:
Developer Credential 有效或 Playground Grant 有效
∩ 目标 Service Account 启用且 Scope 允许
∩ Environment 启用且策略允许
∩ Developer App 启用且应用授权允许
∩ Organization / Personal Space 有效
∩ Workspace / Project / Asset / Agent / MCP Grant
∩ Model Offering 已发布到 api Surface
∩ Entitlement、预算、限额和区域/数据策略任何一层拒绝即停止执行。前端传入的 Organization、App 或 Project ID 只能作为选择提示,服务端必须从 Developer Credential 或 Playground Grant 重新解析所有权和允许范围。
授权结果进入不可变 RunAdmissionManifest@N。Run 上的 executionManifestRef 专门指向这份准入清单,executionManifestSchemaVersion 固定其严格 Schema Version;二者都不指向某次 Attempt 或网关私有路由快照。本页只列出 RunAdmissionManifest 中 Developer Access 相关的必要字段;完整字段表以调度与网关架构为准:
actorPrincipalId
executionPrincipalId
authentication =
{ kind=developer_credential; developerAppId; environmentId; serviceAccountId; developerCredentialId }
| { kind=playground_execution_grant; developerAppId; environmentId; serviceAccountId;
playgroundExecutionGrantId; playgroundExecutionGrantSchemaVersion;
playgroundExecutionGrantClaimsDigestAlgorithmVersion; playgroundExecutionGrantClaimsDigest }
tenantKind / tenantId
workspaceId / projectId?
billingAccountId
source = { surface=developer_api|developer_playground; apiVersion; operation }
product = developer_api | developer_playground
authorizationDecisionId / authorizationDecisionSchemaVersion
authorizationDecisionInputDigestAlgorithmVersion / authorizationDecisionInputDigest
authorizationEvidenceEvaluationSetRef / authorizationEvidenceEvaluationSetSchemaVersion
authorizationEvidenceEvaluationSetDigestAlgorithmVersion / authorizationEvidenceEvaluationSetDigest
authorizationEvidenceArchiveCutRevision
authorizationEvidenceEvaluationCount
authorizationEvidenceEvaluations[] = sorted {
evidenceKind;
authorizationEvidenceEvaluationRef / authorizationEvidenceEvaluationSchemaVersion;
authorizationEvidenceEvaluationDigestAlgorithmVersion / authorizationEvidenceEvaluationDigest;
archiveRevision
}
executionAuthorization = {
authorizationScopeSnapshotRef / authorizationScopeSnapshotSchemaVersion
authorizationScopeSnapshotDigestAlgorithmVersion / authorizationScopeSnapshotDigest
allowedAssetVersionRefs[] / allowedModelOfferingRevisionIds[] / allowedToolVersionRefs[]
authorizationActionIds[]=sorted
authorizationActionCount
}
modelOfferingRevisionId / logicalModelId
modelDeploymentId # 准入时冻结的 initial target
modelRoutingPolicyRevisionId
gatewayPool = text | media
pricingSnapshotId / billingPolicyRevisionId / billingReservationIdmodelDeploymentId 在 RunAdmissionManifest 中只表示准入时的 initial target。每次实际执行由独立不可变的 AttemptExecutionManifest@N 冻结 Attempt 与实际 Deployment;重试只能在已冻结的 modelRoutingPolicyRevisionId 允许时更换 Deployment,且不得改写 RunAdmissionManifest。
合法组合只有两种:
正式 API:actorPrincipalId = serviceAccountPrincipalId
executionPrincipalId = serviceAccountPrincipalId
authentication.serviceAccountId = ServiceAccount 资源 ID
developerCredentialId = required
Playground:actorPrincipalId = customerUserPrincipalId
executionPrincipalId = serviceAccountPrincipalId
authentication.serviceAccountId = ServiceAccount 资源 ID
playgroundExecutionGrantId + Schema Version + Claims Digest = requiredPrincipal ID 与 ServiceAccountId 属于不同命名空间,不能按字符串相等。Core 必须通过受信 Developer Access/Identity 关系验证 authentication.serviceAccountId → serviceAccountPrincipalId;正式 API 的 Actor/Execution Principal相等,Playground的 Customer User Actor与 Service Account Execution Principal不相等。两种认证引用不得同时存在,也不得同时为空;Playground Grant与 Authorization Decision的版本化摘要必须在不可变 Authorization Evidence Archive中验证。Core必须以同一 Operation一次提交完整期望成员、Authorization Scope Snapshot完整四元组、排序去重的 authorizationActionIds[]及authorizationActionCount,由 Archive通过与撤销 Writer共用的 Revision Fence在单一事务用服务端时间签发内容寻址 AuthorizationEvidenceEvaluationSet@1,再将 Set四元组、统一 Archive Cut、Scope/Action Count/Action成员和严格成员集合固定到 Manifest:正式 API恰好包含 Authorization Decision Evaluation,Playground恰好包含 Authorization Decision + Playground Grant Evaluation。Count必须等于Action数组长度并参与摘要,但不能替代精确成员;Scope Snapshot/Action Count/Action集合必须与 Set、run.created@2.0及 Manifest的 executionAuthorization逐项相等,缺失/错误Count、重复Action、含义未定义的 scopes[]或展示标签都不能替代 Action ID。调用方不得逐成员签发、传入过去的评估时间或拼接不同 Archive Revision;先提交的撤销必须进入新 Set并阻止 Valid。历史重放先以冻结 Set/Scope/Action读取,再读取其成员,不按当前撤销 Timeline重新解释。source.surface == product,两个分支分别固定为 developer_api | developer_playground。Console BFF的 Workload Principal只记录在服务跳转与 Invocation Audit中,不能覆盖业务 Actor或执行主体。
幂等身份必须和上述 Actor/Execution 分离规则一致。当前已验收的 execution.run-admission.v1 只允许 Organization + Developer Credential,不能直接接收 Playground。启用 Playground 前必须切换到 Contracts 发布的多 Actor Operation/Idempotency 版本,其规范身份至少包含 tenantKind + tenantId + authentication.kind + product + actorPrincipalId + executionPrincipalId + developerAppId + environmentId + serviceAccountId + operation + Idempotency-Key。因此正式 API 与 Playground、或两个 Customer User 即使共用同一 Service Account、Key 与相同请求,也不会返回彼此的 Run;Credential 轮换则因为稳定 Actor/Execution/Access 资源身份未变,仍可返回原 Run。跨 Actor/认证/Product 合并、客户端覆盖身份和缺失任一身份字段必须在创建 Run/Reservation 前拒绝,并由并发负测证明。
Scope 与资源授权
Scope 表示动作权限,例如模型执行、Run 查询、结果读取或 Webhook 管理;它不能代替资源级权限。
建议将权限分为:
- API 动作:
models:read、runs:create、runs:read、outputs:read; - 能力范围:文本、Embedding、Rerank、图像、视频等;
- Offering 白名单:允许的公开模型或 Family;
- 上下文范围:Workspace、Project 与数据区域;
- 生态资源:固定 Asset Revision、Agent Deployment、MCP Connection Grant;
- 商业约束:预算、并发、速率、合同权益和成本中心。
最终 Scope 名称和粒度需要进入 API 版本契约;实现不得用一个泛化 all 永久绕过策略层。
生命周期
App、Environment 和 Service Account 可以停用并恢复;Credential 只允许 active → revoked/expired,撤销后不可恢复。删除是受治理的资源终止,不得删除 Ledger、Usage、Run、Audit 或历史 Offering/价格快照。
状态传播规则:
- 父资源停用立即阻止所有子 Credential 发起新请求;
- 已在执行的 Run 按已冻结 Manifest 和安全策略继续、取消或人工处置,不能由前端猜测;
- 恢复父资源不会恢复已撤销或已过期 Credential;
- Membership 撤销影响用户管理权限,不自动删除企业 Service Account;
- Credential 状态变化发布审计事件并失效鉴权缓存。
预算、计费与用量归属
每次调用同时记录以下维度:
billingAccountId
tenantKind / tenantId
authentication.kind
authentication.developerAppId / authentication.environmentId / authentication.serviceAccountId
authentication.developerCredentialId | authentication.playgroundExecutionGrantId + Schema Version + Claims Digest(由 kind 严格选择)
authorizationDecisionId / Schema Version / Input Digest
product
workspaceId / projectId?
runId / executionAttemptId
modelOfferingRevisionId / pricingSnapshotId / billingPolicyRevisionIdtenantKind + tenantId 是严格判别联合,不再同时携带 organizationId? / personalSpaceId?;个人请求不能伪造 Organization。预算和限额可以设置在 Organization/Personal Space、Workspace、App、Environment 或 Service Account,并按“上层上限与下层上限取更严格者”计算。它们不是余额复制。企业请求余额不足时不得自动改扣员工个人钱包。
Console AI 专业空间展示 API 与 Playground 维度;Console 顶层展示全平台 Billing Account、订阅和账本;Developer Center 只展示公开列表价;Admin 展示脱敏的内部诊断与供应成本。
Webhook 所有权
Webhook Endpoint 归属一个 Environment,事件投递使用 App/Environment 上下文,并在 Console AI 专业空间管理。签名材料使用独立的 WebhookSigningSecretVersion,采用一次显示、版本化轮换和 Vault 托管,不属于 Service Account 的 Developer Credential,也不复用 Public API Key。
Service Account 可以拥有管理 Webhook 的 Scope,但 Webhook Endpoint 不是 Service Account 子对象,以免轮换或停用某个调用身份时错误删除整个应用的事件出口。
审计不变量
以下动作必须记录 Actor、Owner、App、Environment、目标资源、前后状态、原因、Request/Trace ID 和时间:
- App、Environment、Service Account 的创建、停用、恢复和删除;
- Credential 创建、轮换、撤销、过期与高风险使用;
- Scope、模型白名单、Workspace/Project Binding 和预算策略变更;
- Webhook Secret 轮换、Endpoint 停用与事件重放;
- 企业成员对生产资源的访问和权限提升。
审计不得包含完整 Secret、完整 Prompt、媒体二进制或 Provider Credential。
遗留 oceanway-vozeb 实现基线
旧 oceanway-vozeb 的 developer_api_keys 直接保存 user_id,支持名称、到期、IP 列表、速率和并发字段。服务会保存可逆密文,并允许登录用户之后再次回显 Secret;该迁移来源中尚无 Developer App、Environment、Service Account、Organization/Membership 或相应资源授权表。独立 Core/API Edge 已验收四层 Snapshot 与 Hash-only 验证,但仅覆盖受控 Organization-backed Admission;不能用旧实现描述覆盖这项新基线。
这套实现可以支撑个人 MVP,但不能作为企业生产身份模型继续扩展。
目标状态
目标态中,每个正式客户程序请求都能从 Credential 唯一解析到 Service Account、Environment、Developer App、所有者、Workspace/Project、Billing Account 和策略决策;Playground 请求从受限 Grant 解析到同等业务上下文及 Customer User Actor。员工身份与生产服务身份分离;个人用户仍能通过自动创建的默认层级快速开始。四层资源只有 Developer Access Domain 可以修改,Console AI 只是专业任务入口,公开 Center 没有客户私有状态。
实施阶段
| 阶段 | 交付 | 退出条件 |
|---|---|---|
| ID0 模型冻结 | 数据模型、唯一 Command Owner、角色、Scope、生命周期和审计事件 | 不再新增直接归属 User 或由 BFF 直写的 Key 功能 |
| ID1 默认个人链路 | Personal Space、默认 App/Environment/Service Account | 新个人用户从 Console AI 无需理解层级即可创建一次性 Key |
| ID2 企业链路 | Organization Membership、Developer 角色、Workspace/Project Binding | 成员离职不影响企业生产身份 |
| ID3 Credential 收口 | Hash-only Secret、轮换、撤销、缓存失效和审计 | 任意接口均无法再次回显 Secret |
| ID4 Playground Grant | 短期受限授权、Console BFF 服务端转发与审计 | 浏览器无长期 Key,API Edge 无 Customer Session |
| ID5 策略与商业 | Scope、Offering 白名单、预算、限额和 Billing Account 解析 | 每个请求都有完整且可解释的授权决策 |
验收标准
- 新用户首次进入 Console AI 后自动拥有且只拥有一套幂等创建的默认开发者链路。
- 每枚 API Key 唯一归属一个 Service Account,并可追溯到 Environment、App 和 Owner。
- 完整 Secret 只在创建或轮换响应中出现一次,数据库、日志和查询接口均无法恢复。
- 停用 App、Environment 或 Service Account 后,全部子 Credential 立即拒绝新请求。
- 企业成员离职后不能管理企业 App,但未依赖该成员身份的生产调用不被删除。
- 服务端忽略伪造的 Organization/Workspace/Project 上下文,并按 Credential 重新授权。
- API 用量可按 App、Environment、Service Account 和 Credential 聚合,金额仍归统一 Billing Account。
- 私有网关只接收 OceanWay Workload Principal 使用的短期 Workload Credential,不接收 Customer Session 或 Developer Key。
- Developer Center 无四层资源读写接口;Console BFF 和 API Edge 均不能直接写领域存储。
- Playground 的浏览器网络、页面状态与本地存储中不存在长期 Developer Credential。
- Contracts/Repository 负向测试拒绝 Principal Registry Kind 错误、跨 Tenant 绑定、同一 Principal 绑定多个 Service Account、同一 Service Account 换绑或复用 Principal,以及把
serviceAccountId当作principalId;停用后历史 Run 仍能解析原不可变绑定。