Files
mdm-ordinis/docs/integration/api/webhooks.md
T
Zimin A.N. d5268b9395 docs: Diplodoc setup + полное руководство интеграции по API
Новая документация-as-code на платформе Diplodoc (Yandex open-source) с
YFM (Yandex Flavored Markdown). Build → static HTML, deployable куда угодно.

Что добавлено:
- package.json + .yfm + toc.yaml — Diplodoc setup, навигация для sidebar
- index.md — главная страница с tabs для разных audiences
- README.md — инструкция для contributors

Раздел integration/api/ — 11 страниц про интеграцию consumers:
- index — обзор + глоссарий
- auth — Keycloak service accounts, scope mapping
- read-endpoints — GET /dictionaries, list/by-key, spatial filters
- events — Kafka outbox топики, partition keys, idempotency, DLQ
- webhooks — HMAC, SSRF guard, retry policy, test ping endpoint
- bitemporal — validFrom/validTo + history, future-dated entries, bulk close/export
- x-references — FK syntax, validation errors, scope hiding, orphan scanner, v2 cascade preview
- caching — TTL / push / snapshot strategies, hybrid pattern
- errors — full error code reference (validation/auth/conflict/server), retry strategy
- best-practices — 33 numbered rules для consumers
- bundle-cuod — все 40 справочников с FK графом и сценариями

Обновлены:
- ops/README.md — link на новую integration/api/, убран inline <br>
- ops/slo.md — broken cross-repo links заменены на text references

Build:
  cd docs && pnpm install && pnpm build
  → ./_dist (20 HTML files, 7.1M)
  pnpm serve  # → http://localhost:8088

CI deploy job для GitLab Pages — в roadmap (см. docs/README.md).

Существующий tech/api-integration.md оставлен в toc как legacy
(содержимое мигрировано + расширено в integration/api/).
2026-05-07 23:19:02 +03:00

6.9 KiB
Raw Blame History

Webhooks

Альтернатива Kafka events для consumer'ов которые не хотят / не могут поддерживать Kafka client. Ordinis отправляет HTTP POST на зарегистрированный URL при изменениях справочников.

{% note info %}

Если у тебя есть Kafka access — используй события. Webhooks проще в развёртывании, но менее надёжны (HTTP retry vs Kafka durable log) и медленнее (HTTP overhead vs Kafka batch).

{% endnote %}

Регистрация подписки

curl -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  https://ordinis.k8s.264.nstart.cloud/api/v1/admin/webhooks/subscriptions \
  -d '{
    "name": "altum-cache-invalidation",
    "url": "https://altum-backend.altum.local/webhooks/ordinis",
    "secret": "<HMAC_SECRET_GENERATED_BY_YOU>",
    "scopes": ["PUBLIC", "INTERNAL"],
    "dictionaries": ["spacecraft", "satellite_type", "instrument"],
    "actions": ["created", "updated", "closed"],
    "active": true
  }'

Поля

Поле Тип Назначение
name string Человекочитаемое имя для admin UI.
url string HTTPS endpoint. HTTP блокируется SSRF guard'ом.
secret string HMAC-SHA256 секрет для подписи payload (см. ниже).
scopes array Фильтр по scope (subset из доступных consumer'у).
dictionaries array Фильтр по dictionary names. null/[] = все доступные.
actions array created / updated / closed. null = все.
active bool Можно временно отключить без удаления.

SSRF guard

Webhook URLs валидируются перед отправкой:

  • Запрещено: private CIDR (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), localhost, link-local (169.254/16), metadata IPs.
  • Запрещено: non-HTTPS scheme. Только https://.
  • Запрещено: URL > 2048 chars.

Метрика nsi_webhook_ssrf_rejected_total increments при попытках. См. prometheus rule OrdinisWebhookSsrfRejected.

Payload

POST на зарегистрированный URL с body:

{
  "deliveryId": "wd-abc123-xyz789",
  "subscriptionName": "altum-cache-invalidation",
  "occurredAt": "2026-05-07T15:23:45.123+03:00",
  "events": [
    {
      "schemaVersion": "1.0.0",
      "eventType": "dictionary.changed",
      "dictionary": "spacecraft",
      "businessKey": "RESURS-P-3",
      "action": "updated",
      "version": 3,
      "validFrom": "2026-05-07T00:00:00+03:00",
      "data": { ... }
    }
  ]
}

{% note tip %}

Payload может содержать несколько events (events array) если они произошли в одной transaction (bulk close, bundle import). Не предполагай single-event payload.

{% endnote %}

HMAC подпись

Каждый POST имеет header:

X-Ordinis-Signature: sha256=<HEX>
X-Ordinis-Delivery: wd-abc123-xyz789
X-Ordinis-Subscription: altum-cache-invalidation

Где <HEX> = HMAC-SHA256 от raw body bytes с secret который ты задал при регистрации.

Verify (Python)

import hmac, hashlib

def verify(body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

Verify (Node.js)

import crypto from 'crypto'

function verify(body, signatureHeader, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  )
}

{% note warning %}

Verify подпись до парсинга JSON. Если invalid — 403 Forbidden, не трогай payload. Без verify ты уязвим к подделке events от любого, кто знает твой URL.

{% endnote %}

Response contract

Consumer должен ответить 2xx в течение 10 секунд:

Status Семантика
200 / 201 / 202 / 204 Принято. Ordinis помечает delivered.
4xx Permanent failure (твой bug). Ordinis НЕ retry.
5xx / timeout Transient. Ordinis retry с exponential backoff.

Retry policy

Попытка Интервал
1 сразу
2 через 30s
3 через 2 мин
4 через 10 мин
5 через 1ч
6-10 каждые 6ч
1000+ DLQ → ручной разбор

После 1000 неудач delivery попадает в DLQ (/api/v1/admin/webhooks/deliveries/dlq). Alert: OrdinisWebhookDLQNonEmpty.

Test ping

Endpoint для проверки доступности URL до регистрации:

curl -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  https://ordinis.k8s.264.nstart.cloud/api/v1/admin/webhooks/test-ping \
  -d '{
    "url": "https://altum-backend.altum.local/webhooks/ordinis",
    "secret": "<TEST_SECRET>"
  }'

Response:

{
  "success": true,
  "statusCode": 200,
  "responseBody": "OK",
  "elapsedMs": 142
}

Либо details про failure (DNS issue, timeout, SSRF reject и т.д.).

Best practices

  1. Idempotency — webhook может прийти дважды (network retry, partial failure). Используй deliveryId как dedup key.
  2. Async обработка — отвечай 202 Accepted сразу, обрабатывай в background. 10s timeout жёсткий — если БД медленная, лучше принять и сложить в очередь.
  3. HMAC verify первым — до парсинга JSON.
  4. Metrics — track received / processed / errors для своего side. Сравнивай с Ordinis metrics при расследовании.
  5. Whitelist Ordinis IP на firewall если строгие правила. IP cluster.142 = 192.168.100.142, но в production будет ingress IP. Спросить у Ordinis team при registration.

Alerting (Ordinis side)

Alert Trigger
OrdinisWebhookHighFailureRate failure rate > 50% за 5 мин
OrdinisWebhookDLQNonEmpty events в DLQ > 0
OrdinisWebhookDispatcherIdle 0 deliveries при > 0 active subscriptions
OrdinisWebhookSsrfRejected SSRF guard сработал > 0 раз за 15 мин

Дальше