- infra/runner/tgit-runner: deploy/restart/remove/list по SSH forced command; аргументы не исполняются и не глобятся; архив — только файлы и папки, без «..», ≤ 20 МБ, есть main.py; health_url — только localhost - песочница: uid 10001, read-only, tmpfs noexec, cap-drop ALL, no-new-privileges, 128 МБ без swap, 0.25 CPU, 64 PID, --network none (сеть — только по egress-allow админа), лимит 10 сервисов, 2 образа - новая версия стартует рядом (-next) и заменяет старую, только став healthy; иначе удаляется, старая работает, exit 3 - бот: после push git archive изменённых services/<name>/ → runner; автору — «🚀 работает» или «⛔ не развёрнут» с логом, команде — алерт ⛔; удалённая папка → remove; «♻️ Перезапустить» в «Статусе» для админа (S-5) - examples/escape_attempts: все попытки выбраться — BLOCKED на живом Docker - install.sh, authorized_keys.example; CD бота обновляет runner на хосте Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tgit — git в Телеграме
Телеграм-бот для команды, которая ведёт общий репозиторий небольших Python-сервисов. Прямо из чата можно поправить код и закоммитить его, посмотреть, живы ли контейнеры, поискать по логам — своим grep или вопросом LLM-агенту, — и получать алерты, когда что-то упало.
Учебный проект, курс Python, НИУ ВШЭ.
Основной репозиторий и задачи — Gitea HSE/tgit;
SourceCraft — зеркало.
| Документ | О чём |
|---|---|
docs/SPEC.md |
ТЗ: требования A-, M-, C-, S-, L-, AL- |
docs/ARCHITECTURE.md |
модули, потоки данных, хранилище |
docs/DECISIONS.md |
решения ADR-001…015 с альтернативами |
docs/dev/DEPLOY.md |
CI/CD и деплой на сервер |
examples/README.md |
демо-сервисы и сценарии для проверки |
1. Цели и задачи
Цель — сделать так, чтобы мелкие правки в общем коде и разбор инцидентов не требовали ноутбука с git и SSH: всё, что обычно делают в терминале, доступно из Телеграма, причём безопасно для сервера.
Задачи:
- пускать в бота только своих — по одноразовому QR-приглашению, без регистрации;
- редактировать файлы репозитория и коммитить их из чата с проверкой до пуша;
- показывать состояние контейнеров и запускать проверки здоровья по кнопке;
- давать логи — хвост, grep и вопрос на естественном языке, — не открывая shell и не отдавая логи в LLM;
- сообщать всей команде о падениях и перегрузках без спама;
- защищаться от «случайных людей» и попыток взлома через бота.
2. Что реализовано
| Раздел | Возможности | Требования |
|---|---|---|
| Доступ | вход только по одноразовому QR (TTL 24 ч), роли админ/участник, отзыв доступа сразу, молчание на перебор токенов (> 5 неудач в час) | A-1…A-8 |
| 📝 Коммит | навигация по папкам, просмотр файла, новый файл/папка, замена (текстом или документом), удаление; проверка имени, размера, UTF-8 и синтаксиса Python; черновик до 50 изменений; сообщение коммита; push с проверкой конфликтов и «Перезаписать» | C-1…C-34 |
| 🩺 Статус | все контейнеры 🟢/🟡/🔴/⚪; карточка — health, аптайм, рестарты, CPU/RAM от лимита, ревизия и время деплоя; «🩺 Запустить проверки» — 7 независимых проверок с таймаутами | S-1…S-4 |
| 📜 Логи | хвост 50/200 строк или 5 мин/1 ч/24 ч; свой grep (-i -v -E -c -C N), выполняется в Python, не в shell; длинный ответ — файлом; журнал поисков |
L-1…L-6 |
| 🤖 Агент | вопрос словами → фильтр → LLM-страж → LLM строит только LogQuery → валидация → grep; показывает построенную команду и строки; атака — отказ и 🛡 админам; 30 вопросов в сутки |
L-10…L-17 |
| 🚨 Алерты | 💥 краш, 🧠 OOM, 🔁 цикл рестартов, 🩺 unhealthy, 🔥 перегрузка, 💾 мало места, 🔌 Docker недоступен, 🛡 атака; всем участникам, с хвостом лога и кнопками; повторы — счётчик, «✅ восстановлено» ответом; список за 7 дней | AL-1…AL-6 |
| 🚀 Деплой сервисов | после push бот отправляет изменённые services/<name>/ в tgit-runner на хосте: сборка образа, запуск в песочнице (uid 10001, read-only, без capabilities и сети, 128 МБ / 0.25 CPU / 64 процесса); старая версия заменяется только здоровой новой, иначе ⛔ с логом; «♻️ Перезапустить» в «Статусе» для админа |
#13, S-5 |
| CI/CD бота | Gitea Actions: ruff, mypy strict, миграции up/down/up на PostgreSQL, pytest с порогом покрытия 80 %; автодеплой dev на сервер с проверкой health |
ADR-015 |
Качество: 296 тестов, покрытие 92 %, mypy --strict, ruff. Telegram в тестах — фейковая сессия aiogram,
Docker — фейковый Engine API на httpx.MockTransport, LLM — сценарный фейк.
3. Развёртывание и запуск
Требования
- Python 3.12 и uv — для запуска без Docker и тестов;
- Docker 24+ с Compose v2 — для полного стека;
- токен бота от @BotFather и ваш Telegram ID (узнать — у @userinfobot);
- для агента логов — OpenAI-совместимый LLM с structured output (
response_format: json_schema).
Один токен — один запущенный экземпляр: Telegram отдаёт обновления только одному процессу.
Вариант А — локально за 5 минут (без Docker для бота)
git clone https://gitea.24studbank.ru/HSE/tgit.git && cd tgit
make install # uv sync
cp .env.example .env # впишите TELEGRAM_BOT_TOKEN и ADMIN_IDS
make demo-repo # локальный репозиторий загрузок из шаблона
# → допишите напечатанную строку GIT_UPLOADS_URL=... в .env
make run # бот запущен, база — SQLite ./tgit.db
Чтобы заработали «Статус», «Логи» и алерты, нужен Docker API:
make local-proxy # read-only прокси Docker на 127.0.0.1:23750
echo DOCKER_URL=http://127.0.0.1:23750 >> .env
make demo-up # демо-сервисы: здоровый, падающий, CPU-hog, unhealthy
make run
Вариант Б — полный стек в Docker
cp .env.example .env # токен, ADMIN_IDS, репозиторий загрузок и ключ (см. ниже)
make up # бот + PostgreSQL 16 + docker-socket-proxy
make demo-up # по желанию: демо-сервисы рядом с ботом
make logs # логи бота
В Docker бот ходит в репозиторий загрузок по SSH: нужен отдельный репозиторий (шаблон —
infra/uploads-template/) и deploy-ключ с правом записи. Ключ передаётся в
GIT_SSH_KEY_B64 (base64 -w0 <ключ>): смонтированный файл контейнер прочитать не сможет.
Сервер
CD из Gitea Actions разворачивает dev на сервер скриптом infra/deploy/deploy.sh — его же можно запускать
руками для любого хоста. Подробно — в docs/dev/DEPLOY.md.
Переменные окружения
Полный список с комментариями — .env.example. Секреты хранятся только в .env, в git не попадают.
| Переменная | Обязательна | Что задаёт |
|---|---|---|
TELEGRAM_BOT_TOKEN |
да | токен бота |
ADMIN_IDS |
да | Telegram ID первых администраторов через запятую |
TELEGRAM_PROXY_URL |
нет | HTTP/SOCKS-прокси до Telegram (если сервер не видит api.telegram.org) |
GIT_UPLOADS_URL |
да | репозиторий загрузок: путь, ssh://… или git@… |
GIT_SSH_KEY_B64 / GIT_SSH_KEY_PATH |
для SSH | deploy-ключ бота |
DATABASE_URL |
нет | по умолчанию SQLite; в Compose — PostgreSQL |
DOCKER_URL |
нет | docker-socket-proxy; без него «Статус», «Логи» и алерты отвечают «Docker недоступен» |
LLM_PROVIDER, LLM_BASE_URL, LLM_MODEL, LLM_API_KEY |
нет | LLM для агента; при mock агент выключен |
Проверки
make lint # ruff + mypy --strict
make test # pytest
make cov # pytest с покрытием (порог 80 %)
4. Входные и выходные данные
Команды и кнопки
| Ввод | Что происходит |
|---|---|
/start <токен> (ссылка из QR) |
регистрация по приглашению, главное меню |
/start, /menu |
главное меню; из любого шага диалога |
/help |
справка |
| 📝 Коммит · 🩺 Статус · 📜 Логи · 🚨 Алерты | разделы; у админа ещё 👥 Участники (приглашения, отзыв) |
Внутри разделов — инлайн-кнопки. Текстом бот принимает имя файла или папки, содержимое файла (текстом, в том числе
в блоке ```, или документом до 100 КБ), сообщение коммита, выражение grep и вопрос агенту.
Выходные данные
- В Телеграме: экраны разделов, файлы (
.logдля длинных результатов, исходник для больших файлов), QR-приглашения PNG, алерты. - В репозитории загрузок: коммиты от
tgit-botс автором из Телеграма; push вmainзапускает CI репозитория. - В БД (13 таблиц, миграции Alembic): участники и приглашения, черновики, состояние диалогов, снапшоты контейнеров, алерты и их доставки, журналы поисков по логам и вопросов агенту.
Используемые API
| API | Зачем | Как |
|---|---|---|
| Telegram Bot API | весь интерфейс | aiogram 3, long polling |
| git (SSH) | чтение и коммиты в репозиторий загрузок | git подпроцессом со своим ключом, без shell |
| Docker Engine API | контейнеры, stats, логи | только GET через docker-socket-proxy |
OpenAI-совместимый /chat/completions |
страж и планировщик агента | httpx, response_format: json_schema |
SSH → tgit-runner |
деплой и перезапуск сервисов на хосте | ssh подпроцессом, ключ с forced command |
| SourceCraft REST API | статус прогонов CI репозитория загрузок | httpx, клиент готов, в UI — #11 |
5. Ограничения и особенности
Безопасность:
- Docker-сокет есть только у прокси, и тот пропускает лишь
GET /containers/*. Бот показывает только контейнеры с меткойtgit.managed=trueи отказывает по чужому id ещё до запроса: на сервере живут и другие проекты. - grep разбирается
shlex.splitи выполняется модулемregexс таймаутом на строку и общим бюджетом 3 с; катастрофический шаблон вроде(a|aa)+$бота не вешает. - Агент: логи в LLM не передаются (только вопрос и имена контейнеров), поэтому prompt injection из логов невозможен.
LLM может вернуть только
LogQuery— контейнер из белого списка, период ≤ 7 дней, шаблон ≤ 200 символов без вложенных квантификаторов. Даже обманутый страж даёт в худшем случае медленный grep. - Пути
.git/,.sourcecraft/,.tgit/иruff.tomlиз Телеграма не редактируются. Бот проверяет синтаксис до пуша, но код сервисов не исполняет.
Лимиты:
| Что | Лимит |
|---|---|
| файл из Телеграма | 100 КБ, UTF-8 |
| черновик | 50 изменений |
| push | 20 в час на участника |
| шаблон grep | 200 символов; вывод — 500 строк |
| чтение логов | 50 000 строк и 5 МБ (новейшие) |
| вопрос агенту | 500 символов, 30 в сутки |
| повтор алерта | в течение 10 мин — счётчик, а не новое сообщение |
Что пока не сделано (открытые задачи):
- проверки CI репозитория загрузок (ruff, bandit, тесты) перед деплоем: сейчас бот разворачивает сервис сразу после push, проверив только синтаксис; ожидание зелёного CI появится вместе с #11;
- уведомления о прогонах CI/CD репозитория загрузок и алерты 📦/⛔ — #11, ждут возврата к SourceCraft;
- «🔕 Тишина на час» для алертов (P2);
- prod-окружение: нужен отдельный бот, деплой
mainпропускается, пока его нет.
Структура репозитория
app/
access/ приглашения, QR, проверка доступа
bot/ диспетчер, меню, роутеры разделов, FSM в БД
repo/ клон репозитория загрузок, валидация, черновики, push
runtime/ Docker API, проверки, снапшоты
logs/ grep-движок и журнал
agent/ фильтр, страж, планировщик, схема LogQuery
alerts/ детекторы, сервис алертов, рассылка, монитор
runner/ SSH-клиент tgit-runner и деплой после push
llm/ провайдеры: OpenAI-совместимый, mock
sourcecraft/ клиент API прогонов
db/ модели, миграции Alembic, очистка
config/ тексты интерфейса, пороги проверок и алертов, правила и промпты агента
tests/ pytest; фейки Telegram, Docker и LLM
examples/ демо-сервисы и сценарии
infra/ деплой бота, tgit-runner (песочница сервисов), шаблон репозитория загрузок
docs/ ТЗ, архитектура, ADR, процесс