# ADR-004: Token 不入文件,走 wincred 缓存

**状态**: ✅ 已采纳
**日期**: 2026-06-14
**决策者**: 宋献 + Claude 评审
**关联**: [[风险跟踪表]] 第十二节 12.6 / 推送约定

---

## 1. 背景

之前 Gitea 推送 token 直接嵌入 `.git/config` 的 `origin.url`:
```
url = https://ae236991c3d5...@ds923plus.tail58d872.ts.net/...
```

**风险**:
- ❌ token 明文落盘
- ❌ token 失效后难更新(URL 整体换)
- ❌ 误 `git add .git/` 可能入仓(虽然 .git/config 本身不入仓,但 .git/ 目录其他文件可能)
- ❌ auto-classifier 拒绝重写 URL(防误操作)

**事故**: 2026-06-14 workbuddy-claude token 失效后,`origin.url` 残留死凭据。

## 2. 决策

**`.git/config` 的 `origin.url` 只写用户名,token 走 git credential helper(wincred)缓存**。

## 3. 实现

### 3.1 配 remote URL(无 token)

```bash
git remote add origin https://simon@ds923plus.tail58d872.ts.net/simon/wecom_it_smart_desk.git
# 或修复现有:
git remote set-url origin https://simon@ds923plus.tail58d872.ts.net/simon/wecom_it_smart_desk.git
```

### 3.2 配 credential helper

`.git/config`:
```ini
[credential]
    helper = manager  # Windows = wincred / Linux = git-credential-manager
```

### 3.3 首次推(输一次 token)

```bash
git push -u origin main
# 弹窗 → username 留空,password = token
# wincred 自动缓存
```

### 3.4 换 token(必走)

```bash
# 清旧缓存
printf "protocol=https\nhost=ds923plus.tail58d872.ts.net\nusername=simon\n" | git credential reject

# 存新缓存(一次性,token 在 heredoc 不入文件)
printf "protocol=https\nhost=ds923plus.tail58d872.ts.net\nusername=simon\npassword=NEW_TOKEN\n" | git credential approve

# 验证
git push origin main
# 应不弹窗
```

## 4. workbuddy 推送同理

`.workbuddy/config.json` 是 workbuddy 自己的凭据存储(类比 .git/config),**入仓** ❌。

**正确做法**:
- `.workbuddy/config.json` 写用户名/URL/其他配置,**不写 token**
- workbuddy 启动时读 `gitea.token` 字段(从环境变量 / 启动参数传入)
- 或者 workbuddy 自己也用 git credential helper

**已加 .gitignore**:
```gitignore
.workbuddy/config.json
.workbuddy/config.local.json
.workbuddy/*.token
.workbuddy/credentials*
.workbuddy/.env*
```

## 5. 优势

- ✅ token 不入文件(只入 wincred 系统密钥环)
- ✅ 换 token 简单(`credential reject` + `approve`)
- ✅ 不会误入仓
- ✅ auto-classifier 不拒绝(无 token 写文件)

## 6. 风险与缓解

| 风险 | 缓解 |
|---|---|
| wincred 缓存被读(本机攻击) | 操作系统级防护 + 强密码 + BitLocker |
| 跨设备不能用 wincred | Linux 用 `git-credential-manager`,Mac 用 `git-credential-osxkeychain` |
| 换电脑忘缓存 | `git credential approve` 一次性配置 |
| token 在环境变量 | 仍比文件安全 + CI 用 secret store |
