← All articles · Hosts
SSH tunneling
How Blueprint reaches a remote host's svc over the existing SSH connection — no port forwarding, no firewall changes, no `ssh -L`.
3 min read
The trick
The blueprint-svc on a remote host binds to 127.0.0.1:17832
only. It never exposes itself on the public network — that's an
SSH operator's choice, not ours. So how does the desktop GUI reach
it?
The Go net/http Transport accepts a custom DialContext. Blueprint
swaps the default dialer (which would open a TCP socket on your
local machine) for one that dials through the existing SSH session
to the remote's localhost. From the HTTP client's perspective it's
calling http://127.0.0.1:17832/v1/snapshot; in reality the bytes
flow:
GUI → in-process HTTP client → SSH client → SSH server on remote
→ remote's loopback to localhost:17832 → blueprint-svc
Bytes never touch the public network past the SSH wrapper that secures them.
Why this matters
- Zero firewall configuration on the host — the svc port stays local. You don't open 17832 to the world; you don't even open it to your own LAN.
- No
ssh -Ldance — users don't have to rememberssh -L 17832:127.0.0.1:17832 user@hostbefore they can connect. Connect is one button. - All the security of SSH — the same key authentication you already trust for shell access secures the svc HTTP control plane. No second auth system to manage.
- The svc has zero exposed surface — if you've never started
Blueprint, the port is bound but unreachable from anywhere except
127.0.0.1. Failed bearer attempts (logged in
~/.blueprint/logs/blueprint.log) shouldn't ever appear from any IP other than 127.0.0.1.
What this means for the bearer token
The bearer token at ~/.blueprint/svc-token on the remote is the
ONLY auth between the SSH tunnel and the svc. SSH protects the
network path; the bearer token protects the svc from co-located
processes (anyone with a shell on the same host could otherwise hit
127.0.0.1:17832 unauthenticated).
If someone gains shell access to your host, they can read the token file. The token is 0600 — only the owning user can read it. Treat it like an ssh agent socket: same trust model.
What happens on connection loss
The SSH connection is the lifeline. If it drops (network blip, laptop sleep, manual disconnect), the cached HTTP client is dead. The next IPC call fails fast with an SSH error; the host selector shows the host as disconnected.
Reconnect is a single Connect click. The cached svc token in your OS keychain skips the SFTP refetch, so reconnect is sub-second.
Common pitfalls
- NAT / proxy collapsing idle SSH connections: corporate networks
often drop SSH after 5-10 min idle. Either enable SSH keepalives
(
ServerAliveInterval 60in~/.ssh/config) or accept the occasional disconnect. - HTTP timeouts mid-call: a long-running model pull initiated via the tunnel can exceed the default HTTP client timeout if you put it in front. svcclient sets a 30s timeout for routine calls and a 5-minute timeout for chat; long pulls run as background goroutines on the svc and report status via polling, so they survive HTTP timeouts.
- Trying to reach the remote svc directly over the LAN: even
if the remote IS on your LAN, the svc binds 127.0.0.1 only.
You'd have to change
BindHostin the svc config to expose it — which we don't recommend.
Need help with this in production?
Inspire AI Lab runs LLM optimization engagements end-to-end using Blueprint. If you'd rather hand the work to us instead of running it yourself, book a 30-minute review.