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
2 changes: 1 addition & 1 deletion crates/policy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ fn guesses_log10(entropy: &zxcvbn::Entropy) -> f64 {
(orderings + each).min(entropy.guesses_log10())
}

/// 0-4 for the strength meter, in even steps up to the bar: 4 means the estimate reaches
/// 0-4, in even steps up to the bar: 4 means the estimate reaches
/// 10^16. (zxcvbn's own scale gives 4 from 10^10, which would show full marks for passwords
/// the gate refuses.)
fn score_for(guesses_log10: f64) -> u8 {
Expand Down
2 changes: 1 addition & 1 deletion crates/session/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ impl Session {
pub fn failed_attempts(&self) -> u32 {
self.failures
}
/// True after 3+ consecutive failures ("Forgot it? Use your Recovery Kit").
/// True after 3+ consecutive failures ("Forgot it? Use your Recovery Key").
pub fn show_recovery_hint(&self) -> bool {
self.failures >= FREE_ATTEMPTS
}
Expand Down
5 changes: 3 additions & 2 deletions docs/capture-test.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Screen-capture test checklist (ADR-0002)

Re-run after every macOS or meeting-app update. Use a **release build** (`npx tauri build`; release builds have no screenshot override), open it on a **normal desktop, not a full-screen Space**, and keep it on the welcome or lock screen. A test **passes** when the capture shows whatever is *behind* the encrypted-note window, or a blank or black box, and never the app's contents.
Re-run after every macOS or meeting-app update. Use a **release build** (`npm run tauri -- build -- --locked`; release builds have no screenshot override), open it on a **normal desktop, not a full-screen Space**, and keep it on the welcome or lock screen. A test **passes** when the capture shows whatever is *behind* the encrypted-note window, or a blank or black box, and never the app's contents.

Quick check, run from a terminal that has Screen Recording permission. It passes when macOS refuses the window capture or the captured pixels are uniform. A nonuniform capture requires manual inspection and exits with status 2; file size alone can't tell whether pixels are the app or the desktop behind it. In that case the script keeps the captured image and prints its path, so you can look at it; delete it afterwards (the command is printed too).

Expand Down Expand Up @@ -36,6 +36,7 @@ macOS 26.4.1 · Zoom 7.0.6 · Discord 0.0.413 · OBS 32.1.2 · Chrome 153 · Qui

| Capture path | How to test | Result |
|---|---|---|
| Spotlight clipboard history | turn it on, copy a fake Login password from a fake-data Vault, check the list | ⬜ |
| Window server flag | automatic (window server reports `kCGWindowSharingState`) | ✅ 0 = excluded (2026-09-24) |
| ScreenCaptureKit window capture | an automation tool captured the release window | ✅ blank (2026-09-24) |
| Sharing Guard | open Zoom / Discord / QuickTime while unlocked | ✅ shield named Discord + QuickTime Player (2026-09-23) |
Expand Down Expand Up @@ -115,7 +116,7 @@ Use a fake-data Vault and fake values only. The steps come from the Windows chec
| Recovery Kit retype box during a Retry long enough to say Please wait… | The typed text masks, then unmasks | pending |
| Show in File Explorer | The Vault folder opens | pending |
| A fake value containing `&` | It shows as typed in the panel | pending |
| Panels at 100%, 125%, 150% and 200% scaling | No cut-off text. Record the System font's height at each; a height above 20 px means the panels need scaling work | pending |
| Panels at 100%, 125%, 150% and 200% scaling | No cut-off text. Record the System font's height at each; a height above 20 px means the panels need scaling work. A long fake readout scrolls to its last character | pending |

### Mac ↔ Windows Backup round trip

Expand Down
4 changes: 3 additions & 1 deletion docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Your Notes are still in the Vault file, and the Emergency Reader reads it withou

If the Locked screen keeps saying encrypted-note couldn't read or write a file, or that the file is far larger than any Vault: quit the app and move `vault.enote` out of that folder. Keep it; don't delete it. Reopen the app: it offers your Safety Copies and "I already have a Vault → Open a Backup".

**Read them with the Emergency Reader.** It works on a Vault, a Backup or a Safety Copy. It asks for the Master Password without showing what you type (add `--recovery-key` to use the Recovery Key instead), prints to the terminal and writes nothing to disk.
**Read them with the Emergency Reader.** It works on a Vault, a Backup or a Safety Copy. It asks for the Master Password without showing what you type (add `--recovery-key` to use the Recovery Key instead), prints to the terminal and writes nothing to disk. Your terminal keeps what it prints and may save it.

```bash
cargo run --locked -p enote-reader -- <file> # list every Note with a number; Hidden Fields stay as dots
Expand All @@ -105,6 +105,8 @@ cargo run --locked -p enote-reader -- <file> --reveal # every Note, wi

Give the reader a file, such as `vault.enote`, or a Safety Copy inside the `safety` folder. Don't give it the folder. Run these from a current copy of this project's source, with Rust installed. The first build may need internet, to download the exact versions listed in `Cargo.lock`. Keep the quotes, because the Mac path has a space in it. Don't write `~` inside quotes: the shell won't expand it.

**Do this once, now, while everything works:** run `cargo build --locked --release -p enote-reader`, then copy `target/release/enote-reader` and this guide next to your Backup. Replace it after updating the app. No Rust? [FORMAT.md section 11](FORMAT.md#11-reference-decryptor-python) has a Python decryptor.

Mac, the built app:

```bash
Expand Down
4 changes: 2 additions & 2 deletions docs/native-credential-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Ordinary Note entry/checks, including newly entered Wallet Hidden Fields, remain

## Validation still required

Fake-data core/coordinator tests cover creation and Recovery Kit confirmation, both unlock kinds, recovery reset, Wallet mutations/readouts, both password-change modes, copy inspection, Auto-lock weakening/tightening and stale/expired/cancelled operations. Browser fixture checks cover non-credential IPC contracts and the migrated callers. The presence of these tests is not evidence of physical native behavior.
Fake-data core/coordinator tests cover creation and Recovery Kit confirmation, both unlock kinds, recovery reset, Wallet mutations/readouts, both password-change modes, copy inspection, Auto-lock weakening/tightening and stale/expired/cancelled operations. `npm run typecheck` checks WebView calls against `src/api.ts`; `/fixture.html` only draws screens. The presence of these tests is not evidence of physical native behavior.

Sharing Guard regression: with a detected sharing app running, create a fictional Vault, acknowledge the guard, and lock. The shield must appear before either native unlock route; after an explicit acknowledgement, Master Password unlock must open native controls. Cancelling leaves the Vault locked; a new or restarted sharing app must invalidate the pending native action. Repeat after restarting the app with the fictional Vault already present. A sharing warning arriving while already locked must not move focus to its acknowledgement button.

Expand All @@ -65,4 +65,4 @@ Windows checks on a real PC, with fake values: Create and Unlock panels stay ope

Touch ID, in addition to its own checklist: a closed lid and a disconnected Touch ID keyboard should each keep Touch ID on. That rests on Apple's documented error codes and hasn't been seen on hardware.

Run the current local app on real macOS and Windows for complete create/recovery/Wallet/copy/rotation/settings flows, native keyboard behavior, VoiceOver/Narrator, held Space/button release, long/control-character readouts, lock/switch/sleep races and first-frame capture behavior. Inspect only sanitized outcomes and fake fixture markers when checking IPC/events/logs; real credential or Wallet contents must not enter reports. Existing capture results from an older UI do not validate the new native controls. Touch ID has its own [physical acceptance checklist](quick-unlock-design.md#boundaries-and-acceptance-still-pending).
Run the current local app on real macOS and Windows for complete create/recovery/Wallet/copy/rotation/settings flows, the Agent Companion's Start dialog on Mac (Return and Escape cancel, and the Vault stays Unlocked while it's open), native keyboard behavior, VoiceOver/Narrator, held Space/button release, long/control-character readouts, lock/switch/sleep races and first-frame capture behavior. Inspect only sanitized outcomes and fake fixture markers when checking IPC/events/logs; real credential or Wallet contents must not enter reports. Existing capture results from an older UI do not validate the new native controls. Touch ID has its own [physical acceptance checklist](quick-unlock-design.md#boundaries-and-acceptance-still-pending).
4 changes: 2 additions & 2 deletions docs/security-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
| A stolen laptop or USB stick (Argon2id + XChaCha20-Poly1305, strong password required) | Malware already running on your computer (keyloggers, memory readers) |
| Someone at your unlocked computer (Auto-lock, re-entering the password for wallets, hold-to-show for Wallet values) | A phone camera pointed at your screen, or someone watching it while you choose to show what you typed |
| Accidentally showing Hidden Fields while screen sharing (layered, see below) | A hacked operating system, or being forced to unlock |
| Clipboard history and Universal/cloud clipboard (copies are marked private, which third-party clipboard managers honor by convention, and cleared after 30 s, or 60 s if you choose; Wallet Kinds always 30 s). A password you paste from another app gets none of this protection: this app can't mark it or clear it | Old copies of your Vault: they still open with the old password |
| Clipboard history and Universal/cloud clipboard (copies are marked private, which clipboard tools, including OS history, aren't obliged to honor, and cleared after 30 s, or 60 s if you choose; Wallet Kinds always 30 s). A password you paste from another app gets none of this protection: this app can't mark it or clear it | Old copies of your Vault: they still open with the old password |

**Screen-capture hiding is best-effort on Mac.** Apple offers no API that guarantees it. Windows' exclusion is much stronger but still not a security boundary. That's why the app layers it with hidden-by-default fields and the Sharing Guard, which covers the window when known screen-sharing or recording apps are detected. See [ADR-0002](adr/0002-screen-capture-hiding-is-layered-and-best-effort-on-mac.md).

Expand All @@ -24,7 +24,7 @@
- **Quick Unlock on Mac.** The source implementation uses a biometric-gated Secure Enclave key to wrap only the Vault Key in an encrypted private local file. No account, server, Apple Developer profile or persistent Keychain item is part of this route. Native Master Password verification is required after app restart and every seven days; Touch ID does not extend that deadline, release a Wallet Key or erase password backoff. This file uses filesystem access controls rather than Keychain app access-group isolation: another process with the same user's file access may copy it and request its own protected operation outside the app's process policy. Deleting the current file does not erase backup copies. [Custody details and physical acceptance](quick-unlock-design.md).
- **Where things are stored.** See [Where your keys are stored](guide.md#where-your-keys-are-stored). The Finder or File Explorer window opened from Settings is outside Capture Hiding and shows file names, dates and a path containing the user name, never Note content.
- **Backups.** A Backup is just a copy of the Vault file you save wherever you like (e.g. an exFAT USB stick) and open on your other computer. There's no sync by design ([ADR-0001](adr/0001-local-only-vault-moved-by-hand.md)).
- **Emergency Reader.** `crates/reader` is a small command-line tool that reads a Vault or Backup even if the app stops working ([how to use it](guide.md#if-the-app-wont-open)).
- **Emergency Reader.** `crates/reader` is a small command-line tool that reads a Vault or Backup even if the app stops working ([how to use it](guide.md#if-the-app-wont-open)). It doesn't have the app's wiping allocator.
- **Optional Agent Companion.** A separate helper (an XPC service with its own App Sandbox container) can let one approved agent use one fixed OpenAI text tool with a separately created Agent Key. It can't open the Vault's container, receives nothing from the Vault, and stops on Stop, lock, sleep, grant expiry and quit. Its Agent Key is kept in an owner-only plain-text file and Node.js memory, not encrypted Vault storage ([ADR-0009](adr/0009-optional-agent-companion-helper.md), [guide](agent-companion.md)).
- **Vocabulary.** [CONTEXT.md](../CONTEXT.md) defines Vault, Note, Kind, Hidden Field, Recovery Kit and the rest.

Expand Down
Loading
Loading