basic-password-manager 0.2.3__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. basic_password_manager-0.3.0/PKG-INFO +251 -0
  2. basic_password_manager-0.3.0/README.md +236 -0
  3. basic_password_manager-0.3.0/basic_password_manager.egg-info/PKG-INFO +251 -0
  4. basic_password_manager-0.3.0/basic_password_manager.egg-info/entry_points.txt +2 -0
  5. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/pw_manager/cli.py +4 -5
  6. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/pw_manager/logo.py +44 -2
  7. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/pw_manager/tui.py +12 -2
  8. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/pyproject.toml +2 -2
  9. basic_password_manager-0.2.3/PKG-INFO +0 -214
  10. basic_password_manager-0.2.3/README.md +0 -199
  11. basic_password_manager-0.2.3/basic_password_manager.egg-info/PKG-INFO +0 -214
  12. basic_password_manager-0.2.3/basic_password_manager.egg-info/entry_points.txt +0 -2
  13. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/LICENSE +0 -0
  14. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/basic_password_manager.egg-info/SOURCES.txt +0 -0
  15. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/basic_password_manager.egg-info/dependency_links.txt +0 -0
  16. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/basic_password_manager.egg-info/requires.txt +0 -0
  17. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/basic_password_manager.egg-info/top_level.txt +0 -0
  18. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/pw_manager/__init__.py +0 -0
  19. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/pw_manager/crypto.py +0 -0
  20. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/pw_manager/vault.py +0 -0
  21. {basic_password_manager-0.2.3 → basic_password_manager-0.3.0}/setup.cfg +0 -0
@@ -0,0 +1,251 @@
1
+ Metadata-Version: 2.4
2
+ Name: basic-password-manager
3
+ Version: 0.3.0
4
+ Summary: The Paladin — a local encrypted password manager that lives in your terminal
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/Daisentaur/password-manager
7
+ Project-URL: Changelog, https://github.com/Daisentaur/password-manager/blob/main/CHANGELOG.md
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: argon2-cffi
12
+ Requires-Dist: cryptography
13
+ Requires-Dist: textual
14
+ Dynamic: license-file
15
+
16
+ <p align="center">
17
+ <img src="assets/logo.png" width="160" alt="The Paladin — a pixel-art knight" />
18
+ </p>
19
+
20
+ <h1 align="center">The Paladin</h1>
21
+
22
+ <p align="center"><em>Your passwords, guarded locally. No browser, no cloud, no mercy.</em></p>
23
+
24
+ A password manager that is one encrypted file on my own disk and a small
25
+ terminal app in front of it. No browser, no cloud, no daemon, no ai running
26
+ in the background, no company holding my secrets. One file, one master password,
27
+ that's the whole thing.
28
+
29
+ (It installs as `basic-password-manager` because the name came after the project.
30
+ It answers to `paladin` as is its name)
31
+
32
+ ![unlocking the vault](assets/unlock.gif)
33
+
34
+ ## Install
35
+
36
+ From PyPI ([pipx](https://pipx.pypa.io) keeps CLI tools out of each other's business):
37
+
38
+ ```bash
39
+ pipx install basic-password-manager # or: pip install basic-password-manager
40
+ paladin init
41
+ ```
42
+
43
+ From source:
44
+
45
+ ```bash
46
+ git clone https://github.com/Daisentaur/password-manager && cd password-manager
47
+ pipx install .
48
+ ```
49
+
50
+ Hacking on it: `python3 -m venv venv && ./venv/bin/pip install -e .`
51
+
52
+ ## The TUI
53
+
54
+ Run `paladin` with nothing after it and you get the full app: unlock, arrow
55
+ around, Enter copies the selected password to your clipboard. `n` adds,
56
+ `e` edits, `d` deletes (it asks first), `q` quits. Every key is listed in
57
+ the bottom bar so there's nothing to memorize.
58
+
59
+ The search forgives you. Press `/` and type whatever fragments you remember
60
+ — every word just has to appear *somewhere* in the entry, any field, any
61
+ order. It even searches inside the passwords themselves, for the day all
62
+ you remember is what you typed on that site (matches get highlighted in the
63
+ theme's accent, except password matches, which quietly show the row and
64
+ highlight nothing — passwords never get displayed, that's the deal).
65
+
66
+ ![searching the vault](assets/search.gif)
67
+
68
+ The rarer stuff lives in the command palette — `ctrl+p`, type a few letters,
69
+ done. Importing your browser's passwords and changing the master password
70
+ both live there.
71
+
72
+ ![the command palette](assets/palette.gif)
73
+
74
+ And themes. `t` opens the picker; the bundled ones — muted-slate (default),
75
+ dawn, matrix — are lifted with love from
76
+ [tuxedo](https://github.com/webstonehq/tuxedo)'s palettes, and all of
77
+ Textual's built-ins are in there too. Your pick sticks across runs. Even the
78
+ knight on the unlock screen dresses to match.
79
+
80
+ ![switching themes](assets/themes.gif)
81
+
82
+ ## The CLI
83
+
84
+ Same vault, no interface, for when you just want the thing:
85
+
86
+ | Command | What it does |
87
+ |---|---|
88
+ | `paladin init` | create a new empty vault |
89
+ | `paladin add github` | store an entry (prompts for username/password/notes) |
90
+ | `paladin add github --gen` | same, but it invents a strong random password for you |
91
+ | `paladin get github` | copy the password to the clipboard, show the username |
92
+ | `paladin edit github` | update an entry field by field (Enter keeps what's there) |
93
+ | `paladin passwd` | change the master password |
94
+ | `paladin ls` | list entry names |
95
+ | `paladin rm github` | delete an entry |
96
+ | `paladin find "dt bank"` | search names, usernames, notes — every word must match, any order |
97
+ | `paladin gen -l 32` | just print a random password |
98
+ | `paladin import passwords.csv` | import a browser CSV export (see below) |
99
+ | `paladin about` | meet the knight |
100
+
101
+ The vault lives at `~/.local/share/pw-manager/vault`. Point the `PW_VAULT`
102
+ environment variable somewhere else if you disagree.
103
+
104
+ The `notes` field takes anything you want kept secret next to the password —
105
+ recovery codes, PINs, the answer to "what was your first pet".
106
+
107
+ ### Leaving your browser's password manager
108
+
109
+ This is why the project exists, so here's the exit route:
110
+
111
+ 1. Export: Chrome → `chrome://password-manager/settings` → "Export passwords".
112
+ Firefox → `about:logins` → ⋯ menu → "Export passwords". Either way you get
113
+ a CSV.
114
+ 2. `paladin import passwords.csv`
115
+ 3. **Delete that CSV immediately** — it's every password you own, in
116
+ plaintext: `shred -u passwords.csv`
117
+ 4. Turn off password saving in the browser and delete what it stored.
118
+
119
+ ## How it works
120
+
121
+ Three layers, ~120 lines total, and I mean it about the reading order — this
122
+ project exists because I wanted to understand this stuff, and the code is
123
+ meant to be read:
124
+
125
+ ### 1. `crypto.py` — password → key, key → ciphertext
126
+
127
+ An AES key must be 32 unpredictable bytes; your master password is neither.
128
+ **Argon2id** bridges the gap: `derive_key(password, salt)` always returns the
129
+ same 32 bytes for the same inputs — that's what makes unlocking possible at
130
+ all. It's deliberately slow and memory-hungry (64 MiB *per guess*), so
131
+ someone who steals the vault file can't brute-force short passwords on a GPU
132
+ the way they could against a plain hash. The pause when you unlock? That's
133
+ the lock being hard to pick. I've learned to love it.
134
+
135
+ The **salt** is 16 random bytes stored *unencrypted* in the vault file, and
136
+ that's fine — it's not a secret. Its whole job is making your derived key
137
+ unique so precomputed attack tables are useless. (This took me a while to
138
+ truly believe. It's fine. Smarter people than me checked.)
139
+
140
+ Encryption is **AES-256-GCM**, which is *authenticated*: decrypt with the
141
+ wrong key, or decrypt a file where even one bit was flipped, and it fails
142
+ loudly instead of handing back plausible garbage. Every encryption uses a
143
+ fresh random 12-byte **nonce** (also stored unencrypted, also not a secret).
144
+ The one iron rule of GCM: a (key, nonce) pair must never repeat — which is
145
+ why it's random every single time.
146
+
147
+ ### 2. `vault.py` — the file format
148
+
149
+ ```
150
+ [4 bytes "PWV1"][16-byte salt][12-byte nonce][ciphertext...]
151
+ ```
152
+
153
+ The ciphertext is just your entries as JSON, encrypted. Load = read → derive
154
+ key → decrypt → `json.loads`. Save = the same backwards, with a fresh salt
155
+ and nonce, written to a temp file and atomically renamed so a crash
156
+ mid-write can't destroy the vault.
157
+
158
+ Your master password is stored nowhere. Not hashed, not hidden — nowhere.
159
+ "Wrong password" is just the decryption screaming, translated.
160
+
161
+ ### 3. `cli.py` and `tui.py` — the interfaces
162
+
163
+ Both are thin skins over the two modules above. The TUI (built with
164
+ [Textual](https://textual.textualize.io)) holds the decrypted entries in
165
+ memory for the session; the CLI re-derives the key per command.
166
+
167
+ Passwords go to the clipboard (`wl-copy`/`xclip`/`xsel`, whichever exists)
168
+ rather than the screen, so they don't sit in your terminal scrollback. One
169
+ deliberate asymmetry: CLI `find` does *not* search passwords, because a CLI
170
+ argument lands in your shell history forever — the TUI search box does,
171
+ because nothing you type there is logged anywhere.
172
+
173
+ ## Backups
174
+
175
+ Every save keeps the previous version as `vault.bak` next to the vault —
176
+ one-step undo for when a save goes wrong or you delete the wrong entry.
177
+
178
+ That covers mistakes, not the disk dying. For that: the vault is one file,
179
+ so copy it anywhere. Another disk, a USB stick, even somewhere you don't
180
+ trust — it's gibberish without the master password.
181
+
182
+ ```bash
183
+ cp ~/.local/share/pw-manager/vault /some/backup/location/
184
+ ```
185
+
186
+ ### Moving to a new machine (or another OS)
187
+
188
+ The vault file plus the master password in your head is the *complete*
189
+ system — the salt lives inside the file, nothing belongs to the machine.
190
+ Install the package on the new system, drop your vault at
191
+ `~/.local/share/pw-manager/vault` (don't run `init` — that's for brand-new
192
+ vaults only), and everything works. Dual-booting? Park the vault on a
193
+ partition both systems mount and point both at it:
194
+ `export PW_VAULT=/mnt/shared/vault`.
195
+
196
+ ## Troubleshooting
197
+
198
+ **"I forgot the master password."** The data is gone. Not "gone until
199
+ support resets it" — mathematically gone. That's not a bug, that's the
200
+ entire design: every "forgot password?" flow is a back door, and back doors
201
+ don't check IDs. Pick a long phrase you can't forget (a sentence beats
202
+ `Tr0ub4dor&3`), and know that backups protect you from losing the *file*,
203
+ never from forgetting the *word*.
204
+
205
+ **"wrong master password (or the vault file is corrupted)"** — 99% of the
206
+ time you typo'd it. If you're *certain* it's right, the file got damaged;
207
+ restore the automatic backup
208
+ (`cp ~/.local/share/pw-manager/vault.bak ~/.local/share/pw-manager/vault`)
209
+ or an off-machine copy.
210
+
211
+ **Deleted or overwrote an entry by mistake** — the state before your last
212
+ save is sitting in `vault.bak`; restore it as above. One generation only,
213
+ so do it before the next save.
214
+
215
+ **"not a vault file (bad magic bytes)"** — whatever's at the vault path
216
+ isn't ours (truncated, overwritten, or wrong path). Check `echo $PW_VAULT`
217
+ and restore from a backup.
218
+
219
+ **"no vault at ..."** — run `paladin init`, or `PW_VAULT` points somewhere
220
+ unexpected.
221
+
222
+ **Password prints instead of copying** — no clipboard tool found. Install
223
+ one: `sudo apt install wl-clipboard` (Wayland) or `xclip` (X11).
224
+
225
+ **Unlock feels slow (~1s)** — that's Argon2 doing its job. The delay *is*
226
+ the brute-force resistance. Per command, not per keystroke.
227
+
228
+ **`MemoryError` from Argon2** — the key derivation wants 64 MiB free;
229
+ something is eating your RAM.
230
+
231
+ **Import brought in junk** — browser CSVs export everything, including
232
+ accounts from 2013. `paladin rm <name>` the corpses, or prune the CSV first.
233
+
234
+ ## Honest limitations
235
+
236
+ I'd rather you know these than find them:
237
+
238
+ - No clipboard auto-clear yet: a copied password stays on the clipboard
239
+ until you copy over it. Copy something else when you're done.
240
+ - While a command runs, decrypted data briefly exists in process memory.
241
+ Malware already on your machine could read it — true of every password
242
+ manager ever made; the vault protects the file at rest, not a compromised
243
+ machine.
244
+ - Single file, no sync. Syncing is your problem for now (and honestly, `cp`
245
+ is a fine answer).
246
+
247
+ ## Running the checks
248
+
249
+ ```bash
250
+ ./venv/bin/python test_vault.py
251
+ ```
@@ -0,0 +1,236 @@
1
+ <p align="center">
2
+ <img src="assets/logo.png" width="160" alt="The Paladin — a pixel-art knight" />
3
+ </p>
4
+
5
+ <h1 align="center">The Paladin</h1>
6
+
7
+ <p align="center"><em>Your passwords, guarded locally. No browser, no cloud, no mercy.</em></p>
8
+
9
+ A password manager that is one encrypted file on my own disk and a small
10
+ terminal app in front of it. No browser, no cloud, no daemon, no ai running
11
+ in the background, no company holding my secrets. One file, one master password,
12
+ that's the whole thing.
13
+
14
+ (It installs as `basic-password-manager` because the name came after the project.
15
+ It answers to `paladin` as is its name)
16
+
17
+ ![unlocking the vault](assets/unlock.gif)
18
+
19
+ ## Install
20
+
21
+ From PyPI ([pipx](https://pipx.pypa.io) keeps CLI tools out of each other's business):
22
+
23
+ ```bash
24
+ pipx install basic-password-manager # or: pip install basic-password-manager
25
+ paladin init
26
+ ```
27
+
28
+ From source:
29
+
30
+ ```bash
31
+ git clone https://github.com/Daisentaur/password-manager && cd password-manager
32
+ pipx install .
33
+ ```
34
+
35
+ Hacking on it: `python3 -m venv venv && ./venv/bin/pip install -e .`
36
+
37
+ ## The TUI
38
+
39
+ Run `paladin` with nothing after it and you get the full app: unlock, arrow
40
+ around, Enter copies the selected password to your clipboard. `n` adds,
41
+ `e` edits, `d` deletes (it asks first), `q` quits. Every key is listed in
42
+ the bottom bar so there's nothing to memorize.
43
+
44
+ The search forgives you. Press `/` and type whatever fragments you remember
45
+ — every word just has to appear *somewhere* in the entry, any field, any
46
+ order. It even searches inside the passwords themselves, for the day all
47
+ you remember is what you typed on that site (matches get highlighted in the
48
+ theme's accent, except password matches, which quietly show the row and
49
+ highlight nothing — passwords never get displayed, that's the deal).
50
+
51
+ ![searching the vault](assets/search.gif)
52
+
53
+ The rarer stuff lives in the command palette — `ctrl+p`, type a few letters,
54
+ done. Importing your browser's passwords and changing the master password
55
+ both live there.
56
+
57
+ ![the command palette](assets/palette.gif)
58
+
59
+ And themes. `t` opens the picker; the bundled ones — muted-slate (default),
60
+ dawn, matrix — are lifted with love from
61
+ [tuxedo](https://github.com/webstonehq/tuxedo)'s palettes, and all of
62
+ Textual's built-ins are in there too. Your pick sticks across runs. Even the
63
+ knight on the unlock screen dresses to match.
64
+
65
+ ![switching themes](assets/themes.gif)
66
+
67
+ ## The CLI
68
+
69
+ Same vault, no interface, for when you just want the thing:
70
+
71
+ | Command | What it does |
72
+ |---|---|
73
+ | `paladin init` | create a new empty vault |
74
+ | `paladin add github` | store an entry (prompts for username/password/notes) |
75
+ | `paladin add github --gen` | same, but it invents a strong random password for you |
76
+ | `paladin get github` | copy the password to the clipboard, show the username |
77
+ | `paladin edit github` | update an entry field by field (Enter keeps what's there) |
78
+ | `paladin passwd` | change the master password |
79
+ | `paladin ls` | list entry names |
80
+ | `paladin rm github` | delete an entry |
81
+ | `paladin find "dt bank"` | search names, usernames, notes — every word must match, any order |
82
+ | `paladin gen -l 32` | just print a random password |
83
+ | `paladin import passwords.csv` | import a browser CSV export (see below) |
84
+ | `paladin about` | meet the knight |
85
+
86
+ The vault lives at `~/.local/share/pw-manager/vault`. Point the `PW_VAULT`
87
+ environment variable somewhere else if you disagree.
88
+
89
+ The `notes` field takes anything you want kept secret next to the password —
90
+ recovery codes, PINs, the answer to "what was your first pet".
91
+
92
+ ### Leaving your browser's password manager
93
+
94
+ This is why the project exists, so here's the exit route:
95
+
96
+ 1. Export: Chrome → `chrome://password-manager/settings` → "Export passwords".
97
+ Firefox → `about:logins` → ⋯ menu → "Export passwords". Either way you get
98
+ a CSV.
99
+ 2. `paladin import passwords.csv`
100
+ 3. **Delete that CSV immediately** — it's every password you own, in
101
+ plaintext: `shred -u passwords.csv`
102
+ 4. Turn off password saving in the browser and delete what it stored.
103
+
104
+ ## How it works
105
+
106
+ Three layers, ~120 lines total, and I mean it about the reading order — this
107
+ project exists because I wanted to understand this stuff, and the code is
108
+ meant to be read:
109
+
110
+ ### 1. `crypto.py` — password → key, key → ciphertext
111
+
112
+ An AES key must be 32 unpredictable bytes; your master password is neither.
113
+ **Argon2id** bridges the gap: `derive_key(password, salt)` always returns the
114
+ same 32 bytes for the same inputs — that's what makes unlocking possible at
115
+ all. It's deliberately slow and memory-hungry (64 MiB *per guess*), so
116
+ someone who steals the vault file can't brute-force short passwords on a GPU
117
+ the way they could against a plain hash. The pause when you unlock? That's
118
+ the lock being hard to pick. I've learned to love it.
119
+
120
+ The **salt** is 16 random bytes stored *unencrypted* in the vault file, and
121
+ that's fine — it's not a secret. Its whole job is making your derived key
122
+ unique so precomputed attack tables are useless. (This took me a while to
123
+ truly believe. It's fine. Smarter people than me checked.)
124
+
125
+ Encryption is **AES-256-GCM**, which is *authenticated*: decrypt with the
126
+ wrong key, or decrypt a file where even one bit was flipped, and it fails
127
+ loudly instead of handing back plausible garbage. Every encryption uses a
128
+ fresh random 12-byte **nonce** (also stored unencrypted, also not a secret).
129
+ The one iron rule of GCM: a (key, nonce) pair must never repeat — which is
130
+ why it's random every single time.
131
+
132
+ ### 2. `vault.py` — the file format
133
+
134
+ ```
135
+ [4 bytes "PWV1"][16-byte salt][12-byte nonce][ciphertext...]
136
+ ```
137
+
138
+ The ciphertext is just your entries as JSON, encrypted. Load = read → derive
139
+ key → decrypt → `json.loads`. Save = the same backwards, with a fresh salt
140
+ and nonce, written to a temp file and atomically renamed so a crash
141
+ mid-write can't destroy the vault.
142
+
143
+ Your master password is stored nowhere. Not hashed, not hidden — nowhere.
144
+ "Wrong password" is just the decryption screaming, translated.
145
+
146
+ ### 3. `cli.py` and `tui.py` — the interfaces
147
+
148
+ Both are thin skins over the two modules above. The TUI (built with
149
+ [Textual](https://textual.textualize.io)) holds the decrypted entries in
150
+ memory for the session; the CLI re-derives the key per command.
151
+
152
+ Passwords go to the clipboard (`wl-copy`/`xclip`/`xsel`, whichever exists)
153
+ rather than the screen, so they don't sit in your terminal scrollback. One
154
+ deliberate asymmetry: CLI `find` does *not* search passwords, because a CLI
155
+ argument lands in your shell history forever — the TUI search box does,
156
+ because nothing you type there is logged anywhere.
157
+
158
+ ## Backups
159
+
160
+ Every save keeps the previous version as `vault.bak` next to the vault —
161
+ one-step undo for when a save goes wrong or you delete the wrong entry.
162
+
163
+ That covers mistakes, not the disk dying. For that: the vault is one file,
164
+ so copy it anywhere. Another disk, a USB stick, even somewhere you don't
165
+ trust — it's gibberish without the master password.
166
+
167
+ ```bash
168
+ cp ~/.local/share/pw-manager/vault /some/backup/location/
169
+ ```
170
+
171
+ ### Moving to a new machine (or another OS)
172
+
173
+ The vault file plus the master password in your head is the *complete*
174
+ system — the salt lives inside the file, nothing belongs to the machine.
175
+ Install the package on the new system, drop your vault at
176
+ `~/.local/share/pw-manager/vault` (don't run `init` — that's for brand-new
177
+ vaults only), and everything works. Dual-booting? Park the vault on a
178
+ partition both systems mount and point both at it:
179
+ `export PW_VAULT=/mnt/shared/vault`.
180
+
181
+ ## Troubleshooting
182
+
183
+ **"I forgot the master password."** The data is gone. Not "gone until
184
+ support resets it" — mathematically gone. That's not a bug, that's the
185
+ entire design: every "forgot password?" flow is a back door, and back doors
186
+ don't check IDs. Pick a long phrase you can't forget (a sentence beats
187
+ `Tr0ub4dor&3`), and know that backups protect you from losing the *file*,
188
+ never from forgetting the *word*.
189
+
190
+ **"wrong master password (or the vault file is corrupted)"** — 99% of the
191
+ time you typo'd it. If you're *certain* it's right, the file got damaged;
192
+ restore the automatic backup
193
+ (`cp ~/.local/share/pw-manager/vault.bak ~/.local/share/pw-manager/vault`)
194
+ or an off-machine copy.
195
+
196
+ **Deleted or overwrote an entry by mistake** — the state before your last
197
+ save is sitting in `vault.bak`; restore it as above. One generation only,
198
+ so do it before the next save.
199
+
200
+ **"not a vault file (bad magic bytes)"** — whatever's at the vault path
201
+ isn't ours (truncated, overwritten, or wrong path). Check `echo $PW_VAULT`
202
+ and restore from a backup.
203
+
204
+ **"no vault at ..."** — run `paladin init`, or `PW_VAULT` points somewhere
205
+ unexpected.
206
+
207
+ **Password prints instead of copying** — no clipboard tool found. Install
208
+ one: `sudo apt install wl-clipboard` (Wayland) or `xclip` (X11).
209
+
210
+ **Unlock feels slow (~1s)** — that's Argon2 doing its job. The delay *is*
211
+ the brute-force resistance. Per command, not per keystroke.
212
+
213
+ **`MemoryError` from Argon2** — the key derivation wants 64 MiB free;
214
+ something is eating your RAM.
215
+
216
+ **Import brought in junk** — browser CSVs export everything, including
217
+ accounts from 2013. `paladin rm <name>` the corpses, or prune the CSV first.
218
+
219
+ ## Honest limitations
220
+
221
+ I'd rather you know these than find them:
222
+
223
+ - No clipboard auto-clear yet: a copied password stays on the clipboard
224
+ until you copy over it. Copy something else when you're done.
225
+ - While a command runs, decrypted data briefly exists in process memory.
226
+ Malware already on your machine could read it — true of every password
227
+ manager ever made; the vault protects the file at rest, not a compromised
228
+ machine.
229
+ - Single file, no sync. Syncing is your problem for now (and honestly, `cp`
230
+ is a fine answer).
231
+
232
+ ## Running the checks
233
+
234
+ ```bash
235
+ ./venv/bin/python test_vault.py
236
+ ```