github Leadaxe/singbox-launcher v1.0.0
release v1.0.0

9 hours ago

Release v1.0.0

Downloads

macOS (Universal) - Supports both Apple Silicon and Intel

Option 1: Installation Script (Recommended)

Install with a single command (version v1.0.0):

curl -fsSL https://raw.githubusercontent.com/Leadaxe/singbox-launcher/develop/scripts/install-macos.sh | bash -s -- v1.0.0

The script will:

  • Download the release archive
  • Extract and install to /Applications/
  • Fix macOS quarantine attributes and permissions
  • Launch the application automatically

Option 2: Manual Installation

  1. Download: singbox-launcher-v1.0.0-macos.zip
  2. Extract the ZIP file
  3. Remove quarantine attribute (required):
    xattr -cr "singbox-launcher.app" && chmod +x "singbox-launcher.app/Contents/MacOS/singbox-launcher"
  4. Double-click singbox-launcher.app to run
    • If macOS blocks the app, go to System Settings → Privacy & Security and click "Open Anyway"
    • Alternatively, right-click the app and select "Open" (first time only)

Windows (amd64)

  1. Download: singbox-launcher-v1.0.0-win64.zip
  2. Extract the ZIP file to a folder, for example: C:\Program Files\singbox-launcher\
  3. Run singbox-launcher.exe from that folder
    • You may need administrator rights to install to Program Files
    • The launcher will automatically download sing-box and wintun.dll on first launch

Windows 7 (x86, legacy)

  1. Download: singbox-launcher-v1.0.0-win7-32.zip
  2. Extract the ZIP file to a folder and run singbox-launcher-win7-32.exe
    • For Windows 7 / 32-bit or legacy compatibility only

Linux Support

⚠️ Linux build temporarily unavailable - мы ищем тестировщика для ручного тестирования перед включением автоматической сборки.

Checksums

See checksums.txt for SHA256 checksums of all files.

v1.0.0 — first stable release

After an extended 0.x cycle, this is the first stable release — the launcher graduates out of testing. The configurator, the subscription / preset / outbound / DNS / rules pipeline, the Debug API, and the macOS + Windows (incl. Win7) builds are considered production-ready. Everything below accumulated since v0.9.9.

Первый стабильный релиз. После долгого 0.x-цикла лаунчер выходит из тестирования. Конфигуратор, пайплайн подписок / пресетов / outbound / DNS / правил, Debug API и сборки macOS + Windows (включая Win7) считаются production-ready. Всё ниже накопилось с v0.9.9.

EN

Highlights

  • Reorder subscriptions and servers on the Sources screen. Each row now has up/down (↑/↓) buttons (disabled at list boundaries) to change the order of subscriptions and direct servers. The order lives in model.Sources slice order, which persists to state.connections.sources on Save — so it survives save/load. After a swap the same refresh chain the Delete handler uses runs (re-derive ParserConfig, invalidate preview cache, refresh outbound options, rebuild list) and the Save button lights up.
  • Provider support / web-page link per source row. Each subscription row now shows a clickable support link under the meta subtitle — meta.SupportURL (preferred) or meta.ProfileWebPageURL, data already parsed from the subscription (no backend change). Telegram links (t.me / telegram.* / tg://) get a blue paper-plane icon, everything else a generic themed link icon. Safe schemes (http/https/tg) render as a clickable hyperlink that opens the browser/Telegram; an unsafe-but-present URL shows as plain text (no OpenURL); nothing present → the line is omitted. The link renders as an inline info-panel icon, adding no row height.
  • "Fetch now" for disabled subscriptions in Preview. The subscription Preview now offers a "Fetch now" button even for subscriptions that are toggled off, so you can pull and inspect nodes without first enabling the source.
  • Rule-set (.srs) cleanup — manual + on-state-delete. A new "Clean unused rule-sets" button in the Diagnostics tab removes orphan .srs files (toast reports the count), and deleting a saved state now runs the same cleanup in the background so a .srs only that state referenced (e.g. base.json) is freed. Multi-stage GC semantics are kept — an .srs stays while ANY saved state references it (intentional, so switching states doesn't force a re-download); cleanup is conservative on template-load failure (no delete) so it can't wipe still-referenced preset .srs.
  • Masked password fields for type:"secret" template vars. Any vars[].type:"secret" now renders uniformly in Settings: a masked PasswordEntry (dots + Fyne's built-in show/hide eye toggle) with a Regenerate button. The previous clash_secret-only special-case is gone — secret vars that were silently skipped (e.g. proxy_in_password) now show up. When the row is active and the value is empty/placeholder, a random secret is generated and persisted to state.
  • Windows 7: stale TUN adapters cleaned automatically. On launcher startup (when sing-box is not already running), accumulated singbox-tun* WinTun ghosts from prior sessions are removed — no manual Device Manager cleanup after upgrade. Also runs after each VPN Stop/Restart (SPEC 065).
  • Proxy-in inbound supports username/password authentication. New Settings vars Proxy-in require authentication + Proxy-in username / Proxy-in password. When the toggle is off (default), the mixed inbound stays open (anonymous) as before. When on, users: [{username, password}] is emitted into the inbound — clients connecting to the local proxy must authenticate. The users field is only emitted when auth is enabled AND the username is non-empty, so a half-filled form can't produce a broken users:[{"":""}] state.
  • Template engine: #if construct for conditional field inclusion (SPEC 067). Templates can now declaratively include or omit individual fields based on conditions, without Go post-substitute hooks. Supports an expression language with predicates (#in, #not, #notEmpty, #isEmpty, #notIn, #matches) and a runtime-globals namespace @runtime.* (@runtime.platform / @runtime.arch, mapping to runtime.GOOS / runtime.GOARCH; extensible). Two placement modes: map-spread (key inside object → fields merged on truthy condition) and array-element (single-key wrapper → element replaced or removed). vars[].default_value can also be a #if expression — the default is computed at runtime via @runtime.* only (no user-var refs). See docs/TEMPLATE_REFERENCE.md §9 (semantics) + §10 (stock JSON formatting) + docs/WIZARD_TEMPLATE.md.

Security

  • Code threat-model audit (SPEC 068) — 10 verified findings fixed. A full trust-boundary / STRIDE audit (25+ files, two rounds of adversarial verification) landed fixes for the findings that survived review:
    • BUG1 (critical): loading a legacy v5 state.json now derives the canonical v6 view (deriveV6FromLegacy), so a headless/Debug-API Save no longer drops rules/DNS on upgrade. Covered by load_legacy_save_test.go.
    • BUG2 (high): ConfigStale now stays set when sing-box rejects a rebuilt config — the stale marker is cleared (and ConfigBuilt{OK:true} published) only on a config sing-box actually accepted, so a failed check can no longer be silently treated as valid.
    • BUG6 (medium): events.VpnStateChanged is now published from RunningState.Set (previously subscribed but never emitted).
    • TD6 (medium): Traffic Profiler now evicts connProcessMap/dnsAccum on close and sweeps dnsByIP, fixing the long-session memory growth.
    • Plus BUG3 (Save success-dialog gated on SaveInProgress && Window != nil), BUG7 (if_or no longer mutated by evalIf), TG4 (testable ghostTunDecision for Win7 cleanup), TD7/TD9 (dead code + stale comment removed), and TD1 (docs/ARCHITECTURE.md brought up to date with ui/configurator).
    • Round-2 candidates that were refuted (fetcher nil-guard, logtail Remove-before-Add) and intentional build/resolve resilience were deliberately left unchanged — see SPEC 068 §15/§18.

Fixed

  • DNS scalars persist on Save. resolver / strategy / final DNS scalars are now written through on Save (previously they could be lost).
  • DNS template-server enabled-state restored on reopen. Re-opening the configurator no longer loses the enabled/disabled state of TEMPLATE DNS servers.
  • Outer @-form conditionals evaluate correctly in the UI. Three UI mirrors of core/build.evalIf (preset outbounds, preset rule_set, preset vars) compared if/if_or names without stripping the leading @, so SPEC 067 @-form outer conditionals were evaluated wrong in the UI (the build path was already correct, so emitted config was fine, but UI state diverged). All now strip @ before the varsMap lookup.

Windows

  • NLA + ghost-TUN cleanup hardening (Win7 + Win64). A series of fixes to the network-adapter cleanup path:
    • Force the 64-bit registry view (KEY_WOW64_64KEY) so NLA profile cleanup works under WoW64.
    • Use separate read/write registry handles (plus verbose logs) instead of a single read-write handle.
    • NLA cleanup now runs on all Windows versions (not Win7-only) and prefix-matches singbox-tun*.
    • Ghost-TUN cleanup filter switched from FriendlyName to NetConnectionID; dropped the name-prefix check in aggressive cleanup (relies on WinTun + DN_STARTED); added NLA profile + signature cleanup for singbox-tun.

Migration notes

  • Breaking template format: outer if/if_or arrays require an @-prefix on every var reference. Bare if: ["tun"] is now a loader error — must be if: ["@tun"]. The bundled bin/wizard_template.json is migrated automatically; existing users get the new template re-downloaded on the first launch via SPEC 046 invalidation (no user action needed). Custom-template authors must update their if/if_or arrays to @-form before upgrade. Also: vars[].name value runtime is now reserved (runtime-globals namespace @runtime.*) — rename if used (platform / arch are not reserved).

Technical / Internal

  • SPEC 065 follow-up: aggressive cleanup mode (prefix + Wintun only, no CM_PROB_PHANTOM gate) for the taskkill stop path; startup hook CleanupStaleTunAtStartUtil in main.go.
  • SPEC 067 implementation: core/template/substitute.go (+377 LOC) — #if walker with map-spread + array-element modes, 8-form predicate language (bare bool, equality, #notEmpty/#isEmpty, #in/#notIn, #matches, #not), @runtime.platform/@runtime.arch runtime globals; template_validate.go (+343 / -16) — load-time validation including reserved-name check + strict @-only outer if/if_or; 39+ new unit tests (TestIf_* + TestOuterIf_* + TestVars_Reserved*). Runtime globals are a namespace @runtime.* (reserved var name runtime; platform/arch freed); vars[].default_value supports #if (runtime-only) via VarDefaultValue.ForPlatform + validateDefaultValueIf.
  • SubstituteVarsInJSON signature changed: now takes goos, goarch string params (previously inferred from runtime). Internal callers updated.
  • SPEC 067 Phase 8: preset substitution unified — core/build/preset_expand.go::substituteAny replaced with template.SubstituteVarsInJSON, so #if and the expression language now work in preset bodies too. ExpandPreset / ExpandPresetOutbounds gain goos, goarch; new template.SubstituteVarsInJSONStrict returns UnresolvedVarError to preserve the legacy "skip preset entirely" semantic.
  • SPEC 067 Phase 9: Preset.Rule mapPreset.Rules []map (JSON key "rule""rules"), hard cutover, no backward compat — unblocks multi-rule presets like split-all-traffic (previously silently dropped). Touches 14 preset entries in bin/wizard_template.json + 34 test fixtures. dns_rule (singular) untouched.
  • Template #if consolidation: TUN inbound platform-split collapsed via #if + @runtime.platform; route.rules tun-in + proxy-in collapsed via #if + inbound array — fewer duplicated entries, common fields no longer mirrored across platform variants.
  • Bundled template migrated: 19 elements in if/if_or arrays prefixed with @; inbounds[proxy-in] wraps the optional users field in #if keyed on @proxy_in_auth_enabled (and {"@proxy_in_username": "#notEmpty"}). Diff is purely additive plus the prefix edits; no semantic regressions.
  • Proxy-in auth: new template vars proxy_in_auth_enabled (bool), proxy_in_username (text), proxy_in_password (text); the last two visible only when proxy_in_auth_enabled is on (vars[].if cascade).
  • type:"secret" UI: dropped the clash_secret-only special-case (clashSecretSecretVar removed); a shared masked-password row now handles every secret var (used by the SPEC 067 follow-up so proxy_in_password is no longer silently hidden).
  • Sources: source_tab toggle/delete now calls MarkAsChanged (fixes the Save button not lighting up); ui/icons now has a live importer (was flagged unused by the SPEC 069 audit — now used by the source-row link icons).
  • Shared row scaffolding extracted for the Rules/DNS row builders, with tightHBox used to pack row icons consistently across Rules/DNS/Sources rows (pure-presentation refactor, no behavior change).
  • SPEC 069 — code-cleanup audit. A three-pass audit (module read + staticcheck/deadcode/gofmt + duplication scan, then manual verification) catalogued 304 confirmed findings, dominated by migration debt (SPEC 045/047/053–060/063/064/067/068). Landed as staged cleanup: Stage 1 correctness/safety fixes (SRS cache-key dedup, apiLogFile data race, AutoLoadInProgress leak, truncated-JSON-body handling, config_service nil-deref, source_tab Save trigger), Stage 2 large dead-code cluster removal, Stage 3 api/config dedup + helper unification (evalIf / outbound / label helpers), Stage 4 cosmetic sweep (dead code, tombstone comments, doc drift, gofmt).
  • SPEC 070 — architecture refactor & cleanup. docs/ARCHITECTURE.md rewritten around an explicit 8-layer model with ADRs, diagrams, responsibility zones and a per-package file inventory (docs/DATA_FLOW.md updated alongside). Behavior-preserving work landed in staged commits: event-bus + DI cleanup, dedup, domain / outbound_generator / UI-monolith file splits (pure moves), core lifecycle dedup + ConfigBuilt event wiring, and a gofmt -w of the whole tree. Dual-state elimination was attempted (canonical v6 as sole stored truth, ADR-070-2) and then reverted (a58a176) after a GUI round-trip test surfaced a DNS save regression — it remains deferred and documented for a supervised follow-up.

RU

Основное

  • Переупорядочивание подписок и серверов на экране Sources. У каждой строки появились кнопки ↑/↓ (заблокированы на границах списка) для смены порядка подписок и прямых серверов. Порядок хранится в slice-порядке model.Sources, который сохраняется в state.connections.sources на Save — переживает save/load. После свопа запускается тот же refresh-chain, что у Delete (re-derive ParserConfig, инвалидация preview-кэша, refresh outbound options, rebuild списка), и кнопка Save зажигается.
  • Ссылка на поддержку / веб-страницу провайдера в строке источника. Каждая строка подписки теперь показывает кликабельную support-ссылку под meta-сабтайтлом — meta.SupportURL (предпочтительно) или meta.ProfileWebPageURL, данные уже парсятся из подписки (без изменений в backend). Telegram-ссылки (t.me / telegram.* / tg://) получают синюю иконку бумажного самолётика, остальные — обычную themed link-иконку. Безопасные схемы (http/https/tg) рендерятся как кликабельный hyperlink (открывает браузер/Telegram); unsafe-но-присутствующий URL показан как plain text (без OpenURL); ничего нет → строка опускается. Ссылка — inline-иконка в info-панели, высоту строки не увеличивает.
  • «Fetch now» для выключенных подписок в Preview. В Preview подписки теперь есть кнопка «Fetch now» даже для выключенных подписок — можно подтянуть и посмотреть ноды, не включая источник сначала.
  • Очистка rule-set (.srs) — вручную + при удалении state. Новая кнопка «Clean unused rule-sets» во вкладке Diagnostics удаляет orphan .srs (toast с количеством), а удаление сохранённого state теперь запускает ту же очистку в фоне — .srs, на который ссылался только этот state (например base.json), освобождается. Multi-stage GC сохранён — .srs остаётся пока на него ссылается ХОТЬ ОДИН сохранённый state (намеренно, чтобы переключение states не форсило re-download); при ошибке загрузки шаблона очистка консервативна (без удаления) — не сотрёт still-referenced preset .srs.
  • Маскированные поля паролей для template-vars type:"secret". Любой vars[].type:"secret" теперь рендерится в Settings единообразно: маскированный PasswordEntry (точки + встроенный Fyne show/hide-глазок) с кнопкой Regenerate. Прежний special-case только для clash_secret убран — secret-vars, которые молча скипались (например proxy_in_password), теперь видны. Когда строка активна, а значение пустое/placeholder, генерируется случайный secret и сохраняется в state.
  • Windows 7: старые TUN-адаптеры чистятся сами. При запуске лаунчера (если sing-box ещё не работает) снимаются накопившиеся ghost singbox-tun* с прошлых сессий — после обновления не нужен ручной Device Manager. Плюс очистка после каждого Stop/Restart VPN (SPEC 065).
  • Proxy-in inbound поддерживает аутентификацию по логин/пароль. В Settings появились Proxy-in require authentication + Proxy-in username / Proxy-in password. Выключено по умолчанию — mixed inbound остаётся открытым (anonymous) как раньше. Включено — в inbound эмитится users: [{username, password}], клиенты должны аутентифицироваться. Поле users эмитится только когда auth включён И username непустой — недозаполненная форма не может породить broken-состояние users:[{"":""}].
  • Template engine: control-construct #if для условных полей (SPEC 067). Шаблон теперь декларативно умеет включать/исключать отдельные поля по условию, без Go-хуков. Expression language с предикатами (#in, #not, #notEmpty, #isEmpty, #notIn, #matches) и namespace runtime globals @runtime.* (@runtime.platform / @runtime.arch, соответствуют runtime.GOOS / runtime.GOARCH; расширяемый). Два режима размещения: map-spread (ключ внутри объекта → поля мерджатся в parent при true) и array-element (single-key wrapper → элемент заменяется или удаляется). vars[].default_value тоже может быть #if — дефолт вычисляется в runtime по @runtime.* (без ссылок на другие vars). См. docs/TEMPLATE_REFERENCE.md §9 (семантика) + §10 (оформление stock JSON) + docs/WIZARD_TEMPLATE_RU.md.

Безопасность

  • Аудит модели угроз кода (SPEC 068) — исправлены 10 верифицированных находок. Полный аудит границ доверия / STRIDE (25+ файлов, два раунда adversarial-верификации) закрыл находки, прошедшие review:
    • BUG1 (critical): загрузка legacy v5 state.json теперь выводит канонический v6-view (deriveV6FromLegacy), поэтому headless / Debug-API Save больше не теряет rules/DNS при апгрейде. Покрыто load_legacy_save_test.go.
    • BUG2 (high): ConfigStale остаётся выставленным, когда sing-box отклоняет пересобранный конфиг — marker сбрасывается (и публикуется ConfigBuilt{OK:true}) только на конфиге, который sing-box реально принял; failed-check больше нельзя молча считать валидным.
    • BUG6 (medium): events.VpnStateChanged теперь публикуется из RunningState.Set (раньше был подписан, но никогда не эмитился).
    • TD6 (medium): Traffic Profiler теперь чистит connProcessMap/dnsAccum на close и подметает dnsByIP — фикс роста памяти в долгих сессиях.
    • Плюс BUG3 (success-диалог Save под SaveInProgress && Window != nil), BUG7 (if_or больше не мутируется evalIf), TG4 (тестируемый ghostTunDecision для Win7-cleanup), TD7/TD9 (мёртвый код + stale-комментарий убраны), TD1 (docs/ARCHITECTURE.md приведён в соответствие с ui/configurator).
    • Round-2 кандидаты, которые были опровергнуты (fetcher nil-guard, logtail Remove-before-Add), и намеренная build/resolve-resilience оставлены без изменений — см. SPEC 068 §15/§18.

Исправлено

  • DNS-скаляры сохраняются на Save. DNS-скаляры resolver / strategy / final теперь записываются на Save (раньше могли теряться).
  • Enabled-state DNS template-серверов восстанавливается на reopen. Повторное открытие конфигуратора больше не теряет enabled/disabled-состояние TEMPLATE DNS-серверов.
  • Outer @-form условия корректно оцениваются в UI. Три UI-зеркала core/build.evalIf (preset outbounds, preset rule_set, preset vars) сравнивали имена if/if_or без срезания ведущего @, поэтому SPEC 067 @-form outer-условия оценивались в UI неверно (build-путь уже был корректен, эмиченный config был fine, но UI-state расходился). Все теперь делают TrimPrefix @ до lookup в varsMap.

Windows

  • Усиление очистки NLA + ghost-TUN (Win7 + Win64). Серия фиксов в пути очистки сетевых адаптеров:
    • Принудительный 64-битный registry view (KEY_WOW64_64KEY), чтобы очистка NLA-профиля работала под WoW64.
    • Раздельные read/write registry-хэндлы (плюс verbose-логи) вместо одного read-write хэндла.
    • Очистка NLA теперь идёт на всех версиях Windows (не только Win7) и prefix-матчит singbox-tun*.
    • Фильтр ghost-TUN cleanup переключён с FriendlyName на NetConnectionID; убран name-prefix check в aggressive cleanup (опирается на WinTun + DN_STARTED); добавлена очистка NLA-профиля + signature для singbox-tun.

Migration notes

  • Breaking template format: outer if/if_or массивы требуют @-префикс у каждого var-ref'а. Голое if: ["tun"] теперь loader error — только if: ["@tun"]. Bundled bin/wizard_template.json мигрирован автоматически; существующим пользователям шаблон скачается заново на первом запуске через механизм инвалидации SPEC 046 (без действий юзера). Авторам кастомных шаблонов — обновить if/if_or на @-форму до апгрейда. Также: имя runtime в vars[] теперь зарезервировано (namespace runtime-globals @runtime.*) — переименовать если использовалось (platform / arch больше не зарезервированы).

Техническое / Внутреннее

  • SPEC 065: aggressive cleanup (только префикс + Wintun, без CM_PROB_PHANTOM gate) для taskkill-stop пути; startup-хук CleanupStaleTunAtStartUtil в main.go.
  • SPEC 067 реализация: core/template/substitute.go (+377 LOC) — #if walker с map-spread + array-element режимами, expression language из 8 форм (bare bool, equality, #notEmpty/#isEmpty, #in/#notIn, #matches, #not), runtime globals @runtime.platform/@runtime.arch; template_validate.go (+343 / -16) — load-time валидация включая reserved-name check + strict @-only outer if/if_or; 39+ новых unit-тестов (TestIf_* + TestOuterIf_* + TestVars_Reserved*). Runtime globals — namespace @runtime.* (reserved-имя runtime; platform/arch освобождены); vars[].default_value поддерживает #if (runtime-only) через VarDefaultValue.ForPlatform + validateDefaultValueIf.
  • Signature SubstituteVarsInJSON изменился: теперь принимает goos, goarch string параметры (раньше брал из runtime). Internal callers обновлены.
  • SPEC 067 Phase 8: унификация preset-substitution — core/build/preset_expand.go::substituteAny заменён на template.SubstituteVarsInJSON, теперь #if и expression language работают и в телах presets. ExpandPreset / ExpandPresetOutbounds получили goos, goarch; новый template.SubstituteVarsInJSONStrict возвращает UnresolvedVarError для сохранения legacy-семантики «skip preset entirely».
  • SPEC 067 Phase 9: Preset.Rule mapPreset.Rules []map (JSON-ключ "rule""rules"), hard cutover без backward compat — разблокирует multi-rule presets вроде split-all-traffic (раньше молча дропались). Затрагивает 14 preset-entries в bin/wizard_template.json + 34 test-фикстуры. dns_rule (singular) не тронут.
  • Консолидация template #if: TUN inbound platform-split схлопнут через #if + @runtime.platform; route.rules tun-in + proxy-in схлопнуты через #if + inbound-массив — меньше дублирующихся entries, общие поля больше не зеркалятся между платформенными вариантами.
  • Bundled template мигрирован: 19 элементов в if/if_or массивах получили @-prefix; inbounds[proxy-in] оборачивает опциональное users поле в #if по @proxy_in_auth_enabled{"@proxy_in_username": "#notEmpty"}). Diff чисто аддитивный плюс prefix-правки; semantic regression нет.
  • Proxy-in auth: новые template vars proxy_in_auth_enabled (bool), proxy_in_username (text), proxy_in_password (text); последние два видны только когда proxy_in_auth_enabled включён (vars[].if каскад).
  • type:"secret" UI: убран special-case только для clash_secret (clashSecretSecretVar удалён); общая masked-password строка обрабатывает любой secret-var (использовано в SPEC 067 follow-up, чтобы proxy_in_password больше не был молча скрыт).
  • Sources: source_tab toggle/delete теперь вызывает MarkAsChanged (фикс — кнопка Save не зажигалась); ui/icons теперь имеет живой importer (был помечен unused аудитом SPEC 069 — теперь используется иконками link'ов в строках источников).
  • Извлечён общий row-scaffold для билдеров строк Rules/DNS, с tightHBox для единообразной упаковки иконок строк в Rules/DNS/Sources (чисто presentation-рефактор, без смены поведения).
  • SPEC 069 — аудит уборки кода. Трёхпроходный аудит (module read + staticcheck/deadcode/gofmt + поиск дубликатов, затем ручная верификация) выявил 304 confirmed-находки, доминирует migration-долг (SPEC 045/047/053–060/063/064/067/068). Внедрено поэтапно: Stage 1 — корректность/безопасность (дедуп SRS cache-key, data race apiLogFile, leak AutoLoadInProgress, обработка усечённого JSON-body, nil-deref config_service, Save-триггер source_tab), Stage 2 — снос крупных dead-code кластеров, Stage 3 — дедуп api/config + унификация хелперов (evalIf / outbound / label), Stage 4 — косметический sweep (dead code, tombstone-комментарии, doc drift, gofmt).
  • SPEC 070 — архитектурный рефакторинг и уборка. docs/ARCHITECTURE.md переписан вокруг явной 8-слойной модели с ADR, схемами, зонами ответственности и per-package file-inventory (docs/DATA_FLOW.md обновлён параллельно). Behavior-preserving работа внедрена поэтапными коммитами: чистка event-bus + DI, дедуп, разнесение по файлам domain / outbound_generator / UI-монолитов (чистые move'ы), дедуп core lifecycle + проводка события ConfigBuilt, gofmt -w по всему дереву. Устранение dual-state было предпринято (канонический v6 как единственная хранимая правда, ADR-070-2) и затем откачено (a58a176) после того, как GUI round-trip-тест вскрыл регрессию сохранения DNS — отложено и задокументировано под supervised follow-up.

Don't miss a new singbox-launcher release

NewReleases is sending notifications on new releases.