# План работ ## Активные задачи ### Вывод memoir-bot (CT 142) Решение от 2026-09-02: сервис не используется и выводится из эксплуатации. Repository-часть выполнена: удалены playbook, host, запись реестра, VMID из backup job и backup audit, SSH host-блок, проверки status, Prometheus target и no_proxy Uptime Kuma. Оговорка "Memoir Bot строится локально" убрана из документации - теперь единственные исключения из digest pinning это frozen Prometheus и docker-test. Live-шаги оператора выполнены 2026-09-02: deploy key `SecondBrain` отозван, monitor в Uptime Kuma снят, `pct stop 142` выполнен (подтверждено `pct list` на mini-pc). Это заодно репетиция удаления старого контейнера перед первым blue-green переездом (`migration-tofu.md`). Оставшиеся шаги: 1. Выдержать паузу (по аналогии с общим инвариантом blue-green - минимум неделю), затем `pct destroy 142`. 2. Решить судьбу цепочки бэкапов VMID 142 в PBS. 3. Не переиспользовать VMID 142 и `192.168.1.26` сразу. ### Регрессия переезда: update-плейбуки зашивают старые VMID Обнаружено 2026-09-02 сразу после cutover. `playbooks/vaultwarden-update.yml` и `playbooks/grimmory-update.yml` теперь берут backup VMID из registry, а не из жёстко прошитых чисел. Это закрывает старую регрессию со «страховочным бэкапом» на остановленном контейнере. Аудит также нашёл и исправил две проблемы в Gitea: небезопасный импорт legacy provisioning без явного выключателя и запуск `homelab-restic-offsite-gitea` на `cloud-pc` вместо `gitea` (профиль теперь живёт в CT 153). Теперь consumer status следует реальному backup unit на `gitea`, а `homelab-backup-audit-gitea` остаётся на `cloud-pc`. Обновлено 2026-09-03: `adguard-update.yml` и `mihomo-update.yml` изначально читают VMID из реестра (`homelab_services[''].vmid`), поэтому после переезда adguard→158 и mihomo→159 они автоматически указывают на новые контейнеры — правок не потребовалось. `make update-vaultwarden`, `make update-grimmory`, `make update-gitea`, `make update-adguard`, `make update-mihomo` и `make update-all` не запускались во время проверки. ### Task 3 — PBS storage-level prune removed declaratively Выполнено 2026-09-02: storage-level `prune-backups` на PVE storage `pbs` удалён декларативно, retention authority остался в PBS `prune-pbs`. Локальное недельное PBS-container backup на storage `backup` с `keep-last=2` — намеренное исключение и не трогалось. ### Кворум кластера: qdevice не голосует Обнаружено 2026-09-02. `pvecm status`: `Expected votes: 3`, `Total votes: 2`, флаги узлов `A,NV,NMW` (NV = Not-Voted), `corosync-qdevice: Connect timeout`. Причина: `corosync.conf` указывает арбитр по публичному адресу ru-vps (`host: 157.22.231.198`), а UFW пускал 5403/tcp только из `10.122.62.0/24` — сети ZeroTier, выведенной в июле 2026. Арбитр отвалился молча. Последствие: у двухнодового кластера нет третьего голоса. Кворум держится лишь пока живы обе ноды; отказ любой из них оставляет выжившую с `1 < 2`. Решение: чинить ПРЯМОЙ путь нода -> ru-vps:5403, а не заворачивать арбитр в OpenVPN. Туннель терминируется в `ovpn-mini` (CT 132 на mini-pc), поэтому при падении mini-pc арбитр исчез бы вместе с ним — защищён был бы только отказ cloud-pc. Плюс `ovpn-mini` стоит в плане на blue-green переезд, и его пересоздание роняло бы кворум. Сделано: `homelab_pve_egress_ip` в `group_vars/all/main.yml`, правила UFW в `playbooks/ru-vps-base.yml` (открыть 5403 с этого адреса, удалить правило для `10.122.62.0/24`), проверка кворума добавлена в `playbooks/status.yml` — секция CLUSTER QUORUM, чтобы повторный отказ не был снова молчаливым. ВЫПОЛНЕНО 2026-09-02. После прогона `make ru-vps-base`: `Total votes: 3`, флаги узлов сменились с `A,NV,NMW` на `A,V,NMW`, qdevice отдаёт голос. Кластер снова имеет арбитра. Открытым остаётся динамический `homelab_pve_egress_ip`: адрес зафиксирован статически, при его смене qdevice снова замолчит. Отличие от прошлого раза в том, что теперь это видно в секции CLUSTER QUORUM отчёта `make status`. ### Вывод ZeroTier с ru-vps Решение от 2026-09-02: выводить полностью. Проверено — ZT-интерфейса на хосте нет, маршрутов через него нет, на ZT-адресах никто не слушает; контейнер `zerotier` подключён в никуда. Мёртв и `ssh-zt22.service` (sshd на порту 22 для setup qdevice) — порт 22 не слушает никто. ВЫПОЛНЕНО 2026-09-02 через `playbooks/ru-vps-zerotier-decommission.yml` (`make zerotier-decommission CONFIRM=1`). Контейнер снят, `ssh-zt22.service` отключён, правила UFW для интерфейса `zt6q3dmi2d`, `9993/udp`, `9001` и подсети `10.122.62.0/24` удалены. Сброшено состояние failed у `ssh-zt22.service` и фантомного `homelab-pve-routes.service`. Прогон идемпотентен, публичные сервисы и кворум не пострадали. Намеренно НЕ удалено: каталог `/opt/services/ru-vps/zerotier`, данные `/opt/data/zerotier`, файл `/etc/ssh/sshd_config_zt22`. `docker compose down` выполняется без `-v`, identity узла сохранена. Удаление — отдельный шаг. Правила UFW для `3128/tcp` (squid не запущен), `1080/tcp` (danted слушает 1081), `993/tcp` и `7892/tcp` оставлены: они не наследие ZeroTier. В плейбуке есть переключатель `zt_cleanup_unrelated_stale_rules`, по умолчанию выключен. ### resticprofile-check@profile-default падает еженедельно Обнаружено 2026-09-02 при чистке упавших юнитов на ru-vps. Fatal: Please specify repository location (-r or --repository-file) check on profile 'default': exit status 1 Профиль `default` в `/opt/services/ru-vps/resticprofile/profiles.toml` не имеет репозитория — это профиль-заготовка, для которого не должно быть таймера проверки. Реальный `profile-services` бэкапится и проверяется штатно, так что это шум, а не потеря бэкапов. Значение: юнит постоянно висит в секции FAILED SYSTEMD UNITS отчёта `make status` и притупляет внимание к настоящим отказам. Стек resticprofile на ru-vps в Ansible не описан, поэтому чинится либо вручную (`systemctl disable --now resticprofile-check@profile-default.timer`), либо вместе со взятием стека под управление. Прочие упавшие юниты на ru-vps — `ifup@eth0.service` и `networking.service`; не разбирались, хост при этом полностью работоспособен. ### Gitea Actions: документация устарела Проверено 2026-09-02. Контейнер `gitea-runner-gitea-runner-1` на ru-vps работает и опрашивает Gitea, то есть раннер ЗАРЕГИСТРИРОВАН. Утверждения в `.gitea/workflows/lint.yml` и `tech-stack.md` об обратном неверны. CI при этом всё равно не выполняется: у раннера метка `ru-vps`, а workflow требует `runs-on: ubuntu-latest`. Отдельный вопрос перед включением CI: раннер монтирует `/var/run/docker.sock` и `/opt/services` на запись, то есть любой workflow получает root над публичной VPS. ### hermes-ai недостижим по SSH: прозрачный прокси съедает обратный путь Обнаружено 2026-09-02. CT 147 запущен, sshd слушает `*:22`, UFW разрешает 22/tcp из `192.168.1.0/24` и `10.78.0.0/30`, но хост не отвечает ни с ru-vps, ни с cloud-pc (то есть и из самой LAN). `make check` показывает его unreachable. Причина видна в `ip rule` внутри контейнера: 9002: not from all iif lo lookup 2022 Правило заворачивает в таблицу прозрачного прокси (`hermes-tun`) всё, что не пришло с `lo`, включая ответные пакеты входящих соединений. SYN доходит, SYN-ACK уходит в туннель — соединение не устанавливается. Исключения для трафика, пришедшего с eth0, в конфигурации нет. Побочное следствие: `playbooks/pve-hermes-ai.yml` больше не может отработать — Ansible не достучится до хоста. Управлять контейнером можно только через `pct exec` с cloud-pc. Плейбук, судя по всему, отработал один раз и запер себя: включение прокси не рвёт уже установленную сессию, только новые. Не чиню: сервис признан малополезным и заморожен (см. ниже). Но `make check` и `make status` будут показывать его DOWN, и это ожидаемо, а не новая поломка. ### Hermes AI (CT 147) - осознанно заморожен Решение от 2026-09-02: сервис признан малополезным, но контейнер остается. `playbooks/pve-hermes-ai.yml` разворачивает runtime, Docker и transparent TUN proxy через mihomo, но не разворачивает приложение Hermes - это не недоделка, а принятое состояние. Не предлагать "дописать деплой Hermes" как opportunistic cleanup. ### Секреты переехали в корень репозитория Решение от 2026-09-02. `.env` и `.env.example` перенесены из `ansible/` в корень: их потребляет не только Ansible, но и OpenTofu, а держать два файла или ходить в соседний подкаталог неудобно. `ENV_FILE` в `ansible/Makefile` теперь абсолютный (`$(REPO_ROOT)/.env`), поэтому цели работают из любого cwd. Переименование `.env.example` заведено в индекс git, чтобы файл не потерялся при коммите. Оба пути покрыты `.gitignore`. Принято решение хранить пароль `root@pam` в `.env` (а не спрашивать его при запуске). Компромисс осознанный: пользователь `ansible` и так имеет passwordless sudo на нодах, то есть эффективный root в автоматизации уже был; пароль добавляет не новый класс доступа, а секрет с худшими свойствами — он же логин в веб-интерфейс и консоль, его нельзя ограничить по scope и нельзя отозвать иначе, чем сменив пароль root на нодах. Переменные: `PROXMOX_ROOT_USER` (по умолчанию `root@pam`) и `PROXMOX_ROOT_PASSWORD`. Их читают ТОЛЬКО цели `tofu-*`; Ansible ими не пользуется. Если пароль не задан или равен `replace-me`, Tofu идёт токеном `ansible@pve`. Выбранный режим печатается в stderr перед запуском. ### Пилот OpenTofu: граница возможностей API-токена Выполнено 2026-09-02. Каталог `tofu/`, цели `make tofu-*`, подробности и матрица возможностей — в [`../../tofu/README.md`](../../tofu/README.md). Кратко, проверено на живом кластере контейнером VMID 199: - API-токен создаёт LXC со всеми ресурсами, сетью, rootfs, `nesting`, startup и тегами; `plan` идемпотентен. - **Mount point как volume на datastore токеном создаётся.** Это снимает вопрос по `mp0` у gitea: bind mount каталога хоста требует root@pam, а volume — нет. - `keyctl`, `fuse`, `mount` и `device_passthrough` требуют root@pam (HTTP 403). Это ограничение Proxmox, а не Tofu: Ansible упирается в то же самое, поэтому в репозитории уже есть `pct set --features` по SSH и правка `/etc/pve/lxc/.conf`. - Гибрид Tofu+Ansible требует `lifecycle { ignore_changes = [features] }`, иначе Tofu откатывает выставленный извне `keyctl` и ломает Docker в контейнере. - Tofu не умеет ProxyJump: вне LAN нужен SSH-туннель, он встроен в цели `tofu-*`. РЕЖИМ ВЫБРАН 2026-09-02: root@pam по паролю. Проверено на пилоте — `dev0`, `features: fuse=1,keyctl=1,nesting=1` и volume mount point выставляются декларативно, повторный `plan` даёт `No changes`. Значит при переезде каждого сервиса из его `pve-*.yml` можно убирать `pct set --features` и правку `/etc/pve/lxc/.conf`; `ignore_changes` не нужен. Токен, принадлежащий root@pam, ограничение НЕ обходит: `$authuser` при токенной аутентификации равен полному `user@realm!tokenname` (`PVE/HTTPServer.pm:86`), а проверка в `PVE/LXC.pm:1658` сравнивает строку с `root@pam` буквально. Права токену добавлять бесполезно. Единственная альтернатива гибриду — пароль root@pam, то есть системный пароль root узлов Proxmox. Гибридная схема (Tofu создаёт, Ansible доводит по SSH) отвергнута в пользу полной декларативности. Она остаётся запасным вариантом, если пароль root решат из `.env` убрать. Пилотный контейнер VMID 199 `tofu-pilot` снесён (подтверждено 2026-09-02: `pct list` на cloud-pc его не показывает). ## Переход на OpenTofu Пошаговый план миграции provisioning на Tofu вынесен в отдельный документ: [`migration-tofu.md`](migration-tofu.md). Там же порядок сервисов, специфика каждого и процедура отката. Сервис №1 `emergency-bot` переехал 2026-09-02: OLD (VMID 148) остановлен (откат минимум неделю, затем `pct destroy`), боевой — NEW (VMID 151, `tofu/services.tf`), адрес не менялся (192.168.1.32). Реестр обновлён (`vmid: 151`, `provisioner: tofu`), `make validate` и `make status` зелёные. Две находки, важные для следующих сервисов, задокументированы в `migration-tofu.md` (раздел 5.1) и `tofu/README.md`: конфигурационная часть сервиса может жить в отдельном playbook с play, таргетящими другие хосты (не подходит под буквальный `--limit -new`), и провайдер отслеживает `cmode` через блок `console`, который нужно объявлять явно с самого начала. ### Пакетный переезд сервисов 2-7 (параллельно) Решение от 2026-09-02: последовательный переезд по одному сервису слишком медленный. Инвариант «один сервис за раз» уточнён в `migration-tofu.md`: последовательным обязан быть только cutover, а подготовка, создание и конфигурация на временных адресах параллелятся безопасно. **Создано и проверено 2026-09-02.** Один пакетный `tofu apply` (`6 to add, 0 to change, 0 to destroy` — уже переехавший emergency-bot дрейфа не дал): | Сервис | OLD | NEW | Узел | TMPIP | |---|---|---|---|---| | docker-test | 145 | 152 | cloud-pc | 192.168.1.11 | | gitea | 141 | 153 | cloud-pc | 192.168.1.12 | | vaultwarden | 140 | 154 | mini-pc | 192.168.1.13 | | monitoring | 146 | 155 | cloud-pc | 192.168.1.14 | | gyro | 150 | 156 | mini-pc | 192.168.1.15 | | grimmory | 149 | 157 | cloud-pc | 192.168.1.16 | `pct config` всех шести сверен с эталонами: cpu, память, swap, диск, features, startup, `cmode: shell` совпали. Запас на нодах после создания: cloud-pc 12.3 ГиБ RAM и 849 ГиБ на `data`, mini-pc 8.8 ГиБ RAM и 117 ГиБ на `local-lvm`. `make validate` зелёный до и после — новые контейнеры в реестре пока не числятся, это ожидаемо до шага 4.6. Что подтвердилось на живых контейнерах впервые: - **`/dev/fuse` через `features.fuse` эквивалентен старому обходу.** Устройство присутствует в 152, 153, 154, 157 и отсутствует в 156 (у gyro его и не должно быть). Это снимает вопрос, который до сих пор был помечен как непроверенный: пилот проверял `device_passthrough` только на `/dev/net/tun`. - **Bind mount gitea заменён на volume декларативно:** `mp0: data:153/vm-153-disk-1.raw,mp=/opt/gitea/data,size=32G`. Ради этого миграция и затевалась. - **Пустой `features` у gyro сохраняется.** В `pct config 156` строки `features` нет вообще, `nesting` не подкрался. Две ловушки, найденные при этом заходе и почищенные в коде: 1. **`--limit -new` не перенацеливает play, а обнуляет его** — и это общее свойство, а не особенность emergency-bot, как считалось раньше. Проверено `--list-hosts`: ноль хостов во ВСЕХ play всех плейбуков. Заведён переопределяемый таргет `pve_config_target`, поведение по умолчанию не изменилось. Подробности — `migration-tofu.md`, шаг 4.3.9. 2. **Inline `vars:` у группы в inventory-файле проигрывают `group_vars/all/`.** Из-за этого Ansible ходил в новые контейнеры под `ansible@` вместо `root` и получал `Permission denied`. Транспорт вынесен в `group_vars/lxc_migration_new/main.yml`. Попутно `roles/uptime_kuma` научился не падать на хосте, где замороженного юнита `homelab-monitoring` никогда не было: заморозка теперь выполняется только при его наличии. Ещё один потребитель, которого не было в плане: `playbooks/vaultwarden-update.yml` жёстко зашивает VMID 140, включая `vzdump "140"`. Его нужно поправить на шаге 4.6 вместе с реестром. **Конфигурация на временных адресах (шаг 4.3) выполнена для пяти сервисов.** Все прогоны идемпотентны — второй заход даёт `changed=0`. Боевые контейнеры не затронуты: проверено после прогонов, на каждом боевом хосте активен ровно свой сервис. | Сервис | Итог | Проверка | |---|---|---| | docker-test 152 | OK | Docker active, storage-driver `fuse-overlayfs`, `hello-world` проходит, версия Docker совпала с боевой | | gitea 153 | OK | HTTP 200, порт 2222 слушает, volume — отдельная ФС `/dev/loop10` ext4 32 ГиБ | | vaultwarden 154 | OK | HTTP 200, контейнер healthy, база свежая 278 КБ без `icon_cache` | | monitoring 155 | OK | HTTP 302→200, `kuma.db` создан на месте, `/opt/monitoring` отсутствует | | grimmory 157 | OK после починки | оба юнита active, health 200, Flyway 144 — как на боевом | | gyro 156 | OK | конфигурация и cutover завершены 2026-09-02 | Digest'ы образов на всех пяти совпали с боевыми побайтово. **Баг в `pve-grimmory.yml`, найденный при этом.** Боевой адрес был зашит в конфигурационном play трижды: в `ports` compose, в правилах `GRIMMORY-FILTER` и в URL health-check. Первое роняло `grimmory.service` на любом другом адресе. Третье опаснее: задача выполняется на целевом хосте, поэтому после починки биндинга она уходила бы в БОЕВОЙ grimmory, получала 200 и давала ложно-положительный результат. Исправлено через `grimmory_bind_ip: "{{ expected_lan_ip }}"`; на боевом хосте рендер не изменился, `--check --diff` даёт `changed=0`. **Следствие для cutover.** Grimmory — единственный из шести, кто биндится на конкретный адрес. После смены адреса его плейбук обязан быть прогнан заново, иначе compose останется с временным адресом. Добавлено в шаг 4.5.16. **Ещё одно ошибочное утверждение снято.** План предупреждал, что `roles/gyro` пинит SSH host key gitea и после переезда сломается. Проверено дословно: там `state: absent` — задача УДАЛЯЕТ устаревшую запись, а пинится `github.com`, и `gyro_repo_url` ведёт на GitHub. Переезд gitea на gyro не влияет. Legacy `pve-gyro.yml` теперь по умолчанию завершает все три plays, когда registry говорит `provisioner: tofu`; для intentional legacy rollback/recovery нужен явный override `-e pve_gyro_legacy_provisioning_enabled=true`. Это не normal deployment. **Cutover выполнен 2026-09-02 для пяти сервисов.** Все прошли по процедуре раздела 4: свежий бэкап, перенос данных со сверкой, остановка старого контейнера, смена адреса, правка реестра, перегенерация заданий бэкапа. | Сервис | OLD → NEW | Сверка данных | |---|---|---| | docker-test | 145 → 152 | данных нет; Docker на fuse-overlayfs, hello-world проходит | | vaultwarden | 140 → 154 | 1 users, 314 ciphers; integrity_check ok | | monitoring | 146 → 155 | kuma.db совпал по sha256 и размеру; редирект на /dashboard | | gitea | 141 → 153 | 64 repos, 5 users, 203 actions; integrity_check ok | | grimmory | 149 → 157 | book=31 author=30 book_file=40 category=87 reading_sessions=68; Flyway 142/144 без новых миграций | Публичные домены снаружи: `pass.ada-dev.ru` 200, `git.ada-dev.ru` 200, `books.ada-dev.ru` health 200 (OPDS отдаёт штатный `401 Basic realm="Booklore OPDS"` — это требование авторизации, а не поломка; в базе 2 учётки OPDS). `make validate`, `make lint` зелёные, `make backup-jobs` перегенерировал списки VMID сам из реестра. **Смена адреса выполняется провайдером на месте.** Проверено на всех пяти: `0 added, 1 changed, 0 destroyed`. Это снимало главный риск — пересоздание контейнера с данными. Старые контейнеры остановлены и целы: 140, 141, 145, 146, 149. Не удалять минимум неделю, до 2026-09-09. ### Находки cutover, которых не было в плане 1. **Данные gitea никогда не попадали в PBS.** `vzdump` печатает `excluding bind mount point mp0 ('/opt/gitea/data') from backup (not a volume)` — снапшоты `ct/141` содержали только rootfs. Защищал данные лишь offsite-профиль restic. Переезд на volume это чинит: теперь данные в бэкапе. Побочно это самый весомый аргумент за миграцию, которого в плане не было. 2. **`vzdump` падает, пока работает Docker с fuse-overlayfs на rootfs в каталоговом хранилище.** rootfs на `data` снапшоты не поддерживает, vzdump уходит в режим `suspend` с rsync и спотыкается о `var/lib/docker/fuse-overlayfs/.../merged`: Permission denied. У vaultwarden этого нет — его rootfs на `local-lvm`, там настоящий снапшот. Лечится остановкой сервиса перед бэкапом. 3. **Offsite-профиль restic надо переустанавливать на новом контейнере**, если он выполняется внутри LXC. Сделано для vaultwarden и grimmory, проверено прогоном юнита. Профиль gitea переписан с ноды внутрь контейнера (`playbooks/offsite-restic-yadisk.yml`), старый таймер на cloud-pc отключён. 4. **`lost+found` на новом volume ломает restic.** Свежая ext4 создаёт этот каталог с владельцем uid 0 хоста, внутри unprivileged LXC он `nobody:nogroup` и нечитаем; restic отдаёт exit 3, юнит падает каждую ночь, хотя снапшот сохраняется. Добавлен в исключения профиля gitea. 5. **SSH host-ключи gitea перенеслись вместе с данными.** Ключ на порту 2222 совпал с боевым (`AAAAC3NzaC1lZDI1NTE5AAAAIDE+iqCj`), поэтому у git-клиентов `known_hosts` остался валидным. Обновлять пришлось только ключ sshd контейнера на порту 22. 6. **Зависший ControlMaster даёт таймаут на баннере после cutover.** Мастер остаётся живым, но его туннель ведёт в остановленный контейнер, и новые сессии мультиплексируются поверх мёртвого соединения. Лечится `ssh -O exit ` или разовым `-o ControlPath=none`. 7. **`systemctl start grimmory` — no-op.** Юнит `Type=oneshot` с `RemainAfterExit=yes` уже числится активным, нужен `restart`. 8. **`MYSQL_ROOT_PASSWORD` из `.env` не подошёл к MariaDB** (`Access denied for user 'root'@'localhost'`). Restore выполнен пользователем `grimmory`, у него `ALL PRIVILEGES` на свою базу — этого достаточно для DROP/CREATE DATABASE. ### Gyro cutover verified `gyro` завершён как cutover on 2026-09-02: CT 156 живёт на неизменном production IP `192.168.1.35` и provisioned by Tofu. CT 150 остановлен и удерживается как rollback минимум на неделю; его PBS snapshot от `2026-09-02T19:40:56Z` сохранён, цепочку PBS не удаляли. Файл фаервола `156.fw` уже копия `150.fw`, Datacenter firewall по-прежнему disabled. Timer `gyro.timer` active, last oneshot success. Данных для переноса не было, и TMPIP-specific bind/re-run не потребовались. Удаление старого CT или его backup chain не выполнялось. ### Пакетный переезд сервисов 8-10: adguard, mihomo, ovpn-mini Выполнено 2026-09-03 автономно, одной сессией. Этим OpenTofu-переезд всех мигрируемых LXC завершён (осталось: `hermes-ai` заморожен, `pbs` не мигрируется). | Сервис | OLD → NEW | Узел | Боевой IP (не менялся) | Свежий PBS перед стартом | |---|---|---|---|---| | adguard | 144 → **158** | mini-pc | 192.168.1.28 | `ct/144/2026-09-02T21:06:12Z` | | mihomo | 143 → **159** | mini-pc | 192.168.1.27 | `ct/143/2026-09-02T21:12:49Z` | | ovpn-mini | 132 → **160** | mini-pc | 192.168.1.23 | `ct/132/2026-09-02T21:17:55Z` | OLD 144/143/132 остановлены и удерживаются как откат минимум неделю (до ~2026-09-10). PBS-цепочки не трогали; у NEW 158/159/160 сняты первые свои снапшоты (`…T21:37-39Z`), чтобы `backup-audit` был чист сразу. `make validate`, `make lint`, `make tofu-plan` (`No changes`), `make openvpn-check` (7/7) — зелёные. Внешние домены `git/pass/books.ada-dev.ru` — 200. **Смена адреса — провайдером на месте** (`0 added, 1 changed, 0 destroyed`), пересоздания контейнера с данными нет — как и на сервисах 2-7. Специфика и находки перенесены в [`migration-tofu.md`](migration-tofu.md) раздел 5 (пункты 8-10). Кратко: - **adguard:** свежий AdGuard на пустом `conf/` не проходит health-гейт плейбука (setup-wizard, порт 80 не слушает), поэтому данные заливаются ДО config play, а не после. DNS-провал ~2 мин, ночью; ноды на DNS роутера, контейнеры на 1.1.1.1. - **mihomo:** первый боевой `device_passthrough` для `/dev/net/tun` (`dev0: path=/dev/net/tun,mode=0660`). `ru-vps-mihomo-harden.yml` повторно прогонять не нужно. Все зависимые ссылки на .27 остаются валидны. - **ovpn-mini — петля транспорта.** `-F ansible/ssh_config` и `make tofu-*` ходят к PVE через ru-vps → туннель, который терминирует сам ovpn-mini. `pct stop 132` рвёт этот путь и `make tofu-apply` не достаёт API. Разрыв: поднять шлюз на НОВОМ контейнере ещё на TMPIP (одноразовый плейбук), туннель встаёт → tofu-apply работает → сменить адрес → повторный `openvpn-vps-mini.yml` (идемпотентен). Прямой путь к нодам во время провала — `ssh -o ProxyJump=none @192.168.1.{5,10}` из LAN. Совокупный провал туннеля (внешний reverse-proxy) ~10 мин. - **`ssh_config`:** у `ovpn-mini` выставлен `ProxyJump none` (было — через ru-vps). Путь через ru-vps закольцовывался бы на его же туннель. - Три creation-play (`pve-adguard.yml`, `pve-mihomo.yml`, `pve-ovpn-mini.yml`) за гейтом `provisioner != 'tofu'` (`meta: end_play`, override `-e pve__legacy_provisioning_enabled=true`) — как у `pve-gyro.yml`. Их handlers иначе сделали бы `pct start ` на боевом адресе. - **Ошибка сессии:** диагностический `pct start 132` при живом CT 160 дал конфликт IP .23 и провал туннеля на 1-2 мин (восстановилось само). OLD контейнеры трогать нельзя, их `pct config` держит боевой адрес. - Косметика: `polkit.service` на CT 160 в `failed` (`status=217/USER`, минимальный LXC-шаблон). На OpenVPN не влияет, не чинилось (можно `systemctl mask polkit.service` — ничего от него не зависит). **`onboot: 0` на всех остановленных OLD-контейнерах (2026-09-03).** Найдено: 132/140/141/142/143/144/145/146/148/149/150 стояли с `onboot: 1` и держат боевые адреса в `pct config` — ребут любой ноды поднял бы их в конфликт с живыми NEW (151-160). Снято через `pct set --onboot 0`. Это временная мера на окно отката: `pct destroy` (шаг 4.7) уберёт их целиком через неделю. Обратимо (`--onboot 1`) для намеренного продвижения OLD обратно. **Коммиты (2026-09-03).** Всё рабочее дерево (накопление сессий 2026-09-02/03) разложено в 11 тематических коммитов на `main` (`22394cb`..`d2e1e68`), не запушено. Границы — по инициативам: build-env, .env-в-корень+Make, tofu scaffold, validate gate, registry-driven backups, вывод memoir-bot, ru-vps base+кворум+ZeroTier, PBS storage prune, blue-green миграция на Tofu, фикс VMID в update-плейбуках, docs. Несколько файлов (`services.yml`, `status.yml`, `ssh_config`) несут вторичные правки других инициатив — отмечено в телах коммитов. **НЕ сделано (осознанно):** уборка раздела 6 `migration-tofu.md` — удаление `roles/pve_lxc` (ещё используется creation-play пяти сервисов батча 2026-09-02 через `- role: pve_lxc`), стрип/полное гейчение creation plays, правки `architecture.md`/`legacy-warning.md`, расширение `validate.yml` (features, devices, mount points). Это отдельная задача; переездом сервисов 8-10 разблокирована (все 10 на Tofu), но исполнение — не сейчас. Obsidian-заметки (`HomeLab.md`, `Notes/Текущее состояние…`, `Log.md`) по этому переезду ещё не обновлены. ## Исторические планы - [`../../current-task.md`](../../current-task.md) - исторический план Prometheus monitoring. Он не описывает текущую активную архитектуру: Uptime Kuma активен, Prometheus stack заморожен. - [`../../tasks/grimmory-deployment-plan.md`](../../tasks/grimmory-deployment-plan.md) - план и запись развертывания Grimmory; фактическое состояние проверять по inventory, service registry и playbooks. Выявленные риски и deferred work записаны в [`edge-cases.md`](edge-cases.md) и [`legacy-warning.md`](legacy-warning.md), но не считаются утвержденным backlog.