augbastos/scpe

GitHub: augbastos/scpe

SCPE 是一个开放协议,通过可离线验证的签名信封为代码变更和各类数字制品提供不可篡改的来源证明与 AI 使用披露。

Stars: 1 | Forks: 0

SCPE

SCPE

明确是谁签署了贡献——以及他们声明使用了哪种 AI。
单一信封。多种规范。
合并代码,而非主张。

CI 3 impls spec python license status

augbastos.github.io/scpe —— 一页纸了解它是什么

一个开放协议,旨在解决如今的 pull request 无法回答的两个问题:**谁签署了此项变更**,以及**他们就 AI 的使用声明了什么**——并附带证明,证实 diff 与他们签署的内容逐字节一致。无需 SCPE 服务器,无需新账号,无需新密钥:贡献者使用其 git 主机上已有的 SSH 密钥进行签名,而所有者使用 `ssh-keygen` 和 `git` 重新推导一切。 关于 AI 使用的披露是**已签署的**,而不是填入表单中的。这就是根本区别。签名并不能使主张变为真实——它使其变得*可归责*且*防篡改*:与特定身份及确切的 diff 绑定,因此事后无法编辑或被悄悄附加到不同的代码上。某人是否如实说明了他们使用的工具依然属于人类判断;而 SCPE 确保了该主张、作者及变更这三者不可分割。 当收到来自你不认识的人(个人,或日益增多的 AI agent)的 PR 时,如今的信任建立在用户名、平台以及肉眼阅读 diff 的基础之上。SCPE 将前两者替换为所有者可在本地校验的内容,并将第三项留在其原本属于的位置。由于不存在 SCPE 服务器,因此没有任何需要信任的对象,也没有任何可能被关停的服务。 **SCPE 标准化的是证据,而非内容。** 它绝不会声称某个产物是优质的、真实的或安全的——它标准化的是可验证的证据(谁生成了它、它是未被篡改的,以及任何已签署的证明)如何与带有哈希的产物一同传输,并支持离线校验。 **一个核心,多种规范。** *SCPE Core*——即信封、身份和验证——被所有领域共享。每个 *SCPE Specification* 仅在其基础上添加该领域的惯例;`profile` 标签会被展示,但绝不会改变验证决策。 | 规范 | 适用对象 | 封装内容 | 示例 | |---|---|---|---| | **SCPE-C** | 代码 | diff (`code-change`) | pull request | | **SCPE-I** | 图像 | 文件字节 (`artifact`) | `.png`, `.jpg` | | **SCPE-V** | 视频 | 文件字节 | `.mp4`, `.mov` | | **SCPE-A** | 音频 | 文件字节 | `.wav`, `.mp3` | | **SCPE-M** | 模型 | 文件字节 | `.safetensors`, `.gguf` | | **SCPE-DATA** | 数据集 | 文件字节 | `.csv`, `.parquet` | | **SCPE-D** | 文档 | 文件字节 | `.pdf` | | **SCPE-AR** | 任何产物 | 文件字节 | 任何文件 | 身份是一个 `(provider, subject)` 对,会根据来自固定主机表(`github`、`gitlab`、`codeberg`)的密钥或根据密钥文件进行校验——该文件可以是验证器所有者提供的,也可以是打包在提交内容中的。清单绝不会携带主机名,因此贡献无法将验证器引导至攻击者的主机;它*可以*附带自己的密钥,这也是为什么验证器会将提供答案的锚点报告为 `key_source` 的原因。 **开放,并注定保持如此。** SCPE 是——并且将永远是——开源的:规范和所有参考实现均可免费阅读、实现和分叉。我们的目标是打造一个单一、开放且*通用*的产物溯源标准,而一个标准只有在任何人无需许可即可实现的情况下才能成为通用标准。因此,在这里,开源不是一种许可协议的选择;而是其全部意义所在。 ## SCPE 是什么 —— 以及不是什么 | **SCPE 是** | **SCPE 不是** | |---|---| | 一种用于贡献或产物的轻量级、可传输、可离线校验的**证据格式**——包括*谁*生成了它、它是*未被篡改*的证明,以及任何*已签署的证明*——具有单一核心和针对各领域的轻量级配置。 | 代码审查器、恶意软件扫描器、产物注册表、CI/CD 安全系统、合规框架或托管服务。它不评判产物是否*优质*或*安全*——仅验证证据是否成立。 | 关于 SCPE 如何与代码审查、构建溯源和归属记录相关联,请参阅 [docs/comparison.md](docs/comparison.md)。 ## 此仓库包含的内容 一个协议,以及证明它是一个协议所需的最小组件集合。这里没有任何东西用于编写、审查或生成代码——SCPE 从不关注变更的*作用*,只关注是谁签署了它以及它是否仍然匹配。 | | 它是什么 | |---|---| | [`spec/`](spec/) | 规范性协议:[SPEC.md](spec/SPEC.md)、威胁模型、清单 schema,以及作为一致性契约的 18 个测试向量。 | | [`reference/standalone/verify_envelope.py`](reference/standalone/verify_envelope.py) | **验证器。** 一个仅依赖标准库的文件,不导入本仓库中的任何其他内容。 | | [`reference/producer.py`](reference/producer.py) | 生产器(`scpe-envelope pack` / `pack-artifact` / `attest` / `verify` / `submit`)——使用贡献者已拥有的密钥对信封进行签名。 | | [`impl/go/`](impl/go/), [`impl/rust/`](impl/rust/) | 验证器的两个独立移植版本,通过差异测试确保它们得出相同的判定结果。 | | [`action.yml`](action.yml) | 面向维护者的 GitHub Action。它在相同的格式下、自身的检出中运行上述验证器。 | | [`scpe/`](scpe/) | `scpe-protocol` 包:一个基于相同验证器的纯标准库 CLI,以及 Action 渲染的印章和可选的徽章。 | 该包是协议的*分发*,而不是它的第二个实现:`scpe verify` 是一个直通管道,其 JSON 输出和退出码与直接运行该单文件在字节上完全一致。只有一个信封格式、一种验证算法和一个判定结果——上述所有内容只是达成同一结果的不同方式。 ## 保障阶梯 在适合你项目的层级采用,以后无需更改格式即可升级。 | 层级 | 仓库的要求 | 贡献者的成本 | |---|---|---| | **L1 — 披露** | 存在 AI 使用披露(一个 `Assisted-by:` 尾部标记或 PR 模板复选框)。 | 零成本——这可能是你已经写过的策略,现在被强制执行了。 | | **L2 — 已签名的信封** | 一个有效的已签名 SCPE 信封:可验证的身份 + 未被篡改的 diff。 | 一个签名命令。 | | **L3 — 连署** *(路线图)* | 第三方(审查者或 agent 平台)进行连署。 | — | 较高级别包含较低级别。大多数已经要求进行 AI 披露的项目现在需要 **L1**;签名是*机制*,策略是*产品*——这与 SLSA 用于推销其层级的模式相同。详见 [docs/LEVELS.md](docs/LEVELS.md)。 ## 工作原理 1. **贡献者**(人类或 agent)将变更打包成一个已签名的*信封*:包含清单(目标仓库、基础提交、确切 diff 的 SHA-256、AI 使用披露以及可选的归属记录),并使用其 GitHub 个人资料中已有的 SSH 密钥(`ssh-keygen -Y sign -n scpe/0.1`)进行签名。无需新账号。 2. 它在一个**普通的 pull request** 内部传输——diff 位于分支中,大小约为 1–2 KB 的已签名证明则嵌入在 PR 正文中。合并后依然保持仓库历史记录的整洁。 3. **所有者端**自行重新推导一切,无需涉及任何 SCPE 服务器:从 PR 重新计算 diff 的 SHA-256 并进行比较,然后发布一个印章(或者,在 require 模式下,拒绝不可验证的 PR)。这里**只有一个验证器,而不是两个**——Action 从你锁定的标签 checkout 出的自身代码中,运行下文描述的同一个单文件验证器,因此不存在会产生偏差的第二个实现。不同运行之间唯一可能不同的是哪个密钥锚点进行了响应,并且结果始终会指明它: - **通过 Action 运行时**,贡献无法替换用于评判它的密钥。传输层仅携带 `manifest.json` 和 `manifest.sig`(SPEC §9),因此不存在可查找的内置密钥集,密钥实时来源于 `github.com/.keys`,状态为 `key_source: forge`。在气隙环境中运行的维护者可以改用手动提供的密钥文件——这会报告为 `flag`,并且印章会如实说明,因为“此仓库提供的密钥”与“GitHub 为该账户提供的密钥”是不同的声明。 - **直接运行时**,同一个文件就是一个通用工具,可在十分钟内完成审计:它会首先解析你传入的密钥文件,其次是输入中捆绑的任何 `keys` 文件,只有在两者均未响应时,才会向贡献者的主机发起一次 HTTPS GET 请求。它将响应来源报告为 `key_source`,因此离线一致性运行绝不会与 forge 校验混淆。 ## `verified` 证明了什么 —— 以及没有证明什么 **`verified` 结果的含义取决于验证器从何处获取密钥**——它会将此报告为 `key_source`。在 `forge` 状态下,它的确切含义是:*贡献者的 git 主机为该账户发布的密钥准确地签署了此变更和此披露,并且你正在查看的 diff 在规范化换行符后,与他们签署的内容逐字节匹配。* 在 `flag` 状态下,含义相同,只是用验证器所有者自己的密钥集代替了主机提供的密钥。在 `bundled` 状态下(即密钥由提交者自行打包在提交内容中携带),它仅仅意味着这些确切的字节是由随它们一起送达的密钥签署的,而与所指名的账户毫无关系。**如果使用者需要基于 forge 的身份验证,必须要求 `key_source == "forge"`**,或者自行提供密钥集并要求状态为 `"flag"`。 在任何锚点下,它都**不能**证明代码是安全或优质的(SCPE 不是代码审查),不能证明披露是诚实的(签名只能证明*是谁做出了主张*,不能证明该主张为真),并且在做出响应的密钥集遭到破坏或由攻击者选择的情况下,它也证明不了任何事情——因为该密钥集本身就是信任根。在依赖它之前,请阅读 [spec/THREAT_MODEL.md](spec/THREAT_MODEL.md):§2.1 列出了这三种锚点以及在每种锚点下判定结果的价值。 ## 面向维护者 —— 启用功能 添加一个工作流来验证每个 PR 并发布印章。设置 `require` 来拦截合并请求。 ``` # .github/workflows/scpe.yml — the step. Copy BOTH files from docs/workflows/ for the # fork-safe version: scpe.yml (verify) and its companion scpe-seal.yml (post the seal). - uses: augbastos/scpe@v0.2.1 with: level: "1" # 1 = disclosure lint · 2 = signed envelope required require: "true" # fail the check on anything not verifiable ``` 在协议版本达到 1.0 之前,请锁定确切的标签。这里的每一个标签都是一个**不可变的别名**——它指向同一个提交且永远不会被移动,因此修复会以新标签的形式出现,你需要通过修改锁定来采用它,而不是在你已写入的旧标签下进行静默更改。在实际操作中 `v0.2.1` 就是如此:`v0.2` 发布了一个会在未提供证明的 PR 上显示 `VERIFIED` 的印章横幅,以及一个 GitHub 拒绝注册的工作流模板;这两者都是通过发布新标签而不是移动旧标签来修复的。`v0.2` 系列也是第一个由 Action 本身验证 `scpe/0.1` 信封的系列;`v0.1.x` 验证的是另一种现已移除的格式,因此从中升级属于行为变更,而非补丁——相关步骤请参阅 [docs/MIGRATION.md](docs/MIGRATION.md)。 该 Action 使用了对分叉安全的双任务拆分,这两个任务存在于**两个文件**中:位于 `scpe.yml` 中的不可信任务(它会运行贡献者的代码)不包含任何敏感信息,只有位于 `scpe-seal.yml` 中的可信后续任务才会发布评论。使用两个文件是 GitHub 的限制,而非偏好——如果一个工作流在其自身的 `workflow_run` 触发器中引用了自身,将完全无法注册。这两个层级都不会在 runner 中安装任何东西——两者都直接从 Action 自身的 checkout 中运行仅依赖标准库的 Python,因此决定合并的字节就是你锁定的标签所对应的字节,而不是包索引当天提供的任何内容。 请使用 `fetch: 0` 进行检出:L2 级别会以 `git diff ...` 的方式重新计算 diff,而默认的浅层检出没有可供比较的基础提交。 它发布的印章携带的信息不仅仅是判定结果——还包括风险等级、文件/行数统计以及可选的测试运行结果。**这些是 Action 自身的报告层,并不是协议的一部分**:没有状态、没有 `verified`,`spec/` 中的任何内容都不依赖于它们。判定结果属于验证器;其余的只是一份报告。 ## 自行验证任何内容 参考验证器是一个仅依赖标准库的单文件——从头到尾阅读它,你就能确切知道印章的含义: ``` python reference/standalone/verify_envelope.py --keys # → [OK] verified (attestations: none) [keys: flag] # (or a precise reject status: tampered, signature-invalid, …) ``` `[keys: …]` 指明了响应的锚点——这里是 `flag`,因为通过 `--keys` 提供了密钥集。如果没有该标志,输入中位于 `manifest.json` 旁边的 `keys` 文件会以 `bundled` 的形式响应;只有在两者都不存在时,验证器才会从贡献者的主机获取数据,状态记为 `forge`。 18 个规范性的[测试向量](spec/test-vectors)构成了关于**状态**的一致性契约:如果一个实现能够产生它们预期的状态,就符合规范的状态行为。它们并没有固定所有的规范性要求——没有任何一个向量携带预期的 `key_source`,因此仅仅通过全部十八个测试并不能证明 `key_source` 的 MUST 要求得到了遵守;该项需要通过人工检查来验证。每个向量都自带 `keys` 文件,因此测试套件可以离线运行,并且没有任何一个向量会触及 `forge` 锚点。 **开销** | | 测量值 | |---|---| | PR 正文证明(manifest + sig,Base64 编码) | 1.1–1.5 KB | | 独立信封(3 个文件 / 27 行 PR,压缩后) | ~1.5 KB | | 验证实际耗时 (wall-time) | 冷启动 CLI ~210 毫秒 · 进程热加载下 ~39 毫秒 | `artifact` 主体会在 ~800 字节的固定开销基础上增加其 payload 的大小。这只是数量级的参考,基于单机测量——并非正式的基准测试套件。 ## 它的定位 - **不是代码审查。** Copilot / CodeRabbit 用于评判代码优劣。SCPE 则证明*是谁*以及*完整性*。 - **是对归属记录的补充,而非竞争。** [Agent Trace](https://github.com/cursor/agent-trace) 和 [git-ai](https://github.com/git-ai-project/git-ai) *记录*了是谁/什么编写了哪些代码行, 属于自我报告;SCPE 将该记录携带在已签名的清单内,使其变得可验证。 - **不同于构建溯源层面的内容。** Sigstore / SLSA / in-toto 用于证明*产物和构建*;而 SCPE 是在 pull request 的边界上证明一项*贡献*。 - **直接的前期技术:** `patatt` + `b4` ([kernel.org](https://github.com/mricon/patatt)) 已经在 Linux 内核的邮件列表上运行这种确切的模式长达数年——使用平台发布的密钥自行对补丁进行签名,独立验证,无需 CA,无需服务器。SCPE 将同样的模式应用到了 GitHub 的 pull request 边界上。 ## 状态 **v0.2.1 —— 早期阶段。** 这是一个规范加上参考实现(一个单文件验证器、一个生产器和一个面向维护者的 Action)。完整的测试套件——包括 100 个 PR 的压力测试和本地端到端测试——会在每次推送时运行;上方的 CI 徽章就是其最新的实时结果。另外两个独立的验证器(用 Go 和 Rust 编写)在所有 18 个规范性向量上得出了与 Python 参考实现相同的判定结果,并且一项差异测试通过所有三个验证器运行了修改后的清单,确认它们之间绝无分歧。这种三方一致性涵盖了这些测试向量所涉及的路径——即针对提供的密钥文件进行离线校验的目录输入,对应于 `flag` 锚点;没有向量涉及到网络获取功能。Rust 移植版仅能做到这一步:它不支持网络密钥获取(必须使用 `--keys`),并且无法解析 Python 和 Go 均能接受的 zip-envelope 或 PR 正文内的证明输入格式。目前**尚无外部采用**。它不是一个托管服务,并且永远也不会是。 **`v0.1.x` 已过时:其签名信封的路径验证的是一种未包含在此规范中的格式。** 这些标签处的 Action 过去会检查打包在包内并在 `0.2.0` 中被删除的另一种信封格式。仍然锁定在那里的工作流并不会报错失败——它会继续为这里已不产生任何内容的格式发布印章,而且一旦 `0.2.0` 进入 PyPI,它就会发生中断,因为该 Action 是在运行时从包索引中解析其验证器,而不是从自身的 checkout 中解析。[CHANGELOG.md](CHANGELOG.md) 记录了变更内容;[docs/MIGRATION.md](docs/MIGRATION.md) 提供了应对措施。 ## 文档 - [spec/SPEC.md](spec/SPEC.md) —— 协议 (`scpe/0.1`) - [spec/THREAT_MODEL.md](spec/THREAT_MODEL.md) —— 它能防御什么以及不能防御什么 - [spec/FAQ.md](spec/FAQ.md) —— 为什么用 SSH,为什么用 PR 正文,与 Agent Trace / Sigstore / patatt 的关系 - [docs/LEVELS.md](docs/LEVELS.md) —— L1 / L2 / L3 保障阶梯 - [CHANGELOG.md](CHANGELOG.md) —— 每个版本的变更内容,以及它移动了哪个版本轴 - [docs/MIGRATION.md](docs/MIGRATION.md) —— 如何升级 `v0.1.x` 的锁定,以及为什么它不会发出警告 ## 许可协议 代码许可为 [Apache-2.0](LICENSE);规范(`spec/` 下的所有内容)许可为 [CC-BY-4.0](LICENSE-SPEC)。© 2026 Augusto Bastos。
标签:内存分配, 可视化界面, 日志审计, 网络安全研究, 逆向工具