Introduction
Section titled “Introduction”The Hoody Tunnel service exposes the management surface for the hoody-tunnel kit. Use these endpoints to inspect active tunnel sessions, expose and pull bindings, stream counts, FD budget, health, and Prometheus metrics, and to administratively terminate sessions. The WebSocket control plane endpoint is documented separately at the bottom of this page.
Health & Monitoring
Section titled “Health & Monitoring”GET /api/v1/tunnel/health
Section titled “GET /api/v1/tunnel/health”Standard kit health endpoint. Returns runtime information about the tunnel process including status, build, start time, memory, file descriptor count, PID, IP, and user agent. No authentication is required.
This endpoint takes no parameters.
curl https://tunnel.example.com/api/v1/tunnel/healthconst health = await client.tunnel.health.check();{ "status": "ok", "service": "hoody-tunnel", "built": "2024-01-15T10:30:00Z", "started": "2024-01-15T12:00:00Z", "memory": { "heap": 15728640, "rss": 41943040 }, "fds": 128, "pid": 12345, "ip": "10.0.0.1", "userAgent": "hoody-cli/1.0.0"}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | Kit health status |
service | string | Yes | Service identifier |
built | string | null | No | Build timestamp |
started | string | Yes | Process start timestamp |
memory | object | null | No | Memory usage; contains heap and rss |
fds | integer | null | No | Open file descriptor count |
pid | integer | Yes | Process ID |
ip | string | Yes | Bound IP address |
userAgent | string | Yes | Reporting user agent |
GET /api/v1/tunnel/metrics
Section titled “GET /api/v1/tunnel/metrics”Returns Prometheus text-format metrics for the tunnel kit. Exposes active session count, active binding count, and available FD permits. Intended for scraping by a Prometheus server.
This endpoint takes no parameters.
curl https://tunnel.example.com/api/v1/tunnel/metricsconst metrics = await client.tunnel.getMetrics();# HELP hoody_tunnel_sessions_active Number of active tunnel sessions# TYPE hoody_tunnel_sessions_active gaugehoody_tunnel_sessions_active 3# HELP hoody_tunnel_bindings_active Number of active bindings# TYPE hoody_tunnel_bindings_active gaugehoody_tunnel_bindings_active 7# HELP hoody_tunnel_fd_permits_available Available file descriptor permits# TYPE hoody_tunnel_fd_permits_available gaugehoody_tunnel_fd_permits_available 450The response uses the text/plain content type in Prometheus exposition format.
Sessions & Bindings
Section titled “Sessions & Bindings”GET /api/v1/tunnel/sessions
Section titled “GET /api/v1/tunnel/sessions”Lists all active tunnel sessions with their bindings, stream counts, and protocol version. Use this to discover live sessions, inspect the V1/V2 protocol in use, and see how many connections and streams each session has open.
This endpoint takes no parameters.
curl https://tunnel.example.com/api/v1/tunnel/sessionsconst { sessions, total } = await client.tunnel.listSessions();{ "sessions": [ { "sessionId": "sess_abc123def456", "peerAddr": "192.168.1.100:54321", "isV2": true, "connectionsGranted": 5, "activeStreams": 3, "maxStreams": 100, "bindings": [ { "bindId": 1, "containerPort": 3000, "kind": "EXPOSE", "mode": "http" } ] } ], "total": 1}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
sessions | array | Yes | Array of SessionInfo objects |
total | integer | Yes | Total number of active sessions |
Each SessionInfo contains: sessionId, peerAddr, isV2, connectionsGranted, activeStreams, maxStreams, and a bindings array. Each BindingInfo contains: bindId, containerPort, kind, and mode.
GET /api/v1/tunnel/bindings
Section titled “GET /api/v1/tunnel/bindings”Lists all active EXPOSE and PULL bindings across every session, with the owning session ID, port, kind, mode, and bind ID. Use this when you need a flat, cross-session view of port bindings.
This endpoint takes no parameters.
curl https://tunnel.example.com/api/v1/tunnel/bindingsconst { bindings, total } = await client.tunnel.listBindings();{ "bindings": [ { "bindId": 1, "kind": "EXPOSE", "mode": "http", "port": 3000, "sessionId": "sess_abc123def456" }, { "bindId": 2, "kind": "PULL", "mode": "tcp", "port": 5432, "sessionId": "sess_abc123def456" } ], "total": 2}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
bindings | array | Yes | Array of BindingDetail objects |
total | integer | Yes | Total number of active bindings |
Each BindingDetail contains: bindId, kind, mode, port, and sessionId.
GET /api/v1/tunnel/tunnels
Section titled “GET /api/v1/tunnel/tunnels”Returns a unified overview of all active tunnels: sessions with their expose and pull bindings broken out, total stream and binding counts, orphan count, and remaining FD budget. Use this as a single-call dashboard for tunnel fleet health.
This endpoint takes no parameters.
curl https://tunnel.example.com/api/v1/tunnel/tunnelsconst overview = await client.tunnel.listTunnels();{ "fdPermitsAvailable": 450, "orphanedSessions": 0, "totalBindings": 3, "totalStreams": 8, "sessions": [ { "sessionId": "sess_abc123def456", "peerAddr": "192.168.1.100:54321", "protocol": "hoody-tunnel.v2", "connectionsGranted": 5, "activeStreams": 3, "exposeBindings": [ { "bindId": 1, "containerPort": 3000 } ], "pullBindings": [ { "bindId": 2, "containerPort": 5432 } ] } ]}Response fields
| Field | Type | Required | Description |
|---|---|---|---|
fdPermitsAvailable | integer | Yes | Remaining file descriptor permits in the FD budget |
orphanedSessions | integer | Yes | Count of orphaned (non-resumable) sessions |
totalBindings | integer | Yes | Total number of bindings across all sessions |
totalStreams | integer | Yes | Total number of active streams across all sessions |
sessions | array | Yes | Array of TunnelSessionView objects |
Each TunnelSessionView contains: sessionId, peerAddr, protocol, connectionsGranted, activeStreams, exposeBindings, and pullBindings. Each TunnelBindingView contains: bindId and containerPort.
DELETE /api/v1/tunnel/sessions/{session_id}
Section titled “DELETE /api/v1/tunnel/sessions/{session_id}”Administratively terminates an active tunnel session. The kit sends a GOAWAY(0x0001, "closed by admin") frame on the WebSocket and force-closes the session after the grace_ms drain window. Admin kills skip the orphan-parking path, so the session is not resumable.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
session_id | path | string | Yes | Session ID as returned by GET /sessions |
grace_ms | query | integer | No | GOAWAY drain budget in ms (0–5000, default 50) |
curl -X DELETE \ "https://tunnel.example.com/api/v1/tunnel/sessions/sess_abc123def456?grace_ms=200"await client.tunnel.killSession({ session_id: "sess_abc123def456", grace_ms: 200});{ "sessionId": "sess_abc123def456", "status": "closing"}The response confirms that close has been initiated. The actual disconnect happens after the grace_ms window.
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string | Yes | Echo of the session ID being closed |
status | string | Yes | Current close status |
{ "error": "Invalid grace_ms: must be between 0 and 5000"}| Field | Type | Required | Description |
|---|---|---|---|
error | string | Yes | Human-readable error message describing the validation failure |
{ "error": "Session not found"}| Field | Type | Required | Description |
|---|---|---|---|
error | string | Yes | Human-readable error message |
WebSocket Control Plane
Section titled “WebSocket Control Plane”GET /api/v1/tunnel/connect
Section titled “GET /api/v1/tunnel/connect”WebSocket upgrade endpoint for the multiplexed tunnel session. Clients MUST request subprotocol hoody-tunnel.v1 or hoody-tunnel.v2 and send a HELLO frame as the first binary message after the upgrade succeeds. See the kit README for the full wire protocol, frame types, and stream multiplexing rules.
This endpoint takes no parameters.
curl -i \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Protocol: hoody-tunnel.v2" \ https://tunnel.example.com/api/v1/tunnel/connectconst socket = await client.tunnel.tunnelConnect();// First binary frame must be a HELLO on the control stream.HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Protocol: hoody-tunnel.v2The upgrade is accepted. The connection is now a WebSocket; the kit expects a HELLO frame as the first binary message.
The upgrade is rejected when the client does not advertise a supported subprotocol. The response includes the x-hoody-tunnel-versions header listing the protocol versions the kit supports.