sefy-cli 0.8.0 → 0.10.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 +68 -197
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
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
8
|
</p>
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
> An encrypted store for notes, credentials and files that does not announce itself: one file with no header, no extension it must keep, and no name that gives it away.
|
|
11
11
|
|
|
12
12
|
Every way of storing secrets announces itself. A `.kdbx` file says "password
|
|
13
13
|
database". age and gpg write a header. VeraCrypt wants a container and a mount.
|
|
@@ -50,7 +50,7 @@ Master password:
|
|
|
50
50
|
created /home/you/backups/notes.bak
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
Put things in
|
|
53
|
+
Put things in - notes, logins, cards, ssh keys, and files kept byte for byte:
|
|
54
54
|
|
|
55
55
|
```
|
|
56
56
|
$ sefy add note "bank card" --text "PIN 4815" --tag money
|
|
@@ -64,177 +64,69 @@ $ sefy add file ~/.ssh/id_ed25519 --tag keys
|
|
|
64
64
|
added "id_ed25519" as 3
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
$ sefy ls
|
|
71
|
-
3 id_ed25519 file [keys]
|
|
72
|
-
2 mail login [mail]
|
|
73
|
-
1 bank card note [money]
|
|
74
|
-
|
|
75
|
-
$ sefy show mail
|
|
76
|
-
id: 2
|
|
77
|
-
title: mail
|
|
78
|
-
kind: login
|
|
79
|
-
tags: mail
|
|
80
|
-
login: someone@example.com
|
|
81
|
-
password: <hidden — use sefy get>
|
|
82
|
-
url: https://mail.example.com
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Or do not look around at all: `sefy` on its own opens a picker over the vault.
|
|
86
|
-
Type a few letters, press Enter. The command line is exact and remembering is
|
|
87
|
-
not.
|
|
67
|
+
Bare `sefy` opens a picker over everything in the vault:
|
|
88
68
|
|
|
89
69
|
```
|
|
90
70
|
$ sefy
|
|
91
|
-
? Item
|
|
71
|
+
? Item >
|
|
92
72
|
> bank card note [money]
|
|
93
73
|
mail login [mail]
|
|
94
74
|
id_ed25519 file [keys]
|
|
95
75
|
```
|
|
96
76
|
|
|
97
77
|
Take a secret out. It goes to the clipboard and is taken back off after 45
|
|
98
|
-
seconds -
|
|
99
|
-
|
|
78
|
+
seconds - and only if the secret is still what is sitting there, so anything
|
|
79
|
+
you copied meanwhile is left alone.
|
|
100
80
|
|
|
101
81
|
```
|
|
102
82
|
$ sefy get mail
|
|
103
83
|
copied password of "mail" to the clipboard; clearing in 45s
|
|
104
84
|
clipboard cleared
|
|
105
|
-
|
|
106
|
-
$ sefy get "bank card" --stdout
|
|
107
|
-
PIN 4815
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
For a login you are about to use, `sefy open` does both halves at once: the
|
|
111
|
-
browser loads while the password waits on the clipboard.
|
|
112
|
-
|
|
113
|
-
```
|
|
114
|
-
$ sefy open mail
|
|
115
|
-
opened https://mail.example.com
|
|
116
|
-
copied password of "mail" to the clipboard; clearing in 45s
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Items are addressed by title, by an exact id, or by text to search for. When
|
|
120
|
-
your words could mean more than one thing, sefy shows what they could mean
|
|
121
|
-
instead of guessing:
|
|
122
|
-
|
|
123
|
-
```
|
|
124
|
-
$ sefy get ma
|
|
125
|
-
error: 2 items match "ma":
|
|
126
|
-
4 mailing list note
|
|
127
|
-
2 mail login
|
|
128
|
-
narrow the text, or use an id
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Several machines? `sefy sync` carries the vault through a transport and folds
|
|
132
|
-
what comes back into this one:
|
|
133
|
-
|
|
134
|
-
```
|
|
135
|
-
$ sefy sync
|
|
136
|
-
Master password:
|
|
137
|
-
synced "vault" through github
|
|
138
|
-
merged: 2 added, 0 updated, 14 unchanged
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Or fold in a copy you carried across by hand. Either way, where the two disagree
|
|
142
|
-
about the same item, sefy keeps both versions rather than letting a timestamp
|
|
143
|
-
decide which password you get to keep:
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
$ sefy merge ~/from-laptop.bak
|
|
147
|
-
Password for /home/you/from-laptop.bak:
|
|
148
|
-
merged: 1 added, 1 updated, 1 unchanged
|
|
149
|
-
|
|
150
|
-
1 item changed on both sides and could not be resolved here.
|
|
151
|
-
This vault's version was kept; the incoming one is beside it:
|
|
152
|
-
"mail" → also kept as "mail (conflicted copy)"
|
|
153
|
-
Compare them, keep the right one, and remove the other.
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
New machine, or back after a week? `sefy status` says what you are holding
|
|
157
|
-
without saying what is in it - counts and versions, never a title or a value:
|
|
158
|
-
|
|
159
|
-
```
|
|
160
|
-
$ sefy status
|
|
161
|
-
vault /home/you/backups/notes.bak
|
|
162
|
-
size 72.1 KB
|
|
163
|
-
items 17 items (12 login, 4 note, 1 file)
|
|
164
|
-
tags 6 tags
|
|
165
|
-
schema 4
|
|
166
|
-
synced 2026-09-16 19:15 UTC (2 hours ago) through github (sync)
|
|
167
|
-
plugins github, sftp
|
|
168
85
|
```
|
|
169
86
|
|
|
170
|
-
A
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
```
|
|
175
|
-
$ sefy
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
- **
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
both sides can actually be read.
|
|
214
|
-
|
|
215
|
-
Broken plugins are listed with the reason rather than skipped: an omitted line
|
|
216
|
-
would look exactly like a plugin that was never installed.
|
|
217
|
-
|
|
218
|
-
The protocol is small enough to implement in a shell script - manifest on
|
|
219
|
-
`--manifest`, one JSON request on stdin for `run`. Writing one:
|
|
220
|
-
[plugin reference](https://lacodda.github.io/sefy/reference/plugin/) ·
|
|
221
|
-
[ADR-0002](https://github.com/lacodda/sefy/blob/main/docs/adr/0002-plugin-protocol.md).
|
|
222
|
-
|
|
223
|
-
## How it works
|
|
224
|
-
|
|
225
|
-
- The master password is stretched into a key with **Argon2id**.
|
|
226
|
-
- The whole SQLite database is sealed with **XChaCha20-Poly1305** (AEAD).
|
|
227
|
-
- The file on disk is `salt ‖ nonce ‖ ciphertext`. Salt and nonce are fresh on
|
|
228
|
-
every save, so two saves of identical content share no prefix - and the
|
|
229
|
-
format version lives *inside* the ciphertext, because a version byte in the
|
|
230
|
-
clear would be the signature the format exists to avoid.
|
|
231
|
-
- The decrypted database exists **only in memory**. SQLite is never given a
|
|
232
|
-
path, so no page, journal or temporary file lands on disk.
|
|
233
|
-
- Saves are atomic: ciphertext goes to a temporary file, is synced, and is
|
|
234
|
-
renamed over the vault. A crash leaves either the old vault or the new one,
|
|
235
|
-
and never plaintext.
|
|
236
|
-
|
|
237
|
-
Details and rationale: [ADR-0001](https://github.com/lacodda/sefy/blob/main/docs/adr/0001-vault-file-format-and-cryptography.md).
|
|
87
|
+
A new account needs a new password, and `sefy gen` makes one and keeps it in
|
|
88
|
+
the same breath - stored as a login, then put on the clipboard for the sign-up
|
|
89
|
+
form:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
$ sefy gen --save forum --login someone@example.com --url https://forum.example.com
|
|
93
|
+
added "forum" as 4
|
|
94
|
+
generated 20 characters: 130 bits of entropy, strength 4/4
|
|
95
|
+
copied it to the clipboard; clearing in 45s
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Items are addressed by title, by an exact id, or by text to search for; when
|
|
99
|
+
the words could mean more than one thing, sefy shows what they could mean
|
|
100
|
+
rather than guessing. `sefy open` loads the site and puts the password on the
|
|
101
|
+
clipboard in one step, `sefy fill` passes login, password and one-time code
|
|
102
|
+
over one Enter at a time, `sefy sync` carries the vault through a transport and
|
|
103
|
+
folds what comes back into this one, and `sefy status` says what you are
|
|
104
|
+
holding without saying what is in it.
|
|
105
|
+
|
|
106
|
+
Every command, with its flags: **[the reference](https://lacodda.github.io/sefy/reference/commands/)**.
|
|
107
|
+
|
|
108
|
+
## What you get
|
|
109
|
+
|
|
110
|
+
- **A file that looks like nothing.** No magic bytes, no header, no extension
|
|
111
|
+
convention - salt and nonce fresh on every save, so two saves of identical
|
|
112
|
+
content share no prefix.
|
|
113
|
+
- **Notes, logins, cards, ssh keys, Wi-Fi networks, API tokens, bank
|
|
114
|
+
accounts and files** in one vault, tagged and searchable, with files kept
|
|
115
|
+
byte for byte.
|
|
116
|
+
- **Passwords made where they are kept.** `sefy gen` draws random characters,
|
|
117
|
+
pronounceable ones or a diceware passphrase (English or Russian lists built
|
|
118
|
+
in), reports its exact entropy beside an offline zxcvbn score, and can save
|
|
119
|
+
the result as a login in the same command.
|
|
120
|
+
- **Two-factor sign-in built in.** `sefy otp` turns the key a site shows
|
|
121
|
+
into the one-time code, and `sefy fill` hands over login, password and code
|
|
122
|
+
one Enter at a time.
|
|
123
|
+
- **Transports, not lock-in.** `sefy sync` carries the vault through a
|
|
124
|
+
`sefy-plugin-*` executable - github and sftp ship today - and folds what
|
|
125
|
+
comes back in without a credential ever passing through the transport.
|
|
126
|
+
- **Merge instead of overwrite.** Where two copies disagree about an item,
|
|
127
|
+
both are kept rather than letting a timestamp pick a winner.
|
|
128
|
+
- **A vault that is never a trap.** `export` writes plaintext JSON only after
|
|
129
|
+
you say so explicitly.
|
|
238
130
|
|
|
239
131
|
## Install
|
|
240
132
|
|
|
@@ -269,51 +161,30 @@ place any transport the archive carries into sefy's plugins directory, so
|
|
|
269
161
|
Shell completions: `sefy completions bash` (also `zsh`, `fish`, `powershell`,
|
|
270
162
|
`elvish`).
|
|
271
163
|
|
|
272
|
-
##
|
|
273
|
-
|
|
274
|
-
The **vault file format is stable at version 1**. Files written by this release
|
|
275
|
-
will stay readable: any future change to the format arrives as version 2, able
|
|
276
|
-
to read version 1 and migrate it. The Argon2 parameters are part of that
|
|
277
|
-
promise, not a tuning knob.
|
|
278
|
-
|
|
279
|
-
The database inside the ciphertext is versioned separately, and it does move:
|
|
280
|
-
0.2.0 added an identity to items so two copies of a vault can be merged, and
|
|
281
|
-
0.7.0 replaced the credential table with named fields so that a kind of record
|
|
282
|
-
costs a template rather than a table. A vault from an earlier release opens and
|
|
283
|
-
is migrated on the way in; the file on disk does not change shape.
|
|
284
|
-
|
|
285
|
-
What an older build makes of it afterwards depends on the change. The 0.2.0
|
|
286
|
-
migration left everything readable by 0.1.x. The 0.7.0 one does not: a login is
|
|
287
|
-
now a kind 0.6.0 has never heard of, so that build lists it, exports it and
|
|
288
|
-
syncs it while saying to upgrade before reading it. That is the
|
|
289
|
-
forward-compatibility contract from 0.6.0 doing its job rather than a break -
|
|
290
|
-
nothing is lost, and the newer build reads everything - but it is why the
|
|
291
|
-
rename happened before 1.0 rather than after.
|
|
292
|
-
|
|
293
|
-
The **plugin protocol is at version 1** as of 0.3.0. 0.4.0 put it to work and
|
|
294
|
-
0.5.0 added a second transport of a different shape without changing a field of
|
|
295
|
-
it, which is the evidence that it was not built around the first one. Optional
|
|
296
|
-
fields may be added to the manifest without breaking a plugin that predates
|
|
297
|
-
them; changing what an existing field means would arrive as version 2, with both
|
|
298
|
-
accepted for a time.
|
|
164
|
+
## Documentation
|
|
299
165
|
|
|
300
|
-
|
|
166
|
+
Full command reference, concepts and guides:
|
|
167
|
+
**[lacodda.github.io/sefy](https://lacodda.github.io/sefy/)** - including
|
|
168
|
+
[how the vault works](https://lacodda.github.io/sefy/concepts/vault-format/)
|
|
169
|
+
and the [plugin reference](https://lacodda.github.io/sefy/reference/plugin/)
|
|
170
|
+
for transports.
|
|
301
171
|
|
|
302
|
-
##
|
|
172
|
+
## Status
|
|
303
173
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
174
|
+
The vault file format is stable at version 1, and the plugin protocol at
|
|
175
|
+
version 1 since 0.3.0; both promises hold across every release below. The
|
|
176
|
+
database schema inside the ciphertext moves between releases, and older
|
|
177
|
+
builds keep reading a vault safely even when they cannot see everything in
|
|
178
|
+
it - the [versions and compatibility](https://lacodda.github.io/sefy/concepts/versions/)
|
|
179
|
+
page has the detail.
|
|
180
|
+
|
|
181
|
+
Released versions and what landed in each: [CHANGELOG on the Releases page](https://github.com/lacodda/sefy/releases).
|
|
308
182
|
|
|
309
|
-
|
|
310
|
-
unoptimized build makes it several times slower still.
|
|
183
|
+
## Contributing
|
|
311
184
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
[`docs/`](https://github.com/lacodda/sefy/tree/main/docs); architecture decision
|
|
315
|
-
records are in [`docs/adr/`](https://github.com/lacodda/sefy/tree/main/docs/adr).
|
|
185
|
+
Building the workspace, repository layout and commit conventions:
|
|
186
|
+
[CONTRIBUTING.md](https://github.com/lacodda/sefy/blob/main/CONTRIBUTING.md).
|
|
316
187
|
|
|
317
188
|
## License
|
|
318
189
|
|
|
319
|
-
[
|
|
190
|
+
MIT (c) [Kirill Lakhtachev](https://lacodda.com)
|
package/package.json
CHANGED