release / govulncheck (push) Successful in 18s
release / web (push) Successful in 1m18s
release / go (push) Successful in 1m40s
release / integration (push) Successful in 4m29s
release / android (push) Successful in 5m17s
release / Build signed APK (releases and dev) (push) Successful in 5m20s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 16s
release / Verify release artifacts (tag releases only) (push) Skipped
The session cookie is a bearer credential, and it sat in plain text in the Room auth_session row. It now lives in a SessionVault: AES-256-GCM under a key held in the Android Keystore, with only the ciphertext in a private prefs file. A copy of the app's files no longer yields a usable session. Platform APIs only, no new dependency (androidx.security-crypto is deprecated). Nobody is signed out by the upgrade. On first launch AuthStore moves a cookie still in the row into the vault and clears the column. If the Keystore can't be used on a device, the cookie stays in the row as before rather than being lost. A sign-in or 401 that lands during the move wins over the value it read, and the move never throws. The auth gate now waits for this before choosing Login or Home, with a 10s deadline so a wedged Keystore can't leave the start screen spinning. Tests: AuthStoreSessionVaultTest (upgrade move, vault-only load, Keystore fallback, sign-in/out, hydration race) and SealedBoxTest (round trip, fresh IV, tamper and wrong-key rejection). The real Keystore path needs a device; the first launch after updating is that check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>