Document experimental multi-account switching

This commit is contained in:
thePR0M3TH3AN committed 2026-07-22 20:54:46 -04:00
1 parent 0c9011e064
commit 6f0bc2a056
1 file changed
+325
+325
View File
@@ -0,0 +1,325 @@
# BitLogin Multi-Account Switching
**Status:** Experimental design
**Document version:** 0.1
**Date:** July 22, 2026
**Implementation status:** Not implemented
## 1. Summary
BitLogin should support keeping multiple independently authenticated accounts on
a device and switching between them without repeating the full password and
relay-login flow every time.
The first implementation should treat each account as an independent BitLogin
account with its own credentials, recovery phrase, capsules, Nostr identity,
relay preferences, messages, application state, connections, and entitlements.
```text
BitLogin on one device
├── Personal account
├── Business account
├── Anonymous account
└── Additional imported account
```
This feature is primarily a local session and user-interface capability. It does
not initially change the BitLogin wire protocol or place several independent
accounts beneath one recovery phrase.
## 2. Motivation
Nostr users commonly maintain more than one identity:
- A personal identity
- A business or project identity
- An anonymous or pseudonymous identity
- A test identity
- An identity dedicated to a specific application or community
Today, switching identities often requires logging out, re-entering credentials,
reloading extensions, or manually importing keys. BitLogin can make account
switching feel like an ordinary multi-profile application while preserving the
fact that each Nostr identity is independently owned and recoverable.
The product goal is:
> Add an account once, then switch identities deliberately and visibly.
## 3. Initial scope: independent accounts
Each remembered account remains cryptographically independent:
```text
Account A
├── login name + password
├── recovery phrase
├── credential capsule
├── recovery capsule
└── everyday Nostr identity
Account B
├── login name + password
├── recovery phrase
├── credential capsule
├── recovery capsule
└── everyday Nostr identity
```
Compromise or recovery of one account must not automatically expose another.
The local account switcher may remember encrypted or origin-scoped session
material for several accounts, but it must not merge their remote records or
recovery paths.
## 4. Future scope: identity collections
A later protocol version may support several operational Nostr identities under
one BitLogin recovery root:
```text
One recovery root
├── Personal identity
├── Business identity
└── Pseudonymous identity
```
That is a separate feature and requires explicit protocol design for:
- Identity creation and deletion
- Per-identity credential wrapping
- Recovery and identity rotation
- Connection Vault sharing or separation
- Cross-identity privacy
- Export and migration
The first account switcher must not imply that independently added accounts are
recoverable from one phrase.
## 5. Local account registry
The client may keep an origin-scoped local registry containing only the data
needed to render an account picker and locate the account's local session.
Example:
```json
{
"schema": "bitlogin.local-account-registry.v1",
"accounts": [
{
"local_account_id": "local-random-id",
"npub": "npub1...",
"display_name": "Adam",
"picture": "https://example.com/avatar.jpg",
"last_used_at": 1784750000,
"session_state": "unlocked"
}
],
"active_local_account_id": "local-random-id"
}
```
The registry must not store:
- Login passwords
- Recovery phrases
- Password-derived keys
- Recovery signing keys
- Plaintext Connection Vault credentials
- Plaintext private messages
Display metadata is a cache and may be rebuilt from the account's signed profile.
## 6. Session storage
Each account session must be namespaced by an opaque local account identifier.
A single global `currentSession` record is insufficient.
Conceptual IndexedDB layout:
```text
bitlogin
├── account-registry
├── sessions/<local-account-id-a>
├── sessions/<local-account-id-b>
└── active-account
```
Each session should contain only the minimum material already accepted by the
BitLogin session-persistence model, independently encrypted or protected by the
origin's storage boundary.
A local attacker with access to the browser profile may obtain every unlocked
or persisted account session. The interface must state this limitation plainly.
## 7. Account states
An account may be:
```text
unlocked usable immediately
locked remembered, but credentials must be entered
unavailable local metadata exists, but remote login failed
removed deleted from this device only
```
Removing an account from one device does not delete its relay capsules, rotate
its identity, or revoke sessions on other devices.
## 8. Switching behavior
Switching accounts must be an explicit state transition:
1. Pause subscriptions and relay cursors associated with the current account.
2. Clear in-memory references to the current account's private keys and decrypted
data from components that are not shared intentionally.
3. Activate the selected account's worker and session namespace.
4. Reconfigure the signer surface, relay lists, messaging state, Connection
Vault, application state, and entitlements for the selected identity.
5. Dispatch an account-change event to the host application.
6. Refresh all account-dependent user interface.
The interface must never leave one account's profile visible while another
account's signer is active.
## 9. Signer behavior
The single global `window.nostr` slot can represent only one active signer.
Therefore:
- Only the active account may own BitLogin's global signer surface.
- Switching accounts must replace or rebind that surface atomically.
- Element-scoped BitLogin instances should expose the active account explicitly.
- Host applications must receive an account-change event and discard cached
public keys, relay lists, permissions, and app state.
Suggested event:
```javascript
window.addEventListener("bitlogin-account-changed", event => {
console.log(event.detail.previousPubkey);
console.log(event.detail.currentPubkey);
});
```
No private key or credential is included in the event.
## 10. User interface
Suggested account picker:
```text
Accounts
● Adam
npub1abc...xyz
Current account
○ BitRoad
npub1def...uvw
Last used yesterday
○ Anonymous
npub1ghi...rst
Locked
[Add account] [Manage accounts]
```
Account-switch confirmation should be optional for ordinary switching but
required when an application has an operation awaiting signature or payment.
The active account must remain visibly identifiable in:
- Signer approval dialogs
- Wallet and storage permission dialogs
- Purchase and entitlement views
- Message composition
- Profile editing
## 11. Add-account flows
The client should support:
- Sign into an existing BitLogin account
- Recover an existing BitLogin account with its phrase
- Create a new independent BitLogin account
- Import an existing `nsec` and wrap it in a new BitLogin account
Adding an account must not silently log out or overwrite the current account.
## 12. Logout and removal
The interface should distinguish:
- **Lock account:** clear active in-memory secrets but retain the remembered
account and any permitted encrypted session state.
- **Log out account:** clear that account's persisted session from this device.
- **Remove account from device:** remove local registry metadata and session data.
- **Log out all accounts:** clear every local BitLogin session.
None of these operations revoke a Nostr private key already exported elsewhere.
## 13. Privacy requirements
BitLogin should avoid creating remote records that publicly enumerate all
accounts used on one device. The account registry is local by default.
Independent accounts must not share:
- Locator identities
- Recovery identities
- Connection Vault roots
- Relay queries on one authenticated relay connection
- Application permission grants
- Messaging cursors
Network timing may still correlate accounts used from the same device or IP.
Separate relay connections and privacy-preserving transport remain recommended.
## 14. Application API
Conceptual API:
```javascript
const accounts = await bitlogin.accounts.list();
await bitlogin.accounts.switch(accounts[1].localAccountId);
await bitlogin.accounts.lock(accounts[0].localAccountId);
await bitlogin.accounts.removeFromDevice(accounts[2].localAccountId);
```
The API must never return passwords, recovery phrases, private keys, or plaintext
Connection Vault records.
## 15. Acceptance criteria
The initial feature is complete when:
1. At least two independent BitLogin accounts can remain remembered on one
device.
2. Switching an unlocked account does not rerun Argon2id or fetch its capsule.
3. Locking one account does not lock or corrupt another account.
4. The signer, profile, relay configuration, messages, app state, connections,
and entitlements always correspond to the visibly active account.
5. A host app receives an explicit account-change notification.
6. Removing an account deletes only its local state.
7. `Log out all accounts` clears every persisted BitLogin session.
8. No remote event exposes the device's account list.
9. Cross-account storage keys, cursors, and relay connections remain isolated.
10. Browser refresh restores the previously active account when its session is
still available.
## 16. Recommended implementation order
1. Replace the single local session record with a namespaced session store.
2. Add an account registry and active-account pointer.
3. Add lock, logout, remove, and switch operations.
4. Make the crypto worker lifecycle account-aware.
5. Add host-application account-change events.
6. Add account-aware relay, messaging, app-state, entitlement, and Connection
Vault modules.
7. Add tests for rapid switching, reload restoration, and cross-account leakage.