# km-wiki-ingest — 部署(cypher workflow 版,走 cypher binding) > **架構(D28)**:知識庫 ingest =**跑在 cypher-executor 上的 workflow**,跨-worker 呼叫全走 > **cypher binding**(零件節點,由 component-loader 解析),**零 Service Bindings**。 > 汰換舊的 standalone `arcrun-km-wiki-drainer`(`drainer/`,已誤用 service binding → 見 `drainer/DEPRECATED.md`)。 > > **本輪不部署**(等總管/leo 審架構)。以下是過閘後的部署順序。 ## 0) 先決條件(兩個平台端小改,皆本分支已含,需各自 gated 部署) 這兩處是「讓 workflow 能純走 cypher binding」的最小平台補丁,**不是** ingest 專屬邏輯: 1. **cypher-executor:`code` 零件進 loader 白名單** - 檔:`cypher-executor/src/lib/component-loader.ts` → `WASM_HTTP_RUNNER_IDS` 加 `'code'`(本分支已加 1 行)。 - 效果:workflow 節點 `component: code` → 解析成 `arcrun-code.{WORKER_SUBDOMAIN}.workers.dev`(cypher binding)。 - 部署:`cd cypher-executor && npx wrangler deploy`(要 leo 過閘;動到共用 executor)。 - **替代(不改 executor)**:把兩個 workflow 裡 `component: code` 改成 code 的完整 URL `component: "https://arcrun-code..workers.dev"`(走 loader step 2 外部 URL)。省一次 executor 部署, 但少了「canonical 零件」的乾淨語義。建議用白名單版(這才是 D28 說的 cypher binding)。 2. **KBDB base:`POST /entries/ingest`(server 端冪等 upsert)** - 檔:`kbdb/src/routes/entries.ts`(本分支已加)。語義對稱 graph 的 `/triplets/ingest`: page_name 當鍵、`content_hash` 同 → skip、不同/無 → update、無則 create。 - 為何需要它:cypher 的 flow DSL **無資料條件分支**(只有 ON_SUCCESS/ON_FAIL/FOREACH), 無法在 workflow 內做「查→比對→create 或 patch」三岔。把冪等推到 server 端,workflow 只打一發。 - 部署:`cd kbdb && npx wrangler deploy`(要過閘)。 > ⚠️ **subdomain 對齊**:兩個 workflow 內的 URL 用 `uncle6-me`(官方 `WORKER_SUBDOMAIN`,見 `cypher-executor/wrangler.toml`)。 > 若你的 arcrun-kbdb / kbdb-graph-plugin / arcrun-code 部在別的 workers.dev subdomain(例 `leo21c`), > 把兩檔的 URL 一起改。**舊 standalone drainer 誤用 `leo21c` 是坑之一。** ## 1) CLI 設定 ```bash # self-hosted(namespace 明碼分區): acr config set mode self-hosted acr config set cypher_url https://cypher.arcrun.dev # 或你自架的 executor echo "NAMESPACE=leo" >> .env # standard(平台多租戶):acr init 取 api_key ``` ## 2) 設 Gitea token(機密;節點以 {{credential.gitea_token}} 引用) ```bash acr creds set gitea_token <你的 GITEA_TOKEN> # 讀 Leo/notes 用 ``` > graph 的 `X-Arcrun-API-Key: leo` 是 namespace 字串(非機密),已內嵌節點,不需設 credential。 ## 3) 部署 Phase 0 drain(cron 觸發) ```bash acr push workflow.yaml ``` - `watch_cron` 的 `cron_expr: "*/2 * * * *"` 會被 `webhooks-named` 的 `extractCronExpr` 抓出, 寫進 `cron-idx:_all`;cypher `scheduled()` 每分鐘比對觸發本 workflow(**不需另設 CF cron**)。 - 游標**不需 seed**:首跑 `load_cursor` GET 回空 → `pick_card` 從頭;`save_cursor` 首 tick 自動 create。 - 手動補跑一 tick(驗收):`acr run km_wiki_ingest_drain` ## 4) 部署穩態 delta(Gitea webhook 觸發) ```bash acr push workflow.delta.yaml ``` - 取得 trigger URL(self-hosted 例): `https://cypher.arcrun.dev/webhooks/named/leo/km_wiki_ingest_delta/trigger` - Gitea repo `Leo/notes` → **Settings → Webhooks → Add Webhook → Gitea**: - Target URL:上面的 trigger URL - Content-Type:`application/json` - Trigger:Push events - (standard 模式改帶 `X-Arcrun-API-Key` header;self-hosted 免 header,namespace 在 path) - 這是 Gitea→Cloudflare(cypher),**非 GitHub Actions** → 不觸 GitHub flag(D4/D20)。 ## 5) 驗收 ```bash # entry 落地? curl -s "https://arcrun-kbdb.uncle6-me.workers.dev/entries?owner_id=leo&entry_type=wiki_card&limit=5" | jq '.count' # triplet 落地? curl -s "https://kbdb-graph-plugin.uncle6-me.workers.dev/graph?owner_id=leo" | jq '.nodes|length' # 游標? curl -s "https://arcrun-kbdb.uncle6-me.workers.dev/entries?owner_id=leo&page_name=cursor:km_wiki_ingest_drain:Leo/notes" | jq '.entries[0].content' ``` 冪等自證:連跑兩次 `acr run km_wiki_ingest_drain`,第二次同卡 entry `action:skipped`、graph per-source no-op。 ## 6) 舊 standalone drainer 退役 - 全 drain 完 + delta webhook 穩定收斂後,退役 `arcrun-km-wiki-drainer`:`wrangler delete arcrun-km-wiki-drainer`。 - 保留 `drainer/` 原始碼一個週期(含 `DEPRECATED.md`)供對照,確認新版逐卡輸出與舊版一致再刪。 - 見 `drainer/DEPRECATED.md`。 ## 節點圖速覽 **drain(cron)**:`watch_cron(cron)` → `load_cursor(http GET kbdb)` → `list_cards(http GET Gitea tree)` → `pick_card(code)` → `fetch_card(http GET Gitea raw)` → `parse_card(code)` → `upsert_entry(http POST kbdb /entries/ingest)` → `save_cursor(http POST kbdb /entries/ingest)` → `post_envelopes(foreach)` → `post_one_envelope(http POST graph /triplets/ingest)` **delta(webhook)**:`collect_changed(code)` →〔對每個 card〕→ `fetch_card_d` → `parse_card_d(code)` → `upsert_entry_d(http)` → `post_envelopes_d(foreach)` → `post_one_envelope_d(http)` 零件對應的 cypher binding:`code`→arcrun-code;`http_request`→arcrun-http-request(打 Gitea/kbdb/graph); `cron`→觸發登記;`foreach_control`→平台 logic primitive(唯一允許的 service-binding 用法:零件等級,非本工作流編排)。 **全程無工作流等級的 service binding。**