Release · v2607.5 · Firepuncher

Firepuncher: reach any TCP port
through an agent

Tunnels give you a public HTTPS URL. That's the right answer for a dashboard you want to open in a browser. It's the wrong answer for SSH, Postgres, or anything else that isn't a website. Firepuncher is the other half.

🔐

Private by construction

No public port is ever opened. Both ends dial out and authenticate.

📡

Any TCP service

SSH, Postgres, Redis, a debugger — if it speaks TCP, it forwards.

Runs as a service

Install it once and the local port is always there.

A local port that comes out somewhere else

You run one command on the machine you're sitting at:

hle fp --agent rpi --to 22 --port 9922

Now 127.0.0.1:9922 on your laptop is port 22 on the Pi. Point SSH at it and you're in.

ssh -p 9922 pi@127.0.0.1

Nothing about the Pi changed. No port opened on its router, no public hostname, no certificate. The agent already holds an outbound connection to the relay; firepuncher borrows it.

The direction is inverted

Your laptop
hle fp
HLE Relay
pairs the two
The agent
dials the target

Everywhere else in HLE, the relay is the server side of a connection: someone visits your public URL and traffic is proxied out to your agent. Firepuncher turns that around. An authenticated client dials in, and the relay's only job is to pair it with one of your own agents.

That's why there's no public URL for a firepuncher forward, and never will be. There is nothing to visit. Both ends are yours, both ends authenticate, and the relay passes bytes between them without looking at what they are.

The agent decides what it will dial

Authorization happens twice, on purpose.

The relay checks that you own the agent you're asking for. Then the agent independently checks the target against its own allowlist — and refuses anything that isn't on it.

The second check is the one that matters. Owning an agent shouldn't imply the right to reach every machine on the network behind it. Without that check, a leaked API key would turn a Raspberry Pi in your living room into a doorway into your LAN.

Out of the box, an agent will only dial itself — loopback and nothing else. That covers "SSH into the box the agent runs on" with no setup at all. Reaching a NAS or a database elsewhere on the network is deliberately a thing you turn on:

POST /api/agents/{id}/forward-rules
{ "host": "192.168.1.50", "port": 5432 }

Rules reach a running agent within seconds, so revoking access doesn't mean restarting anything.

Install it once, forget it

Running hle fp in a spare terminal before every connection gets tedious fast. Install the forward as a service instead:

hle service install --fp --agent-name rpi --to 22 --port 9922

systemd on Linux, launchd on macOS. It starts at boot and restarts itself if the relay blips. From there, ordinary SSH config finishes the job:

Host rpi
    HostName 127.0.0.1
    Port 9922
    User pi
    HostKeyAlias rpi.hle

Then it's just ssh rpi — and scp, rsync, and VS Code Remote come along for free, because they all read the same config.

Full walkthrough in SSH into your home lab from anywhere.

It's just TCP

# Postgres, as if it were local
hle fp --agent nas --to 5432 --port 15432

# Redis
hle fp --agent devbox --to 6379 --port 16379

# An internal admin panel, no public URL
hle fp --agent rpi --to 8080 --port 18080

Each forward multiplexes many connections, so scp and rsync and a dozen parallel psql sessions all share one session.

Service discovery

An agent running on a Kubernetes cluster or a Docker host now reports what's running there, so you can expose something without hunting for its address first:

$ hle agent services

  Name       Where    Address                                        Suggested label
  jellyfin   media    http://jellyfin.media.svc.cluster.local:8096   jellyfin
  gitea      git      http://gitea.git.svc.cluster.local:3000        gitea

Read-only, and it detects its own environment — an agent on a plain VM reports nothing and stays quiet.

Where this goes

Dashboard controls. Allowlist rules and discovered services are API-only today. Both are getting proper UI.

Traffic visibility. Firepuncher traffic doesn't currently show up in analytics — it rides a different path than tunnel traffic, so the existing counters don't see it. Splitting analytics by transport and protocol is in progress.

Agent-to-agent forwards. Nothing about the protocol requires the near end to be a laptop. An agent can hold the local port too, so a service on one machine can reach a database on another without either being public.

TCP only, for now. UDP isn't supported. The honest reason is that a reliable, ordered transport is the wrong shape for most UDP traffic — a dropped packet becomes a stall. If you have a use case, tell us what it is.

Upgrade and try it

Firepuncher needs hle-client 2607.5 or newer on both machines. Run hle update, then pick a port.