KinohTaGo/cc-cleanroom
GitHub: KinohTaGo/cc-cleanroom
为 Claude Code CLI 提供可复现的摘要固定隔离容器,附带运行时验证器以消除环境漂移对运行结果的影响。
Stars: 0 | Forks: 0
# cc-cleanroom
[](https://github.com/KinohTaGo/cc-cleanroom/actions/workflows/container-image-build.yml)
一个用于运行 Claude Code CLI 的可复现的干净环境容器(clean-room container),因此一次运行的结果取决于你选择的输入,而不是运行它的机器。
```
docker pull ghcr.io/kinohtago/cc-cleanroom:latest
docker run --rm ghcr.io/kinohtago/cc-cleanroom:latest verify_clean_room.sh
```
## 为什么
在开发者笔记本电脑上运行的 CLI 会继承一个庞大且不可见的环境:全局的
`CLAUDE.md`、已安装的插件、会话钩子(hooks)、内存目录、shell 别名,以及
上次自动更新的任何 CLI 版本。其中任何一个都可能改变结果。
这使得两个常见任务变得不可靠:
- **比较两次运行。** 如果运行 A 和运行 B 是在不同的机器上执行的——或者在同一台
机器上相隔一周——输出的差异不能说明任何问题。
- **以后复现一次运行。** 如果产生它的环境不再存在,“它在五月的行为是这样的”这种说法就是无法证伪的。
这个镜像移除了这个表面因素,并固定(pin)了剩余的内容。
## 固定了什么,以及如何固定
| 层级 | 机制 |
|---|---|
| 基础镜像 | 通过**清单列表摘要**(manifest-list digest)而非标签固定的 `node:20-slim`。多架构:相同的摘要可在 `linux/amd64` 和 `linux/arm64` 上解析。 |
| CLI | 精确版本(`@anthropic-ai/claude-code@X.Y.Z`)。没有 semver 范围。被撤回的版本会导致构建失败,而不是静默解析为其他内容。 |
| HOME | `/cleanroom`,空的。没有 `CLAUDE.md`,没有 `plugins/`,没有 `hooks/`,没有内存目录。 |
| 工具链 | 在构建时写入 `/etc/cleanroom-*` 的 `node`/`npm` 版本,以便运行中的容器可以报告它是什么。 |
通过标签固定会违背初衷——`node:20-slim` 是一个移动的目标。摘要被捕获在 `FROM` 行中,并在 OCI 标签中重复出现。
## `verify_clean_room.sh`
固定构建是不够的,因为容器启动时可以带有挂载(mounts)和
环境,从而重新引入镜像确切移除的内容。验证器被内置到
镜像中,并在**运行时**断言干净环境属性:
```
docker run --rm ghcr.io/kinohtago/cc-cleanroom:latest verify_clean_room.sh
```
发生漂移的容器会大声报错,而不是产生一个看起来没问题的结果。
这是很容易被跳过的部分,也是使工件值得信赖的关键部分。
## 身份验证
`setup-auth.sh` 执行一次交互式登录,并将结果持久化到一个命名的
Docker volume 中:
```
./setup-auth.sh # login, store credentials in the auth volume
./setup-auth.sh --force # wipe and re-login (e.g. after token expiry)
```
**凭证被写入 volume,而不是镜像中。** 发布的镜像
不包含任何 token——你可以自己在镜像配置上验证这一点:
```
docker buildx imagetools inspect ghcr.io/kinohtago/cc-cleanroom:latest --raw
```
后续运行会挂载该 volume:
```
docker run -it --rm -v claude-cleanroom-auth:/cleanroom/.config \
ghcr.io/kinohtago/cc-cleanroom:latest claude --version
```
## 构建和发布
`.github/workflows/container-image-build.yml` 构建并推送到 GHCR。
| 触发器 | 生成的标签 |
|---|---|
| 推送到 `main` | `sha-`, `main` |
| 推送标签 `v*` | ``, ``, `latest` |
| `workflow_dispatch` | 你提供的 `extra_tag`(永远不会是 `latest`) |
`latest` 仅在 `v*` 标签上移动,因此 `main` 可以随意变动,而不会改变其他人
拉取的内容。
多架构(`linux/amd64` + `linux/arm64`),并启用了**来源证明(provenance attestations)和 SBOM**——
可复现工件的意义在于第三方可以对其进行审计,而这需要的不仅仅是一个摘要。
### 关于工作流的 shell 处理的说明
每个 `${{ }}` 扩展都位于 action 的 `with:` 或 `env:` 块中。没有
内联到 `run:` 主体中;在 `run:` 块内,外部值作为 `$ENVVAR` 读取。
这是故意的——将 `${{ github.event.* }}` 内联到 shell 命令中是标准的
GitHub Actions 脚本注入向量,而在人们拉取的容器构建工作流中存在这种问题是极不妥的。
## 重新固定
```
docker buildx imagetools inspect node:20-slim # new index digest
npm view @anthropic-ai/claude-code version # new CLI version
```
更新 `FROM` 摘要和 `CLAUDE_CLI_VERSION`,然后在**新**标签下发布。保留
旧镜像:能够重新运行前一个镜像是进行固定的原因。
当前 CLI 的固定版本故意不是最新版本;Dockerfile 记录了原因。
## 适用范围
- 在 macOS (Apple Silicon) 和 `linux/amd64` 上构建和使用。
- 该镜像隔离了*环境*。它不会使模型输出具有确定性——
这里没有任何东西固定模型行为,也不应将其描述为能做到这一点。
- 不隶属于 Anthropic,也未得到其认可。
## 许可证
MIT
标签:Claude Code, Cutter, Docker容器, Hakrawler, MITM代理, SOC Prime, 可复现性, 开发工具, 攻击面发现, 环境一致性, 环境隔离, 请求拦截, 验证脚本