# Устройство backup-master ## Компоненты ``` Старый сервер Сервер бэкапов Новый сервер backupctl backup ──PUT части──► backupctl server ◄── backupctl restore (systemd timer) SQLite + файлы, веб-морда provision.sh → recipes → распаковка → сервисы ретеншн, ntfy ``` | Пакет | Назначение | |---|---| | `internal/manifest` | формат манифеста бэкапа (общий для всех) | | `internal/catalog` | категории бэкапа: встроенный `default.yaml` + переопределения клиента | | `internal/archive` | tar (PAX) + zstd: запись, распаковка, исключения, владельцы по имени | | `internal/api` | протокол и HTTP-клиент | | `internal/agent` | сбор: files / systemd / xui / compose, дампы, проверка crontab, отправка | | `internal/restore` | восстановление: provision.sh, recipes бинарников, compose, порядок категорий | | `internal/server` | API, веб-морда, ретеншн, уведомления | ## Формат архива Каждая часть — `tar.zst`. В main первой записью идёт `manifest.json`. Пути внутри архива: - `/files/<абсолютный путь>` — восстанавливается в `/`; - `/dumps/<файл>` — дампы БД и снимок x-ui.db; - `/volumes/<том>/…` — именованные docker-тома; - `/bin/<абсолютный путь>` — fallback-копии бинарников. Heavy-часть называется именем категории и устроена так же. Main без неё самодостаточен. ## Протокол | Метод | Токен | Назначение | |---|---|---| | `GET /api/v1/agent/config` | агент | домен, переопределения каталога, пора ли слать heavy | | `POST /api/v1/backups` | агент | манифест → id | | `PUT /api/v1/backups/{id}/parts/{name}` | агент | потоковая загрузка части | | `POST /api/v1/backups/{id}/complete` | агент | sha256/размеры частей + предупреждения архивации | | `POST /api/v1/backups/{id}/fail` | агент | ошибка | | `GET /api/v1/restore/manifest[?backup=]` | restore | манифест и доступные части | | `GET /api/v1/restore/{id}/parts/{name}` | restore | скачивание (Range — докачка) | Агентский токен даёт только запись: если клиента взломают, злоумышленник не сможет прочитать или удалить его историю. Restore-токен выпускается в морде на 24 часа и даёт только чтение бэкапов одного клиента. В БД хранятся только sha256 токенов. Агент архивирует часть прямо в тело PUT (`io.Pipe`), временный архив на диске не создаётся. Дампы БД снимаются **до** архивации. Поэтому в Stoat порядок «сначала mongodump, потом файлы MinIO» соблюдается сам собой: файлы, загруженные во время бэкапа, станут лишними объектами без ссылок, но ссылок на отсутствующие файлы не появится. ## Compose - Проекты берутся из `docker compose ls -a`, к ним добавляются явные `dir` из каталога. Сервисы, bind-mounts и тома — из `docker compose config --format json`. - БД определяются по образу. Если контейнер запущен, делается дамп через `docker exec`: учётные данные берутся из окружения контейнера, а каталог данных исключается. Если остановлен — каталог данных копируется как есть (он консистентен). - Данные `rabbitmq` и сервисов из `skip_services` не бэкапятся. - Записываются digest запущенных образов. При восстановлении создаётся `compose.restore.yml` с `image: repo@sha256:…`, потому что `latest` на новом сервере дал бы другую Mongo/MinIO. - Отсутствующие external-сети создаются пустыми. Так stoat-mbot поднимается без psiphon. - Порядок восстановления: `pull` → `build` → `create` → для каждой БД `up -d ` → ожидание TCP-готовности → заливка дампа → `up -d`. ## Версии | Что | Как записывается | Как восстанавливается | |---|---|---| | apt-пакеты (caddy, telegraf, docker-ce, docker-compose-plugin) | `dpkg-query` | `provision.sh` ставит `pkg=version`, если версия есть в репозитории | | 3x-ui, nebula, sing-box-extended | `version_cmd` | recipes в `internal/restore/recipes.go`: release-архивы нужного тега; при неудаче — копия из `bin/` | | образы compose | digest | `compose.restore.yml` | ## Расширение - Новая категория — запись в `default.yaml` или в YAML клиента в морде. - Новый способ установки бинарника — функция в `recipes`, имя указывается в поле `install` категории. - Новый тип БД в compose — `dbKind`/`dumpCmds` в `internal/agent/compose.go` и `dbReady`/`dbRestore` в `internal/restore/compose.go`.