yao/tai/docs/tunnel.md
Max d70694b7ac ci: update Tai to 1.2.0 with new default ports and fix documentation
- Update CI (unit-test.yml, pr-test.yml) to use yaoapp/tai:1.2.0
  with new default ports (gRPC:19100, HTTP:8099, VNC:16080, Docker:12375)
- Add explicit 0.0.0.0 bind for containerized Tai instances
- Fix sandbox/v2 grpc.go default port fallback (9100 → 19100)
- Fix tai/tunnel/proxy.go fallback ports (8080→8099, 6080→16080)
- Sync tai SDK and sandbox/v2 documentation with implementation
- Add new docs: api.md, registry.md, tunnel.md

Made-with: Cursor
2026-03-08 11:33:56 +08:00

3.1 KiB

Package tunnel

WebSocket tunnel handlers for Tai nodes that cannot be reached directly (e.g. behind NAT/firewall). Provides Gin HTTP handlers mounted on the Yao server.

Handlers

Method Route Handler Description
GET /ws/tai HandleControl Control channel WebSocket
GET /ws/tai/data/:channel_id HandleData Data channel WebSocket
ANY /tai/:taiID/proxy/*path HandleProxy HTTP reverse proxy via tunnel
GET /tai/:taiID/vnc/*path HandleVNC VNC WebSocket proxy via tunnel

All WebSocket endpoints require Authorization: Bearer <token> header.

HandleControl

Manages the long-lived control WebSocket for a Tai node.

Flow:

  1. Authenticate Bearer token via OAuth
  2. Upgrade to WebSocket
  3. Read register message (JSON):
{
  "type": "register",
  "tai_id": "tai-abc123",
  "machine_id": "m-001",
  "version": "1.2.0",
  "server": "http://yao-server:5099",
  "ports": {"grpc": 19100, "http": 8099, "vnc": 16080, "docker": 12375},
  "capabilities": {"docker": true, "host_exec": true},
  "system": {"os": "linux", "arch": "amd64", "hostname": "host-01", "num_cpu": 8}
}
  1. Register node in global registry with Mode="tunnel"
  2. Reply {"type": "registered", "tai_id": "tai-abc123"}
  3. Loop reading messages:
    • {"type": "ping"} → update heartbeat, reply {"type": "pong"}
  4. On disconnect → unregister node

HandleData

Handles on-demand data channel connections from Tai.

Flow:

  1. Authenticate Bearer token
  2. Extract :channel_id from URL
  3. Upgrade to WebSocket
  4. Wrap WebSocket as net.Conn (bidirectional byte bridge)
  5. Call registry.AcceptDataChannel(channelID, clientID, conn)

The channel_id must match a pending RequestChannel call. The clientID (from token) must match the Tai node that owns the channel.

HandleProxy

HTTP reverse proxy for tunnel-connected Tai nodes.

Flow:

  1. Look up Tai node from :taiID in registry
  2. Get the node's HTTP port (from node.Ports["http"], default 8099)
  3. Open a tunnel data channel to that port via RequestChannel
  4. Forward the incoming HTTP request through the tunnel
  5. Read the response and stream it back to the client

HandleVNC

VNC WebSocket proxy for tunnel-connected Tai nodes.

Flow:

  1. Look up Tai node from :taiID in registry
  2. Get the node's VNC port (from node.Ports["vnc"], default 16080)
  3. Open a tunnel data channel to that port via RequestChannel
  4. Upgrade the client connection to WebSocket
  5. Bridge client WebSocket ↔ tunnel data channel (binary messages)

Internal Types

wsConn

wsConn wraps gorilla/websocket.Conn to implement net.Conn for bidirectional byte bridging. This allows tunnel data channels to be treated as standard TCP connections by the registry's bridge logic.

type wsConn struct { ... }
func (c *wsConn) Read(p []byte) (int, error)   // reads WS binary messages
func (c *wsConn) Write(p []byte) (int, error)  // writes WS binary messages
func (c *wsConn) Close() error
// Also implements: LocalAddr, RemoteAddr, SetDeadline, SetReadDeadline, SetWriteDeadline