Back Original

Show HN: Pipenet – A Modern Alternative to Localtunnel

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'));

Client Options

portnumber Local port to expose
hoststring Tunnel server URL
subdomainstring Request specific subdomain
localHoststring Proxy to this hostname instead of localhost
localHttpsboolean Tunnel to local HTTPS server
allowInvalidCertboolean Skip cert validation

Events

requestFired on each proxied request with method and path
errorFired when an error occurs
closeFired 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);

Server Options

domainsstring[] Custom domain(s) for tunnel server
secureboolean Require HTTPS
landingstring Redirect URL for root requests
maxTcpSocketsnumber Max sockets per client (default: 10)
tunnelPortnumber Shared port for cloud deployments

Lifecycle Hooks

onTunnelCreatedCalled when a new tunnel is created
onTunnelClosedCalled when a tunnel is closed
onRequestCalled on each proxied request

Endpoints

GET /api/statusServer status and tunnel count
GET /api/tunnels/:id/statusStatus of specific tunnel
GET /:idRequest new tunnel with ID