Repository navigation
feat: opt-in encrypted config backup - #81
Merged
Merged
Conversation
A bridge knows a phone by its identity pubkey and a phone mints fresh session keys on its own, so a new phone with the same identity needs only what the phone alone knows to carry on: each paired machine's relays, label, direct endpoints and new-session defaults, the settings and the quick prompts. `ConfigBackup` holds exactly that; everything a heartbeat re-sends, session keys and grants are left out. The backup is a NIP-78 kind 30078 event whose `d` tag is a sha256 of the identity and a context only this app uses, so it names no app on the relay and no other app's data can share it. Decoding is total and drops relays the phone may not dial; importing adds the machines the phone lacks, leaves the ones it has as they are, and takes the backup's settings and quick prompts. The backup relay is kept under its own key, apart from the settings the backup carries. Co-Authored-By: Claude Code <noreply@anthropic.com>
A subscription can now ask for addressable events by their `d` tag (`#d`), so a client fetching one of its own (say, a backup stored as application data) asks the relay for just that one instead of every event of the kind. Co-Authored-By: Claude Code <noreply@anthropic.com>
The phone can keep its configuration on a relay the user picks. The backup relay joins the set the transport dials, so it goes through Tor when that is on and answers the relay's AUTH with the identity, like every other relay. Its content is NIP-44 encrypted from the identity to the identity through the identity's signer (an on-device key or a NIP-55 signer app alike), so only that identity can read it; the relay sees the pubkey, the kind, when it was saved and an opaque d tag. Setting the relay looks there first. A backup found waits for the user to import it (merged into the phone) or keep this phone's, and nothing is saved meanwhile, so a new phone never overwrites the backup it is about to restore; with none there, this phone's is saved at once. After that a backup is saved half a minute after the last change to the machines, settings or quick prompts, only when what it holds changed, each one dated past the last. Turning it off can also send a NIP-09 deletion. The settings view carries where the backup stands. Co-Authored-By: Claude Code <noreply@anthropic.com>
The backup now holds the phone's session-key ring (secrets and expiries) and each machine's confirmed grant. A restored phone takes them while it has granted none of its own keys, so it reads what its bridges encrypt to those keys right away instead of asking the identity's signer to grant every bridge again. A phone that already granted its own keys keeps them, and a backed-up grant is restored only if it names a live key of the ring the phone ends up with. A session key never signs, so reading it cannot command a bridge, and the backup is NIP-44 encrypted to the identity, which reads every payload anyway. The ring's Debug output shows no secret. A rotation or prune of the ring now also schedules a backup. Co-Authored-By: Claude Code <noreply@anthropic.com>
Five intents (set the backup relay, import the backup found on it, keep this phone's configuration instead, back up now, turn it off with an optional deletion), the backup's relay, last save and status in the settings view, and nsec_of for a "show my key" on an on-device login. The Kotlin bindings are regenerated. Co-Authored-By: Claude Code <noreply@anthropic.com>
Settings gets a Backup page: off, it takes a relay; on, it says when it last saved, offers a save now and turning it off (with or without asking the relay to delete the backup); a backup already on the relay waits for Restore or Keep this phone's, the latter confirmed since it replaces the backup. Right after logging in with an existing key, a restore step offers to look on a relay before the app opens; Skip turns backup off again. Account can show and copy the nsec of a key kept on the phone, read off the main thread and copied as sensitive so the clipboard preview hides it. Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Merged
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.
Summary
An opt-in backup of the phone's setup, so a new or reinstalled phone with the same identity picks up where the old one left off.
dtag is a hash of the identity and an app-private context, so it names neither the app nor the content. The backup relay joins the relay set, so the Tor and NIP-42 rules apply to it.nsecof an on-device key, read off the main thread and copied as sensitive.Commits
#dfiltersUniffiBackupView,nsec_of; bindings regeneratedTest plan
cargo clippy --workspace --all-targets -D warnings,cargo test --workspacetestDebugUnitTest verifyPaparazziDebug(new goldens reviewed)🤖 Generated with Claude Code