kin · rendezvous

会合服务

它只做一件事:让两个都在 NAT 后面的朋友互相知道对方当前的外部地址。 之后它就无事可做了 —— 隧道是两台设备之间直接建立的,一个字节都不经过这里。

为什么需要它

kin 是朋友之间互相提供网络出口的 VPN。要让两台设备直接相连,双方必须 同时 知道对方此刻的外部地址,才能同时朝对方发包、把 NAT 上的洞打开。

麻烦在于:双方都在 NAT 后面,谁也不可达 —— 没有人能把这个地址直接递给对方。 所以需要一个在公网上的、双方都能连上的撮合方,把两张名片放进同一个抽屉里。 这就是这个服务,也仅此而已。

它看得到什么,看不到什么

✓ 它看得到

  • 一个随机的一次性 token(配对码里带的,不含任何身份信息)
  • 一个公钥指纹和一个 base64 公钥
  • 最多 16 个 ip:port 候选地址
  • 一个你自己填的显示名,比如「哥哥的台式机」

✕ 它看不到

  • 你的流量内容 —— 它不在数据路径上
  • 你访问了什么网站、流量去了哪里
  • 你是谁:没有账号、没有注册、没有用户表
  • 这次握手最后成没成 —— 打洞是两端之间的事

它不留什么

每条记录的 TTL 是 300 秒,到点自己消失。会合是短暂的,不该留痕。

它不记录客户端 IP,不写访问日志,没有分析脚本 —— 这一页连 JavaScript 都没有。 服务端的全部状态就是一个 KV namespace:没有数据库,没有队列,没有一个 secret, 因为它不需要知道任何需要保密的东西。

它也不能偷偷变成中继:这个服务只有一个端点,只收发 JSON, 不具备转发 UDP 的能力。流量要么直连,要么根本不通。

接口

两条,就这些。超额或畸形的输入一律拒绝。

POST /api/rv
  → {"token":"…","endpoint":{"peer_id":…,"pub_key":…,
                             "candidates":["203.0.113.7:51820"],"name":…}}
  ← 200 {"ok":true}

GET  /api/rv?token=…&self=…
  ← 200 {"ready":true,"endpoint":{…}}   // 该 token 下不是你自己的那条
  ← 200 {"ready":false}                 // 对端还没来,等一会儿再问

自己跑一个

这个服务是开源的,也刻意做得很小 —— 小到你可以读完全部代码, 然后部署成你自己的那一个:只需要一个 Cloudflare 账号和一条 wrangler pages deploy。 不用相信别人的会合服务,是这件事里最重要的一部分。