docs: ТЗ v2 — QR-доступ, меню разделов, коммит по дереву, агент со стражем, общие алерты

ADR-011 (QR-приглашения), ADR-012 (командная модель), ADR-013 (агент логов).

Refs #24

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 22:50:31 +03:00
co-authored by Claude Opus 5.5
parent af1585c634
commit d34bc33aeb
5 changed files with 364 additions and 200 deletions
+169 -104
View File
@@ -1,143 +1,200 @@
# Архитектура tgit
> Требования — в [`SPEC.md`](SPEC.md), решения и их обоснования — в [`DECISIONS.md`](DECISIONS.md).
> Требования — в [`SPEC.md`](SPEC.md) (v2), решения и их обоснования — в [`DECISIONS.md`](DECISIONS.md).
## 1. Общая схема
```
Telegram ◄──long polling──┐
│
┌────────────┴─────────────────────────── хост (VPS / ноутбук) ─────────────┐
│ tgit-bot (один процесс, asyncio) │
│ ├─ bot/ aiogram: команды, загрузка файлов, кнопки │
│ ├─ uploads/ валидация → GitPublisher (очередь, git + SSH) ───────────┼──► SourceCraft
│ ├─ ci/ CIWatcher: polling прогонов по sha, логи кубов ◄────────┼─── REST API
│ ├─ runtime/ Monitor: события и метрики контейнеров, логи │
│ ├─ alerts/ правила, антиспам, доставка │
│ └─ llm/ LogAssistant: вопрос → LogQuery → поиск → ответ ─────────┼──► LLM API
│ │ │ │
│ ┌────┴─────┐ ┌────────┴────────┐ │
│ │ postgres │ │ docker-proxy │ (только чтение: ps/logs/ │
│ └──────────┘ │ socket-proxy │ events/stats) │
│ └────────┬────────┘ │
│ │ /var/run/docker.sock │
│ tgit-runner (скрипт на хосте) ─┤ │
│ ▲ ssh, forced command ▼ │
│ │ tgit-app-<project> контейнеры (песочница) │
└─────┼─────────────────────────────────────────────────────────────────────┘
┌────────────┴──────────────────────────────── хост (VPS / ноутбук) ───────────┐
│ tgit-bot (один процесс, asyncio) │
│ ├─ bot/ меню, FSM, роутеры разделов, middleware доступа │
│ ├─ access/ приглашения (QR), участники, антиперебор │
│ ├─ repo/ дерево репозитория, черновики, Publisher (git + SSH) ────────┼──► SourceCraft
│ ├─ pipeline/ PipelineWatcher: CI и CD по sha, уведомления автору ◄────────┼─── REST API
│ ├─ runtime/ состояние контейнеров, проверки, логи │
│ ├─ logs/ grep-движок (Python, без shell) │
│ ├─ agent/ фильтр → LLM-страж → LLM → LogQuery ─────────────────────────┼──► LLM API
│ └─ alerts/ детекторы, антиспам, рассылка всем, восстановление │
│ │ │ │
│ ┌────┴─────┐ ┌────────┴────────┐ │
│ │ postgres │ │ docker-proxy │ (только GET: containers/logs/ │
│ └──────────┘ │ │ events/stats) │
│ └────────┬────────┘ │
│ │ /var/run/docker.sock │
│ tgit-runner (скрипт на хосте) ─┤ │
│ ▲ ssh, forced command ▼ │
│ │ tgit-svc-<service> контейнеры (песочница) │
└─────┼──────────────────────────────────────────────────────────────────────── ┘
│
SourceCraft CI (tgit-uploads): check → deploy ──┘
```
**Два репозитория:**
**Репозитории:**
| Репозиторий | Что внутри | CI/CD |
|---|---|---|
| `tgit` (Gitea → зеркало в SourceCraft) | код бота | push в любую ветку: lint + types + tests; push `main`: + деплой бота на хост |
| `tgit-uploads` | `projects/<name>/…` от пользователей | push `main`: проверки проекта; успех → `ssh tgit-runner deploy <project> <sha>` |
| `tgit-uploads` (SourceCraft) | общий репозиторий команды: `services/<name>/…` и любые файлы | push `main`: `check` → при успехе `deploy` изменённых сервисов через `tgit-runner` |
## 2. Компоненты
### 2.1 `app/bot` — интерфейс Telegram
aiogram 3, роутеры по фичам (`upload`, `projects`, `logs`, `ask`, `admin`). Middleware: проверка доступа по белому
списку, логирование апдейтов без содержимого файлов. Длинные ответы → файл.
aiogram 3. Главное меню — reply-клавиатура (📝 Коммит · 🩺 Статус · 📜 Логи · 🚨 Алерты · 👥 Участники для админа);
внутри разделов — инлайн-кнопки, сообщение раздела редактируется на месте.
### 2.2 `app/uploads` — загрузка и коммит
- `validate(file) -> ValidatedUpload` — размер, кодировка, `ast.parse`, режим `# tgit: mode=…`.
- `GitPublisher` — держит локальный клон `tgit-uploads` в volume; **единственная очередь** (`asyncio.Queue`, один воркер),
чтобы пуши не конфликтовали. Шаги: `fetch` → `reset --hard origin/main` → запись файла → `commit` → `push`;
при отказе push — повтор с `pull --rebase` (до 3 раз). git вызывается через `asyncio.create_subprocess_exec`
с `GIT_SSH_COMMAND="ssh -i <deploy_key> -o IdentitiesOnly=yes"`.
- Пустой diff → «изменений нет», коммита нет.
- **Middleware доступа** — первым в цепочке, на `message` и `callback_query`: пускает только участников; исключение —
`/start <token>`. Неавторизованным — одна фраза раз в сутки, остальное игнорируется.
- **FSM** — `aiogram` storage поверх Postgres (своя реализация `BaseStorage`, таблица `fsm_state`) → состояние переживает рестарт.
- **callback_data ≤ 64 байт** — в кнопки не кладём пути: `CallbackData` с коротким id, а соответствие id → путь
хранится в данных FSM текущего экрана.
### 2.3 `app/sourcecraft` — клиент API
### 2.2 `app/access` — приглашения и участники
- `create_invite(admin) -> (link, png)`: `secrets.token_urlsafe(16)` → в БД `sha256(token)`, `expires_at`, `created_by`;
QR — библиотека `segno` (PNG в памяти).
- `redeem(token, tg_user)`: хэш → поиск → не истёк, не использован → в транзакции `used_by/used_at` + создание участника.
- Антиперебор: счётчик неудачных попыток по `tg_id` за час (таблица `access_attempts`).
- `revoke(user)`: `is_active = false`, удаление черновика, сброс FSM.
### 2.3 `app/repo` — дерево, черновики, пуш
- **Локальный клон** `tgit-uploads` в volume. Перед открытием браузера — `git fetch` (не чаще раза в 10 с);
дерево — `git ls-tree origin/main <path>`, содержимое — `git show origin/main:<path>`. Быстро и без API.
- **Черновик** (`drafts` + `draft_changes`): `base_sha` (HEAD при начале), список изменений `add|modify|delete` с
содержимым. Виртуальные новые папки живут только в FSM, пока в них не добавлен файл.
- **Валидация** (`repo/validate.py`): имя файла, размер, UTF-8, NUL, `ast.parse` для `.py`, запрет `.sourcecraft/` и служебных файлов.
- **Publisher** — единственная очередь (`asyncio.Queue`, один воркер), чтобы пуши не конфликтовали:
`fetch` → проверка конфликта (`git diff --name-only base_sha origin/main` ∩ пути черновика) → `reset --hard origin/main`
→ применить изменения → `commit` с трейлером `Telegram-User` → `push`; отказ push → `pull --rebase` (до 3 раз).
git — через `asyncio.create_subprocess_exec` (аргументы списком), `GIT_SSH_COMMAND` с ключом бота.
### 2.4 `app/sourcecraft` — клиент API
`httpx.AsyncClient`, Bearer-токен, ретраи на сетевые ошибки и 5xx (экспоненциально, до 5 попыток),
типизированные ответы (pydantic). Методы: `list_runs`, `get_run`, `get_cube_logs`, `trigger_workflow`.
Подробности API — [`dev/SOURCECRAFT_API.md`](dev/SOURCECRAFT_API.md).
pydantic-модели ответов. Методы: `list_runs`, `get_run`, `get_cube_logs`. Подробности — [`dev/SOURCECRAFT_API.md`](dev/SOURCECRAFT_API.md).
### 2.4 `app/ci` — наблюдатель за CI
Фоновая задача. Опрашивает `/cicd/runs` **только пока есть незавершённые загрузки** (интервал 10 с, бэкофф до 60 с
при ошибках). Сопоставляет прогон с загрузкой по sha коммита, на смене статуса редактирует сообщение пользователя.
При падении скачивает лог упавшего куба. Таймаут прогона — 30 мин → статус `timeout`.
Webhooks у SourceCraft нет — отсюда polling (ADR-003).
### 2.5 `app/pipeline` — наблюдатель CI/CD
Фоновая задача. Пока есть незавершённые пуши — опрашивает `/cicd/runs` (10 с, бэкофф до 60 с), сопоставляет прогон с
пушем по sha. Отслеживает workflow `check` (CI) и `deploy` (CD) отдельно; на смене статуса — редактирует сообщение
о пуше и шлёт автору уведомление (успех / падение + лог упавшего куба). Падение `deploy` → событие для `alerts`.
Таймаут — 30 мин. Webhooks у SourceCraft нет (ADR-003).
### 2.5 `app/runtime` — мониторинг приложений
Через `docker-socket-proxy` (только `GET` на containers/logs/events/stats):
- поток `events` с фильтром `label=tgit.project` → `die` (код выхода), `oom`, `restart`;
- раз в 15 с `stats` по контейнерам приложений → CPU% / RAM% от лимита, скользящее окно 1 мин;
- `logs` с `since`/`tail` — для `/logs` и LLM.
### 2.6 `app/runtime` — контейнеры и проверки
Через `docker-socket-proxy` (только `GET`):
- **Список** — контейнеры с меткой `tgit.managed=true` (сервисы и служебные: bot, postgres, proxy).
- **Карточка** — `inspect` (state, health, restart count, started_at, метки `tgit.sha`, `tgit.deployed_at`) + `stats`.
- **Проверки** (`runtime/checks.py`) — набор независимых функций `Check -> CheckResult(ok, detail)`:
`running`, `docker_health`, `restarts_stable` (сравнение с 5 мин назад, история из БД), `cpu_ok`, `mem_ok`,
`no_errors_in_logs` (5 мин, шаблон из `config/checks.yaml`), `http_probe` (если в `services/<name>/tgit.yaml`
объявлен `health_url`; проба из сети сервиса через runner). Результат — отчёт-таблица.
- **Мониторинг** — поток `events` (`die`, `oom`, `health_status`, `restart`) и опрос `stats` раз в 15 с → события для `alerts`.
Управление (`stop`/`restart`) бот **не** делает через сокет — он вызывает `tgit-runner` (ADR-006).
### 2.7 `app/logs` — grep-движок
- `parse_grep(expr) -> GrepQuery` — подмножество синтаксиса grep: шаблон, `-i`, `-v`, `-E`, `-c`, `-C N`; всё
остальное — ошибка с подсказкой. Разбор `shlex.split`, **никакого исполнения**.
- `run(query, source)` — источник `runtime.logs(container, since, tail)`; поиск модулем `regex` с таймаутом на
строку; лимиты: шаблон ≤ 200, вход ≤ 5 МБ, вывод ≤ 500 строк.
- `to_command(query)` — человекочитаемый `grep …` для показа пользователю.
### 2.6 `app/alerts` — алерты
Правила (пороги — в `config/alerts.yaml`) → событие → антиспам (ключ `project+kind`, окно 10 мин, счётчик) →
доставка владельцу и подписчикам. Системные алерты (недоступен API/Docker/БД) — администраторам.
### 2.7 `app/llm` — ассистент по логам
### 2.8 `app/agent` — LLM-агент для логов
```
вопрос ──► LLM #1 (JSON-схема LogQuery) ──► валидация ──► поиск по логам ──► маскирование ──► LLM #2 ──► ответ
промт ─► prefilter ─✗─► отказ + алерт 🛡
│✓
▼
guard (LLM, JSON {verdict, reason}) ─ attack / невалидный JSON ─► отказ + алерт 🛡
│safe
▼
planner (LLM, JSON LogQuery: container, since, pattern, flags, limit) ─ невалидно ─► «не смог построить запрос»
│
▼
validate (pydantic, белый список контейнеров, компиляция regex, анализ бэктрекинга)
│
▼
logs.run(query) ─► ответ: grep-команда + строки
```
- `LogQuery`: `source` (`app` | `ci`), `since` (≤ 7 д), `pattern` (regex ≤ 200 символов), `level`, `limit` (≤ 500).
- Поиск — Python `regex` с таймаутом (защита от ReDoS), **никакого shell** (ADR-007).
- Маскирование: токены, `KEY=…`, `password=…`, JWT, e-mail, IP → `***`.
- Провайдер — за протоколом `LLMProvider` + фабрика по конфигу: OpenAI-совместимый API (YandexGPT, OpenAI, локальная
модель) и `mock` для тестов (ADR-008).
- `prefilter.py` — детерминированные правила из `config/agent_rules.yaml` (длина, шаблоны атак).
- `guard.py` и `planner.py` — **отдельные** вызовы с узкими системными промптами (`config/prompts/`); в planner
передаются только промт пользователя и список контейнеров — **логи в LLM не попадают** (ADR-007, ADR-013).
- Провайдер — `LLMProvider` + фабрика (ADR-008); в тестах `mock`.
- Журнал: `agent_requests` (промт, вердикт, запрос, число строк, токены).
### 2.8 `app/db` — хранилище
### 2.9 `app/alerts` — алерты
- **Детекторы** превращают события runtime/pipeline/хоста в `AlertEvent(kind, service, payload)`; пороги — `config/alerts.yaml`.
- **Дедупликация**: ключ `kind + service`; открытый алерт с тем же ключом → `count += 1`, исходные сообщения
редактируются (не чаще раза в 10 мин), новые не шлются.
- **Рассылка** всем активным участникам (кроме 🛡 — только администраторам) с учётом лимита Telegram (~30 msg/s,
очередь отправки); `alert_deliveries` хранит `chat_id`/`message_id` — чтобы обновлять счётчик и отвечать «✅ восстановлено».
- **Восстановление**: детектор сообщает, что условие прошло → `resolved_at`, ответ на исходные сообщения.
- **Список**: активные + история 7 дней, пагинация.
### 2.10 `app/db` — хранилище
SQLAlchemy 2 (async) + Alembic. PostgreSQL в Compose; в тестах — SQLite.
| Таблица | Поля (основные) |
|---|---|
| `users` | `tg_id` PK, `username`, `role` (`user`/`admin`), `is_allowed`, `llm_quota_used`, `created_at` |
| `projects` | `id`, `name` UNIQUE, `owner_id` → users, `mode` (`service`/`job`), `network_enabled`, `alerts_enabled` |
| `uploads` | `id`, `project_id`, `user_id`, `filename`, `sha`, `status` (`committed`/`ci_running`/`ci_ok`/`ci_failed`/`deployed`/`timeout`), `chat_id`, `message_id`, `created_at` |
| `ci_runs` | `id`, `upload_id`, `run_slug`, `status`, `failed_cube`, `url`, `updated_at` |
| `alerts` | `id`, `project_id`, `kind`, `payload` JSON, `count`, `first_at`, `last_at`, `delivered_at` |
| `llm_requests` | `id`, `user_id`, `project_id`, `question`, `log_query` JSON, `tokens`, `created_at` |
| `users` | `tg_id` PK, `username`, `full_name`, `role` (`member`/`admin`), `is_active`, `invited_by`, `joined_at`, `last_seen_at` |
| `invites` | `id`, `token_hash` UNIQUE, `created_by`, `created_at`, `expires_at`, `used_by`, `used_at` |
| `access_attempts` | `tg_id`, `at`, `ok` |
| `fsm_state` | `key` PK, `state`, `data` JSON |
| `drafts` | `id`, `user_id` UNIQUE, `base_sha`, `message`, `created_at`, `expires_at` |
| `draft_changes` | `id`, `draft_id`, `path`, `op` (`add`/`modify`/`delete`), `content` BYTEA |
| `pushes` | `id`, `user_id`, `sha`, `message`, `paths` JSON, `chat_id`, `message_id`, `ci_status`, `cd_status`, `created_at` |
| `pipeline_runs` | `id`, `push_id`, `workflow` (`check`/`deploy`), `run_slug`, `status`, `failed_cube`, `url`, `updated_at` |
| `container_snapshots` | `container`, `at`, `restart_count`, `cpu_pct`, `mem_pct`, `health` — для проверок «за 5 мин» (TTL 1 сутки) |
| `alerts` | `id`, `kind`, `service`, `payload` JSON, `count`, `opened_at`, `last_at`, `resolved_at` |
| `alert_deliveries` | `alert_id`, `chat_id`, `message_id` |
| `log_queries` | `id`, `user_id`, `container`, `mode` (`tail`/`grep`/`agent`), `query` JSON, `lines`, `at` |
| `agent_requests` | `id`, `user_id`, `prompt`, `verdict`, `reason`, `log_query` JSON, `tokens`, `at` |
### 2.9 Конфигурация
`app/config.py` — `pydantic-settings`, один класс на подсистему (`TelegramSettings`, `SourceCraftSettings`,
`DockerSettings`, `LLMSettings`, …), всё из `.env`. Пороги и тексты — `config/*.yaml`.
### 2.11 Конфигурация
`app/config.py` — `pydantic-settings`, класс на подсистему (`TelegramSettings`, `AccessSettings`, `SourceCraftSettings`,
`GitSettings`, `DockerSettings`, `LLMSettings`, `AlertSettings`), всё из `.env`. Пороги, тексты, правила, промпты — `config/*.yaml`.
## 3. Потоки данных
### 3.1 Загрузка → CI → CD
### 3.1 Вход по приглашению
```
user ──.py──► bot ── validate ──✗──► «ошибка, строка N»
│✓
▼
GitPublisher ──push──► tgit-uploads@main
│ │ (SourceCraft CI)
«✅ a1b2c3d, ⏳ CI» ◄┘ ├─ check: py_compile · ruff · pytest · bandit
└─ deploy: ssh tgit-deploy@host → tgit-runner deploy <project> <sha>
│
CIWatcher ──poll runs(sha)──► статус ──► edit message ▼
docker build + run tgit-app-<project>
Monitor ◄── events/stats ── docker-proxy ◄──────────────────┘
└──► alerts ──► user
админ ─ «Пригласить» ─► access.create_invite ─► QR (PNG) + ссылка
новый пользователь ─ скан QR ─► t.me/<bot>?start=<token> ─► /start <token> ─► redeem ─✓─► users + главное меню
└✗─► отказ (+ счётчик попыток)
```
### 3.2 Вопрос к логам
`/ask` → квота → LLM #1 → `LogQuery` → валидация → `runtime.logs()` / `sourcecraft.get_cube_logs()` → фильтр regex →
маскирование → LLM #2 (вопрос + найденные строки) → ответ + запрос + доказательства → запись в `llm_requests`.
### 3.2 Коммит → CI → CD
```
📝 Коммит ─► дерево origin/main ─► файл/папка ─► изменения в черновик ─► 💬 сообщение ─► 🚀 Push ─► подтверждение
│
Publisher (очередь) ─ fetch, конфликт?, commit, push ──► tgit-uploads@main
│ SourceCraft CI
«a1b2c3d ⏳ CI» ◄──────────────────────────────────────────────────────────────────────────┘ check: py_compile · ruff · pytest · bandit
PipelineWatcher ─ poll runs(sha) ─► «✅ CI» ─► «⏳ CD» ─► «🚀 CD: weather» deploy: ssh → tgit-runner deploy <svc> <sha>
└► ❌ + лог шага автору; ❌ CD → алерт всем
```
## 4. Песочница приложений (`tgit-runner`)
### 3.3 Логи через агента
См. §2.8; ответ — grep-команда + найденные строки; запись в `agent_requests` и `log_queries`.
`infra/runner/tgit-runner` — bash-скрипт на хосте; единственная команда, доступная ключу CI (`command="…"` в
`authorized_keys` пользователя `tgit-deploy`). Подкоманды: `deploy <project> <sha>`, `stop <project>`, `restart <project>`.
### 3.4 Алерт
`runtime`/`pipeline`/хост → детектор → дедупликация → рассылка всем участникам → … → условие прошло → «✅ восстановлено».
`deploy`: забирает архив проекта по sha из `tgit-uploads` → собирает образ `tgit-app-<project>:<sha>` из шаблона
(`python:3.12-slim` + `requirements.txt`) → запускает:
## 4. Песочница сервисов (`tgit-runner`)
`infra/runner/tgit-runner` — bash-скрипт на хосте; единственная команда, доступная ключам CI и бота (`command="…"` в
`authorized_keys` пользователя `tgit-deploy`). Подкоманды: `deploy <service> <sha>`, `restart <service>`,
`probe <service> <path>` (HTTP-проба изнутри сети сервиса). Аргументы валидируются: имя `^[a-z][a-z0-9-]{1,30}$`, sha — hex.
`deploy`: забирает архив `services/<service>` по sha из `tgit-uploads` → собирает образ `tgit-svc-<service>:<sha>` из
шаблона (`python:3.12-slim` + `requirements.txt`, `HEALTHCHECK` из `tgit.yaml` сервиса, если задан) → запускает:
```
docker run -d --name tgit-app-<project> --label tgit.project=<project> --label tgit.sha=<sha>
docker run -d --name tgit-svc-<service> --label tgit.managed=true --label tgit.service=<service>
--label tgit.sha=<sha> --label tgit.deployed_at=<iso>
--user 10001:10001 --read-only --tmpfs /tmp:size=16m
--cap-drop ALL --security-opt no-new-privileges
--memory 128m --memory-swap 128m --cpus 0.25 --pids-limit 64
--network none # или tgit-apps-egress, если сеть разрешена
--restart on-failure:3 # service; для job — без рестарта
--network none # или tgit-svc-egress, если сеть разрешена администратором
--restart on-failure:3
--log-opt max-size=5m --log-opt max-file=2
```
Если новый контейнер не стал healthy за 60 с — откат на предыдущий образ и ненулевой код выхода (CD падает → алерт ⛔).
## 5. CI/CD
@@ -152,7 +209,11 @@ docker run -d --name tgit-app-<project> --label tgit.project=<project> --label t
### 5.2 Репозиторий загрузок (`tgit-uploads/.sourcecraft/ci.yaml`)
Шаблон хранится в `tgit/infra/uploads-template/` и ставится в `tgit-uploads` один раз.
Определение изменённого проекта — `git diff --name-only HEAD~1 -- projects/` внутри куба.
| Workflow | Шаги |
|---|---|
| `check` | изменённые файлы (`git diff --name-only HEAD~1`) → `py_compile` · `ruff check` (мягкий набор правил) · `pytest` для затронутых сервисов · `bandit` |
| `deploy` (после `check`) | изменённые `services/<name>/` → `ssh tgit-deploy@host deploy <name> <sha>` для каждого |
## 6. Деплой
@@ -166,18 +227,18 @@ docker run -d --name tgit-app-<project> --label tgit.project=<project> --label t
(хост режет часть соединений). Хост задаётся `DEPLOY_HOST` — тот же скрипт работает для любого сервера.
Бюджет ресурсов на VPS (свободно ~1,9 ГБ RAM): бот ≤ 256 МБ, postgres ≤ 256 МБ, proxy ≤ 32 МБ,
приложения ≤ 10 × 128 МБ.
сервисы ≤ 10 × 128 МБ.
## 7. Структура репозитория
```
app/ код бота: bot, uploads, sourcecraft, ci, runtime, alerts, llm, db, config.py, main.py
config/ alerts.yaml, texts.yaml, llm-промпты
app/ код бота: bot, access, repo, sourcecraft, pipeline, runtime, logs, agent, alerts, db, config.py, main.py
config/ alerts.yaml, checks.yaml, agent_rules.yaml, texts.yaml, prompts/
infra/
deploy/ deploy.sh, docker-compose.server.yml
runner/ tgit-runner, шаблон Dockerfile приложения, authorized_keys.example
uploads-template/ .sourcecraft/ci.yaml и README для tgit-uploads
examples/ демо-скрипты для загрузки (ok, syntax error, lint fail, crash, cpu hog)
runner/ tgit-runner, шаблон Dockerfile сервиса, authorized_keys.example
uploads-template/ .sourcecraft/ci.yaml, ruff.toml, README для tgit-uploads
examples/ демо-сервисы: ok, syntax error, lint fail, crash, cpu hog, unhealthy, escape attempts
tests/ pytest: unit + интеграционные с фейками
docs/ SPEC, ARCHITECTURE, DECISIONS, dev/, презентация
Dockerfile · docker-compose.yml · Makefile · pyproject.toml · .env.example
@@ -187,13 +248,17 @@ Dockerfile · docker-compose.yml · Makefile · pyproject.toml · .env.example
| Риск | Что делаем |
|---|---|
| Чужой код на одном сервере с прод-Vobot | Песочница §4, лимиты, сеть выключена; общий лимит 10 приложений; в перспективе — отдельный хост (ADR-005) |
| Случайные люди и спам | Только одноразовые приглашения с TTL; middleware на каждом апдейте; антиперебор токенов; лимиты пушей и запросов |
| Утечка QR | Одноразовость + 24 ч; админ видит, кто вошёл по какому приглашению, и может отозвать доступ |
| Участник ломает CI из бота | Запрет правок `.sourcecraft/` и служебных файлов (C-16) |
| Чужой код на одном сервере с прод-Vobot | Песочница §4, лимиты, сеть выключена; общий лимит 10 сервисов; в перспективе — отдельный хост (ADR-005) |
| Docker-сокет = root на хосте | Бот — только через read-only proxy; запуск — через `tgit-runner` с forced command |
| Пользователь `tgit-deploy` в группе `docker` ≈ root | Ключ ограничен одной командой; `tgit-runner` валидирует аргументы (`^[a-z][a-z0-9-]{1,30}$`, sha hex) |
| Prompt injection через логи | LLM только возвращает `LogQuery`, который валидируется; ответ LLM — просто текст пользователю |
| ReDoS в regex от LLM | Модуль `regex` с таймаутом, лимит длины паттерна и объёма логов |
| Утечка секретов в LLM | Маскирование перед отправкой; секреты бота не попадают в логи |
| Пользователь `tgit-deploy` в группе `docker` ≈ root | Ключ ограничен одной командой; `tgit-runner` валидирует аргументы |
| Атака через агента (injection, exfiltration) | Фильтр → LLM-страж → структурированный `LogQuery` → валидация; логи в LLM не передаются; алерт админам |
| ReDoS в regex (свой grep и от LLM) | Модуль `regex` с таймаутом, лимит длины шаблона и объёма логов |
| Утечка секретов | Секреты не логируются и не идут в LLM; маскирование в выводе P2 |
| Нестабильный SourceCraft API | Ретраи, идемпотентность, проверка перед повторным POST |
| Гонки пушей | Одна очередь GitPublisher, `pull --rebase` при отказе |
| Лимиты Telegram (4096 символов, ~30 msg/s) | Длинное — файлом; редактирование одного сообщения вместо потока |
| Диск хоста занят на 75% | Ротация логов контейнеров, `docker image prune` старых ревизий приложений в `tgit-runner` |
| Гонки пушей и конфликты | Одна очередь Publisher; проверка изменений с `base_sha`; `pull --rebase` при отказе |
| Лимиты Telegram (4096 символов, 64 байта callback_data, ~30 msg/s) | Длинное — файлом; короткие id в кнопках; очередь рассылки алертов |
| Шторм алертов | Дедупликация `kind+service`, счётчик вместо новых сообщений, «восстановлено» ответом |
| Диск хоста занят на 75 % | Ротация логов контейнеров, `docker image prune` старых ревизий в `tgit-runner`, алерт 💾 |