Releases: munzzyy/starling
Release list
0.5.0
Protocol v2. This is a hard break: a 0.4.x app cannot talk to a 0.5.0 relay, and existing circles have to be created again.
What changed
Forward secrecy. Content keys advance every ten minutes and the old ones are destroyed. A phone can only decrypt the window its owner chose to keep, from ten minutes up to a day, and that setting is worded as the trade it is.
Removing somebody actually removes them. A re-key mixes fresh entropy and moves the circle to a new channel. Whoever was removed does not get the new key and cannot follow. In v1 removal was a request.
Invite links are one-time. They commit to the inviter's identity, and somebody has to compare a safety number before anyone is admitted. A v1 link was a bearer token: anyone who ever saw it held the keys forever.
Safety numbers, with a screen for checking them and an explicit accept when a member's keys change.
Help links are per person, revocable, and expire. Sending one to a friend no longer means everyone who was ever sent one can watch.
Honest limits
Starling has not been audited by a human being. Six rounds of adversarial review went into this release and each one found real defects in the code the round before had passed, including an invite link that was a full takeover of the joining device and a re-key field that let any member erase every other member's circle. Those are fixed. The rate they were being found at is the reason for this paragraph.
docs/AUDIT.md is written for whoever looks at this next, and docs/THREAT-MODEL.md says plainly what the relay still learns.
Until somebody qualified has actually reviewed it, this is not something to hand to a person whose safety depends on it.
Verify this build
sha256 45f4a9fb569cd24fff030039c73a59ff8dfe5a29b679a72dd287b18ddb094033 starling-0.5.0.apk
signing key SHA-256 DB:B0:C4:91:53:0F:74:75:40:9C:4C:29:53:E9:F6:8A:52:51:9C:2F:68:1D:D9:E5:F6:99:38:F3:BF:9B:8C:9E
Starling 0.4.1
0.4.1 adds a way to get help from people who are not in your circle, and fixes something real in how devices decide who a message came from. Everyone should update: 0.3.x clients cannot talk to this relay.
(0.4.1 replaces 0.4.0 within the hour. The site serves the help page at /help and answered the .html spelling with a redirect, so links were minted at an address that redirected, and the service worker's exclusion for that page checked the spelling nobody lands on: a helper who already had the app installed would have been handed the app instead of the emergency. Caught by testing against the deployed site rather than the dev server, which is where it could only ever have shown up.)
Get help from outside your circle
Your circle is a list you chose in advance. In an actual emergency the people who can reach you are often not on it, and are not going to install an app in the next two minutes: a neighbour, a colleague, whoever is nearby.
An SOS can now mint a help link. It opens your live position in any browser, on any phone or laptop, with no app and no account. It runs on its own secret, its own channel and its own signing identity, so it shows that one emergency and can never turn into access to your circle or its history. Checking in safe, stopping the share, or locking the app ends it, and the person watching sees that it ended instead of a dot that quietly stops moving.
Anyone you send the link to can forward it. That is what a link with no account means, and it is the trade: reachability when you need it, in exchange for not controlling who ends up watching.
Devices verify who sent a message now
The circle's content key is shared by everyone in it. That means decrypting a message only ever proved that some member wrote it, never which one. The thing that answers "which one" is the per-sender signature, and until now the relay was the only party checking it.
So a member working with a compromised relay could have put another member's name on any position. The threat model claimed otherwise, which made it wrong.
The feed now carries each signature and every device checks it against the key that member's id hashes from, before decrypting anything. test/roster.test.mjs runs that exact forgery, and with the check removed it succeeds, which is the point of having the test.
Member ids went from 64 to 128 bits at the same time, since that id is how a key gets named and a receiver's whole trust in a sender rests on it. Existing installs upgrade in place and keep their circles, keys and invite links.
Also
- Tor mode asks Orbot which SOCKS port it is actually on instead of assuming 9050, which is still the fallback. It only trusts an answer to a question it just asked, since the receiver has to be exported and cannot see who sent it an intent.
- The relay stops a member's replay window from re-opening under concurrent posts, and separates its rate limit accounting so flooding new channels can no longer clear an address's own limit.
On the audits
This release was reviewed by models and tooling other than the ones that wrote it, which is the right way round for new code doing cryptography, and was suggested by someone whose opinion on that is worth more than mine. Two independent passes plus a fan-out of reviewers with an adversarial refutation round.
What that caught and what shipped fixed: a location failure racing an SOS could leave a beacon running with no way on screen to stop it, and the next SOS would have reused its secret; the viewer left the beacon secret in the browser's address bar and history, where it would sync to the helper's other devices; ending a beacon could hang on a stalled request and never cancel the sender.
What it got wrong, for the record: a confident report that alg could be set to hmac to make the verifier import a public key as a symmetric secret. It is an allowlist lookup, so hmac, HMAC, __proto__ and constructor all return false. Worth writing down, because a plausible crypto finding that is wrong costs as much time as a real one.
Still true, and still worth saying: nobody independent has audited this. Boring constructions and real tests are not the same thing as review by a person.
Verify this build
Signed with the certificate whose SHA-256 fingerprint is served at starlingmap.app/.well-known/assetlinks.json. Reproducible: a clean checkout of tag v0.4.1 (npm ci, bash tools/sync-vendor.sh, ./gradlew assembleRelease in android/) yields an unsigned APK with sha256 a52636f8de73fb2c0a79b3e79482b65f9f2ebd1192ff54e11e21f14314dce3fb, and signing with the same key yields this exact file:
0c0632f55e3075601a5dae50ae189e0fe54223e0c3877f86bc81eff5062298a2 starling-0.4.1.apk
Starling 0.4.0
0.4.0 adds a way to get help from people who are not in your circle, and fixes something real in how devices decide who a message came from. Everyone should update: 0.3.x clients cannot talk to the 0.4.0 relay.
Get help from outside your circle
Your circle is a list you chose in advance. In an actual emergency the people who can reach you are often not on it, and are not going to install an app in the next two minutes: a neighbour, a colleague, whoever is nearby.
An SOS can now mint a help link. It opens your live position in any browser, on any phone or laptop, with no app and no account. It runs on its own secret, its own channel and its own signing identity, so it shows that one emergency and can never turn into access to your circle or its history. Checking in safe, stopping the share, or locking the app ends it, and the person watching sees that it ended instead of a dot that quietly stops moving.
Anyone you send the link to can forward it. That is what a link with no account means, and it is the trade: reachability when you need it, in exchange for not controlling who ends up watching.
Devices verify who sent a message now
The circle's content key is shared by everyone in it. That means decrypting a message only ever proved that some member wrote it, never which one. The thing that answers "which one" is the per-sender signature, and until now the relay was the only party checking it.
So a member working with a compromised relay could have put another member's name on any position. The threat model claimed otherwise, which made it wrong.
The feed now carries each signature and every device checks it against the key that member's id hashes from, before decrypting anything. test/roster.test.mjs runs that exact forgery, and with the check removed it succeeds, which is the point of having the test.
Member ids went from 64 to 128 bits at the same time, since that id is how a key gets named and a receiver's whole trust in a sender rests on it. Existing installs upgrade in place and keep their circles, keys and invite links.
Also
- Tor mode asks Orbot which SOCKS port it is actually on instead of assuming 9050, which is still the fallback. It only trusts an answer to a question it just asked, since the receiver has to be exported and cannot see who sent it an intent.
- The relay stops a member's replay window from re-opening under concurrent posts, and separates its rate limit accounting so flooding new channels can no longer clear an address's own limit.
On the audits
This release was reviewed by models and tooling other than the ones that wrote it, which is the right way round for new code doing cryptography, and was suggested by someone whose opinion on that is worth more than mine. Two independent passes plus a fan-out of reviewers with an adversarial refutation round.
What that caught and what shipped fixed: a location failure racing an SOS could leave a beacon running with no way on screen to stop it, and the next SOS would have reused its secret; the viewer left the beacon secret in the browser's address bar and history, where it would sync to the helper's other devices; ending a beacon could hang on a stalled request and never cancel the sender.
What it got wrong, for the record: a confident report that alg could be set to hmac to make the verifier import a public key as a symmetric secret. It is an allowlist lookup, so hmac, HMAC, __proto__ and constructor all return false. Worth writing down, because a plausible crypto finding that is wrong costs as much time as a real one.
Still true, and still worth saying: nobody independent has audited this. Boring constructions and real tests are not the same thing as review by a person.
Verify this build
Signed with the certificate whose SHA-256 fingerprint is served at starlingmap.app/.well-known/assetlinks.json. Reproducible: a clean checkout of tag v0.4.0 (npm ci, bash tools/sync-vendor.sh, ./gradlew assembleRelease in android/) yields an unsigned APK with sha256 6ad4533c4a9a71a3b846d9d53a05d56a877fa96b5386bc5f32522a7d6b962341, and signing with the same key yields this exact file:
ebc95c6a6573e0733a47acd600a08f49242c4cb9ddc93298866da8af36e8e9b1 starling-0.4.0.apk
Starling 0.3.1
0.3.1 hardens the Android wrapper. It came out of an external review round on the panic wipe, the Tor toggle, and the biometric path. Nothing new to learn. Just update.
What changed
The panic wipe stopped trusting the system to clean up the Keystore. Android does clear an app's keystore namespace during clear-data, but that half runs in another process and swallows its errors. The wipe now deletes the wrap key and the share notification channel itself, in-process, before handing off to the system wipe.
Tor mode got three fixes. The proxy rule says socks5 outright, so hostname lookups go through the proxy. Bare socks already meant SOCKS5 in Chromium, but nobody should have to read Chromium source to trust that. Flipping the toggle reloads the page, which strands connections opened under the old setting. And while the toggle is on, fixes come from GPS only: the network provider works by sending nearby wifi and cell identifiers to an off-device lookup, which is the exact side channel you turned Tor on to avoid.
The biometric prompt used to zero its copy of the vault key only when auth succeeded. Now it zeroes on every exit, dismissal and error included.
Two doc changes worth naming. The threat model says outright that nothing here has had an external audit. The Android notes spell out the Tor toggle's limits: it assumes Orbot's default port 9050 and fails closed if the port moved, it only covers the WebView, and nothing stops another local app squatting the port. Orbot's per-app VPN mode has none of those caveats and works with zero code on our side.
Verify this build
Signed with the certificate whose SHA-256 fingerprint is served at starlingmap.app/.well-known/assetlinks.json. Reproducible: a clean checkout of tag v0.3.1 (npm ci, bash tools/sync-vendor.sh, ./gradlew assembleRelease in android/) yields an unsigned APK with sha256 5aa9d250b6e79d42db6e3327198bb0e55a99db3beae2f52eb8776aa812852be6, and signing with the same key yields this exact file:
c4eea5fd4653b264e79f75505462e8fb75af5e2952eb42ee073a65f1be9b8cb2 starling-0.3.1.apk
Starling 0.3.0
Starling 0.3.0 adds the feature groups actually need: circles, plural.
Multiple circles
Keep one circle for family and another for the friends you split up from at a fair or a concert. Tap the circle name to switch, start a new circle, or join one from an invite. Each circle has its own secret, its own channel, and its own signing identity, so even the relay cannot tell that two circles share a member. The map, sharing, and invites follow whichever circle is active, and switching away says goodbye to the old circle so nobody sees a ghost dot.
Storage stayed backward compatible: 0.3.0 installs straight over 0.2.0 and your existing circle, passcode, and settings carry over untouched (tested by upgrading a locked 0.2.0 install on a real Android image). With the app lock on, inactive circles are sealed under the same vault key as everything else. The write orderings are built so that a crash or a failed storage operation at any point leaves every circle secret recoverable; crash-window property tests halt a simulated store after every single write to prove it.
Also in this release
- The hosted website is now a landing page and demo only; circles live in the app, where the secret sits behind the OS keystore instead of a browser tab.
- The website serves the APK directly with no redirect chain, which fixes downloads that hung at 100% on some phones.
- Members never see each other's IP addresses; route the app through Orbot and the relay does not see yours either. The landing page and privacy policy now spell that out.
Verify this build
Signed with the certificate whose SHA-256 fingerprint is served at starlingmap.app/.well-known/assetlinks.json. Reproducible: a clean checkout of tag v0.3.0 (npm ci, bash tools/sync-vendor.sh, ./gradlew assembleRelease in android/) yields an unsigned APK with sha256 71140134efd9e10314415a2c4e4237840fd245b8f343d92c802e6db7614ef8fc, and signing with the same key yields this exact file:
a658fba27ca064d4d08f7aef6eb33ec487ded15fe27fc422d73ccb0a19298c9d starling-0.3.0.apk
Starling 0.2.0
Starling 0.2.0 is the first Android release: the same app that runs at starlingmap.app, shipped as a native package with the pieces a web page cannot provide.
Android app
- Background sharing through a while-in-use foreground service, with a persistent notification the whole time it runs. The app never requests background location permission.
- Fingerprint or face unlock for the app lock, backed by an Android Keystore key that requires a fresh strong biometric per use and dies if a new biometric is enrolled.
- Panic wipe wired to PanicKit: pair Ripple and a trigger wipes all app data instantly, even with the app locked.
- Orbot support: per-app VPN mode works with no setup, and a SOCKS toggle routes WebView traffic through Orbot's proxy with no direct fallback.
- Verified app links, so invite links open in the app.
Everywhere
- Custom relay setting in the Android app for self-hosters, and CORS on the relay to match.
- Leaflet is now a lockfile-pinned npm dependency synced unminified at build time instead of a vendored blob.
- A real disclosure policy in SECURITY.md and an Android section in the threat model.
Verify this build
The APK is signed with the certificate whose SHA-256 fingerprint is served at starlingmap.app/.well-known/assetlinks.json. Release builds are reproducible: npm ci, bash tools/sync-vendor.sh, then ./gradlew assembleRelease in android/ at tag v0.2.0 produces an unsigned APK with sha256 928acad757ef55b800e7536574f479e8eb3fef6972a3716c685806338c0586b6, and apksigner from build-tools 34 with the same key yields this exact file:
075bc281dd87da708bb6671eda7e834e5c681b76439ec6b65c151b2d80139dc5 starling-0.2.0.apk
Build docs: docs/ANDROID.md. Full changes: CHANGELOG.md.
Starling v0.1.0
First release. Starling lets you share your location with the people you trust and no one else. It has the parts of Life360 people actually use, a live map of your circle, SOS, check-ins, battery, invite links, minus the part where a company gets everyone's location.
Try it at https://starlingmap.app. On a phone, add it to your home screen and it behaves like an app.
The whole point is that your location is encrypted on your device with a key the server never gets. The relay only ever holds ciphertext, and it deletes it after a day. There are no accounts, no phone numbers, and no analytics. An invite link carries the secret in the part of the URL that never leaves your browser, so you send it over something you already trust and whoever has it is in the circle.
A few things worth calling out. You can lock the app with a passcode (and a fingerprint or Face ID where the browser supports it), which encrypts your circle secret so a lost phone gives up nothing. Sharing stays off until you turn it on. And there's an off-grid map that draws locally and makes no network requests at all.
If you want the details, the repo has the exact protocol and an honest threat model that says what this does not do as clearly as what it does. It ships with 153 unit tests and two real-browser tests, one of which reads the relay's database back and checks there's no name or coordinate anywhere in it.
Fair warning on the limits: it shares while the app is open, not in the background, the relay can still see your IP and timing, and there's no forward-secrecy ratchet yet. All of that is in the threat model.