Decisions
Why tunnels work the way they do. What they do is the feature page; what they protect is Security.
Why does the tunnel only dial out?
lnk tunnel open opens one WebSocket to the relay, and every public
request comes back over it, many at once, with responses streamed to the
visitor as they're produced. The user's machine never opens an inbound
port, so it works behind any router and nothing on it listens for the
internet.
Who owns a name under the relay's domain?
The user a token belongs to. <you> in <app>.<you>.local.link comes
from the login, never from the request, so nobody else can claim a name
under it, and a reconnecting lnk gets the same URL back.
Who can sign up?
GitHub accounts on the relay's invite list (LINK_GITHUB_ALLOW), logging
in through the relay's own GitHub OAuth app. * opens it to any GitHub
account, with the extra checks in open signups,
because open relays attract abuse.
Why does a tunnel have to say who can reach it?
lnk tunnel open refuses to start without --public, --github or
--auth. An unusual URL isn't a secret: tunnel hostnames show up in
public Certificate Transparency logs, so going public is a choice the
user makes out loud.
How do you let in only some people?
With a GitHub sign-in per visitor, checked at the relay (--github). With
one shared password, anyone who has it gets in, and many addresses
guessing can use up its wrong-password allowance. A sign-in per person
has neither problem, and most of the people a developer shares with have
a GitHub account. The relay's own OAuth app does it, so a tunnel needs no
setup.
How does a sign-in reach the tunnel?
GitHub sends the browser back to the bare domain, which hands a one-time
code to the tunnel's host, where the relay sets a host-only cookie. The
OAuth app has one callback URL, and a cookie set on the bare domain would
reach every tunnel, since local.link isn't on the Public Suffix List. A
nonce cookie from the tunnel's host ties the code to the browser that
started the sign-in.
Why does the relay check passwords and sign-ins?
So nothing reaches the user's machine until a visitor is let in, and the
app never sees the password or the sign-in cookie: the relay strips both
before forwarding. lnk refuses to serve a protected tunnel unless the
relay confirms it enforces the protection.
Why does a password not skip the visitor warning?
A browser takes a password written into a link
(https://user:password@host/) and answers the relay's request for one
with it, so a phisher can publish the password with the link. On an
open-signup user's tunnel the relay asks for the password first, then
shows the warning. A GitHub sign-in skips it: each visitor signs in as
themselves, and only the accounts the tunnel lists get in.
Why does lnk hold each connection to the local app itself?
A local app that stops reading leaves what its HTTP client took waiting
in that client's buffers. A shared client's connections run on their
own and wait to flush them before closing, so a cancelled request could
keep its body, and its socket, for good. The port service instead holds
each connection for as long as its request, closes it when the request
ends unfinished or is cancelled, and keeps up to 8 finished ones for the
next requests. What it hands a connection stays counted against lnk's
64 MiB until it has been written.
Why does lnk send its token before Hello?
The relay holds a few slots for connections that haven't said Hello,
capped per address and network, and anyone can take them. A few dozen
IPv4 addresses could keep them all full, so agents got 503. With the
token in the upgrade request, the relay knows an agent before its
socket opens, and gives it a slot of its user's own: only strangers
wait. Hello still carries the token, so an old lnk and an old relay
keep working with each other and with new ones.
