fix(self-hosted): 身份改明碼 namespace(.env)+ path-based webhook trigger

壓測 §7.2:seed 通了但 creds push/push/runtime 全卡「缺少 api_key」——
self-hosted init 從不發 api_key,但三條路徑都建在多租戶 {api_key}:cred 模型上。

richblack 拍板:self-hosted 不需祕密 api_key,只需 namespace(分區標籤):
- config:ENV_MAP 加 NAMESPACE/ENCRYPTION_KEY + .env 自動載入(無 dotenv 依賴)
- namespace 明碼用戶自填(.env NAMESPACE=leo),沿用 api_key 路徑 → 零分叉
- encryption_key 用戶 .env 自填(工具不生成不 hash),須與 worker secret 一致
- creds/push/init:缺值改引導設 .env,不再叫去 register
- runtime:cypher 加 POST /webhooks/named/:ns/:name/trigger(namespace 走 path,
  公開表單免 header);與 header 路徑共用 triggerNamed,不分叉
- push:self-hosted 顯示 path-based 公開 webhook URL

誠實限制:namespace 明碼非密碼;防外部呼叫靠 webhook 保護(mindset §6)。
CLI 1.3.0 → 1.3.1。SDD: self-hosted-init.md §7.7。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
uncle6me-web
2026-06-06 17:30:16 +08:00
parent 772e4a12ff
commit 44b915554b
9 changed files with 148 additions and 29 deletions
+19 -3
View File
@@ -22,6 +22,7 @@
*/
import { Hono } from 'hono';
import type { Context } from 'hono';
import type { Bindings } from '../types';
import { executeWebhookGraph } from '../actions/webhook-handlers';
import { writeExecutionVerdict } from '../actions/execution-logger';
@@ -130,14 +131,29 @@ webhooksNamedRouter.post('/webhooks/named', async (c) => {
}, 201);
});
// POST /webhooks/named/:name/trigger — 觸發執行
// POST /webhooks/named/:name/trigger — 觸發執行api_key 走 header;標準/向後相容)
webhooksNamedRouter.post('/webhooks/named/:name/trigger', async (c) => {
const apiKey = c.req.header('X-Arcrun-API-Key');
if (!apiKey) {
return c.json({ error: '缺少 X-Arcrun-API-Key header' }, 401);
}
return triggerNamed(c, apiKey, c.req.param('name'));
});
const name = c.req.param('name');
// POST /webhooks/named/:ns/:name/trigger — namespace 走 URL path(給公開表單用)。
// self-hosted namespace 是明碼分區標籤(非密碼),故可放 path 讓無法帶 header 的公開呼叫者觸發。
// 要防外部濫用 → 對 webhook 加保護(mindset §6);arcrun 不做授權判斷(mindset §3)。
// SDD: sdk-and-website/self-hosted-init.md(壓測 §7.2 第3點 runtime 觸發)
webhooksNamedRouter.post('/webhooks/named/:ns/:name/trigger', async (c) => {
return triggerNamed(c, c.req.param('ns'), c.req.param('name'));
});
// 共用觸發邏輯(header 路徑與 path 路徑都用,避免分叉)
async function triggerNamed(
c: Context<{ Bindings: Bindings }>,
apiKey: string,
name: string,
) {
const raw = await c.env.WEBHOOKS.get(kvKey(apiKey, name), 'text');
if (!raw) {
return c.json({ error: `找不到 workflow "${name}",請先執行 acr push` }, 404);
@@ -192,7 +208,7 @@ webhooksNamedRouter.post('/webhooks/named/:name/trigger', async (c) => {
);
return c.json(result, result.success ? 200 : 500);
});
}
// GET /webhooks/named — 列出當前 api_key 下所有 workflow
webhooksNamedRouter.get('/webhooks/named', async (c) => {