# 003. Декларативный reconcile вместо императивных скриптов - **Статус:** принято - **Контекст/issue:** #15, #24, #27 ## Контекст PoC (`entrypoint.sh`) применяет правила императивно и только при старте: изменения не откатываются, GeoIP не обновляется без рестарта, частичная ошибка оставляет систему в неопределённом состоянии. ## Решение Единый reconcile-цикл: desired state (SQLite) -> diff -> идемпотентное применение -> проверка -> rollback при ошибке. Операции netfilter идемпотентны (проверка `-C` перед `-A`), замена больших наборов атомарна (`ipset create tmp`, `swap`, `destroy`), применение конфига транзакционно (validate -> stage -> apply -> rollback). ## Альтернативы - **Скрипты по событию** (как в PoC) — быстро написать, но невозможно гарантировать сходимость после сбоя. - **Готовый IaC-инструмент** — избыточен, требует внешнего рантайма в образе и не покрывает ipset/dnsmasq. ## Последствия - Старт контейнера = reconcile с нуля: состояние всегда восстановимо. - Нужны тесты на идемпотентность (повторный apply не создаёт дублей) — #41. - Сложнее скрипта: оправдано требованиями FR-2 (смена режимов и правил без рестарта). - Diff reconcile логируется и отдаётся в UI (`/logs`) и audit log.