Skip to content

What stays on a phone, and whose it is ​

A phone can be signed in as one account today and another tomorrow — a family's tablet, a phone handed on, a tester with two accounts. This page is the table of every piece of state the nanoMuse apps keep on the phone, where it lives, whether it belongs to the account, the device or to nobody in particular, and what happens to it on each of the four events: sign out, switch (sign in as another account), delete the account and reinstall. Contract C12, 0.1.40; the earlier rule for conversations alone (C10, 0.1.39) is in every-device.md.

The rule ​

  • Every chat has an owner. The account that was signed in when the chat was first seen on this phone, or nobody for a chat made while signed out. Chats from before 0.1.40 are claimed the same way the first time the updated app runs: by the account signed in at that moment (a chat that had already been synced keeps the account its sync mapping names), else as nobody's.
  • You see the signed-in owner's chats and no others — in the drawer, the search, the Chat tab, the Library, Today's chats, Siri's shortcuts, and in what goes up to the relay. Signed out, only the chats made while signed out show; every account's are out of sight.
  • A sign-out asks one question: Keep this account's chats on this device, off by default. Off, the account's chats, memory, feed, goals, routines and face are removed from the phone; with sync on, the relay still has the chats for the next sign-in. On, all of it is put aside in a folder of the account's and comes back untouched when the account signs in again. Signing in as a different account, and Use a different server, go through the same sign-out.
  • Delete the account deletes it at the relay and removes everything of it from the phone, with no question — there is nothing to come back to. The next sign-in with the same address is a new account and starts empty.
  • A key the relay refuses (Sign out everywhere from another device, a relay reset, a relay bug — a 401 nobody on this phone asked for) is a sign-out nobody could answer, so the phone answers it the careful way: the account's data is kept, exactly as Keep this account's chats on this device would — the chats stay hidden behind their owner rows, the files and preferences go aside under the account's folder, the relay key is removed — and the sign-in page says Your sign-in on this phone was ended — sign in again to continue; your chats are kept on this device until then. The next sign-in with the same account id restores all of it; a different account sees nothing of it. Only when the relay answers 401 account_deleted — the account itself no longer exists — is there nothing to come back to, and the phone removes the data as Delete the account would.
  • A reinstall starts empty. On Android, the app no longer takes part in the device backup (allowBackup="false"): nothing of it is copied to Google, and a fresh install has no chats, no memory, no key. On the iPhone, a fresh install (no marker in UserDefaults, no provider, no chat) deletes the device-only Keychain items a previous install left (the relay's key, OAuth tokens, environment variables); the relay's key is saved on the device only, never in iCloud Keychain, and a 0.1.39 key moves there on the first launch. A restore from the person's own iPhone backup brings UserDefaults and files back together, marker included, and is treated as the same install.
  • The account's key is the relay's opaque account.id — never the number or the address; the folder names on disk are hashes of it. Nothing of the sorting involves the relay: an older relay behaves the same.

Android ​

Paths are under the app's private files/; preferences are the nanomuse SharedPreferences file unless named. Out means removed; aside means moved under minis-global/nanomuse/accounts/<key>/ and restored when that account returns; stays means untouched. The Key refused column reads bad_key (or any other 401) first, account_deleted second.

StateWhereWhoseSign out (keep off / on)SwitchKey refused (bad_key / account_deleted)Delete accountReinstall
Chats, messages, attachments, hands tracesupstream minis.db, minis-sessions/<id>/account (owner row in nanomuse_sync.db → session_owners)out / stay hiddenas sign out, then the new account's showstay hidden / outoutgone (no backup)
Chats made while signed outthe samenobody ("")aside while signed in, back when signed outasideback / backstaygone
Sync mappings (cids, cursor, known mids)nanomuse_sync.dbaccountout with the chats / staystay for the account that leftstay / outoutgone
What the muse remembers (SOUL.md, USER.md, GLOBAL.md, diary)minis-global/memory/accountout / asideaside; the new account's back or emptyaside / outoutgone
Feed posts and preferencesminis-global/nanomuse/feed/, feed-preferences.mdaccountout / asideasideaside / outoutgone
Goalsminis-global/nanomuse/goals.jsonaccountout / asideasideaside / outoutgone
Routines (the scheduled tasks, the feed's and the goals' included)minis_scheduled_tasks_prefsaccountalarms off, out / aside (routines.json)asidealarms off, aside / outoutgone
The faceminis-global/nanomuse/avatar/account (the relay's profile has it too)out / asideasideaside / outoutgone
The shared workspaceminis-global/shared/accountout / asideasideaside / outoutgone
Main chat, first conversation, feed switchesprefs main_chat.*, first_conversation.*, feed.*accountout / aside (prefs.json)asideaside / outoutgone
The relay key, account id, allowance, modelsthe provider store (the key encrypted), prefs cloud.* and the account rows of nanomuseaccountoutreplacedout / outoutgone
Device name and presence at the hubminis_device_identity, prefs hub.*devicestaystaystay / staystaynew identity
Remembered approvalsminis-global/nanomuse/grants.jsondevicestay (per chat: out with the chat)staystay / stay (per chat: out with the chat)staygone
Your own providers and keys, appearance, hands, languageupstream storesdevicestaystaystay / staystaygone
Ideasapp assetsnobody—————

Code: io.github.nanomuse.account.AccountScope (the rules, unit-tested), AccountData (applies them: reconcile, leave, enter), the session_owners table in SyncEntities.kt, ConversationSync.hidden (what every list leaves out), the hooks in ChatRepository.createSession and MinisApp, the sheet in CloudAccountScreen.

iPhone and iPad ​

Paths are in the app's sandbox — Application Support, or the app group's MinisConfig/ and var/minis/. Aside means moved under MinisConfig/nanomuse/accounts/<hash>/. The Key refused column reads as on Android: bad_key first, account_deleted second.

StateWhereWhoseSign out (keep off / on)SwitchKey refused (bad_key / account_deleted)Delete accountReinstall
Chats, messages, mediaupstream's SQLite storeaccount (owner row in nanomuse-owners.json)out / stay hiddenas sign out, then the new account's showstay hidden / outoutgone
Chats made while signed outthe samenobodyaside while signed in, back when signed outasideback / backstaygone
Sync tables (per account, cids, cursor, known mids)nanomuse-sync-accounts.jsonaccountout with the chats / staystay for the account that leftstay / outoutgone
What the muse remembersapp group memory/accountout / asideasideaside / outoutgone
Feed posts and preferencesMinisConfig/nanomuse/feed/, feed-preferences.mdaccountout / asideasideaside / outoutgone
GoalsMinisConfig/nanomuse/goals.jsonaccountout / asideasideaside / outoutgone
RoutinesMinisConfig/nanomuse/routines.jsonaccountnotifications off, out / asideasidenotifications off, aside / outoutgone
The face (stills; the clips are redrawn)Application Support nanomuse/avatar/accountout / asideasideaside / outoutgone
The shared workspaceapp group shared/accountout / asideasideaside / outoutgone
Main chat, first conversation, feed switchesUserDefaults nanomuse.main_chat.*, nanomuse.first_conversation.*, nanomuse.feed.*accountout / aside (defaults.plist)asideaside / outoutgone
The relay keyKeychain, this device only (…ThisDeviceOnly, not synchronizable)accountoutreplacedout / outoutswept on the first launch
Account id, allowance, models, regionUserDefaults nanomuse.cloud.*, the provider storeaccountoutreplacedout / outoutgone
Device id and name at the hubUserDefaults nanomuse.hub.*, Keychain …app.devicedevicestaystaystay / staystaynew identity
Your own providers and keysthe provider store; keys in Keychain, synchronizable (upstream's choice)devicestaystaystay / staystaythe config is gone; a synchronizable key stays in the person's iCloud Keychain
Appearance, the star, setup flagsUserDefaultsdevicestaystaystay / staystaygone

iCloud. Upstream's own iCloud sync of chats and providers (Settings → iCloud, off by default) and iCloud Keychain are the person's Apple ID, not the account's. With upstream's iCloud sync on, a chat deleted by a sign-out is deleted through it as a chat deleted by hand would be, and a chat kept aside can reach the person's other iPhone without its owner row — on that phone it is claimed by whoever is signed in there. The owners file, the sync tables and the folders put aside are in the iPhone's device backup like the rest of the app's data.

Code: NanoMuse/NanoMuseAccountData.swift (owners, leave, enter, the Keychain), NanoMuseSync.shows and dropTable, NanoMuseSignOutSheet.swift, the hooks in ChatStore.createSession and NanoMuseShell.

The relay ​

POST /v1/auth/delete removes every row of the account — the account, its keys, devices, profile (name, face, connectors), the ledger, events, the kept Data controls conversations and video tasks, the synced conversations, messages and cursors — drops its hub connections and, from the relay that ships with 0.1.40, forgets the account's live working notes too (they were memory, not rows). The relay's tests sign an account up, fill every table, delete it, and check that every table has zero rows for it and that the same address signing up again gets a new account id and an empty sync store.

One thing outlives the account for a while: the SHA-256 hashes of the keys it held, in deleted_keys, for 90 days. They name nobody (no account id, no address, no device), and they are what lets a phone that still holds one of those keys hear 401 account_deleted instead of 401 bad_key — the difference between remove the account's data, there is nothing to come back to and keep it aside, the person may sign in again. A relay from before 0.1.40 answers bad_key for both, and the phone keeps the data; nothing is lost, the folder simply waits.

For the maintainers ​

The old records a person saw after deleting an account in 0.1.39 were the phone's: a chat with no sync mapping — a side chat with the side-chat switch off, a chat from before 0.1.39 — had no owner, so it showed under every account and was pushed under the next one. The relay had already deleted its side. C12 gives every chat an owner and makes a sign-out take the account's data with it unless asked not to.

GPL-3.0-or-later. nanoMuse is an independent community project, not affiliated with Meta.