Fail closed, and say what that costs
An honest account of what this system enforces, where it enforces it, and what it still cannot do for you.
One enforcement point
The rendezvous server is the only component that authorizes anything. The relay is left unmodified precisely so that nobody has to reason about two half-enforced paths.
Deny is the default
If the permission API cannot be reached, times out, or answers something unparseable, the connection is refused. There is no configuration that turns that into an allow.
Refusals cost nothing
The authorization call sits after the cheap checks, so a wrong key, an unknown device or an offline one is refused without an API request — and cannot be used as an amplifier.
Written down, including the gaps
The section below is the uncomfortable half. It is on the marketing site on purpose: a security model you only learn about after deploying is not a model, it is a surprise.
What is enforced, and where
Sessions are encrypted between the two devices
That is upstream’s design and we kept it. A session is encrypted end to end with keys the servers never hold; the rendezvous server introduces peers and vouches for public keys, and the relay — when it is used at all — copies ciphertext. Neither one can watch a session.
The rendezvous channel is encrypted too
This one we had to add. Upstream’s open-source server never answers the client’s key exchange, so a login token would have had to cross it in the clear. Ours offers a signed key exchange on every connection, which is what makes it safe for the token to travel over it at all, and it costs no measurable time at connect.
Authorization happens at the introduction
Both of the rendezvous server’s connection paths ask the API before introducing two peers: the punch-hole path and the relay path. The second one matters more than it sounds: a relay request reaches the named peer without passing through the punch-hole handler at all, so a deployment that gated only the obvious path would still introduce strangers over the relay.
Termination is enforced at the endpoint
Ending a session does not go through the relay, because most sessions never touch the relay. It rides the heartbeat the controlled device already sends, so a direct peer-to-peer session is closed by the same mechanism, and within the same few seconds, as a relayed one.
Devices stay reachable through an outage
Connections fail closed, but registration does not. If it did, an outage of the auth backend would take every device offline at once and leave the emergency path with nothing to connect to. So devices keep announcing themselves while nobody is allowed to reach them — the one asymmetry in the system, and a deliberate one.
Break-glass, in the open
The rendezvous server accepts one capability it can verify without the API: signed, scoped to a single device, and time-limited, so a leak is bounded and the server needs only a public key. It rides the token field the protocol already has, which means it needs no special client. Every use is recorded locally first and reconciled when the backend returns, and the console shows a banner for as long as one is unexpired.
What this does not fix
Inherited from upstream's design. None of it is an authorization bypass; all of it is worth knowing before you deploy.
- A real device ID is distinguishable from a fake one. Authorization runs after the exists-and-online checks, so a stranger learns whether an ID is real. That ordering is deliberate: the alternative spends an API request per guess and hands the same stranger a denial-of-service amplifier.
- The NAT-test port answers anybody. It returns which IDs in a batch are online, with no key and no token, because that is how a client greys out offline machines. Treat that port as not-for-the-internet.
- The relay trusts its own key and a matching UUID. It pairs whoever presents the same relay UUID — safety rests on that UUID being unguessable and known only to two peers the rendezvous server already introduced. Give the relay a key explicitly: started without one it compares nothing.
- The login token is stored in plain text on the device, in the client’s own config file, as upstream stores it. File permissions are the protection. Treat a compromised endpoint as a compromised account and revoke it.
- The console is a browser client. Its route guards are navigation, not a boundary. The boundary is the API, which re-reads the user from the database on every request — so do not put anything in front of the console that you would not put in front of the API.
Found something?
Send it to us before you send it anywhere else, and we will work the timeline with you.
Trust, but read the source
Every claim on this page is checkable — the forks are public, and the patch map lists what we changed and why.