Home / All Skills / Workflow & Core Tools / tunnel-doctor
Workflow & Core Tools · daymade/claude-code-skills

tunnel-doctor

Diagnoses concrete tunnel and proxy-path failures across macOS and Windows/WSL: Tailscale routing, proxy env/system bypass, SSH double tunneling, VM/container propagation, stalled DNS, TUN DIRECT split-brain, Windows-host TUN cascades, and single-hop or chained proxy node/exit throughput. Use when Tailscale ping works but SSH/HTTP fails, browser returns 503 while curl works, Git SSH closes through the proxy, Docker pull/build fails behind TUN, getaddrinfo stalls while nslookup is fast, raw probes report physically impossible results, domestic DIRECT-routed sites fail while proxied sites work, a blocked port could be your tunnel or the destination's firewall, or the proxy is reachable but real downloads and full Git clones crawl. Also use when Git reports "failed to begin relaying via HTTP", ssh -vvv freezes at "debug2: resolving", ping works but dig times out, or setting up Tailscale SSH to WSL. Use debugging-network-issues only when the root cause remains unknown or belongs to an application/protocol layer.

L4 High-Privilege · Graded auth & full auditV1 Commit-PinnedExecute CommandNetwork AccessWrite FileDual-pass Audit
Author daymadeGitHub daymade/claude-code-skills ↗Stars 1392License MIT(仓库根 LICENSE,Copyright (c) 2025 daymade;GitHub API spdx MIT)Commit d5c4678cb5
Control levels describe skill capabilities and suggested control measures; they do not represent quality or safety certifications, nor do they dictate usability. Usage decisions rest entirely with users and administrators. Different agent hosts apply varying constraints; do not assume hosts provide automatic protection. Third-party skill copyrights belong to their original authors. Use at your own risk.

1How It Works & Mechanism

It decomposes the conflict of 'Tailscale and proxy/VPN coexistence' across the network stack into 5 layers, labeling each with 'what's broken / what's intact / root cause', using 'what is still working' to deduce the failing layer—serving as the diagnostic backbone of the entire skill.

tunnel-doctor/SKILL.md
| Layer | What breaks | What still works | Root cause | |-------|-------------|------------------|------------| | 1. Route table | Everything (SSH, curl, browser) | `tailscale ping` | `tun-excluded-routes` adds `en0` route overriding Tailscale utun |
Accompanying key criteria: 'SSH does NOT use `http_proxy`/`NO_PROXY` env vars. If SSH works but HTTP doesn't → Layer 2.' and '`curl` uses `http_proxy` env var, NOT the system proxy.'

Its core methodology is 'first verify the component's own health endpoint, do not conclude from circumstantial evidence'—since multiple layers can produce overlapping symptoms, jumping straight to the most specific verification prevents drilling down into the wrong layer.

tunnel-doctor/SKILL.md
When symptoms point at a component (proxy, VPN, route table, DNS), **don't commit to a hypothesis from circumstantial evidence — verify with that component's own health endpoint first.**
Causal explanation in the same section: 'A symptom that matches the description of Step 2X does not, by itself, prove component X is the problem.' along with a component-to-health-check mapping table (curl -x for proxy, tailscale status for Tailscale, dig @ns for DNS).

Addressing the phenomenon that 'probes lie' under TUN global mode, it provides falsifiable criteria: 0.00s transoceanic connections and sub-millisecond pings are physically impossible, indicating the measurement hits the local TUN rather than the real network.

tunnel-doctor/SKILL.md
**Counter-move**: before citing any latency / reachability number while a TUN is up, ask *"would this number be physically possible if the packet really traversed to the destination?"* A `0.00s` connect or a `0.2ms` ping to another continent is the tell that you measured the TUN, not the network.
It also lists 'what you can trust under TUN': curl's time_appconnect/time_starttransfer, domestic IP-geo sources, and proxy configs dumped from disk + the tool's own GUI.

For the same error message, it offers two mutually exclusive explanations and mandates a distinguishing experiment: 198.18.x.x only proves a TUN exists, not that fake-IP caused the failure; (A) protocol misidentification vs (B) intermittent forwarding path jitter require opposite fixes.

tunnel-doctor/SKILL.md
**First, a warning about this symptom**: the `198.18.x.x` in the error message is **not evidence that fake-IP caused the failure**. Under TUN, *every* hostname resolves to a fake IP, so that address appears in the error whether the connection failed for this reason or any other.
The distinguishing method is a loop running 40 standard HTTPS requests over the same proxy path to measure failure rate, with quantitative benchmarks: 'ssh -T git@github.com` succeeded 20/20 in a good window and failed roughly 15–20% of attempts in a bad one'.

It turns 'port probing is entirely untrustworthy under TUN' into a mandatory baseline calibration experiment: probe a port that the destination certainly does not serve; if it also reports open, all port probes on that machine yield zero information.

tunnel-doctor/SKILL.md
**First: stop trusting `nc -z`.** Under a TUN the local stack completes the TCP handshake on the destination's behalf, so `nc -z` succeeds for **every** port, including ports the destination has closed.
Original calibration command: nc -z -G 5 -w 5 <destination-ip> 65001 alongside the control group 203.0.113.199 (RFC 5737 TEST-NET-3).

The question of 'tunnel issue or remote firewall', which cannot be distinguished from the client side, is directed to out-of-band channels (cloud provider guest-agent) for forensic inspection on the target host, backed by an empirical counterexample: 'journalctl instead of auth.log'.

tunnel-doctor/SKILL.md
**Then ask the destination out-of-band.** The only way to settle "did my packets arrive?" is a channel that does not traverse the failing data path. On a cloud VM that is the provider's guest-agent control plane
Accompanying empirical warning: 'On Ubuntu 24.04 the file still exists and still receives `CRON` and `sudo` lines, but `rsyslog` is not installed by default and **`sshd` writes nothing to it** — measured on a live 24.04 host: 90 lines in `auth.log`, 0 of them from `sshd`'

The built-in quick diagnostic script condenses the four most common macOS conflict types (env proxy, system proxy bypass, direct/proxy split-path, local TLS trust) into a single command, producing an easily interpretable three-line verdict.

tunnel-doctor/SKILL.md
python3 scripts/quick_diagnose.py --host local.example.com --url https://local.example.com/health
Original interpretation rules: '- `direct=PASS` + `forced_proxy=FAIL` = host must bypass proxy (`skip-proxy` + `NO_PROXY`).' and '- `strict_tls=FAIL` + `direct=PASS` = path is reachable; trust issue only'.

It establishes a gating prerequisite for 'destructive network changes': before modifying VPN/routes/services, one must read the recovery reference, verify preconditions, and set up bounded recovery mechanisms, clarifying that 'successful stop / detached process / restart at script end' do not count as proof of recovery.

tunnel-doctor/SKILL.md
Before any authorized VPN disconnect, app termination, route cutover, or service restart, read [network_change_recovery.md](references/network_change_recovery.md). Validate prerequisites before interruption, arm bounded recovery before the first disruptive action, and verify both the repaired path and the user's original network use. A successful stop command, a detached process, or a restart placed at the end of a script is not recovery evidence.
The three-stage structure of that reference: Before interruption / Execute and recover / Verify and finish.

2Core Capabilities

01Routing-layer conflict diagnosis and remediation (proxy preempting Tailscale CGNAT subnets)
02NO_PROXY / skip-proxy environment variable and system proxy bypass fixes (including Go net/http CIDR incompatibility pitfalls)
03TUN DIRECT split-brain diagnosis (domain connection hangs while real IP + correct SNI succeeds)
04Binary search localization across nameservers for stalled DNS getaddrinfo chains (nslookup fast but system resolution hangs for 60s)
05VM/container proxy propagation remediation (OrbStack/Docker Desktop transparent proxy conflicts with TUN, buildkit direct routing, v2rayN port splitting)
06SSH double tunneling and intermittent git over SSH failure handling (including wrapper contracts that only retry before key exchange)
07Tailscale SSH silent interception remediation on WSL (kex_exchange_identification + tcpdump 0-packet criteria)
08Capacity and throughput diagnosis for proxy nodes/exits/chains (single-hop and multi-hop chains, serial comparison, real client retesting, raw workload replay)
09Windows-host TUN cascading failure diagnosis (WSL + its Tailscale dropping together) and event log timeline forensics
10Tailscale remote development SOP (proxy-safe Makefile, SSH tunnel destinations, multi-port forwarding, non-login shell environments)

3External Dependencies

KindDependency / Endpoint
clitailscale(status / ssh / up / version)
climacOS 网络诊断工具族(ifconfig / netstat / route / scutil / lsof / dscacheutil)
clicurl(含 -x 显式代理、--noproxy '*'、-w 计时模板)
clidig / nslookup(逐 nameserver 二分 DNS)
clinc / tcpdump / ss(端口探测与到达性取证,含 sudo tcpdump)
clissh / ssh -vvv / ssh -T(连通性与 git over SSH 诊断)
clidocker / docker build / docker run(VM 与容器代理传播诊断)
clipnpm(SOP 示例,含 npmmirror 镜像参数)
cli阿里云 CLI(aliyun ecs …,带外命令执行取证)
clijournalctl / grep(目标机日志取证;经带外通道执行)
apiShadowrocket 配置编辑器 API(读/写整份代理配置,LAN 内 HTTP)
network通用可达性目标(Google generate_204、api.github.com、alpine CDN)用于路径健康与容量测量
networkTailscale 官方站点(ACL 管理页与独立版安装包)
network本地/内网服务与 vanity 域名(文档中的占位目标,非固定端点)

4Recommended Governance & Evidence

Suggested Controls for Safe Operation
For Agent Runtime
Graded/per-use authorization, sandbox recommended
For Administrators
Approval workflow, rollback support, full audit
Evidence & Triggers (File Paths & Excerpts)
  • scripts/: Executes local scripts or commands: python3 scripts/quick_diagnose.py --host … --url … [--tailscale-ip …] (bundled in repo)
  • 网络端点: Declares external network access: curl via local/LAN proxy ports (127.0.0.1:1082, host.internal:1082, etc.) and direct connections to external endpoints for path health and capacity testing
  • 本地工作目录: File I/O and artifact generation: writes back Shadowrocket config: POST /api/save --data-binary @config.txt to replace the editor buffer (reference)
Audit Notes: Three solid justifications for an Amber rating: (1) [Credential Exposure] references/proxy_conflict_reference.md instructs users/agents to read the entire Shadowrocket configuration (containing proxy nodes and credentials) over plaintext HTTP across the LAN and write it back, presenting a direct access path to proxy credentials. (2) [System-Level Changes] The core actions of the skill involve altering route tables, modifying system proxy bypass lists, and restarting/reinstalling Tailscale (including sudo route delete, sudo killall -HUP mDNSResponder, sudo snap remove tailscale, sudo tailscale up --ssh), representing high-impact modifications to user network state. (3) [Cross-Host Forensics] Initiates SSH into hosts inside Tailscale and executes shell commands on target cloud instances via cloud provider guest-agents (aliyun ecs RunCommand). Why not Red: Security degradation is not a primary execution path (Step 6 includes a single StrictHostKeyChecking=no, but that is a connectivity probe rather than a data transport channel, and section 2H explicitly warns against relaxing this check for convenience), no web-scraping/evasion/mass-crawling intent exists, and all outbound requests target standard diagnostic reachability endpoints or the user's own proxies/cloud consoles. Why not Yellow: The combination of credential reading, system configuration modifications, and remote host operations represents an impact blast radius significantly exceeding 'routine API calls'. Usage recommendation: Run only on your own (or explicitly authorized) machines; configure recovery mechanisms per network_change_recovery.md before dropping VPNs or switching routes; avoid dumping entire configuration payloads into chat sessions or logs when reading proxy configs.

5Second-Pass Verification

  • [discrepancy] 每条外部依赖的调用点是否真实存在(13 个 CLI/API/network 条目) — 【本项在第二遍自查时曾误判为 ok,后经磁盘级校验工具抓出并已修正】。逐条回查后确认:tailscale 调用点见 Step 2D 的 `tailscale status` 与诊断纪律表的 `| Tailscale daemon | \`tailscale status\` returns peer list (not connection error) |`、Step 5 的 `sudo tailscale up --ssh` / `--ssh=false` / `tailscale ssh` / `tailscale version`;其余依赖亦各有真实调用点(env/ifconfig/route/scutil 见 2A/2B、curl 见 2A/2C/2E 与容量参考、dig 见 2I/2J/2K、nc/tcpdump/ss 见 2K 与 Step 6、ssh 见 Step 6 与 SSH 双隧道、docker 见 2G、pnpm 见 SOP 第 5 节、aliyun ecs 与 journalctl 见 2K、Shadowrocket config API 见 proxy_conflict_reference.md、google/github/alpine CDN 见 2H/2K/2G、Tailscale 官方站点见 Step 4/5B、localhost/local.<domain> 见 Step 1 症状清单)。**纠正的具体错误**:首稿有两条 evidence 的 quote 被误取自同批另一份源码(windows-remote-desktop-connection-doctor/SKILL.md)——`tailscale status` / `tailscale netcheck` 与 `ifconfig | grep -E "^[a-z]|inet |utun"` / `netstat -rn | head -40` / `scutil --proxy` 均属该文件而非本 skill;本 skill 内 tailscale 的实际命令是 `tailscale status`、`tailscale ssh`、`sudo tailscale up --ssh`、`tailscale version`(**不含 netcheck**),macOS 工具的实际命令是 `ifconfig | grep -A2 'inet 100\.'`。两条 quote 已按 pin 源码逐字替换,`netcheck` 相关表述已从 name/scripts_executed 中清除。教训记录:跨文件批量取证时,同批并行读取的相邻源码极易被错误归属,evidence 必须在写入前逐条回到目标文件复核。
  • [ok] 自带脚本是否与文档声明一致(是否只做诊断、不写盘、无 shell 注入) — quick_diagnose.py 的 docstring 自述只做五类冲突检测(env+NO_PROXY、系统代理例外、代理路径 vs 直连、本地 TLS 信任、可选路由归属);其 subprocess.run 全部传参数列表(无 shell=True);os.environ.copy() 后仅 pop 代理变量以做对照请求;无文件写入、无 subprocess 之外的网络栈。结论成立。
  • [ok] 是否漏记外发目标(把每个域名回查调用点) — 全目录域名提取:api.github.com、www.google.com、dl-cdn.alpinelinux.org、registry.npmmirror.com、login.tailscale.com、pkgs.tailscale.com、docs.python.org、docs.ansible.com、tailscale.com、local.example.com、localhost(多端口)、127.0.0.1:1082、host.internal:1082、192.168.31.110:8080(Shadowrocket IP 占位)、100.101.102.103:8002、203.0.113.199(RFC 5737 对照)、198.18.x.x(fake-IP 段)。均已归入 external_deps 或注入面说明;无未记载的隐蔽外发。
  • [discrepancy] 『安全降级』检索是否有命中 — 命中并构成文档内部张力:Step 6 的端到端验证命令为 `ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no <user>@<tailscale-ip> 'echo SSH_OK && hostname && whoami'`,即关闭主机密钥校验;而 Step 2H 在讨论『不要硬编码 GitHub IP』时明确把『pressure you into loosening `StrictHostKeyChecking`』列为真实成本,并称「That is a real cost paid for an uncertain benefit.」。同一文档一处反对放宽该检查、另一处把它写进推荐验证命令,属于未消解的表述张力(实际影响有限:该命令只执行 echo/hostname/whoami,无数据外发)。按分级范式这不足以把本 skill 判红,但应作为使用提醒记录下来。
  • [ok] 『读取代理配置』这一凭据接触面是否被文档自己披露 — 披露且被谨慎处理:proxy_conflict_reference.md 明确要求不要硬编码设备 IP('The device IP changes with DHCP. Do not hardcode it.')、给出端口 8080 可能被别的服务占用的 lsof 检查、并用粗体警告 -d 会 URL 编码从而『destroys the entire configuration』。即作者意识到了该接口的高风险性。此事以「凭据读取 + 配置写入」双重性质记入 security,不构成隐蔽行为。
  • [ok] 是否存在混淆或隐蔽脚本 — 全目录扫描 >200 字符无空格长行零命中;无 base64 大块(仅 2K 节的 aliyun --ContentEncoding Base64 说明与 RFC 引述);唯一可执行文件 quick_diagnose.py 全文可读、无动态代码加载。
  • [ok] 文档内部是否另有自相矛盾(除 StrictHostKeyChecking 外) — 抽查了 nc -z 的表述:Step 1 的关键区分里曾以 nc -z 成功作为判据,Step 2K 则明确『This supersedes the plain `nc -z` reading in the bullet above whenever a TUN — not just Tailscale — is in the path』——即作者主动标注了覆盖关系而非留矛盾,属已消解的张力。其余抽查(utun MTU 判据、TUN 可信探针清单)内部一致。
  • [ok] pin commit 与 skill.path 是否与任务书表格一致 — git rev-parse HEAD = d5c4678cb5d4fd6acc9c922690df035dbd33d247,与任务书表格『库内 HEAD』一致;目录位于仓库根,相对路径 tunnel-doctor;本目录最后一次提交 829d9758(2026-09-08 22:22)。GitHub API 复核 MIT / stars 1392 / pushed 2026-09-15T09:30:04Z。

6Verdict & Scenarios

  • The layered diagnostic framework (5 layers + symptom-to-step index) allows ambiguous symptoms like 'Tailscale is reachable but SSH/HTTP fails' to converge quickly to a specific layer instead of relying on guesswork.
  • The 'measurement pollution' section is rare among similar network troubleshooting guides: it explicitly identifies which probes yield fabricated metrics under TUN and provides trusted alternatives with physical common-sense criteria.
  • Avoids premature attribution for 'multiple root causes behind identical error messages'; instead, it designs differentiating experiments (HTTPS failure rate loops, nc calibration, out-of-band forensics) and explicitly acknowledges cases that cannot be determined from the client side.
  • Destructive operations require explicit recovery contracts and reject false proof of recovery (successful stop, process detachment, or restarts at the end of a script are not valid evidence).
  • Maintains strong operational boundary awareness on external hosts as mandatory rules (never log into a colleague's machine for testing, existing authorizations do not extend to new environments, even during peer handoffs).
  • Demonstrates self-correction: explicitly revokes the earlier recommendation of 'hardcoding GitHub IPs in ~/.ssh/config' with three solid technical rationales, representing an uncommon traceable engineering track record.
  • Best for: Best for: macOS developers and DevOps engineers wrestling with local network conflicts involving Tailscale/VPN/proxy tools (especially in mainland China network environments: Shadowrocket/Clash/Surge + domestic DIRECT rules + needing SSH access to WSL or cloud instances); also suitable for those facing concrete symptoms like 'SSH fails but ping works', 'browser returns 503 but curl works', 'nslookup is fast but ssh freezes for 60s', or 'docker pull is slow/failing', and seeking actionable criteria rather than generic advice.
    Not for: Not suitable for: troubleshooting where the root cause is unknown or resides strictly in application/protocol layers (SSE/CDN/HTTP2 behavior)—the documentation explicitly mandates falling back to debugging-network-issues; not for users unwilling to modify local routing/proxy configurations; not for lightweight checks that merely ask 'is the internet down?' (where diagnostic overhead far exceeds checking status). Clear and scoped authorization must be obtained before running on others' machines or production network gear.
    Install Agent-Ready
    ① Verified Mirror 2026-09-15
    Option A · Direct DownloadDownload tunnel-doctor.tar.gz
    sha256: a9f26e4f66655f0e…
    Option B · Copy Guide for Agent
    install.json
    Agents automatically download and verify mirrors from this JSON guide.
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit d5c4678cb5.