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>
7.6 KiB
零件架構與部署模式(必讀,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.dev(cypher-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 shim(POST / → stdin → WASM → stdout → JSON)
src/index.ts 是通用模板,所有零件都用同一份。這個 TS 只做 WASI runtime,不是業務邏輯。
R2(WASM_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) - KV:
WEBHOOKSKV(用戶acr push後存入)
Cypher executor 執行 workflow 時:
- 從 KV 讀出 workflow YAML
- 按 graph 順序解析每個節點的
component - 用 HTTP fetch 打對應的零件 URL
- 把 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 裡的 15 個 [[services]] 綁定就是這個用途。
禁止新增任何 Service Binding。所有新零件(特別是 auth primitive)都走 HTTP URL 路徑。
零件之間怎麼串:實際流程
假設 workflow 是:webhook → gmail(要 auth)→ google_sheets(要 auth)
用戶 POST https://cypher.arcrun.dev/webhooks/named/xxx/trigger
│
▼
cypher-executor(Worker)讀 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
15 個現有的 SVC_* 是歷史遺產(邏輯零件效能優化),不新增。新零件(尤其 auth primitive)一律走 HTTP URL。
禁止重建已存在的零件 Worker
要改 gmail 零件邏輯 → 改 registry/components/gmail/main.go,重新編譯 .wasm,重新部署對應 Worker。不要在 cypher-executor/src/lib/ 或其他地方建「新的 gmail 實作」。
部署一個新零件的完整步驟(auth_static_key 為例)
- 建立
registry/components/auth_static_key/:main.go(TinyGo 實作)component.contract.yaml(IO 規格)
- 編譯:
cd registry/components/auth_static_key && tinygo build -target=wasi -o auth_static_key.wasm main.go - 建立
.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
- 複製
cd .component-builds/auth_static_key && pnpm install && pnpm deploy- Dashboard 啟用 workers.dev URL(必須,否則 cypher-executor fetch 不到):
- Workers & Pages →
arcrun-auth-static-key→ Settings → Domains & Routes → workers.dev → Enable - 啟用後 URL:
arcrun-auth-static-key.{WORKER_SUBDOMAIN}.workers.dev
- Workers & Pages →
- 驗證對外:
curl https://auth-static-key.arcrun.dev→ 應回{ok: true, component: "auth_static_key"} - 驗證對內:
curl https://arcrun-auth-static-key.{WORKER_SUBDOMAIN}.workers.dev→ 應同樣回 200 - cypher-executor 透過
wasmWorkerUrl()自動組對內 URL 呼叫(不用手動註冊)
這是唯一正確的部署流程。任何偏離這個流程的「替代方案」都要先和 richblack 確認。
Step 5 為什麼必須:見 arcrun.md P0 #9(2026-05-13)。cypher-executor 走對內 URL 避開同 zone 自循環死鎖;若 workers.dev 未啟用,cypher-executor fetch 該 component 會 404。