# 隐私与数据处理

本地构建不会发送任何内容。云端构建在内存中完成混淆，不保留任何东西，除了你要求存储的 Protection Map，这项功能可以关闭。遥测默认开启，仅在构建报告错误级别诊断时上报，干净的构建从不上报，并可通过一个环境变量关闭。

Source: https://www.afterpack.dev/zh/docs/privacy

AfterPack 的混淆产品按以下方式处理你的代码和账户数据。本网站的 Cookie 与分析数据在[隐私政策](https://www.afterpack.dev/zh/privacy)中单独说明。

> **简要说明**
>
> [本地（Free）构建](https://www.afterpack.dev/zh/docs/deployment-modes)从不会将你的源代码发送到任何地方。云端（Pro）构建在内存中完成混淆，并在请求结束时丢弃源代码。有一项产物会被存储：供[控制台](https://www.afterpack.dev/zh/docs/dashboard)使用的 **[Protection Map](https://www.afterpack.dev/zh/docs/protection-map)**。它以[工作区](https://www.afterpack.dev/zh/docs/dashboard/workspaces)为作用域、会过期，并且你可以按项目关闭它。遥测默认开启，仅在构建报告错误级别诊断（被拒绝或部分完成的构建）时上报，干净的构建从不上报，并可通过 [`AFTERPACK_telemetry_enabled=false`](https://www.afterpack.dev/zh/docs/config#telemetry-enabled) 关闭。DPA 与完整的分包处理方名单可应要求提供。

## 本地构建不会发送任何内容

[本地构建](https://www.afterpack.dev/zh/docs/deployment-modes)以原生二进制程序的形式在你的机器上运行 AfterPack。没有任何东西会离开你的文件系统，因此下文的云端部分不适用于它。本地构建唯一发出的网络请求是下文所述的遥测上报，它只在构建报告错误级别诊断（被拒绝或部分完成的构建）时触发，不携带源代码，并且可以关闭。

## 云端构建在内存中完成混淆，不保留任何东西

当一次 [Pro 构建](https://www.afterpack.dev/zh/docs/pro)将源代码发送到 `api.afterpack.dev` 进行混淆时，它**仅在内存中**处理，仅持续那一次请求的时长，随后即被丢弃：

- **请求运行在一个沙盒化的 Cloudflare Worker 隔离环境中**，该环境会在请求边界处被销毁。响应结束后，没有任何东西会留存下来。
- **日志永远不携带源代码。** 日志行携带构建 ID、文件数量、字节总数、诊断代码，以及失败文件的路径。它们永远不携带文件内容、标识符名称或常量值。

无论你是直接调用 API、通过设置了 [`AFTERPACK_key`](https://www.afterpack.dev/zh/docs/config#key) 的 [CLI](https://www.afterpack.dev/zh/docs/cli)，还是通过[框架插件](https://www.afterpack.dev/zh/docs/frameworks)，情况都完全相同。它们都使用[云端 API 参考](https://www.afterpack.dev/zh/docs/cloud-api)中描述的协议。

有一项产物会被存储：下文所述的 Protection Map，它在混淆完成后写入。你上传用于混淆的源代码，在请求结束时依然会被清除。

### 日志记录什么，不记录什么

| 已记录 | 从不记录 |
| --- | --- |
| 时间戳（收到请求、响应关闭） | 文件内容 |
| HTTP 状态码 | 标识符名称 |
| 构建 ID、文件数量、输入/输出字节总数 | 常量值，包括恰好出现在源代码中的密钥 |
| [诊断代码](https://www.afterpack.dev/zh/docs/diagnostics)（`DIAG_*`） | 遥测中的诊断*消息文本* |
| 客户端 IP，用于限流 | Source map 的 `sources` / `sourcesContent` |
| 工作区层级，用于计量 | 任何可能暴露你业务逻辑的内容 |

聚合指标（每日请求数、按工作区计量的字节数、构建耗时）以计数器形式保留，不包含任何代码。

## 遥测默认开启，仅报告构建的错误级别诊断

客户端可以上报匿名的构建诊断信息，以便发现并修复引擎缺陷。除非你关闭它，否则它默认开启。一次干净的构建没有什么可报告的：

- **只有发出 `error` 或 `critical` 诊断的构建才会发送任何内容。** 一次干净的构建什么都不会发送：没有周期性心跳，没有阶段耗时，也没有一次正常构建的使用统计。
- **通过 [`AFTERPACK_telemetry_enabled=false`](https://www.afterpack.dev/zh/docs/config#telemetry-enabled)** 关闭它，在 CLI 上使用 [`--telemetry.enabled=false`](https://www.afterpack.dev/zh/docs/config#telemetry-enabled)，或在你的集成配置中使用 [`telemetry: { enabled: false }`](https://www.afterpack.dev/zh/docs/config#telemetry-enabled)。这就是全部的开关。
- **它在 CI 中的行为完全相同。** 没有任何 TTY 或 CI 检测会改变默认值或跳过上报；负载只是记录 `ci: true`，读取自你的流水线本就设置的同一个 `CI` 变量。
- **绝不询问。** 安装后的第一次构建会打印一条一次性提示（收集了什么、没有收集什么、如何关闭），然后继续执行。它从不会阻塞等待回答。状态（一个每 30 天轮换一次的随机安装 ID，以及该提示是否已打印过）保存在 `~/.afterpack/telemetry.json` 中。
- **测试运行豁免。** `NODE_ENV=test` 和 Vitest 自身的 `VITEST` 标记会禁用遥测。

### 当构建报告错误级别诊断时，会收集什么

- 失败诊断的**代码**、严重级别和字节偏移范围，加上按诊断类型选取的一小组带类型字段，但绝不包含其自由文本消息
- 引擎、客户端和 Node 的**版本**、操作系统/架构，以及检测到的框架
- 构建是否运行在 **CI** 中
- 复杂度级别：一个具名的[预设](https://www.afterpack.dev/zh/docs/presets)，或者在使用原始 [`complexity`](https://www.afterpack.dev/zh/docs/config#complexity) 时为 `custom`
- 文件数量和构建耗时，以**分档**（`5-20`、`1-5s`）而非精确数字呈现；精确数字会成为某个项目的指纹
- 一个每 30 天轮换一次的随机**安装 ID**，绝不从你的机器或项目派生

### 无论是否遥测，都绝不收集的内容

- 源代码或文件内容
- 遥测中的文件名和文件路径。如上所述，云端构建的服务器端日志会携带一个路径，即批次中失败的那个文件
- 诊断的**消息文本**，永远不会；只有其稳定的 `code`
- API 密钥或凭据
- 常量值
- 字符串编码方式的选择
- 除你账户本身持有的信息之外，任何能识别用户身份的信息

遥测的结构是固定的。

## 云端构建的 Protection Map 会被存储，你可以关闭它

[Protection Map](https://www.afterpack.dev/zh/docs/protection-map) *就是*你的原始源代码，按其中每个词元经历了多少变换着色。在云端构建中，AfterPack 会在完成混淆的服务器上生成并保留它，因此你可以从[控制台](https://www.afterpack.dev/zh/docs/dashboard/builds)打开任意一次最近构建的地图。

> **你的源代码在此处静态留存的唯一位置**
>
> 存储 Protection Map 默认**按项目开启**，已存储的地图可被**拥有该项目的[工作区](https://www.afterpack.dev/zh/docs/dashboard/workspaces)的每一位成员**读取。如果你的源代码不应在整个工作区内可读，工作区管理员可以在该项目的设置中[关闭这项设置](https://www.afterpack.dev/zh/docs/protection-map#turning-it-off)。

- **存储的内容：** 每次构建的地图数据——你的原始源代码，加上着色背后按词元计算的指标，压缩并 gzip 后存入 Cloudflare R2，静态加密存储，再加上一行用于控制台的汇总数字。你的混淆输出不会被存储。
- **作用域限定在一个项目**，即该次构建的[密钥](https://www.afterpack.dev/zh/docs/dashboard/api-keys)所绑定的那个项目。
- **只有该工作区的成员才能读取。** 不公开、不被索引，也没有可分享的链接。
- **保留期：一年，以及每个项目最近 500 次构建，以先到者为准。** 一个按小时运行的任务会删除超出任一界限的内容。对于频繁构建的项目，次数上限会先触发。它限制了你的源代码在任一时刻存在多少个副本。
- **地图可能以部分形式被存储。** 当一次构建耗时过长，捕获会在混淆结束之前停止。存储的记录会说明哪些代码块被跳过，以及原因。
- **关闭它**会让该项目的新构建不再存储任何内容。已存储的地图会按上述保留期自然过期，而不会被立即删除。本地构建不受影响：本地构建[写入 `.afterpack/`](https://www.afterpack.dev/zh/docs/cli#files) 的 Protection Map 永远不会离开你的机器。
- **关闭后仍会记录的内容：** 构建的各项数字（覆盖率、膨胀率、复杂度、耗时），这样项目的[控制台图表](https://www.afterpack.dev/zh/docs/dashboard/builds)才能继续正常工作。这些是从构建中得出的聚合数据，不包含任何代码，其中也没有任何内容可以被还原为代码。

## 分包处理方

一次云端构建会涉及以下处理方。源代码在被混淆期间**仅在内存中**经过 Cloudflare；Cloudflare 也是唯一会静态持有源代码的一方，即上文所述的 Protection Map：

| 分包处理方 | 角色 |
| --- | --- |
| **Cloudflare** | Workers / R2 / D1：边缘计算、路由、Protection Map 存储，以及账户数据库 |
| **Creem** | 仅用于[账单](https://www.afterpack.dev/zh/docs/dashboard/usage)，作为记录商户；从不接收源代码 |
| **Email / auth providers** | 用于账户和密钥生命周期事件的魔法链接与事务性邮件；从不接收源代码 |
| **Anthropic**（通过 Cloudflare AI Gateway） | 为站内助手（"Ask AfterPack"）提供支持；会接收你输入给它的内容，以及它为回答而读取的文档页面，但绝不会接收你的源代码 |

## 数据驻留

上面的内容回答了源代码是否被存储的问题。数据驻留是一个独立的问题：请求处理期间数据在哪里被处理，以及已存储的地图存放在哪里。

两者都不会固定在某个区域。云端构建会被 Cloudflare 路由到附近的区域，在故障转移或负载较高时可能落在其他区域。没有按请求固定的区域，R2 存储也不会被锁定在某个区域。**如果你需要确定的数据驻留保证，请使用[本地构建](https://www.afterpack.dev/zh/docs/deployment-modes)。** AfterPack 完全运行在你自己的基础设施上，因此不存在数据驻留问题。

## DPA 与合规

数据处理协议（DPA）可应要求提供。你签署的 DPA 是具有法律效力、可执行的合同，并指回上文所述的分包处理方名单和数据驻留行为。

## 下一步

- [云端 API 参考](https://www.afterpack.dev/zh/docs/cloud-api)：这些保证所适用的传输协议。
- [部署模式](https://www.afterpack.dev/zh/docs/deployment-modes)：本地与云端的对比，以及各自发送的内容。
- [Protection Map](https://www.afterpack.dev/zh/docs/protection-map)：已存储的地图展示了什么，以及按项目开关的位置。
- [Pro](https://www.afterpack.dev/zh/docs/pro)：在云端运行到底能换来什么。
- [工作区与项目](https://www.afterpack.dev/zh/docs/dashboard/workspaces)：谁能读取什么，以及密钥的作用域如何界定。
- [控制台](https://www.afterpack.dev/zh/docs/dashboard)：已存储的地图及其构建记录展示在哪里。
