From 7f0b085a0719351238517f83c4e9fbe0a47fd16f Mon Sep 17 00:00:00 2001 From: Dushnilin Date: Wed, 26 Aug 2026 11:30:52 +0300 Subject: [PATCH] =?UTF-8?q?feat(tailscale):=20=D0=BD=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D0=B2=D0=BD=D1=8B=D0=B9=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=20tai?= =?UTF-8?q?lscaled=20=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=20Tailscale?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - новый компонент 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 - тесты: генерация конфига, смоук рантайма с моками, обновлена сигнатура состояния --- fe-app-tachyon/locales/tachyon.ru.po | 24 + .../tachyon/helpers/getComponentActionKey.ts | 3 + .../src/tachyon/services/store.service.ts | 5 + .../tachyon/services/systemInfo.service.ts | 2 + .../src/tachyon/services/uiState.service.ts | 3 + .../tachyon/tabs/dashboard/initController.ts | 3 +- .../tabs/diagnostic/diagnostic.store.ts | 6 + .../tachyon/tabs/updates/initController.ts | 31 +- fe-app-tachyon/src/tachyon/types.ts | 5 +- .../resources/view/tachyon/main.js | 49 +- .../resources/view/tachyon/server.js | 50 +- luci-app-tachyon/po/ru/tachyon.po | 24 + tachyon/files/etc/config/tachyon | 7 + tachyon/files/usr/lib/components/action.uc | 34 ++ tachyon/files/usr/lib/components/updates.uc | 2 +- tachyon/files/usr/lib/config/validator.uc | 26 +- tachyon/files/usr/lib/diagnostics/runtime.uc | 5 + tachyon/files/usr/lib/nft/apply.uc | 31 +- .../usr/lib/providers/tailscale/runtime.uc | 526 ++++++++++++++++++ tachyon/files/usr/lib/service/lifecycle.uc | 8 +- tachyon/files/usr/lib/service/state.uc | 3 + .../files/usr/lib/singbox/generator_routes.uc | 3 + tachyon/files/usr/lib/singbox/servers.uc | 17 +- tachyon/files/usr/lib/tachyon/rag_index.json | 2 +- tests/runtime_state_predicates.sh | 6 + tests/tailscale_native_mode.sh | 135 +++++ tests/tailscale_native_runtime.sh | 150 +++++ 27 files changed, 1138 insertions(+), 22 deletions(-) create mode 100644 tachyon/files/usr/lib/providers/tailscale/runtime.uc create mode 100644 tests/tailscale_native_mode.sh create mode 100644 tests/tailscale_native_runtime.sh diff --git a/fe-app-tachyon/locales/tachyon.ru.po b/fe-app-tachyon/locales/tachyon.ru.po index fa135044..f9a9a0d7 100644 --- a/fe-app-tachyon/locales/tachyon.ru.po +++ b/fe-app-tachyon/locales/tachyon.ru.po @@ -3726,6 +3726,30 @@ msgid "Tachyon-managed nfqws2 runtime is ready" msgstr "NFQWS2 Tachyon готов" msgid "Tailscale auth key" +msgid "Tailscale mode" +msgstr "Режим Tailscale" + +msgid "sing-box endpoint (userspace, default)" +msgstr "Endpoint в sing-box (userspace, по умолчанию)" + +msgid "Native tailscaled (tailnet reachable from LAN without client)" +msgstr "Нативный tailscaled (тейлнет доступен из LAN без клиента)" + +msgid "Masquerade LAN traffic into tailnet" +msgstr "Маскарадинг LAN-трафика в тейлнет" + +msgid "SNAT LAN sources when leaving through tailscale0 so peers can answer. Required unless every peer accepts routes." +msgstr "Подменять LAN-адреса при выходе через tailscale0, чтобы пиры могли отвечать. Нужно, если не все пиры принимают маршруты." + +msgid "Use tailnet exit node for LAN" +msgstr "Использовать exit-node тейлнета для LAN" + +msgid "Route all non-local LAN traffic through the tailnet exit node instead of the proxy engines." +msgstr "Направлять весь не-локальный LAN-трафик через exit-node тейлнета вместо прокси-движков." + +msgid "Tailscale" +msgstr "Tailscale" + msgstr "Ключ аутентификации Tailscale" msgid "Tailscale control URL" diff --git a/fe-app-tachyon/src/tachyon/helpers/getComponentActionKey.ts b/fe-app-tachyon/src/tachyon/helpers/getComponentActionKey.ts index ac7f43a8..8fe3b146 100644 --- a/fe-app-tachyon/src/tachyon/helpers/getComponentActionKey.ts +++ b/fe-app-tachyon/src/tachyon/helpers/getComponentActionKey.ts @@ -23,6 +23,9 @@ const componentActionKeyMap: Record = { 'byedpi:check_update': 'byedpiCheck', 'byedpi:install': 'byedpiInstall', 'byedpi:remove': 'byedpiRemove', + 'tailscale:check_update': 'tailscaleCheck', + 'tailscale:install': 'tailscaleInstall', + 'tailscale:remove': 'tailscaleRemove', }; export function getComponentActionKey( diff --git a/fe-app-tachyon/src/tachyon/services/store.service.ts b/fe-app-tachyon/src/tachyon/services/store.service.ts index 4bea18cf..1ec2de0d 100644 --- a/fe-app-tachyon/src/tachyon/services/store.service.ts +++ b/fe-app-tachyon/src/tachyon/services/store.service.ts @@ -217,6 +217,8 @@ export interface StoreType { zapret2_installed: number; byedpi_version: string; byedpi_installed: number; + tailscale_version: string; + tailscale_installed: number; server_inbounds_enabled_count: number; openwrt_version: string; device_model: string; @@ -241,6 +243,9 @@ export interface StoreType { byedpiCheck: { loading: boolean }; byedpiInstall: { loading: boolean }; byedpiRemove: { loading: boolean }; + tailscaleCheck: { loading: boolean }; + tailscaleInstall: { loading: boolean }; + tailscaleRemove: { loading: boolean }; }; updatesChecks: Record< Tachyon.ComponentName, diff --git a/fe-app-tachyon/src/tachyon/services/systemInfo.service.ts b/fe-app-tachyon/src/tachyon/services/systemInfo.service.ts index d339e75a..6f17696c 100644 --- a/fe-app-tachyon/src/tachyon/services/systemInfo.service.ts +++ b/fe-app-tachyon/src/tachyon/services/systemInfo.service.ts @@ -24,6 +24,8 @@ const UNKNOWN_SYSTEM_INFO: StoreType['diagnosticsSystemInfo'] = { zapret2_installed: 0, byedpi_version: _('unknown'), byedpi_installed: 0, + tailscale_version: _('unknown'), + tailscale_installed: 0, server_inbounds_enabled_count: -1, openwrt_version: _('unknown'), device_model: _('unknown'), diff --git a/fe-app-tachyon/src/tachyon/services/uiState.service.ts b/fe-app-tachyon/src/tachyon/services/uiState.service.ts index 48207a7f..2871b8b3 100644 --- a/fe-app-tachyon/src/tachyon/services/uiState.service.ts +++ b/fe-app-tachyon/src/tachyon/services/uiState.service.ts @@ -32,6 +32,9 @@ function getEmptyUpdatesActions(): StoreType['updatesActions'] { byedpiCheck: { loading: false }, byedpiInstall: { loading: false }, byedpiRemove: { loading: false }, + tailscaleCheck: { loading: false }, + tailscaleInstall: { loading: false }, + tailscaleRemove: { loading: false }, }; } diff --git a/fe-app-tachyon/src/tachyon/tabs/dashboard/initController.ts b/fe-app-tachyon/src/tachyon/tabs/dashboard/initController.ts index 87b74881..1272cac8 100644 --- a/fe-app-tachyon/src/tachyon/tabs/dashboard/initController.ts +++ b/fe-app-tachyon/src/tachyon/tabs/dashboard/initController.ts @@ -1651,7 +1651,8 @@ async function renderSectionsWidget() { outbounds: section.outbounds.map((outbound) => ({ ...outbound, latency: - SERVICE_TYPES.has(outbound.type) || !customProxyLatencies.has(outbound.code) + SERVICE_TYPES.has(outbound.type) || + !customProxyLatencies.has(outbound.code) ? outbound.latency : customProxyLatencies.get(outbound.code)!, })), diff --git a/fe-app-tachyon/src/tachyon/tabs/diagnostic/diagnostic.store.ts b/fe-app-tachyon/src/tachyon/tabs/diagnostic/diagnostic.store.ts index 63e2e4f3..5526aa00 100644 --- a/fe-app-tachyon/src/tachyon/tabs/diagnostic/diagnostic.store.ts +++ b/fe-app-tachyon/src/tachyon/tabs/diagnostic/diagnostic.store.ts @@ -94,6 +94,8 @@ export const initialDiagnosticStore: Pick< zapret2_installed: 0, byedpi_version: 'loading', byedpi_installed: 0, + tailscale_version: 'loading', + tailscale_installed: 0, server_inbounds_enabled_count: -1, openwrt_version: 'loading', device_model: 'loading', @@ -158,6 +160,9 @@ export const initialDiagnosticStore: Pick< byedpiCheck: { loading: false }, byedpiInstall: { loading: false }, byedpiRemove: { loading: false }, + tailscaleCheck: { loading: false }, + tailscaleInstall: { loading: false }, + tailscaleRemove: { loading: false }, }, updatesChecks: { tachyon: { status: null, latest_version: '', release_url: '' }, @@ -165,5 +170,6 @@ export const initialDiagnosticStore: Pick< zapret: { status: null, latest_version: '', release_url: '' }, zapret2: { status: null, latest_version: '', release_url: '' }, byedpi: { status: null, latest_version: '', release_url: '' }, + tailscale: { status: null, latest_version: '', release_url: '' }, }, }; diff --git a/fe-app-tachyon/src/tachyon/tabs/updates/initController.ts b/fe-app-tachyon/src/tachyon/tabs/updates/initController.ts index e48ce2e7..b580eaf4 100644 --- a/fe-app-tachyon/src/tachyon/tabs/updates/initController.ts +++ b/fe-app-tachyon/src/tachyon/tabs/updates/initController.ts @@ -62,6 +62,8 @@ function getComponentCardTitle(component: Tachyon.ComponentName): string { return 'Zapret2'; case 'byedpi': return 'ByeDPI'; + case 'tailscale': + return 'Tailscale'; default: return String(component); } @@ -82,6 +84,8 @@ function getComponentCurrentVersion( return sys.zapret2_version; case 'byedpi': return sys.byedpi_version; + case 'tailscale': + return sys.tailscale_version; default: return undefined; } @@ -1003,6 +1007,8 @@ function getComponentInstallKey( return 'zapret2Install'; case 'byedpi': return 'byedpiInstall'; + case 'tailscale': + return 'tailscaleInstall'; } } @@ -1038,7 +1044,7 @@ function getOptionalComponentActions({ installKey, removeKey, }: { - component: 'zapret' | 'zapret2' | 'byedpi'; + component: 'zapret' | 'zapret2' | 'byedpi' | 'tailscale'; installed: boolean; checkKey: UpdatesActionKey; installKey: UpdatesActionKey; @@ -1066,6 +1072,7 @@ const COMPONENT_REPO_URLS: Record = { zapret: 'https://github.com/remittor/zapret-openwrt', zapret2: 'https://github.com/Dushnilin/zapret2-openwrt', byedpi: 'https://github.com/DPITrickster/ByeDPI-OpenWrt', + tailscale: 'https://openwrt.org/packages/pkgdata/tailscale', }; function getComponentCards(): ComponentCard[] { @@ -1076,6 +1083,7 @@ function getComponentCards(): ComponentCard[] { const zapretInstalled = Boolean(systemInfo.zapret_installed); const zapret2Installed = Boolean(systemInfo.zapret2_installed); const byedpiInstalled = Boolean(systemInfo.byedpi_installed); + const tailscaleInstalled = Boolean(systemInfo.tailscale_installed); const singBoxInstalled = !isNotInstalled(systemInfo.sing_box_version); const singBoxStable = singBoxInstalled && @@ -1176,6 +1184,13 @@ function getComponentCards(): ComponentCard[] { installKey: 'byedpiInstall', removeKey: 'byedpiRemove', }); + const tailscaleActions = getOptionalComponentActions({ + component: 'tailscale', + installed: tailscaleInstalled, + checkKey: 'tailscaleCheck', + installKey: 'tailscaleInstall', + removeKey: 'tailscaleRemove', + }); return [ { @@ -1253,6 +1268,20 @@ function getComponentCards(): ComponentCard[] { repoUrl: COMPONENT_REPO_URLS.byedpi, actions: byedpiActions, }, + { + component: 'tailscale', + column: 1, + title: 'Tailscale', + version: systemInfoLoading + ? _('Loading...') + : tailscaleInstalled + ? systemInfo.tailscale_version + : _('Not installed'), + latestVersion: getLatestVersion('tailscale'), + releaseUrl: getGitHubReleaseUrl('tailscale'), + repoUrl: COMPONENT_REPO_URLS.tailscale, + actions: tailscaleActions, + }, ]; } diff --git a/fe-app-tachyon/src/tachyon/types.ts b/fe-app-tachyon/src/tachyon/types.ts index bc9ab1b5..6c973f77 100644 --- a/fe-app-tachyon/src/tachyon/types.ts +++ b/fe-app-tachyon/src/tachyon/types.ts @@ -551,6 +551,8 @@ export namespace Tachyon { zapret2_installed: 0 | 1; byedpi_version: string; byedpi_installed: 0 | 1; + tailscale_version: string; + tailscale_installed: 0 | 1; openwrt_version: string; device_model: string; generated_at?: number; @@ -627,7 +629,8 @@ export namespace Tachyon { | 'sing_box' | 'zapret' | 'zapret2' - | 'byedpi'; + | 'byedpi' + | 'tailscale'; export type ComponentAction = | 'check_update' diff --git a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js index 55c4148e..40109ac4 100644 --- a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js +++ b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js @@ -5253,6 +5253,8 @@ var initialDiagnosticStore = { zapret2_installed: 0, byedpi_version: "loading", byedpi_installed: 0, + tailscale_version: "loading", + tailscale_installed: 0, server_inbounds_enabled_count: -1, openwrt_version: "loading", device_model: "loading" @@ -5316,14 +5318,18 @@ var initialDiagnosticStore = { zapret2Remove: { loading: false }, byedpiCheck: { loading: false }, byedpiInstall: { loading: false }, - byedpiRemove: { loading: false } + byedpiRemove: { loading: false }, + tailscaleCheck: { loading: false }, + tailscaleInstall: { loading: false }, + tailscaleRemove: { loading: false } }, updatesChecks: { tachyon: { status: null, latest_version: "", release_url: "" }, sing_box: { status: null, latest_version: "", release_url: "" }, zapret: { status: null, latest_version: "", release_url: "" }, zapret2: { status: null, latest_version: "", release_url: "" }, - byedpi: { status: null, latest_version: "", release_url: "" } + byedpi: { status: null, latest_version: "", release_url: "" }, + tailscale: { status: null, latest_version: "", release_url: "" } } }; @@ -5680,7 +5686,10 @@ var componentActionKeyMap = { "zapret2:remove": "zapret2Remove", "byedpi:check_update": "byedpiCheck", "byedpi:install": "byedpiInstall", - "byedpi:remove": "byedpiRemove" + "byedpi:remove": "byedpiRemove", + "tailscale:check_update": "tailscaleCheck", + "tailscale:install": "tailscaleInstall", + "tailscale:remove": "tailscaleRemove" }; function getComponentActionKey(component, action) { return componentActionKeyMap[`${component}:${action}`]; @@ -5792,7 +5801,10 @@ function getEmptyUpdatesActions() { zapret2Remove: { loading: false }, byedpiCheck: { loading: false }, byedpiInstall: { loading: false }, - byedpiRemove: { loading: false } + byedpiRemove: { loading: false }, + tailscaleCheck: { loading: false }, + tailscaleInstall: { loading: false }, + tailscaleRemove: { loading: false } }; } function getEmptyDiagnosticsActions() { @@ -9715,6 +9727,8 @@ var UNKNOWN_SYSTEM_INFO = { zapret2_installed: 0, byedpi_version: _("unknown"), byedpi_installed: 0, + tailscale_version: _("unknown"), + tailscale_installed: 0, server_inbounds_enabled_count: -1, openwrt_version: _("unknown"), device_model: _("unknown") @@ -16572,6 +16586,8 @@ function getComponentCardTitle(component) { return "Zapret2"; case "byedpi": return "ByeDPI"; + case "tailscale": + return "Tailscale"; default: return String(component); } @@ -16589,6 +16605,8 @@ function getComponentCurrentVersion(component) { return sys.zapret2_version; case "byedpi": return sys.byedpi_version; + case "tailscale": + return sys.tailscale_version; default: return void 0; } @@ -17257,6 +17275,8 @@ function getComponentInstallKey(component) { return "zapret2Install"; case "byedpi": return "byedpiInstall"; + case "tailscale": + return "tailscaleInstall"; } } function getComponentInstallAction(component) { @@ -17300,7 +17320,8 @@ var COMPONENT_REPO_URLS = { sing_box: "https://github.com/SagerNet/sing-box", zapret: "https://github.com/remittor/zapret-openwrt", zapret2: "https://github.com/Dushnilin/zapret2-openwrt", - byedpi: "https://github.com/DPITrickster/ByeDPI-OpenWrt" + byedpi: "https://github.com/DPITrickster/ByeDPI-OpenWrt", + tailscale: "https://openwrt.org/packages/pkgdata/tailscale" }; function getComponentCards() { const systemInfo = normalizeSingBoxVariantFields( @@ -17310,6 +17331,7 @@ function getComponentCards() { const zapretInstalled = Boolean(systemInfo.zapret_installed); const zapret2Installed = Boolean(systemInfo.zapret2_installed); const byedpiInstalled = Boolean(systemInfo.byedpi_installed); + const tailscaleInstalled = Boolean(systemInfo.tailscale_installed); const singBoxInstalled = !isNotInstalled(systemInfo.sing_box_version); const singBoxStable = singBoxInstalled && !systemInfo.sing_box_extended && !systemInfo.sing_box_tiny; const singBoxExtended = Boolean(systemInfo.sing_box_extended) && !systemInfo.sing_box_compressed && !systemInfo.sing_box_lx; @@ -17398,6 +17420,13 @@ function getComponentCards() { installKey: "byedpiInstall", removeKey: "byedpiRemove" }); + const tailscaleActions = getOptionalComponentActions({ + component: "tailscale", + installed: tailscaleInstalled, + checkKey: "tailscaleCheck", + installKey: "tailscaleInstall", + removeKey: "tailscaleRemove" + }); return [ { component: "tachyon", @@ -17451,6 +17480,16 @@ function getComponentCards() { releaseUrl: getGitHubReleaseUrl("byedpi"), repoUrl: COMPONENT_REPO_URLS.byedpi, actions: byedpiActions + }, + { + component: "tailscale", + column: 1, + title: "Tailscale", + version: systemInfoLoading ? _("Loading...") : tailscaleInstalled ? systemInfo.tailscale_version : _("Not installed"), + latestVersion: getLatestVersion("tailscale"), + releaseUrl: getGitHubReleaseUrl("tailscale"), + repoUrl: COMPONENT_REPO_URLS.tailscale, + actions: tailscaleActions } ]; } diff --git a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/server.js b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/server.js index 56cf73bb..43eadc2c 100644 --- a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/server.js +++ b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/server.js @@ -538,11 +538,11 @@ function populateProtocolValues(option, capabilities) { resetOptionValues(option); - if (normalized.singBoxTailscale) { - Object.entries(TAILSCALE_PROTOCOL_LABELS).forEach(([value, label]) => { - addOptionValue(option, value, label); - }); - } + // Tailscale is offered unconditionally: the native tailscaled mode does not + // depend on the sing-box build, only on the installable Tailscale component. + Object.entries(TAILSCALE_PROTOCOL_LABELS).forEach(([value, label]) => { + addOptionValue(option, value, label); + }); Object.entries(BASE_PROTOCOL_LABELS).forEach(([value, label]) => { addOptionValue(option, value, label); @@ -3277,6 +3277,46 @@ function createServerContent(section, options = {}) { o.rmempty = true; addTailscaleDepends(o); + o = section.option(form.ListValue, "tailscale_mode", _("Tailscale mode")); + o.value( + "singbox", + _("sing-box endpoint (userspace, default)"), + ); + o.value( + "native", + _("Native tailscaled (tailnet reachable from LAN without client)"), + ); + o.default = "singbox"; + o.modalonly = true; + o.rmempty = false; + addTailscaleDepends(o); + + o = section.option( + form.Flag, + "tailscale_masquerade", + _("Masquerade LAN traffic into tailnet"), + _( + "SNAT LAN sources when leaving through tailscale0 so peers can answer. Required unless every peer accepts routes.", + ), + ); + o.default = "1"; + o.modalonly = true; + o.rmempty = false; + o.depends({ protocol: "tailscale", tailscale_mode: "native" }); + + o = section.option( + form.Flag, + "tailscale_use_exit_node", + _("Use tailnet exit node for LAN"), + _( + "Route all non-local LAN traffic through the tailnet exit node instead of the proxy engines.", + ), + ); + o.default = "0"; + o.modalonly = true; + o.rmempty = false; + o.depends({ protocol: "tailscale", tailscale_mode: "native" }); + // AWG Server Options o = section.option( form.Value, diff --git a/luci-app-tachyon/po/ru/tachyon.po b/luci-app-tachyon/po/ru/tachyon.po index fa135044..f9a9a0d7 100644 --- a/luci-app-tachyon/po/ru/tachyon.po +++ b/luci-app-tachyon/po/ru/tachyon.po @@ -3726,6 +3726,30 @@ msgid "Tachyon-managed nfqws2 runtime is ready" msgstr "NFQWS2 Tachyon готов" msgid "Tailscale auth key" +msgid "Tailscale mode" +msgstr "Режим Tailscale" + +msgid "sing-box endpoint (userspace, default)" +msgstr "Endpoint в sing-box (userspace, по умолчанию)" + +msgid "Native tailscaled (tailnet reachable from LAN without client)" +msgstr "Нативный tailscaled (тейлнет доступен из LAN без клиента)" + +msgid "Masquerade LAN traffic into tailnet" +msgstr "Маскарадинг LAN-трафика в тейлнет" + +msgid "SNAT LAN sources when leaving through tailscale0 so peers can answer. Required unless every peer accepts routes." +msgstr "Подменять LAN-адреса при выходе через tailscale0, чтобы пиры могли отвечать. Нужно, если не все пиры принимают маршруты." + +msgid "Use tailnet exit node for LAN" +msgstr "Использовать exit-node тейлнета для LAN" + +msgid "Route all non-local LAN traffic through the tailnet exit node instead of the proxy engines." +msgstr "Направлять весь не-локальный LAN-трафик через exit-node тейлнета вместо прокси-движков." + +msgid "Tailscale" +msgstr "Tailscale" + msgstr "Ключ аутентификации Tailscale" msgid "Tailscale control URL" diff --git a/tachyon/files/etc/config/tachyon b/tachyon/files/etc/config/tachyon index aa611477..822f8ff5 100644 --- a/tachyon/files/etc/config/tachyon +++ b/tachyon/files/etc/config/tachyon @@ -200,11 +200,18 @@ config settings 'settings' # option enabled '1' # option protocol 'tailscale' # option routing_mode 'rules' +# # tailscale_mode: singbox (userspace endpoint inside sing-box, default) +# # or native (system tailscaled: tailnet reachable from LAN without client, +# # requires Tailscale component installed on the Updates tab) +# option tailscale_mode 'singbox' # option tailscale_control_url 'https://controlplane.tailscale.com' # option tailscale_hostname 'tachyon-server-tailscale-example' # option tailscale_auth_key 'tskey-auth-...' # option tailscale_accept_routes '0' # option tailscale_advertise_exit_node '1' +# # Native mode only: +# #option tailscale_masquerade '1' +# #option tailscale_use_exit_node '0' # #list tailscale_advertise_routes '192.168.1.0/24' config telegram 'telegram' diff --git a/tachyon/files/usr/lib/components/action.uc b/tachyon/files/usr/lib/components/action.uc index 07578238..fc037a77 100644 --- a/tachyon/files/usr/lib/components/action.uc +++ b/tachyon/files/usr/lib/components/action.uc @@ -21,6 +21,7 @@ const COMPONENT_LOCK_DIR = getenv("UPDATES_LOCK_DIR") || RUNTIME_STATE_DIR + "/c const TMP_STALE_TTL_MINUTES = getenv("UPDATES_TMP_STALE_TTL_MINUTES") || "30"; const TMP_FILE_STALE_TTL_MINUTES = getenv("UPDATES_TMP_FILE_STALE_TTL_MINUTES") || "10"; const SB_MANAGED_SERVICE_MARKER = getenv("SB_MANAGED_SERVICE_MARKER") || constants.SB_MANAGED_SERVICE_MARKER || "Tachyon managed sing-box service for binary variants"; +const TAILSCALE_PACKAGE_URL = getenv("TAILSCALE_PACKAGE_URL") || "https://openwrt.org/packages/pkgdata/tailscale"; let as_string = common.as_string; let shell_quote = common.shell_quote; @@ -1302,6 +1303,35 @@ function install_byedpi(action) { action_success("byedpi", action, "ByeDPI package has been installed", current_version, pkg.version, 1, "latest", release.release_url || ""); } +function install_tailscale(action) { + let component = "tailscale"; + let label = "Tailscale"; + let runtime_module = LIB_DIR + "/providers/tailscale/runtime.uc"; + init_tmp_dir() || action_fail(component, action, "Failed to create temporary directory"); + + if (action == "check_update") { + if (!pkg_is_installed("tailscale")) { + run_logged("Refreshing package index", pkg_list_update_command()); + let available_version = available_package_version("tailscale"); + action_success(component, action, label + " is not installed", "", available_version, 0, "", TAILSCALE_PACKAGE_URL); + } + run_logged("Refreshing package index", pkg_list_update_command()); + check_success(component, installed_package_version("tailscale"), available_package_version("tailscale"), TAILSCALE_PACKAGE_URL); + } + + if (!run_logged("Refreshing package index", pkg_list_update_command())) + updates_log("Package index refresh failed; trying to install from the cached index", "warn"); + if (!run_logged("Installing " + label + " package", pkg_install_name_command("tailscale"))) + action_fail(component, "install", "Failed to install " + label + " package from the feed"); + disable_standalone_service("tailscale"); + restart_tachyon_after_successful_change(); + clear_version_caches(); + let current_version = provider_package_version(runtime_module); + if (current_version == "") + current_version = "unknown"; + action_success(component, "install", label + " package has been installed", current_version, current_version, 1, "latest", TAILSCALE_PACKAGE_URL); +} + function remove_optional_component(component, package_name, label, runtime_module) { if (!pkg_is_installed(package_name)) { if (provider_installed(runtime_module)) @@ -2507,6 +2537,10 @@ function component_action(component, action) { install_byedpi(action); else if (component == "byedpi" && action == "remove") remove_optional_component("byedpi", "byedpi", "ByeDPI", LIB_DIR + "/providers/byedpi/runtime.uc"); + else if (component == "tailscale" && (action == "check_update" || action == "install")) + install_tailscale(action); + else if (component == "tailscale" && action == "remove") + remove_optional_component("tailscale", "tailscale", "Tailscale", LIB_DIR + "/providers/tailscale/runtime.uc"); else action_fail(component != "" ? component : "unknown", action != "" ? action : "unknown", "Unknown component action"); } diff --git a/tachyon/files/usr/lib/components/updates.uc b/tachyon/files/usr/lib/components/updates.uc index 279a8478..271fe286 100644 --- a/tachyon/files/usr/lib/components/updates.uc +++ b/tachyon/files/usr/lib/components/updates.uc @@ -1370,7 +1370,7 @@ function normalize_component_name(component) { function valid_component_name(component) { component = normalize_component_name(component); return component == "tachyon" || component == "sing_box" || component == "zapret" || - component == "zapret2" || component == "byedpi"; + component == "zapret2" || component == "byedpi" || component == "tailscale"; } function component_update_check_cache_path(component) { diff --git a/tachyon/files/usr/lib/config/validator.uc b/tachyon/files/usr/lib/config/validator.uc index 60d27589..c14906b4 100644 --- a/tachyon/files/usr/lib/config/validator.uc +++ b/tachyon/files/usr/lib/config/validator.uc @@ -1772,6 +1772,25 @@ function validate_runtime_config(context) { let schedules = sections_by_type("schedule"); validate_runtime_mark_ranges_context(context); + + // Native-mode Tailscale servers need the system tailscaled binary; the + // check lives here so every validation entry point (including fixtures) + // enforces it. + for (let ts_server in sections_by_type("server")) { + if (!server_enabled(ts_server)) + continue; + if (option(ts_server, "protocol", "vless") != "tailscale") + continue; + if (option(ts_server, "tailscale_mode", "singbox") != "native") + continue; + // Without a key `tailscale up` falls back to interactive login and + // would block the runtime forever on a headless router. + if (trim(as_string(option(ts_server, "tailscale_auth_key", "") || "")) == "") + fail_requirement("Server '" + section_name(ts_server) + "' uses native Tailscale mode without an auth key. Generate one in the Tailscale admin console and fill in 'Tailscale auth key', or switch this server back to sing-box mode. Aborted.", "fatal"); + if (!command_exists("tailscaled")) + fail_requirement("Server '" + section_name(ts_server) + "' uses native Tailscale mode, but tailscaled is not installed. Install the Tailscale component on the Updates tab, or switch this server back to sing-box mode. Aborted.", "fatal"); + } + validate_dns_settings(settings, sections, context); validate_list_update_settings(settings); validate_http_url_option(option(settings, "latency_test_url", DEFAULT_LATENCY_TEST_URL) || DEFAULT_LATENCY_TEST_URL, "settings.latency_test_url"); @@ -2029,8 +2048,11 @@ function validate_extended_server_features(ctx, sing_box_version, sing_box_versi if (protocol == "mtproto") fail_requirement("Server '" + name + "' uses MTProto proxy, but sing-box-extended is not installed. Install sing-box-extended or disable this server. Aborted.", "fatal"); - if (protocol == "tailscale" && !sing_box_supports_tailscale(ctx, sing_box_version, sing_box_version_output)) - fail_requirement("Server '" + name + "' uses Tailscale, but the installed sing-box binary was built without Tailscale support. Install full sing-box or sing-box-extended, or disable this server. Aborted.", "fatal"); + // Native-mode Tailscale runs tailscaled outside sing-box and does + // not need the with_tailscale build. + if (protocol == "tailscale" && option(section, "tailscale_mode", "singbox") != "native" && + !sing_box_supports_tailscale(ctx, sing_box_version, sing_box_version_output)) + fail_requirement("Server '" + name + "' uses Tailscale, but the installed sing-box binary was built without Tailscale support. Install full sing-box or sing-box-extended, switch this server to native Tailscale mode, or disable this server. Aborted.", "fatal"); if (transport == "xhttp" && !sing_box_supports_xhttp(ctx, sing_box_version, sing_box_version_output)) fail_requirement("Server '" + name + "' uses XHTTP transport, but the installed sing-box binary does not support it. Install sing-box-extended or a binary with XHTTP support, or change the transport. Aborted.", "fatal"); diff --git a/tachyon/files/usr/lib/diagnostics/runtime.uc b/tachyon/files/usr/lib/diagnostics/runtime.uc index 5b174f14..b11f7a66 100644 --- a/tachyon/files/usr/lib/diagnostics/runtime.uc +++ b/tachyon/files/usr/lib/diagnostics/runtime.uc @@ -57,6 +57,7 @@ const SINGBOX_RUNTIME_UC = LIB_DIR + "/singbox/runtime.uc"; const ZAPRET_RUNTIME_UC = LIB_DIR + "/providers/zapret/runtime.uc"; const ZAPRET2_RUNTIME_UC = LIB_DIR + "/providers/zapret2/runtime.uc"; const BYEDPI_RUNTIME_UC = LIB_DIR + "/providers/byedpi/runtime.uc"; +const TAILSCALE_RUNTIME_UC = LIB_DIR + "/providers/tailscale/runtime.uc"; const ZAPRET_VALIDATOR_UC = LIB_DIR + "/providers/zapret/validator.uc"; const ZAPRET2_VALIDATOR_UC = LIB_DIR + "/providers/zapret2/validator.uc"; @@ -1146,6 +1147,8 @@ function build_system_info() { let zapret2_version = zapret2_installed ? provider_version(ZAPRET2_RUNTIME_UC) : "not installed"; let byedpi_installed = provider_installed(BYEDPI_RUNTIME_UC) ? 1 : 0; let byedpi_version = byedpi_installed ? provider_version(BYEDPI_RUNTIME_UC) : "not installed"; + let tailscale_installed = provider_installed(TAILSCALE_RUNTIME_UC) ? 1 : 0; + let tailscale_version = tailscale_installed ? provider_version(TAILSCALE_RUNTIME_UC) : "not installed"; let device_model = first_line_value("/tmp/sysinfo/model", "unknown"); return { @@ -1166,6 +1169,8 @@ function build_system_info() { zapret2_installed, byedpi_version, byedpi_installed, + tailscale_version, + tailscale_installed, openwrt_version: openwrt_release(), device_model, generated_at: int(clock()[0]) diff --git a/tachyon/files/usr/lib/nft/apply.uc b/tachyon/files/usr/lib/nft/apply.uc index 66626fdf..4e448787 100644 --- a/tachyon/files/usr/lib/nft/apply.uc +++ b/tachyon/files/usr/lib/nft/apply.uc @@ -41,6 +41,20 @@ function uci_settings() { return uci_section("settings"); } +// True when at least one enabled server section runs Tailscale in native +// (tailscaled) mode; those need tailnet bypass rules in the mangle chain. +function native_tailscale_enabled() { + for (let section in uci_sections("server")) { + if (as_string(section["enabled"]) == "0" || as_string(section["enabled"]) == "false") + continue; + if (as_string(section["protocol"] || "") != "tailscale") + continue; + if (as_string(section["tailscale_mode"] || "singbox") == "native") + return true; + } + return false; +} + function section_by_name(sections, section_name) { section_name = as_string(section_name); for (let section in sections) @@ -1094,8 +1108,21 @@ function nft_create_runtime_base(table, localv4_set, common_set, port_set, ip_po return false; if (!nft_add_rule(table, "mangle", [ "ct", "status", "dnat", "return" ]) || - !nft_add_rule(table, "mangle", [ "jump", "parental_control" ]) || - !nft_add_rule(table, "mangle", [ "iifname", "@" + as_string(interface_set), "ip", "daddr", "@" + as_string(localv4_set), "return" ]) || + !nft_add_rule(table, "mangle", [ "jump", "parental_control" ])) + return false; + + // Native Tailscale: tailnet-bound traffic and anything already marked by + // the Tailscale runtime (mask 0x00ff0000, see config validator) must not + // be captured into tproxy — it is routed to tailscale0 instead. + if (native_tailscale_enabled()) { + if (!nft_add_rule(table, "mangle", [ "meta", "mark", "&", "0x00ff0000", "!=", "0", "return" ]) || + !nft_add_rule(table, "mangle", [ "ip", "daddr", "100.64.0.0/10", "return" ]) || + !nft_add_rule(table, "mangle", [ "ip6", "daddr", "fd7a:115c:a1e0::/48", "return" ])) + return false; + log_debug("Native Tailscale bypass rules added to mangle chain"); + } + + if (!nft_add_rule(table, "mangle", [ "iifname", "@" + as_string(interface_set), "ip", "daddr", "@" + as_string(localv4_set), "return" ]) || !nft_add_rule(table, "mangle", [ "iifname", "@" + as_string(interface_set), "ip6", "daddr", "@" + as_string(localv6_set), "return" ])) return false; diff --git a/tachyon/files/usr/lib/providers/tailscale/runtime.uc b/tachyon/files/usr/lib/providers/tailscale/runtime.uc new file mode 100644 index 00000000..0bc84fbc --- /dev/null +++ b/tachyon/files/usr/lib/providers/tailscale/runtime.uc @@ -0,0 +1,526 @@ +#!/usr/bin/env ucode + +let fs = require("fs"); +let common = require("core.common"); +let uci_core = require("core.uci"); + +// NOTE: ucode does not hoist function declarations - a callee must be +// textually defined above its first caller. Keep that order intact. + +let as_string = common.as_string; +let bool_value = common.bool_value; +let write_json = common.write_json; +let shell_quote = common.shell_quote; +let write_file = common.write_file; +let command_from_args = common.command_from_args; +let command_output = common.command_output; +let command_output_from_args = common.command_output_from_args; +let command_status = common.command_status; +let command_success_from_args = common.command_success_from_args; +let object_or_empty = common.object_or_empty; +let option = common.option; +let parent_dir = common.parent_dir; + +const CONFIG_NAME = getenv("TACHYON_CONFIG_NAME") || "tachyon"; +const TAILSCALE_BIN = getenv("TAILSCALE_BIN") || "/usr/sbin/tailscale"; +const TAILSCALED_BIN = getenv("TAILSCALED_BIN") || "/usr/sbin/tailscaled"; +const PERSISTENT_STATE_BASE = getenv("TAILSCALE_STATE_BASE") || "/etc/tachyon/tailscale"; +const RUNTIME_DIR = getenv("TAILSCALE_RUNTIME_DIR") || "/var/run/tachyon/tailscale"; +const TAILSCALED_PORT = getenv("TAILSCALED_PORT") || "41641"; +const DEFAULT_CONTROL_URL = "https://controlplane.tailscale.com"; +const MAGICDNS_DNSMASQ_FILE = getenv("TAILSCALE_DNSMASQ_FILE") || "/tmp/dnsmasq.d/tachyon-tailscale.conf"; +const MAGICDNS_ADDRESS = getenv("TAILSCALE_MAGICDNS_ADDRESS") || "100.100.100.100"; + +// Marks live inside the mask reserved by the config validator (0x00ff0000): +// no other Tachyon engine may allocate bits there. +const TS_MARK_MASK = getenv("TAILSCALE_MARK_MASK") || "0x00ff0000"; +const TS_MARK_EXIT_NODE = getenv("TAILSCALE_MARK_EXIT") || "0x00200000"; +const TS_ROUTE_TABLE = getenv("TAILSCALE_ROUTE_TABLE") || "4247"; +const TS_EXIT_TABLE = getenv("TAILSCALE_EXIT_TABLE") || "4248"; +const TS_RULE_PRIORITY = getenv("TAILSCALE_RULE_PRIORITY") || "100"; +const TS_EXIT_RULE_PRIORITY = getenv("TAILSCALE_EXIT_RULE_PRIORITY") || "90"; + +// Tailnet CGNAT ranges every peer address lives in. +const TAILNET_V4 = "100.64.0.0/10"; +const TAILNET_V6 = "fd7a:115c:a1e0::/48"; +// Destinations that must never be pushed through an exit node tunnel. +const EXIT_NODE_EXCLUDES_V4 = [ + "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16", + "100.64.0.0/10", "127.0.0.0/8", "224.0.0.0/4", "240.0.0.0/4" +]; +const EXIT_NODE_EXCLUDES_V6 = [ "fc00::/7", "fe80::/10", "ff00::/8" ]; + +const NFT_TS_TABLE = "tachyon_tailscale"; + +function log_message(message, level) { + level = as_string(level || "info"); + command_success_from_args([ "logger", "-t", "tachyon", "[tailscale] [" + level + "] " + as_string(message) ]); +} + +function bool_option(section, key, fallback) { + let value = object_or_empty(section)[key]; + return value == null ? !!fallback : bool_value(value); +} + +function list_option(section, key) { + let value = object_or_empty(section)[key]; + if (value == null) + return []; + if (type(value) != "array") + value = [ value ]; + let result = []; + for (let item in value) { + item = trim(as_string(item)); + if (item != "") + push(result, item); + } + return result; +} + +function section_name(section) { + return as_string(object_or_empty(section)[".name"]); +} + +function uci_sections(type_name) { + return uci_core.section_objects(CONFIG_NAME, as_string(type_name)); +} + +function is_native_mode(section) { + return as_string(option(section, "tailscale_mode", "") || "") == "native"; +} + +function native_tailscale_sections() { + let result = []; + for (let section in uci_sections("server")) { + if (!bool_option(section, "enabled", true)) + continue; + if (as_string(option(section, "protocol", "")) != "tailscale") + continue; + if (!is_native_mode(section)) + continue; + push(result, section); + } + return result; +} + +function safe_filename(name) { + name = as_string(name); + name = replace(name, /[^a-zA-Z0-9_-]/g, "_"); + return name == "" ? "section" : name; +} + +function section_state_dir(section) { + return PERSISTENT_STATE_BASE + "/" + safe_filename(section_name(section)) + "/native"; +} + +function section_runtime_dir(section) { + return RUNTIME_DIR + "/" + safe_filename(section_name(section)); +} + +function section_pid_file(section) { + return section_runtime_dir(section) + "/tailscaled.pid"; +} + +function command_exists(name) { + return command_success_from_args([ "command", "-v", name ]); +} + +function binary_available() { + return command_exists("tailscaled") && command_exists("tailscale"); +} + +function read_pid(path) { + let raw = trim(as_string(fs.readfile(path)) || ""); + if (raw == "" || int(raw) <= 0) + return 0; + return int(raw); +} + +function pid_alive(pid) { + if (pid <= 0) + return false; + return command_success_from_args([ "kill", "-0", as_string(pid) ]); +} + +function running_pid(section) { + return pid_alive(read_pid(section_pid_file(section))); +} + +function package_installed() { + if (command_exists("apk") && command_success_from_args([ "apk", "info", "-e", "tailscale" ])) + return true; + if (!command_exists("opkg")) + return false; + for (let line in split(command_output_from_args([ "opkg", "list-installed" ]), "\n")) { + if (match(trim(as_string(line)), /^tailscale[ \t]+-/)) + return true; + } + return false; +} + +function daemon_version() { + if (!binary_available()) + return ""; + let out = trim(command_output_from_args([ "tailscale", "version" ])); + if (out == "") + return ""; + return split(out, "\n")[0]; +} + +function ensure_dirs(section) { + let state_dir = section_state_dir(section); + let runtime_dir = section_runtime_dir(section); + if (!command_success_from_args([ "mkdir", "-p", state_dir, runtime_dir ])) + return false; + // The daemon stores node identity here; it must survive reboots and resets. + fs.chmod(state_dir, 448); + return true; +} + +function run_step(description, args) { + let status = command_status(command_from_args(args)); + if (status != 0) { + log_message(description + " failed (exit " + as_string(status) + ")", "warn"); + return false; + } + log_message(description + ": ok", "debug"); + return true; +} + +function tailscale_client_args(section) { + let runtime_dir = section_runtime_dir(section); + return [ TAILSCALE_BIN, "--socket", runtime_dir + "/tailscaled.sock" ]; +} + +function remove_magicdns_dns() { + fs.unlink(MAGICDNS_DNSMASQ_FILE); + command_status(command_from_args([ "/etc/init.d/dnsmasq", "restart" ]) + " >/dev/null 2>&1"); +} + +function remove_kernel_routing(keep_dns) { + command_status(command_from_args([ "ip", "rule", "del", "to", TAILNET_V4, + "priority", TS_RULE_PRIORITY, "table", TS_ROUTE_TABLE ]) + " 2>/dev/null"); + command_status(command_from_args([ "ip", "-6", "rule", "del", "to", TAILNET_V6, + "priority", TS_RULE_PRIORITY, "table", TS_ROUTE_TABLE ]) + " 2>/dev/null"); + command_status(command_from_args([ "ip", "route", "flush", "table", TS_ROUTE_TABLE ]) + " 2>/dev/null"); + command_status(command_from_args([ "ip", "-6", "route", "flush", "table", TS_ROUTE_TABLE ]) + " 2>/dev/null"); + command_status(command_from_args([ "ip", "rule", "del", "fwmark", TS_MARK_EXIT_NODE + "/" + TS_MARK_MASK, + "priority", TS_EXIT_RULE_PRIORITY, "table", TS_EXIT_TABLE ]) + " 2>/dev/null"); + command_status(command_from_args([ "ip", "route", "flush", "table", TS_EXIT_TABLE ]) + " 2>/dev/null"); + command_status(command_from_args([ "nft", "delete", "table", "inet", NFT_TS_TABLE ]) + " 2>/dev/null"); + if (!keep_dns) + remove_magicdns_dns(); +} + +function install_exit_node_routing() { + // Opt-in: forward all non-local LAN traffic through the tailnet exit node. + // Marked packets are skipped by Tachyon's mangle chain (mask-based return), + // so they never enter tproxy, then follow the dedicated routing table. + command_status(command_from_args([ "ip", "rule", "del", "fwmark", TS_MARK_EXIT_NODE + "/" + TS_MARK_MASK, + "priority", TS_EXIT_RULE_PRIORITY, "table", TS_EXIT_TABLE ]) + " 2>/dev/null"); + command_status(command_from_args([ "ip", "route", "flush", "table", TS_EXIT_TABLE ]) + " 2>/dev/null"); + + let nft_lines = [ + "table inet " + NFT_TS_TABLE + " {", + " set exit_exclude_v4 { type ipv4_addr; flags interval; elements = { " + join(", ", EXIT_NODE_EXCLUDES_V4) + " } }", + " set exit_exclude_v6 { type ipv6_addr; flags interval; elements = { " + join(", ", EXIT_NODE_EXCLUDES_V6) + " } }", + " chain prerouting_mark {", + " type filter hook prerouting priority -150; policy accept;", + " meta mark & " + TS_MARK_MASK + " != 0 return", + " ip daddr @exit_exclude_v4 return", + " ip6 daddr @exit_exclude_v6 return", + " meta mark set " + TS_MARK_EXIT_NODE + " comment \"tachyon-tailscale exit-node\"", + " }", + "}" + ]; + if (command_status("nft -f - <<'EOF'\n" + join("\n", nft_lines) + "\nEOF\n") != 0) { + log_message("Failed to install exit-node marking rules", "warn"); + return false; + } + let ok = + run_step("Adding exit-node route rule", [ "ip", "rule", "add", "fwmark", TS_MARK_EXIT_NODE + "/" + TS_MARK_MASK, + "priority", TS_EXIT_RULE_PRIORITY, "table", TS_EXIT_TABLE ]) && + run_step("Adding exit-node default route", [ "ip", "route", "add", "default", "dev", "tailscale0", "table", TS_EXIT_TABLE ]); + if (!ok) + log_message("Exit-node routing setup failed", "warn"); + return ok; +} + +function configure_firewall(section) { + // Masquerade LAN sources leaving through the tunnel unless the user opted + // out; without SNAT peers cannot answer addresses they don't know. + let masquerade = bool_option(section, "tailscale_masquerade", true); + let nft_lines = [ + "table inet " + NFT_TS_TABLE + " {", + " chain postrouting_snat {", + " type nat hook postrouting priority 110; policy accept;", + " oifname \"tailscale0\" ip saddr " + TAILNET_V4 + " return", + " oifname \"tailscale0\" ip6 saddr " + TAILNET_V6 + " return" + ]; + if (masquerade) + push(nft_lines, " oifname \"tailscale0\" counter masquerade"); + push(nft_lines, " }"); + push(nft_lines, "}"); + if (command_status("nft -f - <<'EOF'\n" + join("\n", nft_lines) + "\nEOF\n") != 0) { + log_message("Failed to install tailscale firewall rules", "warn"); + return false; + } + return true; +} + +function configure_magicdns_dns() { + // Hand *.ts.net to the tailnet resolver without letting tailscaled touch + // resolv.conf; dnsmasq picks the file up from its conf-dir. + if (!command_success_from_args([ "mkdir", "-p", parent_dir(MAGICDNS_DNSMASQ_FILE) ])) + return false; + let lines = [ + "# Managed by Tachyon native Tailscale runtime", + "server=/ts.net/" + MAGICDNS_ADDRESS + ]; + if (!write_file(MAGICDNS_DNSMASQ_FILE, join("\n", lines) + "\n")) { + log_message("Failed to write MagicDNS dnsmasq config", "warn"); + return false; + } + command_status(command_from_args([ "/etc/init.d/dnsmasq", "restart" ]) + " >/dev/null 2>&1"); + return true; +} + +function install_kernel_routing(section) { + // Steer tailnet-bound traffic into the tunnel before Tachyon's policy + // routing (fwmark rule at priority 105) can send it into tproxy. + // Start from a clean slate so restarts never stack duplicate rules. + remove_kernel_routing(true); + + let installed = + run_step("Adding IPv4 tailnet route", [ "ip", "route", "add", TAILNET_V4, "dev", "tailscale0", "table", TS_ROUTE_TABLE ]) && + run_step("Adding IPv6 tailnet route", [ "ip", "-6", "route", "add", TAILNET_V6, "dev", "tailscale0", "table", TS_ROUTE_TABLE ]) && + run_step("Adding IPv4 route rule", [ "ip", "rule", "add", "to", TAILNET_V4, "priority", TS_RULE_PRIORITY, "table", TS_ROUTE_TABLE ]) && + run_step("Adding IPv6 route rule", [ "ip", "-6", "rule", "add", "to", TAILNET_V6, "priority", TS_RULE_PRIORITY, "table", TS_ROUTE_TABLE ]); + if (!installed) { + log_message("Kernel routing setup failed for " + section_name(section), "warn"); + return false; + } + + if (bool_option(section, "tailscale_use_exit_node", false)) + install_exit_node_routing(); + configure_firewall(section); + configure_magicdns_dns(); + return true; +} + +function start_daemon(section) { + if (running_pid(section)) { + log_message("tailscaled for " + section_name(section) + " already running", "debug"); + return true; + } + + let runtime_dir = section_runtime_dir(section); + let socket_path = runtime_dir + "/tailscaled.sock"; + let log_file = runtime_dir + "/tailscaled.log"; + let state_dir = section_state_dir(section); + + let args = [ + TAILSCALED_BIN, + "--tun=tailscale0", + "--statedir=" + state_dir, + "--socket=" + socket_path, + "--port=" + TAILSCALED_PORT, + "--netfilter-mode=on" + ]; + + let cmdline = command_from_args(args) + + " >" + shell_quote(log_file) + " 2>&1 0) + push(args, "--advertise-routes=" + join(",", advertise_routes)); + else + push(args, "--advertise-routes="); + if (bool_option(section, "tailscale_advertise_exit_node", false)) + push(args, "--advertise-exit-node"); + else + push(args, "--advertise-exit-node=false"); + // DNS stays under dnsmasq/Tachyon control; MagicDNS names are resolved + // through the dedicated dnsmasq forward instead of resolv.conf takeover. + push(args, "--accept-dns=false"); + // Never block the runtime on interactive prompts or dead control planes. + push(args, "--timeout=120s"); + push(args, "--reset"); + + if (!run_step("tailscale up (" + section_name(section) + ")", args)) + return false; + return install_kernel_routing(section); +} + +function stop_section(section) { + let down_args = tailscale_client_args(section); + push(down_args, "down"); + command_status(command_from_args(down_args) + " 2>/dev/null"); + let pid_file = section_pid_file(section); + let pid = read_pid(pid_file); + if (pid_alive(pid)) { + command_success_from_args([ "kill", as_string(pid) ]); + for (let attempt = 0; attempt < 10 && pid_alive(pid); attempt++) + command_success_from_args([ "sleep", "1" ]); + if (pid_alive(pid)) + command_success_from_args([ "kill", "-9", as_string(pid) ]); + } + fs.unlink(pid_file); +} + +function backend_state() { + if (!binary_available()) + return ""; + let out = trim(command_output_from_args([ TAILSCALE_BIN, "status", "--json", "--peers=false" ])); + if (out == "") + return ""; + let parsed = json(out); + return as_string(object_or_empty(parsed)["BackendState"] || ""); +} + +function status_json() { + let sections = native_tailscale_sections(); + let configured = length(sections) > 0; + let available = binary_available(); + let pkg = package_installed(); + let version = available ? (daemon_version() || "unknown") : "not installed"; + + let running = 0; + let states = {}; + for (let section in sections) { + if (running_pid(section)) { + running++; + let state = backend_state(); + if (state != "") + states[section_name(section)] = state; + } + } + + let message = "tailscale provider status is normal"; + if (configured && !available) + message = "native Tailscale is configured, but the tailscale package is missing; install it on the Updates tab"; + else if (configured && running < length(sections)) + message = "native Tailscale is configured, but tailscaled is not running for every section"; + else if (!configured && available) + message = "Tailscale package is installed, but no server section uses native mode"; + else if (!configured && !available) + message = "tailscale package is not installed; native Tailscale is unavailable"; + + write_json({ + installed: available, + package_installed: pkg, + provider_available: available, + provider_path: TAILSCALED_BIN, + version, + configured, + expected_process_count: length(sections), + running_process_count: running, + backend_states: states, + ready: configured && available && running == length(sections) && length(sections) > 0, + conflict: running > length(sections), + status_message: message + }); +} + +function check_json() { + write_json({ + tailscale_installed: binary_available(), + tailscale_package_installed: package_installed(), + tailscale_provider_path: TAILSCALED_BIN + }); +} + +function stop_runtime() { + for (let section in native_tailscale_sections()) + stop_section(section); + // Also clean leftovers for sections that disappeared from the config. + let entries = null; + try { + entries = fs.readdir(RUNTIME_DIR); + } catch (e) { + entries = null; + } + if (entries != null) { + for (let entry in entries) { + let pid_file = RUNTIME_DIR + "/" + as_string(entry) + "/tailscaled.pid"; + let pid = read_pid(pid_file); + if (pid_alive(pid)) + command_success_from_args([ "kill", as_string(pid) ]); + } + } + remove_kernel_routing(false); + return 0; +} + +function start_runtime() { + let sections = native_tailscale_sections(); + if (length(sections) == 0) { + // Nothing configured: make sure nothing is left over from a removed section. + stop_runtime(); + return 0; + } + if (!binary_available()) { + log_message("tailscale binaries are missing; install the Tailscale component first", "warn"); + return 1; + } + let failures = 0; + for (let section in sections) { + if (!ensure_dirs(section) || !start_daemon(section) || !bring_up(section)) + failures++; + } + return failures > 0 ? 1 : 0; +} + +let mode = ARGV[0] || ""; + +if (mode == "start-runtime") + exit(start_runtime()); +else if (mode == "stop-runtime") + exit(stop_runtime()); +else if (mode == "status") + status_json(); +else if (mode == "check") + check_json(); +else if (mode == "installed" || mode == "provider-available") + exit(binary_available() ? 0 : 1); +else if (mode == "package-installed") + exit(package_installed() ? 0 : 1); +else if (mode == "package-version") + print(daemon_version(), "\n"); +else if (mode == "native-section-count") + print(length(native_tailscale_sections()), "\n"); +else { + warn("Usage: providers/tailscale/runtime.uc \n"); + exit(1); +} diff --git a/tachyon/files/usr/lib/service/lifecycle.uc b/tachyon/files/usr/lib/service/lifecycle.uc index b83121bc..961ea2a6 100644 --- a/tachyon/files/usr/lib/service/lifecycle.uc +++ b/tachyon/files/usr/lib/service/lifecycle.uc @@ -119,6 +119,7 @@ const DIAGNOSTICS_UC = LIB_DIR + "/diagnostics/runtime.uc"; const ZAPRET_UC = LIB_DIR + "/providers/zapret/runtime.uc"; const ZAPRET2_UC = LIB_DIR + "/providers/zapret2/runtime.uc"; const BYEDPI_UC = LIB_DIR + "/providers/byedpi/runtime.uc"; +const TAILSCALE_UC = LIB_DIR + "/providers/tailscale/runtime.uc"; const PACKAGES_UC = LIB_DIR + "/core/packages.uc"; const WATCHDOG_UC = LIB_DIR + "/service/watchdog.uc"; const TELEGRAM_UC = LIB_DIR + "/service/telegram.uc"; @@ -891,6 +892,8 @@ function stop_main() { log_message("Zapret2 stop failed (non-fatal)", "warn"); if (!module_success(BYEDPI_UC, [ "stop-runtime" ])) log_message("ByeDPI stop failed (non-fatal)", "warn"); + if (!module_success(TAILSCALE_UC, [ "stop-runtime" ])) + log_message("Tailscale stop failed (non-fatal)", "warn"); if (command_success_from_args([ "nft", "list", "table", "inet", NFT_TABLE_NAME ])) { if (!command_success_from_args([ "nft", "delete", "table", "inet", NFT_TABLE_NAME ])) @@ -1370,7 +1373,10 @@ function reload(reason) { if (plan.needs_zapret2_restart == 1) module_success(ZAPRET2_UC, [ "start-runtime" ]); if (plan.needs_byedpi_restart == 1) - module_success(BYEDPI_UC, [ "start-runtime" ]); + module_success(BYEDPI_UC, [ "start-runtime" ]); + // Native Tailscale must be up before sing-box so tailnet routes win over + // policy routing from the very first packet. + module_success(TAILSCALE_UC, [ "start-runtime" ]); if (plan.needs_dnsmasq_configure == 1) { status = dnsmasq_configure(true); diff --git a/tachyon/files/usr/lib/service/state.uc b/tachyon/files/usr/lib/service/state.uc index d74f647e..e5fe1f8c 100644 --- a/tachyon/files/usr/lib/service/state.uc +++ b/tachyon/files/usr/lib/service/state.uc @@ -1492,6 +1492,9 @@ function append_sing_box_server_signature_body(body, server) { [ "tailscale_auth_key", "" ], [ "tailscale_control_url", "" ], [ "tailscale_hostname", "" ], + [ "tailscale_mode", "singbox" ], + [ "tailscale_masquerade", "1" ], + [ "tailscale_use_exit_node", "0" ], [ "tailscale_accept_routes", "" ], [ "tailscale_advertise_routes", "" ], [ "tailscale_advertise_exit_node", "" ] diff --git a/tachyon/files/usr/lib/singbox/generator_routes.uc b/tachyon/files/usr/lib/singbox/generator_routes.uc index 32f06be4..1569f80f 100644 --- a/tachyon/files/usr/lib/singbox/generator_routes.uc +++ b/tachyon/files/usr/lib/singbox/generator_routes.uc @@ -1498,6 +1498,9 @@ function section_by_name(sections, name) { function add_server_routes(config, servers, sections) { for (let server in servers) { + // Native-mode Tailscale runs outside sing-box: no inbound, no rules. + if (runtime_servers.is_native_tailscale(server)) + continue; runtime_servers.add_sniff_rule(config, server); let inbound = runtime_constants.server_inbound_tag(server[".name"]); diff --git a/tachyon/files/usr/lib/singbox/servers.uc b/tachyon/files/usr/lib/singbox/servers.uc index 0c337f96..ce645025 100644 --- a/tachyon/files/usr/lib/singbox/servers.uc +++ b/tachyon/files/usr/lib/singbox/servers.uc @@ -416,9 +416,18 @@ function add_tailscale_endpoint(config, section, tag_name) { push(config.endpoints, endpoint); } +function is_native_tailscale(section) { + return option(section, "protocol", "vless") == "tailscale" && + option(section, "tailscale_mode", "singbox") == "native"; +} + function add_dns_bypass(config, section) { if (option(section, "protocol", "vless") != "tailscale") return; + // Native mode runs tailscaled outside sing-box; MagicDNS is served by + // dnsmasq instead of a sing-box DNS server. + if (is_native_tailscale(section)) + return; let section_name = section[".name"]; let inbound = runtime_constants.server_inbound_tag(section_name); @@ -443,8 +452,11 @@ function add_server(config, section) { let protocol = option(section, "protocol", "vless"); let tag_name = runtime_constants.server_inbound_tag(section_name); - if (protocol == "tailscale") + if (protocol == "tailscale") { + if (is_native_tailscale(section)) + return; add_tailscale_endpoint(config, section, tag_name); + } else if (protocol == "json_inbound") add_json_inbound(config, section, tag_name); else @@ -517,5 +529,6 @@ function clone_rules_for_inbound(config, source_inbound, target_inbound, skip_do return { add_server, add_sniff_rule, - clone_rules_for_inbound + clone_rules_for_inbound, + is_native_tailscale }; \ No newline at end of file diff --git a/tachyon/files/usr/lib/tachyon/rag_index.json b/tachyon/files/usr/lib/tachyon/rag_index.json index dddf2192..645338c5 100644 --- a/tachyon/files/usr/lib/tachyon/rag_index.json +++ b/tachyon/files/usr/lib/tachyon/rag_index.json @@ -1 +1 @@ -{"version": 1, "chunk_size": 2000, "chunk_overlap": 200, "total_chunks": 40, "files": ["01_SYSTEM_ARCHITECTURE.md", "02_UCODE_MODULE_REFERENCE.md", "03_NETWORKING_NFTABLES_AND_ROUTING.md", "04_DPI_BYPASS_ENGINES.md", "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "06_FRONTEND_LUCI_TYPESCRIPT.md", "07_BUILD_TESTS_AND_DEPLOYMENT.md", "08_UCI_CONFIGURATION_SCHEMA.md", "09_TELEGRAM_BOT_ARCHITECTURE.md", "10_HOSTS_ENGINE_AND_BLOCKLISTS.md", "11_TROUBLESHOOTING_AND_FIELD_MANUAL.md", "12_UCODE_PROGRAMMING_PATTERNS.md", "13_REST_API_AND_MCP_SPECIFICATION.md", "INDEX.md"], "chunks": [{"id": 0, "file": "01_SYSTEM_ARCHITECTURE.md", "heading": "2.2. Deterministic State & Atomic Writes", "text": "# 01. System Architecture & Core Concepts\n\n## 1. High-Level Overview\n\n**Tachyon** is an enterprise-grade, lightweight traffic orchestration, multi-protocol proxying, and DPI anti-censorship platform built natively for **OpenWrt** (supporting 23.05, 24.10, 25.x, and SNAPSHOT).\n\n```mermaid\ngraph TD\n Client[LAN Clients] -->|TCP/UDP| Router[OpenWrt Router]\n Router --> NFT[nftables: inet TachyonTable]\n \n NFT -->|Direct List / 0ms| WAN[Direct WAN Gateway]\n NFT -->|NFQueue Desync| Zapret[Zapret v1 / v2 nfqws/2]\n NFT -->|SOCKS Desync| ByeDPI[ByeDPI ciadpi]\n NFT -->|TProxy / Redirect| SingBox[sing-box Core v1.11+]\n \n SingBox -->|VLESS Reality / H2 / WG| VPS[External VPS / Global Web]\n Zapret --> WAN\n ByeDPI --> WAN\n \n subgraph Tachyon Control Plane\n UCODE[ucode Core Runtime] --> NFT\n UCODE --> SingBox\n UCODE --> Zapret\n UCODE --> ByeDPI\n Watchdog[Autonomous Watchdog 5-15s] --> UCODE\n AIDoctor[AI Doctor v2.5 / Local Doctor] --> Watchdog\n Telegram[Telegram Bot Daemon] --> UCODE\n RestAPI[HTTP REST Agent API / OpenAPI 3.0] --> UCODE\n LuCI[LuCI TypeScript Web UI] --> UCODE\n end\n```\n\n---\n\n## 2. Core Architectural Pillars\n\n### 2.1. Native ucode Runtime (Zero Heavy Runtimes)\n* All control plane logic, UCI manipulation, config generation, health checks, and JSON APIs are written in **ucode**, OpenWrt's native C-like scripting language.\n* **Benefits**:\n * Consumes $\\approx$ 1–3 MB RAM (contrasted with 30–60 MB for Python/Node).\n * Executes directly in C-speed with native bindings to `libuci`, `libubus`, `libblobmsg`, and posix syscalls (`fs`, `math`, `socket`).\n * Runs on routers with as little as 128 MB RAM.\n\n### 2.2. Deterministic State & Atomic Writes\n* All configuration changes (`/etc/config/tachyon`) and JSON runtime configurations (`/tmp/tachyon/sing-box.json`) use an **atomic write pattern** (`tmp_file + rename`):\n ```ucode\n fs.writefile(tmp_path, json_str);\n fs.rename(tmp_path, target_path);", "embedding": []}, {"id": 1, "file": "01_SYSTEM_ARCHITECTURE.md", "heading": "3. Directory Layout & Repository Anatomy", "text": "* All configuration changes (`/etc/config/tachyon`) and JSON runtime configurations (`/tmp/tachyon/sing-box.json`) use an **atomic write pattern** (`tmp_file + rename`):\n ```ucode\n fs.writefile(tmp_path, json_str);\n fs.rename(tmp_path, target_path);\n ```\n This prevents file corruption during sudden power losses or kernel panics.\n\n### 2.3. Dual Control Interfaces: LuCI & Headless Telegram/REST\n1. **LuCI Web UI**: Modern TypeScript SPA (`fe-app-tachyon`) compiled into LuCI JS assets (`luci-app-tachyon`).\n2. **Interactive Telegram Bot**: Embedded polling daemon with inline keyboards for on-the-fly server switching, rule updates, and instant diagnostics.\n3. **HTTP REST Agent API**: Secure CGI endpoint (`/cgi-bin/tachyon-agent/`) with OpenAPI 3.0.3 spec for integration with LLM agents (ChatGPT Custom GPTs, Cursor, Claude Code, N8N).\n\n---\n\n## 3. Directory Layout & Repository Anatomy\n\n```\ntachyon/\n├── tachyon/files/ # Core backend package root\n│ ├── etc/\n│ │ ├── config/tachyon # Main UCI configuration (chmod 600)\n│ │ ├── init.d/tachyon # Procd service init script\n│ │ └── uci-defaults/ # Post-installation default configs\n│ ├── usr/\n│ │ ├── bin/tachyon # Main CLI entrypoint wrapper\n│ │ └── lib/tachyon/ # Modular ucode libraries (60+ modules)\n│ │ ├── core/ # Low-level helpers, UCI, constants\n│ │ ├── service/ # Lifecycle, Watchdog, APIs, Telegram\n│ │ ├── diagnostics/ # AI Doctor, Local Doctor, Quick Fixes\n│ │ ├── singbox/ # Config generator, DNS, routing\n│ │ ├── providers/ # Zapret v1/v2, ByeDPI, NFQueue\n│ │ ├── nft/ # nftables ruleset compiler & applier\n│ │ ├── dns/ # dnsmasq & DNS routing coordinator\n│ │ ├── components/ # Updater, package manager, hosts\n│ │ ├── config/ # Validator, rule engine, migrations", "embedding": []}, {"id": 2, "file": "01_SYSTEM_ARCHITECTURE.md", "heading": "4. Key Runtime Processes & Daemons", "text": "│ │ ├── dns/ # dnsmasq & DNS routing coordinator\n│ │ ├── components/ # Updater, package manager, hosts\n│ │ ├── config/ # Validator, rule engine, migrations\n│ │ └── subscription/ # Subscriptions fetcher & parser\n├── luci-app-tachyon/ # OpenWrt LuCI package\n│ ├── htdocs/luci-static/resources/view/tachyon/ # Compiled frontend JS bundle\n│ ├── root/etc/uci-defaults/ # LuCI menu registration\n│ └── po/ # Translations (ru, en)\n├── fe-app-tachyon/ # Frontend TypeScript source\n│ ├── src/ # TypeScript SPA source\n│ ├── tsup.config.ts # Builds and patches LuCI baseclass\n│ └── package.json # Dependencies (vitest, typescript, tsup)\n├── tests/ # Comprehensive test suite (100+ shell tests)\n├── build.sh # Release packager (ipk + apk)\n├── install.sh # One-liner end-user installer\n└── docs/ # Technical documentation & Knowledge Base\n```\n\n---\n\n## 4. Key Runtime Processes & Daemons\n\n| Process | Role | Execution Model |\n|---|---|---|\n| `sing-box` | Core proxying & packet tunneling engine | Managed by procd (`/etc/init.d/tachyon`) |\n| `nfqws` / `nfqws2` | Local TCP/UDP packet desynchronizer (Zapret) | Spawned into background per configured rule |\n| `ciadpi` | Local SOCKS desync proxy (ByeDPI) | Spawned on `127.0.0.1:1080` / custom port |\n| `watchdog.uc` | State supervision, loop mitigation, self-healing | Background periodic daemon (every 5–15s) |\n| `telegram.uc` | Telegram Bot Long-Polling Daemon | Procd-supervised background worker |\n| `agent_api.uc` | HTTP REST Agent API Handler | Invoked on-demand via `uhttpd` CGI |", "embedding": []}, {"id": 3, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "2. Service Management Subsystem (`service/`)", "text": "# 02. ucode Backend Module Reference\n\nTachyon's backend is modularized under `tachyon/files/usr/lib/tachyon/`. Modules are loaded dynamically via ucode's `require()` or executed directly via `ucode -L `.\n\n---\n\n## 1. Core Subsystem (`core/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `constants.uc` | Central repository of paths, default ports, mark values, version detection (`detect_installed_version()`), commit SHA resolver (`detect_installed_commit_sha()`), UCI config names. |\n| `common.uc` | String manipulation (`trim`, `split_lines`), file helpers (`read_file`, `write_atomic`), logging formatters (`log_info`, `log_error`, `log_debug`). |\n| `helpers.uc` | Process management (`popen_read`, `system_exec`), CIDR formatting, MAC address validators, integer conversions with safety fallbacks. |\n| `ip.uc` | IP v4/v6 parser, subnet containment (`ip_in_subnet`), CIDR validator, loopback & private IP range detection. |\n| `url.uc` | RFC 3986 URL parser, extraction of scheme, host, port, path, query params, hash fragment, safe URL encoding/decoding. |\n| `uci.uc` | High-level wrapper over OpenWrt `libuci`: `get_section`, `set_option`, `add_section`, `delete_section`, atomic commit with lock. |\n| `packages.uc` | Package manager abstraction: detects `opkg` vs `apk` (OpenWrt 25.x+), checks installed packages, versions, and dependencies. |\n| `events.uc` | Internal event bus mechanism: publish/subscribe for service state transitions and notification dispatching. |\n\n---\n\n## 2. Service Management Subsystem (`service/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `lifecycle.uc` | Service start, stop, restart, enable, disable. Coordinates teardown order (nftables -> singbox -> zapret -> dnsmasq) to prevent routing deadlocks. |\n| `state.uc` | Comprehensive runtime state reader (`get_runtime_state()`): active proxy server, watchdog status, interface health, uptime, memory consumption. |\n| `reload.uc` | Hot-reload planner (`plan_reload()`): inspects what changed in UCI and applies the minimal necessary restart (e.g. soft-reload sing-box vs full nftables rebuild). |", "embedding": []}, {"id": 4, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "3. Diagnostics & AI Subsystem (`diagnostics/`)", "text": "| `state.uc` | Comprehensive runtime state reader (`get_runtime_state()`): active proxy server, watchdog status, interface health, uptime, memory consumption. |\n| `reload.uc` | Hot-reload planner (`plan_reload()`): inspects what changed in UCI and applies the minimal necessary restart (e.g. soft-reload sing-box vs full nftables rebuild). |\n| `reset.uc` | Factory reset routines: wipes `/etc/config/tachyon`, cleans up temporary caches, restores default templates. |\n| `watchdog.uc` | Autonomous supervisor loop: checks sing-box process, memory thresholds (OOM guard), DNS resolution, loop-restart mitigation (`safe_proxy_restart`). |\n| `snapshot.uc` | Assembles a structured system snapshot JSON for AI Doctor and external diagnostics. |\n| `agent_api.uc` | CGI handler for `/cgi-bin/tachyon-agent/`: handles routing, Bearer authentication validation, endpoint dispatch, JSON serialization. |\n| `agent_mcp.uc` | Model Context Protocol (MCP) tool schema and handlers for integration with MCP clients (Cursor, Claude Desktop). |\n| `telegram.uc` | Telegram Bot daemon: polling loop (`getUpdates`), command dispatcher (`/doctor`, `/ai_doctor`, `/fix`, `/restart`, `/status`), inline keyboards. |\n| `warp_generator.uc` | Cloudflare WARP account generator & key exchange for auto-provisioning WireGuard outbounds. |\n| `event_controller.uc` | Coordinates async background job workers, PID tracking, lock files, and log ring-buffer management. |\n\n---\n\n## 3. Diagnostics & AI Subsystem (`diagnostics/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `runtime.uc` | **Local Rule Doctor & AI Doctor engine**: runs 10+ local heuristic checks, detects NTP desync, conntrack overflow, MTU issues, bootstrap DNS deadlock; applies 13+ quick fix codes (`apply_quick_fix()`), executes emergency internet restoration (`restore_native_internet()`). |\n| `service_check.uc` | Health probes: tests sing-box TCP/UDP proxying, DNS response latency, WAN internet connectivity, gateway reachability. |\n| `status.uc` | Detailed diagnostics status assembler for LuCI Diagnostic tab and CLI `tachyon doctor`. |", "embedding": []}, {"id": 5, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "5. Providers & DPI Bypass Subsystem (`providers/`)", "text": "| `service_check.uc` | Health probes: tests sing-box TCP/UDP proxying, DNS response latency, WAN internet connectivity, gateway reachability. |\n| `status.uc` | Detailed diagnostics status assembler for LuCI Diagnostic tab and CLI `tachyon doctor`. |\n\n---\n\n## 4. sing-box Subsystem (`singbox/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `generator.uc` | Master sing-box JSON configuration compiler (`/tmp/tachyon/sing-box.json`): binds inbounds, outbounds, routing rules, DNS, experimental. |\n| `generator_outbounds.uc` | Compiles VLESS (Reality/gRPC/WS), VMess, Shadowsocks, Trojan, Hysteria2, WireGuard, Direct, and Block outbounds. |\n| `generator_routes.uc` | Compiles routing rules: domain matchers, IP CIDR matchers, GeoIP rules, client MAC/IP matchers, detour assignments. |\n| `dns.uc` | Compiles sing-box DNS configuration: local DNS, remote DoH/DoT/UDP DNS, fakeip/direct resolution rules. |\n| `dns_failover.uc` | High-availability DNS failover logic: switches between primary and secondary bootstrap DNS upon timeouts. |\n| `dns_prefetch.uc` | Domain pre-fetching & cache warming for frequently accessed sites. |\n| `dns_presets.uc` | Built-in DNS presets (Cloudflare, Google, AdGuard, Quad9, OpenDNS, Custom DoH). |\n| `priority.uc` | Outbound group priority manager: auto-failover, fallback groups, URL-test latency sorting. |\n| `route.uc` | Low-level sing-box route rule builder & selector logic. |\n| `rulesets.uc` | Rule-set binary (`.srs`) and JSON ruleset compiler and parser. |\n| `servers.uc` | Server node parser, validator, and URI serializer (VLESS, VMess, SS, Trojan, H2). |\n| `subscription.uc` | Subscriptions processor: download, base64 decoding, node deduplication, format normalizer. |\n| `urltest.uc` | RTT ping & latency testing worker: measures real response times across proxy servers. |\n\n---\n\n## 5. Providers & DPI Bypass Subsystem (`providers/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `zapret/runtime.uc` | Manages **Zapret v1 (`nfqws`)** process arguments, queue numbers, NFQueue bindings, daemon start/stop. |", "embedding": []}, {"id": 6, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "7. Configuration & Components Subsystem (`config/`, `components/`, `subscription/`)", "text": "## 5. Providers & DPI Bypass Subsystem (`providers/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `zapret/runtime.uc` | Manages **Zapret v1 (`nfqws`)** process arguments, queue numbers, NFQueue bindings, daemon start/stop. |\n| `zapret2/runtime.uc` | Manages **Zapret v2 (`nfqws2`)** advanced multi-vector desync arguments (`--dpi-desync=multisplit`, `--dpi-desync-seqovl`). |\n| `byedpi/runtime.uc` | Manages **ByeDPI (`ciadpi`)** SOCKS5 proxy daemon, port allocation, payload split parameters. |\n| `nfqueue/runtime.uc` | Kernel NFQueue allocation table, prevents queue ID collisions between Zapret v1 and v2. |\n| `rules.uc` | Maps UCI routing rules to specific provider action handlers (`action = zapret | zapret2 | byedpi | proxy`). |\n| `status.uc` | Real-time process inspection and health monitoring of all active DPI bypass daemons. |\n\n---\n\n## 6. Networking & Firewall Subsystem (`nft/`, `dns/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `nft/apply.uc` | Generates and commits nftables ruleset (`inet TachyonTable`): prerouting, output, divert chains, marks, fwmark, bypass sets. |\n| `dns/apply.uc` | Coordinates DNS interception: configures dnsmasq forwarders, port redirection (`127.0.0.1:5353`), ipset / nftset auto-population. |\n\n---\n\n## 7. Configuration & Components Subsystem (`config/`, `components/`, `subscription/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `config/validator.uc` | Strict UCI schema validation: validates IP addresses, ports, UUIDs, private keys, domain names, action modes. |\n| `config/migration.uc` | Automatic migration engine: migrates configurations from legacy Forkop, Podkop Plus, and Podkop configs without data loss. |\n| `components/updater.uc` | Background component installer/updater: handles downloading, unpacking, installing, and version checking of binaries. |\n| `components/hosts.uc` | Remote hosts lists ingestor: fetches blocklists, merges into unified `combined.txt`, handles GitHub mirror failovers. |", "embedding": []}, {"id": 7, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "7. Configuration & Components Subsystem (`config/`, `components/`, `subscription/`)", "text": "| `components/updater.uc` | Background component installer/updater: handles downloading, unpacking, installing, and version checking of binaries. |\n| `components/hosts.uc` | Remote hosts lists ingestor: fetches blocklists, merges into unified `combined.txt`, handles GitHub mirror failovers. |\n| `subscription/cache.uc` | Atomic subscription cache store, prevents redundant network downloads. |\n| `subscription/parser.uc` | Universal proxy URI parser: parses `vless://`, `vmess://`, `ss://`, `trojan://`, `hysteria2://`, `wireguard://`. |", "embedding": []}, {"id": 8, "file": "03_NETWORKING_NFTABLES_AND_ROUTING.md", "heading": "2.1. Key Chains", "text": "# 03. Networking, nftables & Traffic Routing\n\n## 1. Packet Journey & Routing Lifecycle\n\nTachyon intercepts traffic at the OpenWrt kernel level using **nftables** and policy routing (`ip rule` + routing table `100`).\n\n```mermaid\nsequenceDiagram\n autonumber\n participant Client as LAN Client\n participant PREROUTING as nftables PREROUTING\n participant Sets as nftables Sets (Bypass / Target)\n participant Divert as Detour Action\n participant SingBox as sing-box / Zapret / ByeDPI\n participant WAN as WAN / Internet\n\n Client->>PREROUTING: TCP/UDP SYN Packet (e.g. 192.168.1.100 -> 1.1.1.1:443)\n PREROUTING->>Sets: Check destination IP / Port against Tachyon sets\n alt IP in Bypass Set (Local Subnet / RuNet / Direct)\n Sets->>WAN: Direct Forwarding (0 ms overhead)\n else IP in Target Set or Default Detour\n Sets->>Divert: Mark packet with FWMARK (e.g. 0x10000000)\n Divert->>SingBox: Divert via TProxy / Redir / NFQueue\n SingBox->>WAN: Processed / Encrypted / Desynchronized Outbound\n end\n```\n\n---\n\n## 2. nftables Table Structure (`inet TachyonTable`)\n\nTachyon encapsulates all its firewall rules inside a dedicated table `inet TachyonTable`. This ensures clean isolation from the system `inet fw4` firewall.\n\n### 2.1. Key Chains\n\n1. **`dstnat` / `prerouting` (Hook `prerouting`, priority `-100` / `dstnat`)**:\n * Intercepts incoming LAN client packets before routing decisions.\n * Checks client MAC / IP exclusion lists.\n * Matches destination IP against `tachyon_bypass_v4` / `tachyon_target_v4` sets.\n * Performs TProxy redirect (`tproxy to :10080 meta mark set 0x10000000 accept`) or Port redirect.\n2. **`output` (Hook `output`, priority `-100`)**:\n * Intercepts router's own locally generated packets (e.g. DNS lookups, curl, router traffic).\n * Prevents self-interception loops (`meta skuid == \"tachyon\" return`).\n3. **`nfqueue_chain`**:\n * Directs packets destined for Zapret v1 / v2 to kernel NFQueue numbers (`queue num 200`, `queue num 201`).", "embedding": []}, {"id": 9, "file": "03_NETWORKING_NFTABLES_AND_ROUTING.md", "heading": "3.3. Bootstrap DNS Dead-Lock Prevention", "text": "* Prevents self-interception loops (`meta skuid == \"tachyon\" return`).\n3. **`nfqueue_chain`**:\n * Directs packets destined for Zapret v1 / v2 to kernel NFQueue numbers (`queue num 200`, `queue num 201`).\n\n### 2.2. Bitmask & Firewall Marks\n\n| Mark Value | Meaning | Action |\n|---|---|---|\n| `0x10000000` | Redirected to sing-box TProxy | Routed to local loopback via table 100 |\n| `0x20000000` | Processed by Zapret / NFQueue | Dispatched to nfqws daemon |\n| `0x40000000` | Marked for ByeDPI SOCKS | Forwarded to local SOCKS redirect |\n| `0x80000000` | Bypass marker (Direct WAN) | Accepted immediately without inspection |\n\n---\n\n## 3. DNS Architecture & Interception\n\n### 3.1. DNS Resolution Pipeline\n\n```\n[LAN Client DNS Query: 53]\n │\n ▼\n [dnsmasq / Router]\n │\n ├─► If Domain in Direct / RuNet / Local ──► Local Upstream DNS (ISP / DoH)\n │\n ├─► If Domain in Hosts Section ──────────► Static IP from dns_hosts\n │\n └─► If Domain in Target Proxy / Desync ──► sing-box DNS (127.0.0.1:5353)\n │\n ▼\n [Remote Secure DoH/DoT]\n [Populates nftables set]\n```\n\n### 3.2. Dynamic nftables Set Population (`nftset`)\n* When dnsmasq resolves a domain associated with a proxy or Zapret rule, it automatically adds the resolved IP address to the corresponding kernel nftables set (e.g. `tachyon_target_v4`).\n* This enables sub-millisecond, line-rate packet redirection without re-evaluating domain names in user-space for every packet.\n\n### 3.3. Bootstrap DNS Dead-Lock Prevention\n* A critical failure mode in proxy routers is a **DNS loop**: sing-box needs to resolve the VPS hostname (e.g. `vps.example.com`), but its own DNS resolver routes queries through sing-box, which is not yet connected.", "embedding": []}, {"id": 10, "file": "03_NETWORKING_NFTABLES_AND_ROUTING.md", "heading": "4.1. Emergency Internet Restoration Mechanism", "text": "* A critical failure mode in proxy routers is a **DNS loop**: sing-box needs to resolve the VPS hostname (e.g. `vps.example.com`), but its own DNS resolver routes queries through sing-box, which is not yet connected.\n* **Tachyon's Solution**:\n * Dedicated `direct_resolvers` (Cloudflare `1.1.1.1`, Google `8.8.8.8`) bypass all proxy rules.\n * Local Rule Doctor monitors bootstrap DNS reachability and automatically executes `fix_bootstrap_dns` if a circular dependency is detected.\n\n---\n\n## 4. Policy Routing (`ip rule` & Routing Tables)\n\nTachyon provisions routing table `100`:\n```sh\n# Routing rule for marked packets\nip rule add fwmark 0x10000000/0x10000000 lookup 100 priority 10000\n\n# Local loopback route in table 100\nip route add local default dev lo table 100\n```\n\n### 4.1. Emergency Internet Restoration Mechanism\nWhen `tachyon restore_native_internet` is triggered:\n1. `inet TachyonTable` is atomically deleted: `nft delete table inet TachyonTable`.\n2. Policy routing rule `ip rule del lookup 100` is removed.\n3. dnsmasq configuration is reverted to native upstream forwarders.\n4. sing-box process is stopped cleanly.\n5. Result: Full, native WAN connectivity is restored in under 200 ms without rebooting the router.", "embedding": []}, {"id": 11, "file": "04_DPI_BYPASS_ENGINES.md", "heading": "2.1. Key Desync Strategies", "text": "# 04. DPI Bypass & Desynchronization Engines\n\nTachyon integrates three distinct local packet desynchronization engines that operate entirely on the router hardware without requiring an external proxy server (Zero-VPS mode).\n\n---\n\n## 1. Comparison Matrix: Zapret v1 vs Zapret v2 vs ByeDPI\n\n| Feature | Zapret v1 (`nfqws`) | Zapret v2 (`nfqws2`) | ByeDPI (`ciadpi`) |\n|---|---|---|---|\n| **Binary** | `/usr/bin/nfqws` | `/usr/bin/nfqws2` | `/usr/bin/ciadpi` |\n| **Operating Model** | Kernel NFQueue Intercept | Kernel NFQueue Multi-Q | Local SOCKS5 / TProxy |\n| **Target Protocols** | HTTP, TLS (ClientHello) | HTTP, TLS, QUIC, WireGuard | HTTP, TLS |\n| **Packet Splitting** | `split2`, `disorder`, `fake` | `multisplit`, `seqovl`, `wsize`, `fake` | `--split-pos`, `--disorder`, `--fake` |\n| **Multi-Vector Chains** | Single strategy per rule | Multi-phase composite chains | Composite SOCKS args |\n| **CPU Overhead** | Minimal (< 1%) | Low (< 2%) | Low (< 2%) |\n| **Best For** | Legacy OpenWrt, Basic DPI | YouTube 4K, Discord, TSPU | Non-root users, SOCKS routing |\n\n---\n\n## 2. Desynchronization Techniques Explained\n\nDeep Packet Inspection (DPI) systems inspect packet headers (such as TLS Server Name Indication `SNI` or HTTP `Host` headers) to detect and block access. Desynchronization tricks the DPI into losing track of the connection state while the destination server reconstructs the stream normally.\n\n```\nOriginal Stream: [TLS ClientHello with SNI] ──────────► (Blocked by DPI)\n\nDesynchronized: [Fake Bad Checksum Packet] ──────────► (DPI resets state)\n [Payload Part 1: Snippet] ──────────► (DPI misses SNI)\n [Payload Part 2: Remainder] ─────────► (Server merges stream)\n```\n\n### 2.1. Key Desync Strategies\n\n1. **`split2` / `multisplit`**:\n * Splits the TCP segment right inside the TLS SNI or HTTP Host header. The DPI does not reassemble small fragments, while the end server's TCP stack reconstructs the full buffer seamlessly.\n2. **`disorder` / `disorder2`**:", "embedding": []}, {"id": 12, "file": "04_DPI_BYPASS_ENGINES.md", "heading": "4. NFQueue Multiplexing & Conflict Prevention", "text": "* Splits the TCP segment right inside the TLS SNI or HTTP Host header. The DPI does not reassemble small fragments, while the end server's TCP stack reconstructs the full buffer seamlessly.\n2. **`disorder` / `disorder2`**:\n * Sends the second half of the packet before the first half with appropriate TCP sequence numbers. The DPI sees out-of-order data and skips inspection.\n3. **`fake` / `fake_sni`**:\n * Sends a fake TLS ClientHello packet with a low TTL (Time To Live). The DPI intercepts the fake packet and approves the connection; the packet dies before reaching the destination server.\n4. **`seqovl` (Sequence Overlap)**:\n * Sends a fake packet with overlapping sequence numbers that the server's TCP stack discards while the DPI's state machine is fooled.\n5. **`wsize` (Window Size Manipulation)**:\n * Forces a small TCP window size to prevent DPI from receiving full payloads in single bursts.\n\n---\n\n## 3. Configuration & Runtime Management\n\n### 3.1. Zapret v2 Configuration Example (`/etc/config/tachyon`)\n\n```ini\nconfig provider 'zapret2'\n option enabled '1'\n option daemon_bin '/usr/bin/nfqws2'\n option qnum '201'\n option args_tcp '--dpi-desync=multisplit --dpi-desync-split-pos=1,midsld --dpi-desync-seqovl=1 --dpi-desync-fooling=badseq'\n option args_udp '--dpi-desync=fake --dpi-desync-any-protocol=1 --dpi-desync-cutoff=d4'\n\nconfig rule 'youtube_rule'\n option enabled '1'\n option name 'YouTube 4K Desync'\n option action 'zapret2'\n list domain_pattern 'googlevideo.com'\n list domain_pattern 'youtube.com'\n list domain_pattern 'ytimg.com'\n list domain_pattern 'discord.gg'\n list domain_pattern 'discord.com'\n```\n\n### 3.2. ByeDPI Configuration Example\n\n```ini\nconfig provider 'byedpi'\n option enabled '1'\n option daemon_bin '/usr/bin/ciadpi'\n option listen_ip '127.0.0.1'\n option listen_port '1080'\n option args '--split-pos 1 --disorder 1 --fake -1 --ttl 8'\n```\n\n---\n\n## 4. NFQueue Multiplexing & Conflict Prevention\n\n* Kernel `nfnetlink_queue` requires unique queue numbers per daemon instance.", "embedding": []}, {"id": 13, "file": "04_DPI_BYPASS_ENGINES.md", "heading": "4. AmneziaWG в sing-box-extended: верификация (2026-08)", "text": "option args '--split-pos 1 --disorder 1 --fake -1 --ttl 8'\n```\n\n---\n\n## 4. NFQueue Multiplexing & Conflict Prevention\n\n* Kernel `nfnetlink_queue` requires unique queue numbers per daemon instance.\n* Tachyon's `providers/nfqueue/runtime.uc` dynamically manages queue allocation:\n * Default Zapret v1 queue: `200`\n * Default Zapret v2 queue: `201`\n* If both Zapret v1 and v2 are enabled simultaneously for different rules, Tachyon enforces strict queue isolation and prevents PID collisions.\n\n## 4. AmneziaWG в sing-box-extended: верификация (2026-08)\n\nПроверено против бинарника релиза `v1.13.18-extended-2.6.5` (shtorm-7/sing-box-extended):\n\n- `endpoint.amnezia` полностью реализован: `AmneziaOptions` (`transport/wireguard/endpoint_options.go`)\n → IPC `jc/jmin/jmax/s1-s4/h1-h4/i1-i5` (`transport/wireguard/endpoint.go`)\n → wireguard-go форк `shtorm-7/wireguard-go v0.0.4-extended-1.5.3`:\n I1-I5 шлются отдельными пакетами ПЕРЕД handshake initiation\n (`device/send.go SendHandshakeInitiation`), формат значения — hex-строка.\n- Парсер конфига строгий: неизвестные поля и мусорные значения в `amnezia.*`\n дают FATAL при старте, «молчаливого игнорирования» не существует.\n- Генерация Tachyon (`generator_outbounds.uc add_awg_endpoint`) совпадает со\n схемой форка: peer содержит required `allowed_ips`, `pre_shared_key` и\n `persistent_keepalive_interval` опциональны, hex i1-i5 проходит\n `uci_bin_to_hex` без двойного кодирования.\n\nСледствие для диагностики: если AWG/WARP-туннель «TX есть, RX нет» на\nsing-box-extended, конфиг секции применён корректно — причина вне Tachyon\n(сервер, провайдер, UDP-фильтрация). Чек 6c доктора (1.2.91+) сообщает это\nпользователю по свидетельствам из лога ядра.\n\nКейс «kernel работает, sing-box нет» с идентичным conf остаётся внешним:\nразличие в реализации special-handshake между amneziawg-linux kernel-модом\nи userspace-форком может быть критично только для конкретных строгих\nсерверов. Шаги диагностики для таких случаев:\n\n1. `tcpdump -i any -X udp port <порт>` — сравнить первые байты I1-пакетов", "embedding": []}, {"id": 14, "file": "04_DPI_BYPASS_ENGINES.md", "heading": "4. AmneziaWG в sing-box-extended: верификация (2026-08)", "text": "различие в реализации special-handshake между amneziawg-linux kernel-модом\nи userspace-форком может быть критично только для конкретных строгих\nсерверов. Шаги диагностики для таких случаев:\n\n1. `tcpdump -i any -X udp port <порт>` — сравнить первые байты I1-пакетов\n kernel vs sing-box (у special-handshake пакетов префикс `c7`).\n2. Временно убрать i1-i5 из секции: если handshake поднимется — сервер\n не требует special handshake, а требует его некорректную передачу.\n3. Попробовать вариант ядра sing-box-lx как эксперимент.", "embedding": []}, {"id": 15, "file": "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "heading": "3. Local Rule Doctor & 13 Quick Fix Codes", "text": "# 05. AI Stack, AI Doctor & Autonomous Self-Healing\n\n## 1. Overview of Tachyon AI Stack\n\nTachyon provides a dual-layer AI and autonomous resilience stack:\n1. **Autonomous Layer (Local Rule Doctor & Watchdog)**: Runs locally on the router every 5–15 seconds, detects network drops, memory leaks, DNS deadlocks, and time desync without requiring internet access or API tokens.\n2. **Cognitive Layer (AI Doctor v2.5)**: Deep diagnostics powered by LLMs (OpenAI, Anthropic Claude, DeepSeek, or local Ollama/OpenRouter models) with contextual prompt enrichment and automated Multi-Fix generation.\n3. **Agent Integration Layer (HTTP REST Agent API & OpenAPI 3.0)**: Programmatic interface for autonomous AI agents (Cursor, Claude Code, AutoGPT, ChatGPT Custom GPTs, N8N, Dify).\n\n---\n\n## 2. AI Doctor Architecture (v2.5)\n\n```mermaid\ngraph TD\n subgraph Data Collection\n R1[Process Probes: singbox/zapret/dnsmasq]\n R2[Kernel Metrics: conntrack, mem, uptime]\n R3[Watchdog History: failure streaks, OOM events]\n R4[System Logs: 15 most recent critical lines]\n end\n\n R1 --> Aggregator[snapshot.uc Assembler]\n R2 --> Aggregator\n R3 --> Aggregator\n R4 --> Aggregator\n\n Aggregator --> Prompt[Deep Context LLM Prompt]\n Prompt --> LLM[LLM Provider: OpenAI / Claude / DeepSeek / Custom]\n \n LLM --> Verdict[Structured Diagnosis Report]\n LLM --> MultiFix[Multi-Fix Action Plan: FIX: code1, code2]\n \n MultiFix --> Exec[Local Rule Doctor Execution Engine]\n Exec --> Result[Applied Fixes & Service State Refresh]\n```\n\n---\n\n## 3. Local Rule Doctor & 13 Quick Fix Codes\n\nThe execution engine in `diagnostics/runtime.uc` maps standardized fix codes to automated shell/ucode repair procedures:\n\n| Code | Target Subsystem | Action Performed |\n|---|---|---|\n| `start_singbox` | sing-box | Restarts failed sing-box process via procd. |\n| `rebuild_rules` | nftables | Flushes and recompiles `inet TachyonTable` ruleset. |\n| `fix_dnsmasq` | dnsmasq | Restarts dnsmasq and checks `/tmp/etc/dnsmasq.conf.*`. |", "embedding": []}, {"id": 16, "file": "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "heading": "4.1. Authentication Model", "text": "| `start_singbox` | sing-box | Restarts failed sing-box process via procd. |\n| `rebuild_rules` | nftables | Flushes and recompiles `inet TachyonTable` ruleset. |\n| `fix_dnsmasq` | dnsmasq | Restarts dnsmasq and checks `/tmp/etc/dnsmasq.conf.*`. |\n| `fix_resolv_symlink` | DNS | Restores broken `/etc/resolv.conf` -> `/tmp/resolv.conf.d/resolv.conf.auto` link. |\n| `start_watchdog` | Watchdog | Restarts the background watchdog supervisor process. |\n| `restart_singbox_dns` | sing-box DNS | Resets internal sing-box DNS server module and cache. |\n| `fix_uci_config` | UCI | Restores `/etc/config/tachyon` from the latest valid atomic backup. |\n| `fix_wan_interface` | Networking | Runs `ifup wan` to bounce the primary uplink interface. |\n| `fix_gateway` | Networking | Re-establishes default routing gateway via `/etc/init.d/network restart`. |\n| `clear_dns_cache` | Cache | Flushes dnsmasq cache, deletes sing-box `cache.db`, and restarts DNS. |\n| `update_subscriptions`| Subscriptions | Triggers asynchronous background download and refresh of proxy subscriptions. |\n| `reset_firewall` | Firewall | Full restart of OpenWrt firewall (`/etc/init.d/firewall restart`). |\n| `restart_network` | Network Stack | Full restart of OpenWrt network daemon (`/etc/init.d/network restart`). |\n| `fix_system_time` | System NTP | Resynchronizes router clock via NTP pool servers (`ntpd -q -p ...`). |\n| `flush_conntrack` | Kernel | Flushes congested `nf_conntrack` table during connection floods. |\n| `fix_bootstrap_dns` | Resolver | Unblocks sing-box primary bootstrap resolvers and breaks circular loops. |\n| `optimize_mtu` | Tunnels | Auto-calculates optimal MTU (1280–1420) for WireGuard / AmneziaWG interfaces. |\n| `restore_native_internet` | Emergency | One-click full teardown of Tachyon proxying; returns pure native WAN routing. |\n\n---\n\n## 4. HTTP REST Agent API & OpenAPI 3.0.3\n\nTachyon exposes a standardized REST API hosted under `/cgi-bin/tachyon-agent/` via OpenWrt's native `uhttpd`.\n\n### 4.1. Authentication Model", "embedding": []}, {"id": 17, "file": "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "heading": "4.3. Full Endpoint Index", "text": "| `restore_native_internet` | Emergency | One-click full teardown of Tachyon proxying; returns pure native WAN routing. |\n\n---\n\n## 4. HTTP REST Agent API & OpenAPI 3.0.3\n\nTachyon exposes a standardized REST API hosted under `/cgi-bin/tachyon-agent/` via OpenWrt's native `uhttpd`.\n\n### 4.1. Authentication Model\n* **Read-Only Endpoints**: Accessible within LAN without authentication (or secured via firewall).\n* **Write Endpoints**: Require Bearer token authentication:\n ```http\n POST /cgi-bin/tachyon-agent/ai-doctor/fix\n Authorization: Bearer \n Content-Type: application/json\n\n {\"fix\": \"clear_dns_cache,start_singbox\"}\n ```\n\n### 4.2. OpenAPI 3.0.3 Specification\n* The live OpenAPI JSON schema is available at:\n ```\n GET /cgi-bin/tachyon-agent/openapi.json\n ```\n* This schema allows instant import into **ChatGPT Custom GPT Actions**, **N8N HTTP Request Nodes**, **Dify**, and **Flowise**.\n\n### 4.3. Full Endpoint Index\n\n| Method | Endpoint | Auth Required | Description |\n|---|---|---|---|\n| `GET` | `/health` | No | Lightweight JSON health status probe. |\n| `GET` | `/snapshot` | No | Full system snapshot (interfaces, memory, proxy status). |\n| `GET` | `/diagnose` | No | Local Rule Doctor diagnosis report. |\n| `GET` | `/logs` | No | Returns last 100 lines of Tachyon system log. |\n| `GET` | `/config` | No | Returns sanitized UCI configuration. |\n| `GET` | `/tools` | No | OpenAI Function Calling / MCP tool definitions. |\n| `GET` | `/openapi.json` | No | OpenAPI 3.0.3 specification JSON. |\n| `POST`| `/heal` | **Bearer** | Triggers manual Watchdog self-healing cycle. |\n| `POST`| `/ai-doctor/fix` | **Bearer** | Applies one or more quick-fix codes. |\n| `POST`| `/restart` | **Bearer** | Restarts specified service (`singbox`, `watchdog`, `dnsmasq`). |\n| `POST`| `/reload` | **Bearer** | Soft-reloads configuration and routing tables. |\n| `POST`| `/config/set` | **Bearer** | Updates specific UCI options. |\n| `POST`| `/section/toggle` | **Bearer** | Enables or disables a routing rule section. |", "embedding": []}, {"id": 18, "file": "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "heading": "4.3. Full Endpoint Index", "text": "| `POST`| `/reload` | **Bearer** | Soft-reloads configuration and routing tables. |\n| `POST`| `/config/set` | **Bearer** | Updates specific UCI options. |\n| `POST`| `/section/toggle` | **Bearer** | Enables or disables a routing rule section. |\n| `POST`| `/domain/add` | **Bearer** | Appends domain pattern to target rule list. |", "embedding": []}, {"id": 19, "file": "06_FRONTEND_LUCI_TYPESCRIPT.md", "heading": "3.1. Key Services (`src/tachyon/services/`)", "text": "# 06. Frontend Architecture (LuCI & TypeScript)\n\n## 1. Frontend Architecture Overview\n\nTachyon's web UI is authored as a single-page application in **TypeScript** under `fe-app-tachyon/` and compiled into a standalone JavaScript view under `luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js`.\n\n```\nfe-app-tachyon/ (TypeScript Source)\n │\n ▼ (npm run build: tsc --noEmit && tsup)\n │\n[tsup build + baseclass.extend transformation]\n │\n ▼\nluci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js (LuCI Asset)\n```\n\n---\n\n## 2. The LuCI `baseclass.extend()` Patch Mechanism\n\nLuCI uses a custom asynchronous class system (`baseclass.extend({...})`) rather than standard ES modules or Webpack bundles.\n\n* `fe-app-tachyon/tsup.config.ts` compiles the TypeScript entry point (`src/main.ts`) into an ES module.\n* A custom post-processing step inside `tsup.config.ts` transforms the compiled file:\n 1. Strips ESM `export default` statements.\n 2. Wraps the controller inside LuCI's expected `return baseclass.extend({...})` pattern.\n 3. Injects global LuCI helper references (`ui`, `rpc`, `uci`, `fs`, `_`).\n\n> [!IMPORTANT]\n> **Do not attempt to convert `main.js` back into a standard ES module export.** This LuCI bridge is intentional and strictly required for compatibility with all OpenWrt web themes (Bootstrap, Material, Argon, OpenWrt-Two).\n\n---\n\n## 3. Reactive State Management & Services\n\nThe frontend does not use heavy external frameworks (like React or Vue) to keep package size minimal ($\\approx$ 500 KB uncompressed, $\\approx$ 90 KB gzipped). Instead, it uses lightweight TypeScript observable stores:\n\n### 3.1. Key Services (`src/tachyon/services/`)\n\n| Service | File | Purpose |\n|---|---|---|\n| `StoreService` | `store.service.ts` | Central reactive data store with subscription listeners. |\n| `UiStateService` | `uiState.service.ts` | Tracks UI view state, tab switching, and modal states. |\n| `RuntimeUiStateService`| `runtimeUiState.service.ts`| Polling and caching of backend service metrics and active jobs. |", "embedding": []}, {"id": 20, "file": "06_FRONTEND_LUCI_TYPESCRIPT.md", "heading": "4.1. The Updates & Installation Modal (`renderUpdateProgressModal.ts`)", "text": "| `UiStateService` | `uiState.service.ts` | Tracks UI view state, tab switching, and modal states. |\n| `RuntimeUiStateService`| `runtimeUiState.service.ts`| Polling and caching of backend service metrics and active jobs. |\n| `SocketService` | `socket.service.ts` | WebSocket client for real-time log streaming when available. |\n| `TachyonLogWatcher` | `tachyonLogWatcher.service.ts`| Logread stream deduplication and notification dispatcher. |\n\n---\n\n## 4. Tabs & UI View Structure\n\n```\nsrc/tachyon/tabs/\n├── dashboard/ # Live status, RTT ping matrix, traffic toggles\n├── diagnostic/ # AI Doctor modal, Local Rule Doctor, Emergency Fallback\n├── rules/ # Selective routing rules, domain/IP lists, client matchers\n├── providers/ # Zapret v1, Zapret v2, ByeDPI configurations\n├── updates/ # Component installer, version checks, streaming log modal\n└── settings/ # DNS settings, Telegram bot credentials, AI Doctor API keys\n```\n\n### 4.1. The Updates & Installation Modal (`renderUpdateProgressModal.ts`)\n* Provides a streaming log console that polls `/usr/bin/tachyon` output asynchronously via `componentActionLog(jobId, offset)`.\n* Features:\n * Non-blocking operation: router performs installation in background.\n * Real-time timer and progress badge.\n * Preserves output logs and green success/red error banners.\n * Prevents premature auto-close so users can inspect package manager details.\n * Graceful countdown timer for Tachyon package reloads.", "embedding": []}, {"id": 21, "file": "07_BUILD_TESTS_AND_DEPLOYMENT.md", "heading": "3.1. Frontend Unit Tests (vitest)", "text": "# 07. Build System, Testing & Deployment Pipeline\n\n## 1. Release Package Compilation (`build.sh`)\n\nTachyon produces both legacy **IPK** (opkg) and modern **APK** (apk-tools, OpenWrt 25.x+) packages.\n\n### 1.1. Running the Build\n\n```bash\nbash build.sh [output-dir]\n```\n\n* **Semver Requirement**: `build.sh` strictly enforces semantic versioning (`x.y.z`).\n* **Cross-Filesystem WSL Protection**: When executed under Windows Subsystem for Linux (WSL), `build.sh` automatically clones the source repository into the native Linux filesystem (`/tmp/tachyon-build-...`) before packaging to avoid Windows filesystem metadata and permission corruption.\n* **Outputs**:\n * `dist/release-final/tachyon__all.ipk`\n * `dist/release-final/luci-app-tachyon__all.ipk`\n * `dist/release-final/tachyon-.apk`\n * `dist/release-final/luci-app-tachyon-.apk`\n\n---\n\n## 2. End-User Installer (`install.sh`)\n\nTachyon provides an automated one-liner installer:\n\n```bash\nsh <(wget -O - https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh)\n```\n\n### 2.1. Installer Lifecycle\n1. **Architecture & OS Detection**: Detects OpenWrt version (23.05, 24.10, 25.x, SNAPSHOT), CPU architecture (`mips`, `mipsel`, `arm_cortex-a7`, `aarch64_cortex-a53`, `x86_64`), and package manager (`opkg` vs `apk`).\n2. **Dependency Resolution**: Ensures `ucode`, `ucode-mod-fs`, `ucode-mod-uci`, `ucode-mod-ubus`, `kmod-nft-tproxy`, `kmod-nft-queue`, `ca-bundle` are installed.\n3. **Legacy Migration**: Automatically detects `/etc/config/forkop` or `/etc/config/podkop` and seamlessly migrates all rules and servers to `/etc/config/tachyon`.\n4. **Binary Provisioning**: Downloads pre-compiled, optimized sing-box, Zapret, or ByeDPI binaries tailored for the router's target architecture.\n5. **Zero-Reboot Initialization**: Configures UCI defaults, registers LuCI menus, builds initial rulesets, and starts services without requiring a full router reboot.\n\n---\n\n## 3. Testing Workflows\n\n### 3.1. Frontend Unit Tests (vitest)", "embedding": []}, {"id": 22, "file": "07_BUILD_TESTS_AND_DEPLOYMENT.md", "heading": "4. Key Developer Gotchas & Conventions", "text": "5. **Zero-Reboot Initialization**: Configures UCI defaults, registers LuCI menus, builds initial rulesets, and starts services without requiring a full router reboot.\n\n---\n\n## 3. Testing Workflows\n\n### 3.1. Frontend Unit Tests (vitest)\n\n```bash\ncd fe-app-tachyon\nnpm test # Runs all 522+ vitest unit tests\nnpm run build # Verifies TypeScript compilation and LuCI bundle packaging\n```\n\n### 3.2. Backend Integration Tests & ucode Syntax Linting\n\nBackend tests require the `ucode` binary. On development machines without local ucode, tests run inside the Docker CI image:\n\n```bash\n# 1. ucode syntax check across all 60+ modules\ndocker run --rm -v \"$(pwd):/mnt\" -w /mnt ucode-ci-img:latest bash tests/ucode_syntax_lint.sh\n\n# 2. Run all backend behavioral and unit tests in parallel (-P4)\ndocker run --rm -v \"$(pwd):/mnt\" -w /mnt ucode-ci-img:latest bash tests/run_all.sh\n\n# 3. Run a specific test\ndocker run --rm -v \"$(pwd):/mnt\" -w /mnt ucode-ci-img:latest bash tests/ai_doctor_local.sh\n```\n\n### 3.3. ShellCheck Linting\n\n```bash\nshellcheck --severity=error build.sh install.sh tachyon/files/etc/init.d/* tests/**/*.sh\n```\n\n---\n\n## 4. Key Developer Gotchas & Conventions\n\n1. **LF Line Endings**: Enforced via `.gitattributes`. Committing CRLF will break shell scripts on OpenWrt target systems.\n2. **`fe-app-tachyon` Package Manager**: Always use `npm` (`npm install`, `npm test`, `npm run build`), not `yarn`.\n3. **Version Placeholder**: `__COMPILED_VERSION_VARIABLE__` in `src/constants.ts` is replaced during `build.sh` packaging. Do not remove it.\n4. **UCI Config Permissions**: `/etc/config/tachyon` must remain `chmod 600` because it can store sensitive Telegram bot tokens and API keys.", "embedding": []}, {"id": 23, "file": "08_UCI_CONFIGURATION_SCHEMA.md", "heading": "1. Section: `config settings 'settings'`", "text": "# 08. Complete UCI Configuration Schema Reference\n\nThe primary configuration for Tachyon is stored at `/etc/config/tachyon` (UCI format, mode `0600`).\n\n---\n\n## 1. Section: `config settings 'settings'`\n\nGlobal settings defining service behaviour, DNS resolution, watchdog parameters, Telegram integration, and AI Doctor.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | `boolean` (`0`/`1`) | `1` | Master toggle for Tachyon service. |\n| `log_level` | `string` | `info` | Logging verbosity: `debug`, `info`, `warn`, `error`. |\n| `dns_mode` | `string` | `doh` | DNS routing mode: `direct`, `doh`, `dot`, `udp`, `hosts_only`. |\n| `remote_dns_server` | `string` | `https://1.1.1.1/dns-query` | Upstream secure DNS resolver for proxied domains. |\n| `direct_dns_server` | `string` | `77.88.8.8` | Fallback DNS resolver for direct / local domains. |\n| `tproxy_port` | `integer` | `10080` | Local TCP/UDP transparent proxy port for sing-box. |\n| `dns_port` | `integer` | `5353` | Local DNS listening port for sing-box DNS engine. |\n| `clash_api_port` | `integer` | `9090` | sing-box Clash REST API controller port. |\n| `clash_api_secret` | `string` | `\"\"` | Secret token for sing-box Clash API. |\n| `watchdog_interval` | `integer` | `10` | Watchdog polling interval in seconds (5–60s). |\n| `memory_limit_mb` | `integer` | `0` | Dynamic `GOMEMLIMIT` cap in MB (`0` = auto 50% free RAM). |\n| `enable_telegram` | `boolean` | `0` | Enable interactive Telegram management bot. |\n| `telegram_token` | `string` | `\"\"` | Telegram Bot API token from `@BotFather`. |\n| `telegram_chat_id` | `string` | `\"\"` | Allowed Admin Telegram Chat ID(s) (comma-separated). |\n| `enable_ai_doctor` | `boolean` | `0` | Enable LLM AI Doctor diagnostic engine. |\n| `ai_doctor_provider`| `string` | `openai` | LLM Provider: `openai`, `anthropic`, `deepseek`, `custom`. |\n| `ai_doctor_model` | `string` | `\"\"` | Model override (e.g. `gpt-4o-mini`, `claude-3-5-haiku`). |\n| `ai_doctor_api_key`| `string` | `\"\"` | API key for the chosen LLM provider. |", "embedding": []}, {"id": 24, "file": "08_UCI_CONFIGURATION_SCHEMA.md", "heading": "3. Section: `config subscription 'name'`", "text": "| `ai_doctor_provider`| `string` | `openai` | LLM Provider: `openai`, `anthropic`, `deepseek`, `custom`. |\n| `ai_doctor_model` | `string` | `\"\"` | Model override (e.g. `gpt-4o-mini`, `claude-3-5-haiku`). |\n| `ai_doctor_api_key`| `string` | `\"\"` | API key for the chosen LLM provider. |\n| `ai_doctor_custom_url`| `string` | `\"\"` | Custom OpenAI-compatible endpoint URL (e.g. OpenRouter/Ollama). |\n| `agent_api_token` | `string` | `\"\"` | Bearer token for HTTP REST Agent API write access. |\n\n---\n\n## 2. Section: `config server 'name'`\n\nDefines an individual manual proxy outbound node.\n\n| Option | Type | Required | Description |\n|---|---|---|---|\n| `type` | `string` | Yes | Protocol: `vless`, `vmess`, `shadowsocks`, `trojan`, `hysteria2`, `wireguard`. |\n| `server` | `string` | Yes | Remote server hostname or IPv4/IPv6 address. |\n| `server_port` | `integer` | Yes | Remote server port (e.g. `443`, `8443`). |\n| `uuid` | `string` | Conditional | User UUID for VLESS / VMess. |\n| `password` | `string` | Conditional | Password for Shadowsocks / Trojan / Hysteria2. |\n| `flow` | `string` | No | Flow control: `xtls-rprx-vision` (VLESS). |\n| `security` | `string` | No | Security type: `reality`, `tls`, `none`. |\n| `sni` | `string` | Conditional | Server Name Indication for TLS / Reality. |\n| `pbk` | `string` | Conditional | Public Key for VLESS Reality. |\n| `sid` | `string` | Conditional | Short ID for VLESS Reality (hex string). |\n| `fingerprint` | `string` | No | TLS fingerprint: `chrome`, `firefox`, `safari`, `randomized`. |\n| `method` | `string` | Conditional | Cipher method for Shadowsocks (e.g. `2022-blake3-aes-128-gcm`). |\n\n---\n\n## 3. Section: `config subscription 'name'`\n\nDefines a remote subscription URL for automated batch node fetching.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | `boolean` | `1` | Enable this subscription source. |\n| `url` | `string` | Required | HTTPS / HTTP link to subscription (Base64, Clash, SIP002). |\n| `update_interval`| `integer` | `86400` | Auto-update interval in seconds (default 24 hours). |", "embedding": []}, {"id": 25, "file": "08_UCI_CONFIGURATION_SCHEMA.md", "heading": "Recipe A: Zero-VPS Local DPI Bypass (YouTube + Discord)", "text": "| `enabled` | `boolean` | `1` | Enable this subscription source. |\n| `url` | `string` | Required | HTTPS / HTTP link to subscription (Base64, Clash, SIP002). |\n| `update_interval`| `integer` | `86400` | Auto-update interval in seconds (default 24 hours). |\n| `custom_user_agent`| `string` | `\"\"` | Custom User-Agent header for subscription request. |\n| `filter_keywords` | `list string` | `[]` | Include nodes matching regex/keywords. |\n| `exclude_keywords`| `list string` | `[]` | Exclude nodes matching regex/keywords. |\n\n---\n\n## 4. Section: `config rule 'name'`\n\nDefines a selective routing policy.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | `boolean` | `1` | Enable this routing rule. |\n| `name` | `string` | `\"\"` | Human-readable label for UI. |\n| `action` | `string` | `proxy` | Route target: `proxy`, `zapret`, `zapret2`, `byedpi`, `direct`, `hosts`. |\n| `target_server` | `string` | `\"\"` | Bound server ID or outbound tag when `action = 'proxy'`. |\n| `domain_pattern` | `list string` | `[]` | List of target domains or wildcards (e.g. `*.youtube.com`). |\n| `ip_cidr` | `list string` | `[]` | List of IP subnets (e.g. `104.16.0.0/12`). |\n| `client_group` | `string` | `\"\"` | Restrict this rule to a specific client device group. |\n\n---\n\n## 5. Section: `config hosts 'name'`\n\nConfigures static DNS overrides and remote blocklists ingestion.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | `boolean` | `1` | Enable hosts mapping. |\n| `dns_hosts` | `list string` | `[]` | Static mappings in format `domain:ip` (e.g. `example.com:1.2.3.4`). |\n| `hosts_list_urls` | `list string` | `[]` | External URLs for blocklists (AdAway, StevenBlack). |\n| `update_interval` | `integer` | `86400` | Auto-refresh interval in seconds. |\n\n---\n\n## 6. Ready-to-Use Configuration Recipes\n\n### Recipe A: Zero-VPS Local DPI Bypass (YouTube + Discord)\n```ini\nconfig settings 'settings'\n option enabled '1'\n option dns_mode 'doh'\n\nconfig provider 'zapret2'", "embedding": []}, {"id": 26, "file": "08_UCI_CONFIGURATION_SCHEMA.md", "heading": "Recipe B: Hybrid Mode (Local Desync for Media + VLESS Reality for Blocked Sites)", "text": "## 6. Ready-to-Use Configuration Recipes\n\n### Recipe A: Zero-VPS Local DPI Bypass (YouTube + Discord)\n```ini\nconfig settings 'settings'\n option enabled '1'\n option dns_mode 'doh'\n\nconfig provider 'zapret2'\n option enabled '1'\n option daemon_bin '/usr/bin/nfqws2'\n option qnum '201'\n option args_tcp '--dpi-desync=multisplit --dpi-desync-split-pos=1,midsld --dpi-desync-seqovl=1 --dpi-desync-fooling=badseq'\n\nconfig rule 'youtube_discord'\n option enabled '1'\n option name 'YouTube & Discord Local Desync'\n option action 'zapret2'\n list domain_pattern 'googlevideo.com'\n list domain_pattern 'youtube.com'\n list domain_pattern 'ytimg.com'\n list domain_pattern 'discord.com'\n list domain_pattern 'discord.gg'\n```\n\n### Recipe B: Hybrid Mode (Local Desync for Media + VLESS Reality for Blocked Sites)\n```ini\nconfig server 'my_vps'\n option type 'vless'\n option server 'vps.example.com'\n option server_port '443'\n option uuid '12345678-1234-1234-1234-123456789abc'\n option flow 'xtls-rprx-vision'\n option security 'reality'\n option sni 'yahoo.com'\n option pbk 'XYZ...PUBLIC_KEY...'\n option sid '1a2b3c4d'\n\nconfig rule 'media_rule'\n option enabled '1'\n option action 'zapret2'\n list domain_pattern 'youtube.com'\n list domain_pattern 'googlevideo.com'\n\nconfig rule 'blocked_rule'\n option enabled '1'\n option action 'proxy'\n option target_server 'my_vps'\n list domain_pattern 'instagram.com'\n list domain_pattern 'twitter.com'\n list domain_pattern 'x.com'\n```", "embedding": []}, {"id": 27, "file": "09_TELEGRAM_BOT_ARCHITECTURE.md", "heading": "2. Command Set Reference", "text": "# 09. Telegram Bot Architecture & Management Protocol\n\nTachyon includes an embedded, high-performance Telegram management bot written entirely in **ucode** (`service/telegram.uc`).\n\n---\n\n## 1. Architectural Highlights\n\n```mermaid\ngraph LR\n User[Telegram Client] -->|Encrypted HTTPS| TGAPI[Telegram Bot API Servers]\n TGAPI -->|Long Polling: getUpdates| Daemon[Tachyon telegram.uc Daemon]\n \n Daemon --> Auth{Admin Chat ID Match?}\n Auth -->|No| Reject[Silent Ignore / Warning]\n Auth -->|Yes| Router[Command & Callback Dispatcher]\n \n Router -->|/doctor, /ai_doctor| AIDoc[AI Doctor Engine]\n Router -->|/server, /switch| SrvMgr[Server Manager]\n Router -->|/restart| SvcLife[Service Lifecycle]\n Router -->|/status| StateRep[State Assembler]\n```\n\n* **No Public IP or Port Forwarding Required**: Uses outbound long-polling (`getUpdates` with 30s timeout) to securely communicate through NAT/firewalls.\n* **Zero Overhead Memory Footprint**: Runs as a lightweight ucode process consuming under 2 MB RAM.\n* **Role-Based Access Control**: Rejects any commands from chat IDs not explicitly listed in UCI option `telegram_chat_id`.\n\n---\n\n## 2. Command Set Reference\n\n| Command | Arguments | Action |\n|---|---|---|\n| `/start` | — | Displays main welcome card and interactive control keyboard. |\n| `/status` | — | Returns real-time health card: uptime, active server, RAM usage, ping RTT. |\n| `/doctor` | — | Executes Local Rule Doctor and returns offline diagnostic report. |\n| `/ai_doctor`| — | Invokes AI Doctor with LLM reasoning and returns diagnosis + fix buttons. |\n| `/fix` | `` | Applies specific automated repair code (e.g. `/fix clear_dns_cache`). |\n| `/server` | — | Opens interactive server selector with inline pagination and RTT pings. |\n| `/rules` | — | Shows active routing rules with 1-click toggle switches. |\n| `/restart` | `[service]` | Restarts sing-box, dnsmasq, watchdog, or entire network stack. |\n| `/backup` | — | Sends latest atomic configuration backup directly to Telegram chat. |", "embedding": []}, {"id": 28, "file": "09_TELEGRAM_BOT_ARCHITECTURE.md", "heading": "Commit and restart service", "text": "| `/rules` | — | Shows active routing rules with 1-click toggle switches. |\n| `/restart` | `[service]` | Restarts sing-box, dnsmasq, watchdog, or entire network stack. |\n| `/backup` | — | Sends latest atomic configuration backup directly to Telegram chat. |\n\n---\n\n## 3. Inline Keyboard & Callback Query Protocol\n\nThe bot uses dynamic inline keyboards (`InlineKeyboardMarkup`) for single-tap actions without typing commands.\n\n### 3.1. Callback Data Scheme\nCallbacks follow a structured token format: `action:target:param`\n\n```\nsrv:select:node_1 # Switch active proxy server to node_1\nrule:toggle:youtube # Toggle routing rule 'youtube' on/off\nfix:apply:clear_dns_cache# Execute repair code 'clear_dns_cache'\npage:srv:2 # Navigate to page 2 of server list\n```\n\n### 3.2. Example: Server Switcher Response\n\n```json\n{\n \"chat_id\": 123456789,\n \"text\": \"⚡ Выберите активный сервер:\\nТекущий: 🇩🇪 Frankfurt VLESS (18 ms)\",\n \"parse_mode\": \"HTML\",\n \"reply_markup\": {\n \"inline_keyboard\": [\n [\n {\"text\": \"🇩🇪 Frankfurt (18 ms) ✅\", \"callback_data\": \"srv:select:de_1\"},\n {\"text\": \"🇳🇱 Amsterdam (24 ms)\", \"callback_data\": \"srv:select:nl_1\"}\n ],\n [\n {\"text\": \"🇫🇮 Helsinki (31 ms)\", \"callback_data\": \"srv:select:fi_1\"},\n {\"text\": \"🇺🇸 New York (95 ms)\", \"callback_data\": \"srv:select:us_1\"}\n ],\n [\n {\"text\": \"🔄 Замерить пинг\", \"callback_data\": \"srv:ping:all\"},\n {\"text\": \"🩺 Запустить Доктора\", \"callback_data\": \"cmd:doctor\"}\n ]\n ]\n }\n}\n```\n\n---\n\n## 4. Configuration via CLI\n\n```sh\n# Set Bot Token\nuci set tachyon.settings.enable_telegram='1'\nuci set tachyon.settings.telegram_token='123456789:ABCdefGHIjklMNOpqrsTUVwxyz'\n\n# Set Authorized Chat ID(s)\nuci set tachyon.settings.telegram_chat_id='123456789'\n\n# Commit and restart service\nuci commit tachyon\n/etc/init.d/tachyon restart\n```", "embedding": []}, {"id": 29, "file": "10_HOSTS_ENGINE_AND_BLOCKLISTS.md", "heading": "Example UCI Configuration:", "text": "# 10. Hosts Engine, Blocklists & Smart Caching\n\nThe Hosts Engine (`components/hosts.uc`) provides automated background ingestion, normalization, deduplication, and caching of external host lists and static DNS overrides.\n\n---\n\n## 1. Architecture of Hosts Engine\n\n```mermaid\ngraph TD\n Sources[External Remote URLs: AdAway, StevenBlack, Custom] --> Downloader[HTTP Fetcher with Mirror Retry]\n Static[Static dns_hosts in UCI] --> Downloader\n \n Downloader --> Parser[Multi-Format Parser: Hosts, AdBlock, Domains]\n Parser --> Deduper[Deduplication & Sorting Engine]\n Deduper --> Cache[Unified Cache: /tmp/tachyon/hosts/combined.txt]\n \n Cache --> DNSMasq[dnsmasq /etc/dnsmasq.d/tachyon-hosts.conf]\n Cache --> SingboxDNS[sing-box DNS hosts rule]\n```\n\n---\n\n## 2. Multi-Format Ingestion\n\nThe parser automatically detects and normalizes multiple common blocklist formats:\n\n1. **Standard `/etc/hosts` format**:\n ```\n 0.0.0.0 ads.example.com\n 127.0.0.1 tracker.adserver.net\n ```\n2. **Domain-only list format**:\n ```\n malware-domain.org\n telemetry.app.com\n ```\n3. **AdBlock / DNSmasq syntax**:\n ```\n ||badsite.com^\n server=/blocked.com/\n address=/ads.org/0.0.0.0\n ```\n\n---\n\n## 3. GitHub Mirror Retry Resiliency\n\nWhen fetching blocklists hosted on GitHub (e.g. `raw.githubusercontent.com`), access may be blocked or throttled by regional DPI.\n\nTachyon implements an automated fallback retry chain:\n1. **Primary**: Direct HTTPS request to target URL.\n2. **Mirror 1**: `https://gh-proxy.com/`\n3. **Mirror 2**: `https://ghfast.top/`\n4. **Mirror 3**: `https://ghproxy.net/`\n\nIf direct connection fails with timeout or SSL handshake error, Tachyon transparently cycles through available mirrors before reporting an error.\n\n---\n\n## 4. Dedicated Hosts Routing (`action = 'hosts'`)\n\nTachyon supports routing sections that operate purely as DNS-overriding engines without requiring an outbound proxy association.\n\n### Example UCI Configuration:", "embedding": []}, {"id": 30, "file": "10_HOSTS_ENGINE_AND_BLOCKLISTS.md", "heading": "Example UCI Configuration:", "text": "## 4. Dedicated Hosts Routing (`action = 'hosts'`)\n\nTachyon supports routing sections that operate purely as DNS-overriding engines without requiring an outbound proxy association.\n\n### Example UCI Configuration:\n```ini\nconfig rule 'adblock_section'\n option enabled '1'\n option name 'AdBlock & Anti-Tracker'\n option action 'hosts'\n list hosts_list_urls 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts'\n list hosts_list_urls 'https://adaway.org/hosts.txt'\n```\n\n* Domains in this section are redirected directly to `0.0.0.0` or custom IPs in dnsmasq / sing-box DNS.\n* Zero traffic is routed to external proxies, preserving VPS bandwidth and minimizing latency.", "embedding": []}, {"id": 31, "file": "11_TROUBLESHOOTING_AND_FIELD_MANUAL.md", "heading": "2. Run AI Doctor with LLM analysis", "text": "# 11. Troubleshooting & Field Diagnostic Manual\n\nA comprehensive guide for diagnosing and resolving common network, proxy, and routing issues on OpenWrt routers running Tachyon.\n\n---\n\n## 1. Quick Emergency Recovery\n\nIf router traffic drops completely or DNS resolution is broken, use the **Emergency Native Internet Restoration** command:\n\n```sh\ntachyon restore_native_internet\n```\n\nThis performs the following actions in under 200 ms:\n1. Atomically drops all nftables interception tables (`nft delete table inet TachyonTable`).\n2. Clears policy routing `table 100` and `ip rule` entries.\n3. Restores dnsmasq to stock upstream forwarders.\n4. Stops sing-box and DPI bypass daemons without rebooting the router.\n\n---\n\n## 2. Symptom Resolution Matrix\n\n| Symptom | Likely Cause | Recommended Fix / Command |\n|---|---|---|\n| **Websites fail to open, ping to 8.8.8.8 works** | DNS loop or dnsmasq forwarder crash. | `tachyon apply_quick_fix clear_dns_cache,fix_dnsmasq` |\n| **YouTube 4K buffers indefinitely** | Zapret strategy out-of-date for current ISP. | Switch Zapret v2 strategy to `multisplit` with `seqovl=1`. |\n| **sing-box fails to start after config change** | Syntax error in custom outbound or invalid UUID. | Check `/var/log/tachyon_service.log` and run `tachyon doctor`. |\n| **System clock desynchronized, TLS fails** | Router rebooted without RTC battery, NTP desync. | `tachyon apply_quick_fix fix_system_time` |\n| **High latency or packet drop under heavy load** | Kernel `nf_conntrack` table overflow. | `tachyon apply_quick_fix flush_conntrack` |\n| **VLESS Reality connection timeout** | Reality SNI domain blocked or expired short ID. | Update server SNI to accessible target (e.g. `yahoo.com`). |\n| **Router runs out of RAM (OOM Crash)** | sing-box buffer explosion on low-RAM device (128 MB). | Lower `memory_limit_mb` in UCI settings (`GOMEMLIMIT=40MiB`). |\n\n---\n\n## 3. Diagnostic Commands Reference\n\n```sh\n# 1. Run local offline rule checks\ntachyon doctor\n\n# 2. Run AI Doctor with LLM analysis", "embedding": []}, {"id": 32, "file": "11_TROUBLESHOOTING_AND_FIELD_MANUAL.md", "heading": "4. Log Inspection Paths", "text": "| **Router runs out of RAM (OOM Crash)** | sing-box buffer explosion on low-RAM device (128 MB). | Lower `memory_limit_mb` in UCI settings (`GOMEMLIMIT=40MiB`). |\n\n---\n\n## 3. Diagnostic Commands Reference\n\n```sh\n# 1. Run local offline rule checks\ntachyon doctor\n\n# 2. Run AI Doctor with LLM analysis\ntachyon ai_doctor\n\n# 3. View real-time Watchdog telemetry\ntachyon ai_status_full\n\n# 4. Inspect active nftables firewall tables\nnft list table inet TachyonTable\n\n# 5. Inspect policy routing table 100\nip route show table 100\nip rule show\n\n# 6. Stream live Tachyon system logs\nlogread -f -e tachyon\n\n# 7. Check listening ports (TProxy 10080, DNS 5353, Clash 9090)\nnetstat -tulpn | grep -E 'sing-box|tachyon|5353|10080'\n```\n\n---\n\n## 4. Log Inspection Paths\n\n| Log Location | Content Description |\n|---|---|\n| `/var/log/tachyon_service.log` | Core service lifecycle events (start, stop, reload). |\n| `/var/log/tachyon_watchdog.log` | Background supervisor health checks and loop warnings. |\n| `/tmp/ai_doctor_last.json` | JSON report of the most recent AI Doctor diagnosis. |\n| `/tmp/tachyon/sing-box.json` | Generated live configuration compiled for sing-box. |\n| `/tmp/tachyon/hosts/combined.txt` | Consolidated deduplicated hosts list. |", "embedding": []}, {"id": 33, "file": "12_UCODE_PROGRAMMING_PATTERNS.md", "heading": "2.3. Safe Error Handling (`try-catch`)", "text": "# 12. ucode Programming Patterns & Conventions\n\nA developer's guide to writing safe, high-performance, and idiomatic **ucode** modules for Tachyon on OpenWrt.\n\n---\n\n## 1. ucode vs JavaScript: Critical Distinctions\n\nWhile ucode syntax resembles JavaScript, its runtime semantics differ significantly:\n\n1. **Variables & Scope**:\n * Always declare variables with `let` or `const`.\n * Unassigned variables are `null` (not `undefined`).\n2. **Arrays vs Dictionaries**:\n * Arrays: `[1, 2, 3]`. Iterated with `for (let item in array)`.\n * Dictionaries (Objects): `{\"key\": \"value\"}`. Iterated with `for (let key, val in dict)`.\n3. **Regular Expressions**:\n * Use `match(str, /regex/)` or `replace(str, /regex/g, \"replacement\")`.\n * Global flag `/g` works with `replace()`, while `match()` returns capture groups array `[full_match, group1, ...]`.\n4. **Boolean Coercion**:\n * Empty strings `\"\"`, `0`, and `null` are falsy. Everything else is truthy.\n\n---\n\n## 2. Mandatory Core Design Patterns\n\n### 2.1. Atomic Configuration Writes\nNever overwrite active configuration files in-place. Always write to a temporary file in `/tmp/` and atomically rename:\n\n```ucode\nlet fs = require(\"fs\");\n\nfunction write_atomic(path, content) {\n let tmp_path = path + \".tmp.\" + int(time());\n let f = fs.open(tmp_path, \"w\");\n if (!f) return false;\n \n f.write(content);\n f.close();\n \n return fs.rename(tmp_path, path);\n}\n```\n\n### 2.2. Shell Argument Quoting\nWhen executing external shell commands, all user inputs must be quoted safely:\n\n```ucode\nfunction shell_quote(value) {\n return \"'\" + replace(\"\" + value, /'/g, \"'\\\\''\") + \"'\";\n}\n\nfunction safe_exec(cmd_args) {\n let parts = [];\n for (let arg in cmd_args)\n push(parts, shell_quote(arg));\n return system(join(\" \", parts));\n}\n```\n\n### 2.3. Safe Error Handling (`try-catch`)\nAll external library loading or file parsing must be wrapped in `try-catch` blocks to avoid unhandled script panics:\n\n```ucode\nlet uci_data = null;", "embedding": []}, {"id": 34, "file": "12_UCODE_PROGRAMMING_PATTERNS.md", "heading": "3. Performance & Memory Optimization Guidelines", "text": "return system(join(\" \", parts));\n}\n```\n\n### 2.3. Safe Error Handling (`try-catch`)\nAll external library loading or file parsing must be wrapped in `try-catch` blocks to avoid unhandled script panics:\n\n```ucode\nlet uci_data = null;\ntry {\n let uci = require(\"uci\").cursor();\n uci_data = uci.get_all(\"tachyon\", \"settings\");\n}\ncatch (e) {\n warn(\"Failed to load UCI settings: \" + e + \"\\n\");\n}\n```\n\n---\n\n## 3. Performance & Memory Optimization Guidelines\n\n1. **Stream Large Files**: Avoid loading multi-megabyte blocklists entirely into RAM with `fs.readfile()`. Use line-by-line streaming via `fs.open()` and `f.read(\"line\")`.\n2. **Avoid Spawning Subprocesses in Loops**: Invoking `system()` or `fs.popen()` spawns a fork-exec sequence in Linux. Use native ucode functions for string manipulation and path operations whenever possible.\n3. **Keep Modules Stateless**: Module files under `usr/lib/tachyon/` should export reusable pure functions or class definitions rather than maintaining mutable global state.", "embedding": []}, {"id": 35, "file": "13_REST_API_AND_MCP_SPECIFICATION.md", "heading": "4. Model Context Protocol (MCP) Tool Definitions", "text": "# 13. HTTP REST Agent API & Model Context Protocol (MCP) Specification\n\nTachyon provides a standardized RESTful Agent API and Model Context Protocol (MCP) integration, enabling seamless orchestration by autonomous LLM agents (ChatGPT Actions, Claude Code, Cursor, Windsurf, N8N, Dify).\n\n---\n\n## 1. REST API Overview\n\n* **Base URL**: `http:///cgi-bin/tachyon-agent/`\n* **OpenAPI Schema**: `http:///cgi-bin/tachyon-agent/openapi.json`\n* **Content-Type**: `application/json`\n\n---\n\n## 2. Authentication Model\n\n1. **Read-Only Endpoints**: Unrestricted when accessed from local LAN.\n2. **Write Endpoints**: Require HTTP Authorization header:\n ```http\n Authorization: Bearer \n ```\n\n---\n\n## 3. Endpoint Reference & Payloads\n\n### 3.1. `GET /health`\nReturns lightweight status of all core daemons.\n```json\n{\n \"status\": \"healthy\",\n \"version\": \"1.2.86\",\n \"uptime_seconds\": 18450,\n \"singbox_running\": true,\n \"watchdog_running\": true,\n \"active_server\": \"Frankfurt_VLESS_Reality\"\n}\n```\n\n### 3.2. `GET /diagnose`\nReturns Local Rule Doctor checks and suggested repair codes.\n```json\n{\n \"status\": \"warning\",\n \"checks\": {\n \"dns_loop\": false,\n \"conntrack_full\": false,\n \"time_desync\": true,\n \"singbox_ok\": true\n },\n \"suggested_fixes\": [\"fix_system_time\"]\n}\n```\n\n### 3.3. `POST /ai-doctor/fix`\nApplies one or more quick-fix codes.\n* **Request**:\n ```json\n {\n \"fix\": \"clear_dns_cache,start_singbox\"\n }\n ```\n* **Response**:\n ```json\n {\n \"success\": true,\n \"applied_fixes\": [\"clear_dns_cache\", \"start_singbox\"],\n \"details\": \"DNS cache cleared. sing-box process restarted.\"\n }\n ```\n\n---\n\n## 4. Model Context Protocol (MCP) Tool Definitions\n\nFor MCP-compatible agents (Cursor, Claude Desktop), Tachyon exports tools via `GET /cgi-bin/tachyon-agent/tools`:\n\n```json\n[\n {\n \"name\": \"tachyon_diagnose\",\n \"description\": \"Run diagnostic checks on OpenWrt router network and proxy health\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {}", "embedding": []}, {"id": 36, "file": "13_REST_API_AND_MCP_SPECIFICATION.md", "heading": "4. Model Context Protocol (MCP) Tool Definitions", "text": "```json\n[\n {\n \"name\": \"tachyon_diagnose\",\n \"description\": \"Run diagnostic checks on OpenWrt router network and proxy health\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {}\n }\n },\n {\n \"name\": \"tachyon_apply_fix\",\n \"description\": \"Apply automated repair codes to fix router network or proxy issues\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"fix_codes\": {\n \"type\": \"string\",\n \"description\": \"Comma-separated list of fix codes (e.g. clear_dns_cache,restart_singbox)\"\n }\n },\n \"required\": [\"fix_codes\"]\n }\n },\n {\n \"name\": \"tachyon_switch_server\",\n \"description\": \"Switch active proxy server node\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"server_id\": {\n \"type\": \"string\",\n \"description\": \"Identifier of target proxy server\"\n }\n },\n \"required\": [\"server_id\"]\n }\n }\n]\n```", "embedding": []}, {"id": 37, "file": "INDEX.md", "heading": "📚 Knowledge Base Table of Contents", "text": "# Tachyon Knowledge Base & Architecture Index\n\nWelcome to the comprehensive technical knowledge base for **Tachyon**. This directory contains deep architectural specifications, module references, packet flow maps, and developer guidelines compiled directly from the codebase.\n\n---\n\n## 📚 Knowledge Base Table of Contents\n\n| Document | Description | Key Topics Covered |\n|---|---|---|\n| [**01. System Architecture**](01_SYSTEM_ARCHITECTURE.md) | High-level system architecture & control plane | ucode runtime, atomic writes, repository layout, daemon lifecycle |\n| [**02. ucode Module Reference**](02_UCODE_MODULE_REFERENCE.md) | Comprehensive reference for all 60+ `.uc` files | `core/`, `service/`, `diagnostics/`, `singbox/`, `providers/`, `nft/`, `dns/` |\n| [**03. Networking & nftables**](03_NETWORKING_NFTABLES_AND_ROUTING.md) | Packet lifecycle, firewall tables & DNS routing | `inet TachyonTable`, fwmark bitmasks, TProxy, bootstrap DNS loops |\n| [**04. DPI Bypass Engines**](04_DPI_BYPASS_ENGINES.md) | Local packet desynchronization deep dive | Zapret v1 (`nfqws`), Zapret v2 (`nfqws2`), ByeDPI (`ciadpi`), multisplit, seqovl |\n| [**05. AI Stack & AI Doctor**](05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md) | AI Doctor v2.5, Watchdog & REST Agent API | 13 Quick Fixes, Local Rule Doctor, NTP/MTU tuning, OpenAPI 3.0.3, REST API |\n| [**06. Frontend LuCI Architecture**](06_FRONTEND_LUCI_TYPESCRIPT.md) | TypeScript SPA & LuCI view integration | `baseclass.extend` patch, reactive stores, live log streaming modal |\n| [**07. Build & Testing Pipeline**](07_BUILD_TESTS_AND_DEPLOYMENT.md) | Compilation, testing & release workflows | `build.sh` (ipk/apk), `install.sh`, Docker CI container, vitest tests |\n| [**08. UCI Configuration Schema**](08_UCI_CONFIGURATION_SCHEMA.md) | Complete `/etc/config/tachyon` reference | All UCI sections, options, validators, default values, ready recipes |\n| [**09. Telegram Bot Architecture**](09_TELEGRAM_BOT_ARCHITECTURE.md) | Remote Telegram control daemon protocol | Long-polling model, inline keyboards, callback query routing, security |", "embedding": []}, {"id": 38, "file": "INDEX.md", "heading": "Frontend vitest suite", "text": "| [**08. UCI Configuration Schema**](08_UCI_CONFIGURATION_SCHEMA.md) | Complete `/etc/config/tachyon` reference | All UCI sections, options, validators, default values, ready recipes |\n| [**09. Telegram Bot Architecture**](09_TELEGRAM_BOT_ARCHITECTURE.md) | Remote Telegram control daemon protocol | Long-polling model, inline keyboards, callback query routing, security |\n| [**10. Hosts Engine & Blocklists**](10_HOSTS_ENGINE_AND_BLOCKLISTS.md) | Hosts ingestion, parsing & smart caching | `combined.txt`, multi-format parsing, GitHub mirror retry resiliency |\n| [**11. Troubleshooting Manual**](11_TROUBLESHOOTING_AND_FIELD_MANUAL.md) | Field diagnostics & emergency recovery | Symptom resolution matrix, diagnostic commands, log inspection paths |\n| [**12. ucode Programming Patterns**](12_UCODE_PROGRAMMING_PATTERNS.md) | Developer guidelines for OpenWrt ucode | Atomic file writes, shell quoting, libuci bindings, RAM efficiency |\n| [**13. REST API & MCP Specification**](13_REST_API_AND_MCP_SPECIFICATION.md) | REST Agent API & MCP tool definitions | OpenAPI 3.0.3, Bearer Auth, OpenAI Function Calling, MCP JSON schema |\n\n---\n\n## ⚡ Quick Operational Cheat Sheet\n\n### Common CLI Commands\n```sh\n# Diagnostics & AI\ntachyon doctor # Local offline rule diagnostics\ntachyon ai_doctor # AI Doctor LLM analysis\ntachyon apply_quick_fix # Apply automated repair codes\ntachyon restore_native_internet # 1-Click emergency WAN restore\n\n# Service & Watchdog\ntachyon ai_heal # Trigger immediate self-healing cycle\ntachyon ai_status # Concise Watchdog status JSON\ntachyon ai_status_full # Full Watchdog telemetry JSON\n\n# Component Management\ntachyon check_update # Check for component updates\ntachyon component_action install # Install or update component\n```\n\n### Local Test Execution\n```sh\n# Frontend vitest suite\nnpm --prefix fe-app-tachyon test", "embedding": []}, {"id": 39, "file": "INDEX.md", "heading": "Full backend suite in Docker CI", "text": "tachyon check_update # Check for component updates\ntachyon component_action install # Install or update component\n```\n\n### Local Test Execution\n```sh\n# Frontend vitest suite\nnpm --prefix fe-app-tachyon test\n\n# Full backend suite in Docker CI\ndocker run --rm -v \"$(pwd):/mnt\" -w /mnt ucode-ci-img:latest bash tests/run_all.sh\n```", "embedding": []}]} \ No newline at end of file +{"version": 1, "chunk_size": 2000, "chunk_overlap": 200, "total_chunks": 40, "files": ["01_SYSTEM_ARCHITECTURE.md", "02_UCODE_MODULE_REFERENCE.md", "03_NETWORKING_NFTABLES_AND_ROUTING.md", "04_DPI_BYPASS_ENGINES.md", "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "06_FRONTEND_LUCI_TYPESCRIPT.md", "07_BUILD_TESTS_AND_DEPLOYMENT.md", "08_UCI_CONFIGURATION_SCHEMA.md", "09_TELEGRAM_BOT_ARCHITECTURE.md", "10_HOSTS_ENGINE_AND_BLOCKLISTS.md", "11_TROUBLESHOOTING_AND_FIELD_MANUAL.md", "12_UCODE_PROGRAMMING_PATTERNS.md", "13_REST_API_AND_MCP_SPECIFICATION.md", "INDEX.md"], "chunks": [{"id": 0, "file": "01_SYSTEM_ARCHITECTURE.md", "heading": "2.2. Deterministic State & Atomic Writes", "text": "# 01. System Architecture & Core Concepts\n\n## 1. High-Level Overview\n\n**Tachyon** is an enterprise-grade, lightweight traffic orchestration, multi-protocol proxying, and DPI anti-censorship platform built natively for **OpenWrt** (supporting 23.05, 24.10, 25.x, and SNAPSHOT).\n\n```mermaid\ngraph TD\n Client[LAN Clients] -->|TCP/UDP| Router[OpenWrt Router]\n Router --> NFT[nftables: inet TachyonTable]\n \n NFT -->|Direct List / 0ms| WAN[Direct WAN Gateway]\n NFT -->|NFQueue Desync| Zapret[Zapret v1 / v2 nfqws/2]\n NFT -->|SOCKS Desync| ByeDPI[ByeDPI ciadpi]\n NFT -->|TProxy / Redirect| SingBox[sing-box Core v1.11+]\n \n SingBox -->|VLESS Reality / H2 / WG| VPS[External VPS / Global Web]\n Zapret --> WAN\n ByeDPI --> WAN\n \n subgraph Tachyon Control Plane\n UCODE[ucode Core Runtime] --> NFT\n UCODE --> SingBox\n UCODE --> Zapret\n UCODE --> ByeDPI\n Watchdog[Autonomous Watchdog 5-15s] --> UCODE\n AIDoctor[AI Doctor v2.5 / Local Doctor] --> Watchdog\n Telegram[Telegram Bot Daemon] --> UCODE\n RestAPI[HTTP REST Agent API / OpenAPI 3.0] --> UCODE\n LuCI[LuCI TypeScript Web UI] --> UCODE\n end\n```\n\n---\n\n## 2. Core Architectural Pillars\n\n### 2.1. Native ucode Runtime (Zero Heavy Runtimes)\n* All control plane logic, UCI manipulation, config generation, health checks, and JSON APIs are written in **ucode**, OpenWrt's native C-like scripting language.\n* **Benefits**:\n * Consumes $\\approx$ 1–3 MB RAM (contrasted with 30–60 MB for Python/Node).\n * Executes directly in C-speed with native bindings to `libuci`, `libubus`, `libblobmsg`, and posix syscalls (`fs`, `math`, `socket`).\n * Runs on routers with as little as 128 MB RAM.\n\n### 2.2. Deterministic State & Atomic Writes\n* All configuration changes (`/etc/config/tachyon`) and JSON runtime configurations (`/tmp/tachyon/sing-box.json`) use an **atomic write pattern** (`tmp_file + rename`):\n ```ucode\n fs.writefile(tmp_path, json_str);\n fs.rename(tmp_path, target_path);", "embedding": []}, {"id": 1, "file": "01_SYSTEM_ARCHITECTURE.md", "heading": "3. Directory Layout & Repository Anatomy", "text": "* All configuration changes (`/etc/config/tachyon`) and JSON runtime configurations (`/tmp/tachyon/sing-box.json`) use an **atomic write pattern** (`tmp_file + rename`):\n ```ucode\n fs.writefile(tmp_path, json_str);\n fs.rename(tmp_path, target_path);\n ```\n This prevents file corruption during sudden power losses or kernel panics.\n\n### 2.3. Dual Control Interfaces: LuCI & Headless Telegram/REST\n1. **LuCI Web UI**: Modern TypeScript SPA (`fe-app-tachyon`) compiled into LuCI JS assets (`luci-app-tachyon`).\n2. **Interactive Telegram Bot**: Embedded polling daemon with inline keyboards for on-the-fly server switching, rule updates, and instant diagnostics.\n3. **HTTP REST Agent API**: Secure CGI endpoint (`/cgi-bin/tachyon-agent/`) with OpenAPI 3.0.3 spec for integration with LLM agents (ChatGPT Custom GPTs, Cursor, Claude Code, N8N).\n\n---\n\n## 3. Directory Layout & Repository Anatomy\n\n```\ntachyon/\n├── tachyon/files/ # Core backend package root\n│ ├── etc/\n│ │ ├── config/tachyon # Main UCI configuration (chmod 600)\n│ │ ├── init.d/tachyon # Procd service init script\n│ │ └── uci-defaults/ # Post-installation default configs\n│ ├── usr/\n│ │ ├── bin/tachyon # Main CLI entrypoint wrapper\n│ │ └── lib/tachyon/ # Modular ucode libraries (60+ modules)\n│ │ ├── core/ # Low-level helpers, UCI, constants\n│ │ ├── service/ # Lifecycle, Watchdog, APIs, Telegram\n│ │ ├── diagnostics/ # AI Doctor, Local Doctor, Quick Fixes\n│ │ ├── singbox/ # Config generator, DNS, routing\n│ │ ├── providers/ # Zapret v1/v2, ByeDPI, NFQueue\n│ │ ├── nft/ # nftables ruleset compiler & applier\n│ │ ├── dns/ # dnsmasq & DNS routing coordinator\n│ │ ├── components/ # Updater, package manager, hosts\n│ │ ├── config/ # Validator, rule engine, migrations", "embedding": []}, {"id": 2, "file": "01_SYSTEM_ARCHITECTURE.md", "heading": "4. Key Runtime Processes & Daemons", "text": "│ │ ├── dns/ # dnsmasq & DNS routing coordinator\n│ │ ├── components/ # Updater, package manager, hosts\n│ │ ├── config/ # Validator, rule engine, migrations\n│ │ └── subscription/ # Subscriptions fetcher & parser\n├── luci-app-tachyon/ # OpenWrt LuCI package\n│ ├── htdocs/luci-static/resources/view/tachyon/ # Compiled frontend JS bundle\n│ ├── root/etc/uci-defaults/ # LuCI menu registration\n│ └── po/ # Translations (ru, en)\n├── fe-app-tachyon/ # Frontend TypeScript source\n│ ├── src/ # TypeScript SPA source\n│ ├── tsup.config.ts # Builds and patches LuCI baseclass\n│ └── package.json # Dependencies (vitest, typescript, tsup)\n├── tests/ # Comprehensive test suite (100+ shell tests)\n├── build.sh # Release packager (ipk + apk)\n├── install.sh # One-liner end-user installer\n└── docs/ # Technical documentation & Knowledge Base\n```\n\n---\n\n## 4. Key Runtime Processes & Daemons\n\n| Process | Role | Execution Model |\n|---|---|---|\n| `sing-box` | Core proxying & packet tunneling engine | Managed by procd (`/etc/init.d/tachyon`) |\n| `nfqws` / `nfqws2` | Local TCP/UDP packet desynchronizer (Zapret) | Spawned into background per configured rule |\n| `ciadpi` | Local SOCKS desync proxy (ByeDPI) | Spawned on `127.0.0.1:1080` / custom port |\n| `watchdog.uc` | State supervision, loop mitigation, self-healing | Background periodic daemon (every 5–15s) |\n| `telegram.uc` | Telegram Bot Long-Polling Daemon | Procd-supervised background worker |\n| `agent_api.uc` | HTTP REST Agent API Handler | Invoked on-demand via `uhttpd` CGI |", "embedding": []}, {"id": 3, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "2. Service Management Subsystem (`service/`)", "text": "# 02. ucode Backend Module Reference\n\nTachyon's backend is modularized under `tachyon/files/usr/lib/tachyon/`. Modules are loaded dynamically via ucode's `require()` or executed directly via `ucode -L `.\n\n---\n\n## 1. Core Subsystem (`core/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `constants.uc` | Central repository of paths, default ports, mark values, version detection (`detect_installed_version()`), commit SHA resolver (`detect_installed_commit_sha()`), UCI config names. |\n| `common.uc` | String manipulation (`trim`, `split_lines`), file helpers (`read_file`, `write_atomic`), logging formatters (`log_info`, `log_error`, `log_debug`). |\n| `helpers.uc` | Process management (`popen_read`, `system_exec`), CIDR formatting, MAC address validators, integer conversions with safety fallbacks. |\n| `ip.uc` | IP v4/v6 parser, subnet containment (`ip_in_subnet`), CIDR validator, loopback & private IP range detection. |\n| `url.uc` | RFC 3986 URL parser, extraction of scheme, host, port, path, query params, hash fragment, safe URL encoding/decoding. |\n| `uci.uc` | High-level wrapper over OpenWrt `libuci`: `get_section`, `set_option`, `add_section`, `delete_section`, atomic commit with lock. |\n| `packages.uc` | Package manager abstraction: detects `opkg` vs `apk` (OpenWrt 25.x+), checks installed packages, versions, and dependencies. |\n| `events.uc` | Internal event bus mechanism: publish/subscribe for service state transitions and notification dispatching. |\n\n---\n\n## 2. Service Management Subsystem (`service/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `lifecycle.uc` | Service start, stop, restart, enable, disable. Coordinates teardown order (nftables -> singbox -> zapret -> dnsmasq) to prevent routing deadlocks. |\n| `state.uc` | Comprehensive runtime state reader (`get_runtime_state()`): active proxy server, watchdog status, interface health, uptime, memory consumption. |\n| `reload.uc` | Hot-reload planner (`plan_reload()`): inspects what changed in UCI and applies the minimal necessary restart (e.g. soft-reload sing-box vs full nftables rebuild). |", "embedding": []}, {"id": 4, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "3. Diagnostics & AI Subsystem (`diagnostics/`)", "text": "| `state.uc` | Comprehensive runtime state reader (`get_runtime_state()`): active proxy server, watchdog status, interface health, uptime, memory consumption. |\n| `reload.uc` | Hot-reload planner (`plan_reload()`): inspects what changed in UCI and applies the minimal necessary restart (e.g. soft-reload sing-box vs full nftables rebuild). |\n| `reset.uc` | Factory reset routines: wipes `/etc/config/tachyon`, cleans up temporary caches, restores default templates. |\n| `watchdog.uc` | Autonomous supervisor loop: checks sing-box process, memory thresholds (OOM guard), DNS resolution, loop-restart mitigation (`safe_proxy_restart`). |\n| `snapshot.uc` | Assembles a structured system snapshot JSON for AI Doctor and external diagnostics. |\n| `agent_api.uc` | CGI handler for `/cgi-bin/tachyon-agent/`: handles routing, Bearer authentication validation, endpoint dispatch, JSON serialization. |\n| `agent_mcp.uc` | Model Context Protocol (MCP) tool schema and handlers for integration with MCP clients (Cursor, Claude Desktop). |\n| `telegram.uc` | Telegram Bot daemon: polling loop (`getUpdates`), command dispatcher (`/doctor`, `/ai_doctor`, `/fix`, `/restart`, `/status`), inline keyboards. |\n| `warp_generator.uc` | Cloudflare WARP account generator & key exchange for auto-provisioning WireGuard outbounds. |\n| `event_controller.uc` | Coordinates async background job workers, PID tracking, lock files, and log ring-buffer management. |\n\n---\n\n## 3. Diagnostics & AI Subsystem (`diagnostics/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `runtime.uc` | **Local Rule Doctor & AI Doctor engine**: runs 10+ local heuristic checks, detects NTP desync, conntrack overflow, MTU issues, bootstrap DNS deadlock; applies 13+ quick fix codes (`apply_quick_fix()`), executes emergency internet restoration (`restore_native_internet()`). |\n| `service_check.uc` | Health probes: tests sing-box TCP/UDP proxying, DNS response latency, WAN internet connectivity, gateway reachability. |\n| `status.uc` | Detailed diagnostics status assembler for LuCI Diagnostic tab and CLI `tachyon doctor`. |", "embedding": []}, {"id": 5, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "5. Providers & DPI Bypass Subsystem (`providers/`)", "text": "| `service_check.uc` | Health probes: tests sing-box TCP/UDP proxying, DNS response latency, WAN internet connectivity, gateway reachability. |\n| `status.uc` | Detailed diagnostics status assembler for LuCI Diagnostic tab and CLI `tachyon doctor`. |\n\n---\n\n## 4. sing-box Subsystem (`singbox/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `generator.uc` | Master sing-box JSON configuration compiler (`/tmp/tachyon/sing-box.json`): binds inbounds, outbounds, routing rules, DNS, experimental. |\n| `generator_outbounds.uc` | Compiles VLESS (Reality/gRPC/WS), VMess, Shadowsocks, Trojan, Hysteria2, WireGuard, Direct, and Block outbounds. |\n| `generator_routes.uc` | Compiles routing rules: domain matchers, IP CIDR matchers, GeoIP rules, client MAC/IP matchers, detour assignments. |\n| `dns.uc` | Compiles sing-box DNS configuration: local DNS, remote DoH/DoT/UDP DNS, fakeip/direct resolution rules. |\n| `dns_failover.uc` | High-availability DNS failover logic: switches between primary and secondary bootstrap DNS upon timeouts. |\n| `dns_prefetch.uc` | Domain pre-fetching & cache warming for frequently accessed sites. |\n| `dns_presets.uc` | Built-in DNS presets (Cloudflare, Google, AdGuard, Quad9, OpenDNS, Custom DoH). |\n| `priority.uc` | Outbound group priority manager: auto-failover, fallback groups, URL-test latency sorting. |\n| `route.uc` | Low-level sing-box route rule builder & selector logic. |\n| `rulesets.uc` | Rule-set binary (`.srs`) and JSON ruleset compiler and parser. |\n| `servers.uc` | Server node parser, validator, and URI serializer (VLESS, VMess, SS, Trojan, H2). |\n| `subscription.uc` | Subscriptions processor: download, base64 decoding, node deduplication, format normalizer. |\n| `urltest.uc` | RTT ping & latency testing worker: measures real response times across proxy servers. |\n\n---\n\n## 5. Providers & DPI Bypass Subsystem (`providers/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `zapret/runtime.uc` | Manages **Zapret v1 (`nfqws`)** process arguments, queue numbers, NFQueue bindings, daemon start/stop. |", "embedding": []}, {"id": 6, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "7. Configuration & Components Subsystem (`config/`, `components/`, `subscription/`)", "text": "## 5. Providers & DPI Bypass Subsystem (`providers/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `zapret/runtime.uc` | Manages **Zapret v1 (`nfqws`)** process arguments, queue numbers, NFQueue bindings, daemon start/stop. |\n| `zapret2/runtime.uc` | Manages **Zapret v2 (`nfqws2`)** advanced multi-vector desync arguments (`--dpi-desync=multisplit`, `--dpi-desync-seqovl`). |\n| `byedpi/runtime.uc` | Manages **ByeDPI (`ciadpi`)** SOCKS5 proxy daemon, port allocation, payload split parameters. |\n| `nfqueue/runtime.uc` | Kernel NFQueue allocation table, prevents queue ID collisions between Zapret v1 and v2. |\n| `rules.uc` | Maps UCI routing rules to specific provider action handlers (`action = zapret | zapret2 | byedpi | proxy`). |\n| `status.uc` | Real-time process inspection and health monitoring of all active DPI bypass daemons. |\n\n---\n\n## 6. Networking & Firewall Subsystem (`nft/`, `dns/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `nft/apply.uc` | Generates and commits nftables ruleset (`inet TachyonTable`): prerouting, output, divert chains, marks, fwmark, bypass sets. |\n| `dns/apply.uc` | Coordinates DNS interception: configures dnsmasq forwarders, port redirection (`127.0.0.1:5353`), ipset / nftset auto-population. |\n\n---\n\n## 7. Configuration & Components Subsystem (`config/`, `components/`, `subscription/`)\n\n| Module | Key Functions & Responsibilities |\n|---|---|\n| `config/validator.uc` | Strict UCI schema validation: validates IP addresses, ports, UUIDs, private keys, domain names, action modes. |\n| `config/migration.uc` | Automatic migration engine: migrates configurations from legacy Forkop, Podkop Plus, and Podkop configs without data loss. |\n| `components/updater.uc` | Background component installer/updater: handles downloading, unpacking, installing, and version checking of binaries. |\n| `components/hosts.uc` | Remote hosts lists ingestor: fetches blocklists, merges into unified `combined.txt`, handles GitHub mirror failovers. |", "embedding": []}, {"id": 7, "file": "02_UCODE_MODULE_REFERENCE.md", "heading": "7. Configuration & Components Subsystem (`config/`, `components/`, `subscription/`)", "text": "| `components/updater.uc` | Background component installer/updater: handles downloading, unpacking, installing, and version checking of binaries. |\n| `components/hosts.uc` | Remote hosts lists ingestor: fetches blocklists, merges into unified `combined.txt`, handles GitHub mirror failovers. |\n| `subscription/cache.uc` | Atomic subscription cache store, prevents redundant network downloads. |\n| `subscription/parser.uc` | Universal proxy URI parser: parses `vless://`, `vmess://`, `ss://`, `trojan://`, `hysteria2://`, `wireguard://`. |", "embedding": []}, {"id": 8, "file": "03_NETWORKING_NFTABLES_AND_ROUTING.md", "heading": "2.1. Key Chains", "text": "# 03. Networking, nftables & Traffic Routing\n\n## 1. Packet Journey & Routing Lifecycle\n\nTachyon intercepts traffic at the OpenWrt kernel level using **nftables** and policy routing (`ip rule` + routing table `100`).\n\n```mermaid\nsequenceDiagram\n autonumber\n participant Client as LAN Client\n participant PREROUTING as nftables PREROUTING\n participant Sets as nftables Sets (Bypass / Target)\n participant Divert as Detour Action\n participant SingBox as sing-box / Zapret / ByeDPI\n participant WAN as WAN / Internet\n\n Client->>PREROUTING: TCP/UDP SYN Packet (e.g. 192.168.1.100 -> 1.1.1.1:443)\n PREROUTING->>Sets: Check destination IP / Port against Tachyon sets\n alt IP in Bypass Set (Local Subnet / RuNet / Direct)\n Sets->>WAN: Direct Forwarding (0 ms overhead)\n else IP in Target Set or Default Detour\n Sets->>Divert: Mark packet with FWMARK (e.g. 0x10000000)\n Divert->>SingBox: Divert via TProxy / Redir / NFQueue\n SingBox->>WAN: Processed / Encrypted / Desynchronized Outbound\n end\n```\n\n---\n\n## 2. nftables Table Structure (`inet TachyonTable`)\n\nTachyon encapsulates all its firewall rules inside a dedicated table `inet TachyonTable`. This ensures clean isolation from the system `inet fw4` firewall.\n\n### 2.1. Key Chains\n\n1. **`dstnat` / `prerouting` (Hook `prerouting`, priority `-100` / `dstnat`)**:\n * Intercepts incoming LAN client packets before routing decisions.\n * Checks client MAC / IP exclusion lists.\n * Matches destination IP against `tachyon_bypass_v4` / `tachyon_target_v4` sets.\n * Performs TProxy redirect (`tproxy to :10080 meta mark set 0x10000000 accept`) or Port redirect.\n2. **`output` (Hook `output`, priority `-100`)**:\n * Intercepts router's own locally generated packets (e.g. DNS lookups, curl, router traffic).\n * Prevents self-interception loops (`meta skuid == \"tachyon\" return`).\n3. **`nfqueue_chain`**:\n * Directs packets destined for Zapret v1 / v2 to kernel NFQueue numbers (`queue num 200`, `queue num 201`).", "embedding": []}, {"id": 9, "file": "03_NETWORKING_NFTABLES_AND_ROUTING.md", "heading": "3.3. Bootstrap DNS Dead-Lock Prevention", "text": "* Prevents self-interception loops (`meta skuid == \"tachyon\" return`).\n3. **`nfqueue_chain`**:\n * Directs packets destined for Zapret v1 / v2 to kernel NFQueue numbers (`queue num 200`, `queue num 201`).\n\n### 2.2. Bitmask & Firewall Marks\n\n| Mark Value | Meaning | Action |\n|---|---|---|\n| `0x10000000` | Redirected to sing-box TProxy | Routed to local loopback via table 100 |\n| `0x20000000` | Processed by Zapret / NFQueue | Dispatched to nfqws daemon |\n| `0x40000000` | Marked for ByeDPI SOCKS | Forwarded to local SOCKS redirect |\n| `0x80000000` | Bypass marker (Direct WAN) | Accepted immediately without inspection |\n\n---\n\n## 3. DNS Architecture & Interception\n\n### 3.1. DNS Resolution Pipeline\n\n```\n[LAN Client DNS Query: 53]\n │\n ▼\n [dnsmasq / Router]\n │\n ├─► If Domain in Direct / RuNet / Local ──► Local Upstream DNS (ISP / DoH)\n │\n ├─► If Domain in Hosts Section ──────────► Static IP from dns_hosts\n │\n └─► If Domain in Target Proxy / Desync ──► sing-box DNS (127.0.0.1:5353)\n │\n ▼\n [Remote Secure DoH/DoT]\n [Populates nftables set]\n```\n\n### 3.2. Dynamic nftables Set Population (`nftset`)\n* When dnsmasq resolves a domain associated with a proxy or Zapret rule, it automatically adds the resolved IP address to the corresponding kernel nftables set (e.g. `tachyon_target_v4`).\n* This enables sub-millisecond, line-rate packet redirection without re-evaluating domain names in user-space for every packet.\n\n### 3.3. Bootstrap DNS Dead-Lock Prevention\n* A critical failure mode in proxy routers is a **DNS loop**: sing-box needs to resolve the VPS hostname (e.g. `vps.example.com`), but its own DNS resolver routes queries through sing-box, which is not yet connected.", "embedding": []}, {"id": 10, "file": "03_NETWORKING_NFTABLES_AND_ROUTING.md", "heading": "4.1. Emergency Internet Restoration Mechanism", "text": "* A critical failure mode in proxy routers is a **DNS loop**: sing-box needs to resolve the VPS hostname (e.g. `vps.example.com`), but its own DNS resolver routes queries through sing-box, which is not yet connected.\n* **Tachyon's Solution**:\n * Dedicated `direct_resolvers` (Cloudflare `1.1.1.1`, Google `8.8.8.8`) bypass all proxy rules.\n * Local Rule Doctor monitors bootstrap DNS reachability and automatically executes `fix_bootstrap_dns` if a circular dependency is detected.\n\n---\n\n## 4. Policy Routing (`ip rule` & Routing Tables)\n\nTachyon provisions routing table `100`:\n```sh\n# Routing rule for marked packets\nip rule add fwmark 0x10000000/0x10000000 lookup 100 priority 10000\n\n# Local loopback route in table 100\nip route add local default dev lo table 100\n```\n\n### 4.1. Emergency Internet Restoration Mechanism\nWhen `tachyon restore_native_internet` is triggered:\n1. `inet TachyonTable` is atomically deleted: `nft delete table inet TachyonTable`.\n2. Policy routing rule `ip rule del lookup 100` is removed.\n3. dnsmasq configuration is reverted to native upstream forwarders.\n4. sing-box process is stopped cleanly.\n5. Result: Full, native WAN connectivity is restored in under 200 ms without rebooting the router.", "embedding": []}, {"id": 11, "file": "04_DPI_BYPASS_ENGINES.md", "heading": "2.1. Key Desync Strategies", "text": "# 04. DPI Bypass & Desynchronization Engines\n\nTachyon integrates three distinct local packet desynchronization engines that operate entirely on the router hardware without requiring an external proxy server (Zero-VPS mode).\n\n---\n\n## 1. Comparison Matrix: Zapret v1 vs Zapret v2 vs ByeDPI\n\n| Feature | Zapret v1 (`nfqws`) | Zapret v2 (`nfqws2`) | ByeDPI (`ciadpi`) |\n|---|---|---|---|\n| **Binary** | `/usr/bin/nfqws` | `/usr/bin/nfqws2` | `/usr/bin/ciadpi` |\n| **Operating Model** | Kernel NFQueue Intercept | Kernel NFQueue Multi-Q | Local SOCKS5 / TProxy |\n| **Target Protocols** | HTTP, TLS (ClientHello) | HTTP, TLS, QUIC, WireGuard | HTTP, TLS |\n| **Packet Splitting** | `split2`, `disorder`, `fake` | `multisplit`, `seqovl`, `wsize`, `fake` | `--split-pos`, `--disorder`, `--fake` |\n| **Multi-Vector Chains** | Single strategy per rule | Multi-phase composite chains | Composite SOCKS args |\n| **CPU Overhead** | Minimal (< 1%) | Low (< 2%) | Low (< 2%) |\n| **Best For** | Legacy OpenWrt, Basic DPI | YouTube 4K, Discord, TSPU | Non-root users, SOCKS routing |\n\n---\n\n## 2. Desynchronization Techniques Explained\n\nDeep Packet Inspection (DPI) systems inspect packet headers (such as TLS Server Name Indication `SNI` or HTTP `Host` headers) to detect and block access. Desynchronization tricks the DPI into losing track of the connection state while the destination server reconstructs the stream normally.\n\n```\nOriginal Stream: [TLS ClientHello with SNI] ──────────► (Blocked by DPI)\n\nDesynchronized: [Fake Bad Checksum Packet] ──────────► (DPI resets state)\n [Payload Part 1: Snippet] ──────────► (DPI misses SNI)\n [Payload Part 2: Remainder] ─────────► (Server merges stream)\n```\n\n### 2.1. Key Desync Strategies\n\n1. **`split2` / `multisplit`**:\n * Splits the TCP segment right inside the TLS SNI or HTTP Host header. The DPI does not reassemble small fragments, while the end server's TCP stack reconstructs the full buffer seamlessly.\n2. **`disorder` / `disorder2`**:", "embedding": []}, {"id": 12, "file": "04_DPI_BYPASS_ENGINES.md", "heading": "4. NFQueue Multiplexing & Conflict Prevention", "text": "* Splits the TCP segment right inside the TLS SNI or HTTP Host header. The DPI does not reassemble small fragments, while the end server's TCP stack reconstructs the full buffer seamlessly.\n2. **`disorder` / `disorder2`**:\n * Sends the second half of the packet before the first half with appropriate TCP sequence numbers. The DPI sees out-of-order data and skips inspection.\n3. **`fake` / `fake_sni`**:\n * Sends a fake TLS ClientHello packet with a low TTL (Time To Live). The DPI intercepts the fake packet and approves the connection; the packet dies before reaching the destination server.\n4. **`seqovl` (Sequence Overlap)**:\n * Sends a fake packet with overlapping sequence numbers that the server's TCP stack discards while the DPI's state machine is fooled.\n5. **`wsize` (Window Size Manipulation)**:\n * Forces a small TCP window size to prevent DPI from receiving full payloads in single bursts.\n\n---\n\n## 3. Configuration & Runtime Management\n\n### 3.1. Zapret v2 Configuration Example (`/etc/config/tachyon`)\n\n```ini\nconfig provider 'zapret2'\n option enabled '1'\n option daemon_bin '/usr/bin/nfqws2'\n option qnum '201'\n option args_tcp '--dpi-desync=multisplit --dpi-desync-split-pos=1,midsld --dpi-desync-seqovl=1 --dpi-desync-fooling=badseq'\n option args_udp '--dpi-desync=fake --dpi-desync-any-protocol=1 --dpi-desync-cutoff=d4'\n\nconfig rule 'youtube_rule'\n option enabled '1'\n option name 'YouTube 4K Desync'\n option action 'zapret2'\n list domain_pattern 'googlevideo.com'\n list domain_pattern 'youtube.com'\n list domain_pattern 'ytimg.com'\n list domain_pattern 'discord.gg'\n list domain_pattern 'discord.com'\n```\n\n### 3.2. ByeDPI Configuration Example\n\n```ini\nconfig provider 'byedpi'\n option enabled '1'\n option daemon_bin '/usr/bin/ciadpi'\n option listen_ip '127.0.0.1'\n option listen_port '1080'\n option args '--split-pos 1 --disorder 1 --fake -1 --ttl 8'\n```\n\n---\n\n## 4. NFQueue Multiplexing & Conflict Prevention\n\n* Kernel `nfnetlink_queue` requires unique queue numbers per daemon instance.", "embedding": []}, {"id": 13, "file": "04_DPI_BYPASS_ENGINES.md", "heading": "4. AmneziaWG в sing-box-extended: верификация (2026-08)", "text": "option args '--split-pos 1 --disorder 1 --fake -1 --ttl 8'\n```\n\n---\n\n## 4. NFQueue Multiplexing & Conflict Prevention\n\n* Kernel `nfnetlink_queue` requires unique queue numbers per daemon instance.\n* Tachyon's `providers/nfqueue/runtime.uc` dynamically manages queue allocation:\n * Default Zapret v1 queue: `200`\n * Default Zapret v2 queue: `201`\n* If both Zapret v1 and v2 are enabled simultaneously for different rules, Tachyon enforces strict queue isolation and prevents PID collisions.\n\n## 4. AmneziaWG в sing-box-extended: верификация (2026-08)\n\nПроверено против бинарника релиза `v1.13.18-extended-2.6.5` (shtorm-7/sing-box-extended):\n\n- `endpoint.amnezia` полностью реализован: `AmneziaOptions` (`transport/wireguard/endpoint_options.go`)\n → IPC `jc/jmin/jmax/s1-s4/h1-h4/i1-i5` (`transport/wireguard/endpoint.go`)\n → wireguard-go форк `shtorm-7/wireguard-go v0.0.4-extended-1.5.3`:\n I1-I5 шлются отдельными пакетами ПЕРЕД handshake initiation\n (`device/send.go SendHandshakeInitiation`), формат значения — hex-строка.\n- Парсер конфига строгий: неизвестные поля и мусорные значения в `amnezia.*`\n дают FATAL при старте, «молчаливого игнорирования» не существует.\n- Генерация Tachyon (`generator_outbounds.uc add_awg_endpoint`) совпадает со\n схемой форка: peer содержит required `allowed_ips`, `pre_shared_key` и\n `persistent_keepalive_interval` опциональны, hex i1-i5 проходит\n `uci_bin_to_hex` без двойного кодирования.\n\nСледствие для диагностики: если AWG/WARP-туннель «TX есть, RX нет» на\nsing-box-extended, конфиг секции применён корректно — причина вне Tachyon\n(сервер, провайдер, UDP-фильтрация). Чек 6c доктора (1.2.91+) сообщает это\nпользователю по свидетельствам из лога ядра.\n\nКейс «kernel работает, sing-box нет» с идентичным conf остаётся внешним:\nразличие в реализации special-handshake между amneziawg-linux kernel-модом\nи userspace-форком может быть критично только для конкретных строгих\nсерверов. Шаги диагностики для таких случаев:\n\n1. `tcpdump -i any -X udp port <порт>` — сравнить первые байты I1-пакетов", "embedding": []}, {"id": 14, "file": "04_DPI_BYPASS_ENGINES.md", "heading": "4. AmneziaWG в sing-box-extended: верификация (2026-08)", "text": "различие в реализации special-handshake между amneziawg-linux kernel-модом\nи userspace-форком может быть критично только для конкретных строгих\nсерверов. Шаги диагностики для таких случаев:\n\n1. `tcpdump -i any -X udp port <порт>` — сравнить первые байты I1-пакетов\n kernel vs sing-box (у special-handshake пакетов префикс `c7`).\n2. Временно убрать i1-i5 из секции: если handshake поднимется — сервер\n не требует special handshake, а требует его некорректную передачу.\n3. Попробовать вариант ядра sing-box-lx как эксперимент.", "embedding": []}, {"id": 15, "file": "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "heading": "3. Local Rule Doctor & 13 Quick Fix Codes", "text": "# 05. AI Stack, AI Doctor & Autonomous Self-Healing\n\n## 1. Overview of Tachyon AI Stack\n\nTachyon provides a dual-layer AI and autonomous resilience stack:\n1. **Autonomous Layer (Local Rule Doctor & Watchdog)**: Runs locally on the router every 5–15 seconds, detects network drops, memory leaks, DNS deadlocks, and time desync without requiring internet access or API tokens.\n2. **Cognitive Layer (AI Doctor v2.5)**: Deep diagnostics powered by LLMs (OpenAI, Anthropic Claude, DeepSeek, or local Ollama/OpenRouter models) with contextual prompt enrichment and automated Multi-Fix generation.\n3. **Agent Integration Layer (HTTP REST Agent API & OpenAPI 3.0)**: Programmatic interface for autonomous AI agents (Cursor, Claude Code, AutoGPT, ChatGPT Custom GPTs, N8N, Dify).\n\n---\n\n## 2. AI Doctor Architecture (v2.5)\n\n```mermaid\ngraph TD\n subgraph Data Collection\n R1[Process Probes: singbox/zapret/dnsmasq]\n R2[Kernel Metrics: conntrack, mem, uptime]\n R3[Watchdog History: failure streaks, OOM events]\n R4[System Logs: 15 most recent critical lines]\n end\n\n R1 --> Aggregator[snapshot.uc Assembler]\n R2 --> Aggregator\n R3 --> Aggregator\n R4 --> Aggregator\n\n Aggregator --> Prompt[Deep Context LLM Prompt]\n Prompt --> LLM[LLM Provider: OpenAI / Claude / DeepSeek / Custom]\n \n LLM --> Verdict[Structured Diagnosis Report]\n LLM --> MultiFix[Multi-Fix Action Plan: FIX: code1, code2]\n \n MultiFix --> Exec[Local Rule Doctor Execution Engine]\n Exec --> Result[Applied Fixes & Service State Refresh]\n```\n\n---\n\n## 3. Local Rule Doctor & 13 Quick Fix Codes\n\nThe execution engine in `diagnostics/runtime.uc` maps standardized fix codes to automated shell/ucode repair procedures:\n\n| Code | Target Subsystem | Action Performed |\n|---|---|---|\n| `start_singbox` | sing-box | Restarts failed sing-box process via procd. |\n| `rebuild_rules` | nftables | Flushes and recompiles `inet TachyonTable` ruleset. |\n| `fix_dnsmasq` | dnsmasq | Restarts dnsmasq and checks `/tmp/etc/dnsmasq.conf.*`. |", "embedding": []}, {"id": 16, "file": "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "heading": "4.1. Authentication Model", "text": "| `start_singbox` | sing-box | Restarts failed sing-box process via procd. |\n| `rebuild_rules` | nftables | Flushes and recompiles `inet TachyonTable` ruleset. |\n| `fix_dnsmasq` | dnsmasq | Restarts dnsmasq and checks `/tmp/etc/dnsmasq.conf.*`. |\n| `fix_resolv_symlink` | DNS | Restores broken `/etc/resolv.conf` -> `/tmp/resolv.conf.d/resolv.conf.auto` link. |\n| `start_watchdog` | Watchdog | Restarts the background watchdog supervisor process. |\n| `restart_singbox_dns` | sing-box DNS | Resets internal sing-box DNS server module and cache. |\n| `fix_uci_config` | UCI | Restores `/etc/config/tachyon` from the latest valid atomic backup. |\n| `fix_wan_interface` | Networking | Runs `ifup wan` to bounce the primary uplink interface. |\n| `fix_gateway` | Networking | Re-establishes default routing gateway via `/etc/init.d/network restart`. |\n| `clear_dns_cache` | Cache | Flushes dnsmasq cache, deletes sing-box `cache.db`, and restarts DNS. |\n| `update_subscriptions`| Subscriptions | Triggers asynchronous background download and refresh of proxy subscriptions. |\n| `reset_firewall` | Firewall | Full restart of OpenWrt firewall (`/etc/init.d/firewall restart`). |\n| `restart_network` | Network Stack | Full restart of OpenWrt network daemon (`/etc/init.d/network restart`). |\n| `fix_system_time` | System NTP | Resynchronizes router clock via NTP pool servers (`ntpd -q -p ...`). |\n| `flush_conntrack` | Kernel | Flushes congested `nf_conntrack` table during connection floods. |\n| `fix_bootstrap_dns` | Resolver | Unblocks sing-box primary bootstrap resolvers and breaks circular loops. |\n| `optimize_mtu` | Tunnels | Auto-calculates optimal MTU (1280–1420) for WireGuard / AmneziaWG interfaces. |\n| `restore_native_internet` | Emergency | One-click full teardown of Tachyon proxying; returns pure native WAN routing. |\n\n---\n\n## 4. HTTP REST Agent API & OpenAPI 3.0.3\n\nTachyon exposes a standardized REST API hosted under `/cgi-bin/tachyon-agent/` via OpenWrt's native `uhttpd`.\n\n### 4.1. Authentication Model", "embedding": []}, {"id": 17, "file": "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "heading": "4.3. Full Endpoint Index", "text": "| `restore_native_internet` | Emergency | One-click full teardown of Tachyon proxying; returns pure native WAN routing. |\n\n---\n\n## 4. HTTP REST Agent API & OpenAPI 3.0.3\n\nTachyon exposes a standardized REST API hosted under `/cgi-bin/tachyon-agent/` via OpenWrt's native `uhttpd`.\n\n### 4.1. Authentication Model\n* **Read-Only Endpoints**: Accessible within LAN without authentication (or secured via firewall).\n* **Write Endpoints**: Require Bearer token authentication:\n ```http\n POST /cgi-bin/tachyon-agent/ai-doctor/fix\n Authorization: Bearer \n Content-Type: application/json\n\n {\"fix\": \"clear_dns_cache,start_singbox\"}\n ```\n\n### 4.2. OpenAPI 3.0.3 Specification\n* The live OpenAPI JSON schema is available at:\n ```\n GET /cgi-bin/tachyon-agent/openapi.json\n ```\n* This schema allows instant import into **ChatGPT Custom GPT Actions**, **N8N HTTP Request Nodes**, **Dify**, and **Flowise**.\n\n### 4.3. Full Endpoint Index\n\n| Method | Endpoint | Auth Required | Description |\n|---|---|---|---|\n| `GET` | `/health` | No | Lightweight JSON health status probe. |\n| `GET` | `/snapshot` | No | Full system snapshot (interfaces, memory, proxy status). |\n| `GET` | `/diagnose` | No | Local Rule Doctor diagnosis report. |\n| `GET` | `/logs` | No | Returns last 100 lines of Tachyon system log. |\n| `GET` | `/config` | No | Returns sanitized UCI configuration. |\n| `GET` | `/tools` | No | OpenAI Function Calling / MCP tool definitions. |\n| `GET` | `/openapi.json` | No | OpenAPI 3.0.3 specification JSON. |\n| `POST`| `/heal` | **Bearer** | Triggers manual Watchdog self-healing cycle. |\n| `POST`| `/ai-doctor/fix` | **Bearer** | Applies one or more quick-fix codes. |\n| `POST`| `/restart` | **Bearer** | Restarts specified service (`singbox`, `watchdog`, `dnsmasq`). |\n| `POST`| `/reload` | **Bearer** | Soft-reloads configuration and routing tables. |\n| `POST`| `/config/set` | **Bearer** | Updates specific UCI options. |\n| `POST`| `/section/toggle` | **Bearer** | Enables or disables a routing rule section. |", "embedding": []}, {"id": 18, "file": "05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md", "heading": "4.3. Full Endpoint Index", "text": "| `POST`| `/reload` | **Bearer** | Soft-reloads configuration and routing tables. |\n| `POST`| `/config/set` | **Bearer** | Updates specific UCI options. |\n| `POST`| `/section/toggle` | **Bearer** | Enables or disables a routing rule section. |\n| `POST`| `/domain/add` | **Bearer** | Appends domain pattern to target rule list. |", "embedding": []}, {"id": 19, "file": "06_FRONTEND_LUCI_TYPESCRIPT.md", "heading": "3.1. Key Services (`src/tachyon/services/`)", "text": "# 06. Frontend Architecture (LuCI & TypeScript)\n\n## 1. Frontend Architecture Overview\n\nTachyon's web UI is authored as a single-page application in **TypeScript** under `fe-app-tachyon/` and compiled into a standalone JavaScript view under `luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js`.\n\n```\nfe-app-tachyon/ (TypeScript Source)\n │\n ▼ (npm run build: tsc --noEmit && tsup)\n │\n[tsup build + baseclass.extend transformation]\n │\n ▼\nluci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js (LuCI Asset)\n```\n\n---\n\n## 2. The LuCI `baseclass.extend()` Patch Mechanism\n\nLuCI uses a custom asynchronous class system (`baseclass.extend({...})`) rather than standard ES modules or Webpack bundles.\n\n* `fe-app-tachyon/tsup.config.ts` compiles the TypeScript entry point (`src/main.ts`) into an ES module.\n* A custom post-processing step inside `tsup.config.ts` transforms the compiled file:\n 1. Strips ESM `export default` statements.\n 2. Wraps the controller inside LuCI's expected `return baseclass.extend({...})` pattern.\n 3. Injects global LuCI helper references (`ui`, `rpc`, `uci`, `fs`, `_`).\n\n> [!IMPORTANT]\n> **Do not attempt to convert `main.js` back into a standard ES module export.** This LuCI bridge is intentional and strictly required for compatibility with all OpenWrt web themes (Bootstrap, Material, Argon, OpenWrt-Two).\n\n---\n\n## 3. Reactive State Management & Services\n\nThe frontend does not use heavy external frameworks (like React or Vue) to keep package size minimal ($\\approx$ 500 KB uncompressed, $\\approx$ 90 KB gzipped). Instead, it uses lightweight TypeScript observable stores:\n\n### 3.1. Key Services (`src/tachyon/services/`)\n\n| Service | File | Purpose |\n|---|---|---|\n| `StoreService` | `store.service.ts` | Central reactive data store with subscription listeners. |\n| `UiStateService` | `uiState.service.ts` | Tracks UI view state, tab switching, and modal states. |\n| `RuntimeUiStateService`| `runtimeUiState.service.ts`| Polling and caching of backend service metrics and active jobs. |", "embedding": []}, {"id": 20, "file": "06_FRONTEND_LUCI_TYPESCRIPT.md", "heading": "4.1. The Updates & Installation Modal (`renderUpdateProgressModal.ts`)", "text": "| `UiStateService` | `uiState.service.ts` | Tracks UI view state, tab switching, and modal states. |\n| `RuntimeUiStateService`| `runtimeUiState.service.ts`| Polling and caching of backend service metrics and active jobs. |\n| `SocketService` | `socket.service.ts` | WebSocket client for real-time log streaming when available. |\n| `TachyonLogWatcher` | `tachyonLogWatcher.service.ts`| Logread stream deduplication and notification dispatcher. |\n\n---\n\n## 4. Tabs & UI View Structure\n\n```\nsrc/tachyon/tabs/\n├── dashboard/ # Live status, RTT ping matrix, traffic toggles\n├── diagnostic/ # AI Doctor modal, Local Rule Doctor, Emergency Fallback\n├── rules/ # Selective routing rules, domain/IP lists, client matchers\n├── providers/ # Zapret v1, Zapret v2, ByeDPI configurations\n├── updates/ # Component installer, version checks, streaming log modal\n└── settings/ # DNS settings, Telegram bot credentials, AI Doctor API keys\n```\n\n### 4.1. The Updates & Installation Modal (`renderUpdateProgressModal.ts`)\n* Provides a streaming log console that polls `/usr/bin/tachyon` output asynchronously via `componentActionLog(jobId, offset)`.\n* Features:\n * Non-blocking operation: router performs installation in background.\n * Real-time timer and progress badge.\n * Preserves output logs and green success/red error banners.\n * Prevents premature auto-close so users can inspect package manager details.\n * Graceful countdown timer for Tachyon package reloads.", "embedding": []}, {"id": 21, "file": "07_BUILD_TESTS_AND_DEPLOYMENT.md", "heading": "2.1. Installer Lifecycle", "text": "# 07. Build System, Testing & Deployment Pipeline\n\n## 1. Release Package Compilation (`build.sh`)\n\nTachyon produces both legacy **IPK** (opkg) and modern **APK** (apk-tools, OpenWrt 25.x+) packages.\n\n### 1.1. Running the Build\n\n```bash\nbash build.sh [output-dir]\n```\n\n* **Semver Requirement**: `build.sh` strictly enforces semantic versioning (`x.y.z`).\n* **Cross-Filesystem WSL Protection**: When executed under Windows Subsystem for Linux (WSL), `build.sh` automatically clones the source repository into the native Linux filesystem (`/tmp/tachyon-build-...`) before packaging to avoid Windows filesystem metadata and permission corruption.\n* **Outputs**:\n * `dist/release-final/tachyon__all.ipk`\n * `dist/release-final/luci-app-tachyon__all.ipk`\n * `dist/release-final/tachyon-.apk`\n * `dist/release-final/luci-app-tachyon-.apk`\n\n---\n\n## 2. End-User Installer (`install.sh`)\n\nTachyon provides an automated one-liner installer:\n\n```bash\nwget -O /tmp/tachyon-setup.sh https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh && sh /tmp/tachyon-setup.sh\n```\n\n### 2.1. Installer Lifecycle\n1. **Architecture & OS Detection**: Detects OpenWrt version (23.05, 24.10, 25.x, SNAPSHOT), CPU architecture (`mips`, `mipsel`, `arm_cortex-a7`, `aarch64_cortex-a53`, `x86_64`), and package manager (`opkg` vs `apk`).\n2. **Dependency Resolution**: Ensures `ucode`, `ucode-mod-fs`, `ucode-mod-uci`, `ucode-mod-ubus`, `kmod-nft-tproxy`, `kmod-nft-queue`, `ca-bundle` are installed.\n3. **Legacy Migration**: Automatically detects `/etc/config/forkop` or `/etc/config/podkop` and seamlessly migrates all rules and servers to `/etc/config/tachyon`.\n4. **Binary Provisioning**: Downloads pre-compiled, optimized sing-box, Zapret, or ByeDPI binaries tailored for the router's target architecture.\n5. **Zero-Reboot Initialization**: Configures UCI defaults, registers LuCI menus, builds initial rulesets, and starts services without requiring a full router reboot.", "embedding": []}, {"id": 22, "file": "07_BUILD_TESTS_AND_DEPLOYMENT.md", "heading": "4. Key Developer Gotchas & Conventions", "text": "4. **Binary Provisioning**: Downloads pre-compiled, optimized sing-box, Zapret, or ByeDPI binaries tailored for the router's target architecture.\n5. **Zero-Reboot Initialization**: Configures UCI defaults, registers LuCI menus, builds initial rulesets, and starts services without requiring a full router reboot.\n\n---\n\n## 3. Testing Workflows\n\n### 3.1. Frontend Unit Tests (vitest)\n\n```bash\ncd fe-app-tachyon\nnpm test # Runs all 522+ vitest unit tests\nnpm run build # Verifies TypeScript compilation and LuCI bundle packaging\n```\n\n### 3.2. Backend Integration Tests & ucode Syntax Linting\n\nBackend tests require the `ucode` binary. On development machines without local ucode, tests run inside the Docker CI image:\n\n```bash\n# 1. ucode syntax check across all 60+ modules\ndocker run --rm -v \"$(pwd):/mnt\" -w /mnt ucode-ci-img:latest bash tests/ucode_syntax_lint.sh\n\n# 2. Run all backend behavioral and unit tests in parallel (-P4)\ndocker run --rm -v \"$(pwd):/mnt\" -w /mnt ucode-ci-img:latest bash tests/run_all.sh\n\n# 3. Run a specific test\ndocker run --rm -v \"$(pwd):/mnt\" -w /mnt ucode-ci-img:latest bash tests/ai_doctor_local.sh\n```\n\n### 3.3. ShellCheck Linting\n\n```bash\nshellcheck --severity=error build.sh install.sh tachyon/files/etc/init.d/* tests/**/*.sh\n```\n\n---\n\n## 4. Key Developer Gotchas & Conventions\n\n1. **LF Line Endings**: Enforced via `.gitattributes`. Committing CRLF will break shell scripts on OpenWrt target systems.\n2. **`fe-app-tachyon` Package Manager**: Always use `npm` (`npm install`, `npm test`, `npm run build`), not `yarn`.\n3. **Version Placeholder**: `__COMPILED_VERSION_VARIABLE__` in `src/constants.ts` is replaced during `build.sh` packaging. Do not remove it.\n4. **UCI Config Permissions**: `/etc/config/tachyon` must remain `chmod 600` because it can store sensitive Telegram bot tokens and API keys.", "embedding": []}, {"id": 23, "file": "08_UCI_CONFIGURATION_SCHEMA.md", "heading": "1. Section: `config settings 'settings'`", "text": "# 08. Complete UCI Configuration Schema Reference\n\nThe primary configuration for Tachyon is stored at `/etc/config/tachyon` (UCI format, mode `0600`).\n\n---\n\n## 1. Section: `config settings 'settings'`\n\nGlobal settings defining service behaviour, DNS resolution, watchdog parameters, Telegram integration, and AI Doctor.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | `boolean` (`0`/`1`) | `1` | Master toggle for Tachyon service. |\n| `log_level` | `string` | `info` | Logging verbosity: `debug`, `info`, `warn`, `error`. |\n| `dns_mode` | `string` | `doh` | DNS routing mode: `direct`, `doh`, `dot`, `udp`, `hosts_only`. |\n| `remote_dns_server` | `string` | `https://1.1.1.1/dns-query` | Upstream secure DNS resolver for proxied domains. |\n| `direct_dns_server` | `string` | `77.88.8.8` | Fallback DNS resolver for direct / local domains. |\n| `tproxy_port` | `integer` | `10080` | Local TCP/UDP transparent proxy port for sing-box. |\n| `dns_port` | `integer` | `5353` | Local DNS listening port for sing-box DNS engine. |\n| `clash_api_port` | `integer` | `9090` | sing-box Clash REST API controller port. |\n| `clash_api_secret` | `string` | `\"\"` | Secret token for sing-box Clash API. |\n| `watchdog_interval` | `integer` | `10` | Watchdog polling interval in seconds (5–60s). |\n| `memory_limit_mb` | `integer` | `0` | Dynamic `GOMEMLIMIT` cap in MB (`0` = auto 50% free RAM). |\n| `enable_telegram` | `boolean` | `0` | Enable interactive Telegram management bot. |\n| `telegram_token` | `string` | `\"\"` | Telegram Bot API token from `@BotFather`. |\n| `telegram_chat_id` | `string` | `\"\"` | Allowed Admin Telegram Chat ID(s) (comma-separated). |\n| `enable_ai_doctor` | `boolean` | `0` | Enable LLM AI Doctor diagnostic engine. |\n| `ai_doctor_provider`| `string` | `openai` | LLM Provider: `openai`, `anthropic`, `deepseek`, `custom`. |\n| `ai_doctor_model` | `string` | `\"\"` | Model override (e.g. `gpt-4o-mini`, `claude-3-5-haiku`). |\n| `ai_doctor_api_key`| `string` | `\"\"` | API key for the chosen LLM provider. |", "embedding": []}, {"id": 24, "file": "08_UCI_CONFIGURATION_SCHEMA.md", "heading": "3. Section: `config subscription 'name'`", "text": "| `ai_doctor_provider`| `string` | `openai` | LLM Provider: `openai`, `anthropic`, `deepseek`, `custom`. |\n| `ai_doctor_model` | `string` | `\"\"` | Model override (e.g. `gpt-4o-mini`, `claude-3-5-haiku`). |\n| `ai_doctor_api_key`| `string` | `\"\"` | API key for the chosen LLM provider. |\n| `ai_doctor_custom_url`| `string` | `\"\"` | Custom OpenAI-compatible endpoint URL (e.g. OpenRouter/Ollama). |\n| `agent_api_token` | `string` | `\"\"` | Bearer token for HTTP REST Agent API write access. |\n\n---\n\n## 2. Section: `config server 'name'`\n\nDefines an individual manual proxy outbound node.\n\n| Option | Type | Required | Description |\n|---|---|---|---|\n| `type` | `string` | Yes | Protocol: `vless`, `vmess`, `shadowsocks`, `trojan`, `hysteria2`, `wireguard`. |\n| `server` | `string` | Yes | Remote server hostname or IPv4/IPv6 address. |\n| `server_port` | `integer` | Yes | Remote server port (e.g. `443`, `8443`). |\n| `uuid` | `string` | Conditional | User UUID for VLESS / VMess. |\n| `password` | `string` | Conditional | Password for Shadowsocks / Trojan / Hysteria2. |\n| `flow` | `string` | No | Flow control: `xtls-rprx-vision` (VLESS). |\n| `security` | `string` | No | Security type: `reality`, `tls`, `none`. |\n| `sni` | `string` | Conditional | Server Name Indication for TLS / Reality. |\n| `pbk` | `string` | Conditional | Public Key for VLESS Reality. |\n| `sid` | `string` | Conditional | Short ID for VLESS Reality (hex string). |\n| `fingerprint` | `string` | No | TLS fingerprint: `chrome`, `firefox`, `safari`, `randomized`. |\n| `method` | `string` | Conditional | Cipher method for Shadowsocks (e.g. `2022-blake3-aes-128-gcm`). |\n\n---\n\n## 3. Section: `config subscription 'name'`\n\nDefines a remote subscription URL for automated batch node fetching.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | `boolean` | `1` | Enable this subscription source. |\n| `url` | `string` | Required | HTTPS / HTTP link to subscription (Base64, Clash, SIP002). |\n| `update_interval`| `integer` | `86400` | Auto-update interval in seconds (default 24 hours). |", "embedding": []}, {"id": 25, "file": "08_UCI_CONFIGURATION_SCHEMA.md", "heading": "Recipe A: Zero-VPS Local DPI Bypass (YouTube + Discord)", "text": "| `enabled` | `boolean` | `1` | Enable this subscription source. |\n| `url` | `string` | Required | HTTPS / HTTP link to subscription (Base64, Clash, SIP002). |\n| `update_interval`| `integer` | `86400` | Auto-update interval in seconds (default 24 hours). |\n| `custom_user_agent`| `string` | `\"\"` | Custom User-Agent header for subscription request. |\n| `filter_keywords` | `list string` | `[]` | Include nodes matching regex/keywords. |\n| `exclude_keywords`| `list string` | `[]` | Exclude nodes matching regex/keywords. |\n\n---\n\n## 4. Section: `config rule 'name'`\n\nDefines a selective routing policy.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | `boolean` | `1` | Enable this routing rule. |\n| `name` | `string` | `\"\"` | Human-readable label for UI. |\n| `action` | `string` | `proxy` | Route target: `proxy`, `zapret`, `zapret2`, `byedpi`, `direct`, `hosts`. |\n| `target_server` | `string` | `\"\"` | Bound server ID or outbound tag when `action = 'proxy'`. |\n| `domain_pattern` | `list string` | `[]` | List of target domains or wildcards (e.g. `*.youtube.com`). |\n| `ip_cidr` | `list string` | `[]` | List of IP subnets (e.g. `104.16.0.0/12`). |\n| `client_group` | `string` | `\"\"` | Restrict this rule to a specific client device group. |\n\n---\n\n## 5. Section: `config hosts 'name'`\n\nConfigures static DNS overrides and remote blocklists ingestion.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | `boolean` | `1` | Enable hosts mapping. |\n| `dns_hosts` | `list string` | `[]` | Static mappings in format `domain:ip` (e.g. `example.com:1.2.3.4`). |\n| `hosts_list_urls` | `list string` | `[]` | External URLs for blocklists (AdAway, StevenBlack). |\n| `update_interval` | `integer` | `86400` | Auto-refresh interval in seconds. |\n\n---\n\n## 6. Ready-to-Use Configuration Recipes\n\n### Recipe A: Zero-VPS Local DPI Bypass (YouTube + Discord)\n```ini\nconfig settings 'settings'\n option enabled '1'\n option dns_mode 'doh'\n\nconfig provider 'zapret2'", "embedding": []}, {"id": 26, "file": "08_UCI_CONFIGURATION_SCHEMA.md", "heading": "Recipe B: Hybrid Mode (Local Desync for Media + VLESS Reality for Blocked Sites)", "text": "## 6. Ready-to-Use Configuration Recipes\n\n### Recipe A: Zero-VPS Local DPI Bypass (YouTube + Discord)\n```ini\nconfig settings 'settings'\n option enabled '1'\n option dns_mode 'doh'\n\nconfig provider 'zapret2'\n option enabled '1'\n option daemon_bin '/usr/bin/nfqws2'\n option qnum '201'\n option args_tcp '--dpi-desync=multisplit --dpi-desync-split-pos=1,midsld --dpi-desync-seqovl=1 --dpi-desync-fooling=badseq'\n\nconfig rule 'youtube_discord'\n option enabled '1'\n option name 'YouTube & Discord Local Desync'\n option action 'zapret2'\n list domain_pattern 'googlevideo.com'\n list domain_pattern 'youtube.com'\n list domain_pattern 'ytimg.com'\n list domain_pattern 'discord.com'\n list domain_pattern 'discord.gg'\n```\n\n### Recipe B: Hybrid Mode (Local Desync for Media + VLESS Reality for Blocked Sites)\n```ini\nconfig server 'my_vps'\n option type 'vless'\n option server 'vps.example.com'\n option server_port '443'\n option uuid '12345678-1234-1234-1234-123456789abc'\n option flow 'xtls-rprx-vision'\n option security 'reality'\n option sni 'yahoo.com'\n option pbk 'XYZ...PUBLIC_KEY...'\n option sid '1a2b3c4d'\n\nconfig rule 'media_rule'\n option enabled '1'\n option action 'zapret2'\n list domain_pattern 'youtube.com'\n list domain_pattern 'googlevideo.com'\n\nconfig rule 'blocked_rule'\n option enabled '1'\n option action 'proxy'\n option target_server 'my_vps'\n list domain_pattern 'instagram.com'\n list domain_pattern 'twitter.com'\n list domain_pattern 'x.com'\n```", "embedding": []}, {"id": 27, "file": "09_TELEGRAM_BOT_ARCHITECTURE.md", "heading": "2. Command Set Reference", "text": "# 09. Telegram Bot Architecture & Management Protocol\n\nTachyon includes an embedded, high-performance Telegram management bot written entirely in **ucode** (`service/telegram.uc`).\n\n---\n\n## 1. Architectural Highlights\n\n```mermaid\ngraph LR\n User[Telegram Client] -->|Encrypted HTTPS| TGAPI[Telegram Bot API Servers]\n TGAPI -->|Long Polling: getUpdates| Daemon[Tachyon telegram.uc Daemon]\n \n Daemon --> Auth{Admin Chat ID Match?}\n Auth -->|No| Reject[Silent Ignore / Warning]\n Auth -->|Yes| Router[Command & Callback Dispatcher]\n \n Router -->|/doctor, /ai_doctor| AIDoc[AI Doctor Engine]\n Router -->|/server, /switch| SrvMgr[Server Manager]\n Router -->|/restart| SvcLife[Service Lifecycle]\n Router -->|/status| StateRep[State Assembler]\n```\n\n* **No Public IP or Port Forwarding Required**: Uses outbound long-polling (`getUpdates` with 30s timeout) to securely communicate through NAT/firewalls.\n* **Zero Overhead Memory Footprint**: Runs as a lightweight ucode process consuming under 2 MB RAM.\n* **Role-Based Access Control**: Rejects any commands from chat IDs not explicitly listed in UCI option `telegram_chat_id`.\n\n---\n\n## 2. Command Set Reference\n\n| Command | Arguments | Action |\n|---|---|---|\n| `/start` | — | Displays main welcome card and interactive control keyboard. |\n| `/status` | — | Returns real-time health card: uptime, active server, RAM usage, ping RTT. |\n| `/doctor` | — | Executes Local Rule Doctor and returns offline diagnostic report. |\n| `/ai_doctor`| — | Invokes AI Doctor with LLM reasoning and returns diagnosis + fix buttons. |\n| `/fix` | `` | Applies specific automated repair code (e.g. `/fix clear_dns_cache`). |\n| `/server` | — | Opens interactive server selector with inline pagination and RTT pings. |\n| `/rules` | — | Shows active routing rules with 1-click toggle switches. |\n| `/restart` | `[service]` | Restarts sing-box, dnsmasq, watchdog, or entire network stack. |\n| `/backup` | — | Sends latest atomic configuration backup directly to Telegram chat. |", "embedding": []}, {"id": 28, "file": "09_TELEGRAM_BOT_ARCHITECTURE.md", "heading": "Commit and restart service", "text": "| `/rules` | — | Shows active routing rules with 1-click toggle switches. |\n| `/restart` | `[service]` | Restarts sing-box, dnsmasq, watchdog, or entire network stack. |\n| `/backup` | — | Sends latest atomic configuration backup directly to Telegram chat. |\n\n---\n\n## 3. Inline Keyboard & Callback Query Protocol\n\nThe bot uses dynamic inline keyboards (`InlineKeyboardMarkup`) for single-tap actions without typing commands.\n\n### 3.1. Callback Data Scheme\nCallbacks follow a structured token format: `action:target:param`\n\n```\nsrv:select:node_1 # Switch active proxy server to node_1\nrule:toggle:youtube # Toggle routing rule 'youtube' on/off\nfix:apply:clear_dns_cache# Execute repair code 'clear_dns_cache'\npage:srv:2 # Navigate to page 2 of server list\n```\n\n### 3.2. Example: Server Switcher Response\n\n```json\n{\n \"chat_id\": 123456789,\n \"text\": \"⚡ Выберите активный сервер:\\nТекущий: 🇩🇪 Frankfurt VLESS (18 ms)\",\n \"parse_mode\": \"HTML\",\n \"reply_markup\": {\n \"inline_keyboard\": [\n [\n {\"text\": \"🇩🇪 Frankfurt (18 ms) ✅\", \"callback_data\": \"srv:select:de_1\"},\n {\"text\": \"🇳🇱 Amsterdam (24 ms)\", \"callback_data\": \"srv:select:nl_1\"}\n ],\n [\n {\"text\": \"🇫🇮 Helsinki (31 ms)\", \"callback_data\": \"srv:select:fi_1\"},\n {\"text\": \"🇺🇸 New York (95 ms)\", \"callback_data\": \"srv:select:us_1\"}\n ],\n [\n {\"text\": \"🔄 Замерить пинг\", \"callback_data\": \"srv:ping:all\"},\n {\"text\": \"🩺 Запустить Доктора\", \"callback_data\": \"cmd:doctor\"}\n ]\n ]\n }\n}\n```\n\n---\n\n## 4. Configuration via CLI\n\n```sh\n# Set Bot Token\nuci set tachyon.settings.enable_telegram='1'\nuci set tachyon.settings.telegram_token='123456789:ABCdefGHIjklMNOpqrsTUVwxyz'\n\n# Set Authorized Chat ID(s)\nuci set tachyon.settings.telegram_chat_id='123456789'\n\n# Commit and restart service\nuci commit tachyon\n/etc/init.d/tachyon restart\n```", "embedding": []}, {"id": 29, "file": "10_HOSTS_ENGINE_AND_BLOCKLISTS.md", "heading": "Example UCI Configuration:", "text": "# 10. Hosts Engine, Blocklists & Smart Caching\n\nThe Hosts Engine (`components/hosts.uc`) provides automated background ingestion, normalization, deduplication, and caching of external host lists and static DNS overrides.\n\n---\n\n## 1. Architecture of Hosts Engine\n\n```mermaid\ngraph TD\n Sources[External Remote URLs: AdAway, StevenBlack, Custom] --> Downloader[HTTP Fetcher with Mirror Retry]\n Static[Static dns_hosts in UCI] --> Downloader\n \n Downloader --> Parser[Multi-Format Parser: Hosts, AdBlock, Domains]\n Parser --> Deduper[Deduplication & Sorting Engine]\n Deduper --> Cache[Unified Cache: /tmp/tachyon/hosts/combined.txt]\n \n Cache --> DNSMasq[dnsmasq /etc/dnsmasq.d/tachyon-hosts.conf]\n Cache --> SingboxDNS[sing-box DNS hosts rule]\n```\n\n---\n\n## 2. Multi-Format Ingestion\n\nThe parser automatically detects and normalizes multiple common blocklist formats:\n\n1. **Standard `/etc/hosts` format**:\n ```\n 0.0.0.0 ads.example.com\n 127.0.0.1 tracker.adserver.net\n ```\n2. **Domain-only list format**:\n ```\n malware-domain.org\n telemetry.app.com\n ```\n3. **AdBlock / DNSmasq syntax**:\n ```\n ||badsite.com^\n server=/blocked.com/\n address=/ads.org/0.0.0.0\n ```\n\n---\n\n## 3. GitHub Mirror Retry Resiliency\n\nWhen fetching blocklists hosted on GitHub (e.g. `raw.githubusercontent.com`), access may be blocked or throttled by regional DPI.\n\nTachyon implements an automated fallback retry chain:\n1. **Primary**: Direct HTTPS request to target URL.\n2. **Mirror 1**: `https://gh-proxy.com/`\n3. **Mirror 2**: `https://ghfast.top/`\n4. **Mirror 3**: `https://ghproxy.net/`\n\nIf direct connection fails with timeout or SSL handshake error, Tachyon transparently cycles through available mirrors before reporting an error.\n\n---\n\n## 4. Dedicated Hosts Routing (`action = 'hosts'`)\n\nTachyon supports routing sections that operate purely as DNS-overriding engines without requiring an outbound proxy association.\n\n### Example UCI Configuration:", "embedding": []}, {"id": 30, "file": "10_HOSTS_ENGINE_AND_BLOCKLISTS.md", "heading": "Example UCI Configuration:", "text": "## 4. Dedicated Hosts Routing (`action = 'hosts'`)\n\nTachyon supports routing sections that operate purely as DNS-overriding engines without requiring an outbound proxy association.\n\n### Example UCI Configuration:\n```ini\nconfig rule 'adblock_section'\n option enabled '1'\n option name 'AdBlock & Anti-Tracker'\n option action 'hosts'\n list hosts_list_urls 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts'\n list hosts_list_urls 'https://adaway.org/hosts.txt'\n```\n\n* Domains in this section are redirected directly to `0.0.0.0` or custom IPs in dnsmasq / sing-box DNS.\n* Zero traffic is routed to external proxies, preserving VPS bandwidth and minimizing latency.", "embedding": []}, {"id": 31, "file": "11_TROUBLESHOOTING_AND_FIELD_MANUAL.md", "heading": "2. Run AI Doctor with LLM analysis", "text": "# 11. Troubleshooting & Field Diagnostic Manual\n\nA comprehensive guide for diagnosing and resolving common network, proxy, and routing issues on OpenWrt routers running Tachyon.\n\n---\n\n## 1. Quick Emergency Recovery\n\nIf router traffic drops completely or DNS resolution is broken, use the **Emergency Native Internet Restoration** command:\n\n```sh\ntachyon restore_native_internet\n```\n\nThis performs the following actions in under 200 ms:\n1. Atomically drops all nftables interception tables (`nft delete table inet TachyonTable`).\n2. Clears policy routing `table 100` and `ip rule` entries.\n3. Restores dnsmasq to stock upstream forwarders.\n4. Stops sing-box and DPI bypass daemons without rebooting the router.\n\n---\n\n## 2. Symptom Resolution Matrix\n\n| Symptom | Likely Cause | Recommended Fix / Command |\n|---|---|---|\n| **Websites fail to open, ping to 8.8.8.8 works** | DNS loop or dnsmasq forwarder crash. | `tachyon apply_quick_fix clear_dns_cache,fix_dnsmasq` |\n| **YouTube 4K buffers indefinitely** | Zapret strategy out-of-date for current ISP. | Switch Zapret v2 strategy to `multisplit` with `seqovl=1`. |\n| **sing-box fails to start after config change** | Syntax error in custom outbound or invalid UUID. | Check `/var/log/tachyon_service.log` and run `tachyon doctor`. |\n| **System clock desynchronized, TLS fails** | Router rebooted without RTC battery, NTP desync. | `tachyon apply_quick_fix fix_system_time` |\n| **High latency or packet drop under heavy load** | Kernel `nf_conntrack` table overflow. | `tachyon apply_quick_fix flush_conntrack` |\n| **VLESS Reality connection timeout** | Reality SNI domain blocked or expired short ID. | Update server SNI to accessible target (e.g. `yahoo.com`). |\n| **Router runs out of RAM (OOM Crash)** | sing-box buffer explosion on low-RAM device (128 MB). | Lower `memory_limit_mb` in UCI settings (`GOMEMLIMIT=40MiB`). |\n\n---\n\n## 3. Diagnostic Commands Reference\n\n```sh\n# 1. Run local offline rule checks\ntachyon doctor\n\n# 2. Run AI Doctor with LLM analysis", "embedding": []}, {"id": 32, "file": "11_TROUBLESHOOTING_AND_FIELD_MANUAL.md", "heading": "4. Log Inspection Paths", "text": "| **Router runs out of RAM (OOM Crash)** | sing-box buffer explosion on low-RAM device (128 MB). | Lower `memory_limit_mb` in UCI settings (`GOMEMLIMIT=40MiB`). |\n\n---\n\n## 3. Diagnostic Commands Reference\n\n```sh\n# 1. Run local offline rule checks\ntachyon doctor\n\n# 2. Run AI Doctor with LLM analysis\ntachyon ai_doctor\n\n# 3. View real-time Watchdog telemetry\ntachyon ai_status_full\n\n# 4. Inspect active nftables firewall tables\nnft list table inet TachyonTable\n\n# 5. Inspect policy routing table 100\nip route show table 100\nip rule show\n\n# 6. Stream live Tachyon system logs\nlogread -f -e tachyon\n\n# 7. Check listening ports (TProxy 10080, DNS 5353, Clash 9090)\nnetstat -tulpn | grep -E 'sing-box|tachyon|5353|10080'\n```\n\n---\n\n## 4. Log Inspection Paths\n\n| Log Location | Content Description |\n|---|---|\n| `/var/log/tachyon_service.log` | Core service lifecycle events (start, stop, reload). |\n| `/var/log/tachyon_watchdog.log` | Background supervisor health checks and loop warnings. |\n| `/tmp/ai_doctor_last.json` | JSON report of the most recent AI Doctor diagnosis. |\n| `/tmp/tachyon/sing-box.json` | Generated live configuration compiled for sing-box. |\n| `/tmp/tachyon/hosts/combined.txt` | Consolidated deduplicated hosts list. |", "embedding": []}, {"id": 33, "file": "12_UCODE_PROGRAMMING_PATTERNS.md", "heading": "2.3. Safe Error Handling (`try-catch`)", "text": "# 12. ucode Programming Patterns & Conventions\n\nA developer's guide to writing safe, high-performance, and idiomatic **ucode** modules for Tachyon on OpenWrt.\n\n---\n\n## 1. ucode vs JavaScript: Critical Distinctions\n\nWhile ucode syntax resembles JavaScript, its runtime semantics differ significantly:\n\n1. **Variables & Scope**:\n * Always declare variables with `let` or `const`.\n * Unassigned variables are `null` (not `undefined`).\n2. **Arrays vs Dictionaries**:\n * Arrays: `[1, 2, 3]`. Iterated with `for (let item in array)`.\n * Dictionaries (Objects): `{\"key\": \"value\"}`. Iterated with `for (let key, val in dict)`.\n3. **Regular Expressions**:\n * Use `match(str, /regex/)` or `replace(str, /regex/g, \"replacement\")`.\n * Global flag `/g` works with `replace()`, while `match()` returns capture groups array `[full_match, group1, ...]`.\n4. **Boolean Coercion**:\n * Empty strings `\"\"`, `0`, and `null` are falsy. Everything else is truthy.\n\n---\n\n## 2. Mandatory Core Design Patterns\n\n### 2.1. Atomic Configuration Writes\nNever overwrite active configuration files in-place. Always write to a temporary file in `/tmp/` and atomically rename:\n\n```ucode\nlet fs = require(\"fs\");\n\nfunction write_atomic(path, content) {\n let tmp_path = path + \".tmp.\" + int(time());\n let f = fs.open(tmp_path, \"w\");\n if (!f) return false;\n \n f.write(content);\n f.close();\n \n return fs.rename(tmp_path, path);\n}\n```\n\n### 2.2. Shell Argument Quoting\nWhen executing external shell commands, all user inputs must be quoted safely:\n\n```ucode\nfunction shell_quote(value) {\n return \"'\" + replace(\"\" + value, /'/g, \"'\\\\''\") + \"'\";\n}\n\nfunction safe_exec(cmd_args) {\n let parts = [];\n for (let arg in cmd_args)\n push(parts, shell_quote(arg));\n return system(join(\" \", parts));\n}\n```\n\n### 2.3. Safe Error Handling (`try-catch`)\nAll external library loading or file parsing must be wrapped in `try-catch` blocks to avoid unhandled script panics:\n\n```ucode\nlet uci_data = null;", "embedding": []}, {"id": 34, "file": "12_UCODE_PROGRAMMING_PATTERNS.md", "heading": "3. Performance & Memory Optimization Guidelines", "text": "return system(join(\" \", parts));\n}\n```\n\n### 2.3. Safe Error Handling (`try-catch`)\nAll external library loading or file parsing must be wrapped in `try-catch` blocks to avoid unhandled script panics:\n\n```ucode\nlet uci_data = null;\ntry {\n let uci = require(\"uci\").cursor();\n uci_data = uci.get_all(\"tachyon\", \"settings\");\n}\ncatch (e) {\n warn(\"Failed to load UCI settings: \" + e + \"\\n\");\n}\n```\n\n---\n\n## 3. Performance & Memory Optimization Guidelines\n\n1. **Stream Large Files**: Avoid loading multi-megabyte blocklists entirely into RAM with `fs.readfile()`. Use line-by-line streaming via `fs.open()` and `f.read(\"line\")`.\n2. **Avoid Spawning Subprocesses in Loops**: Invoking `system()` or `fs.popen()` spawns a fork-exec sequence in Linux. Use native ucode functions for string manipulation and path operations whenever possible.\n3. **Keep Modules Stateless**: Module files under `usr/lib/tachyon/` should export reusable pure functions or class definitions rather than maintaining mutable global state.", "embedding": []}, {"id": 35, "file": "13_REST_API_AND_MCP_SPECIFICATION.md", "heading": "4. Model Context Protocol (MCP) Tool Definitions", "text": "# 13. HTTP REST Agent API & Model Context Protocol (MCP) Specification\n\nTachyon provides a standardized RESTful Agent API and Model Context Protocol (MCP) integration, enabling seamless orchestration by autonomous LLM agents (ChatGPT Actions, Claude Code, Cursor, Windsurf, N8N, Dify).\n\n---\n\n## 1. REST API Overview\n\n* **Base URL**: `http:///cgi-bin/tachyon-agent/`\n* **OpenAPI Schema**: `http:///cgi-bin/tachyon-agent/openapi.json`\n* **Content-Type**: `application/json`\n\n---\n\n## 2. Authentication Model\n\n1. **Read-Only Endpoints**: Unrestricted when accessed from local LAN.\n2. **Write Endpoints**: Require HTTP Authorization header:\n ```http\n Authorization: Bearer \n ```\n\n---\n\n## 3. Endpoint Reference & Payloads\n\n### 3.1. `GET /health`\nReturns lightweight status of all core daemons.\n```json\n{\n \"status\": \"healthy\",\n \"version\": \"1.2.86\",\n \"uptime_seconds\": 18450,\n \"singbox_running\": true,\n \"watchdog_running\": true,\n \"active_server\": \"Frankfurt_VLESS_Reality\"\n}\n```\n\n### 3.2. `GET /diagnose`\nReturns Local Rule Doctor checks and suggested repair codes.\n```json\n{\n \"status\": \"warning\",\n \"checks\": {\n \"dns_loop\": false,\n \"conntrack_full\": false,\n \"time_desync\": true,\n \"singbox_ok\": true\n },\n \"suggested_fixes\": [\"fix_system_time\"]\n}\n```\n\n### 3.3. `POST /ai-doctor/fix`\nApplies one or more quick-fix codes.\n* **Request**:\n ```json\n {\n \"fix\": \"clear_dns_cache,start_singbox\"\n }\n ```\n* **Response**:\n ```json\n {\n \"success\": true,\n \"applied_fixes\": [\"clear_dns_cache\", \"start_singbox\"],\n \"details\": \"DNS cache cleared. sing-box process restarted.\"\n }\n ```\n\n---\n\n## 4. Model Context Protocol (MCP) Tool Definitions\n\nFor MCP-compatible agents (Cursor, Claude Desktop), Tachyon exports tools via `GET /cgi-bin/tachyon-agent/tools`:\n\n```json\n[\n {\n \"name\": \"tachyon_diagnose\",\n \"description\": \"Run diagnostic checks on OpenWrt router network and proxy health\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {}", "embedding": []}, {"id": 36, "file": "13_REST_API_AND_MCP_SPECIFICATION.md", "heading": "4. Model Context Protocol (MCP) Tool Definitions", "text": "```json\n[\n {\n \"name\": \"tachyon_diagnose\",\n \"description\": \"Run diagnostic checks on OpenWrt router network and proxy health\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {}\n }\n },\n {\n \"name\": \"tachyon_apply_fix\",\n \"description\": \"Apply automated repair codes to fix router network or proxy issues\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"fix_codes\": {\n \"type\": \"string\",\n \"description\": \"Comma-separated list of fix codes (e.g. clear_dns_cache,restart_singbox)\"\n }\n },\n \"required\": [\"fix_codes\"]\n }\n },\n {\n \"name\": \"tachyon_switch_server\",\n \"description\": \"Switch active proxy server node\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"server_id\": {\n \"type\": \"string\",\n \"description\": \"Identifier of target proxy server\"\n }\n },\n \"required\": [\"server_id\"]\n }\n }\n]\n```", "embedding": []}, {"id": 37, "file": "INDEX.md", "heading": "📚 Knowledge Base Table of Contents", "text": "# Tachyon Knowledge Base & Architecture Index\n\nWelcome to the comprehensive technical knowledge base for **Tachyon**. This directory contains deep architectural specifications, module references, packet flow maps, and developer guidelines compiled directly from the codebase.\n\n---\n\n## 📚 Knowledge Base Table of Contents\n\n| Document | Description | Key Topics Covered |\n|---|---|---|\n| [**01. System Architecture**](01_SYSTEM_ARCHITECTURE.md) | High-level system architecture & control plane | ucode runtime, atomic writes, repository layout, daemon lifecycle |\n| [**02. ucode Module Reference**](02_UCODE_MODULE_REFERENCE.md) | Comprehensive reference for all 60+ `.uc` files | `core/`, `service/`, `diagnostics/`, `singbox/`, `providers/`, `nft/`, `dns/` |\n| [**03. Networking & nftables**](03_NETWORKING_NFTABLES_AND_ROUTING.md) | Packet lifecycle, firewall tables & DNS routing | `inet TachyonTable`, fwmark bitmasks, TProxy, bootstrap DNS loops |\n| [**04. DPI Bypass Engines**](04_DPI_BYPASS_ENGINES.md) | Local packet desynchronization deep dive | Zapret v1 (`nfqws`), Zapret v2 (`nfqws2`), ByeDPI (`ciadpi`), multisplit, seqovl |\n| [**05. AI Stack & AI Doctor**](05_AI_STACK_AND_AUTONOMOUS_DOCTOR.md) | AI Doctor v2.5, Watchdog & REST Agent API | 13 Quick Fixes, Local Rule Doctor, NTP/MTU tuning, OpenAPI 3.0.3, REST API |\n| [**06. Frontend LuCI Architecture**](06_FRONTEND_LUCI_TYPESCRIPT.md) | TypeScript SPA & LuCI view integration | `baseclass.extend` patch, reactive stores, live log streaming modal |\n| [**07. Build & Testing Pipeline**](07_BUILD_TESTS_AND_DEPLOYMENT.md) | Compilation, testing & release workflows | `build.sh` (ipk/apk), `install.sh`, Docker CI container, vitest tests |\n| [**08. UCI Configuration Schema**](08_UCI_CONFIGURATION_SCHEMA.md) | Complete `/etc/config/tachyon` reference | All UCI sections, options, validators, default values, ready recipes |\n| [**09. Telegram Bot Architecture**](09_TELEGRAM_BOT_ARCHITECTURE.md) | Remote Telegram control daemon protocol | Long-polling model, inline keyboards, callback query routing, security |", "embedding": []}, {"id": 38, "file": "INDEX.md", "heading": "Frontend vitest suite", "text": "| [**08. UCI Configuration Schema**](08_UCI_CONFIGURATION_SCHEMA.md) | Complete `/etc/config/tachyon` reference | All UCI sections, options, validators, default values, ready recipes |\n| [**09. Telegram Bot Architecture**](09_TELEGRAM_BOT_ARCHITECTURE.md) | Remote Telegram control daemon protocol | Long-polling model, inline keyboards, callback query routing, security |\n| [**10. Hosts Engine & Blocklists**](10_HOSTS_ENGINE_AND_BLOCKLISTS.md) | Hosts ingestion, parsing & smart caching | `combined.txt`, multi-format parsing, GitHub mirror retry resiliency |\n| [**11. Troubleshooting Manual**](11_TROUBLESHOOTING_AND_FIELD_MANUAL.md) | Field diagnostics & emergency recovery | Symptom resolution matrix, diagnostic commands, log inspection paths |\n| [**12. ucode Programming Patterns**](12_UCODE_PROGRAMMING_PATTERNS.md) | Developer guidelines for OpenWrt ucode | Atomic file writes, shell quoting, libuci bindings, RAM efficiency |\n| [**13. REST API & MCP Specification**](13_REST_API_AND_MCP_SPECIFICATION.md) | REST Agent API & MCP tool definitions | OpenAPI 3.0.3, Bearer Auth, OpenAI Function Calling, MCP JSON schema |\n\n---\n\n## ⚡ Quick Operational Cheat Sheet\n\n### Common CLI Commands\n```sh\n# Diagnostics & AI\ntachyon doctor # Local offline rule diagnostics\ntachyon ai_doctor # AI Doctor LLM analysis\ntachyon apply_quick_fix # Apply automated repair codes\ntachyon restore_native_internet # 1-Click emergency WAN restore\n\n# Service & Watchdog\ntachyon ai_heal # Trigger immediate self-healing cycle\ntachyon ai_status # Concise Watchdog status JSON\ntachyon ai_status_full # Full Watchdog telemetry JSON\n\n# Component Management\ntachyon check_update # Check for component updates\ntachyon component_action install # Install or update component\n```\n\n### Local Test Execution\n```sh\n# Frontend vitest suite\nnpm --prefix fe-app-tachyon test", "embedding": []}, {"id": 39, "file": "INDEX.md", "heading": "Full backend suite in Docker CI", "text": "tachyon check_update # Check for component updates\ntachyon component_action install # Install or update component\n```\n\n### Local Test Execution\n```sh\n# Frontend vitest suite\nnpm --prefix fe-app-tachyon test\n\n# Full backend suite in Docker CI\ndocker run --rm -v \"$(pwd):/mnt\" -w /mnt ucode-ci-img:latest bash tests/run_all.sh\n```", "embedding": []}]} \ No newline at end of file diff --git a/tests/runtime_state_predicates.sh b/tests/runtime_state_predicates.sh index f1d64d06..440c0593 100644 --- a/tests/runtime_state_predicates.sh +++ b/tests/runtime_state_predicates.sh @@ -993,6 +993,12 @@ ws [server.srv1.tailscale_hostname] +[server.srv1.tailscale_mode] +singbox +[server.srv1.tailscale_masquerade] +1 +[server.srv1.tailscale_use_exit_node] +0 [server.srv1.tailscale_accept_routes] 1 [server.srv1.tailscale_advertise_routes] diff --git a/tests/tailscale_native_mode.sh b/tests/tailscale_native_mode.sh new file mode 100644 index 00000000..8adc0d6c --- /dev/null +++ b/tests/tailscale_native_mode.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +set -eo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib" +GENERATOR_UC="$TACHYON_LIB/singbox/generator.uc" +VALIDATOR_UC="$TACHYON_LIB/config/validator.uc" +RUNTIME_UC="$TACHYON_LIB/providers/tailscale/runtime.uc" +WORK_DIR="$(mktemp -d)" + +cleanup() { + rm -rf "$WORK_DIR" +} +trap cleanup EXIT + +fail() { + printf 'FAIL: %s\n' "$1" >&2 + exit 1 +} + +generate_config() { + local fixture="$1" + local output="$2" + mkdir -p "${output}.section-cache" + ucode -L "$TACHYON_LIB" "$GENERATOR_UC" generate-config-fixture \ + "$fixture" "$output" "127.0.0.1" +} + +make_fixture() { + local mode="$1" + cat >"$WORK_DIR/ts_$mode.json" <"$STUB_BIN/logger" <> '$LOG_FILE' +exit 0 +SH +chmod +x "$STUB_BIN/logger" + +validate() { + : >"$LOG_FILE" + PATH="$STUB_BIN:$PATH" ucode -L "$TACHYON_LIB" "$VALIDATOR_UC" \ + validate-runtime-fixture "$1" "{}" +} + +if validate "$WORK_DIR/ts_native.json"; then + fail "validator must reject native mode without tailscaled" +fi +grep -Fq "tailscaled is not installed" "$LOG_FILE" || + fail "validator must require the Tailscale component for native mode" + +# With tailscaled available the validator passes for native mode. +cat >"$STUB_BIN/tailscaled" <<'SH' +#!/usr/bin/env sh +exit 0 +SH +chmod +x "$STUB_BIN/tailscaled" +validate "$WORK_DIR/ts_native.json" || + fail "validator must accept native mode with tailscaled available" +grep -Fq "tailscaled is not installed" "$LOG_FILE" && + fail "validator rejected native mode even though tailscaled is available" + +# Native mode without an auth key is rejected: `tailscale up` would fall back +# to an interactive login and hang the runtime on a headless router. +sed 's/"tailscale_auth_key": "tskey-auth-secret"//' \ + "$WORK_DIR/ts_native.json" >"$WORK_DIR/ts_nokey.json" +if PATH="$STUB_BIN:$PATH" ucode -L "$TACHYON_LIB" "$VALIDATOR_UC" \ + validate-runtime-fixture "$WORK_DIR/ts_nokey.json" "{}" >/dev/null 2>&1; then + fail "validator must reject native mode without an auth key" +fi +grep -Fq "without an auth key" "$LOG_FILE" || + fail "validator message about missing auth key not reported" + +# Runtime provider module: CLI surface responds without a config. +OUTPUT=$(ucode -L "$TACHYON_LIB" "$RUNTIME_UC" package-version) +[ "$OUTPUT" = "" ] || fail "package-version should be empty without binaries, got: $OUTPUT" + +ucode -L "$TACHYON_LIB" "$RUNTIME_UC" status >/dev/null || + fail "status mode must always produce JSON" + +printf 'config tailscale native mode checks passed\n' diff --git a/tests/tailscale_native_runtime.sh b/tests/tailscale_native_runtime.sh new file mode 100644 index 00000000..3f26a343 --- /dev/null +++ b/tests/tailscale_native_runtime.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +# Smoke test for providers/tailscale/runtime.uc: runs the real start/stop +# runtime paths against stubbed tailscale/tailscaled binaries and verifies +# every kernel/nftables/dnsmasq side effect appears and is cleaned up. +# Requires a privileged OpenWrt container (ip rule, nft tables). +set -eo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib" +RUNTIME_UC="$TACHYON_LIB/providers/tailscale/runtime.uc" + +if [ "$(id -u)" != "0" ]; then + printf 'skipped: requires root\n' + exit 0 +fi + +command -v nft >/dev/null || { printf 'skipped: nft not available\n'; exit 0; } +ip link add tailscale0 type dummy 2>/dev/null || + { printf 'skipped: cannot create dummy interface\n'; exit 0; } +ip link set tailscale0 up + +WORK_DIR="$(mktemp -d)" +TS_LOG="$WORK_DIR/ts.log" + +cleanup() { + PATH="$STUB_BIN:$PATH" ucode -L "$TACHYON_LIB" "$RUNTIME_UC" stop-runtime >/dev/null 2>&1 || true + ip link del tailscale0 2>/dev/null || true + rm -rf "$WORK_DIR" "$STATE_FILE" +} +trap cleanup EXIT + +STUB_BIN="$WORK_DIR/bin" +mkdir -p "$STUB_BIN" +ip link set tailscale0 up + +cat >"$STUB_BIN/tailscaled" <> '$TS_LOG' +exec sleep 3600 +EOF +cat >"$STUB_BIN/tailscale" <> '$TS_LOG' +case "\$*" in + *version*) echo "1.80.3-smoke" ;; + *) exit 0 ;; +esac +EOF +chmod +x "$STUB_BIN/tailscaled" "$STUB_BIN/tailscale" + +STATE_FILE="$WORK_DIR/uci.state" +cat >"$STATE_FILE" <<'EOF' +tachyon.settings=settings +tachyon.settings.dns_server=77.88.8.8 +tachyon.ts_native=server +tachyon.ts_native.label=Tailscale native +tachyon.ts_native.enabled=1 +tachyon.ts_native.protocol=tailscale +tachyon.ts_native.tailscale_mode=native +tachyon.ts_native.routing_mode=rules +tachyon.ts_native.tailscale_auth_key=tskey-auth-smoke +tachyon.ts_native.tailscale_hostname=tachyon-smoke +EOF + +export UCI_STATE="$STATE_FILE" +export TACHYON_CONFIG_NAME="tachyon" +export PATH="$STUB_BIN:$PATH" +export TAILSCALED_BIN="$STUB_BIN/tailscaled" +export TAILSCALE_BIN="$STUB_BIN/tailscale" +export TAILSCALE_STATE_BASE="$WORK_DIR/state" +export TAILSCALE_RUNTIME_DIR="$WORK_DIR/run" +export TAILSCALE_DNSMASQ_FILE="$WORK_DIR/dnsmasq.d/tachyon-tailscale.conf" + +fail() { + printf 'FAIL: %s\n' "$1" >&2 + exit 1 +} + +run() { + ucode -L "$TACHYON_LIB" "$RUNTIME_UC" "$@" +} + +# --- version reporting ------------------------------------------------------ +[ "$(run package-version)" = "1.80.3-smoke" ] || fail "package-version output" +run installed || fail "installed must succeed with stubs present" + +# --- start ------------------------------------------------------------------ +run start-runtime || fail "start-runtime exited non-zero" + +PID_FILE="$WORK_DIR/run/ts_native/tailscaled.pid" +[ -f "$PID_FILE" ] || fail "pid file was not written" +PID="$(cat "$PID_FILE")" +kill -0 "$PID" 2>/dev/null || fail "tailscaled pid is not alive" + +grep -Fq "cli --socket $WORK_DIR/run/ts_native/tailscaled.sock up" "$TS_LOG" || + fail "tailscale up was not invoked through the section socket" +grep -Fq -e "--authkey=tskey-auth-smoke" "$TS_LOG" || + fail "auth key was not passed to tailscale up" +grep -Fq -e "--accept-dns=false" "$TS_LOG" || + fail "up must disable tailscaled DNS takeover" +grep -Fq -e "--timeout=120s" "$TS_LOG" || + fail "up must carry a hard timeout" + +ip rule list | grep -Fq "100.64.0.0/10" || fail "tailnet ip rule missing" +ip rule list | grep -Fq "lookup 4247" || fail "route table rule missing" +[ "$(ip route show table 4247 | grep -cF 'dev tailscale0')" -ge 1 ] || + fail "tailnet route missing from table 4247" +nft list table inet tachyon_tailscale >/dev/null 2>&1 || + fail "nft table tachyon_tailscale was not created" +nft list table inet tachyon_tailscale | grep -Fq "masquerade" || + fail "masquerade rule missing (default on)" +[ -f "$WORK_DIR/dnsmasq.d/tachyon-tailscale.conf" ] || + fail "MagicDNS dnsmasq config was not written" +grep -Fq "server=/ts.net/100.100.100.100" \ + "$WORK_DIR/dnsmasq.d/tachyon-tailscale.conf" || + fail "MagicDNS forward entry missing" + +STATUS="$(run status)" +printf '%s' "$STATUS" | grep -Fq '"configured": true' || + fail "status must report configured=true, got: $STATUS" +printf '%s' "$STATUS" | grep -Fq '"ready": true' || + fail "status must report ready=true, got: $STATUS" + +# --- idempotent restart ----------------------------------------------------- +run start-runtime || fail "second start-runtime failed" +RULE_COUNT=$(ip rule list | grep -cF "lookup 4247") +[ "$RULE_COUNT" = "1" ] || fail "restart duplicated route rules ($RULE_COUNT)" + +# --- stop ------------------------------------------------------------------- +run stop-runtime || fail "stop-runtime exited non-zero" +kill -0 "$PID" 2>/dev/null && fail "tailscaled pid survived stop-runtime" +ip rule list | grep -Fq "100.64.0.0/10" && fail "tailnet ip rule survived stop" +ip route show table 4247 | grep -q . && fail "route table 4247 survived stop" +nft list table inet tachyon_tailscale >/dev/null 2>&1 && + fail "nft table survived stop" +[ -f "$WORK_DIR/dnsmasq.d/tachyon-tailscale.conf" ] && + fail "dnsmasq config survived stop" + +# --- exit node mode --------------------------------------------------------- +printf '%s\n' 'tachyon.ts_native.tailscale_use_exit_node=1' >>"$STATE_FILE" +run start-runtime || fail "start-runtime with exit node failed" +ip rule list | grep -Fq "lookup 4248" || fail "exit-node ip rule missing" +[ "$(ip route show table 4248 | grep -cF 'default dev tailscale0')" = "1" ] || + fail "exit-node default route missing" +nft list chain inet tachyon_tailscale prerouting_mark >/dev/null 2>&1 || + fail "exit-node marking chain missing" +run stop-runtime || fail "stop-runtime after exit node failed" +ip rule list | grep -Fq "lookup 4248" && fail "exit-node rule survived stop" + +printf 'tailscale native runtime smoke checks passed\n'