Relay privacy over Tor, encrypted account backup, and a recoverable restore - #5
Open
anthonyonazure wants to merge 14 commits into
Open
anthonyonazure wants to merge 14 commits into
anthonyonazure wants to merge 14 commits into
Conversation
Relay traffic previously always used the clearnet, so relay operators could correlate the user's real IP with their pubkey, catalogue, and NIP-17 gift-wrap timing while only file transfers were protected. An opt-in setting now waits for the bundled Tor SOCKS listener and builds the nostr-sdk client with a proxy targeting all relays. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The webview CSP allowed img-src https: although no view renders remote images, leaving an open IP-leak channel past Tor for any future markup. The GitHub release check also raced the settings snapshot and always fetched clearnet; it now waits for the snapshot and uses only the local cache while relay privacy mode is on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Discovery previously ended at full-text search and random picks; the profile dialog showed name and npub with no way to see the rest of a seeder's music, although the aggregated remote catalogue already carries every source pubkey. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First-run users saw protocol jargon (NIP-17, seeder racing, file IDs, Connect Nostr) and dead-end empty panes that never explained that the catalogue only arrives after connecting. Status copy now leads with plain words and the search/details empty states tell the user what to do next depending on connection state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The result ranking was fixed to seeder count and the file-type select was a disabled placeholder, so a lossless-only collector could neither sort by size nor narrow a search to FLAC. Name, type, size, and seeder columns now toggle ascending/descending/default, and the existing format select filters both network and local searches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The shared view claimed 'Active peers: 0' for every file because the UI hardcoded the count and the backend never persisted completed uploads. Verified deliveries (peer-confirmed TransferComplete after a full send) are now counted per file in upload_stats, active capability grants are reported live, and the shared table shows both — the first signal a sharer gets that their music actually reached anyone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The player offered only stop, folder, and sequential all-tracks continuation; the queue machinery already existed, so shuffle keeps the current track first and randomises the rest of the active scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Withdrawing a catalogue entry only removes the owner's listing, but the UI never said so — sharers could believe removal makes a file disappear from the network. The shared view now counts distinct foreign seeders per own file from the aggregated remote catalogue and states the withdrawal reality next to the listing status. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What Napstr publishes was documented in the protocol but scattered across three one-line notes in the UI, and nothing told users that chat, catalogue, and profile hang off one linkable identity. The profile view now states both lists in plain words, including that kept downloads are re-shared publicly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A reinstall or new computer silently created a fresh identity and the published catalogue, chat history, and download negotiations lost their author, because the keyring was the only copy of the key. The profile view can now export the identity as a passphrase-encrypted ncryptsec file and restore from one, following the keys.justworks model: encryption happens locally and there is deliberately no recovery path, which the dialog states in plain words. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restoring a backup overwrote the keyring identity on the same click that submitted the passphrase. The dialog warned in prose, but never named the account being lost and gave no second chance, so a mistaken restore silently destroyed an identity that is unrecoverable by design. Split the operation. `inspect_identity_backup` decrypts only to report whose account the file holds and stores nothing; the destructive `import_identity_backup` now runs from a separate confirmation step that names both keys, and requires an explicit acknowledgement when an existing account would be lost. Restoring the account already in use, or restoring onto a fresh install, skips the warning because nothing is destroyed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…edgement A confirmation prompt only warns; it does not make the mistake survivable. Restoring a backup now archives the outgoing key to a second, dated keyring entry before the new one is written, so replacement is a move rather than a deletion. Replaced accounts appear under "Previous accounts on this computer" with a switch-back button, which itself archives the key it displaces, so switching in either direction destroys nothing. Exporting a backup records which account the file covers. The confirmation step reads that marker and states the true consequence: an account with its own backup file is merely being set aside, while an account that has never been exported will exist only in this computer's keychain afterwards. Each case requires ticking the matching "I understand" box before the button enables. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
….2.0 # Conflicts: # src-tauri/src/lib.rs # src-tauri/src/network.rs # src-tauri/src/transfer.rs # src/routes/+page.svelte
anthonyonazure
marked this pull request as draft
September 5, 2026 21:36
…ccounts Three defects found by driving the restore flow by hand. The dialog would not close after a successful restore. `closeBackupDialog` refuses to run while an operation is in flight, which is right for a stray backdrop click or Escape but wrong for completion, so the restore succeeded while the interface reported nothing. It now takes an explicit force argument that only the completion paths pass. The click handlers referenced the function directly, which would have passed the mouse event as that argument and forced a close mid-operation, so they now call it explicitly. The archived-account list never appeared. It loaded once at mount, before the first snapshot had established that a native bridge exists, so its own guard skipped it and nothing retried. It now waits for that snapshot. Restoring an account also blocked on reconnecting to every relay before returning, leaving the dialog on "Replacing…" for tens of seconds. The identity is already switched by then, so the reconnect now happens behind the returning call. The list also inherited the profile form's button indent, which pushed rows out of view, and carried no count to distinguish "no archived accounts" from "not loaded". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
anthonyonazure
marked this pull request as ready for review
September 5, 2026 21:45
This branch has not been deployed
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.
Attribution first
Ten of the twelve commits here are @BIMbeamFLX's work, from their
feat/relays-over-torbranch. They had not opened a PR, so this rebases their branch onto currentmainand adds two commits of my own on top. Authorship is preserved per commit.What it does
Relay privacy (@BIMbeamFLX). File transfers already go over Tor —
transfer.rsrefuses any offer without a valid v3 onion. What still went out in the clear were the Nostr relay connections, which let a relay operator tie a home IP to an identity key, its searches, and its published catalogue. This routes those through the bundled Tor as an opt-in mode, defaulting to off.It fails closed: if Tor will not start, the connection errors rather than silently falling back to clearnet. Two smaller leaks are closed alongside it — the GitHub update check becomes cache-only in privacy mode, and
img-srcno longer permits arbitrary remote hosts (nothing renders remote images today, so this costs nothing).Also from @BIMbeamFLX: encrypted account backup and restore via NIP-49, browsing a seeder's full shared library from their profile, sortable result columns, a working file-type filter, seeder and delivery counts, shuffle playback, and plainer status copy.
My two commits address one defect in that backup feature. Restoring a backup overwrote the keyring identity on the same click that submitted the passphrase. The dialog warned in prose, but never named the account being replaced and offered no second chance, so a mistaken restore destroyed an identity that is unrecoverable by design.
inspect_identity_backupdecrypts only to report whose account a file holds, storing nothing. The destructive import now runs from a separate confirmation step naming both keys.Merge notes
Rebasing across the 15 commits to v0.2.0 produced 19 conflicts. Four were substantive:
save_settingswas restructured upstream; the Tor setting is folded into the new shape.mergeSearchResults.Verification
svelte-checkclean across 158 files.cargo clippy --all-targets: 15 lints, every one of which reproduces verbatim onmain. This branch adds none.gitleaksclean;osv-scannerreports one fewer advisory thanmain, and the branch adds no new crates (only thenip49feature on the existingnostr-sdk).That manual pass found three defects in my own commits, now fixed in
b7caefd: the dialog would not close after a successful restore (its dismissal guard also blocked completion), the archived-account list loaded before the native bridge was ready and so stayed permanently empty, and restoring blocked on reconnecting to every relay before returning. None were reachable by the type checker or the test suite — all three were lifecycle and ordering faults that only surfaced by clicking. Worth knowing if you review the earlier commits in isolation.Still unverified: the "switch back" path from the archived-account list, and the wording of the two acknowledgement variants under a real screen reader.
🤖 Generated with Claude Code