Skip to content

Tighten who LAN sharing and pairing reach - #1941

Merged
BarganConstantin merged 4 commits into
developmentfrom
fix/lan-sharing-rules
Oct 4, 2026
Merged

BarganConstantin merged 4 commits into
developmentfrom
fix/lan-sharing-rules

Conversation

@BarganConstantin

Copy link
Copy Markdown
Owner

What changes

  • Share only accounts the user chose with decks the user chose. A login that arrives from a deck somebody here accepted is still ticked here, so this deck can repair the next machine. That tick is now remembered as the arrival's, beside the shared list (lan.onward), and a deck the accept switch paired is offered, and handed, only the logins a person ticked. Unticking and ticking the login again makes it a person's tick, offered to every paired deck. README's Local network section says so.
  • The local accept switch waits for a press on a tailnet-range address it cannot place. An address in 100.64.0.0/10 that the last Tailscale read could not place (no read yet, a failed read, Tailscale still starting) is now a request for somebody to accept, as an unknown tailnet machine already is. Ordinary local addresses, and the owner's own tailnet machines, are answered as before.
  • The beacon's machine id is keyed with a secret that never leaves the machine. The id that folds one computer's decks into one row is now keyed with the machine id the operating system keeps (systemd's or D-Bus's on Linux, the platform UUID on a Mac, the machine GUID on Windows). It stays the same for every deck on the machine and differs on every other. A machine with none to read sends the id it sent before. Decks on other machines only use the id to fold one machine's rows, so older ones keep working. A newer deck still recognises the id an older deck on the same machine sends.
  • Bound the decks a beacon remembers answering. It keeps no more entries than the heard list does (MAX_STRANGERS) and drops the oldest first. A deck dropped that way is answered once more if it is heard again.

Verification

  • npm run typecheck: clean.
  • Full suite, --maxWorkers=3 --minWorkers=1: 901 files, 11783 tests passed.
  • New regression tests, each run alone first and seen failing for the reason it covers:
    • lan-arrival-tick.test.ts: whole engines on loopback. A deck paired by the switch received a login that had been ticked on arrival (imported was ['ccdeck2:slot-7']). It now receives nothing. A deck somebody chose still receives the login, and so does every paired deck once a person ticks it. The tests also cover the prefs side: the mark is written with the tick, an untick drops it, and it is kept out of the page's prefs.
    • lan-tailnet-unread.test.ts: the requests module with the real routeOf. With no snapshot, or a Starting one, the local switch said yes to 100.101.1.2. It now leaves a request, including when the switch is turned on later. Plain local addresses and the owner's own tailnet machine are unchanged.
    • lan-host-id-private.test.ts: the id no longer comes out of a plain hash of the hostname and home directory. Covers which secret each platform reads (with fakes), what goes out when no secret exists, what the beacon actually sends, and an older deck on the same machine being read as self.
    • lan-beacon-answered-bound.test.ts: before the fix, the first deck answered was never dropped, however many followed. Now it is dropped once MAX_STRANGERS newer ones have been answered, and dedup within that window is unchanged.
  • Repointed shape pins: lan-prefs.test.ts and prefs-update-1041.test.ts now include onward.
  • No UI changed. Every deck the tests start binds loopback with a deaf beacon socket. Nothing joined or scanned a real network.

… chose

A login that arrives from a chosen deck is ticked here so this deck can
repair the next machine in the group. That tick is now remembered as the
arrival's, beside the shared list, and a deck the accept switch paired is
offered only the logins a person ticked. Unticking and ticking the login
again makes it a person's tick, offered to every paired deck.
…le places it

The local accept switch says yes only to a deck plainly on the local
network. An address in Tailscale's block that the last tailnet read could
not place (no read yet, a failed read, Tailscale still starting) is now a
request for somebody to accept, as an unknown tailnet machine already is.
Ordinary local addresses and the owner's own tailnet machines are answered
as before.
…r sends

The id that folds one computer's decks into one row is now keyed with the
machine id the operating system keeps (systemd's or D-Bus's on Linux, the
platform UUID on a Mac, the machine GUID on Windows), which never leaves
the machine. It stays the same for every deck on it and differs on every
other, and a machine with none to read sends the id it sent before. A
newer deck still reads the id an older deck on the same machine sends as
its own machine.
A beacon answers each new deck once and remembers whom it answered. That
memory now keeps no more entries than the heard list does, dropping the
one answered longest ago first; a deck dropped that way is answered once
more if it is heard again.
@BarganConstantin
BarganConstantin merged commit 26231c8 into development Oct 4, 2026
10 checks passed
@BarganConstantin BarganConstantin mentioned this pull request Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant