Поверхность безопасности диалогового слоя — что Query Engine держит сам, а что лишь предъявляет. Инъекцию во вход уже разобрал grounding (найденное — данные, не команды), а инъекцию через аргументы инструмента — retrieval (личность для ACL — из сессии, не из аргументов модели, потому подмена доступа невозможна); права на знания и аутентификацию держит Auth. Здесь — четыре вещи, которыми владеет именно диалог: доступ к самому треду, ответ как недоверенный канал вывода, egress контекста к модели и следы запросов. Остальное — аутентификация, права, лимиты, конфиг моделей, рендер — у соседей; карта ниже разводит одно от другого.
conversation_id адресует тред, но не доказывает право на
него. Доступ — только у владельца: на чтении, продолжении
(resume)
и удалении эндпоинт сверяет user_id владельца беседы с
текущим — поверх RBAC, тем же паттерном «над своим ли
объектом», что
Auth
применяет к личным агентам и ключам. Основа уже в модели:
conversations
несут user_id владельца.
conversation_id не даёт доступа: его даёт совпадение
user_id. Без этой сверки продолжение разговора по чужому
идентификатору — типовой IDOR: A читал бы переписку B.
Grounding
закрыл вход — найденное не команда. Зеркало — выход: ответ модели для
рисующего его surface тоже недоверенный. Подложенная в
найденный фрагмент строка может заставить модель вывести ссылку или
картинку вида http://чужой/?leak=…; чат, авто-загрузив
ресурс, утащит контекст наружу. Поэтому рендер ответа обезвреживает
разметку: не исполняет её, не авто-грузит внешние ресурсы (картинки,
fetch), экранирует вывод.
Пользователь выбирает chat-модель, и генерация шлёт ей упакованный контекст — найденные корпоративные фрагменты. Если модель облачная, эти фрагменты покидают периметр и уходят провайдеру. Allow-list моделей у Admin уже сужает выбор — но по стоимости и доступности, не по чувствительности данных.
Диалог оставляет следы — история (conversations /messages с текстами запросов и ответов) и retrieval_trace (что искали и что нашлось). Это корпоративные данные: живут под теми же правами и уходят каскадом при удалении пользователя; в логи не пишем полный контекст и секреты.
Просмотр следов — два разных случая, не путать.
Своя история: пользователь листает собственные беседы
— что спрашивал и что ответили; видна только владельцу (по
user_id, как
владение тредом), а сам
экран — оболочка Web App. Доступ администратора к
чужим беседам и retrieval_trace — право редкое,
надзорное v2: на первой итерации не тянем.
Чего этот слой не держит, а лишь уважает: кто пользователь и что ему видно — устанавливает Auth (surface предъявляет токен, не диктует личность), а прикладные лимиты и стоимость (per-user rate limit, отдельная корзина для дорогих LLM-запросов) спроектированы там же и помечены v2. Query Engine их предъявляет и соблюдает, схему не строит.