# 技術棧硬限制

## 三層語言對應（絕對不可混用）

| 層級 | 語言 | 位置 | 職責 |
|-----|------|------|------|
| 零件（Component） | **TinyGo 或 AssemblyScript → WASM** | `registry/components/{name}/` | 所有業務邏輯 |
| 零件 Worker 包裝 | TypeScript（固定模板，不寫業務邏輯） | `.component-builds/{name}/` | WASI shim，stdin/stdout JSON |
| Orchestration Worker | TypeScript + Hono | `cypher-executor/` | HTTP routing、workflow 執行排程、host functions |
| CLI | TypeScript + Node.js | `cli/` | `acr` 指令 |
| Python SDK | Python | `python-sdk/` | HTTP thin wrapper + client 端加密 |
| JS SDK | TypeScript + Web Crypto | `js-sdk/` | HTTP thin wrapper + client 端加密 |
| Frontend | React 19 + Vite + Tailwind v4 | `landing/` | Cloudflare Pages |

## 零件實作規範

### 只有兩種合法語言
- **TinyGo**：`tinygo build -target=wasi -o {name}.wasm main.go`
- **AssemblyScript**：`asc main.ts --target release -o {name}.wasm`

### I/O 模型
- **stdin**：JSON input
- **stdout**：JSON output
- 不用 HTTP server，不監聽 socket（WASI preview1 沒 socket）

### Host Functions（零件呼叫外部能力的唯一管道）
在 `u6u` namespace 下：

| Host Function | 用途 |
|---|---|
| `u6u.http_request` | 發 HTTP 請求 |
| `u6u.kv_get` | 讀 Cloudflare KV（Worker 側依 key 前綴路由到正確 KV） |
| `u6u.secret_get` | 讀 CF Workers Secrets（只放行 `CRED_` 前綴） |
| `u6u.crypto_decrypt` | ⚠️ 已廢除，保留成永遠回失敗的 stub（現役 wasm 仍宣告此 import） |
| `u6u.crypto_sign_rs256` | RSA-SHA256 簽章（PKCS8 bytes 傳入） |

**所有 host function 在 `cypher-executor/src/lib/wasi-shim.ts` 實作**。零件透過 WASI import 使用。

## 資料儲存

| 儲存 | 用途 | Key 格式 |
|-----|------|---------|
| Cloudflare KV `WEBHOOKS` | workflow 定義（cypher binding YAML） | `webhook:{api_key}:{name}` |
| Cloudflare KV `CREDENTIALS_KV` | 加密 credential | `{api_key}:cred:{name}` |
| Cloudflare KV `RECIPES` | auth recipe / 動態 API recipe | `auth_recipe:{service}`, `rec_{hash}` |
| Cloudflare KV `USERS_KV` | 用戶資料 | `user:{api_key}` |
| Cloudflare KV `SESSIONS_KV` | session | `session:{token}` |
| Cloudflare KV `ANALYTICS_KV` | 執行分析 | `execution:{timestamp}:{id}` |
| Cloudflare KV `EXEC_CONTEXT` | workflow 執行中的 context | `ctx:{execution_id}:{node_id}` |
| Cloudflare R2 `WASM_BUCKET` | **只用於用戶自製零件**（Phase 5 以後啟用） | `{api_key}:cmp:{hash}` |

**警告：R2 不存平台內建零件的 WASM**。平台零件已 bundle 進各自的 Worker binary（`[[wasm_modules]]` 或 `import ... assert { type: 'webassembly' }`）。

## Credential 儲存規範

**arcrun 不自管加密金鑰。** credential 明文由 **Cloudflare Workers Secrets** 託管
（per-script secret，掛在用戶自己的 cypher worker 上）：

- secret 名稱 = `CRED_{NAME}_{sha256(api_key)[:8]}`（跨租戶命名隔離）
- D1 `credentials` 表只存**目錄**（api_key / name / service / secret_ref），不存值
- D19：擁有目錄，不擁有內容物——連 owner 都讀不回，只能覆寫/刪除
- 需要 worker 設 `CF_SECRETS_API_TOKEN`（機密，用戶手動 put）+ `CF_ACCOUNT_ID`（自動注入）

> 註：`crypto_decrypt` host function 是**永遠回失敗的 stub**，因為現役三個 `auth_*`
> `.wasm` 仍宣告該 import（缺項會讓 WASM instantiate 失敗）。三個零件重編後即可刪除。
- **取用位置**：Server 端 **WASM primitive**（透過 host function `secret_get(ref)`）
  - cypher-executor TS 不碰業務邏輯，只提供 host function
  - `secret_get` 只放行 `CRED_` 前綴，WASM 讀不到 worker 本身的其他機密
- **傳輸格式**：明文值走 TLS（`POST /credentials`），不做 client 端加密

## 網路部署

- **平台 API（對外）**：`cypher.arcrun.dev`（cypher-executor）
- **Landing**：`arcrun.dev`
- **每個零件 Worker**：
  - **對內（cypher-executor 用來 fetch component，避開同 zone 死鎖）**：`arcrun-{kebab}.{WORKER_SUBDOMAIN}.workers.dev`
    - 例：`arcrun-kbdb-get.uncle6-me.workers.dev`
    - cypher-executor 從 `wrangler.toml [vars] WORKER_SUBDOMAIN` 組出此 URL
  - **對外（可選，零件對全網開放被 curl 用）**：`{kebab}.arcrun.dev`
    - 例：`gmail.arcrun.dev`、`kbdb-get.arcrun.dev`
    - 仍允許保留，但**禁止 cypher-executor 透過此 URL fetch**（會撞同 zone 自循環，見 [docs/incidents/2026-05-13-cypher-outbound-522.md](../../docs/incidents/2026-05-13-cypher-outbound-522.md)）
  - **新增 component worker 部署清單**：`name = "arcrun-{kebab}"` + `[[routes]]` 對外（可選）+ dashboard 啟用 workers.dev（必須）
- **部署工具**：Wrangler
