// KBDB Base — atomic universal table worker (arcrun self-hosted data layer + official core). // SDD: .agents/specs/arcrun/kbdb-base/design.md // // Base = D1 only (free, no credit card): entries / templates / records + LIKE search + recipe-stats. // Optional modules (NOT in this base): embed (Vectorize+AI binding, semantic search), triplet (separate repo). import { Hono } from 'hono'; import type { Bindings } from './types'; import { entryRoutes } from './routes/entries'; import { templateRoutes } from './routes/templates'; import { recordRoutes } from './routes/records'; import { recipeStatRoutes } from './routes/recipe-stats'; import { embedRoutes } from './routes/embed'; import { mapRoutes } from './routes/map'; import { executionLogRoutes } from './routes/execution-log'; const app = new Hono<{ Bindings: Bindings }>(); // t115 global auth guard(三修=總管手改,fail-closed 到底). // 為什麼不留讀取的寬容窗口:leo 07-28 實證的洞就是「知道網址即可讀走全部知識」—— // 讀取放行等於洞沒補。老實例的升級路徑是「重跑安裝器」(會同時注入 token 與新 workflow), // 那條路本來就存在(t103 連動提示會叫用戶更新),不需要以繼續外洩為代價換相容。 // Health(/ 與 /health)永遠豁免:daemon 的雲端版本偵測與監控要打得到。 app.use('*', async (c, next) => { const path = new URL(c.req.url).pathname; if (path === '/' || path === '/health') return next(); const token = c.env.KBDB_INTERNAL_TOKEN; if (!token) { // 沒有 token=這個實例還沒封口。一律拒絕(含讀取),並在訊息裡告訴維運怎麼修。 console.warn('[kbdb] KBDB_INTERNAL_TOKEN 未設定——全部請求拒絕,請重跑安裝器以注入金鑰'); return c.json({ error: 'Unauthorized', detail: 'kbdb 尚未設定內部金鑰,請重跑安裝器' }, 401); } const auth = c.req.header('Authorization'); if (!auth || auth !== `Bearer ${token}`) { return c.json({ error: 'Unauthorized' }, 401); } return next(); }); app.get('/', (c) => c.json({ service: 'arcrun-kbdb', tier: 'base', status: 'ok' })); app.get('/health', (c) => c.json({ ok: true })); app.route('/entries', entryRoutes); app.route('/templates', templateRoutes); app.route('/records', recordRoutes); app.route('/recipe-stats', recipeStatRoutes); // 執行紀錄(KV 額度事故修復,2026-08-07):cypher-executor fire-and-forget 寫、 // executions.ts / portal-data.ts 讀,取代舊的 ANALYTICS_KV。 app.route('/execution-log', executionLogRoutes); // Optional embed module admin (backfill). Route mounts unconditionally; the handler // honestly 409s when the embed binding is off (base 對內容語意無知,只認通用 embed 旗標)。 app.route('/embed', embedRoutes); // 藏書地圖(library-map SDD M2 / Arcrun#39):聚合 SQL 只准住基本盤(D6 推論), // recompute+讀端都在這裡;ingest workflow 只透過 HTTP 呼叫(A 類接 B 類 API,牆不破)。 app.route('/map', mapRoutes); export default app;