Оформление
Файлы и слой S3
Где физически лежат файлы чатов, как устроен доступ к ним в стиле S3, как инструменты MCP получают временные ключи и какие действуют ограничения.
Главное, что нужно понять
Объектного хранилища в платформе нет. Ни MinIO, ни AWS S3, ни другого сервиса хранения. Файлы лежат в обычном каталоге на диске, а сведения о них в базе данных.
При этом API отдаёт эти файлы через набор адресов, повторяющих часть протокола S3. Сделано это для инструментов MCP: любой сервер, умеющий работать с S3, подключается к EVA без доработки.
Что из этого следует
API и Runtime должны видеть один и тот же каталог. Развести их на разные машины без общего диска нельзя. Репликации между хранилищами тоже нет, резервное копирование это копирование каталога вместе с базой данных.
Схема движения файла
Где лежат файлы
Каталог задаётся переменной SESSION_FILES_DIR, по умолчанию .data/session-files рядом с API. Внутри одна папка на чат:
{SESSION_FILES_DIR}/{идентификатор чата}/{идентификатор файла}__{имя файла}Этот же путь передаётся рабочим процессам Runtime, поэтому инструменты работы с файлами читают их напрямую с диска. Выход за пределы каталога заблокирован.
Имя файла приводится к безопасному виду. Сохраняются буквы и цифры любого алфавита, включая русский, а также точка, дефис, пробел, круглые и квадратные скобки. Всё остальное заменяется на подчёркивание.
Сведения о файле
В базе на каждый файл хранится: чат, вид файла, имя, тип содержимого, размер, путь на диске и признак того, кто последним менял файл, пользователь или ИИ.
Вид файла бывает трёх значений: загруженный пользователем, рабочий и результат работы. Вид это только пометка, на путь к файлу он не влияет.
Одно имя на чат
В одном чате не может быть двух файлов с одинаковым именем. Повторная загрузка или запись файла с тем же именем перезаписывает прежний.
Адреса слоя S3
Все обращения идут на /api/chat-s3 и требуют временный ключ в параметре token.
| Операция | Запрос | Что возвращает |
|---|---|---|
| Список объектов | GET /api/chat-s3/{bucket}?list-type=2&prefix=...&token=... | XML в формате ListBucketResult |
| Получить объект | GET /api/chat-s3/{bucket}/{key}?token=... | Содержимое файла, тип, размер, ETag |
| Проверить объект | HEAD на тот же адрес | Только заголовки |
| Записать объект | PUT /api/chat-s3/{bucket}/{key}?token=... | Пустое тело, ETag и заголовки с именем файла |
| Удалить объект | DELETE /api/chat-s3/{bucket}/{key}?token=... | Код 204, повторное удаление ошибкой не считается |
Параметр list-type должен быть равен ровно 2, иначе запрос будет отклонён.
Имя корзины
Корзина одна, её имя берётся из CHAT_S3_BUCKET, по умолчанию chat-files. Обращение к любой другой корзине вернёт ошибку. Создавать и удалять корзины нельзя.
Ключ объекта
Ключ объекта:
sessions/<идентификатор чата>/<имя файла>Имя файла в ключе закодировано по правилам URL.
Заголовки с русскими именами
При записи объекта API возвращает заголовки с ключом объекта и именем файла. Значения закодированы через encodeURIComponent, потому что в заголовках HTTP нельзя передавать неанглийские символы. Клиент обязан раскодировать их обратно.
Чего в слое нет
Многочастной загрузки, версий объектов, копирования, подписанных ссылок, подписи SigV4, запросов по диапазону байтов и постраничного перебора. Это подмножество, а не полноценный S3.
Временные ключи доступа
Ключ выглядит так:
base64url(JSON с чатом, пользователем и временем истечения) + "." + base64url(подпись HMAC-SHA256)Это не JWT. Подпись считается на общем секрете платформы.
| Свойство | Значение |
|---|---|
| Как передаётся | Параметр запроса token |
| Время жизни | CHAT_S3_TOKEN_TTL_SEC, по умолчанию 900 секунд |
| Когда выдаётся | На каждое сообщение, если хотя бы один подключённый сервер MCP имеет доступ к файлам чата |
| Как попадает к серверу MCP | Только внутри вызова init_eva_session |
| Видит ли ключ модель | Нет, никогда |
Поскольку привязка сессии выполняется заново перед вызовами инструментов, сервер MCP получает обновлённый ключ по ходу работы чата.
Адрес, который передаётся серверу MCP, зависит от настройки конкретного источника: внутренний адрес платформы, внешний адрес или собственный адрес для этого источника. К выбранному адресу добавляется /api/chat-s3.
Адрес должен быть доступен из контейнера MCP
Если сервер MCP работает в своём контейнере, адрес 127.0.0.1 для него бесполезен. Нужно указывать адрес, по которому API виден этому контейнеру.
Изоляция чатов
Проверка простая и строгая:
- чат в ключе доступа должен совпадать с чатом в ключе объекта, иначе запрос отклоняется;
- при запросе списка объектов префикс обязан начинаться с
sessions/<чат из ключа доступа>/, иначе запрос отклоняется, а результат ещё раз фильтруется по префиксу.
Прочитать файлы другого чата по действующему ключу невозможно.
Ограничения на файлы
| Ограничение | Значение |
|---|---|
| Максимальный размер одного файла | 1 ГиБ |
| Максимум файлов за одну загрузку | 10 |
| Таймаут загрузки | 1 час |
| Исполняемые файлы | Запрещены по расширению, по типу содержимого и по первым байтам файла |
| Загрузка по ссылке | Только http и https, без данных для входа в ссылке, не больше 5 переадресаций, таймаут 20 секунд, тот же предел 1 ГиБ |
| Аватар и значок агента | Изображение в виде data-URL, не больше 400 000 символов |
| Чтение текста инструментом | Не больше 200 000 символов за раз |
| Поиск по файлам чата | Не больше 50 совпадений |
Проверка по первым байтам означает, что переименовать исполняемый файл в документ и загрузить его не получится.
Другие пути к файлам
Кроме слоя S3 есть обычные адреса для интерфейса:
- скачивание файла пользователем;
- запись файла из встроенного редактора, при этом файл помечается как изменённый пользователем;
- отдельный предпросмотр HTML: платформа выдаёт временный ключ со временем жизни
PREVIEW_TOKEN_TTL_SEC, по умолчанию 1200 секунд, и обслуживает файлы по имени, чтобы сгенерированная страница могла подключить свои стили.
Что стоит учитывать
Удаление чата удаляет записи в базе, но не удаляет каталог с файлами на диске. Автоматической очистки осиротевших файлов нет, кроме удаления конкретного файла при его удалении или перезаписи.