b223a69884
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>
179 lines
7.6 KiB
TOML
179 lines
7.6 KiB
TOML
name = "arcrun-cypher-executor"
|
||
main = "src/index.ts"
|
||
compatibility_date = "2025-02-19"
|
||
workers_dev = true
|
||
# global_fetch_strictly_public:讓 fetch() 走公網「前門」,解 self-hosted 的 same-zone 1042
|
||
# (2026-06-06 拍板;規則見 .claude/rules/03-component-architecture.md)。
|
||
# ⚠️ 尚未在自架帳號上端到端實測(原 Phase 7.6 遺留驗收缺口)。
|
||
# 病因(官方 docs):self-hosted 的 cypher 與 auth worker 同在 {sub}.workers.dev zone,
|
||
# cypher fetch auth 屬 same-zone fetch,CF 預設擋(1042)。官方 cypher 在 cypher.arcrun.dev、
|
||
# 打 auth 的 *.workers.dev 屬跨 zone 故不踩。此 flag 讓 same-zone fetch 改走公網前門 → 同 zone 也通。
|
||
# 安全(已查證官方 docs):唯一副作用是「Worker fetch 自己 hostname 會 self-loop」,
|
||
# 但 cypher 只打外部 API + sibling auth worker(皆非自己 hostname)→ 不 self-loop。
|
||
compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"]
|
||
|
||
[[kv_namespaces]]
|
||
binding = "EXEC_CONTEXT"
|
||
id = "616967a852eb450a8c01731f71ac8edd"
|
||
|
||
[[kv_namespaces]]
|
||
binding = "WEBHOOKS"
|
||
id = "4d23ccd418414a729bd533ba8e3b341f"
|
||
|
||
[[kv_namespaces]]
|
||
binding = "CREDENTIALS_KV"
|
||
id = "e7f4320f88d343f187e35e3543dd74c9"
|
||
|
||
[[kv_namespaces]]
|
||
binding = "ANALYTICS_KV"
|
||
id = "a43b7997c8e54a34886c2995a853c720"
|
||
|
||
[[kv_namespaces]]
|
||
binding = "RECIPES"
|
||
id = "9cf9db905c6241f78503199e58b2ffe0"
|
||
|
||
[[kv_namespaces]]
|
||
binding = "USERS_KV"
|
||
id = "25bef01d079148919578894434d58c4d"
|
||
|
||
[[kv_namespaces]]
|
||
binding = "SESSIONS_KV"
|
||
id = "455d0505c7534883a4d4985ab8295857"
|
||
|
||
# credential-store-migration T2/T5(D19):credential 目錄表(不含密文)。
|
||
# 與 KBDB base 共用同一顆 arcrun-kbdb D1(比照 kbdb/wrangler.toml 同一個 database id)。
|
||
# self-hosted:deploy.ts injectWranglerConfig 對任何 toml 內的 database_id 賦值行一律注入
|
||
# ctx.d1DatabaseId(既有機制,見 kbdb/wrangler.toml 同款註解),本檔沿用不需額外改 deploy.ts。
|
||
[[d1_databases]]
|
||
binding = "CREDENTIALS_DB"
|
||
database_name = "arcrun-kbdb"
|
||
database_id = "0c580910-e00b-4f8e-9c57-ac54ea52242f" # 官方 prod D1(arcrun-kbdb);self-hosted 由上述機制注入用戶自己的 id
|
||
|
||
# 2026-06-04:移除 WASM_BUCKET R2 binding。R2 wasm 路徑早已 dead(平台零件 = 獨立 Worker,
|
||
# 不從 R2 動態讀),保留只會誤導且 R2 需綁信用卡,與 open source 零費用核心衝突。
|
||
# SDD: .agents/specs/component-registry-canon/tasks.md Phase 1.5(registry 已於 2026-05-07 移除,此為 cypher-executor 補清)
|
||
|
||
[ai]
|
||
binding = "AI"
|
||
|
||
[[services]]
|
||
binding = "SVC_IF_CONTROL"
|
||
service = "arcrun-if-control"
|
||
|
||
[[services]]
|
||
binding = "SVC_SWITCH"
|
||
service = "arcrun-switch"
|
||
|
||
[[services]]
|
||
binding = "SVC_FOREACH_CONTROL"
|
||
service = "arcrun-foreach-control"
|
||
|
||
[[services]]
|
||
binding = "SVC_FILTER"
|
||
service = "arcrun-filter"
|
||
|
||
[[services]]
|
||
binding = "SVC_MERGE"
|
||
service = "arcrun-merge"
|
||
|
||
[[services]]
|
||
binding = "SVC_TRY_CATCH"
|
||
service = "arcrun-try-catch"
|
||
|
||
[[services]]
|
||
binding = "SVC_WAIT"
|
||
service = "arcrun-wait"
|
||
|
||
[[services]]
|
||
binding = "SVC_SET"
|
||
service = "arcrun-set"
|
||
|
||
[[services]]
|
||
binding = "SVC_ARRAY_OPS"
|
||
service = "arcrun-array-ops"
|
||
|
||
[[services]]
|
||
binding = "SVC_STRING_OPS"
|
||
service = "arcrun-string-ops"
|
||
|
||
[[services]]
|
||
binding = "SVC_NUMBER_OPS"
|
||
service = "arcrun-number-ops"
|
||
|
||
[[services]]
|
||
binding = "SVC_DATE_OPS"
|
||
service = "arcrun-date-ops"
|
||
|
||
[[services]]
|
||
binding = "SVC_VALIDATE_JSON"
|
||
service = "arcrun-validate-json"
|
||
|
||
# ai_transform_compile / ai_transform_run 已於 Phase 2(2026-05-29)刪除
|
||
# (Arcrun 是 AI 呼叫的工具,工作流不該內嵌 AI 節點)。對應 worker 已 wrangler delete,
|
||
# service binding 一併移除(否則 deploy 報 referenced Worker not found)。
|
||
|
||
[vars]
|
||
ENVIRONMENT = "production"
|
||
# MULTI_TENANT = "true"
|
||
|
||
# credential-store-migration T3(§2.3 寫入路徑需要的 token+account id):
|
||
# CF_SECRETS_API_TOKEN 是機密,透過 `wrangler secret put CF_SECRETS_API_TOKEN` 設定(不進 toml)。
|
||
# CF_ACCOUNT_ID 非機密(帳號識別碼,不是憑證),比照 WORKER_SUBDOMAIN 由 deploy.ts 自動注入
|
||
# (injectWranglerConfig 新增 CF_ACCOUNT_ID 注入,ctx.accountId 是 init/update 早就有的值)。
|
||
CF_ACCOUNT_ID = ""
|
||
|
||
# Component worker subdomain(workers.dev 帳號 subdomain)
|
||
# cypher-executor fetch component worker 一律走 arcrun-{name}.{WORKER_SUBDOMAIN}.workers.dev
|
||
# 避開同 zone (*.arcrun.dev) 自循環死鎖,見 arcrun.md P0 #9(2026-05-13)
|
||
# Self-hosted fork:改成自己的 CF 帳號 subdomain(Workers & Pages → 你的帳號 → subdomain settings)
|
||
WORKER_SUBDOMAIN = "uncle6-me"
|
||
|
||
# KBDB 基本盤對外 URL(cypher→KBDB:proxy /kbdb/*、recipe-stats、recipe fragment)。
|
||
# 現役 = arcrun-kbdb(workers.dev,無 auth、不需 token)。舊的 kbdb.finally.click 是 inkstone 遺留已死。
|
||
# Self-hosted fork:改成自己部署的 arcrun-kbdb.<你的subdomain>.workers.dev。
|
||
KBDB_BASE_URL = "https://arcrun-kbdb.uncle6-me.workers.dev"
|
||
|
||
# console 固定租戶字串(Arcrun#3 發現②,routes/console-auth.ts)。非機密——self-hosted
|
||
# 架構本就是明碼 namespace。console 登入後端一律用這個字串打 /kbdb/*、/workflows/search
|
||
# (登入系統只擋外人看頁面,不做多租戶)。Self-hosted fork:改成你自己資料實際所在的租戶字串。
|
||
CONSOLE_TENANT = "leo"
|
||
|
||
# 這台實例的**知識命名空間**(Arcrun#108)=知識資料(三元組/卡片/藏書地圖/工作流 KV)
|
||
# 實際掛在哪個 owner_id 底下。**這裡刻意不寫死**:官方 prod 的知識確實在 `CONSOLE_TENANT`
|
||
# (leo)底下,未設就沿用它,行為一字不變。
|
||
#
|
||
# self-hosted 實例由 `acr update` 自動注入(值=你 `~/.arcrun/config.yaml` 的 `api_key`,
|
||
# 也就是 CLI push 工作流、小幫手上傳知識、MCP 查詢用的同一個 namespace),
|
||
# 而且**只在確認那個 namespace 底下真的查得到知識時才寫**(見 cli/src/lib/deploy.ts
|
||
# namespaceHasKnowledge)——避免把一台原本正常的實例指向空的那一格。
|
||
#
|
||
# 為什麼要跟 CONSOLE_TENANT 分開:CONSOLE_TENANT 同時是帳號子 namespace(`{tenant}::portal`)
|
||
# 的組成,改它會讓舊實例登不進去。兩個不同的事實,兩個 var。
|
||
# ARCRUN_NAMESPACE = "your-namespace"
|
||
|
||
# Portal session TTL 秒數(portal-auth P2,#24/#25,routes/portal.ts)。預設 7 天(604800)——
|
||
# issue 要求比 console 30 天短效。停用帳號的即時性不靠這個 TTL(每請求回讀 user record)。
|
||
PORTAL_SESSION_TTL = "604800"
|
||
|
||
# Portal 工作流頁可見性(portal-auth P3,design D-8 定案):admin(預設)/ all / off。
|
||
# 唯讀 list+最近執行、不開 trigger;enforce 在 /portal/data/workflows 路由層。
|
||
PORTAL_SHOW_WORKFLOWS = "admin"
|
||
|
||
# 駕駛艙「等你的事」活資料源(fix/console-dashboard-live-data,2026-07-07):
|
||
# InkStoneCo sprint 檔「## 等 leo 清單」(progress-guard 每日維護)——KBDB dash_wait 沒有
|
||
# 活的維護管線(07-04 之後沒人寫、銷案不同步),不再當首選資料源。
|
||
# ⚠ 還需 `wrangler secret put GITEA_TOKEN`(建議唯讀 scope)才會啟用;缺 token → dashboard
|
||
# 「等你的事」fallback dash_wait 並誠實標 age/stale。Self-hosted fork:清空 GITEA_BASE_URL 即整段停用。
|
||
GITEA_BASE_URL = "https://git.uncle6.me"
|
||
GITEA_SPRINT_REPO = "Leo/InkStoneCo"
|
||
GITEA_SPRINT_DIR = "system-dev/docs/3-specs/autonomy-dispatch"
|
||
|
||
[[routes]]
|
||
pattern = "cypher.arcrun.dev/*"
|
||
zone_name = "arcrun.dev"
|
||
|
||
# Cron triggers — 每分鐘 tick,scheduled handler 掃 WEBHOOKS KV 找註冊 cron_expr 的 workflow
|
||
# 對應 arcrun.md 三-A P1 #3 / src/scheduled.ts
|
||
[triggers]
|
||
crons = ["* * * * *"]
|