@mstone6969/vault 0.2.0 → 0.5.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.
Files changed (58) hide show
  1. package/README.md +198 -6
  2. package/dist/crypto.d.ts +99 -2
  3. package/dist/crypto.d.ts.map +1 -1
  4. package/dist/errors.d.ts +122 -4
  5. package/dist/errors.d.ts.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +447 -33
  9. package/dist/index.js.map +9 -7
  10. package/dist/providers.d.ts +131 -0
  11. package/dist/providers.d.ts.map +1 -0
  12. package/dist/stores/file.d.ts +190 -0
  13. package/dist/stores/file.d.ts.map +1 -0
  14. package/dist/stores/file.js +1 -0
  15. package/dist/stores/memory.d.ts +98 -7
  16. package/dist/stores/memory.d.ts.map +1 -1
  17. package/dist/stores/sqlite.d.ts +140 -9
  18. package/dist/stores/sqlite.d.ts.map +1 -1
  19. package/dist/stores/sqlite.js +47 -12
  20. package/dist/stores/sqlite.js.map +3 -3
  21. package/dist/types.d.ts +408 -17
  22. package/dist/types.d.ts.map +1 -1
  23. package/dist/vault.d.ts +554 -19
  24. package/dist/vault.d.ts.map +1 -1
  25. package/docs/README.md +10 -0
  26. package/docs/index/README.md +48 -0
  27. package/docs/index/classes/FileStore.md +341 -0
  28. package/docs/index/classes/MemoryStore.md +240 -0
  29. package/docs/index/classes/Vault.md +805 -0
  30. package/docs/index/classes/VaultError.md +371 -0
  31. package/docs/index/classes/VaultKeyError.md +370 -0
  32. package/docs/index/functions/envKey.md +43 -0
  33. package/docs/index/functions/fileKey.md +46 -0
  34. package/docs/index/functions/generateKey.md +39 -0
  35. package/docs/index/functions/importKey.md +49 -0
  36. package/docs/index/functions/isKeyProvider.md +43 -0
  37. package/docs/index/functions/open.md +67 -0
  38. package/docs/index/functions/randomValue.md +53 -0
  39. package/docs/index/functions/seal.md +56 -0
  40. package/docs/index/functions/staticKey.md +39 -0
  41. package/docs/index/type-aliases/Generator.md +58 -0
  42. package/docs/index/type-aliases/HistoryEntry.md +65 -0
  43. package/docs/index/type-aliases/KeyProvider.md +74 -0
  44. package/docs/index/type-aliases/PutOptions.md +141 -0
  45. package/docs/index/type-aliases/RekeyReport.md +37 -0
  46. package/docs/index/type-aliases/RotationContext.md +49 -0
  47. package/docs/index/type-aliases/RotationPolicy.md +142 -0
  48. package/docs/index/type-aliases/SecretRecord.md +214 -0
  49. package/docs/index/type-aliases/SecretSummary.md +56 -0
  50. package/docs/index/type-aliases/VaultEvent.md +95 -0
  51. package/docs/index/type-aliases/VaultOptions.md +142 -0
  52. package/docs/index/type-aliases/VaultStore.md +156 -0
  53. package/docs/index/variables/DEFAULT_ALPHABET.md +29 -0
  54. package/docs/index/variables/DEFAULT_HISTORY_LIMIT.md +28 -0
  55. package/docs/index/variables/DEFAULT_PREFIX.md +23 -0
  56. package/docs/stores/sqlite/README.md +11 -0
  57. package/docs/stores/sqlite/classes/SqliteStore.md +307 -0
  58. package/package.json +15 -5
package/dist/vault.d.ts CHANGED
@@ -1,24 +1,266 @@
1
- import type { SecretSummary, VaultStore } from "./types";
2
- /** How a stored value is referenced from configuration. */
1
+ import { type KeyProvider } from "./providers";
2
+ import type { PutOptions, SecretSummary, VaultEvent, VaultStore } from "./types";
3
+ /**
4
+ * How a stored value is referenced from configuration.
5
+ *
6
+ * @remarks
7
+ * {@link Vault.resolve} substitutes any value that starts with this. Give a
8
+ * vault its own {@link VaultOptions.prefix} when `@vault:` already means
9
+ * something else in the configuration you are resolving.
10
+ *
11
+ * @defaultValue `"@vault:"`
12
+ */
3
13
  export declare const DEFAULT_PREFIX = "@vault:";
14
+ /**
15
+ * How many previous values an entry keeps, unless you say otherwise.
16
+ *
17
+ * @remarks
18
+ * Only {@link Vault.rotate} adds to history, so this is how many superseded
19
+ * values {@link Vault.versions} can still hand back.
20
+ *
21
+ * @defaultValue 5
22
+ * @see {@link VaultOptions.historyLimit}
23
+ */
24
+ export declare const DEFAULT_HISTORY_LIMIT = 5;
25
+ /**
26
+ * What a random rotation draws from unless the policy says otherwise.
27
+ *
28
+ * @remarks
29
+ * Letters and digits only, so a generated value survives being pasted into a
30
+ * shell command or a connection string without quoting. Set
31
+ * {@link RotationPolicy.alphabet} for anything narrower or wider.
32
+ *
33
+ * @defaultValue A-Z, a-z and 0-9: 62 characters
34
+ * @see {@link randomValue}
35
+ */
36
+ export declare const DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
37
+ /**
38
+ * Everything a generator is told: which entry is being rotated and the
39
+ * non-secret arguments its policy carries. Deliberately not the current value —
40
+ * a generator that needs it can ask the vault for it.
41
+ *
42
+ * @see {@link Generator}, {@link RotationPolicy.arguments}
43
+ */
44
+ export type RotationContext = {
45
+ /** Whose entry is being rotated. */
46
+ owner: string;
47
+ /** Which entry is being rotated. */
48
+ name: string;
49
+ /** The non-secret arguments its policy carries. */
50
+ arguments: Record<string, string>;
51
+ };
52
+ /**
53
+ * Produces the next value for an entry whose policy names it.
54
+ *
55
+ * @remarks
56
+ * Registered under a name in {@link VaultOptions.generators} and asked for by
57
+ * {@link RotationPolicy.generator}. Use one where the vault cannot invent the
58
+ * value itself — a key only the far end can mint, or a password that has to be
59
+ * set on a database before it means anything.
60
+ *
61
+ * @param context Which entry is being rotated and its policy's arguments. Not
62
+ * the value being replaced.
63
+ * @returns The next value, or a promise of it.
64
+ *
65
+ * @example Minting a key at the provider that issues it
66
+ * ```ts
67
+ * const vault = new Vault({
68
+ * key: generateKey(),
69
+ * store: new MemoryStore(),
70
+ * generators: {
71
+ * provider: async ({ arguments: args }) => mintKeyFor(args.account),
72
+ * },
73
+ * })
74
+ *
75
+ * await vault.put("alice", "api", "old-key", {
76
+ * rotation: {
77
+ * kind: "generator",
78
+ * generator: "provider",
79
+ * arguments: { account: "acct_123" },
80
+ * },
81
+ * })
82
+ * await vault.rotate("alice", "api")
83
+ * ```
84
+ */
85
+ export type Generator = (context: RotationContext) => Promise<string> | string;
86
+ /**
87
+ * A random string of `length` characters drawn from `alphabet`.
88
+ *
89
+ * Sampling is rejected rather than folded with `%`, so every character is
90
+ * equally likely however odd the alphabet's length.
91
+ *
92
+ * @param length How many characters to produce. Defaults to 32.
93
+ * @param alphabet The characters to draw from. Defaults to
94
+ * {@link DEFAULT_ALPHABET}.
95
+ * @returns A fresh string of exactly `length` characters.
96
+ * @throws {@link VaultError} 422 when `length` is below one, or `alphabet` has
97
+ * fewer than two characters — neither can produce anything unguessable.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * randomValue() // 32 characters of A-Z, a-z, 0-9
102
+ * randomValue(16, "0123456789abcdef") // 16 hex characters
103
+ * ```
104
+ *
105
+ * @see {@link RotationPolicy} to have the vault call this during a rotation.
106
+ */
107
+ export declare function randomValue(length?: number, alphabet?: string): string;
108
+ /**
109
+ * Everything a {@link Vault} is built from.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * import { Vault, MemoryStore, envKey } from "@mstone6969/vault"
114
+ *
115
+ * const vault = new Vault({
116
+ * key: envKey("VAULT_KEY"),
117
+ * store: new MemoryStore(),
118
+ * historyLimit: 2,
119
+ * onAccess: (event) => console.log(event.action, event.owner, event.name),
120
+ * })
121
+ * ```
122
+ */
4
123
  export type VaultOptions = {
5
- /** Base64 key from `generateKey()`, or an already imported CryptoKey. */
6
- key: string | CryptoKey;
124
+ /** The master key: base64, already imported, or a provider that finds one. */
125
+ key: string | CryptoKey | KeyProvider;
126
+ /** Where records are kept. */
7
127
  store: VaultStore;
8
- /** Reference prefix, `@vault:` unless you say otherwise. */
128
+ /**
129
+ * Reference prefix, `@vault:` unless you say otherwise.
130
+ *
131
+ * @defaultValue {@link DEFAULT_PREFIX}
132
+ */
9
133
  prefix?: string;
134
+ /**
135
+ * Keys this vault will still open values with, but never seal under.
136
+ *
137
+ * Keep the old key here while a `rekey` is in flight, or after one that did
138
+ * not finish: values left under it stay readable instead of becoming
139
+ * unopenable the moment the primary key changes.
140
+ *
141
+ * @defaultValue none
142
+ * @see {@link Vault.rekey}
143
+ */
144
+ previousKeys?: (string | CryptoKey | KeyProvider)[];
145
+ /**
146
+ * How many previous values `rotate` keeps.
147
+ *
148
+ * @defaultValue {@link DEFAULT_HISTORY_LIMIT}
149
+ */
150
+ historyLimit?: number;
151
+ /**
152
+ * Functions that mint new values, by the name a rotation policy uses.
153
+ *
154
+ * The vault stores the *name*, never the function — so what is written down
155
+ * is that an entry can be rotated, not how to impersonate the thing that
156
+ * rotates it.
157
+ *
158
+ * @defaultValue none, so a `generator` policy fails with 501
159
+ */
160
+ generators?: Record<string, Generator>;
161
+ /**
162
+ * Called after everything the vault does, for an audit trail. It is never
163
+ * awaited and its failures are ignored — logging must not break a vault.
164
+ *
165
+ * @see {@link VaultEvent} for what it is told, including the refusals,
166
+ * which arrive as `denied` with a `detail` saying which rule was hit.
167
+ */
168
+ onAccess?: (event: VaultEvent) => void;
169
+ };
170
+ /**
171
+ * What a `rekey` did, and to what it could not do it.
172
+ *
173
+ * @see {@link Vault.rekey}
174
+ */
175
+ export type RekeyReport = {
176
+ /** How many entries were re-sealed under the new key. */
177
+ rekeyed: number;
178
+ /** Entries that would not open, by `owner/name`, left exactly as they were. */
179
+ failed: string[];
10
180
  };
11
181
  /**
12
- * A write-only credential store: values go in, and only `open` and `resolve`
13
- * take them out again. Listing never exposes a value, so a vault can back an
14
- * API without a reveal endpoint.
182
+ * A write-only credential store: values go in, and only `open`, `read` and
183
+ * `resolve` take them out again.
184
+ *
185
+ * Every value is sealed under its own data key, and only that key is sealed
186
+ * under the master key. Changing the master key therefore re-seals a handful of
187
+ * bytes per entry rather than every value, and one exposed data key exposes one
188
+ * value rather than all of them.
189
+ *
190
+ * @example Storing a credential and handing it to the thing that needs it
191
+ * ```ts
192
+ * import { Vault, MemoryStore, generateKey } from "@mstone6969/vault"
193
+ *
194
+ * const vault = new Vault({ key: generateKey(), store: new MemoryStore() })
195
+ *
196
+ * await vault.put("alice", "stripe_key", "sk_live_x", {
197
+ * metadata: { kind: "api" },
198
+ * })
199
+ *
200
+ * // Nothing but open, read and resolve gets the value back out.
201
+ * await vault.list("alice") // name, metadata, dates — no value
202
+ * await vault.open("alice", "stripe_key")
203
+ * await vault.resolve("alice", { STRIPE_KEY: "@vault:stripe_key" })
204
+ * ```
205
+ *
206
+ * @see {@link VaultOptions} for what it is built from, {@link VaultStore} for
207
+ * where the records go, and {@link VaultError} for what it throws.
15
208
  */
16
209
  export declare class Vault {
17
- private readonly key;
210
+ private keySource;
211
+ private previousSources;
212
+ /** Resolved on first use, not at construction: a provider may need to wait,
213
+ * or fail, and a vault nobody uses should do neither. */
214
+ private keyCache;
215
+ private previousCache;
18
216
  private readonly store;
217
+ private readonly historyLimit;
218
+ private readonly generators;
219
+ private readonly onAccess;
220
+ /** The prefix {@link Vault.resolve} treats as a reference. */
19
221
  readonly prefix: string;
20
- constructor({ key, store, prefix }: VaultOptions);
21
- /** Names and dates for one owner. Never values. */
222
+ /**
223
+ * @param options The key, the store and the policies this vault applies.
224
+ * Nothing is contacted here: the key is resolved on first use, so a vault
225
+ * built from a provider that is slow or unreachable costs nothing until
226
+ * something asks it for a value.
227
+ */
228
+ constructor({ key, store, prefix, previousKeys, historyLimit, generators, onAccess, }: VaultOptions);
229
+ private master;
230
+ private retired;
231
+ private record;
232
+ /** Opens something with the master key, falling back to retired ones. */
233
+ private unsealWithMaster;
234
+ /** Opens a value: its data key first, then the value under that key. */
235
+ private unseal;
236
+ /** Seals a value under a fresh data key, and that key under the master. */
237
+ private enseal;
238
+ private checkName;
239
+ /**
240
+ * Everything an owner holds, sorted by name. Never a sealed value.
241
+ *
242
+ * @param owner Whose entries to list.
243
+ * @returns One summary per entry, in name order. A sealed value is absent
244
+ * entirely; an entry stored in the open carries its value in `value`.
245
+ *
246
+ * @remarks
247
+ * Expiry hides nothing here: an entry past its `expiresAt` is still listed,
248
+ * and still refuses to open, until {@link Vault.purgeExpired} clears it.
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * await vault.put("alice", "stripe_key", "sk_live_x", {
253
+ * metadata: { kind: "api" },
254
+ * })
255
+ *
256
+ * const [entry] = await vault.list("alice")
257
+ * entry.name // "stripe_key"
258
+ * entry.metadata // { kind: "api" }
259
+ * entry.versions // 0
260
+ * ```
261
+ *
262
+ * @see {@link SecretSummary}
263
+ */
22
264
  list(owner: string): Promise<SecretSummary[]>;
23
265
  /**
24
266
  * Stores a value, replacing whatever was under that name.
@@ -26,25 +268,318 @@ export declare class Vault {
26
268
  * `metadata` is kept in the clear and comes back from `list`, so it must
27
269
  * hold nothing secret — a credential's kind, or the username it belongs to,
28
270
  * not the password.
271
+ *
272
+ * @param owner Whose entry it is. Owners never see each other's entries.
273
+ * @param name What to call it: 1-64 characters of letters, numbers, dot,
274
+ * dash or underscore. Surrounding whitespace is trimmed.
275
+ * @param value The value to store.
276
+ * @param options Metadata, expiry, rotation policy, and whether the entry
277
+ * is sealed, final, or keeps what it replaces. An option left out is
278
+ * inherited from the existing entry.
279
+ * @returns The stored entry, summarised — never its sealed value.
280
+ * @throws {@link VaultError} 422 when the name is not 1-64 characters of
281
+ * letters, numbers, dot, dash or underscore, or the value is empty.
282
+ * @throws {@link VaultError} 409 when the entry is already there and
283
+ * `final`: it can be deleted, never replaced.
284
+ *
285
+ * @example Replacing a value without restating what the entry is
286
+ * ```ts
287
+ * await vault.put("alice", "db", "first-password", {
288
+ * metadata: { kind: "login", username: "ada" },
289
+ * rotation: { kind: "random", length: 24 },
290
+ * })
291
+ *
292
+ * // Still a login, still rotatable at 24 characters, still sealed.
293
+ * await vault.put("alice", "db", "second-password")
294
+ * ```
295
+ *
296
+ * @see {@link Vault.rotate} to replace a value and keep the old one,
297
+ * {@link PutOptions} for the rest of the options.
298
+ */
299
+ put(owner: string, name: string, value: string, options?: PutOptions): Promise<SecretSummary>;
300
+ /**
301
+ * Replaces a value, keeping the one it replaces.
302
+ *
303
+ * Called without a value, the entry's rotation policy produces one — which
304
+ * is the point of storing a policy: whatever runs the rotation is told how
305
+ * to make the next password, never what the current one is.
306
+ *
307
+ * Previous values stay openable, so a job that read the credential moments
308
+ * before a rotation can still finish on what it was given.
309
+ *
310
+ * @param owner Whose entry to rotate.
311
+ * @param name The entry to rotate.
312
+ * @param value The new value. Left out, the entry's rotation policy makes
313
+ * one.
314
+ * @param options As {@link Vault.put}, minus `keepHistory`: a rotation
315
+ * always keeps what it replaced, up to {@link VaultOptions.historyLimit}.
316
+ * @returns The rotated entry, summarised, with `rotatedAt` stamped.
317
+ * @throws {@link VaultError} 404 when no value is given and there is no
318
+ * such entry to take a policy from.
319
+ * @throws {@link VaultError} 410 when no value is given and the entry has
320
+ * expired.
321
+ * @throws {@link VaultError} 422 when no value is given and the entry has
322
+ * no rotation policy to make one with.
323
+ * @throws {@link VaultError} 501 when the policy names a generator this
324
+ * vault was not constructed with.
325
+ * @throws {@link VaultError} 409 when the entry is final.
326
+ *
327
+ * @example
328
+ * ```ts
329
+ * await vault.put("alice", "db", "first-password", {
330
+ * rotation: { kind: "random", length: 24 },
331
+ * })
332
+ *
333
+ * const rotated = await vault.rotate("alice", "db")
334
+ * rotated.rotatedAt // stamped just now
335
+ * await vault.versions("alice", "db") // ["first-password"]
336
+ * ```
337
+ *
338
+ * @see {@link Vault.rotationDue} for which entries are asking for this.
339
+ */
340
+ rotate(owner: string, name: string, value?: string, options?: Omit<PutOptions, "keepHistory">): Promise<SecretSummary>;
341
+ /** The next value an entry's policy calls for. */
342
+ private generate;
343
+ /**
344
+ * Entries whose policy says how often they want rotating, and whose time
345
+ * has come. Nothing rotates them for you — schedule this and act on it.
346
+ *
347
+ * @param now The moment to judge against. Pass a later one to ask what will
348
+ * be due by then.
349
+ * @returns Summaries of every entry, whoever owns it, whose
350
+ * {@link RotationPolicy.every} seconds have passed since it was last
351
+ * rotated — or since it was stored, if it never has been.
352
+ *
353
+ * @remarks
354
+ * One of the three calls that reach across owners, so it belongs to
355
+ * whatever runs the schedule rather than to a request.
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * await vault.put("alice", "db", "x", {
360
+ * rotation: { kind: "random", every: 86_400 },
361
+ * })
362
+ *
363
+ * for (const entry of await vault.rotationDue()) {
364
+ * await vault.rotate(entry.owner, entry.name)
365
+ * }
366
+ * ```
367
+ */
368
+ rotationDue(now?: Date): Promise<SecretSummary[]>;
369
+ /**
370
+ * Previous values of an entry, newest first, opened.
371
+ *
372
+ * @param owner Whose entry it is.
373
+ * @param name The entry to look back through.
374
+ * @returns The values it used to hold, newest first, in the clear. Empty
375
+ * for an entry that has never been rotated.
376
+ * @throws {@link VaultError} 404 when there is no such entry.
377
+ * @throws {@link VaultError} 410 when the entry has expired.
378
+ * @throws {@link VaultError} 422 when the name is not a legal one.
379
+ * @throws {@link VaultKeyError} 500 when a kept value will not open under
380
+ * the master key or any of {@link VaultOptions.previousKeys}.
381
+ *
382
+ * @example
383
+ * ```ts
384
+ * await vault.put("alice", "deploy", "v1")
385
+ * await vault.rotate("alice", "deploy", "v2")
386
+ * await vault.rotate("alice", "deploy", "v3")
387
+ *
388
+ * await vault.versions("alice", "deploy") // ["v2", "v1"]
389
+ * ```
390
+ */
391
+ versions(owner: string, name: string): Promise<string[]>;
392
+ /**
393
+ * True when the owner has a secret under that name, expired or not.
394
+ *
395
+ * @param owner Whose entry to look for.
396
+ * @param name The name to look for.
397
+ * @returns Whether a record exists under it.
398
+ * @throws {@link VaultError} 422 when the name is not a legal one.
399
+ *
400
+ * @remarks
401
+ * Opens nothing and is not stopped by expiry, so it answers "is this name
402
+ * taken" rather than "can this value still be used".
29
403
  */
30
- put(owner: string, name: string, value: string, metadata?: Record<string, string>): Promise<SecretSummary>;
31
- /** True when the owner has a secret under that name. */
32
404
  has(owner: string, name: string): Promise<boolean>;
33
- /** Removes a secret, returning false if it wasn't there. */
405
+ /**
406
+ * Removes a secret, returning false if it wasn't there.
407
+ *
408
+ * @param owner Whose entry to delete.
409
+ * @param name The entry to delete.
410
+ * @returns True when something was deleted, false when there was nothing
411
+ * under that name.
412
+ * @throws {@link VaultError} 422 when the name is not a legal one.
413
+ *
414
+ * @remarks
415
+ * Deleting takes the kept previous values with it, and it is the one thing
416
+ * a `final` entry allows.
417
+ */
34
418
  remove(owner: string, name: string): Promise<boolean>;
419
+ private require;
35
420
  /**
36
- * Reads one value back. The only way plaintext leaves the vault — keep it
37
- * in memory and out of logs and responses.
421
+ * Reads one value back. The only way plaintext leaves a sealed entry — keep
422
+ * it in memory and out of logs and responses.
423
+ *
424
+ * @param owner Whose entry to open.
425
+ * @param name The entry to open.
426
+ * @returns The value, sealed or not.
427
+ * @throws {@link VaultError} 404 when there is no such entry.
428
+ * @throws {@link VaultError} 410 when the entry has expired. The record is
429
+ * still there; it just cannot be used.
430
+ * @throws {@link VaultError} 422 when the name is not a legal one.
431
+ * @throws {@link VaultKeyError} 500 when the value will not open under the
432
+ * master key or any of {@link VaultOptions.previousKeys} — a wrong key or
433
+ * an altered value, which GCM cannot tell apart.
434
+ *
435
+ * @example
436
+ * ```ts
437
+ * await vault.put("alice", "token", "shhh")
438
+ * await vault.open("alice", "token") // "shhh"
439
+ * ```
440
+ *
441
+ * @see {@link Vault.read} for entries stored in the open,
442
+ * {@link Vault.resolve} for substituting several at once.
38
443
  */
39
444
  open(owner: string, name: string): Promise<string>;
445
+ /**
446
+ * Reads an entry stored in the open. A sealed one refuses.
447
+ *
448
+ * @param owner Whose entry to read.
449
+ * @param name The entry to read.
450
+ * @returns The value, which was stored with `open: true` and was therefore
451
+ * never secret.
452
+ * @throws {@link VaultError} 403 when the entry is sealed.
453
+ * {@link Vault.open} is the only way a sealed value comes out.
454
+ * @throws {@link VaultError} 404 when there is no such entry.
455
+ * @throws {@link VaultError} 410 when the entry has expired.
456
+ * @throws {@link VaultError} 422 when the name is not a legal one.
457
+ *
458
+ * @example
459
+ * ```ts
460
+ * await vault.put("alice", "region", "eu-west-1", { open: true })
461
+ * await vault.read("alice", "region") // "eu-west-1"
462
+ *
463
+ * await vault.put("alice", "token", "shhh")
464
+ * await vault.read("alice", "token") // throws: 403, sealed
465
+ * ```
466
+ */
467
+ read(owner: string, name: string): Promise<string>;
40
468
  /**
41
469
  * Substitutes `@vault:<name>` references in a set of values — an
42
470
  * environment, a config object — leaving everything else alone.
43
471
  *
44
- * A reference to a secret that isn't there throws: running with a blank
45
- * credential is worse than not running.
472
+ * A reference to a secret that isn't there, or that has expired, throws:
473
+ * running with a blank credential is worse than not running.
474
+ *
475
+ * @param owner Whose entries the references name.
476
+ * @param values The set to substitute into. Not modified.
477
+ * @returns A copy with every reference replaced by its value, or the same
478
+ * object back when nothing in it is a reference.
479
+ * @throws {@link VaultError} 404 when a reference names no entry.
480
+ * @throws {@link VaultError} 410 when a referenced entry has expired.
481
+ * @throws {@link VaultError} 422 when what follows the prefix is not a
482
+ * legal name.
483
+ *
484
+ * @remarks
485
+ * Whitespace around the name is ignored, so `"@vault: token "` finds
486
+ * `token`. Sealed and open entries both resolve; the prefix comes from
487
+ * {@link Vault.prefix}.
488
+ *
489
+ * @example Filling in an environment before spawning something
490
+ * ```ts
491
+ * await vault.put("alice", "token", "secret")
492
+ *
493
+ * await vault.resolve("alice", {
494
+ * PLAIN: "kept",
495
+ * API_TOKEN: "@vault:token",
496
+ * })
497
+ * // { PLAIN: "kept", API_TOKEN: "secret" }
498
+ * ```
46
499
  */
47
500
  resolve(owner: string, values: Record<string, string>): Promise<Record<string, string>>;
48
- private checkName;
501
+ /**
502
+ * Re-seals values under fresh data keys, without changing the master key.
503
+ *
504
+ * Cheap hygiene: the ciphertext of an unchanged secret stops being
505
+ * comparable between two copies of the database taken at different times.
506
+ *
507
+ * @param owner Limit it to one owner's entries. Left out, it walks the
508
+ * whole store, whoever owns it.
509
+ * @returns How many entries were re-sealed. Entries stored in the open hold
510
+ * nothing to re-seal and are skipped.
511
+ * @throws {@link VaultKeyError} 500 when a value will not open. Unlike
512
+ * {@link Vault.rekey} this stops there, having already re-sealed the
513
+ * entries it got to — those are unharmed, since the key did not change.
514
+ *
515
+ * @example
516
+ * ```ts
517
+ * await vault.reseal("alice") // just this owner
518
+ * await vault.reseal() // the whole store
519
+ * ```
520
+ */
521
+ reseal(owner?: string): Promise<number>;
522
+ /**
523
+ * Re-seals every data key under a new master key.
524
+ *
525
+ * The old key is kept as a fallback for the rest of this vault's life, so a
526
+ * run that stops halfway leaves a mix that still opens. Construct the next
527
+ * vault with `key: next, previousKeys: [old]` until you are confident, then
528
+ * drop the old one.
529
+ *
530
+ * An entry that will not open is left untouched and named in the report,
531
+ * because re-sealing what you cannot read would only destroy it.
532
+ *
533
+ * @param next The new master key: base64, already imported, or a provider
534
+ * that finds one.
535
+ * @returns How many entries moved, and which would not, by `owner/name`.
536
+ * @throws {@link VaultKeyError} 500 when `next` is not a usable key. It is
537
+ * resolved before anything is written, so nothing has changed.
538
+ *
539
+ * @remarks
540
+ * Walks every owner, and changes this vault as it goes: from here on it
541
+ * seals under `next` and keeps the key it had as a fallback. An entry
542
+ * stored in the open holds no key and is skipped, and one written before
543
+ * envelope encryption is given a data key on the way past.
544
+ *
545
+ * @example
546
+ * ```ts
547
+ * const next = generateKey()
548
+ * const report = await vault.rekey(next)
549
+ * report.rekeyed // 2
550
+ * report.failed // ["alice/stranger"] — left exactly as they were
551
+ *
552
+ * // Until the failures are dealt with, keep the old key readable.
553
+ * const moved = new Vault({ key: next, store, previousKeys: [old] })
554
+ * ```
555
+ *
556
+ * @see {@link RekeyReport}, {@link VaultOptions.previousKeys}
557
+ */
558
+ rekey(next: string | CryptoKey | KeyProvider): Promise<RekeyReport>;
559
+ /** Moves an entry's kept values onto the new master key alongside it. */
560
+ private rekeyHistory;
561
+ /**
562
+ * Deletes entries whose time is up. Returns how many went.
563
+ *
564
+ * @param now The moment to judge against. Pass a later one to see what a
565
+ * run then would take.
566
+ * @returns How many entries were deleted.
567
+ *
568
+ * @remarks
569
+ * Walks every owner. Expiry stops an entry being used, not being stored:
570
+ * until this runs, an expired entry is still there and still listed.
571
+ *
572
+ * @example
573
+ * ```ts
574
+ * await vault.put("alice", "temporary", "x", {
575
+ * expiresAt: new Date(Date.now() - 1),
576
+ * })
577
+ *
578
+ * await vault.open("alice", "temporary") // throws: 410, expired
579
+ * await vault.purgeExpired() // 1
580
+ * await vault.has("alice", "temporary") // false
581
+ * ```
582
+ */
583
+ purgeExpired(now?: Date): Promise<number>;
49
584
  }
50
585
  //# sourceMappingURL=vault.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAExD,2DAA2D;AAC3D,eAAO,MAAM,cAAc,YAAY,CAAA;AAIvC,MAAM,MAAM,YAAY,GAAG;IACvB,yEAAyE;IACzE,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IACvB,KAAK,EAAE,UAAU,CAAA;IACjB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;;;GAIG;AACH,qBAAa,KAAK;IACd,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAEX,EAAE,GAAG,EAAE,KAAK,EAAE,MAAuB,EAAE,EAAE,YAAY;IAMjE,mDAAmD;IAC7C,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAOnD;;;;;;OAMG;IACG,GAAG,CACL,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACtC,OAAO,CAAC,aAAa,CAAC;IAczB,wDAAwD;IAClD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxD,4DAA4D;IAC5D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrD;;;OAGG;IACG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMxD;;;;;;OAMG;IACG,OAAO,CACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAalC,OAAO,CAAC,SAAS;CASpB"}
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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"}
package/docs/README.md ADDED
@@ -0,0 +1,10 @@
1
+ **@mstone6969/vault**
2
+
3
+ ***
4
+
5
+ # @mstone6969/vault
6
+
7
+ ## Modules
8
+
9
+ - [index](index/README.md)
10
+ - [stores/sqlite](stores/sqlite/README.md)
@@ -0,0 +1,48 @@
1
+ [**@mstone6969/vault**](../README.md)
2
+
3
+ ***
4
+
5
+ [@mstone6969/vault](../README.md) / index
6
+
7
+ # index
8
+
9
+ ## Classes
10
+
11
+ - [VaultError](classes/VaultError.md)
12
+ - [VaultKeyError](classes/VaultKeyError.md)
13
+ - [FileStore](classes/FileStore.md)
14
+ - [MemoryStore](classes/MemoryStore.md)
15
+ - [Vault](classes/Vault.md)
16
+
17
+ ## Type Aliases
18
+
19
+ - [KeyProvider](type-aliases/KeyProvider.md)
20
+ - [HistoryEntry](type-aliases/HistoryEntry.md)
21
+ - [RotationPolicy](type-aliases/RotationPolicy.md)
22
+ - [SecretRecord](type-aliases/SecretRecord.md)
23
+ - [SecretSummary](type-aliases/SecretSummary.md)
24
+ - [PutOptions](type-aliases/PutOptions.md)
25
+ - [VaultStore](type-aliases/VaultStore.md)
26
+ - [VaultEvent](type-aliases/VaultEvent.md)
27
+ - [RotationContext](type-aliases/RotationContext.md)
28
+ - [Generator](type-aliases/Generator.md)
29
+ - [VaultOptions](type-aliases/VaultOptions.md)
30
+ - [RekeyReport](type-aliases/RekeyReport.md)
31
+
32
+ ## Variables
33
+
34
+ - [DEFAULT\_PREFIX](variables/DEFAULT_PREFIX.md)
35
+ - [DEFAULT\_HISTORY\_LIMIT](variables/DEFAULT_HISTORY_LIMIT.md)
36
+ - [DEFAULT\_ALPHABET](variables/DEFAULT_ALPHABET.md)
37
+
38
+ ## Functions
39
+
40
+ - [generateKey](functions/generateKey.md)
41
+ - [importKey](functions/importKey.md)
42
+ - [seal](functions/seal.md)
43
+ - [open](functions/open.md)
44
+ - [staticKey](functions/staticKey.md)
45
+ - [envKey](functions/envKey.md)
46
+ - [fileKey](functions/fileKey.md)
47
+ - [isKeyProvider](functions/isKeyProvider.md)
48
+ - [randomValue](functions/randomValue.md)