怎么防风险 · Klarpix
用户授权要能被证明,所以留了三层只追加的证据
Klarpix 把「用户同意过」做成可以举证的东西:代码库里的闸口与政策文本、设备侧只追加的同意历史、服务端只追加的台账,三层可以互相对应,台账不含任何能识别个人的字段。
一张老照片上传到服务器修复,中间发生了什么、用户是否同意、同意的是哪一版条款,这些事在出问题之前没有人问。但一旦有人问,「弹过窗了」不是答案。我想要的是:任何一张被处理过的照片,都能对应到一条用户当时的同意记录,以及一条服务端确实按承诺处理了的记录。授权不是一个开关,是一条证据链。
要回答的问题
把它拆开,其实是三个不同的问题,分别由不同的人在不同的时间问出来:
- 产品当时到底承诺了什么?这是政策文本和代码逻辑的问题。
- 这个用户在什么时候、对哪一版承诺,做了什么选择?这是用户行为的问题。
- 服务端是否真的按承诺处理了那张照片?这是系统行为的问题。
三个问题的证据来源不同,试图用一条记录同时回答三个问题,结果往往是三个都答不完整。所以我把它们分开存。
三层证据
第一层在代码库里。授权闸口的逻辑和政策文本一起进版本控制。每一个政策版本号对应一次提交,提交里同时包含法务文本的变更和闸口逻辑的变更。这样「产品当时承诺了什么」可以精确回溯到任意一个版本。
第二层在用户的设备上。一份只追加、不可修改的同意历史,每条记录包含三个状态之一:agreed、declined、reconsented,以及当时的政策版本号和时间。只追加意味着用户改变主意不会覆盖旧记录,而是新增一条,历史完整。这份记录属于用户,随应用数据一起留在设备上。
第三层在服务端。一份只追加的处理台账,记录每一次修复请求对应的同意版本、处理时间和处理结果。这份台账保存五年,可以整批导出。
三层怎么对上
照片、同意记录、台账条目之间是双向可对应的:从一张照片能找到它当时依据的同意记录和服务端的处理条目;从一条台账条目也能反过来找到它对应的同意版本,进而找到那个版本的政策文本。
这里有一个刻意的设计:服务端台账里不含任何能识别个人的字段。没有姓名、没有邮箱、没有设备标识。对应关系靠请求级别的标识符建立,这些标识符只在用户自己的设备上能和具体的人对应起来。这样台账可以被审计、可以被导出,但拿到台账的人无法反推出用户是谁。举证能力和隐私保护,两个目标在这里没有冲突。
另一个刻意的设计是把政策版本变更列为发布闸口:政策文本和版本号必须在同一次提交里更新,否则不允许发版。这条规则防的是一种很常见的疏忽,条款改了,代码里的版本号忘了跟,于是用户同意的记录指向了一个不存在的版本。
红线与承诺
证据链是为了在出问题时能说清楚,但更好的是让问题少发生。这一层是两条硬线:
- 手动编辑全部在设备本地完成,不发出任何网络请求。裁切、旋转、局部调整,照片不离开手机。
- AI 修复需要上传,但服务端副本在一小时内删除,不留存、不用于训练。
这两条写在隐私政策里,也对应到代码里可以核对的实现。我给自己定的标准是:政策里的每一句承诺,都要能指出实现它的那段代码。反过来,代码做不到的事,政策里就不能写。
由此定下的口径
- 授权记录只追加,不修改、不删除。历史比现状更重要。
- 三类问题分三处存证据,不用一条记录回答所有问题。
- 服务端台账不含可识别个人的字段,可审计与可识别是两件事,只做前者。
- 政策版本与法务文本同次提交,列为发布闸口。
- 承诺与实现一一对应,写不出对应代码的承诺不写。
- 合规和授权留痕在上线前做完,不等出事再补。
结论
「用户同意过」必须是一个可以举证的事实,而不是一段记忆。三层只追加的记录,加上一条不可识别个人的台账,让这个事实在五年内的任何时刻都能被重新拿出来核对。对一个独立开发者来说,这是上线前必须付的成本。真正贵的是出了事再补。