Skip to content

himanshkukreja/waygate

Repository files navigation

Waygate

Waygate is an open-source, self-hosted tunnel for securely exposing local services through infrastructure you control.

It sits in the same problem space as ngrok and Cloudflare Tunnel, but is designed for teams that want to run the relay and control plane themselves, issue their own credentials, and control how sessions, URLs, and access policies work.

What It Does

  • Shares localhost services from a laptop or workstation through an outbound tunnel
  • Issues short-lived share sessions with configurable expiry
  • Generates unique browser URLs such as https://calm-harbor-x8k1.tunnel.example.com
  • Supports browser-friendly HTTP access and SOCKS5 proxy access
  • Uses control-plane-issued JWTs and short-lived certificates for session auth
  • Lets operators validate real user API keys before a session is created
  • Works as a starting point for more advanced policies like device binding, private ingress, or internal-only networking

Components

Component Binary Purpose
Agent waygate-agent Runs on the developer machine and connects local services to the relay
Relay waygate-relay Accepts agent tunnels and serves browser/proxy traffic
Control plane waygate-cp Creates sessions, validates API keys, and issues session credentials

How It Works

  1. A developer runs waygate-agent share --port 3000.
  2. The agent authenticates to the control plane with an API key.
  3. The control plane creates a session, mints a session JWT, generates a unique share hostname/token, and returns the share URL.
  4. The agent obtains a short-lived certificate and connects outbound to the relay over WebSocket.
  5. Browser traffic to the relay is forwarded over the tunnel to the developer's local service.

Why Self-Host This

  • You want full control over where traffic flows and how logs are stored.
  • You want your own domain, your own Nginx, and your own certificates.
  • You need session expiry, custom access logic, or internal/private networking that public tunnel vendors do not match.
  • You want a hackable codebase you can extend for your own platform.

Advantages Over Hosted Tunnel Services

Self-hosting Waygate gives you flexibility that is hard to get from managed tunnel products.

  • You can plug in your own authentication and authorization logic instead of being limited to a vendor's account model.
  • You can issue your own API keys, session tokens, and certificates with policies that match your product or organization.
  • You control the relay, control plane, logs, domains, and TLS termination points.
  • You can keep traffic inside your own cloud, VPC, VPN, or private network boundary.
  • You can expose services on your own wildcard domains and integrate them with your existing DNS and certificate setup.
  • You can enforce custom rules such as session expiry, per-user limits, per-tenant routing, or device-bound access.
  • You are free to add platform-specific behavior such as internal headers, audit hooks, custom session stores, or approval workflows.
  • You avoid vendor lock-in and can evolve the tunnel behavior as your product grows.

Current Capabilities

  • Share flow with login, share, connect, and logout
  • Real API key validation via a control-plane key file
  • Wildcard browser URLs through --browser-base-host
  • Short-lived URLs with user-configurable expiry from the agent
  • Direct relay mode for local development
  • Cross-platform builds for macOS, Linux, and Windows

Prerequisites

  • To build Waygate from source, install Go 1.25 or newer.
  • To run prebuilt Waygate binaries, Go is not required.
  • To terminate TLS with Nginx and Certbot, install those separately on your server.

Quick Start

Choose one of these two paths first:

Option A: Build from source

make all

Or build every supported target:

make dist-all

That generates binaries like:

  • bin/waygate-agent
  • bin/waygate-relay
  • bin/waygate-cp
  • bin/waygate-agent-linux-amd64
  • bin/waygate-relay-linux-amd64

Option B: Use prebuilt binaries

If you already have the binaries, place them somewhere convenient, for example:

bin/waygate-agent
bin/waygate-relay
bin/waygate-cp

In this mode, Go is not required. You only need the correct binaries for your operating system and architecture.

Optional: Install waygate-agent on your PATH

If you want to run:

waygate-agent share --port 3000

instead of:

./bin/waygate-agent share --port 3000

install the binary into a directory that is already on your shell PATH, or use the helper script:

bash ./scripts/install-waygate-agent.sh ./bin/waygate-agent

By default this installs to ~/.local/bin/waygate-agent.

If ~/.local/bin is not already on your PATH, add this to your shell profile such as ~/.zshrc or ~/.bashrc:

export PATH="$HOME/.local/bin:$PATH"

Then reload your shell:

source ~/.zshrc

After that, you can run:

waygate-agent share --port 3000

To install system-wide instead, copy the binary to /usr/local/bin with the appropriate privileges.

1. Create an API key file for the control plane

Use the example in examples/api-keys.json.example.

Minimal example:

{
  "keys": [
    {
      "name": "Example User",
      "api_key": "replace-this-with-a-real-secret",
      "user_id": "example-user",
      "tenant_id": "example-tenant",
      "can_create": true
    }
  ]
}

2. Run the relay

For production-style browser URLs, choose a public hostname for your relay, such as relay.example.com, and a wildcard under it such as *.relay.example.com.

Before starting the relay or control plane, generate the JWT signing keypair and session CA:

bash ./scripts/generate-waygate-keys.sh ./certs

On a Linux host you can write them directly to /etc/waygate:

sudo mkdir -p /etc/waygate
sudo bash ./scripts/generate-waygate-keys.sh /etc/waygate
./bin/waygate-relay serve \
  --addr 127.0.0.1:8080 \
  --jwt-key /etc/waygate/jwt-public.pem \
  --browser-base-host relay.example.com \
  --dev=false \
  --verbose

3. Run the control plane

./bin/waygate-cp serve \
  --addr 127.0.0.1:9090 \
  --api-base-url https://api.example.com \
  --relay wss://relay.example.com/tunnel/connect \
  --api-key-file /etc/waygate/api-keys.json \
  --jwt-key /etc/waygate/jwt-private.pem \
  --ca-cert /etc/waygate/ca-cert.pem \
  --ca-key /etc/waygate/ca-key.pem \
  --verbose

4. Save login once on the developer machine

./bin/waygate-agent login \
  --api-key replace-this-with-a-real-secret \
  --api-endpoint https://api.example.com

5. Share a local service

./bin/waygate-agent share --port 3000 --expiry 2h

Expected output:

Share URL: https://calm-harbor-x8k1.relay.example.com/
Expires At: 2026-04-12T18:30:00Z

If your local app lives at http://localhost:3000/docs/, open:

https://calm-harbor-x8k1.relay.example.com/docs/

Wildcard DNS And Nginx

To use meaningful per-session subdomains, you need:

  • one public hostname for the relay, for example relay.example.com
  • a wildcard DNS record for the relay, for example *.relay.example.com
  • one public hostname for the control plane, for example api.example.com
  • Nginx terminating TLS and proxying both normal HTTP and WebSocket upgrade traffic

Example Nginx layout:

  • api.example.com -> 127.0.0.1:9090
  • relay.example.com and *.relay.example.com -> 127.0.0.1:8080

Minimal relay server block:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name relay.example.com *.relay.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header Authorization $http_authorization;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Then issue certs with Certbot using a DNS challenge for the wildcard name:

sudo certbot certonly \
  --manual \
  --preferred-challenges dns \
  -d relay.example.com \
  -d '*.relay.example.com' \
  -d api.example.com

You can also use a provider-specific Certbot DNS plugin instead of --manual.

Self-Hosted Deployment Flow

The recommended self-hosted deployment pattern is:

  1. Provision one cloud VM or container host for the relay and control plane.
  2. Point your public DNS names at that host.
  3. Run Nginx in front of both services.
  4. Issue a TLS certificate for the relay hostname, its wildcard subdomain, and the control-plane hostname.
  5. Run waygate-relay on 127.0.0.1:8080.
  6. Run waygate-cp on 127.0.0.1:9090.
  7. Give users an API key from your control-plane key file.
  8. Ask users to run waygate-agent login once, then waygate-agent share --port <port>.

Example hostname layout:

  • Relay API and browser ingress: relay.example.com
  • Wildcard share URLs: *.relay.example.com
  • Control plane API: api.example.com

Example DNS records:

  • A relay.example.com -> <your-server-ip>
  • A *.relay.example.com -> <your-server-ip>
  • A api.example.com -> <your-server-ip>

If your DNS provider does not support wildcard A records directly, use the equivalent wildcard record type it supports.

Authentication Model

Waygate currently uses two layers:

  • User API keys for authenticating the agent to the control plane
  • Per-session JWTs and short-lived certs for authenticating the agent to the relay

The control plane will reject login and session creation if the API key is not present in the configured key file.

Session Expiry

Users can choose expiry directly from the agent:

./bin/waygate-agent share --port 3000 --expiry 30m
./bin/waygate-agent share --port 3000 --expiry 24h

Current limits:

  • Minimum: 5m
  • Maximum: 168h
  • Default: 4h

CLI

waygate-agent login

Stores the control-plane endpoint and API key in the local user config directory.

./bin/waygate-agent login \
  --api-key replace-this-with-a-real-secret \
  --api-endpoint https://api.example.com

Environment variables are also supported:

  • WAYGATE_API_KEY
  • WAYGATE_API_ENDPOINT

Legacy TUNNEL_API_KEY and TUNNEL_API_ENDPOINT are still accepted for compatibility.

waygate-agent share

Creates a new share session and keeps the tunnel alive.

./bin/waygate-agent share --port 3000
./bin/waygate-agent share --port 3000 --local-host localhost --expiry 2h

waygate-agent connect

Lower-level command for direct relay or explicit control-plane wiring.

./bin/waygate-agent connect \
  --relay ws://localhost:8080/tunnel/connect \
  --local-ports 3000 \
  --insecure

waygate-relay serve

Runs the relay and browser/proxy entrypoint.

Important flags:

  • --browser-base-host
  • --jwt-key
  • --socks5-user
  • --socks5-pass
  • --dev

waygate-cp serve

Runs the control plane.

Important flags:

  • --api-key-file
  • --api-base-url
  • --relay
  • --jwt-key
  • --ca-cert
  • --ca-key

Dockerfiles

The repository includes:

Build Script

The cross-platform build script is:

It removes older generated tunnel binaries and rebuilds fresh binaries for all supported targets.

Project Layout

cmd/
  agent/
  relay/
  controlplane/
internal/
  agent/
  relay/
  controlplane/
  shared/
scripts/
examples/

Roadmap

  • Stronger multi-tenant policy controls
  • Device-bound access enforcement beyond share URL secrecy
  • Better response/header rewriting for complex browser apps
  • Better operator UX around API key provisioning and revocation
  • Production deployment manifests and systemd examples

About

Waygate is an open-source, self-hosted tunnel for securely exposing local services through infrastructure you control

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors