Оформление
Контракт chat S3
Как сервис MCP читает файл чата и записывает результат: адреса, структура ключей, папка результатов, временные доступы, изоляция чатов и ограничения.
Адрес обращения
{endpoint}/{bucket}/{key}?token={token}где endpoint заканчивается на /api/chat-s3, а bucket по умолчанию chat-files.
Пример:
http://localhost:3000/api/chat-s3/chat-files/sessions/sess_123/report.html?token=...Границы чата
Все файлы чата лежат под общим префиксом:
sessions/<идентификатор чата>/Структура ключей
sessions/<идентификатор чата>/<имя файла>Вид файла, то есть загрузка, рабочий или результат, это пометка в базе данных платформы. Частью пути он не является.
| Что передала модель | Во что превращается |
|---|---|
report.docx | sessions/<чат>/report.docx |
| Идентификатор файла | sessions/<чат>/<идентификатор> |
| Путь с подкаталогом | Отклоняется |
Путь с .. | Отклоняется |
| Полный ключ другого чата | Отклоняется |
Проверка выхода за границы своего чата это главная граница безопасности на стороне сервиса.
Имя результата должно отличаться от имени источника
В одном чате не может быть двух файлов с одинаковым именем. Поэтому инструмент, который запишет результат под именем исходного файла, заменит исходный файл. Имя результата должно отличаться: либо другим расширением, либо добавкой к имени.
Операции
| Операция | Запрос |
|---|---|
| Список объектов | GET /{bucket}?list-type=2&prefix=...&token=... |
| Чтение | GET /{bucket}/{key}?token=... |
| Проверка наличия | HEAD на тот же адрес |
| Запись | PUT /{bucket}/{key}?token=... с телом |
| Удаление | DELETE /{bucket}/{key}?token=... |
Ответ на список объектов приходит в виде XML:
xml
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>chat-files</Name>
<Prefix>sessions/sess_123/</Prefix>
<KeyCount>1</KeyCount>
<MaxKeys>1000</MaxKeys>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>sessions/sess_123/report.html</Key>
<Size>1234</Size>
<LastModified>2026-08-11T09:14:02.113Z</LastModified>
</Contents>
</ListBucketResult>Ошибка приходит так:
json
{ "error": "FORBIDDEN", "message": "Token does not match session scope" }Коды ошибок: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, BAD_REQUEST, INTERNAL_ERROR.
Ни один сервис не пользуется всеми операциями
Проверку наличия, удаление и список объектов не вызывает ни один из переданных сервисов. Все они только читают по известному имени и пишут результат.
Правила работы по сети
Одинаковые у всех сервисов и описаны в общем регламенте.
| Правило | Значение |
|---|---|
| Ключ доступа | Добавляется к каждому запросу отдельно, не встраивается в клиент |
| Чтение | Обычный GET |
| Запись | PUT с указанием типа содержимого |
| Ответ 415 при записи | Одна повторная попытка с типом application/octet-stream |
| Повторные попытки | Только при кодах 502, 503, 504 и сетевых сбоях, с растущей задержкой |
| Коды 404, 415, 500 | Возвращаются без повторов, разбираются вызывающей стороной |
Соответствие ответов ошибкам сервиса:
| Ответ | Ошибка сервиса |
|---|---|
| 401 или 403 | Ключ доступа истёк |
| 404 при чтении | Файл не найден |
| Остальное при чтении | Не удалось прочитать |
| Остальное при записи | Не удалось записать |
Временные доступы
| Свойство | Значение |
|---|---|
| Что это | Подпись HMAC-SHA256 над идентификаторами чата, пользователя и временем истечения |
| Как передаётся | Параметр запроса token |
| Время жизни | 15 минут по умолчанию |
| Когда выдаётся | Один раз на сообщение пользователя |
| Как попадает в сервис | Только внутри init_eva_session |
Долгие задачи могут не успеть
Ключ выдаётся один раз на сообщение, а не при каждой привязке сессии. Расшифровка длинной записи или распознавание большого документа могут идти дольше остатка срока. Тогда работа выполнится полностью, а на самой последней операции, записи результата, придёт ошибка истёкшего ключа. Ни один сервис не перечитывает доступы в середине задачи.
Изоляция чатов
Проверок пять, в двух местах.
На стороне сервиса:
- При привязке сессии префикс обязан совпадать с
sessions/<chatId>/. - При каждом обращении ключ проверяется на выход за префикс и на
...
На стороне платформы:
- Чат в ключе доступа должен совпадать с чатом в ключе объекта, иначе 403.
- При запросе списка префикс должен начинаться с префикса своего чата, иначе 403.
- Обращение к другой корзине даёт 404.
Дополнительно при выдаче ключа проверяется, что пользователь действительно имеет доступ к этому чату.
Ограничения
| Ограничение | Значение |
|---|---|
| Предел размера на стороне платформы | 1 ГиБ |
| Предел на входе Doc Worker | 50 МиБ |
| Предел на входе Transcript | 500 МиБ |
| Предел на входе OCR | Отсутствует |
| Таймаут обращения к хранилищу | 20 секунд у большинства сервисов |
Ответ на запись
Платформа приводит имя файла к безопасному виду и сообщает получившееся имя в заголовках ответа: ключ объекта и имя файла, оба закодированы по правилам URL.
Сервисы не читают этот ответ
Все сервисы отбрасывают ответ на запись и сообщают модели то имя, которое придумали сами. Для русских имён и имён, изменённых при приведении, получается расхождение: имя, которое модель называет пользователю, не совпадает с именем реально сохранённого файла.
Сервис может обойти это, приведя имя к латинице заранее, но делают так не все.
Полный путь одного вызова
модель вызывает чтение документа с именем файла
общая обёртка
получение доступа:
проверка привязанной сессии
сборка доступов из сохранённых данных
создание клиента хранилища
чтение:
разбор имени в полный ключ и проверка префикса
проверка срока действия ключа доступа
GET по адресу с ключом доступа
разбор кода ответа в понятную ошибку
работа над файлом
запись:
сборка ключа результата
PUT с типом содержимого, при 415 повтор
ответ модели: имя файла, ключ, размер, контрольная суммаМодель при этом передала только имя файла и формат.