# pipenet

Expose your local server to the public internet instantly

## Installation

```bash
npm install pipenet
```

## CLI Usage

```bash
# Expose local port 3000 to the internet
npx pipenet client --port 3000

# Request a specific subdomain
npx pipenet client --port 3000 --subdomain myapp

# Use a custom tunnel server
npx pipenet client --port 3000 --host https://your-tunnel-server.com
```

## API

The pipenet client is also usable through an API (for test integration, automation, etc)

### pipenet(port [,options][,callback])

Creates a new pipenet tunnel to the specified local `port`. Will return a Promise that resolves once you have been assigned a public tunnel url. `options` can be used to request a specific `subdomain`. A `callback` function can be passed, in which case it won't return a Promise. This exists for backwards compatibility with the old Node-style callback API. You may also pass a single options object with `port` as a property.

```js
import { pipenet } from 'pipenet';

const tunnel = await pipenet({
  port: 3000,
  host: 'https://pipenet.dev'
});

// the assigned public url for your tunnel
// i.e. https://abcdefgjhij.pipenet.dev
tunnel.url;

tunnel.on('close', () => {
  // tunnels are closed
});
```

#### options

- `port` (number) [required] The local port number to expose through pipenet.
- `host` (string) URL for the upstream proxy server. Defaults to `https://pipenet.dev`.
- `subdomain` (string) Request a specific subdomain on the proxy server. **Note** You may not actually receive this name depending on availability.
- `localHost` (string) Proxy to this hostname instead of `localhost`. This will also cause the `Host` header to be re-written to this value in proxied requests.
- `localHttps` (boolean) Enable tunneling to local HTTPS server.
- `localCert` (string) Path to certificate PEM file for local HTTPS server.
- `localKey` (string) Path to certificate key file for local HTTPS server.
- `localCa` (string) Path to certificate authority file for self-signed certificates.
- `allowInvalidCert` (boolean) Disable certificate checks for your local HTTPS server (ignore cert/key/ca options).

Refer to [tls.createSecureContext](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) for details on the certificate options.

### Tunnel

The `tunnel` instance returned to your callback emits the following events

| event   | args | description                                                                          |
| ------- | ---- | ------------------------------------------------------------------------------------ |
| request | info | fires when a request is processed by the tunnel, contains _method_ and _path_ fields |
| error   | err  | fires when an error happens on the tunnel                                            |
| close   |      | fires when the tunnel has closed                                                     |

The `tunnel` instance has the following methods

| method | args | description      |
| ------ | ---- | ---------------- |
| close  |      | close the tunnel |

## Server

This package includes both the client and server components. You can run your own pipenet server.

### Running the Server

```bash
# Using the CLI
npx pipenet server --port 3000

# With a custom domain
npx pipenet server --port 3000 --domain tunnel.example.com

# With multiple domains
npx pipenet server --port 3000 --domain tunnel.example.com --domain tunnel.example.org

# For cloud deployments (single tunnel port mode)
npx pipenet server --port 3000 --tunnel-port 3001 --domain tunnel.example.com

# Or programmatically
```

```js
import { createServer } from 'pipenet/server';

const server = createServer({
  domains: ['tunnel.example.com'],   // Optional: custom domain(s)
  secure: false,                     // Optional: require HTTPS
  landing: 'https://pipenet.dev',    // Optional: landing page URL
  maxTcpSockets: 10,                 // Optional: max sockets per client
  tunnelPort: 3001,                  // Optional: shared tunnel port for cloud deployments

  // Lifecycle hooks for tracking tunnels and requests
  onTunnelCreated: (tunnel) => {
    console.log(`Tunnel created: ${tunnel.id} at ${tunnel.url}`);
  },
  onTunnelClosed: (tunnel) => {
    console.log(`Tunnel closed: ${tunnel.id}`);
  },
  onRequest: (request) => {
    console.log(`Request: ${request.method} ${request.path} via ${request.tunnelId}`);
  },
});

// Start tunnel server if using shared tunnel port
if (server.tunnelServer) {
  await server.tunnelServer.listen(3001);
}

server.listen(3000, () => {
  console.log('pipenet server listening on port 3000');
});
```

### Server Options

- `domains` (string[]) Custom domain(s) for the tunnel server.
- `secure` (boolean) Require HTTPS connections
- `landing` (string) URL to redirect root requests to
- `maxTcpSockets` (number) Maximum number of TCP sockets per client (default: 10)
- `tunnelPort` (number) Shared tunnel port for cloud deployments (enables single-port mode)

### Server Hooks

The server supports lifecycle hooks for tracking tunnels and requests:

- `onTunnelCreated(tunnel)` - Called when a new tunnel is created. Receives `{ id, url, domain }`.
- `onTunnelClosed(tunnel)` - Called when a tunnel is closed. Receives `{ id, url, domain }`.
- `onRequest(request)` - Called when a request is proxied through a tunnel. Receives `{ method, path, tunnelId, headers, remoteAddress }`.

The `domain` field identifies which configured domain was used when the tunnel was created, which is useful when running a server with multiple domains.

The `onRequest` hook provides access to request headers and the client's remote IP address, useful for logging, rate limiting, or authentication.

### Server API Endpoints

- `GET /api/status` - Server status and tunnel count
- `GET /api/tunnels/:id/status` - Status of a specific tunnel
- `GET /:id` - Request a new tunnel with the specified ID

### Cloud Deployments

When deploying pipenet server to cloud platforms like fly.io, Docker, or Kubernetes, you typically can only expose a limited number of ports. By default, pipenet creates a random TCP port for each tunnel client, which doesn't work well in these environments.

Use the `--tunnel-port` option to enable single-port mode, where all tunnel clients connect to a single shared port:

```bash
# fly.io example
pipenet server --port 8080 --tunnel-port 8081 --domain tunnel.example.com --secure
```

Then expose both ports in your deployment configuration. For fly.io:

```toml
[[services]]
  internal_port = 8080
  protocol = "tcp"
  [[services.ports]]
    port = 80
    handlers = ["http"]
  [[services.ports]]
    port = 443
    handlers = ["http", "tls"]

[[services]]
  internal_port = 8081
  protocol = "tcp"
  [[services.ports]]
    port = 8081
```

## Why pipenet?

pipenet was developed by [glama.ai](https://glama.ai) to enable local [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers to connect with remote AI clients (e.g., to give AI assistants access to your local file system).

This capability is now integrated into [mcp-proxy](https://github.com/punkpeye/mcp-proxy).

## pipenet vs localtunnel

pipenet is a modernized fork of [localtunnel](https://github.com/localtunnel/localtunnel) with several improvements:

| Feature | pipenet | localtunnel |
| ------- | ------- | ----------- |
| Cloud deployment support | ✅ Single-port mode via `--tunnel-port` | ❌ Requires random ports |
| Multiple domains | ✅ `--domain` can be specified multiple times | ❌ Single domain only |
| TypeScript | ✅ Written in TypeScript with full type definitions | ❌ JavaScript only |
| ESM support | ✅ Native ES modules | ❌ CommonJS only |
| Active maintenance | ✅ Actively maintained | ⚠️ Limited maintenance |
| WebSocket support | ✅ Full WebSocket proxying | ✅ Full WebSocket proxying |

### Key Differences

**Cloud Deployment Support**: localtunnel creates a random TCP port for each tunnel client, which doesn't work in containerized environments like Docker, fly.io, or Kubernetes where only specific ports are exposed. pipenet solves this with the `--tunnel-port` option, enabling all clients to connect through a single shared port.

**Modern JavaScript**: pipenet uses ES modules and is written in TypeScript, providing better IDE support, type safety, and compatibility with modern JavaScript tooling.

## Acknowledgments

pipenet is based on [localtunnel](https://github.com/localtunnel/localtunnel).

Development of pipenet is sponsored by [glama.ai](https://glama.ai).
