Хакатон MAX 2026 · команда 397 · технический анализ

Архитектура «Накладной в кармане»

Что из задуманного можно сделать на стороне MAX, как подписывать титулы, как бот связывает участников одной перевозки и на каком стеке собирать MVP к заморозке 27 сентября. Каждый пункт сверен с документацией и OpenAPI-схемой MAX, постановлением № 931 и форматом ФНС; там, где можно, проверен руками.

Составлено 22 сентября 2026 Дополняет техплан «Накладная в кармане» и разбор «ЭПД с 1 сентября» Сверено с Bot API 0.0.33 (17.09.2026), SDK 0.3.1, ПП № 931, ЕД-7-26/1065@

Коротко

Что проверено руками сегодня. TLS обоих доменов API: platform-api2 отвечает, если доверять Russian Trusted Root CA. Цепочка сертификатов УНЭП «Госключа» проверена OpenSSL с пакетом libengine-gost-openssl. Официальные XSD титулов Т1–Т4 скачаны из документации Диадока и разобраны: у Т1 62 обязательных элемента, у Т2–Т4 по 6. Исходный код SDK @maxhub/max-bot-api 0.3.1 прочитан. Сайт nalog.gov.ru с нашего сервера отдаёт 403, поэтому XSD взяли у Диадока: это те же файлы ФНС.

Что умеет MAX для нашего сценария

Возможности, на которые опирается продукт, с методом, лимитами и выводом для нас. Источник по каждой строке — схема Bot API и страницы dev.max.ru из списка в конце.

ВозможностьСтатусКак в API и какие лимитыЧто это значит для нас
Написать участнику в личкуможноPOST /messages?user_id=…. Личный диалог адресуется по user_id, а не по chat_id (иначе 404 chat.not.found). До 2 сообщений в секунду в один диалог и 30 запросов в секунду всего.Поиска человека по телефону в API нет: user_id мы узнаём, только когда он сам открыл бота. Поэтому каждого участника приводим по ссылке.
Групповой чат перевозкинельзяМетода создания чата нет. POST /chats/{chatId}/members ограничен с 09.09 и удаляется 30.09.2026. GET /chats убран в июне 2026.«Звезда» вокруг бота, раздел «Как бот связывает участников».
Ссылка-приглашениеможноhttps://max.ru/<бот>?start=… — до 128 символов, приходит в bot_started.payload. ?startapp=… — до 512 символов из A–Z a–z 0–9 _ -, приходит в start_param мини-приложения.Одноразовые токены для водителя и получателя. В ссылке только случайный токен, никаких данных.
Подтверждённый номерможноКнопка request_contact → вложение contact с vcf_info и hash; сверка: HMAC-SHA256 от vcf_info на токене бота. В мини-приложении WebApp.requestContact() → {phone, authDate, hash}.Водитель делится номером, и бот сам находит его рейсы в данных учётной системы.
Геоточка погрузкис оговоркойКнопка request_geo_location с quick: true → вложение location (широта, долгота). До 3 таких кнопок в ряду. В MAX Bridge геолокации нет.В мобильном MAX работает; в web.max.ru и на десктопе — подтверждение адреса без точки.
«Живая карточка»можноPUT /messages?message_id=… меняет текст и кнопки. POST /answers?callback_id=… по нажатию меняет сообщение и/или показывает всплывающее уведомление.У каждого участника одно сообщение на перевозку, бот обновляет его при каждом событии.
Отправить XML, PDF, QRможноPOST /uploads?type=file|image → токен → POST /messages. Файл должен быть единственным вложением сообщения. Сразу после загрузки бывает attachment.not.ready, нужен повтор с паузой. Файл до 4 ГБ, картинка до 50 МБ.Документ и кнопки уходят двумя разными сообщениями.
Принять файл, в том числе пересланныйможноmessage_created. У пересланного message.link.type = "forward", есть link.sender и link.message.attachments[].payload.url.Принимаем .sig от @goskey_bot и видим, из какого бота переслано. На живом боте проверить в первый день.
Мини-приложение без логинаможноwindow.WebApp.initData; подпись HMAC-SHA256, ключ равен HMAC-SHA256("WebAppData", токен бота). Документация советует считать auth_date годным час.На сервере проверяем initData и выдаём сессию; роли берём из своей базы.
Кнопка «Открыть карточку»можноКнопка open_app: web_app — имя бота, payload до 512 символов.Любое уведомление открывает нужную перевозку в мини-приложении.
Сканер QRс оговоркойWebApp.openCodeReader(fileSelect): камера или картинка из галереи.На десктопе — ручной ввод кода. QR приглашения сканируется и обычной камерой телефона.
Поделиться приглашениемс оговоркойWebApp.shareMaxContent({text, link}) или ссылка https://max.ru/:share?text=… (на десктопе пока нет). shareContent работает только на iOS и Android.Диспетчер пересылает приглашение получателю прямо из MAX.
Яркость экрана под QRможноWebApp.requestScreenMaxBrightness() — максимальная яркость на 30 секунд.Экран с кодом для получателя или инспектора.
Вебхукс оговоркойPOST /subscriptions {url, secret, update_types}: только порт 443 и сертификат доверенного УЦ, ответ 200 за 30 секунд, до 10 повторов (60 с, дальше ×2,5). После 8 часов ошибок подписка снимается сама. Подписки копятся, старые надо удалять. Long polling одновременно с вебхуком не работает.Отвечаем 200 сразу после записи в inbox. Воркер раз в 10 минут проверяет подписку и восстанавливает её.
Адрес APIс оговоркойС 19.07.2026 — platform-api2.max.ru, сертификат от Russian Trusted Root CA (Минцифры). Старый platform-api.max.ru пока отвечает.Корневой сертификат кладём в образ и включаем через NODE_EXTRA_CA_CERTS.
Уведомления по инициативе ботас оговоркойПравила платформы (ред. 11.06.2026) называют их «сервисными сообщениями», а п. 1.5 Требований разрешает слать их через API только по договору с MAX.На хакатоне бот организаторов, это не мешает. Для пилота уточнить у MAX, покрывает ли их лицензионный договор.
Цифровой IDнельзяТолько для верифицированных юрлиц и ИП; сценарии возраста и льготных статусов.Не используем.
Подпись через «Госключ»с оговоркойРуками через @goskey_bot: PDF, TXT, XML, PNG, JPEG, TIFF; до 20 файлов и 100 МБ за раз. Нужны подтверждённая учётная запись Госуслуг и приложение «Госключ». Программного запроса подписи в Bot API нет.Следующий раздел.

Подпись: кто, чем и как это собрать в MAX

Юридическая рамка — пункт 3 Правил направления ЭТрН в ГИС ЭПД, утверждённых постановлением Правительства № 931 от 21.05.2022. Там прямо сказано, чьей подписью закрывается каждый файл. Для нас важны две вещи: УНЭП «Госключа» допустима на всех четырёх обязательных титулах, а подпись водителя дополнительная и не заменяет подпись перевозчика.

Титул и файл ФНСКто обязан подписатьЧемЧто можно добавитьКак в MVP
Т1
ON_TRNACLGROT
Грузоотправитель: уполномоченное лицо или тот, кто грузитУКЭП или УНЭП; сотруднику нужна МЧД—Диспетчер: УНЭП через @goskey_bot живое или демо-подпись. Номер МЧД — поле в XML, реестр ФНС не проверяем модель
Т2
ON_TRNACLPPRIN
ПеревозчикУКЭП или УНЭПВодитель после сверки груза: ПЭП или УНЭП, «при условии последующего подписания» перевозчикомВодитель: ПЭП в MAX, по желанию УНЭП «Госключа». Перевозчик: демо-подпись в эмуляторе модель
Т3
ON_TRNACLGRPO
Грузополучатель: уполномоченное лицоУКЭП или УНЭП; сотруднику нужна МЧДПриёмщик может дополнительно подтвердить приёмку своей УНЭППолучатель: УНЭП через @goskey_bot живое или демо-подпись
Т4
ON_TRNACLPVYN
ПеревозчикУКЭП или УНЭПВодитель: ПЭП или УНЭПКак Т2

Титулы сцеплены подписями. В XSD у Т2 обязательный атрибут ИдИнфГО/@ЭП — это подпись Т1; у Т3 ИдИнфПрвПрием/@ЭП — подпись Т2; у Т4 ИдИнфГП/@ЭП — подпись Т3. Поэтому эмулятор оператора хранит сами подписи, а не флаг «подписан», и следующий титул собирается из подписи предыдущего. Формат — приказ ФНС от 09.12.2021 № ЕД-7-26/1065@, версия 5.01, кодировка windows-1251; имя файла Т1, например, ON_TRNACLGROT_A_E_O_W_ГГГГММДД_GUID.xml.

Мост с «Госключом»

Наш бот и сервер XML по XSD, проверка подписи Участник в MAX водитель, получатель, диспетчер @goskey_bot официальный бот «Госключа» Приложение «Госключ» ключ живёт в телефоне 1. XML титула + PDF для чтения 2. пересылает XML PDF, TXT, XML, PNG, JPEG, TIFF до 20 файлов и 100 МБ за раз 3. «Какой подписью?» УНЭП / УКЭП 4. открывает «Госключ», пароль, «Подписать» 5. подпись готова первый раз: связать MAX с Госуслугами и выпустить сертификат 6. документ + .sig (PKCS#7) 7. пересылает ответ боту 8. Проверка на сервере openssl cms -verify -engine gost хеш по Стрибогу, подпись ГОСТ, цепочка УЦ «Госключа» ФИО из сертификата = «Подписант» в XML
Бот не может положить документ в «Госключ» сам: файл туда и подпись обратно пересылает человек. Зато всё, что вернулось, наш сервер проверяет криптографически, поэтому пересылке не нужно доверять на слово.
  1. Бот собирает XML титула в формате ФНС 5.01, проверяет его по XSD и отправляет участнику двумя сообщениями: XML на подпись и PDF для чтения. Третье сообщение — кнопка-ссылка https://max.ru/goskey_bot и инструкция в три шага.
  2. Участник пересылает XML в @goskey_bot, выбирает УНЭП или УКЭП и подтверждает паролем в приложении «Госключ». В первый раз придётся связать профиль MAX с Госуслугами и выпустить сертификат.
  3. @goskey_bot возвращает документ и откреплённую подпись .sig в формате PKCS#7 (так описывают сторонние инструкции, проверить руками), участник пересылает их нашему боту. Если пересылка не сработает, те же файлы можно загрузить через скрепку.
  4. Бот находит ожидающий титул по GUID в имени файла, скачивает .sig по payload.url и проверяет его командой openssl cms -verify -engine gost -binary -inform DER -in Т.xml.sig -content Т.xml -CAfile goskey-roots.pem -purpose any. Из сертификата достаёт ФИО и СНИЛС и сверяет ФИО с полем «Подписант» в XML.
  5. Подпись сохраняется целиком (CMS), титул получает отметку «подписан УНЭП „Госключа“, подпись проверена», а её base64 уходит в атрибут ЭП следующего титула.

Если «Госключа» нет

Кнопка «Подписать позже» оставляет титул в состоянии «ждёт подписи» с напоминаниями. Для проверяющих есть «Демо-подпись»: эмулятор подписывает ГОСТ-ключом демонстрационного УЦ, и интерфейс прямо пишет, что это модель.

web.max.ru и десктоп

«Госключ» — мобильное приложение. На компьютере бот предлагает подписать с телефона, а карточка в мини-приложении обновится сама, когда .sig придёт.

ПЭП водителя

Это не криптография, а запись доказательств: user_id, подтверждённый телефон, время, геоточка, хеш титула, callback_id и mid сообщения. Силу ПЭП даёт соглашение участников с оператором: в MVP это модель, в пилоте договор.

Проверить руками в первые сутки (подойдёт HAKATON-9, как только у кого-то заработает «Госключ»):

1) переслать тестовый XML в @goskey_bot и посмотреть, что вернулось: есть ли .sig, как назван файл; прогнать команду openssl выше. 2) Переслать ответ нашему боту и посмотреть сырое обновление: есть ли link.type = "forward", link.sender и вложения. 3) Загрузить анимированный GIF через /uploads?type=image и проверить, анимируется ли он в чате: это нужно для настоящего QR на пилоте.

Как бот связывает участников одной перевозки

Хотелось бы: общий чат перевозки групповой чат «Рейс 0417» Диспетчер Водитель Получатель Перевозчик Бот · админ все видят всё, бот пишет в общий поток ✕ API не создаёт групповые чаты ✕ добавлять участников бот может до 30.09.2026, потом метод удаляют ✕ список чатов бота (GET /chats) убран в июне 2026 Делаем: бот в центре, у каждого своя личка Бот + ядро одна перевозка Диспетчеротправителя Водительссылка или QR на воротах Получательссылка или QR водителя Перевозчикмодель в MVP живая карточка и «ваш ход» реплики и фото пересылает бот общая карточка и лента событий — в мини-приложении @goskey_bot — отдельная личка, файлы туда и обратно пересылает человек
Групповой чат был бы естественным «местом перевозки», но в MAX его нельзя собрать ботом. Поэтому общее место — это данные перевозки у нас на сервере: бот показывает их каждому участнику в его личке и в мини-приложении.

Кто, что и когда получает

ШагКто действуетЧто видит и нажимаетЧто получают остальныеПод капотом
1Учётная система—Диспетчер: карточка «Черновик Т1» с проверками реквизитов и кнопками «Подписать Т1», «Исправить», «Нужна ли накладная?»вебхук erp-emu → app; проверки ИНН, массы, адресов, срока МЧД
2ДиспетчерПодписывает Т1 («Госключ» или демо), жмёт «Пригласить водителя» и получает ссылку и QR—XML Т1 по XSD → эмулятор оператора
3ВодительОткрывает ссылку, жмёт «Поделиться номером»Диспетчер: «Водитель Петров в боте»bot_started.payload; contact.hash сверяется HMAC
4Водитель«Я на погрузке» → сверка груза → «Принял без замечаний» или «Есть замечания» + геоточкаДиспетчер: «Груз принят водителем»ПЭП водителя; request_geo_location с quick
5Перевозчик (модель)—Водитель: QR и «Можно ехать»; диспетчер: «УИД получен, машину можно выпускать»эмулятор подписывает Т2 демо-УЦ, выдаёт УИД и демо-QR
6ВсеРеплики через ботаЛента перевозкитаймеры: нет QR 20 минут, простой
7Водитель«На выгрузке» → «Показать код получателю»Получатель открывает мини-приложение по QR или ссылке?startapp=rcv_…, requestScreenMaxBrightness()
8Получатель«Принято без расхождений» или «Есть расхождения» с фото → кто подписывает (ФИО, должность, МЧД) → подпись Т3Диспетчер и водитель: «Т3 подписан»XML Т3 → @goskey_bot → .sig → проверка ГОСТ
9Водитель, перевозчик (модель)«Груз сдан» (ПЭП)Все: «Перевозка закрыта»; учётная система получает статусТ4 → эмулятор; writeBack в учётку

Напоминания и эскалации

УсловиеКомуКнопка
Т1 не подписан за 2 часа до погрузкиДиспетчер отправителя«Подписать сейчас»
Нет QR через 20 минут после приёма грузаДиспетчер и водитель«Проверить статус у оператора»
Т3 не подписан 24 часа после выгрузкиПолучатель, затем диспетчер«Напомнить получателю»
Т4 не подписан 24 часа после Т3Перевозчик (модель), затем диспетчер«Открыть перевозку»
МЧД диспетчера истекает через 5 днейДиспетчер«Обновить данные МЧД»

Архитектура

У УЧАСТНИКА В MAX @goskey_bot → «Госключ» подпись УНЭП или УКЭП физлица Личные диалоги с ботом у каждого участника свой Мини-приложение React + MAX UI, MAX Bridge пересылка XML и .sig MAX Bot API platform-api2.max.ru · сертификат Минцифры сообщения, кнопки, файлы НАШ СЕРВЕР · NGINX, TLS, ПОРТ 443 worker · pg-boss outbox → MAX: до 30 запросов/с до 2 сообщений/с в один диалог таймеры и эскалации обмен с оператором, повторы самопроверка подписки вебхука PostgreSQL 18 перевозки, участники, титулы подписи и доказательства журнал событий → лента inbox обновлений MAX очереди и таймеры pg-boss app · Fastify /bot/webhook: секрет → inbox → 200 /api: initData → сессия, роли ядро: состояния, «чей ход» XML по XSD ФНС, PDF, QR openssl + gost-engine: проверка .sig вебхук + секрет POST /messages, /answers, /uploads HTTPS + initData ВНЕШНИЕ СИСТЕМЫ — ЗА АДАПТЕРАМИ ЯДРА Оператор ЭПД → ГИС ЭПД epd-emu модель в MVP Диадок, Такском на пилоте Учётная система erp-emu модель в MVP МойСклад по желанию титулы XML + подписи статусы, УИД, QR вебхук «отгрузка создана» статус обратно в учётку живое, работает на хакатоне модель: эмулятор или демо-подпись после хакатона
Наш сервер никогда не пишет пользователю напрямую: исходящие идут в Bot API, входящие приходят вебхуком. Мини-приложение — единственный прямой канал от пользователя к нам. Эмуляторы крутятся в том же docker compose, но ядро видит их только через адаптеры, как настоящие внешние системы.

Почему app и worker, а не bot и api

В техплане бот и API — два сервиса. Им нужны одно и то же ядро и одна база, поэтому лучше один образ и два процесса с разными ролями. app принимает всё входящее: вебхук MAX, запросы мини-приложения, вебхуки учётной системы и эмулятора оператора. worker делает всё исходящее и отложенное: сообщения в MAX с ограничением скорости, таймеры, обмен с оператором. Тогда медленный ответ MAX или оператора никогда не задерживает ответ на вебхук, а схема на слайде остаётся простой.

Ядро и переменная часть

Ядро не знает ни про MAX, ни про МойСклад, ни про конкретного оператора. Всё, что меняется при переносе на другой завод, регион или оператора, сидит за четырьмя интерфейсами:

interface ErpAdapter {          // emulator | moysklad | 1c-odata
  getShipment(ref: string): Promise<ErpShipment>
  writeBack(ref: string, status: WaybillStatus): Promise<void>
}
interface EpdOperator {         // emulator | diadoc | taxcom
  submit(title: TitleFile, signatures: Cms[]): Promise<{ operatorDocId: string }>
  status(operatorDocId: string): Promise<OperatorStatus>  // sent | registered(УИД) | error(коды ГИС)
  qr(operatorDocId: string): Promise<Gif>
}
interface SignatureProvider {   // pep_max | goskey_relay | demo_ca | (пилот) operator_cloud
  request(title: TitleFile, signer: Participant): Promise<SignRequest>
  accept(evidence: Evidence): Promise<Signature>          // проверка и данные подписанта
}
interface Messenger {           // MAX; в тестах заглушка
  upsertCard(userId: number, card: Card): Promise<void>    // PUT /messages или новая карточка
  notify(userId: number, text: string, buttons: Button[]): Promise<void>
}

Надёжность: ничего не теряем и не дублируем

app.post('/bot/webhook', async (req, reply) => {
  if (!safeEqual(req.headers['x-max-bot-api-secret'], env.MAX_WEBHOOK_SECRET)) return reply.code(401).send()
  const update = req.body as Update
  const key = dedupeKey(update)            // callback_id | message.body.mid | bot_started:user:ts
  const fresh = await db.insert(inbox).values({ key, update }).onConflictDoNothing().returning()
  reply.code(200).send('ok')               // не позже 30 с, иначе MAX повторит
  if (fresh.length) setImmediate(() => processInbox(key))
})

Состояния перевозки

исправленный Т1 — до Т2 отказ в Т2 → новая накладная расхождения → оговорки в Т3 Черновик Т1проверки Т1 подписанотправитель Груз принятПЭП водителя Т2 подписанперевозчик В путиУИД и QR есть Т3 подписанполучатель Т4 подписанперевозчик Закрытастатус в учётке таймер 2 ч до погрузки Т1 не подписан таймер 20 мин нет QR после приёма таймер 24 ч Т3 не подписан таймер 24 ч Т4 не подписан Переадресация и замена водителя или машины в MVP показываются только сценариями эмулятора.
Зелёное состояние — точка, после которой машине можно ехать. Над цепочкой боковые ветки, под ней таймеры эскалаций.

Модель данных

ТаблицаЧто хранит
orgИНН, КПП, название; роли (отправитель, перевозчик, получатель); вид учётки и оператора; признак демо
personmax_user_id (уникальный), подтверждённый телефон, имя из MAX, время согласия на обработку данных
membershipчеловек × организация, роль (диспетчер, водитель, приёмщик, админ), МЧД: номер и срок
shipmentссылка на отгрузку в учётке, стороны, адреса, груз, машина, водитель, состояние, версия, УИД, идентификатор у оператора
participantперевозка × роль × человек (пусто, пока не вошёл), хеш токена приглашения, как пришёл
titleТ1…Т8, ИдФайл, XML в windows-1251, хеши SHA-256 и по Стрибогу, ссылка на предыдущий титул
signatureтитул, роль подписанта, вид (ПЭП в MAX, УНЭП «Госключа», демо-УЦ), CMS целиком, ФИО и СНИЛС из сертификата, результат проверки, доказательства
cardперевозка × человек → mid живой карточки и хеш последней отрисовки
eventжурнал: тип, кто (человек, учётка, оператор, таймер), данные. Из него лента в мини-приложении и метрики пилота
inboxобновления MAX с ключом дедупликации и статусом обработки
pgboss.*очереди outbox и таймеров; схему создаёт сам pg-boss

Безопасность

Разработка при одном боте

Боевой бот один, его выдали организаторы, а long polling с вебхуком одновременно не работает. Поэтому живой бот смотрит только на сервер, а разработчики работают локально с заглушкой MAX: записанные настоящие обновления в JSON проигрываются в /bot/webhook, исходящие вызовы пишутся в лог. Мини-приложение локально открывается в браузере с dev-входом: initData подписывается тестовым токеном.

Переключатель MAX_MODE=webhook | polling | off. У проверяющего по умолчанию off: всё поднимается, мини-приложение открывается в браузере. polling — если у него есть свой токен.

Развёртывание

Сервер слабый и почти полный: 2 ядра, 7,8 ГБ памяти, свободно 7,6 ГБ диска (занято 93%). Образы на нём не собираем: их собирает CI (например, GitHub Actions с публикацией в GHCR) или Mac mini, а сервер делает docker compose pull && docker compose up -d. Логи docker ограничиваем (max-size: 10m), pg_dump раз в сутки, держим всё до 14.10.

FROM node:24-bookworm-slim AS base
RUN apt-get update \
 && apt-get install -y --no-install-recommends openssl libengine-gost-openssl libxml2-utils ca-certificates \
 && rm -rf /var/lib/apt/lists/*
# корневой сертификат Минцифры: без него не открыть platform-api2.max.ru
COPY certs/russian_trusted_root_ca.pem /usr/local/share/ca-certificates/russian_trusted_root_ca.crt
RUN update-ca-certificates
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt

Стек

СлойБерёмПочемуНе берём
СредаNode.js 24 LTS (24.21.0 от 07.09.2026)Поддержка до 30.04.2028. SDK MAX требует Node ≥ 20.19, pg-boss 12 — ≥ 22.12Node 20 снят с поддержки 30.04.2026; Node 26 станет LTS только 28.10
ЯзыкTypeScript 6.0; tsx 4 для разработки, esbuild 0.28 собирает бэкенд в один файл для образаСборка бэкенда занимает секунды, сборка образа укладывается в 5 минутTypeScript 7 (нативный) — можно для tsc --noEmit, но инструменты ещё догоняют
HTTPFastify 5.12, zod 4.6, pino 10Быстрый, схемы на zod, JSON-логиNestJS — тяжёлый для пяти дней
MAX@maxhub/max-bot-api 0.3.1 (официальный, MIT)Типы, клавиатуры, загрузки; по умолчанию ходит на platform-api2Его встроенный вебхук: он отвечает 200 до обработки — делаем свой маршрут с inbox
БазаPostgreSQL 18.6, Drizzle ORM 0.45, drizzle-kit 0.31uuidv7() из коробки, SQL-миграции лежат в репозиторииPrisma — лишний шаг генерации клиента при сборке образа
Очереди и таймерыpg-boss 12.33Повторы, отложенные задания, singletonKey — на той же базеRedis и BullMQ — лишний сервис
Мини-приложениеReact 19.2.8 строго, @maxhub/max-ui 0.5.0, Vite 8.3, TanStack Query 5; MAX Bridge скриптом st.max.ru/js/max-web-app.jsMAX UI 0.5.0 требует ровно react 19.2.8 в peerDependencies, с 19.3 npm install упадёт. TanStack Query обновляет карточку опросом раз в 3–5 секундNext.js — не нужен, это одностраничное приложение
Документыfast-xml-parser 5 и iconv-lite 0.7 (windows-1251); xmllint и официальные XSD; pdfmake 0.3 со шрифтом с кириллицей под OFL; qrcode 1.5, gifenc 1.0 для анимированного демо-QRФормат ФНС и проверка по официальной схеме, а не «похожий XML»—
КриптографияOpenSSL 3 + libengine-gost-openssl из Debian, вызов через child_processПроверка CMS «Госключа» и демо-подпись по ГОСТКриптоПро — проприетарный, закрытые библиотеки использовать нельзя
Тестыvitest 5Машина состояний, проверки ИНН и XSD, HMAC initData и контакта, проверка CMS на тестовых файлах — без MAX—
Сборка и запускnpm workspaces, один package-lock.json; Docker ComposeОдна команда запуска, зависимости с точными версиями—
apps/app          вебхук MAX, API мини-приложения, ядро, адаптеры   (процессы app и worker)
apps/web          мини-приложение: React + MAX UI
apps/erp-emu      модель учётной системы: отгрузки, вебхук, /demo
apps/epd-emu      модель оператора и ГИС: титулы, УИД, QR, ручки сбоев
packages/domain   типы, машина состояний, правила «чей ход» — без ввода-вывода
packages/etrn     сборка XML Т1–Т4, XSD ФНС, PDF, обёртки openssl

Что поправить в техплане и задачах

  1. Node 20 → Node 24 LTS. Node 20 снят с поддержки 30.04.2026; это увидят в критерии про зависимости.
  2. platform-api.max.ru → platform-api2.max.ru и сертификат Минцифры в образе. Хост не «не отвечает», просто без Russian Trusted Root CA не проходит проверка TLS. Поправить комментарий в HAKATON-6, README и CLAUDE.md.
  3. Ссылка ?start= несёт до 128 символов, а не 512. 512 — это ?startapp= для мини-приложения.
  4. Групповых чатов нет. Схема «звезда» с живыми карточками и пересылкой реплик вместо общего чата.
  5. Подпись «Госключа» проверяем уже в MVP (gost-engine и корневые сертификаты с goskey.ru) — убрать из известных ограничений.
  6. В «Госключе» подписываем XML-титул, а не PDF: @goskey_bot принимает XML, а юридически значим именно файл обмена. PDF остаётся для чтения.
  7. Проверка по XSD ФНС — в MVP, а не на пилоте. Схемы открыто лежат в документации Диадока. Т2–Т4 простые, Т1 — 62 обязательных элемента, на него отвести полдня.
  8. Титулы сцеплены подписями (атрибут ЭП): в модель данных добавить таблицу signature с CMS целиком.
  9. Лимиты МойСклада: 100 запросов за 5 секунд и до 5 параллельных от одного пользователя, а не 45 за 3 секунды.
  10. postgres:18: том в /var/lib/postgresql.
  11. React строго 19.2.8 — так требует MAX UI 0.5.0.
  12. bot + api → app + worker из одного образа.
  13. Файл и кнопки нельзя в одно сообщение — карточка с документом всегда из двух сообщений.
  14. QR: настоящий код ЭТрН — анимированный GIF, скриншот не годится, но отправлять его водителю в мессенджере допустимо (Астрал). На пилоте бот может отдавать QR от оператора прямо в MAX; в MVP — демо-QR с пометкой.

Границы MVP

Must — без этого не сдаём

  • Сценарий Т1 → Т4 тремя ролями до закрытия
  • erp-emu и команда /demo для новой отгрузки
  • epd-emu: УИД, демо-QR, ручки сбоев
  • Живые карточки, ссылки-приглашения, привязка по номеру
  • Мини-приложение: список, карточка, лента, «чей ход»
  • XML Т1–Т4 по XSD, демо-подпись
  • Inbox, outbox, самопроверка вебхука, 2–3 таймера

Should — делаем, если Must готов к 25.09

  • Мост с «Госключом» и проверка по ГОСТ
  • QR на экране водителя для получателя
  • Реплики через бота в ленту перевозки
  • Фото расхождений в Т3
  • Дерево «Нужна ли накладная?» с источниками

Could

  • Адаптер МойСклада на живом аккаунте
  • Сканер QR в мини-приложении
  • Переадресация и замена водителя в эмуляторе

Won't — прямо пишем в README

  • Настоящий оператор ЭПД и ГИС ЭПД
  • УКЭП организации, проверка МЧД в реестре ФНС
  • Подпись по запросу компании через «Госключ»
  • Групповые чаты перевозки
  • Электронный путевой лист

Кто что делает до заморозки

Предложение, как разложить модули по людям; календарь и синки — в HAKATON-15.

Риски и открытые вопросы

Источники

  1. MAX Bot API, OpenAPI-схема 0.0.33 и README — кнопки, вложения, обновления, deep link до 128 символов
  2. dev.max.ru: обзор API — platform-api2 и сертификат Минцифры
  3. История изменений API — GET /chats (июнь), POST /chats/{chatId}/members (09.09 и 30.09.2026)
  4. POST /subscriptions — порт 443, 30 секунд, повторы, отписка через 8 часов
  5. POST /messages и POST /uploads — 2 сообщения в секунду, 30 запросов в секунду, attachment.not.ready
  6. Клавиатура в сообщениях — типы кнопок, лимиты, проверка hash контакта
  7. Подключение мини-приложения — диплинки start и startapp, :share
  8. MAX Bridge — requestContact, openCodeReader, shareMaxContent, яркость, ограничения web
  9. Валидация данных мини-приложения — initData
  10. Правила размещения ботов (ред. 11.06.2026) и Требования (ред. 16.12.2025) — сервисные сообщения
  11. SDK @maxhub/max-bot-api 0.3.1 — вебхук, platform-api2 по умолчанию
  12. MAX UI 0.5.0 — peerDependencies react 19.2.8
  13. Хабр: «Бот в MAX молчит: четыре грабли Bot API» — user_id и chat_id, копящиеся подписки
  14. @goskey_bot — официальный бот «Госключа» в MAX
  15. Помощь MAX: как подписать документы
  16. fdoc.ru, 14.07.2026 — форматы и лимиты @goskey_bot
  17. ecp-shop.ru — документ и .sig в ответ, PKCS#7
  18. goskey.ru: корневые сертификаты УНЭП
  19. iXBT, 25.08.2025 и Forbes — подпись по запросу организаций через MAX
  20. vc.ru — самостоятельная подпись в «Госключе» только через MAX с 18.12.2025
  21. ПП РФ № 931 от 21.05.2022 — п. 3 Правил направления ЭТрН в ГИС ЭПД
  22. Приказ ФНС № ЕД-7-26/1065@ — формат ЭТрН 5.01, имена файлов
  23. Диадок API: форматы и XSD и документооборот ЭТрН
  24. Астрал: кто подписывает какой титул (обн. 10.06.2026)
  25. Астрал: КЭП, УНЭП и МЧД для ЭПД (обн. 14.07.2026)
  26. Астрал: QR-код ЭТрН и проверка ГИБДД (обн. 20.04.2026)
  27. Node.js Release — сроки LTS; endoflife.date — PostgreSQL 18.6
  28. docker-library/postgres — PGDATA и VOLUME начиная с 18
  29. Debian: libengine-gost-openssl — ГОСТ-модуль для OpenSSL 3
  30. МойСклад JSON API 1.2 — ограничения на запросы