Запустить за 5 минут

Минимальный путь от пустого браузера до первого алерта на staging-стенде staging.antifraud.slim-shaggy.com (Hetzner).

0. Что нужно

  • SSH-доступ к root@178.104.214.209 (ключ ~/.ssh/id_rsa) — стек уже поднят, нужны только команды внутри.
  • Python 3.12 на ноуте (для scripts/dry_run.py и synthetic generator — стримит на staging API через HTTPS).
  • Браузер для дашборда.

1. Проверить, что стек жив

curl https://staging.antifraud.slim-shaggy.com/healthz   # → 200
curl https://staging.antifraud.slim-shaggy.com/readyz    # → 200 (CH+Kafka+Redis+PG все ОК)

Если 200 — деплой здоров. Если нет — раздел Если сломалось.

Опционально посмотреть статус всех контейнеров:

ssh root@178.104.214.209 'cd /root/antifraud/infra && docker compose ps'

Все должны быть Up (healthy). Перманентно unhealthy образы (mlflow, alertmanager, blackbox-exporter, promtail) — норма, deploy-workflow их игнорирует.

2. Засеять dev-данные (один раз, если ещё не делал)

ssh root@178.104.214.209
cd /root/antifraud/infra
docker compose exec dashboard python manage.py bootstrap_dev

В выводе:

============================================================
Operator:   Casino Alpha
Admin user: admin@antifraud.dev / devpassword123
API key:    sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
============================================================
Важно: API-ключ показывается один раз. Скопируй сразу — в БД лежит только PBKDF2-хеш. Команда идемпотентна, но при повторе ключ покажется заново только если выставить --rotate.

Альтернативно — отдельный суперюзер на твою почту:

docker compose exec dashboard python manage.py createsuperuser
# email: твоя@почта
# password: что-то длинное

3. Зайти в админку

https://staging.antifraud.slim-shaggy.com/admin/ → логин admin@antifraud.dev / devpassword123 (или твой суперюзер).

4. Послать поток событий с ноута

cd ~/PycharmProjects/iGamingAntiFraudSaaS
python -m tools.synthetic_events \
  --count 500 --mode stream --rps 50 \
  --api-url https://staging.antifraud.slim-shaggy.com \
  --api-key sk_live_xxxxxxxx \
  --fraud-rate 0.15

5. Посмотреть алерты

https://staging.antifraud.slim-shaggy.com/dashboard/alerts/ — там сработавшие правила.

Полный smoke по всем 9 поверхностям сразу — раздел Первое событие → dry-run.

Логин и регистрация

Auth построен на django-allauth. Поддержано: email+пароль, magic-link, Google OAuth. Роли operator_admin и risk_lead также проходят TOTP MFA (Sprint 24 — подробнее в UI v2).

Все точки входа на staging

URLЧто там
/accounts/login/Логин (email + пароль или magic-link или Google)
/accounts/2fa/authenticate/Ввод TOTP-кода (шаг 2 для привилегированных ролей)
/accounts/signup/Самостоятельная регистрация (на free-плане)
/accounts/logout/Выход
/accounts/password/reset/Сброс пароля
/accounts/email/Управление email-ами учётки
/accounts/social/connections/Привязанные OAuth-провайдеры (Google)

Какие учётки есть сейчас на staging

admin@antifraud.dev / devpassword123 — суперюзер, есть доступ ко всему (и в админку, и в дашборд).

Создать свою учётку

Способ 1 — самостоятельно через signup:

  1. Открыть /accounts/signup/.
  2. Подтвердить email (на staging email уходит на console — смотри лог dashboard-контейнера).
  3. Зайти. Без оператора попадёшь на /dashboard/access-pending/ — суперюзер должен в админке привязать тебя к Operator-у через User → operator_membership.

Способ 2 — суперюзер из консоли:

ssh root@178.104.214.209
cd /root/antifraud/infra
docker compose exec dashboard python manage.py createsuperuser
# email: твоя@почта
# password: длинный

Способ 3 — приглашение в существующего оператора (Module 09 invite flow):

  1. В админке Operators → твой оператор → "Invite user".
  2. Ввести email + role.
  3. На указанный email приходит ссылка /operators/<id>/accept-invite/<url_key>/.
  4. Получатель кликает, ставит пароль, заходит в дашборд оператора.

Роли

RoleЧто можетMFA обязательна
operator_adminВсё в дашборде своего оператора + биллинг + приглашать юзеров + управлять API-ключамиДа (TOTP)
risk_leadАлерты, кейсы, разметка, граф, audit log. Без биллинга и инвайтов.Да (TOTP)
operator_viewerТолько чтение: алерты, кейсы, статистикаНет
platform_admin (только мы)Админка + все операторыНет (bypass для staff)

UI v2 — design system + новые экраны (Sprint 24)

Sprint 24 обновил визуальный слой дашборда с нуля: появилась дизайн-система на CSS-переменных, все 9 операторских экранов переработаны, добавлено 5 новых URL, введён MFA-флоу для привилегированных ролей.

Design system

Файл токенов: apps/dashboard/static/css/tokens.css. Содержит CSS-переменные для цветов, типографики, отступов, радиусов, теней — единый источник правды для всего UI. Брендовые SVG-ассеты лежат в apps/dashboard/static/img/: logo-mark.svg, logo-mark-solid.svg, wordmark.svg.

Переработанные экраны (9 поверхностей)

Все экраны перешли на токены из tokens.css. Визуальный ревамп не менял маршруты — URL-ы остались прежними.

ЭкранURL (prefix /dashboard/)Что изменилось
Биллингbilling/Карточки плана + usage-gauge в новом стиле
ML labels (разметка)active-learning/Queue-UI с compact-карточками
Кейсыcases/Список с табами статусов (см. новые URL ниже)
Профили игроковplayers/<id>/Header-карточка + новые HTMX-табы wallet/ip
Аффилейтыaffiliate/Cohort-dashboard с sparkline-метриками
Вебхукиwebhooks/Endpoint-карточки с inline-статусом доставок
API-ключиsettings/api-keys/Self-service CRUD (новый URL, см. ниже)
Audit logaudit/Таблица с CSV-экспортом (новый URL, см. ниже)
Уведомленияnotifications/Preferences-форма (новый URL, см. ниже)

Кастомный Unfold Admin Index (/admin/) тоже переработан: stat-карточки на главной странице.

Новые URL Sprint 24 (не существовавшие раньше)

URLЧтоБыло раньше
/dashboard/cases/ Список всех кейсов с фильтрацией по статусу (open / under_review / closed_clean / closed_fraud). Детальная страница — /dashboard/cases/<uuid>/. Список был только в /admin/cases/case/. Детальная страница создавалась только как форма на профиле игрока.
/dashboard/settings/api-keys/ Self-service CRUD: оператор сам создаёт ключ, видит список, отзывает. Секрет показывается один раз (как в Admin). Только через /admin/apikeys/ — платформенный администратор.
/dashboard/audit/ Audit log оператора: что кто и когда менял в рамках своего тенанта. Кнопка CSV-экспорт (/dashboard/audit/export.csv). Только /admin/audit/ — не было доступа для самого оператора.
/dashboard/notifications/ Preferences уведомлений: Email / Slack / Telegram, матрица событий (alert.created / case.closed_fraud / decision.updated / network.report_published), тихие часы. Не существовало.
/dashboard/players/<id>/wallet/ HTMX-таб wallet intel на профиле игрока: wallet_address, chain-age, OFAC-статус, mixer-хопы. Данные считались в hot-path, но в UI не выводились.
/dashboard/players/<id>/ip/ HTMX-таб IP intel: provider chain resolution, VPN/Tor/proxy-флаги, geo, ASN. Данные считались в hot-path, но в UI не выводились.

MFA enforcement (новый flow)

Роли operator_admin и risk_lead обязаны пройти TOTP-регистрацию после первого логина. Реализовано в apps/dashboard/apps/accounts/mfa_middleware.py (EnforceMfaForPrivilegedMiddleware).

Флоу:

  1. /accounts/login/ — ввод email + пароля (или Google OAuth).
  2. /accounts/2fa/authenticate/ — ввод TOTP-кода (если уже зарегистрирован) или редирект на setup если ещё нет.
  3. /dashboard/ — вход в дашборд.

Platform admins (Django staff/superusers) MFA не требуют — чтобы не заблокировать recovery. Остальные роли (operator_viewer и т.д.) MFA не требуют.

Важно для staging: дев-учётка admin@antifraud.dev — суперюзер (staff=True), поэтому MFA bypass активен. Для тестирования MFA-флоу создай отдельную учётку с ролью operator_admin.

Дашборд оператора (UI)

Это то, что видит риск-менеджер казино после логина. Не путать с Django Admin — это разные интерфейсы под разные задачи.

Live: https://staging.antifraud.slim-shaggy.com/dashboard/ — после логина (admin@antifraud.dev / devpassword123).

Различие админка vs дашборд

Django Admin (/admin/)Operator Dashboard (/dashboard/)
Для когоПлатформенные админы (мы, поддержка)Риск-менеджер казино-оператора
СтильCRUD-таблицы Django Admin (Unfold theme)HTMX + Alpine + Tailwind, оптимизирован под workflow
ВидитВсех операторов, всех юзеров, любой объектТолько своего оператора (multi-tenant scope)
ДействияСоздать оператора, изменить лимит, посмотреть audit всей платформыРасследовать алерт, открыть/закрыть кейс, разметить случай, настроить вебхук, управлять своими API-ключами и уведомлениями

Карта страниц

Главная и поиск

URLЧто там
/dashboard/Лендинг оператора: сводка событий за сутки, top reasons, последние алерты, лента активности
/dashboard/search/Глобальный HTMX-поиск по player_id / email / wallet / fingerprint_hash
/dashboard/stats/Расширенная статистика: графики decisions по часам/дням, conversion воронка

Алерты (Module 04)

URLЧто там
/dashboard/alerts/Лента: rule_code, severity, player, reason, timestamp. Фильтры: rule, severity, period, status. Сортировка по любой колонке.
/dashboard/alerts/rows/HTMX-эндпоинт: подгружает строки бесконечного скролла
/dashboard/alerts/<uuid>/resolve/Resolve-action — пометить алерт как обработанный с комментарием

Профиль игрока (Module 05 + 09 + 12 + 13)

URLЧто там
/dashboard/players/<player_id>/Профиль с 6 табами (HTMX): Events, Alerts, Links, Graph, Wallet, IP
/dashboard/players/<id>/tabs/events/Таб событий — таймлайн всех signup/login/deposit/withdraw/bet/bonus
/dashboard/players/<id>/tabs/alerts/Таб алертов — все срабатывания на этом игроке
/dashboard/players/<id>/tabs/links/Таб связей — таблица shared identifiers (карты, IP /24, fingerprint, кошельки)
/dashboard/players/<id>/wallet/HTMX-таб wallet intel: адрес, chain-age, OFAC-статус, mixer-хопы. Добавлен в Sprint 24.
/dashboard/players/<id>/ip/HTMX-таб IP intel: VPN/Tor/proxy-флаги, geo, ASN, provider chain. Добавлен в Sprint 24.
/dashboard/players/<id>/graph.json/vis-network JSON для графа связей (1-2 hops). Render через Alpine.js.

Кейсы (Module 09)

URLЧто там
/dashboard/cases/Список всех кейсов с табами статусов: open / under_review / closed_clean / closed_fraud. Добавлен в Sprint 24.
/dashboard/cases/<uuid>/Детальная страница кейса.
/dashboard/cases/players/<player_id>/label/Создать case-event на игроке: статус + комментарий. closed_fraud триггерит Module 16.

Аффилейты (Module 17)

URLЧто там
/dashboard/affiliate/Список аффилейт-кейсов (cohort-level, не per-player)
/dashboard/affiliate/<uuid>/Детали кейса: какие правила сработали, какие игроки в когорте
/dashboard/affiliate/cohort/<affiliate_id>/Drilldown по конкретному аффилейту: метрики когорты (LTV, retention, IP concentration, ML-скор)
/dashboard/affiliate/manage/Управление: блокировка/одобрение аффилейтов
/dashboard/affiliate/manage/<uuid>/Профиль конкретного аффилейта

Вебхуки (Module 15)

URLЧто там
/dashboard/webhooks/Endpoint-ы оператора: добавить, отключить, ротировать секрет, удалить
/dashboard/webhooks/<uuid>/rotate/Сгенерировать новый секрет — старый перестаёт работать сразу
/dashboard/webhooks/<uuid>/toggle/Вкл/выкл endpoint без удаления
/dashboard/webhooks/<uuid>/deliveries/Журнал доставок: успехи, ретраи, DLQ. Фильтр по статусу.

Active learning (Module 09 + 08)

URLЧто там
/dashboard/active-learning/Очередь кейсов на разметку: показывает наиболее uncertain (по ML score) + случайные. Risk manager помечает «фрод подтверждён» / «ложное» / «не уверен» — корпус для обучения.
/dashboard/active-learning/rows/HTMX-подгрузка следующих
/dashboard/active-learning/<case_id>/label/Action: записать лейбл

Биллинг и тарифы

URLЧто там
/dashboard/billing/Текущий план, потребление за месяц (events, RPS-пики), ближайший биллинг-цикл
/dashboard/billing/usage/HTMX-эндпоинт: график использования
/dashboard/billing/change-plan/Сменить тариф (free / starter / pro)
/pricing/Публичная страница с тарифами — без логина

API-ключи (самообслуживание)

URLЧто там
/dashboard/settings/api-keys/Список ключей оператора, создание нового, отзыв. Секрет показывается один раз. Добавлен в Sprint 24 — больше не нужно обращаться в поддержку.
/dashboard/settings/api-keys/create/Форма создания ключа (POST)
/dashboard/settings/api-keys/<uuid>/revoke/Отозвать конкретный ключ

Audit log (для оператора)

URLЧто там
/dashboard/audit/Журнал изменений в рамках тенанта оператора. Добавлен в Sprint 24.
/dashboard/audit/export.csvCSV-экспорт для периодических проверок

Уведомления

URLЧто там
/dashboard/notifications/Preferences: Email/Slack/Telegram, матрица событий, тихие часы. Добавлен в Sprint 24.

Инвайты (Module 09)

URLЧто там
/operators/<operator_id>/invite-user/Форма приглашения нового юзера в оператора (для owner-а). Email + role.
/operators/<operator_id>/accept-invite/<url_key>/Ссылка из письма-инвайта. Кликаешь → форма установки пароля.

Стек UI

  • Django 5 + DRF (API endpoints) + django-allauth (auth + MFA)
  • HTMX — частичные обновления страниц без JS-фреймворков (alert feed, инфинит-скролл, табы)
  • Alpine.js — реактивность для сложных компонентов (граф, фильтры)
  • Tailwind CSS — стили, бандлятся через whitenoise (не CDN — Sprint 23 #393)
  • Design systemapps/dashboard/static/css/tokens.css (CSS-переменные), SVG-ассеты в apps/dashboard/static/img/
  • Unfold — современная тема для Django Admin (кастомный index — Sprint 24)
  • vis-network — рендер графа связей в профиле игрока

Multi-tenant scoping

Каждый запрос к дашборду фильтруется по request.user.operator. Юзер из casino-A физически не может видеть события casino-B — middleware OperatorScopedMiddleware подсаживает фильтр в каждый ORM-запрос (apps.accounts.middleware). Тесты на изоляцию: apps/dashboard/tests/test_scoping.py.

Если впервые открываешь дашборд

Если событий ноль — все страницы покажут пустые состояния. Чтобы увидеть систему «в действии»:

  1. Залогинься admin@antifraud.dev.
  2. С ноута запусти synthetic generator (→ раздел Первое событие):
    python -m tools.synthetic_events --count 500 --mode stream --rps 50 \
      --api-url https://staging.antifraud.slim-shaggy.com \
      --api-key <твой-API-ключ из bootstrap_dev> \
      --fraud-rate 0.20
  3. Через 30-60 секунд обнови /dashboard/alerts/ — должны появиться сработавшие правила.
  4. Кликни в любого игрока в алерте — увидишь профиль с табами + графом.

Django Admin (для платформы)

Django Admin на staging — главная панель управления оператором, юзерами, ключами, кейсами.

URL

https://staging.antifraud.slim-shaggy.com/admin/

Креды

  • Дев-учётка (после bootstrap_dev): admin@antifraud.dev / devpassword123.
  • Свой суперюзер (рекомендую сделать сразу для прода):
    ssh root@178.104.214.209
    cd /root/antifraud/infra
    docker compose exec dashboard python manage.py createsuperuser
    После — войти под своим email + паролем.

Сменить пароль дев-учётке

ssh root@178.104.214.209
cd /root/antifraud/infra
docker compose exec dashboard python manage.py changepassword admin@antifraud.dev

Что есть в админке

Accounts → Users

apps/accounts

Платформенные юзеры. Роли: admin, risk_manager, operator_owner, operator_viewer.

Operators → Operators

apps/operators

Клиенты (казино). Поля: имя, plan (free/starter/pro), network_optin, лимиты.

Apikeys → API keys

apps/apikeys

API-ключи всех операторов. Секрет показывается один раз. В БД хранится PBKDF2-хеш. Оператор управляет своими ключами самостоятельно через /dashboard/settings/api-keys/ (Sprint 24).

Players → Players

apps/players

Все игроки, по которым прошли события. Заполняется автоматически из ingest-а.

Alerts → Alerts

apps/alerts

Сработавшие правила: rule_code, severity, player, context.

Cases → Cases

apps/cases

Кейсы расследований. Status: open / under_review / closed_clean / closed_fraud. closed_fraud триггерит Module 16.

Webhooks → Endpoints

apps/webhooks

HTTPS endpoint оператора + HMAC-секрет + список подписанных событий.

Pricing → Plans

apps/pricing

Тарифы: events/month, RPS, retention, цена.

Audit → AuditLog

apps/audit

Что кто и когда менял. Только чтение. PII в before/after-значениях санитизирован.

ML labels

apps/ml_labels

UI разметки риск-менеджером (правда/ложь по алерту) — корпус для будущей supervised-модели.

Первое событие

Три способа отправить trafic: curl (одно событие), synthetic generator (поток), dry-run (полный smoke).

1. curl — одно событие на staging API

curl -X POST https://staging.antifraud.slim-shaggy.com/v1/events \
  -H "Authorization: Bearer sk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "event_type": "signup",
    "player_id": "player_001",
    "timestamp": "2026-05-07T10:00:00Z",
    "ip": "1.2.3.4",
    "email": "test@example.com",
    "fingerprint": {"fingerprint_hash": "abc123", "signals": {}}
  }'

Ответ:

{"risk_score": 12, "decision": "allow", "reasons": []}

2. Синтетический генератор

Лежит в tools/synthetic_events/ на ноуте. Запускается локально, стримит в staging API через HTTPS.

  • --mode parquet — пишет в файл (для офлайн-обучения / тестов).
  • --mode stream — стримит в API с указанной RPS.
# 10000 событий на диск (локально)
python -m tools.synthetic_events --count 10000 --mode parquet --out events.parquet

# Поток в staging API
python -m tools.synthetic_events --count 500 --mode stream --rps 50 \
  --api-url https://staging.antifraud.slim-shaggy.com \
  --api-key sk_live_xxx \
  --fraud-rate 0.15

Параметры:

ФлагЧто делаетDefault
--countСколько событий сгенерить1000
--fraud-rateДоля игроков с фрод-паттернами0.15
--seedRandom seed для воспроизводимости42
--rpsRPS для stream-режима10
--operatoroperator_id, который штампуется на событияdemo_operator

3. Полный operator dry-run против staging

Шипнули в Sprint 23 (sid #397). Делает прогон по 9 поверхностям и печатает PASS/FAIL/SKIP.

python scripts/dry_run.py \
  --api-base https://staging.antifraud.slim-shaggy.com \
  --api-key sk_live_xxx \
  --dashboard-base https://staging.antifraud.slim-shaggy.com  # для проб alert-feed/graph
  # --webhook-receiver-url https://your-receiver  # опционально

Что проверяет:

  1. Ingest /healthz → 200
  2. Ingest /readyz → подключения к CH+Redis+Kafka+PG
  3. Decision quality — >0 решений block/review
  4. ATO baseline — срабатывание правил new_device/new_country
  5. Wallet intel — проверка OFAC/mixer (SKIP на синтетике)
  6. Alert feed — лента в дашборде (SKIP без сессии)
  7. Graph — построение графа связей
  8. Webhook delivery — отправка на receiver (SKIP без флага)
  9. Network blocklist — Module 16 round-trip
  10. Affiliate cohort — ML-скор

Полный runbook: docs/integration/dry-run.md.

Архитектура целиком

Два приложения, четыре хранилища, несколько consumers, observability-стек. Всё в Docker Compose.

graph TB subgraph Operator OP[Casino backend] SDK[JS SDK fingerprint] end CDN[Caddy + JA3/JA4] API[FastAPI ingest API] KAFKA[(Redpanda Kafka)] CH[(ClickHouse)] PG[(Postgres)] REDIS[(Redis)] DASH[Django dashboard + admin] AC[alert-consumer] WC[webhook-consumer] ARC[auto-report-consumer] EC[erasure-consumer] ARRC[async-rules-consumer] WHRX[Operator webhook receiver] OP -- POST /v1/events --> CDN SDK -- fingerprint --> CDN CDN --> API API -- idempotency / rate-limit --> REDIS API -- write event --> KAFKA API -- score sync --> CH API -- response --> CDN CDN --> OP KAFKA --> AC KAFKA --> WC KAFKA --> ARC KAFKA --> EC KAFKA --> ARRC AC --> PG ARRC --> CH ARC -- hashed identifiers --> CH WC -- HMAC-signed --> WHRX DASH --> PG DASH --> CH

Hot path vs async path

Hot path — что мы успеваем сделать за <100мс перед ответом оператору:

  • Распаковать payload, проверить API-ключ, idempotency.
  • Применить «дешёвые» правила (geo, IP, fingerprint, OFAC лукап по wallet, network blocklist hit).
  • Агрегировать score, вернуть {risk_score, decision, reasons}.

Async path — что считается в фоне через Kafka и попадает в дашборд позже:

  • Тяжёлые графовые правила (shared_card_with_many_accounts, communities).
  • ML-скор аффилиат-когорт (LightGBM).
  • ATO-baseline пересчёт.
  • Webhook-доставка.
  • Module 16 — публикация в network_blocklist.

Принцип: синхронный ответ всегда быстрый, точность дотачивается асинхронно. Если async-правила улучшили решение — летит вебхук decision.updated.

Сервисы (контейнеры)

Все 21 контейнера крутятся на staging-хосте 178.104.214.209. Compose project name = infra, поэтому имена контейнеров infra-*. Порты внутренних сервисов забинды на 127.0.0.1 (наружу торчат только :80 и :443 через Caddy).

Заглянуть в любой:

ssh root@178.104.214.209
cd /root/antifraud/infra
docker compose ps              # все 21 контейнера
docker compose logs <name>     # лог конкретного

Приложения

infra-api-1

FastAPI · :18000 (host)

Ingest endpoint /v1/events. Auth, rate-limit, idempotency, hot-path rules, scoring, sync response. Снаружи доступен через Caddy на https://staging.antifraud.slim-shaggy.com/v1/events. См. apps/api/.

infra-dashboard-1

Django 5 · :18001 (host)

Веб-UI + админка. Снаружи: https://staging.antifraud.slim-shaggy.com/admin/, /dashboard/. HTMX, Alpine.js, Tailwind через whitenoise.

infra-caddy-1

:80, :443 (public)

Reverse-proxy + auto-HTTPS (Let's Encrypt) + JA3/JA4 plugin. Сертификат для staging.antifraud.slim-shaggy.com.

Хранилища

infra-clickhouse-1

ClickHouse · :18123

Аналитика. Таблицы: events, players, links, network_blocklist, behavioral_signals.

infra-postgres-1

Postgres 16 · :15432

Метаданные Django: операторы, юзеры, ключи, кейсы, вебхуки, биллинг, audit.

infra-redis-1

Redis 7 · :16379

Idempotency keys, rate-limit counters, session cache.

infra-redpanda-1

Redpanda · :19092

Kafka-совместимая очередь. Топики: events.raw, alerts.created, webhooks.outbound, network.auto_report_requests, *.dlq.

Consumers

alert-consumer

Module 04

Читает alerts.created, пишет в Postgres + триггерит UI-уведомление.

webhook-consumer

Module 15

Читает webhooks.outbound, делает HMAC-подпись, ретраит с экспоненциальным backoff, в DLQ если 5xx>5.

auto-report-consumer

Module 16

Читает network.auto_report_requests, лукапит opt-in, пишет хешированные идентификаторы в network_blocklist. Шипнули Sprint 22 (sid #382 — был BLOCKER).

erasure-consumer

GDPR

Читает erasure.requests, удаляет персональные данные игрока из CH + PG.

async-rules-consumer

Module 04 / 05

Тяжёлые правила (графовые, ATO baseline, ML scorers). Пишет апдейт-события и triggerит decision.updated webhook.

ML и observability

infra-mlflow-1

MLflow · :15000

Tracking + model registry. Эксперименты, метрики, артефакты моделей.

infra-prometheus-1

:19090

Метрики со всех сервисов. Скрейпит api, dashboard, consumers, redpanda, postgres-exporter, blackbox.

infra-grafana-1

:13000

Дашборды поверх Prometheus + Loki. admin/admin при первом входе.

infra-loki-1

:13100

Лог-агрегация. Promtail кормит из Docker.

infra-alertmanager-1

:19093

Маршрутизация прометеевских алертов в Slack/email/PagerDuty.

Жизненный цикл одного события

Что происходит с момента POST /v1/events до возврата ответа — и что ещё догоняется в фоне.

sequenceDiagram participant OP as Operator participant CDN as Caddy participant API as FastAPI participant R as Redis participant CH as ClickHouse participant K as Kafka participant ASY as async-rules-consumer participant W as webhook-consumer participant WH as Operator webhook OP->>CDN: POST /v1/events (Bearer key) CDN->>API: TLS-decrypt + add JA3/JA4 API->>R: check idempotency_key alt уже видели R-->>API: cached response API-->>OP: 200 (from cache) else новое API->>R: rate-limit check API->>API: hot rules (geo, IP, OFAC, network blocklist, fingerprint) API->>API: aggregate score → decision API->>K: produce event + decision (events.raw) API->>CH: insert event row (sync) API-->>OP: 200 {risk_score, decision, reasons} end K-->>ASY: read event ASY->>CH: load player history + graph subgraph ASY->>ASY: heavy rules + ML scorer alt decision changed ASY->>K: produce decision.updated K-->>W: read W->>WH: POST signed payload end

Идемпотентность

Каждый запрос несёт Idempotency-Key header. Redis хранит первый ответ 24 часа. Повторы возвращают тот же ответ без выполнения правил — оператор может безопасно ретраить.

Rate limit

Sliding-window counter в Redis по ключу operator_id. Лимит читается из плана (Plan.rps_limit). При превышении — 429 + Retry-After.

Hot path budget

p99 цель: <100мс. Поэтому в hot-path только всё, что можно посчитать за O(1) лукапы:

  • OFAC list (in-memory set).
  • Network blocklist (Redis SET по хешу).
  • IP intel (Redis cache + provider chain).
  • Fingerprint dedup (CH dictionary).
  • Geo по IP (MaxMind в памяти).

Типы событий

Шесть базовых типов. Discriminated union в Pydantic — каждый со своей схемой и набором обязательных полей.

ТипКогда оператор шлётКлючевые поляЧто чекаем
signupПри регистрации игрокаemail, ip, fingerprint, affiliate_id, utm_*Disposable email, geo, IP-интел, аффилиат-фрод
loginКаждый логинplayer_id, ip, fingerprint, ua, ja3ATO (новый device/country), биометрика
depositДепозит (фиат или крипта)amount, currency, payment_method, wallet_addressWallet intel (OFAC/mixer/age), shared_card
withdrawВыводamount, currency, wallet_addresswithdraw_without_play, round_trip, recovery_then_withdraw
betСтавка / спинgame_id, stake, rtp, outcomehigh_rtp_slots_only_pattern, bot timing
bonus_claimАктивация бонусаbonus_id, amountbonus_to_withdraw_velocity, multi-account-by-fingerprint

Общие поля

На каждом событии:

{
  "event_type": "",
  "player_id": "operator-side ID",
  "timestamp": "ISO 8601 UTC",
  "ip": "x.x.x.x",
  "fingerprint": {
    "fingerprint_hash": "...",
    "signals": { "canvas": "...", "webgl": "...", "audio": "...", "fonts": [...], "screen": "...", "tz": "...", "ua_ch": {...} }
  },
  "behavioral": {
    "mouse_path": [...],
    "keystroke_intervals": [...],
    "form_fill_ms": 4200
  }
}

Полные схемы: packages/shared/schemas/events.py. Документация для оператора: docs/integration/events.md.

Как мы ловим фрод

Стек защиты — четыре слоя: правила, граф, сигналы fingerprint/biometric, кросс-операторная сеть.

graph TD EV[Event] --> H[Hot rules ~30мс] H --> SC[Score aggregator] EV --> K[Kafka] K --> ASY[Async rules + ML] ASY --> SC2[Re-score] SC --> RESP[Sync response] SC2 --> WHU[decision.updated webhook] H -.- R1[Geo/IP/OFAC/network/fp dedup] ASY -.- R2[Graph: shared_card, communities, ML cohort]

Слой 1 — Rules engine

Каждое правило — Python класс, drop-in в apps/api/app/rules/plugins/. Авто-discovery, регистр не редактируется. Сейчас 51 правило.

Интерфейс правила:

class Rule:
    code: str
    severity: int  # 0..100
    hot: bool      # True → синхронно; False → async-rules-consumer

    def applies_to(self, event_type: str) -> bool: ...
    async def evaluate(self, event, context) -> RuleHit | None: ...

Новое правило = новый файл + декоратор. Никогда не правится центральный if/else.

Слой 2 — Graph module

Узлы: player, card_hash, IP /24, email_domain, fingerprint_hash, wallet_address. Рёбра: «использовал». Nawcomers: → полный раздел.

Слой 3 — Fingerprint + biometrics

Серверные сигналы (TLS JA3/JA4, HTTP header order) + клиентские (ThumbmarkJS canvas/webgl/audio/fonts) + behavioral (мышь, клавиатура, время заполнения формы) → 11 правил, в т.ч. biometrics_no_human_input, device_emulator_detected.

Слой 4 — Cross-operator network (Module 16)

Opt-in voluntary blocklist между операторами. Идентификаторы хешированы (SHA-256 от card_hash / email_domain / fingerprint_hash) — никаких сырых PII между операторами. Полный раздел: → Module 16.

51 правило

Все правила группированы по категориям. Файлы в apps/api/app/rules/plugins/.

Аффилиат-фрод (12)

КодОписание
affiliate_self_referralАффилиат сам себя зарегистрировал
affiliate_cohort_velocity_burstАномальный всплеск регистраций по аффилиату
affiliate_cohort_ip_concentrationВсе игроки аффилиата с одного /24
affiliate_cohort_low_ltvКогорта с подозрительно низким LTV (бонус-абьюз)
affiliate_low_diversityОднородные fingerprint/UA в когорте
affiliate_device_diversityСлишком мало уникальных устройств
affiliate_ip_qualityДоля прокси/VPN/Tor выше треshhold
affiliate_zero_retentionВсе игроки покинули после первого депо
affiliate_bonus_abuseКогорта оптимизирует только бонус-флоу
affiliate_timingРегистрации идут в подозрительные интервалы
affiliate_utm_consistencyUTM не бьются с трафик-источником
affiliate_network_blocklist_hit_rateИгроки аффилиата часто матчатся в Module 16

Биометрика и поведение (7)

biometrics_no_human_inputНулевая активность мыши/клавы
biometrics_no_mouse_movementТолько клики, без траектории
biometrics_robotic_typingИдеально равные интервалы клавиш
keystroke_too_uniformРазброс латентности нажатий ≈0
mouse_no_jitterПрямая линия без дрожи
form_filled_too_fastРегистрация <1с
paste_in_sensitive_fieldПароль/email вставлен через clipboard

Fingerprint / device (4)

device_emulator_detectedBlueStacks/NoxPlayer/Multilogin сигнатуры
http2_fingerprint_anomalyHTTP/2 setting frame не бьётся с UA
ja3_user_agent_mismatchJA3 не соответствует заявленному UA
shared_fingerprint_with_many_accountsОдин fingerprint у N+ аккаунтов

ATO / identity change (5)

new_device_loginЛогин с устройства, которого не было в baseline
new_country_loginЛогин из новой страны
password_reset_then_large_withdrawReset → крупный вывод в окне 1-24ч
email_change_then_withdraw_windowСмена email → вывод
phone_change_then_withdraw_windowСмена телефона → вывод
recovery_then_withdrawRecovery flow → вывод
behavior_change_post_loginБиометрика после логина не похожа на baseline

Wallet intel (4)

wallet_in_ofac_sanctionsАдрес в OFAC SDN list
wallet_interacted_with_mixer≥1 hop через Tornado/ChipMixer/etc.
wallet_in_scam_listChainabuse/CryptoScamDB hit
wallet_age_too_youngКошелёк создан <7 дней до депо
fresh_wallet_funded_only_for_depositКошелёк фондировался единственным переводом

Граф / shared identifiers (4)

shared_card_with_many_accountsОдна карта у N+ аккаунтов
shared_ip_24_with_many_accountsОдин /24 у N+ аккаунтов
shared_wallet_with_many_accountsОдин кошелёк у N+ аккаунтов
network_blocklist_hitModule 16 — match по хешу с другого оператора

Бонус / play patterns (4)

bonus_to_withdraw_velocityАктивировал бонус → быстрый вывод
withdraw_without_playДепо → вывод без ставок
round_trip_deposit_withdrawДепо и вывод почти равны и близко по времени
high_rtp_slots_only_patternИграет только в high-RTP слоты

Geo / IP / identity (8)

country_mismatch_signup_vs_depositСтрана signup ≠ страна deposit
geo_impossibleПеремещение быстрее, чем самолётом
ip_proxy_or_torIP — известный прокси/Tor exit
residential_proxy_signupРезидентный прокси на signup
disposable_email_domain10minutemail/mailinator/etc.
rapid_signup_burstN signup-ов за секунды с одного fingerprint
deposit_anomaly_first_timeПервый депо в N раз больше типичного
timing_pattern_botИнтервалы между событиями строго периодичны

Scoring и decision

Как из набора сработавших правил рождается одно решение.

Aggregator

Каждое сработавшее правило возвращает RuleHit{code, severity ∈ [0,100], reason_string}. Aggregator делает:

risk_score = min(100, sum(hit.severity for hit in hits) * decay)

где decay — фактор насыщения (чтобы 10 слабых правил не перевесили 1 сильное). Конфигурируется per-operator в operators.scoring_config.

Decision

Пороги — per-operator config. Дефолты:

ScoreDecisionЧто оператор должен сделать
0–39allowПропустить без вмешательства
40–69reviewStep-up auth / ручная модерация
70–100blockОтказать

Reasons

Список snake_case строк — машинно-читаемый, дашборд маппит на UI-копи. Пример:

{
  "risk_score": 78,
  "decision": "block",
  "reasons": [
    "wallet_in_ofac_sanctions",
    "shared_fingerprint_with_many_accounts",
    "ip_proxy_or_tor"
  ]
}

Объяснимость

В дашборде на странице игрока для каждого решения показывается:

  • Список сработавших правил с severity-вкладом.
  • Для async ML-скоров — SHAP top-N features (через apps/api/app/ml/audit.py).
  • Граф связей: вершины и рёбра, контрибьютировавшие в shared_*-правила.

Графовый модуль

Module 05. Сейчас на networkx + ClickHouse, в будущем — Neo4j (за тем же интерфейсом).

Что в графе

Узлы: player, card_hash, IP /24, email_domain, fingerprint_hash, wallet_address. Рёбра: «использовал» с весом и timestamp. Хранятся в ClickHouse links.

graph LR P1[Player A] --- C1[Card hash] P2[Player B] --- C1 P3[Player C] --- C1 P1 --- IP1[IP /24 1.2.3.0/24] P2 --- IP1 P4[Player D] --- IP1 P1 --- F1[Fingerprint xyz] P2 --- F1

Так формируется community вокруг общих идентификаторов. Если узлов в community > threshold — каждый player в community получает алерт shared_*_with_many_accounts.

Алгоритмы

  • Connected components — базовый детектор колец.
  • PageRank — выявить «хабовых» игроков (мул-аккаунты).
  • Louvain communities — кластеризация плотных сообществ.
  • Fingerprint similarity (cosine) — связки по похожим, но не идентичным fingerprint-ам.

Когда считается

Не на hot path (слишком дорого). async-rules-consumer подгружает subgraph радиуса 2 вокруг игрока на каждое событие, считает за <500мс, при ringe-detection пишет алерт + триггерит decision.updated.

Кеш

Считанные subgraph и community-IDs кешируются в Redis по хешу содержимого. TTL 5 минут. Инвалидация при новых рёбрах в links.

Известный баг: cache invalidation не покрывает PageRank/community ключи (intake #356 — low).

Module 16 — кросс-операторная сеть

Стратегический моат, который конкуренты не повторят. Voluntary opt-in blocklist между операторами с privacy by hash.

Зачем

Регуляторные регистры самоисключения (GamStop, Spelpaus и т.д.) недоступны крипто-казино с лицензиями Curaçao/Anjouan. Module 16 — наша замена: операторы добровольно складывают хеши идентификаторов известных фродеров в общий пул, на ингесте каждый opt-in оператор лукапит входящие хеши.

Как работает

sequenceDiagram participant OPa as Operator A (opt-in) participant CASE as Case "closed_fraud" в Casino A participant ARC as auto-report-consumer participant CH as ClickHouse network_blocklist participant API as Ingest API participant OPb as Operator B (opt-in) CASE->>ARC: Kafka event {hashed identifiers} ARC->>ARC: проверить optin OPa ARC->>CH: insert hashed rows OPb->>API: POST /v1/events (новый игрок) API->>CH: lookup network_blocklist by hashes CH-->>API: hit API-->>OPb: decision=block, reason=network_blocklist_hit

Что хешируется

  • SHA-256(card_hash) — карты.
  • SHA-256(email_domain) — НЕ полный email, только домен.
  • SHA-256(fingerprint_hash) — устройства.
  • SHA-256(wallet_address) — крипто-кошельки.

Сырых PII в blocklist нет. Соответствие восстановить по хешу нереально — только посмотреть «знакомо ли».

Проверка end-to-end

Sprint 23 (sid #391) шипнул staging smoke: scripts/smoke/module16_e2e.py + tests/staging/test_smoke.py @smoke. Прогоняется при каждом деплое.

Включить для оператора

В админке: Operators → выбрать оператора → галочка network_optin = True. После включения — кейсы с лейблом closed_fraud начинают публиковаться.

ML-пайплайн

Module 08. Сейчас в проде одна модель — affiliate cohort baseline (LightGBM). Inference ML scorer-ов реализован за Scorer protocol — модели свапаются конфигом.

graph LR R[(Raw events CH)] --> F[Feature engineering] F --> FS[Feast feature store] L[Operator labels] --> T[Training notebooks] FS --> T T --> ML[MLflow registry] ML --> SC[Scorer protocol] SC --> API[Sync hot path] SC --> ASY[Async rules consumer]

Этапы

  1. Feature engineeringapps/api/app/ml/features/. Рассчитывает признаки из events + links: aggregations за 1ч/24ч/7д, графовые (degree, PR-rank), behavioral (mouse_entropy_x — TODO #350).
  2. Feature store — Feast. Online (для инференса) + offline (для обучения). Time-travel lookups (важно: PaySim leakage был intake #358 — нужен time-travel, не обычный join).
  3. Training — Jupyter ноутбуки в notebooks/. Сейчас работают с public datasets (IEEE-CIS, PaySim) для смоук-проверки плумбинга.
  4. Tracking — MLflow на :15000. Каждый run пишет params/metrics/artifacts.
  5. Registry — модели регистрируются в MLflow Model Registry с тегом artifact_sha256 (для верификации в проде — intake #378).
  6. DeploymentScorer протокол:
    class Scorer(Protocol):
        def score(self, features: dict) -> tuple[float, list[str]]: ...
    
    Реализации: rules-only (baseline), LightGBM affiliate cohort, GNN (DGL — пока не в проде), ensemble.
  7. Driftapps/api/app/ml/drift.py. Считает PSI/JS-divergence между training distribution и live, шлёт прометеевские алерты при дрифте.
  8. Explainability — SHAP top-N features логируется на каждом async-инференсе для дашборда.

Откуда данные для модели

Три источника, по приоритету: (1) operator labels, (2) public datasets, (3) synthetic.

1. Размеченные данные оператора

Канонический спек: DATA_REQUIREMENTS.md в корне репо. Что просим:

  • events за 6+ месяцев (шесть типов).
  • player labels — fraud_category ∈ {clean, multi_account, bonus_abuse, ato, mule, chargeback, fake_kyc}.
  • cases history — что закрыто как фрод/чисто.
  • chargeback ledger.

Импорт: scripts/import_operator_dataset.py. Когда первый партнёр-казино пришлёт данные — за ~3-4 недели поднимем baseline LightGBM (multi-class fraud).

2. Public datasets (для plumbing-проверки)

  • IEEE-CIS Fraud Detection (Kaggle) — 590k строк e-commerce фрода. Используется в смоук-ноутбуках, чтобы убедиться что MLflow + Feast + LightGBM едут.
  • PaySim — синтетический mobile-money dataset. Был leak в dest_fan_in feature → intake #358 предписывает Feast time-travel.

3. Synthetic events (для dev / demo / load test)

tools/synthetic_events/generator.pySyntheticEventGenerator класс. Генерит:

  • Реалистичные распределения (Pareto на amount, lognormal на time-between-events).
  • fraud-rate%-доля игроков с fraud-патернами (multi-account по shared fingerprint, bonus → withdraw, OFAC wallet, mixer hop).
  • Output: parquet (для офлайн) или stream в API.

Используется в:

  • Локальный smoke (scripts/dry_run.py).
  • CI integration tests.
  • Demo/sales-демонстрации.

Active learning loop

В дашборде есть /dashboard/active-learning/ — UI разметки. Risk manager помечает алерт как «фрод подтверждён» / «ложное срабатывание» / «не уверен». Эти лейблы накапливаются в apps/dashboard/apps/ml_labels/ и кормят следующий цикл обучения. Это и есть наш «корпус» для supervised fine-tuning, когда оператор-данные приедут.

Деплой моделей

Модели свапаются конфигом, не кодом. Принцип «конфигурация над кодом» из CLAUDE.md.

Текущее состояние

МодельФайлИспользует
affiliate_cohort_baselineapps/api/app/ml/models/affiliate_cohort_baseline.lgbaffiliate_cohort_scorer.py в async path

Как добавить новую модель

  1. Натренировать → залогировать в MLflow с тегом артефакта.
  2. Положить артефакт в apps/api/app/ml/models/ (или подгрузить из MLflow registry на старте).
  3. Реализовать Scorer protocol в apps/api/app/ml/scorers/your_model.py.
  4. Зарегистрировать в scoring_config у оператора в админке (через JSON-поле).

Перезапуск api НЕ нужен — конфиг читается из БД на каждом scoring call.

Roll-out / Roll-back

В scoring_config есть shadow_mode: True — модель считает скор, но в decision не учитывается. Логи пишутся параллельно с baseline для сравнения. Через 1-2 недели shadow → flip в active. Roll-back = снять флаг.

Drift и мониторинг

Прометеевские метрики (Module 08):

  • ml_inference_latency_seconds — p99 на скоринг.
  • ml_feature_psi — PSI между train и live на каждый feature.
  • ml_score_distribution_jsd — JS-divergence распределения скоров.

Alertmanager шлёт алерт в Slack при PSI > 0.2 на любом критичном feature — сигнал что пора переобучать.

JS SDK + fingerprint

Module 06. Оператор embed-ит SDK в страницы signup/login/deposit. SDK собирает fingerprint и behavioral signals, кладёт в request на ingest API.

Что внутри

  • База: ThumbmarkJS — canvas, webgl, audio, fonts, screen, timezone.
  • UA Client Hints (новый стандарт).
  • Behavioral capture (Module 11): mouse path, keystroke timing, form-fill duration, paste detection.
  • GDPR / DNT compliance — если выставлен navigator.doNotTrack или флаг antifraud_consent=false, behavioral capture отключается.

Дистрибуция

  • npm: @antifraud/sdk (планируется).
  • CDN bundle (для прямого embed): https://cdn.antifraud.example/sdk-0.2.0.min.js.
  • Build: tsup + vite в apps/sdk/.

Версии

Sprint 22 (sid #379) шипнул transition window 0.1.x → 0.2.0. Дашборд лукапит правила по обоим хешам в течение 30-90 дней. Полный гайд: docs/integration/sdk-fingerprint-migration.md.

Использование

<script src="https://cdn.antifraud.example/sdk-0.2.0.min.js"></script>
<script>
  const fp = await window.AntifraudSDK.collect();
  // fp = { fingerprint_hash, signals, behavioral }
  await fetch('/api/casino/signup', {
    method: 'POST',
    body: JSON.stringify({...formData, antifraud: fp})
  });
</script>

Дальше backend оператора прокидывает antifraud субобъект в ingest API в поле fingerprint.

Webhook-и

Module 15. Async-доставка событий оператору: alert.created, decision.updated, case.closed, network.report_published.

Настройка

  1. Админка → Webhooks → Endpoints → Add.
  2. Поля: operator, url (только https), events_subscribed (CSV), secret (генерится).
  3. Секрет показывается один раз — записать.

Доставка

  • POST на url с body JSON и заголовками X-Antifraud-Signature (HMAC-SHA256) + X-Antifraud-Event.
  • Retry: экспоненциальный backoff 1s, 5s, 30s, 5m, 30m. Всего 5 попыток.
  • После 5 неудач — в DLQ webhooks.dlq. В дашборде /dashboard/webhooks/deliveries/ — фильтр failed.

Verify пример (Python)

import hmac, hashlib

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

Полные примеры (Node, PHP, Java): docs/integration/webhooks.md.

API-ключи и лимиты

Формат ключа

sk_live_ + 32 символа base62. Префикс позволяет grep-нуть в логах. В Postgres хранится pbkdf2(secret, salt, 100k iterations).

Создание

Оператор сам: /dashboard/settings/api-keys/ → Create. Платформенный admin: Apikeys → API keys → Add в /admin/. После сохранения секрет показывается один раз в обоих случаях.

Программно: apps.apikeys.services.create_api_key(operator, label).

Rotation

Создать новый ключ → раздать оператору → подождать <TTL → пометить старый revoked=True. Старый перестаёт авторизовать запросы (401), новый продолжает.

Лимиты per-plan

PlanEvents/месRPSRetention
free50k2030 дней
starter500k10090 дней
pro5M500365 дней
enterprisecustomcustom365+ дней

Конфигурятся в apps/dashboard/apps/pricing/. При превышении — 429 + Retry-After.

Деплой и rollback

Auto-deploy

Push в main → GitHub Actions .github/workflows/deploy.yml → SSH на Hetzner → git pull → migrations → docker compose up -d --build → smoke. Время цикла ~1 мин.

CI/e2e/SLO workflows отключены с автотриггеров (Sprint 23, commit b921503) чтобы не палить Actions-бюджет. Запуск вручную: Actions tab → "CI" → Run workflow.

Ручной деплой конкретного SHA

gh workflow run deploy.yml -f sha=<commit-sha>

Rollback

gh workflow run deploy.yml -f sha=<previous-green-sha>

Workflow идемпотентный — деплой того же SHA повторно безопасен.

Hotfix

Не редактировать прод по SSH. Патч → коммит → push → дождаться auto-deploy. Если очень срочно — workflow_dispatch с конкретным SHA.

ClickHouse миграции

SQL-файлы в apps/api/migrations/clickhouse/NNN_*.sql. Применяются deploy-workflow-ом по порядку. Идемпотентные (CREATE ... IF NOT EXISTS, ALTER ... ADD COLUMN IF NOT EXISTS).

Логи и метрики

Логи через SSH

ssh root@178.104.214.209
cd /root/antifraud/infra
docker compose logs -f api --tail=200
docker compose logs -f dashboard --tail=200
docker compose logs -f auto-report-consumer alert-consumer webhook-consumer --tail=100

Логи через Loki (Grafana)

Grafana запущена на хосте на :13000 (только 127.0.0.1). Чтобы открыть в браузере — туннель:

ssh -L 3000:127.0.0.1:13000 root@178.104.214.209
# открыть http://localhost:3000 → Explore → Loki

Полезные запросы:

{container="infra-api-1"} |= "ERROR"
{container=~"infra-(alert|webhook|auto-report)-consumer-1"} |~ "exception"
{container="infra-dashboard-1"} | json | level="error"

Метрики (Prometheus, через туннель)

ssh -L 9090:127.0.0.1:19090 root@178.104.214.209
# открыть http://localhost:9090/targets — все scrape targets
  • API: infra-api-1:8000/metrics
  • Dashboard: infra-dashboard-1:8001/metrics
  • Auto-report consumer: infra-auto-report-consumer-1:9400/metrics
  • Redpanda: infra-redpanda-1:9644/metrics
  • Postgres / CH / Redis — через соответствующие exporter-ы

Дашборды Grafana

Готовые дашборды в infra/grafana/provisioning/:

  • Ingest API — RPS, p50/p95/p99 latency, error rate.
  • Decisions — распределение allow/review/block, top reasons.
  • Consumers lag — Kafka lag по каждому consumer.
  • Database — CH inserts/sec, PG connections, Redis ops/sec.
  • ML — inference latency, PSI, score distribution.

Health-checks (с ноута через HTTPS)

curl https://staging.antifraud.slim-shaggy.com/healthz   # 200 — стек жив
curl https://staging.antifraud.slim-shaggy.com/readyz    # 200 — все подключения ОК

Если /healthz = 200, а /readyz ≠ 200 — какой-то из бэкендов отвалился. Тело ответа покажет какой (ClickHouse/Kafka/Redis/Postgres).

Если сломалось

Все диагностические команды — через ssh root@178.104.214.209 с переходом в /root/antifraud/infra.

HTTPS не отвечает

Сначала с ноута:

curl -v https://staging.antifraud.slim-shaggy.com/healthz

Если timeout / TLS error — Caddy лежит. SSH на хост:

docker compose ps caddy
docker compose logs caddy --tail=200
docker compose restart caddy

Контейнер не поднимается

docker compose ps                       # увидеть статус всех 21
docker compose logs <service> --tail=200
docker compose restart <service>

API возвращает 500

Проверь подключения изнутри хоста:

docker compose exec clickhouse clickhouse-client --query 'SELECT 1'
docker compose exec postgres pg_isready -U antifraud
docker compose exec redis redis-cli ping
docker compose exec redpanda rpk cluster health

Алерты не появляются

  1. docker compose logs alert-consumer --tail=50 — есть ли exceptions?
  2. docker compose exec redpanda rpk topic consume events.raw --num 5 — события вообще пишутся?
  3. Если consumer стоит → docker compose restart alert-consumer.

Module 16 не пишет в blocklist

  1. В админке проверь у оператора галочку network_optin = True.
  2. docker compose logs auto-report-consumer --tail=100.
  3. Запусти staging smoke (с ноута):
    cd ~/PycharmProjects/iGamingAntiFraudSaaS
    python scripts/smoke/module16_e2e.py --full-pipeline \
      --api-base https://staging.antifraud.slim-shaggy.com \
      --api-key sk_live_xxx

Деплой упал

С ноута:

gh run list --branch main --limit 5     # последний run
gh run view <run-id> --log-failed       # лог упавшего шага

Откат на предыдущий зелёный SHA:

gh workflow run deploy.yml -f sha=<previous-green-sha>

Где смотреть «что сработало» в правилах

Поставь LOG_LEVEL=DEBUG на хосте в /root/antifraud/.env, потом docker compose restart api. Каждое решение пишется со списком hits.

Полный perf-debug

Через SSH-туннель открой Grafana (ssh -L 3000:127.0.0.1:13000 root@178.104.214.209) → дашборд "Ingest API" покажет p99-spike. В Loki фильтр {container="infra-api-1"} |~ "slow query" найдёт медленные правила. Если правило конкретное — смотри apps/api/app/rules/plugins/<rule_code>.py.

Куда копать дальше

ХочешьКуда смотреть
Полный план + архитектура (RU+EN)Plan.md, Implementation.md
CLAUDE.md (агентам)CLAUDE.md
Dev-обзор на русскомdocs/обзор.md
Дока для оператора-клиентаdocs/integration/README.md
Operator dry-run runbookdocs/integration/dry-run.md
Onboarding оператораdocs/onboarding/
Runbook-и для продаdocs/runbooks/
Бенчмаркиdocs/benchmarks/
DATA_REQUIREMENTSDATA_REQUIREMENTS.md
Postman / Brunodocs/integration/postman/, bruno/
Code samples (Python/Node/PHP/Java)docs/integration/code-samples/
Plane (задачник)Backlog
Спринт-историяmemory/sprint_*_plan.md
Staging credentialsmemory/staging_server.md
Plane API referencememory/plane_api_works.md

Кого спрашивать

Каждый area:* в Plane имеет свой sub-agent в .claude/agents/. При работе с задачей label определяет агента:

  • area:backendbackend-engineer (FastAPI, rules, async pipelines)
  • area:frontenddjango-engineer (Django, HTMX, Alpine, Tailwind)
  • area:infrainfra-engineer (Docker, Caddy, observability)
  • area:deploydeploy-engineer (CD, hetzner, rollback)
  • area:mlml-engineer (LightGBM, GNN, Feast, MLflow, SHAP)
  • area:sdksdk-engineer (TS SDK, Thumbmark, biometrics)
  • area:graphgraph-engineer (networkx, communities)
  • area:cryptocrypto-engineer (wallet APIs, OFAC, mixer)
  • area:docsdocs-writer (integration guides, samples)
  • cross-cutting → security-engineer (auth, secrets, hashing, multi-tenant isolation)

Статус проекта

На 2026-05-07: 24 спринта закрыты, MVP + UI v2 отгружены, все 17 модулей реализованы (~1450 тестов), все production launch gates closed. Sprint 24 добавил design system, 9 переработанных экранов, 5 новых URL и MFA-флоу. Следующий шаг — живой operator dry-run через scripts/dry_run.py с реальным opt-in оператором, потом подписать первый контракт.