Zero port-forwarding. Zero VPN. Zero accounts.
Hole connects any two machines over the Holepunch / HyperDHT stack. Each agent announces itself on a distributed hash table; clients punch through NAT and connect directly — or via a relay when both sides are behind strict CGNAT.
client ──[HyperDHT]── agent
(or)
client ──[relay]──── agent
| Feature | Description |
|---|---|
hole ssh |
One-command interactive SSH through the tunnel |
hole exec |
Run a command remotely, get output, exit |
hole copy |
scp-style file transfer over P2P |
hole client |
Expose any TCP service (RDP, HTTP, DB…) locally |
hole dashboard |
Browser fleet UI — terminals, tunnels, exec, files, ACLs |
hole relay |
Self-hosted relay for CGNAT / mobile networks |
hole agent |
Daemon that announces the machine on the DHT |
State lives in ~/.hole/ — keypair, device registry, ACL, audit log.
git clone https://github.com/Annatar3/Hole && cd Hole
npm install
npm link # puts `hole` on your PATH
hole helpGrab the binary for your OS from the Releases page, then:
chmod +x hole
./hole helpTo build your own binaries:
npm run build # outputs to dist/./hole agent --name my-server
# Key : 9320641058af2f76abd1... ← copy thisLeave it running. Optionally install it as a service.
hole add my-server 9320641058af2f76abd1... \
--user alice \
--identity ~/.ssh/id_ed25519 # optionalhole ssh my-serverThat's it. Hole opens the P2P tunnel and drops you into an SSH session. The tunnel closes when you exit.
When direct hole-punching fails, run a relay on any VPS with a public IP:
# On the VPS — open UDP 49737 inbound in your firewall
./hole relay
# Agent side
./hole agent --name my-server --relay <vps-ip>:49737
# Client side
hole add my-server <key> --relay <vps-ip>:49737
hole ssh my-serverTraffic is still end-to-end encrypted; the relay only shuffles UDP packets.
Each forwarded service gets its own key derived from the agent's master key:
# Agent
./hole agent --name my-pc \
--forward rdp:3389 \
--forward web:127.0.0.1:3000
# Client — SSH works as before
hole ssh my-pc
# Open a local port for RDP
hole client my-pc rdp
# → connect your RDP client to localhost:<printed-port>
# Open a local port for the web service
hole client my-pc web --port 8080hole dashboard
# → http://localhost:4321/?token=<auto-generated-token>The token is generated once and stored in ~/.hole/dashboard-token. It's embedded in the URL printed on start, so just open that link.
What the dashboard gives you:
- Fleet sidebar — all registered devices, online/offline status, latency, search and tag filter
- + Add device — register a new device from the browser, no CLI needed
- Remove button in the Details tab to delete a device
- Details tab — edit per-device
user,relay,identity, tags, and service-key mappings - Terminal tab — full interactive SSH session in the browser (xterm.js + node-pty)
- Tunnels tab — open / close local tunnel ports from the UI; tunnels survive dashboard restarts
- Exec tab — run a shell command on the current device, all devices, or a tag-filtered subset; results shown per-device with timing
- Files tab — browse and transfer files (15 s timeout per operation)
- ACL tab — manage
~/.hole/acl.jsonon the remote host directly from the browser - Audit tab — recent connection events, exportable to CSV
By default any client that knows a service key can connect. To restrict access:
# On the agent host
hole acl add laptop <64-char-client-public-key>
hole acl list
hole acl remove laptopAn empty ACL means open mode — any key is accepted.
# On the agent host
hole install-service --name my-server
systemctl --user status hole-agent
# Remove
hole uninstall-serviceCreates ~/.config/systemd/user/hole-agent.service. Starts on login, restarts on failure.
hole.exe install-service --name my-server
# Remove
hole.exe uninstall-servicehole doctor # checks outbound TCP 443, UDP bind, DHT bootstrap
hole ping my-server # latency + reachability
hole audit --tail 20 # recent connection eventsIf doctor passes and ping returns a latency, everything is working. If ping shows the device as offline, try adding --relay <host>:<port> to both sides.
| Command | What it does |
|---|---|
hole agent [--name N] [--relay host:port] [--forward svc:port] |
Start the agent daemon |
hole ssh <device> [user] [-- extra-ssh-args] |
Open an interactive SSH session |
hole exec <device> <user> -- <cmd> |
Run a command, capture output, exit |
hole copy <src> <dest> [user] |
Copy files (device:/path for remote side) |
hole client <device> [service] [--port N] |
Expose a TCP service locally |
hole relay [--port N] |
Run a relay server |
hole dashboard |
Start the web dashboard |
hole add <name> <key> [--user U] [--relay R] [--identity I] |
Register a device |
hole remove <name> |
Remove a device |
hole list |
List registered devices |
hole ping <device> |
Check reachability |
hole status <device> |
Detailed device status |
hole install-service [--name N] |
Install agent as a system service |
hole uninstall-service |
Remove the system service |
hole acl list | add | remove |
Manage connection ACL on the agent |
hole audit [--tail N] |
View audit log |
hole doctor |
Environment health check |