Files
Arcrun/system-dev/docs/3-specs/arcrun/sdk-and-website/config-layering.md
T
Leo 20c7610371 refactor: 移除已廢棄的自管加密金鑰機制(credential 全面託管 CF Workers Secrets)
leo 2026-07-20 明令:「已經改用 cf 自己的 secrets,不要再說它了」
「我希望以後再也看不到這個詞再出現」

背景:credential 早已遷移至 CF Workers per-script Secrets + D1 目錄,
舊的自管金鑰(client 端 AES-GCM + KV 密文 + crypto_decrypt)是遷移期遺留。
本次連根移除,含一併作廢的死 SaaS 碼。

移除:
- 舊 KV 密文解密路徑(credential-injector.ts 整檔、dual-read fallback)
  前置驗證:leo21c / youlin 兩帳號 CREDENTIALS_KV 實測 *:cred:* 皆 0 筆
- migrate-to-workers-secrets 搬家端點(回填已完成,無可回填)
- /register 路由與 generateApiKey(HMAC 產 ak_ key 是 SaaS 遺物;
  self-hosted 走 namespace 明碼 D21,已無人使用)
- platform_crypto component(三帳號實測 404 已退役,無 workflow 引用)

保留(附理由):
- crypto_decrypt 保留為永遠回失敗的 stub——現役三個 auth .wasm 仍宣告該
  import,缺項會讓 WASM instantiate 直接失敗。待零件重編後可真正刪除。

順帶修復(原不在範圍,但會實際壞事):
- /auth/callback 有 `if (!key) redirect(server_error)` 閘,未設該 secret 的
  實例會登入直接失敗 → 已移除
- OAuth 兩處把 provider token 寫進舊加密 KV(租戶鍵與實際 api_key 在 rotate
  後必然分歧,已失效)→ 改導向 Workers Secrets,包 try/catch 不影響登入
- acr init Standard 模式呼叫已刪除的 /register → 改引導 OAuth 取 key
- .claude/rules 與 system-dev/docs 是同一規範的兩份鏡像,先前只改 rules
  導致鏡像仍在教舊做法 → 已同步(此類雙檔同步應納入檢查)

新用戶安裝從此零 secret 前置。
測試 187/188(唯一 fail 為 pre-existing,stash 驗證與本次無關);
cypher-executor 與 cli typecheck 全綠。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 01:32:48 +08:00

109 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design 補充:設定分層(env > 專案層 > 全域)+ init 非互動
> 2026-06-04 建立。`sdk-and-website/design.md` 的單檔補充(規則 02 §4.3 允許)。
> 來源:`docs/壓測報告.md` §1.2(方案 C+ §2.2init 非互動)= 阻斷項 #7、#8。
> richblack 2026-06-04 授權:「#7#8 是同一問題,明顯發現的問題當然要解決,完成後就要推。」
---
## 1. 問題(壓測實測)
`acr` v1.1.0 把設定**寫死在全域唯一一份** `~/.arcrun/config.yaml``config.ts`
`CONFIG_DIR = join(homedir(), '.arcrun')`),且 `init` 只能 readline 互動。後果:
- **#8 多帳號**:同一台電腦只能一個 arcrun 身份 → 接案者 / 多公司 / 個人+公司混用做不到。
壓測者克難法是覆寫 `HOME`(需讀原始碼才找得到、不直覺)。
- **#7 非互動**init 只能 TTY 問答 → AI / CI 無法用 flag / env 完成。
兩者本質同一:**設定來源不該只有「全域單檔 + 強制 TTY」一條路**。
## 2. 設計(richblack 2026-06-04 拍板:三層全上 + init flag/env
### 2.1 設定分層優先序(仿 git config / Claude Code MCP
```
1. 環境變數 ARCRUN_* / CLOUDFLARE_* ← 最高,解 #7AI/CI 非互動)
2. 專案層設定 <就近往上找>/.arcrun.yaml ← 解 #8(接案多帳號),有 fallback
3. 全域設定 ~/.arcrun/config.yaml ← 平常自動用(自己的帳號)
```
- **就近往上找**:從 `process.cwd()` 往上層目錄逐層找 `.arcrun.yaml`,找到第一個即用(停在檔案系統根)。
→ 自己的專案不放檔 → 自動 fallback 全域;客戶資料夾放檔 → 只在該樹生效,離開自動切回。零心智負擔、不會忘記切換。
- **覆蓋是「欄位級 merge」**:高層只覆蓋它有提供的欄位,未提供的欄位 fallback 到低層。
(例:專案層只放 `cypher_executor_url`,其餘仍用全域。)
### 2.2 env 變數對應(最高層,欄位級覆蓋)
| env | 覆蓋 config 欄位 | 用途 |
|---|---|---|
| `ARCRUN_MODE` | `mode` | local/standard/self-hosted |
| `ARCRUN_API_KEY` | `api_key` | standard |
| `ARCRUN_CYPHER_EXECUTOR_URL` | `cypher_executor_url` | self-hosted 指向自己的 cypher |
| `CLOUDFLARE_ACCOUNT_ID` | `cloudflare_account_id` | self-hosted(沿用 wrangler 慣用名)|
| `CLOUDFLARE_API_TOKEN` | `cf_api_token` | self-hosted(沿用 wrangler 慣用名)|
> CF 兩個用 `CLOUDFLARE_*` 而非 `ARCRUN_*`:與 wrangler / deploy.ts 既有慣例一致(deploy.ts 跑 wrangler 時就是設這兩個 env),CI 設一次兩邊通用。
### 2.3 init 非互動(flag > env > 互動問答)
`acr init --self-hosted` 取得 account-id / api-token 的順序:
1. **flag**`--account-id <id>` / `--api-token <token>`
2. **env**`CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_API_TOKEN`
3. **互動**:前兩者缺才 readline 問(保留現有 UX
> mindset §7 判準:帳號設定**不是**「暴露資料 / 建零件」類風險確認,是單純設定值,flag/env 合法、不違反「非 TTY 拒絕代人類確認」。
> 風險確認(exposure_consent 等)仍維持需人類明示,不在本次放寬範圍。
## 3. 實作(只動 cli/
| 檔案 | 動作 |
|---|---|
| `cli/src/lib/config.ts` | `loadConfig()` 改三層解析:全域 → merge 專案層 `findProjectConfig()` → merge env`applyEnvOverrides()`)。新增 `acr config --where` 用的 `resolveConfigSources()` |
| `cli/src/commands/init.ts` | `cmdInit` / `initSelfHosted``accountId`/`apiToken` options,缺才走 env 再 fallback 互動 |
| `cli/src/index.ts` | init 加 `--account-id <id>` / `--api-token <token>` option |
**`loadConfig()` 是唯一設定入口**(12 個指令全走它)→ 改它一處,全指令自動受益分層。
### 3.1 `acr config --where`(壓測 §1.2 建議 #3,避免用錯帳號)
新增輕量 `acr config` 指令,印出「現在這個資料夾正用哪個帳號 / 設定來自哪一層」:
```
mode: self-hosted(來源:專案層 /path/客戶A/.arcrun.yaml
cloudflare_account_id: abc...(來源:env CLOUDFLARE_ACCOUNT_ID
cypher_executor_url: https://...(來源:全域 ~/.arcrun/config.yaml
```
> 安全價值:部署前一眼確認「沒用錯帳號」。本次一併做(成本低、直接回應壓測痛點)。
### 3.2 安全:專案層 .arcrun.yaml 含憑證 → 必須 gitignore
專案層 `.arcrun.yaml` 可能含 `cf_api_token``createCredentialsYamlIfMissing()` 既有 gitignore 邏輯
擴充為一併忽略 `.arcrun.yaml`(壓測 §1.2 安全附帶發現:憑證進版控 = 帳號外洩)。
## 4. 驗收標準(客觀證據,mindset §7)
1. 專案資料夾放 `.arcrun.yaml``acr config --where` 顯示來源為該專案層;離開該樹 → 顯示全域。
2. `CLOUDFLARE_ACCOUNT_ID=x CLOUDFLARE_API_TOKEN=y acr init --self-hosted` → 不問互動直接跑(#7)。
3. `acr init --self-hosted --account-id x --api-token y` → 同上(flag 優先於 env)。
4. 三者皆缺 → fallback 互動問答(既有 UX 不破壞)。
5. env > 專案層 > 全域 的欄位級覆蓋:單元測試覆蓋三層 merge。
6. `npx tsc --noEmit` 全綠。
## 4.1 實作完成記錄(2026-06-04
全部 task 完成,客觀證據如下(mindset §7):
- [x] `config.ts` 三層解析:`loadConfig()` = 全域 → 專案層 → env 欄位級 merge;新增 `findProjectConfig()`(就近往上找)/ `resolveConfigSources()` / `activeProjectConfigPath()`
- [x] `init.ts``initSelfHosted` 收 flag/env,缺才互動;gitignore 一併排除 `.arcrun.yaml`
- [x] `index.ts`init 加 `--account-id`/`--api-token`;新增 `acr config [--where]` 指令
- [x] 新增 `commands/config.ts`token 遮罩、來源層標示)
- **驗收證據**
- 端對端測試(真實 fs 臨時目錄樹)8/8 通過:深層就近找專案層、未提供欄位 fallback、env 最高層覆蓋、來源層標示、離開專案樹回全域
- `acr init --help` 顯示 flag`acr config --where` 正確標來源 + token 遮罩 `secret_t…`
- `npx tsc --noEmit` 全綠
## 5. 為何不違反鐵律
- 只動 `cli/`,不碰零件 / cypher-executor 執行路徑 / Service Binding。
- flag/env 是帳號設定值,非風險確認(mindset §7 風險確認仍須人類明示)。
- 不新增頂層 SDD 目錄(本檔是 sdk-and-website 子系統內單檔補充,rule 02 §4.3)。