Husk 1.0
Husk 1.0 (versionCode 51).
The update button now names its source, in the app
F-Droid review !40810 asked that the built-in updater make clear, in the UI, that an update
taken through it comes directly from the developer rather than from F-Droid. It now says so
in two places: a permanent note under the button, and a confirmation dialog naming the source
before anything is downloaded.
The dialog is on the button only. GET /update is unchanged, so a headless phone on your own
private network can still be updated with nobody at the screen; that is what the endpoint is
for. Nothing checks for updates on its own: there is no background check and no scheduled
update, and the only two entry points are the button and that endpoint.
To be precise about what the updater fetches: it reads the version number from
xplat.co/husk/latest.json (falling back to this repository's copy) and downloads the APK
from the address that file names, today husk-latest.apk on the main branch of this
repository. That is not the release asset below, which is what F-Droid's Binaries:
reproducibility check uses.
Fixed: a carrier address was labelled as a Tailscale address
Net.tailscaleIp() returned the first 100.64.0.0/10 address on any interface. That block is
RFC 6598 CGNAT and mobile carriers hand it out to ordinary subscribers, so on mobile data the
carrier's address was displayed as "Tailscale IP" in /info and on the main screen. The
F-Droid tester measured this on a Galaxy S9 with Tailscale not installed at all.
An address now counts as Tailscale's only when the same NetworkInterface also carries an
address in fd7a:115c:a1e0::/48. Measured before release on an Android 12 device: the
Tailscale tun0 carries both the 100.x/32 address and the fd7a: one, so the label stays
correct on a real tailnet, while a carrier address (on rmnet0, no fd7a:) no longer
qualifies. This deliberately fails to the cautious side: a self-hosted Headscale using a
different ULA prefix will read as "no Tailscale". localIp() is unchanged; it already
excluded the CGNAT block.
Server responses are no longer Danish
The HTTP API is documented in English but returned some Danish error strings, and the two
built-in browser control pages (/control, /controlhw) had Danish buttons and status text.
Both are now English. The motion push and the two notification channel names were also
hardcoded Danish regardless of device language; they are string resources now, so English is
the default and Danish follows the device.
Corrected comments (no behaviour change)
ControlServer and Net claimed the server "binds only to loopback + the Tailscale IP, never
0.0.0.0". It has bound 0.0.0.0 since 0.9.x; protection is the source-IP allowlist plus an
optional token. Updater named the wrong GitHub host for the APK. Only the comments changed.
Not in this release: making the token mandatory and narrowing peerAllowed(). Turning the
token on by default breaks every existing deployment until it is re-paired, which is a fleet
migration rather than a patch.
husk-v1.0.apk is signed with the release key CN=xplat, SHA-256
96195cfd540e75f8a34dfc08764438769d4bc3e5d7970a9527d97004d4f2c17d, matching
AllowedAPKSigningKeys in the F-Droid recipe.