Overview
Section titled “Overview”The Hoody Tunnel kit exposes endpoints for inspecting and managing active tunnel sessions. The kit multiplexes port-forwarding traffic between containers and the broader Hoody runtime. Use these endpoints to monitor health, enumerate active sessions and bindings, scrape Prometheus metrics, and terminate problematic sessions.
The WebSocket control plane uses a subprotocol-negotiated binary protocol (hoody-tunnel.v1 or hoody-tunnel.v2); the REST endpoints below are for introspection and administration only.
All endpoints are served from the container-scoped URL https://{projectId}-{containerId}-tunnel-1.{serverName}.containers.hoody.icu.
Health & Metrics
Section titled “Health & Metrics”GET /api/v1/tunnel/health
Section titled “GET /api/v1/tunnel/health”Returns the standard kit health snapshot. No authentication is required.
This endpoint takes no parameters.
curl https://prj_alpha-ctnr_7f3b-tunnel-1.us-east-1.containers.hoody.icu/api/v1/tunnel/healthawait client.tunnel.health.check(){ "built": "2024-01-15T10:30:00Z", "fds": 128, "ip": "10.0.0.42", "memory": { "heap": 41943040, "rss": 83886080 }, "pid": 12345, "service": "tunnel", "started": "2024-01-15T12:00:00Z", "status": "ok", "userAgent": "hoody-cli/1.2.3"}The status field is always "ok" on a healthy kit. memory is an object containing rss (resident set size) and optional heap bytes; either may be null on some platforms. built and fds may be null when the underlying runtime does not expose them.
GET /api/v1/tunnel/metrics
Section titled “GET /api/v1/tunnel/metrics”Returns Prometheus text-format metrics for scraping, including active session count, active binding count, and file-descriptor permits available.
This endpoint takes no parameters.
curl https://prj_alpha-ctnr_7f3b-tunnel-1.us-east-1.containers.hoody.icu/api/v1/tunnel/metricsawait client.tunnel.getMetrics()# HELP tunnel_active_sessions Number of active tunnel sessions# TYPE tunnel_active_sessions gaugetunnel_active_sessions 3# HELP tunnel_active_bindings Number of active bindings# TYPE tunnel_active_bindings gaugetunnel_active_bindings 7# HELP tunnel_fd_permits_available File descriptor permits available# TYPE tunnel_fd_permits_available gaugetunnel_fd_permits_available 1017The response body uses the text/plain Prometheus exposition format with one or more metric series per stat.
Sessions
Section titled “Sessions”GET /api/v1/tunnel/sessions
Section titled “GET /api/v1/tunnel/sessions”Returns all active tunnel sessions with their bindings, stream counts, and protocol version.
This endpoint takes no parameters.
curl https://prj_alpha-ctnr_7f3b-tunnel-1.us-east-1.containers.hoody.icu/api/v1/tunnel/sessionsawait client.tunnel.listSessions(){ "sessions": [ { "activeStreams": 4, "bindings": [ { "bindId": 1, "containerPort": 8080, "kind": "expose", "mode": "tcp" }, { "bindId": 2, "containerPort": 3000, "kind": "pull", "mode": "tcp" } ], "connectionsGranted": 12, "isV2": true, "maxStreams": 256, "peerAddr": "10.0.0.100:54321", "sessionId": "sess_abc123def456" }, { "activeStreams": 0, "bindings": [], "connectionsGranted": 1, "isV2": false, "maxStreams": 64, "peerAddr": "10.0.0.101:54322", "sessionId": "sess_xyz789ghi012" } ], "total": 2}activeStreamsis the current count of in-flight multiplexed streams.maxStreamsis the negotiated concurrency ceiling.isV2reports whether the session negotiated thehoody-tunnel.v2subprotocol.bindingslists every binding registered against the session, regardless of kind.
DELETE /api/v1/tunnel/sessions/{session_id}
Section titled “DELETE /api/v1/tunnel/sessions/{session_id}”Terminates an active tunnel session. The kit sends a GOAWAY(0x0001, "closed by admin") frame on the live WebSocket and force-closes the connection after grace_ms. Admin kills are non-resumable: orphan parking is skipped.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
session_id | path | string | Yes | Session ID as returned by GET /api/v1/tunnel/sessions |
grace_ms | query | integer | No | GOAWAY drain budget in ms (0–5000, default 50) |
curl -X DELETE \ "https://prj_alpha-ctnr_7f3b-tunnel-1.us-east-1.containers.hoody.icu/api/v1/tunnel/sessions/sess_abc123def456?grace_ms=250"await client.tunnel.killSession("sess_abc123def456", { grace_ms: 250 }){ "sessionId": "sess_abc123def456", "status": "closing"}The response is returned immediately after the GOAWAY frame is queued. The session is force-closed once grace_ms elapses.
{ "error": "grace_ms must be between 0 and 5000"}Returned when grace_ms is outside the 0–5000 range or is not an integer.
{ "error": "session not found"}Returned when no active session matches session_id.
Bindings
Section titled “Bindings”GET /api/v1/tunnel/bindings
Section titled “GET /api/v1/tunnel/bindings”Returns all active EXPOSE and PULL bindings across every session, with their owning session, port, kind, mode, and bind ID.
This endpoint takes no parameters.
curl https://prj_alpha-ctnr_7f3b-tunnel-1.us-east-1.containers.hoody.icu/api/v1/tunnel/bindingsawait client.tunnel.listBindings(){ "bindings": [ { "bindId": 1, "kind": "expose", "mode": "tcp", "port": 8080, "sessionId": "sess_abc123def456" }, { "bindId": 2, "kind": "pull", "mode": "tcp", "port": 3000, "sessionId": "sess_abc123def456" }, { "bindId": 3, "kind": "expose", "mode": "udp", "port": 5353, "sessionId": "sess_xyz789ghi012" } ], "total": 3}kindis either"expose"(container-initiated outbound exposure) or"pull"(runtime-initiated inbound pull).modeis the transport mode, typically"tcp"or"udp".bindIdis unique per session; the same port may appear under different sessions.
Combined Tunnel View
Section titled “Combined Tunnel View”GET /api/v1/tunnel/tunnels
Section titled “GET /api/v1/tunnel/tunnels”Returns a unified view of every active tunnel session with its expose and pull bindings broken out separately, plus stream counts, orphan count, and FD budget status.
This endpoint takes no parameters.
curl https://prj_alpha-ctnr_7f3b-tunnel-1.us-east-1.containers.hoody.icu/api/v1/tunnel/tunnelsawait client.tunnel.listTunnels(){ "fdPermitsAvailable": 1024, "orphanedSessions": 0, "sessions": [ { "activeStreams": 4, "connectionsGranted": 12, "exposeBindings": [ { "bindId": 1, "containerPort": 8080 } ], "peerAddr": "10.0.0.100:54321", "protocol": "hoody-tunnel.v2", "pullBindings": [ { "bindId": 2, "containerPort": 3000 } ], "sessionId": "sess_abc123def456" } ], "totalBindings": 2, "totalStreams": 4}orphanedSessionscounts sessions parked after unexpected disconnect but not yet garbage-collected.fdPermitsAvailableis the remaining file-descriptor capacity the kit can grant to new bindings.protocolis the negotiated subprotocol version string (e.g."hoody-tunnel.v2"or"hoody-tunnel.v1").
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. See the kit README for the full wire protocol.
This endpoint takes no parameters.
curl -i \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ -H "Sec-WebSocket-Protocol: hoody-tunnel.v2" \ https://prj_alpha-ctnr_7f3b-tunnel-1.us-east-1.containers.hoody.icu/api/v1/tunnel/connectawait client.tunnel.tunnelConnect()The HTTP response status 101 Switching Protocols indicates the WebSocket upgrade was accepted. The server echoes the negotiated subprotocol in the Sec-WebSocket-Protocol response header. From this point on, the connection carries binary frames following the hoody-tunnel wire protocol.
The server rejects the upgrade when the requested subprotocol is missing or unsupported. The response includes the header x-hoody-tunnel-versions listing the supported subprotocol versions.