Pre-release. The app does not offer this to anyone automatically — the update check reads /releases/latest, which skips prereleases. Share the link with whoever should try it.This is the release that closes out the 1.8.0 incident, and it adds two things people kept asking for.One record per device, so the 1.8.0 state cannot happen again1.8.0 could leave a phone holding two identities for a single remote device and then using the one that had already been revoked — which is why the app stopped connecting for so many people, and why rolling back did not always help.The engine now keeps one record per device with the role it fills attached to it. The shapes behind those failures are no longer things that can be written down. An install left in the broken state repairs itself on the first connect, with nothing asked of you, and nothing is deleted — going back to an older build is removing one file.The whole search is bounded, not just each attemptA route search had a limit per attempt but none overall, so on a difficult network two full passes could run back to back before the app gave up: about 27 minutes of "finding a working route". The search now has one ceiling and will not begin a pass it cannot finish inside it.It notices when the phone changes networkMoving between Wi-Fi and mobile data used to be discovered by failing. The tunnel is now carried across to the new network where that is possible, and re-planned where it is not, instead of being torn down and rebuilt from nothing.Update from inside the appThe "a new version is out" card can now fetch and install it, rather than sending you to a download page you may not be able to reach.It only does that where it is honest to:only under Whole device coverage — the system downloader runs outside the app, so a proxy-only tunnel does not carry it, and a request like that leaving uncovered says plainly what this device is running. On proxy coverage you still get the page.only an APK signed by the same key as the one you have installed, matching the version this release published, built for your phone's architecture, and matching the SHA-256 published in SHA256SUMS, which is fetched separately from the file it checks.never in an F-Droid build, which F-Droid updates.A prerelease — this one included — is never offered automatically.A home-screen widgetConnect and disconnect without opening the app. It offers a stop while the engine is still searching, because that is when you actually reach for it, and it reads its words in the language you chose in the app rather than the launcher's.The build fails before the certificate doesA pinned certificate close to expiry now breaks the build instead of breaking the app in somebody's hand months later.MASQUE-in-MASQUE connectsNesting never worked. The inner hop only ever tried one framing — whicheverthe profile implied — so an inner edge that would not complete that handshakeended the attempt, and the one that would have worked was never reached. Thesingle hop has had both for as long as it has existed. Now the inner hop doestoo.Confirmed on a device, on a network where a single MASQUE hop connects fine.Four other explanations were eliminated first, each against a real phone: theaddress the inner hop dials, the age of its enrolment, the obfuscation itcarried, and the size of its first packet. The log now says enough to tellthose apart without guessing — which edge came from where, and what each hopwas sized at.Two fixes over 1.9.0MASQUE-in-MASQUE. Nesting did not connect on networks where a single MASQUEhop connects perfectly well. The inner hop was picking its gateway by keepingthe outer edge's first three octets and guessing the last one — six guesses,each landing on an ordinary Cloudflare edge that refused the handshake. It nowasks where Cloudflare put the inner device, the same way the single hop hassince 1.8.1, and keeps the guesses only as a fallback.This is not yet confirmed against a real network. If you use this mode, that isthe thing worth reporting.And one moreThe in-app updater in 1.9.0 read the published checksum file with the wrongname for each file, so it would have refused every real update — at exactly thepoint where somebody on a broken version is trying to reach a working one. Thetest that should have caught it used an invented example rather than apublished one. Both are fixed. Use this build rather than 1.9.0, 1.9.1 or 1.9.2.Verification362 engine tests and 244 app unit tests, all green. Every new test was also run against the code without its fix, to confirm it fails there — a test that passes either way proves nothing.Not yet verified on a device. That is what this prerelease is for.