Files
Arcrun/system-dev/docs/2-architecture/03-component-architecture.md
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

8.8 KiB
Raw Permalink Blame History

零件架構與部署模式(必讀,CC 最常搞錯的地方)

第一核心概念:每個 WASM 零件 = 一個獨立 Worker = 兩個 URL

不是從 R2 即時載入 WASM 執行。 不是用 service binding 串零件。 不是一個 Worker 裡跑多個零件。

:每個零件都是獨立部署的 Worker,每個都有兩個 URL

URL 類型 Pattern 用途
對內(cypher-executor 用) arcrun-{kebab}.{WORKER_SUBDOMAIN}.workers.dev cypher-executor fetch component 走這個,避開同 zone 自循環死鎖(P0 #9)
對外(直接 curl 用,可選) {kebab}.arcrun.dev 用戶單獨打 component 測試或 self-hosted 用法

例:kbdb_get 零件:

  • 對內:arcrun-kbdb-get.uncle6-me.workers.devcypher-executor 走這個)
  • 對外:kbdb-get.arcrun.dev(用戶 / 直 curl

為什麼這樣設計CF Workers 「同 zone 自循環防護」會讓綁 cypher.arcrun.dev/* 的 cypher-executor fetch 同 zone *.arcrun.dev 撞 522。完整事件報告:docs/incidents/2026-05-13-cypher-outbound-522.md。改走 workers.dev 子域繞過。

零件 Worker 的結構

registry/components/{name}/
├── main.go                  ← TinyGo 原始碼(實際零件邏輯)
├── component.contract.yaml  ← 輸入/輸出規格
└── {name}.wasm              ← TinyGo 編譯產物

部署時,透過 component-worker-template/ 把 WASM 包進一個 Hono Worker

.component-builds/{name}/
├── package.json
├── wrangler.toml            ← name = "arcrun-{name}"route = "{name}.arcrun.dev"
├── component.wasm           ← 從 registry/components/{name}/ 複製過來
└── src/index.ts             ← 固定的 WASI shimPOST / → stdin → WASM → stdout → JSON

src/index.ts 是通用模板,所有零件都用同一份。這個 TS 只做 WASI runtime,不是業務邏輯。


R2WASM_BUCKET)的真正用途

R2 存 WASM 只是用戶自製零件上傳用的。

平台內建零件不從 R2 讀取——它們在部署時就已 bundle 進 Worker 的 binary(透過 [[wasm_modules]]import with assert { type: 'webassembly' })。

Phase 5(封測後)才會啟用「用戶 push 自製零件 → 存 R2 → 動態執行」這條路徑。

結論:當 CC 問「怎麼從 R2 取出 WASM」時,幾乎都是走錯路徑。平台零件是獨立 Worker,走 HTTP 呼叫,不是 R2 動態載入。


Cypher binding 的正確定義

Cypher binding 不是 Cloudflare 的任何 binding 機制。

Cypher binding 是一張 YAML 清單,內容是「一個 workflow 要呼叫哪些零件 URL」。存放在:

  • 本地:workflow.yaml(用戶寫的 workflow
  • KVWEBHOOKS KV(用戶 acr push 後存入)

Cypher executor 執行 workflow 時:

  1. 從 KV 讀出 workflow YAML
  2. 按 graph 順序解析每個節點的 component
  3. 用 HTTP fetch 打對應的零件 URL
  4. 把 output 當作下個節點的 input

這就是 Cypher binding——用 HTTP URL 把零件串起來,存在 YAML/KV 裡

為什麼不能用 Service Binding

Service binding 需要 wrangler.toml 裡寫死 [[services]],且要 redeploy 才生效。arcrun 是類 n8n 服務,用戶建立新 workflow 時絕對不可能要他 redeploy。所以 workflow 層一定要 HTTP。

Service Binding 的僅存合法用途

只在 cypher-executor平台內建邏輯零件之間保留(效能優化,避免公網往返)。看 cypher-executor/wrangler.toml 裡的 13 個 [[services]] 綁定就是這個用途。

禁止新增任何 Service Binding。所有新零件(含 auth primitive)都走 HTTP URL 路徑。

same-zone 1042 的解(來源:credential-primitives-wasm Phase 72026-06-06;該卷已封存於 system-dev/docs/3-specs/archive/但本段規則仍現行有效):self-hosted 的 cypher 與 auth worker 同在 {sub}.workers.dev zonecypher fetch() 打 auth 觸發 CF 1042(官方 docs:「fetch from another Worker on the same zone」;官方 cypher 在 cypher.arcrun.dev、打 *.workers.dev 屬跨 zone 故不踩——非官方有 flag)。解法不是 service binding(評估後廢:靜態、加/改要重 deploy),而是 cypher wrangler.toml 加 global_fetch_strictly_public flag——讓 same-zone fetch 走公網前門 → 同 zone 也通。auth 維持 HTTP fetch、不加 binding。官方加此 flag 行為不變(本就跨 zone),self-host 被修好 → 官方與 self-host 共用同一份 toml

仍禁止:為用戶自製 / 服務專屬零件gmail-workernotion-worker 之類)新增 binding——那些是 recipe 的事,不該有 binding。workflow 層(用戶串零件)一律 HTTP URL 不變。


零件之間怎麼串:實際流程

假設 workflow 是:webhook → gmail(要 auth)→ google_sheets(要 auth

用戶 POST https://cypher.arcrun.dev/webhooks/named/xxx/trigger
        │
        ▼
cypher-executorWorker)讀 workflow YAML
        │
        ├─ 節點 1: component = gmail
        │    a. 查 auth_recipe:gmail → primitive = static_key
        │    b. HTTP POST https://auth-static-key.arcrun.dev
        │         { action: "authenticate", api_key, service: "gmail" }
        │       → 回傳 { auth_headers: { Authorization: "Bearer ..." } }
        │    c. HTTP POST https://gmail.arcrun.dev
        │         { to, subject, body, _auth_headers }
        │       → gmail 零件 Worker 執行 WASM → 回傳 { success, data }
        │
        └─ 節點 2: component = google_sheets
             ... 相同模式

cypher-executor 本身不做 credential 解密、不做 JWT signing、不做 auth header 組裝。這些全在 auth primitive WASM 零件內,cypher-executor 只負責 HTTP routing 和工作流排程。


實際禁令(CC 看這裡)

禁止在 registry/components/ 下建立 TypeScript 檔案

零件邏輯一律 TinyGo 或 AssemblyScript,編譯成 .wasm

禁止把 auth 邏輯寫在 cypher-executor/src/

credential 解密、JWT signing、template 展開({{secret.X}})全部屬於 auth primitive WASM 零件的職責。cypher-executor 只呼叫它們。

禁止問「怎麼從 R2 取 WASM」

平台內建零件不從 R2 取。每個零件已部署成獨立 Worker,走 HTTP URL。用戶自製零件才用 R2(Phase 5,未啟用)。

禁止新增 Service Binding

13 個現有的 SVC_*(邏輯零件)是歷史遺產,不新增。新零件(含 auth primitive)一律走 HTTP URL。self-hosted 的 same-zone 1042 用 global_fetch_strictly_public flag 解,不靠新增 binding(見上「合法用途」段,Phase 7)。

禁止重建已存在的零件 Worker

要改 gmail 零件邏輯 → 改 registry/components/gmail/main.go,重新編譯 .wasm,重新部署對應 Worker。不要cypher-executor/src/lib/ 或其他地方建「新的 gmail 實作」。


部署一個新零件的完整步驟(auth_static_key 為例)

  1. 建立 registry/components/auth_static_key/
    • main.goTinyGo 實作)
    • component.contract.yamlIO 規格)
  2. 編譯:cd registry/components/auth_static_key && tinygo build -target=wasi -o auth_static_key.wasm main.go
  3. 建立 .component-builds/auth_static_key/
    • 複製 component-worker-template/src/index.ts
    • 複製 component-worker-template/package.json
    • 新建 wrangler.toml
      name = "arcrun-auth-static-key"
      main = "src/index.ts"
      compatibility_date = "2025-02-19"
      [vars]
      COMPONENT_ID = "auth_static_key"
      [[routes]]
      pattern = "auth-static-key.arcrun.dev/*"
      zone_name = "arcrun.dev"
      
    • 複製 auth_static_key.wasm 到此目錄為 component.wasm
  4. cd .component-builds/auth_static_key && pnpm install && pnpm deploy
  5. Dashboard 啟用 workers.dev URL(必須,否則 cypher-executor fetch 不到):
    • Workers & Pages → arcrun-auth-static-key → Settings → Domains & Routes → workers.dev → Enable
    • 啟用後 URLarcrun-auth-static-key.{WORKER_SUBDOMAIN}.workers.dev
  6. 驗證對外:curl https://auth-static-key.arcrun.dev → 應回 {ok: true, component: "auth_static_key"}
  7. 驗證對內:curl https://arcrun-auth-static-key.{WORKER_SUBDOMAIN}.workers.dev → 應同樣回 200
  8. cypher-executor 透過 wasmWorkerUrl() 自動組對內 URL 呼叫(不用手動註冊)

這是唯一正確的部署流程。任何偏離這個流程的「替代方案」都要先和 richblack 確認。

Step 5 為什麼必須:見 arcrun.md P0 #92026-05-13)。cypher-executor 走對內 URL 避開同 zone 自循環死鎖;若 workers.dev 未啟用,cypher-executor fetch 該 component 會 404。