arcrun — AI workflow execution engine (clean history)
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>
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
# arcrun.dev Landing Page — SDD
|
||||
|
||||
> **目標**:給工程師一個門面,可以取得 API Key、管理 Key、探索 API(Swagger),同時藉此獲得會員 Email。
|
||||
> **原則**:先快速可用,不追求功能完整。榮譽牆、Python Lib 是後期。
|
||||
|
||||
---
|
||||
|
||||
## 0. 範圍(這份 SDD 涵蓋)
|
||||
|
||||
| 功能 | 說明 |
|
||||
|---|---|
|
||||
| 首頁 Hero | 說明 arcrun 是什麼,CTA 取得 API Key |
|
||||
| OAuth 登入 | Google / GitHub(用自己的 auth recipe — dogfooding) |
|
||||
| API Key 管理 | 查看、Rotate、Revoke |
|
||||
| Swagger UI | 嵌入 `/api`,讓工程師直接試打 |
|
||||
| 榮譽牆 `/integrations` | 靜態骨架,先列 20 個 recipe,無動態數字 |
|
||||
| 中英切換 | `?lang=zh` |
|
||||
|
||||
**不在本次範圍**:Python lib、Donate 整合、Social Proof 即時數字、貢獻者排行。
|
||||
|
||||
---
|
||||
|
||||
## 1. 技術選型
|
||||
|
||||
### 1.1 框架:Next.js(App Router)
|
||||
|
||||
**選 Next.js 而非 Astro 的原因**:
|
||||
- `finally-click` 已有完整 Next.js + OAuth 回調實作,可直接複用模式
|
||||
- API Key 管理頁有登入態保護需求,Next.js 的 middleware 最直接
|
||||
- Cloudflare Pages 支援 Next.js(`@cloudflare/next-on-pages`)
|
||||
- Astro 在動態路由保護上摩擦較多
|
||||
|
||||
### 1.2 部署:Cloudflare Pages
|
||||
|
||||
```
|
||||
arcrun.dev → Cloudflare Pages(Next.js)
|
||||
API calls → cypher.arcrun.dev(現有 Worker)
|
||||
```
|
||||
|
||||
### 1.3 儲存:現有 cypher-executor CREDENTIALS_KV + 新增 USERS_KV
|
||||
|
||||
現有 cypher-executor Worker 已有:
|
||||
- `CREDENTIALS_KV`:`{api_key}:cred:{name}` 存 tenant credentials
|
||||
- `RECIPES`:auth recipes
|
||||
|
||||
新增需求:
|
||||
- **USERS_KV**:存 user 帳號,key = `user:{provider}:{provider_user_id}`
|
||||
- value: `{ email, display_name, api_key, created_at, provider }`
|
||||
- **SESSIONS_KV**:存 login session,key = `sess:{session_id}`
|
||||
- value: `{ api_key, email, expires_at }`
|
||||
- TTL = 7 天
|
||||
|
||||
兩個 KV 都加到 cypher-executor `wrangler.toml`。
|
||||
|
||||
### 1.4 OAuth — Dogfooding 自己的 Auth Recipe
|
||||
|
||||
登入用 arcrun 自己的 auth recipe:
|
||||
- 不是用 arcrun auth recipe 的 `http_request` runner 去打第三方
|
||||
- 而是 **直接複用 recipe YAML 裡定義的 OAuth App 設定(client_id/secret)**
|
||||
- Worker 端實作 standard OAuth2 authorization_code flow
|
||||
|
||||
**支援提供商(MVP)**:
|
||||
- Google(google_drive recipe 的 OAuth App,或另建 arcrun-login Google App)
|
||||
- GitHub(github recipe 的 OAuth App)
|
||||
|
||||
登入 OAuth App 與 auth recipe 的 OAuth App **可以是同一個**(只要 scopes 包含 `openid profile email`),但更乾淨的做法是登入用獨立的 Google/GitHub App(只要 email scope),auth recipe 用的是資源存取 App。
|
||||
|
||||
**決策:登入用獨立 OAuth App**
|
||||
- `GOOGLE_CLIENT_ID`、`GOOGLE_CLIENT_SECRET` — 只申請 `openid profile email`
|
||||
- `GITHUB_CLIENT_ID`、`GITHUB_CLIENT_SECRET` — 只申請 `read:user` + `user:email`
|
||||
- 以 Worker Secret 方式存入 cypher-executor
|
||||
|
||||
---
|
||||
|
||||
## 2. 頁面結構
|
||||
|
||||
```
|
||||
arcrun.dev/
|
||||
├── / 首頁(Hero + Code snippet + CTA)
|
||||
├── /login 登入頁(Google / GitHub 按鈕)
|
||||
├── /auth/callback OAuth callback(Pages Function)
|
||||
├── /dashboard API Key 管理(需登入)
|
||||
├── /api Swagger UI(嵌入 swagger.json)
|
||||
└── /integrations 服務目錄(靜態,20 個 recipe)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 登入 / OAuth 流程
|
||||
|
||||
### 3.1 流程圖
|
||||
|
||||
```
|
||||
用戶點「Google 登入」
|
||||
→ GET /auth/google/start(Worker 端)
|
||||
→ redirect 到 Google OAuth(state = random, 存 SESSIONS_KV sess:state:{state} = {provider, redirect_back})
|
||||
→ 用戶同意
|
||||
→ GET /auth/callback?code=...&state=...(Worker 端)
|
||||
→ 驗 state
|
||||
→ 用 code 換 access_token(POST google token endpoint)
|
||||
→ 用 token 取 userinfo(GET google userinfo)
|
||||
→ upsert USERS_KV user:{provider}:{provider_id} = {email, display_name, api_key, ...}
|
||||
→ 若新用戶:呼叫現有 /register?email=... 取得 arcrun API Key
|
||||
→ 建立 session:SESSIONS_KV sess:{session_id} = {api_key, email, ...},TTL=7d
|
||||
→ Set-Cookie: arcrun_session={session_id}; HttpOnly; Secure; SameSite=Lax
|
||||
→ redirect 到 /dashboard
|
||||
```
|
||||
|
||||
### 3.2 「若新用戶取得 API Key」的邏輯
|
||||
|
||||
現有 `/register` endpoint 接受 `email` 回傳 `api_key`(HMAC 確定性)。
|
||||
但 landing page 需要的是**真正綁定到用戶帳號的 key**,且用戶可以 rotate/revoke。
|
||||
|
||||
**方案:延伸現有 register endpoint**
|
||||
|
||||
`/register` 目前:`HMAC(secret, email)` → 確定性 api_key,存 CREDENTIALS_KV
|
||||
|
||||
新增邏輯:
|
||||
1. 若 USERS_KV 已有此 user → 直接用記錄裡的 api_key
|
||||
2. 若新 user → 呼叫現有 `/register`(保持 HMAC 確定性邏輯)→ 拿到 api_key → 存入 USERS_KV
|
||||
|
||||
**好處**:不破壞現有 register 邏輯;登入後的 dashboard 顯示的 key = 現有 key = 封測用的 key。
|
||||
|
||||
### 3.3 Rotate / Revoke
|
||||
|
||||
- **Rotate**:產生新 UUID v4 key → 更新 USERS_KV 記錄 → 舊 key 失效(透過把新 key 加到 CREDENTIALS_KV,舊 key 的資料都跟著 API Key 命名空間走,所以 credentials 會留在舊 namespace)
|
||||
- 簡化版:Rotate 後顯示提示「您的 workflow credentials 已和舊 Key 分離,請重新設定」
|
||||
- **Revoke**:USERS_KV 記錄 `revoked: true` → Worker middleware 拒絕此 key
|
||||
|
||||
---
|
||||
|
||||
## 4. API 端點(新增到 cypher-executor)
|
||||
|
||||
```
|
||||
GET /auth/google/start → redirect 到 Google OAuth
|
||||
GET /auth/github/start → redirect 到 GitHub OAuth
|
||||
GET /auth/callback?code=&state= → 換 token、建立 session
|
||||
POST /auth/logout → 清 session cookie
|
||||
GET /me → 回傳當前登入用戶資訊(需 session cookie 或 API Key)
|
||||
PUT /me/api-key/rotate → 產生新 key
|
||||
DELETE /me/api-key → Revoke(標記撤銷)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 前端頁面設計
|
||||
|
||||
### 5.1 首頁(/)
|
||||
|
||||
```
|
||||
Hero:
|
||||
Stop fighting OAuth.
|
||||
One API key. Every service. Works anywhere.
|
||||
|
||||
[Get API Key — Free] [View on GitHub]
|
||||
|
||||
Before/After:
|
||||
40 行 OAuth 程式碼 → auth.bind("google_drive")
|
||||
|
||||
Code Demo(三個 tab):
|
||||
Python / JavaScript / HTTP(n8n 用戶)
|
||||
|
||||
[Get Free API Key] 按鈕
|
||||
```
|
||||
|
||||
### 5.2 登入頁(/login)
|
||||
|
||||
```
|
||||
arcrun
|
||||
|
||||
登入或建立帳號
|
||||
|
||||
[Continue with Google]
|
||||
[Continue with GitHub]
|
||||
|
||||
不需要信用卡。API Key 立即可用。
|
||||
```
|
||||
|
||||
### 5.3 Dashboard(/dashboard)
|
||||
|
||||
```
|
||||
歡迎,{display_name}
|
||||
|
||||
您的 API Key
|
||||
┌────────────────────────────────┐
|
||||
│ ak_xxxxxxxxxxxxxxxxxxxx [複製] │
|
||||
└────────────────────────────────┘
|
||||
|
||||
[Rotate Key] [Revoke Key]
|
||||
|
||||
使用說明:
|
||||
Authorization: Bearer {key}
|
||||
或 X-Arcrun-API-Key: {key}
|
||||
|
||||
[登出]
|
||||
```
|
||||
|
||||
### 5.4 Swagger UI(/api)
|
||||
|
||||
- 嵌入 `<SwaggerUIBundle>` JS(CDN)
|
||||
- `url: 'https://cypher.arcrun.dev/swagger.json'`(現有 Worker 已有 `/docs` openapi endpoint)
|
||||
- 頂部說明:「這是 arcrun 的原始 API。Python / JS lib 是它的包裝,任何能發 HTTP request 的工具都能直接用。」
|
||||
|
||||
### 5.5 服務目錄(/integrations)
|
||||
|
||||
- 靜態列出 20 個 auth recipe(從 seed data 產生)
|
||||
- 每個 recipe:名稱、認證方式(static_key / service_account)、所需 credentials
|
||||
- 「找不到你要的服務?開 PR 貢獻 Recipe」CTA
|
||||
|
||||
---
|
||||
|
||||
## 6. 檔案結構
|
||||
|
||||
```
|
||||
arcrun/landing/ ← 新 Next.js 專案
|
||||
├── app/
|
||||
│ ├── layout.tsx
|
||||
│ ├── page.tsx 首頁
|
||||
│ ├── login/
|
||||
│ │ └── page.tsx
|
||||
│ ├── dashboard/
|
||||
│ │ ├── page.tsx
|
||||
│ │ └── middleware.ts (或 root middleware)
|
||||
│ ├── api-docs/
|
||||
│ │ └── page.tsx Swagger UI
|
||||
│ └── integrations/
|
||||
│ └── page.tsx
|
||||
├── middleware.ts 保護 /dashboard(讀 cookie)
|
||||
├── lib/
|
||||
│ └── auth.ts session helpers
|
||||
├── public/
|
||||
├── next.config.ts
|
||||
├── package.json
|
||||
└── wrangler.toml CF Pages 設定
|
||||
```
|
||||
|
||||
cypher-executor 新增:
|
||||
```
|
||||
arcrun/cypher-executor/src/routes/
|
||||
├── auth.ts ← 新增(OAuth start/callback/logout/me)
|
||||
```
|
||||
|
||||
cypher-executor wrangler.toml 新增:
|
||||
```toml
|
||||
[[kv_namespaces]]
|
||||
binding = "USERS_KV"
|
||||
id = "<to be created>"
|
||||
|
||||
[[kv_namespaces]]
|
||||
binding = "SESSIONS_KV"
|
||||
id = "455d0505c7534883a4d4985ab8295857"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 環境變數 / Secrets
|
||||
|
||||
### cypher-executor(Worker Secrets)
|
||||
|
||||
```
|
||||
GOOGLE_CLIENT_ID Google OAuth App client_id(僅 openid profile email scope)
|
||||
GOOGLE_CLIENT_SECRET Google OAuth App client_secret
|
||||
GITHUB_CLIENT_ID GitHub OAuth App client_id(read:user + user:email scope)
|
||||
GITHUB_CLIENT_SECRET GitHub OAuth App client_secret
|
||||
SESSION_SECRET 隨機 32 bytes,用於 HMAC session ID(或直接用 UUID)
|
||||
```
|
||||
|
||||
### landing(Pages Environment Variables)
|
||||
|
||||
```
|
||||
NEXT_PUBLIC_API_BASE https://cypher.arcrun.dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 實作步驟(Checklist)
|
||||
|
||||
### Phase 1:cypher-executor 後端擴充
|
||||
|
||||
- [x] `wrangler kv:namespace create USERS_KV` → 填入 wrangler.toml (id: 25bef01d079148919578894434d58c4d)
|
||||
- [x] `wrangler kv:namespace create SESSIONS_KV` → 填入 wrangler.toml (id: 455d0505c7534883a4d4985ab8295857)
|
||||
- [x] 建立 `arcrun/cypher-executor/src/routes/auth.ts`
|
||||
- [x] GET `/auth/google/start`
|
||||
- [x] GET `/auth/github/start`
|
||||
- [x] GET `/auth/callback`(換 token → userinfo → upsert USERS_KV → 建 session → Set-Cookie → redirect)
|
||||
- [x] POST `/auth/logout`
|
||||
- [x] GET `/me`(讀 session cookie 或 API Key header)
|
||||
- [x] PUT `/me/api-key/rotate`
|
||||
- [x] DELETE `/me/api-key`(revoke)
|
||||
- [x] 在 `src/index.ts` 掛載 `authRouter`
|
||||
- [ ] `wrangler secret put GOOGLE_CLIENT_ID` 等 4 個 secrets ← **用戶需自建 Google/GitHub OAuth App**
|
||||
- [x] `wrangler deploy` ← 已部署(Worker version 7877857b)
|
||||
|
||||
### Phase 2:Next.js Landing 專案
|
||||
|
||||
- [x] `npx create-next-app@latest arcrun/landing --typescript --tailwind --app`
|
||||
- [x] 設定 `@cloudflare/next-on-pages`(Next.js 15 + .npmrc legacy-peer-deps)
|
||||
- [ ] 建立 `middleware.ts`(保護 /dashboard,讀 `arcrun_session` cookie)← 待做
|
||||
- [x] 首頁(`app/page.tsx`):Hero + Code Demo tab + CTA
|
||||
- [x] 登入頁(`app/login/page.tsx`):Google / GitHub 按鈕(href 到 cypher.arcrun.dev/auth/google/start)
|
||||
- [x] Dashboard(`app/dashboard/page.tsx`):顯示 API Key,Rotate / Revoke 按鈕
|
||||
- [x] Swagger UI(`app/api-docs/page.tsx`):client component,動態 import swagger-ui CDN
|
||||
- [x] 服務目錄(`app/integrations/page.tsx`):靜態,列 20 個 recipe
|
||||
- [ ] 中英切換 ← 低優先,可延後
|
||||
- [x] `wrangler pages deploy` → https://42a8d302.arcrun-landing.pages.dev
|
||||
- [ ] Cloudflare dashboard 設定 arcrun.dev custom domain → arcrun-landing Pages project
|
||||
|
||||
### Phase 3:驗收(待 OAuth Secrets 填入後)
|
||||
|
||||
- [ ] Google / GitHub OAuth 完整流程(登入 → dashboard → 看到 key)
|
||||
- [ ] Rotate:新 key 出現
|
||||
- [ ] Revoke:舊 key 的 API 呼叫回傳 401
|
||||
- [ ] Swagger UI 正常載入,可試打 `/health`
|
||||
- [x] `/integrations` 正確列出 20 個服務
|
||||
|
||||
---
|
||||
|
||||
## 9. 待決事項(開始實作前確認)
|
||||
|
||||
| 問題 | 預設決策 |
|
||||
|---|---|
|
||||
| Google OAuth App 是否要另建(只有 email scope)? | 是,另建;auth recipe 的 App 不動 |
|
||||
| Rotate 後舊 credentials 是否遷移? | 不遷移,顯示提示 |
|
||||
| Domain arcrun.dev 是否已購入且在 Cloudflare? | 假設是(wrangler.toml 有設 zone_name) |
|
||||
| 登入後 redirect 預設到 /dashboard | 是,可從 `?redirect=` 覆寫 |
|
||||
Reference in New Issue
Block a user