# Dramivio 额度 HTTP 边界独立复审

2026 年 10 月 3 日。本轮复审主线新增的 Node HTTP、Go 公开网关、游客合并调用及目录锁配合，**没有发现可复现的 P1/P2**。独立完整链路的 10 组检查通过；主线新增的 Node HTTP 测试 1/1、Go 网关测试 1/1 也通过。

E00 额度领域本身由本复审 agent 编写，因此本报告只将主线新增的 HTTP/Go 集成列为独立复审，不将领域作者自测称为独立审查。所有业务文件只读，未发布、未连接生产、未读取真实媒体。

## 实测方法

独立脚本 `work/entitlement-review/http-go-boundaries.mjs` 启动当前实际 Go routes、Better Auth 1.7.7 和隔离 PostgreSQL 18 RAM 容器。两名用户均通过公开 Go 登录代理获取实际 OTP 会话；邮件仅在内存捕获。客户端持有 Go 实际签发的 HMAC viewer cookie，不由测试伪造有效签名。

Go 编译使用 `work/entitlement-review/go-overlay.json`：只把现有测试 fixture 的固定监听地址换成随机回环端口，未修改仓库文件或业务 handler。Node 和 PostgreSQL 也使用随机回环端口，没有占用 18091、18092、18991、18992。源能力审批与服务端预检仅为隔离 metadata fixture；没有真实 issuer 或媒体请求。完成后 Go、Node、数据库均执行清理路径。

最终逐项结果保存在 `work/entitlement-review/http-go-result.json`；Go fixture 正常退出记录位于同目录 `go-fixture.log`。

## 验证结果

| 边界 | 实际结果 |
| --- | --- |
| 身份和 App 声明覆盖 | 公开请求在 body/header 伪造 `ownerId`、`userId`、`guestId`、`deviceId`、`platform`、`appSessionId`、管理角色和 bridge header，最终预留仍属于当前签名 guest，使用真实设备及 Web 10。随后真实登录请求也不能用另一账号 ID/会话 ID 替换 owner。 |
| 源授权覆盖 | 为不在 Go 实际权限集合内的独立来源创建有效目录与受控能力 fixture；伪造 `allowedSources` 和管理头仍返回 403 `rights_unavailable`，该源没有产生预留。 |
| 两账号争抢同一游客 | 同一个已消耗 10 集的签名 guest，通过两个真实登录账号并发发送 30 次额度请求，只形成一个持久链接。赢家继承 10 集；另一账号不获得游客事实。竞争中的请求可被明确拒绝，后续不会改写已有链接。 |
| 合并重试 | 赢家再并发请求 20 次，全部返回原已用 10；链接仍只有一个，账本仍只有原 10 笔。没有复制余额或重复入账。 |
| 跨账号、同设备预算 | 未继承游客事实的另一账号虽然账号已用为 0，申请设备当日第 11 个新集仍返回 `device_budget_exhausted`。重看设备已使用的第 1 集可为该账号建立授权，设备计数保持 10。 |
| 操作归属 | 另一账号查询或释放原 guest 的操作均返回 404。自报原 owner 不会取得操作权限。 |
| App 实际会话和设备绑定 | 仅可信服务审批的实际 BA `session.id` 获得 App 50；继承 10 后显示剩余 40。客户端填写另一 session 或 `platform=web` 不改变真实资格。同一账号携带实际 BA cookie 换到新的 HMAC 设备时仍为 Web，重播已有当日解锁不增加新设备预算。 |
| cookie 与跨站请求 | 有效 BA 登录但缺少或篡改 HMAC viewer cookie 时返回 401；跨站 `Sec-Fetch-Site` 和不匹配 Origin 的写请求返回 403。直接访问 Node 且缺 bridge 凭据返回 403 `bridge_required`。 |
| 公开管理和媒体命令 | `commit/preflight/approve/credit/authorize/policy/terminal/merge/claim/ack` 在公开额度路径均返回 404；即使测试携带正确 bridge 凭据，这些 Node HTTP 路径也不存在。管理命令表计数没有变化。 |
| 撤销、终态和退出 | 撤销已批准安装会话后，公开额度请求返回 `app_session_required`；可信置 owner 终态后，公开读取/预留返回 `account_terminal`。实际 BA sign-out 后重放旧 cookie，实际 `getSession` 返回 null，原已链接 guest 被拒绝，未重新获得匿名额度。 |

Go 额度入口 仅转发四种命令，并在构造桥接参数时重新填入经签名验证的设备和上下文源权限。Node 身份派生使用实际验证会话，且 App 资格必须匹配同 owner/device 的持久审批；路由映射没有对外管理或媒体提交函数。

## 目录与额度锁等待

独立测试不是只检查 SQL 文本：先在 PostgreSQL 持有真实 source 行锁，再分别让公开额度请求先进入、目录映射请求先进入。通过 `pg_stat_activity` 确认第一个请求确实等待 source，第二个请求确实等待额度表锁；释放阻塞后，两种顺序的两端请求都正常结束，没有出现死锁。

这与当前目录表锁顺序相符：目录在 source 行之前取得 `entitlement_reservation`、`entitlement_unlock` 的冲突锁；额度事务同样先锁这两张表，再访问 source。实测使用完全相同映射保存，避免已有状态保护先行拒绝而掩盖锁等待过程。结论限于上述已验证的交错，不表示证明所有数据库工作负载永远不会死锁。

## 验证范围与后续接入

本次另外运行了原有 `services/platform/tests/entitlement-http.test.mjs` 和 `TestConsumerEntitlementGatewayUsesVerifiedDeviceAndGrants`，均通过。没有通过改业务文件来让检查通过。

当前登出后的已链接 guest 返回 403 `guest_already_linked`，这是保留原归属、不重新赠送 10 集的明确拒绝行为；本报告没有把它描述成“登出后仍可完整使用游客观看”。若后续要提供该体验，应单独定义身份转换与原额度事实的保留方式。

账号终态实测使用可信领域命令后检查 HTTP 拒绝；没有验证尚未完成的 A03 注销 OTP、实际身份删除及数据保留流程。设备 cookie 也不证明硬件唯一性，真实 App 平台证明未在此轮实现或验证。

公开额度 API、数据库扣减和受控媒体交付是不同的接入环节。当前 Go 播放发行、真实短期源令牌/CDN、外部 DRM、强制旧媒体会话收敛均未由本轮验证为已接通。fixture 的源能力与预检记录不证明上游媒体可控，**本报告不宣称线上媒体硬额度已经完成**。
