# Hoody Tunnel

**Page:** api/kit/tunnel

[Download Raw Markdown](./api/kit/tunnel.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



## 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

### `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.



```bash
curl https://tunnel.example.com/api/v1/tunnel/health
```


```typescript
const health = await client.tunnel.health.check();
```


```json
{
  "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`

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.



```bash
curl https://tunnel.example.com/api/v1/tunnel/metrics
```


```typescript
const metrics = await client.tunnel.getMetrics();
```


```
# HELP hoody_tunnel_sessions_active Number of active tunnel sessions
# TYPE hoody_tunnel_sessions_active gauge
hoody_tunnel_sessions_active 3
# HELP hoody_tunnel_bindings_active Number of active bindings
# TYPE hoody_tunnel_bindings_active gauge
hoody_tunnel_bindings_active 7
# HELP hoody_tunnel_fd_permits_available Available file descriptor permits
# TYPE hoody_tunnel_fd_permits_available gauge
hoody_tunnel_fd_permits_available 450
```

The response uses the `text/plain` content type in Prometheus exposition format.




## Sessions & Bindings

### `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.



```bash
curl https://tunnel.example.com/api/v1/tunnel/sessions
```


```typescript
const { sessions, total } = await client.tunnel.listSessions();
```


```json
{
  "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`

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.



```bash
curl https://tunnel.example.com/api/v1/tunnel/bindings
```


```typescript
const { bindings, total } = await client.tunnel.listBindings();
```


```json
{
  "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`

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.



```bash
curl https://tunnel.example.com/api/v1/tunnel/tunnels
```


```typescript
const overview = await client.tunnel.listTunnels();
```


```json
{
  "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}`

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

| 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&ndash;5000, default 50) |



```bash
curl -X DELETE \
  "https://tunnel.example.com/api/v1/tunnel/sessions/sess_abc123def456?grace_ms=200"
```


```typescript
await client.tunnel.killSession({
  session_id: "sess_abc123def456",
  grace_ms: 200
});
```


```json
{
  "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 |



```json
{
  "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 |



```json
{
  "error": "Session not found"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `error` | string | Yes | Human-readable error message |




## WebSocket Control Plane

### `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.


This is a WebSocket upgrade endpoint, not a standard JSON-over-HTTP call. The SDK accessor returns a WebSocket handle that you drive with `HELLO`/`WELCOME`, `BIND`/`BOUND`, `OPEN`/`DATA`/`CLOSE`, and `GOAWAY` frames.




```bash
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/connect
```


```typescript
const socket = await client.tunnel.tunnelConnect();
// First binary frame must be a HELLO on the control stream.
```


```
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Protocol: hoody-tunnel.v2
```

The 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.