sefy-cli 0.1.1 → 0.2.0
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 +212 -0
- package/package.json +43 -27
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
<p align="center"><img src="https://github.com/lacodda/sefy/raw/main/assets/banner.svg" alt="sefy - an inconspicuous encrypted vault" width="720"></p>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://crates.io/crates/sefy"><img src="https://img.shields.io/crates/v/sefy?style=flat-square" alt="crates.io"></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/sefy-cli"><img src="https://img.shields.io/npm/v/sefy-cli?style=flat-square" alt="npm"></a>
|
|
6
|
+
<a href="https://github.com/lacodda/sefy/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/lacodda/sefy/ci.yml?branch=main&style=flat-square" alt="CI"></a>
|
|
7
|
+
<a href="https://github.com/lacodda/sefy/blob/main/LICENSE"><img src="https://img.shields.io/crates/l/sefy?style=flat-square" alt="MIT"></a>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
# sefy
|
|
11
|
+
|
|
12
|
+
Every way of storing secrets announces itself. A `.kdbx` file says "password
|
|
13
|
+
database". age and gpg write a header. VeraCrypt wants a container and a mount.
|
|
14
|
+
Whoever looks at your disk, your backup drive or your cloud folder can tell
|
|
15
|
+
exactly where the interesting file is.
|
|
16
|
+
|
|
17
|
+
**sefy is a secret store whose file looks like nothing.** Notes, credentials and
|
|
18
|
+
files live in an encrypted SQLite database sealed into a single blob with no
|
|
19
|
+
magic bytes, no header and no extension convention. Call it `notes.bak`, leave
|
|
20
|
+
it among your other backups, and there is nothing to notice.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
$ head -c 32 notes.bak | xxd
|
|
24
|
+
00000000: 65b0 1375 a933 361d 89e2 9338 1b8d cb76 e..u.36....8...v
|
|
25
|
+
00000010: 0749 7515 ad12 dd40 1c3e 3e93 9282 4b5a .Iu....@.>>...KZ
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Threat model, honestly
|
|
29
|
+
|
|
30
|
+
- **Protects against:** a passing glance, a curious file listing, a cloud-side
|
|
31
|
+
scanner looking for known formats, anyone who does not already know the file
|
|
32
|
+
is a vault.
|
|
33
|
+
- **Does not protect against:** forensic analysis - a high-entropy headerless
|
|
34
|
+
file is recognizable as *some* container to an examiner - or anyone who can
|
|
35
|
+
compel you to give up the password.
|
|
36
|
+
|
|
37
|
+
sefy is **inconspicuous, not deniable**, and it will not pretend otherwise.
|
|
38
|
+
|
|
39
|
+
## A day with it
|
|
40
|
+
|
|
41
|
+
Point sefy at a file. There is no default location: a vault at a predictable
|
|
42
|
+
path would undo the whole point.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
$ export SEFY_VAULT=~/backups/notes.bak
|
|
46
|
+
|
|
47
|
+
$ sefy init
|
|
48
|
+
Master password:
|
|
49
|
+
created /home/you/backups/notes.bak
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Put things in. Notes, logins, and files kept byte for byte.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
$ sefy add note "bank card" --text "PIN 4815" --tag money
|
|
56
|
+
added "bank card" as 1
|
|
57
|
+
|
|
58
|
+
$ sefy add credential mail --login someone@example.com --url https://mail.example.com --tag mail
|
|
59
|
+
Password for this item:
|
|
60
|
+
added "mail" as 2
|
|
61
|
+
|
|
62
|
+
$ sefy add file ~/.ssh/id_ed25519 --tag keys
|
|
63
|
+
added "id_ed25519" as 3
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Look around.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
$ sefy ls
|
|
70
|
+
3 id_ed25519 file [keys]
|
|
71
|
+
2 mail credential [mail]
|
|
72
|
+
1 bank card note [money]
|
|
73
|
+
|
|
74
|
+
$ sefy show mail
|
|
75
|
+
id: 2
|
|
76
|
+
title: mail
|
|
77
|
+
kind: credential
|
|
78
|
+
tags: mail
|
|
79
|
+
login: someone@example.com
|
|
80
|
+
password: <hidden — use sefy get>
|
|
81
|
+
url: https://mail.example.com
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Take a secret out. It goes to the clipboard and is taken back off after 45
|
|
85
|
+
seconds - sefy clears it only if the secret is still what is sitting there, so
|
|
86
|
+
anything you copied meanwhile is left alone.
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
$ sefy get mail
|
|
90
|
+
copied password of "mail" to the clipboard; clearing in 45s
|
|
91
|
+
clipboard cleared
|
|
92
|
+
|
|
93
|
+
$ sefy get "bank card" --stdout
|
|
94
|
+
PIN 4815
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Items are addressed by title, by an exact id, or by text to search for. When
|
|
98
|
+
your words could mean more than one thing, sefy shows what they could mean
|
|
99
|
+
instead of guessing:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
$ sefy get ma
|
|
103
|
+
error: 2 items match "ma":
|
|
104
|
+
4 mailing list note
|
|
105
|
+
2 mail credential
|
|
106
|
+
narrow the text, or use an id
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Two machines, two copies, both edited? Fold one into the other. Where they
|
|
110
|
+
disagree about the same item, sefy keeps both versions rather than letting a
|
|
111
|
+
timestamp decide which password you get to keep:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
$ sefy merge ~/from-laptop.bak
|
|
115
|
+
Password for /home/you/from-laptop.bak:
|
|
116
|
+
merged: 1 added, 1 updated, 1 unchanged
|
|
117
|
+
|
|
118
|
+
1 item changed on both sides and could not be resolved here.
|
|
119
|
+
This vault's version was kept; the incoming one is beside it:
|
|
120
|
+
"mail" → also kept as "mail (conflicted copy)"
|
|
121
|
+
Compare them, keep the right one, and remove the other.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
A vault is never a trap. `sefy export` writes everything back out as plain
|
|
125
|
+
JSON - which is exactly as sensitive as the vault and protects nothing, so the
|
|
126
|
+
command makes you say so out loud:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
$ sefy export -o backup.json
|
|
130
|
+
error: export writes every secret in this vault in the clear
|
|
131
|
+
the resulting file protects nothing — encrypt it, or delete it when done
|
|
132
|
+
pass --i-know-this-writes-plaintext to go ahead
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Full command reference: **[lacodda.github.io/sefy](https://lacodda.github.io/sefy/)**.
|
|
136
|
+
|
|
137
|
+
## How it works
|
|
138
|
+
|
|
139
|
+
- The master password is stretched into a key with **Argon2id**.
|
|
140
|
+
- The whole SQLite database is sealed with **XChaCha20-Poly1305** (AEAD).
|
|
141
|
+
- The file on disk is `salt ‖ nonce ‖ ciphertext`. Salt and nonce are fresh on
|
|
142
|
+
every save, so two saves of identical content share no prefix - and the
|
|
143
|
+
format version lives *inside* the ciphertext, because a version byte in the
|
|
144
|
+
clear would be the signature the format exists to avoid.
|
|
145
|
+
- The decrypted database exists **only in memory**. SQLite is never given a
|
|
146
|
+
path, so no page, journal or temporary file lands on disk.
|
|
147
|
+
- Saves are atomic: ciphertext goes to a temporary file, is synced, and is
|
|
148
|
+
renamed over the vault. A crash leaves either the old vault or the new one,
|
|
149
|
+
and never plaintext.
|
|
150
|
+
|
|
151
|
+
Details and rationale: [ADR-0001](https://github.com/lacodda/sefy/blob/main/docs/adr/0001-vault-file-format-and-cryptography.md).
|
|
152
|
+
|
|
153
|
+
## Install
|
|
154
|
+
|
|
155
|
+
**One-line installers.** Windows (PowerShell):
|
|
156
|
+
|
|
157
|
+
```powershell
|
|
158
|
+
irm https://raw.githubusercontent.com/lacodda/sefy/main/tools/install.ps1 | iex
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
macOS / Linux:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
curl -fsSL https://raw.githubusercontent.com/lacodda/sefy/main/tools/install.sh | sh
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**cargo** - `cargo install sefy`
|
|
168
|
+
|
|
169
|
+
**npm** - `npm install -g sefy-cli`
|
|
170
|
+
|
|
171
|
+
**Binary releases** - grab the archive for your platform from [Releases](https://github.com/lacodda/sefy/releases/latest)
|
|
172
|
+
(Windows x86_64, Linux x86_64, macOS arm64), unpack and put `sefy` on your
|
|
173
|
+
`PATH`.
|
|
174
|
+
|
|
175
|
+
Both installers take the newest release by default; set `SEFY_VERSION` to a tag
|
|
176
|
+
to pin one, and `SEFY_INSTALL_DIR` to choose where the binary lands.
|
|
177
|
+
|
|
178
|
+
Shell completions: `sefy completions bash` (also `zsh`, `fish`, `powershell`,
|
|
179
|
+
`elvish`).
|
|
180
|
+
|
|
181
|
+
## Stability
|
|
182
|
+
|
|
183
|
+
The **vault file format is stable at version 1**. Files written by this release
|
|
184
|
+
will stay readable: any future change to the format arrives as version 2, able
|
|
185
|
+
to read version 1 and migrate it. The Argon2 parameters are part of that
|
|
186
|
+
promise, not a tuning knob.
|
|
187
|
+
|
|
188
|
+
The database inside the ciphertext is versioned separately, and it does move:
|
|
189
|
+
0.2.0 added an identity to items so two copies of a vault can be merged. A vault
|
|
190
|
+
from 0.1.x opens, is migrated on the way in, and is still readable by 0.1.x
|
|
191
|
+
afterwards - the file on disk did not change shape.
|
|
192
|
+
|
|
193
|
+
Released versions and what landed in each: [CHANGELOG on the Releases page](https://github.com/lacodda/sefy/releases).
|
|
194
|
+
|
|
195
|
+
## Building
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
cargo build --release # workspace: sefy-core (library) + sefy (CLI)
|
|
199
|
+
cargo test # unit, integration and doc tests
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Use a release build for daily work: Argon2id is deliberately expensive, and an
|
|
203
|
+
unoptimized build makes it several times slower still.
|
|
204
|
+
|
|
205
|
+
The library is published separately as [`sefy-core`](https://crates.io/crates/sefy-core)
|
|
206
|
+
if you want vaults from your own code. The documentation site lives in
|
|
207
|
+
[`docs/`](https://github.com/lacodda/sefy/tree/main/docs); architecture decision
|
|
208
|
+
records are in [`docs/adr/`](https://github.com/lacodda/sefy/tree/main/docs/adr).
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
[MIT](https://github.com/lacodda/sefy/blob/main/LICENSE)
|
package/package.json
CHANGED
|
@@ -1,27 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "sefy-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"sefy": {
|
|
5
|
-
"binary": "v0.
|
|
6
|
-
},
|
|
7
|
-
"description": "An inconspicuous encrypted store for notes, credentials and files",
|
|
8
|
-
"license": "MIT",
|
|
9
|
-
"author": "Kirill Lakhtachev <lahtachev@gmail.com> (https://lacodda.com)",
|
|
10
|
-
"homepage": "https://lacodda.github.io/sefy/",
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "git+https://github.com/lacodda/sefy.git"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "sefy-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"sefy": {
|
|
5
|
+
"binary": "v0.2.0"
|
|
6
|
+
},
|
|
7
|
+
"description": "An inconspicuous encrypted store for notes, credentials and files",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"author": "Kirill Lakhtachev <lahtachev@gmail.com> (https://lacodda.com)",
|
|
10
|
+
"homepage": "https://lacodda.github.io/sefy/",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/lacodda/sefy.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cli",
|
|
17
|
+
"secrets",
|
|
18
|
+
"password-manager",
|
|
19
|
+
"encryption",
|
|
20
|
+
"vault"
|
|
21
|
+
],
|
|
22
|
+
"bin": {
|
|
23
|
+
"sefy": "run.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"download.js",
|
|
27
|
+
"install.js",
|
|
28
|
+
"run.js",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"prepack": "node -e \"require('fs').copyFileSync('../README.md','README.md')\"",
|
|
33
|
+
"postinstall": "node install.js"
|
|
34
|
+
},
|
|
35
|
+
"os": [
|
|
36
|
+
"win32",
|
|
37
|
+
"darwin",
|
|
38
|
+
"linux"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
}
|
|
43
|
+
}
|