<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>技术解读 on 火炬树</title>
        <link>https://torchtree.com/categories/%E6%8A%80%E6%9C%AF%E8%A7%A3%E8%AF%BB/</link>
        <description>Recent content in 技术解读 on 火炬树</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <copyright>TorchTree Co., Ltd.</copyright>
        <lastBuildDate>Sun, 30 Aug 2026 17:30:00 +0800</lastBuildDate><atom:link href="https://torchtree.com/categories/%E6%8A%80%E6%9C%AF%E8%A7%A3%E8%AF%BB/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Tailcat：Tailscale 开源的免账号加密直连工具</title>
        <link>https://torchtree.com/post/tailcat-tailscale-netcat/</link>
        <pubDate>Sun, 30 Aug 2026 17:30:00 +0800</pubDate>
        
        <guid>https://torchtree.com/post/tailcat-tailscale-netcat/</guid>
        <description>&lt;p&gt;Tailcat 是 Tailscale 在 2026 年 8 月 TailscaleUp 大会上开源的网络连接工具，定位是 netcat 的替代品。它在两台机器之间建立 WireGuard 加密的连接，不需要注册账号、登录、组网，也不需要任何一方具备公网 IP 或开放端口。服务端启动后生成一个 &lt;code&gt;tc&lt;/code&gt; 开头的连接 token，把 token 交给对方就能连上。项目用 Go 编写，采用 BSD-3-Clause 许可证，开源 4 天 GitHub stars 超过 3800，Hacker News 讨论帖拿到 662 points。&lt;/p&gt;
&lt;h2 id=&#34;解决什么问题&#34;&gt;解决什么问题
&lt;/h2&gt;&lt;p&gt;netcat（nc）从上世纪 90 年代沿用至今，是 Unix 工具箱里最常见的网络工具之一，自称 “TCP/IP 瑞士军刀”，把两台机器的 stdin/stdout 接通就能传文件、调试端口、搬一个远程 shell。它的短板在加密和穿透：nc 本身不加密，两端只要有一端在 NAT 后面就连不上，要安全就得自己再叠一层 SSH 隧道。&lt;/p&gt;
&lt;p&gt;这类临时连接的需求其实到处都是。给外包工程师开一小时数据库访问，任务结束权限就该收回；让 CI 任务回连内网机器取一次构建产物；给 AI agent 一台测试机的 shell，让它跑完验证就断开。共同特征是连接生命周期短，两端都不想为此维护一套长期的网络关系。&lt;/p&gt;
&lt;p&gt;现有方案各自要付出代价：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;方案&lt;/th&gt;
          &lt;th&gt;缺什么&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;裸 WireGuard&lt;/td&gt;
          &lt;td&gt;没有 NAT 打洞，两端都在 NAT 后就废了，还需要固定公网端点&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Tailscale / Nebula / ZeroTier&lt;/td&gt;
          &lt;td&gt;需要注册账号、身份体系、控制平面，接入前先组网&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Magic Wormhole&lt;/td&gt;
          &lt;td&gt;只做文件传输，提供不了通用连接&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;SSH 反向隧道&lt;/td&gt;
          &lt;td&gt;需要一台有公网 IP 的中间机器，还要开放端口&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Tailcat 的取舍是只保留数据平面。Tailscale 产品由控制平面和数据平面组成，控制平面负责协调服务器、身份、ACL 策略这些治理能力，数据平面负责 WireGuard 加密、disco 打洞协议、DERP 中继这些传输能力。Tailcat 把控制平面整个剥掉，只留下数据平面，接入动作从“注册账号、安装、登录、组网”压缩到“跑一条命令，分享一个 token”。管道模型沿用 nc，换掉的是底下的传输层。Tailscale 对这个定位的说法是 “Tailscale without Tailscale, by Tailscale”。&lt;/p&gt;
&lt;h2 id=&#34;来历&#34;&gt;来历
&lt;/h2&gt;&lt;p&gt;原型诞生于 2023 年 9 月，作者 Brad Fitzpatrick（Tailscale 联合创始人）在一次长途航班上写出了最初版本，当时叫 derpcat，第一次跑通的 commit message 里留着一行当时的备注：“UA 605 PDX-ORD en route to Ireland. yay not buying the wifi.”（航班上没买机上的 Wi-Fi。）&lt;/p&gt;
&lt;p&gt;此后几年它一直活在 tailscale.com 主仓库的 fork 里，随着 Tailscale 内部代码演进几次失修（bitrot），后来重构成 tailscale.com 的常规 Go module 客户端，2026 年 8 月在 TailscaleUp 大会上正式开源。贡献者超过 70 人，包括 danderson、apenwarr 在内的多位 Tailscale 核心成员。当前最新版本 v0.2.0，2026 年 8 月 30 日发布，距离开源只过了 4 天。&lt;/p&gt;
&lt;h2 id=&#34;工作原理&#34;&gt;工作原理
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;连接 token 就是地址。&lt;/strong&gt; 服务端启动后打印一个 &lt;code&gt;tc&lt;/code&gt; 开头的 token，约 50 字节，内部叫 ConnBlob，内容是 base64 编码的 CBOR 数据（一种紧凑的二进制序列化格式），包含服务端的 WireGuard 公钥（Curve25519，32 字节）和 DERP 区域信息。token 通过任意渠道传给对方，聊天窗口、邮件、DNS TXT 记录都行。客户端拿到 token 就有了发起连接所需的全部信息，不需要事先知道对方的 IP 或域名。&lt;/p&gt;
&lt;p&gt;token 有两种形态。默认形态内嵌一个区域编号，客户端首次连接时需要拉取一次最新的 DERP map（中继节点列表）；&lt;code&gt;tailcat resolve&lt;/code&gt; 子命令或服务端 &lt;code&gt;--full-address&lt;/code&gt; 参数生成自包含形态，把中继节点的完整信息直接写进 token，长度更长，但客户端连 DERP map 服务器都不用碰，连接更快。&lt;code&gt;tailcat parse &amp;lt;token&amp;gt;&lt;/code&gt; 可以离线解析任意 token，把公钥和区域信息打印成 JSON，不发起任何连接。&lt;/p&gt;
&lt;p&gt;连接建立分四步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;双方各自连到同一个 DERP 中继（Tailscale 运营的中继服务器网络）&lt;/li&gt;
&lt;li&gt;客户端通过中继发送发现消息 “Meow”，附带自己的公钥，服务端回应 “Meowed”，并把客户端公钥加入 WireGuard peer 列表&lt;/li&gt;
&lt;li&gt;WireGuard 握手建立加密隧道，初期流量走中继&lt;/li&gt;
&lt;li&gt;双方并行执行 STUN 探测（一种探测各自公网 UDP 地址的协议）和 UDP 打洞，成功后升级为 P2P 直连，失败则继续走 DERP 中继&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;DERP 承担的角色是牵线和兜底。多数网络环境下打洞会成功，实际流量走直连，不经过 Tailscale 的服务器；打洞失败（比如两端都在对称 NAT 后）才由中继转发，官方托管的中继有限速。默认 DERP 集群是 Tailscale 为 Tailcat 单独跑的一套，地址在 tailcat.dev/derpmap.json，域名和证书里都不出现 tailscale.com，与 Tailscale 主产品的中继网络分开。&lt;/p&gt;
&lt;p&gt;纯用户态是第三个关键设计，网络栈的四个组件全部跑在用户态。wireguard-go 负责加密所有隧道流量；magicsock 是 Tailscale 的传输层，把直连 UDP 和 DERP 中继的流量复用在同一条连接上，STUN 探测和 UDP 打洞都在这一层完成；gVisor netstack 是用户态 TCP/IP 栈，在进程内终结 TCP 连接，让 Tailcat 不配置任何操作系统网络就能收发连接；DERP 中继协议承担牵线通道和兜底数据路径。整个程序不创建内核 TUN 设备（虚拟网卡）、不改路由表、不改 DNS、不需要 root 权限。一个静态二进制拷到目标机器就能跑，放进 Docker 容器也不需要特权模式。Tailscale 产品本身要动系统网络配置，Tailcat 什么都不碰，这是两者部署摩擦差距最大的地方。&lt;/p&gt;
&lt;p&gt;每个 peer 的内部地址是从 WireGuard 公钥推导出的确定性 IPv6 地址，README 特别注明这是实现细节，未暴露给用户，后续版本可能改掉。&lt;/p&gt;
&lt;p&gt;服务端启动时先打印选中的中继区域（如 “Selected bootstrap relay region 302, San Francisco”），再打印一行带 🐈 图标的监听地址。这些信息走 stderr，token 和状态输出不会混进 stdout 的数据流，默认管道模式下通道里只有数据，写脚本时不用做输出分离。&lt;/p&gt;
&lt;h2 id=&#34;功能模式&#34;&gt;功能模式
&lt;/h2&gt;&lt;p&gt;默认模式把两端的 stdin/stdout 接通，行为与 nc 一致。&lt;/p&gt;
&lt;h3 id=&#34;基本使用方法&#34;&gt;基本使用方法
&lt;/h3&gt;&lt;p&gt;最基础的用法是管道互通。机器 A 裸跑 &lt;code&gt;tailcat&lt;/code&gt; 当服务端，它挂起等待连接，并打印自己的地址：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tailcat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Selected bootstrap relay region 302, San Francisco&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 🐈 Server listening with new address: tcomFwWCCcjS5nKNqAod034nWoJZW0LZq...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;hangs, waiting...&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;机器 B 用这个地址当参数，把数据送进管道：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo hello | tailcat tcomFwWCCcjS5nKNqAod034nWoJZW0LZq...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;机器 A 的进程解除阻塞，从 stdout 收到数据：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tailcat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 🐈 Server listening with new address: tcomFwWCCcjS5nKNqAod034nWoJZW0LZq...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hello
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;两端管道里流什么由你自己决定，&lt;code&gt;tar cz projects/ | tailcat&lt;/code&gt; 打包目录发过去、对端 &lt;code&gt;tailcat &amp;lt;token&amp;gt; | tar xz&lt;/code&gt; 解开，就是一次端到端加密的文件传输。&lt;/p&gt;
&lt;p&gt;第二个用法是把本地 TCP 端口暴露到隧道对端：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 机器 A，暴露本地 8080 和 8443 端口（--serve=all 暴露全部）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tailcat --serve&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;8080,8443
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 🐈 Server listening with new address: tcXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 机器 B，地址后面跟端口号，得到一条直通该端口的原始 TCP 连接&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tailcat tcXXXXXXXXX &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET / HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Host: foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt; OK
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;客户端这个模式是交互式的，输入什么就以原始字节发到服务端端口，效果等同端口转发，但中间没有公网监听。&lt;/p&gt;
&lt;p&gt;第三个用法是 SSH。服务端启动免认证 SSH（隧道层有 WireGuard 认证兜底），客户端用 &lt;code&gt;tailcat ssh&lt;/code&gt; 连接：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 机器 A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tailcat --serve&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;no-auth-ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 🐈 Server listening with new address: tcXXXXXXXXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 机器 B，进入交互式 shell，或在命令里直接附上要执行的命令&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tailcat ssh tcXXXXXXXXX
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tailcat ssh tcXXXXXXXXX ls -la
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要复用系统自己的 SSH 服务端（保留原有认证和配置），服务端改成 &lt;code&gt;tailcat --serve=22&lt;/code&gt;，把连接代理给本机 sshd 即可。&lt;/p&gt;
&lt;p&gt;在此之上有更多子命令和参数：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;命令&lt;/th&gt;
          &lt;th&gt;用途&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;tailcat&lt;/code&gt;（默认）&lt;/td&gt;
          &lt;td&gt;stdin/stdout 管道互通&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;--serve=8080,8443&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;把本地 TCP 端口暴露到隧道另一端&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;--serve=no-auth-ssh&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;提供 SSH 服务，认证由隧道层的 WireGuard 密钥承担&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;tailcat ssh &amp;lt;token&amp;gt; [cmd]&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;作为 SSH 客户端连接&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;--serve=exit-node&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;客户端借用服务端的网络出口&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;tailcat socks &amp;lt;token&amp;gt; curl ...&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;起一个 SOCKS5 代理&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;tailcat ping --until-direct&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;测连通性，显示当前走中继还是直连&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;tailcat parse / resolve&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;离线解析 token / 换成自包含形态&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;--allow=nodekey:xxx&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;客户端公钥白名单，列表外的握手直接忽略&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;几个模式的机制值得展开。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--serve&lt;/code&gt; 暴露端口时，客户端拿到 token 后通过隧道直接访问服务端指定的本地端口，效果等同端口转发，但中间没有公网监听，也不需要在防火墙上开口子。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;no-auth-ssh&lt;/code&gt; 的“免认证”听上去危险，实际上认证发生在更底层。WireGuard 握手要求双方密钥匹配，能通过握手到达 SSH 服务的只有持有 token 并完成密钥协商的对端，SSH 层的密码认证被隧道层替代了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;exit-node&lt;/code&gt; 把服务端当成出口网关，客户端的出网流量从服务端所在的网络出去，IP 归属也随之改变。临时借用某个地区的网络环境时用得上。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--allow&lt;/code&gt; 白名单是访问控制的全部。没有它时，任何拿到 token 的机器都能发起连接；配上之后，公钥不匹配的握手在服务端被静默丢弃，对方连服务器是否存在都探测不到。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tailcat ping&lt;/code&gt; 的输出直接展示连接质量，每一跳都标明路径：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tailcat ping --until-direct &amp;lt;token&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pong in 42.1ms via DERP&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;sfo&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pong in 1.2ms via 203.0.113.7:41641
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一跳走旧金山的中继（42.1ms），打洞成功后第二跳起就是直连（1.2ms）。&lt;code&gt;--until-direct&lt;/code&gt; 会持续探测直到出现直连路径，默认 10 秒超时。&lt;/p&gt;
&lt;p&gt;有个巧妙的设计是 token 可以直接当 URL 主机名用。SOCKS 代理模式识别 token 形态的主机名并自动拨号，所以 &lt;code&gt;tailcat socks curl http://&amp;lt;token&amp;gt;:8081/&lt;/code&gt; 里 token 参数都可以省掉。限制是 token 大小写敏感，浏览器会把主机名转成小写，这条路径只在 curl 和多数命令行工具里可用。&lt;/p&gt;
&lt;h3 id=&#34;密钥生命周期&#34;&gt;密钥生命周期
&lt;/h3&gt;&lt;p&gt;默认 ephemeral key（临时密钥），每次运行生成新密钥，进程退出地址即作废，适合一次性任务。&lt;code&gt;tailcat genkey&lt;/code&gt; 生成持久密钥，存到 &lt;code&gt;~/.config/tailcat/keys/&lt;/code&gt; 下，地址长期稳定。&lt;/p&gt;
&lt;p&gt;这里有个要留意的默认行为。&lt;code&gt;default&lt;/code&gt; 是一个魔法密钥名，一旦这个文件存在，之后裸跑 &lt;code&gt;tailcat&lt;/code&gt; 会静默改用持久密钥，不再生成一次性地址。区分方式看启动输出，“listening with saved key” 是复用旧地址，“listening with new address” 是新生成的。想强制一次性密钥用 &lt;code&gt;--key=new&lt;/code&gt;，用别的密钥用 &lt;code&gt;--key=&amp;lt;name&amp;gt;&lt;/code&gt;，删掉默认密钥用 &lt;code&gt;genkey --delete --key=default&lt;/code&gt;，&lt;code&gt;genkey --list&lt;/code&gt; 列出所有已存密钥。本地存过密钥的机器上跑一次性任务，记得检查这一行。&lt;/p&gt;
&lt;h3 id=&#34;一个完整的固定域名-ssh-方案&#34;&gt;一个完整的固定域名 SSH 方案
&lt;/h3&gt;&lt;p&gt;把前面的机制组合起来，就是官方 README 里给出的 “Protected SSH over DNS” 方案，全程零开放入站端口、零端口转发：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;客户端生成身份密钥对，拿到自己的公钥：&lt;code&gt;tailcat genkey --client&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;服务端生成绑定固定区域的密钥并启动 SSH 服务，白名单只放行这个公钥：&lt;code&gt;tailcat genkey --fixed-region&lt;/code&gt;，然后 &lt;code&gt;tailcat --serve=22 --allow=nodekey:cfb6bf...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;把服务端 token 发布成 DNS TXT 记录，形如 &lt;code&gt;my-server.example.com. 300 IN TXT &amp;quot;tailcat=tcXXX...&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;客户端一条命令连接：&lt;code&gt;tailcat ssh my-server.example.com&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;--fixed-region&lt;/code&gt; 的作用是在 genkey 时刻探测一次最近的中继区域并固化进密钥文件，服务重启后 token 不变，发布在 DNS 里的记录保持有效。默认的 &lt;code&gt;--region=auto&lt;/code&gt; 每次启动重新探测，适合一次性场景，不适合发布出去的地址。这个方案里 SSH 服务在公网上完全不可见，端口扫描看不到它，WireGuard 在 SSH 服务看到任何数据包之前就完成了客户端认证，白名单之外的所有握手被静默丢弃。&lt;/p&gt;
&lt;h2 id=&#34;作为-go-库嵌入&#34;&gt;作为 Go 库嵌入
&lt;/h2&gt;&lt;p&gt;Tailcat 的代码组织成一个普通 Go module，CLI 只是它的一个使用者。Server 和 Client 两个类型的零值即可工作，隧道在第一次拨号时才建立：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tailcat&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Server&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;OnTCP&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;net&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Conn&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;net&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Conn&lt;/span&gt;) { &lt;span style=&#34;color:#75715e&#34;&gt;/* 处理到来的连接 */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Start&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ConnBlob&lt;/span&gt;()) &lt;span style=&#34;color:#75715e&#34;&gt;// 打印连接 token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;客户端侧两行完成拨号：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tailcat&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tailcat&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ConnBlob&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Args&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DialTCPPort&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Background&lt;/span&gt;(), &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个形态把“打洞加加密”封装成可嵌入的库能力，应用自己决定 token 的分发方式和连接的生命周期。远程开发工具、agent 执行框架这类需要临时打通网络的产品，可以直接把 Server 嵌进自己的进程，跳过 CLI 和管道。前面对比表里 iroh 一行“库形态与工具形态”的差异，指的就是这个维度。&lt;/p&gt;
&lt;h2 id=&#34;安全与信任模型&#34;&gt;安全与信任模型
&lt;/h2&gt;&lt;p&gt;官方文档明确说明 Tailcat 不是完全匿名的服务。身份就是“持有 token 加公钥对”，没有用户体系，托管中继会记录连接 metadata，官方对隐私场景的原话是 “isn’t intended for privacy”。&lt;/p&gt;
&lt;p&gt;使用时有三条边界要清楚：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;token 等同密码。&lt;/strong&gt; 泄露即授权，官方警告不要提交进 git、不要公开发布&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;托管中继有日志。&lt;/strong&gt; 中间人看不到内容（流量 WireGuard 端到端加密），但中继运营方知道谁在何时与谁建立过连接&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无稳定性承诺。&lt;/strong&gt; README 的 Stability 一节写得很直白，Go API、CLI 参数与输出、wire format 都可能变，官方中继无 uptime SLA 和吞吐承诺，且“可能以任何理由随时收回访问权限”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对信任敏感的部署，&lt;code&gt;genkey --region=derp.example.com&lt;/code&gt; 可以把自建 derper 的主机名直接写进 token，客户端从此完全不接触 Tailscale 的 DERP map 服务器和中继，限速策略也换成了自己的。自建 derper 只需要一台有域名和 TLS 证书的机器（derper 自己能通过 Let’s Encrypt 签证书），是 Tailscale 长期维护的开源组件。跑整个中继集群的话，用 &lt;code&gt;--derpmap-url&lt;/code&gt; 让双方指向自己的 DERP map JSON 即可。这些都不想维护，Tailscale 也提供付费的专用中继和技术支持，README 里那句 &amp;ldquo;contact sales&amp;rdquo; 后面跟着的措辞照例带着这个团队一贯的玩笑味。&lt;/p&gt;
&lt;h2 id=&#34;与同类工具的对比&#34;&gt;与同类工具的对比
&lt;/h2&gt;&lt;p&gt;Hacker News 上 tptacek 的评价是 “Magic Wormhole but for generalized connectivity”，一句话点出了它和文件传输工具的分界。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;工具&lt;/th&gt;
          &lt;th&gt;与 Tailcat 的差异&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;iroh / dumbpipe&lt;/td&gt;
          &lt;td&gt;功能最接近的 Rust 实现，打洞算法参考了 Tailscale，差异在库形态与工具形态&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;wush（coder 出品）&lt;/td&gt;
          &lt;td&gt;底层同样基于 Tailscale，功能是 Tailcat 的子集&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Magic Wormhole&lt;/td&gt;
          &lt;td&gt;短人类可读码加 PAKE（口令认证密钥交换）做文件传输，覆盖不了通用连接&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;裸 WireGuard&lt;/td&gt;
          &lt;td&gt;缺 NAT 打洞，Tailcat 相对它的增量主要就在这一层&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Tailscale 本体&lt;/td&gt;
          &lt;td&gt;保留控制平面，可审计、可治理，适合长期组网&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;裸 WireGuard 那一行值得引一句 Brad Fitzpatrick 在讨论帖里的原话：“WireGuard doesn’t do NAT traversal. That’s the main thing this adds.” 两端都有公网可达性时 WireGuard 更简单，两端都藏在 NAT 后时 Tailcat 的打洞层就是全部增量。&lt;/p&gt;
&lt;p&gt;iroh 是 Rust 生态里功能最接近的项目，同样提供不依赖控制平面的点对点连接，disco 打洞算法本身就参考了 Tailscale 的实现。两者的差别在生态位，iroh 主打嵌入 Rust 应用的库场景，Tailcat 同时提供开箱即用的 CLI 和 Go 库，命令行侧的完成度更高。&lt;/p&gt;
&lt;p&gt;两者的分界线官方给得很直接，需要可治理的网络（身份、策略、审计）用 Tailscale，需要一条快速连接用 Tailcat，一个管长期组网，一个管临时连接。&lt;/p&gt;
&lt;p&gt;社区讨论里有两个主要争议。一是 netcat 类比是否恰当，有评论认为 netcat 没有第三方依赖，Tailcat 依赖中继集群；回应是 DERP 本身是 Tailscale 运营 6 年以上的公共品，Tailcat 用的是独立集群，且中继只在牵线和兜底时参与。二是恶意软件可能拿它做 C2 通道（受控端回连指挥服务器的通道），官方中继的限速是主要缓解手段，自建 derper 的运营方则要自己面对这个问题。&lt;/p&gt;
&lt;h2 id=&#34;适用场景与局限&#34;&gt;适用场景与局限
&lt;/h2&gt;&lt;p&gt;官方点名的场景都带临时属性：给开发环境开一小时 SSH 访问、让 agent 连一次测试机、传一批文件、联机游戏、CI 任务跳板。共性是连接生命周期短，事后不需要维持关系，用完即弃。&lt;/p&gt;
&lt;p&gt;agent 场景被官方摆在很靠前的位置，README 里有个细节能看出重视程度。完整的用法文档被编译进了二进制，&lt;code&gt;tailcat --readme&lt;/code&gt; 可以在无网络的机器上直接查看，对应 commit message 写明是为了“只有二进制的人和 AI agent 不联网也能学会用法”。产品页给 agent 留的说明也很直白，agent 走不完注册和登录流程，给它一个 token 就够了，任务结束地址作废，不需要事后清理设备或回收权限。&lt;/p&gt;
&lt;p&gt;局限同样明确：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;连接质量依赖打洞结果，直连失败时官方中继限速，不适合大流量长期转发&lt;/li&gt;
&lt;li&gt;没有身份和策略体系，多团队、多设备的权限治理做不了，这是剥掉控制平面的代价&lt;/li&gt;
&lt;li&gt;项目处于早期，生产环境依赖要么自建 derper，要么接受官方中继随时可收回的现状&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;上手路径&#34;&gt;上手路径
&lt;/h2&gt;&lt;p&gt;发布工程走 GoReleaser，tag 推送后自动构建发布到 GitHub Releases。Linux 提供 tar.gz、deb、rpm 三种格式，覆盖 amd64、arm64、armv7 三种架构；Windows 提供 zip，覆盖 amd64 和 arm64。全部是静态二进制，没有 Go 工具链也能直接运行，&lt;code&gt;tailcat --version&lt;/code&gt; 确认版本。&lt;/p&gt;
&lt;p&gt;Docker 镜像发布在 &lt;code&gt;ghcr.io/tailscale/tailcat&lt;/code&gt;，多架构 manifest，基础镜像用 distroless static，以非 root 用户运行，自带连接 DERP 中继所需的 CA 证书。macOS 暂时没有预编译包，通过 &lt;code&gt;go install github.com/tailscale/tailcat/cmd/tailcat@latest&lt;/code&gt; 安装，Homebrew 支持在计划中。&lt;/p&gt;
&lt;p&gt;装好后从上面「基本使用方法」的管道互通开始验证即可。连通性质量用 &lt;code&gt;tailcat ping --until-direct &amp;lt;token&amp;gt;&lt;/code&gt; 查看，输出会显示当前连接走中继还是直连，对评估实际带宽有参考价值。&lt;/p&gt;
&lt;h2 id=&#34;选型判断&#34;&gt;选型判断
&lt;/h2&gt;&lt;p&gt;三个问题帮你在 Tailcat 和其他方案之间做选择：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;连接是一次性的还是长期的？&lt;/strong&gt; 一次性用 Tailcat，ephemeral key 用完即废。长期在线的设备组网用 Tailscale 或裸 WireGuard&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;两端有没有一方具备公网可达性？&lt;/strong&gt; 有且流量敏感，裸 WireGuard 更简单。两端都藏在 NAT 后，Tailcat 的打洞是直接收益&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;能不能接受托管中继看到连接 metadata？&lt;/strong&gt; 不能就自建 derper，把 token 里的区域指向自己的服务器&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;参考来源&#34;&gt;参考来源
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://tailscale.com/tailcat&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Tailcat 产品页&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/tailscale/tailcat&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;tailscale/tailcat GitHub 仓库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://news.ycombinator.com/item?id=49452990&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hacker News 讨论帖（662 points）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://tailcat.dev/derpmap.json&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Tailcat 默认 DERP map&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
