Article

Binflare 技术拆解:零知识协议怎么落到免费层上

2026年10月22日 · 2 分钟阅读 · ixNieStudio

Binflare 的工程细节:为什么主存储是 D1 而不是 KV、大粘贴如何按大小溢出到 R2、没有原生 TTL 怎么实现到期清理、以及一键部署背后的权限边界实测。

Binflare 技术拆解:零知识协议怎么落到免费层上

上一篇讲了 Binflare 是什么。这篇讲它怎么做——把一个为 PHP + 文件存储设计的零知识协议,搬到 Cloudflare Workers + D1 + R2 上,中间每一个取舍都有具体的约束在驱动。

存储:一行元数据永远在 D1,密文按大小分流

每条粘贴在 D1 里都有一行记录:元数据(adata)、盐、删除令牌的哈希、过期时间。密文则按大小分流:

  • 密文 ≤ 1.875 MB:直接内联在 D1 行里;
  • 密文更大:整段写入 R2 的 pastes/<id>,D1 行的密文字段留空,用 r2_key 指向它。

为什么是 1.875 MB?这是 D1 免费层行大小与请求体限制推出来的工程上限,不是拍脑袋的数字。附件不单独存对象——它们本来就是加密载荷的一部分,跟着密文一起走。评论永远不进 R2,量级不值得。

为什么不用 KV:焚毁语义输不起

Cloudflare Workers KV 看起来是更顺手的键值存储,但它有两条硬伤:

  1. 最终一致性:写入后全球传播约 60 秒。粘贴板的核心承诺之一是「阅后即焚」——读完立刻删。如果删除要一分钟才在全球生效,焚毁就是一句空话。
  2. 免费额度:KV 免费层每天只允许 1,000 次写入,D1 是 100,000 行。差了两个数量级。

D1 是强一致的 SQLite,并且原生跑在 Workers 旁边。这个选择没有悬念:凡是涉及删除可见性的语义,一致性不能将就。

到期清理:没有 TTL,就读时过期 + 定时扫除

D1 没有原生 TTL。Binflare 的处理是两层:

  • 读时过期:每次读取先判过期时间,过期的粘贴当场拒绝并删除——用户永远读不到过期内容;
  • 定时清理:Cron 触发器周期扫除过期行与对应的 R2 对象,回收存储空间。

语义正确性由读时判断保证,定时任务只负责卫生,不负责正确性。这个分工在没有 TTL 的存储上是通用解法。

协议对齐:兼容是逐项对出来的

「兼容 PrivateBin 2.x」不是一句宣传语,它意味着:数据格式、加密参数、粘贴 ID 的生成、删除令牌的构造、根 API 的请求与响应形态,逐项与上游 2.1.0 对齐验证。好处是存量生态直接复用——为 PrivateBin 写的客户端、插件、脚本,改一个服务地址就能连到 Binflare。兼容的代价是约束多,但换来的分发价值远大于这点代价。

一键部署的权限边界:一个真实踩过的坑

「点一下按钮就能部署」要成立,前提是部署令牌权限最小化。实测中确认了一个边界:只给 Workers 权限的部署令牌跑不了 D1 数据库迁移(会被权限校验拦下)。要求每个自部署用户先去开一堆额外权限,一键部署就名存实亡了。

解法是把建表挪进应用本身:Worker 在首次请求时用幂等的建表语句初始化 schema(全部 IF NOT EXISTS,并登记迁移版本),部署脚本不再碰数据库。效果是:一个全新的 Cloudflare 账号点完按钮,第一次访问就能用,数据库是应用自己长出来的。

顺带留了一条维护纪律:迁移文件与内置建表语句从此是两份事实来源,任何一次 schema 变更必须两边同步。

防滥用只属于部署层,不属于产品

公共预览实例需要防刷,但产品本体保持干净。Binflare 的分工是:

  • 产品层:Turnstile 只校验「创建粘贴」和「发表评论」两个写动作,读、删永远不拦——分享链接点开还要过人机验证,是对零知识分享体验的破坏;
  • 部署层:频率限制、过期上限、默认过期时间都做成部署期环境变量。公共实例收紧(比如过期最长一周),自部署实例缺省即上游全量行为。限制是部署者的选择,不是产品写死的策略。

实例自带的 /setup 页面把这些运行时配置只读地展示出来:当前存储档位、粘贴上限、过期菜单、防护是否开启,一目了然。


零知识协议的价值在「服务器不可信」这个前提,免费层的价值在「部署无门槛」。Binflare 的工程,基本就是让这两个前提在 Cloudflare 的约束里同时成立。代码在 https://github.com/openshiye/binflare ,欢迎实测与挑错。