Commit Graph

13 Commits

Author SHA1 Message Date
uncle6me-web b223a69884 fix(portal): 藏書地圖看得到自己的知識——租戶字串改從「寫入端」來,不再拿環境變數預設值(Arcrun#108)
leo 2026-08-12 實撞:藏書地圖回 0 個庫,同一分鐘 KBDB 裡有 1854 條三元組,
`arcrun_whoami` 顯示 admin/全部知識庫、`kbdb_search` 也查得到——只有地圖那格是空的。

病根(不是資料掉了,是讀寫兩端各拿一個來源):
  寫入端 owner_id = `~/.arcrun/config.yaml` 的 `api_key`(CLI push/小幫手上傳/MCP,
    leo = `bfezv28v`)
  讀取端過濾 = `portalTenant(env) = env.CONSOLE_TENANT || "leo"`
    ——repo toml 帶的**官方 prod 值**,而 `acr` 從來不注入 CONSOLE_TENANT
  ⇒ 那個 `"leo"` 不是理論邊角,是每台 self-hosted 實例的實際行為,1854 條全被濾掉。

與 #105(`env.MCP_OWNER_NAMESPACE || "leo"`)同一句話,換一個檔案。

租戶字串該從哪裡來(本票的核心判斷):
  **從「寫入這批知識的那一方」來,不是從一份手抄的環境變數預設值來。**
  不是「掛到每個帳號上」——portal 帳號共用同一台實例的知識庫(design D-2),
  帳號之間的差別是 libraries 權限不是 owner_id;複製一份到帳號上只是多一個會過期的副本。
  #105 真正的教訓是:過濾用的租戶字串要有單一權威來源、解析不到要誠實失敗、且要能機械驗證。

修法:
1. 唯一產地 `cypher-executor/src/lib/tenant.ts`
   - `knowledgeOwner(env)` → branded `TenantId`:`ARCRUN_NAMESPACE` → `CONSOLE_TENANT` →
     丟 `TenantUnresolvedError`。**沒有字面預設值**——`|| 'leo'` 正是把「這台機器沒設定」
     偽裝成「你沒有資料」的元凶。
   - `accountTenant(env)` → 普通 `string`(帳號子 namespace `{tenant}::portal` 與 cypher
     自己寫的設定用它)。**回 string 是刻意的**:型別上就不可能流進知識資料面。
   - 資料面過濾一律經 `ownerQuery()` / `ownerField()`,只吃 `TenantId`。
2. 值的正解由 CLI 從真相源導出:`acr update` 把 config 的 `api_key` 注入成 `ARCRUN_NAMESPACE`,
   但**先驗再寫**(`GET /kbdb/map?owner_id=<api_key>` 查得到庫才寫;查不到/問不到就一個字
   都不動)。無條件覆蓋會把「知識本來就在 CONSOLE_TENANT 底下」的一鍵安裝實例指向空的那一格
   ——那是 #97/#106 那類「更新一次把人家的東西弄不見」,比原本的 bug 更糟。
   未注入時回退 CONSOLE_TENANT ⇒ 對官方 prod 與未更新的實例,這次改動是惰性的。
3. 空地圖分四態(沿 #100「讀不到就說讀不到」):no_library_grant/filtered_out/
   scope_mismatch/confirmed_empty。scope_mismatch 以前不存在,所以設定錯誤被畫成
   「你沒有資料」。回應仍不含租戶字串(design §3.3 紅線)。
4. 同族一起修(同一道閘一次抓到):console-dashboard 4 處、console-auth 1 處
   ——console 首頁的規模數字與藏書地圖對 leo 也一直是空的。

留下的閘(規則存在但沒機制驗證=會再犯第三次):
  · 型別閘:TenantId 只能由 tenant.ts 產出 → 拿隨手一個 string 去過濾,tsc 當場不給過。
  · 出貨閘:scripts/build-worker-artifacts.mjs 編 tier2 成品前先掃,違規 → 編不出成品。
  · 閘自己可測:規則是純函式(tenant-source-rules.mjs),tests/tenant-gate.test.ts
    逐條驗「5 種壞例子會擋」+「11 種合法寫法零誤攔」;掃描範圍只有 src/,擋不到自己。
  規範寫入 .claude/rules/02-forbidden.md 第六類、system-dev/wiki/mistakes.md #26。

沒動:庫權限過濾(一字未改,回歸測試釘住)、帳號資料落點、任何金鑰、租戶字串仍不下發前端。

驗證:
  cypher   vitest 441 綠 / 14 紅,14 紅與 base commit e05518a 逐字相同(既有)
           tsc 5 個既有錯誤,零新增
  cli      node:test 60/60 綠(含本次新增 12 條);tsc 零錯誤
  閘       壞例子實跑 exit 1;build 實跑「建置中止」;乾淨時實跑通過
  端到端   ◐ 未驗:需部署到 leo21c,那道閘要 leo 親手解(見 PR ③)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 00:15:58 +08:00
Leo 8d8b01d240 docs(sdd): credential-primitives-wasm 封存進 archive(T10 完成,卷已結案)
leo 2026-07-21 明令封存:卷已完成,留主目錄會讓未來 session 誤以為進行中。
最後一項 T10(廢除自管加密金鑰)已於 20c7610 完成(移除約 2500 行)。

- git mv 整卷 → system-dev/docs/3-specs/archive/credential-primitives-wasm/
  design.md status: closed;superseded_by 留空(據實:非被另一卷取代,是機制整個換掉)
- 卷首補「封存時仍未完成的項目」——逐條查 code 核實,不當作完成:
  真缺口=auth_mtls 從未實作、7.6 self-hosted auth 鏈端到端從未驗;
  另有勾選過期(auth_oauth2 其實已完成)與驗收條件已作廢(與現行 rule 07 牴觸)者
- 修好 14 處引用(原盤點 10 處,實際更多):session-start-load-sdd.sh 內容嚴重過期
  (把已完成 Phase 寫成進行中)→ 改為以 frontmatter 為判準;四份 rules、CLAUDE.md、
  BACKLOG、3-specs/README、deploy.ts、credentials.ts、wrangler.toml 逐一按性質處理
- 額外:system-dev/docs/2-architecture/ 有四個規則檔重複副本(06-08 遷移遺留)
  → 同步修好,否則留一份過期真相(今日第三次撞到「同一資訊兩份副本」的債)
- 02-forbidden.md §2.1 列的三個「待刪違規 TS」其實早已不存在
  → 改標刪除線+禁止重新引入(過期文件同時製造假待辦與假進行中)

驗證:sdd-active-check exit 0(active 仍恰一份=portal-auth);
session-start-load-sdd.sh exit 0;cypher-executor 與 cli typecheck 全綠;
測試 187/188(唯一 fail 為 pre-existing,stash 覆驗相同)。

未動:pre-write-guard.sh 的 KNOWN_SDDS 白名單不含 archive 路徑
(放寬 guardrail 需明確授權,留待決定)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 01:57:34 +08:00
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
Leo 3577e9255c portal-auth P3(#24 #25):Portal UI+/portal/data/* server-side enforce(不 merge,待總管審) (#52) 2026-07-14 07:19:01 +00:00
Leo 1260d8cffb portal-auth P2(#24 #25):portal_user 模型+認證 API(不 merge,待總管審) (#51) 2026-07-14 04:19:54 +00:00
Leo 99da35a885 fix(console): 駕駛艙首頁改讀 live 一手資料,stale 殘骸誠實標示
leo 抱怨「等你的事」錯了好幾天、首頁總覽對不上實際進度。診斷結論:
- dash_wait 沒有活的維護管線(最後寫入 07-04;等leo清單#11 已於 07-05
  銷案=誤判,dashboard 卻繼續掛著)——真相源其實是 InkStoneCo sprint 檔
  「## 等 leo 清單」(progress-guard 每日核實維護)
- dash_task 的 scope:today 沒有日期,07-04 的路線到 07-07 還被當今天的
- 燈號吃到 stale 任務;系統狀況/總覽無 live 信號

修法:
- 等你的事:首選 Gitea sprint 等leo清單(合併最新兩檔——07b 開了、🔴 仍
  掛 07a;需 GITEA_TOKEN secret);讀不到 fallback dash_wait 必標 age+stale
- 今日路線:台北日曆日判 is_today,非今日寫入降級「最後路線(N 天前)」
- 燈號只吃今日寫入任務+心跳+KBDB /health,附 light_reason
- 新增系統狀況(KBDB /health、embed enabled:false 誠實顯示、graph
  /triplets/stats、workflow 數)與總庫規模(entries/wiki_card/triplets)全 live
- 聚合判定抽純函式 lib/console-dashboard-model.ts,vitest 15 案例
  (含真實 sprint 檔形態:劃掉列/ 急迫欄/R1 字母編號/🔴 排序)

驗證:tsc --noEmit 0;vitest 65/66(1 失敗為 main 既有 executor.test.ts,
stash 復原後同樣失敗,非本次引入);parser 對 live sprint-2026-07a/07b
dry-run 撈出 🔴#13 等 9 項 open、清單維護於 31 分鐘前。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015d5jDbuqT5Htwv3Q88XXKk
2026-07-07 11:02:22 +00:00
Claude c24edbbdb6 feat(credential-store-migration): T5 寫入路徑改走 Workers Secrets + D1 ref
POST/PUT /credentials 改寫:密文值 PUT 進 CF Workers per-script Secrets
(唯寫,D19 不持有內容物),D1 credentials 表只存目錄(不含密文)。
secret_ref = CRED_<NAME>_<sha256(api_key)[:8]>,避免跨租戶撞名。
DELETE/GET /credentials 本次不動(仍走舊 KV,T9 範圍),已誠實標注。

新增 CREDENTIALS_DB D1 binding(與 KBDB base 共用同一顆 arcrun-kbdb,
沿用既有 database_id 注入機制)+ CF_SECRETS_API_TOKEN/CF_ACCOUNT_ID
env vars(CF_ACCOUNT_ID 由 deploy.ts 自動注入,同 WORKER_SUBDOMAIN 模式)。

§2.4 選項甲落地:client 不再加密,明文值經 TLS 傳輸,cypher 短暫經手
明文不落地不持金鑰——與舊版 01-tech-stack.md 傳輸格式不同,是本 SDD
對舊格式的刻意取代(§6 Q-b 仍需 leo 明確接受)。

驗證:tsc --noEmit exit 0;vitest 26/27(1 pre-existing 無關失敗)。
端到端:真實部署 leo21c arcrun-cypher-executor,POST/PUT /credentials
成功寫入 Workers Secrets + D1 row(CF API + D1 query 雙重確認),
清理測試資料後 restore wrangler.toml(無帳號專屬 id 殘留 git)。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018D6QoC5waFkcjc2N7csJBB
2026-07-03 23:04:59 +00:00
Leo abf232327c feat(console): API Key 介面錯位收斂為簡單 email+password 登入 (Arcrun#3 發現②)
背景:self-hosted 單租戶下 console 原本那格「API Key」其實只是 namespace
明碼字串,不是註冊制 key(leo 原話:理論上根本沒有 API Key 這件事)。leo 拍板:
換成簡單 email+password 登入頁(自己設一組帳密,不用 OAuth),登入成功後端發
session token 存 localStorage;後端 API 呼叫仍用固定租戶字串打 KBDB(登入系統
只擋外人看頁面,不做多租戶)。

租戶字串收斂:發現①已核實 owner_id='leo' 是 D1 中 458,357 筆資料實際使用的
租戶字串(ak_... 只有 2 筆孤兒資料)。CONSOLE_TENANT 預設 "leo",不製造第三個租戶。

新增:
- cypher-executor/src/routes/console-auth.ts:/console/auth-status、
  /console/setup(首次自助設定帳密,寫入 SESSIONS_KV console:credentials)、
  /console/setup/reset(換帳密,需舊密碼)、/console/login、/console/session
  (驗 session + 回傳固定租戶字串)、/console/logout。密碼用 salt + 3 輪
  SHA-256 雜湊,不存明碼。
- cypher-executor/src/types.ts:Bindings 加 CONSOLE_TENANT。
- cypher-executor/wrangler.toml:[vars] 加 CONSOLE_TENANT = "leo"。
- cypher-executor/src/routes/console.ts:① 卡片從「貼 API Key」改成登入/首次
  設定表單;查詢函式改用登入後端回的固定租戶字串,使用者不再需要知道任何
  namespace 字串。

驗證見 issue #3 留言。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 09:01:04 +00:00
uncle6me-web a410af0b6c fix(cypher): KBDB proxy 指向現役 arcrun-kbdb(舊 fallback kbdb.finally.click 已死)
煙霧測試發現 proxy fallback 寫死舊的 kbdb.finally.click(inkstone 遺留、
回 Missing token),非現役 KBDB。改:
- kbdb-proxy.ts fallback → arcrun-kbdb.uncle6-me.workers.dev(現役、無 auth)
- cypher wrangler.toml [vars] KBDB_BASE_URL 明設現役 URL(self-host fork 覆蓋自己的)

現役 arcrun-kbdb 無 auth middleware,不需 KBDB_INTERNAL_TOKEN。
cli 版本檔 1.3.8→1.3.9(deploy 腳本自動 bump,含 acr kbdb 命令)。

cypher tsc exit 0。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 22:16:18 +08:00
uncle6me-web 95a1462b65 fix(cypher): 改用 global_fetch_strictly_public flag 解 same-zone 1042(revert service binding)
richblack 拍板:service binding(前一 commit)靜態、加/改要重 deploy cypher,廢。
改用 global_fetch_strictly_public compatibility flag——cypher wrangler.toml 加一行,
讓 fetch() 走公網前門,self-hosted 的 same-zone fetch(cypher 與 auth 同在
{sub}.workers.dev zone)也能通。

- wrangler.toml:compatibility_flags 加 global_fetch_strictly_public(移除 SVC_AUTH_*)
- auth-dispatcher.ts / types.ts:還原到 service binding 之前(單純 fetch workers.dev)
- 安全(官方 docs):唯一副作用 self-loop 僅在 fetch 自己 hostname;cypher 不 self-loop
- 官方/self-host 共用同一份 toml:官方本就跨 zone 行為不變,self-host 被修好
- 規範還原:rule 02/03/CLAUDE.md/pre-bash-guard 的 service binding 禁令維持原狀

SDD: credential-primitives-wasm Phase 7(A→廢→B)。tsc exit 0。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 21:44:13 +08:00
uncle6me-web d2048e26a7 fix(cypher): auth primitive 改走 service binding(解 self-hosted CF 1042)
壓測階段 11:self-hosted 帳號 cypher 用 fetch(workers.dev) 打同帳號
auth worker 被 CF 子請求限制回 1042,service account token 換不到 →
表單寫不進 Google Sheets。token/解密鏈本身正常(直打 auth worker 回真 ya29)。

架構演化(richblack 2026-06-06 拍板):用戶產生的是 recipe(KV 資料,不 deploy),
primitive 是平台固定基礎設施、用戶不新增 → 解除「auth primitive 禁 service binding」
舊禁令。service binding 是 CF 內部 RPC,繞開同 zone 522 + 同帳號 workers.dev 1042。

- wrangler.toml:加 SVC_AUTH_STATIC_KEY/SERVICE_ACCOUNT/OAUTH2(已部署者;mtls 未部署留註解)
- auth-dispatcher.ts:binding 優先 svc.fetch(),無 binding fallback fetch(workers.dev)
- types.ts:4 個 optional SVC_AUTH_*
- deploy.ts 無需改:stripOfficialOnlyBindings 不碰 services,tier1 auth 先於 tier2 cypher
- 已驗證 self-hosted(leo21c)13 邏輯零件 binding 實綁成功,auth binding 走同路

規範同步:rule 02 / 03 / CLAUDE.md / pre-bash-guard 例外。SDD: Phase 7。tsc exit 0。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 21:09:49 +08:00
uncle6me-web 5f381a44a6 fix(self-hosted): 修壓測四阻斷項 + 設定分層 + init 非互動
壓測(docs/壓測報告.md)發現 acr init --self-hosted 對任何非官方 CF
帳號都裝不起來,且設定寫死全域單檔 + 強制 TTY。本次一併修:

R2 dead storage 全清(#3#4,registry-canon Phase 1.5 補完):
- cypher-executor wrangler.toml/test.toml/types.ts 移除 WASM_BUCKET binding
- CLI deploy.ts/init.ts/cf-api.ts/config.ts 移除 R2 建立邏輯與 wasm_bucket
- R2 綁信用卡違背「開源免費自架」核心;bucket 名 WASM_BUCKET 本就非法
  → self-hosted 改為只需 Workers + KV(皆免費額度、不綁卡)

fork 帳號部署阻斷(#1#2):
- deploy.ts 新增 stripOfficialOnlyBindings(),注入暫存副本時移除
  [[routes]]/zone_name/[[r2_buckets]]/[ai](fork 沒有 arcrun.dev zone)
- 不刪 repo 內 toml(官方 prod CI 部署仍需 routes),只在 CLI self-hosted 路徑 strip

設定分層 + 非互動(#7#8):
- config.ts loadConfig 改三層:env > 專案層 .arcrun.yaml(就近往上找)> 全域
- init 支援 --account-id/--api-token flag + CLOUDFLARE_* env,缺才互動
- 新增 acr config --where 顯示每個值的來源層(token 自動遮罩)
- gitignore 一併排除 .arcrun.yaml

驗收:tsc 全綠;三層 merge 端對端測試 8/8;strip 對真實 toml 驗證
routes/R2/AI 移除而 name/workers_dev/KV 保留。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 07:22:37 +08:00
uncle6me-web 922a57fe34 arcrun — AI workflow execution engine (clean history)
Self-hosted 開源:WASM 零件 + recipe + cypher-executor,跑在你自己的 Cloudflare。

此為重建的乾淨歷史起點(移除曾誤 commit 的 GCP SA 金鑰,舊歷史保留在
richblack/arcrun 與本地 backup 分支)。含:
- acr init --self-hosted installer(建 KV/R2 + codeload 拉預編譯 wasm + wrangler deploy + seed recipe)
- recipe push 把關(資料外流提醒 + 打通檢查)
- 19 個正當零件預編譯 wasm(claude_api/km_writer/kbdb_upsert_block 排除:違反 DECISIONS §1)
- CLI / cypher-executor / registry / 完整 SDD

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:52:38 +08:00