platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
100
docs/phase-b/B1_CORE_VERIFICATION.md
Normal file
100
docs/phase-b/B1_CORE_VERIFICATION.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# B1 Проверка ядра и внешних зависимостей
|
||||
|
||||
Дата: 2026-02-27
|
||||
Статус: draft
|
||||
Владелец: Engineering
|
||||
|
||||
## 1) Цель
|
||||
- Убедиться, что Go-ядро имеет централизованную логику для работы с nftables, policy routing, systemd, cgroup и SmartDNS, и что эти зависимости отражены в конфигурациях/документации.
|
||||
- Выявить потенциальные отверстия (например, части логики, которые напрямую обращаются к shell-скриптам `routes_update`, `autoloop`, `trace`) перед тем, как стартовать задачи для веба.
|
||||
|
||||
## 2) Критерии завершения
|
||||
- Описание работы ключевых модулей ({`routes_update.go`, `traffic_mode.go`, `events_bus.go`, `watchers.go`, `resolver.go`, `smartdns_runtime.go`, `vpn_handlers.go`}) подготовлено, включая внешние зависимости (nftables, SmartDNS, systemd, AdGuard VPN API).
|
||||
- Сформулирован список требований (root, обязательные сервисы, долгие операции, возможные блокировки) на случай запуска через веб-консоль.
|
||||
- Уточнено, какие данные уже сохраняются в `stateDir`, `appmark` и `routes` файлах, чтобы веб мог отображать информацию без дополнительного API.
|
||||
|
||||
## 3) Задачи
|
||||
- Пройти `routes_update.go`, `routes_units.go`, `routes_cache.go`, `nft_update.go` и описать, как обновляются маршруты и кэшируются состояния.
|
||||
- Проанализировать `traffic_mode.go`, `traffic_appmarks.go`, `traffic_app_profiles.go`, `traffic_audit.go`: что именно меняют (fwmark, cgroup, nft sets) и какие проверки выполняются.
|
||||
- Прописать взаимодействие с DNS/SmartDNS (`dns_settings.go`, `smartdns_runtime.go`, `smartdns_wildcards_store.go`, `resolver.go`), включая init/restore/caching жизненный цикл.
|
||||
- Проверить `watchers.go`, `events_bus.go`, `trace_handlers.go`: какие события стримятся, какие веб-интерфейсы они смогут потреблять, и нужны ли дополнительные фильтры или буферизация.
|
||||
- Отметить, какие части кода требуют root-доступ и как это влияет на веб (например, API будет работать под сервис-аккаунтом и выполнять nft/update через internal API, но сама служба должна запускаться с нужными привилегиями).
|
||||
- Собрать стартовый список фактов по каждому модулю: с какими `systemd` unit'ами, `nft` set'ами, SmartDNS/AdGuard VPN API и `stateDir` работает, и какие ограничения это накладывает.
|
||||
- Задокументировать потенциальные точки отказа (недоступность nftables/systemd, SmartDNS/AdGuard API) и предложить стратегию контроля ошибок для веб-интерфейса.
|
||||
|
||||
## 4) Модули и зависимости
|
||||
|
||||
### `routes_update.go`
|
||||
- Основной контроллер обновления маршрутов: строит policy routes, nftables-цепочки `agvpn`/`agvpn4`/`agvpn_dyn4`, запускает `runResolverJob`, пишет список доменов/IP в `stateDir` и сохраняет `status.json`.
|
||||
- Поддерживает прогресс через `events.push("routes_nft_progress", ...)`, пишет heartbeat-файлы и требует доступа к `iproute2`, `nft`, `stateDir`, `trace.log` и воркеру резольвера.
|
||||
- Обрабатывает `force`/`auto` конфигурации из `TrafficModeState` и фиксирует `trafficEval` в статусе, что важно для отображения в вебе.
|
||||
|
||||
### `routes_units.go`, `routes_cache.go`, `nft_update.go`
|
||||
- `routes_units.go` разрешает `systemd` имя (`routesServiceUnitName`, `routesTimerUnitName`) и используется в `/routes/service`/`timer`.
|
||||
- `routes_cache.go` кеширует `domains`, `ips`, `domains` state; можно использовать для расчёта прогресса и восстановления.
|
||||
- `nft_update.go` держит “умный” апдейтер, который управляет правилами и sets; важно документировать, какие команды выполняются и как проверяется успех (`runNFTUpdate`, `progressCb`) для веба.
|
||||
|
||||
### `traffic_mode.go` + `traffic_appmarks*`
|
||||
- Управляют состоянием (`stateDir/state-traffic-mode.json`), применяют флаги fwmark и policy rules (`applyTrafficMode`), читают/пишут список принудительных субнетов/UID/cgroup.
|
||||
- `traffic_appmarks.go` работает с cgroup v2, создаёт маркеры `MARK_APP`/`MARK_DIRECT` и задаёт TTL (через systemd scopes) для runtime управления per-app traffic.
|
||||
- `traffic_app_profiles.go` хранит профили в `stateDir` и предоставляет CRUD, что пригодится вебу для создания shortcut-профилей.
|
||||
- `traffic_audit.go` проверяет состояние nft/route и формирует `TrafficAudit` issues, полезные для мониторинга.
|
||||
|
||||
### `dns_settings.go` + `resolver.go`
|
||||
- Хранит конфигурацию `dns-upstreams.conf`, режим (`dns_mode.json`) и pool upstreams; отвечает за benchmark, SmartDNS control (`smartdns` service start/stop) и режим `ViaSmartDNS`.
|
||||
- `resolver.go` запускает Go-резольвер, читает `domains.txt`, `meta-special.txt`, `static-ips.txt`, использует кеши (`domain-cache.json`, `ptr-cache.json`) и пишет результат в `stateDir`.
|
||||
- Сервисы SmartDNS (`smartdns_runtime.go`, `smartdns_wildcards_store.go`) управляют дополнительными wildcard-базами, runtime stats и prewarm, что потребует отображения статуса SmartDNS в веб.
|
||||
|
||||
### `vpn_handlers.go` + `vpn_login_session.go`
|
||||
- Интеграция с AdGuard VPN API: `autoloop`, `autoconnect`, `locations`, `set location`, `logout` и status založený na HTTP-запросах к локальному `adguardvpn` сервису.
|
||||
- `vpn_login_session.go` создаёт PTY-сессию (через `runCommand` + `systemd-run --user`?), сохраняет состояния в `loginStatePath` и выпускает события (`events.push`) для SSE; вебу потребуется поддерживать эти пользователи.
|
||||
|
||||
### `events_bus.go`, `watchers.go`, `trace_handlers.go`
|
||||
- `startWatchers` запускает наблюдение за `status.json`, `loginState`, autoloop логом, `trace.log`, state traffic appmarks TTL и systemd unitами (routes service/timer, VPN unit, SmartDNS).
|
||||
- Все watcher-изменения отправляются в `events` и поступают клиенту через `handleEventsStream`, что даёт вебу источник realtime данных.
|
||||
- `trace_handlers.go` читает `trace.log`, `trace-json` и принимает append, что позволяет вебу показывать live trace и записывать дополнительные строки.
|
||||
|
||||
### `routes_handlers.go`
|
||||
- Управление systemd-unit'ами (`routes_service`, `routes_timer`), ручной rollback/clear, fixing policy route, переключение режимов/advanced config.
|
||||
- Служит фасадом для CLI (как `routes-update`, `routes-clear`, `autoloop`), следовательно API может использоваться как `POST /api/v1/routes/service` и `POST /api/v1/routes/update`.
|
||||
|
||||
### Корневые ограничения и привилегии
|
||||
- `routes_update`/`routes_handlers` запускаются как root (nft, ip, systemctl); веб-интерфейс должен вызывать API, а не повторять команды, сохраняя сервис privileged.
|
||||
- SmartDNS требует запуска `smartdns-local.service`; VPN команды ждут доступ к AdGuard VPN (возможно, `adguardvpn.service`).
|
||||
- `stateDir` и `domains` файлы должны быть доступны API, а вебу важно понять, какие endpoints кешируют/читают эти файлы. Например, `routes timer enable` хранится в `routes_timer_state.json`.
|
||||
- Потенциальные точки отказа:
|
||||
- `nft` команды падут, если kernel не поддерживает nftables или service не запущен — API должен возвращать `CmdResult` с `stderr` и `exitCode`.
|
||||
- `systemd` unit может быть недоступна (не установлен `routes-service`), мониторинг через watchers должен отправлять `unit_state_changed`.
|
||||
- SmartDNS runtime может не стартовать: нужно отразить ошибку через `events.push("smartdns_error"... )` и вернуть `CmdResult.ok=false`.
|
||||
- AdGuard VPN API или PTY могут быть недоступны; API должен возвращать ошибки и веб должен приступать к повторной попытке.
|
||||
|
||||
## 5) Матрица зависимостей
|
||||
|
||||
| Модуль | http/cli | Зависимости | Состояния/файлы | Потенциальные ошибки |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `events_bus.go`, `watchers.go`, `events_handlers.go` | `/api/v1/events/stream` | in-memory queue, watcher goroutines | `status.json`, `login_state.json`, `trace.log`, systemd unit polling, `autoloop.log` | SSE disconnected, poll errors, buffer overflow |
|
||||
| `routes_update.go` | `POST /api/v1/routes/update`, CLI `routes-update` | `iproute2`, `nft`, `runResolverJob`, `SmartDNS` | `stateDir/{domains,ips}*`, `status.json`, `trace.log`, `heartbeat` | Нет VPN-интерфейса, резольвер падает, `nft` пишет ошибки |
|
||||
| `routes_units.go` | `/api/v1/routes/service`, `/routes/timer`, CLI `routes-clear` | `systemd` (service/timer names) | `routes_timer_state.json`, unit name env | Unit не установлена, systemctl возвращает error |
|
||||
| `routes_cache.go`, `nft_update.go` | внутренняя логика routes update | `nft`, temp files | `/var/run/selective-vpn` temp files | `nft` прогресс в stderr, temp-файлы не создаются |
|
||||
| `traffic_mode.go` + `traffic_appmarks*` | `/api/v1/traffic/*` | `nft`, policy routing, `cgroup v2`, `systemd` | `stateDir/traffic-mode.json`, `appmarks.json`, cgroup scopes | Некорректная конфигурация, `nft` не применяет правила |
|
||||
| `traffic_app_profiles.go` | `/api/v1/traffic/app-profiles` | файловые операции | `stateDir/app-profiles.json` | Файл повреждён, не сохраняется |
|
||||
| `dns_settings.go` + `resolver.go` | `/api/v1/dns-*` | `resolv.conf`, SmartDNS binary | `dns-upstreams.conf`, `dns_mode.json`, `domain-cache.json`, `ptr-cache.json` | Нет upstream, benchmark таймаут |
|
||||
| `smartdns_runtime.go`, `smartdns_wildcards_store.go` | `/api/v1/smartdns/*` | `smartdns-local.service`, wildcard files | `smartdns.conf`, `wildcards.json` | Service fail, wildcard file read-only |
|
||||
| `vpn_handlers.go`, `vpn_login_session.go` | `/api/v1/vpn/*` | AdGuard VPN API, PTY, systemd-user | `login_state.json`, `autoloop.log` | API недоступен, PTY не стартует |
|
||||
| `trace_handlers.go` | `/api/v1/trace*` | доступ к `trace.log` | `trace.log` | Файл недоступен, append write error |
|
||||
|
||||
Эта матрица показывает, какие компоненты требуют привилегий и как ошибка отражается на UI. В Phase D можно перенести эти строки в таблицу `web-ready` и отметить `status` (готово/требует/blocked).
|
||||
|
||||
### Технические state-файлы и кеши
|
||||
- `stateDir` (по умолчанию `/var/lib/selective-vpn`) содержит:
|
||||
- `domains.txt`, `ips-*.txt`, `ipmap-*.txt` — сериализация выходных данных resolver, используются trace/expand logic.
|
||||
- `state-traffic-mode.json`, `appmarks.json`, `app-profiles.json` — основные состояния traffic mode/appmarks/profiles, читаются `traffic_mode.go`, `traffic_appmarks.go`, `traffic_app_profiles.go`.
|
||||
- `domain-cache.json`, `ptr-cache.json` — кеш resolver, обновляется из `runResolverJob` и влияет на `routes_update` (limit/ttl).
|
||||
- `status.json`, `heartbeat` — пишутся `routes_update`. `watchStatusFile` читает file, `events.push("status_changed")` доставляет web info (`iface`, `table`, `healthy`).
|
||||
- `login_state.json` — создаётся `vpn_login_session.go`, SSE `login_state_changed` отражает current state; при `CmdResult.ok=false` пишется `error` в file/event.
|
||||
- `trace.log` — задействован `trace_handlers.go` и `watchFileChange`, SSE `trace_changed` парсит tail. `trace_append` endpoint пишет новые строки (CmdResult). Web должна ограничить size (<=1<<20) и показывать stderr/exitCode.
|
||||
|
||||
### Watchers и события
|
||||
- `watchStatusFile`, `watchLoginFile`, `watchAutoloop`, `watchTrafficAppMarksTTL`, `watchSystemdUnit*` запускаются на `startWatchers` и публикуют события (`status_changed`, `login_state_changed`, `autoloop_status_changed`, `unit_state_changed`, `appmarks_ttl`).
|
||||
- `events_bus` буферизует последние `SVPN_EVENTS_CAP` событий (config via env). SSE `events/stream` читает их `since`/`Last-Event-ID`. При ошибок (blocked mutex, dropped events) нужно логировать `selective-vpn-api` и отображать `status_error`.
|
||||
- `events.push` используется для `routes_nft_progress`, `smartdns`, `trace_changed`. Вебу важно знать ключи, чтобы фильтровать события по статус/trace/health.
|
||||
76
docs/phase-b/B2_RESOLVER_DIFF_AND_IMPROVEMENT_PLAN.md
Normal file
76
docs/phase-b/B2_RESOLVER_DIFF_AND_IMPROVEMENT_PLAN.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# B2 Resolver: разница ролей и план улучшения
|
||||
|
||||
Дата: 2026-03-07
|
||||
Статус: planned
|
||||
Владелец: Engineering
|
||||
|
||||
## 1) Цель
|
||||
- Зафиксировать, чем отличается текущий системный resolver selective-vpn от DNS-модуля `sing-box`.
|
||||
- Зафиксировать roadmap доработки нашего resolver, чтобы он оставался основой для `selective`-маршрутизации.
|
||||
|
||||
## 2) Роли компонентов
|
||||
- `System resolver` (Go + SmartDNS + nftset):
|
||||
- источник истины для PBR (`routes/update`, nft sets, wildcard/static fallback),
|
||||
- системный контроль резолва для selective-режима.
|
||||
- `SingBox DNS`:
|
||||
- DNS-часть transport-профиля `sing-box`,
|
||||
- управляет резолвом внутри конкретного engine/profile.
|
||||
|
||||
Правило слоя:
|
||||
- В текущей архитектуре authoritative для маршрутизации остаётся системный resolver.
|
||||
- `SingBox DNS` используется как transport-level capability и не заменяет системный PBR resolver.
|
||||
|
||||
## 3) Матрица различий
|
||||
|
||||
| Область | System resolver (текущий) | SingBox DNS |
|
||||
| --- | --- | --- |
|
||||
| Основная роль | Системная selective-маршрутизация | DNS внутри transport engine |
|
||||
| Связь с nft/PBR | Прямая (`agvpn4/agvpn_dyn4`, `routes/update`) | Непрямая, через поведение engine |
|
||||
| Wildcard runtime | Да (SmartDNS nftset + merge в `routes/update`) | Нет прямого контроля системных nft sets |
|
||||
| Static fallback | Да (`static-ips.txt`, policy merge) | Через raw/typed config профиля |
|
||||
| Единая картина для всех клиентов | Да, через Go state/API | Нет, scoped к конкретному профилю |
|
||||
| Риск рассинхрона | Низкий при single source of truth | Высокий, если использовать как отдельный source of truth |
|
||||
|
||||
## 4) Основные gap-ы текущего resolver
|
||||
- Недостаточная наблюдаемость по качеству резолва (latency/error-rate/NX/servfail per upstream).
|
||||
- Нет отдельного endpoint'а с полным health snapshot resolver pipeline.
|
||||
- Нужна более строгая TTL/refresh-стратегия для mixed источников (cache + wildcard runtime + static).
|
||||
- Нужен формализованный negative caching и rate-limit на проблемные домены при деградации upstream.
|
||||
- Нужна формализация owner-map для доменов в multi-client сценариях (без дублей и гонок).
|
||||
|
||||
## 5) План улучшения resolver (приоритеты)
|
||||
|
||||
### R1 Надёжность и консистентность
|
||||
- Ввести unified resolver state snapshot (`updated_at`, `stale`, `refresh_in_progress`, `last_error`, `next_retry_at`, counters).
|
||||
- Усилить single-flight на expensive refresh-path и добавить доменный backoff.
|
||||
- Нормализовать merge-порядок источников: `static fallback -> wildcard runtime -> resolver cache`.
|
||||
|
||||
### R2 Качество резолва
|
||||
- Добавить adaptive upstream scoring (latency/success/NX/timeout/servfail).
|
||||
- Добавить sticky-preferred upstream с безопасным failover.
|
||||
- Ввести отдельную политику negative cache TTL для NXDOMAIN/SERVFAIL.
|
||||
|
||||
### R3 Наблюдаемость и API
|
||||
- Добавить API snapshot для resolver health:
|
||||
- `GET /api/v1/resolver/status` (target),
|
||||
- `POST /api/v1/resolver/refresh` (target trigger),
|
||||
- `GET /api/v1/resolver/upstreams` (target metrics).
|
||||
- Добавить SSE-события:
|
||||
- `resolver_status_changed`,
|
||||
- `resolver_upstream_degraded`,
|
||||
- `resolver_refresh_completed`.
|
||||
|
||||
### R4 Multi-client безопасность
|
||||
- Ввести `domain_owner_map` и conflict-detection до применения policy.
|
||||
- Гарантировать, что один domain-selector не получает двух владельцев без explicit override.
|
||||
- Синхронизировать resolver ownership с transport policy revision.
|
||||
|
||||
## 6) Критерии готовности B2
|
||||
- Есть документированная и реализуемая разница ролей `system resolver` vs `sing-box DNS`.
|
||||
- Resolver имеет прозрачный health/status API и наблюдаемость по upstream.
|
||||
- При деградации upstream UI получает stale-safe состояние, а не "пустой провал".
|
||||
- В multi-client режиме домены не уходят в двойное владение без подтверждённого override.
|
||||
|
||||
## 7) Ограничения этапа
|
||||
- В этом этапе не переводим управление маршрутизацией на `SingBox DNS`.
|
||||
- `DNSTT/Phoenix` DNS-аспекты не расширяются в UI до завершения `SingBox` API/GUI трека.
|
||||
50
docs/phase-b/B4_RUNTIME_DEPENDENCIES_AND_PREFLIGHT.md
Normal file
50
docs/phase-b/B4_RUNTIME_DEPENDENCIES_AND_PREFLIGHT.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# B4 Runtime Dependencies And Preflight
|
||||
|
||||
## Важно
|
||||
- `go.mod` хранит только Go-модули (библиотеки, которые импортируются в коде).
|
||||
- Внешние сервисы/бинарники (`systemd`, `nft`, `ip`, `sing-box`, `dnstt-client`, `phoenix-client`) не являются Go-модулями и не должны фиксироваться в `go.mod`.
|
||||
|
||||
## Текущие Go-зависимости (по `go.mod`)
|
||||
- `github.com/cenkalti/backoff/v4`
|
||||
- `github.com/creack/pty`
|
||||
|
||||
## Runtime-зависимости ядра (вне go.mod)
|
||||
|
||||
### Required (core path)
|
||||
- `systemctl`
|
||||
- `nft`
|
||||
- `ip`
|
||||
- `curl`
|
||||
- `/usr/local/bin/adguardvpn-cli-root`
|
||||
|
||||
### Required service units (current production path)
|
||||
- `singbox@.service`
|
||||
|
||||
### Recommended / optional
|
||||
- `nsenter` (предпочтительный exec-mode для netns)
|
||||
- `wget` (fallback для части egress probe)
|
||||
- `ps`
|
||||
- `ipset`
|
||||
|
||||
### Optional by enabled transport kind
|
||||
- `sing-box` (`/usr/local/bin/sing-box` или `/usr/bin/sing-box`)
|
||||
- `dnstt-client` (`/usr/local/bin/dnstt-client` или `/usr/bin/dnstt-client`)
|
||||
- `phoenix-client` (`/usr/local/bin/phoenix-client` или `/usr/bin/phoenix-client`)
|
||||
|
||||
### Optional service units (зависят от deployment-профиля)
|
||||
- `adguardvpn-autoconnect.service`
|
||||
- `smartdns-local.service`
|
||||
- `selective-vpn2@.service`
|
||||
- `sing-box.service` (legacy/compat)
|
||||
- `dnstt-client.service`
|
||||
- `phoenix-client.service`
|
||||
|
||||
## Preflight-check
|
||||
- Скрипт: `scripts/check_runtime_dependencies.sh`
|
||||
- Режимы:
|
||||
- `scripts/check_runtime_dependencies.sh` — проверка required + warning по optional.
|
||||
- `scripts/check_runtime_dependencies.sh --strict` — fail если есть missing/warning.
|
||||
|
||||
## Зачем это перед рефакторингом
|
||||
- Убираем смешение понятий: Go-зависимости отдельно, runtime/system-зависимости отдельно.
|
||||
- Перед декомпозицией (`F1.*`) быстро проверяем среду и снижаем ложные регрессии.
|
||||
@@ -0,0 +1,61 @@
|
||||
# B5 SingBox Template Migration And Rollback Runbook
|
||||
|
||||
## Цель
|
||||
Операционный runbook для миграции `legacy singbox-<id>.service` к template-модели `singbox@<id>.service` и для аварийного отката при инциденте деплоя.
|
||||
|
||||
## Контекст текущей модели
|
||||
- Целевой runtime: `singbox@.service` + per-instance drop-in `singbox@<id>.service.d/10-selective-vpn.conf`.
|
||||
- One-shot миграция legacy unit выполняется автоматически в pre-action (`start/restart`) для `kind=singbox`.
|
||||
- Safety guard: legacy unit обрабатывается только при ownership marker `Environment=SVPN_TRANSPORT_ID=<client_id>`.
|
||||
|
||||
## Preflight перед деплоем
|
||||
1. Проверить runtime-зависимости:
|
||||
- `scripts/check_runtime_dependencies.sh`
|
||||
2. Проверить strict-режим (опционально для CI/релиза):
|
||||
- `scripts/check_runtime_dependencies.sh --strict`
|
||||
3. Проверить наличие template unit:
|
||||
- `systemctl list-unit-files 'singbox@.service' --no-legend`
|
||||
|
||||
## Управление миграцией
|
||||
- Отключить авто-миграцию для профиля:
|
||||
- `config.singbox_legacy_unit_migrate=false`
|
||||
- Включить dry-run без изменений unit-файлов:
|
||||
- `config.singbox_legacy_unit_migrate_dry_run=true`
|
||||
- Поведение dry-run:
|
||||
- backend пишет trace/stdout о планируемом `legacy -> template` переходе,
|
||||
- `stop/disable/remove` не выполняются.
|
||||
|
||||
## Нормальный migrate-путь
|
||||
1. Обновить API-бинарь и перезапустить `selective-vpn-api.service`.
|
||||
2. На `start/restart` конкретного SingBox-клиента backend:
|
||||
- валидирует ownership legacy unit,
|
||||
- выполняет `stop + disable + remove` legacy unit,
|
||||
- выполняет `daemon-reload + reset-failed` для legacy unit,
|
||||
- запускает template instance `singbox@<id>.service`.
|
||||
3. Проверить:
|
||||
- `systemctl status 'singbox@<id>.service'`
|
||||
- `systemctl list-unit-files 'singbox-*' --no-legend` (legacy не должен появляться как managed unit)
|
||||
|
||||
## Аварийный rollback (template -> legacy)
|
||||
Важно: текущий прод-код нормализует SingBox clients к `config.unit=singbox@.service`, поэтому rollback делается на уровне release rollback (предыдущий API build + восстановление unit/state).
|
||||
|
||||
1. Зафиксировать инцидент и остановить изменения policy:
|
||||
- временно не выполнять `start/restart/switch` через GUI/API.
|
||||
2. Откатить API до предыдущего релиза (где legacy path поддерживался штатно).
|
||||
3. Восстановить `transport-clients` state из backup/снапшота релиза (если в инциденте state был изменён).
|
||||
4. Восстановить legacy unit-файлы `singbox-*.service` из backup среды (если удалены).
|
||||
5. Выполнить:
|
||||
- `systemctl daemon-reload`
|
||||
- `systemctl reset-failed`
|
||||
6. Поднять нужные legacy unit и проверить egress/health.
|
||||
|
||||
## Проверка после rollback
|
||||
- `systemctl is-active selective-vpn-api.service`
|
||||
- `systemctl is-active 'singbox-<id>.service'` (для rollback-релиза)
|
||||
- API smoke:
|
||||
- `curl -fsS http://127.0.0.1:8080/healthz`
|
||||
- `curl -fsS http://127.0.0.1:8080/api/v1/transport/clients`
|
||||
|
||||
## Примечание
|
||||
- Legacy `sing-box.service` остаётся только как compat-артефакт окружения.
|
||||
- Production path для новых профилей: только `singbox@.service`.
|
||||
Reference in New Issue
Block a user