keebox 0.9.1 → 0.9.2

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.
package/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
  <p align="center">
21
21
  <a href="https://keebox.eissa.top">Website</a> ·
22
22
  <a href="#get-started">Get started</a> ·
23
- <a href="#commands-using-the-pass-alias">Commands</a> ·
23
+ <a href="#commands">Commands</a> ·
24
24
  <a href="#encryption">Security</a>
25
25
  </p>
26
26
 
@@ -35,7 +35,7 @@ Most password managers ask you to trust them with everything: your passwords sit
35
35
  - **Nothing in the cloud.** KeeBox never touches the network: no sync server to breach, no telemetry, no account to phish. Your encrypted vault only exists where you put it.
36
36
  - **Strong, standard encryption.** Every password, login and note is encrypted with AES-256-GCM. The key is locked behind your master password with scrypt, which makes guessing slow and expensive. Tampered or damaged entries are detected and refused, never silently decrypted into garbage.
37
37
  - **A recovery phrase, like a crypto wallet.** Lost your credentials file or forgot your master password? A 12-word recovery phrase gets you back in. Without the phrase or the password, nobody can open the vault, not even the author of KeeBox.
38
- - **Zero dependencies.** No `npm install` and no third-party packages, so there's no supply chain to compromise. The whole tool is about 1,300 lines of plain Node.js that you can read and audit in an afternoon.
38
+ - **Zero dependencies.** No third-party packages, so there's no supply chain to compromise. The whole tool is about 1,300 lines of plain Node.js that you can read and audit in an afternoon.
39
39
  - **Portable.** Copy the folder plus a Node.js binary to a USB drive and it runs on Linux, macOS, Windows and Android (Termux), no installation needed.
40
40
  - **Fast from the terminal.** Generate strong passwords, find accounts by any field, and copy a password to the clipboard. It's wiped automatically after 20 seconds.
41
41
  - **Yours to keep.** Export everything to CSV whenever you want, import accounts from CSV, and check your vault for damage. Open source and MIT licensed.
@@ -47,9 +47,22 @@ Most password managers ask you to trust them with everything: your passwords sit
47
47
  Requires Node.js 18+.
48
48
 
49
49
  ```
50
- git clone https://github.com/eissapk/keybox.git keebox
50
+ npm install -g keebox
51
+ keebox -h
52
+ ```
53
+
54
+ Your vault (`db.json` + `credentials.json`) is kept in `~/.keebox/`, outside the npm folder, so updating or uninstalling KeeBox never touches it. To keep it somewhere else (an encrypted drive, a backed-up folder...), set `KEEBOX_HOME`:
55
+
56
+ ```
57
+ export KEEBOX_HOME=/path/to/vault-folder # in your .bashrc / .zshrc
58
+ ```
59
+
60
+ Or run it from source, without installing (the vault then lives in the project folder):
61
+
62
+ ```
63
+ git clone https://github.com/eissapk/keebox.git
51
64
  cd keebox # no npm install needed, there are no dependencies
52
- node index.js -h
65
+ node bin/index.js -h
53
66
  ```
54
67
 
55
68
  Clipboard: macOS and Windows/WSL work as is, Linux needs `xclip` (X11) or `wl-clipboard` (Wayland). On Android (Termux) passwords are printed instead of copied.
@@ -59,9 +72,10 @@ Clipboard: macOS and Windows/WSL work as is, Linux needs `xclip` (X11) or `wl-cl
59
72
  ```
60
73
  ├── assets
61
74
  │ └── logo.svg
75
+ ├── bin
76
+ │ └── index.js # CLI entry (the `keebox` command): commands + master password prompt
62
77
  ├── credentials.json # created on first run (master password lock), not in git
63
78
  ├── db.json # created on first run (accounts + recovery lock), not in git
64
- ├── index.js # CLI entry: commands + master password prompt
65
79
  ├── node # optional, for a portable copy (see below), not in git
66
80
  │ ├── linux/node
67
81
  │ ├── mac/node
@@ -94,9 +108,11 @@ Clipboard: macOS and Windows/WSL work as is, Linux needs `xclip` (X11) or `wl-cl
94
108
  └── wordlist.js # BIP39 English wordlist
95
109
  ```
96
110
 
111
+ `db.json` / `credentials.json` sit in the project folder when run from source or a portable copy, in `~/.keebox/` when installed with npm, or in `$KEEBOX_HOME` when it's set.
112
+
97
113
  ##### Make it portable (USB flash)
98
114
 
99
- Copy the project with a Node.js binary per OS, so it runs on machines without Node installed:
115
+ Copy the project with a Node.js binary per OS, so it runs on machines without Node installed. The vault is kept next to it, on the flash drive:
100
116
 
101
117
  ```
102
118
  cp -r /path/to/project /path/to/usb-flash
@@ -106,46 +122,47 @@ cp "$(command -v node)" /path/to/usb-flash/node/linux/node # repeat for mac/node
106
122
  Run it from the flash drive:
107
123
 
108
124
  ```
109
- /path/to/usb-flash/node/linux/node /path/to/usb-flash/index.js -h
125
+ /path/to/usb-flash/node/linux/node /path/to/usb-flash/bin/index.js -h
110
126
  ```
111
127
 
112
128
  Or add an alias in your `.bashrc` / `.zshrc`, and use it anywhere:
113
129
 
114
130
  ```
115
- alias pass='node /path/to/usb-flash/index.js'
116
- pass --help
117
- pass --version
131
+ alias keebox='node /path/to/usb-flash/bin/index.js'
132
+ keebox --help
133
+ keebox --version
118
134
  ```
119
135
 
120
- ##### Commands (using the `pass` alias)
136
+ ##### Commands
121
137
 
122
138
  ```
123
- pass get # list all accounts
124
- pass get --id 2 # show account with id 2
125
- pass get --name google # show accounts whose name contains "google"
126
- pass get --id 2 --cp # copy its password to the clipboard (auto-cleared after 20s)
127
- pass get --id 2 --show # show its login/notes
128
- pass get --id 2 --edit # update it (Enter = keep a value, '-' = clear it)
129
- pass get --id 2 --rm # delete it
130
- pass add --name google --username foo@gmail.com --url www.google.com # add an account (asks for its password)
131
- pass add --name github --username foo --url github.com --login foo@mail.com --notes "2FA via phone" # with login/notes (encrypted)
132
- pass make --size 20 # generate a random strong password of 20 chars
133
- pass export # export all accounts, decrypted, to keebox-export-<date>.csv
134
- pass export --name google --out google.csv # export matching accounts to google.csv
135
- pass import --file accounts.csv # add accounts from a csv (same columns as export; id is ignored, duplicates skipped)
136
- pass check # damage check: lists accounts that are broken or can't be decrypted (read-only, exit code 1 if any)
137
- pass recovery # create (or replace) the 12-word recovery phrase
138
- pass recover # lost credentials.json / forgot the master password: unlock with the recovery phrase, set a new master password
139
+ keebox get # list all accounts
140
+ keebox get --id 2 # show account with id 2
141
+ keebox get --name google # show accounts whose name contains "google"
142
+ keebox get --id 2 --cp # copy its password to the clipboard (auto-cleared after 20s)
143
+ keebox get --id 2 --show # show its login/notes
144
+ keebox get --id 2 --edit # update it (Enter = keep a value, '-' = clear it)
145
+ keebox get --id 2 --rm # delete it
146
+ keebox add --name google --username foo@gmail.com --url www.google.com # add an account (asks for its password)
147
+ keebox add --name github --username foo --url github.com --login foo@mail.com --notes "2FA via phone" # with login/notes (encrypted)
148
+ keebox make --size 20 # generate a random strong password of 20 chars
149
+ keebox export # export all accounts, decrypted, to keebox-export-<date>.csv
150
+ keebox export --name google --out google.csv # export matching accounts to google.csv
151
+ keebox import --file accounts.csv # add accounts from a csv (same columns as export; id is ignored, duplicates skipped)
152
+ keebox check # damage check: lists accounts that are broken or can't be decrypted (read-only, exit code 1 if any)
153
+ keebox recovery # create (or replace) the 12-word recovery phrase
154
+ keebox recover # lost credentials.json / forgot the master password: unlock with the recovery phrase, set a new master password
155
+ keebox master # create or change the master password
139
156
  ```
140
157
 
141
158
  Exported CSV files are **not encrypted**, delete them when you're done.
142
159
 
143
160
  ##### Create / change master password
144
161
 
145
- On a new (empty) vault, the first `pass add` / `pass get` asks you to create one. To create it yourself, or change it later (asks the current one first; `db.json` stays as is, and the recovery phrase keeps working):
162
+ On a new (empty) vault, the first `keebox add` / `keebox get` asks you to create one. To create it yourself, or change it later (asks the current one first; `db.json` stays as is, and the recovery phrase keeps working):
146
163
 
147
164
  ```
148
- node utils/config.js
165
+ keebox master
149
166
  ```
150
167
 
151
168
  ##### Encryption
@@ -155,14 +172,14 @@ Each password, login and notes is encrypted (AES-256-GCM) with one random 256-bi
155
172
  | Where | Locked with | Opened by |
156
173
  |---|---|---|
157
174
  | `credentials.json` → `{ salt, n, key }` | scrypt(master password, salt) + AES-256-GCM | the master password |
158
- | `db.json` → `recovery: { salt, n, key }` | scrypt(12-word phrase, salt) + AES-256-GCM | the recovery phrase (`pass recover`) |
175
+ | `db.json` → `recovery: { salt, n, key }` | scrypt(12-word phrase, salt) + AES-256-GCM | the recovery phrase (`keebox recover`) |
159
176
 
160
177
  What gets you into the accounts:
161
178
 
162
179
  | You have | Access? |
163
180
  |---|---|
164
181
  | `db.json` + `credentials.json` + master password | yes (normal use) |
165
- | `db.json` + recovery phrase | yes: `pass recover` sets a new master password and writes a new `credentials.json` |
182
+ | `db.json` + recovery phrase | yes: `keebox recover` sets a new master password and writes a new `credentials.json` |
166
183
  | `db.json` + master password, but no `credentials.json` | **no**: the master password only opens `credentials.json` (restore `credentials.json.bak` or a backup) |
167
184
  | `db.json` alone | **no**, for anyone: it would mean guessing a 128-bit phrase or a 256-bit key |
168
185
 
@@ -170,15 +187,15 @@ So keep `db.json` and `credentials.json` together and backed up, choose a strong
170
187
 
171
188
  ##### Recovery phrase (emergency key)
172
189
 
173
- `pass recovery` makes a 12-word phrase (BIP39 English words, like a crypto wallet seed: 128 random bits + checksum) and locks the same vault key with it into `db.json`. The phrase itself is never saved, it's shown once: write it down, then type it back to confirm.
190
+ `keebox recovery` makes a 12-word phrase (BIP39 English words, like a crypto wallet seed: 128 random bits + checksum) and locks the same vault key with it into `db.json`. The phrase itself is never saved, it's shown once: write it down, then type it back to confirm.
174
191
 
175
- If `credentials.json` is deleted or you forget the master password, `pass recover` asks for the 12 words (any case/spacing; typos and swapped words are caught by the checksum), unlocks the vault key and lets you set a new master password. Only `credentials.json` is written, `db.json` isn't changed, and the same phrase keeps working afterwards. Running `pass recovery` again replaces the phrase (the old one stops working, except against an older `db.json.bak`).
192
+ If `credentials.json` is deleted or you forget the master password, `keebox recover` asks for the 12 words (any case/spacing; typos and swapped words are caught by the checksum), unlocks the vault key and lets you set a new master password. Only `credentials.json` is written, `db.json` isn't changed, and the same phrase keeps working afterwards. Running `keebox recovery` again replaces the phrase (the old one stops working, except against an older `db.json.bak`).
176
193
 
177
194
  Anyone with the phrase + `db.json` can read every account, so guard it like the master password.
178
195
 
179
196
  ##### Files
180
197
 
181
- `db.json` (`recovery` is `null` until you run `pass recovery`):
198
+ `db.json` (`recovery` is `null` until you run `keebox recovery`):
182
199
 
183
200
  ```json
184
201
  {
@@ -205,12 +222,12 @@ Anyone with the phrase + `db.json` can read every account, so guard it like the
205
222
  { "salt": "...", "n": 32768, "key": "iv.tag.data" }
206
223
  ```
207
224
 
208
- Both are created automatically if missing, and every save keeps the previous version as `db.json.bak` / `credentials.json.bak`. If `credentials.json` is missing but `db.json` has data, KeeBox won't create a new master password (that would make a new vault key and lock out the existing accounts); it asks you to run `pass recover` or restore `credentials.json.bak` instead.
225
+ Both are created automatically if missing, and every save keeps the previous version as `db.json.bak` / `credentials.json.bak`. If `credentials.json` is missing but `db.json` has data, KeeBox won't create a new master password (that would make a new vault key and lock out the existing accounts); it asks you to run `keebox recover` or restore `credentials.json.bak` instead.
209
226
 
210
227
  ##### Decryption flow
211
228
 
212
229
  ```
213
- pass get --id 11 --cp
230
+ keebox get --id 11 --cp
214
231
  │
215
232
  ▼
216
233
  ┌───────────────────────────────┐
@@ -277,7 +294,7 @@ Both are created automatically if missing, and every save keeps the previous ver
277
294
  ##### Recovery flow
278
295
 
279
296
  ```
280
- pass recover
297
+ keebox recover
281
298
  │
282
299
  ▼
283
300
  ┌───────────────────────────────┐
@@ -304,10 +321,10 @@ Both are created automatically if missing, and every save keeps the previous ver
304
321
 
305
322
  | Step | File | Uses |
306
323
  |---|---|---|
307
- | Ask for or create the master password | `index.js` → `askMaster`, `utils/config.js` → `createMaster` | master password |
324
+ | Ask for or create the master password | `bin/index.js` → `askMaster`, `utils/config.js` → `createMaster` | master password |
308
325
  | Master password → vault key | `utils/vault.js` → `unlock` | `credentials.json` `salt`, `n`, `key` |
309
- | Create the recovery phrase (`pass recovery`) | `options/recovery.js` → `createRecovery`, `utils/vault.js` → `makeRecovery`, `utils/mnemonic.js` | `db.json` `recovery` |
310
- | Recovery phrase → vault key (`pass recover`) | `options/recovery.js` → `recoverVault`, `utils/vault.js` → `recover` | `db.json` `recovery` |
326
+ | Create the recovery phrase (`keebox recovery`) | `options/recovery.js` → `createRecovery`, `utils/vault.js` → `makeRecovery`, `utils/mnemonic.js` | `db.json` `recovery` |
327
+ | Recovery phrase → vault key (`keebox recover`) | `options/recovery.js` → `recoverVault`, `utils/vault.js` → `recover` | `db.json` `recovery` |
311
328
  | Vault key → account password | `utils/index.js` → `decrypt` (called from `options/get.js` → `copyEntry`) | `iv`, `tag`, `data` |
312
329
 
313
330
  ##### Tests
@@ -1,7 +1,8 @@
1
- const { mkPass, askPassword, loadData, copyOrPrint, run, FIELDS, SECRET_FIELDS, log, hasMaster, unlock, createMaster, parseArgs } = require("./utils");
1
+ #!/usr/bin/env node
2
+ const { mkPass, askPassword, loadData, copyOrPrint, run, FIELDS, SECRET_FIELDS, log, hasMaster, unlock, createMaster, changeMaster, parseArgs } = require("../utils");
2
3
  const crypto = require("crypto");
3
- const pkgJSON = require("./package.json");
4
- const { getEntry, addEntry, exportEntries, importEntries, checkDb, createRecovery, recoverVault } = require("./options");
4
+ const pkgJSON = require("../package.json");
5
+ const { getEntry, addEntry, exportEntries, importEntries, checkDb, createRecovery, recoverVault } = require("../options");
5
6
 
6
7
  run(main);
7
8
 
@@ -29,9 +30,9 @@ async function main() {
29
30
  ["--out <file>", "CSV file to write (default: keebox-export-<date>.csv) - use it with 'export' command"],
30
31
  ["--file <file>", "CSV file to read (columns: name,password + optional url,username,login,notes,created,updated) - use it with 'import' command"],
31
32
  ],
32
- commands: { add: "Add account", get: "display account(s)", make: "Generate a password", export: "Export account(s) as decrypted CSV (filter with --id/--name/...)", import: "Import account(s) from a CSV file (e.g. one made by 'export')", check: "Damage check: find accounts that are broken or can't be decrypted (changes nothing)", recovery: "Create (or replace) the 12-word recovery phrase", recover: "Lost credentials.json / forgot the master password: unlock with the recovery phrase and set a new master password" },
33
+ commands: { add: "Add account", get: "display account(s)", make: "Generate a password", export: "Export account(s) as decrypted CSV (filter with --id/--name/...)", import: "Import account(s) from a CSV file (e.g. one made by 'export')", check: "Damage check: find accounts that are broken or can't be decrypted (changes nothing)", recovery: "Create (or replace) the 12-word recovery phrase", recover: "Lost credentials.json / forgot the master password: unlock with the recovery phrase and set a new master password", master: "Create or change the master password (accounts and recovery phrase keep working)" },
33
34
  });
34
- if (parsed) await { add, get, make, export: exportCsv, import: importCsv, check, recovery, recover }[parsed.command](parsed.opts, db, cred);
35
+ if (parsed) await { add, get, make, export: exportCsv, import: importCsv, check, recovery, recover, master }[parsed.command](parsed.opts, db, cred);
35
36
  }
36
37
 
37
38
  // ask master password -> vault key, or null
@@ -106,3 +107,7 @@ async function recovery(opts, db, cred) {
106
107
  async function recover(opts, db, cred) {
107
108
  await recoverVault(db, cred);
108
109
  }
110
+
111
+ async function master(opts, db, cred) {
112
+ await changeMaster(db, cred);
113
+ }
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "keebox",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "KeeBox is an offline password manager that lives in your terminal",
5
- "main": "index.js",
5
+ "bin": {
6
+ "keebox": "./bin/index.js"
7
+ },
8
+ "engines": {
9
+ "node": ">=18"
10
+ },
6
11
  "scripts": {
7
12
  "test": "node --test"
8
13
  },
package/utils/config.js CHANGED
@@ -1,14 +1,17 @@
1
1
  const { askPassword, loadData, saveOrReport, run, credPath, log, lockVault, hasMaster, unlock } = require("./index");
2
2
  const crypto = require("crypto");
3
3
 
4
- // usage: node utils/config.js -> create the master password, or change it
4
+ // usage: keebox master (or node utils/config.js) -> create the master password, or change it
5
5
  if (require.main === module) run(main);
6
- module.exports = { createMaster };
6
+ module.exports = { createMaster, changeMaster };
7
7
 
8
8
  async function main() {
9
9
  const data = loadData();
10
- if (!data) return;
11
- const { db, cred } = data;
10
+ if (data) await changeMaster(data.db, data.cred);
11
+ }
12
+
13
+ // create the master password, or change it (asks the current one first)
14
+ async function changeMaster(db, cred) {
12
15
  if (!hasMaster(cred)) {
13
16
  // a new vault key would make the existing accounts unreadable
14
17
  if (db.accounts.length || db.recovery) return log.error("credentials.json is missing, but db.json has data. Run 'recover' with your recovery phrase, or restore credentials.json.bak");
package/utils/index.js CHANGED
@@ -12,8 +12,19 @@ const prompt = require("./prompt");
12
12
  const drawTable = require("./table");
13
13
  const parseArgs = require("./cli");
14
14
 
15
- const dbPath = path.resolve(__dirname, "../db.json");
16
- const credPath = path.resolve(__dirname, "../credentials.json");
15
+ // where db.json + credentials.json live:
16
+ // - $KEEBOX_HOME when set
17
+ // - ~/.keebox when installed with npm (inside node_modules, which npm wipes on update/uninstall)
18
+ // - the project folder otherwise (git clone / portable USB copy)
19
+ function dataDir() {
20
+ if (process.env.KEEBOX_HOME) return path.resolve(process.env.KEEBOX_HOME);
21
+ const appDir = path.resolve(__dirname, "..");
22
+ if (appDir.split(path.sep).includes("node_modules")) return path.join(require("os").homedir(), ".keebox");
23
+ return appDir;
24
+ }
25
+ const dir = dataDir();
26
+ const dbPath = path.join(dir, "db.json");
27
+ const credPath = path.join(dir, "credentials.json");
17
28
 
18
29
  // cryptographically secure, exact length, at least one char of each class (length >= 4)
19
30
  function mkPass(length) {
@@ -67,7 +78,8 @@ function decryptField(entry, field, key) {
67
78
  function saveJson(file, data) {
68
79
  const fs = require("fs");
69
80
  const tmp = file + ".tmp";
70
- const fd = fs.openSync(tmp, "w");
81
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
82
+ const fd = fs.openSync(tmp, "w", 0o600);
71
83
  try {
72
84
  fs.writeFileSync(fd, JSON.stringify(data, null, 2));
73
85
  fs.fsyncSync(fd);
package/utils/prompt.js CHANGED
@@ -1,12 +1,12 @@
1
1
  // minimal inquirer replacement, public Node APIs only
2
2
  // await prompt([{ type: "input" | "password" | "confirm", name, message, default }]) -> answers
3
3
  // - interactive (stdin is a terminal): readline for input/confirm, raw-mode reader for passwords (masked with "•")
4
- // - piped stdin: one shared line queue, so answers can be scripted: printf 'pw\ny\n' | node index.js get --id 1 --rm
4
+ // - piped stdin: one shared line queue, so answers can be scripted: printf 'pw\ny\n' | keebox get --id 1 --rm
5
5
  // - Ctrl+C exits 130; Ctrl+D / end of input cancels with exit 1 (never auto-answers, e.g. "delete? (Y/n)")
6
6
  const readline = require("readline");
7
7
  const readlinePromises = require("readline/promises");
8
8
 
9
- // prompts go to the terminal even when stdout is redirected (node index.js get > list.txt)
9
+ // prompts go to the terminal even when stdout is redirected (keebox get > list.txt)
10
10
  function promptOut() {
11
11
  return process.stdout.isTTY || !process.stderr.isTTY ? process.stdout : process.stderr;
12
12
  }