历史 · 管理员平台与运维控制面
重构前档案,仅供追溯,不作为新版本执行指令
历史档案 · 2026-09-11 重构前快照。 本文中的“当前”“已冻结”和实施顺序属于旧基线。新版本以现行总览和实施计划为准。
OceanWay 管理员平台不是服务器日志查看器,也不是把用户、模型、账单和 Gateway 管理页拼在一起。它是一套以业务执行链为中心的内部控制面:从一个用户可提供的 Request ID 出发,定位身份、预算、Run、网关、Provider、资产和账务事实,判断影响范围,执行受控处置,并留下可复核证据。
本章定义 admin.oceanway.tech 的产品定位、身份边界、运行数据契约、页面结构、处置权限与交付顺序。可靠投递、投影和查询的实现边界见 ADR-030,首个界面契约见 Run Explorer;具体模型执行状态见调度、执行与模型网关池,账务事实见钱包与商业系统,客户与内部权限边界见权限、安全与治理。
正式决策
| 事项 | 正式选择 | 含义 |
|---|---|---|
| 内部入口 | admin.oceanway.tech | OceanWay 员工、值班与审计人员的内部管理入口,不进入客户应用启动器 |
| 首期实现 | 复用现有 OceanWay /admin 作为代码基座 | 不另起项目重写;通过 Host Surface、模块边界和权限逐步收敛 |
| 仓库边界 | oceanway-admin 独立 Git 与发布边界 | 现有 /admin 是迁移来源;目标仓不复制 Core 事实或直接访问 Gateway 数据库 |
| 产品形态 | 一个后台外壳、按角色呈现工作区 | 首期不再拆一套 ops 系统;查询与命令在同一产品内保持独立边界 |
| 身份边界 | Workforce SSO Client + Host-only Session | 可以复用同一个底层 IdP,但不能复用客户 Session 或普通企业角色 |
| 客户管理 | 与 OceanWay 内部管理员平台分离 | 企业 Owner、Billing Admin 等只管理自己的组织,不进入内部控制面 |
| 实施顺序 | 先只读诊断,后开放运维命令 | 没有完整关联、错误确定性、权限和审计前,不开放危险写操作 |
| 数据访问 | 领域事实、规范化事件、运维读模型和 Telemetry Query | 控制面不直连 Text/Media Gateway 数据库,也不通过直接改表处置故障 |
| Operations Owner | oceanway-core Operations 模块 | 唯一拥有 Outbox Dispatcher/Consumer/Projector、Operations Read Model、Observation accepted source 与 Private Query API |
| Admin Owner | oceanway-admin | 只拥有 Workforce Session、Admin BFF 与 UI,不拥有或复制 Operations 投影事实 |
| 首期部署 | 同一 Core image:core-api + 单一 managed core-operations-worker | 使用 PostgreSQL Outbox;不引入 Kafka、NATS、Redis 或新的业务服务 |
| 当前上线状态 | 契约已冻结,尚未实现 | Public Admission 与公共 Operations API 继续禁用,不把文档目标写成现有能力 |
Admin 与客户 Surface 的仓库边界已经建立;独立构建/发布、Workforce Session、Admin BFF 与 UI 迁移仍是 A1/A2 的目标,尚未形成已验收运行能力。业务事实和 Command Owner 始终位于 oceanway-core。依据 ADR-030,Outbox Dispatcher、Consumer、Projector、Operations Read Model、Observation accepted source 与 Private Query API 统一由 oceanway-core Operations 模块拥有;oceanway-admin 最终只拥有 Workforce Session、BFF 与 UI。首期目标使用同一 Core image 的两个 Process Role,并且每个环境只有一个 managed Worker,不创建新服务或第二份投影事实。
当前实现基线与处置
现有后台已经拥有可复用的业务骨架,但尚未形成平台级运维控制面:
| 当前能力 | 目标归属 | 处置 |
|---|---|---|
| 经营概览与用户管理 | 经营分析、客户支持 | 复用只读信息架构;写路由需对应 Domain Command 验收后逐项开放 |
| 商品、订单、促销、优惠券、邀请 | 商品运营与财务管理 | 复用产品工作流设计;逐步改接统一 Product、Billing Account 和追加式账本,遗留写路由不随迁移上线 |
| 积分、钱包、支付、CDK 与支付对账 | 财务管理 | 复用诊断界面;人工修复必须迁入已注册 Billing Command,只有两种正式 Billing sourceScope 可进入 Reconciliation Case |
| 模型渠道、模型配置与 Agent Skills | 上游配置 | 复用只读配置视图;重构为 Offering 发布、Text Pool 与 Media Pool 供应运维后再逐项开放命令 |
| 调用记录 | 运行记录 | 保留为有生命周期的业务记录,不能冒充不可变审计日志 |
| 生成运维、Worker、Heartbeat、Lease 与人工确认 | Run Explorer 与执行对账 | 只复用查询与信息架构;遗留人工确认/修复写路由默认禁用 |
| 站点、邮件、存储与备份 | 系统管理 | 复用只读视图;动作按风险拆分权限并完成命令审计后逐项开放 |
| 作品、公告和公共提示词 | 内容运营 | 复用领域界面,不与模型运行值班权限混合;写路由仍需独立命令验收 |
| Audit API 与管理员行为记录 | 审计中心 | 补齐正式页面、查询范围与服务主体审计 |
| 后台帮助和更新记录 | 运维知识与变更记录 | 关联 Runbook、Incident、发布版本与回滚说明 |
迁移不是简单改域名。以下当前做法只能作为过渡:普通用户 Session 加 role=admin、大粒度 system.manage/generation.manage、可以删除的“日志”、孤立的人工补录按钮,以及通过页面各自查询数据后由管理员自行拼接因果关系。
两类管理员必须分离
| 对象 | OceanWay 内部管理员 | 企业客户管理员 |
|---|---|---|
| 入口 | admin.oceanway.tech | console.oceanway.tech 或对应客户产品 |
| 身份 | Workforce Principal | Customer Identity + Organization Membership |
| 管理范围 | OceanWay 平台、供应、运行、财务、内容与安全 | 自己的 Organization、Workspace、Project 和合同范围 |
| 可见数据 | 按岗位脱敏后的跨租户运行信息 | 仅本组织资源、成员、预算、账单、Key 和审计 |
| 特权动作 | JIT、职责分离、Incident/Case 约束 | 组织策略允许的客户自助动作 |
| 内容访问 | 默认无权;Case-scoped Debug Grant | 按组织角色和资源授权 |
企业 Owner、Organization Admin、Billing Admin 和 Security Admin 不是 OceanWay 内部管理员。OceanWay 客服也不能因为能够搜索客户账户,就自动获得 Prompt、媒体原件、Provider Secret 或账务修改权限。
管理员角色基线
| 内部角色 | 主要职责 | 明确不自动获得 |
|---|---|---|
| Business Operator | 经营、商品、订单和客户运营 | Gateway Secret、客户内容、生产处置命令 |
| Support Operator | 账号和请求排障、客户沟通 | Secret、直接改账、完整 Prompt 和媒体原件 |
| Model / Supply Operator | Offering、Deployment、Channel/Supply 和 Provider 运行 | 用户钱包修改、客户内容和组织权限 |
| Platform SRE / On-call | 告警、Incident、队列、Worker、Deployment 缓解 | Secret 明文、任意退款、内容读取 |
| Asset Operator | 结果取回、校验、落盘与引用修复 | Provider Credential、用户余额调整 |
| Billing Reconciler | Reservation、Settlement、Release、Refund 对账 | Prompt、媒体内容、Provider Secret |
| Content Operator | 作品、公告和公共内容治理 | 生产运行命令、供应配置、账务处置 |
| Security Administrator | Workforce、策略、授权、凭据撤销与访问审查 | 客户内容、付款操作、凭据明文 |
| Auditor | 只读查看审计、变更、Incident 和账务证据 | 任何写操作 |
| Incident Commander | 协调影响范围、处置和沟通 | 自动获得其他岗位的技术或财务权限 |
“全权限管理员”只用于受控 Break-glass,不是日常岗位。Incident Commander 是协调责任,不是权限叠加器。
目标控制面拓扑
Host-only Workforce Cookie/Session 只在 Admin BFF 本地终止,绝不转发到 Core。Admin Query BFF 的普通运维与 Audit 投影读取只通过 Core Private Operations Query API,聚合经授权的读模型、低敏 Audit Projection 与 Telemetry Reference;它使用独立 Workload Principal、绑定该 Workload 的 Core Signed Query Grant 与 private, no-store 响应。Audit 原始存储没有独立的 BFF 直连路径,Core 按 Grant 的 Tenant/Resource/Action/Field Scope 裁剪后返回,并以受控 Audit Append Procedure 记录敏感查询。只有操作员在活跃 Incident/Reconciliation Case 中显式展开受限原始内容时,BFF 才能走第二条独立的 Authorized Telemetry Query:使用面向 Telemetry Audience 的短期 Workload JWT、由 Core Authorization 签发且绑定基础 Query Grant/同一 BFF Workload 的 SensitiveDebugGrant,并对 query.authorized → query.released | query.not_found | query.inconclusive | query.failed 执行 fail-closed Audit。两条路径使用不同 Client/Audience/Scope 和网络策略;普通 Query Grant 不能解引用 Telemetry,Telemetry Query 也不能读取 Operations/Audit 数据库或返回范围外内容。BFF 不持有任何数据库身份,也没有连接 Core/Gateway 数据库的网络权限。只有 Core Operations Query Role 可以读取批准的只读 View,并执行受控 Audit Append Procedure。Admin Query 与未来 Command Gateway 不共享身份或网络权限。Admin Command Gateway 只接受明确的领域命令,不能执行任意 SQL、任意 HTTP 转发或绕过 Text/Media Gateway 的状态机。
一条可还原的业务执行链
管理员需要同时看到关系图与真实时间轴。标准业务顺序是:
Public Request
→ Identity / Policy Decision
→ Quote / Billing Reservation
→ Run / RunStep / Execution Attempt
→ Gateway Dispatch
→ Text Gateway Invocation 或 Media Gateway Task
→ Provider Attempt / Provider Evidence Reference / Observation
→ Result Fetch / Validation / AssetVersion
→ Usage Path:Execution/Output Eligibility + Usage Evidence → canonical MeterEvent
→ Billing:eligible MeterEvent + frozen Price/Policy → Settlement / Release / Refund
↘ Cost Path:Attempt + Binding/Route + Cost Evidence → ProviderCostFact → Margin / Provider Reconciliation
→ Product Binding / Customer Webhook账务预占发生在外部调度前;Settlement 由具结算资格的 canonical MeterEvent、准入时冻结的 Price/Billing Policy 与对应可信 Execution/Output Eligibility 决定,失败/取消是否收费不能只看终态。ProviderCostFact 不要求成功 Output、Asset、MeterEvent 或 Settlement,且绝不进入客户扣款链。Gateway 只拥有不可变 ProviderUsageEvidence / ProviderCostEvidence 与 source ref,Observation 只可引用 Evidence;二者都不能生成规范 Fact 或触发结算。页面不能为了画出一条直线而改变真实因果顺序。某一环没有可信事实时显示“未知/未观测”,不能按时间接近、相同模型或相似 Prompt 猜测关联。
ID 与关联契约
长任务不能让一个 traceId 承担所有身份语义:
| ID | 生命周期 | 用途 |
|---|---|---|
requestId | 一次入口或服务请求 | 客户可提供的公开排障编号;每次请求不同 |
traceId / spanId | 一次分布式 Trace | 同步调用关系、延迟和错误位置 |
correlationId | 一条跨事件业务流程 | 连接异步事件、回调、恢复和补偿 |
operationId | 一次可能产生副作用的逻辑操作 | 幂等、计费和外部动作身份,不能重复复用 |
runId / runStepId / executionAttemptId | OceanWay 执行生命周期 | 用户目标、步骤、实际执行与业务重试 |
gatewayInvocationId / gatewayTaskId / providerAttemptId | 私有网关执行生命周期 | Text Invocation、Media Task 和真实 Provider 尝试;三者命名空间独立 |
deliveryAttemptId | Outbox 投递生命周期 | 一次受 Lease/Fencing 保护的投递尝试,不得与 Execution Attempt 混用 |
assetVersionId | 资产版本生命周期 | 正式结果、血缘与产品绑定 |
reservationId / meterEventId / providerCostFactId / ledgerEntryId | 账务生命周期 | 预占、规范计量/供应成本与追加式账本 |
alertId / incidentId / reconciliationCaseId | 运维处置生命周期 | 异常聚合、事故和善后案件 |
规则:
- OceanWay 在调用私有网关前固定
correlationId + operationId + executionAttemptId。 - 网关接收标准
traceparent和不透明 OceanWay Operation Reference;Text Gateway 产生gatewayInvocationId,Media Gateway 产生gatewayTaskId,两者可关联独立providerAttemptId。 - Provider 不支持 Trace 透传时,由 Gateway 保存本地映射;不得伪造 Provider 已支持 Trace。
- Poll、未来 Callback、队列恢复和人工命令通常形成新的 Trace,通过 Span Link、
correlationId和稳定领域 ID 关联原操作。 - Asset 登记保存来源 RunStep、Execution Attempt、严格 Gateway Execution Anchor(Text Invocation 或 Media Task)和可用的 Provider Attempt 引用。
- Reservation 保存
operationId与价格快照;结算只关联 Metering 写入的可信规范MeterEvent,Provider Cost Evidence/Fact 不单独决定客户费用。 - Public Request ID 只能通过授权查询解析到内部 ID,不能用连续编号或可猜测 URL 暴露跨租户信息。
运维读模型
Operational Read Model 是可重建的跨域投影,不是新的万能事实库。它至少提供:
OperationSummary
request / correlation / operation
actor / tenantKind / tenantId / workspace / project?
product / surface / capability
run / step / execution attempt
gateway pool / deployment
gateway execution anchor = { kind=text_invocation; gatewayInvocationId }
| { kind=media_task; gatewayTaskId }
provider attempt summary
asset persistence summary
billing reservation / settlement summary
current error / alert / incident / reconciliation links
latest trustworthy boundary
first_accepted_observation_at? / latest_accepted_observation_at?它由 Core Operations Projector 消费领域 Outbox 和 Core Intake 已接受的规范化 Observation 更新。控制面不读取 Core/Gateway 私有表,也不复制 Provider Credential、完整请求、原始媒体或用户账本。读模型滞后时页面同时显示投影版本、新鲜度、Source Snapshot anti-join、Revision Gap、Quarantine、完整性和 accepted health window,不能把“尚未同步”误判成“业务不存在”。首期不使用全局递增位置或其他标量水位证明完整性;详细契约见 ADR-030。
所有运维时间线统一使用五值 evidenceKind:domain_fact、operational_observation、operations_runtime、telemetry_reference、derived_summary。Delivery、Attempt、Receipt、Checkpoint(仅版本/快照状态)、Source Snapshot、Revision Gap 与 Quarantine 统一属于 operations_runtime;Gateway、Asset、Metering 与 Settlement 尚无对应领域事实时只显示“未接入/未知”,不能从 Observation、Logs、Trace 或相邻时间推断。
统一错误模型
错误不能只保存 status + message。每个 ErrorOccurrence 包含下列公共字段:
errorId
occurredAt? / sourceClaimedObservedAt? / acceptedAt? / derivedAt?
source / evidenceKind
layer / phase
normalizedCode / severity
certainty
retryDisposition
compensationDisposition
messageTemplateKey / sanitizedParameters
causeErrorId?
requestId? / traceId? / correlationId? / operationId?
runId? / runStepId? / executionAttemptId?
gatewayPool? / gatewayDeploymentId?
gatewayInvocationId? / gatewayTaskId?(严格互斥)
attemptRouteBindingRef? / attemptRouteBindingSchemaVersion?
attemptRouteBindingDigestAlgorithmVersion? / attemptRouteBindingDigest?
gatewayRouteSnapshotRef? / gatewayRouteSnapshotSchemaVersion?
gatewayRouteSnapshotDigestAlgorithmVersion? / gatewayRouteSnapshotDigest?
logicalModelId? / modelDeploymentId? / configRevision?
assetVersionId? / reservationId? / meterEventId?未认证、准入前或尚未接入下游的错误必须让未知关联字段保持缺失;不能为了满足 Schema 伪造 Tenant、Operation、Run、Gateway、Model、Asset 或 Billing 引用。
evidenceKind 是严格判别项。只有 operational_observation 且来源为同一条 Core Accepted Error Observation 时,才必须携带 observationId + acceptedAt + source.service + surface + errorNormalizationPolicyVersion + errorFingerprintVersion + errorFingerprint;source.service 是该分支唯一规范服务字段,不另设顶层 service。domain_fact | operations_runtime | telemetry_reference | derived_summary 禁止出现三个 Fingerprint/Policy 字段,也不进入 v1 Fingerprint Group。不能把五类 Evidence 的公共展示 DTO 误当成同一套指纹输入。
operational_observation 的 Gateway执行字段只能从同一条已接受 ErrorOccurrence@1.gatewayExecution展平,不能从同 Run、相近时间或相邻 GatewayDiagnostic补入。存在该分支时,runId + runStepId + executionAttemptId + gatewayPool + gatewayDeploymentId + AttemptRouteBinding完整四元组 + GatewayRouteSnapshot完整四元组与一个且仅一个 Invocation/Task Anchor必须完整并已被 Core验证;任一四元组半缺、未知算法、同 Ref/Schema异 Digest或错配都进入隔离,不进入普通 Error DTO。规范 Pool字段只有 gatewayPool=text|media,不接受 gatewayPoolId。
时间字段按 evidenceKind 条件约束:domain_fact 与 operations_runtime 使用各自 Owner 写入的 occurredAt,operational_observation 必须使用 Core Intake 注入的 acceptedAt 并只把可选 sourceClaimedObservedAt 当作来源声明,telemetry_reference 与 derived_summary 的 derivedAt 只表示引用或摘要的生成时间。四者不得互相回填。Observation 的列表顺序、首次/最近时间、accepted health window、Retention 和 Freshness 一律从 acceptedAt 计算;claimed time 只进入 Timeline 展示。完整矩阵见可观测性与错误链。
错误展示只允许 Contracts 注册的模板 Key 和逐字段白名单、限长、按输出上下文编码的参数;任意“已脱敏”自由文本不能进入普通 Error、日志或页面。Accepted Error Observation 的 Fingerprint 只能由 Core Operations Projector 按 Contracts 的版本化 canonicalization 与固定测试向量计算,Producer 值不被接受;其他 Evidence Kind 不计算 v1 Fingerprint。backendModelId、Provider Model/Credential、Supply/Channel 和 Provider Attempt 详情只允许通过 Case/JIT 受控证据引用查看,不进入普通 Error 或 Operations DTO。原始技术细节只保存 Case-scoped Reference。
错误层级
稳定层级至少覆盖:
edge
identity_policy
billing_preflight
execution
gateway_dispatch
gateway_protocol
provider
poll_reconciliation
result_fetch
media_validation
asset_persistence
billing_settlement
customer_webhook
mcp
internal_worker未来启用 Provider Callback 后,可以增加 provider_callback,但不能提前把它写成当前主链路能力。
提交确定性
certainty 是判断能否重试的关键字段:
rejected_before_dispatch
not_submitted
submitted
provider_accepted
unknownrejected_before_dispatch/not_submitted可以依据同一 Operation 的安全语义恢复。submitted/provider_accepted只能继续查询、取回结果或等待终态。unknown必须进入 Reconciliation,禁止自动换 Gateway、Supply 或 Provider 重发。- 用户主动“再次生成”创建新的业务 Attempt,不能覆盖旧错误或复用旧经济身份。
错误内容分层
| 内容 | 面向对象 | 规则 |
|---|---|---|
customerMessage | 用户与开发者 | 安全、可执行,不暴露 Provider、渠道、堆栈和内部策略 |
operatorSummary | 获得对应权限的运维人员 | 脱敏阶段、确定性、影响和建议动作 |
technicalDetailRef | 获得临时调试授权的人员 | 指向受限原始证据,不复制进普通页面和 Incident 备注 |
Error Fingerprint v1 只能由 Core Operations Projector 使用同一条 Accepted Error Observation 的 errorFingerprintVersion + errorNormalizationPolicyVersion + source.service + surface + layer + phase + normalizedCode 七项计算;UTF-8 NFC、RFC 8785、SHA-256 与固定向量以事件契约为唯一来源。不能从相邻记录补入 Pool、Deployment、Provider、模型或函数位置。完整错误正文、Request/Tenant/User ID、Prompt 或 Provider 动态任务号不能参与指纹。
Error、Alert 与 Incident 的关系
Error Occurrence
→ Error Fingerprint Group
→ SLO / Rule Evaluation
→ Alert Instance
→ Incident(存在实际或高风险客户影响时)
→ Mitigation / Recovery
→ Reconciliation Cases
→ Postmortem / Follow-up- 一次错误不必产生告警。
- 相同 Fingerprint 的错误按受影响产品、Pool、Deployment、组织数和时间窗聚合。
- Alert 是需要值班确认的信号,不等于已经发生客户事故。
- Incident 是有人负责、需要协同处置和沟通的影响事件。
- Incident 关闭前必须扫描受影响的 Run、Asset、Reservation 和 Webhook,不能只看成功率恢复。
Logs、Metrics、Trace、业务事实与 Audit
| 数据面 | 主要用途 | 是否权威事实 | 关键限制 |
|---|---|---|---|
| Logs | 单服务诊断与上下文 | 否 | 结构化、脱敏、可按保留策略清理 |
| Trace | 跨服务调用关系、阶段和延迟 | 否 | 可以采样;异步边界使用 Span Link |
| Metrics | 趋势、SLO、容量和告警 | 否 | 标签保持低基数,不放用户或请求 ID |
| Run/Gateway/Asset/Billing Fact | 任务、资产和经济状态 | 是 | 由各领域唯一写入并按状态机演进 |
| Audit Event | 谁以什么权限执行了什么 | 是,治理证据 | 追加写入,普通管理员不可修改或删除 |
| Incident / Reconciliation Timeline | 运维判断与处置证据 | 是,运维治理证据 | 追加事件修正旧结论,不覆盖历史 |
结构化日志
普通日志使用结构化字段,至少包含服务、环境、版本、时间、严重度、规范化错误码和可用的 Trace/Correlation 引用。不得记录:
- Prompt、System Prompt、内部规划和完整模型输出;
- 图片、视频、音频、Base64 和文档正文;
- API Key、Cookie、Session、Authorization Header;
- Provider Credential、Callback Secret、支付凭据;
- 长效或短期签名 URL;
- 未脱敏的 Provider 错误页和完整 Callback Payload;
- 不必要的用户邮箱、手机号或支付信息。
Telemetry 写入失败不能阻塞正常模型调用。确需保存原始 Provider 证据时,只能进入加密、限时、Case-scoped 的受限存储,并在普通日志中保存不可逆 Digest 和受控引用。
Trace
建议的 Span 语义:
http.request
identity.authorize
billing.quote
billing.reserve
run.step
gateway.dispatch
gateway.accept
provider.submit
provider.poll
result.fetch
asset.persist
billing.settle / release / reconcile
customer_webhook.deliverTrace 采样不能让经济事实丢失。Reservation、Usage、Ledger、未知提交、人工处置和特权动作无论是否采样,都必须进入自己的领域事实或 Audit。
Metrics 与 SLO
Metrics 标签只使用低基数维度,例如 service、region、surface、capability、pool、deployment、model family、provider、status 和 normalized error code。禁止把 userId、tenantId、requestId、traceId、runId 或 upstreamTaskId 放进标签。
首轮指标至少覆盖:
- 请求量、成功率和延迟;
- Text Pool 与 Media Pool 独立健康;
- Provider 429、5xx、超时和未知提交;
- 排队时长、任务停留、Worker Lease 与恢复;
- Provider 成功但结果未取回、Asset 校验与落盘失败;
- 未终态 Reservation 数量、金额和停留时长;
reconciliation_required数量、金额与最旧案件;- Gateway Provider Evidence、Metering 规范
MeterEvent/ProviderCostFact与 OceanWay Settlement 差异; - Webhook 延迟、失败和重放;
- Telemetry、Outbox 与 Read Model 的摄取缺口。
SLO、告警阈值、容量和保留期来自客户合同、Provider 公开约束、容量测试和管理员配置。架构文档不写无依据的固定次数、延时或百分比。
运行指挥台
后台首页同时回答“现在是否异常、影响谁、从哪里开始处理”:
- OceanWay Edge、Identity、Billing、Execution、Asset 和 MCP 状态;
- Text Pool、Media Pool、每个 Gateway Deployment 和 Provider 的独立状态;
- 队列、Worker、Lease、未知提交、结果落盘和对账积压;
- 当前 SLO Burn、Open Alerts、Open Incidents;
- 受影响 Surface、Offering、Organization 数量和预计经济影响;
- 最近 Deployment、模型、路由、价格和 Secret 变更;
- Telemetry 与读模型自身的新鲜度。
一个 Provider 异常不能让首页把整个 OceanWay 显示为“全部不可用”。平台健康、产品健康、Pool 健康和单一 Deployment 健康必须分层展示。
全局检索与 Run Explorer
本节说明目标范围;当前 Outbox 纵切只实现 Public API 准入的 Operation、Run、Reservation 与 Event,并严格停在 reserved。页面级查询、权限、完整性和空状态见 Run Explorer。
全局检索支持精确输入:
- 用户公开账号 ID、受限邮箱或企业名称;
- Public Request ID、Trace ID、Correlation/Operation ID;
- Run、RunStep、Execution Attempt;
- Gateway Execution Anchor(Text Invocation 或 Media Task)、Provider Attempt 或上游任务 ID;
- Asset、AssetVersion、Storage Key;
- Reservation、Meter Event、Ledger Entry;
- Alert、Incident、Reconciliation Case。
模糊搜索只能返回获得权限的脱敏摘要;内部 UUID 不得作为用户信息缺失时的回退展示。上游任务 ID、Storage Key 和财务 ID 的查询需要对应职责,不能因为能搜索 Run 就自动扩展权限。
Run Explorer 详情页至少包括:
- 请求摘要、客户影响和当前可信状态;
- 身份、组织、Workspace、Project 与付款方;
- Quote、Reservation 和预算判断;
- Run、Step、Attempt 与 Execution Manifest Revision;
- Pool、Gateway Deployment、模型和配置版本;
- Provider Attempt、Poll/Observation 与提交确定性;
- Result Fetch、媒体校验、AssetVersion 和产品 Binding;
- Usage、Settlement、Release、Refund 与 Margin Fact;
- 规范化错误、关联 Logs/Trace、Alert 和 Incident;
- 人工命令、审批与 Audit Timeline。
原始 Prompt、参考素材和生成结果默认不展示。需要内容诊断时,必须从具体 Incident 或 Reconciliation Case 申请临时只读访问。
Error Center
Error Occurrence、Fingerprint、evidenceKind 证据边界、确定性与分阶段门禁见 Error Center。本能力是 Run Explorer 之后的目标阶段,不能写成当前已实现。
Error Center 按 Fingerprint 聚合并展示:
- 首次与最近发生时间、趋势和代表性脱敏 Trace;
- 影响的产品、Surface、Pool、Deployment、模型、Provider 和配置版本;
- 影响的用户、Organization、Run、Asset 和金额摘要;
- 提交确定性、可重试性和待补偿类型;
- 与最近发布、路由或 Secret 变更的时间相关性;
- 是否已有 Alert、Incident、Runbook 或已知问题;
- 当前 Owner 和下一步动作。
页面只能表达“相关”,不能因为错误恰好出现在发布之后,就自动宣称该发布是根因。
Gateway 与执行运行中心
Text Pool 与 Media Pool 使用同一导航层级,但保留不同运行语义:
| 视图 | Text Gateway Pool | Media Gateway Pool |
|---|---|---|
| 主要对象 | Invocation、Channel、Stream、Token Usage | Task、Provider Attempt、Poll/Reconcile、Result |
| 健康维度 | 协议、首 Token、断流、429/5xx、Token 计量 | 提交确定性、队列、停留、轮询、结果取回和落盘 |
| 路由详情 | UUMI/new-api Deployment 与 Channel 摘要 | Media Gateway Deployment、Supply 与 Adapter 摘要 |
| 不可见内容 | Provider Credential、客户 Prompt | Credential Version 明文、Provider 原始 Payload、客户媒体 |
Text Gateway 如果尚不能提供可信 Provider Attempt,只显示 Gateway Invocation 和“Provider 未观测”,不能由中央控制面读取 new-api 数据库后猜测。Media Gateway 通过自己的 Task/Attempt Registry 输出规范化 Observation;中央读模型只保存必要摘要和不透明引用。
Alert 与 Incident 中心
Incident 的追加式时间线、关闭条件以及与对账案件的边界见 Incident 与 Reconciliation。
Alert 生命周期建议保持简单:
open → acknowledged → mitigating / silenced → resolvedSilence 必须绑定 Owner、原因、范围和有效期;Maintenance Window 与异常静默分开。通知失败本身可观测,告警正文不得包含 Prompt、用户邮箱、Secret 或签名 URL。
Incident 至少记录:
incidentId / severity / status
commander / participants / owners
detectedAt / startedAt / mitigatedAt / resolvedAt
affectedProducts / surfaces / pools / deployments / offerings
affectedOrganizations / runs / assets / billing exposure summary
linkedAlerts / errorGroups / traces / changes / cases
timeline / decisions / mitigations / validation
customerCommunicationStatus
rootCause / followUps / postmortemStatusIncident Timeline 追加写入;后续修正通过新事件补充,不覆盖旧判断。一次标准处置流程是:
Signal
→ Triage
→ Incident
→ Impact Analysis
→ Minimal Mitigation
→ Metrics Recovery Validation
→ Run / Asset / Billing Scan
→ Reconciliation
→ Customer Communication
→ Postmortem / Follow-upReconciliation 工作台
Reconciliation 工作台可以汇总执行、资产、Usage 与账务核对信号,但“需要核对”不等于“已经创建正式 Case”。以下异常首先属于 Owner 本地状态、Blocked Work、Error/Alert 或 Incident:
- 无法确认 Provider 是否创建任务;
- Provider 成功但 OceanWay 没有取得结果;
- 结果已落盘但 AssetVersion 或 Run 未完成;
- Run 已完成但 Reservation 未结算或未释放;
- Usage 缺失、超出预占或与 Provider 账单不一致;
- Poll 与未来 Callback 报告冲突终态;
- 重复 Provider Attempt、重复资产或疑似重复结算;
- 用户已退款但 Provider 成本已经发生;
- 客户 Webhook 未送达,但业务与账务已经终态。
首期 Operations Reconciliation Case 只接受严格 sourceScope 联合:late_settlement_dimension 与 billing_finalization_run。前者由 Billing 的正差额 Exposure 请求打开,后者由 Billing Finalization Decision 请求打开。Provider、Gateway、Execution、Asset、Webhook 或普通客户计量异常没有 Canonical Case Event/Mandatory Delivery/Owner Read/终态 Authority 时,只能显示为明确标注不完整的诊断信号,禁止伪造第三种 Case。
案件状态:
open
investigating
awaiting_external_evidence
resolution_proposed
approval_required
resolving
resolved
rejectedresolved | rejected 是 Case 状态集合中的终态。维度级 late_settlement_dimension 只能由 BillingCaseResolutionApplied@1 驱动终态,Run 级 billing_finalization_run 只能由 FinalizationReconciliationResolutionApplied@1 驱动终态。两种来源在对应 Fact 到达前,Operations/Admin 都只允许停留在调查、补证、提案、审批或 resolving,不得本地写入 resolved | rejected | no_action。当前没有“平台承担/供应损失/核销”的 Billing Disposition 契约,因此也不能让 Operations 与账本各自决定终态。
当前 Case-scoped 领域命令只有两类:
- 对
late_settlement_dimension,基于 Current Exposure、Funding Decision、审批/JIT 和一次性 Grant 执行已注册的 Late Settlement Command; - 对
billing_finalization_run,基于 Current Case/Decision、调查引用与一次性 Grant 请求 Billing 重新读取 Owner 状态并重评,不能覆盖任何 Owner Fact。
查询原任务、结果恢复、投影重建或 Webhook 重放可在未来作为独立 Incident-scoped Domain Command 进入,但不能复用 Billing Case Scope 或当前 Grant Specialization。每类新增命令必须先发布自己的 Target Owner、Expected Revision、幂等 Result 和 Audit 契约。
禁止直接编辑 Gateway Execution Anchor(Text Invocation 或 Media Task)状态、AssetVersion、Reservation 或 Ledger 行。账务修复只追加分录,未知提交不通过“重试看看”解决。
两种 Billing Case 的状态机测试必须分别覆盖本地终态命令与新 Exposure/Watermark、Billing Applied Fact 的并发:本地 resolved/rejected/no-action 命令始终 fail closed,只有完整验证的来源专属 Owner Applied Fact 可以收敛终态;Applied 与 Case Request/Create 乱序也不得产生第二 Owner、错关另一代 Case 或把已解决 Case 恢复为 Open。
Admin Command Gateway
所有带写效果的运维动作通过 Admin Command Gateway。授权边界冻结以下两个严格契约;@1 内禁止增删字段、改变类型或改变摘要语义,任何演进必须发布新的 @N+1 Schema,并让未知版本 fail closed。
ImpactPreview@1 {
schemaVersion: "ImpactPreview@1"
adminCommandId: string
commandType: registered command enum
target: { targetType: registered target enum, targetId: string, expectedRevision: string }
caseScope:
| { kind: "incident", incidentId: string, expectedIncidentRevision: string }
| { kind: "reconciliation", reconciliationCaseId: string, expectedCaseRevision: string }
| { kind: "none" }
requestedScope: registered scope enum[]
policyRevisionId: string
sourceFacts: StrictPreviewSourceFact@1[]
affectedResources: StrictAffectedResource@1[]
predictedEffects: StrictPredictedEffect@1[]
riskLevel: "low" | "medium" | "high" | "critical"
approvalRequirement:
| { kind: "not_required", approvalPolicyRevisionId: string }
| { kind: "required", approvalPolicyRevisionId: string, requirementSetDigest: Sha256Hex }
evaluatedAt: RFC3339 UTC timestamp
digestAlgorithmVersion: "jcs-sha256-v1"
impactPreviewDigest: Sha256Hex
}
AdminCommandCandidate@1 {
adminCommandCandidateRef: string
schemaVersion: "AdminCommandCandidate@1"
adminCommandId: string
commandType: registered command enum
actorWorkforcePrincipalId: string
boundWorkload: registered workload identity
target: { targetType: registered target enum, targetId: string, expectedRevision: string }
caseScope: ImpactPreview@1.caseScope
requestedScope: registered scope enum[]
reasonCode: registered reason enum
reason: non-empty string
idempotencyKey: string
commandRequest: {
ref: string
schemaVersion: registered command request schema version
payload: strict discriminated command payload
digestAlgorithmVersion: "jcs-sha256-v1"
commandRequestDigest: Sha256Hex
}
impactPreview: {
schemaVersion: "ImpactPreview@1"
previewRef: string
digestAlgorithmVersion: "jcs-sha256-v1"
impactPreviewDigest: Sha256Hex
}
approval:
| { kind: "not_required", approvalPolicyRevisionId: string, requirementSetDigest: Sha256Hex }
| {
kind: "required"
approvalPolicyRevisionId: string
requirementSetDigest: Sha256Hex
approvalDecisionRef: string
approvalDecisionSchemaVersion: string
expectedApprovalRevision: string
records: StrictApprovalRecord@1[]
approvalSetDigestAlgorithmVersion: "jcs-sha256-v1"
approvalSetDigest: Sha256Hex
}
jitAuthorization:
| { kind: "not_required", jitPolicyRevisionId: string }
| {
kind: "required"
jitGrantRef: string
jitGrantSchemaVersion: string
expectedJitGrantRevision: string
jitScopeDigestAlgorithmVersion: "jcs-sha256-v1"
jitScopeDigest: Sha256Hex
}
candidateDigestAlgorithmVersion: "jcs-sha256-v1"
adminCommandCandidateDigest: Sha256Hex
}
SignedWorkforceCommandExecutionGrant@1 {
commandGrantRef / commandGrantSchemaVersion
commandGrantDigestAlgorithmVersion = "jcs-sha256-v1"
commandGrantDigest
iss / aud / sub / boundWorkload / jti / iat / nbf / exp
adminCommandId / commandType
targetType / targetId / expectedTargetRevision
caseScope # 含 Expected Revision 的严格联合
approval =
{ kind=not_required; approvalPolicyRevisionId; requirementSetDigest }
| { kind=required; approvalPolicyRevisionId; requirementSetDigest;
approvalDecisionRef / approvalDecisionSchemaVersion / expectedApprovalRevision;
approvalSetDigestAlgorithmVersion / approvalSetDigest }
jitAuthorization # 与 Candidate 的严格联合逐项相等
previewSchemaVersion / previewDigestAlgorithmVersion / impactPreviewDigest
requestRef / requestSchemaVersion / requestDigestAlgorithmVersion / commandRequestDigest
candidateRef / candidateSchemaVersion / candidateDigestAlgorithmVersion / commandDigest
commandSpecialization =
{ kind=none }
| { kind=late_settlement;
reconciliationCaseRef; expectedCaseRevision; caseGeneration;
reconciliationCaseIdentityReservationRef / reconciliationCaseIdentityReservationSchemaVersion;
reconciliationCaseIdentityReservationDigestAlgorithmVersion / reconciliationCaseIdentityReservationDigest;
lateSettlementExposureRef / lateSettlementExposureSchemaVersion;
lateSettlementExposureDigestAlgorithmVersion / lateSettlementExposureDigest / exposureRevision;
fundingDecisionRef / fundingDecisionSchemaVersion;
fundingDecisionDigestAlgorithmVersion / fundingDecisionDigest / fundingDecisionJti;
fundingPolicyRevisionId; positiveDeltaDecimal;
settlementEconomicIdentity =
{ kind=credits }
| { kind=entitlement; entitlementKey; unit }
| { kind=money; currency } }
| { kind=finalization_reconciliation_resolution;
resolutionOperationId;
tenantKind / tenantId / workspaceId / projectId?;
billingAccountId / billingReservationId / runId / billingFinalizationFenceId;
reconciliationCaseRef / reconciliationGeneration;
expectedCaseRevision / expectedResolutionRevision=1;
finalizationCaseIdentityReservationRef / finalizationCaseIdentityReservationSchemaVersion;
finalizationCaseIdentityReservationDigestAlgorithmVersion / finalizationCaseIdentityReservationDigest;
caseRequestEventId / caseRequestEventType / caseRequestEventSchemaVersion;
caseRequestEventEnvelopeDigestAlgorithmVersion / caseRequestEventEnvelopeSha256;
finalizationReconciliationResolutionCommandRequestRef / finalizationReconciliationResolutionCommandRequestSchemaVersion;
finalizationReconciliationResolutionCommandRequestDigestAlgorithmVersion / finalizationReconciliationResolutionCommandRequestDigest;
resolutionDecisionRef / resolutionDecisionSchemaVersion;
resolutionDecisionDigestAlgorithmVersion / resolutionDecisionDigest;
action=reevaluate_current_owner_state;
investigationEvidenceReferenceCount;
investigationEvidenceReferences[] }
authorizationLinearizedAt
}StrictPreviewSourceFact@1、StrictAffectedResource@1、StrictPredictedEffect@1、StrictApprovalRecord@1 和每个命令的 Payload 都必须是 Contracts 仓库注册的封闭判别联合:拒绝未知字段、未知枚举和未注册子类型,不能用开放 map<string, any> 承载授权语义。caseScope、approval、jitAuthorization 必须分别精确命中一个分支,缺失值与 null 不等价,解析时不得静默补默认值。Required Approval 使用一个不可变 Aggregate Approval Decision 收敛多条按序 Record;Decision Ref/Schema/Expected Revision、Records 和 Approval Set Digest 必须互相验证,不能让领域命令从 Record 中自行挑选“足够的批准”。
摘要算法统一为 jcs-sha256-v1:输入必须是严格 Schema 校验后的 UTF-8 JSON;拒绝重复 Key、无效 Unicode,以及任何 Key/字符串不是 NFC 或 NFC 归一化后发生 Key 冲突的输入,摘要路径不得静默改写文本;集合数组先拒绝重复元素,再按 Contracts 规定的稳定排序键排序,之后执行 RFC 8785 JCS,最后对 canonical bytes 计算 SHA-256,输出小写 64 位十六进制。requestedScope 按枚举值排序;Source/Resource/Effect 分别按 Contracts 注册的复合键排序;Approval Record 按 approvalRequirementId + approverWorkforcePrincipalId + decisionRevision 排序。JCS 只规范对象 Key、不重排数组,因此任何未注册数组顺序都必须拒绝。Contracts 仓库必须发布原始 JSON、canonical bytes 和摘要的 Golden Vector,Gateway、Core 与所有 Domain 实现共用并通过同一组向量。
摘要字段集不可由实现自行选择:impactPreviewDigest = SHA256(JCS(ImpactPreview@1 去除Repository-owned Ref/Time与摘要自身));commandRequestDigest = SHA256(JCS(commandRequest 去除Repository-owned Ref/Time与摘要自身));approvalSetDigest = SHA256(JCS(approval.records));adminCommandCandidateDigest = SHA256(JCS(AdminCommandCandidate@1 去除Repository-owned Ref/Time与摘要自身))。adminCommandCandidateDigest 就是授权协议中的唯一完整 commandDigest,两者必须逐字节相等。因此 Command Digest 覆盖 Actor、Workload、Command、Target/Revision、Case/Revision、Scope、Reason、Idempotency Key、Request完整四元组、Preview Digest、Aggregate Approval/Set 与 JIT 联合;只签若干散列字段或动态挑选字段不是合法实现。
SignedWorkforceCommandExecutionGrant@1 是 Core Authorization 签发、只允许目标 Domain 消费一次的执行凭证,不是需要在 Domain 提交前再次远程查询的普通 Session。除标准主体与时间声明外,它完整绑定 Workload、Command、Target/Revision、Case/Revision、Approval Decision/Set、JIT、Preview、Request 与 Candidate Digest;sub 唯一表示 actorWorkforcePrincipalId,commandDigest 必须逐字节等于 adminCommandCandidateDigest。commandSpecialization 与 commandType 是双向 Refinement:Late Settlement Command 必须且只能使用 late_settlement,逐项回显 Case/Generation/Identity、Current Exposure、Funding Decision/Policy、正差额和严格经济身份;Finalization 重评命令必须且只能使用 finalization_reconciliation_resolution,逐项回显 Run/Fence、Case/Generation/Revisions、Identity、Case Request Event、Request、Resolution Decision、唯一 Action 与调查引用集合,并绑定由完整 Decision 四元组确定性派生的 resolutionOperationId;其余注册命令才使用 none。这些字段必须与严格 Request、caseScope、Operations Identity Reservation、Candidate、Decision 和 Billing 当前 Case Link 逐项相等。Specialization 不替代完整 Request/Candidate Digest,而是让 Billing 显式拒绝跨 Case 代际、Bucket/Currency、Fence、Decision 或 Evidence 集合的 Grant 重放。Grant 只能绑定最终 Candidate;Preview、Approval、JIT、Case Revision/Generation/Reservation、Funding/Exposure、Finalization Decision 或 Request 任一变化都必须生成新 Candidate 并重新换取 Grant。Grant Ref/Schema/Digest 与 JTI 成组持久化,未知 Schema/Algorithm、缺 Claims、宽泛 Audience、分支混合或 Bearer 式转用必须拒绝。
命令执行流程:
- Admin Command BFF 在本地重新验证 Workforce Session、职责权限与 JIT/Case 状态;
- 先固定命令专属严格Request业务Payload及完整四元组;跨域Request只使用注册
reasonCode,不携带自由理由; - 读取目标当前Revision和影响事实,以该Request为输入生成并持久化不可变
ImpactPreview@1。危险动作等待职责分离审批并冻结Approval Set/JIT;最后才由Request、Preview、Approval/JIT与Actor/Workload等全部输入生成唯一AdminCommandCandidate@1完整四元组与Digest,不得在Candidate后回填或替换Request; - Admin Command BFF 以自身短期 Workload JWT,加 Workforce IdP/Identity Authorization 唯一签发或交换的防重放 Actor Assertion,调用精确 Audience 的 Command Grant Exchange。Assertion 至少绑定
iss/aud/sub、auth_time、acr/amr、Session/Authn Version、boundWorkload、包含完整 Candidate 四元组与 Digest 的 Command Exchange Request Digest/Nonce、jti与iat/nbf/exp;不能复用 Query/Admin Audience Token,也不能由 BFF 自报、签发或转签 Actor。CommandGrantExchangeResult@1.actorAssertionIssuer + actorAssertionJti分别逐字节等于已验证Assertion的iss + jti,commandGrantExchangeOperationId由 Contracts 固定 Namespace 仅按这两个Result字段确定性派生;Candidate 不进入 Operation ID,而作为必须 insert-or-compare 的正文,从而让同 JTI 偷换 Candidate 明确成为冲突; - Core Authorization 先认证当前 BFF Workload、验证 Assertion 签名/Issuer/Audience 与
boundWorkload,再按commandGrantExchangeOperationId读取不可变 Grant Issuance Result。found且 Assertion 身份、Workload、Request Digest/Nonce、Candidate 完整四元组及 Digest 全等时返回首次 Grant,即使 Assertion 已过期或 JTI 已消费;同 Operation 绑定任何不同正文时返回conflicting。只有not_found才执行当前时间、Session、Approval/JIT/Case 与 JTI 未消费校验,严格解析 Candidate并重算 Preview、Approval、JIT、Request/Candidate Digest;随后在同一 Core 事务一次性消费 Assertion JTI、持久化 Grant Issuance Result 与SignedWorkforceCommandExecutionGrant@1完整四元组、追加签发 Audit,再返回 Grant。任一步失败都不返回 Grant;唯一约束竞争后必须回到首次 Result 读取分支。该提交时刻是外部 Approval/JIT/Case 授权的线性化边界。Workforce Cookie/Session 只留在 Admin Host,Assertion 与 Grant 都不能进入浏览器; - Admin Command Gateway 在调用领域命令前耐久追加
command.authorized,事件必须携带 Grant JTI、完整 Candidate Digest 和各子摘要;若授权 Audit 失败则不调用 Domain; - Admin Command Gateway 以独立 Workload JWT、Execution Grant、完整 Candidate、所引用的 Preview/Approval/JIT、原始严格 Request 和幂等键调用唯一拥有数据的 Domain Command Service;任何请求字段都不能覆盖 Candidate;
- Domain Service先认证当前调用 Workload并确认等于
Grant.boundWorkload,强制Candidate.actorWorkforcePrincipalId == Grant.sub,严格解码全部输入、验证 Grant签名完整性/Issuer/Audience/Ref/Schema/Digest并重算 Candidate,然后按 Contracts由commandType + commandRequestSchemaVersion + domainIdempotencyKey推导稳定domainCommandResultKey。它必须在检查 Grant当前时间窗、JTI未消费或 Target当前 Revision之前查询已提交 Result;若同 Key的 Result完整绑定同一 Candidate/Grant/JTI则直接返回首次结果,即使响应丢失后 Grant已过期,若 Key绑定不同候选则冲突; - 只有 Result不存在时,Domain Service才检查 Grant当前时间窗、JTI未消费,并逐项比对 Grant内 Actor、Command、Target Type/ID/Expected Revision、Case/Revision、Approval Decision/Policy/Requirement/Set、JIT联合、各子摘要与完整 Candidate Digest;随后只在自己的 Repository中锁定并重验它所拥有的 Target Revision、Case(若该 Case归本 Domain)、Funding/Quota和业务不变量。不得在本地事务前后远程回查 Core Approval/JIT Current状态并假装原子。Core Grant签发边界后的撤销不追溯取消这个已授权动作,签发前撤销必须阻止 Grant;需要对已签发动作硬停止时,必须新增由目标 Domain所有、可在同一事务消费的版本化 Revocation Fence;
- 通过全部新执行校验后,Domain Service在一个领域事务内以 Result Key与 Grant JTI唯一约束原子写入
CommandGrantConsumption、业务事实、命令结果事实和 Audit Outbox;任一必需写入失败则整体回滚。唯一约束竞争失败后重新读取 Result并完整比较,不能重新按当前时间窗把已经提交的成功改成失败。后续失败和完成通过追加事实/Audit表达,再通过正常查询路径验证结果,不把前端成功提示当作完成证据。
所有拒绝路径均 fail closed:Grant Exchange 失败追加 command.authorization_rejected;Domain 校验失败通过自身 Audit Outbox 追加 command.rejected,且不得产生业务事实;授权后执行失败追加 command.failed。若拒绝 Audit/Outbox 本身无法耐久写入,只能返回不含敏感细节的失败并保持零业务副作用,不能降级为执行后补记。Audit 事件记录 Actor、Workload、Grant JTI、Candidate Digest、各子摘要、失败阶段和稳定 Failure Code,不复制密钥、Cookie、Assertion 或完整敏感 Payload。
Command Grant Exchange 与 Domain契约测试必须至少覆盖:错误 Audience/Workload、过期或撤销 Session、签发前 Approval/JIT/Case撤销、签发后撤销不追溯、Assertion JTI同 Candidate重放与异 Candidate冲突、Core 已提交 Grant Issuance/签发 Audit 但 HTTP 响应丢失,重试跨过 Assertion 到期仍返回同一 Grant、Grant JTI同 Candidate响应丢失重放与异 Candidate偷换、有效期内 Domain 提交成功后响应丢失并跨过 Grant到期重放、Exchange/Result Key唯一竞争、并发双消费、Grant.sub与 Candidate Actor不同、Target Type/ID或 Expected Revision被替换、Case/Revision被删除/切换/关闭、审批记录增删或 Aggregate Decision Revision变化、JIT联合变化、Preview内容/Schema/Policy/Source与摘要不符、Request Payload/Schema与摘要不符、Candidate任一字段与完整摘要不符、Late Settlement Specialization缺失/混合或 Case Ref/Expected Revision/Generation/Identity Reservation/Exposure/Funding Policy/金额/Credits/Entitlement Key/Unit/Currency任一错配、Finalization Resolution Specialization 缺失/混合或 Fence/Case/Generation/Revisions/Identity/Event/Request/Decision/Operation/Action/Evidence 任一错配、未知摘要算法、重复 Key、未知字段、null冒充缺失值、非 NFC/归一化冲突、数组顺序未规范化,以及授权 Audit、Consumption、业务写入或 Domain Audit Outbox失败。每个用例都必须断言至多一个业务效果并存在相应 Audit;Query Grant与 Command Execution Grant使用不同 Audience、Scope、Workload Credential和 JTI消费域,不能相互替代。
控制面不得提供任意 SQL、任意 Provider 请求、任意文件删除或通用“强制成功”按钮。健康检查也不能偷偷发起付费模型调用;需要合成探测时,必须有独立预算、模型、环境和可识别的 Probe Principal。
敏感调试访问
普通运维默认不读取客户 Prompt、原始媒体和完整输出。确需内容诊断时创建 SensitiveDebugGrant:
grantId
iss / aud / sub / boundWorkload / jti
baseQueryGrantId / baseQueryGrantJti
requester / approver
incidentId 或 reconciliationCaseId
tenantKind / tenantId / workspaceId / resourceIds[]
allowedFields / purpose
sessionVersion / policyRevision / mfaAssurance
issuedAt / notBefore / expiresAt / revokedAt?规则:只有 Core Authorization/Policy 模块可以签发 Grant;Admin BFF、IdP 与人工配置不能自行生成。SensitiveDebugGrant.jti 必须是独立唯一值,不得复用父 Query Grant JTI;它通过 baseQueryGrantId + baseQueryGrantJti 建立可撤销父链,并要求 Issuer/Audience、Workload Binding、Session/Policy/MFA 与时间约束兼容。实际权限取当前父 Grant、活跃 Case 和已完成审批的交集;父子任一撤销/过期即失效,相同 JTI、错误父链或 Workload 错配都 fail closed。它不是可转移 Bearer,浏览器不能取得。每次内容释放仍执行 query.authorized → query.released | query.not_found | query.inconclusive | query.failed 两阶段 Audit fail-closed,任一必需 Audit 追加失败都不返回数据。测试覆盖 JTI 复用、错误/不存在父链、父 Grant 撤销和权限交集。Grant 必须最小只读范围、明确理由、限时、审批、不可转授并全量 Audit。内容不能复制到普通日志、Alert、Incident Note、工单标题或聊天工具。Break-glass 使用独立流程,并在事后通知、复核和撤销。
后台信息架构
现有后台继续按商业 SaaS 职责分组,不把所有新能力平铺成一条长菜单:
| 分组 | 目标内容 |
|---|---|
| 经营分析 | 角色化首页、经营与客户摘要、跨产品用量和支持入口 |
| 商品运营 | 商品、套餐、促销、优惠券和邀请 |
| 财务管理 | 订单、支付、CDK、钱包、Ledger、账务对账和财务审批 |
| 上游配置 | Model Offering、Text/Media Deployment、Channel/Supply、价格证据和 Agent Skills |
| 系统管理 | Run Explorer、Error Center、Alert/Incident、队列/Worker、Telemetry、Audit、身份、存储和备份 |
| 内容运营 | 作品、公告、案例和公共提示词治理 |
不同岗位进入同一个后台,但首页、默认页面、字段和动作不同。On-call 首先看到运行与 Incident;财务首先看到金额暴露与对账;内容运营不会因为共享后台而加载供应 Secret 或技术 Trace。
数据存储与保留
PostgreSQL 适合保存:
- Operation 关系索引和摘要;
- Error Occurrence 与 Fingerprint;
- Alert Rule/Instance;
- Incident、Timeline 与 Follow-up;
- Reconciliation Case、Evidence Link 与 Resolution;
- Admin Command、Approval 与 Audit;
- Gateway 规范化 Observation 投影;
- Outbox、Delivery/Receipt、Observation accepted source 与版本化完整性快照。
PostgreSQL 不应长期承载全量高频日志、全部 Span Payload、时序 Metrics、完整 Provider Callback、Prompt 或媒体内容。Logs、Metrics 和 Trace 通过 OpenTelemetry-compatible Collector 进入专门后端;PostgreSQL 只保存稳定对象和受控查询引用。
保留期按数据类型、环境、合同、监管和存储配置制定,不在架构中写死天数。运行时 API、Admin BFF、Worker 普通路径和人工 SQL 角色不能原地 UPDATE/DELETE Outbox Event Envelope/Payload、Delivery Attempt Start/Finish、Applied Receipt、Accepted Observation、Observation ID Registry/Purge Marker、Audit 和必要 Incident/Reconciliation 证据;Dispatcher 不直接更新 published_at,只能调用在同一 Lease-fenced 确认事务验证冻结 Destination→Consumer、Mandatory Delivery terminal 状态和匹配 Receipt 引用全集后首次置值的数据库函数。到期归档/删除只允许专用、受审计的 Retention/Archive Procedure 在满足恢复基线、Legal Hold、数据分类和 accepted health window 边界后执行,且绝不原地改写历史内容。Source Snapshot 与 Projection State Snapshot 都不能替代 Canonical Source;Outbox Event 离开热表后,完整 Envelope/Payload、Digest 与 Delivery Set Definition 必须进入 Rebuild/Verifier 每次读取的批准归档 Source View,并完成固定 Schema/运行时的隔离 Restore Drill。Active/Shadow、Delivery Ack 引用及仍有可重放 Source 的 Receipt 禁删;回滚窗、Pointer、Delivery、Quarantine 与 Legal Hold 全部解除后,只能成组清理退休版本 Receipt 与派生 Projection,不能删除归档 Canonical Event Source。Accepted Observation 的 Retention/Archive 资格和 accepted health window 边界只能依据 Core acceptedAt,不得使用 Producer 的 sourceClaimedObservedAt。Observation Payload 可按版本化 Retention Source 范围清除,但低敏 ID Registry 与已存在 Purge Marker 必须保留,或迁入 Intake 每次接受前都检查的不可变完整性归档;Retention 不得删除、重绑或绕过它们。普通 Telemetry 可以按热查询、冷归档和删除策略处理。
Projection、Timeline、Exact Identifier Index 与派生摘要继承源字段的 Lineage、Data Classification、Expiry 和 Legal Hold;源 Observation 到期删除后,所有仅由其派生的副本必须同步清除或降级,不能通过读模型绕过原始保留策略。
从现有 /admin 演进
A0:冻结边界
- 盘点现有 Section、API、权限、人工操作和数据源。
- 明确调用记录、运行事实、Audit 和 Ledger 的不同保留语义。
- 冻结
admin.oceanway.tech、内部管理员与企业客户管理员边界。 - 禁止新增通过直接改表完成的后台操作。
- 立即对全部遗留 Mutation Route、Server Action 和直接数据库写入口执行服务端 default-deny:不打包、关闭或统一拒绝;生成明确路由清单并以 HTTP/权限负向测试证明不可达,不能只隐藏 UI。后续只有对应 Domain Command 与完整身份/授权/审计门禁验收后才能逐项加入 allowlist。
A1:建立独立 Surface
- 将现有 OceanWay
/adminUI/BFF 迁入独立oceanway-admin仓库并由admin.oceanway.tech单独发布。 canvas.oceanway.tech/admin和ai.oceanway.tech/admin不提供内部后台。- 引入 Workforce Client、Host-only Session、MFA 和独立 Audience。
- 当前
role=admin只作为迁移映射,不作为长期授权模型。 - 迁移 UI/BFF/Session 时持续执行 A0 的路由级 allowlist/default-deny,不得因代码搬迁恢复任何遗留写入口。只有对应 Domain Command、独立 Command Workload、定向 Grant、幂等、审批与 Audit 已验收的动作,才可在后续阶段逐项加入 allowlist。
A2:只读诊断控制台
- 冻结 ID、Error Envelope 和 Gateway Observation Contract。
- 将现有生成运维升级为 Run Explorer。
- 增加全局 ID 搜索、Error Fingerprint 和 Text/Media Pool 独立状态。
- 补齐 Audit UI,关联现有调用、资产和财务记录。
- 继续只发布明确枚举的 Query Route;遗留钱包/支付、模型配置、人工确认、备份恢复等 Mutation Route 必须保持服务端不可达。CI 以路由清单和 HTTP 负向测试证明写入口未部署/被拒绝,不能把“页面没有按钮”当作安全门禁。
A3:Alert 与 Incident
- 建立 SLO、Alert Rule/Instance、值班 Owner 和 Maintenance Window。
- 建立 Incident、追加式 Timeline、影响分析和客户沟通状态。
- 接入发布、配置、路由和 Secret 变更记录。
- 通过 Admin Command Gateway 开放少量最小缓解动作。
A4:统一对账
- 自动发现未知提交、结果/资产不一致、Reservation 未终态和 Usage 差异,并区分 Owner-local Conflict/Blocked、Incident Signal 与正式 Case。
- 首期只为
late_settlement_dimension | billing_finalization_run建立 Reconciliation Case、Evidence、Proposal、Approval 和幂等 Resolution;其他来源必须先通过独立契约准入。 - 将现有人工补录、退款与结果恢复迁入领域命令。
- Incident 关闭前自动生成受影响对象扫描结果。
A5:规模化运维
- 建设完整 OpenTelemetry Pipeline 与独立 Logs/Metrics/Trace 后端。
- 完善 On-call、SLO/Error Budget、容量、成本和多区域视图。
- 为企业提供受限状态、审计导出和支持案件视图。
- 根据真实容量与故障域决定是否把 Admin BFF 和 Operational Read Model 作为独立部署单元;仍留在既定 Admin/Core 仓库边界内,不新建零散 Git 仓库。
管理员平台总体验收门禁
下列清单覆盖 Run Explorer、Gateway、Asset、Billing、Incident 与写命令的完整目标,不是当前 Outbox 纵切的一次性退出门禁。当前阶段只按 ADR-030 与 Run Explorer 验收;后续能力逐项接入后再勾选对应条目。
-
admin.oceanway.tech不进入客户应用启动器,客户 Session 不能直接获得内部后台会话。 - 企业客户管理员和 OceanWay Workforce 权限模型完全分离。
- 一个 Public Request ID 可以定位到授权范围内的 Run、Gateway、Asset 和 Billing 摘要。
-
requestId、traceId、correlationId、operationId和领域 ID 不再混用。 - Text 与 Media Pool 独立展示健康、Deployment、任务和错误。
- 未观测的 Provider 事实明确显示未知,不按时间或模型猜测。
- Error 记录阶段、提交确定性、重试与补偿语义。
-
unknown提交不能自动重试或跨网关切换。 - Provider 成功、Asset 成功和 Billing 成功是可独立核验的事实。
- Logs/Trace/Metrics 不作为任务或账务事实源。
- 普通 Telemetry 不含 Prompt、Secret、签名 URL 和完整媒体。
- Metrics 不使用用户和请求高基数标签。
- 控制面不直连 Text/Media Gateway 数据库。
- 所有列表按权限、状态、时间窗和分页定向查询。
- 所有运维写操作通过 Domain Command,幂等、有理由、有 Audit。
-
ImpactPreview@1、AdminCommandCandidate@1、命令 Request 和 Approval 使用严格 Schema;三类子摘要与完整 Candidate Digest 均按jcs-sha256-v1可跨服务复算,并通过统一 Golden Vector。 - Command Grant 的
sub与 Candidate Actor 相等,Grant 绑定完整 Candidate Digest;Domain 对 Actor、Target/Revision、Case、Approval、Preview、Request 和 Candidate 逐项复算校验,任何差异均 fail closed。 - 授权、拒绝、执行失败和完成均具备耐久 Audit;
command.authorized或领域 Audit Outbox 写入失败时无 Domain 调用或业务副作用,并有负向测试证明。 - 账务修复只追加 Ledger,不覆盖历史。
- 内容调试访问限时、最小、只读并可审计。
- Telemetry 或后台故障不会阻断客户正常模型调用。
不可妥协的规则
- 内部管理员平台和企业客户管理是两个身份与授权边界。
- 当前
/admin是迁移基座,不是目标权限与运维模型的最终形态。 - 管理员首先通过稳定业务 ID 和事实链排障,而不是在多套日志中猜测。
correlationId/operationId连接长期业务流程,traceId描述一次调用链;异步边界使用 Span Link。- 错误必须表达发生层级、提交确定性、重试和补偿语义。
- Text Pool 与 Media Pool 独立观测;中央控制面不侵入其私有数据库。
- Logs、Metrics 和 Trace 是诊断证据,不是 Run、Asset、账本或审计事实源。
- 普通运维无权读取客户内容和 Secret;调试访问必须 Case-scoped、限时并审计。
- 运维动作只能通过幂等 Domain Command,不能直接改表、强制改状态或盲目重提。
- Incident 恢复后必须检查受影响的任务、资产、账务和 Webhook,不能只关闭告警。
非目标
首期不做:
- 为了形式完整立即拆出独立 Admin、Ops、Alert、Incident 和 Trace 微服务;
- 在 PostgreSQL 中自建全量高频日志和时序数据库;
- 用一个“大盘正常”掩盖单个 Pool、Provider 或产品故障;
- 让后台直接调用 Provider、读取 Gateway 数据库或编辑 Ledger 行;
- 把完整 Prompt、媒体和 Provider 原始 Payload复制到 Incident;
- 在没有关联、权限、审计和幂等前开放“一键修复”;
- 允许日常使用永久全权限超级管理员。