Sign-in loads the identity it already has

🔴 **This is why signing out and back in broke everything.** Sign-in only
ever consulted the in-memory `identity` field, and sign-out clears it —
so it minted a fresh `DeviceIdentity` and OVERWROTE the vault's, making
this phone a brand new device on every single sign-in.

Everything the owner hit follows from that one line:

  - contacts saw a safety-number change and had to accept new keys;
  - the account accumulated a registered device per sign-in, and fan-out
    is per device, so half of everything sent to this account went to a
    mailbox nobody reads — which is why the failures looked random and
    different each time;
  - the relay's public key log had not caught up with a key registered
    seconds earlier, so even after accepting, the next send failed on
    "the keys offered by this contact are not in the public log".

⚠️ It was harmless until it wasn't. Sign-out used to destroy the vault,
so there was never a stored identity to find and minting one was
correct. Keeping the device across a plain sign-out — which is what
makes signing out cheap — is what turned this into the bug it always
looked like. The two changes had to land together and only one did.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Paul Hitt
2026-09-01 15:28:17 -04:00
co-authored by Claude Opus 4.8
parent fcc6692284
commit fc0dff18ca
@@ -506,10 +506,26 @@ class MessengerViewModel(app: Application) : AndroidViewModel(app) {
} }
val vault = vault ?: error("the vault is not open") val vault = vault ?: error("the vault is not open")
val device = identity ?: DeviceIdentity.create().also { // 🔴 **The stored identity FIRST, and this line is why signing out and
vault.saveIdentity(it) // back in used to cost every contact a prompt.** Sign-in only ever
identity = it // consulted the in-memory field, and sign-out clears it — so it minted
} // a fresh identity and overwrote the vault's, making this phone a brand
// new device on every sign-in. Contacts saw a safety-number change,
// sends were refused until they accepted it, and the relay's public key
// log had not yet caught up with the key being offered, so even after
// accepting, the next send failed on "the keys offered by this contact
// are not in the public log".
//
// ⚠️ It was harmless right up until it wasn't: sign-out used to destroy
// the vault, so there was never a stored identity to find and minting
// one was correct. Keeping the device across a plain sign-out is what
// turned this into the bug it always looked like.
val device = identity
?: vault.loadIdentity()?.also { identity = it }
?: DeviceIdentity.create().also {
vault.saveIdentity(it)
identity = it
}
val relay = RelayClient(config.baseUrl, username = account.username, appPassword = credential) val relay = RelayClient(config.baseUrl, username = account.username, appPassword = credential)
relay.useRelayAuth(device) relay.useRelayAuth(device)