The Passbox desktop app for Windows, macOS and Linux is a Tauri 2 shell around the web client of passboxnet/web: the window shows the web vault's build (app/dist in a checkout of passboxnet/web next to this repository), so the vault UI, the WebAssembly core and therefore all cryptography are exactly the same as in the browser. The Rust crate in src-tauri only adds desktop integration and contains no cryptographic code.
The vault itself (languages, Emergency Kit, trash, security check, devices and so on) is described in web.md. The desktop app reports itself as "Passbox Desktop on …" in the device list.
Features
| Feature | How |
|---|---|
| Server of your choice | On first start the app asks for the server address (stored in localStorage as passbox.server). It can be changed on the sign-in screen under "Advanced" → "Use a different server" while signed out; plain http is only accepted for this computer. The server must allow cross-origin requests, which passbox-server does (see self-hosting.md). |
| Window | Title "Passbox", 1200 × 780 by default, at least 720 × 520. Size, position and maximized state are restored on the next start (tauri-plugin-window-state). The web client draws the Passbox Marina vault window (glass columns on the app canvas, see design/system.md). |
| Window material | Windows 11: the window is transparent with the Mica material (src-tauri/tauri.windows.conf.json, merged over tauri.conf.json on Windows); the web client detects Windows 11 (applyWindowMaterial in app/src/desktop.ts of passboxnet/web) and lets Mica show through the canvas while text stays on (nearly) solid surfaces. Windows 10, macOS and Linux get the solid canvas: macOS vibrancy would need Tauri's private macOS API (macOSPrivateApi), which we do not enable. Before the page loads, the window background is the dark Marina canvas (#0C1920). |
| Tray icon | The app icon with a menu: Open Passbox, Lock vault and Quit Passbox (German: „Passbox öffnen“, „Tresor sperren“, „Passbox beenden“). The labels follow the web client's language (set_tray_labels). Left click opens the window on Windows and macOS. Closing the window keeps Passbox running in the tray; quit from the tray menu. |
| Single instance | Starting Passbox again focuses the running window (tauri-plugin-single-instance). |
| Global shortcut | Ctrl+Shift+Space (Cmd+Shift+Space on macOS) shows the window and opens the command palette. If the vault has to lock first (see below), it shows the unlock screen instead. If another app owns the shortcut, Passbox starts without it. |
| Lock on close, sleep and screen lock | See below. |
| Auto-lock | The web client's inactivity timer (Settings → Security) applies as in the browser. While the window is hidden the shell enforces it as well, because hidden webviews throttle timers. |
| Clipboard | Copied secrets are cleared after the delay set in Settings → Security, also while the window is hidden, and when the vault locks or Passbox quits. They are marked to stay out of clipboard history. See below. |
Locking
The shell sends a passbox://lock event and the web client calls client.lock(), which removes all keys and decrypted data from memory, and answers with the vault_locked command. Every lock also clears a copied secret from the clipboard. This happens on:
- Tray menu → Lock vault, on every platform.
- Closing the window, when Lock when the window is closed is on (Settings → Security, desktop app only, on by default).
- Showing the window again (global shortcut, tray, starting Passbox again, Dock icon) after it was closed with that setting on, or after it was hidden for longer than the auto-lock time. The shell then sends the lock first and shows the window only after
vault_lockedarrives, so it appears with the unlock screen (after 2 seconds without an answer it shows the window anyway). The shell also locks by itself as soon as the hidden time exceeds the auto-lock time. The web client passes both settings withset_lock_settings; until it does, lock on close is on. - Sleep and hibernation, on every platform. A background thread wakes every 5 seconds; threads do not run while the computer is suspended, so a jump of the wall clock by more than 35 seconds means the computer slept. (Setting the clock forward also locks, which is harmless.) The lock happens right after waking, before anyone can use the window.
- Screen lock and suspend on Linux, through D-Bus:
ActiveChanged(true)of the screen saver interfaces of GNOME, KDE (org.freedesktop.ScreenSaver), Cinnamon and MATE on the session bus, andSession.LockandPrepareForSleep(true)from systemd-logind on the system bus. - Screen lock and suspend on Windows: a hidden window registered with
WTSRegisterSessionNotificationreceivesWM_WTSSESSION_CHANGEwithWTS_SESSION_LOCK(Win+L, lock screen, switching users), andWM_POWERBROADCASTwithPBT_APMSUSPENDbefore the computer sleeps. - Screen lock, sleep and user switching on macOS: the distributed notification
com.apple.screenIsLocked, andNSWorkspaceWillSleepNotificationandNSWorkspaceSessionDidResignActiveNotification.
The Windows and macOS watchers use native FFI. The desktop crate therefore denies unsafe (instead of forbidding it like the other Passbox crates) and allows it only in those two modules of src-tauri/src/lock_watch.rs.
In the web client (browser and desktop), the auto-lock timer also re-checks the inactivity time immediately on visibilitychange, focus and pageshow, and before an interaction counts as activity, so a vault that expired while the page was hidden or the computer slept locks before it can be used.
Clipboard
In browsers the web client cannot read the clipboard without a permission prompt, and may only write to it while the page has focus. It therefore remembers the secret it copied and writes an empty string when the delay is over, unless text was copied or cut in the page since. If the page is in the background then, clearing waits for the next focus or visibilitychange; the toast says so. Locking clears right away (or as soon as the page has focus again).
In the desktop app the web client copies through the shell (clipboard_copy, clipboard_clear), which uses arboard in Rust. The shell clears the clipboard after the delay if it still holds the copied secret, also while the window is hidden, and on every lock and on quit. The secret is marked so clipboard history tools skip it:
| Platform | Marker |
|---|---|
| Windows | ExcludeClipboardContentFromMonitorProcessing: not in clipboard history (Win+V), not synced to the cloud clipboard, ignored by clipboard monitors |
| macOS | org.nspasteboard.ConcealedType (nspasteboard.org), honored by most clipboard managers |
| Linux | x-kde-passwordManagerHint, honored by KDE Klipper and most other clipboard managers |
Bridge to the web client
app/src/desktop.ts in passboxnet/web is the only place in the web client that knows about the desktop app. It detects Tauri with "__TAURI_INTERNALS__" in window, loads @tauri-apps/api lazily, and is a no-op in browsers. Events:
| Event | Sent when | Web client |
|---|---|---|
passbox://lock | Tray "Lock", closing or reopening the window (see above), sleep, screen lock | client.lock(), then vault_locked |
passbox://command-palette | Global shortcut | Opens the command palette (when unlocked) |
Commands the web client calls:
| Command | Arguments | Purpose |
|---|---|---|
clipboard_copy | text, clearAfterSecs | Copies a secret and clears it later |
clipboard_clear | none | Clears a copied secret now (the web client calls it whenever the vault locks) |
vault_locked | none | Confirms a lock request; shows a window that waits for it |
set_lock_settings | lockOnClose, autoLockMinutes | Lock settings for the hidden window |
set_tray_labels | open, lock, quit | Tray menu labels in the app's language (trimmed, at most 60 characters each) |
The desktop app does not import the web client's code: it only references its build output (frontendDist) and its dev server (devUrl).
Security
- Content Security Policy (
tauri.conf.json): scripts only from the app itself plus'wasm-unsafe-eval'for the WebAssembly core, no remote scripts, no frames, workers, objects or form targets. Fonts and images come from the app ordata:URIs (Vite inlines small files).connect-srcallowshttps:,http://localhost:*andhttp://127.0.0.1:*, because the server is chosen at runtime and cannot be listed statically. Plainhttp://servers on other hosts are rejected by the app.style-srcallows inline styles (React style attributes); Tauri is told not to add nonces tostyle-src, which would disable that. - Capabilities (
src-tauri/capabilities/main.json): the page may listen to events (core:event:allow-listen,core:event:allow-unlisten) and call the app commands above (allow-clipboard-copy,allow-clipboard-clear,allow-vault-locked,allow-set-lock-settings,allow-agent-request-attention,allow-breach-notify,allow-set-tray-labels).build.rsdeclares the commands in the app manifest, so any other command is denied; the generated permission files insrc-tauri/permissions/autogeneratedare not checked in. Tray, shortcut and window state are handled in Rust and not exposed to JavaScript. Remote pages get no capabilities at all. - Session tokens stay in memory as in the browser; the account (email, Secret Key, KDF parameters) is stored in the webview's
localStorage, like in the browser.