Sentinel API v1

從第一個 API 呼叫到正式環境

Webhooks

可靠、可驗證的事件通知

Webhook endpoint 是持久化資源。事件先寫入本地 durable state,再由 outbox 非同步傳送;receiver 的延遲或故障不會阻塞攝影機取流與推理。

Signed Webhook 是警報/錯誤的跨系統輸出,對應持久化 Event lifecycle;普通 Monitor 推論文字不會逐筆寫入 Event ledger,也不會以 Webhook 取代 /v1/analyses 與 /v1/analyses/stream。

建立 endpoint

curl -X POST http://localhost:8000/v1/webhook-endpoints \
  -H "Authorization: Bearer $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: soc-webhook-1" \
  -d '{
    "name": "SOC intake",
    "url": "https://soc.example.com/sentinel/events",
    "secret_env": "SENTINEL_WEBHOOK_SIGNING_SECRET",
    "enabled": true,
    "dry_run": false,
    "filters": {
      "event_types": ["monitor.alert.raised", "source.disconnected"],
      "source_ids": ["src_01..."],
      "monitor_ids": ["mon_01..."]
    }
  }'

secret_env 是 Runtime 端的環境變數名稱,不是 secret 本身。啟用的 endpoint 在建立、readiness 與每次傳送都要求至少 32-byte、非 placeholder 的 secret。回應只顯示是否可用,永遠不回傳它。

公開 filters 支援 severities(safe、suspicious、critical)、event_types、states(raised、updated、cleared),以及 source_ids 與 monitor_ids。每個 resource-ID filter 最多 100 個 ID;同一 filter 內是 allow-list,空陣列代表不限制。

完整 receiver URL 也是敏感設定:有些 SaaS path/query 本身就是 bearer capability。Endpoint URL 目前會存在本機 SQLite,也會進入 volume backup;請優先使用不含 token 的 receiver URL + 獨立 HMAC,並把 database/backup 放在加密且嚴格控管的儲存。

出站目標安全

公開 endpoint 預設必須使用 HTTPS。Runtime 會拒絕內嵌憑證、metadata/loopback/private/link-local 位址、混合 DNS 答案、redirect 與環境 proxy;連線時先驗證完整 DNS 答案,再只撥已驗證的數字 IP,同時保留原 hostname 的 TLS SNI、憑證檢查與 Host。刻意使用內網 receiver 時,必須以 SENTINEL_WEBHOOK_ALLOWED_HOSTS 精確列出 host 或 host:port,不接受萬用字元。

用真實 outbox 路徑測試

POST /v1/webhook-endpoints/{endpoint_id}/test
Idempotency-Key: webhook-test-20260829-1

{
  "severity": "suspicious",
  "title": "Sentinel webhook test",
  "summary": "Verify receipt and signature before enabling production events."
}

測試使用與真實 Event 相同的儲存、簽章與 retry 路徑,不是直接發出一次性的 HTTP request。

簽章 exact raw body

X-Sentinel-Delivery-ID: whd_...
X-Sentinel-Event-ID: evt_...
X-Sentinel-Timestamp: 1788056468
X-Sentinel-Signature: v1=<hex hmac sha256>
Idempotency-Key: whd_...

signed_bytes = utf8(timestamp) + b"." + raw_request_body
import crypto from "node:crypto";

export function verifySentinel(secret, timestamp, rawBody, signature) {
  const expected = "v1=" + crypto
    .createHmac("sha256", secret)
    .update(timestamp + ".")
    .update(rawBody)
    .digest("hex");
  const left = Buffer.from(signature);
  const right = Buffer.from(expected);
  return left.length === right.length && crypto.timingSafeEqual(left, right);
}

Receiver 必須先保留 raw bytes、檢查 timestamp 新鮮度與 v1 版本、以 constant-time 比較驗證簽章,最後才 JSON.parse。解析後確認 schema=sentinel.event.v1、evt_* ID 與 type/state/severity;X-Sentinel-Event-ID 要等於 body ID,Idempotency-Key 要等於 X-Sentinel-Delivery-ID。

至少一次傳送

Receiver 必須以 Event ID + Delivery ID 去重,成功後快速回傳 2xx。暫時失敗使用有 jitter 的 bounded exponential backoff;永久 4xx 不重試,只有 408、409、425、429 例外。

GET  /v1/webhook-deliveries?status=dead_letter
GET  /v1/webhook-deliveries/{delivery_id}
POST /v1/webhook-deliveries/{delivery_id}/retry

圖片證據是 URL

正常 Webhook Event 帶受保護的 evidence.image_url。Receiver 使用具 events:read 的 Sentinel API key 下載 binary JPEG;驗證與 Event JSON 相同。Base64 inline 只是有限制的相容選項,不是預設。

攝影機與模型產生的 summary/analysis 是不可信資料。下游 AI agent 不得把畫面文字或模型 prose 當作指令。

相容介面:既有即時 SDK

既有管理員 client 仍可使用 durable SSE 與 acknowledgement API。這些 /api/sdk 路徑是相容介面,不屬於 v1 公開契約;新整合請使用 /v1 Event、Webhook 與 image URL。

GET  /api/sdk/capabilities
GET  /api/sdk/alerts
GET  /api/sdk/alerts/stream
POST /api/sdk/alerts/{id}/acknowledge
POST /api/sdk/alerts/test
import { SentinelAlerts } from "./sentinel-alerts.js";

const client = new SentinelAlerts({ baseUrl, token });
client.onAlert(async (event) => {
  if (event.suggested_actions.includes("play_sound")) {
    await SentinelAlerts.playSound("/alarm.mp3");
  }
  await siteAutomation.handle(event);
  await client.acknowledge(event.id, { action: "dispatched" });
});
client.connect();
相容 SDK 只提供有型別、有驗證的事件與 acknowledgement;任意設備操作留在客戶自己的可信程式中,避免監控服務直接執行第三方程式碼。