- backend: добавлены команды list_update_async и list_update_status с отслеживанием шагов в /var/run/tachyon/list-update.status
- ui: кнопка ручного обновления списков теперь запускает операцию в фоне, показывает модалку с индикатором и опрашивает статус (устранен таймаут LuCI XHR в 30 сек)
- json: sing-box config.json теперь форматируется с отступами в 2 пробела (sprintf '%.2J') для удобства чтения
- tests: обновлены тесты и добавлены проверки для list-update-status (122/122 passed)
- Добавлен разделитель '--' перед модулем в run_spec (/usr/bin/tachyon) и module_args (diagnostics/runtime.uc), чтобы аргументы стратегий не интерпретировались как флаги ucode
- В tests/cli_entrypoint.sh передан безопасный аргумент strategy_dummy
- Зарегистрирована команда validate_byedpi_strategy_json в command_spec CLI (/usr/bin/tachyon)
- Добавлена функция validate_byedpi_strategy_json и режим validate-byedpi-strategy-json в diagnostics/runtime.uc с вызовом providers/byedpi/validator.uc
- Исправлена ошибка 'Unexpected token U' в LuCI, из-за которой отменялось сохранение стратегии ByeDPI в UCI и сбрасывалось на дефолтное значение
- Добавлены тесты CLI-валидации стратегий в tests/byedpi_validator.sh
- When users configure plain IP addresses (e.g. 8.8.8.8, 1.1.1.1, 77.88.8.8) with DoH (https in sing-box), sing-box requires a non-empty HTTP path (defaulting to /dns-query) and proper TLS SNI (server_name) matching the provider certificate.
- Added default /dns-query path fallback in server_from_options().
- Added KNOWN_DNS_SERVER_NAMES mapping for Google, Cloudflare, Yandex, Quad9, AdGuard, Mullvad, Control D, AliDNS, OpenDNS, DNS.SB to provide correct TLS SNI when connecting to raw DNS IPs.
- Added comprehensive DoH/DoT unit tests in tests/dns_failover.sh.
A value like 'cloudflare-dns.com/dns-query' is the sing-box DoH
path format. The previous check only caught 'https://' URLs.
dns_server_value_valid() accepts it because it validates only the
host part — so 'cloudflare-dns.com' passed as a valid hostname.
Fallback servers are plain UDP only; any '/' in the value is the
DoH path separator and must be rejected.
Changes:
- validator.uc: add index(value, '/') >= 0 check before the
dns_server_value_valid call
- config_validator_runtime.sh: add regression test for path-style
DoH ('cloudflare-dns.com/dns-query') in dns_fallback_server
Fallback servers must be plain UDP (IP or IP:port). Before this fix a
user could accidentally enter a DoH URL (https://...) as a fallback
server — the validator passed it through, and it silently failed at
runtime because dns_failover treats all fallback entries as UDP.
Changes:
- validator.uc: after main/bootstrap validation, check all
dns_fallback_server values: reject entries containing '://'
(protocol URL scheme) with a clear error, then validate each
with dns_server_value_valid for format correctness
- config_validator_runtime.sh: assert_rejects for a DoH URL in
dns_fallback_server
Fixes Tachyon startup abort when migrated config has no dns_server or
bootstrap_dns_server (forkop/podkop did not require these fields).
Changes:
- migration.uc: add ensure_dns_server_defaults migration that injects
77.88.8.8 as fallback when either dns list is empty
- default config: pre-mark orphan_section_interface_cleanup and
ensure_dns_server_defaults as applied so fresh installs skip them
- tests/config_migration.sh: regression tests for both missing-DNS cases
and stable named-marker assertion for the new migration
- tests/package_contract.sh: enforce that default config ships both new
migration IDs in applied_migrations
- tests/docker_e2e_test.sh: also install luci-app-tachyon and verify
menu.d + acl.d files are present after install (LuCI menu bug class)
- Добавлена CLI команда tachyon import_settings [файл] (и алиас import-settings) для автоматического сканирования и бесшовного импорта настроек из Forkop, Forkop Plus, Podkop, Netshift или резервных копий
- Поддержка in-place миграции прямо из активного /etc/config/tachyon при обнаружении унаследованных правил config rule
- Расширенное сканирование резервных копий и файлов конфигураций в /etc/config/, /root/ и /tmp/
- Исправлен режим миграции Forkop в Makefile, build.sh и install.sh (использование migrate-podkop для корректного преобразования старых секций config rule в config section)
- Добавлен интеграционный тест tests/import_settings.sh (119 тестов бэкенда пройдены)
- Backend: реализован запуск автоматического обновления компонентов в фоновом режиме (trigger_component_auto_update, component_auto_update_apply)
- Cron: поддержка окна обслуживания (schedule mode) с регистрацией задачи в crontab по заданному времени (HH:MM)
- UCI: новые опции component_auto_update_enabled, component_auto_update_mode, component_auto_update_time, component_auto_update_targets
- Web UI: элементы управления во вкладке настроек «Обновления» (чекбокс включения, переключатель режима, время и мультиселект разрешенных компонентов)
- Тесты: добавлен интеграционный тест component_auto_update.sh, обновлены cron-тесты updates_due.sh, все 118 тестов успешно пройдены в Docker
- Локализация: добавлены русские переводы для всех новых элементов интерфейса
- удалён неподдерживаемый флаг --netfilter-mode=on, вызывавший ошибку старта на OpenWrt
- проба готовности парсит status --json (BackendState), а не exit-code: незалогиненный узел отвечает NeedsLogin с non-zero и раньше считался неготовым — корень «did not become ready» при первом входе
- перед стартом своего tailscaled останавливается и отключается init-скрипт официального пакета: его демон держал UDP 41641, из-за чего наш умирал мгновенно
- при сбое старта в лог пишется хвост tailscaled.log для диагностики
- генератор: при section_failover_enabled catch-all указывает на селектор tachyon-failover со всеми секциями-кандидатами
- выбор по умолчанию переживает рестарты через /etc/tachyon/failover_section, устаревшее имя откатывается на первую секцию
- service/failover.uc: пробер каждой секции через Clash API delay, порог N подряд — переключение на живую, TG-уведомление
- watchdog: вызов проверки в slow-тире (модуль сам отключён при выключенной опции)
- настройки: флаг + порог во вкладке Advanced, RU-локализация
- тесты: генерация вкл/выкл/персист/устаревшее состояние + логика переключения со стабом Clash API
- watchdog: проверка native-секции в slow-тире, авторестарт демона после 2 сбоев, TG-алерт раз в час
- AI Doctor: узел Tailscale (готов/не установлен/упал) только при настроенном native
- провайдер: режим peers — парсинг status --json через per-section socket
- RPC get_tailscale_status/get_tailscale_peers/tailscale_restart
- дашборд: карточка Tailscale (состояние, IP роутера, до 5 пиров с онлайн-статусом)
- новый компонент Tailscale: установка пакета из фида, карточка в Обновлениях
- режим native для секций Tailscale: системный tailscaled вместо sing-box endpoint
- доступ к тейлнету с LAN без клиента: маршруты 100.64.0.0/10 и MagicDNS через dnsmasq
- masquerade и exit-node (opt-in) с обходом tproxy по зарезервированной маске
- validator: требование tailscaled и auth-key для native; bypass-правила в mangle
- тесты: генерация конфига, смоук рантайма с моками, обновлена сигнатура состояния
Багфиксы:
- rag.uc: исправлен порядок аргументов push() — RAG-эмбеддинги снова строятся
- fix_uci_config: правильный путь бэкапа (/etc/backup/tachyon_config + fallback)
- apply_quick_fix: честные exit codes для всех 23 фиксов
- doctor(): прокидывается busy flag в JSON-вывод
- diagnose_json: строится из checks[] вместо парсинга эмодзи из текста
UI:
- Кнопка Telegram канал (https://t.me/tachyon_proxy) на странице диагностики
Тесты:
- 5 новых проверок в ai_doctor_local.sh (uci_backup, exit codes, busy, structured checks, rag push)
В ветке mtproto тип инбаунда перезатирался на shadowsocks-2022,
и ee-секрет уходил в password: SS2022 парсит его как base64-PSK —
отсюда FATAL «decode psk: illegal base64 data at input byte 60»
у всех, у кого включён MTProto-сервер.
- mtproto снова эмитит type=mtproxy (как в схеме extended 2.6.x),
юзер — {name, secret}, а не password (регрессия fe0099a3)
- конфиг с MTProto и AWG проверен `sing-box check` на реальном
sing-box-extended 1.13.18-extended-2.6.5 — PASS
- warp-генератор: прямой префлайт-зонд с --resolve (DNS-отравление
больше не даёт ложное «API недоступен»), живость 4534 — локальная
по /proc/net/tcp вместо сквозного запроса через туннель
- багрепорт маскирует ai_doctor_api_key и tuic_password
- i1–i5/j1–j3: классический hex конвертируется в теги <b 0x…>;
userspace WireGuard extended/lx молча игнорирует голый hex,
handshake не завершался (#39#41#42)
- j1/j2/j3/itime эмитятся только для extended ≤2.6.0: в 2.6.1+
поля удалены из схемы, sing-box падает на unknown fields
- j1–j3 больше не ломаются uci_bin_to_hex
- h1–h4: диапазоны «min-max» на обеих ветках
- MTProto: секрет канонизируется в ee-hex из любой формы
(hex/base64), валидатор даёт понятную ошибку вместо
FATAL decode psk (#23)
- XHTTP v2: парсинг и экспорт ключей подписок
- валидатор: warp/masque/openvpn/snell/mieru/sudoku — только extended
- тесты: stat→ucode (busybox), раннеры исключены из E2E,
новые server_mtproto.sh и awg_variant_schema.sh
Жалобы вида «AWG не работает» (#39, #42) почти всегда означают: конфиг
секции применён корректно, но пир не отвечает (сервер лежит, провайдер
режет UDP до эндпоинта). Раньше доктор об этом молчал, и виноватым
выглядел Tachyon.
Новый чек 6c сканирует свежий лог ядра на ошибки outbound/wireguard
(operation timed out / handshake timeout): при свидетельствах мёртвого
туннеля - явный вердикт «проблема вне Tachyon» с подсказкой проверить
сервер и UDP-порт у провайдера. Если wireguard-трафика в логе нет вовсе,
чек не выдаёт ни одной строки - без шума для пользователей без AWG.
- has_outbound_section: туннельные секции (awg/warp/vpn/openvpn/masque/
anytls/snell/mieru/sudoku) теперь считаются исходящей конфигурацией -
одинокая AWG-секция больше не получает вводящее в заблуждение
«No proxy outbound sections found» (жалобы пользователей #39/#42)
- новая миграция orphan_section_interface_cleanup: удаление секции
маршрутизации раньше оставляло после себя дочерние section_interface,
ссылающиеся на несуществующего родителя (у пользователя - запись на
секцию '2' при живой только '1'); сироты удаляются через новую операцию
delete_section; функция объявлена до массива MIGRATIONS - ucode не
поднимает объявления
- strip_unmarked_tachyon_cron_lines: старые версии писали cron-строки без
маркер-комментария, они переживали фильтрацию по маркерам, копились при
обновлениях и crond запускал component_updates_if_due дважды за тик;
немаркированные строки с нашими командами теперь вычищаются при каждом
refresh и remove
На сетях, где api.cloudflareclient.com недоступен (типичный случай из
issue #39), генератор сжигал весь временной бюджет - фронтенд умирал по
XHR timeout через 30 секунд, не показывая причины.
- префлайт: одна проба (4с connect / 6с max) напрямую и через доступный
транспорт; если Cloudflare недостижим - мгновенная понятная ошибка
«Cloudflare API недоступен...» вместо минутного зависания
- бюджеты ужаты до 20с на call_api и 20с на всю регистрацию, чтобы
худший случай укладывался в XHR-таймаут LuCI
- все ошибочные выходы теперь печатают JSON-конверт и exit(0): фронтенд
парсит data.message и показывает настоящую причину (ненулевой код
заставлял его писать безликое Unknown error)
- новая slow-проверка (раз в 5 мин): sing-box запущен, но mixed-порт 4534
не слушается две проверки подряд (~10 мин) -> один авторестарт ядра;
если не помогло - уведомление админу не чаще раза в час, без циклов
бесконечных рестартов
- проверка живости телеграм-воркера: бот включён, pid жив, но heartbeat
старше 15 минут (зависший long-poll/мёртвый цикл) или pid отсутствует ->
рестарт воркера через telegram_start с ограничением раз в час
- обе проверки добавлены в perform_slow_checks через safe_call
Корень «бот молчит»: get_proxy_args считал достаточным условием маршрутизации
через 4534 сам факт запущенного sing-box - если mixed-inbound не поднялся,
каждый запрос вечно падал, воркер уходил в бесконечный backoff (468 ретраев
подряд у пользователя), хотя api.telegram.org был доступен напрямую.
- mixed_port_alive(): TCP-проба слушается ли 4534 (netstat) с кэшем 30с;
маршрут через прокси выдаётся только при живом порте
- tg_request разделён на tg_request_via + fallback-цепочку: прокси не ответил -
повтор без прокси; отключается UCI telegram.bot_direct_fallback='0'
- алерт админу однократно за эпизод после 5 неудачных опросов подряд с
указанием причины; сбрасывается при восстановлении опроса
- heartbeat /var/run/tachyon_telegram.heartbeat после каждого успешного
цикла - основа для вотчдог-проверки живости (следующий коммит)
- ротация tmpfs-лога воркера: потолок 512KB, хвост 100KB, проверка раз в час
- /doctor в боте рендерит человекочитаемую сводку (проблемы/исправлено/
запланировано) вместо сырого JSON
- config_version убран из человекочитаемого вывода конфига (внутренний
маркер миграций)
UTF-8 BOM раньше прятал shebang CGI от скана инвентаря; после удаления BOM
скрипт (корректно) определяется как shell, поэтому ожидаемый список
пополняется tachyon/files/usr/lib/cgi-bin/tachyon-agent.