188 KiB
188 KiB
Execution Tracker
Дата обновления: 2026-03-15 Владелец: Engineering
Статусы фаз
- A. Аудит API и HTTP-маршрутов
- [~] B. Проверка ядра и внешних зависимостей
- [~] C. Подготовка к веб-совместимости
- [~] D. Документирование и итоговые проверки
- [~] E. Дизайн multi-client PBR и anti-conflict guardrails
- [~] F. Рефакторинг и модульность (декомпозиция крупных файлов GUI/API/Go)
Порядок реализации (фикс)
-
- Сначала полностью закрываем backend-интеграцию transport-клиентов (
singbox,dnstt-client,phoenix) в Go-ядре.
- Сначала полностью закрываем backend-интеграцию transport-клиентов (
-
- Затем переиспользуем backend-контракт в desktop GUI (без дублирования бизнес-логики).
-
- Веб-прототип (
Vite + React + TS) начинаем только после завершения backend+GUI этапа.
- Веб-прототип (
Фокус текущего этапа (freeze)
- Desktop-first: сейчас работаем только по
SingBoxвкладке иSingBoxGo API. DNSTTиPhoenix: backend foundation/e2e уже зафиксированы, UI-вкладки и протокольные экраны для них в этом этапе не развиваем.- Приоритет реализации:
- временно сужаем активный backend scope до
E3.3/E3.6:- единый
interface orchestratorpath для всех mutating transport-операций, validate -> plan -> confirm -> apply -> health-check -> commitбез частично подтверждённого runtime;
- единый
E5.4GUI-протоколы внутри отдельной вкладкиSingBoxне расширяем, покаE3.3/E3.6не доведены до стабильного backend-state.
- временно сужаем активный backend scope до
Текущие шаги
- 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 resolvervssingbox 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) + templateexec_start+ restart/watchdog tuning + unit hardening +runtime_modefoundation + 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] внедрён
singboxbootstrap-bypass в Go backend (transport): endpoint/32host-routes вtable agvpnчерезmaindefault-route передstart/restart, cleanup наstop - Step D4.2.14: [x] добавлен
netnstest-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+ statetransport-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_idlock 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_idlock helper с детерминированным порядком захвата для patch/multi-target операций; lifecycle start/restartтеперь расширяют lock-set до всехsame-netnsSingBoxpeer-ов, включая binding черезiface -> netns, поэтому cross-iface_idnetns-конфликт не обходит 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-netnspeer matching без фильтра по runtime-status (lock захватывает всех netns-peer клиентов), чтобы избежать гонок при stale/lag status и не пропускать cross-iface сериализацию. - lifecycle/provision/create/patch/delete/netns теперь используют общий
ifaceserialization 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 setsagvpn_pi_*); - optional ip rule stage (
fwmark/pref -> lookup table) под отдельным флагом;
- nft stage: per-interface CIDR set apply/cleanup (
- 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.
- compile-plan
- 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) + endpointGET /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_digestguard: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 (
conntrackstickiness + 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->ownermapping; endpointGET /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 на
domainselector: owner-switch поdomain/*.domainтеперь учитывает resolverdomain-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_checksummary для 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-Keystorage/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 pathtransport client provisionпереведён на двухфазный flow:- под
transportMuостаются только snapshot/bind и final commit, - сам
backend.Provision()выполняется подiface-lock, но вне глобального mutex;
- под
transport client lifecycleпереведён на двухфазный flow:same-netnspeer-stop и targetbackend.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-netnspeeriface_id, если restart затрагивает binding после toggle.
- config patch/save остаётся под
- Step E3.6.2.3: [x] расширен health gate на более полный per-interface observability/runtime coverage
TransportPolicyResponse.health_checkвозвращает per-iface_idsummary 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 подход для
netnstoggle-пути (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-unlockconditional-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).
- lock-id resolver теперь использует короткий snapshot (
- 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переведён на pipelinevalidate -> confirm -> apply+ добавленRollback policybutton - 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).
- read-only панель
- 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.
- draft-таблица intents (
- 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)для лучшей читаемости.
- добавлены quick templates (
- 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 используют единый
policyTargetssnapshot (transport clients + adguardvpn); - в GUI включён scope-filter
All/Transport/AdGuard VPN, client selector для intents поддерживаетAdGuard VPN (adguardvpn); - сняты artificial guard-блоки
adguard-only, чтобы low-level MultiIF слой работал единообразно для всех engine.
- в backend compile/validate/apply/rollback path добавлен virtual client
- Step E4.5.6: [x] интерфейсный слой MultiIF переведён на backend-source-of-truth для AdGuard:
GET /api/v1/transport/interfacesтеперь добавляет virtual interface-rowadguardvpn(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создаёт тестовыйdomainintent (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иAppliedpolicy intents:- добавлена отдельная read-only таблица
Applied intents(текущая backend policy); - status/state строка теперь показывает оба счётчика (
draftиapplied) для быстрого сравнения; - API-unavailable/error paths очищают обе таблицы синхронно, чтобы не было stale-данных.
- добавлена отдельная read-only таблица
- Step E4.5.9: [x] добавлена визуализация конфликтов валидации policy (MultiIF):
- отдельная read-only таблица
Validation conflicts(type/severity/owners/reason/suggested resolution); - таблица синхронизируется из
validate/applyflow и сохраняет последний результат проверки; - при API-unavailable/endpoint-error таблица очищается вместе с draft/applied, чтобы не показывать stale-conflicts.
- отдельная read-only таблица
- Step E4: [~] UX предупреждения и conflict flow (
validate -> confirm -> apply, включая engine switch/connect) - Step E5: [~] зафиксировать требования для протоколов во вкладке
SingBoxи target Go APIsingbox 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-linkengine -> 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-menuRun/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_idruntime snapshot изtransport interfaces + clients + policy compile-plan + egress identity; - DTO включает binding (
runtime_iface/active_iface/netns/routing_table), active client, aggregatestatus/latency/last_error, status counters иengine_counts/rule_countбез ручной склейки на UI.
- новый backend aggregator собирает per-
- Step E6.6.2: [x] endpoint
GET /api/v1/transport/runtime/observability+ SSEtransport_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 clientadguardvpnв том же 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 snapshotadguardvpnи использует 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.
- добавлен отдельный UI-cache
- 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/clientsbackground refresh +POST /transport/health/refresh+ SSEtransport_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, сохранён единыйregisterAPIRoutesfacade) - 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-контракта endpointGET /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 flowhelper'ы в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 runtimehealth/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 handlersservice/timer блока вapp/routes_handlers_service.go;routes_handlers.goоставлен для status/clear/fix/update endpoints - Step F1.11.45: [x] выполнен разнос
routes handlersclear/precheck/op-lock блока вapp/routes_handlers_ops.go;routes_handlers.goоставлен для status/policy-fix/update endpoints - Step F1.11.46: [x] вынесены
egress identityHTTP 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 identityruntime-контура: 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/cleanuporchestration - 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, общий helperasString/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-locationendpoint-потока и 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 mappingdomainFiles - 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 wrappermakeCmdHandler - 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, базовый файл оставлен для sharedautoloopLocationSpecи 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без изменения кодов/контрактов endpointPOST /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-контракта endpointPOST /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 facadeapi_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) с сохранением фасадов иtrafficRulesStatealias вapp/traffic_mode.go - Step F1.13.6: [x] удалены лишние внутренние wrapper-функции
traffic_mode.go(cgroup/nftObjectMissing),buildEffectiveOverridesпереведён на прямые вызовыtrafficmodepkg.ResolveCgroupUIDRanges - Step F1.13.7: [x] вынесен
ip ruleapply/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_modecore: вынесеныiface/confighelper'ы в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
SingBoxruntime на шаблонныйsystemdunitsingbox@.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-.service -> template instance singbox@.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-.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).
- compile-plan перешёл на owner-scoped nft naming:
- 2026-03-20: закрыт E3.5 domain destination-lock coverage:
detectTransportDestinationLockConflictsрасширен наdomainselector (включая*.domain), теперь owner-switch проверяет sticky-lock поdomain-cache(direct+wildcard) без сетевых запросов;- добавлен
domain-cachebridge 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=...строкой.
- добавлен rate-limited trace helper (
- 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):systemdprovision-path дляsingbox@<id>.serviceпереведён на модельtemplate + per-instance drop-in;- добавлена генерация/обновление
singbox@.service(managed template) и drop-insingbox@<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(legacysing-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).
- state
- 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переведён наtransportcfgsecrets-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_identityendpoint 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.go820 -> 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 контрактов backendsystemd;- выполнен 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.go923 -> 820,transport_singbox_profiles.go979 -> 897,transport_singbox_profiles_flow.go1372 -> 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);
- template command builders (
- добавлен
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).
- latency probing pipeline (
- 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.goresolver_dns_config_bridge.goresolver_domain_cache_bridge.goresolver_host_lookup_bridge.goresolver_mode_runtime_bridge.goresolver_planning_bridge.goresolver_precheck_finalize_bridge.goresolver_precheck_types_bridge.goresolver_resolve_batch_bridge.goresolver_runtime_tuning_bridge.goresolver_start_log_bridge.goresolver_static_labels_bridge.goresolver_summary_log_bridge.goresolver_timeout_recheck_bridge.goresolver_types_bridge.goresolver_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 DTOapp <-> 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 corruptedexpires_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(вынесен parserip 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(вынесен parsingip rule show+ route-probeip 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'ыsmartdnsunit 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. - вынесен
systemdadapter в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 ./...— успешно.
- вынесен systemd helper-блок в
- 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+ bridgeselective-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) + bridgeselective-vpn-api/app/resolver_static_labels_bridge.go; - вынесен timeout-quarantine recheck блок в
selective-vpn-api/app/resolver/timeout_recheck.go(RunTimeoutQuarantineRecheck) + bridgeselective-vpn-api/app/resolver_timeout_recheck_bridge.go; - вынесен загрузчик DNS-конфига в
selective-vpn-api/app/resolver/dns_config.go(LoadDNSConfig) + bridgeselective-vpn-api/app/resolver_dns_config_bridge.go; - вынесен host-lookup блок в
selective-vpn-api/app/resolver/host_lookup.go(ResolveHost,DigAWithPolicy) + bridgeselective-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) + bridgeselective-vpn-api/app/resolver_runtime_tuning_bridge.go; - вынесен artifact-builder блок (
IPs/IPMap/Direct/Wildcard) вselective-vpn-api/app/resolver/artifacts.go(BuildResolverArtifacts) + bridgeselective-vpn-api/app/resolver_artifacts_bridge.go; - вынесен planning блок (
fresh/toResolve/cache_negative/quarantine/stale/precheck_scheduled) вselective-vpn-api/app/resolver/planning.go(BuildResolvePlanning) + bridgeselective-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) + bridgeselective-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) + bridgeselective-vpn-api/app/resolver_resolve_batch_bridge.go; - вынесен summary/breakdown/precheck-log блок в
selective-vpn-api/app/resolver/summary_log.go(LogResolverSummary) + bridgeselective-vpn-api/app/resolver_summary_log_bridge.go; - вынесен runtime DNS mode apply/log блок в
selective-vpn-api/app/resolver/mode_runtime.go(ApplyDNSModeRuntime,LogDNSMode) + bridgeselective-vpn-api/app/resolver_mode_runtime_bridge.go; - вынесен start/policy log блок в
selective-vpn-api/app/resolver/start_log.go(LogResolverStart) + bridgeselective-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— успешно.
- вынесен wildcard matcher в подпакет
- 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 stopidempotency для 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/restartauto-provision для новых SingBox-профилей:- в
transportSystemdBackend.Actionдобавлен общий fallback дляSingBox: приstart/restartи ошибкеunit not found/not loadedbackend автоматически выполняет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()).
- добавлены отдельные main-пакеты:
- 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 ./...успешны.
- вынесен token-store в
- 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+ facadeapi/transport.py; - backward compatibility сохранена (
api_client.pyfacade и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.modvs 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) добавлен backendegress refresh burstдляadguardvpn(force refresh сразу + отложенные итерации), чтобы egress IP/geo догонял фактическое переключение туннеля; - в
egress identityforce-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без ранней остановки.
- в Go API (
- 2026-03-10: доведён anti-regression контур
SingBox netns + multi-profile switch:- в backend lifecycle
POST /api/v1/transport/clients/{id}/start|restartдобавлен обязательный preflight дляSingBox(render/validate + materializeconfig_path) доsystemctl, чтобы старт не обходил профильную проверку; - при
start/restartвыбранногоSingBoxвnetnsbackend теперь автоматически останавливает другиеSingBox-клиенты в этом же namespace перед запуском (убран конфликтlisten 127.0.0.1:10808: address already in useпри switch между профилями); - в
systemdbackend добавлен best-effortsystemctl reset-failed <unit>передstart/restart, чтобы switch не упирался вstart-limit-hitпосле прошлых неуспешных стартов; - закрыт legacy кейс
packet_encoding: none(SingBox panicunknown value): нормализация выполняется в Go-render pipeline, а в GUI-editor/link-importnoneбольше не пишется в raw config; - для
egress identityприtransport:<id>иnetns_enabled=trueуSingBoxdirect-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).
- в backend lifecycle
- 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с локальнымsocksinbound 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-inbounddirect-netns fallback отключён; при ошибке proxy probe возвращается ошибка, чтобы UI не показывал ложный AdGuard/system IP как egress transport-клиента; - в GUI
Run/Startдобавлен preflightsingbox profile apply(skip_runtime=true) перед switch/start, чтобы materializeconfig_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: доведён
E6netns runtime contour дляtransport:<id>:- netns-probe больше не зависит от DNS внутри namespace (
curl --resolveс host-side резолвом endpoint хоста); - добавлен fallback probe через локальный
SingBoxSOCKS inbound (socks5h://127.0.0.1:<port>) для изолированных netns; - ограничена длительность netns-probe (по умолчанию
SVPN_EGRESS_NETNS_MAX_ENDPOINTS=1) и уплотнены сообщения ошибок (last_error) для UI.
- netns-probe больше не зависит от DNS внутри namespace (
- 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) и SSEegress_identity_changed; - добавлен GeoIP lookup (country_code/country_name) с кэшем;
- desktop GUI подключён к новому API:
AdGuardVPNиSingBoxкарточки показываютIP + country, флаг строится в UI изcountry_code.
- добавлены endpoint'ы
- 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:transportlatency probe timeout снижен до900ms;- устранён lock-contention в background health refresh: долгий
backend.Health()вынесен из-подtransportMu, чтобы запросыGET /transport/clients/{id}/healthне ждали второй probe под mutex.
- 2026-03-09: полировка
SingBoxruntime 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:
systemdbackend теперь пишетhealth.latency_msдля активных клиентов через TCP probe endpoint'ов из runtime-конфига (singbox outbounds);- для
netns_enabled=trueprobe выполняется через тот же 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).
- добавлен и подключён endpoint
- 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и SSEvpn_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), затем обновляет карточки.
- подключён endpoint
- 2026-03-09: стабилизирован
SingBox + netnsruntime после регрессии:- backend переведён на adaptive
nsenter(default) с fallback наip netns exec; - netns setup/runtime используют единый exec selector (без дублирования режимов);
- исправлен
nftcomment-tag для NAT-правил (совместимо с текущим синтаксисом nft); - добавлен документ ready-case:
docs/phase-d/D5_NETNS_RUNTIME_CASE.md.
- backend переведён на adaptive
- 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-обвязка.
- GUI логика переключения вынесена в
- Созданы фазы 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.
- добавлены endpoint'ы
- Добавлены 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.
- добавлены endpoint'ы
- Добавлены 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 матрицей (
53legacy ручки) со статусамиready-read/ready-write/ready-async/ready-interactive. - По состоянию на 2026-03-07 в ядре
61mux-маршрут (53 legacy + 8 transport base), плюс action-subpathGET /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для поэтапного подключения бизнес-логики.
- App shell (sidebar/header/navigation) на
- Зафиксирован целевой набор внешних 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иapplyc 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) как единой системы.
- выбор backend по конфигу клиента (
- Запущен пункт 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).
- для transport unit добавлены настраиваемые
- Запущен пункт 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).
- для systemd unit включён baseline hardening по умолчанию (
- Запущен пункт D4.2.4 (
runtime_modearchitecture 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) и switchpackaging_system_fallback; require_binary=trueвключает fail-fast проверку наличия бинаря (включая ручные*_binoverride);GET /api/v1/transport/capabilitiesрасширенpackaging_profiles.
- для template-команд добавлены профили установки бинарей:
- Запущен пункт 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.jsonpolicy подхватывается автоматически (если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+ missingsingbox_bin->TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED;
- успешный lifecycle на
- тест подключён в
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;
- успешный lifecycle на
- тест подключён в
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+ missingphoenix_bin->TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED;
- успешный lifecycle на
- тест подключён в
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);
- удаляются только unit-файлы с ownership-marker
- добавлен тест
tests/transport_systemd_real_e2e.py:- lifecycle для
singbox,dnstt(+ssh_unit),phoenixна реальномsystemdbackend (runner=systemd,exec_start=/usr/bin/sleep); - проверка, что unit-файлы создаются с ownership-marker и удаляются после delete cleanup;
- lifecycle для
- тест подключён в
tests/run_all.sh.
- в backend добавлен cleanup для
- Запущен пункт 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;
- проверяет health клиента, делает до
- добавлен smoke
tests/transport_recovery_runbook_smoke.sh:- case1: успешное восстановление (
mock+exec) ->rc=0, - case2: ожидаемый fail-path (
runtime_mode=embedded) ->rc=2+ diagnostics file;
- case1: успешное восстановление (
- 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-клиента; singboxendpoint-hosts извлекаются изclient.configиsingboxconfig-файла (outbounds[*].server/address/host);- перед
start/restartbackend вычисляетmaindefault-route и ставитip -4 route replace <endpoint>/32 table agvpn ...(избежание bootstrap черезtun0); - на
stopbackend удаляет привязанные bypass-маршруты клиента; - добавлен strict-режим
config.bootstrap_bypass_strict=true(ошибкаTRANSPORT_BACKEND_BOOTSTRAP_BYPASS_FAILEDпри невозможности применить bypass); - добавлены unit-тесты
transport_bootstrap_bypass_test.go,go test ./...проходит.
- добавлен backend-модуль
- Запущен пункт D4.2.14 (netns test contour):
- добавлен backend-модуль
app/transport_netns.go:netnssetup передstart/restart(veth pair,ip_forward,nftmasquerade),- optional cleanup на
stop(config.netns_auto_cleanup=true), - cleanup при backend
DELETE clientчерезCleanup()(best-effort);
Provision()для systemd поддерживает запуск transport внутри namespace через adaptive exec (nsenterdefault, fallbackip 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 ./...проходит.
- добавлен backend-модуль
- Запущен пункт 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(defaulttrue),singbox_dns_migrate_strict(fail-fast сTRANSPORT_BACKEND_SINGBOX_DNS_MIGRATE_FAILED);
- добавлены unit-тесты
transport_singbox_dns_migration_test.go; - проверено на live client
sg-realnetns: warninglegacy 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 режим: presetxtls-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://(и aliaswg://) вCreate connection.
- добавлен protocol
- Запущен пункт E4.3.2 (SingBox switch pipeline):
- блок engine вынесен в отдельную вкладку
SingBox(отдельно отAdGuardVPN); Connect/Switchтеперь выполняет policy pipelinevalidate -> confirm -> applyи только потомstart;- при блокирующих конфликтах UI показывает confirm-диалог с force apply;
- добавлена кнопка
Rollback policy(черезPOST /api/v1/transport/policies/rollback).
- блок engine вынесен в отдельную вкладку
- Запущен пункт E5.1 (requirements freeze для протоколов SingBox):
- добавлен документ
docs/phase-e/E5_SINGBOX_PROTOCOLS_REQUIREMENTS.md; - зафиксированы архитектурные границы (
enginevspolicyvsprotocol 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 globaloverride-переключатели, 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-linkmeta.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 включены (
SingBoxtab: 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:
smartdnsruntime был сnftset-timeout yes, аagvpn_dyn4создан безtimeout-флага (flags interval), из-за чего runtime-добавление IP не происходило; - applied fix:
nftset-timeout noвsmartdns.conf; - verification: после
smartdns-localrestart + DNS query через127.0.0.1#6053все актуальныеA-IPeu.posthog.comпоявились вinet/agvpn/agvpn_dyn4.
- root cause:
- Диагностирован второй корень проблемы для
selective:routes/updateперезаписывалagvpn_dyn4только списком из resolver cache, из-за чего runtime-IP (SmartDNS nftset) периодически терялись;- applied fix в Go (
routes_update.go): mergeresolver wildcard IPs+existing agvpn_dyn4приruntime_nftset=true; - verification: после
smartdnsquery иPOST /api/v1/routes/updatePostHog 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(маркировка срабатывает).
- в wildcard state добавлены
- Финальная проверка: пользователь подтвердил, что
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, чтобы повысить точность матчинга и убрать ложные реконнекты.
- backend теперь резолвит пользовательский выбор в безопасный
- Добавлены проверки для блока локаций:
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отправляет triggerGET /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.
- interface orchestrator (
- 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).