# Gitea API token scope 是真实的运维提供项依赖

> **日期**: 2026-05-13
> **类型**: 运维依赖识别 / 项目特有约定

## 现象

实现 internal-app-platform 的 GiteaClientService 时尝试探测 Gitea API：
```bash
curl -X POST -H "Authorization: token $GITEA_API_TOKEN" \
  http://43.130.59.228/api/v1/orgs/FFAIApps/repos \
  -d '{"name":"test"}'
# → 403 token does not have at least one of required scope(s), required=[write:organization]
```

也试过本人 user namespace：
```bash
curl -X POST .../api/v1/user/repos
# → 403 required=[write:user]
```

CLAUDE.md 里的 `GITEA_API_TOKEN` 当前 scope 是 `write:issue, write:repository`，
**故意限缩**，仅用于 ops 脚本（PR 创建/合并、issue 评论、ai-review 等场景）。

## 根因

不同 token 服务不同目的：
- `GITEA_API_TOKEN`（个人 PAT，CLAUDE.md 提到的）—— ops 脚本用，scope 故意小
- internal-app-platform 服务需要的是**应用层 service token**，能：
  - `write:organization`：在 FFAIApps org 下建仓
  - `write:repository`：管理仓库设置 / 装 webhook
  - `write:user`：V2 颁发 5min ephemeral fine-grained push token

这两个 token **必须分离**（最小权限原则）。强行把 GITEA_API_TOKEN 升 scope
会让 ops 脚本意外获得过大权限。

## 解决（已实施）

1. 代码层 — 引入新 env `INTERNAL_APP_GITEA_API_TOKEN`：
   ```
   .env.example:
   # 必须 scope: write:organization + write:repository
   # 推荐 scope: write:user (V2 ephemeral token)
   INTERNAL_APP_GITEA_API_TOKEN=__SET_FROM_GITEA_UI__
   ```
2. 服务层 — GiteaClientService 启动时探测，未配置 → 立即返回 `gitea_token_missing` 错误
3. 错误透传 — deploy_prepare 把 `gitea_token_insufficient_scope` 透传到 MCP，
   Claude Code 拿到后能给员工友好提示"平台还没配好，请联系 IT"

## 不该的做法

- ❌ 改 GITEA_API_TOKEN scope —— 影响其它 ops 脚本
- ❌ 让员工自己的 Gitea token 来跑 —— 破坏"非技术员工零摩擦"原则
- ❌ 用 SSH deploy key —— 每仓库一个 key 太重，员工 push 体验也差

## 适用面

任何"应用层调外部 SaaS API（Gitea / Slack / SES / 钉钉等）"都要识别清楚：
- 该 API token 是 **ops 脚本共享** 还是 **服务专用**？
- 服务专用 token 必须独立 env 命名 + 文档列明 scope 要求
- token 缺失 / scope 不够 都要在启动期 warn + 调用时结构化错误

## 状态

- ✅ **已解决（2026-05-14）**
- token 由 lijian.dai 个人生成（write:organization + write:repository 全通过 probe）
- FFAIApps org 顺手建好（id=8，private，由 token 持有人创建）
- 已写入：本机 `backend/.env`（gitignored）+ 测试服 `/srv/internal-apps/.env.platform`（0600）
- Gitea org-level webhook 注册：FFAIApps hook id=1 → `http://43.166.182.155:3000/api/v1/internal-apps/webhook/gitea`，HMAC secret 同步两侧
- 阻塞解除：deploy_prepare 现在可真建仓 / webhook 现在可真触发

## 经验沉淀（避免下次踩同样的坑）

1. **token 验证三步走**：(a) 自身 `/user` 200 + 拿 login (b) 目标 org `/orgs/{name}` 看到 / 看不到 (c) 实际建仓 dry-run 然后立即删
2. **org 不存在 vs 没权限**：Gitea 错误信息 `GetOrgByName` 不区分；建议先 `/user/orgs` 列自己所属 org，再 `/orgs/search?q=X` 确认
3. **写 service token 的人不必是 org owner**——只要有"建 org / 在 org 内建仓"权限就行
4. **token 不要打印到任何文件**：本次故意走「写入 .env（gitignored）+ 终端显示脱敏值」模式
