Files
story/docs/privacy-requests.md
T

7.9 KiB
Raw Permalink Blame History

隐私数据请求开发说明

1. 当前交付边界

当前版本完成了ACCESS数据访问申请和DELETE主体删除申请的端到端闭环。ACCESS由Worker在服务端租户/家庭/长者边界内盘点数据范围;DELETE在受理事务内立即冻结主体,再由Worker对六个子系统逐项处理、持久化状态并对账,只有全部子任务进入COMPLETED或经核验的SKIPPED后才将总请求置为COMPLETED。

隐私请求结果始终设置content_included=false,不把档案、转写、故事或记忆正文复制到请求表、审计记录和普通日志。正文导出由独立私有导出任务实现,见data-export.md。当前仍未开放CORRECTION和独立的CONSENT_WITHDRAWAL批处理请求。

2. 权限与安全边界

能力 家庭所有者 管理员 编辑者/查看者
privacy.read 允许 允许 拒绝
privacy.manage 允许 拒绝 拒绝

服务端从OIDC主体重新解析账号、客户组织、家庭成员和长者归属,不使用浏览器提交的租户条件。创建请求必须提交16至128字符的Idempotency-Key;同键同正文返回原请求,同键不同正文返回IDEMPOTENCY_KEY_CONFLICT。详情响应使用Cache-Control: no-store和强ETag。

DELETE还必须满足以下门禁:

  • reason为1至500字符,但服务端仅持久化SHA-256摘要,不保存原因正文;
  • confirmation必须与主体当前称呼完全一致,确认值不持久化;
  • 主体存在PREPARING、ROOM_ISSUED、STARTING_AGENT、ACTIVE或STOPPING通话时返回SUBJECT_ACTIVE_CONVERSATION,必须先正常结束通话;
  • 受理后主体立即变为deleting,一般主体、内容、设备和导出接口不再返回该主体;所有有效授权同时撤回,记忆停用并清除可召回向量;
  • 删除申请详情使用请求编号仍可查询,即使主体已进入deleting或deleted。

3. 业务流程

sequenceDiagram
    participant U as 家庭所有者
    participant A as API
    participant D as PostgreSQL
    participant O as Outbox
    participant W as Worker
    U->>A: POST /subjects/{id}/privacy-requests
    A->>A: 验证OIDC、privacy.manage、长者边界和幂等键
    A->>D: 创建SUBMITTED请求与PENDING子任务
    A->>O: privacy.access_requested
    A-->>U: 202 + 请求编号
    W->>D: VERIFYING -> EXECUTING
    W->>D: 按冻结分类统计当前长者数据
    W->>D: 子任务COMPLETED,请求COMPLETED
    W->>D: 写入无正文系统审计
    U->>A: GET /privacy-requests/{id}
    A-->>U: 状态、分类数量和子任务

3.1 DELETE删除与对账流程

sequenceDiagram
    participant U as 家庭所有者
    participant A as API
    participant D as PostgreSQL
    participant O as Outbox
    participant W as Worker
    participant R as Redis
    participant S as MinIO
    U->>A: DELETE申请+原因+精确称呼确认
    A->>D: 校验权限、确认值及无活动通话
    A->>D: 主体deleting、撤回授权、停用记忆/向量
    A->>D: 创建6个PENDING子任务
    A->>O: privacy.delete_requested
    A-->>U: 202 + 请求编号
    W->>R: 删除会话租约和设备在线键
    W->>S: 删除所有私有导出对象
    W->>D: 核验媒体对象和外部索引引用
    W->>D: 清空pgvector及供应商引用
    W->>D: 脱敏正文、快照、回调、绑定和档案
    W->>D: 六任务对账+最小tombstone
    W->>D: 请求COMPLETED或PARTIAL_FAILED

六类子任务如下:

子系统 当前动作 成功判定
redis 删除该主体历史通话租约键和关联设备在线键 Redis删除操作成功,允许键本来不存在
export_files 删除全部export_task.object_ref对应私有对象,任务改为EXPIRED并清空对象元数据 每个对象删除成功且数据库引用为空
object_storage 删除该主体全部已登记录音对象,并把分片置为DELETED、清空对象引用和文件摘要 对象存储删除失败时记FAILED并持续重试;无对象时安全完成
pgvector 清空本地embedding和provider引用,索引记DELETED 目标主体本地向量均不可召回
provider_index 检查外部供应商索引引用 无外部引用时记SKIPPED;存在引用但无删除适配器时失败并使总请求PARTIAL_FAILED
postgresql 对档案、事实、转写、摘要、故事、记忆、快照、回调和绑定做删除或不可逆脱敏 主体为deleted,正文为固定删除标记,审计只留最小证明

4. 接口

接口 能力 关键约束 结果
POST /api/v1/subjects/{id}/privacy-requests privacy.manage 接受ACCESS或DELETE;必须Idempotency-Key;DELETE还需原因和精确确认 202受理,幂等重放为200
GET /api/v1/subjects/{id}/privacy-requests privacy.read 固定长者边界 请求列表及盘点摘要
GET /api/v1/privacy-requests/{id} privacy.read 服务端反查家庭归属;禁止缓存 请求详情和子任务

可请求的分类为profile、consents、device_bindings、conversations、transcripts、summaries、stories和memories。不指定分类时盘点全部分类。

5. 数据库

表 作用 关键约束
privacy_request 隐私请求聚合根 租户+申请人+幂等键唯一;请求摘要用于检测正文冲突
privacy_subtask 子系统执行与重试状态 请求+子系统+目标唯一;保留尝试次数和最后错误
tombstone 后续删除闭环的最小不可逆证明 只保存资源摘要和保留原因,不保存待删正文

V9迁移提供了请求、子任务和最小证明模型。当前ACCESS和DELETE已开放;CORRECTION与独立CONSENT_WITHDRAWAL仍保持关闭。 独立临时空库已完成up 1—9 → down -all → up 1—9,最终迁移状态为9|dirty=false,验证库随后已删除。

6. 自动化验证

scripts/smoke-privacy.ps1使用真实本地OIDC令牌验证:

  • 所有者创建、同键重放和同键不同范围冲突;
  • 编辑者提交请求返回403;
  • Worker完成数据库子任务,请求收敛为COMPLETED;
  • 返回的分类严格等于请求范围且content_included=false;
  • 列表可见、测试数据和关联Outbox/审计可清理。

scripts/smoke-privacy-delete.ps1使用临时家庭和主体,并验证:

  • 错误称呼确认和非所有者提交均被拒绝;
  • 存在活动通话时删除返回409,正常结束后才允许受理;
  • 受理后普通主体读取和新建导出立即被阻断;
  • 冻结后的同键同正文仍能安全重放,不会创建第二个请求;
  • 六个子任务全部收敛为COMPLETED或经核验的SKIPPED;
  • 档案和稳定事实正文已脱敏,原因明文未进入请求表;
  • 私有导出数据库引用与MinIO对象均已删除;
  • 仅留下不含个人正文的SHA-256主体摘要墓碑;
  • 测试完成后临时业务数据、Outbox、审计和对象均被清理。

家庭工作台的“提交全量盘点”现通过页面内受控操作区提交。操作区固定当前长者公开编号和八类盘点范围:长者档案、授权记录、设备绑定、通话、话轮、摘要、故事和记忆;同时明确ACCESS结果仅返回各分类数量、处理状态和子任务状态,不会把个人正文复制到隐私请求、审计或普通日志。用户必须显式确认范围后才能提交,取消或切换业务上下文会清除草稿且不会创建请求。

页面按低强度策略使用真实OIDC执行无提交冒烟:检查固定对象、八类范围、正文边界、确认门禁和取消行为,并验证零创建请求、请求数量不变、无横向溢出及无非预期浏览器错误。服务端权限、幂等、租户隔离和Worker闭环继续由既有定向脚本覆盖,不在每次页面改动后重复执行全套隐私删除回归。