mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
**Category:** new-feature **User Impact:** Users can create, download, and verify a password-protected backup of their private identity from desktop Settings. **Problem:** Buzz does not currently give signed-in users a Settings-based path to protect or validate their private identity independently of onboarding. **Solution:** Add a focused backup menu to the private-key row, keep encryption and verification local in Rust, and preserve completed encrypted backups briefly so native saves can be retried without repeating encryption. <details> <summary>File changes</summary> **desktop/src/features/settings/** Adds the background backup lifecycle, create and test dialogs, private-key menu integration, password handling, and focused unit coverage. **desktop/src/features/onboarding/ui/NsecMaskedDisplay.tsx** Extends the masked private-key display with reusable overflow-menu actions used by Settings. **desktop/src/app/App.tsx** Mounts the backup provider at app scope so encryption and save work survive closing Settings or the modal. **desktop/src/shared/api/tauriIdentity.ts** Adds typed desktop bindings for local backup creation, save, selection, and verification. **desktop/src-tauri/src/key_backup.rs and desktop/src-tauri/src/commands/identity.rs** Implements local NIP-49 encryption, password generation, file handling, and public-identity-only verification results. **desktop/src-tauri/src/egress_guard.rs and guarded call sites** Blocks encrypted secret material from relay, websocket, snapshot, sharing, and huddle egress paths. **desktop/src-tauri tests and fixtures** Covers encryption, verification, file behavior, and fail-closed no-egress protections. **desktop/src/testing/e2eBridge.ts, desktop/tests/, and desktop/playwright.config.ts** Expands the mock native bridge and browser coverage across create, retry, expiry, and current/different-identity verification states. **desktop/src-tauri/Cargo.toml, Cargo.lock, and assets** Adds the local cryptography/password-generation dependencies and embedded short-word list. </details> ## Reproduction steps 1. Run the desktop app and open **Settings → Profile → Identity**. 2. Open the private-key overflow menu and choose **Create backup**. 3. Enter or generate a valid password, submit, and confirm progress continues if the dialog or Settings is closed. 4. Save the resulting `.ncryptsec` file; cancel and retry to confirm the temporary download remains available. 5. Choose **Test backup**, select the file, enter a wrong password, then retry with the correct password. 6. Confirm success identifies whether the backup matches the current identity and displays only the public `npub`. ## Screenshots | Settings identity | Private-key menu | Create backup | |---|---|---| | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/981e391b-6829-4081-95ca-ca75a369de71" /> | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/7972c68e-7635-47d8-b0ad-9639390d3e6c" /> | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/4709c8f7-cf02-46f1-bec9-b3f98fe56fb2" /> | | Encrypting | Download available | Test success | |---|---|---| | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/1ac3e934-2b4b-4135-bae6-126c715c8c59" /> | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/cb6f07ee-a16f-44a5-b9a0-6b9fe0e4d40d" /> | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/ea58b1b1-966c-46aa-8d59-92c9f06a25bd" /> | Visual review and additional states: [Buzz thread](buzz://message?channel=50ca7ef1-201e-4159-9499-40de3964b7c3&id=87eceb5f0f82fd50c32e560de3d35be48e293760f6620718aafdcef289d475fe) --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
146 lines
7.0 KiB
TOML
146 lines
7.0 KiB
TOML
[workspace]
|
|
|
|
[package]
|
|
name = "buzz-desktop"
|
|
version = "0.5.2"
|
|
description = "Buzz desktop app"
|
|
authors = ["you"]
|
|
edition = "2021"
|
|
|
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
|
|
[lib]
|
|
# The `_lib` suffix may seem redundant but it is necessary
|
|
# to make the lib name unique and wouldn't conflict with the bin name.
|
|
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
|
name = "buzz_lib"
|
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
|
|
[features]
|
|
default = ["system-keyring"]
|
|
mesh-llm = ["dep:iroh", "dep:mesh-llm-sdk", "dep:mesh-llm-host-runtime", "dep:mesh-llm-client", "dep:mesh-llm-node", "dep:mesh-llm-system", "dep:mesh-llm-events"]
|
|
# OS keyring backing for desktop secret storage (nsec private keys). When
|
|
# disabled, secrets fall back to 0o600 files. On by default for real builds.
|
|
system-keyring = ["dep:keyring"]
|
|
|
|
[build-dependencies]
|
|
base64 = "0.22"
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
tauri-build = { version = "2", features = [] }
|
|
|
|
[target.'cfg(unix)'.dependencies]
|
|
libc = "0.2"
|
|
ctrlc = { version = "3", features = ["termination"] }
|
|
|
|
[target.'cfg(target_os = "linux")'.dependencies]
|
|
keyring = { version = "3.6.3", default-features = false, features = ["sync-secret-service", "vendored"], optional = true }
|
|
# Used directly (alongside tauri-plugin-notification) so we can hold the posting
|
|
# D-Bus connection open. GNOME 46+ dismisses a notification the moment that
|
|
# connection is dropped, which the plugin does immediately. Default features
|
|
# keep the pure-Rust zbus backend, matching the plugin (no libdbus needed).
|
|
notify-rust = "4"
|
|
# Enable getUserMedia in the WebKitGTK webview (see src/linux_media.rs). Pinned
|
|
# to the exact version wry links so both resolve to one webkit2gtk-sys and we
|
|
# don't get duplicate symbols; bump in lockstep with wry.
|
|
webkit2gtk = { version = "=2.0.2", features = ["v2_22"] }
|
|
|
|
[target.'cfg(target_os = "macos")'.dependencies]
|
|
objc2 = { version = "0.6.4", default-features = false }
|
|
objc2-app-kit = { version = "0.3.2", default-features = false, features = ["NSHapticFeedback", "NSMenu", "NSMenuItem", "NSStatusItem"] }
|
|
objc2-foundation = { version = "0.3.2", default-features = false, features = ["NSProcessInfo", "NSString"] }
|
|
keyring = { version = "3.6.3", default-features = false, features = ["apple-native", "vendored"], optional = true }
|
|
security-framework = { version = "3.7.0", features = ["OSX_10_15"] }
|
|
window-vibrancy = "0.6"
|
|
user-idle = { version = "0.6", default-features = false }
|
|
plist = "1"
|
|
|
|
[target.'cfg(windows)'.dependencies]
|
|
windows-sys = { version = "0.61", features = ["Win32_Security", "Win32_Storage_FileSystem", "Win32_System_JobObjects", "Win32_System_Registry", "Win32_System_Threading", "Win32_Foundation"] }
|
|
keyring = { version = "3.6.3", default-features = false, features = ["windows-native", "vendored"], optional = true }
|
|
user-idle = { version = "0.6", default-features = false }
|
|
|
|
[dependencies]
|
|
atomic-write-file = "0.3"
|
|
anyhow = "1"
|
|
dirs = "6"
|
|
tauri = { version = "2", features = ["macos-private-api", "tray-icon"] }
|
|
tauri-plugin-deep-link = "2"
|
|
tauri-plugin-opener = "2"
|
|
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
|
|
tauri-plugin-window-state = "2"
|
|
tauri-plugin-dialog = "2"
|
|
tauri-plugin-updater = "2"
|
|
tauri-plugin-process = "2"
|
|
infer = "0.19"
|
|
hex = "0.4"
|
|
ed25519-dalek = "=3.0.0-rc.0"
|
|
tokio = { version = "1", features = ["fs", "sync", "rt", "macros", "time", "net", "io-util"] }
|
|
tokio-tungstenite = { version = "0.29", features = ["rustls-tls-webpki-roots"] }
|
|
tokio-util = { version = "0.7", features = ["rt"] }
|
|
bytes = "1"
|
|
futures-util = "0.3"
|
|
opus = "0.3"
|
|
neteq = { version = "0.8", default-features = false }
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
serde_yaml = "0.9"
|
|
toml = "0.8"
|
|
nostr = { version = "0.44", features = ["nip44", "nip49"] }
|
|
# OS-entropy source for backup passphrase generation (already in the tree as a
|
|
# transitive dependency; pinned here for direct use).
|
|
getrandom = "0.2"
|
|
zeroize = "1"
|
|
reqwest = { version = "0.13", features = ["json", "query", "stream", "blocking"] }
|
|
rustls = { version = "0.23", default-features = false, features = ["aws_lc_rs", "std"] }
|
|
url = "2"
|
|
buzz_core_pkg = { package = "buzz-core", path = "../../crates/buzz-core" }
|
|
buzz_persona_pkg = { package = "buzz-persona", path = "../../crates/buzz-persona" }
|
|
buzz_sdk_pkg = { package = "buzz-sdk", path = "../../crates/buzz-sdk" }
|
|
buzz_agent_pkg = { package = "buzz-agent", path = "../../crates/buzz-agent" }
|
|
iroh = { version = "1.0.2", optional = true }
|
|
mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true }
|
|
mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true }
|
|
# Model catalog + hardware survey for the Share-compute model picker (same
|
|
# diagnose pattern as mesh-console). Lib name of mesh-llm-client is mesh_client.
|
|
mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-client", optional = true }
|
|
mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-node", optional = true }
|
|
mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-system", optional = true }
|
|
mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.74.0", package = "mesh-llm-events", optional = true }
|
|
base64 = "0.22"
|
|
sha2 = "0.11"
|
|
tar = "0.4"
|
|
bzip2 = "0.6"
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
tauri-plugin-global-shortcut = "2"
|
|
tauri-plugin-notification = "2.3.3"
|
|
uuid = { version = "1", features = ["v4", "v5"] }
|
|
png = "0.18"
|
|
# wayland-data-control: without it arboard is X11-only on Linux, so copies made
|
|
# in a Wayland session land in XWayland's clipboard where Wayland-native apps
|
|
# never see them (set_text still returns Ok). The backing wl-clipboard-rs dep is
|
|
# target-gated inside arboard; at runtime X11 sessions still fall back to X11.
|
|
arboard = { version = "3", features = ["wayland-data-control"] }
|
|
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp", "gif"] }
|
|
zip = "8"
|
|
flate2 = "1"
|
|
sherpa-onnx = "1.12"
|
|
regex = "1"
|
|
rusqlite = { version = "0.37", features = ["bundled"] }
|
|
axum = "0.8"
|
|
rodio = "0.22"
|
|
earshot = "1.0"
|
|
rubato = "3.0"
|
|
audioadapter-buffers = "3.0"
|
|
tempfile = "3"
|
|
strip-ansi-escapes = "0.2"
|
|
tracing = "0.1"
|
|
|
|
[dev-dependencies]
|
|
# `test-util` enables tokio's paused-clock (`start_paused`) so the relay
|
|
# admission gate tests can assert exact wait durations without real sleeps.
|
|
tokio = { version = "1", features = ["test-util"] }
|
|
# The relay's media validation, so the snapshot-sharing tests can prove the
|
|
# full export → sanitize → relay-accept → import contract end to end.
|
|
buzz_media_pkg = { package = "buzz-media", path = "../../crates/buzz-media" }
|