文档
信任与验证
在 reuseforge.com 的托管应用上,你可以知道是谁发布了一个条目,也可以确认手里的文件就是发布时的文件,但还不能确认代码是安全的。本页说明平台检查了什么、只是记录了什么、计划做什么,以及哪些事情仍要你自己检查。
两种产品共用这些文档。本页讲的是托管应用,它使用账号、会话和 API 令牌。Ed25519 密钥、签名账本和验证记录,只适用于你自己运行 Python 节点的情况,已集中放在页面末尾的自托管节点一节。
账号与登录
托管应用上的参与者是一个账号,包含用户名(handle)、邮箱和登录方式。账号与法律意义上的人之间没有任何关联,也没有身份核验或 KYC;邮箱通过验证,只说明对方能收到发到该地址的邮件。
你可以用密码、GitHub 或两者登录。密码登录可以再加一层验证器应用。密码至少 10 个字符;如果它在常见密码列表里,或包含你邮箱地址的用户名部分,就会被拒绝。服务器保存的是带盐的 PBKDF2-SHA256 哈希(100,000 次迭代),而不是密码本身。
会话与 Cookie
- 登录会设置一个 Cookie:
__Host-rf_sid,带有HttpOnly、Secure和SameSite=Lax属性,内容是一个随机值。服务器只保存它的 SHA-256,所以即使会话表被复制,也无法还原出可用的 Cookie。 - 会话有效期 30 天。一个账号最多保留 20 个会话,最早的会被淘汰。每次登录都会生成新的会话 ID。
- Settings, Security 列出你的会话,包括每个会话最近使用的时间、IP 地址和浏览器,你可以退出某一个会话,或退出其余所有会话。修改密码会退出你的其他会话,重置密码会退出全部会话。
- 带有 Cookie 的、会修改数据的请求,必须来自本站自己的来源,以此防止跨站伪造。
- 登录猜测受到限制。同一个地址在 15 分钟内对同一个邮箱或用户名尝试 8 次之后,后续尝试会收到
429,直到时间窗口结束或你重置密码。单个地址每 10 分钟最多 40 次登录尝试。 - 同一页面还列出你账号最近 50 条安全事件(登录、修改密码和邮箱、两步验证变更、令牌创建与撤销)。在启用了邮件发送的情况下,平台还会就其中较重要的事件给你发邮件。
两步验证
你可以在 Settings, Security 中开启基于时间的一次性密码(TOTP:6 位数字,30 秒一换)。设置时会先要求输入密码,因此被盗的会话无法给自己绑定验证器。你会一次性拿到 10 个恢复码;服务器只保存它们的加盐哈希,每个恢复码只能用一次。验证器生成的验证码同样只能用一次。输错超过 5 次,待完成的登录就会被终止;待完成状态持续 10 分钟。关闭两步验证需要密码和一个验证码。两步验证对 GitHub 登录同样有效。
GitHub 登录
GitHub 登录使用带 PKCE 的授权码流程,并用与 Cookie 绑定的 state 值。它向 GitHub 请求 read:user 和 user:email。新账号只会用你经过验证的 GitHub 主邮箱创建。平台绝不会仅因邮箱相同,就把 GitHub 登录关联到已有账号:你要先用密码登录,再到 Settings, Connections 里连接 GitHub。如果 GitHub 是你唯一的登录方式,就不能断开它。
这与导入仓库是两回事:导入读取的是公开的 GitHub 数据,不使用你的登录。见文件从哪里来。
邮箱与账号恢复
- 验证链接 24 小时内有效,密码重置链接 1 小时内有效。两者都只能使用一次,服务器只保存它们的哈希。
- 修改邮箱时会要求输入密码,并且在启用了邮件发送的情况下,向新地址发送一条确认链接。
- 重置密码同时会验证该邮箱。如果这个邮箱此前没有验证过,重置会移除之前设置的两步验证、API 令牌和 GitHub 关联,因为这些可能是别人抢先注册该邮箱时创建的。
- “忘记密码”无论该地址是否有账号,回应都一样。
API 令牌与权限范围
脚本和智能体使用 API 令牌,而不是你的密码。请在 Settings, API tokens 中创建。
- 令牌以
rf_开头,只显示一次。服务器只保存它的 SHA-256,以及前 10 个字符,方便你在列表里辨认。 - 你可以设置名称、权限范围,以及 1 到 365 天的有效期(或不设期限)。最多可以同时持有 50 个有效令牌,随时可以撤销任意一个。列表会显示每个令牌最近一次的使用时间。
- 权限范围:
read(读取你的私有条目和账号数据)、write(以你的身份发布和编辑条目、版本、评价和讨论)、orders(查看你的订单,并解锁你已拥有的付费文件),以及admin,只有管理员账号才能创建或使用。 - 令牌不能修改凭据。创建令牌、修改密码或邮箱、管理会话和两步验证、删除账号,都需要浏览器会话。
- 持有令牌的人可以在其权限范围内以你的身份行事。
write令牌可以为你名下的每个条目发布新版本。请为每个集成单独创建令牌,授予最窄的范围,并设置有效期。
支付运行在测试模式下,没有真实资金流动,所以目前没有任何令牌能动用真实资金。API 参考文档暂时只有占位页,见 API 参考。
如何识别一个条目版本
条目指一个已发布的上架内容:仓库、技能、harness、MCP 服务器或其他类型。所有者为它发布版本,每个版本有一个版本号(例如 1.2.0)和一组文件(最多 100 个文件,每个不超过 512 KB,每个版本总计不超过 2 MB)。
内容哈希
发布版本时,服务器会计算每个文件的 SHA-256,再为整个版本算出一个内容哈希:对排序后的 path:file-sha256 行(以换行符连接)计算 SHA-256。哈希不是发布者提供的,所以发布者无法为另一批文件声称某个哈希。
- 条目的 Versions 标签页显示前 12 个字符,前面加
sha256:,旁边的按钮可以复制完整值。API 会在版本列表、文件列表和 JSON 打包文件中以content_hash返回完整值。 - 版本是不可变的。没有任何操作可以编辑已发布版本的文件,重复发布同一个版本号会被拒绝。所有者发现错误时,应发布新版本,或把有问题的版本撤回(yank)。被撤回的版本会有标记,不再算作最新版本,也不会被默认安装,但仍可按版本号获取。
- 文件路径会被检查:必须是相对路径,不能含有
..段,也不能含有控制字符或改变文字方向的字符。
你可以用打包文件重新计算这个哈希(把 OWNER 和 SLUG 换成实际值):
curl -sL -o bundle.json "https://reuseforge.com/api/items/OWNER/SLUG/bundle"
python3 - <<'PY'
import base64, hashlib, json
b = json.load(open("bundle.json"))
lines = sorted(
f["path"] + ":" + hashlib.sha256(
base64.b64decode(f["content"]) if f["encoding"] == "base64" else f["content"].encode()
).hexdigest()
for f in b["files"])
print("computed:", hashlib.sha256("\n".join(lines).encode()).hexdigest())
print("reported:", b["content_hash"])
PY哈希能告诉你什么,不能告诉你什么
| 它能告诉你 | 它不能告诉你 |
|---|---|
| 你手里的文件,就是服务器为该版本号保存的文件。 | 代码是否安全、能否运行,或是否真的像描述所说的那样工作。 |
| 某个版本自你记录哈希以来是否发生过变化。采用一个版本时请记下它的哈希,以后再比对。 | 代码是否出自发布者之手,或发布者是否有权发布。 |
| 下载过程中文件是否损坏或被截断。 | 之后版本的任何情况。每个版本都有自己的哈希。 |
哈希不是签名。它由 ReuseForge 计算,并和文件存放在一起。它能防范你记录哈希之后发生的变更,也能防范损坏的下载;但无法防范平台自身被攻破,因为能改文件的人也能改哈希。平台也不会在每次下载时,把已存储的文件与哈希重新比对。如果某个版本对你很重要,请自己保存一份哈希。
REST API v1 会为版本报告它自己的内容哈希,带有 sha256: 前缀,且基于另一种行格式计算。这两个值互不相等,所以只能把一个值与同类的值比较。
文件从哪里来
- 发布的版本带有存放在 ReuseForge 上的文件,并有内容哈希。
- 从 GitHub 导入的条目只有元数据和 README,是导入时从公开仓库复制的。代码仍在 GitHub 上,所以没有内容哈希。条目列出的安装命令是一条普通的
git clone,你运行时拿到的是默认分支当时的内容,而不是导入时的内容。显示的原仓库星标数是导入时的快照。 - 导入并不能证明你拥有该仓库。任何已登录的用户都可以导入任何公开仓库。在信任一个条目之前,请核对来源链接、条目所有者和仓库是否一致。
卖家与条目如何审查
目前已有
| 措施 | 作用 | 局限 |
|---|---|---|
| 验证邮箱才能出售 | 上架付费条目,或把条目改为付费,需要已验证的邮箱。免费条目不需要。 | 它只说明你能控制一个邮箱,不说明你是谁。付费条目目前还不能购买;支付运行在测试模式下,没有真实资金流动。 |
| 保留用户名 | 品牌名和路由名,以及以 reuseforge 或 repoblock 开头的用户名,都不能注册。 | 这是目前唯一的防冒充措施,近似的名字拦不住。 |
| 举报 | 任何已登录用户都可以举报条目、账号、评价、讨论或评论(垃圾内容、恶意软件、许可证、诈骗、滥用或其他)。你不能举报自己的内容,对同一对象不能重复提交未处理的举报,每人每天最多 20 次。 | 举报由人工处理,没有自动处置。 |
| 审核处置 | 管理员处理举报队列,可以驳回举报、下架条目或封停账号。下架和封停都必须附上书面说明。举报人会收到带有结论的通知,被下架条目的所有者会被告知原因。 | 审核发生在发布之后,而不是之前。 |
| 封停 | 被封停的账号会被登出,其 API 令牌被撤销,名下有效的条目被归档。管理员可以撤销封停。 | 封停只针对单个账号。 |
| 管理员工具 | 管理员还可以推荐或下架条目,并把账号标记为经员工验证。每一次管理操作都会写入审计日志。使用管理工具要求登录时间不超过 12 小时;管理员角色无法通过应用获得,而是由运营者直接在数据库中授予。 | 员工验证标记是手动设置的,并不是安全保证。 |
| 速率限制 | 每个账号每小时:30 个新条目、60 次版本上传、20 次 GitHub 导入、20 条评价。注册按地址限速。 | 它们能拖慢滥用,但拦不住。 |
| 安全处理内容 | 文件以 nosniff 和严格的内容安全策略提供。HTML、SVG 和脚本只会被下载或作为纯文本显示,绝不会在 ReuseForge 的来源下运行。README 经过 Markdown 渲染器处理,先转义文本,只链接 http、https、mailto 和站内路径。安装字段会拒绝隐藏字符和改变文字方向的字符。 | 这保护的是网站和你的浏览器,并不意味着你下载的代码可以放心运行。 |
| 按需的静态检查 | 对条目有写权限的人,可以通过 API 请求对某个已存储版本做静态检查。它会重新计算哈希,并检查大小限制、清单、许可证是否为有效的 SPDX 表达式、提交进去的密钥、原生二进制文件,以及技能或 MCP 的配置。结果会与该版本一起保存。 | 只能通过 API 使用,网页应用不显示,也不会自动运行。它不运行条目的测试,也不是恶意软件或漏洞扫描。 |
ReuseForge 不会运行已发布条目的测试。上架信息上显示的测试通过率,并不是平台产生的结果。
计划中,尚未实现
- 对已发布版本的静态安全扫描,由平台自动运行,而不是按需请求。
- 在上述保留用户名之外的防冒充保护。
在这些功能出现之前,请把每个条目都当作陌生人写的、未经审查的代码。
浏览器里能看到什么
- 所有者。用户名;如果员工把该账号标记为已验证,还会有一个对勾。这是员工手动设置的。
- 许可证。显示所有者声明的许可证徽章;没有声明时显示 “No license” 或 “Not stated”。没有任何机制扫描代码来确认它。
- 演示。示例条目带有徽章,用来展示上架信息是什么样子,它们不是真实的产品。
- 来源与版本。代码所在位置的链接、最新版本号;在 Versions 标签页上,每个版本都有内容哈希、大小,以及(如适用的)Yanked 徽章。
- 评价、评分和星标。由其他账号提供。星标数包含导入时记录的 GitHub 星标。它们是信号,不是检查。
- Report this item。对你不拥有的每个条目,已登录用户都能看到这个按钮。
你仍应自己检查什么
- 运行之前先读代码。技能会引导智能体,harness 会运行你的智能体,MCP 服务器会在你的机器上以你授予的权限运行。请留意网络请求、文件访问,以及读取环境变量的地方。
- 锁定你采用的版本。记录版本号和内容哈希,更新时比对。对导入的条目,请自己锁定到某个 commit。
- 自己核对许可证。徽章显示的是所有者声明的内容。方案内各许可证是否兼容,并没有检查。
- 核对发布者。点开来源链接,对照条目所有者和仓库所有者,并阅读讨论和近期评价。
- 在用完即弃的容器或虚拟机里运行不熟悉的代码,其中不放任何密钥,条件允许时也不要开放外网。
- 让令牌保持最窄。除非工具必须发布,否则只用
read;设置有效期,并撤销不再使用的令牌。 - 为会发布他人要安装的条目的账号开启两步验证。
哪些事情没有保证
| 你可能以为 | 实际情况 |
|---|---|
| 代码能用。 | 没有人替你运行过它。评分和星标是意见和热度,不是测试结果。 |
| 代码是安全的。 | 目前还没有自动安全扫描。按需的静态检查不是安全扫描。 |
| 许可证没问题。 | 它就是所有者声明的内容,没有任何机制扫描代码。 |
| 发布者就是他们所说的人。 | 账号不与真实身份绑定。员工验证的对勾,只表示员工设置过它,不表示代码经过审计。导入仓库也不能证明你拥有该仓库。 |
| 文件不会改变。 | 已发布的版本在应用里不可变,但内容哈希没有签名。导入的条目的代码在 GitHub 上,随时可能变化。 |
| 评价相互独立。 | 除所有者外,任何已登录账号都可以评价,托管应用上的评价也不与购买关联。 |
| 资金是安全的。 | 支付运行在测试模式下,没有真实资金流动。付费上架、托管、出资自建、订单和收益,在托管应用上都即将推出。文档中描述的托管合约未经审计,也没有部署。请不要使用真实资金。设计说明见用托管购买。 |
| 审核是即时的。 | 条目在发布之后,有人举报时才会被审核。 |
| 买到的代码会保持私密。 | 已交付的文件可以被复制。付款保护不等于保密。 |
| 条目里的文字都可以照做。 | 描述、README 和链接都来自陌生人。应用会对其转义,并且只链接 http 和 https,但请把它们当作不可信内容。 |
报告问题
- ReuseForge 自身的安全漏洞。请私下报告,不要公开提 issue。安全页面说明了报告方式、应包含的内容、你可以期待的回应,以及哪些测试不被允许。
- 恶意、侵权或误导性的条目、账号、评价或讨论。请登录后使用 Report this item 或页面上的举报入口,选择最接近的原因并补充细节。管理员会审核,你会收到带有结果的通知。
- 被误下架或误封停。请通过服务条款中的联系方式申请复核。
- 你认为自己的账号已被盗用。请重置密码,然后在设置中退出其他会话,并撤销你的 API 令牌。
自托管节点
本节只适用于你自己运行 Python 节点(python -m repoblock)的情况。reuseforge.com 上的托管应用不使用 Ed25519 密钥、ledger.jsonl、签名事件或验证记录,你在那里发布的内容也不会写入任何账本。节点把条目称为“模块”(block);本节使用“条目”,只在命令、事件类型和字段名这些字面内容中保留 block。
节点把全部状态保存在一份仅可追加的签名事件日志里。这让记录可以发现篡改,也能追溯到具体的密钥。但它不会让代码变好,不会让卖家变诚实,也不会让一份未经审计的合约变安全。
身份
在节点上,参与者是一对密钥,而不是账号。节点、卖家、买家和仲裁方,各自都是一个 Ed25519 公钥。公开 ID 是 rb1 加上 URL 安全的 base64 编码公钥。节点把具名身份保存在 keys/NAME.pem:未加密的 PEM 文件,创建时文件权限为 600,所在目录权限为 700。身份名称为 1 到 32 个字符,只能用 a-z 0-9 _ -。密钥是假名性的:没有任何东西把密钥与法律意义上的人关联起来。
签名账本
事件保存在 ledger.jsonl 中,每行一个 JSON 对象。一个事件大致如下(已缩短):
{
"id": "9c1f0a...e2",
"sig": "Qm9v...",
"body": {
"kind": "order.accept",
"author": "rb1HkDai0ooqp6bhUoq4r068YjluVtSBeSapZz9sHtWwig",
"data": { "order_id": "ord_6daf528ce3094372" },
"lamport": 18,
"parents": ["5be1c7...04"],
"ts": 1796000000
}
}sig是作者对body的规范化 JSON 做出的签名。id是规范化的body连同sig的 SHA-256,所以只要有一个字节变化,id 就会变化。parents列出作者当时所见日志末端事件的 id,由此用哈希把历史串联起来。lamport是逻辑时钟,不依赖挂钟时间就能给事件排序。
节点收到事件时,会检查格式和大小,检查 id 与内容是否一致,以及签名对声称的作者是否有效。不通过的事件会被丢弃,对等节点发来的某一条坏事件不会影响其余事件。
| 类别 | 事件类型 |
|---|---|
| 注册表 | block.publish、block.retract、block.attest、review |
| 订单 | order.create、order.fund、order.deliver、order.accept、order.dispute、order.resolve、order.cancel、order.ack、order.timeout、order.settling、order.settled |
| 项目 | project.create、project.pledge、project.confirm、project.deliver、project.vote、project.transfer、project.product、project.refund |
节点如何达成一致
这不是区块链。没有挖矿,没有代币,也没有全局共识,因为注册表不存在双花问题。它的做法是:
- 合并就是取并集。两个节点通过交换对方缺少的事件来同步(
python -m repoblock sync URL)。无论顺序如何,最终得到同一个集合。 - 状态是日志的纯函数。注册表、每一笔订单和每个项目,都是按顺序重放事件算出来的,规则在重放时执行:只有买家能验收,只有该条目的卖家能交付它,只有指定的托管方能确认存款或裁决争议。由错误一方签名的事件会被忽略,每个节点的处理结果完全一致。
- 期限按托管方的时钟计算。买家或卖家事件里的
ts只是参考信息。验收期、交付期限这类由时间驱动的步骤,只有在托管方(仲裁方)签名后才算数。
账本确实保证什么
- 可追溯到人。每个动作都由做出它的密钥签名。没有你的密钥,别人无法以你的名义发布、验收或投票。
- 可发现篡改。你无法在不破坏事件 id 和签名的情况下修改历史事件。
- 角色约束。上述订单和项目规则,在每个读取相同事件的节点上都成立。
- 已验证购买。只有评价者针对该条目的订单已放款并完成结算,且订单上的买家、仲裁方和每位卖家是不同的密钥,评价才会计数。节点还可以设置成只信任指定的仲裁方。
- 可审计。你可以自己重放日志,复现任何订单的状态、放款金额,以及任何项目的份额表。
验证记录
block.attest 事件表示:这个身份运行了该条目的测试,得到了这样的通过率。它由运行测试的人签名,并附带 block id、通过率和测试数量。注册表会用每个不同验证者最新一次结果的平均值,取代卖家自报的数字。所有者自己提交的验证记录永远不计入,节点也可以把计数限定在一份验证者白名单内。
验证记录能证明什么,不能证明什么。它只证明某个密钥声称得到了这个结果。默认情况下,任何其他身份都可以为任何条目提交验证记录,而一个人可以持有多把密钥。ReuseForge 不会对验证者加权,也不检查测试本身是否有意义。请把它视为比卖家的说法更可信、但不如你自己运行测试可信的证据。
在沙箱中运行 verify
python -m repoblock verify BLOCK_ID 会克隆该条目的仓库并执行它的测试命令。这就是在运行陌生人写的任意代码,所以在你传入 --i-know-this-runs-untrusted-code 之前,这条命令会拒绝运行。除非上架信息中的 content_hash 是一个 40 位的 git commit,否则它也会拒绝运行,以确保运行的就是发布时的代码;--allow-unpinned 可以覆盖这一限制,改为运行默认分支的最新提交。
它为你做的事:只接受公开主机上的普通 https git 地址;克隆到临时目录;只传入最小化的环境变量(PATH、一个临时的 HOME 和 CI=1,所以你的令牌和密钥不会被继承);设置 CPU 时间、内存(2 GB)、文件大小(64 MB)和打开文件数的限制;强制超时(默认克隆 120 秒,测试 300 秒);最多保留 1 MB 输出;结束后删除目录。如果安装了 bwrap,测试还会在没有网络、且看不到你主目录内容的环境里运行;只有 unshare 时,测试没有网络,但文件系统并不隔离。
它没有做的事:它不是安全边界。没有 bwrap 或 unshare 时,测试代码以你的操作系统用户身份运行,可以读取该用户能读取的任何文件,也可以发起网络请求。
只在容器或用完即弃的虚拟机里运行 verify,其中不要放任何密钥,最好也没有外网。
划出这条边界的一种做法,是使用账本的临时副本,让容器看不到你的密钥:
mkdir -p scratch && cp ~/.repoblock/ledger.jsonl scratch/
docker run --rm -it -e REPOBLOCK_HOME=/data -v "$PWD/scratch":/data -v "$PWD":/app:ro -w /app python:3.12 \
bash -c "pip install -q -r requirements.txt && python -m repoblock verify BLK_ID --i-know-this-runs-untrusted-code"验证记录由一把全新的密钥签名,保存在 scratch/ledger.jsonl 里。要把它并入你的主节点,可以在另一个端口上运行这个目录对应的节点,再对它执行 sync。请按你的环境调整示例,重点在于这条边界,而不是具体命令。
节点没有保证什么
| 你可能以为 | 实际情况 |
|---|---|
| 交付内容与上架信息一致。 | 节点只记录访问链接和卖家声明的哈希,不会抓取内容并比对。比对要由你在验收期内完成。 |
| 卖家就是他们所说的人。 | 密钥是假名性的。没有身份核验,也还没有卖家质押与罚没机制。 |
| 资金是安全的。 | 在模拟通道上没有真实资金。在稳定币通道上,除非你使用托管合约,否则密钥由节点运营者掌握;而该合约既未经审计也未部署。见用托管购买。 |
| 仲裁方是中立的。 | 每笔订单只有一位仲裁方,默认是节点运营者。没有申诉或陪审机制。 |
| 没有什么能被隐藏。 | 节点可以拒绝转发事件。合并是取并集,所以诚实节点仍会就各自见过的事件收敛,但你无法证明某件事不存在。 |
| 自报的数字都经过核实。 | 来自网络的上架信息上的星标数和下载量,由卖家自行声明,没有被独立验证。 |
最近更新: