feat(singbox): поддержка составных длительностей и автоподнятие urltest idle_timeout, зеркала gh-proxy в README и расширение Knowledge Base
This commit is contained in:
@@ -196,6 +196,16 @@ Run the following single command in your router's SSH terminal:
|
||||
sh <(wget -O - https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh)
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> **Installation Mirrors (if direct access to GitHub is blocked or throttled):**
|
||||
> ```bash
|
||||
> # Mirror 1 (gh-proxy.com):
|
||||
> sh <(wget -O - https://gh-proxy.com/raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh)
|
||||
>
|
||||
> # Mirror 2 (ghfast.top):
|
||||
> sh <(wget -O - https://ghfast.top/https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh)
|
||||
> ```
|
||||
|
||||
> [!NOTE]
|
||||
> **Automatic Migration:** Existing configurations from **Forkop**, **Podkop Plus**, or original **Podkop** are fully compatible. The installer will automatically migrate your settings to `/etc/config/tachyon` without data loss.
|
||||
|
||||
@@ -207,6 +217,11 @@ To cleanly remove Tachyon, restore stock DNS, flush nftables rules, and preserve
|
||||
sh <(wget -O - https://raw.githubusercontent.com/Dushnilin/tachyon/main/uninstall.sh)
|
||||
```
|
||||
|
||||
*Via mirror gh-proxy.com:*
|
||||
```bash
|
||||
sh <(wget -O - https://gh-proxy.com/raw.githubusercontent.com/Dushnilin/tachyon/main/uninstall.sh)
|
||||
```
|
||||
|
||||
**Options & Flags:**
|
||||
* `-y`, `--yes` — non-interactive mode without confirmation prompts.
|
||||
* `-p`, `--purge` — completely remove all files including configurations and backups.
|
||||
|
||||
@@ -196,6 +196,16 @@ curl http://192.168.1.1/cgi-bin/tachyon-agent/openapi.json
|
||||
sh <(wget -O - https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh)
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> **Зеркала установки (при блокировке или замедлении GitHub):**
|
||||
> ```bash
|
||||
> # Зеркало 1 (gh-proxy.com):
|
||||
> sh <(wget -O - https://gh-proxy.com/raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh)
|
||||
>
|
||||
> # Зеркало 2 (ghfast.top):
|
||||
> sh <(wget -O - https://ghfast.top/https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh)
|
||||
> ```
|
||||
|
||||
> [!NOTE]
|
||||
> **Автоматическая миграция:** Конфигурации от **Forkop**, **Podkop Plus** и оригинального **Podkop** полностью совместимы. Инсталлятор автоматически перенесет ваши правила в `/etc/config/tachyon` без потери данных.
|
||||
|
||||
@@ -207,6 +217,11 @@ sh <(wget -O - https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.
|
||||
sh <(wget -O - https://raw.githubusercontent.com/Dushnilin/tachyon/main/uninstall.sh)
|
||||
```
|
||||
|
||||
*Через зеркало gh-proxy.com:*
|
||||
```bash
|
||||
sh <(wget -O - https://gh-proxy.com/raw.githubusercontent.com/Dushnilin/tachyon/main/uninstall.sh)
|
||||
```
|
||||
|
||||
**Опции и флаги:**
|
||||
* `-y`, `--yes` — автоматическое выполнение без интерактивного подтверждения.
|
||||
* `-p`, `--purge` — полное удаление всех файлов вместе с конфигурациями и бэкапами.
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# 08. Complete UCI Configuration Schema Reference
|
||||
|
||||
The primary configuration for Tachyon is stored at `/etc/config/tachyon` (UCI format, mode `0600`).
|
||||
|
||||
---
|
||||
|
||||
## 1. Section: `config settings 'settings'`
|
||||
|
||||
Global settings defining service behaviour, DNS resolution, watchdog parameters, Telegram integration, and AI Doctor.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `enabled` | `boolean` (`0`/`1`) | `1` | Master toggle for Tachyon service. |
|
||||
| `log_level` | `string` | `info` | Logging verbosity: `debug`, `info`, `warn`, `error`. |
|
||||
| `dns_mode` | `string` | `doh` | DNS routing mode: `direct`, `doh`, `dot`, `udp`, `hosts_only`. |
|
||||
| `remote_dns_server` | `string` | `https://1.1.1.1/dns-query` | Upstream secure DNS resolver for proxied domains. |
|
||||
| `direct_dns_server` | `string` | `77.88.8.8` | Fallback DNS resolver for direct / local domains. |
|
||||
| `tproxy_port` | `integer` | `10080` | Local TCP/UDP transparent proxy port for sing-box. |
|
||||
| `dns_port` | `integer` | `5353` | Local DNS listening port for sing-box DNS engine. |
|
||||
| `clash_api_port` | `integer` | `9090` | sing-box Clash REST API controller port. |
|
||||
| `clash_api_secret` | `string` | `""` | Secret token for sing-box Clash API. |
|
||||
| `watchdog_interval` | `integer` | `10` | Watchdog polling interval in seconds (5–60s). |
|
||||
| `memory_limit_mb` | `integer` | `0` | Dynamic `GOMEMLIMIT` cap in MB (`0` = auto 50% free RAM). |
|
||||
| `enable_telegram` | `boolean` | `0` | Enable interactive Telegram management bot. |
|
||||
| `telegram_token` | `string` | `""` | Telegram Bot API token from `@BotFather`. |
|
||||
| `telegram_chat_id` | `string` | `""` | Allowed Admin Telegram Chat ID(s) (comma-separated). |
|
||||
| `enable_ai_doctor` | `boolean` | `0` | Enable LLM AI Doctor diagnostic engine. |
|
||||
| `ai_doctor_provider`| `string` | `openai` | LLM Provider: `openai`, `anthropic`, `deepseek`, `custom`. |
|
||||
| `ai_doctor_model` | `string` | `""` | Model override (e.g. `gpt-4o-mini`, `claude-3-5-haiku`). |
|
||||
| `ai_doctor_api_key`| `string` | `""` | API key for the chosen LLM provider. |
|
||||
| `ai_doctor_custom_url`| `string` | `""` | Custom OpenAI-compatible endpoint URL (e.g. OpenRouter/Ollama). |
|
||||
| `agent_api_token` | `string` | `""` | Bearer token for HTTP REST Agent API write access. |
|
||||
|
||||
---
|
||||
|
||||
## 2. Section: `config server 'name'`
|
||||
|
||||
Defines an individual manual proxy outbound node.
|
||||
|
||||
| Option | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `type` | `string` | Yes | Protocol: `vless`, `vmess`, `shadowsocks`, `trojan`, `hysteria2`, `wireguard`. |
|
||||
| `server` | `string` | Yes | Remote server hostname or IPv4/IPv6 address. |
|
||||
| `server_port` | `integer` | Yes | Remote server port (e.g. `443`, `8443`). |
|
||||
| `uuid` | `string` | Conditional | User UUID for VLESS / VMess. |
|
||||
| `password` | `string` | Conditional | Password for Shadowsocks / Trojan / Hysteria2. |
|
||||
| `flow` | `string` | No | Flow control: `xtls-rprx-vision` (VLESS). |
|
||||
| `security` | `string` | No | Security type: `reality`, `tls`, `none`. |
|
||||
| `sni` | `string` | Conditional | Server Name Indication for TLS / Reality. |
|
||||
| `pbk` | `string` | Conditional | Public Key for VLESS Reality. |
|
||||
| `sid` | `string` | Conditional | Short ID for VLESS Reality (hex string). |
|
||||
| `fingerprint` | `string` | No | TLS fingerprint: `chrome`, `firefox`, `safari`, `randomized`. |
|
||||
| `method` | `string` | Conditional | Cipher method for Shadowsocks (e.g. `2022-blake3-aes-128-gcm`). |
|
||||
|
||||
---
|
||||
|
||||
## 3. Section: `config subscription 'name'`
|
||||
|
||||
Defines a remote subscription URL for automated batch node fetching.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `enabled` | `boolean` | `1` | Enable this subscription source. |
|
||||
| `url` | `string` | Required | HTTPS / HTTP link to subscription (Base64, Clash, SIP002). |
|
||||
| `update_interval`| `integer` | `86400` | Auto-update interval in seconds (default 24 hours). |
|
||||
| `custom_user_agent`| `string` | `""` | Custom User-Agent header for subscription request. |
|
||||
| `filter_keywords` | `list string` | `[]` | Include nodes matching regex/keywords. |
|
||||
| `exclude_keywords`| `list string` | `[]` | Exclude nodes matching regex/keywords. |
|
||||
|
||||
---
|
||||
|
||||
## 4. Section: `config rule 'name'`
|
||||
|
||||
Defines a selective routing policy.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `enabled` | `boolean` | `1` | Enable this routing rule. |
|
||||
| `name` | `string` | `""` | Human-readable label for UI. |
|
||||
| `action` | `string` | `proxy` | Route target: `proxy`, `zapret`, `zapret2`, `byedpi`, `direct`, `hosts`. |
|
||||
| `target_server` | `string` | `""` | Bound server ID or outbound tag when `action = 'proxy'`. |
|
||||
| `domain_pattern` | `list string` | `[]` | List of target domains or wildcards (e.g. `*.youtube.com`). |
|
||||
| `ip_cidr` | `list string` | `[]` | List of IP subnets (e.g. `104.16.0.0/12`). |
|
||||
| `client_group` | `string` | `""` | Restrict this rule to a specific client device group. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Section: `config hosts 'name'`
|
||||
|
||||
Configures static DNS overrides and remote blocklists ingestion.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `enabled` | `boolean` | `1` | Enable hosts mapping. |
|
||||
| `dns_hosts` | `list string` | `[]` | Static mappings in format `domain:ip` (e.g. `example.com:1.2.3.4`). |
|
||||
| `hosts_list_urls` | `list string` | `[]` | External URLs for blocklists (AdAway, StevenBlack). |
|
||||
| `update_interval` | `integer` | `86400` | Auto-refresh interval in seconds. |
|
||||
|
||||
---
|
||||
|
||||
## 6. Ready-to-Use Configuration Recipes
|
||||
|
||||
### Recipe A: Zero-VPS Local DPI Bypass (YouTube + Discord)
|
||||
```ini
|
||||
config settings 'settings'
|
||||
option enabled '1'
|
||||
option dns_mode 'doh'
|
||||
|
||||
config provider 'zapret2'
|
||||
option enabled '1'
|
||||
option daemon_bin '/usr/bin/nfqws2'
|
||||
option qnum '201'
|
||||
option args_tcp '--dpi-desync=multisplit --dpi-desync-split-pos=1,midsld --dpi-desync-seqovl=1 --dpi-desync-fooling=badseq'
|
||||
|
||||
config rule 'youtube_discord'
|
||||
option enabled '1'
|
||||
option name 'YouTube & Discord Local Desync'
|
||||
option action 'zapret2'
|
||||
list domain_pattern 'googlevideo.com'
|
||||
list domain_pattern 'youtube.com'
|
||||
list domain_pattern 'ytimg.com'
|
||||
list domain_pattern 'discord.com'
|
||||
list domain_pattern 'discord.gg'
|
||||
```
|
||||
|
||||
### Recipe B: Hybrid Mode (Local Desync for Media + VLESS Reality for Blocked Sites)
|
||||
```ini
|
||||
config server 'my_vps'
|
||||
option type 'vless'
|
||||
option server 'vps.example.com'
|
||||
option server_port '443'
|
||||
option uuid '12345678-1234-1234-1234-123456789abc'
|
||||
option flow 'xtls-rprx-vision'
|
||||
option security 'reality'
|
||||
option sni 'yahoo.com'
|
||||
option pbk 'XYZ...PUBLIC_KEY...'
|
||||
option sid '1a2b3c4d'
|
||||
|
||||
config rule 'media_rule'
|
||||
option enabled '1'
|
||||
option action 'zapret2'
|
||||
list domain_pattern 'youtube.com'
|
||||
list domain_pattern 'googlevideo.com'
|
||||
|
||||
config rule 'blocked_rule'
|
||||
option enabled '1'
|
||||
option action 'proxy'
|
||||
option target_server 'my_vps'
|
||||
list domain_pattern 'instagram.com'
|
||||
list domain_pattern 'twitter.com'
|
||||
list domain_pattern 'x.com'
|
||||
```
|
||||
@@ -0,0 +1,101 @@
|
||||
# 09. Telegram Bot Architecture & Management Protocol
|
||||
|
||||
Tachyon includes an embedded, high-performance Telegram management bot written entirely in **ucode** (`service/telegram.uc`).
|
||||
|
||||
---
|
||||
|
||||
## 1. Architectural Highlights
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
User[Telegram Client] -->|Encrypted HTTPS| TGAPI[Telegram Bot API Servers]
|
||||
TGAPI -->|Long Polling: getUpdates| Daemon[Tachyon telegram.uc Daemon]
|
||||
|
||||
Daemon --> Auth{Admin Chat ID Match?}
|
||||
Auth -->|No| Reject[Silent Ignore / Warning]
|
||||
Auth -->|Yes| Router[Command & Callback Dispatcher]
|
||||
|
||||
Router -->|/doctor, /ai_doctor| AIDoc[AI Doctor Engine]
|
||||
Router -->|/server, /switch| SrvMgr[Server Manager]
|
||||
Router -->|/restart| SvcLife[Service Lifecycle]
|
||||
Router -->|/status| StateRep[State Assembler]
|
||||
```
|
||||
|
||||
* **No Public IP or Port Forwarding Required**: Uses outbound long-polling (`getUpdates` with 30s timeout) to securely communicate through NAT/firewalls.
|
||||
* **Zero Overhead Memory Footprint**: Runs as a lightweight ucode process consuming under 2 MB RAM.
|
||||
* **Role-Based Access Control**: Rejects any commands from chat IDs not explicitly listed in UCI option `telegram_chat_id`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Command Set Reference
|
||||
|
||||
| Command | Arguments | Action |
|
||||
|---|---|---|
|
||||
| `/start` | — | Displays main welcome card and interactive control keyboard. |
|
||||
| `/status` | — | Returns real-time health card: uptime, active server, RAM usage, ping RTT. |
|
||||
| `/doctor` | — | Executes Local Rule Doctor and returns offline diagnostic report. |
|
||||
| `/ai_doctor`| — | Invokes AI Doctor with LLM reasoning and returns diagnosis + fix buttons. |
|
||||
| `/fix` | `<fix_code>` | Applies specific automated repair code (e.g. `/fix clear_dns_cache`). |
|
||||
| `/server` | — | Opens interactive server selector with inline pagination and RTT pings. |
|
||||
| `/rules` | — | Shows active routing rules with 1-click toggle switches. |
|
||||
| `/restart` | `[service]` | Restarts sing-box, dnsmasq, watchdog, or entire network stack. |
|
||||
| `/backup` | — | Sends latest atomic configuration backup directly to Telegram chat. |
|
||||
|
||||
---
|
||||
|
||||
## 3. Inline Keyboard & Callback Query Protocol
|
||||
|
||||
The bot uses dynamic inline keyboards (`InlineKeyboardMarkup`) for single-tap actions without typing commands.
|
||||
|
||||
### 3.1. Callback Data Scheme
|
||||
Callbacks follow a structured token format: `action:target:param`
|
||||
|
||||
```
|
||||
srv:select:node_1 # Switch active proxy server to node_1
|
||||
rule:toggle:youtube # Toggle routing rule 'youtube' on/off
|
||||
fix:apply:clear_dns_cache# Execute repair code 'clear_dns_cache'
|
||||
page:srv:2 # Navigate to page 2 of server list
|
||||
```
|
||||
|
||||
### 3.2. Example: Server Switcher Response
|
||||
|
||||
```json
|
||||
{
|
||||
"chat_id": 123456789,
|
||||
"text": "⚡ <b>Выберите активный сервер:</b>\nТекущий: <i>🇩🇪 Frankfurt VLESS (18 ms)</i>",
|
||||
"parse_mode": "HTML",
|
||||
"reply_markup": {
|
||||
"inline_keyboard": [
|
||||
[
|
||||
{"text": "🇩🇪 Frankfurt (18 ms) ✅", "callback_data": "srv:select:de_1"},
|
||||
{"text": "🇳🇱 Amsterdam (24 ms)", "callback_data": "srv:select:nl_1"}
|
||||
],
|
||||
[
|
||||
{"text": "🇫🇮 Helsinki (31 ms)", "callback_data": "srv:select:fi_1"},
|
||||
{"text": "🇺🇸 New York (95 ms)", "callback_data": "srv:select:us_1"}
|
||||
],
|
||||
[
|
||||
{"text": "🔄 Замерить пинг", "callback_data": "srv:ping:all"},
|
||||
{"text": "🩺 Запустить Доктора", "callback_data": "cmd:doctor"}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configuration via CLI
|
||||
|
||||
```sh
|
||||
# Set Bot Token
|
||||
uci set tachyon.settings.enable_telegram='1'
|
||||
uci set tachyon.settings.telegram_token='123456789:ABCdefGHIjklMNOpqrsTUVwxyz'
|
||||
|
||||
# Set Authorized Chat ID(s)
|
||||
uci set tachyon.settings.telegram_chat_id='123456789'
|
||||
|
||||
# Commit and restart service
|
||||
uci commit tachyon
|
||||
/etc/init.d/tachyon restart
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
# 10. Hosts Engine, Blocklists & Smart Caching
|
||||
|
||||
The Hosts Engine (`components/hosts.uc`) provides automated background ingestion, normalization, deduplication, and caching of external host lists and static DNS overrides.
|
||||
|
||||
---
|
||||
|
||||
## 1. Architecture of Hosts Engine
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Sources[External Remote URLs: AdAway, StevenBlack, Custom] --> Downloader[HTTP Fetcher with Mirror Retry]
|
||||
Static[Static dns_hosts in UCI] --> Downloader
|
||||
|
||||
Downloader --> Parser[Multi-Format Parser: Hosts, AdBlock, Domains]
|
||||
Parser --> Deduper[Deduplication & Sorting Engine]
|
||||
Deduper --> Cache[Unified Cache: /tmp/tachyon/hosts/combined.txt]
|
||||
|
||||
Cache --> DNSMasq[dnsmasq /etc/dnsmasq.d/tachyon-hosts.conf]
|
||||
Cache --> SingboxDNS[sing-box DNS hosts rule]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Multi-Format Ingestion
|
||||
|
||||
The parser automatically detects and normalizes multiple common blocklist formats:
|
||||
|
||||
1. **Standard `/etc/hosts` format**:
|
||||
```
|
||||
0.0.0.0 ads.example.com
|
||||
127.0.0.1 tracker.adserver.net
|
||||
```
|
||||
2. **Domain-only list format**:
|
||||
```
|
||||
malware-domain.org
|
||||
telemetry.app.com
|
||||
```
|
||||
3. **AdBlock / DNSmasq syntax**:
|
||||
```
|
||||
||badsite.com^
|
||||
server=/blocked.com/
|
||||
address=/ads.org/0.0.0.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. GitHub Mirror Retry Resiliency
|
||||
|
||||
When fetching blocklists hosted on GitHub (e.g. `raw.githubusercontent.com`), access may be blocked or throttled by regional DPI.
|
||||
|
||||
Tachyon implements an automated fallback retry chain:
|
||||
1. **Primary**: Direct HTTPS request to target URL.
|
||||
2. **Mirror 1**: `https://gh-proxy.com/<original_url>`
|
||||
3. **Mirror 2**: `https://ghfast.top/<original_url>`
|
||||
4. **Mirror 3**: `https://ghproxy.net/<original_url>`
|
||||
|
||||
If direct connection fails with timeout or SSL handshake error, Tachyon transparently cycles through available mirrors before reporting an error.
|
||||
|
||||
---
|
||||
|
||||
## 4. Dedicated Hosts Routing (`action = 'hosts'`)
|
||||
|
||||
Tachyon supports routing sections that operate purely as DNS-overriding engines without requiring an outbound proxy association.
|
||||
|
||||
### Example UCI Configuration:
|
||||
```ini
|
||||
config rule 'adblock_section'
|
||||
option enabled '1'
|
||||
option name 'AdBlock & Anti-Tracker'
|
||||
option action 'hosts'
|
||||
list hosts_list_urls 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts'
|
||||
list hosts_list_urls 'https://adaway.org/hosts.txt'
|
||||
```
|
||||
|
||||
* Domains in this section are redirected directly to `0.0.0.0` or custom IPs in dnsmasq / sing-box DNS.
|
||||
* Zero traffic is routed to external proxies, preserving VPS bandwidth and minimizing latency.
|
||||
@@ -0,0 +1,73 @@
|
||||
# 11. Troubleshooting & Field Diagnostic Manual
|
||||
|
||||
A comprehensive guide for diagnosing and resolving common network, proxy, and routing issues on OpenWrt routers running Tachyon.
|
||||
|
||||
---
|
||||
|
||||
## 1. Quick Emergency Recovery
|
||||
|
||||
If router traffic drops completely or DNS resolution is broken, use the **Emergency Native Internet Restoration** command:
|
||||
|
||||
```sh
|
||||
tachyon restore_native_internet
|
||||
```
|
||||
|
||||
This performs the following actions in under 200 ms:
|
||||
1. Atomically drops all nftables interception tables (`nft delete table inet TachyonTable`).
|
||||
2. Clears policy routing `table 100` and `ip rule` entries.
|
||||
3. Restores dnsmasq to stock upstream forwarders.
|
||||
4. Stops sing-box and DPI bypass daemons without rebooting the router.
|
||||
|
||||
---
|
||||
|
||||
## 2. Symptom Resolution Matrix
|
||||
|
||||
| Symptom | Likely Cause | Recommended Fix / Command |
|
||||
|---|---|---|
|
||||
| **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` |
|
||||
| **YouTube 4K buffers indefinitely** | Zapret strategy out-of-date for current ISP. | Switch Zapret v2 strategy to `multisplit` with `seqovl=1`. |
|
||||
| **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`. |
|
||||
| **System clock desynchronized, TLS fails** | Router rebooted without RTC battery, NTP desync. | `tachyon apply_quick_fix fix_system_time` |
|
||||
| **High latency or packet drop under heavy load** | Kernel `nf_conntrack` table overflow. | `tachyon apply_quick_fix flush_conntrack` |
|
||||
| **VLESS Reality connection timeout** | Reality SNI domain blocked or expired short ID. | Update server SNI to accessible target (e.g. `yahoo.com`). |
|
||||
| **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`). |
|
||||
|
||||
---
|
||||
|
||||
## 3. Diagnostic Commands Reference
|
||||
|
||||
```sh
|
||||
# 1. Run local offline rule checks
|
||||
tachyon doctor
|
||||
|
||||
# 2. Run AI Doctor with LLM analysis
|
||||
tachyon ai_doctor
|
||||
|
||||
# 3. View real-time Watchdog telemetry
|
||||
tachyon ai_status_full
|
||||
|
||||
# 4. Inspect active nftables firewall tables
|
||||
nft list table inet TachyonTable
|
||||
|
||||
# 5. Inspect policy routing table 100
|
||||
ip route show table 100
|
||||
ip rule show
|
||||
|
||||
# 6. Stream live Tachyon system logs
|
||||
logread -f -e tachyon
|
||||
|
||||
# 7. Check listening ports (TProxy 10080, DNS 5353, Clash 9090)
|
||||
netstat -tulpn | grep -E 'sing-box|tachyon|5353|10080'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Log Inspection Paths
|
||||
|
||||
| Log Location | Content Description |
|
||||
|---|---|
|
||||
| `/var/log/tachyon_service.log` | Core service lifecycle events (start, stop, reload). |
|
||||
| `/var/log/tachyon_watchdog.log` | Background supervisor health checks and loop warnings. |
|
||||
| `/tmp/ai_doctor_last.json` | JSON report of the most recent AI Doctor diagnosis. |
|
||||
| `/tmp/tachyon/sing-box.json` | Generated live configuration compiled for sing-box. |
|
||||
| `/tmp/tachyon/hosts/combined.txt` | Consolidated deduplicated hosts list. |
|
||||
@@ -0,0 +1,81 @@
|
||||
# 12. ucode Programming Patterns & Conventions
|
||||
|
||||
A developer's guide to writing safe, high-performance, and idiomatic **ucode** modules for Tachyon on OpenWrt.
|
||||
|
||||
---
|
||||
|
||||
## 1. ucode vs JavaScript: Critical Distinctions
|
||||
|
||||
While ucode syntax resembles JavaScript, its runtime semantics differ significantly:
|
||||
|
||||
1. **Variables & Scope**:
|
||||
* Always declare variables with `let` or `const`.
|
||||
* Unassigned variables are `null` (not `undefined`).
|
||||
2. **Arrays vs Dictionaries**:
|
||||
* Arrays: `[1, 2, 3]`. Iterated with `for (let item in array)`.
|
||||
* Dictionaries (Objects): `{"key": "value"}`. Iterated with `for (let key, val in dict)`.
|
||||
3. **Regular Expressions**:
|
||||
* Use `match(str, /regex/)` or `replace(str, /regex/g, "replacement")`.
|
||||
* Global flag `/g` works with `replace()`, while `match()` returns capture groups array `[full_match, group1, ...]`.
|
||||
4. **Boolean Coercion**:
|
||||
* Empty strings `""`, `0`, and `null` are falsy. Everything else is truthy.
|
||||
|
||||
---
|
||||
|
||||
## 2. Mandatory Core Design Patterns
|
||||
|
||||
### 2.1. Atomic Configuration Writes
|
||||
Never overwrite active configuration files in-place. Always write to a temporary file in `/tmp/` and atomically rename:
|
||||
|
||||
```ucode
|
||||
let fs = require("fs");
|
||||
|
||||
function write_atomic(path, content) {
|
||||
let tmp_path = path + ".tmp." + int(time());
|
||||
let f = fs.open(tmp_path, "w");
|
||||
if (!f) return false;
|
||||
|
||||
f.write(content);
|
||||
f.close();
|
||||
|
||||
return fs.rename(tmp_path, path);
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. Shell Argument Quoting
|
||||
When executing external shell commands, all user inputs must be quoted safely:
|
||||
|
||||
```ucode
|
||||
function shell_quote(value) {
|
||||
return "'" + replace("" + value, /'/g, "'\\''") + "'";
|
||||
}
|
||||
|
||||
function safe_exec(cmd_args) {
|
||||
let parts = [];
|
||||
for (let arg in cmd_args)
|
||||
push(parts, shell_quote(arg));
|
||||
return system(join(" ", parts));
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3. Safe Error Handling (`try-catch`)
|
||||
All external library loading or file parsing must be wrapped in `try-catch` blocks to avoid unhandled script panics:
|
||||
|
||||
```ucode
|
||||
let uci_data = null;
|
||||
try {
|
||||
let uci = require("uci").cursor();
|
||||
uci_data = uci.get_all("tachyon", "settings");
|
||||
}
|
||||
catch (e) {
|
||||
warn("Failed to load UCI settings: " + e + "\n");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Performance & Memory Optimization Guidelines
|
||||
|
||||
1. **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")`.
|
||||
2. **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.
|
||||
3. **Keep Modules Stateless**: Module files under `usr/lib/tachyon/` should export reusable pure functions or class definitions rather than maintaining mutable global state.
|
||||
@@ -0,0 +1,117 @@
|
||||
# 13. HTTP REST Agent API & Model Context Protocol (MCP) Specification
|
||||
|
||||
Tachyon 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).
|
||||
|
||||
---
|
||||
|
||||
## 1. REST API Overview
|
||||
|
||||
* **Base URL**: `http://<router-ip>/cgi-bin/tachyon-agent/`
|
||||
* **OpenAPI Schema**: `http://<router-ip>/cgi-bin/tachyon-agent/openapi.json`
|
||||
* **Content-Type**: `application/json`
|
||||
|
||||
---
|
||||
|
||||
## 2. Authentication Model
|
||||
|
||||
1. **Read-Only Endpoints**: Unrestricted when accessed from local LAN.
|
||||
2. **Write Endpoints**: Require HTTP Authorization header:
|
||||
```http
|
||||
Authorization: Bearer <agent_api_token>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Endpoint Reference & Payloads
|
||||
|
||||
### 3.1. `GET /health`
|
||||
Returns lightweight status of all core daemons.
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"version": "1.2.85",
|
||||
"uptime_seconds": 18450,
|
||||
"singbox_running": true,
|
||||
"watchdog_running": true,
|
||||
"active_server": "Frankfurt_VLESS_Reality"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2. `GET /diagnose`
|
||||
Returns Local Rule Doctor checks and suggested repair codes.
|
||||
```json
|
||||
{
|
||||
"status": "warning",
|
||||
"checks": {
|
||||
"dns_loop": false,
|
||||
"conntrack_full": false,
|
||||
"time_desync": true,
|
||||
"singbox_ok": true
|
||||
},
|
||||
"suggested_fixes": ["fix_system_time"]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3. `POST /ai-doctor/fix`
|
||||
Applies one or more quick-fix codes.
|
||||
* **Request**:
|
||||
```json
|
||||
{
|
||||
"fix": "clear_dns_cache,start_singbox"
|
||||
}
|
||||
```
|
||||
* **Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"applied_fixes": ["clear_dns_cache", "start_singbox"],
|
||||
"details": "DNS cache cleared. sing-box process restarted."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Model Context Protocol (MCP) Tool Definitions
|
||||
|
||||
For MCP-compatible agents (Cursor, Claude Desktop), Tachyon exports tools via `GET /cgi-bin/tachyon-agent/tools`:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "tachyon_diagnose",
|
||||
"description": "Run diagnostic checks on OpenWrt router network and proxy health",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tachyon_apply_fix",
|
||||
"description": "Apply automated repair codes to fix router network or proxy issues",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fix_codes": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of fix codes (e.g. clear_dns_cache,restart_singbox)"
|
||||
}
|
||||
},
|
||||
"required": ["fix_codes"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tachyon_switch_server",
|
||||
"description": "Switch active proxy server node",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"server_id": {
|
||||
"type": "string",
|
||||
"description": "Identifier of target proxy server"
|
||||
}
|
||||
},
|
||||
"required": ["server_id"]
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -15,6 +15,12 @@ Welcome to the comprehensive technical knowledge base for **Tachyon**. This dire
|
||||
| [**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 |
|
||||
| [**06. Frontend LuCI Architecture**](06_FRONTEND_LUCI_TYPESCRIPT.md) | TypeScript SPA & LuCI view integration | `baseclass.extend` patch, reactive stores, live log streaming modal |
|
||||
| [**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 |
|
||||
| [**08. UCI Configuration Schema**](08_UCI_CONFIGURATION_SCHEMA.md) | Complete `/etc/config/tachyon` reference | All UCI sections, options, validators, default values, ready recipes |
|
||||
| [**09. Telegram Bot Architecture**](09_TELEGRAM_BOT_ARCHITECTURE.md) | Remote Telegram control daemon protocol | Long-polling model, inline keyboards, callback query routing, security |
|
||||
| [**10. Hosts Engine & Blocklists**](10_HOSTS_ENGINE_AND_BLOCKLISTS.md) | Hosts ingestion, parsing & smart caching | `combined.txt`, multi-format parsing, GitHub mirror retry resiliency |
|
||||
| [**11. Troubleshooting Manual**](11_TROUBLESHOOTING_AND_FIELD_MANUAL.md) | Field diagnostics & emergency recovery | Symptom resolution matrix, diagnostic commands, log inspection paths |
|
||||
| [**12. ucode Programming Patterns**](12_UCODE_PROGRAMMING_PATTERNS.md) | Developer guidelines for OpenWrt ucode | Atomic file writes, shell quoting, libuci bindings, RAM efficiency |
|
||||
| [**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 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -72,28 +72,34 @@ function ruleset_registered(config, tag_name) {
|
||||
}
|
||||
|
||||
function duration_to_seconds(value) {
|
||||
value = as_string(value);
|
||||
if (value == "")
|
||||
let rest = as_string(value);
|
||||
if (rest == "")
|
||||
return null;
|
||||
if (match(value, /^[0-9]+$/) != null)
|
||||
return int(value, 10);
|
||||
if (match(rest, /^[0-9]+$/) != null)
|
||||
return int(rest, 10);
|
||||
|
||||
let suffix = substr(value, length(value) - 1);
|
||||
let number = substr(value, 0, length(value) - 1);
|
||||
if (match(number, /^[0-9]+$/) == null)
|
||||
let total = 0.0;
|
||||
let multipliers = {
|
||||
ns: 0.000000001,
|
||||
us: 0.000001,
|
||||
ms: 0.001,
|
||||
s: 1,
|
||||
m: 60,
|
||||
h: 3600,
|
||||
d: 86400
|
||||
};
|
||||
|
||||
while (rest != "") {
|
||||
let matched = match(rest, /^([0-9]+(\.[0-9]+)?)(ns|us|ms|s|m|h|d)/);
|
||||
if (!matched)
|
||||
return null;
|
||||
|
||||
let multiplier = null;
|
||||
if (suffix == "s")
|
||||
multiplier = 1;
|
||||
else if (suffix == "m")
|
||||
multiplier = 60;
|
||||
else if (suffix == "h")
|
||||
multiplier = 3600;
|
||||
else if (suffix == "d")
|
||||
multiplier = 86400;
|
||||
let token = as_string(matched[0]);
|
||||
total += (matched[1] * 1) * multipliers[matched[3]];
|
||||
rest = substr(rest, length(token));
|
||||
}
|
||||
|
||||
return multiplier == null ? null : int(number, 10) * multiplier;
|
||||
return total <= 0 ? null : int(total + 0.5);
|
||||
}
|
||||
|
||||
function urltest_check_interval(section, urltest_id) {
|
||||
@@ -101,23 +107,22 @@ function urltest_check_interval(section, urltest_id) {
|
||||
return interval != "" ? interval : "3m";
|
||||
}
|
||||
|
||||
function legacy_urltest_idle_timeout(section, urltest_id) {
|
||||
if (urltest_id != "urltest")
|
||||
return "";
|
||||
|
||||
let settings = connections.urltest_settings(section, urltest_id);
|
||||
if (type(settings) == "object" && as_string(settings[".type"] || "") == "urltest")
|
||||
return "";
|
||||
|
||||
let interval = urltest_check_interval(section, urltest_id);
|
||||
let interval_seconds = duration_to_seconds(interval);
|
||||
let default_idle_seconds = duration_to_seconds(runtime_constants.URLTEST_DEFAULT_IDLE_TIMEOUT);
|
||||
return interval_seconds != null && interval_seconds > default_idle_seconds ? interval : "";
|
||||
}
|
||||
|
||||
function urltest_idle_timeout(section, urltest_id) {
|
||||
let configured = connections.urltest_idle_timeout(section, urltest_id);
|
||||
return configured != "" ? configured : legacy_urltest_idle_timeout(section, urltest_id);
|
||||
let interval = urltest_check_interval(section, urltest_id);
|
||||
let interval_seconds = duration_to_seconds(interval);
|
||||
let idle_seconds = duration_to_seconds(configured != ""
|
||||
? configured
|
||||
: runtime_constants.URLTEST_DEFAULT_IDLE_TIMEOUT);
|
||||
|
||||
// sing-box refuses to start a URLTest group whose interval exceeds its
|
||||
// idle_timeout, and substitutes URLTEST_DEFAULT_IDLE_TIMEOUT when the
|
||||
// option is omitted. Raise the timeout to the interval instead of emitting
|
||||
// a config that fails with "interval must be less or equal than idle_timeout".
|
||||
if (interval_seconds != null && idle_seconds != null && interval_seconds > idle_seconds)
|
||||
return interval;
|
||||
|
||||
return configured;
|
||||
}
|
||||
|
||||
function supported_urltest_filter_mode(mode) {
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib"
|
||||
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" "$TACHYON_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
"$fixture" "$output" "127.0.0.1"
|
||||
}
|
||||
|
||||
# sing-box rejects a URLTest group whose interval exceeds its idle_timeout and
|
||||
# substitutes its own 30m default when the option is omitted, so the generator
|
||||
# has to raise idle_timeout to the check interval whenever it would be smaller.
|
||||
cat >"$WORK_DIR/children.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
".name": "settings",
|
||||
".type": "settings",
|
||||
"log_level": "warn"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "proxy",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"selector_proxy_links": [
|
||||
"vless://00000000-0000-4000-8000-000000000001@example.com:443?encryption=none&security=tls&sni=example.com#first",
|
||||
"vless://00000000-0000-4000-8000-000000000002@example.org:443?encryption=none&security=tls&sni=example.org#second"
|
||||
]
|
||||
}
|
||||
],
|
||||
"urltest": [
|
||||
{
|
||||
".name": "cfg010001",
|
||||
".type": "urltest",
|
||||
"section": "proxy",
|
||||
"name": "Hourly",
|
||||
"check_interval": "1h",
|
||||
"tolerance": "50",
|
||||
"testing_url": "https://www.gstatic.com/generate_204",
|
||||
"idle_timeout": "30m",
|
||||
"interrupt_exist_connections": "1"
|
||||
},
|
||||
{
|
||||
".name": "cfg010002",
|
||||
".type": "urltest",
|
||||
"section": "proxy",
|
||||
"name": "Compound",
|
||||
"check_interval": "1h30m",
|
||||
"tolerance": "50",
|
||||
"testing_url": "https://www.gstatic.com/generate_204",
|
||||
"interrupt_exist_connections": "1"
|
||||
},
|
||||
{
|
||||
".name": "cfg010003",
|
||||
".type": "urltest",
|
||||
"section": "proxy",
|
||||
"name": "Short idle",
|
||||
"check_interval": "3m",
|
||||
"tolerance": "50",
|
||||
"testing_url": "https://www.gstatic.com/generate_204",
|
||||
"idle_timeout": "1m",
|
||||
"interrupt_exist_connections": "1"
|
||||
},
|
||||
{
|
||||
".name": "cfg010004",
|
||||
".type": "urltest",
|
||||
"section": "proxy",
|
||||
"name": "Explicit idle",
|
||||
"check_interval": "3m",
|
||||
"tolerance": "50",
|
||||
"testing_url": "https://www.gstatic.com/generate_204",
|
||||
"idle_timeout": "45m",
|
||||
"interrupt_exist_connections": "1"
|
||||
},
|
||||
{
|
||||
".name": "cfg010005",
|
||||
".type": "urltest",
|
||||
"section": "proxy",
|
||||
"name": "Default idle",
|
||||
"check_interval": "3m",
|
||||
"tolerance": "50",
|
||||
"testing_url": "https://www.gstatic.com/generate_204",
|
||||
"interrupt_exist_connections": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
children_output="$WORK_DIR/children-config.json"
|
||||
generate_config "$WORK_DIR/children.json" "$children_output"
|
||||
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
let cfg = json(fs.readfile(ARGV[0]));
|
||||
let groups = {};
|
||||
for (let outbound in cfg.outbounds || [])
|
||||
if (outbound && outbound.type == "urltest")
|
||||
groups[outbound.tag] = outbound;
|
||||
|
||||
let expected = {
|
||||
"proxy-urltest-cfg010001-out": [ "1h", "1h" ],
|
||||
"proxy-urltest-cfg010002-out": [ "1h30m", "1h30m" ],
|
||||
"proxy-urltest-cfg010003-out": [ "3m", "3m" ],
|
||||
"proxy-urltest-cfg010004-out": [ "3m", "45m" ]
|
||||
};
|
||||
|
||||
for (let tag, values in expected) {
|
||||
let group = groups[tag];
|
||||
if (!group)
|
||||
die(sprintf("missing URLTest outbound %s\n", tag));
|
||||
if (group.interval != values[0])
|
||||
die(sprintf("%s: expected interval %s, got %s\n", tag, values[0], group.interval));
|
||||
if (group.idle_timeout != values[1])
|
||||
die(sprintf("%s: expected idle_timeout %s, got %s\n", tag, values[1], group.idle_timeout));
|
||||
}
|
||||
|
||||
let relaxed = groups["proxy-urltest-cfg010005-out"];
|
||||
if (!relaxed)
|
||||
die("missing URLTest outbound proxy-urltest-cfg010005-out\n");
|
||||
if (relaxed.idle_timeout != null)
|
||||
die(sprintf("expected no idle_timeout below the sing-box default, got %s\n", relaxed.idle_timeout));
|
||||
' "$children_output" || fail "URLTest child idle_timeout regression"
|
||||
|
||||
echo "PASS: urltest_idle_timeout test passed"
|
||||
Reference in New Issue
Block a user