Skip to content

Jcambass/tailhopper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tailhopper

Tailhopper

A SOCKS5 proxy manager for personal Tailscale users.
Connect to multiple Tailnets simultaneously and route traffic automatically via a PAC file.


Why Tailhopper?

Tailscale only lets you be connected to one Tailnet at a time. If you need to access resources on two different Tailnets simultaneously — for example, reaching web services on your personal Tailnet while your work machine is already connected to a work Tailnet — you'd normally have to disconnect from one and reconnect to the other every time.

Tailhopper lets you stay connected to multiple Tailnets at once, without touching your primary Tailscale connection.

What it does

  • Single web dashboard to manage your additional Tailnets
  • Works alongside your existing Tailscale connection — your primary Tailnet is unaffected
  • Automatic proxy routing via PAC file — no per-request configuration needed
  • Manual per Tailnet SOCKS5 proxy option for apps that don't support PAC or for non-HTTP traffic
  • Runs as a background service on macOS (auto-starts at login, auto-restarts on crash)

How it works

Tailhopper connects to each Tailnet using tsnet and starts a dedicated local SOCKS5 proxy for each one. Traffic can reach those proxies in two ways:

PAC file (recommended for browsers) — Tailhopper serves a PAC file that maps each Tailnet's MagicDNS suffix to the corresponding SOCKS5 proxy. Configure your browser or OS to use the PAC file once, and every request is automatically sent to the right proxy based on the destination hostname — with no manual switching needed, even when accessing multiple Tailnets in the same browser session.

Direct SOCKS5 (per Tailnet) — Each Tailnet's proxy can also be configured directly in any app that supports SOCKS5. This is useful for non-HTTP traffic (SSH, database clients, etc.) or apps that don't respect the systems PAC setting. Because each proxy represents exactly one Tailnet, an app configured to use a specific proxy can only reach that one Tailnet at a time — unlike the PAC approach, there is no automatic multi-Tailnet routing.

Limitations

  • Requires proxy-aware apps. Only applications that respect PAC or SOCKS5 proxy settings will route traffic through Tailhopper. Apps that bypass system proxy settings or manage their own networking will not.
  • PAC routing only works with MagicDNS names. The PAC file routes based on MagicDNS hostnames only. Accessing Tailnet machines by IP address is not supported via PAC — use the manual SOCKS5 proxy for that Tailnet directly instead.
  • Tailnets without MagicDNS enabled are not supported.

Usage

Once running, open the dashboard at http://localhost:8888.

From there you can:

  • Add Tailnets and authenticate them via the Tailscale
  • Temporarily enable/disable individual Tailnets
  • Configure your browser or OS to use the PAC file for automatic proxy routing — see the in-app How to configure PAC section for OS- and browser-specific instructions
  • Alternatively, configure a SOCKS5 proxy manually per-app using the host and port shown on each connected Tailnet's card (useful for non-HTTP traffic or apps that don't respect system wide PAC settings)

Installation

macOS

Using Homebrew (recommended)

Install with Homebrew (recommended):

brew install jcambass/homebrew-tap/tailhopper
brew services start tailhopper

To view stop and uninstall instructions after the initial install:

brew info tailhopper

Manual installation

Download the binary from Releases and run it directly. To run as a background service, create your own launchd user agent.

Linux

Using the install script (recommended)

curl -fsSL https://raw.githubusercontent.com/jcambass/tailhopper/main/linux/install.sh | bash

Install a specific version:

VERSION=v0.1.0 curl -fsSL https://raw.githubusercontent.com/jcambass/tailhopper/main/linux/install.sh | bash

Use a custom dashboard port:

HTTP_PORT=9999 curl -fsSL https://raw.githubusercontent.com/jcambass/tailhopper/main/linux/install.sh | bash

View logs:

journalctl --user -fu tailhopper

Stop:

systemctl --user disable --now tailhopper

Uninstall:

systemctl --user disable --now tailhopper
rm ~/.local/bin/tailhopper ~/.config/systemd/user/tailhopper.service
systemctl --user daemon-reload

To also remove state files:

rm -rf ~/.local/share/tailhopper

Manual installation

Download the binary from Releases and run it directly. To run as a background service, create your own systemd user service.

Windows

Download the binary from Releases and run it. To run as a background service, use your preferred Go service wrapper or task scheduler.

Build from source

Requires Go 1.22+.

git clone https://github.com/jcambass/tailhopper.git
cd tailhopper
go build -o tailhopper ./cmd/tailhopper
./tailhopper

Configuration & State

Tailhopper is configured via environment variables:

Variable Default Description
HTTP_PORT 8888 Dashboard and PAC file port
LOG_LEVEL INFO Log verbosity (DEBUG, INFO, WARN, ERROR)

Tailhopper stores state in its working directory:

  • tailhopper.json
  • tailnets/ (per-tailnet runtime/state data)

Common working directories:

  • Homebrew service (macOS): $(brew --prefix)/var/tailhopper
  • Linux installer (systemd user service): ~/.local/share/tailhopper
  • Manual runs: current working directory

Logs

Tailhopper outputs structured logs in logfmt format to stderr. For colored output, pipe through hl:

./tailhopper 2>&1 | hl

Release process

This section is for maintainers.

Releases are automated via GoReleaser. When you push a v* tag, GitHub Actions automatically:

  1. Builds cross-platform binaries (macOS, Linux, Windows)
  2. Creates a GitHub Release with artifacts
  3. Updates Formula/tailhopper.rb in jcambass/homebrew-tap (url and sha256) via mislav/bump-homebrew-formula-action

Publish a release

git tag v0.1.0
git push origin v0.1.0

The workflow requires a TAP_PR_WRITER secret with the following permissions on jcambass/homebrew-tap:

  • Contents: Read and write (needed to create branches / push commits).
  • Pull requests: Read and write (needed to open/manage PRs)

Run the Homebrew update manually

You can also run just the Homebrew update from the GitHub Actions UI using:

  • mode = homebrew
  • tag-name = v0.1.0

This is useful if the release already exists and you need to re-run the tap PR creation.

Validate release pipeline locally

Run a full local snapshot release (no publish):

go run github.com/goreleaser/goreleaser/v2@latest release --clean --snapshot --skip=publish

Validate config only:

go run github.com/goreleaser/goreleaser/v2@latest check

Test Homebrew formula locally

Homebrew formula changes live in jcambass/homebrew-tap. Make sure to update the url and sha256 in Formula/tailhopper.rb to point to the new release artifact before testing.

git clone https://github.com/jcambass/homebrew-tap.git
cd homebrew-tap
brew install --build-from-source --verbose --debug ./Formula/tailhopper.rb

About

Use multiple Tailscale networks at the same time, without constantly switching.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors