diff --git a/docs/relay-and-messaging-portability.md b/docs/relay-and-messaging-portability.md new file mode 100644 index 0000000..e96b0cf --- /dev/null +++ b/docs/relay-and-messaging-portability.md @@ -0,0 +1,423 @@ +# BitLogin Relay and Messaging Portability + +**Status:** Experimental design +**Document version:** 0.1 +**Date:** July 22, 2026 +**Implementation status:** Partially planned in the core specification; not implemented as a complete user-facing system + +## 1. Summary + +BitLogin should make relay configuration and private-message synchronization +portable, understandable, and recoverable across devices. + +The feature covers: + +- General relay preferences +- DM inbox relay preferences +- DM outbox and sent-message recovery +- Relay role separation +- Relay health and redundancy checks +- Per-account message cursors and encrypted local indexes +- Conversation reconstruction on a clean device + +The goal is not to invent a new messaging protocol. It is to provide one +coherent account-level implementation that compatible applications can reuse. + +## 2. Motivation + +Nostr applications often implement relay selection and messaging independently. +That creates several user-facing problems: + +- The same account behaves differently in different apps. +- Users repeatedly configure the same relays. +- Private messages may appear on one device but not another. +- Sent messages may be missing after switching clients. +- Users cannot easily tell which relays are used for public posts, DMs, vault + records, or discovery. +- A single unavailable relay can silently make an account appear empty. + +BitLogin already has a portable identity and relay-backed recovery model. It is +a natural place to normalize relay and messaging behavior for compatible apps. + +## 3. Relay roles + +BitLogin should distinguish relay roles instead of presenting one undifferentiated +list. + +```text +General relays + Public profile, follows, notes, and ordinary events + +DM inbox relays + Receive encrypted messages addressed to the user + +DM outbox relays + Store or discover sent-message copies and delivery records + +Discovery relays + Locate relay lists and other account bootstrap records + +Vault relays + Store BitLogin capsules and encrypted portable records + +Entitlement relays + Optional source for signed purchase and access records +``` + +A relay may serve more than one role, but the interface and internal state must +record those roles explicitly. + +## 4. Canonical relay preferences + +The user's signed Nostr relay-list events remain the canonical public source for +ordinary and DM relay preferences where standards define them. + +BitLogin should: + +1. Retrieve the latest valid signed relay-list events. +2. Validate event signatures and timestamps. +3. Merge them with local bootstrap and vault requirements without overwriting + unrelated user choices. +4. Republish only when the user confirms a change or when a defined repair flow + is required. +5. Preserve signed copies in encrypted recovery exports. + +BitLogin-specific relay roles that do not have a standard public event should be +stored in encrypted, independently replaceable account records rather than the +core capsules whenever practical. + +## 5. Relay settings interface + +Suggested interface: + +```text +Relays + +Public activity + relay-one.example Connected + relay-two.example Slow + +Private messages + inbox-one.example Connected + inbox-two.example Unreachable + +BitLogin vault + vault-one.example Healthy + vault-two.example Healthy + vault-three.example Healthy + +[Add relay] [Test all] [Advanced] +``` + +For each relay, show: + +- URL +- Assigned roles +- Connection status +- Last successful read +- Last successful write +- Authentication requirement +- Advertised limits when available +- Whether it is user-selected, discovered, or a BitLogin bootstrap relay + +The interface should explain consequences in ordinary language, such as: + +> You currently have only one reachable private-message inbox relay. If it goes +> offline, new messages may not reach this account until another inbox relay is +> added. + +## 6. Relay health + +BitLogin may run lightweight health checks while the account is active. + +Checks may include: + +- WebSocket connection success +- Read subscription success +- Auth challenge handling +- Write acknowledgement +- Read-after-write verification for BitLogin-owned records +- Advertised event-size limits +- Last observed latency + +Health results are advisory and time-sensitive. A relay marked healthy can still +lose events or become unavailable later. + +BitLogin must not continuously publish test events to public relays without user +consent. Read-only checks should be preferred where possible. + +## 7. Messaging architecture + +Conceptual flow: + +```text +Signed relay preferences + | + +-- DM inbox relay list + +-- general/outbox discovery + | +BitLogin messaging synchronizer + +-- downloads encrypted incoming messages + +-- downloads sent-message copies + +-- validates and decrypts locally + +-- reconstructs conversations + `-- updates encrypted local index +``` + +Sensitive message plaintext must remain local to the active account session. + +## 8. Inbox synchronization + +The inbox synchronizer should: + +1. Determine the active account's current DM inbox relays. +2. Open account-scoped relay connections. +3. Fetch encrypted messages using persisted cursors and a bounded overlap window. +4. Validate event IDs, signatures, wrappers, and recipient binding. +5. Decrypt messages locally. +6. Deduplicate copies received from several relays. +7. Store an encrypted local message index. +8. Advance cursors only after durable local processing. + +A message received from multiple relays is one logical message, not several +conversation entries. + +## 9. Outbox and sent-message recovery + +A portable inbox alone is insufficient. Users expect messages they sent on one +device to appear on another. + +BitLogin should support sent-message recovery through the applicable messaging +protocol's sender-copy or outbox mechanism. + +The synchronizer should recover: + +- The sender's encrypted copy of outgoing messages +- Delivery metadata required to reconstruct threads +- Reactions or replies when supported +- Message timestamps and event IDs + +BitLogin must not fabricate a sent-message history from recipient copies that the +user cannot decrypt or verify. + +## 10. Conversation reconstruction + +The local conversation model should be derived from validated message events. + +Example internal record: + +```json +{ + "conversation_id": "derived-local-id", + "participants": ["", ""], + "last_message_at": 1784750000, + "unread_count": 2, + "message_ids": ["", ""] +} +``` + +The local index is a cache and search aid. Signed encrypted events remain the +portable source material. + +Conversation identifiers should not be published publicly unless required by a +standard protocol. + +## 11. Local encrypted index + +The message index may include: + +- Event ID +- Conversation membership +- Sender and recipient pubkeys +- Decrypted timestamp +- Delivery status +- Unread state +- Search tokens or local full-text index +- Relay source metadata + +The index must be encrypted at rest or protected within the established BitLogin +session-storage model. It must be namespaced by account. + +Plaintext message bodies must never be included in analytics, logs, exceptions, +or unencrypted browser storage. + +## 12. Read state and application interoperability + +Read/unread state is application data rather than cryptographic message truth. +BitLogin may synchronize it as portable application state. + +Suggested records: + +```text +messaging/read-cursor/ +messaging/archive-state/ +messaging/mute-state/ +``` + +Compatible apps may choose to use BitLogin's normalized state or keep their own. +The user should be told when read state is device-local rather than portable. + +## 13. Application API + +Conceptual API: + +```javascript +const relayConfig = await bitlogin.relays.getConfiguration(); + +const conversations = await bitlogin.messaging.listConversations({ + limit: 50 +}); + +const messages = await bitlogin.messaging.getMessages({ + conversationId: conversations[0].id, + limit: 100 +}); + +await bitlogin.messaging.send({ + recipients: [""], + plaintext: "Hello" +}); +``` + +A host application should not need direct access to the everyday private key or +raw decrypted index database. + +## 14. Permissions + +Suggested scopes: + +```text +relays:read +relays:manage +messages:list +messages:read +messages:send +messages:manage-state +``` + +A permission dialog should explain whether an app can: + +- See relay URLs +- Read conversation metadata +- Read message contents +- Send messages as the user +- Change relay preferences + +Reading relay configuration should not automatically grant access to private +messages. + +## 15. Account switching + +Every relay connection, cursor, message index, and permission must be scoped to +the active account. + +Switching accounts must: + +1. Stop or suspend subscriptions for the previous account. +2. Clear previous-account plaintext from visible components. +3. Activate the selected account's relay configuration. +4. Open new account-scoped subscriptions. +5. Switch to the selected account's encrypted message index. +6. Notify host applications that all cached messaging objects are stale. + +The interface must never display one account's conversation while another +account's signer is active. + +## 16. Recovery and clean-device behavior + +On a clean device, BitLogin should: + +1. Restore the everyday identity through the password or recovery path. +2. Retrieve signed general and DM relay preferences. +3. Restore BitLogin-specific encrypted relay-role records. +4. Connect to the configured inbox and outbox sources. +5. Download and validate encrypted messages. +6. Rebuild the encrypted local index. +7. Restore portable read state if enabled. + +The process may be incremental. The interface should distinguish: + +```text +Account unlocked +Relay settings restored +Messages syncing +Message history complete through +``` + +## 17. Export + +A full encrypted BitLogin export should include: + +- Latest signed relay-list events +- BitLogin-specific encrypted relay-role records +- Message events or a bounded encrypted message archive, according to user choice +- Relay cursors +- Encrypted local index metadata when useful +- Portable read and archive state + +The export should state whether it contains message ciphertext only or decrypted +message content. Decrypted export must require explicit confirmation. + +## 18. Failure handling + +BitLogin should surface specific conditions: + +- No reachable inbox relay +- Relay authentication failed +- Relay rejected event size +- Conflicting relay-list events +- Message decrypt failed +- Invalid signature or wrapper +- Cursor rollback or stale relay response +- Outbox history incomplete + +The client should continue using healthy relays when one fails and should not +silently discard a user's configured relay merely because it is temporarily +offline. + +## 19. Privacy and security + +Relay use can reveal social relationships, timing, IP address, and account +linkage even when message contents are encrypted. + +Implementations should: + +- Use separate connections for identity-capsule and everyday-identity traffic. +- Avoid querying all account roles over one relay connection when separation is + practical. +- Minimize unnecessary broad subscriptions. +- Avoid transmitting plaintext message content outside the local client. +- Keep per-account cursors isolated. +- Validate all event signatures and wrappers before indexing. +- Treat relay-provided metadata and limits as untrusted input. +- Make Tor, VPN, proxy, or privacy-relay support possible in future clients. + +BitLogin cannot prevent network observers from correlating traffic from the same +device or IP. + +## 20. Acceptance criteria + +The first complete relay and messaging layer is ready when: + +1. A user can view and manage relay roles in plain language. +2. General and DM relay preferences restore on a clean device. +3. Incoming encrypted messages synchronize from at least two relays. +4. Duplicate relay copies appear as one logical message. +5. Sent messages created on one device appear on another device. +6. Conversation history can be rebuilt from signed encrypted events. +7. Message and relay state remain isolated across account switching. +8. Relay failures are visible and do not block healthy alternatives. +9. App permissions distinguish relay access, message metadata, message content, + and sending authority. +10. No plaintext messages are stored in logs or unencrypted analytics. + +## 21. Recommended implementation order + +1. Build the relay-role data model and dashboard. +2. Complete signed relay-list restoration and safe editing. +3. Implement account-scoped inbox synchronization and deduplication. +4. Add sent-message recovery and conversation reconstruction. +5. Add encrypted local indexing and search. +6. Add portable read/archive state through application-state records. +7. Expose a permission-scoped messaging API. +8. Integrate account switching, export, and relay-health diagnostics.