Skip to content

ADR-006 · 公开仓库脱敏策略 · 4 层防护 · 准备 GitHub repo 公开化

字段
状态accepted · 2026-05-28
日期2026-05-28
决策者CTO + 项目主理
承接ADR-001 (multi-anchor) · ADR-005 (hybrid deployment)
相关scripts/redact_check.py · scripts/check_public_safe.py · .gitignore · .github/workflows/check-public-safe.yml

1. Context

1.1 · 起源 · 2 轮脱敏的累积代价

ADR-005 hybrid Phase 1 落地后, 项目主理 问 "检查 github 仓库是不是经过脱敏". 全仓审计 (107 处硬规则命中, 50 文件) 暴露了 4 个层次的脱敏 paradox:

层次命中文件类型是否必要含真名
公网 (CF Pages serve)www/*不允许 (已清, 24/24 PASS)
仓库 HEAD (collaborator 可见)README/CLAUDE/Makefile/skills/templates不必要, 历史残留
仓库 history当时被 git tracked 的 docs/internal/* + 早期 docs/*是 (反映项目演进)
脱敏机制本身scripts/redact_check.py + tests/fixture必须 (拦真名要先写真名)

1.2 · 用户决策

询问"4 选 1":

  • A · PRIVATE 不动: 0 工作量
  • B · PRIVATE + 顺手清 C-cat: ~30 min
  • C · 准备变 PUBLIC · 完整脱敏: ~2-3h ← user 选
  • D · 暂不决定

并选了 docs/internal/ 处置 = A (gitignore 移出 git, 仅本地保留), git history = Y (用 git filter-repo 重写 history).

2. Decision

采用 "4 层防护" 公开仓库脱敏架构:

┌─ Layer 1 · 公网部署面 (www/) ────────────────────────────┐
│  CF Pages serve · check_public_safe preflight + GHA CI   │
│  invariant: 0 硬规则命中 (真名/内部 URL)                  │
└──────────────────────────────────────────────────────────┘
┌─ Layer 2 · 仓库 HEAD (collaborator + 未来 public) ───────┐
│  README/CLAUDE/Makefile/skills/templates/configs         │
│  invariant: 0 硬规则命中, 软规则 placeholder 化 (.example) │
└──────────────────────────────────────────────────────────┘
┌─ Layer 3 · 仓库 history (`git log -p` 可见) ─────────────┐
│  git filter-repo 重写, docs/internal + 早期 docs/* 全清 │
│  invariant: history 任何 commit 不含 docs/internal/ 路径 │
└──────────────────────────────────────────────────────────┘
┌─ Layer 4 · 脱敏机制本身 (justified self-reference) ──────┐
│  redact_check.py + tests/fixture + feishu_sync_filter.py │
│  invariant: 真名以 SENSITIVE_PATTERNS 形态存在, 不是数据  │
└──────────────────────────────────────────────────────────┘

3. Consequences

3.1 · 优势

维度改造前改造后
HEAD 真名 hits1070 (除 justified self-reference)
docs/internal/ tracked是 (12 文件 143 hits)否 (gitignore + git rm --cached, 本地保留)
config/wiki_aliases.yaml tracked是 (含 anchor real-name 别名)否 (gitignored, ship .yaml.example placeholder 版)
config/feishu_sync_filter_rules.yaml.example含真名 triggerplaceholder 化 (<NAME_VARIANT_*>)
git history 含 docs/internal是 (54 commits)否 (filter-repo 重写, 76 → 60 commits)

3.2 · 接受的 tradeoff

  • filter-repo 是 destructive: 所有 commit SHA 改变, 所有现有 clone 失效, collaborator 必须重 clone. 备份 tag pre-filter-repo-backup-<ts> 保留 24h 应急
  • docs/internal/ 仅本地存在: dev-log/dev-plan/PRD/audit 历史价值仅项目主理/collaborator 本机可访问. 跨设备同步需另起私仓 (V1 考虑)
  • wiki_aliases.yaml 也 gitignored: 项目重新搭建时, alias 映射必须本地手工补全 (从 .example placeholder 替换)

3.3 · 不变的承诺

  • ADR-001 multi-anchor (anchors/<slug>/) 不变
  • ADR-003 三形态 D1/D2/D3 不变
  • ADR-004 v1 HTTP API 不变
  • ADR-005 hybrid deployment 不变
  • check_public_safe + redact_check 行为不变 (allowlist 仍含 docs/, 但 docs/internal/ 现在 gitignored 所以非问题)
  • 测试 fixture 与 filter rule 仍含真名 (justified self-reference, Layer 4)

3.4 · Invariant impact (新版 ADR 模板要求段)

本变更让以下 invariant 失效:

  • "docs/internal/ 在 git 内, 仅 collaborator 可见" → 失效. docs/internal/ 不再 tracked, 仅本机
  • "git history 完整保留所有 commit" → 失效. filter-repo 重写 history, 早期 16 commits 合并/删除, 所有 commit SHA 变了
  • "config/wiki_aliases.yaml 是 git tracked 配置" → 失效. 移到 .example placeholder 模式

本变更引入以下新 invariant:

  • inv-1: HEAD 任何 non-justified-self-reference 文件 0 硬规则命中 (由 P1 + GHA check-public-safe 守住)
  • inv-2: git history 任何 commit 不含 docs/internal/ docs/dev-log/ docs/dev-plan* 等 13 路径 (由 P2 filter-repo 一次清, 此后由 .gitignore 守住)
  • inv-3: justified self-reference 文件 (10 个 SKIP_FILES) 含真名必须有 docstring/注释说明 "此处真名为 redact rule pattern / test fixture, 不是泄漏" (人工 review)

4. Alternatives Considered

方案为什么没选
A · PRIVATE 不动用户明确想去 public, 早晚做
B · PRIVATE + 仅清 HEADhistory 仍有 docs/internal/ 真名, public 后任何人 git log -p 可见
C · 选择性 sed history 替换复杂 (要 exclude SKIP_FILES from sed), 容易破坏 redact_check 等 pattern 文件
D · BFG 替代 filter-repoBFG 是老工具, 官方推荐 filter-repo · filter-repo 更精准, 支持 paths-from-file
E · 创建 boss-vault-internal 私仓 + submodule跨设备同步好, 但 V0 期人手少, 复杂度过高 · 留 V1

5. Migration Plan

5.1 · 已执行 (2026-05-28, 本 ADR 触发)

Phase步骤Commit
P1 机械清理27 文件 (C+E cat) 真名变体 → 中性 / <name-variant-*> / placeholderda73a37 (本次 force push 后 SHA 变, 见 git log)
P1.5 gitignoredocs/internal/ + config/wiki_aliases.yaml 加 .gitignore + git rm --cached同上
P2 history rewritegit filter-repo --paths-from-file --invert-paths, 移 13 路径 from history(rewrite 后 HEAD = 2be53f1 类)
P2 force push备份 tag + force push origin main

5.2 · Public 切换前 checklist

在把 GitHub repo 从 PRIVATE 切到 PUBLIC 前, 跑这 6 项:

  • [ ] make check-public-safe PASS (www/ 0 硬规则)
  • [ ] make pre-public-push PASS (全仓 5 步检查 — Makefile 已有)
  • [ ] make lint PASS (skill_lint + redact_check)
  • [ ] make test-unit PASS (确认机械替换没破坏机制)
  • [ ] 人工 review HEAD 任何含真名文件都在 SKIP_FILES (即 inv-3)
  • [ ] 通知现有 collaborator: history 已重写, 需 rm -rf .git && git clone 重新拿

5.3 · 切 PUBLIC 后监控

  • GHA check-public-safe 每次 PR 跑
  • pre-commit redact_check 每次 commit 跑
  • 月度人工: python3 scripts/check_public_safe.py --root . 全仓硬规则扫一次 (而非默认 www/)

6. 关键反共识立场

"PRIVATE 仓库的真名不算暴露" — 错.

PRIVATE 仓库仍有:

  1. collaborator 流动风险: 离职/换岗的 collaborator clone 后可永久持有数据
  2. 意外公开风险: github settings 一键改 PUBLIC, 没有 throttle/审计 (实际事故案例多)
  3. history travel risk: 即便 HEAD 干净, git log -p 可挖出几个月前的真名

正确立场: 任何 git tracked 的真名都是泄漏风险, 区别只是"对谁泄漏" + "什么时候 trigger". 准备 public 是把这个 risk 真正归 0 的契机.

"filter-repo 重写 history 太重" — 错.

filter-repo:

  • 30 秒跑完
  • 不可逆只对没备份的 case 危险 — 我们已经 git tag pre-filter-repo-backup-<ts> + push 到 origin
  • collaborator re-clone 一次的 cost 远低于 真名 history 永久暴露的 cost

7. 何时回看本 ADR

  • 切 PUBLIC 后 1 周: §5.3 监控指标是否健康
  • 任何意外回归 (commit 含真名通过 CI): trigger ADR-006 修订, 收紧 invariants
  • 引入新 collaborator: 把 §5.2 checklist 加进 onboarding

ADR-006 · accepted · 2026-05-28 · ADR 体系第 6 个 · 准备 GitHub repo 公开化 · 4 层防护 · 60 commits rewritten

判断力工程化 · Judgement, Engineered · 主站 · GitHub