Cluster · разработчикам
Устройство Cluster
Cluster состоит из ядра, HTTP-службы и сред модулей. Ядро хранит состояние и проверяет действия. HTTP-служба принимает запросы внешнего интерфейса и передаёт их ядру через Unix-сокет. Код модуля выполняется отдельно от обоих процессов.
Компоненты
| Компонент | Назначение |
|---|---|
| Ядро | Профили, права, Telegram-клиенты, базы, модули и задачи |
| HTTP-служба | FastAPI, запросы клиентов и входящие обновления ботов |
| Среда модуля | Исполнение пакета через Podman и gVisor/runsc |
| Среда терминала | Bash, рабочие файлы и операции владельца |
| Mekansm | Центральная база и отдельная база каждого юзербота |
| Caddy | HTTPS и передача запросов HTTP-службе |
| systemd | Запуск и перезапуск служб |
Ядро работает на одном узле и единолично открывает базы. HTTP-служба использует отдельную системную учётку. Ей доступен api.sock, но недоступны базы, ключ и операторский сокет. Модули используют собственные подключения к module.sock, оператор использует control.sock.
Структура
Службы ядра находятся в src/cluster/manager:
| Папка | Назначение |
|---|---|
accounts | Профили, вход, почта, подписки и администрирование |
dispatch | Команды, события, интерфейс модулей, очередь и расписание |
modules | Публикации, версии, зависимости, вызовы и обновления |
network | Внешние запросы, входящие запросы и обмен юзерботов |
runtime | Сборка, запуск, остановка, ресурсы и терминал |
storage | Базы юзерботов, файлы, тома и резервные копии |
telegram | Telegram-клиенты, боты, формы, кнопки и события |
В корне manager находятся общие службы: permissions.py проверяет права, secrets.py работает с секретами, journal.py ведёт журнал, maintenance.py запускает обслуживание.
app.py создаёт службы и связывает их между собой. Функции operations, controls и external в manager/dispatch/routes.py возвращают явные перечни операций. Для регистрации новой операции нужно изменить этот перечень.
shared содержит протокол, контекст вызова, проверки данных и клиенты внешних служб. user содержит клиентский App, загрузчик и обработчики модуля. Эта часть работает внутри его среды и обращается к API ядра. Серверные объекты Python в модуль не передаются.
Сайт на React и TypeScript находится в web/ и собирается отдельно от Python-пакета. Его подключение к HTTP-службе описано в API.md, сборка в WEB.md. Ядро хранит описания полей и страниц модулей. Сайт получает их через API и строит формы.
Профиль
Профиль имеет постоянный внутренний ID и одного собственного юзербота. Telegram-аккаунт можно заменить, сохранив модули, настройки, данные и договоры обмена.
Дополнительный овнер использует собственный зарегистрированный профиль и входит через Mini App. Сервер проверяет Telegram initData и привязку Telegram ID. Первоначального овнера удалить нельзя. Отзыв овнерки закрывает его терминалы и прекращает доступ к выбранному юзерботу.
Доверке выдаются отдельные действия, общие или для конкретного модуля. Локальная настройка заменяет общую. Отсутствие локальной настройки наследует общее правило, пустой список запрещает действия. Терминал доверке недоступен.
Права
У вызова две проверки: права человека на использование функции и права модуля на само действие. Для модуля учитываются заявленные разрешения, согласие пользователя, общие ограничения и настройки экземпляра. Пустой набор ничего не разрешает.
Права ограничивают чаты, пользователей, сайты и другие модули. Локальные настройки могут сузить принятое разрешение. Вызов другого модуля сохраняет ограничения всей цепочки и происхождение команды.
telegram.full и bot.full дают низкоуровневые методы через ядро. Настоящая сессия и токен по-прежнему недоступны. При ограничении по чатам или пользователям непроверяемый запрос отклоняется. Неизвестные операции и действия авторизации также закрыты.
Изменение прав применяется к работающим средам и соединениям. Уже принятое внешней службой действие отменить таким изменением нельзя. Полный перечень правил приведён в PERMISSIONS.md.
Изоляция
У экземпляра отдельная gVisor-среда с закрытой сетью и корнем только для чтения. Рабочие каталоги подключаются отдельно. Служебные базы, сессии, ключи, сокет Podman и каталоги других пользователей туда не подключаются.
Пакеты могут содержать Python-зависимости, библиотеки и исполняемые файлы. Сборка выполняется в отдельной среде без прямой сети. Python-зависимости получаются из PyPI через ограниченный канал ядра или устанавливаются из приложенных wheels. Ядро скачивает только данные, установка и сборка стороннего кода выполняются внутри контейнера. Совместимость конкретной программы зависит от образа и gVisor.
Модули, сборки и терминал делят общие пределы процессора, памяти и числа процессов юзербота. Экземпляру можно задать меньший предел. Хранилище ограничивается логической квотой и, при --volumes, размером ext4-тома.
Внешние соединения выполняются сетевой службой ядра. Проверяются имя сайта, DNS, адрес подключения и TLS. Частные и служебные адреса закрыты даже при общем разрешении внешней сети.
Модуль обменивается с ядром ограниченными сообщениями данных. Протокол не принимает pickle, живые объекты Python или выражения для исполнения в ядре.
Публикация и запуск
При первой публикации создаётся постоянный ID модуля. Версия закрепляет описание, права, зависимости и пакет по SHA256. Исправление публикуется новой версией. Исходники GitHub закрепляются за коммитом.
Публикация проверяет автора и файл, резервирует место, распаковывает архив и повторно проверяет автора перед сохранением. При ошибке удаляются только пакет и резерв этого запроса. Уже сохранённый общий пакет другого запроса остаётся на месте.
Сборка проверяет зависимости, создаёт рабочий каталог и резервирует место. Результат принимается через ограниченный протокол и получает контрольную сумму. После успеха резерв заменяется фактическим размером, после ошибки или отмены освобождается.
Перед запуском ядро повторно проверяет версию, привязку экземпляра и то, что пользователь не отключил его за время подготовки. Завершение прежнего процесса не меняет состояние нового запуска. При остановке завершаются среда и её дочерние процессы, старое подключение отзывается.
Включённый исправный экземпляр запускается после перезапуска ядра. Необработанная ошибка отключает его до ручного включения. Архивированная версия не предлагается для установки, уже установленная может продолжать работу.
Данные
Центральная Mekansm хранит профили, подписки, Telegram-подключения, публикации и управление. База юзербота хранит прикладные данные, журнал, расписание и учёт места. Пакеты, сборки и файлы находятся вне базы.
У каждого экземпляра свой раздел данных и сетевой профиль. Несколько экземпляров одной публикации различаются именем экземпляра, их права и настройки независимы.
Договор обмена связывает юзерботов, установки и конкретный ресурс. Согласие адресата нужно для каждого направления. Договор не позволяет запускать чужие команды. Подробнее в EXCHANGE.md.
Терминал и поддержка
Терминал доступен овнерам через сайт и Telegram-команду !exec. Он выполняет Bash в своей среде, хранит рабочие файлы и предоставляет API владельца через terminal_client. База и Telegram-сессия остаются в ядре. Модуль не может вызвать терминал от имени владельца.
Доступ поддержки выдаётся на сайте для конкретного юзербота и действует до отзыва или завершения ремонта. Среда ремонта отделена от обычного терминала. Отдельный журнал действий поддержки не ведётся, записи самих модулей продолжают работать.
Для модерации есть отдельная проверка прикладных данных: только чтение, ограниченный срок, подтверждение администратора и журнал запросов. Она не создаёт разрешение поддержки и не даёт Telegram-чатов, терминала или содержимого файлов. Действия маркета разделены между отдельной версией и публикацией целиком. Порядок работы.