6ee6fee8b9
credentials/ was a leftover duplicate — all credential routes already live in cypher-executor/src/routes/credentials.ts. Adds the SDD protocol, tech-stack, forbidden-list, component-architecture, and progress rules that guide Phase 1-6 refactors. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
148 lines
6.3 KiB
Markdown
148 lines
6.3 KiB
Markdown
# 零件架構與部署模式(必讀,CC 最常搞錯的地方)
|
||
|
||
## 第一核心概念:每個 WASM 零件 = 一個獨立 Worker = 一個公開 URL
|
||
|
||
**不是**從 R2 即時載入 WASM 執行。
|
||
**不是**用 service binding 串零件。
|
||
**不是**一個 Worker 裡跑多個零件。
|
||
|
||
**是**:每個零件都是獨立部署的 Worker,每個都有自己的 URL,例如:
|
||
- `https://if-control.arcrun.dev`
|
||
- `https://gmail.arcrun.dev`
|
||
- `https://auth-static-key.arcrun.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:`WEBHOOKS` 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` 裡的 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 為例)
|
||
|
||
1. 建立 `registry/components/auth_static_key/`:
|
||
- `main.go`(TinyGo 實作)
|
||
- `component.contract.yaml`(IO 規格)
|
||
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`:
|
||
```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. 驗證:`curl https://auth-static-key.arcrun.dev` → 應回 `{ok: true, component: "auth_static_key"}`
|
||
6. 在 cypher-executor 的 auth-dispatcher 註冊對應 URL(或用慣例 `{name}.arcrun.dev`)
|
||
|
||
**這是唯一正確的部署流程**。任何偏離這個流程的「替代方案」都要先和 richblack 確認。
|