Local Development
Share your local server with teammates, test webhooks, or demo work without deploying.
SDK Integration
Embed pipenet in your own tools to provide tunneling capabilities. mcp-proxy uses pipenet to connect local MCP servers with remote AI clients.
Self-Hosted Infrastructure
Run your own tunnel server for full control over security, domains, and availability.
Client
# Expose local port npx pipenet client --port 3000 # Custom subdomain npx pipenet client --port 3000 \ --subdomain myapp # Your own server npx pipenet client --port 3000 \ --host https://tunnel.example.com
Server
# Start server npx pipenet server --port 3000 # Custom domain npx pipenet server --port 3000 \ --domain tunnel.example.com # Cloud-ready npx pipenet server --port 3000 \ --tunnel-port 3001
| Feature | pipenet | localtunnel |
|---|---|---|
| Cloud deployment | single-port | random ports |
| Multiple domains | ✓ | — |
| TypeScript | ✓ | — |
| ES Modules | ✓ | — |
| Maintenance | Active | Limited |
| WebSocket | ✓ | ✓ |
| Protocol | Notes |
|---|---|
| HTTP / HTTPS | Standard request/response |
| WebSocket | Full duplex via HTTP upgrade |
| SSE | Long-lived HTTP connections |
| HTTP Streaming | Chunked transfer encoding |
import { pipenet } from 'pipenet'; const tunnel = await pipenet({ port: 3000 }); console.log(tunnel.url); // https://abc123.pipenet.dev tunnel.on('request', (info) => console.log(info.method, info.path)); tunnel.on('close', () => console.log('closed'));
| port | number Local port to expose |
| host | string Tunnel server URL |
| subdomain | string Request specific subdomain |
| localHost | string Proxy to this hostname instead of localhost |
| localHttps | boolean Tunnel to local HTTPS server |
| allowInvalidCert | boolean Skip cert validation |
| request | Fired on each proxied request with method and path |
| error | Fired when an error occurs |
| close | Fired when tunnel closes |
import { createServer } from 'pipenet/server'; const server = createServer({ domains: ['tunnel.example.com'], secure: true, tunnelPort: 3001, // Lifecycle hooks onTunnelCreated: (tunnel) => { console.log(`Tunnel created: ${tunnel.id} at ${tunnel.url}`); }, onTunnelClosed: (tunnel) => { console.log(`Tunnel closed: ${tunnel.id}`); }, onRequest: (req) => { console.log(`${req.method} ${req.path} via ${req.tunnelId}`); }, }); await server.tunnelServer.listen(3001); server.listen(3000);
| domains | string[] Custom domain(s) for tunnel server |
| secure | boolean Require HTTPS |
| landing | string Redirect URL for root requests |
| maxTcpSockets | number Max sockets per client (default: 10) |
| tunnelPort | number Shared port for cloud deployments |
| onTunnelCreated | Called when a new tunnel is created |
| onTunnelClosed | Called when a tunnel is closed |
| onRequest | Called on each proxied request |
| GET /api/status | Server status and tunnel count |
| GET /api/tunnels/:id/status | Status of specific tunnel |
| GET /:id | Request new tunnel with ID |