Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion docs/identity.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,23 @@ events retry them, including a coalesced follow-up when an event arrives during
active pass. Upload success requires a matching stored receipt. Backup
failure never replaces or deletes the local identity.

The main WebView can observe the uploader through `identity.backup`, which
registers on first read and stays out of identity snapshots. Its status says
whether Builderlab backup can run in this build and whether the latest pass is
idle, running, complete, waiting for consent, a session or a key binding, or
failed with a named kind; no service response body reaches the renderer. A login
handoff or retry resolves to the number of the pass it starts, or to none, so a
caller can wait for that pass's own outcome. A pass superseded by a key install
reports nothing; reloading the window mid-pass reports idle.

When secure storage confirms absence, native recovery commands support explicit
browser sign-in. The session and recovered keys remain native; the renderer sees
only validated public identities and attempt-scoped handles. Recovery prefers the
account's current associated identity. A previous identity requires explicit
selection and does not change the account association. Failed mapping requests,
unavailable endpoints, failed backups and an empty successful result remain distinct.
unavailable endpoints, an account refused recovery (outside a private
organization, or without Buzz access), failed backups and an empty successful
result remain distinct.
Installation uses the existing create-only secure store; cancel, reload, signout
or a competing create/import prevents stale installation.
Remembering recovered completion is best effort; retry-file failure does not block
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ fn main() {
"nsec_backup_available",
"nsec_backup_session",
"nsec_backup_retry",
"nsec_backup_state",
"nsec_backup_watch",
"nsec_restore_begin",
"nsec_restore_wait",
"nsec_restore_install",
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
"allow-nsec-backup-available",
"allow-nsec-backup-session",
"allow-nsec-backup-retry",
"allow-nsec-backup-state",
"allow-nsec-backup-watch",
"allow-nsec-restore-begin",
"allow-nsec-restore-wait",
"allow-nsec-restore-install",
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/browser_permissions_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ fn native_command_permissions_isolate_webviews() {
"nsec_backup_available",
"nsec_backup_session",
"nsec_backup_retry",
"nsec_backup_state",
"nsec_backup_watch",
"nsec_restore_begin",
"nsec_restore_wait",
"nsec_restore_install",
Expand Down
28 changes: 21 additions & 7 deletions src-tauri/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,19 +719,33 @@ pub struct IdentityHost(Arc<Mutex<Identity>>);
impl IdentityHost {
#[cfg(test)]
pub(crate) fn fixture() -> Self {
Self::fixture_in(State::Ready(Key(Zeroizing::new([1; 32]))))
}
/// No saved key, or a key that cannot be read (a signing-out host refuses every read).
#[cfg(test)]
pub(crate) fn fixture_without_key(readable: bool) -> Self {
Self::fixture_in(if readable {
State::Missing
} else {
State::Closed
})
}
#[cfg(test)]
fn fixture_in(state: State) -> Self {
Self(Arc::new(Mutex::new(Identity {
state: State::Ready(Key(Zeroizing::new([1; 32]))),
state,
store: Box::new(OsStore),
})))
}

/// The saved key's public key, or `None` when secure storage confirms none.
pub(crate) async fn saved_viewer(&self) -> Result<Option<String>> {
with_identity(self.clone(), Identity::restore).await
}
pub(crate) async fn viewer(&self) -> Result<String> {
with_identity(self.clone(), |identity| {
identity
.restore()?
.ok_or_else(|| "Set up your identity first".into())
})
.await
self.saved_viewer()
.await?
.ok_or_else(|| "Set up your identity first".into())
}

pub(crate) async fn require_missing(&self) -> Result<()> {
Expand Down
7 changes: 5 additions & 2 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ mod nsec_backup;
mod nsec_backup_choice;
mod nsec_backup_config;
use nsec_backup::{
nsec_backup_available, nsec_backup_retry, nsec_backup_session, nsec_restore_begin,
nsec_restore_cancel, nsec_restore_install, nsec_restore_wait, NsecBackupHost,
nsec_backup_available, nsec_backup_retry, nsec_backup_session, nsec_backup_state,
nsec_backup_watch, nsec_restore_begin, nsec_restore_cancel, nsec_restore_install,
nsec_restore_wait, NsecBackupHost,
};
use nsec_backup_choice::{nsec_backup_choice, nsec_backup_set_choice};
mod image_clipboard;
Expand Down Expand Up @@ -583,6 +584,8 @@ fn commands<R: tauri::Runtime>() -> impl Fn(tauri::ipc::Invoke<R>) -> bool + Sen
nsec_backup_available,
nsec_backup_session,
nsec_backup_retry,
nsec_backup_state,
nsec_backup_watch,
nsec_restore_begin,
nsec_restore_wait,
nsec_restore_install,
Expand Down
Loading
Loading