1167 lines
188 KiB
Markdown
1167 lines
188 KiB
Markdown
# Execution Tracker
|
||
|
||
Дата обновления: 2026-03-15
|
||
Владелец: Engineering
|
||
|
||
## Статусы фаз
|
||
- [x] A. Аудит API и HTTP-маршрутов
|
||
- [~] B. Проверка ядра и внешних зависимостей
|
||
- [~] C. Подготовка к веб-совместимости
|
||
- [~] D. Документирование и итоговые проверки
|
||
- [~] E. Дизайн multi-client PBR и anti-conflict guardrails
|
||
- [~] F. Рефакторинг и модульность (декомпозиция крупных файлов GUI/API/Go)
|
||
|
||
## Порядок реализации (фикс)
|
||
- 1. Сначала полностью закрываем backend-интеграцию transport-клиентов (`singbox`, `dnstt-client`, `phoenix`) в Go-ядре.
|
||
- 2. Затем переиспользуем backend-контракт в desktop GUI (без дублирования бизнес-логики).
|
||
- 3. Веб-прототип (`Vite + React + TS`) начинаем только после завершения backend+GUI этапа.
|
||
|
||
## Фокус текущего этапа (freeze)
|
||
- Desktop-first: сейчас работаем только по `SingBox` вкладке и `SingBox` Go API.
|
||
- `DNSTT` и `Phoenix`: backend foundation/e2e уже зафиксированы, UI-вкладки и протокольные экраны для них в этом этапе не развиваем.
|
||
- Приоритет реализации:
|
||
- временно сужаем активный backend scope до `E3.3/E3.6`:
|
||
- единый `interface orchestrator` path для всех mutating transport-операций,
|
||
- `validate -> plan -> confirm -> apply -> health-check -> commit` без частично подтверждённого runtime;
|
||
- `E5.4` GUI-протоколы внутри отдельной вкладки `SingBox` не расширяем, пока `E3.3/E3.6` не доведены до стабильного backend-state.
|
||
|
||
## Текущие шаги
|
||
- Step A1: [x] каталогизация маршрутов и сопоставление с Go-обработчиками
|
||
- Step A2: [x] валидация GUI как клиента (api_client.py, dashboard_controller)
|
||
- Step B1: [~] оценка nftables/systemd/SmartDNS зависимостей (`routes_*`, `traffic_*`, `dns_*`, `smartdns_runtime`)
|
||
- Step B2: [~] запись требований root/long-running/состояний для веба
|
||
- Step B3: [~] resolver hardening plan: зафиксировать разницу `system resolver` vs `singbox DNS` и закрыть backlog улучшений
|
||
- Step C1: [~] ревью binding и SSE (`server.go`, `events_bus.go`, `trace_handlers.go`)
|
||
- Step C2: [x] запись потребностей по CORS/auth/token для веб-интерфейса
|
||
- Step C3: [x] зафиксирован стек web prototype: `Vite + React + TypeScript` (SPA, без Next.js на MVP-этапе)
|
||
- Step C4: [x] создан web foundation-модуль `selective-vpn-web/` (routing + query + SSE connectivity, без mutating controls)
|
||
- Step D1: [x] матрица endpoint → handler → dependencies + web-ready
|
||
- Step D2: [x] чеклист проверок (curl, SSE, VPN login)
|
||
- Step D3: [~] последовательность запуска multi-client (web + iOS + Android) зафиксирована
|
||
- Step D4: [~] зафиксирован транспортный интеграционный бэклог (`sing-box client`, `dnstt-client`, `phoenix->slipstream`)
|
||
- Step D4.1: [x] внедрён минимальный общий transport backend-контракт в Go (`lifecycle + health + metrics + unified errors + capabilities contract hints`)
|
||
- Step D4.2: [~] внедрены backend-адаптеры foundation (`mock/systemd`) + template `exec_start` + restart/watchdog tuning + unit hardening + `runtime_mode` foundation + packaging updater/rollback foundation для `singbox|dnstt|phoenix`
|
||
- Step D4.2.8: [x] e2e backend-проверки по клиентам (`singbox -> dnstt(+ssh) -> phoenix`) через API lifecycle/runbook
|
||
- Step D4.2.9: [x] добавлен операционный runbook helper (`scripts/transport_runbook.py`) + smoke (`tests/transport_runbook_cli_smoke.sh`)
|
||
- Step D4.2.10: [x] добавлены real-systemd e2e и backend-cleanup unit artifacts при delete client
|
||
- Step D4.2.11: [x] добавлены production-like e2e (template commands + packaging profiles `bundled|system`)
|
||
- Step D4.2.12: [x] добавлен recovery runbook (health->restart->recheck->provision/start fallback + diagnostics)
|
||
- Step D4.2.13: [x] внедрён `singbox` bootstrap-bypass в Go backend (`transport`): endpoint `/32` host-routes в `table agvpn` через `main` default-route перед `start/restart`, cleanup на `stop`
|
||
- Step D4.2.14: [x] добавлен `netns` test-contour для transport backend (`setup/cleanup + NAT + optional strict mode`)
|
||
- Step D4.2.15: [x] миграция legacy DNS-конфига `sing-box` (`address -> type/server`) в `Provision()` + backup + strict-mode
|
||
- Step D4.3: [x] зафиксирована матрица совместимости `web + iOS + Android` (единый transport control-plane контракт + platform constraints)
|
||
- Step V1: [x] стабилизация UX списка VPN локаций (async + cache + auto-apply + лёгкий manual refresh trigger)
|
||
- Step V2: [x] добавить "умный поиск" в списке локаций (по набору символов без отдельной строки поиска)
|
||
- Step E1: [x] подготовлен дизайн multi-client PBR (`docs/phase-e/E1_MULTI_CLIENT_PBR_DESIGN.md`)
|
||
- Step E2: [x] спроектирован API `transport/*` (clients/policies/validate/apply/conflicts)
|
||
- Step E2.2: [x] реализован dry-run validator конфликтов (`ownership`, `cidr_overlap`, `unknown_client`)
|
||
- Step E2.3: [x] реализован apply flow (`base_revision`, `confirm_token`, snapshot rollback, SSE events)
|
||
- Step E3.1: [x] усилен allocator (`mark/pref` резервы, auto re-balance, deterministic restore)
|
||
- Step E3.2: [x] добавлен endpoint-level rollback (`/transport/policies/rollback`)
|
||
- Step E3.3: [x] реализовать единый interface orchestrator в Go (`create/bind/start/stop/cleanup` для engine-инстансов, netns/table sync без конфликтов)
|
||
- Step E3.3.0: [x] зафиксирован execution roadmap мультиинтерфейса (`docs/phase-e/E3_MULTI_INTERFACE_EXECUTION_PLAN.md`)
|
||
- Step E3.3.1: [x] foundation-этап: `iface_id` в `transport client` + state `transport-interfaces.json` + `GET /api/v1/transport/interfaces` (без изменения data-plane)
|
||
- Step E3.3.2: [x] внедрить interface orchestrator lock+mapping (`iface_id -> runtime iface/netns/table`) с единым lifecycle path
|
||
- Step E3.3.2.1: [x] добавлен per-`iface_id` lock manager для mutating lifecycle/provision операций (`start/stop/restart/provision`) в transport-контуре; операции одного интерфейса сериализуются
|
||
- Step E3.3.2.2: [x] runtime mapping-layer завершён: `iface_id -> routing_table` (dedicated iface default + interface hints + sync в create/patch/lifecycle/netns-toggle/provision) + owner-scoped nft compile naming (`iface+client+selector`) без shared-set mixing
|
||
- Step E3.3.2.3: [x] оставшиеся mutating transport-paths переведены на iface-aware orchestration lock:
|
||
- `create`, `PATCH /transport/clients/{id}`, `DELETE /transport/clients/{id}`, `POST /transport/netns/toggle`;
|
||
- добавлен multi-`iface_id` lock helper с детерминированным порядком захвата для patch/multi-target операций;
|
||
- `lifecycle start/restart` теперь расширяют lock-set до всех `same-netns` `SingBox` peer-ов, включая binding через `iface -> netns`, поэтому cross-`iface_id` netns-конфликт не обходит orchestrator;
|
||
- `netns toggle` больше не делает прямой `backend restart`: restart/provision теперь идут через общий backend execution path (`provision` + lifecycle preflight/orchestrator), поэтому peer-stop guard, egress refresh и runtime commit не дублируются отдельной веткой;
|
||
- lock-resolver для lifecycle/netns toggle теперь использует `same-netns` peer matching без фильтра по runtime-status (lock захватывает всех netns-peer клиентов), чтобы избежать гонок при stale/lag status и не пропускать cross-iface сериализацию.
|
||
- lifecycle/provision/create/patch/delete/netns теперь используют общий `iface` serialization pattern, без отдельной GUI/local orchestration-ветки.
|
||
- Step E3.3.3: [x] внедрён M3 per-interface policy compiler + atomic apply executor foundation:
|
||
- compile-plan `iface_id -> table/mark/pref/nft-set` подключён в validate/apply/rollback/get-policy;
|
||
- apply/rollback теперь проходят через atomic runtime executor с snapshot/restore (`transport-policies.runtime.json`, `transport-policies.runtime.prev.json`) до commit policy revision;
|
||
- подключён kernel stage в executor:
|
||
- nft stage: per-interface CIDR set apply/cleanup (`inet agvpn`, managed sets `agvpn_pi_*`);
|
||
- optional ip rule stage (`fwmark/pref -> lookup table`) под отдельным флагом;
|
||
- kernel stage включается через env-флаги: `SVPN_TRANSPORT_POLICY_KERNEL_APPLY=1`, `SVPN_TRANSPORT_POLICY_KERNEL_IPRULES=1`.
|
||
- M3 owner scope закрыт: compile-plan теперь генерирует owner-scoped `nft_set` (`owner_scope=iface+client`), что исключает shared-set mixing для нескольких клиентов на одном `iface_id`.
|
||
- Step E3.4: [x] внедрён strict ownership registry для policy-intent (single-owner на domain/ip/cidr/app intent, явные conflict reason до apply)
|
||
- Step E3.4.1: [x] добавлен persisted ownership registry (`transport-ownership.json`) + endpoint `GET /api/v1/transport/owners` (auto-rebuild из compile-plan при рассинхроне revision)
|
||
- Step E3.4.2: [x] apply-guardrails усилены: `force_override` разрешён только для `owner_switch`; non-overridable block (`ownership`, `cidr_overlap`, `unknown_client`, allocator conflicts) не обходятся override-флагом
|
||
- Step E3.4.3: [x] read-side ownership rebuild усилен `plan_digest` guard: `GET /api/v1/transport/owners` пересобирает ownership не только по `policy_revision`, но и при drift compile-plan digest; в response добавлен `plan_digest`, ownership records возвращают `owner_scope`
|
||
- Step E3.5: [x] добавлен anti-mixing guard (`conntrack` stickiness + destination owner lock), чтобы один destination не ходил через два engine одновременно
|
||
- Step E3.5.1: [x] добавлен runtime owner-lock guard в validate/apply: owner switch блокируется, если previous owner клиента активен (`up|starting|degraded`); `force_override` это не обходит
|
||
- Step E3.5.2: [x] расширен observability ownership: `GET /api/v1/transport/owners` теперь возвращает `owner_status` + `lock_active` по каждой записи и агрегат `lock_count`
|
||
- Step E3.5.3: [x] добавлен conntrack stickiness foundation: kernel stage умеет собирать destination-lock state (`transport-owner-locks.json`) по `mark->owner` mapping; endpoint `GET /api/v1/transport/owner-locks`; validate/apply блокируют owner-switch по `cidr` при активных `destination_lock`
|
||
- Step E3.5.4: [x] добавлен безопасный recovery clear-flow для owner-locks: `POST /api/v1/transport/owner-locks/clear` (filter: `client_id`, `destination_ip(s)`), двухшаговый confirm-token (`clr-*`), без unscoped clear-all
|
||
- Step E3.5.5: [x] расширен destination-lock guard на `domain` selector: owner-switch по `domain`/`*.domain` теперь учитывает resolver `domain-cache` (`direct+wildcard`) и блокируется при sticky-совпадении `destination_ip` у previous owner
|
||
- Step E3.6: [x] расширен apply pipeline до `validate -> plan -> confirm -> apply -> health-check -> commit` с auto-rollback при fail health-check
|
||
- Step E3.6.1: [x] добавлен post-apply transport health-check до policy commit:
|
||
- apply/rollback после runtime apply выполняют backend health probe по активным transport-owner клиентам из compile-plan;
|
||
- для `enabled/up|starting|degraded` клиентов failure/down считается commit-blocking, inactive draft clients (`enabled=false`, `status=down`) пропускаются;
|
||
- при health-check fail выполняется runtime auto-rollback на `transport-policies.runtime.prev.json`, policy revision не коммитится;
|
||
- `TransportPolicyResponse` дополнен `health_check` summary для GUI/Web наблюдаемости.
|
||
- Step E3.6.2: [x] добить transaction pipeline:
|
||
- уменьшить удержание глобального `transportMu` на long-running runtime steps после стабилизации orchestrator path;
|
||
- расширить health gate на более полный per-interface observability/runtime coverage.
|
||
- Step E3.6.2.1: [x] добавлен persisted `Idempotency-Key` storage/replay для mutating policy endpoints:
|
||
- `POST /api/v1/transport/policies/apply` и `POST /api/v1/transport/policies/rollback` сохраняют response snapshot по `(scope, idempotency_key, request_hash)`;
|
||
- повтор того же ключа с тем же payload возвращает сохранённый response без повторного runtime apply/rollback и без инкремента `policy_revision`;
|
||
- reuse того же ключа с другим payload блокируется кодом `IDEMPOTENCY_KEY_REUSED`;
|
||
- state вынесен в отдельный persisted backend store (`transport-policy-idempotency.json`) с pruning по TTL/size.
|
||
- Step E3.6.2.2: [x] уменьшить удержание глобального `transportMu` на long-running runtime steps после стабилизации orchestrator path
|
||
- `transport client provision` переведён на двухфазный flow:
|
||
- под `transportMu` остаются только snapshot/bind и final commit,
|
||
- сам `backend.Provision()` выполняется под `iface`-lock, но вне глобального mutex;
|
||
- `transport client lifecycle` переведён на двухфазный flow:
|
||
- `same-netns` peer-stop и target `backend.Action()` выполняются под `iface`-lock set, но вне глобального mutex;
|
||
- под `transportMu` остаются только snapshot/bind/reload/final commit, включая commit peer-stop результатов;
|
||
- partial peer-stop failure теперь сохраняет уже выполненные peer changes в state до возврата ошибки target lifecycle, без silent drift между runtime и persisted state;
|
||
- `transport netns toggle` переведён на такой же двухфазный flow:
|
||
- config patch/save остаётся под `transportMu`,
|
||
- долгие `provision/restart` шаги выполняются уже через общий provision/lifecycle path под `iface`-lock set, но вне глобального mutex;
|
||
- lock resolver для toggle теперь заранее включает `same-netns` peer `iface_id`, если restart затрагивает binding после toggle.
|
||
- Step E3.6.2.3: [x] расширен health gate на более полный per-interface observability/runtime coverage
|
||
- `TransportPolicyResponse.health_check` возвращает per-`iface_id` summary c runtime-полями:
|
||
- `iface_id/mode/runtime_iface/netns_name/routing_table`,
|
||
- `client_count/checked_count/failed_count/skipped_count`,
|
||
- `status`, `latency_ms`, `last_error`, `active_client_id`,
|
||
- interface-level `ok/message` для GUI/Web без ручной агрегации по client items;
|
||
- summary строится из того же post-probe runtime snapshot (включая netns-клиентов), что и client-level health items, без отдельной UI-склейки.
|
||
- Step E3.6.2.4: [x] сокращён hold-time `transportMu` в read-side snapshot handlers (`/transport/interfaces`, `/transport/policies`, `/transport/ownership`, `/transport/runtime/observability`):
|
||
- под `transportMu` выполняется только короткий state snapshot (`clients/interfaces/policy/plan/ownership`) + token capture;
|
||
- нормализация/compile-plan выполняются вне глобального mutex;
|
||
- добавлен snapshot-aware conditional commit helper (`compileTransportPolicyPlanForSnapshot`, `saveTransportInterfacesIfSnapshotCurrent`, `saveTransportPlanIfSnapshotCurrent`, `saveTransportOwnershipIfSnapshotCurrent`) для safe save без stale overwrite;
|
||
- API/DTO контракты endpoint’ов сохранены без изменений.
|
||
- Step E3.6.2.5: [x] доведён аналогичный snapshot/conditional-save подход для `netns` toggle-пути (`POST /api/v1/transport/netns/toggle`):
|
||
- lock-id resolver теперь использует короткий snapshot (`clients/interfaces`) под `transportMu`, нормализация interfaces и conditional-save выполняются вне глобального mutex;
|
||
- в execute-path убран persist interfaces из-под `transportMu`: используется in-memory normalize для binding, а persist вынесен в post-unlock `conditional-save helper`;
|
||
- добавлен pure helper `resolveTransportLifecycleLockIDsForSnapshot` (in-memory), lock-resolver теперь использует snapshot-state без повторного I/O под mutex; flaky unit-case стабилизирован.
|
||
- устранён stale-guard gap для mutating netns-path: post-commit persist interfaces больше не зависит от `clients.updated_at` (используется interfaces-only snapshot), поэтому нормализованные iface-записи не теряются после `saveTransportClientsState`.
|
||
- mutating контракт netns-toggle сохранён (clients state commit остаётся atomic под `transportMu`, provision/restart по-прежнему вне глобального mutex).
|
||
- Step E3: [x] схема allocator'ов (`mark/table/pref`) и стратегия atomic apply/rollback
|
||
- Step E4.1: [x] зафиксирован UX-flow `validate -> confirm -> apply` для web/mobile
|
||
- Step E4.2: [x] внедрён foundation state-machine в GUI controller (`draft/validated/risky/confirm/applied`)
|
||
- Step E4.3.1: [x] добавлен GUI foundation-блок `Transport engine` (select + prepare/connect/disconnect/restart через `/api/v1/transport/clients/{id}/*`)
|
||
- Step E4.3.2: [x] `Connect/Switch` переведён на pipeline `validate -> confirm -> apply` + добавлен `Rollback policy` button
|
||
- Step E4.3: [~] детализирован UX-подпоток `Engine Switch / Connect` (desired/active engine, switch states, rollback action)
|
||
- Step E4.4: [x] зафиксирован desktop-first дизайн общего multi-interface GUI блока (`Ownership + Destination locks + safe clear-flow`) в `docs/phase-e/E4_2_MULTI_INTERFACE_GUI_DESIGN.md`
|
||
- Step E4.5: [x] desktop GUI подключён к ownership/lock API:
|
||
- read-only панель `Ownership & destination locks` во вкладке `SingBox` (таблицы ownership + destination locks, summary/revision);
|
||
- refresh-хук встроен в `refresh_singbox_tab` и transport refresh-поток;
|
||
- подключён безопасный clear-flow через `POST /api/v1/transport/owner-locks/clear` (2-step confirm token, без optimistic update, только re-fetch).
|
||
- Step E4.5.1: [x] desktop GUI подключён к `GET /api/v1/transport/interfaces`:
|
||
- в панели `Ownership & destination locks` добавлена таблица интерфейсов (`iface_id/mode/runtime_iface/netns/routing_table/client up/total`);
|
||
- summary расширен данными `interfaces count + policy revision + intents count`.
|
||
- Step E4.5.2: [x] добавлен desktop GUI policy-intents editor (в `SingBox` блоке `Ownership & destination locks`):
|
||
- draft-таблица intents (`selector_type/value`, `client_id`, `mode`, `priority`);
|
||
- actions: `Reload policy`, `Add intent`, `Remove selected`, `Validate policy`, `Apply policy`, `Rollback policy`;
|
||
- apply использует backend flow `validate -> (optional confirm for risky) -> apply`, rollback через `/transport/policies/rollback`;
|
||
- черновик не перетирается авто-refresh'ем при dirty state.
|
||
- Step E4.5.3: [x] UX-polish policy-intents в MultiIF:
|
||
- добавлены quick templates (`domain/wildcard/cidr/ip/app_key/uid`) с prefill без auto-add;
|
||
- добавлен `Load selected` + double-click по строке draft для обратной подгрузки intent в форму редактирования;
|
||
- добавлен Enter-to-add для selector value и duplicate guard (не даёт добавить полностью идентичный intent);
|
||
- в draft-таблице клиент показывается как `name (client_id)` для лучшей читаемости.
|
||
- Step E4.5.4: [x] добавлено inline-редактирование draft intent в MultiIF:
|
||
- добавлена кнопка `Update selected` (обновляет выбранную строку без remove/add);
|
||
- общий validator формы переиспользуется для add/update (domain/cidr/uid guardrails едины);
|
||
- duplicate guard поддерживает `skip_index` при update (строку можно обновлять без ложного self-duplicate).
|
||
- Step E4.5.5: [x] MultiIF policy/ownership отвязаны от `transport-only` и включают `AdGuard VPN` как virtual policy target:
|
||
- в backend compile/validate/apply/rollback path добавлен virtual client `adguardvpn` (без записи в `transport-clients.json`);
|
||
- ownership/locks/read-side используют единый `policyTargets` snapshot (`transport clients + adguardvpn`);
|
||
- в GUI включён scope-filter `All/Transport/AdGuard VPN`, client selector для intents поддерживает `AdGuard VPN (adguardvpn)`;
|
||
- сняты artificial guard-блоки `adguard-only`, чтобы low-level MultiIF слой работал единообразно для всех engine.
|
||
- Step E4.5.6: [x] интерфейсный слой MultiIF переведён на backend-source-of-truth для AdGuard:
|
||
- `GET /api/v1/transport/interfaces` теперь добавляет virtual interface-row `adguardvpn` (status/iface/table/up-count) из backend observer;
|
||
- GUI больше не строит adguard-строку локально через `vpn_status_model`/egress-хелперы;
|
||
- filter `All/Transport/AdGuard VPN` применяется единообразно к interfaces/ownership/locks таблицам.
|
||
- Step E4.5.7: [x] добавлен быстрый UX-flow для первичной проверки MultiIF policy editor:
|
||
- кнопка `Add demo intent` создаёт тестовый `domain` intent (`demo.invalid`, авто-уникализация `demo-N.invalid`) на выбранный client;
|
||
- quick-help обновлён до пошагового сценария `Add demo/fill -> Validate -> Apply`;
|
||
- flow использует тот же draft/validate/apply pipeline без отдельной ветки логики.
|
||
- Step E4.5.8: [x] в MultiIF добавлено визуальное разделение `Draft` и `Applied` policy intents:
|
||
- добавлена отдельная read-only таблица `Applied intents` (текущая backend policy);
|
||
- status/state строка теперь показывает оба счётчика (`draft` и `applied`) для быстрого сравнения;
|
||
- API-unavailable/error paths очищают обе таблицы синхронно, чтобы не было stale-данных.
|
||
- Step E4.5.9: [x] добавлена визуализация конфликтов валидации policy (MultiIF):
|
||
- отдельная read-only таблица `Validation conflicts` (`type/severity/owners/reason/suggested resolution`);
|
||
- таблица синхронизируется из `validate/apply` flow и сохраняет последний результат проверки;
|
||
- при API-unavailable/endpoint-error таблица очищается вместе с draft/applied, чтобы не показывать stale-conflicts.
|
||
- Step E4: [~] UX предупреждения и conflict flow (`validate -> confirm -> apply`, включая engine switch/connect)
|
||
- Step E5: [~] зафиксировать требования для протоколов во вкладке `SingBox` и target Go API `singbox profiles`
|
||
- Step E5.1: [x] requirements freeze для `SingBox Protocols` (UI + Go API + storage/events)
|
||
- Step E5.1.2: [x] собран шаблон и матрица полей по протоколам `singbox` (`vless/trojan/shadowsocks/wireguard/hysteria2/tuic`) + JSON manifest для генерации форм
|
||
- Step E5.1.3: [x] зафиксирована client-side UI матрица (по блокам формы, VLESS baseline + guardrails, без server-only полей)
|
||
- Step E5.2: [x] реализовать Go model/state для `singbox profiles` (CRUD + versioning + secrets store)
|
||
- Step E5.3: [x] реализовать Go flow `validate/render/apply/rollback/history/features` для `singbox profiles`
|
||
- Step E5.4: [~] реализовать GUI-блок протоколов в `SingBox` вкладке (list/editor/validate/preview/apply/rollback)
|
||
- Step E5.4.1: [x] внедрён desktop foundation-дизайн `SingBox` вкладки: `connection card` + `profile settings` + `global defaults`
|
||
- Step E5.4.2: [x] подключены GUI-кнопки `Validate profile`/`Apply profile` к Go API `/api/v1/transport/singbox/profiles/{id}/validate|apply` (с activity-log и runtime refresh)
|
||
- Step E5.4.3: [x] реализованы `Preview render` + `Rollback profile` + `History` и auto-link `engine -> profile` (auto-create/patch по `client_id`) + лёгкий рефакторинг handlers вкладки `SingBox`
|
||
- Step E5.4.4: [x] добавлен VLESS client-editor flow в GUI: auto-load по выбранному engine/profile, `Save draft` в Go API (`raw_config`), auto-sync draft перед `Preview/Validate/Apply`
|
||
- Step E5.4.5: [x] `Connection profiles` переведены в dashboard-плитки с context-menu `Run/Edit/Delete`; активный профиль подсвечивается зелёным, protocol editor вынесен в отдельный modal edit-dialog
|
||
- Step E5.4.6: [x] улучшен editor UX: non-destructive переключение `security/transport` (без reset полей), `Flow` сделан editable (preset + custom), добавлен `Create connection` (`Clipboard|Link|Manual`)
|
||
- Step E5.4.7: [x] унифицирован link-import pipeline: общий dispatcher + shared raw-config helpers для `vless/trojan/ss/hysteria2(hy2)/tuic`, без привязки к одному протоколу
|
||
- Step E5.4.8: [x] расширен form-editor на `trojan/shadowsocks/hysteria2/tuic` (единая форма + protocol switch + guardrails + raw save по выбранному protocol)
|
||
- Step E5.4.9: [x] добавлен `wireguard` в единый editor/import pipeline (поля WG + raw save + wireguard:// link parser)
|
||
- Step E6: [x] реализовать единый Go-сервис `egress identity` (IP + geo/country) для всех движков (`AdGuardVPN`, `transport:*`)
|
||
- Step E6.1: [x] зафиксировать backend-контракт `/api/v1/egress/identity` + `/api/v1/egress/identity/refresh` (scope-aware: `adguardvpn|transport:<id>|system`)
|
||
- Step E6.2: [x] реализовать provider-адаптеры в Go (общий интерфейс источника egress IP; netns-aware для transport-клиентов)
|
||
- Step E6.3: [x] добавить общий SWR/cache/backoff для egress identity (без блокировки UI, с `stale/updated_at/last_error`)
|
||
- Step E6.4: [x] добавить GeoIP-слой (country_code/country_name) и нормализованный ответ для GUI/Web/Mobile
|
||
- Step E6.5: [x] интегрировать в desktop-карточки `AdGuardVPN` и `SingBox` (показывать `IP + country`, флаг рендерится в UI из `country_code`)
|
||
- Step E6.6: [x] добавить unified runtime observability API для multi-interface (`active_iface`, `egress`, `latency`, `last_error`, counters per engine/policy)
|
||
- Step E6.6.1: [x] добавить backend DTO snapshot для карточек multi-interface (`client_id`, `iface_id`, `active_iface`, `egress`, `latency`, `last_error`, counters)
|
||
- новый backend aggregator собирает per-`iface_id` runtime snapshot из `transport interfaces + clients + policy compile-plan + egress identity`;
|
||
- DTO включает binding (`runtime_iface/active_iface/netns/routing_table`), active client, aggregate `status/latency/last_error`, status counters и `engine_counts/rule_count` без ручной склейки на UI.
|
||
- Step E6.6.2: [x] endpoint `GET /api/v1/transport/runtime/observability` + SSE `transport_runtime_snapshot_changed`
|
||
- handler и SSE publisher переиспользуют один и тот же backend snapshot builder без отдельной event-specific логики;
|
||
- `transport_runtime_snapshot_changed` публикуется после create/patch/delete, lifecycle, health refresh, policy apply/rollback, netns toggle и transport egress updates;
|
||
- payload события содержит unified snapshot (`items`) и метаданные `reason/client_ids/iface_ids/generated_at`, так что UI может либо re-fetch endpoint, либо обновиться напрямую тем же DTO.
|
||
- Step E6.7: [~] (обязательный post-transport этап) подключить `AdGuardVPN` через adapter к unified engine control-plane (без ломки autoloop/login/location flow), чтобы multi-interface observability/control был единым для всех движков
|
||
- Step E6.7.1: [x] backend adapter-путь `adguardvpn` подключён к unified transport control-plane:
|
||
- `GET /api/v1/transport/clients?include_virtual=true` возвращает virtual client `adguardvpn` в том же DTO-контракте, что и transport clients;
|
||
- `GET /api/v1/transport/clients/adguardvpn` + `health|metrics|start|stop|restart|provision` работают через virtual adapter без записи в `transport-clients.json`;
|
||
- legacy `POST /api/v1/vpn/autoconnect` переведён на тот же adapter execution path, чтобы исключить рассинхрон между old VPN flow и unified control-plane;
|
||
- runtime observability (`/transport/runtime/observability`) теперь включает virtual snapshot `adguardvpn` и использует scope-aware egress lookup (`adguardvpn` вместо `transport:adguardvpn`).
|
||
- Step E6.7.2: [x] GUI policy-editor переведён на unified policy targets из backend (`include_virtual=true`) без локального adguard-костыля:
|
||
- добавлен отдельный UI-cache `transport_policy_clients` (source of truth для `policy client selector` и label-резолва);
|
||
- client selector и draft/applied таблицы теперь используют общий backend-список targets (`transport + virtual`) и не подмешивают `adguardvpn` вручную;
|
||
- refresh policy locks делает явный prefetch policy-targets, а при ошибке использует безопасный fallback на текущий transport-list, без остановки UI.
|
||
- Step E6.7.3: [x] добавлена визуальная индикация типа policy target в MultiIF (`[transport]`/`[virtual]`):
|
||
- policy client selector, draft/applied intents и ownership/locks таблицы показывают тип target в label;
|
||
- interfaces таблица маркирует строки по типу target и отдельно помечает virtual-mode (`... | VIRTUAL`);
|
||
- summary дополнен счётчиками targets (`transport=N`, `virtual=M`) и hint с legend по меткам.
|
||
- Step E7: [ ] (deferred) разделить policy-слои `System selective PBR` и `SingBox L7 routing` (реализация позже, после текущего контура)
|
||
- Step E7.1: [ ] зафиксировать единый L7 rule-contract для SingBox (`domain/suffix/keyword/regex`, `ip_cidr`, `port`, `network`, `protocol`, `process/user/package`, `rule_set`, `priority`, `action`)
|
||
- Step E7.2: [ ] реализовать Go renderer `policy -> singbox.json` (`dns`, `route.rules`, `rule_set`, `outbounds`, `final`) без ручного JSON в UI
|
||
- Step E7.3: [ ] внедрить pipeline `validate -> dry-run -> apply -> rollback` для L7 policy (`sing-box check` обязателен)
|
||
- Step E7.4: [ ] реализовать DNS-стратегию для L7 (`SingBox DNS policy` + `bootstrap bypass`) без дублирования intent в системном resolver-контуре
|
||
- Step E7.5: [ ] добавить conflict guardrails между PBR и L7 (single-owner intent: правило живет только в одном policy-слое)
|
||
- Step E7.6: [ ] добавить наблюдаемость L7 (`effective policy`, `active outbound`, `last match reason`, counters/hits per rule, egress per-engine)
|
||
- Step F1: [~] выполнить поэтапный рефакторинг и модульность без изменения поведения
|
||
- Step F1.1: [x] зафиксирован план декомпозиции крупных файлов (`docs/phase-f/F1_REFACTOR_MODULARITY_PLAN.md`)
|
||
- Step F1.2: [x] netns-логика вынесена в отдельные модули GUI/API (`netns_debug.py`, `transport_netns_exec.go`) + оформлен runtime-case документ
|
||
- Step F1.2.1: [x] orchestration netns-toggle перенесён в Go API endpoint `/api/v1/transport/netns/toggle`, GUI переключатель переведён на единый backend вызов
|
||
- Step F1.2.2: [x] добавлен общий backend `refresh coordinator` (SWR + single-flight + backoff), `vpn locations` переведены на общую схему без изменения API-контракта
|
||
- Step F1.2.3: [x] та же SWR-схема применена для `transport health` (`/transport/clients` background refresh + `POST /transport/health/refresh` + SSE `transport_client_health_changed`)
|
||
- Step F1.6: [x] перевести точки входа Go API на `cmd/*` (отдельные бинари `api`, `routes-update`, `routes-clear`, `autoloop`) с сохранением legacy entrypoint
|
||
- Step F1.7: [x] разрезать `selective-vpn-api/app/server.go` на `entrypoints/api_bootstrap/api_routes` без изменения API-контракта и CLI-поведения
|
||
- Step F1.8: [x] вынести доменные route-helper'ы в отдельные `app/api_routes_*.go` файлы (без изменения endpoint-path/handler-контракта)
|
||
- Step F1.9: [x] вынести CLI/bootstrap раннеры в подпакеты `app/cli` и `app/bootstrap` с сохранением фасадов `Run*` в `app`
|
||
- Step F1.10: [x] разложить `app/transport_handlers.go` на модульные файлы (`handlers_clients`, `handlers_policy`, `policy_validate`, `client_runtime`, `tokens_state`, `shared`) без изменения endpoint-path
|
||
- Step F1.11: [~] вынести переиспользуемую transport-логику в отдельные подпакеты (`app/transport/*`) только для частей без циклических зависимостей (через facade+deps)
|
||
- Step F1.11.1: [x] вынесен runtime/config helper-блок transport backend в подпакет `app/transportcfg` с сохранением app-facade (`transport_backends_runtime_helpers.go`)
|
||
- Step F1.11.2: [x] вынесен systemd helper-блок transport backend в `app/transportcfg` с сохранением app-facade (`transport_backends_systemd_helpers.go`)
|
||
- Step F1.11.3: [x] вынесен exec/binary/template helper-блок transport backend в `app/transportcfg` с сохранением app-facade (`transport_backends_exec_helpers.go`)
|
||
- Step F1.11.4: [x] вынесен probe/endpoint helper-блок transport backend в `app/transportcfg` с сохранением app-facade (`transport_backends_probe_helpers.go`)
|
||
- Step F1.11.5: [x] вынесен in-memory SSE event-bus в подпакет `app/eventsbus` с сохранением app-facade (`events_bus.go`)
|
||
- Step F1.11.6: [x] вынесен API route-registry в подпакет `app/apiroutes` (удалены `api_routes_{core,dns,trace,traffic,transport,vpn}.go`, сохранён единый `registerAPIRoutes` facade)
|
||
- Step F1.11.7: [x] объединены misc transport adapters в `transport_backends.go` (удалён `transport_backends_adapters_misc.go`)
|
||
- Step F1.11.8: [x] вынесены низкоуровневые command helpers в `app/syscmd` (`RunCommand`, `RunCommandTimeout`, `CheckPolicyRoute`) с сохранением фасада `app/shell.go`
|
||
- Step F1.11.9: [x] вынесены общие HTTP helper'ы в `app/httpx` (`LogRequests`, `WriteJSON`, `HandleHealthz`) с сохранением фасада `app/http_helpers.go`
|
||
- Step F1.11.10: [x] вынесен SSE stream-loop в `app/eventstream` (`ParseSinceID`, `Serve`) с сохранением фасада `app/events_handlers.go`
|
||
- Step F1.11.11: [x] вынесен SWR/backoff coordinator в `app/refreshcoord` с сохранением фасада `app/refresh_coordinator.go` и без изменения поведения egress/vpn-locations/transport-health контуров
|
||
- Step F1.11.12: [x] вынесен NFT update engine в `app/nftupdate` (atomic + chunked fallback) с сохранением фасада `app/nft_update.go`
|
||
- Step F1.11.13: [x] вынесен traffic candidates collector в `app/trafficcandidates` с сохранением API-контракта endpoint `GET /api/v1/traffic/candidates`
|
||
- Step F1.11.14: [x] устранена «россыпь bridge-файлов» resolver-контура: `resolver_*_bridge.go` схлопнуты в единый `app/resolver_bridge.go` (без изменения API/логики)
|
||
- Step F1.11.15: [x] вынесен traffic app profiles store в `app/trafficprofiles` с сохранением API-контракта `GET/POST/DELETE /api/v1/traffic/app-profiles`
|
||
- Step F1.11.16: [x] вынесена canonical `app_key` нормализация в `app/trafficprofiles` и удалён root-файл `traffic_appkey.go` (совместимость через app-facade функции сохранена)
|
||
- Step F1.11.17: [x] вынесены state/dedupe/persistence helper'ы `traffic app marks` в подпакет `app/trafficappmarks` с сохранением runtime/API-контракта
|
||
- Step F1.11.18: [x] вынесены cgroup path/inode helper'ы `traffic app marks` в `app/trafficappmarks` с сохранением фасадов в `app/traffic_appmarks.go`
|
||
- Step F1.11.19: [x] вынесены NFT helper'ы `traffic app marks` в `app/trafficappmarks/nft.go` (insert/delete/has/local-bypass/handle parse/ipv4-compact) с сохранением фасадов в `app/traffic_appmarks.go`
|
||
- Step F1.11.20: [x] вынесены cleanup helper'ы `traffic app marks` в `app/trafficappmarks/nft.go` (`CleanupLegacyRules`, `ClearManagedRules`) с сохранением фасадов в `app/traffic_appmarks.go`
|
||
- Step F1.11.21: [x] вынесена TTL prune-логика `traffic app marks` в `app/trafficappmarks/store.go` (`PruneExpired`) с сохранением удаления runtime nft-правил через app-facade callback
|
||
- Step F1.11.22: [x] вынесен базовый nft ensure bootstrap `traffic app marks` в `app/trafficappmarks/nft.go` (`EnsureBase`) с сохранением фасада `ensureAppMarksNft` в `app/traffic_appmarks.go`
|
||
- Step F1.11.23: [x] вынесены общие `singbox profile flow` helper'ы в `app/transportcfg/singbox_helpers.go` (`typed protocol support`, `parse port`, `digest/diff config`, `json config file io`, `optional file restore`, `history stamp`, `issue message join`) с сохранением app-facade вызовов в `transport_singbox_profiles_flow.go`
|
||
- Step F1.11.24: [x] вынесены history helper'ы `singbox profile flow` в `app/transportcfg/history_helpers.go` (`WriteFileAtomic`, `ReadJSONFiles`, `SelectRecordCandidate`, `DecodeBase64Optional`) и переведены вызовы `append/load/select/decode` в `transport_singbox_profiles_flow.go`
|
||
- Step F1.11.25: [x] удалены локальные helper'ы `findSingBoxBinary`/`sanitizeHistoryStamp` в `transport_singbox_profiles_flow.go`; вызовы переведены на `transportcfg.FirstExistingBinaryCandidate` и `transportcfg.SanitizeHistoryStamp`
|
||
- Step F1.11.26: [x] вынесены secrets-map helper'ы в `app/transportcfg/secrets_helpers.go` (`NormalizeSecretUpdates`, `Clone/Equal/MaskStringMap`, `Read/WriteStringMapJSON`), `transport_singbox_profiles.go` переведён на пакетные вызовы
|
||
- Step F1.11.27: [x] очищен `transport_singbox_profiles_flow.go` от остаточных локальных wrapper'ов (config io/file optional/diff/normalize), вызовы переведены на `transportcfg` напрямую
|
||
- Step F1.11.28: [x] вынесен state/normalization-блок `singbox profiles` в отдельный файл `app/transport_singbox_profiles_state.go` (`load/save/normalize/derive/index/active/select + cloneMapDeep`) без изменения API-контрактов
|
||
- Step F1.11.29: [x] вынесены secrets/error helper'ы `singbox profiles` в `app/transport_singbox_profiles_secrets.go` и `app/transport_singbox_profiles_errors.go`; `transport_singbox_profiles.go` оставлен как handlers/mutate фасад
|
||
- Step F1.11.30: [x] вынесен mutate-блок `singbox profiles` (`create/patch`) в `app/transport_singbox_profiles_mutate.go`; `transport_singbox_profiles.go` сфокусирован на HTTP handlers/route dispatch
|
||
- Step F1.11.31: [x] вынесены runtime helper'ы `singbox profile flow` в `app/transport_singbox_profiles_runtime.go` (`find profile by client`, `prepare profile`, `resolve apply client`, `apply runtime`)
|
||
- Step F1.11.32: [x] вынесены history helper'ы `singbox profile flow` в `app/transport_singbox_profiles_history.go` (`append/load/select/decode history`, `join issue messages`)
|
||
- Step F1.11.33: [x] вынесен eval/render helper-блок `singbox profile flow` в `app/transport_singbox_profiles_eval.go` (`evaluate`, `binary validate`, `rendered path/config writer`, issue mapping)
|
||
- Step F1.11.34: [x] вынесен state/normalization-блок `transport client runtime` в `app/transport_client_runtime_state.go` (`create builder`, `kind/capabilities normalize`, `runtime snapshot normalize`, `uptime/index/clone helpers`) без изменения API-контрактов
|
||
- Step F1.11.35: [x] вынесен `transport client runtime` health/netns-блок в отдельные файлы `app/transport_client_runtime_health.go` и `app/transport_client_runtime_netns.go` (response snapshot + netns peer-stop guard) без изменения lifecycle/API-контрактов
|
||
- Step F1.11.36: [x] выполнен разнос transport client handlers: netns toggle вынесен в `app/transport_handlers_netns.go`, lifecycle action-dispatch вынесен в `app/transport_handlers_actions.go`, `transport_handlers_clients.go` оставлен list/card routing-фасадом
|
||
- Step F1.11.37: [x] вынесен helper-блок `routes update` в `app/routes_update_helpers.go` (table/list/io/counter/fs/readiness helpers) без изменения orchestration-пайплайна `routesUpdate`
|
||
- Step F1.11.38: [x] вынесен state/storage/normalize блок `dns settings` в `app/dns_settings_state.go` (`upstream pool`, `mode state`, `smartdns helpers`, `dnscfg adapters`) без изменения API handlers
|
||
- Step F1.11.39: [x] начата декомпозиция `types.go`: DNS/SmartDNS DTO вынесены в `app/types_dns.go` (без изменения JSON-контрактов/имён типов)
|
||
- Step F1.11.40: [x] продолжена декомпозиция `types.go`: вынесены доменные DTO в `app/types_traffic.go`, `app/types_egress.go`, `app/types_transport.go`, `app/types_singbox.go`; `types.go` оставлен для shared/system/event/resolver моделей
|
||
- Step F1.11.41: [x] выполнен разнос `transport policy handlers`: `validate/apply/rollback` вынесены в `app/transport_handlers_policy_mutations.go`; `transport_handlers_policy.go` оставлен для `list/conflicts/capabilities`
|
||
- Step F1.11.42: [x] вынесен endpoint `POST /transport/health/refresh` в `app/transport_handlers_health_refresh.go`; `transport_health_refresh.go` оставлен как runtime S-W-R/probe logic
|
||
- Step F1.11.43: [x] вынесен low-level probe/helper блок `egress identity` в `app/egress_identity_probe.go` (`probe via system/iface/netns/proxy`, endpoint helpers, singbox socks parse, iface bind address) без изменения API/SWR-контракта
|
||
- Step F1.11.44: [x] выполнен разнос `routes handlers` service/timer блока в `app/routes_handlers_service.go`; `routes_handlers.go` оставлен для status/clear/fix/update endpoints
|
||
- Step F1.11.45: [x] выполнен разнос `routes handlers` clear/precheck/op-lock блока в `app/routes_handlers_ops.go`; `routes_handlers.go` оставлен для status/policy-fix/update endpoints
|
||
- Step F1.11.46: [x] вынесены `egress identity` HTTP handlers в `app/egress_identity_handlers.go`; `egress_identity.go` оставлен для service/SWR/state orchestration
|
||
- Step F1.11.47: [x] выполнен разнос монолита `app/traffic_appmarks.go` на `traffic_appmarks_handlers.go`, `traffic_appmarks_ops.go`, `traffic_appmarks_runtime.go`; `traffic_appmarks.go` оставлен для core-констант/типов (без изменения API/runtime поведения)
|
||
- Step F1.11.48: [x] вынесен helper-блок `routes cache` в `app/routes_cache_helpers.go` (table lines/io/restore-route/nft-snapshot/meta/file helpers); `routes_cache.go` оставлен orchestration-фасадом restore/save
|
||
- Step F1.11.49: [x] выполнен разнос `egress identity` runtime-контура: provider-probe вынесен в `app/egress_identity_providers.go`, refresh/snapshot orchestration в `app/egress_identity_refresh.go`, geo cache/lookup в `app/egress_identity_geo.go`, scope/identity helpers в `app/egress_identity_scope.go`; `app/egress_identity.go` оставлен с константами/типами/инициализацией
|
||
- Step F1.11.50: [x] выполнен разнос `vpn handlers` монолита на `app/vpn_handlers_auth.go` (login/logout/systemd), `app/vpn_handlers_status.go` (autoloop/status parse), `app/vpn_handlers_locations.go` (autoconnect/locations/set-location resolver); `app/vpn_handlers.go` оставлен как модульный фасад-комментарий
|
||
- Step F1.11.51: [x] вынесены helper'ы `autoloop` в `app/autoloop_helpers.go` (`login-state write/parse`, `connected check`, `location resolve`, `ISO/cache helpers`), `app/autoloop.go` оставлен как loop orchestration без изменения runtime-поведения
|
||
- Step F1.11.52: [x] выполнен разнос `transport systemd backend` на роль-файлы: `transport_backends_adapter_systemd_action.go`, `..._health.go`, `..._provision.go`, `..._cleanup.go`; базовый `transport_backends_adapter_systemd.go` оставлен для shared type aliases/render helpers
|
||
- Step F1.11.53: [x] выполнен stage-разнос `routes_update.go`: preflight вынесен в `routes_update_stage_preflight.go`, policy routing в `routes_update_stage_policy.go`, nft setup/progress update в `routes_update_stage_nft.go`, domains+resolver merge в `routes_update_stage_resolve.go`, artifacts/status publish в `routes_update_stage_artifacts.go`; `routes_update.go` оставлен orchestration-фасадом
|
||
- Step F1.11.54: [x] выполнен разнос `vpn_login_session.go`: HTTP endpoints и `loginStateAlreadyLogged` вынесены в `vpn_login_session_handlers.go`, базовый `vpn_login_session.go` оставлен для manager/PTY state-machine (без изменения API login session `/api/v1/vpn/login/session/*`)
|
||
- Step F1.11.55: [x] выполнен разнос `vpn_locations_cache.go`: refresh/SWR orchestration вынесен в `vpn_locations_cache_refresh.go`, parser/location-target блок в `vpn_locations_cache_parse.go`, cache io/normalize/store в `vpn_locations_cache_store.go`; базовый файл оставлен с типами/константами/инициализацией
|
||
- Step F1.11.56: [x] выполнен разнос `transport_health_refresh.go`: SWR/state mutex блок вынесен в `transport_health_refresh_state.go`, queue/candidate scheduling в `transport_health_refresh_queue.go`, probe runtime в `transport_health_refresh_probe.go`, persist/change-bucket helpers в `transport_health_refresh_compare.go`; базовый файл оставлен с константами/типами/конструктором
|
||
- Step F1.11.57: [x] выполнен разнос `transport_client_runtime_alloc.go`: normalize/reconcile блок вынесен в `transport_client_runtime_alloc_normalize.go`, table/mark/pref slot helper'ы в `transport_client_runtime_alloc_slots.go`; базовый файл оставлен для `allocateTransportSlots` фасада
|
||
- Step F1.11.58: [x] выполнен разнос `transport_netns.go`: spec/name/prefix/hash/existence вынесены в `transport_netns_spec.go`, nft/policy-route helpers в `transport_netns_rules.go`, command soft/must wrappers в `transport_netns_run.go`; базовый файл оставлен для `ensure/cleanup` orchestration
|
||
- Step F1.11.59: [x] выполнен разнос `transport_bootstrap_bypass.go`: candidate/source extractors вынесены в `transport_bootstrap_bypass_candidates.go`, resolve/target/main-route в `transport_bootstrap_bypass_resolve.go`, route mutation в `transport_bootstrap_bypass_routes.go`, state io/normalize в `transport_bootstrap_bypass_state.go`; базовый файл оставлен для control-flow и shared error helpers
|
||
- Step F1.11.60: [x] выполнен разнос `transport_singbox_profiles_flow.go`: HTTP handlers вынесены в role-файлы `transport_singbox_profiles_flow_{validate,render,apply,rollback,history}.go`; базовый `transport_singbox_profiles_flow.go` оставлен для shared моделей/dispatcher
|
||
- Step F1.11.61: [x] выполнен разнос `transport_singbox_profiles.go`: list handlers вынесены в `transport_singbox_profiles_list.go`, by-id/card handlers в `transport_singbox_profiles_card.go`, features endpoint в `transport_singbox_profiles_features.go`; базовый файл оставлен для state/constants/shared mutex
|
||
- Step F1.11.62: [x] выполнен разнос benchmark-контура `dns_settings.go`: heavy benchmark handlers/helper'ы вынесены в `dns_settings_benchmark.go`; `dns_settings.go` оставлен для DNS mode/upstreams/smartdns control handlers
|
||
- Step F1.11.63: [x] выполнен разнос `resolver_bridge.go`: базовые alias/type/const вынесены в `resolver_bridge.go`, util wrappers в `resolver_bridge_utils.go`, domain-cache adapters в `resolver_bridge_cache.go`, dns-mode/lookup adapters в `resolver_bridge_dns.go`, planning/pipeline adapters в `resolver_bridge_pipeline.go`
|
||
- Step F1.11.64: [x] выполнен разнос `transport_backends.go`: probe adapters (`dial/probe/endpoints/deps`) вынесены в `transport_backends_probe.go`, runtime-mode backend structs (`unsupported/mock`) в `transport_backends_runtime_modes.go`; базовый файл оставлен для backend selection/config facade
|
||
- Step F1.11.65: [x] выполнен разнос `vpn_login_session.go`: session state/mutex methods вынесены в `vpn_login_session_state.go`, PTY lifecycle/parser в `vpn_login_session_pty.go`, базовый файл оставлен для DTO/models/manager struct; HTTP handlers остаются в `vpn_login_session_handlers.go`
|
||
- Step F1.11.66: [x] выполнен разнос `types_transport.go`: модели разделены на `types_transport_core.go` (client/runtime/capabilities), `types_transport_policy.go` (policy/conflicts), `types_transport_runtime.go` (health/lifecycle/refresh/netns responses); поведение API/JSON-контракты не изменены
|
||
- Step F1.11.67: [x] выполнен разнос `transport_singbox_dns_migration.go`: rule helper'ы вынесены в `transport_singbox_dns_migration_rules.go`, convert/parse блок в `transport_singbox_dns_migration_convert.go`, общий helper `asString/parsePort` вынесен в `helpers_values.go`; миграционный runtime/API-поток сохранён без изменений
|
||
- Step F1.11.68: [x] выполнен разнос `vpn_handlers_locations.go`: selection/validation helper'ы вынесены в `vpn_locations_selection.go`, основной handler-файл оставлен для `autoconnect/list/set-location` endpoint-потока и egress refresh trigger
|
||
- Step F1.11.69: [x] выполнен разнос `egress_identity_refresh.go`: runtime refresh/publish блок вынесен в `egress_identity_refresh_runner.go`, entry snapshot/state/sem helper'ы в `egress_identity_refresh_state.go`; базовый файл оставлен для queue/scope orchestration
|
||
- Step F1.11.70: [x] выполнен разнос `dns_settings_state.go`: upstream/pool persistence вынесены в `dns_settings_state_upstreams.go`, mode load/save в `dns_settings_state_mode.go`, smartdns env/systemd/normalize helper'ы в `dns_settings_state_smartdns.go`; DNS API-контракты и runtime-поведение сохранены
|
||
- Step F1.11.71: [x] выполнен разнос `traffic_audit.go`: проверки/парсинг nft-правил вынесены в `traffic_audit_checks.go`, pretty-render блока в `traffic_audit_render.go`, базовый файл оставлен как HTTP handler-orchestration
|
||
- Step F1.11.72: [x] выполнен разнос `routes_update_helpers.go`: list/io helper'ы вынесены в `routes_update_helpers_lists.go`, smartdns wildcard trace в `routes_update_helpers_trace.go`, fs/readiness helper'ы в `routes_update_helpers_fs.go`; базовый файл оставлен для table identifiers (`routesTableName/routesTableNum`)
|
||
- Step F1.11.73: [x] выполнена декомпозиция `types_traffic.go`: traffic mode/candidates DTO вынесены в `types_traffic_mode.go`, app-marks/app-profiles DTO вынесены в `types_traffic_apps.go`; JSON-контракты сохранены без изменений
|
||
- Step F1.11.74: [x] выполнен разнос `transport_handlers_actions.go`: action-routing оставлен в `transport_handlers_actions.go`, heavy action execution блоки (`health/metrics/provision/start-stop-restart`) вынесены в `transport_handlers_actions_exec.go` без изменения transport lifecycle/API поведения
|
||
- Step F1.11.75: [x] выполнен разнос `transport_handlers_policy_mutations.go`: thin wrappers оставлены в базовом файле, validate/apply/rollback execution вынесены в `transport_handlers_policy_mutations_{validate,apply,rollback}.go` без изменения policy-контрактов (`confirm token`, `snapshot/rollback`, `conflicts`)
|
||
- Step F1.11.76: [x] выполнен разнос `resolver_pipeline.go`: `buildResolverJobContext` вынесен в `resolver_pipeline_context.go`, базовый `resolver_pipeline.go` оставлен для execution pipeline (`planning -> batch -> artifacts -> summary -> precheck finalize`)
|
||
- Step F1.11.77: [x] выполнен разнос `resolver_dns_policy.go`: DNS attempt-policy helper'ы вынесены в `resolver_dns_attempt_policy.go`, cooldown runtime в `resolver_dns_cooldown.go`, precheck force flags в `resolver_precheck_flags.go`; базовый файл оставлен с типами policy/cooldown state
|
||
- Step F1.11.78: [x] выполнен разнос `trace_handlers.go`: read endpoints вынесены в `trace_handlers_read.go`, append/write helper'ы в `trace_handlers_write.go`, bounded tail reader в `trace_tail.go`; базовый `trace_handlers.go` оставлен как module doc/header
|
||
- Step F1.11.79: [x] выполнен разнос `transport_policy_validate.go`: normalization/CIDR helper'ы вынесены в `transport_policy_validate_normalize.go`, diff+conflict-dedupe блок в `transport_policy_validate_diff.go`; базовый файл оставлен для основной валидации ownership/overlap
|
||
- Step F1.11.80: [x] выполнен разнос `watchers.go`: bootstrap/start блок вынесен в `watchers_start.go`, file/hash watchers в `watchers_state_files.go`, runtime/systemd watchers в `watchers_runtime.go`; базовый `watchers.go` оставлен как module doc/header
|
||
- Step F1.11.81: [x] выполнен разнос `domains_handlers.go`: table endpoint вынесен в `domains_handlers_table.go`, file CRUD в `domains_handlers_file.go`, smartdns wildcard endpoints в `domains_handlers_smartdns.go`, observed-hosts helper в `domains_handlers_helpers.go`; базовый `domains_handlers.go` оставлен для shared mapping `domainFiles`
|
||
- Step F1.11.82: [x] выполнен разнос `egress_identity_probe.go`: external probe блок вынесен в `egress_identity_probe_external.go`, netns/interface/proxy probe блок в `egress_identity_probe_netns.go`, endpoint/socks/bind-address helper'ы в `egress_identity_probe_helpers.go`; базовый файл оставлен как модульный entrypoint
|
||
- Step F1.11.83: [x] выполнен разнос `traffic_appmarks_handlers.go`: POST mutation flow вынесен в `traffic_appmarks_handlers_post.go`, items list endpoint вынесен в `traffic_appmarks_handlers_items.go`, базовый handler оставлен для method routing + status response
|
||
- Step F1.11.84: [x] выполнен разнос `transport_handlers_netns.go`: per-client apply/restart/provision + response finalize вынесены в `transport_handlers_netns_apply.go`, базовый файл оставлен для HTTP endpoint + target resolution/orchestration
|
||
- Step F1.11.85: [x] выполнен разнос `transport_handlers_clients.go`: list/create endpoints вынесены в `transport_handlers_clients_list_create.go`, card get/patch/delete endpoints вынесены в `transport_handlers_clients_card_ops.go`, базовый handler оставлен для route-dispatch (`/transport/clients`, `/transport/clients/{id}`) без изменения API-контрактов
|
||
- Step F1.11.86: [x] выполнен разнос `smartdns_runtime.go`: config detect/apply/normalize вынесены в `smartdns_runtime_config.go`, state load/save/infer вынесены в `smartdns_runtime_state.go`, базовый `smartdns_runtime.go` оставлен для shared runtime model/constants + status snapshot
|
||
- Step F1.11.87: [x] выполнен разнос `traffic_mode_handlers.go`: POST apply-flow вынесен в `traffic_mode_handlers_apply.go`, advanced reset endpoint вынесен в `traffic_mode_handlers_advanced.go`, базовый `traffic_mode_handlers.go` оставлен для GET/interface/test endpoint-роутинга и lock helper'а
|
||
- Step F1.11.88: [x] выполнен разнос `routes_handlers.go`: status endpoint вынесен в `routes_handlers_status.go`, policy-fix endpoint вынесен в `routes_handlers_policy_fix.go`, async update endpoint вынесен в `routes_handlers_update.go`; базовый `routes_handlers.go` оставлен для service command wrapper `makeCmdHandler`
|
||
- Step F1.11.89: [x] выполнен разнос `dns_smartdns_handlers.go`: runtime endpoint (`GET/POST /dns/smartdns/runtime`) вынесен в `dns_smartdns_handlers_runtime.go`, prewarm endpoint + helper'ы вынесены в `dns_smartdns_handlers_prewarm.go`, базовый файл оставлен как module header
|
||
- Step F1.11.90: [x] выполнен разнос `traffic_appmarks_runtime.go`: state/prune helper'ы вынесены в `traffic_appmarks_runtime_state.go`, restore sequence вынесен в `traffic_appmarks_runtime_restore.go`, базовый runtime-файл оставлен для NFT adapters/config helper'ов
|
||
- Step F1.11.91: [x] выполнен разнос `dns_settings.go`: upstream endpoints вынесены в `dns_settings_handlers_upstreams.go`, mode/status handlers вынесены в `dns_settings_handlers_mode.go`, smartdns service handlers вынесены в `dns_settings_handlers_smartdns_service.go`; базовый файл оставлен как module header
|
||
- Step F1.11.92: [x] выполнен разнос `autoloop_helpers.go`: login-state/parser helper'ы вынесены в `autoloop_helpers_login.go`, location spec/ISO resolve helper'ы вынесены в `autoloop_helpers_location.go`, базовый файл оставлен для shared `autoloopLocationSpec` и email regexp
|
||
- Step F1.11.93: [x] выполнен разнос `traffic_app_profiles.go`: store/conversion/sanitize helper'ы вынесены в `traffic_app_profiles_store.go`, базовый файл оставлен для HTTP handler `/traffic/app-profiles` и package-level store init
|
||
- Step F1.11.94: [x] выполнен разнос `routes_handlers_ops.go`: core lock/clear/status snapshot логика вынесена в `routes_handlers_ops_core.go`, базовый `routes_handlers_ops.go` оставлен для rollback/cache-restore/precheck-debug endpoint-слоя
|
||
- Step F1.11.95: [x] выполнен разнос `transport_singbox_profiles_state.go`: state load/save вынесены в `transport_singbox_profiles_state_store.go`, profile/state normalize + active-id/derive-id helper'ы вынесены в `transport_singbox_profiles_state_normalize.go`, базовый файл оставлен для shared primitive helper'ов (`mode/schema/int64/cloneMap`)
|
||
- Step F1.11.96: [x] выполнен разнос `transport_singbox_profiles_runtime.go`: client/profile selection helper'ы вынесены в `transport_singbox_profiles_runtime_select.go`, preflight render/write flow вынесен в `transport_singbox_profiles_runtime_prepare.go`, backend provision/start/restart runtime flow вынесен в `transport_singbox_profiles_runtime_apply.go`; базовый файл оставлен как module header
|
||
- Step F1.11.97: [x] выполнен разнос `transport_handlers_actions_exec.go`: health/metrics handlers вынесены в `transport_handlers_actions_exec_health.go`, provision handler вынесен в `transport_handlers_actions_exec_provision.go`, lifecycle handler (`start/stop/restart`) вынесен в `transport_handlers_actions_exec_lifecycle.go`; базовый файл оставлен как module header
|
||
- Step F1.11.98: [x] выполнен разнос `routes_update_stage_resolve.go`: доменное расширение/каппинг/wildcard trace вынесены в `routes_update_stage_resolve_domains.go`, bootstrap временных файлов вынесен в `routes_update_stage_resolve_tempfiles.go`; базовый stage-файл оставлен для resolver orchestration + runtime wildcard merge + artifact writes
|
||
- Step F1.11.99: [x] выполнен разнос `transport_backends.go`: transportcfg wrapper/helper слой вынесен в `transport_backends_config_helpers.go`, unsupported runtime backend methods вынесены в `transport_backends_runtime_unsupported.go`, базовый файл оставлен для core backend types/constants + backend selection
|
||
- Step F1.11.100: [x] выполнен разнос `transport_singbox_dns_migration.go`: migration entry/settings/path helper'ы оставлены в базовом файле, apply flow вынесен в `transport_singbox_dns_migration_apply.go`, DNS map conversion вынесен в `transport_singbox_dns_migration_map.go`
|
||
- Step F1.11.101: [x] выполнен разнос `traffic_appmarks_ops.go`: status summary helper оставлен в базовом файле, mutation-операции (`add/delete/clear`) вынесены в `traffic_appmarks_ops_mutations.go`
|
||
- Step F1.11.102: [x] выполнен разнос `routes_cache_helpers.go`: file io/helper'ы вынесены в `routes_cache_helpers_files.go`, route parse/restore в `routes_cache_helpers_routes.go`, nft snapshot/parser в `routes_cache_helpers_nft.go`, meta helpers в `routes_cache_helpers_meta.go`; базовый файл оставлен как module header
|
||
- Step F1.11.103: [x] выполнен разнос `transport_client_runtime_alloc_normalize.go`: reconcile marks/prefs allocation блок вынесен в `transport_client_runtime_alloc_reconcile.go`, базовый normalize-файл оставлен для state canonicalization и deterministic client merge
|
||
- Step F1.11.104: [x] выполнен разнос `dns_settings_benchmark.go`: HTTP benchmark endpoint вынесен в `dns_settings_benchmark_handler.go`, базовый файл оставлен для benchmark normalize/run/topN helper'ов и package aliases
|
||
- Step F1.11.105: [x] выполнен разнос `transport_singbox_profiles_card.go`: route dispatch + card method switch оставлены в базовом файле, `GET` вынесен в `transport_singbox_profiles_card_get.go`, `PATCH` в `transport_singbox_profiles_card_patch.go`, `DELETE` в `transport_singbox_profiles_card_delete.go`
|
||
- Step F1.11.106: [x] выполнен разнос `types_singbox.go`: профильные DTO вынесены в `types_singbox_profiles.go`, validate/render/apply/rollback DTO вынесены в `types_singbox_flow.go`, history DTO вынесены в `types_singbox_history.go`; базовый файл оставлен как module header
|
||
- Step F1.11.107: [x] выполнен разнос `routes_cache.go`: save snapshot flow вынесен в `routes_cache_save.go`, restore flow вынесен в `routes_cache_restore.go`, базовый файл оставлен для shared cache meta type
|
||
- Step F1.11.108: [x] выполнен разнос `transport_tokens_state.go`: clients state load/save вынесены в `transport_tokens_state_clients.go`, policy state/snapshot load/save вынесены в `transport_tokens_state_policy.go`, conflicts state load/save вынесены в `transport_tokens_state_conflicts.go`; базовый файл оставлен для token/digest helper'ов
|
||
- Step F1.11.109: [x] выполнен разнос `routes_handlers_service.go`: routes service action handler/helper'ы вынесены в `routes_handlers_service_action.go`, timer handlers/helper'ы вынесены в `routes_handlers_service_timer.go`, базовый файл оставлен как module header
|
||
- Step F1.11.110: [x] выполнен разнос `vpn_login_session_handlers.go`: login state helper вынесен в `vpn_login_session_handlers_state_helper.go`, start/state/action/stop endpoints вынесены в `vpn_login_session_handlers_{start,state,action,stop}.go`, базовый файл оставлен как module header
|
||
- Step F1.11.111: [x] выполнен разнос `resolver_bridge_pipeline.go`: planning/runtime tuning wrappers вынесены в `resolver_bridge_pipeline_planning.go`, execution/recheck/artifacts wrappers вынесены в `resolver_bridge_pipeline_exec.go`, базовый файл оставлен как module header
|
||
- Step F1.11.112: [x] выполнен разнос `transport_singbox_profiles_flow_rollback.go`: HTTP wrapper + decode body вынесены в базовый файл, rollback execution flow вынесен в `transport_singbox_profiles_flow_rollback_exec.go` без изменения кодов/контрактов endpoint `POST /api/v1/transport/singbox/profiles/{id}/rollback`
|
||
- Step F1.11.113: [x] выполнен stage-разнос `transport_singbox_profiles_flow_apply_exec.go`: preflight/validate/render и dispatch оставлены в базовом execution-файле, target-config/runtime/commit вынесены в `transport_singbox_profiles_flow_apply_exec_rendered.go` без изменения API-контрактов `POST /api/v1/transport/singbox/profiles/{id}/apply`
|
||
- Step F1.11.114: [x] выполнен stage-разнос `transport_backends_adapter_systemd_action.go`: lifecycle action orchestration оставлена в базовом файле, pre/post hooks вынесены в `transport_backends_adapter_systemd_action_hooks.go`, unit execution + auto-provision path вынесены в `transport_backends_adapter_systemd_action_exec.go` (без изменения backend/runtime контрактов)
|
||
- Step F1.11.115: [x] выполнен stage-разнос `transport_singbox_profiles_flow_rollback_exec.go`: prepare/select/restore шаг оставлен в базовом execution-файле, runtime+commit/history шаг вынесен в `transport_singbox_profiles_flow_rollback_exec_restored.go` без изменения API-контракта rollback endpoint
|
||
- Step F1.11.116: [x] выполнен разнос `transport_singbox_profiles_flow_render.go`: HTTP wrapper + body decode оставлены в базовом файле, render execution flow вынесен в `transport_singbox_profiles_flow_render_exec.go` без изменения API-контракта endpoint `POST /api/v1/transport/singbox/profiles/{id}/render`
|
||
- Step F1.11.117: [x] выполнен stage-разнос `routes_update_stage_resolve.go`: wildcard runtime merge вынесен в `routes_update_stage_resolve_wildcard_runtime.go`, запись resolver artifacts вынесена в `routes_update_stage_resolve_artifacts.go`, базовый stage-файл оставлен orchestration-слоем (без изменения routes-update поведения)
|
||
- Step F1.11.118: [x] выполнен stage-разнос `transport_backends_adapter_systemd_provision.go`: input/preflight build вынесен в `transport_backends_adapter_systemd_provision_inputs.go`, unit write + daemon-reload/enable finalize вынесены в `transport_backends_adapter_systemd_provision_finalize.go`, базовый `Provision()` оставлен orchestration-слоем без изменения runtime-контрактов
|
||
- Step F1.11.119: [x] выполнен разнос `transport_client_runtime_state.go`: client create/kind/capabilities вынесены в `transport_client_runtime_create.go`, shared helpers (`find index`, `cloneMap`) вынесены в `transport_client_runtime_helpers.go`, базовый state-файл оставлен для runtime snapshot/normalize/uptime helper'ов
|
||
- Step F1.11.120: [x] выполнен разнос `traffic_appmarks_ops_mutations.go`: mutation flow разделён на role-файлы `traffic_appmarks_ops_mutations_add.go`, `traffic_appmarks_ops_mutations_delete.go`, `traffic_appmarks_ops_mutations_clear.go`; базовый файл оставлен как module header без изменения appmarks API/runtime поведения
|
||
- Step F1.11.121: [x] выполнен stage-разнос `transport_handlers_actions_exec_lifecycle.go`: HTTP lifecycle handler оставлен в базовом файле, preflight flow вынесен в `transport_handlers_actions_exec_lifecycle_preflight.go`, locked execution/save/response flow вынесен в `transport_handlers_actions_exec_lifecycle_locked.go` без изменения lifecycle API-контракта
|
||
- Step F1.11.122: [x] выполнен stage-разнос `dns_settings_state_upstreams.go`: pool store/load/save вынесены в `dns_settings_state_upstreams_pool_store.go`, conf file load/save вынесены в `dns_settings_state_upstreams_conf_store.go`, базовый upstream-state файл оставлен для conversion/normalize/load-enabled и legacy facade методов
|
||
- Step F1.11.123: [x] выполнен stage-разнос `traffic_appmarks_handlers_post.go`: request decode/validate вынесен в `traffic_appmarks_handlers_post_parse.go`, op-specific execution вынесен в `traffic_appmarks_handlers_post_ops.go`, базовый POST handler оставлен как thin dispatch без изменения API-контракта
|
||
- Step F1.11.124: [x] выполнен stage-разнос `traffic_audit_checks.go`: duplicate-detector helper'ы вынесены в `traffic_audit_duplicates.go`, NFT appmark rule parse helper'ы вынесены в `traffic_audit_nft_parse.go`, базовый checks-файл оставлен для audit-orchestration (`auditNftAppMarks`) без изменения audit API/вывода
|
||
- Step F1.11.125: [x] зафиксирован stop-condition декомпозиции Go-ядра: max non-test файл в `selective-vpn-api/app` = `195` строк; дальнейший разнос отложен как low-priority polish
|
||
- Step F1.12: [x] выполнить пакетную декомпозицию `selective-vpn-gui/api_client.py` в `selective-vpn-gui/api/*` с сохранением legacy facade `api_client.py`
|
||
- Step F1.13: [~] выполнить декомпозицию `selective-vpn-api/app/traffic_mode.go` на подпакеты `app/trafficmode/*` без изменения policy-routing поведения
|
||
- Step F1.13.1: [x] вынесены normalize helper'ы (`tokenize/subnet/uid/cgroup`) в `app/trafficmode/normalize.go`, в `app/traffic_mode.go` оставлены фасады
|
||
- Step F1.13.2: [x] вынесен parser auto-local-bypass маршрутов в `app/trafficmode/autolocal.go` (`ParseAutoBypassRoutes` + helper'ы), в `app/traffic_mode.go` сохранены совместимые фасады для `traffic_candidates`
|
||
- Step F1.13.3: [x] вынесен cgroup->uid resolution блок в `app/trafficmode/cgroup.go` (`CgroupCandidates/ResolveCgroupPath/CollectPIDsFromCgroup/UIDRangeForPID/ResolveCgroupUIDRanges`), в `app/traffic_mode.go` сохранены фасады
|
||
- Step F1.13.4: [x] вынесен ingress-reply bypass nft-контур в `app/trafficmode/ingress.go` (`Ensure/Flush/Enable/Disable/Active`) с сохранением фасадов в `app/traffic_mode.go`
|
||
- Step F1.13.5: [x] вынесен route-rules/probe блок в `app/trafficmode/rules.go` (`ReadRules/DetectAppliedMode/ProbeMode/PrefStr`) с сохранением фасадов и `trafficRulesState` alias в `app/traffic_mode.go`
|
||
- Step F1.13.6: [x] удалены лишние внутренние wrapper-функции `traffic_mode.go` (cgroup/nftObjectMissing), `buildEffectiveOverrides` переведён на прямые вызовы `trafficmodepkg.ResolveCgroupUIDRanges`
|
||
- Step F1.13.7: [x] вынесен `ip rule` apply/remove блок в `app/trafficmode/apply.go` (`RemoveRulesForTable`, `ApplyRule`, `ApplyOverrides`) с сохранением фасадов в `app/traffic_mode.go`
|
||
- Step F1.13.8: [x] вынесен interface selection/resolve блок в `app/trafficmode/interfaces.go` (`NormalizePreferredIface`, `IfaceExists`, `ListUpIfaces`, `ListSelectableIfaces`, `ResolveTrafficIface`, `IsVPNLikeIface`), `app/traffic_mode.go` переведён на thin-facade
|
||
- Step F1.13.9: [x] вынесены HTTP/lock handlers traffic-mode в `app/traffic_mode_handlers.go` (`mode get/post`, `advanced reset`, `interfaces`, `mode test`, apply-lock); `app/traffic_mode.go` сокращён до core policy/apply/evaluate блока
|
||
- Step F1.13.10: [x] вынесены state/normalization/persistence helper'ы traffic-mode в `app/traffic_mode_state.go` (`normalize*`, `load/save/infer state`), `app/traffic_mode.go` оставлен для routing apply/evaluate orchestration
|
||
- Step F1.13.11: [x] завершена декомпозиция `traffic_mode` core: вынесены `iface/config` helper'ы в `app/traffic_mode_iface.go`, apply-orchestration в `app/traffic_mode_apply.go`, runtime-check/evaluate в `app/traffic_mode_evaluate.go`; `app/traffic_mode.go` оставлен с константами/описанием модуля
|
||
- Step F3: [ ] (deferred) выделить собственную библиотеку модулей Go-ядра (linux-first), чтобы backend собирался из переиспользуемых пакетов
|
||
- Step F3.1: [ ] вынести `netns` как отдельный debug-модуль библиотеки (`pkg/netnsdebug` / `pkg/netns`), зафиксировать Linux-only scope
|
||
- Step F3.2: [ ] зафиксировать, что `netns` модуль не является целью для mobile SDK (iOS/Android out-of-scope), используется для backend/desktop test-contour
|
||
- Step F3.2.1: [ ] не реализовывать `netns` интеграцию для mobile-клиентов; для iOS/Android использовать только API control-plane без system/netns модулей
|
||
- Step F3.3: [ ] выделить стабильные библиотечные пакеты ядра (`pkg/orchestrator`, `pkg/transport`, `pkg/pbr`, `pkg/resolver`) с явными интерфейсами зависимостей
|
||
- Step F3.4: [ ] перевести backend на использование этих пакетов через thin-facade слой `app/*` без изменения внешнего API-контракта
|
||
- Step F3.5: [ ] подготовить отдельный документ архитектуры библиотеки модулей и runbook миграции (`docs/phase-f/F3_CORE_MODULE_LIBRARY_PLAN.md`)
|
||
- Step F1.14: [~] выполнить декомпозицию `selective-vpn-api/app/egress_identity.go` на подпакеты `app/egress*` без изменения egress API/SWR-поведения
|
||
- Step F1.14.1: [x] вынесены egress utility helper'ы в `app/egressutil/util.go` (parse ip/geo, endpoint lists, curl/wget path, socks url parse, URL host resolve, generic any/string helpers)
|
||
- Step F1.14.2: [x] удалены лишние локальные egress helper-функции в `app/egress_identity.go` (dead wrappers), оставлены только необходимые facade entrypoints
|
||
- Step F1.14.3: [x] вынесены helper'ы scope/identity/probe-loop в `app/egressutil` (`ParseScope`, `IdentityChanged`, `ProbeFirstSuccess`) с сохранением app-facade функций
|
||
- Step F1.14.4: [x] вынесен HTTP fetch helper в `app/egressutil/http.go` (`HTTPGetBody`), egress probe/geo вызовы в `app/egress_identity.go` переведены на пакетный вызов
|
||
- Step F1.14.5: [x] удалены чистые egress wrapper-функции в `app/egress_identity.go` (parse geo/ip, country normalize, curl/wget/timeout/resolve helpers); app-тесты переведены на прямые вызовы `app/egressutil`
|
||
- Step F1.15: [~] выполнить декомпозицию `selective-vpn-api/app/dns_settings.go` на подпакеты `app/dnscfg/*` без изменения DNS API/SmartDNS поведения
|
||
- Step F1.15.1: [x] вынесены SmartDNS addr/upstream normalizers в `app/dnscfg/smartdns.go` (`ResolveDefaultSmartDNSAddr`, `SmartDNSAddrFromConfig`, `NormalizeDNSUpstream`, `NormalizeSmartDNSAddr`) с сохранением фасадов в `app/dns_settings.go`
|
||
- Step F1.15.2: [x] вынесен SmartDNS systemd helper-блок в `app/dnscfg/systemd.go` (`UnitState`, `RunUnitAction`) с сохранением фасада `runSmartdnsUnitAction` и прежнего success-message формата
|
||
- Step F1.15.3: [x] вынесен DNS benchmark engine в `app/dnscfg/benchmark.go` (`NormalizeProfile/Options/Upstreams/Domains`, `BenchmarkDNSUpstream`, `DNSLookupAOnce`, `BenchmarkTopN`) с сохранением HTTP/API-контракта в `app/dns_settings.go`
|
||
- Step F1.15.4: [x] вынесены pool helper'ы в `app/dnscfg/pool.go` (`NormalizeUpstreamPoolItems`, `UpstreamPoolFromLegacy`, `UpstreamPoolToLegacy`, `EnabledPool`) с сохранением фасадов в `app/dns_settings.go`
|
||
- Step F1.15.5: [x] вынесен mode-state storage в `app/dnscfg/mode.go` (`ModeConfig`, `ModeState`, `LoadMode`, `SaveMode`) с сохранением JSON-контракта `dns_mode.json`
|
||
- Step F1.15.6: [x] очищен `app/dns_settings.go` от лишних benchmark-wrapper'ов (прямые вызовы `dnscfg`), без изменения endpoint/DTO
|
||
- Step F1.15.7: [x] вынесены parse/normalize/render helper'ы `dns-upstreams.conf` в `app/dnscfg/upstreams.go` (`ParseUpstreamsConf`, `NormalizeUpstreams`, `RenderUpstreamsConf`) с сохранением файлового/JSON mirror поведения
|
||
- Step F1.15.8: [x] вынесен SmartDNS prewarm engine в `app/dnscfg/prewarm.go` (`RunPrewarm` + metrics/domain expansion/manual merge/log pipeline) через dependency-injection callbacks
|
||
- Step F1.15.9: [x] runtime/prewarm HTTP handlers вынесены из `app/dns_settings.go` в `app/dns_smartdns_handlers.go` (в `dns_settings.go` оставлен DNS settings facade слой)
|
||
- Step F2.1: [x] выполнен production-upgrade `SingBox` runtime на шаблонный `systemd` unit `singbox@.service` (instance-per-profile без генерации отдельного unit-файла на каждый профиль)
|
||
- Step F2.2: [x] после `F2.1` внедрён миграционный и cleanup-контур (`old per-profile units -> template instances`, idempotent remove/disable/reset-failed, сохранение совместимости API)
|
||
- Step F2.1.1: [x] добавлен template unit `singbox@.service` + drop-in env strategy (`SVPN_TRANSPORT_ID`, `SVPN_CONFIG_PATH`, `SVPN_NETNS_*`) и зафиксированы hardening/tuning defaults
|
||
- Step F2.1.2: [x] backend renderer/provision переведён на instance model (`singbox@<profile_id>.service`) без изменения API-контракта `transport/clients/*`
|
||
- Step F2.1.3: [x] lifecycle/health/metrics path переведён на instance model (`start/stop/restart/is-active/reset-failed`) с сохранением runtime-code/ошибок
|
||
- Step F2.1.4: [x] добавлены e2e/smoke проверки instance model (`create/start/switch/stop/delete`) включая netns-enabled профили и auto-provision fallback
|
||
- Step F2.2.1: [x] реализован one-shot migrator `legacy unit -> template instance` (deterministic mapping + marker/ownership check + dry-run)
|
||
- Step F2.2.2: [x] реализован cleanup legacy artifacts (`disable/stop/reset-failed/remove unit file + daemon-reload`) с idempotent повторным запуском
|
||
- Step F2.2.3: [x] добавлен rollback-план миграции (`template -> legacy`) для инцидентов деплоя и зафиксирован runbook
|
||
- Step F2.2.4: [x] обновлены docs/scripts деплоя и preflight-check для новой модели (`singbox@.service` обязательный runtime dependency)
|
||
- Step F1.3: [x] выполнить декомпозицию `selective-vpn-gui/dashboard_controller.py` на domain-контроллеры с сохранением фасада `DashboardController`
|
||
- Step F1.4: [x] выполнить декомпозицию `selective-vpn-gui/vpn_dashboard_qt.py` на `main_window/*` mixin-модули (без изменения поведения)
|
||
- Step Z1: [ ] (experimental tail) глобальный L7 orchestration-layer поверх текущего ядра для multi-engine (`AdGuardVPN` / `SingBox` / future transports) с единым policy-adapter слоем
|
||
- Step Z1.1: [~] ранняя подготовка разрешена до старта Z1 runtime: единая policy-модель/intent ownership, scope-нормализация, telemetry schema и compatibility matrix; без включения L7 data-plane в прод-путь
|
||
|
||
## Что уже сделано
|
||
- 2026-03-20: закрыт F2.2.1 (one-shot migrator legacy unit -> template instance) для SingBox systemd path:
|
||
- в pre-action hook (start/restart) добавлен best-effort мигратор legacy unit singbox-<id>.service -> template instance singbox@<id>.service;
|
||
- миграция использует deterministic candidate mapping (instance-id/unit + client-id sanitize), ownership-check по marker SVPN_TRANSPORT_ID и не трогает foreign unit-файлы;
|
||
- добавлен dry-run режим через config.singbox_legacy_unit_migrate_dry_run=true (без stop/disable/remove), плюс флаг отключения config.singbox_legacy_unit_migrate=false;
|
||
- после фактической миграции выполняются daemon-reload + reset-failed для legacy unit (best-effort, idempotent).
|
||
- cleanup-path Cleanup() для template-instance расширен: удаляются и owned legacy unit-файлы singbox-<id>.service (stop/disable/remove + daemon-reload/reset-failed), template unit singbox@.service сохраняется.
|
||
- добавлены тесты transport_systemd_singbox_template_test.go: migrate owned legacy, dry-run, ownership mismatch; локально go test ./... (ok).
|
||
- 2026-03-20: закрыт E3.3 (interface orchestrator + M3 owner-scope):
|
||
- compile-plan перешёл на owner-scoped nft naming: `owner_scope=iface+client`, `nft_set=agvpn_pi_<owner_scope>_<selector>`;
|
||
- для длинных `iface/client` добавлен deterministic hash fallback, длина set name ограничена 63 символами;
|
||
- `TransportPolicyCompileRule/Set` расширены полем `owner_scope` для observability/runtime debug;
|
||
- добавлены тесты: `TestCompileTransportPolicyPlanUsesOwnerScopedNftSets`, `TestTransportPolicyNftSetNameDeterministicAndBounded`; локально `go test ./...` (ok).
|
||
- 2026-03-20: закрыт E3.5 domain destination-lock coverage:
|
||
- `detectTransportDestinationLockConflicts` расширен на `domain` selector (включая `*.domain`), теперь owner-switch проверяет sticky-lock по `domain-cache` (`direct+wildcard`) без сетевых запросов;
|
||
- добавлен `domain-cache` bridge hook в policy guard (`transportPolicyLoadDomainCacheState`, path: `/var/lib/selective-vpn/domain-cache.json`), с lazy-load и per-selector memoization;
|
||
- добавлены тесты: `TestDetectTransportDestinationLockConflictsDomainFromCache`, `TestDetectTransportDestinationLockConflictsWildcardDomainFromCache`, `TestDetectTransportDestinationLockConflictsSkipsDomainWithoutCacheHit`; локально `go test ./...` (ok).
|
||
- 2026-03-13: уменьшен trace-spam в transport-контуре:
|
||
- добавлен rate-limited trace helper (`appendTraceLineRateLimited`) с дедупом повторяющихся строк в окне времени;
|
||
- на throttled-логирование переведены шумные ветки (`singbox dns migrate warning`, `bootstrap bypass failed`, `netns setup/cleanup failed`, `systemctl unit missing/reset-failed`);
|
||
- повторяющиеся сообщения теперь схлопываются с компактной `trace dedup: suppressed=...` строкой.
|
||
- 2026-03-13: стартовал `F2.1` (SingBox instance-model foundation):
|
||
- в `transportcfg.BackendUnit` для `kind=singbox` добавлен резолв unit в `singbox@<client_id>.service`;
|
||
- в прод-коде отключён auto-map legacy unit-имён `sing-box.service` / `singbox-*.service`;
|
||
- для `kind=singbox` включена жёсткая нормализация `config.unit=singbox@.service` на create/patch/state-save;
|
||
- добавлены/обновлены тесты `app/transportcfg/runtime_helpers_test.go` (default/template/custom/sanitize).
|
||
- 2026-03-13: продолжен `F2.1` (template/drop-in provisioning для SingBox):
|
||
- `systemd` provision-path для `singbox@<id>.service` переведён на модель `template + per-instance drop-in`;
|
||
- добавлена генерация/обновление `singbox@.service` (managed template) и drop-in `singbox@<id>.service.d/10-selective-vpn.conf`;
|
||
- в drop-in пробрасываются env-поля `SVPN_TRANSPORT_ID`, `SVPN_TRANSPORT_KIND`, `SVPN_CONFIG_PATH`, `SVPN_NETNS_*`;
|
||
- cleanup для template-instance теперь удаляет клиентский drop-in (с ownership-check) и не удаляет общий template unit;
|
||
- добавлены e2e-like unit tests: `transport_systemd_singbox_template_test.go` (provision, auto-provision-on-missing, cleanup-keep-template);
|
||
- обновлены runtime dependency docs/script: добавлен check `singbox@.service` (legacy `sing-box.service` сохранён как compat).
|
||
- 2026-03-13: завершён cleanup legacy unit-контур для SingBox:
|
||
- state `/var/lib/selective-vpn/transport-clients.json` мигрирован на `config.unit=singbox@.service` для всех `kind=singbox` профилей;
|
||
- legacy unit-файлы `singbox-*.service` удалены из `/etc/systemd/system`, оставлен только `singbox@.service` + instance drop-in;
|
||
- после сборки/рестарта API прогнаны проверки: `go test ./...` (ok), `systemctl list-unit-files singbox*` (только template), runtime start через `singbox@<id>.service` (ok).
|
||
- 2026-03-13: расширены smoke/e2e проверки под instance-model:
|
||
- `tests/transport_systemd_real_e2e.py` обновлён: SingBox проверяется через default instance unit (`singbox@<client_id>.service`) + ownership в drop-in + сохранение `singbox@.service` после delete;
|
||
- `tests/transport_production_like_e2e.py` обновлён: production-like SingBox-кейс переведён на instance model (проверка template/drop-in артефактов и cleanup);
|
||
- добавлен unit-test на netns-instance drop-in env (`SVPN_NETNS_ENABLED`, `SVPN_NETNS_NAME`) в `transport_systemd_singbox_template_test.go`;
|
||
- прогон локальных проверок: `go test ./...` (ok), `python3 -m py_compile` для обновлённых e2e-скриптов (ok).
|
||
- 2026-03-12: продолжен рефакторинг `F1.11/F1.13` без изменения API-контрактов:
|
||
- в `app/transportcfg` добавлен `secrets_helpers.go` (нормализация/маскирование/сравнение secret-map + файловые JSON helper'ы);
|
||
- `app/transport_singbox_profiles.go` переведён на `transportcfg` secrets-helper'ы; удалены локальные дубли `normalizeSingBoxSecretUpdates`, `maskSingBoxSecrets`, `cloneStringMap`, `equalStringMap`;
|
||
- `app/transport_singbox_profiles_flow.go` дополнительно очищен от локальных wrapper-функций `config io/file optional/diff`; вызовы переведены на прямой `transportcfg`;
|
||
- state/normalization функции `singbox profiles` вынесены из `app/transport_singbox_profiles.go` в `app/transport_singbox_profiles_state.go` (декомпозиция без изменения DTO/handlers);
|
||
- функции secrets patch/store + error helpers `singbox profiles` вынесены в отдельные файлы (`transport_singbox_profiles_secrets.go`, `transport_singbox_profiles_errors.go`);
|
||
- create/patch mutate-логика `singbox profiles` вынесена в `transport_singbox_profiles_mutate.go`, а основной `transport_singbox_profiles.go` сокращён до `413` строк;
|
||
- runtime/history helper'ы `singbox profile flow` вынесены в `transport_singbox_profiles_runtime.go` и `transport_singbox_profiles_history.go`;
|
||
- eval/render helper'ы `singbox profile flow` вынесены в `transport_singbox_profiles_eval.go`;
|
||
- state/normalization helper'ы `transport client runtime` вынесены в `transport_client_runtime_state.go`, а `transport_client_runtime.go` сокращён до orchestration/lifecycle части;
|
||
- health/netns helper'ы `transport client runtime` вынесены в `transport_client_runtime_health.go` и `transport_client_runtime_netns.go`;
|
||
- `transport_handlers_clients.go` декомпозирован: netns-toggle и action-dispatch вынесены в отдельные файлы `transport_handlers_netns.go`/`transport_handlers_actions.go` без изменения endpoint-контрактов;
|
||
- helper-блок `routes_update.go` вынесен в `routes_update_helpers.go` (table/list/io/counter/fs/readiness), основной `routes_update.go` оставлен как orchestration-пайплайн;
|
||
- helper/state блок `dns_settings.go` вынесен в `dns_settings_state.go` (upstream pool + mode/smartdns helpers + dnscfg adapters), handler-файл оставлен как control-plane API фасад;
|
||
- DNS/SmartDNS типы вынесены из `types.go` в `types_dns.go` (контракты API/JSON не менялись);
|
||
- `types.go` дополнительно разрезан на доменные файлы (`types_traffic.go`, `types_egress.go`, `types_transport.go`, `types_singbox.go`) с сохранением совместимости по полям/тегам;
|
||
- `transport_handlers_policy.go` декомпозирован: мутационные endpoints (`validate/apply/rollback`) вынесены в `transport_handlers_policy_mutations.go`;
|
||
- `handleTransportHealthRefresh` вынесен в `transport_handlers_health_refresh.go`, а `transport_health_refresh.go` оставлен runtime-блоком фоновых health probe;
|
||
- из `egress_identity.go` вынесен probe/helper блок в `egress_identity_probe.go` (system/iface/netns/proxy probes + endpoint helpers + socks/iface bind helpers);
|
||
- из `routes_handlers.go` вынесен service/timer блок в `routes_handlers_service.go` (systemd service action, timer get/toggle/set);
|
||
- из `routes_handlers.go` вынесен clear/precheck/op-lock блок в `routes_handlers_ops.go` (clear/cache-restore/precheck-debug/status-snapshot/op-lock helpers);
|
||
- `egress_identity` endpoint handlers (`GET/refresh`) вынесены в `egress_identity_handlers.go`, основной файл оставлен как service/runtime orchestration;
|
||
- монолит `traffic_appmarks.go` разнесён по ролям на `traffic_appmarks_handlers.go` (HTTP), `traffic_appmarks_ops.go` (add/del/clear/status) и `traffic_appmarks_runtime.go` (nft/cgroup/state/restore), базовый файл оставлен с core-константами и alias-типами;
|
||
- `transport_singbox_profiles_flow.go` сокращён с `1336` до `893` строк без изменения API-контрактов;
|
||
- в `app/trafficmode` добавлен `interfaces.go` (iface detect/select/resolve), а `app/traffic_mode.go` переведён на thin-facade для этого блока;
|
||
- handlers/apply-lock блок `traffic mode` вынесен в `app/traffic_mode_handlers.go` без изменения endpoint-контрактов (`/api/v1/traffic/mode`, `/traffic/interfaces`, `/traffic/mode/test`, `/traffic/advanced/reset`);
|
||
- state/normalization/persistence блок `traffic mode` вынесен в `app/traffic_mode_state.go`, основной `traffic_mode.go` очищен до apply/evaluate orchestration;
|
||
- завершён разнос remaining-core `traffic mode`: `traffic_mode_iface.go` (iface/table/config helpers), `traffic_mode_apply.go` (build/apply overrides + route base + apply mode), `traffic_mode_evaluate.go` (rules read/probe/evaluate health);
|
||
- handlers/ops/runtime блок `traffic app marks` разнесён на отдельные файлы (`traffic_appmarks_handlers.go`, `traffic_appmarks_ops.go`, `traffic_appmarks_runtime.go`), core-файл `traffic_appmarks.go` сокращён до констант/типов (`34` строки);
|
||
- размеры крупных файлов дополнительно снижены: `traffic_mode.go` `820 -> 35`;
|
||
- helper-блок `routes cache` вынесен в `routes_cache_helpers.go`, основной `routes_cache.go` сокращён до orchestration restore/save пайплайна;
|
||
- `egress identity` разнесён на файлы ролей: `providers`, `refresh`, `geo`, `scope`; базовый `egress_identity.go` сокращён до констант/типов/bootstrap;
|
||
- `vpn_handlers.go` (`531` строка) разложен на отдельные роли (`auth/status/locations`) с сохранением endpoint-path и прежнего поведения location switch / autoconnect;
|
||
- `autoloop.go` очищен от внутренних helper closure-блоков; helper-логика вынесена в `autoloop_helpers.go`, loop orchestration оставлен неизменным по таймингам и reconnect-пайплайну;
|
||
- `transport_backends_adapter_systemd.go` разложен на role-файлы (`action/health/provision/cleanup`) без изменения lifecycle/runtime контрактов backend `systemd`;
|
||
- выполнен stage-разнос `routes_update.go`: preflight/policy/nft/resolve/artifacts вынесены в `routes_update_stage_*.go`, основной файл оставлен orchestration-фасадом;
|
||
- `vpn_login_session.go` разложен на manager/PTY и HTTP handlers: endpoint-обработчики вынесены в `vpn_login_session_handlers.go` без изменения API login session;
|
||
- `vpn_locations_cache.go` разложен на role-файлы (`refresh`, `parse`, `store`) без изменения SWR-контракта (`stale/refresh_in_progress/next_retry_at`) и endpoint поведения;
|
||
- `transport_health_refresh.go` разложен на role-файлы (`state`, `queue`, `probe`, `compare`) без изменения фонового health-refresh контракта (`fresh TTL`, `backoff`, `max parallel probes`, `persist min age`);
|
||
- `transport_client_runtime_alloc.go` разложен на role-файлы (`normalize/reconcile`, `table/mark/pref slots`) без изменения allocation-контракта (`MarkHex`, `PriorityBase`, `RoutingTable`, duplicate-id winner);
|
||
- `transport_netns.go` разложен на role-файлы (`spec`, `rules`, `run`) без изменения netns lifecycle-контракта (`ensure`, `cleanup`, nft NAT comment tags, policy-route update);
|
||
- `transport_bootstrap_bypass.go` разложен на role-файлы (`candidates`, `resolve`, `routes`, `state`) без изменения bypass-контракта (`start/restart sync`, `stop cleanup`, strict mode, bootstrap state persistence);
|
||
- `transport_singbox_profiles_flow.go` разложен на handler role-файлы (`validate`, `render`, `apply`, `rollback`, `history`) с сохранением endpoint-контрактов `/api/v1/transport/singbox/profiles/{id}/{action}`;
|
||
- `transport_singbox_profiles.go` разложен на role-файлы (`list`, `card`, `features`) с сохранением endpoint-контрактов `/api/v1/transport/singbox/profiles*`;
|
||
- benchmark-контур DNS вынесен из `dns_settings.go` в `dns_settings_benchmark.go` с сохранением API-контракта `POST /api/v1/dns/benchmark` и профилей `quick/load`;
|
||
- `resolver_bridge.go` разложен на role-файлы (`utils`, `cache`, `dns`, `pipeline`) без изменения bridge-контракта с подпакетом `app/resolver`;
|
||
- `transport_backends.go` разложен на role-файлы (`selection/config facade`, `probe adapters`, `runtime-mode backends`) без изменения backend action/health/provision/cleanup контрактов;
|
||
- `vpn_login_session.go` разложен на role-файлы (`models/manager`, `state methods`, `pty lifecycle`, `http handlers`) без изменения login-session endpoint-контрактов;
|
||
- размеры крупных файлов снижены: `traffic_mode.go` `923 -> 820`, `transport_singbox_profiles.go` `979 -> 897`, `transport_singbox_profiles_flow.go` `1372 -> 1336`;
|
||
- после изменений выполнены `go test ./...` и `go build ./...` — успешно.
|
||
- 2026-03-11: продолжен шаг `F1.11` (поэтапный вынос transport в подпакеты):
|
||
- добавлен новый подпакет `selective-vpn-api/app/transportcfg` и файл `runtime_helpers.go`;
|
||
- в подпакет перенесены runtime/config helper'ы transport backend:
|
||
- `ConfigString`, `ConfigBool`;
|
||
- `RuntimeMode`;
|
||
- `BackendUnit`/`DefaultBackendUnit`;
|
||
- `DNSTTSSHTunnelEnabled`, `DNSTTSSHUnit`;
|
||
- `SystemdActionUnits`, `SystemdHealthUnits`;
|
||
- добавлен `selective-vpn-api/app/transportcfg/systemd_helpers.go` и в подпакет перенесены systemd helper'ы transport backend:
|
||
- unit-name/path/ownership/file-write helper'ы;
|
||
- systemd unit renderer'ы (`primary` + `ssh-overlay`);
|
||
- service tuning/hardening нормализация и prefix-fallback parser'ы;
|
||
- добавлен `selective-vpn-api/app/transportcfg/exec_helpers.go` и в подпакет перенесены exec/binary helper'ы transport backend:
|
||
- template command builders (`singbox`, `dnstt`, `phoenix`, `ssh-overlay`);
|
||
- binary resolution/validation helpers (`ResolveBinary`, `FindBinaryPath`, `BinaryExists`, packaging profile);
|
||
- shell/config helpers (`ShellJoinArgs`, `ShellQuoteArg`, `ConfigInt`, `DefaultConfigPath`);
|
||
- добавлен `selective-vpn-api/app/transportcfg/probe_helpers.go` и в подпакет перенесены probe helper'ы transport backend:
|
||
- latency probing pipeline (`ProbeClientLatency`, host/netns endpoint probe);
|
||
- endpoint extraction/parsing/dedupe helpers для config/singbox-json;
|
||
- shared parse helpers (`ParseInt`, `SplitCSV`).
|
||
- app-facade сохранён внутри существующих transport-файлов (`transport_backends.go`, `transport_backends_adapter_systemd.go`), а отдельные файлы `transport_backends_runtime_helpers.go` и `transport_backends_systemd_helpers.go` удалены для снижения шума в корне `app/`;
|
||
- отдельный файл `transport_backends_exec_helpers.go` удалён; совместимые фасады сохранены в `transport_backends.go`;
|
||
- отдельный файл `transport_backends_probe_helpers.go` удалён; runtime dial/netns фасады и backward-compatible symbols для тестов собраны в `transport_backends.go`;
|
||
- добавлен подпакет `selective-vpn-api/app/eventsbus` (`Bus`, `Push`, `Since`); `app/events_bus.go` переведён в thin-adapter/facade с сохранением контракта `events.push/events.since` и `envInt`;
|
||
- добавлен подпакет `selective-vpn-api/app/apiroutes` (`Register` + доменные route-registrars), `app/api_routes.go` переведён на dependency-assembly facade;
|
||
- удалены файлы route-registry из корня `app`: `api_routes_core.go`, `api_routes_dns.go`, `api_routes_trace.go`, `api_routes_traffic.go`, `api_routes_transport.go`, `api_routes_vpn.go`;
|
||
- удалён файл `transport_backends_adapters_misc.go`; типы `transportUnsupportedRuntimeBackend` и `transportMockBackend` перенесены в `transport_backends.go` (единая точка transport-facade);
|
||
- добавлен подпакет `selective-vpn-api/app/syscmd` и перенесены низкоуровневые command helper'ы (`RunCommand`, `RunCommandTimeout`, `CheckPolicyRoute`); `app/shell.go` оставлен как thin-facade;
|
||
- добавлен подпакет `selective-vpn-api/app/httpx` и перенесены общие HTTP helper'ы (`LogRequests`, `WriteJSON`, `HandleHealthz`); `app/http_helpers.go` переведён в thin-facade;
|
||
- добавлен подпакет `selective-vpn-api/app/eventstream` и перенесён SSE polling/heartbeat loop (`ParseSinceID`, `Serve`); `app/events_handlers.go` оставлен как adapter к `events.since`;
|
||
- добавлен подпакет `selective-vpn-api/app/refreshcoord` и перенесена SWR/backoff state-machine (`Coordinator`, `Snapshot`); `app/refresh_coordinator.go` оставлен как thin-facade с прежними методами (`beginRefresh/finish*/snapshot`) для совместимости;
|
||
- в `egress_identity.go` убран прямой доступ к внутренним полям SWR (`refreshInProgress`, `nextRetryAt`) — заменено на фасадные методы (`refreshInProgress()`, `nextRetryAt()`, `clearBackoff()`) для безопасной декомпозиции;
|
||
- добавлен подпакет `selective-vpn-api/app/nftupdate` и перенесён алгоритм обновления nft-set (`interval compression`, `atomic transaction`, `chunked fallback`);
|
||
- `app/nft_update.go` переведён в thin-facade с прежним публичным контрактом (`nftUpdateIPsSmart`, `nftUpdateSetIPsSmart`) и прежним trace-логированием через `appendTraceLine("routes", ...)`;
|
||
- добавлен подпакет `selective-vpn-api/app/trafficcandidates` и перенесён сбор candidates (`subnets`, `units`, `uids`) в DI-модуль;
|
||
- `app/traffic_candidates.go` переведён в thin-adapter (маппинг pkg DTO -> API DTO без изменения endpoint path/поля ответа);
|
||
- для визуальной очистки корня `app/` удалены 16 отдельных resolver bridge-файлов:
|
||
- `resolver_artifacts_bridge.go`
|
||
- `resolver_dns_config_bridge.go`
|
||
- `resolver_domain_cache_bridge.go`
|
||
- `resolver_host_lookup_bridge.go`
|
||
- `resolver_mode_runtime_bridge.go`
|
||
- `resolver_planning_bridge.go`
|
||
- `resolver_precheck_finalize_bridge.go`
|
||
- `resolver_precheck_types_bridge.go`
|
||
- `resolver_resolve_batch_bridge.go`
|
||
- `resolver_runtime_tuning_bridge.go`
|
||
- `resolver_start_log_bridge.go`
|
||
- `resolver_static_labels_bridge.go`
|
||
- `resolver_summary_log_bridge.go`
|
||
- `resolver_timeout_recheck_bridge.go`
|
||
- `resolver_types_bridge.go`
|
||
- `resolver_wildcard_bridge.go`
|
||
- вместо них добавлен единый `selective-vpn-api/app/resolver_bridge.go` (консолидированный bridge-слой resolver-пайплайна);
|
||
- добавлен подпакет `selective-vpn-api/app/trafficprofiles` и вынесена state/store-логика профилей приложений (`list/upsert/delete/dedupe`, json persistence, id-derive);
|
||
- `app/traffic_app_profiles.go` переведён в thin-adapter: HTTP decode/encode + mapping DTO `app <-> trafficprofiles`;
|
||
- функция `sanitizeID` сохранена в `app` (runtime-совместимость с transport/egress/singbox контуром);
|
||
- добавлен `selective-vpn-api/app/trafficprofiles/appkey.go` и в него вынесены `CanonicalizeAppKey` + `SplitCommandTokens` + wrapper parser helpers;
|
||
- удалён root-файл `selective-vpn-api/app/traffic_appkey.go`; в `app/traffic_app_profiles.go` оставлены совместимые фасады `canonicalizeAppKey`/`splitCommandTokens` для существующих вызовов и тестов;
|
||
- добавлен подпакет `selective-vpn-api/app/trafficappmarks` и вынесены хелперы состояния app-marks:
|
||
- `LoadState` / `SaveState` (json persistence),
|
||
- `DedupeItems` / `UpsertItem`,
|
||
- `IsAllDigits`;
|
||
- в `app/traffic_appmarks.go` сохранены совместимые фасады (`loadAppMarksState`, `saveAppMarksState`, `dedupeAppMarkItems`, `upsertAppMarkItem`, `isAllDigits`) и type-alias на пакетные модели (`appMarksState`, `appMarkItem`);
|
||
- `traffic_appmarks.go` сокращён с `1140` до `1002` строк, без изменения endpoint-поведения и nft runtime-логики;
|
||
- добавлен файл `selective-vpn-api/app/trafficappmarks/cgroup.go` и вынесены `ResolveCgroupV2PathForNft`, `NormalizeCgroupRelOnly`, `CgroupDirInode`;
|
||
- в `app/traffic_appmarks.go` оставлены одноимённые фасады для совместимости текущих вызовов;
|
||
- добавлен файл `selective-vpn-api/app/trafficappmarks/nft.go` и вынесены `InsertAppMarkRule/DeleteAppMarkRule/HasAppMarkRule`, `UpdateLocalBypassSet`, `ParseNftHandle`, `CompactIPv4IntervalElements`;
|
||
- туда же вынесены cleanup helper'ы `CleanupLegacyRules` и `ClearManagedRules`, а в `app/traffic_appmarks.go` оставлены фасады `cleanupLegacyAppMarksRules`/`clearManagedAppMarkRules`;
|
||
- в `selective-vpn-api/app/trafficappmarks/store.go` добавлен `PruneExpired` (TTL prune + safe handling corrupted `expires_at` + callback на удаление nft-rule);
|
||
- в `selective-vpn-api/app/trafficappmarks/nft.go` добавлен `EnsureBase` (best-effort bootstrap table/chains/set + jump to app chain), фасад `ensureAppMarksNft` в `app` сохранён;
|
||
- в `app/traffic_appmarks.go` сохранены совместимые фасады `nftInsertAppMarkRule/nftDeleteAppMarkRule/nftHasAppMarkRule/updateAppMarkLocalBypassSet/parseNftHandle`;
|
||
- `traffic_appmarks.go` дополнительно сокращён до `723` строк;
|
||
- добавлен подпакет `selective-vpn-api/app/trafficmode` и файл `normalize.go` (вынесены `TokenizeList`, `NormalizeSubnetList`, `NormalizeUIDToken`, `NormalizeUIDList`, `NormalizeCgroupList`);
|
||
- `app/traffic_mode.go` переведён на фасады к `trafficmode` для normalize-контура (без изменения API/маршрутной логики);
|
||
- добавлен `selective-vpn-api/app/trafficmode/autolocal.go` (вынесен parser `ip route show table main` для auto-local-bypass);
|
||
- в `app/traffic_mode.go` оставлены фасады `parseRouteDevice/isContainerIface/routeLineIsLinkDown/isAutoBypassDestination` для backward-compatible вызовов из `traffic_candidates.go`;
|
||
- добавлен `selective-vpn-api/app/trafficmode/cgroup.go` (вынесен cgroup scan/resolve и derive uidrange из `/proc/<pid>/status`);
|
||
- добавлен `selective-vpn-api/app/trafficmode/ingress.go` (вынесен ingress-reply bypass nft lifecycle и state-check);
|
||
- добавлен `selective-vpn-api/app/trafficmode/rules.go` (вынесен parsing `ip rule show` + route-probe `ip route get`);
|
||
- добавлен `selective-vpn-api/app/trafficmode/apply.go` (вынесен apply/remove управляемых `ip rule` и overlay overrides);
|
||
- удалены промежуточные wrapper-функции cgroup/nftObjectMissing из `traffic_mode.go`; `buildEffectiveOverrides` использует пакетные вызовы напрямую;
|
||
- `traffic_mode.go` сокращён с `1449` до `923` строк;
|
||
- добавлен подпакет `selective-vpn-api/app/egressutil` и файл `util.go`;
|
||
- в `app/egress_identity.go` переведены на фасады: `parseEgressIPFromBody`, `egressParseGeoResponse`, `normalizeCountryCode`, `egressIPEndpoints`, `egressGeoEndpointsForIP`, `egressLimitEndpointsForNetns`, `egressJoinErrorsCompact`, `egressParseSingBoxSOCKSProxyURL`, `egressResolvedHostForURL`, `resolveEgressCurlPath`, `resolveEgressWgetPath`, `egressTimeoutSec`;
|
||
- удалены устаревшие внутренние переменные/функции egress (`egressCurlPathOnce/egressWgetPathOnce`, локальные any/url helpers);
|
||
- удалены мёртвые egress wrappers (неиспользуемые after-extract), сохранён публичный runtime-контракт для активных вызовов;
|
||
- `egress_identity.go` сокращён с `1208` до `932` строк;
|
||
- добавлен подпакет `selective-vpn-api/app/dnscfg` и файл `smartdns.go`;
|
||
- `app/dns_settings.go` переведён на фасады `dnscfg` для SmartDNS default/config parsing и DNS upstream normalization;
|
||
- добавлен `selective-vpn-api/app/dnscfg/systemd.go` и вынесены helper'ы `smartdns` unit state/action;
|
||
- добавлен `selective-vpn-api/app/dnscfg/benchmark.go` и вынесен benchmark-контур DNS (core scoring/probe/normalize) с callback-зависимостями (`splitDNS`, `classifyDNSError`, `isPrivateIPv4`);
|
||
- `app/dns_settings.go` оставлен как facade HTTP/DTO слой для benchmark-endpoint, без изменения JSON-контракта;
|
||
- добавлен `selective-vpn-api/app/dnscfg/pool.go` и вынесены helper'ы pool/legacy-конвертации;
|
||
- `app/dns_settings.go` использует фасады `dnscfg` для pool нормализации/конвертации, сохраняя прежние JSON-модели и endpoint-поведение;
|
||
- в `selective-vpn-api/app/dnscfg/mode.go` добавлены `ModeConfig/ModeState/LoadMode/SaveMode`; `app/dns_settings.go` переведён на тонкий фасад для чтения/сохранения `dns_mode.json`;
|
||
- `dns_settings.go` дополнительно очищен от лишних benchmark-wrapper-функций; benchmark-контур работает через прямые вызовы `dnscfg` без изменения API-контракта;
|
||
- добавлен `selective-vpn-api/app/dnscfg/upstreams.go` и вынесены parse/normalize/render helper'ы конфига `dns-upstreams.conf`;
|
||
- `loadDNSUpstreamsConfFile/saveDNSUpstreamsConfFile` переведены на фасады `dnscfg` без изменения формата `dns-upstreams.conf` и legacy JSON mirror;
|
||
- добавлен `selective-vpn-api/app/dnscfg/prewarm.go`; логика SmartDNS prewarm (domain expansion, parallel dig, runtime/manual nft merge, summary/per-upstream log) вынесена из `app/dns_settings.go` в пакет `dnscfg` через callback-deps;
|
||
- добавлен `selective-vpn-api/app/dns_smartdns_handlers.go`; туда вынесены `handleSmartdnsRuntime`, `handleSmartdnsPrewarm`, `runSmartdnsPrewarm` (тонкий app-adapter к `dnscfg.RunPrewarm`);
|
||
- `dns_settings.go` дополнительно сокращён до `749` строк (было `1073` до этого шага), без изменения endpoint-path/JSON-контрактов;
|
||
- в `app/egressutil` добавлены `scope.go`/`identity.go`/`probe.go`/`http.go` (scope parser, identity diff, endpoint probe loop, HTTP body fetch helper);
|
||
- в `app/egress_identity.go` сохранены совместимые фасады (`parseEgressScope`, `egressIdentityChanged`) и перевод вызовов probe/geo на пакетные helper'ы;
|
||
- удалены локальные pure-wrapper'ы egress; `egress_identity_test.go` использует `egressutil` напрямую;
|
||
- `egress_identity.go` сокращён до `854` строк (было `932` на старте блока F1.14);
|
||
- добавлен `selective-vpn-api/app/transportcfg/singbox_helpers.go` и в него вынесены повторно используемые helper'ы профилей singbox (digest/diff/path/io/validators/stamp/messages);
|
||
- `transport_singbox_profiles_flow.go` переведён на пакетные helper-вызовы `transportcfg` с сохранением API/flow-поведения; файл сокращён до `1624` строк;
|
||
- добавлен `selective-vpn-api/app/transportcfg/history_helpers.go`; вынос history io/selection helpers из `transport_singbox_profiles_flow.go` выполнен через пакетные фасады;
|
||
- удалены локальные helper'ы `findSingBoxBinary`/`sanitizeHistoryStamp` из `transport_singbox_profiles_flow.go` (замена на package helpers);
|
||
- `transport_singbox_profiles_flow.go` дополнительно сокращён до `1563` строк;
|
||
- `dns_settings.go` сокращён с `1521` до `1196` строк;
|
||
- валидация: `go test ./...` и `go build ./...` в `selective-vpn-api` — успешно.
|
||
- 2026-03-10: продолжен шаг `F1.11` (декомпозиция `transport_backends.go` без смены поведения):
|
||
- вынесен systemd helper-блок в `selective-vpn-api/app/transport_backends_systemd_helpers.go`:
|
||
- unit-name/ownership/file-write helpers;
|
||
- unit renderer + ssh-overlay renderer;
|
||
- tuning/hardening normalizers и prefix-fallback config helpers.
|
||
- вынесен probe/latency helper-блок в `selective-vpn-api/app/transport_backends_probe_helpers.go`:
|
||
- endpoint collector/parser/dedupe;
|
||
- host/netns probe и latency sampling.
|
||
- вынесен exec/binary helper-блок в `selective-vpn-api/app/transport_backends_exec_helpers.go`:
|
||
- `resolveTransportPrimaryExecStart` + `buildTransport*Command`;
|
||
- packaging/binary resolution helpers (`resolveTransportBinary`, `validateRequiredBinary`, `transportPackagingProfile`);
|
||
- shell/config int helpers (`shellJoinArgs`, `shellQuoteArg`, `transportConfigInt`).
|
||
- вынесен runtime helper-блок в `selective-vpn-api/app/transport_backends_runtime_helpers.go`:
|
||
- `transportBackendUnit/defaultTransportBackendUnit`;
|
||
- `transportRuntimeMode`;
|
||
- `transportSystemdActionUnits/transportSystemdHealthUnits`;
|
||
- `transportConfigString/transportConfigBool`.
|
||
- вынесены adapter-реализации `unsupported/mock` в `selective-vpn-api/app/transport_backends_adapters_misc.go`.
|
||
- вынесен `systemd` adapter в `selective-vpn-api/app/transport_backends_adapter_systemd.go` (действия/health/provision/cleanup + missing-unit helpers).
|
||
- итоговая декомпозиция: `transport_backends.go` сокращён с `1954` до `73` строк (тонкий core/facade).
|
||
- валидация: `go test ./app -run TestTransportSystemdBackendHealth -count=1`, `go test ./...`, `go build ./...` — успешно.
|
||
- 2026-03-10: продолжен `F1.11` по resolver-контурy (введена отдельная папка `selective-vpn-api/app/resolver/`):
|
||
- вынесен wildcard matcher в подпакет `selective-vpn-api/app/resolver/wildcard_matcher.go` (`NormalizeWildcardDomain/NewWildcardMatcher/Match/IsExact/Count`);
|
||
- вынесены общие resolver helper'ы в `selective-vpn-api/app/resolver/common.go` (`UniqueStrings`, `PickDNSStartIndex`, `StripANSI`, `IsPrivateIPv4`);
|
||
- вынесены DNS метрики/типы в `selective-vpn-api/app/resolver/dns_metrics.go` (`DNSErrorKind`, `DNSMetrics`, `DNSUpstreamMetrics`);
|
||
- вынесены JSON/IO helper'ы в `selective-vpn-api/app/resolver/io_helpers.go` (`ReadLinesAllowMissing`, `LoadJSONMap`, `SaveJSON`, `LoadResolverPrecheck*`, `LoadResolverLiveBatch*`);
|
||
- вынесены DNS parser/helper'ы в `selective-vpn-api/app/resolver/dns_helpers.go` (`SplitDNS`, `ClassifyDNSError`);
|
||
- вынесен adaptive live-batch policy-калькулятор в `selective-vpn-api/app/resolver/live_batch.go` (`ComputeNextLiveBatchTarget`, `ComputeNextLiveBatchNXHeavyPct`) с сохранением прежней формулы;
|
||
- вынесен live-batch selector в `selective-vpn-api/app/resolver/live_batch_select.go` (`ClassifyLiveBatchHost`, `SplitLiveBatchCandidates`, `PickAdaptiveLiveBatch`);
|
||
- вынесен upstream-pool helper в `selective-vpn-api/app/resolver/dns_upstreams.go` (`BuildResolverFallbackPool`, `MergeDNSUpstreamPools`);
|
||
- вынесен error-policy helper в `selective-vpn-api/app/resolver/error_policy.go` (`SmartDNSFallbackForTimeoutEnabled`, `ShouldFallbackToSmartDNS`, `ClassifyHostErrorKind`, `ShouldUseStaleOnError`);
|
||
- полностью вынесен `domain cache` блок в `selective-vpn-api/app/resolver/domain_cache.go` (state/model + normalize/migrate + get/set/quarantine/stale + score/state policy + json render/summary);
|
||
- вынесены precheck/live-batch типы в `selective-vpn-api/app/resolver/precheck_types.go` + bridge `selective-vpn-api/app/resolver_precheck_types_bridge.go`;
|
||
- вынесено сохранение precheck-state в `selective-vpn-api/app/resolver/precheck_state.go` (`SaveResolverPrecheckState`) с bridge-вызовом из `app`;
|
||
- вынесен static/PTR блок в `selective-vpn-api/app/resolver/static_labels.go` (`ParseStaticEntries`, `ResolveStaticLabels`, `DigPTR`) + bridge `selective-vpn-api/app/resolver_static_labels_bridge.go`;
|
||
- вынесен timeout-quarantine recheck блок в `selective-vpn-api/app/resolver/timeout_recheck.go` (`RunTimeoutQuarantineRecheck`) + bridge `selective-vpn-api/app/resolver_timeout_recheck_bridge.go`;
|
||
- вынесен загрузчик DNS-конфига в `selective-vpn-api/app/resolver/dns_config.go` (`LoadDNSConfig`) + bridge `selective-vpn-api/app/resolver_dns_config_bridge.go`;
|
||
- вынесен host-lookup блок в `selective-vpn-api/app/resolver/host_lookup.go` (`ResolveHost`, `DigAWithPolicy`) + bridge `selective-vpn-api/app/resolver_host_lookup_bridge.go` (`resolveHostGo`, `digA`, `digAWithPolicy`);
|
||
- типы DNS policy/cooldown (`dnsAttemptPolicy`, `dnsRunCooldown`, `dnsCooldownState`) перенесены из `resolver.go` в `selective-vpn-api/app/resolver_dns_policy.go` для очистки монолита;
|
||
- вынесен runtime-tuning блок (`TTL/workers/dns timeout/precheck/live-batch/negative TTL`) в `selective-vpn-api/app/resolver/runtime_tuning.go` (`BuildResolverRuntimeTuning`) + bridge `selective-vpn-api/app/resolver_runtime_tuning_bridge.go`;
|
||
- вынесен artifact-builder блок (`IPs/IPMap/Direct/Wildcard`) в `selective-vpn-api/app/resolver/artifacts.go` (`BuildResolverArtifacts`) + bridge `selective-vpn-api/app/resolver_artifacts_bridge.go`;
|
||
- вынесен planning блок (`fresh/toResolve/cache_negative/quarantine/stale/precheck_scheduled`) в `selective-vpn-api/app/resolver/planning.go` (`BuildResolvePlanning`) + bridge `selective-vpn-api/app/resolver_planning_bridge.go`;
|
||
- вынесен precheck finalize/save блок (next live-batch calc + state persist + force-file consume) в `selective-vpn-api/app/resolver/precheck_finalize.go` (`FinalizeResolverPrecheck`) + bridge `selective-vpn-api/app/resolver_precheck_finalize_bridge.go`;
|
||
- вынесен concurrent resolve-блок (`workers/jobs/results` + stale-on-error apply) в `selective-vpn-api/app/resolver/resolve_batch.go` (`ExecuteResolveBatch`) + bridge `selective-vpn-api/app/resolver_resolve_batch_bridge.go`;
|
||
- вынесен summary/breakdown/precheck-log блок в `selective-vpn-api/app/resolver/summary_log.go` (`LogResolverSummary`) + bridge `selective-vpn-api/app/resolver_summary_log_bridge.go`;
|
||
- вынесен runtime DNS mode apply/log блок в `selective-vpn-api/app/resolver/mode_runtime.go` (`ApplyDNSModeRuntime`, `LogDNSMode`) + bridge `selective-vpn-api/app/resolver_mode_runtime_bridge.go`;
|
||
- вынесен start/policy log блок в `selective-vpn-api/app/resolver/start_log.go` (`LogResolverStart`) + bridge `selective-vpn-api/app/resolver_start_log_bridge.go`;
|
||
- финальный orchestration split: добавлен pipeline-файл `selective-vpn-api/app/resolver_pipeline.go` (`buildResolverJobContext`, `runResolverPipeline`), а `runResolverJob` оставлен тонким фасадом;
|
||
- добавлен bridge `selective-vpn-api/app/resolver_domain_cache_bridge.go` (сохранены старые имена/методы `domainCache*` в пакете `app` через wrapper-слой);
|
||
- добавлен совместимый bridge `selective-vpn-api/app/resolver_wildcard_bridge.go` (сохранены старые вызовы `normalizeWildcardDomain/newWildcardMatcher` в пакете `app`);
|
||
- добавлен type bridge `selective-vpn-api/app/resolver_types_bridge.go` (alias-константы/типы для `dnsErrorKind`/`dnsMetrics` без смены API внутри `app`);
|
||
- вынесен DNS policy/cooldown блок из монолита в `selective-vpn-api/app/resolver_dns_policy.go`;
|
||
- `resolver.go` сокращён с `2983` до `41` строк без изменения API-контрактов;
|
||
- валидация: `go test ./...` и `go build ./...` в `selective-vpn-api` — успешно.
|
||
- 2026-03-10: закрыт шаг `F1.4` (декомпозиция `vpn_dashboard_qt.py`):
|
||
- добавлен пакет `selective-vpn-gui/main_window/` и вынесены базовые модули: `constants.py` (shared UI constants), `workers.py` (`EventThread`, `LocationsThread`);
|
||
- вынесен UI shell-контур (`build tabs + helpers + locations/egress`) в `main_window/ui_shell_mixin.py`;
|
||
- вынесен крупный SingBox-контур в подпакет `main_window/singbox/*` (`editor`, `cards`, `links`, `runtime`) и сохранён фасад `main_window/singbox_mixin.py`;
|
||
- вынесен runtime/refresh/actions контур в `main_window/runtime_actions_mixin.py` (events stream, refresh, login/auth, routes/dns/domains actions, close lifecycle);
|
||
- добавлена вторичная декомпозиция mixin-слоёв: `ui_tabs_*`, `ui_helpers`, `ui_location_runtime`, `runtime_{state,refresh,auth,ops}`, `singbox/{links_*,runtime_*}`; фасады `ui_tabs_mixin.py`, `ui_shell_mixin.py`, `runtime_actions_mixin.py`, `singbox_mixin.py` сохранены;
|
||
- отдельный split `ui_tabs_singbox_mixin.py` на `ui_tabs_singbox_{layout,editor}_mixin.py` (убран последний UI-файл >700 строк);
|
||
- `selective-vpn-gui/vpn_dashboard_qt.py` сокращён с `6103` до `116` строк, при этом сохранён как thin-bootstrap+UI wiring;
|
||
- валидация: `python3 -m py_compile vpn_dashboard_qt.py main_window/*.py main_window/singbox/*.py` и import-проверка `MainWindow` проходят.
|
||
- 2026-03-10: hotfix `systemd stop` idempotency для transport lifecycle:
|
||
- в `transportSystemdBackend.Action` добавлен no-op режим для `action=stop`, если `systemctl` возвращает `Unit ... not loaded/not found/unknown unit` (чтобы отсутствующий unit не валил switch-пайплайн);
|
||
- добавлен unit-тест `TestTransportSystemdBackendStopMissingUnitIsNoop`;
|
||
- runtime-валидация на живом API: `POST /api/v1/transport/clients/sg-finland-yur/start` и `.../sg-realnetns/start` проходят, `.../sg-torjantest-ylu9ja7f/stop` возвращает `ok=true` с warning вместо `TRANSPORT_BACKEND_ACTION_FAILED`.
|
||
- 2026-03-10: hotfix `systemd start/restart` auto-provision для новых SingBox-профилей:
|
||
- в `transportSystemdBackend.Action` добавлен общий fallback для `SingBox`: при `start/restart` и ошибке `unit not found/not loaded` backend автоматически выполняет `Provision` и повторяет `systemctl start/restart` один раз;
|
||
- добавлен unit-тест `TestTransportSystemdBackendStartAutoProvisionOnMissingUnit`;
|
||
- runtime-валидация: `POST /api/v1/transport/clients/sg-torjantest-ylu9ja7f/start` вернул `ok=true`, `status_after=up` без ручного `Prepare`.
|
||
- 2026-03-10: закрыт шаг `F1.6` по `cmd/*` entrypoints в `selective-vpn-api`:
|
||
- добавлены отдельные main-пакеты: `cmd/selective-vpn-api`, `cmd/selective-vpn-routes-update`, `cmd/selective-vpn-routes-clear`, `cmd/selective-vpn-autoloop`;
|
||
- в `app/server.go` выделены явные entrypoint-функции `RunAPIServer`, `RunRoutesUpdateCLI`, `RunRoutesClearCLI`, `RunAutoloopCLI`, route registration вынесена в `registerAPIRoutes`;
|
||
- legacy `main.go` сохранён для обратной совместимости (старые unit/скрипты продолжают работать через `app.Run()`).
|
||
- 2026-03-10: выполнена повторная валидация `F1.6` (контрольный прогон перед следующим рефактором):
|
||
- `go test ./...` и `go build ./...` в `selective-vpn-api` прошли успешно;
|
||
- собраны отдельные бинарники из `cmd/*` в `/tmp` (`selective-vpn-api`, `selective-vpn-routes-update`, `selective-vpn-routes-clear`, `selective-vpn-autoloop`).
|
||
- 2026-03-10: закрыт шаг `F1.7` (декомпозиция API bootstrap/роутера):
|
||
- удалён монолит `selective-vpn-api/app/server.go` и введены файлы `entrypoints.go`, `api_bootstrap.go`, `api_routes.go`;
|
||
- сигнатуры и поведение сохранены: `Run*CLI`, `RunAPIServer`, `runAPIServerAtAddr`, `registerAPIRoutes`;
|
||
- `registerAPIRoutes` дополнительно разложен на доменные helper'ы (`registerCoreRoutes`, `registerRoutesControlRoutes`, `registerTrafficRoutes`, `registerTransportRoutes`, `registerDNSRoutes`, `registerVPNRoutes`) без изменения endpoint-path;
|
||
- контрольная валидация `go test ./...` и `go build ./...` прошла успешно.
|
||
- 2026-03-10: закрыт шаг `F1.8` (физическая декомпозиция route-registry по доменам):
|
||
- `register*Routes` вынесены из общего файла в `app/api_routes_core.go`, `api_routes_traffic.go`, `api_routes_transport.go`, `api_routes_trace.go`, `api_routes_dns.go`, `api_routes_vpn.go`;
|
||
- `app/api_routes.go` оставлен как thin-facade (`registerAPIRoutes`), который только собирает доменные registrars;
|
||
- endpoint-path и handlers сохранены 1:1; контрольная валидация `go test ./...` и `go build ./...` прошла успешно.
|
||
- 2026-03-10: закрыт шаг `F1.9` (переход от "всё в app" к подпапкам runtime-слоя):
|
||
- добавлен подпакет `app/cli` с раннерами `routes-update`, `routes-clear`, `autoloop` и dependency-injection через callbacks;
|
||
- добавлен подпакет `app/bootstrap` с HTTP server runner (`Run(ctx, Config)`), `app/api_bootstrap.go` переведён на thin-wrapper + `prepareAPIRuntime()`;
|
||
- фасады в `app` сохранены (`RunRoutesUpdateCLI`, `RunRoutesClearCLI`, `RunAutoloopCLI`, `runAPIServerAtAddr`), поэтому внешний контракт и поведение не изменились;
|
||
- контрольная валидация: `go test ./...` и `go build ./...` успешны.
|
||
- 2026-03-10: закрыт шаг `F1.10` (декомпозиция transport handler-монолита):
|
||
- удалён монолит `app/transport_handlers.go` (`~2440` строк), код разложен на модули: `transport_shared.go`, `transport_handlers_clients.go`, `transport_handlers_policy.go`, `transport_policy_validate.go`, `transport_client_runtime.go`, `transport_tokens_state.go`;
|
||
- все endpoint-handler сигнатуры и policy/lifecycle поведение сохранены (роуты `transport/*` без изменений);
|
||
- контрольная валидация: `go test ./...` и `go build ./...` успешны.
|
||
- 2026-03-10: начат шаг `F1.11` (подпакеты без циклов):
|
||
- вынесен token-store в `app/transporttoken/store.go` (issue/consume/ttl cleanup + token generation);
|
||
- `transport_tokens_state.go` переведён на новый store через facade (`issueTransportConfirmToken`, `consumeTransportConfirmToken`, `newTransportToken`);
|
||
- обновлены unit-тесты confirm-token lifecycle под новый store (`TestTransportConfirmStoreExpiresToken`), `go test ./...` и `go build ./...` успешны.
|
||
- 2026-03-10: начат шаг `F1.12` (декомпозиция `api_client.py` по папкам):
|
||
- добавлен пакет `selective-vpn-gui/api/` (`models.py`, `errors.py`, `utils.py`, `client.py`, `__init__.py`);
|
||
- `api_client.py` переведён в backward-compatible facade (реэкспорт `ApiClient/ApiError/models/strip_ansi`);
|
||
- сохранена обратная совместимость импортов (`from api_client import ...`) и добавлен новый путь (`from api import ...`);
|
||
- проверка: `py_compile` + импорты `dashboard_controller.py`, `vpn_dashboard_qt.py`, `dns_benchmark_dialog.py` проходят.
|
||
- 2026-03-10: закрыт шаг `F1.12` (полная доменная декомпозиция Python API-клиента):
|
||
- `selective-vpn-gui/api/client.py` сокращён до base HTTP/SSE + shared helpers (`_request/_json/_to_int/_parse_cmd_result`);
|
||
- доменные методы вынесены в mixin-модули: `api/status.py`, `api/routes.py`, `api/traffic.py`, `api/dns.py`, `api/domains.py`, `api/vpn.py`, `api/trace.py`;
|
||
- дополнительный разрез transport-домена: `api/transport_clients.py`, `api/transport_policy.py`, `api/transport_singbox.py` + facade `api/transport.py`;
|
||
- backward compatibility сохранена (`api_client.py` facade и `api.transport.TransportApiMixin`), `py_compile` и импорты GUI-модулей проходят.
|
||
- 2026-03-10: закрыт шаг `F1.3` (декомпозиция `dashboard_controller.py`):
|
||
- добавлен пакет `selective-vpn-gui/controllers/` с domain mixin-модулями: `status`, `vpn`, `routes`, `traffic`, `transport`, `dns`, `domains`, `trace`, плюс общий `core` и `views`;
|
||
- `dashboard_controller.py` сокращён до thin-facade (`DashboardController`) с прежней точкой импорта и совместимыми экспортами (`TraceMode`, view-models);
|
||
- проверка: `py_compile` + импорты `vpn_dashboard_qt.py`, `traffic_mode_dialog.py`, `dns_benchmark_dialog.py` проходят.
|
||
- 2026-03-10: зафиксировано разделение зависимостей `go.mod` vs runtime services:
|
||
- выполнен `go mod tidy` для `selective-vpn-api` (изменений не потребовалось; Go-модули актуальны);
|
||
- добавлен preflight-check `scripts/check_runtime_dependencies.sh` (required/optional + `--strict`);
|
||
- добавлен документ `docs/phase-b/B4_RUNTIME_DEPENDENCIES_AND_PREFLIGHT.md` с реестром бинарей/unit-зависимостей до начала крупного рефакторинга.
|
||
- 2026-03-10: стабилизирован egress refresh для `AdGuardVPN` после смены локации:
|
||
- в Go API (`/api/v1/vpn/location`) добавлен backend `egress refresh burst` для `adguardvpn` (force refresh сразу + отложенные итерации), чтобы egress IP/geo догонял фактическое переключение туннеля;
|
||
- в `egress identity` force-refresh теперь обходит negative geo-cache (ошибка geo не блокирует повторную попытку при `force=true`), а TTL negative geo-cache снижен до `30s`;
|
||
- в GUI (`refresh_vpn_tab`) добавлены дополнительные post-switch trigger'ы egress-refresh (на смену desired location и на завершение switching), а polling расширен до стабилизации `IP + country` без ранней остановки.
|
||
- 2026-03-10: доведён anti-regression контур `SingBox netns + multi-profile switch`:
|
||
- в backend lifecycle `POST /api/v1/transport/clients/{id}/start|restart` добавлен обязательный preflight для `SingBox` (render/validate + materialize `config_path`) до `systemctl`, чтобы старт не обходил профильную проверку;
|
||
- при `start/restart` выбранного `SingBox` в `netns` backend теперь автоматически останавливает другие `SingBox`-клиенты в этом же namespace перед запуском (убран конфликт `listen 127.0.0.1:10808: address already in use` при switch между профилями);
|
||
- в `systemd` backend добавлен best-effort `systemctl reset-failed <unit>` перед `start/restart`, чтобы switch не упирался в `start-limit-hit` после прошлых неуспешных стартов;
|
||
- закрыт legacy кейс `packet_encoding: none` (SingBox panic `unknown value`): нормализация выполняется в Go-render pipeline, а в GUI-editor/link-import `none` больше не пишется в raw config;
|
||
- для `egress identity` при `transport:<id>` и `netns_enabled=true` у `SingBox` direct-netns fallback полностью отключён: probe идёт только через локальный SOCKS inbound, иначе возвращается ошибка (без ложного AdGuard/system IP);
|
||
- runtime-проверка: `sg-finland-yur` стартует даже после удаления `/etc/selective-vpn/transports/sg-finland-yur/singbox.json` (файл воссоздаётся preflight); при последовательном switch подтверждён ожидаемый egress: `sg-finland-yur -> FI (92.42.102.181)`, `sg-realnetns -> NL (46.17.97.149)`.
|
||
- 2026-03-10: добавлен хвостовой экспериментальный этап `Z1` (глобальный L7 orchestration-layer) и отдельно зафиксировано правило: подготовку можно делать заранее, но runtime-включение только в конце планов.
|
||
- 2026-03-10: зафиксирован отложенный этап `E7` (без реализации в текущем спринте):
|
||
- целевое разделение `System selective PBR` (L3/L4 transport guardrails) и `SingBox L7 routing` (policy-engine);
|
||
- зафиксирован полный список будущих работ по L7 правилам, renderer, pipeline `validate/apply/rollback`, DNS-стратегии и observability.
|
||
- 2026-03-10: исправлен приоритет egress-проверки для `transport:<id>` в `netns`:
|
||
- для `SingBox` с локальным `socks` inbound probe теперь сначала идёт через `socks5h://127.0.0.1:<port>` (реальный tunnel egress), а direct-netns probe оставлен только как fallback;
|
||
- устранён ложный кейс, когда `transport:sg-realnetns` показывал системный/AdGuard IP вместо IP tunnel-профиля.
|
||
- 2026-03-10: дополнили фиксы `SingBox + netns` и старт нового профиля:
|
||
- для `transport:<id>` при `netns + socks-inbound` direct-netns fallback отключён; при ошибке proxy probe возвращается ошибка, чтобы UI не показывал ложный AdGuard/system IP как egress transport-клиента;
|
||
- в GUI `Run/Start` добавлен preflight `singbox profile apply` (`skip_runtime=true`) перед switch/start, чтобы materialize `config_path` и исключить падение нового клиента с ошибкой `open .../singbox.json: no such file or directory`.
|
||
- 2026-03-10: исправлен конфликт `netns`/`agvpn` маршрутов для `SingBox Real NetNS`:
|
||
- устранена причина флапов `lookup n3.elmprod.tech ... deadline exceeded` при живом профиле;
|
||
- в `traffic_mode` авто-bypass больше не подхватывает `linkdown` маршруты и `svh*/svn*` интерфейсы;
|
||
- в `transport_netns` добавлена принудительная синхронизация policy-route (`table agvpn`) для `netns_subnet` на актуальный host-veth при `start/restart` + cleanup при удалении namespace;
|
||
- выполнена одноразовая очистка сиротского legacy namespace (`svpn-realnetns`) и проверен стабильный egress (`transport:sg-realnetns` -> `stale=false`, IP/geo обновляется).
|
||
- 2026-03-10: доведён `E6` netns runtime contour для `transport:<id>`:
|
||
- netns-probe больше не зависит от DNS внутри namespace (`curl --resolve` с host-side резолвом endpoint хоста);
|
||
- добавлен fallback probe через локальный `SingBox` SOCKS inbound (`socks5h://127.0.0.1:<port>`) для изолированных netns;
|
||
- ограничена длительность netns-probe (по умолчанию `SVPN_EGRESS_NETNS_MAX_ENDPOINTS=1`) и уплотнены сообщения ошибок (`last_error`) для UI.
|
||
- 2026-03-10: закрыт E6.2–E6.5 (egress identity implementation):
|
||
- добавлены endpoint'ы `GET /api/v1/egress/identity` и `POST /api/v1/egress/identity/refresh` в Go API;
|
||
- реализованы scope-provider’ы `adguardvpn|system|transport:<id>` с netns-aware probe для transport;
|
||
- добавлены SWR/single-flight/backoff метаданные (`updated_at/stale/refresh_in_progress/last_error/next_retry_at`) и SSE `egress_identity_changed`;
|
||
- добавлен GeoIP lookup (country_code/country_name) с кэшем;
|
||
- desktop GUI подключён к новому API: `AdGuardVPN` и `SingBox` карточки показывают `IP + country`, флаг строится в UI из `country_code`.
|
||
- 2026-03-10: закрыт E6.1 requirements freeze для общего `egress identity`:
|
||
- добавлен контракт `docs/phase-e/E6_EGRESS_IDENTITY_API_CONTRACT.md`;
|
||
- зафиксированы scope (`adguardvpn|transport:<id>|system`), endpoint'ы `GET /api/v1/egress/identity` и `POST /api/v1/egress/identity/refresh`;
|
||
- зафиксированы SWR/SSE поля и правило рендера флага в UI из `country_code`.
|
||
- 2026-03-10: ускорен live health-probe для `SingBox`:
|
||
- `transport` latency probe timeout снижен до `900ms`;
|
||
- устранён lock-contention в background health refresh: долгий `backend.Health()` вынесен из-под `transportMu`, чтобы запросы `GET /transport/clients/{id}/health` не ждали второй probe под mutex.
|
||
- 2026-03-09: полировка `SingBox` runtime UX и live latency:
|
||
- удалён отдельный блок `Runtime details` (убран дублирующий UI-слой в `SingBox` вкладке);
|
||
- `last update` перенесён в верхнюю карточку `Profile` (`id + updated timestamp`);
|
||
- GUI переведён на live health для выбранного профиля через `GET /api/v1/transport/clients/{id}/health` (через `ApiClient`/`DashboardController`), поэтому `latency_ms` обновляется сразу после refresh/SSE, а не только по snapshot `/transport/clients`.
|
||
- 2026-03-09: закрыт baseline по latency в transport health:
|
||
- `systemd` backend теперь пишет `health.latency_ms` для активных клиентов через TCP probe endpoint'ов из runtime-конфига (`singbox outbounds`);
|
||
- для `netns_enabled=true` probe выполняется через тот же netns (`ip netns exec/nsenter`), чтобы latency считался по фактическому runtime-контуру клиента;
|
||
- при неуспешном probe статус не деградирует (остаётся `up`, latency остаётся пустым), чтобы избежать ложных падений lifecycle;
|
||
- добавлены unit-тесты на endpoint extraction и latency sampling (`transport_backends_test.go`).
|
||
- 2026-03-09: завершён перенос netns-toggle orchestration из GUI в Go-ядро:
|
||
- добавлен и подключён endpoint `POST /api/v1/transport/netns/toggle` (config patch + provision + restart running clients);
|
||
- `ApiClient`/`DashboardController` получили отдельный метод `transport_netns_toggle`;
|
||
- `netns_debug.py` больше не выполняет локальную orchestration-цепочку и использует только backend call;
|
||
- добавлены unit-тесты `applyTransportNetnsToggleLocked` (success / partial-failure / no-targets).
|
||
- 2026-03-09: начата унификация SWR-механики для переиспользования:
|
||
- добавлен общий модуль `selective-vpn-api/app/refresh_coordinator.go` (`freshTTL`, single-flight refresh gate, exponential backoff with cap, snapshot metadata);
|
||
- `vpn_locations_cache.go` переведён на coordinator (без изменения endpoint `/api/v1/vpn/locations` и SSE `vpn_locations_changed`);
|
||
- добавлены unit-тесты coordinator (`refresh_coordinator_test.go`).
|
||
- 2026-03-09: SWR-схема расширена на transport health:
|
||
- подключён endpoint `POST /api/v1/transport/health/refresh` (manual trigger/force queue, без блокировки UI);
|
||
- `GET /api/v1/transport/clients` теперь ставит background health-refresh по eligible клиентам (`up|starting|degraded`) через shared coordinator (single-flight + backoff);
|
||
- GUI подписан на SSE `transport_client_health_changed`, кнопка refresh сначала триггерит backend health-refresh (best-effort), затем обновляет карточки.
|
||
- 2026-03-09: стабилизирован `SingBox + netns` runtime после регрессии:
|
||
- backend переведён на adaptive `nsenter` (default) с fallback на `ip netns exec`;
|
||
- netns setup/runtime используют единый exec selector (без дублирования режимов);
|
||
- исправлен `nft` comment-tag для NAT-правил (совместимо с текущим синтаксисом nft);
|
||
- добавлен документ ready-case: `docs/phase-d/D5_NETNS_RUNTIME_CASE.md`.
|
||
- 2026-03-09: выполнен рефакторинг netns-модулей:
|
||
- GUI логика переключения вынесена в `selective-vpn-gui/netns_debug.py` (state/button/toggle pipeline);
|
||
- API exec-логика вынесена в `selective-vpn-api/app/transport_netns_exec.go`;
|
||
- `vpn_dashboard_qt.py` и `transport_netns.go` оставлены как orchestrator/UI-обвязка.
|
||
- Созданы фазы A–D в `docs/phase-{a,b,c,d}` с описанием целей, критериев и задач.
|
||
- Задокументированы ожидания от Go API (routes, traffic, DNS, SmartDNS, VPN).
|
||
- Подготовлен первоначальный план для проверки GUI+API разделения.
|
||
- Закрыт этап E5.2 в Go API для `SingBox profiles`:
|
||
- добавлены endpoint'ы `GET/POST /api/v1/transport/singbox/profiles`, `GET/PATCH/DELETE /api/v1/transport/singbox/profiles/{id}`, `GET /api/v1/transport/singbox/features`;
|
||
- добавлен persistent state с ревизиями профилей (`profile_revision`) и active profile resolution;
|
||
- добавлен secrets-store (`/var/lib/selective-vpn/transport/secrets/singbox/*.json`, `0600`) с masked выдачей в API и без утечки plaintext в state.
|
||
- Добавлены unit-тесты E5.2 (`selective-vpn-api/app/transport_singbox_profiles_test.go`): CRUD+revision conflict, secrets persistence/masking, features endpoint.
|
||
- Закрыт этап E5.3 в Go API для `SingBox profiles`:
|
||
- добавлены endpoint'ы `POST /api/v1/transport/singbox/profiles/{id}/validate|render|apply|rollback` и `GET /api/v1/transport/singbox/profiles/{id}/history`;
|
||
- реализован flow `validate -> render -> apply` с binary-check (`sing-box check`), persisted rendered config и runtime apply через существующий transport backend;
|
||
- реализован rollback через history snapshots (включая restore предыдущего target config) + SSE события `singbox_profile_validated|rendered|applied|rollback|failed`.
|
||
- Добавлены unit-тесты E5.3 (`selective-vpn-api/app/transport_singbox_profiles_flow_test.go`): render/apply/rollback/history сценарий и негативная валидация.
|
||
- Усилены smoke-скрипты в `tests/`:
|
||
- `api_sanity.sh` (валидация JSON ключей, проверка method guard, проверка SSE headers),
|
||
- `events_stream.py` (живой SSE + триггер `trace_append`),
|
||
- `vpn_login_flow.py` (start/state/action/stop),
|
||
- `trace_append.sh` (append + readback через plain и json),
|
||
- `transport_flow_smoke.py` (state-machine smoke: draft/validate/confirm/apply/rollback).
|
||
- Добавлен общий раннер `tests/run_all.sh`.
|
||
- Скрипты прогнаны локально с `API_URL=http://127.0.0.1:8080`: все 5 smoke-тестов прошли успешно.
|
||
- Phase D дополнен endpoint-level матрицей (`53` legacy ручки) со статусами `ready-read/ready-write/ready-async/ready-interactive`.
|
||
- По состоянию на 2026-03-07 в ядре `61` mux-маршрут (`53 legacy + 8 transport base`), плюс action-subpath `GET /api/v1/transport/clients/{id}/metrics` внутри `clients/{id}/*`.
|
||
- В Phase C/Phase D добавлены архитектурные требования для переиспользования API на `web + iOS + Android` (единый `/api/v1`, TLS/token auth, mobile fallback/retry/idempotency).
|
||
- Зафиксирован frontend-стек для web prototype: `Vite + React + TypeScript` (SPA). `Next.js` отложен как опция только при требованиях SSR/edge/BFF.
|
||
- Запущен web prototype foundation в `selective-vpn-web/`:
|
||
- App shell (sidebar/header/navigation) на `React Router`,
|
||
- query-слой на `TanStack Query` с read-only snapshot (`healthz`, `status`, `vpn/status`, `vpn/login-state`),
|
||
- SSE connectivity-hook для `/api/v1/events/stream`,
|
||
- placeholder-страницы `VPN/Routes/DNS/Transport/Trace` для поэтапного подключения бизнес-логики.
|
||
- Зафиксирован целевой набор внешних transport-клиентов: `sing-box` (клиент), `dnstt-client`, `phoenix` (подключение к `slipstream` серверу).
|
||
- Подготовлен отдельный дизайн multi-client маршрутизации поверх единого PBR-ядра: per-client `fwmark/table/pref`, ownership-lock для доменов/IP, conntrack stickiness, UX предупреждения о конфликтных сценариях.
|
||
- Подготовлен API-контракт `transport/*` с JSON-примерами, dry-run валидацией и apply-моделью (`docs/phase-e/E2_TRANSPORT_API_CONTRACT.md`).
|
||
- В Go-ядре добавлены endpoint'ы `/api/v1/transport/*` (clients/policies/validate/apply/rollback/conflicts/capabilities + `clients/{id}/metrics`).
|
||
- Реализованы `validate` и `apply` c anti-conflict guardrails, optimistic revision lock, confirm token для force override, snapshot предыдущей policy и SSE события.
|
||
- Добавлены unit-тесты для transport validator/token lifecycle (`selective-vpn-api/app/transport_handlers_test.go`), `go test ./...` проходит.
|
||
- Реализован allocator policy v2 для transport clients: резервные диапазоны `mark/pref`, автонормализация состояния, детерминированное восстановление и auto re-balance при коллизиях.
|
||
- Реализован `POST /api/v1/transport/policies/rollback`: откат из snapshot с проверкой ревизии и валидацией перед применением.
|
||
- Подготовлен документ UX предупреждений и подтверждения конфликтного apply (`docs/phase-e/E4_VALIDATE_CONFIRM_APPLY_UX.md`).
|
||
- В `E1`/`E4` добавлены подпункты UX для переключения/подключения engine (`singbox|dnstt|phoenix`): `desired_engine vs active_engine`, switch states, guardrails и rollback action.
|
||
- Усилен transport backend-контракт (D4.1) в Go API:
|
||
- `GET /api/v1/transport/clients/{id}/metrics`,
|
||
- унифицированные DTO `TransportClientLifecycleResponse`, `TransportClientHealthResponse`, `TransportClientMetricsResponse`,
|
||
- runtime-поля клиента (`backend`, `allowed_actions`, counters/uptime, last_error с code/message),
|
||
- lifecycle/health ответы теперь возвращают кодируемые backend-ошибки и метрики в одном формате.
|
||
- Добавлен foundation backend-адаптеров (D4.2) в Go:
|
||
- выбор backend по конфигу клиента (`runner=mock|systemd`, fallback в `mock`),
|
||
- `provision/lifecycle/health` действия идут через единый adapter слой (без дублирования логики в UI),
|
||
- добавлен `POST /api/v1/transport/clients/{id}/provision` для backend-side подготовки runner (systemd units),
|
||
- для `dnstt` добавлен режим `ssh_tunnel/ssh_overlay` с orchestration двух unit (`ssh_unit` + `unit`) как единой системы.
|
||
- Запущен пункт D4.2.1 (production templates):
|
||
- `config.exec_start` переведён в optional override;
|
||
- если override не задан, Go-ядро формирует `ExecStart` по шаблонам:
|
||
- `singbox`: `<bin> run -c <config_path>`,
|
||
- `dnstt`: resolver/pubkey/domain/local_addr,
|
||
- `phoenix`: `<bin> -config <config_path>`;
|
||
- добавлены unit-тесты `transport_backends_test.go` для manual override, template build и DNSTT required fields.
|
||
- Запущен пункт D4.2.2 (systemd restart/watchdog tuning):
|
||
- для transport unit добавлены настраиваемые `Restart/RestartSec`, `StartLimitIntervalSec/StartLimitBurst`, `TimeoutStartSec/TimeoutStopSec`, `WatchdogSec`;
|
||
- для `dnstt + ssh overlay` добавлены отдельные `ssh_*` overrides tuning-полей для SSH unit;
|
||
- добавлены unit-тесты рендера tuning-полей (`transport_backends_test.go`).
|
||
- Запущен пункт D4.2.3 (unit hardening):
|
||
- для systemd unit включён baseline hardening по умолчанию (`NoNewPrivileges`, `ProtectSystem`, `ProtectHome`, `RestrictSUIDSGID`, `UMask` и др.);
|
||
- добавлен профиль `strict` и режим `off` через `config.hardening_profile`, а также `config.hardening_enabled`;
|
||
- добавлены точечные overrides hardening-полей и `ssh_*` overrides для overlay unit;
|
||
- добавлены unit-тесты hardening рендера (`baseline`, disable, `ssh_hardening_enabled=false`).
|
||
- Запущен пункт D4.2.4 (`runtime_mode` architecture foundation):
|
||
- в `client.config` введён `runtime_mode` (`exec|embedded|sidecar`) с нормализацией alias (`external|companion -> exec`);
|
||
- `exec` использует текущую production-цепочку backend-адаптеров;
|
||
- `embedded/sidecar` пока отвечают унифицированной ошибкой `TRANSPORT_BACKEND_RUNTIME_MODE_UNSUPPORTED` (без silent fallback);
|
||
- `GET /api/v1/transport/capabilities` расширен матрицей `runtime_modes`.
|
||
- Запущен пункт D4.2.5 (exec packaging profiles):
|
||
- для template-команд добавлены профили установки бинарей: `packaging_profile=system|bundled`;
|
||
- для `bundled` добавлен `bin_root` (default `/opt/selective-vpn/bin`) и switch `packaging_system_fallback`;
|
||
- `require_binary=true` включает fail-fast проверку наличия бинаря (включая ручные `*_bin` override);
|
||
- `GET /api/v1/transport/capabilities` расширен `packaging_profiles`.
|
||
- Запущен пункт D4.2.6 (manual pinned packaging automation):
|
||
- добавлены скрипты `scripts/transport-packaging/update.sh` и `rollback.sh` (checksum verify + atomic symlink switch + history rollback);
|
||
- добавлен `manifest.example.json` для pinned версий/URL/checksum;
|
||
- добавлен `manifest.production.json` с pinned артефактами для `singbox` (`v1.13.2`) и `phoenix` (`v1.0.1`) под `linux-amd64/linux-arm64`; для `dnstt` зафиксирован prebuilt-source (checksum pinned), но компонент по умолчанию `enabled=false` до trusted-source/signature этапа;
|
||
- добавлен foundation trusted source/signature/canary policy:
|
||
- `scripts/transport-packaging/source_policy.production.json` (trusted URL-prefix + signature mode policy);
|
||
- `update.sh` поддерживает `--source-policy`, `signature.type=openssl-sha256`, `--rollout-stage stable|canary|any`, `--cohort-id`, `--force-rollout`/`--canary`;
|
||
- для `manifest.production.json` policy подхватывается автоматически (если `source_policy.production.json` рядом);
|
||
- добавлен локальный smoke `tests/transport_packaging_smoke.sh` и включён в `tests/run_all.sh`;
|
||
- добавлен дополнительный smoke `tests/transport_packaging_policy_rollout.sh` (source trust + signature verify + canary gating);
|
||
- default остаётся manual, но добавлен opt-in auto-update слой.
|
||
- Запущен пункт D4.2.7 (auto-update opt-in):
|
||
- добавлен `scripts/transport-packaging/auto_update.sh` (interval gate + jitter + flock lock + state files);
|
||
- добавлены systemd-шаблоны `scripts/transport-packaging/systemd/transport-packaging-auto-update.{service,timer}` и env-шаблон;
|
||
- добавлен smoke `tests/transport_packaging_auto_update.sh` и включён в `tests/run_all.sh`.
|
||
- Запущен пункт D4.2.8a (singbox backend e2e):
|
||
- добавлен `tests/transport_singbox_e2e.py`:
|
||
- успешный lifecycle на `runner=mock` (`provision/start/health/restart/stop/metrics`);
|
||
- negative guard `runtime_mode=embedded` -> `TRANSPORT_BACKEND_RUNTIME_MODE_UNSUPPORTED`;
|
||
- fail-fast `require_binary=true` + missing `singbox_bin` -> `TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED`;
|
||
- тест подключён в `tests/run_all.sh`.
|
||
- Запущен пункт D4.2.8b (dnstt backend e2e):
|
||
- добавлен `tests/transport_dnstt_e2e.py`:
|
||
- успешный lifecycle на `runner=mock`;
|
||
- guard для `ssh_overlay` конфигурации (`ssh_host` обязателен, `ssh_unit` должен быть валидным) -> `TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED`;
|
||
- guard валидации шаблона DNSTT при неполном config -> `TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED`;
|
||
- тест подключён в `tests/run_all.sh`.
|
||
- Запущен пункт D4.2.8c (phoenix backend e2e):
|
||
- добавлен `tests/transport_phoenix_e2e.py`:
|
||
- успешный lifecycle на `runner=mock` (`provision/start/health/restart/stop/metrics`);
|
||
- guard `runtime_mode=embedded` -> `TRANSPORT_BACKEND_RUNTIME_MODE_UNSUPPORTED`;
|
||
- fail-fast `require_binary=true` + missing `phoenix_bin` -> `TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED`;
|
||
- тест подключён в `tests/run_all.sh`.
|
||
- Верификация D4.2.8 на live backend (2026-03-07):
|
||
- `selective-vpn-api` пересобран из текущего кода и перезапущен через systemd;
|
||
- `./tests/run_all.sh` выполнен полностью без `SKIP` на `transport_singbox_e2e`, `transport_dnstt_e2e`, `transport_phoenix_e2e`;
|
||
- подтверждён рабочий контракт `provision/start/health/restart/stop/metrics` и negative guards по каждому transport-клиенту.
|
||
- Запущен пункт D4.2.9 (операционные runbook'и):
|
||
- добавлен `scripts/transport_runbook.py` для воспроизводимого lifecycle-flow через API (`capabilities/create/provision/start/health/metrics/restart/stop/delete`);
|
||
- добавлен smoke `tests/transport_runbook_cli_smoke.sh` (mock singbox lifecycle через runbook helper);
|
||
- smoke подключён в `tests/run_all.sh`.
|
||
- Запущен пункт D4.2.10 (real-systemd e2e + cleanup):
|
||
- в backend добавлен cleanup для `runner=systemd` при `DELETE /api/v1/transport/clients/{id}`:
|
||
- удаляются только unit-файлы с ownership-marker `SVPN_TRANSPORT_ID=<client_id>`;
|
||
- выполняется `stop/disable`, затем `daemon-reload` + `reset-failed` (best-effort, с предупреждением в `message`, не блокируя delete);
|
||
- добавлен тест `tests/transport_systemd_real_e2e.py`:
|
||
- lifecycle для `singbox`, `dnstt(+ssh_unit)`, `phoenix` на реальном `systemd` backend (`runner=systemd`, `exec_start=/usr/bin/sleep`);
|
||
- проверка, что unit-файлы создаются с ownership-marker и удаляются после delete cleanup;
|
||
- тест подключён в `tests/run_all.sh`.
|
||
- Запущен пункт D4.2.11 (production-like transport e2e):
|
||
- добавлен тест `tests/transport_production_like_e2e.py`;
|
||
- проверяется lifecycle `provision/start/health/metrics/restart/stop/delete` для `singbox|dnstt(+ssh)|phoenix` на `runner=systemd`;
|
||
- вместо manual `exec_start` проверяются template-команды и `packaging_profile=bundled` с `bin_root`/`require_binary=true`;
|
||
- проверяется, что в unit-файлах присутствуют ожидаемые template-аргументы (`run -c`, `-config`, `-doh`/`domain`/`local_addr`) и ownership marker;
|
||
- тест подключён в `tests/run_all.sh`.
|
||
- Запущен пункт D4.2.12 (recovery runbook):
|
||
- добавлен `scripts/transport_recovery_runbook.py`:
|
||
- проверяет health клиента, делает до `N` попыток `restart`,
|
||
- при необходимости выполняет fallback `provision -> start`,
|
||
- при нерешённой деградации пишет diagnostics (`metrics`, `health`, `client_card`, шаги recovery) и возвращает `rc=2`;
|
||
- добавлен smoke `tests/transport_recovery_runbook_smoke.sh`:
|
||
- case1: успешное восстановление (`mock+exec`) -> `rc=0`,
|
||
- case2: ожидаемый fail-path (`runtime_mode=embedded`) -> `rc=2` + diagnostics file;
|
||
- smoke подключён в `tests/run_all.sh`.
|
||
- Запущен пункт D4.3 (platform compatibility matrix):
|
||
- добавлен артефакт `docs/phase-d/D4_PLATFORM_COMPATIBILITY_MATRIX.md` с матрицей `web + iOS + Android`;
|
||
- зафиксированы ограничения runtime (`exec=true`, `embedded/sidecar=false`) и правило backend-only orchestration для `singbox/dnstt/phoenix`;
|
||
- добавлен smoke `tests/transport_platform_compatibility_smoke.py` (capabilities + policy-contract checks), тест подключён в `tests/run_all.sh`.
|
||
- Верификация D4.3 на live backend (2026-03-07):
|
||
- `env API_URL=http://127.0.0.1:8080 ./tests/transport_platform_compatibility_smoke.py` -> `passed`;
|
||
- полный `./tests/run_all.sh` выполнен успешно с включённым `transport_platform_compatibility`.
|
||
- Запущен пункт D4.2.13 (singbox bootstrap-bypass):
|
||
- добавлен backend-модуль `app/transport_bootstrap_bypass.go`;
|
||
- для `runner=systemd` в `start/restart/stop` добавлена синхронизация bypass-маршрутов endpoint'ов transport-клиента;
|
||
- `singbox` endpoint-hosts извлекаются из `client.config` и `singbox` config-файла (`outbounds[*].server/address/host`);
|
||
- перед `start/restart` backend вычисляет `main` default-route и ставит `ip -4 route replace <endpoint>/32 table agvpn ...` (избежание bootstrap через `tun0`);
|
||
- на `stop` backend удаляет привязанные bypass-маршруты клиента;
|
||
- добавлен strict-режим `config.bootstrap_bypass_strict=true` (ошибка `TRANSPORT_BACKEND_BOOTSTRAP_BYPASS_FAILED` при невозможности применить bypass);
|
||
- добавлены unit-тесты `transport_bootstrap_bypass_test.go`, `go test ./...` проходит.
|
||
- Запущен пункт D4.2.14 (netns test contour):
|
||
- добавлен backend-модуль `app/transport_netns.go`:
|
||
- `netns` setup перед `start/restart` (veth pair, `ip_forward`, `nft` masquerade),
|
||
- optional cleanup на `stop` (`config.netns_auto_cleanup=true`),
|
||
- cleanup при backend `DELETE client` через `Cleanup()` (best-effort);
|
||
- `Provision()` для systemd поддерживает запуск transport внутри namespace через adaptive exec (`nsenter` default, fallback `ip netns exec`) при `config.netns_enabled=true`;
|
||
- для безопасного fail-fast добавлен strict-режим `config.netns_setup_strict=true` (`TRANSPORT_BACKEND_NETNS_SETUP_FAILED`);
|
||
- добавлены unit-тесты `transport_netns_test.go`, `go test ./...` проходит.
|
||
- Запущен пункт D4.2.15 (sing-box DNS migration):
|
||
- добавлен модуль `app/transport_singbox_dns_migration.go` (best-effort миграция legacy DNS в typed format);
|
||
- migration trigger встроен в `Provision()` для `singbox`:
|
||
- `dns.servers[*].address` -> `dns.servers[*].type/server/server_port`,
|
||
- `address_resolver/address_strategy` -> `domain_resolver/domain_strategy`,
|
||
- удаление `detour=direct` для DNS servers (невалидно для новых версий),
|
||
- backup оригинала: `<config>.legacy-dns.bak`;
|
||
- добавлены config-флаги:
|
||
- `singbox_dns_migrate_legacy` (default `true`),
|
||
- `singbox_dns_migrate_strict` (fail-fast с `TRANSPORT_BACKEND_SINGBOX_DNS_MIGRATE_FAILED`);
|
||
- добавлены unit-тесты `transport_singbox_dns_migration_test.go`;
|
||
- проверено на live client `sg-realnetns`: warning `legacy DNS servers is deprecated` исчез, transport после миграции поднимается и трафик через SOCKS проходит.
|
||
- В `selective-vpn-gui` добавлены transport методы в `api_client.py` и transition-логика в `dashboard_controller.py`:
|
||
- `draft -> validate -> (validated|risky) -> confirm -> apply`,
|
||
- обработка `POLICY_REVISION_MISMATCH` с возвратом в `draft`,
|
||
- общий foundation для последующего web/iOS/Android UX.
|
||
- Запущен пункт E4.3.1 (GUI engine foundation):
|
||
- в `selective-vpn-gui` на вкладке `AdGuardVPN` добавлен блок `Transport engine`;
|
||
- реализованы действия `Prepare/Connect/Disconnect/Restart` для выбранного transport-клиента через Go API;
|
||
- Запущен пункт E5.4.4 (GUI protocol editor flow):
|
||
- `Save draft` теперь сохраняет VLESS raw profile через `POST/PATCH /api/v1/transport/singbox/profiles/*` (через controller/api_client);
|
||
- при смене transport engine в `SingBox` вкладке editor автоматически подгружает профиль из Go API и отключается при `API unavailable/нет выбранного клиента`;
|
||
- `Preview/Validate/Apply` перед вызовом action теперь автоматически синхронизируют текущий draft из формы (guardrail: валидация обязательных client-полей до API action).
|
||
- добавлено состояние выбранного engine (status/iface/table/latency/last_error) и авто-обновление по transport SSE-событиям;
|
||
- расширен client/controller слой: `ApiClient.transport_client_action()` + `DashboardController.transport_client_action()`.
|
||
- Запущен пункт E5.4.5 (dashboard cards + context menu):
|
||
- карточки `Connection profiles` переведены на widget-плитки со state-driven стилями;
|
||
- активный runtime-профиль (`status=up`) подсвечивается зелёным;
|
||
- добавлено правокликовое меню по карточке: `Run`, `Edit`, `Delete`;
|
||
- `Edit` открывает отдельный modal-диалог и использует тот же VLESS form-editor (inline-редактор скрыт из основной вкладки);
|
||
- для `Delete` добавлен GUI/API flow с поддержкой `force=true` при policy-ссылках.
|
||
- Запущен пункт E5.4.6 (editor usability + profile creation):
|
||
- `security/transport` в VLESS editor больше не сбрасывают введённые значения при переключении режима;
|
||
- `Flow` переведён в editable режим: preset `xtls-rprx-vision` + возможность custom/raw значения;
|
||
- в блок `Connection profiles` добавлена кнопка `Create connection` с режимами:
|
||
- `Create from clipboard` (парсинг `vless://` из буфера),
|
||
- `Create from link...` (вставка URL),
|
||
- `Create manual` (пустой профиль с последующим edit);
|
||
- для создания профиля добавлен GUI/API create-flow transport-клиента (`POST /api/v1/transport/clients`) и auto-seed editor + optional draft save.
|
||
- Запущен пункт E5.4.7 (unified protocols import):
|
||
- link-import больше не завязан на VLESS: добавлен общий dispatcher по схеме URL;
|
||
- реализованы парсеры и raw-profile build для `vless/trojan/ss/hysteria2(hy2)/tuic`;
|
||
- добавлен общий набор helper-функций (query/tls/transport/raw route/inbound builder), чтобы следующие протоколы подключались без повторения кода;
|
||
- для non-VLESS профилей сохраняется raw-конфиг и доступен lifecycle (`Run/Validate/Apply`) через тот же pipeline.
|
||
- Запущен пункт E5.4.8 (multi-protocol form editor):
|
||
- `Protocol` в editor переключается между `vless/trojan/shadowsocks/hysteria2/tuic`;
|
||
- форма стала модульной: protocol-specific поля (password/ss_method/hy2_obfs/tuic options) показываются/скрываются по выбранному протоколу;
|
||
- `Save draft` теперь пишет `protocol=<selected>` и генерирует соответствующий outbound raw-конфиг;
|
||
- сохранены guardrails по transport/security и обязательным полям каждого протокола.
|
||
- Запущен пункт E5.4.9 (wireguard in common editor):
|
||
- добавлен protocol `wireguard` в тот же form-editor, без отдельной ветки UI;
|
||
- добавлены поля `private_key`, `peer_public_key`, `pre_shared_key`, `local_address`, `reserved`, `mtu`;
|
||
- включена валидация обязательных WG полей и сохранение в raw outbound `type=wireguard`;
|
||
- расширен universal link-import parser: поддержка `wireguard://` (и alias `wg://`) в `Create connection`.
|
||
- Запущен пункт E4.3.2 (SingBox switch pipeline):
|
||
- блок engine вынесен в отдельную вкладку `SingBox` (отдельно от `AdGuardVPN`);
|
||
- `Connect/Switch` теперь выполняет policy pipeline `validate -> confirm -> apply` и только потом `start`;
|
||
- при блокирующих конфликтах UI показывает confirm-диалог с force apply;
|
||
- добавлена кнопка `Rollback policy` (через `POST /api/v1/transport/policies/rollback`).
|
||
- Запущен пункт E5.1 (requirements freeze для протоколов SingBox):
|
||
- добавлен документ `docs/phase-e/E5_SINGBOX_PROTOCOLS_REQUIREMENTS.md`;
|
||
- зафиксированы архитектурные границы (`engine` vs `policy` vs `protocol profile`);
|
||
- зафиксированы требования к новой группе API `/api/v1/transport/singbox/profiles/*` (`CRUD/validate/render/apply/rollback/history/features`);
|
||
- зафиксированы требования к `typed + raw` режимам, secrets storage, versioning и SSE событиям.
|
||
- Запущен пункт E5.1.2 (protocol field inventory):
|
||
- добавлен шаблон `docs/phase-e/E5_SINGBOX_PROTOCOL_MATRIX_TEMPLATE.md`;
|
||
- добавлена заполненная матрица `docs/phase-e/E5_SINGBOX_PROTOCOLS_MATRIX.md` (общие блоки + 6 протоколов + guardrails + MVP/Advanced/Raw-only split);
|
||
- добавлен machine-readable пример `docs/phase-e/E5_SINGBOX_PROTOCOLS_MANIFEST.example.json` для последующей генерации UI-форм.
|
||
- Запущен пункт E5.1.3 (client form baseline):
|
||
- добавлен документ `docs/phase-e/E5_SINGBOX_CLIENT_FORM_MATRIX.md`;
|
||
- зафиксированы UI-блоки клиентской формы (`Profile`, `Server/Auth`, `Transport`, `Security`, `Sniffing`, `Advanced Dial`);
|
||
- явно исключены server-only поля (billing/traffic/expiry/subscription) и добавлены guardrails для `VLESS+Reality`.
|
||
- Запущен пункт E5.4.1 (desktop dashboard foundation):
|
||
- добавлен документ `docs/phase-e/E5_2_SINGBOX_DESKTOP_DASHBOARD_SPEC.md`;
|
||
- вкладка `SingBox` перестроена на 3 зоны: runtime card, profile settings, global defaults;
|
||
- внедрён `card-based dashboard`: верхние metric cards + grid profile cards (с выбором карточки);
|
||
- включён compact UX: настройки и activity log открываются кнопками (по умолчанию скрыты);
|
||
- добавлены `Use global` override-переключатели, effective summary и локальное сохранение настроек;
|
||
- runtime pipeline `Prepare/Connect-Switch/Disconnect/Restart/Rollback` сохранён без изменения API-контракта.
|
||
- Запущен пункт E5.4.2 (GUI wiring profile actions):
|
||
- в `selective-vpn-gui/api_client.py` добавлены typed-модели и методы для `POST /api/v1/transport/singbox/profiles/{id}/validate|apply`;
|
||
- в `selective-vpn-gui/dashboard_controller.py` добавлены `singbox_profile_validate_action()` и `singbox_profile_apply_action()` с унифицированным `ActionView`;
|
||
- в `selective-vpn-gui/vpn_dashboard_qt.py` заглушки `Validate profile`/`Apply profile` заменены на реальные вызовы API с логированием в activity-log и авто-refresh runtime state.
|
||
- Запущен пункт E5.4.3 (profile flow completeness + UX hardening):
|
||
- в `selective-vpn-gui/api_client.py` добавлены методы `profiles list/get/create/patch/render/rollback/history` и соответствующие typed DTO;
|
||
- в `selective-vpn-gui/dashboard_controller.py` добавлены:
|
||
- `singbox_profile_render_preview_action()`,
|
||
- `singbox_profile_rollback_action()`,
|
||
- `singbox_profile_history_lines()`,
|
||
- `singbox_profile_ensure_linked()` с auto-create профиля из `config_path` (raw mode) и auto-link `meta.client_id`;
|
||
- в `selective-vpn-gui/vpn_dashboard_qt.py` добавлены кнопки `Preview render`, `Rollback profile`, `History`;
|
||
- добавлена авто-синхронизация `engine -> profile` при выборе/refresh engine (`_sync_selected_singbox_profile_link`);
|
||
- выполнен лёгкий рефакторинг profile handlers (`_selected_singbox_profile_context`, `_run_singbox_profile_action`) без смены backend-контракта.
|
||
- Запущен пункт B3 (resolver diff + improvement plan):
|
||
- добавлен документ `docs/phase-b/B2_RESOLVER_DIFF_AND_IMPROVEMENT_PLAN.md`;
|
||
- зафиксированы границы ролей `system resolver` (authoritative для PBR) и `singbox DNS` (transport-level resolver);
|
||
- зафиксирован приоритетный backlog улучшений resolver (`R1-R4`: надёжность, качество резолва, observability API/SSE, multi-client ownership safety).
|
||
- Запущен пункт F1.1 (план модульности):
|
||
- добавлен документ `docs/phase-f/F1_REFACTOR_MODULARITY_PLAN.md`;
|
||
- зафиксированы целевые разрезы для `vpn_dashboard_qt.py`, `api_client.py`, `dashboard_controller.py`, `transport_handlers.go`;
|
||
- зафиксирован безопасный порядок реализации и DoD без изменения API-контракта.
|
||
- Текущий статус: transport backend/API foundation и базовый desktop UI-flow включены (`SingBox` tab: lifecycle actions + switch pipeline + rollback).
|
||
- Зафиксирован план по VPN локациям:
|
||
- без большой кнопки `Refresh`/`Apply`, но с лёгким icon-trigger refresh (фоновый, неблокирующий),
|
||
- в будущем без кнопки `Apply location` (выбор в списке сразу инициирует connect/reconnect),
|
||
- список грузится асинхронно, UI не блокируется при недоступном CLI/большом latency,
|
||
- используется кеш последнего успешного списка + SWR (stale-while-revalidate) + backoff/retry + single-flight lock.
|
||
- Диагностирован кейс `eu.posthog.com` (страница "region unavailable" в браузере при включенном wildcard):
|
||
- root cause: `smartdns` runtime был с `nftset-timeout yes`, а `agvpn_dyn4` создан без `timeout`-флага (`flags interval`), из-за чего runtime-добавление IP не происходило;
|
||
- applied fix: `nftset-timeout no` в `smartdns.conf`;
|
||
- verification: после `smartdns-local` restart + DNS query через `127.0.0.1#6053` все актуальные `A`-IP `eu.posthog.com` появились в `inet/agvpn/agvpn_dyn4`.
|
||
- Диагностирован второй корень проблемы для `selective`:
|
||
- `routes/update` перезаписывал `agvpn_dyn4` только списком из resolver cache, из-за чего runtime-IP (SmartDNS nftset) периодически терялись;
|
||
- applied fix в Go (`routes_update.go`): merge `resolver wildcard IPs` + `existing agvpn_dyn4` при `runtime_nftset=true`;
|
||
- verification: после `smartdns` query и `POST /api/v1/routes/update` PostHog IP (`3.121.142.0`, `3.67.52.82`, `18.158.106.188`, `63.183.90.15`, `63.177.143.4`, `63.182.85.19`) не исчезают из `agvpn_dyn4`.
|
||
- По запросу добавлен временный static fallback для PostHog в `/etc/selective-vpn/static-ips.txt`:
|
||
- `3.121.142.0/24`, `3.67.52.0/24`, `18.158.106.0/24`, `63.177.143.0/24`, `63.182.85.0/24`, `63.183.90.0/24`;
|
||
- verification: после `routes/update` подсети присутствуют в `inet/agvpn/agvpn4`.
|
||
- Расширено покрытие PostHog-хостов для selective:
|
||
- в wildcard state добавлены `app-static.eu.posthog.com` и `internal-j.posthog.com`;
|
||
- добавлен временный static fallback для `internal-j.posthog.com`: `3.88.247.0/24`, `3.95.129.0/24`, `54.90.36.0/24`;
|
||
- verification: по packet capture (`tcpdump`) HTTPS к `internal-j.posthog.com` уходит через `tun0` (маркировка срабатывает).
|
||
- Финальная проверка: пользователь подтвердил, что `eu.posthog.com` в режиме `selective` открывается и работает штатно.
|
||
- Реализован backend cache/SWR для `/api/v1/vpn/locations`:
|
||
- мгновенный ответ из кеша (`/var/lib/selective-vpn/vpn-locations-cache.json`),
|
||
- фоновый refresh `list-locations` с single-flight lock,
|
||
- метаданные ответа: `updated_at`, `stale`, `refresh_in_progress`, `last_error`, `next_retry_at`,
|
||
- backoff на ошибках, SSE-событие `vpn_locations_changed`.
|
||
- Реализована стабилизация GUI по локациям:
|
||
- загрузка списка через отдельный `QThread` (без блокировки главного потока),
|
||
- убрана кнопка `Apply & restart loop`, включён auto-apply при выборе локации в `QComboBox`,
|
||
- добавлена status/meta строка для состояния кеша/обновления/ошибок.
|
||
- Реализован V2 "умный поиск" без отдельного поля ввода:
|
||
- typed-buffer в списке локаций с live-фильтрацией и сортировкой по релевантности,
|
||
- матч по началу ISO/строки/слова, backspace уменьшает фильтр,
|
||
- таймаут-сброс буфера.
|
||
- Исправлена точность применения VPN-локации:
|
||
- вместо `ISO` в `connect -l` теперь передаётся точный `target` локации (например `United States Los Angeles`),
|
||
- устранён кейс, когда в UI выбран один город, а autoloop подключается к другому дефолтному городу страны.
|
||
- Усилен safety flow для `set location` (L1 hardening):
|
||
- backend теперь резолвит пользовательский выбор в безопасный `connect`-аргумент (`city` или `ISO`) по каталогу локаций,
|
||
- при нераспознанной локации возвращается `422` без рестарта autoloop (текущий туннель сохраняется),
|
||
- в GUI при apply передаётся `target + iso + label`, чтобы повысить точность матчинга и убрать ложные реконнекты.
|
||
- Добавлены проверки для блока локаций:
|
||
- `tests/vpn_locations_swr.sh`,
|
||
- unit-тесты parser-а `selective-vpn-api/app/vpn_locations_cache_test.go`.
|
||
- Smoke-набор `tests/run_all.sh` обновлён и прогнан успешно (включая новый `vpn_locations_swr`).
|
||
- В GUI локаций `Sort/Refresh` оставлены снаружи списка (рядом с `Location`), popup содержит только локации.
|
||
- `Refresh` отправляет trigger `GET /api/v1/vpn/locations?refresh=1` в фоне через `LocationsThread`, UI не блокируется.
|
||
|
||
## Следующие шаги
|
||
- M1 (priority): закрыть backend multi-interface foundation:
|
||
- interface orchestrator (`E3.3`), ownership registry (`E3.4`), anti-mixing guard (`E3.5`), transaction pipeline (`E3.6`);
|
||
- критерий: минимум 2 engine работают одновременно без пересечения table/mark/pref и без ложного egress mixing.
|
||
- M2 (priority): довести runtime наблюдаемость (`E6.6`) для всех engine в едином контракте ядра.
|
||
- M3 (priority): после backend foundation включить GUI-флоу выбора/переключения engine-профилей поверх нового orchestration-слоя (тонкий UI, без бизнес-логики в GUI).
|
||
- E5.4: реализовать GUI-блок протоколов в `SingBox` вкладке (list/editor/validate/preview/apply/rollback).
|
||
- B3.1: реализовать resolver status snapshot API + SSE (`resolver_status_changed`, `resolver_refresh_completed`).
|
||
- B3.2: реализовать adaptive upstream scoring + negative cache TTL policy.
|
||
- B3.3: внедрить `domain_owner_map` и conflict guard для multi-client DNS ownership.
|
||
- D4.2 (supporting track): поддерживать backend-ready состояние `dnstt-client` и `phoenix` без UI-расширения в текущем этапе.
|
||
- P1.1: спроектировать `service profiles` (например, `PostHog`) как пресеты доменов/wildcard/static-fallback/policy.
|
||
- P1.2: добавить API для профилей (`list/apply/remove/export`) и idempotent apply в Go-ядре.
|
||
- P1.3: добавить в GUI чекбоксы/тогглы профилей с применением в 1 клик и rollback.
|
||
- P1.4: подготовить переносимый формат профилей для web + iOS + Android (единый JSON schema).
|
||
- V2.2: собрать пользовательский отчёт по UX поиска/автоприменения локаций в desktop (latency, false matches, edge-cases).
|
||
- V2.3: при подтверждении UX перенести одинаковую модель локаций (`cache/SWR + typed-buffer + auto-apply`) в web/mobile клиенты.
|
||
- Доработать Phase B (описание контроллеров, зависимости, условия запуска).
|
||
- Завершить Phase D (матрица endpoint со статусом `web-ready` и явные блокеры для веб-прототипа).
|
||
- Формализовать реализацию D3.1-D3.4 (gateway/auth, web старт, mobile transport profile, запуск iOS/Android на общем API).
|
||
- E4.4: перенести foundation state-machine в web prototype UI (`React + TypeScript` на `Vite`) без изменения API-контракта Go.
|
||
- E4.5: добавить настройки видимости protocol tabs (включать/выключать вкладки `SingBox/DNSTT/Phoenix`) через UI settings/profile.
|
||
- F1.4: выполнить декомпозицию `selective-vpn-gui/vpn_dashboard_qt.py` на tab/modules и вынести event/locations сервисы.
|
||
- F1.11: вынести переиспользуемую transport-логику в подпакеты (`app/transport/*`) для частей без циклических зависимостей (через facade+deps).
|
||
- F1.11.r1: продолжить resolver-декомпозицию в отдельной папке `app/resolver/*` через bridge-слой (`domain cache`, `adaptive live-batch`, `io/json helpers`) без смены внешнего поведения.
|
||
- F2.1 (post-refactor): перейти на `singbox@.service` как целевой production-runtime (instance-per-profile + без лавины unit-файлов).
|
||
- F2.2 (post-refactor): сделать управляемую миграцию/cleanup старых `singbox-*.service` и зафиксировать runbook для деплоя/отката.
|
||
|
||
## Дальний backlog (после завершения текущего приложения)
|
||
- L1.1: `AmneziaWG` интеграция как отдельный transport executor/backend kind (`amneziawg`) через единый Go control-plane API.
|
||
- L1.2: поддержка `AmneziaWG` в GUI как отдельный engine/profile workflow (без смешивания с текущим `wireguard`/`singbox` этапом).
|
||
- L1.3: обновление capability-матрицы, runbook/e2e и packaging-профилей для `amneziawg` после полного закрытия приоритетных задач desktop (`SingBox` + core stability).
|