13 KiB
Xbin
Xbin 是一个构建在 Cloudflare Workers 之上的、类似 PrivateBin 的端到端加密 Pastebin。它包含一个用于创建和读取加密内容的浏览器应用、一个现代 JSON API,以及一个兼容旧版 PrivateBin 的接口层,方便迁移和互操作。
所有内容都会先在浏览器中完成加密,再上传到服务端。Worker 仅将加密后的载荷存储到 R2,将生命周期元数据存储到 D1,并借助 Durable Objects 与 Queues 处理阅后即焚声明和后台清理任务。
特性亮点
- 浏览器端加密,分享密钥通过 URL fragment 传递
- 支持可选密码、过期时间、阅后即焚链接,以及每条内容独立的删除令牌
- 支持纯文本、代码高亮和 Markdown 渲染
- 支持加密附件上传、下载与预览
- 支持兼容粘贴内容的讨论线程
- 提供位于
/api/v1/*的现代 REST API - 支持兼容旧版 PrivateBin 的 API,以及文件系统导入工具
- 原生运行在 Cloudflare 平台之上,使用 Workers、D1、R2、Durable Objects、Queues 和定时清理
安全模型
- 浏览器生成随机密钥,并在上传前完成内容加密。
- Worker 仅在 R2 中保存加密信封,在 D1 中保存元数据。
- 分享链接通过查询参数携带 paste id,通过 URL fragment 携带解密密钥,例如
https://paste.example.com/?abcdef1234567890#secretKey。fragment 不会发送给服务器。 - 如果设置了可选密码,客户端会在解密前使用 PBKDF2 将密码与 fragment 密钥组合。
- 任何拿到完整分享 URL 的人都可以解密内容。删除令牌与分享链接分离,且仅在创建或导入 paste 时返回。
架构
| 组件 | 职责 |
|---|---|
| Cloudflare Worker | HTTP API、静态资源分发、SEO 元数据重写、配置接口和导入鉴权 |
| Durable Object | 串行化处理阅后即焚的声明与消费操作 |
| D1 | Paste 和评论的元数据、生命周期状态、删除令牌哈希、焚毁声明 |
| R2 | 加密后的 paste 与评论载荷 blob |
| Queue | 异步清理已过期、已删除和已焚毁的内容 |
| Cron trigger | 每分钟释放过期的焚毁声明并扫描已过期的 paste |
assets/ SPA |
在浏览器中完成加密、解密、渲染和分享 |
快速开始
前置条件
- Bun
- 一个可使用 Workers、D1、R2、Queues 和 Durable Objects 的 Cloudflare 账号
- 如果你计划在本机部署,需要本地已完成 Wrangler 登录认证
安装依赖
bun install
创建 Cloudflare 资源
首次部署前,请先创建你自己的资源,然后将 wrangler.jsonc 中的名称和 ID 替换为你账号下的值。
bunx wrangler d1 create xbin
bunx wrangler r2 bucket create xbin-pastes
bunx wrangler queues create xbin-gc
说明:
- 当前存储库中的
wrangler.jsonc已包含具体的 D1 和 R2 标识符。如果你要 fork 或部署自己的实例,请务必替换成你自己账号中的值。 - 你只需要手动创建 D1、R2 和 Queue。Durable Object 绑定以及对应的 SQLite 类已经在
wrangler.jsonc中声明,会在部署和迁移时自动创建。 - 当前存储库设置了
workers_dev = false。部署你自己的 fork 前,请先在wrangler.jsonc中配置自己的路由或自定义域名,或者将其改为workers_dev = true,这样应用才会有可访问的主机名。 - 变更绑定或环境变量后,请重新生成 Worker 类型:
bun run cf-typegen
配置本地密钥
将 .dev.vars.example 复制为 .dev.vars,并填写你要使用的可选密钥:
Copy-Item .dev.vars.example .dev.vars
可用密钥:
TURNSTILE_SITE_KEYTURNSTILE_SECRET_KEYIMPORT_TOKEN
大多数非敏感的运行时默认值都位于 wrangler.jsonc 的 vars 段中。
本地运行
bun run dev
Wrangler 会通过同一个 Worker 入口同时提供 SPA 和 API。
通过 GitHub Actions 部署
默认的部署路径是 GitHub Actions。如果你要部署自己的实例,建议从 xixu-me/xbin fork 开始。
发布流程如下:
- 在你的 fork 中,把
CLOUDFLARE_ACCOUNT_ID和CLOUDFLARE_API_TOKEN配置为 GitHub Actions secrets。 - 向你 fork 的
main分支推送代码。 CI会运行格式检查、类型检查、Wrangler 类型校验、带覆盖率的测试,以及 Wrangler 部署 dry run。- 如果该次
main推送通过了 CI,Deploy会发布通过校验的同一份提交。
Pull Request 同样会运行 CI,但不会自动部署。你也可以在自己的 fork 中通过 workflow_dispatch 手动触发部署流程。
部署工作流最终执行的是:
bunx wrangler deploy --keep-vars --message "GitHub Actions deploy for ${GITHUB_SHA}"
--keep-vars 表示除非你有意在 Cloudflare 或部署配置中修改,否则远端 Worker 变量会被保留。
本地手动部署
如果你需要绕过 GitHub Actions,在本地直接部署:
bun run deploy
常用命令
| 命令 | 用途 |
|---|---|
bun run dev |
使用 Wrangler 在本地运行 Worker |
bun run start |
本地 Wrangler 开发的别名 |
bun run check |
对 TypeScript 代码库执行类型检查 |
bun run test |
使用 Vitest 运行 Worker 集成测试 |
bun run test:coverage |
运行测试并输出 Istanbul 覆盖率报告 |
bun run format |
使用 Prettier 格式化整个存储库 |
bun run format:check |
检查格式但不修改文件 |
bun run cf-typegen |
在配置变化后刷新 Worker 绑定类型 |
bun run deploy |
发布 Worker |
配置项
| 变量 | 默认值 | 用途 |
|---|---|---|
XBIN_APP_NAME |
Xbin |
UI 和元数据中显示的品牌名 |
XBIN_APP_VERSION |
1.0.0 |
在应用配置接口和页脚中暴露的版本号 |
XBIN_PROJECT_PAGE_URL |
https://github.com/xixu-me/xbin |
UI 中显示的项目链接 |
XBIN_BASE_PATH |
/ |
用于构建分享链接、规范 URL 和站点地图链接的挂载路径 |
XBIN_MAX_PASTE_BYTES |
10000000 |
加密 paste 载荷的最大字节数 |
XBIN_DEFAULT_EXPIRATION |
1hour |
UI 和 API 使用的默认过期键 |
XBIN_SUPPORTED_EXPIRATIONS |
5min,10min,30min,1hour,3hour,6hour,12hour,1day,3day,1week |
应用暴露的过期键列表,逗号分隔 |
XBIN_ENABLE_LEGACY_API |
true |
是否启用兼容 PrivateBin 的旧版 JSON API |
XBIN_REQUIRE_TURNSTILE |
false |
是否要求在创建 paste 和评论时传入 turnstileToken |
XBIN_BURN_CLAIM_TTL_SECONDS |
120 |
阅后即焚声明在释放前可保留的秒数 |
TURNSTILE_SITE_KEY |
未设置 | 启用 Turnstile 时暴露给客户端的站点密钥 |
TURNSTILE_SECRET_KEY |
未设置 | Worker 用于校验 Turnstile 令牌的密钥 |
IMPORT_TOKEN |
未设置 | 启用并保护 PrivateBin 导入接口 |
配置解析器还支持 1month、1year 和 never 这些过期键,只要你愿意将它们暴露给客户端即可。
API 概览
写入 API 接收的是经过加密的、PrivateBin 风格的信封对象,而不是明文内容。一个最小创建请求示例如下:
{
"v": 2,
"adata": [["iv", "salt", 100000, 256, 128, "aes", "gcm", "none"], "plaintext", 0, 0],
"ct": "ciphertext",
"meta": { "expire": "1day" }
}
核心接口:
| 方法 | 路径 | 用途 |
|---|---|---|
GET |
/api/v1/config |
返回运行时 UI 和功能配置 |
POST |
/api/v1/pastes |
创建 paste,并返回 { id, shareUrl, deleteToken } |
GET |
/api/v1/pastes/:id |
返回加密 paste 信封和评论 |
DELETE |
/api/v1/pastes/:id |
在提供 { "deleteToken": "..." } 时删除 paste |
POST |
/api/v1/pastes/:id/comments |
为启用讨论的 paste 创建评论 |
POST |
/api/v1/pastes/:id/consume |
使用 { "claimToken": "..." } 完成一次阅后即焚读取 |
POST |
/api/v1/admin/import/privatebin |
在授权后导入 PrivateBin 文件系统数据 |
说明:
- 对于阅后即焚的 paste,
GET /api/v1/pastes/:id会返回一个claimToken。客户端在成功解密后必须继续调用/consume。 - 启用 Turnstile 后,在创建 paste 和创建评论的请求体中都需要包含
turnstileToken。 - 阅后即焚 paste 不支持评论。
PrivateBin 兼容性与导入
Xbin 提供两种兼容路径:
- 通过
X-Requested-With: JSONHttpRequest识别旧版 JSON API 调用。 - 对于
/api/v1/pastes?<pasteId>这类旧版浏览器分享 URL,返回 SPA 外壳页面,让客户端在本地恢复分享内容。
如果要从 PrivateBin 的文件系统导出中导入数据,先为 Worker 设置 IMPORT_TOKEN,然后执行:
bun run import:privatebin:fs -- --source /path/to/privatebin/data --base-url https://paste.example.com --token your-import-token --report ./import-report.json
导入器会:
- 遍历
*.phppaste 文件及其相邻的.discussion/目录 - 在可用时保留创建时间和过期时间元数据
- 跳过已经过期的 paste
- 为每条导入后的 paste 返回一个新的
deleteToken,因为 Xbin 会在导入时重新生成删除凭证
存储库结构
| 路径 | 用途 |
|---|---|
src/index.ts |
Worker 入口,负责 HTTP 路由、静态资源分发、定时清理和队列处理 |
src/lib/ |
配置解析、校验、数据访问、Schema 和共享类型 |
assets/ |
浏览器应用、HTML 外壳、CSS 和 vendored 客户端库 |
scripts/ |
一次性工具,例如 PrivateBin 文件系统导入器 |
migrations/ |
D1 Schema 迁移 |
test/ |
Worker 集成测试和仓储层测试 |
测试与质量
本存储库使用带 Cloudflare Workers 运行池的 Vitest、TypeScript 类型检查、Wrangler 类型校验和 Prettier 格式化。CI 还会在允许生产部署前执行一次 Wrangler 部署 dry run。Codecov 配置了 95% 的项目覆盖率和 90% 的补丁覆盖率目标,因此除文档外,代码改动通常都应附带测试。
与主要 CI 质量门槛一致的一组本地校验命令如下:
bun run format:check
bun run check
bunx wrangler types --check
bun run test:coverage
相关文档
许可证
本项目基于 GNU Affero General Public License v3.0 发布。完整条款请参见 LICENSE。