Dial-Out WebSockets Explained: How Bernato Reaches Your Agents Without Opening a Port
Bernato's daemon, bernatod, never accepts inbound connections. It dials out to a broker over an ordinary outbound HTTPS request, upgrades that request to a WebSocket, and holds the socket open. Your dashboard talks to the same broker, which relays messages between the two ends. To your router, it just looks like your machine loaded a web page, so no port forward, no UPnP, and no fight with CGNAT. When the dashboard is on the same Wi-Fi as the daemon, it skips the broker entirely and talks straight to 127.0.0.1.
Say you kick off a coding agent on your desktop before leaving for the day, and you want to check on it from your phone on the train. The obvious-sounding fix is "open a port so the outside world can reach your machine." I want to walk through why that's a worse answer than it sounds, and how a dial-out WebSocket avoids the problem by changing who has to be reachable in the first place.
The traditional way to reach a home machine is fragile by design
Port forwarding is the classic answer to "how do I reach my home server from outside my house." You log into your router's admin panel, find the port forwarding page (every vendor names it something slightly different), and tell it that traffic arriving on some external port should get handed to your machine's internal IP. It works, but every step of it is a place to get stuck: you need admin access to the router, you need to find the right menu, and you need to remember to update it if your machine's internal IP changes, which it often does unless you've also set up a DHCP reservation.
UPnP was supposed to make this automatic: an app on your machine asks the router to open a port on its behalf, no human required. In practice it's inconsistent. Some routers ship with UPnP off by default for good reason: any process on your network can ask for a port to be opened, which is a small but real attack surface. Some ISP-provided routers implement it partially or drop the mapping on reboot. I have seen UPnP requests silently ignored on hardware that claims to support it. Depending on it to work is a bet, not a plan.
Then there's the problem that kills port forwarding outright for a lot of people: CGNAT, carrier-grade NAT. Plenty of home ISPs don't give every customer a real public IPv4 address anymore. They give you a private address and share one public IP across many customers. If that's your situation, there is no public IP on your side of the connection to forward a port to. You can configure port forwarding perfectly on your own router and it still won't work, because your router isn't the thing facing the internet. Your ISP's shared gateway is, and you don't control it.
Dial-out flips who has to be reachable
The alternative is to stop trying to make the home machine reachable at all. Instead, bernatod, the daemon that runs on the machine you own, makes an outbound connection to a broker service and keeps it open. The broker is the one thing that has to sit at a stable, public address. Your dashboard, wherever you're using it from, also connects outbound to that same broker, and the broker relays messages between the two live connections.
Nothing about this requires your router to accept anything unsolicited from the internet. The daemon initiates the connection, exactly the way a browser tab initiates a connection when you load a page. From the router's point of view, that's all it ever sees: your machine, on the inside, making a request to something on the outside. There is no listening socket facing your ISP, so there is nothing to scan, nothing to forward, and nothing that breaks the day your ISP hands you a new IP.
What dial-out actually looks like on the wire
The mechanism underneath this is not exotic. It's a WebSocket, and a WebSocket connection always starts life as a normal outbound HTTP request. The daemon sends a GET request to the broker's address with a few special headers asking to switch protocols, the broker replies agreeing to the switch, and from that point on the same TCP connection carries WebSocket frames in both directions instead of more HTTP. The whole exchange rides over HTTPS on port 443, the same port and the same TLS handshake your browser uses for every site you visit.
That last detail is what makes it work almost everywhere. Firewalls, corporate proxies, and home routers are built around the assumption that outbound requests to port 443 are just people browsing the web, and they let that traffic through by default because blocking it would break the internet for everyone on the network. A dial-out WebSocket rides on exactly that assumption. It doesn't need a special port opened, doesn't need a special protocol allowed, and doesn't look any different to the router than you opening a new tab.
GET /tunnel HTTP/1.1
Host: broker.example
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
That's illustrative, a stripped-down version of the standard WebSocket upgrade handshake defined in RFC 6455, not a dump of Bernato's actual traffic. But it's the same shape the daemon uses: one outbound HTTPS request, one upgrade, one long-lived socket that both sides then keep alive and reuse for everything that follows. The daemon holds its end open; if it drops (network blip, laptop goes to sleep, whatever), it reconnects the same way it connected the first time.
LAN-direct: skip the broker when you don't need it
The broker exists for the case where your dashboard and your daemon aren't on the same network, which is most of what makes remote access useful in the first place. But if you're sitting on your couch on the same Wi-Fi as the machine running bernatod, routing every request through a broker somewhere on the internet is unnecessary. So the dashboard doesn't always use the tunnel: it checks whether it can reach the daemon directly at 127.0.0.1, and if it can, it talks to the daemon straight, no broker involved at all. It only falls back to the broker-relayed tunnel when that direct check fails, meaning you're actually off the local network.
This is a genuinely separate code path, not a config flag you have to flip. You don't choose LAN-direct versus tunnel mode; the dashboard figures out which one applies and uses it, and switches back the moment your network situation changes. I've written more about how that detection works in the LAN-direct post linked below, including why loopback is a safe thing for a browser tab to reach.
What you're trading for the simplicity
Dial-out solves the reachability problem, but it's worth being honest about what it introduces: a broker in the middle of every remote connection. That's a real dependency, and the FAQ below doesn't dodge it. Here's how the three approaches stack up on the practical questions people actually ask before picking one:
| Manual port forward | UPnP | Dial-out broker | |
|---|---|---|---|
| Router configuration | Log into the router, find the right menu, forward a port | None, if UPnP is enabled and works | None |
| Works behind CGNAT | No, there's no public IP on your side to forward | No, same problem | Yes, outbound traffic doesn't care what kind of NAT you're behind |
| What's reachable from the internet | Your home machine, on that port | Your home machine, on whatever port UPnP opened | Only the broker; your machine accepts nothing inbound |
| Typical failure mode | ISP rotates your IP, the forward goes stale | Router firmware disables UPnP or mishandles the request | Broker connection drops, daemon reconnects on its own |
The last row is the honest part. A port forward fails quietly and stays broken until you notice. A dial-out connection fails loudly (the socket drops) and the daemon's whole job is to notice and reconnect, so recovery is automatic in a way manual configuration never is. But it does mean the broker is now a dependency you didn't have before, which is exactly what the FAQ below is about.
FAQ
Is the broker a single point of failure?
Yes, for the tunnel path specifically, and it's worth saying plainly instead of hand-waving around it. If the broker is down, the broker-relayed tunnel stops working, which means dashboard connections that rely on it (anywhere off your local network) are affected. What doesn't break is LAN-direct: when your dashboard and daemon are on the same network, the dashboard talks straight to 127.0.0.1 and never touches the broker at all, so that path keeps working regardless of broker status.
What's the latency cost of the extra broker hop versus LAN-direct?
LAN-direct is about as fast as local networking gets, since the dashboard is talking to loopback on the same machine or hopping across your own Wi-Fi with no relay in between. The broker-relayed tunnel adds a real hop: your packets go out to wherever the broker is hosted and back, on top of whatever the broker itself adds to shuttle a message between two open sockets. It's not going to be as fast as talking to 127.0.0.1, which is the whole reason LAN-direct exists as a separate path rather than routing everything through the broker unconditionally. If you want the actual numbers, the LAN-direct post linked below goes into how that path is measured.
Does this mean the broker operator can read your traffic?
That's the right question to ask about any relay-based approach, not just this one, and I'd rather flag it than dodge it. A broker that relays messages between two endpoints sits in a position where, depending on how the connection is encrypted, it could see plaintext. I don't have specifics here about the encryption model between the daemon, the broker, and the dashboard beyond the outer TLS on the WebSocket connection itself, so I'm not going to claim more than that. If that matters for what you're running, ask directly whether the relay ever sees decrypted payloads or whether there's encryption independent of the outer transport, and don't take "it's just a relay" as an answer on its own.
Do I need to configure my router at all to use Bernato?
No. That's the point of the dial-out model: the daemon initiates the connection outbound, so there's nothing to forward, no UPnP to enable, and no firewall rule to add. If your network already lets your machine browse the web over HTTPS, it already lets bernatod reach the broker.
Does dial-out work on hotel Wi-Fi or behind a strict corporate firewall?
Generally yes, because it rides on the same outbound HTTPS path that lets you check email or load a website on that network. Networks that are locked down enough to block all outbound port 443 traffic are rare and usually break far more than this. It's a much safer bet than depending on inbound access working, which most hotel and corporate networks block outright.