@safekb/mcp 0.9.1 → 0.10.1
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 +48 -45
- package/dist/safekb-mcp.js +305 -466
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,10 +107,8 @@ every tool that keeps a key this way; see [Where secrets are kept](#where-secret
|
|
|
107
107
|
| `--rotate-key <ref>` | | Replace a seal root's secret with a new generated key; takes `--store`. |
|
|
108
108
|
| `--move-key <ref>` | | Move a seal root's current key to `--store system\|file`. |
|
|
109
109
|
| `--delete-key <ref>` | | Remove a seal root's keys from this machine, after a typed confirmation. |
|
|
110
|
-
| `--auto-unlock <ref>\|all` | | With `--set on\|off`: let the sealed tools unlock this root (or every root) by themselves. See [Unlocking automatically](#unlocking-automatically). |
|
|
111
110
|
| `--export <file>` | | Write what this token reaches to a new passphrase-protected file. Takes `--space`, `--doc`, `--compress`, `--skip-locked`. See [Export and import](#export-and-import). |
|
|
112
111
|
| `--import <file>` | | Create what such a file holds in this token's account, as new things. Takes `--into-space`, `--store`, `--keys`. |
|
|
113
|
-
| | `SAFEKB_SEALED_IDLE_TTL` | Seconds an unlocked seal root may stay idle. Default 900. `unlock_sealed` can set it per root with `idle_ttl_seconds` (60 to 3600). |
|
|
114
112
|
| | `SAFEKB_HOSTED_TIMEOUT` | Seconds a hosted tool call may take. Default 300. |
|
|
115
113
|
| | `SAFEKB_MCP_NO_BROWSER` | Set to anything: never start a browser, only show the authorize link. |
|
|
116
114
|
|
|
@@ -134,15 +132,17 @@ Its secret is a generated key or a passphrase.
|
|
|
134
132
|
- `create_sealed_folder` / `insert_sealed_document` with `store` or `key_out_path` make a new seal root
|
|
135
133
|
and keep its secret on this machine. The secret is never returned to the model.
|
|
136
134
|
**Back it up: a lost secret cannot be recovered.**
|
|
137
|
-
-
|
|
138
|
-
it
|
|
139
|
-
|
|
135
|
+
- There is no unlock step. Every sealed tool opens the seal root it needs by itself, with the secret
|
|
136
|
+
the one local store that has it keeps (system keyring, key file directory, or the sidecar), and the
|
|
137
|
+
root's keys stay in the client's memory until the process ends. **Any session on this machine that
|
|
138
|
+
uses these key stores can read every root whose secret is kept here.** See
|
|
139
|
+
[Opening seal roots](#opening-seal-roots).
|
|
140
|
+
- `search_sealed`, `list_sealed`, `read_sealed_document`, `get_sealed_section`,
|
|
140
141
|
`update_sealed_document`, `replace_in_sealed_document`, and the move, archive, delete,
|
|
141
142
|
attachment and deposit tools.
|
|
142
143
|
- `seal_document` and `unseal_document` move a document between hosted and sealed form; see
|
|
143
144
|
[Moving documents in and out of a seal root](#moving-documents-in-and-out-of-a-seal-root).
|
|
144
145
|
`unseal_document` is not a backup: see [Export and import](#export-and-import).
|
|
145
|
-
- `lock_sealed`, the idle timeout, or the end of the process forget the keys.
|
|
146
146
|
- `change_seal_password` replaces the root's secret. Once the server has taken the new one, the
|
|
147
147
|
superseded key is removed from the store that kept it (the system keyring, or any key file in the
|
|
148
148
|
key file directory). A superseded passphrase, or a key file you had written elsewhere with
|
|
@@ -188,8 +188,9 @@ conversation.
|
|
|
188
188
|
## Where secrets are kept
|
|
189
189
|
|
|
190
190
|
Every seal root has its own secret, and each can be kept in a different place. All three work side by
|
|
191
|
-
side:
|
|
192
|
-
|
|
191
|
+
side: a sealed tool asks each store and uses the one that has the secret. If two have it, the tool
|
|
192
|
+
fails and names them: `provide_seal_secret` with `store` says which one to use, `move_seal_key` leaves
|
|
193
|
+
one copy.
|
|
193
194
|
|
|
194
195
|
| `store` | What is kept | Where | At rest |
|
|
195
196
|
| --- | --- | --- | --- |
|
|
@@ -207,6 +208,14 @@ Secret Service (GNOME Keyring, KWallet, KeePassXC); the kernel keyring is never
|
|
|
207
208
|
forgets its content at logout. Where it does not work, keys are kept as files in `keys/` next to the
|
|
208
209
|
token file: readable only by your user, but not encrypted on disk. The client tells you when it does this. Keys are kept there with [`@napi-rs/keyring`](https://github.com/Brooooooklyn/keyring-node).
|
|
209
210
|
|
|
211
|
+
A locked keyring does not answer until it is unlocked. On Linux it waits for an unlock dialog on the
|
|
212
|
+
machine's own screen, which nobody sees over SSH or after an autologin that typed no password. Every
|
|
213
|
+
keyring call is therefore given up after 10 seconds. When the choice is being made, that counts as no
|
|
214
|
+
keyring: keys go to files, and you are told why. Later, the call that waited fails with "The system
|
|
215
|
+
keyring did not answer". Unlock the keyring at the machine (log in at the desktop, or unlock it in
|
|
216
|
+
your keyring manager) and try again; after unlocking, `--configure` can choose the system keyring
|
|
217
|
+
again.
|
|
218
|
+
|
|
210
219
|
### The sidecar
|
|
211
220
|
|
|
212
221
|
The sidecar keeps passphrases out of files, out of the keyring and out of the conversation. Run it in
|
|
@@ -225,8 +234,7 @@ npx -y @safekb/mcp --sidecar
|
|
|
225
234
|
the tool call waits.
|
|
226
235
|
- With `--confirm` it asks for your approval before every release of a secret.
|
|
227
236
|
- The [key tools](#the-key-tools) and the backup tools ask here for the passphrase of a file, for
|
|
228
|
-
the name or id that confirms a deletion
|
|
229
|
-
passphrase is used for that one call and not held. An empty entry declines.
|
|
237
|
+
the name or id that confirms a deletion. Such a passphrase is used for that one call and not held. An empty entry declines.
|
|
230
238
|
- The sidecar and the MCP client must be the same version of `@safekb/mcp`.
|
|
231
239
|
|
|
232
240
|
Several roots can be held at once, each with its own passphrase and time. The sidecar listens on a Unix
|
|
@@ -236,20 +244,20 @@ ask it for a held secret, which is what `--confirm` is for.
|
|
|
236
244
|
### Names of seal roots
|
|
237
245
|
|
|
238
246
|
The server cannot read the name of a sealed folder, and an id like `6acdb031893533e0ae1f5456` is no
|
|
239
|
-
name to type. So the client remembers the name of every seal root it creates or
|
|
247
|
+
name to type. So the client remembers the name of every seal root it creates or opens, in
|
|
240
248
|
`roots.json` next to the token file (mode 0600), and follows a rename or a delete. The sidecar, the
|
|
241
249
|
key export and the key modes show a root as `"Tax papers" (6acd…)`, and the name is taken wherever a
|
|
242
250
|
seal root's id is: at the sidecar, in the key modes, and in the `root_id` of the MCP tools.
|
|
243
251
|
|
|
244
252
|
- The names are in plaintext in that file: readable on this machine by programs running as you,
|
|
245
|
-
never by the server. Delete the file to forget them; they come back as roots are
|
|
246
|
-
- The client never puts a name into a tool result, so
|
|
253
|
+
never by the server. Delete the file to forget them; they come back as roots are opened.
|
|
254
|
+
- The client never puts a name into a tool result, so opening a root does not tell the model what
|
|
247
255
|
it is called. An error repeats only the name the caller passed.
|
|
248
256
|
- A name stays a usable handle: creating or renaming a seal root is refused when the name has the
|
|
249
257
|
shape of an id (24 hex characters), or when another root known on this machine already has it.
|
|
250
258
|
- Text with the shape of an id always means the root with that id. A root that got such a title
|
|
251
|
-
elsewhere is not given a name here when it is
|
|
252
|
-
- Two roots can still share a name when both were named elsewhere and then
|
|
259
|
+
elsewhere is not given a name here when it is opened or its key is imported: use its id.
|
|
260
|
+
- Two roots can still share a name when both were named elsewhere and then opened or imported
|
|
253
261
|
here. Such a name stands for neither and is refused as ambiguous: use the id. Such a root can
|
|
254
262
|
still be changed while its name stays the same.
|
|
255
263
|
|
|
@@ -297,41 +305,34 @@ A seal root is given by its [name](#names-of-seal-roots) or its id.
|
|
|
297
305
|
When a step after the point of no return fails (the superseded key cannot be removed after a rotation,
|
|
298
306
|
the old copy cannot be removed after a move), the command fails and says where the key now is.
|
|
299
307
|
|
|
300
|
-
###
|
|
308
|
+
### Opening seal roots
|
|
301
309
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
npx -y @safekb/mcp --auto-unlock all --set on # every root without a setting of its own
|
|
305
|
-
npx -y @safekb/mcp --auto-unlock "Bank" --set off
|
|
306
|
-
```
|
|
310
|
+
A sealed tool that needs a seal root this process does not hold yet opens it from the local stores,
|
|
311
|
+
and keeps its keys in memory until the process ends. Nothing is asked and nothing has to be turned on.
|
|
307
312
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
`list_seal_keys` and `get_auto_unlock` show `auto_unlock`.
|
|
321
|
-
- `set_auto_unlock` turns it on only after you type the root's name or id (or `all`) at the sidecar:
|
|
322
|
-
the model cannot widen access on its own. Turning it off needs no confirmation.
|
|
313
|
+
- Since 0.10.0 there is no `unlock_sealed`, `lock_sealed`, idle timeout (`SAFEKB_SEALED_IDLE_TTL`,
|
|
314
|
+
`idle_ttl_seconds`), `--auto-unlock`, `get_auto_unlock` or `set_auto_unlock`. An `auto-unlock.json`
|
|
315
|
+
left next to the token file is not read; delete it.
|
|
316
|
+
- A root opened by a passphrase opens once you have typed the passphrase at the
|
|
317
|
+
[sidecar](#the-sidecar) (`add <name or id>`). The secret leaves the sidecar when its time is up,
|
|
318
|
+
but a client that already opened the root keeps it open until it ends.
|
|
319
|
+
- A root whose secret no store holds fails with an error that says how to supply it: type it at the
|
|
320
|
+
sidecar and repeat the call, or give a key file or a file holding only the secret with
|
|
321
|
+
`provide_seal_secret` (`secret_file`). Nothing is guessed: a secret found in two stores is an
|
|
322
|
+
error too.
|
|
323
|
+
- To stop a session from reading a root, end the client, or remove the root's key from this machine
|
|
324
|
+
(`--move-key` it elsewhere, or `--delete-key`).
|
|
323
325
|
|
|
324
326
|
### The key tools
|
|
325
327
|
|
|
326
328
|
Everything above is also an MCP tool, and none of them takes or returns a secret: `list_seal_keys`,
|
|
327
329
|
`rename_seal_key`, `rotate_seal_key`, `move_seal_key`, `delete_seal_key`, `export_seal_keys`,
|
|
328
|
-
|
|
330
|
+
and `import_seal_keys`, and for backups `export_backup` and
|
|
329
331
|
`import_backup` (see [Export and import](#export-and-import)).
|
|
330
332
|
|
|
331
333
|
What you type at the terminal for the command line, you type at the [sidecar](#the-sidecar) for a
|
|
332
334
|
tool, while the call waits: the passphrase of an export file or a key export (twice for a new one),
|
|
333
|
-
the name or id that confirms `delete_seal_key
|
|
334
|
-
reaches this client only, never the conversation. Without a running sidecar such a tool fails and says
|
|
335
|
+
and the name or id that confirms `delete_seal_key`. It reaches this client only, never the conversation. Without a running sidecar such a tool fails and says
|
|
335
336
|
to start one (`safekb-mcp --sidecar`); on Windows, which has no sidecar, it names the command to run
|
|
336
337
|
instead. File paths given to the tools must be absolute.
|
|
337
338
|
|
|
@@ -364,9 +365,9 @@ sealed custom fields are encrypted on your machine; its title, username, url, ta
|
|
|
364
365
|
custom fields and the key name of every custom field (sealed ones too) stay readable by the server, so the standard `search_credentials`, `list_credentials` and
|
|
365
366
|
`find_credential_duplicates` keep working. Never put a secret into `notes`.
|
|
366
367
|
|
|
367
|
-
- `insert_sealed_credential`, `update_sealed_credential`, `delete_sealed_credential`: signed by the
|
|
368
|
-
- `move_sealed_credential` hands a credential to another seal root of its space; both roots must
|
|
369
|
-
|
|
368
|
+
- `insert_sealed_credential`, `update_sealed_credential`, `delete_sealed_credential`: signed by the credential's seal root.
|
|
369
|
+
- `move_sealed_credential` hands a credential to another seal root of its space; both roots must
|
|
370
|
+
open here. A sealed credential changes space only with its root: moving the root's folder to another
|
|
370
371
|
space takes its credentials along (a credential's group in the old space is left behind).
|
|
371
372
|
- `reveal_sealed_credential` and `get_sealed_totp` decrypt locally. The hosted `reveal_credential` and
|
|
372
373
|
`get_credential_totp` cannot open a sealed credential.
|
|
@@ -379,7 +380,7 @@ custom fields and the key name of every custom field (sealed ones too) stay read
|
|
|
379
380
|
sealed one; `in_other_spaces` counts those that need a seal root of their own space. It is
|
|
380
381
|
irreversible per credential: the server drops its own encrypted secrets and that credential's history.
|
|
381
382
|
|
|
382
|
-
1.
|
|
383
|
+
1. Run it with `dry_run: true` (the default). Nothing changes. It lists the
|
|
383
384
|
credentials, says which have notes, and flags notes that match a few secret patterns. That scan is
|
|
384
385
|
advisory and misses secrets.
|
|
385
386
|
2. Decide every note, because today notes are encrypted and afterwards they are either sealed or
|
|
@@ -473,8 +474,10 @@ there. Merged tasks are left out.
|
|
|
473
474
|
- Whatever a tool returns enters the local conversation transcript and is sent to the model provider.
|
|
474
475
|
The same holds for a secret passed as text; use a store or `secret_file`.
|
|
475
476
|
- A program running as your user can read a key file, ask the system keyring or the sidecar for a
|
|
476
|
-
secret, and read the memory of this client while a root
|
|
477
|
-
-
|
|
477
|
+
secret, and read the memory of this client while it holds a root open.
|
|
478
|
+
- Every session on this machine that uses these key stores can read every root whose secret is kept
|
|
479
|
+
here: there is no unlock step that would stop it.
|
|
480
|
+
- A session can be tricked by content it reads into copying sealed content elsewhere.
|
|
478
481
|
- An export file: it holds the sealed content of every root that opened under the passphrase of the
|
|
479
482
|
file, not under its seal roots.
|
|
480
483
|
- A compromised local machine, or a malicious server serving stale data.
|