Newer
Older
gn-synapse / docs / 05-ingestion-api.md

05 — Ingestion API: контракт приёма событий (v1, принят)

Договор о том, как сервисы экосистемы (bugtrail, gntodo, smart-home, Navi-инстансы, gnexus-auth, ad-hoc скрипты) шлют события в Synapse.

Инвариант

Источник знает только «что случилось у меня». В конверте события нет получателей, каналов и подписок — выбор «кому, куда и каким каналом» целиком принадлежит Routing Engine (правилам в админке). Клиент, который «знает про других клиентов» — признак ошибки проектирования. Когда-то понадобятся адресные алерты конкретному человеку (например assignee bugtrail) — путь через payload-переменную в шаблоне ({{ payload.assignee_email }}), не через поля конверта.

Endpoint

POST /api/v1/events
Authorization: Bearer syn_<token>       # единый паттерн экосистемы (Bearer)
Content-Type: application/json

Для пачек: POST /api/v1/events/batch — тот же конверт в массиве, ответ — список id (частичная валидация честная).

Конверт

{
  "source": "monitoring",
  "subject": "container",
  "action": "down",
  "priority": "critical",
  "payload": { "container": "nomin-web", "host": "melody", "exit_code": 137 },
  "dedup_key": "container-nomin-web-down-2026-10-03",
  "ttl_seconds": 3600,
  "scheduled_at": null
}
Поле Обяз. Смысл
source да Явное имя источника (см. «Источник и ключ»). Правила маршрутизации матч имя, не токен — ротация ключа не трогает правила.
subject да ЧТО: сущность события — issue, container, build, motion…
action да Что с ней случилось: created, down, resolved…
priority нет `low \ normal \ high \ critical, дефолтnormal. Фиксированная шкала, не число.critical` в будущем = обход шумодавов, отдельная очередь.
payload нет Любой JSON. Опциональная JSON Schema типа enforced'ится воркером (не на приёме). Шаблоны каналов должны быть устойчивы к отсутствию полей.
dedup_key нет Ретраи источника: тот же ключ в TTL-окне → deduplicated: true, id: <первый>. Дубли в каналах раздражают сильнее всего.
ttl_seconds нет Событие «контейнер упал» бесполезно через час. Дефолт — «вечно», поле обязательно с первого дня — ретрофит дороже.
scheduled_at резерв Не реализуется в MVP; поле зарезервировано.

Не существует и не появится в клиентском контракте: recipients, topics, channel — получатели/каналы/подписки живут только внутри Synapse (правила в админке). tags из обсуждений убраны из v1; вернутся, когда появится реальное правило, требующее тега (решение обсуждалось 2026-10-03).

Ответы

Код Когда
401 Нет/битый ключ
403 Ключ валиден, но не принадлежит source из тела (анти-спуфинг)
422 Неизвестная тройка (source, subject, action) — «тип не зарегистрирован». Payload при приёме не валидируется — deep-валидация в воркере, её исход — Delivery Log, не откат приёма.
202 Принято → {"id": "uuid", "status": "queued", "deduplicated": false}

Валидация конверта жёсткая и мгновенная, payload — мягкая. Схема «принял мгновенно → разберусь» ломается, если требовать на входе знание всех payload-схем.

Статус события

GET /api/v1/events/{id} — только источнику события, чужой id → 404:

{
  "id": "uuid", "status": "delivered",
  "deliveries": [
    {"channel": "telegram", "target": "infra-squad", "status": "delivered", "attempts": 1, "error": null},
    {"channel": "internal_log", "status": "delivered", "attempts": 1}
  ]
}

Обратный канал (webhook об изменении статуса) позже; контракт поллинга стабилен — подписки докинутся сверху. Для MVP источники либо fire-and-forget, либо поллят этот GET.

Типы и правила (админка)

  • Реестр типов: тройка (source, subject, action), опционально JSON Schema payload'а. Заводит админ Synapse в UI.
  • Правило маршрутизации: условия (source, subject, набор actions, priority >= X, позже — payload-матчинг и теги) → действия (каналы + шаблон + цели: TG-чат, SMTP, s2s-ендпоинт).

Таблицы БД (постановка #34)

api_keys (хэш, привязка к source) · notification_types(source, subject, action) · routing_rules (условия, действия, шаблон) · events (конверт + payload + статус) · deliveries (канал, цель, статус, попытки, ошибки) · channel_targets.