# Архитектура ## Границы ответственности Hysteria2 URI содержит переносимые реквизиты: auth, hostname/ports, SNI, `insecure`, тип obfs и obfs password. Новые production-установки HY2XS получают Gecko URI. Salamander остаётся явно согласуемым compatibility fallback. `policy.json` хранит локальную инфраструктуру: `eth0`, `eth1`, TUN CIDR и MTU, route exclusions, таблицу 2022, marks, NFQUEUE, DNS, bandwidth и healthcheck. Renderer никогда не патчит старый JSON, а строит полный deterministic config из typed model. ## Поток применения ```text URI + policy │ ├─ strict validation ├─ exact 1.14.0 + build-tag gate ├─ renderer_1_14_0 └─ sing-box check на private candidate │ current отсутствует ── current существует │ │ │ provenance gate │ │ │ unchanged ───── changed │ │ │ │ ▼ ├─ versioned last-good backup │ state.json │ └────────────────────────────────┼─ atomic replace ├─ systemctl restart └─ bounded healthcheck ``` Первое применение после чистой установки не создаёт `last-good`: предыдущего управляемого конфига нет. При следующих изменениях backup разрешён только если SHA установленного config совпадает со state этого же релиза и sing-box 1.14.0. Побайтовое совпадение с candidate не доказывает ownership: существующий config без такого state отклоняется и не «усыновляется» контроллером. ## Gecko и DNS Renderer добавляет для Gecko `min_packet_size=512` и `max_packet_size=1200`. Эти параметры являются частью локального HY2XS compatibility profile и не читаются из URI. Renderer явно задаёт штатный для sing-box 1.14.0 `dns_mode=hijack`. Поле `dns_address` не задаётся: sing-box выводит IPv4 DNS address `172.19.0.2` как следующий адрес после `172.19.0.1/30`, автоматически связывает его с DNS module и при `auto_redirect=true` устанавливает nftables DNAT для TCP/UDP 53. Поэтому отдельного route action `hijack-dns` в generated config нет. Bootstrap UDP DNS привязан к `eth0`; remote DoH использует detour `hy2-out`. Собственный outbound sing-box исключается из auto-redirect по output mark, что необходимо подтвердить privileged Linux acceptance без bootstrap recursion. Platform hijack не создаёт DNS listener на локальном LAN-адресе шлюза: трафик к такому адресу попадает в kernel `local` table раньше пользовательских rules. Если DHCP должен выдавать сам gateway как DNS server, требуется отдельный listener/service, который не входит в контракт 0.2.0. ## Endpoint routing Hysteria outbound хранит hostname и `bind_interface=eth0`, а его `domain_resolver` указывает на bootstrap DNS через `eth0`. Публичный IP сервера не добавляется в `route_exclude_address`; смена A-записи не требует изменения policy. ## Файловая модель ```text /etc/vpn-egress/ root:root 0700 ├── policy.json root:root 0600 └── hysteria2.uri root:root 0600 /var/lib/vpn-egress/ root:root 0700 ├── state.json без секретов, 0600 ├── last-good.json содержит secrets, 0600 ├── last-good.meta.json версия и SHA-256, 0600 └── backups/ config + соседний .meta, 0700/0600 /etc/sing-box/config.json root:root 0600 ``` Metadata не обеспечивает доверие от root-компрометации. Её задача — исключить случайное смешивание релизов и повреждённых backup-файлов. ## Граница установки 0.2.0 не содержит кода миграции 0.1.0. `debian/preinst` отклоняет установку поверх любого другого релиза, а `postinst` отклоняет legacy policy/state или unmanaged `/etc/sing-box/config.json`. Same-release reinstall 0.2.0 допускается, если state и config согласованы.