Cluster · разработчикам

Ядро Cluster

Ядро содержит состояние, права, данные модулей и изолированный запуск. Регистрация и подключения описаны в ACCOUNTS.md и TELEGRAM.md. Сайт и Mini App используют HTTP-интерфейс.

Функционал

Полный доступ модуля не открывает служебную базу, сессию или терминал. Для этих запросов нет модульного маршрута. Telegram и личный бот доступны через проверяемые методы ядра. Для низкоуровневых запросов есть app.client.invoke; открытые группы перечислены в TELEGRAM.md.

Структура

ПутьНазначение
src/cluster/app.pyСостав приложения, операции управления и модульные запросы
src/cluster/config.pyПараметры и пределы
src/cluster/sharedКонтекст, JSON, Unix-соединения и владелец базы
src/cluster/manager/accountsПрофили, вход, почта, подписка и администрация
src/cluster/manager/dispatchДиспетчер, пользовательские запросы, очередь, расписание и явный реестр операций
src/cluster/manager/modulesВебшоп, версии, зависимости и обновления
src/cluster/manager/networkВебклиент, входящие запросы и обмен
src/cluster/manager/runtimeСборка, среды, ресурсы, терминал и экземпляры
src/cluster/manager/storageДанные, файлы, тома и копии
src/cluster/manager/telegramTelegram, боты, кнопки и события
src/cluster/manager/permissions.pyПроверки прав разных групп
src/cluster/manager/secrets.pyСлужебные секреты
src/cluster/manager/journal.pyЖурнал и доставка уведомлений
src/cluster/manager/notices.pyПереводы и оформление уведомлений бота
src/cluster/manager/modules/defaults.pyУстановка стандартных модулей
src/cluster/manager/maintenance.pyПериодическое обслуживание
src/cluster/userКлиент и загрузчик модуля внутри изолированной среды

Модуль получает клиентский App из src/cluster/user, служебный App остаётся в ядре. app и module добавляются в глобальную область загруженного модуля. Код импортируется только внутри контейнера. Состав групп и стадии публикации, сборки и запуска описаны в ARCHITECTURE.md.

Установка

Нужен Linux и обычный CPython 3.12-3.14. Mekansm 3.0.0 устанавливается из проверенного пакета под выбранный Python, его наличие в PyPI здесь не предполагается.

python3.12 -m venv .venv
.venv/bin/python -m pip install /path/to/mekansm-3.0.0-cp312-cp312-manylinux_2_34_x86_64.whl
.venv/bin/python -m pip install --require-hashes -r requirements-runtime.txt
.venv/bin/python -m pip install --no-deps -e .
.venv/bin/python -m pip install --require-hashes -r requirements-test.txt

Для контейнеров нужны Podman, runsc и systemd с cgroup v2. Тома требуют fallocate, mkfs.ext4, mount и права на их выполнение. Управляющие команды запускает доверенный оператор, не модуль.

Запуск службы

Для локальных проверок без ограниченного тома:

.venv/bin/cluster --path ./data serve

В этом режиме квота данных логическая. Файл журнала физически не ограничен отдельным томом, поэтому такой запуск не используется для публичных пользователей.

Для запуска с ограниченным томом используется serve --volumes --key-file /path/to/core.key. Ключ является отдельным файлом из 32 случайных байт с правами 0600. Он шифрует центральную и пользовательские базы, в контейнер не подключается. Пример создания ключа:

import os
from pathlib import Path

path = Path("core.key")
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "wb") as file:
    file.write(os.urandom(32))

Одной базе соответствует один владелец, даже внутри одного процесса. При штатной остановке сокеты удаляются. После аварии ядро получает владение базой и проверяет старые сокеты. Неиспользуемый сокет заменяется, действующий сокет или обычный файл сохраняется и не даёт запустить второй экземпляр. При повторном запуске ядро проверяет файловую систему, источник и параметры уже подключённого тома. Среда с неподходящим томом не открывается.

Управление

Сокет control.sock доступен служебной учётке. Это внутренний интерфейс оператора, а не авторизация пользователя сайта. HTTP-служба связывает пользовательские действия с подтверждённым профилем и проверяет вход заново. Сам операторский сокет по-прежнему нельзя выдавать посетителю сайта.

.venv/bin/cluster --path ./data request unit/create '{"owner":"owner","request":"first"}'

Ответ содержит ID юзербота. Повтор той же операции с тем же request возвращает прежний результат. У профиля нельзя создать второй собственный юзербот.

Для установки есть module/install с id, module, slot, declared, granted и version. Связи прав передаются объектами, например {"db.read":{},"db.write":{}}. Новое право нельзя принять, если модуль его не заявил. Ограничения чатов и пользователей являются списками числовых ID, домены задаются именами сайтов.

Другие операции: unit/get, unit/state, unit/owner, unit/trust, unit/settings, module/grant, actor/check, queue/put, queue/claim, queue/complete. unit/state меняет сохранённое состояние и отзывает доступ; само завершение контейнера выполняет управляющий процесс. Этот интерфейс нельзя выдавать модулю.

Пример запуска установленного экземпляра:

.venv/bin/cluster --path ./data run UNIT_ID counter/main docker.io/library/python@sha256:IMAGE_HASH ./examples/module --seconds 30

UNIT_ID и IMAGE_HASH заменяются действительными значениями. Образ должен быть заранее получен оператором, плавающий тег не принимается. Команда вызывает Module.init, обслуживает запросы к ядру и ждёт завершения. Без --seconds управляющий процесс ждёт завершения или остановки модуля. При указанном пределе выполнение ограничивается им. Новые Telegram-сообщения поступают через app.events; декораторы запускают диспетчер команд и кнопок, описанный в MODULES.md.

Пример модуля

class Module:
    async def init(self) -> None:
        count: int = await app.db.get("counter", default=0)
        await app.db.set("counter", count + 1)

Доступны app.db.get, set, delete, keys, app.web.request, app.client, app.bot и app.events. Ответ ошибки содержит код. Обычный print уходит в stderr модуля, протокол использует отдельный сохранённый поток. После отмены запроса его ответ дочитывается, чтобы следующая операция не получила чужой ответ.

Отсутствующий ключ и JSON null различаются через default. Срок хранения задаётся ttl в секундах. Истёкшая запись скрывается при получении и удаляется обслуживанием с освобождением квоты. Для списка ключей есть limit и возвращаемый курсор after.

Текущий канал ограничен 1 MiB, значение базы не больше половины рамки. HTTPS-ответ ограничен четвертью рамки. Эти пределы задаёт протокол Cluster. Поддерживаются HTTPS, заголовки, cookies, HTTP/SOCKS-прокси и WSS на порту 443. Профиль сети хранится отдельно для каждого экземпляра. Подробности в SDK.md.

Хранилище

При --volumes база помещается в отдельный ext4-образ. На сервере резервируется два размера квоты плюс 64 MiB: дополнительное место нужно для атомарного уплотнения. Логическая квота считается по данным и ключам, служебный запас не выдаётся модулю.

Рабочие файлы модуля временные и учитываются в памяти: размер /work зависит от пакета и общего предела, /tmp ограничен 16 MiB. Для постоянных файлов есть app.files. Терминал имеет отдельное постоянное пространство внутри пользовательского тома. Файлы, база, сборки, копии и журнал входят в квоту, подробности в STORAGE.md.

Проверки

.venv/bin/python -m pytest -q
.venv/bin/ruff check src tests examples scripts
.venv/bin/python scripts/check_docs.py

scripts/probe_core.py запускает настоящие контейнеры gVisor. Он требует Linux, systemd, Podman, runsc и права mount. Создаёт временную базу, том, cgroup и модули; после проверки останавливает контейнеры и освобождает том.

Проверяются запись через ядро, закрытая сеть, корень только для чтения, подмена овнера, чужие данные, запрос сессии, терминал, общая cgroup, ошибка модуля и остановка по превышению времени. Проверка относится к выбранной конфигурации gVisor и образу.

Ограничения

Оплата через Stripe поддерживает тестовый и рабочий режимы. Настройка описана в PAYMENTS.md. Продажа модулей и произвольные HTML/JavaScript-страницы пока не реализованы. Почту, вход в Telegram и сервисного бота нужно проверить после подключения собственных служб. Совместимость системных библиотек конкретного пакета с gVisor также проверяется отдельно.

Стандартные модули

Оператор задаёт запись defaults/core-utils с module и version проверенного пакета Core Utils в центральной базе. Обычный HTTP API эту запись не меняет. Создание профиля и стандартного экземпляра происходит в одной транзакции. Фоновая проверка добавляет модуль существующим юзерботам, если установка ещё не выполнялась.

Запись defaults в юзерботе отмечает выполненную установку. Найденный экземпляр в любом профиле считается уже установленным: его права, настройки и состояние не меняются. После явного удаления модуль сам не возвращается. При конфликте имени команды выбирается свободный алиас; существующие команды остаются прежними.

Стандартный модуль получает только заранее ограниченный набор прав: обычные вызовы Telegram и бота, настройку модулей и общих параметров. Права на сеть, файлы, полную сессию и базу не выдаются. Сам конфигуратор доступен овнерам, а изменение настроек ядро допускает только по действующему личному событию. Установка не включает остановленный юзербот и не предоставляет подписку.