| 2026-10-03 |
feat: валидация payload.user_id и ретеншн (ttl_seconds, RETENTION_DAYS)
...
- Routing: _user_recipient() — субьектом user-канала принимается непустая
строка/число; массив/объект/bool → skipped-доставка с причиной (аудит),
а не «delivered в никуда».
- ttl_seconds стал действующим: воркер не маршрутизирует истёкшее событие
(новый статус expired, миграция 0005 пересоздаёт ck_event_status).
- Beat-задача synapse.expire_events (ежечасно): удаление событий с
истёкшим ttl и старше RETENTION_DAYS (дефолт 30; 0 — окно отключено),
deliveries каскадом.
- ui.js: бейдж expired; docs/05: «Ретеншн», формат значения payload.user_id.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 hours ago
|

feat: многопользовательский режим — личный лог по каналу user
...
- Конвенция payload.user_id (sub gnexus-auth): контракт v1 цел, получателей
в конверте нет — дописка в docs/05-ingestion-api.md.
- Канал user в CHANNELS: правило с действием user направляет событие в личный
лог пользователя; без payload.user_id — skipped-delivery с причиной (аудит).
- deliveries.recipient_user_id + ix_deliveries_recipient; миграция 0004
пересоздаёт ck_channel на всех трёх таблицах (deliveries, routing_rule_actions,
channel_targets).
- Routing: _throttled по recipient для user-канала; s2s без цели — прежний
ретрай-цикл.
- Правила: действие user валидно без цели, user + target_id — 422; TargetIn
по-прежнему отвергает user.
- GET /api/v1/me (любой аутентифицированный) и GET /api/v1/me/events (личный
лог строго по своему sub, дедуп по событию).
- OAuth-колбэк: токен выдаётся любому аутентифицированному, роль решает только
раздел; adмин-API остаётся под 403.
- SPA: роль-гейт в router (user -> /my-events, прямой заход в админ-маршруты
редиректит), нав от роли, MyEventsView («Мои события»), getSession-кэш.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 hours ago
|

System-to-System (#28): подписанные доставки в системные webhooks
...
- app/signature.py: HMAC-подпись/проверка — зеркало WebhookSignature.php
gnexus-auth ("t=<unix>,v1=hmac_sha256('<t>.<body>', secret)"); verify —
константное сравнение + окно свежести 300 с (replay). Пригодится и для
входящих вебхуков gnexus-auth (single sign-out).
- app/worker/senders.py: POST в endpoint цели s2s с заголовками
X-Gnexus-Event-Id/Type/Timestamp/Signature + X-Synapse-Source; секрет
per-target (token_ref → S2S_SECRET_*), в БД только ссылка; 2xx = успех.
- tasks.py: первая попытка отправки сразу на маршрутизации (не ждать
beat-цикла), провал — общий ретрай-цикл; _attempt_send теперь одна.
- docs/05: отправка/ретраи/референс-код для принимающих сервисов;
.env.example: S2S_SECRET_*; README: структура, опечатка.
Проверено на живом стеке с приёмником-верификатором: OK (подпись/свежесть/
заголовки/тело), и цель без секрета — честная ошибка с именем переменной.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
23 hours ago
|
Routing Engine полный (#30): payload-матчинг, all/first, шумодав, ретраи
...
- условия правила: payload-матч (точный по top-level ключам, список = any-of)
- ROUTING_MATCH_MODE=all|first (дефолт all — применяются все подошедшие)
- шумодав: throttle_seconds у правила, окно на (channel,target), skipped-
записи с аудит-текстом; critical проходит всегда (договор docs/05)
- ретраи: beat (worker -B) + synapse.retry_due раз в минуту, паузы
30с/2м/10м/30м, 5 попыток -> failed; миграция 0003 (throttle_seconds
+ status skipped в CHECK)
- CLI: --payload и --throttle у add-rule; README/.env.example/docs/04/05
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
23 hours ago
|
s2s: description источника — в реестре, не в теле вебхука
...
Уточнение к 3748cb5: description — атрибут записи источника в админке
Synapse, конверт события и s2s-доставка его не несут; получатель видит
имя в X-Synapse-Source.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
23 hours ago
|
s2s: HMAC-подпись доставок (схема gnexus-auth) + description у источников/целей
...
- docs/05: раздел «Доставка s2s» — подпись t=<unix>,v1=hmac_sha256(secret,
"<t>.<body>") с константным сравнением и окном свежести, заголовки как у
gnexus-auth (X-Gnexus-Event-*) плюс X-Synapse-Source; секрет per-target,
в БД только token_ref; тело доставки несёт source_description.
- model/миграция: description (Text) у sources и channel_targets — кто это,
для ИИ-агента и человека, не гадать по названию.
- CLI: --description у create-source и add-target.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
23 hours ago
|
| 2026-10-02 |
docs/05: контракт Ingestion API v1 — принят
...
- инвариант: источник не знает получателей/каналов; routing целиком в правилах
- source — явное поле, ключ — удостоверение; правила матчают имя, не токен
- subject/action отдельными полями; priority — шкала; payload валидируется мягко
- dedup_key/ttl_seconds в v1, scheduled_at — резерв; tags убраны; topics/recipients
в клиентском контракте не существуют
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|