Чёткое разделение audiences:
- docs/ — consumer-facing only, Diplodoc-built. Public contract для
интеграторов (Альтум, BI, third-party).
- docs-internal/ — operator runbooks, status snapshots, legacy tech pages.
НЕ в Diplodoc build, НЕ публикуются.
Изменения:
- Move docs/{ops,status,tech} → docs-internal/{ops,status,tech}
- docs/toc.yaml: убраны секции Operations, Архитектура (legacy), Status
- docs/index.md: убран tab "оператор / on-call", focus на integrators
- docs/README.md: rewritten — scope только integrator guide, editorial
guidelines (no leak internal architecture, stable API contract)
- .yfm: убран явный ignore status/* (теперь не нужно — папка переехала)
- docs-internal/README.md: index для внутренней документации с rationale
разделения
Scrubbing implementation details из Diplodoc страниц:
- events.md: убраны Strimzi/cluster.142 references, OutboxPoller детали,
internal alert names (OrdinisOutboxLagHigh, OrdinisOutboxDLQNonEmpty),
metric names (nsi_outbox_*). Заменены на consumer-observable contract.
- webhooks.md: убраны metric names (nsi_webhook_ssrf_rejected_total),
alert table (OrdinisWebhookHighFailureRate и т.д.). Retry policy
переписана на user-side perspective.
- errors.md: убран alert name OrdinisReadApiErrorBudgetBurnFast, Tempo
endpoint URL, internal connection details.
- x-references.md: OrphanReferenceScanner → general "Ordinis периодически
детектирует". Убраны metric/alert names. Cascade roadmap без link на
internal design doc.
- auth.md: убран file path ScopeContext.java.
- bundle-cuod.md: убран Maven module path.
- caching.md: cascade design link заменён на text reference.
CI:
- .gitlab-ci.yml: новый job build-docs (stage: build) — Node 20 alpine,
pnpm install --frozen-lockfile + pnpm build, artifact docs/_dist на
неделю. Triggers: auto на docs/**, либо manual web. Готов к follow-up
pages-deploy job когда host решён.
- Новый pattern .docs-changes для rules.
Build verified clean: 13 HTML pages, 6.9M, no broken links/refs.
6.6 KiB
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.
При нарушении registration возвращает 422 + список причин.
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ч |
| После всех попыток | Ordinis team помечает delivery как failed для ручного разбора |
Если consumer недоступен > 1000 retry попыток — Ordinis team будет расследовать. Долгосрочное unavailability стоит coordinate (planned maintenance window) чтобы не накапливать orphan deliveries.
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
- Idempotency — webhook может прийти дважды (network retry, partial
failure). Используй
deliveryIdкак dedup key. - Async обработка — отвечай
202 Acceptedсразу, обрабатывай в background. 10s timeout жёсткий — если БД медленная, лучше принять и сложить в очередь. - HMAC verify первым — до парсинга JSON.
- Metrics — track received / processed / errors для своего side.
- Whitelist Ordinis IP на firewall если строгие правила. Точный адрес запросить у Ordinis team при registration.
Дальше
- События (Kafka) — primary delivery channel
- Caching — webhook invalidation pattern