Repository navigation
feat(auth): give Android a Keychain equivalent for the account list - #1502
Merged
Merged
Conversation
iOS lists every account the user has signed into and lets them switch between them, with the Keychain holding that list across an uninstall. Android had no counterpart: Google Password Manager could restore one account but never enumerate them, and it sat behind a beta flag that was never used. Google Block Store keeps its blob in Play services' own directory rather than the app sandbox, so it survives uninstall when Backup services is on. The whole list lives in one of its 16 entries, packed by AccountRecordCodec as [version:1][count:2][record x count], each record 16 bytes of entropy plus three timestamps. That is 2003 bytes at the 50-account cap, about half the 4KB entry limit. Only the seed is stored; the owner key and the display name derive from it. Writes are best effort. Play services being unavailable costs an account its place in the list, not its funds, and the access key is still the backstop. A mutation never rewrites a list it could not first read, so a failed read can drop an addition but cannot lose an entry. Select an Account is reachable the two ways iOS offers it: Log In routes there when the store holds accounts, and Advanced > Switch Accounts opens it from inside the app. Switch Accounts stays a StaffMenuItem, as it was behind the old flag, so it keeps the beta gate and the badge. Balances are fetched per account with its own owner key as both the account owner and the requesting owner, a self-signed request, which is what makes them resolvable while logged out. A row the backend does not know says "Not Found"; a row whose balance could not be fetched says so separately, because collapsing the two would tell an offline user that every wallet they own is gone. Google Credential Manager and its feature flag are removed. The existing credentials DataStore seeds Block Store on first launch, as a condition rather than a one-shot hook so it also recovers an entry that decodes as empty.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
iOS keeps its account list in the Keychain, which is why deleting the app and reinstalling it still leaves the user a list to pick from. Android had no counterpart. Google Password Manager, which this replaces, could restore a single account but never enumerate them, and it sat behind a beta flag that was never used.
Block Store is the counterpart
Play services keeps the blob in its own directory rather than the app sandbox, so it outlives the app the way a Keychain item does. Three ways the equivalence is not exact, each of which the design leans on:
pm clearwipes it. Play services logsRemoved Blockstore data for <pkg> upon clearing data. This outlives a reinstall, not a data clear.What gets stored
The whole list lives in one of Block Store's 16 entries, packed by
AccountRecordCodecas[version:1][count:2][record × count], each record 16 bytes of entropy plus three timestamps. That is 2003 bytes at the 50-account cap, roughly half the 4KB entry limit, which leaves room for a format change. Only the seed is stored; the owner key and the display name derive from it.AccountRecordmirrors iOS'sAccountDescription, soft delete included.Select an Account
Reachable the two ways iOS offers it: Log In routes there when the store holds accounts, and Advanced → Switch Accounts opens it from inside the app. Switch Accounts stays a
StaffMenuItem, as it was behind the old flag, so it keeps both the beta/staff gate and the badge.Balances are fetched per account with its own owner key as both the account owner and the requesting owner — a self-signed request, which is what makes them resolvable while logged out. A row the backend does not know says "Not Found"; a row whose balance could not be fetched says so separately. Collapsing the two would tell an offline user that every wallet they own is gone.
The in-app switcher hides the "Enter a Different Access Key" footer. From inside the app there is no onboarding flow to fall through to, and a logged-in user reaching for a new access key is logging out, which the menu already offers.
Credential Manager
Removed, along with
FeatureFlag.CredentialManager. The existing credentials DataStore seeds Block Store on first launch, written as a condition rather than a one-shot upgrade hook so it also recovers an entry that decodes as empty. That DataStore is now excluded from Google backup.Maestro flows
Three flows cover the list, the in-app switcher, and the case this whole change exists for.
blockstore_survives_reinstall.yamlneeds a realadb uninstall+installbetween flows, since Maestro has no uninstall command; its header gives the sequence. Switch Accounts is gated, sosubflows/unlock_beta_features.yamlunlocks it the way a human does — seven taps on the version footer. ThebetaFlagslaunch argument only flips individualFeatureFlagkeys and does not set that override.Design and plan:
docs/superpowers/specs/2026-09-18-blockstore-account-list-design.mdanddocs/superpowers/plans/2026-09-18-blockstore-account-list.md.