- build_backend_root ставил defaults/config до создания родительского
каталога usr/lib/tachyon/defaults - релизная сборка падала на install
(обнаружено сборкой тега 1.2.89)
- ensure_po2lmo: частичный клон feeds/luci приводил к компиляции без
template_lmo.c; теперь при отсутствии исходников делается чистый
переклон, а финальный fallback больше не печатает путь к несуществующему
бинарнику - сразу явная ошибка
tachyon/files/usr/bin/tachyon - ucode-скрипт с shebang #!/usr/bin/ucode,
shellcheck умеет проверять только sh/bash/dash/ksh и падает на нём.
Покрытие cgi-bin и hotplug остаётся.
В репозитории лежит package-lock.json и нет yarn.lock, поэтому yarn-workflow
не кэшировал ничего (hashFiles по отсутствующему файлу) и ставил неразрешимые
деревья зависимостей заново каждый раз. Переведено на npm ci с npm-кэшем по
lockfile; проверка diff сборки ограничена выходной директорией LuCI htdocs,
которую build реально производит. Применена prettier-нормализация недавно
правленных исходников.
UTF-8 BOM раньше прятал shebang CGI от скана инвентаря; после удаления BOM
скрипт (корректно) определяется как shell, поэтому ожидаемый список
пополняется tachyon/files/usr/lib/cgi-bin/tachyon-agent.
- workflow shellcheck теперь покрывает агентский CGI, hotplug WAN-монитор и
точки входа usr/bin; в CGI перед shebang стоял UTF-8 BOM, из-за которого
Linux вообще не мог исполнить скрипт (bad interpreter) - удалён вместе со
случайными BOM в исходниках фронтенда
- новый rag_embeddings.sh фиксирует исправленный порядок аргументов push(),
инвалидацию кэша по отпечатку индекса, нормализацию null model_override и
порядок объявлений - ни один из починенных багов RAG не сможет вернуться
незаметно
- hosts_li
run_all.sh гонял набор последовательно с fail-fast: первый упавший тест
отменял всё остальное, а CI (xargs -P4) расходился с локальным порядком.
Теперь поддерживается -P N параллельность как в CI, ведётся лог по каждому
тесту, печатается сводка pass/fail и вывод упавших, принимаются фильтры имён
для запуска подмножества, а ошибки синтакс-проверки по-прежнему прерывают
всё до старта тестов.
- store.set выполнял JSON.stringify с сортировкой ключей над старым и новым
значением каждого обновляемого ключа; ежесекундный ui-state poll дважды в
секунду сериализовал весь payload секций даже без изменений. Дешёвая
проверка по идентичности отсекает частый случай.
- Sets/Maps учёта задач (handled subscription/latency/component/service jobs,
custom proxy latencies) росли всю сессию LuCI без границ; теперь они
ограничены 500 записями через общий хелпер.
- AI-чат модалка: параллельные отправки перемешивали удаление плейсхолдера
"печатает" и портили ветку диалога; поле ввода и кнопка блокируются на
время запроса.
Модалка проверки доступности сервисов была полностью захардкожена русским
и невидима для po-инструментария: англоязычные пользователи получали русские
надписи. Все литералы обёрнуты в _() с английскими msgid, добавлены ru-
переводы и записи pot; пересобран main.js.
Каждые 500мс вся таблица соединений уничтожалась и создавалась заново даже
без изменений - на сотнях соединений это джанки, убивает выделение текста
(ради чего был написан целый обходной механизм защиты выделения) и постоянно
кормит MutationObserver на всём body потоком мутаций.
Строки теперь ключуются по id соединения: структурно неизменённые строки
получают точечную правку ячеек Time/Downloaded/Uploaded, строки со
изменившимся host/route/source/закрытием заменяются по одной, исчезнувшие
соединения удаляют свою строку. tbody с сообщением состояния ("загрузка",
"остановлен") заменяется целиком.
Заодно исправлены предсуществующие eslint-ошибки (пустые catch-блоки,
неиспользуемые переменные) - npm run lint проходит чисто.
- общий хелпер notifyActionFailure: глобальная проверка, просмотр логов и
показ конфига sing-box раньше останавливали спиннер и писали ошибку только
в лог, оставляя пользователя гадать; теперь неудачи показывают тост с
деталью ошибки от бэкенда (добавлены ru-переводы)
- опрос runtime UI state при скрытой вкладке отступает до одного запроса в
30 секунд вместо ежесекундного запуска /usr/bin/tachyon get_ui_state на
роутере круглые сутки
Пять проверок, которых раньше не было, все read-only:
- место на диске: /tmp и overlay при >=90% молча ломают запись кэша,
загрузку подписок и коммиты конфигов
- системные часы: неверный год валит все TLS-соединения (DoH, подписки,
LLM API) после холодной загрузки без NTP
- наличие ca-bundle: HTTPS-проверки падают с SSL-ошибками без пакета;
код уже ссылался на него в вердиктах, но не проверял
- конфликты порта 53: другой DNS-демон на :53 конкурирует с dnsmasq и даёт
плавающие сбои резолвинга
- возраст кэша подписки: sing-box может гонять трафик через списки узлов
многодневной давности, хотя сам URL подписки доступен
Плюс журнал исходов (/tmp/tachyon_doctor_history.json, максимум 50 записей):
повторные сбои за 24 часа помечаются как повторяющиеся, а не как уникальный
инцидент.
Доктор мутировал систему при каждом диагностическом запуске: отключал
rebind protection dnsmasq, делал kill -9 процессу на портах 4534/9090,
стирал syslog во время OOM-аудита и подменял resolv.conf на 1.1.1.1 как
побочный эффект "проверки".
- run_doctor_checks() теперь только диагностирует в dry-run: каждая мутация
идёт через обёртки doc_set/doc_commit/doc_run/doc_unlink, которые
записывают планируемое действие и сообщают "WILL FIX (doctor --fix)";
ремонты выполняются только через "tachyon doctor --fix" или repair=true
- recovery-проверки (сервис остановлен) следуют тому же правилу
- параллельные запуски доктора из LuCI, Telegram и cron сериализуются
mkdir-локом со взятием устаревшего лока через 15 минут тишины
- трекер применённых фиксов пишется атомарно (tmp+mv)
- OOM-проверка больше не стирает logread (это форензика) и атрибутирует
kill именно sing-box
- ai_doctor прогонял весь набор проверок дважды (внутри local_rule_doctor и
напрямую); результаты считаются один раз и переиспользуются
- curl к LLM/Clash/подпискам получили connect + total таймауты, зависший
эндпоинт больше не подвешивает доктора
- агентский CGI: /diagnose мог удалять nft-таблицы, переписывать resolv.conf
и перезапускать sing-box с любого устройства LAN без токена; bearer-токен
теперь передаётся через окружение вместо argv (виден в ps), тело запроса
ограничено 64KB до чтения, wildcard Access-Control-Allow-Origin убран -
произвольные сайты больше не могут читать состояние роутера кросс-доменно
(включая DNS rebinding)
- agent_api: сравнение токена за константное время
- uci/migration: /etc/config/tachyon хранит Telegram bot token и ссылки на
подписки; UCI не сохраняет 0600 при коммитах, поэтому режим ужесточается
после каждого коммита этого конфига и после миграций
- build.yml: убран NODE_TLS_REJECT_UNAUTHORIZED=0 из шагов публикации релиза -
проверка сертификатов не должна отключаться на раннере, заливающем релизы
- initd/state: устранено окно TOCTOU при захвате устаревшего reload-лока:
два ожидающих могли оба выполнить rm+rmdir+mkdir и каждый считал лок своим;
захват теперь атомарным переименованием, которое выигрывает ровно один
претендент
- service_check: lan_interface() запрашивала несуществующий конфиг
forkop/podkop, из-за чего все проверки сервисов смотрели в br-lan вместо
настроенных LAN-интерфейсов - секции AWG/WireGuard всегда получали вердикт
"не отвечает" при рабочем kernel-конфиге (#41, #32, #39)
- rag: у push() были перепутаны аргументы - коллекция эмбеддингов оставалась
пустой для всех провайдеров OpenAI-формата; кэш эмбеддингов теперь
инвалидируется по отпечатку индекса (mtime+size), null model_override
обрабатывается корректно
- warp_generator: curl к Clash API без таймаута и матрица ретраев без общего
дедлайна могли блокировать выполнение минутами и ловить XHR timeout LuCI
(#39); добавлены -m 5 на локальные вызовы и бюджеты 40с/90с на весь цикл
- build.sh: релизные пакеты теперь содержат тот же состав файлов, что и
сборка через OpenWrt Makefile (hotplug WAN-монитор, агентский CGI в
/usr/lib/cgi-bin, defaults/config, профили servicecheck); конфиги с
секретами ставятся с правами 0600; тест паритета инвентаря
- updates UI: после самообновления tachyon страница перезагружается только
когда перезапущенный бэкенд снова отвечает на get_ui_state, вместо
фиксированной задержки 8 секунд (#23)
- Восстановление кэша правил: восстановление .srs, .lst, .json файлов из постоянного каталога /etc/tachyon/rulesets/ при старте; защита от удаления кэша при остановке; добавление фоллбэка пустых ruleset в runtime.uc перед валидацией
- nftables: удаление невалидного auto-merge для интервальных составных сетов (ipv4_addr/ipv6_addr . inet_service)
- core/ip: исправление нормализации IPv6-адреса и порта для TPROXY без дублирования скобок
- dns block: исправление возвращаемого статуса nft_add_dns_block_rules_from_schedules при отсутствии правил блокировки
- watchdog & lifecycle: добавление флага блокировки /var/run/tachyon/starting для исключения сброса правил вочдогом во время старта
- ui & i18n: форматирование бейджей устройств в родительском контроле и обновление переводов
Новые опции config schedule:
- blocked_domains: домены для блокировки (full:/keyword:/regex: префиксы)
- mode: block (список) | allow (whitelist — всё кроме списка)
- dns_level: блокировка на DNS-уровне (NXDOMAIN через dns-block-in)
- notify: уведомление в Telegram при попытке доступа
Механизм:
- sing-box: новый dns-block-in inbound (127.0.0.43:1053) с reject-правилами
для заблокированных доменов; source_ip_cidr для IP-устройств;
invert для whitelist-режима
- nftables: nat chain dns_block — REDIRECT DNS-запросов устройств на
dns-block-in; time-based через meta hour для расписаний; MAC-устройства
матчатся по ether saddr (DHCP-динамические IP поддерживаются)
- route rules reject для always-on расписаний (fallback при обходе DNS)
- telegram worker: проверка nft counters каждые 60s, уведомление о
заблокированной активности
UI (parental.js):
- колонка Blocked Sites в таблице
- textarea с пресетами: соцсети, видео, игры, взрослый контент,
мессенджеры, магазины
- mode / dns_level / notify переключатели
Валидация: домены, mode, dns_level, notify в validator.uc
Тесты: tests/content_blocking.sh (98/98 backend, 522/522 frontend)
Backend: telegram_diagnose returns JSON with 8 checks (token, admin_ids,
sing-box, port, DNS with FakeIP support, direct API, proxy API, send
test message). Registered in CLI dispatcher.
Frontend: Test Connection button in Telegram settings calls
telegram_diagnose and renders results with check/cross icons.
DNS check accepts sing-box FakeIP range (198.18.x.x/198.19.x.x).
Runs 7-step diagnostic: token check, sing-box running, port 4534
listening, DNS resolution, direct IPv4 API, proxy API, send test
message. Shows green/red indicators with actionable advice for each.
After a self-update completes and the page reloads, the state watcher
finds the old action and opens a second modal. Save the job_id to
sessionStorage before reload, and restore it into handledComponentJobs
on page load so the watcher skips it.
The custom renderAdvancedSettings panel was never connected to the UI.
Added enable_rag checkbox to AI tab and warp_proxy_section dropdown
to Advanced tab in the standard LuCI settings.js form.
Also fixed warp_proxy_section load function to use map data instead of
uci.sections() which returns an array (for...in iterated indices).
Added Russian translations for new strings.
The version-based early exit in waitComponentActionJob fired while
the state file still showed running: true and RPC was healthy. This
caused the frontend to declare install complete while the backend
was still restarting services, triggering a page reload before the
install finished.
Now version detection only fires when RPC fails (backend unreachable)
or when state is done with success=false. Normal installs always wait
for the state file to confirm completion.
The graceful degradation for unknown sing-box inbound fields (e.g.
prefer_ip, tolerate_time_skewness) only retried once. When an older
sing-box version doesn't support multiple new fields, the first
unknown field gets stripped and the retry fails on the second one.
Now loops the check-strip-retry cycle until either:
- sing-box check passes
- no more unknown inbound fields are detected
- no stripping occurred (break to avoid infinite loop)
Fixes: 'Generated sing-box configuration is invalid: inbounds[8].
tolerate_time_skewness: json: unknown field' on older sing-box versions.
call_api():
- If warp_proxy_section is set: try proxy FIRST, skip direct entirely
(saves 32s when CF API is only reachable via tunnel)
- If no section: try direct first, then fallback to proxy (as before)
- Error hint: 'Cloudflare API may be blocked on WAN. Try setting
warp_proxy_section in Advanced Settings to route through a tunnel.'
Addresses GitHub issue #39 requirements:
- Section/outbound selection for WARP registration: DONE
- No hardcoded Clash API IP: DONE
- Quick fail on dead WAN IPs: DONE (proxy-first mode)
- Helpful error message: DONE
- Fix response parsing: check report field, not just message
- Pass user query text to LLM instead of ignoring it
- Append user question to diagnostic prompt with system context
- Increase timeout to 120s for LM Studio responses
- Add command_status_from_args to core/common.uc exports
Closes#38
- Исключить повторное открытие окна прогресса для уже завершенных задач (handledComponentJobs)
- Актуализировать состояние фоновых задач в get_ui_state и нормализовать таймаут stale PID
- Исправить определение WAN интерфейса в event_controller во избежание ложного перезапуска (#31)
- Улучшить сверку версий при самообновлении
Makefile was missing the install rule for servicecheck_profiles.json — the community service probe profiles were never copied to /usr/share/tachyon/ on the router, so Service Health Check had zero targets to probe and showed an empty modal.
fix(updates): component_action_worker wrote UPDATES_JOB_LOG to job_id.out.log but component_action_log reads from job_id.log — the two paths never matched so the modal log was always empty. Now derives the log path from the output path by stripping the .out suffix.
The progress modal showed fake step indicators that auto-advanced on a timer, hiding the real operation log behind a small panel. Now the modal is just header + full-size log (350px) + action buttons. Steps, progress bar, and status message removed; updateStep/updateStatus become no-ops.\n\ngenerate-po.js silently dropped every ru.po entry not present in calls.json, wiping ~100 manual translations. It now preserves existing entries. All empty msgstr filled (operation log, port validation, restore).\n\nStyles: removed step/progress bar CSS, enlarged log panel, modal max-width 640px.
The progress modal faked its steps on a timer because the worker only wrote to syslog. Now every updates_log line is appended to <job>.log (UPDATES_JOB_LOG), the running state advertises log_path, the log survives job completion until the TTL sweep, and the new component_action_log CLI mode serves it from a byte offset. The UI polls it into a monospace panel with auto-scroll, exposes a Copy log button, and the modal cannot be closed while the job runs (only the disabled progress button is shown).