Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ jobs:
cp docs/MACOS.md dist/README-MACOS.md
cp LICENSE NOTICE dist/
cd dist && tar czf latch-macos-arm64.tar.gz Latch.app README-MACOS.md LICENSE NOTICE
# A 4.0 updater downloads brolink-macos-arm64.tar.gz and installs the
# BroLink.app inside it. The same app under that folder name is what
# lets it update at all; on its first run it moves itself to Latch.app
# A 4.0 updater downloads only its original archive and app names.
# The same app under that folder name lets it update at all; on its
# first run it moves itself to Latch.app
# and takes over the launch agent (crates/host/src/unix_setup.rs,
# adopt_legacy_bundle). Nothing in 4.1 or later reads this asset.
- name: package for 4.0 updaters
Expand Down Expand Up @@ -100,7 +100,7 @@ jobs:
throw "engine archive is not $($env:ENGINE_TAG): expected $($env:ENGINE_SHA256), got $got"
}
Compress-Archive -Path dist\* -DestinationPath latch-windows-x64.zip
# A 4.0 Mac takes brolink-host.exe out of brolink-windows-x64.zip and
# A 4.0 Mac takes the host executable from its original archive and
# sends it to each PC. The new program under the old name is what lets
# it update the PC; nothing in 4.1 or later reads this asset.
- name: package for 4.0 updaters
Expand Down
32 changes: 15 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,34 @@ GitHub release notes, so each version gets a heading of the form

## 4.1.1 (2026-10-02)

- Setting up a Windows PC that an earlier version had set up (when the
app was called BroLink) now registers the `LatchStream` service. Setup
- Setting up a Windows PC that a version before 4.1.0 had set up now
registers the `LatchStream` service. Setup
moved the engine's folder and removed the old service, but treated the
running engine as needing nothing more, so the new service was never
registered and the engine ran only once someone had signed in.

## 4.1.0 (2026-10-02)

- **BroLink is now Latch, by Bardbro.** The app, the repository
- **Introducing Latch, by Bardbro.** The app, the repository
(`github.com/MrBeldum/latch`), the release files (`latch-macos-arm64.tar.gz`,
`latch-windows-x64.zip`), the executables (`Latch.app`, `latch-host.exe`),
the services and login items, the environment variables (`LATCH_*`) and
bardbro.com all carry the new name, with a new logo: a square with a
corner cut away, and a blue square in the cut. Bardbro is the
project behind it, for cloud gaming.
- An installed BroLink updates itself to Latch. Settings, the pairing
identity, paired devices and the streaming engine carry over. On a Mac
the app moves from `BroLink.app` to `Latch.app` the first time it runs
and its login item with it; on Linux `latch.service` replaces
`brolink.service`; on Windows the host keeps working in place, and
`install-host.ps1` followed by **Set up as administrator** moves it to
`%LOCALAPPDATA%\Latch`, the `LatchStream` service and the new firewall
rule names. Machines on either name still find each other, and a 4.1 Mac
can still update a 4.0 PC. The release also carries
`brolink-macos-arm64.tar.gz` and `brolink-windows-x64.zip`, which are the
same builds under the old file names, because a 4.0 updater looks for
nothing else. Everything that still says BroLink is in
- Older installs update to Latch. Settings, the pairing identity, paired
devices and the streaming engine carry over. On a Mac the app moves to
`Latch.app` the first time it runs and its login item with it; on Linux
`latch.service` replaces the old service; on Windows the host keeps
working in place, and `install-host.ps1` followed by **Set up as
administrator** moves it to `%LOCALAPPDATA%\Latch`, the `LatchStream`
service and the new firewall rule names. Updated and older machines
still find each other, and a 4.1 Mac can still update a 4.0 PC. Releases
also include compatibility downloads under the filenames required by
4.0 updaters. Runtime compatibility identifiers are documented in
`crates/core/src/legacy.rs` and `crates/host/src/legacy.rs`.
- `LATCH_DATA_DIR` and `LATCH_GITHUB_TOKEN` replace the `BROLINK_` names,
which still work. The Docker node and relay read `LATCH_*`.
- `LATCH_DATA_DIR` and `LATCH_GITHUB_TOKEN` replace the old environment
variable names, which still work. The Docker node and relay read `LATCH_*`.
- The About page names Bardbro and links to bardbro.com. The accent
colour for focus and progress is the logo's blue, where it was violet.
- The window is redesigned around one design system, in the product
Expand Down
29 changes: 14 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,23 @@ relay, as this project's own VPS does: [deploy/native/README.md](deploy/native/R
The packet relay (when two NATs cannot punch through) is a separate kit:
[deploy/relay/RELAY.md](deploy/relay/RELAY.md).

## Coming from BroLink
## Upgrading from versions before 4.1.0

Latch was called BroLink until 4.1.0. An installed BroLink updates itself
to Latch and keeps its settings, its pairing and its streaming engine, and
every machine that updates can still talk to one that has not yet.
An older install updates to Latch and keeps its settings, pairing and
streaming engine. Updated machines can still talk to ones that have not
yet updated.

- **Mac:** the app moves from `BroLink.app` to `Latch.app` the first time
it runs, and its login item with it.
- **Mac:** the app moves to `Latch.app` the first time it runs, and its
login item moves with it.
- **Windows:** the host updates in place and keeps working. To move it to
the new folder and names (`%LOCALAPPDATA%\Latch`, `latch-host.exe`, the
`LatchStream` service), run `install-host.ps1` from the new zip, then
**Set up as administrator** once on the **Sharing** tab.
- **Linux:** the first start registers `latch.service` and retires
`brolink.service`. The files in `deploy/native/` have new names; see
[deploy/native/README.md](deploy/native/README.md).

The environment variables beginning `BROLINK_` are now `LATCH_`; for
`LATCH_DATA_DIR` and `LATCH_GITHUB_TOKEN` the old names still work.
`%LOCALAPPDATA%\Latch`, `latch-host.exe` and the `LatchStream` service,
run `install-host.ps1` from the new zip, then **Set up as administrator**
once on the **Sharing** tab.
- **Linux:** the first start registers `latch.service` and retires the old
service. For a native desktop, see [deploy/native/README.md](deploy/native/README.md).

Use `LATCH_DATA_DIR` and `LATCH_GITHUB_TOKEN` for environment overrides.
The equivalent names from versions before 4.1.0 still work.

## Updates

Expand Down
5 changes: 2 additions & 3 deletions crates/core/src/legacy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! What Latch answered to before 4.1.0, when it was called BroLink.
//! Compatibility identifiers used by versions before 4.1.0.
//!
//! Installed copies, other machines on the tailnet and old settings
//! folders keep the old names for a while after an upgrade, so the code
Expand Down Expand Up @@ -28,8 +28,7 @@ pub fn is_own_streamer_kind(kind: &str) -> bool {
}

/// A setting from the environment under its current name, else under the
/// old one: `LATCH_DATA_DIR`, then `BROLINK_DATA_DIR`. Unset and empty are
/// the same.
/// old one from before 4.1.0. Unset and empty are the same.
pub fn env(name: &str) -> Option<OsString> {
let current = std::env::var_os(name).filter(|v| !v.is_empty());
current.or_else(|| {
Expand Down
4 changes: 2 additions & 2 deletions crates/host/src/legacy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! What this host registered with the operating system before 4.1.0, when
//! Latch was called BroLink, and how an upgraded copy retires it.
//! What this host registered with the operating system before 4.1.0,
//! and how an upgraded copy retires it.
//!
//! An upgrade by the updater swaps the program and nothing else, so the
//! launch agent, the user unit, the Windows service, the scheduled task,
Expand Down
6 changes: 3 additions & 3 deletions crates/host/src/unix_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ const OLD_LOGIN_FILE: &str = "brolink-web.json";
const LOGIN_FILE: &str = "latch-web.json";

/// An install that an older version set up has two things in the engine's
/// `sunshine.conf` that name it: the path of the engine login file, which is
/// called `brolink-web.json` and sits wherever setup put it, and any path
/// `sunshine.conf` that name it: the engine login file under its old
/// filename wherever setup put it, and any path
/// under the old data folder, which this copy has moved. Point both at what
/// is there now, and rename the login file, so the engine finds the login
/// it was set up with. Nothing happens on an install that was never moved.
Expand Down Expand Up @@ -494,7 +494,7 @@ fn retire_legacy_launch_agent() {
}
}

/// A copy running from the app folder 4.0 and older used, `BroLink.app`,
/// A copy running from the app folder 4.0 and older used
/// moves into `Latch.app` beside it and carries on from there. The release
/// publishes the new app under the old name as well, because a 4.0 updater
/// installs only a folder of that name, and the first run lands here.
Expand Down
16 changes: 7 additions & 9 deletions deploy/native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,17 @@ in the `input` group: Sunshine must use its XTest fallback for this Xvfb session
Granting `/dev/uinput` access makes input go to kernel devices that Xvfb does not
consume, resulting in visible video but no functioning mouse or keyboard.

## Upgrading from BroLink (before 4.1.0)
## Upgrading from versions before 4.1.0

Latch was called BroLink until 4.1.0, and a node that was set up then has
`brolink*.service` units, `/usr/local/bin/brolink-host`, a `brolink.Xauthority`
cookie and `~/.local/share/brolink`. `migrate-from-brolink.sh` in this
directory swaps the units, the cookie name and the wait script for the new
ones and starts Latch; `latch-host` moves the data folder and the engine's
login file itself the first time it runs, keeping the pairing and the
Tailscale identity.
Nodes set up before 4.1.0 use earlier names for their services, host binary,
display cookie and data folder. `migrate-legacy.sh` in this directory swaps
the units, cookie name and wait script for the current ones and starts
Latch. On its first run, `latch-host` moves the data folder and the engine's
login file, keeping the pairing and Tailscale identity.

```sh
sudo install -m 0755 latch-host /usr/local/bin/latch-host
bash migrate-from-brolink.sh
bash migrate-legacy.sh
```

## Migration and recovery
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/usr/bin/env bash
# Move a native Linux node from the names Latch had before 4.1.0 (when it was
# called BroLink) to the new ones. Run it as the user that owns the desktop,
# Move a native Linux node from the names used before 4.1.0 to the current
# ones. Run it as the user that owns the desktop,
# from this directory, after putting the new latch-host at /usr/local/bin:
#
# sudo install -m 0755 latch-host /usr/local/bin/latch-host
# bash migrate-from-brolink.sh
# bash migrate-legacy.sh
#
# It stops the old stack, swaps the user units, the display cookie name and
# the wait script, and starts the new one. The data folder (settings,
Expand Down Expand Up @@ -67,7 +67,7 @@ systemctl --user enable --now latch.service
for _ in $(seq 1 30); do
if status="$(curl -fsS --max-time 2 http://127.0.0.1:47850/v1/status 2>/dev/null)"; then
echo "$status" | python3 -c 'import json,sys; s=json.load(sys.stdin); print("app=%s version=%s name=%s streamer=%s running=%s" % (s["app"], s["version"], s["name"], s["streamer"]["kind"], s["streamer"]["running"]))'
echo "done. The old binary /usr/local/bin/brolink-host can be removed once this looks right."
echo "done. The previous host binary can be removed once this looks right."
exit 0
fi
sleep 2
Expand Down
6 changes: 3 additions & 3 deletions scripts/install-host.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ if (-not $Exe -or -not (Test-Path $Exe)) {
}

$DestDir = Join-Path $env:LOCALAPPDATA "Latch"
# Where versions before 4.1 installed (they were called BroLink). Their
# Where versions before 4.1 installed. Their
# settings, pairing identity and engine archive come across, and the folder
# goes once this one is in place.
$OldDir = Join-Path $env:LOCALAPPDATA "BroLink"
Expand All @@ -43,7 +43,7 @@ if ((Test-Path $OldDir) -and -not (Test-Path (Join-Path $DestDir "host.toml")))
New-Item -ItemType Directory -Force -Path $DestDir | Out-Null
Get-ChildItem -LiteralPath $OldDir -Force | Where-Object { $_.Name -notmatch '\.exe(\.old|\.new)?$' } |
ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $DestDir -Recurse -Force }
Write-Host "Settings carried over from $OldDir"
Write-Host "Settings carried over from the previous install."
}
New-Item -ItemType Directory -Force -Path $DestDir | Out-Null
Copy-Item $Exe $DestExe -Force
Expand Down Expand Up @@ -75,7 +75,7 @@ New-ItemProperty -Path $Run -Name "LatchHost" -Value "`"$DestExe`" --background"

if (Test-Path $OldDir) {
Remove-Item -LiteralPath $OldDir -Recurse -Force -ErrorAction SilentlyContinue
if (Test-Path $OldDir) { Write-Host "Could not remove $OldDir completely; it is safe to delete." }
if (Test-Path $OldDir) { Write-Host "Could not remove the previous install folder completely; it is safe to delete." }
}

Write-Host "Shortcut: Desktop\Latch Host.lnk"
Expand Down
2 changes: 1 addition & 1 deletion scripts/install-macos-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ if [[ -d "$DEST" ]]; then
else
mv "$STAGE" "$DEST"
fi
# An install from before 4.1 (the app was called BroLink): stop its
# An install from before 4.1: stop its
# background job, drop its login item and remove its app, so only Latch is
# left. The new app registers its own login item when it first runs.
OLD_AGENT="gui/$(id -u)/dev.brolink.node"
Expand Down
2 changes: 1 addition & 1 deletion scripts/install-macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ LSREGISTER="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchS
"$LSREGISTER" -u "$APP" 2>/dev/null || true
"$LSREGISTER" -f /Applications/Latch.app 2>/dev/null || true
rm -rf "$APP"
# An install from before 4.1 (the app was called BroLink): stop its
# An install from before 4.1: stop its
# background job, drop its login item and remove its app, so only Latch is
# left. The new app registers its own login item when it first runs.
OLD_AGENT="gui/$(id -u)/dev.brolink.node"
Expand Down
Loading