Files
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

373 lines
27 KiB
Markdown
Raw Permalink 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 補充:`acr init --self-hosted` 一鍵自動化(installer 模式)
> 2026-06-01 初稿 → 2026-06-02 定案改寫(richblack 拍板 installer 形態)。
> 本檔是 `sdk-and-website/design.md` 的單檔補充(規則 02 §4.3 允許)。
> **狀態:design 已與 richblack 對齊;實作前讀 §6 前置依賴。**
> 背景:戰法從 SaaS 轉 self-hosted 開源(docs/HANDOFF-self-host-harness.md §0)。
---
## 1. 定案形態(richblack 2026-06-02
**arcrun CLI = installer / orchestrator**(類似 rustup / nvm:工具本身小,按需從遠端拉真正內容)。
### 用戶只做 4 件事,中間什麼都不用懂:
1. 申請 CF 帳號
2. 安裝 CF CLI`wrangler`
3. 安裝 arcrun CLI`npm i -g arcrun`
4. `acr init --self-hosted`(貼 CF Account ID + API Token)→ **完成,其餘看機器跑**
### CLI 自動做(用戶無感):
- 驗 CF token 權限
- 建 7 個 KV namespace + 1 個 R2 bucket(冪等)
- **從 GitHub release 下載預編譯部署物**(含 24 個 `.wasm` + 各 Worker 的 wrangler.toml + cypher-executor/registry
- 把建好的 KV namespace id 注入各 wrangler.toml + cypher-executor 的 `WORKER_SUBDOMAIN`
- **`wrangler deploy` 部署全部 Worker**(用戶已裝 wrangler
- seed auth recipe + API recipe 進 RECIPES KV
- 寫回 `~/.arcrun/config.yaml`
- 印出「手動 `wrangler secret put CF_SECRETS_API_TOKEN`」提示(secret 不自動化,rule 05
### 關鍵技術決策(richblack 2026-06-02
| 決策 | 選擇 | 理由 |
|---|---|---|
| 零件部署物 | **預編譯 `.wasm`**(不在用戶端 build)| 用戶不懂 tinygo、也不該懂。下載即用。 |
| 部署工具 | **wrangler**shell out| 用戶已裝 CF CLIself-host 本來就有上傳能力。CLI 不自己重寫 CF Script Upload API。 |
| 源碼來源 | **GitHub release tarball**(含預編譯 wasm)| 版本明確、不需用戶有 git、`acr update` 拉新 release 同一條路。 |
| 為何不是 git clone | repo **沒 commit `.wasm`**rule 05 build 產物不 commit)→ clone 拿不到 wasm | 必須走含 wasm 的 release artifact。 |
---
## 2. 為什麼是 installer 而非「repo 內掃 wrangler.toml」(推翻初稿)
初稿假設「用戶在 repo 內跑、CLI 掃 wrangler.toml」。**推翻**,因為:
- npm 全域裝的 `acr` 手上**沒有** 24 個 Worker 源碼。
- repo 沒 commit `.wasm`(已查證 `git ls-files .component-builds | grep .wasm` = 0)→ 連 clone 都拿不到可部署的 wasm。
- 用戶不該需要懂 git / tinygo / repo 結構。
→ 正解:CLI 當 installer,從 **GitHub release(含預編譯 wasm** 拉部署物到暫存目錄,在暫存目錄注入 KV id 後 `wrangler deploy`
---
## 3. 流程設計(`initSelfHosted` 改寫)
```
acr init --self-hosted
├─ 1. 問 2 輸入:CF Account ID + CF API Token
wrangler 是否已裝?which wrangler;沒裝 → 提示先裝 CF CLI 再來)
│ 驗 tokenCF API GET /accounts/{id}/tokens/verify + GET /accounts/{id}
│ 缺權限(Workers Scripts Edit / KV Edit / R2 Edit)→ exit 1 指出缺哪個 scope
├─ 2. 建資源(冪等:先 list 已存在就重用)
│ 7 KVWEBHOOKS / CREDENTIALS_KV / RECIPES / USERS_KV /
│ SESSIONS_KV / ANALYTICS_KV / EXEC_CONTEXTrule 01 資料儲存表)
│ 1 R2WASM_BUCKET
├─ 3. 下載部署物:GitHub release tarball → 解壓到暫存目錄 (~/.arcrun/.deploy-<ver>/)
│ 內含:cypher-executor/ + registry/ + .component-builds/*(每個含預編譯 component.wasm + wrangler.toml
├─ 4. 注入設定到暫存目錄的 wrangler.toml(不改用戶 repo,改暫存副本)
│ - 各 Worker 的 KV binding id ← step 2 建立的
│ - cypher-executor [vars] WORKER_SUBDOMAIN ← CF API GET /accounts/{id}/workers/subdomain
├─ 5. 部署:對暫存目錄每個含 wrangler.toml 的 dirshell out
│ `wrangler deploy`env CLOUDFLARE_API_TOKEN=<token>, CLOUDFLARE_ACCOUNT_ID=<id>
│ 分兩層:tier1 = .component-builds/*(先)→ tier2 = cypher-executor / registry(後)
│ 每個 wrangler.toml 已含 workers_dev = true → workers.dev URL 自動啟用
├─ 6. seed recipe 進 RECIPES KV(部署後打新 cypher URL,或直接 CF KV API 寫)
│ - auth recipe:重用 AUTH_RECIPE_SEEDScypher-executor/src/lib/auth-recipe-seeds.ts
│ - API recipe:新增 seed-api-recipes.ts(見 §5
├─ 7. 寫回 configmode: self-hosted + 所有 id + cypher_executor_url = 部署後 workers.dev URL
└─ 8. 印手動 secret 提示:
wrangler secret put CF_SECRETS_API_TOKEN --name arcrun-cypher-executor
credential 值交 CF Workers Secrets 託管;auth 零件不需 secret
```
### `acr update`(同一條路,未來新零件)
- 拉新 GitHub release → 解壓 → 注入既有 config 的 KV id → wrangler deploy 變動的 Worker。
- 第一期至少做到「重跑等效 init 的部署步驟」;diff-only 部署可後續優化。
---
## 4. 動到的檔案
| 檔案 | 動作 |
|---|---|
| `cli/src/commands/init.ts` | 改寫 `initSelfHosted()`line 105-131)為 installer 流程 |
| `cli/src/lib/cf-api.ts` | 擴充:KV namespace 建立/list、R2 bucket 建立、subdomain 查詢、token verify |
| 新增 `cli/src/lib/deploy.ts`(暫定)| 下載 release tarball + 解壓 + 注入 wrangler.toml + shell out wrangler deploy |
| 新增 `cli/src/commands/update.ts`(暫定)| `acr update`:拉新 release 重部署 |
| 新增 `cli/src/lib/api-recipe-seeds.ts` | API recipe **種子資料**installer 用;放 CLI 端,**不放 cypher-executor/src**——rule 02 §2.2 hook 擋 cypher-executor TS hard-code endpoint,且 seed 資料本就屬 installer 職責)|
| 新增 `cypher-executor/scripts/seed-api-recipes.ts` | seed **腳本**(給 prod 補灌用,import CLI 的種子資料;`scripts/` 不受 §2.2 hook 管)|
| `cli/src/index.ts` | 註冊 `acr update` 指令 |
**不動**cypher-executor 執行路徑、既有零件 wasm 源碼、config 讀取端(config.ts:52 已支援 self-hosted)。
---
## 5. API recipe seed(新增 seed-api-recipes.tsrichblack 2026-06-02 定)
codebase 只有 auth recipe seed。新增 `seed-api-recipes.ts`,把現役 API recipe hard-code 成種子。
### 現役 API recipe(從 prod KV 查得,2026-06-01
- `kbdb_get`+ create_block / patch_block / delete / ingest)→ auth_service: kbdb
- `gmail_send` → google_gmail_sa
- `google_sheets_append` / `google_sheets_read` → google_sheets_sa
- `telegram_send` → telegram
- `line_notify_send` → line_notify
### KBDB recipe 採 Supabase 模式(richblack 2026-06-02
- **KBDB 是 richblack 提供的服務**(跟 arcrun 一樣),採「基礎免費、大量收費」。
- KBDB recipe **進 seed**(展示能力 = 引子,Supabase 模式)。使用者要用 → 去 **arcrun 取統一 API Key**(已有 /register 入口),把 key 設成 credential。
- ⚠️ **FOLLOW-UP(交 KBDB 端)**:現役 endpoint 是 `kbdb.finally.click{{_path}}`。richblack:這是 KBDB 端要改的問題——KBDB 該用統一對外網址提供大家用,不是 finally.click。**seed 先照現況進;KBDB 端改網址後同步更新 seed。** 此事不擋 init 實作。
---
## 6. 部署物產製:commit wasm 進 repo + codeload tarballrichblack 2026-06-02 定案)
> 此節**取代初稿的「GitHub release artifact」構想**。richblack 拍板更輕的做法:
> 直接把預編譯 wasm commit 進 repoCLI 從 GitHub codeload tarball 拿。不需 release.yml 機制。
### 6.1 策略
- **repo 自帶可部署的 wasm**:刪 `.gitignore``*.wasm` 排除,commit 預編譯 wasm 進 repo。
→ repo 本身就是部署來源,CLI 直接拿、用戶用自己的 CF token deploy。
- **CLI 走 codeload tarball**`https://codeload.github.com/richblack/arcrun/tar.gz/{ref}`
ref = main 或 tag)。用戶不需 git、版本可控(tag)。`acr update` 拉新 ref。
- **理由**richblack):「我在我的 CF 能用 = 我已擁有 wasm;用戶指向我的 GitHub 取得 wasm
用他自己的 CF credential deploy。開源,看不看源碼不重要,體驗好最重要。」
### 6.2 ⚠️ 推翻既有鐵律(rule 05)— 需同步改規則
`.claude/rules/05-deploy-convention.md` 明文「`.component-builds/{name}/component.wasm` **不 commit 進 repo**
build 產物)」「Phase 1-3 暫時 commit 過,**之後會加 .gitignore 清理**」。
**本決策反向**commit wasm 進 repoself-host 需 repo 自帶可部署 wasm)。
**實作時必須同步改 rule 05 + .gitignore**,否則 pre-write hook / 規則與實作打架。
→ deploy.yml 的 CI rebuild 步驟仍保留(CI 部署 prod 時用最新 source rebuild,與 commit 的 wasm 不衝突;
commit 的 wasm 是給「self-host 用戶 + acr init」用的部署來源)。
### 6.3 只 commit 部署所需的 wasm(省空間)
- 實況(2026-06-02 查):`registry/components/*.wasm` 23 個(build 中間產物)+
`.component-builds/*/component.wasm` 22 個(部署物),共 **~50MB**。
- **部署只需 `.component-builds/*/component.wasm`**wrangler deploy 認這個)。
**只 commit `.component-builds/*/component.wasm`22 個),不必 commit registry 那 23 個**(省一半)。
`.gitignore` 改成:保留排除 `registry/components/**/*.wasm`(中間產物),只放行 `.component-builds/**/component.wasm`
- ⚠️ **誠實 trade-off**mindset §7):commit wasm 進 repo → 每次 wasm rebuild 都在 git 歷史累積二進位,
**repo 長期會膨脹**。可接受(self-host 體驗優先),但記錄此代價;未來若膨脹過劇,再考慮 release artifact / git-lfs。
### 6.3.1 「錯做成零件」的 3 個不 commitrichblack 2026-06-02
實際 commit 的是 **19 個正當零件**,不是 22。排除的 3 個:`claude_api` / `km_writer` / `kbdb_upsert_block`
- **原因(richblack 修正「待刪」說法)**:它們**不是 endpoint 薄殼,是把工作流硬塞進零件**(違反 DECISIONS §1)。
例:`kbdb_upsert_block` 的 upsert 邏輯應在 KBDB API 那邊(API 提供 upsert endpoint),零件只該驅動它;
現在卻把「GET 找→有則 PATCH 無則 POST」整段工作流塞進零件。本質是工作流/recipe,被錯做成零件。
- **為何「現在就不 commit」而非「先 commit 之後刪」**commit 二進位進 git 歷史後,即使日後 `git rm`
歷史裡仍永久殘留(repo 體積已被佔),除非 rewrite history(很麻煩)。**錯誤的東西不灌進永久歷史。**
- **落地**`.gitignore` 放行 22 個後**再排除這 3 個**(後出現規則勝出);`deploy.ts discoverWorkerDirs`
只部署「同時有 wrangler.toml + component.wasm」的目錄 → self-host 用戶 codeload 拿到的目錄缺這 3 個 wasm → 自然跳過。
- **後續**:這 3 個的降級(變回工作流/recipe)是 BACKLOG 既有待辦,本次不處理,但確保它們不進 self-host 部署來源。
### 6.4 CLI deploy 流程(deploy.ts downloadAndDeploy 補實作)
```
1. 下載 codeload tarballref 預設 mainacr update 可帶 tag)→ 解壓 ~/.arcrun/.deploy-<ref>/
2. 讀解壓出的 .component-builds/* + cypher-executor/ + registry/
3. 各 wrangler.toml 注入 ctx.kvNamespaceIds + cypher-executor WORKER_SUBDOMAIN
4. tier1=.component-builds/*(先)→ tier2=cypher-executor/registry(後)
每個 dirpnpm install(若有 lock)→ CLOUDFLARE_API_TOKEN=<用戶> wrangler deploy
5. 回 cypherExecutorUrl = https://arcrun-cypher-executor.<subdomain>.workers.dev
```
注意:tier2cypher-executor/registry)是 TSwrangler deploy 會在用戶端用內建 esbuild bundle
(不需額外工具,richblack 確認源碼可見不重要、體驗優先 → artifact 含 TS 源碼即可)。
### 6.5 實作順序
1.`.gitignore`(放行 `.component-builds/**/component.wasm`+ commit 22 個 wasm。
2. 同步改 rule 05(記錄此決策推翻原慣例)。
3. 補實 `deploy.ts downloadAndDeploy`codeload 下載 + 注入 + wrangler deploy)。
4. **在 1-2 完成前,downloadAndDeploy 維持誠實 unimplemented,不假裝(mindset §7)。**
### 6.6 未來方向:零件按需安裝(richblack 2026-06-02,現在不做)
- 現在 `acr init --self-hosted` **全裝基礎零件**22 個一次部署)。簡單、夠用。
- **未來若零件數量真的變很多**,再思考「按需安裝」(只裝 workflow 實際用到的零件 / 用戶選裝)。
- **現在不做的理由**(DECISIONS 附錄「會不會累積成債」):零件目前少且未來絕大多數是 recipe
(不需 deploy)→ 為「零件爆量」做按需安裝基建 = 為不存在的規模做自動化 = 過度工程。
零件真的爆量再回頭做,屆時是「未來一次性處理的設計點」,現在不必焦慮。
---
## 7. 驗收標準(客觀證據,mindset §7)
1. richblack 用**全新 CF 帳號** + wrangler 已裝 + 一個 CF API Token 跑 `acr init --self-hosted`
→ 全程無手動建 KV / 無手動 clone / 無 tinygo / 無手動填 namespace id。
2. 跑完印 secret 提示,richblack 手動 `wrangler secret put CF_SECRETS_API_TOKEN`
3. `acr push` 一個含 http_request + 自建 recipe 的 workflow → trigger → **HTTP 2xx + execution trace**
4. 冪等:重跑 init 不重建已存在 KV / 不報錯。
5. `acr update` 拉新 codeload tarballtag)→ 重部署成功。
---
## 7.5 壓測修正(2026-06-04):fork 帳號裝不起來的四個阻斷項
> 來源:`docs/壓測報告.md`(壓測者:一個「完全不懂程式」的人 + AI 自架)。
> §3/§6 的 installer 流程在**非官方 CF 帳號**上實測**無法完成部署**。根因與修法如下,全部在本 SDD 範圍內(installer 補洞 + R2 dead storage 清除),不是新架構。
### 根因
各 worker 的 `wrangler.toml`repo 內)寫死了**只有 arcrun 官方帳號才有的綁定**:
- `[[routes]] zone_name = "arcrun.dev"`(每個零件 + cypher-executor)→ fork 沒有此 zone → `wrangler deploy` 找不到 zone 失敗(阻斷 #1
- `[[r2_buckets]]` + `[ai]`cypher-executor)→ fork 未必有 R2/AI(阻斷 #2
- R2 是 dead storage,卻因 CF R2 首次啟用**強制綁信用卡** → 違背「開源免費自架」(阻斷 #3
- R2 bucket 名 `WASM_BUCKET`(大寫+底線)違反 R2 命名規則 → 證明該碼從未成功跑過(阻斷 #4
### 修法(只動 `cli/`**不刪 repo 內 worker toml**
**關鍵判準**repo 內 toml 的 `[[routes]]` 是**官方 prod CI 部署**需要的(對外開放零件),直接刪會破壞官方部署。
正解是 `deploy.ts` 在**注入暫存副本**時 strip(§3 step 4 本就「不改用戶 repo,改暫存副本」),因為 deploy.ts 只在 self-hosted 路徑跑。
| 檔案 | 改動 |
|---|---|
| `cli/src/lib/deploy.ts` | 新增 `stripOfficialOnlyBindings()`:注入時移除 `[[routes]]`/`zone_name`/`[[r2_buckets]]`/`[ai]`。worker 靠 `workers_dev=true` 對外。移除 `REQUIRED_R2_BUCKET` |
| `cli/src/commands/init.ts` | 移除 `ensureR2Bucket` 呼叫 + `wasm_bucket` config + token 提示去掉「R2 Edit」+ 結果文案改「7 KV,無需綁卡」 |
| `cli/src/lib/cf-api.ts` | 移除 `ensureR2Bucket()` 方法 |
| `cli/src/lib/config.ts` | 移除 `wasm_bucket?` 欄位 |
| `cli/src/commands/validate.ts` | 錯誤文案「WASM_BUCKET 中找不到」→「registry 中找不到零件」(去命名誤導) |
→ self-hosted 改為**只需 Workers + KV**(皆免費額度、不綁卡),回歸開源免費承諾。
### 客觀證據(mindset §7
- `stripOfficialOnlyBindings()` 對真實 `auth_static_key`/`cypher-executor` toml 實測:routes/R2/AI 全 stripname/workers_dev/kv_namespaces/vars 全保留。
- `npx tsc --noEmit` CLI 全綠;`grep R2/WASM_BUCKET src/` 零殘留。
### 報告其他項(本次未動,記錄待辦)
- **#7 init 只能互動式**readline,無 flag/env)→ AI/CI 不友善。建議支援 `--account-id`/`--api-token` 或讀 env。**待 richblack 決定**。
- **#8 無多帳號/專案 scope**config 寫死全域 `~/.arcrun/config.yaml`)→ 接案者痛點。報告建議專案層 `.arcrun.yaml` 覆蓋全域(git config 模式)。**屬新功能(change),需 richblack 確認後另開 SDD**。
- **#5/#6 權限前置驗證 + 錯誤訊息**:移除 R2 後 R2 權限問題自然消失;KV 前置一次驗證可後續優化。
## 7.6 壓測修正(2026-06-06):seed 下沉成 API 行為(推翻 §5「seed 資料放 CLI」)
> 來源:壓測報告 §4.1seed 機制對所有 self-host 用戶都壞)+ §5.5(薄殼原則)。richblack 點名「薄殼原則寫入鐵律」。
> **此節推翻 §5「種子資料放 CLI 端」+ §8「只動 cli/」的範圍**——seed 改為 API 行為(rule 07)。
### 推翻什麼
§5 原寫「種子資料放 `cli/src/lib/api-recipe-seeds.ts`(installer 職責)」。壓測證明這是 §4.1 bug 的結構根因:
seed 邏輯寫在 CLi `init.ts`(迴圈 POST + `deployFullyOk` gate),registry 一個無關 worker 失敗就連坐讓 seed 永遠被跳過;且 init 從不 seed auth recipe。
### 改法(seed = API 能力,薄殼只呼叫一次)
| 檔案 | 改動 |
|---|---|
| 新增 `cypher-executor/src/routes/init-seed.ts` | `POST /init/seed`:一次灌 API recipe + auth recipe(冪等、直寫 KV、誠實計數)|
| 新增 `cypher-executor/src/lib/api-recipe-seeds.ts` | 種子資料**唯一真相移到 server**rule 02 §2.2 hook 對 `*-seeds.ts` 整類加例外,richblack 2026-06-06 授權)|
| 刪 `cli/src/lib/api-recipe-seeds.ts` | CLI 重複檔刪除(避免漂移)|
| `cypher-executor/scripts/seed-api-recipes.ts` | import 改指 server 端;備援路徑 |
| `cli/src/commands/init.ts` | `seedApiRecipes` 迴圈 → `callSeedEndpoint` 一次呼叫;移除 deployFullyOk gate |
| `cli/src/commands/update.ts` | 重部署後也呼叫 `/init/seed`;改重解析全部 KV id |
| `cli/src/lib/deploy.ts` | `REQUIRED_KV_NAMESPACES``SUBMISSIONS_KV`(修 registry 20/21,§4.1.1|
### 對 §8「為何不違反鐵律」的修正
§8 原寫「只動 cli/ + scripts/」。現在**也動 cypher-executor/src/**(新增 `/init/seed` route + 種子資料),
但**不違反 rule 02 §2.2**:seed 端點只做「讀內嵌種子資料 → 寫 KV」的 routing/資料搬運,不解密/不簽章/不展開 template
種子的 endpoint 字串是資料宣告(`*-seeds.ts` 類豁免),真正 API 呼叫仍走零件路徑。符合薄殼原則(rule 07)。
## 7.7 壓測修正(2026-06-06):self-hosted 身份 = 明碼 namespace(不發 api_key
> 來源:壓測報告 §7.2seed 通了,但 creds push/push/runtime 全卡「缺少 api_key」)。
> richblack 拍板:self-hosted 不需要「祕密 api_key」,因為它只需要 namespace(分區標籤),不需要認證(你的 cypher 在你自己的 CF,無「別人」會冒用)。
### 根因
credential/部署/執行三條路徑全建在多租戶 `{api_key}:cred:{name}` 模型,但 self-hosted(單租戶)init 從不發 api_key →
`creds push`/`push` 硬要 api_key 而退出;runtime webhook trigger 也要 `X-Arcrun-API-Key` header(公開表單帶不了)。
### 設計(richblack 2026-06-06
**self-hosted 的「api_key」= 明碼 namespace,用戶在 `.env` 自填。工具不生成、不 hash、不外傳。**
- 一欄(由用戶持有):
- `NAMESPACE=leo`(明碼分區標籤,當 KV 前綴;**非密碼**)
- **誠實限制**mindset §7):namespace 明碼不提供認證。要防外部呼叫 → 對 webhook 加保護(mindset §6);arcrun 不做授權判斷(mindset §3)。
- **零分叉**SaaS 仍走 register 發的真 api_keyself-hosted 走 .env 的 NAMESPACE。**同一條 `api_key` 路徑、同一份 config 解析**,只是值來源不同。
### 改法
| 檔案 | 改動 |
|---|---|
| `cli/src/lib/config.ts` | `ENV_MAP``NAMESPACE`/`ARCRUN_NAMESPACE` → 對應 `api_key`;新增 `.env` 自動載入(就近往上找,shell > .env,無 dotenv 依賴)|
| `cli/src/commands/creds.ts``push.ts` | self-hosted 缺值時,引導設 `.env` 的 NAMESPACE |
| `cli/src/commands/init.ts` | self-hosted 結尾改提示「在 .env 設 NAMESPACE」+「把 CF_SECRETS_API_TOKEN wrangler secret put 進 worker;不想自己跑可明示同意我代設」|
| `cypher-executor/src/routes/webhooks-named.ts` | 新增 `POST /webhooks/named/:ns/:name/trigger`namespace 走 path,公開表單免 header);與 header 路徑共用 `triggerNamed`,不分叉 |
| `cli/src/commands/push.ts` | self-hosted 輸出 path-based 公開 webhook URL(免 header|
### 為何 namespace 進 URL path 安全
namespace 是明碼標籤非密碼,放 path 不洩漏任何祕密。真正的祕密是 credential 值(由 CF Workers Secrets 託管,永不進 URL/path)。要防外部濫用 webhook 是「webhook 保護層」職責,非 namespace。
## 7.8 壓測修正(2026-06-08):onboarding 四缺陷 → pip 式自我修復安裝(CHANGE,待 review
> 來源:Haiku 冷啟動壓測(test_arcrun/2/test_record.md+ richblack 觀察。
> **核心判準([[haiku-capable-is-design-goal]]**Haiku 撞牆 = 介面缺陷,不是換強模型。
> 暴露的四個缺陷都是 onboarding/AI-UX,非功能 bug。**richblack 要的是系統級正解,不是暫時解法/外掛 doctor。**
### 缺陷清單(壓測實證)
| # | 缺陷 | 根因 | Haiku 實際行為(撞牆) |
|---|------|------|------|
| D1 | **安裝不偵測環境**(缺 node/wrangler/CF登入/D1 任一就卡) | init 假設前置齊備,缺了不自己補也不引導 | Haiku 遇 D1 不存在 → **跑去讀原始碼自己想辦法建**(非技術用戶災難) |
| D2 | **帳號 scopeAI 繞過 CLI 自己 curl 全域** | config 優先序**已實作**(專案>全域,config.ts:169-193),但 AI 不用 CLI 讀帳號、自己 curl 猜 → 打到全域 | Haiku curl 全域帳號 URL,非當前 project scope |
| D3 | **MCP 掛不進不提示重啟** | 安裝完 project scope MCP 顯示無法用,沒告訴用戶「MCP 要重啟 client 才載入」 | 用戶以為壞了 |
| D4 | (壓測舊版 1.3.0 無 D1 自動建;1.3.2 已修,但 D1↑ 的「偵測+冪等」更根本) | — | — |
### 正解:安裝流程內建偵測 + 自我修復 + 冪等(pip 式)—— richblack 2026-06-08 拍板
**`acr init`(安裝流程本身)= 像 pip:先偵測環境有什麼、版本對不對,才動手;缺什麼 Arcrun 自己負責裝。
重跑會檢查後說「什麼也沒動」(冪等)。不另做 doctor——偵測是正規安裝流程的一部分。**
1. **偵測先於動作**init 先檢查各前置(node / wrangler / CF 登入 / D1 / KV / migration+ 版本 →
缺的才裝/建、有的跳過、版本不對才升。**不是假設齊備直接動手**。
2. **Arcrun 負責裝它需要的**:缺什麼是 Arcrun 的工作,不丟給 AI/用戶自己想辦法(D1 不存在 → init 自己建+套 migration
非讓 AI 讀原始碼)。需人類授權的(建 CF 資源、暴露)仍停下請示(mindset §7 非 TTY 不偽造同意)。
3. **冪等可重跑**:重跑檢查後「什麼也沒動」(像 pip install 已裝),不報錯不重建。
4. **AI 無腦入口 = 一個 GitHub 連結**:AI 讀 README/連結就照著走,**不繞、不讀原始碼、不自己 curl**。
安裝指示集中在一處,AI 跟著做即可。
### 配套介面(讓 AI 不需也不該猜)
- **`acr whoami` / `acr config show`**(D2 修法):印「當前生效帳號 + scope 來源(project/global/env)」。
AI 無腦問 CLI 拿正確帳號,**不自己 curl**。同步加 **MCP `arcrun_whoami`**(薄殼一致,rule 07 §5)。
→ 治本 D2:不是 config 沒實作(已對),是 AI 該用工具讀、不該繞 CLI 猜帳號。
- **MCP 掛載後提示重啟**(D3 修法):`acr init` / `acr mcp-setup` 寫完 `.mcp.json` 後,
明確印「⚠️ MCP 已設定,**請重啟 IDE/client 才會載入** project scope MCP」。掛不進時引導重啟,不讓用戶以為壞了。
### 範圍 / 分期(richblack 2026-06-09 授權 P0-P2 執行)
- [x] **P0**`acr init` 偵測 + 驗收(pip 式)。新增 `cli/src/lib/preflight.ts`
- `detectEnvironment()` 安裝前偵測 node/wrangler(缺=fatal 停下給補救指令,不假設齊備直接動手);
- `verifyInstall()` 裝完實查 CFKV/D1 listKv/listD1+ 打 cypher `/health` 確認真就緒,缺哪項報哪項 + 一鍵補裝(acr update 冪等)。
- `initSelfHosted` 開頭接 detectfatal 即 exit 1)、結尾接 verify(未就緒印補裝指引)。冪等沿用 ensureKv/ensureD1。cli tsc exit 0。**2026-06-09 完成**
- [x] **P1**`acr whoami``cli/src/commands/whoami.ts`,人讀 + `--json`,印 mode/帳號/連哪台/來源層,薄殼讀 resolveConfigSources+ MCP `arcrun_whoami``mcp/src/tools/arcrun_whoami.ts`,回報 orgNamespace + binding,與 CLI 對齊)。AI 問工具拿身份,不自己 curl 猜。cli+mcp tsc exit 0。**2026-06-09 完成**
- [x] **P2**MCP 掛載重啟提示(D3)。`cli/src/commands/mcp-setup.ts` 寫完 `.mcp.json` 後印「⚠ 請重啟 IDE/client 才會載入 project scope MCP」+ 信任工作區提醒。經 init 也會流到(init 呼叫 cmdMcpSetup)。**2026-06-09 完成**
- [~] **P3**README/GitHub 入口整理成「AI 讀了就照著裝」的單一指引(D 無腦入口)。**部分完成 2026-06-09**
- 新增 repo 根 `.env.example` 範本(CF Account/Token 基礎兩格 + NAMESPACE + 服務 token 區,
每格上面白話說明「去哪申請、怎麼拿」,值留空。`.gitignore``!.env.example` 放行進 repo)。
- `llms.txt` step 3 改成「**你(AI)先 `cp .env.example .env`**,帶用戶填值(用戶只填「=」右邊)」——
把「建 .env 結構」從用戶身上挪到 AI,用戶只做貼值。step 4 補 init「安裝驗收」說明。
- 仍待:`arcrun.dev/llms.txt` 沒 servelanding/public 缺檔,404GitHub repo 內 llms.txt 正常)。
test/5 prompt 給 GitHub URL 故不阻擋;arcrun.dev serve 另排。
> **誠實**:偵測各前置 + 版本比對 + 冪等重跑工程量不小(跨 CLI 多步)。但「裝很久 + AI 一直繞」對非技術用戶
> 是致命體驗 → 這是 onboarding 必修不是優化。
>
> **2026-06-09 補(test_arcrun/4 壓測根因)**D1/D2/D3 之上還有更致命的一層——壓測時 AI **整個沒跑 `acr init`**
> settings.local.json 沒 allow init),導致 D1/harness/MCP/slash-command 全沒裝(全掛在 cmdInit 內)。
> P0 的「偵測+驗收」讓「跑了 init 但環境半殘」能被看見並自癒;但「**根本沒跑 init**」要靠 P3README/入口
> 引導 AI「第一件事就是 acr init」)+ harness 的 mindset/CLAUDE.md 提醒。P3 後續補。
---
## 8. 為何不違反鐵律
- 只動 `cli/` + 新增 `cypher-executor/scripts/`(seed 腳本,非執行路徑業務邏輯)。
- 不在 `registry/components/` 寫 TS;不在 cypher-executor TS 實作 credential/auth/JWT。
- 不新增 Service Binding。
- secret 不進自動化(§3 step 8 手動)。
- 不重寫部署輪子(用 wrangler,不自寫 CF Script Upload)。