0012. Журнал аудита действий пользователей и AI-агентов
Копия ADR из репозитория:
backend/docs/adr/0012-audit-log.md. Канонический текст — в репозитории; портал обновляется командойnpm run sync:adr.
- Статус: accepted
- Дата: 2026-06-22
- Связанные задачи: OP-45
Контекст и проблема
ТЗ 5.10 и требования модулей предписывают журнал аудита значимых операций
(изменение цен/ставок, отправка ответов/жалоб, бронирование) — с явным указанием
актора, в том числе AI-агента, объекта и результата. Сейчас аудит эмулируется
строковыми логами (this.logger.log('audit.wb-account-…') в wb-accounts.service.ts),
которые помечены как заглушки под будущий F6-стор.
Ограничения и опоры из уже сделанного:
- ORM — TypeORM, миграции явные (
0001-orm-and-migrations). - Корреляция —
CorrelationService.getCorrelationId()из ALS (0011-structured-logging). - Актор в системе — только
JwtAuthenticatedUser {id,email,role}(0004-rbac…); понятия «AI-агент»/«система» как актора ещё нет. - JSONB разрешён конвенцией (
0001), но в коде ещё не применялся.
Рассмотренные варианты
Способ фиксации
- Только программный
AuditService.record()— модули вызывают явно.- Плюсы: простота, полный контроль над before/after.
- Минусы: «пометка операций» из ТЗ закрыта лишь частично; многословно на местах вызова.
- Только декоратор + интерсептор (
@Audit) — авто-фиксация.- Плюсы: декларативно, минимум шума в коде.
- Минусы: интерсептор не знает доменного состояния «до» → before/after недоступны; невыразимы сложные случаи.
- Гибрид: сервис + декоратор/интерсептор (выбран).
- Плюсы: декоратор закрывает массовый простой случай (факт+актор+исход+correlation-id), сервис — сложные случаи с before/after; покрывает оба требования ТЗ.
- Минусы: два пути фиксации (компенсируется тем, что интерсептор — тонкая обёртка над сервисом).
Модель актора
- Хранить только
userId.- Минусы: не выразить агента/систему (cron, фоновые джобы) — прямо противоречит ТЗ 5.10.
- Полиморфный актор:
actorType(user/agent/system) +actorId+actorLabel(выбран).- Плюсы: forward-compatible под AI-агентов (появятся с OP-47/48/49) и системные действия;
уже сейчас выражает
userиsystem. - Минусы: на момент OP-45 ветка
agentне имеет продюсера (схема готова, данные — позже).
- Плюсы: forward-compatible под AI-агентов (появятся с OP-47/48/49) и системные действия;
уже сейчас выражает
Решение
Гибридный механизм + полиморфный актор. Состав модуля src/modules/audit-log/:
AuditLogEntity(audit_logs, наследуетBaseEntity):actorType(enumaudit_actor_type),actorId,actorLabel,action,entityType,entityId,outcome(enumaudit_outcome),before/after/metadata(JSONB — первое применение в проекте),correlationId. Индексы по актору, объекту, действию, correlation-id и дате. Записи иммутабельны (не обновляются, не удаляются).AuditService.record()— подставляет correlation-id из ALS, маскирует секреты (redactSecrets), и никогда не пробрасывает исключение в бизнес-код (провал аудита логируется и проглатывается).@Audit({action, entityType})+AuditInterceptor— авто-фиксация факта/актора/исхода; при ошибке хэндлера ставитoutcome=failureи пробрасывает оригинальную ошибку.GET /audit-logs— только для ролиAdmin, с whitelist-фильтрами и пагинацией (OP-27).- Первый консьюмер: ретрофит строковых
audit.*-логов вwb-accounts.service.tsнаrecord().
Последствия
- Положительные: единый, тестируемый журнал аудита; готовность к акторам-агентам и системным действиям без миграции; сшивка с логами через correlation-id; чувствительные данные маскируются; аудит не влияет на надёжность бизнес-операций.
- Отрицательные / компромиссы: два пути фиксации; ветка актора
agentпока без продюсера; before/after недоступны через интерсептор (только через явныйrecord). - Дальше: партиционирование
audit_logsпо времени при росте — после OP-91 (менеджер партиций); продюсеры-агенты — с инфраструктурой LLM (OP-47/48/49); остальные модули подключают аудит по мере реализации.