# infra/runner — `tgit-runner` Запуск сервисов из репозитория загрузок (`services//`) на хосте в песочнице — ADR-005, ADR-006, [ARCHITECTURE §4](../../docs/ARCHITECTURE.md). ``` бот: push ─► git archive :services/ ─► ssh tgit-deploy@host deploy (архив — в stdin) │ forced command: только tgit-runner host: tgit-runner ─► проверка аргументов и архива ─► docker build ─► docker run …-next (песочница) ─► healthy за 60 с? ─► да: заменить старый контейнер · нет: удалить новый, старый работает, exit 3 ``` | Команда | Что делает | |---|---| | `deploy ` | собрать образ `tgit-svc-:` и запустить, старый заменяется только здоровым новым | | `restart ` | перезапуск (кнопка «♻️ Перезапустить» в «Статусе», только админ) | | `remove ` | сервис удалён из репозитория — остановить и удалить образы | | `list` | контейнеры сервисов | **Песочница:** uid 10001, read-only FS, `/tmp` — tmpfs 16 МБ с `noexec`, `--cap-drop ALL`, `no-new-privileges`, 128 МБ без swap, 0.25 CPU, 64 процесса, `--network none` (интернет — только если `network: true` в `tgit.yaml` **и** имя сервиса есть в `/srv/tgit/runner/egress-allow`), без Docker-сокета, `--restart on-failure:3`, ротация логов. Не больше 10 сервисов на хост; хранятся два последних образа сервиса. **Проверено** [`examples/escape_attempts`](../../examples/escape_attempts/): запись в код и `/etc`, сеть, DNS, docker.sock, `setuid(0)`, raw-сокет, 200 процессов, запуск из `/tmp` — всё `BLOCKED`. **Установка** (один раз, root на хосте): ```bash ssh-keygen -t ed25519 -N '' -C tgit-runner -f tgit-runner-key # ключ бота для раннера sudo infra/runner/install.sh tgit-runner-key.pub tgit-deploy # в .env бота на сервере: # RUNNER_HOST=host.docker.internal RUNNER_SSH_KEY_B64=$(base64 -w0 tgit-runner-key) ``` Дальше скрипт обновляется сам: деплой бота (`infra/deploy/remote.sh`) копирует свежий `tgit-runner`, если раннер установлен. Тесты — `tests/runner/` (фейковые `docker` и `ssh`, сам скрипт настоящий).