@coffre/vault 0.14.5 → 0.15.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 (43) hide show
  1. package/README.md +6 -4
  2. package/dist/cloudflare.d.ts +341 -4
  3. package/dist/cloudflare.js +802 -6
  4. package/dist/index.js +1 -1
  5. package/dist/log-entry-BYWqj3O5.d.ts +364 -0
  6. package/dist/node.d.ts +181 -6
  7. package/dist/node.js +358 -16
  8. package/dist/{src-Bg0ZeMJW.js → src-DheG92EM.js} +1 -1
  9. package/dist/vault-kmtojYnW.js +14623 -0
  10. package/package.json +4 -5
  11. package/src/bulk.ts +89 -0
  12. package/src/changes.ts +478 -0
  13. package/src/checkpointer-node.ts +342 -0
  14. package/src/checkpointer-workers.ts +514 -0
  15. package/src/checkpointer.ts +1165 -0
  16. package/src/cloudflare.ts +207 -13
  17. package/src/config.ts +3 -2
  18. package/src/credentials.postgres.ts +78 -0
  19. package/src/credentials.sqlite.ts +240 -0
  20. package/src/credentials.ts +630 -0
  21. package/src/cutover.ts +275 -0
  22. package/src/decide.ts +437 -2
  23. package/src/inputs.ts +499 -0
  24. package/src/local.ts +20 -4
  25. package/src/log-checkpointer.ts +116 -0
  26. package/src/log-entry.ts +113 -0
  27. package/src/log-replay.ts +188 -0
  28. package/src/log-store.ts +695 -0
  29. package/src/node.ts +73 -9
  30. package/src/objects.ts +141 -0
  31. package/src/reads.ts +502 -0
  32. package/src/retention.ts +229 -0
  33. package/src/snapshot.postgres.ts +39 -9
  34. package/src/snapshot.sqlite.ts +28 -3
  35. package/src/snapshot.ts +64 -10
  36. package/src/store.ts +832 -36
  37. package/src/use.postgres.ts +111 -0
  38. package/src/use.sqlite.ts +542 -0
  39. package/src/use.ts +1060 -0
  40. package/src/vault.ts +1366 -220
  41. package/src/verifier.ts +136 -0
  42. package/src/witness-workers.ts +163 -0
  43. package/dist/vault-Cbi45kLB.js +0 -5197
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as verifyCheckpoint, i as checkpointMessage, n as KekUnavailableError, r as awsKms, s as derivedSigningKey, t as KekBadClaimError } from "./src-Bg0ZeMJW.js";
1
+ import { a as verifyCheckpoint, i as checkpointMessage, n as KekUnavailableError, r as awsKms, s as derivedSigningKey, t as KekBadClaimError } from "./src-DheG92EM.js";
2
2
  export { KekBadClaimError, KekUnavailableError, awsKms, checkpointMessage, derivedSigningKey, verifyCheckpoint };
@@ -0,0 +1,364 @@
1
+ import { Sealing } from "@coffre/core/vault";
2
+ import { Hash, LogEntry, LogReader, Pair, PairNotes, PairProofs, PairSigners, PairVerifiers, StoredPair, Tile, TreeName, WitnessAnswer } from "@coffre/core/merkle";
3
+ //#region src/checkpointer.d.ts
4
+ /**
5
+ * 0.15's checkpointer (docs/design/audit-tree.md): every tick it seals what
6
+ * has committed into the two trees, signs both heads as one pair, remembers
7
+ * the pair outside the database, writes it, and hands it to the witnesses.
8
+ * The rest of the vault sees one seam: `tick()` (the window in, a signed pair
9
+ * out), `sealThrough(number)` for a change that waits for its seal (R51), and
10
+ * `prove(number)`. The trees' shape stays behind it.
11
+ *
12
+ * What it stands on, each a protection formal/Seal.tla shows necessary:
13
+ * - the barrier (`close`): every number at or below the `upto` it returns has
14
+ * ended, committed or not, so a window is final once read;
15
+ * - the signer's memory (`Memory`, core, R54): the last pair it signed, kept
16
+ * where a database writer cannot reach, written before the database
17
+ * (RememberFirst) and checked at every open, so neither a tail cut nor a
18
+ * rollback gets a second history signed;
19
+ * - each tick compares the database's newest row with the pair it holds
20
+ * (RowChecked), so a deleted row shows within a tick, traffic or not;
21
+ * - nothing is sealed over an entry that fails its MAC (`authentic`), is out
22
+ * of its window, or is much older than the pair it holds (a replay).
23
+ *
24
+ * And nothing it awaits is unbounded (T169): a tick that runs past
25
+ * `TICK_BOUND_MS` stalls the checkpointer, closed for good behind its
26
+ * `Fence`, so that its host opens another while the database still holds
27
+ * the old one's statement (Seal.tla's Fenced); a change waits at most
28
+ * `SEAL_WAIT_MS` for its seal, then answers `Unsealed`.
29
+ */
30
+ /** What the checkpointer needs of the database. Postgres and SQLite implement it; so do the tests. */
31
+ type CheckpointStore = LogReader & {
32
+ /**
33
+ * The barrier, then the window: `upto`, such that every number at or below
34
+ * it has ended; the entries in (after, upto], by number; and the newest
35
+ * pair, as `newest()` reads it. The window is read after the barrier's lock
36
+ * is let go, in a snapshot of its own (`close_one`, then one read): a
37
+ * snapshot from before the lock would miss what committed while it waited.
38
+ *
39
+ * The barrier also raises the floor of `at` (`close_one`): `floor.found` is
40
+ * the floor it found, null if none, and `floor.set` the one it left. The
41
+ * window's uses are read from `from`, the floor the signer holds, as the
42
+ * last barrier set it. `floor.ceiling` is the database's clock less the
43
+ * use deadline, read once the barrier has run: the highest floor any
44
+ * barrier before it can have left (HotPathOne.tla's BarrierAcceptsLate).
45
+ *
46
+ * With `limit` (T169), the window's first `limit` entries by number, and
47
+ * `more` when there were more: `more.above`, the lowest `at` of the uses
48
+ * the window holds above the last of them, from `from` on (null: none).
49
+ * `barrier`, told the barrier's `upto` before the window is read.
50
+ */
51
+ close(after: bigint, from: number, limit?: number, barrier?: (upto: bigint) => void): Promise<{
52
+ upto: bigint;
53
+ newest: StoredPair | null;
54
+ entries: LogEntry[];
55
+ floor: {
56
+ found: number | null;
57
+ set: number;
58
+ ceiling: number;
59
+ };
60
+ more?: {
61
+ above: number | null;
62
+ } | null;
63
+ }>;
64
+ /**
65
+ * The newest pair: each tree's own newest note, `upto` the larger of theirs
66
+ * (Seal.tla's PairChecked). Stored as one row a pair, that is the newest
67
+ * row; stored as a row a tree, never one tree's newest row for both, since
68
+ * deleting the other tree's newest row would then pass unseen. Notes of two
69
+ * places then fail every check that wants one pair.
70
+ */
71
+ newest(): Promise<StoredPair | null>;
72
+ /**
73
+ * One statement: both trees' tiles and both notes (PairWrittenTogether). No
74
+ * state holds one head without the other.
75
+ */
76
+ write(row: StoredPair, tiles: Record<TreeName, Tile[]>): Promise<void>;
77
+ /**
78
+ * Entry `number` and its leaf index in its tree, if it is in the log; a
79
+ * use found at `at` when the caller knows its time, which spares a scan
80
+ * of its window's time. With `tree`, an entry in the other is refused
81
+ * (`misplaced`) before its index is sought.
82
+ */
83
+ locate(number: bigint, options?: Locating): Promise<{
84
+ entry: LogEntry;
85
+ index: number;
86
+ } | null>;
87
+ };
88
+ /**
89
+ * The signer's memory: the one record holding the last pair it signed. A
90
+ * Durable Object's storage on Workers, a synced file beside a Node vault.
91
+ */
92
+ type Memory = {
93
+ last(): Promise<string | null>;
94
+ remember(record: string): Promise<void>;
95
+ };
96
+ /** What a proof asks of `locate`: the use's time, if known, and the tree the entry must be in. */
97
+ type Locating = {
98
+ at?: number;
99
+ tree?: TreeName;
100
+ };
101
+ /** Somewhere each signed pair goes that a database writer cannot reach: a witness, an archive (R40's plugin). */
102
+ type Witness = {
103
+ name: string;
104
+ /**
105
+ * Publishes the pair; `proofs(from)` gives each tree's consistency proof
106
+ * from the sizes the witness holds. A try that outlives its bound (2 s) is
107
+ * a failure to the pump, and `signal` aborts: an implementation that makes
108
+ * a request passes it on, so abandoned tries do not pile up.
109
+ */
110
+ publish(notes: PairNotes, proofs: (from: Record<TreeName, number>) => Promise<PairProofs>, signal: AbortSignal): Promise<WitnessAnswer>;
111
+ };
112
+ /**
113
+ * What a checkpointer given up on may still do (T169; Seal.tla's Fenced):
114
+ * nothing it starts once its fence is closed. Every call it makes to its
115
+ * store, and every write to its memory, is checked against the fence
116
+ * first. What it sent before may still land: a database write is refused
117
+ * where a row holds its place (`write_checkpoint`, one statement), and a
118
+ * barrier raises the floor no higher than the next one does
119
+ * (HotPathOne.tla's LateBarrier); the memory writes in flight, `close`
120
+ * waits for, so that the next checkpointer reads them.
121
+ */
122
+ declare class Fence {
123
+ #private;
124
+ /** Why it was closed; null while it is open. */
125
+ get closed(): string | null;
126
+ /** Closed for good; resolves once the memory writes already sent have landed. */
127
+ close(why: string): Promise<void>;
128
+ /** The store, each of its calls refused once the fence is closed, and each answer that comes after. */
129
+ store<T extends object>(store: T): T;
130
+ /** The memory, its writes refused once the fence is closed, and those in flight kept for `close`. */
131
+ memory(memory: Memory): Memory;
132
+ }
133
+ type Tick = {
134
+ /**
135
+ * Whether a pair landed in the database this tick: a new one, or a pending
136
+ * one finished. A tick can finish a pending pair and then refuse its own
137
+ * window: `failures` says so.
138
+ */
139
+ signed: boolean;
140
+ upto: bigint;
141
+ sizes: Record<TreeName, number>;
142
+ entries: number;
143
+ /** Why nothing was signed, when something is wrong: each is an alarm for an operator. */
144
+ failures: string[];
145
+ /** Database time: the barrier and window, then the write. */
146
+ closeMs: number;
147
+ writeMs: number;
148
+ /** Whether the window held more than this tick sealed (`WINDOW_MAX`): its host ticks again at once. */
149
+ more: boolean;
150
+ };
151
+ type Sealed = {
152
+ pair: Pair;
153
+ notes: PairNotes;
154
+ /** null when no witness is set up, so nothing reads "witnessed" there. */
155
+ witnessed: boolean | null;
156
+ failures: string[];
157
+ /**
158
+ * Each witness's cosignature of the newest pair it took in time: a pair
159
+ * covering the change, which may be later than `pair` if a tick landed
160
+ * meanwhile. Each note says its own place. An archive cosigns nothing and
161
+ * adds none: whether the change was witnessed is `witnessed`, never the
162
+ * length of this.
163
+ */
164
+ cosigned: PairNotes[];
165
+ /** The change's entry, proved in the pair: what the admin's client checks and can keep. */
166
+ proof: InclusionProof;
167
+ };
168
+ /**
169
+ * The change path's seam (plan PR 4): a change, once committed, asks for its
170
+ * seal and answers with what this gives it (R51). The Node checkpointer's
171
+ * `sealThrough` is one; on Workers, `durableSealThrough` asks the
172
+ * checkpointer's Durable Object.
173
+ */
174
+ type SealThrough = (number: bigint, deadline?: number) => Promise<Sealed>;
175
+ /** An alarm the checkpointer raised, as its host keeps it for whoever watches (R40's plugin, PR 8's surfaces). */
176
+ type Alarm = {
177
+ at: number;
178
+ message: string;
179
+ };
180
+ type InclusionProof = {
181
+ number: bigint;
182
+ tree: TreeName;
183
+ index: number;
184
+ size: number;
185
+ /** The leaf's data: the whole entry. */
186
+ leaf: Buffer;
187
+ proof: Hash[];
188
+ /** The pair it is proved against. */
189
+ notes: PairNotes;
190
+ };
191
+ type Options = {
192
+ memory: Memory;
193
+ witnesses?: readonly Witness[];
194
+ /**
195
+ * Whether an entry's MAC holds: the vault checks it with its key before
196
+ * anything is sealed. The MAC must cover the entry's tree (Seal.tla's
197
+ * KindInMac): otherwise a change moved into the uses partition before its
198
+ * tick would be sealed into the uses tree, and pruned with the uses.
199
+ */
200
+ authentic: (entry: LogEntry) => boolean;
201
+ /** Where alarms go: refused ticks, unwitnessed changes, memory and database out of step. */
202
+ alert?: (message: string) => void;
203
+ freshMs?: number;
204
+ now?: () => number;
205
+ /**
206
+ * What closes this checkpointer for good (T169): a host bounding the open
207
+ * passes its own, to close it if the open stalls. One is made otherwise.
208
+ */
209
+ fence?: Fence;
210
+ /** `TICK_BOUND_MS`, `SEAL_WAIT_MS` and `WINDOW_MAX`, for the tests. */
211
+ tickBoundMs?: number;
212
+ sealWaitMs?: number;
213
+ windowMax?: number;
214
+ };
215
+ declare class Checkpointer {
216
+ private store;
217
+ private signers;
218
+ private options;
219
+ private fence;
220
+ private edges;
221
+ private head;
222
+ private running;
223
+ private pumps;
224
+ /**
225
+ * A pair signed, perhaps remembered, not known to be written: the memory
226
+ * may hold it, or the database may (a write that committed, then threw).
227
+ * It is finished before anything else is signed, never replaced by another
228
+ * pair for its place.
229
+ */
230
+ private pending;
231
+ /** The last pairs it wrote, newest last, each with what proves a change it sealed (T171). */
232
+ private signed;
233
+ /** The tick running now (T171): a change committed before its barrier is sealed by it, and waits for no other. */
234
+ private flight;
235
+ /**
236
+ * The floor of `at` signed into the newest pair: windows are read from it.
237
+ * It moves only when a pair carrying a new floor is written.
238
+ */
239
+ private floor;
240
+ /**
241
+ * The floor this process's last barrier set, which the next barrier must
242
+ * find exactly; null until its first barrier (a restart cannot know what
243
+ * barriers whose pairs went unsigned raised it to).
244
+ */
245
+ private barrier;
246
+ /** Set once a barrier finds the floor elsewhere: every tick refuses until the checkpointer is reopened. */
247
+ private floorAlarm;
248
+ /** The failures the last refused tick alerted: a refusal that stands is alerted once, not every tick. */
249
+ private refused;
250
+ /** The log's public keys, which open its pairs. */
251
+ readonly verifiers: PairVerifiers;
252
+ private constructor();
253
+ /**
254
+ * What stands against it sealing, whichever tick met it, an alarm's or a change's: why it stalled, or the refusal of
255
+ * its last tick; null when its last tick held. Readiness reads it beside its host's own view (T169, review r5).
256
+ */
257
+ get standing(): string | null;
258
+ /** Why its last tick threw, whichever tick it was, a change's or its host's; null once one ends well (review r6). */
259
+ private thrown;
260
+ /** The newest pair the signer signed and the database holds. */
261
+ get pair(): {
262
+ pair: Pair;
263
+ notes: PairNotes;
264
+ };
265
+ /** How much older than its pair an entry the signer seals may be (`FRESH_MS`): retention drops a day two of these past its cut. */
266
+ get freshMs(): number;
267
+ /**
268
+ * From the newest checkpoint row, checked against what the signer
269
+ * remembers, which comes first: nothing is signed or remembered before the
270
+ * two are compared. The signer remembers a pair before the database has it
271
+ * (RememberFirst), so the memory may be ahead of the database: after a crash
272
+ * between the two, or because a writer deleted the newest rows. Either way,
273
+ * if the entries the database holds give the remembered pair, its row is
274
+ * written again and nothing was lost; if they do not, the database lost
275
+ * entries the signer sealed, and it refuses. A database beside the memory,
276
+ * another pair at the same place, is refused too.
277
+ */
278
+ static open(store: CheckpointStore, signers: PairSigners, options: Options): Promise<Checkpointer>;
279
+ /**
280
+ * One checkpoint: seal what has committed, sign it, remember it, store it,
281
+ * publish it. Ticks run one at a time, the regular ones and those a change
282
+ * asks for.
283
+ */
284
+ /**
285
+ * Stops this checkpointer for good: every tick after, and every change
286
+ * that would tick, is refused, and its fence refuses whatever its tick in
287
+ * flight still does. Resolves once that tick has ended or stalled
288
+ * (`TICK_BOUND_MS`), and its memory writes in flight have landed. A host
289
+ * that opens another over the same memory closes this one first, so that
290
+ * two signers never act at once.
291
+ */
292
+ close(): Promise<void>;
293
+ private closed;
294
+ /** Why it stalled (T169): a tick past its bound. */
295
+ private stalled;
296
+ /** When the tick in flight stalls: `TICK_BOUND_MS` after it started, and more for a long window. */
297
+ private until;
298
+ /** What the tick in flight awaits, which a stall says, so that an operator sees what stalled. */
299
+ private phase;
300
+ /**
301
+ * A tick: the one queued behind the running one, if there is one yet,
302
+ * else a new one. Its barrier comes after every commit made before it
303
+ * starts, so whoever asks before it starts is sealed by it as well as by
304
+ * one of their own: many changes waiting at once share ticks, rather than
305
+ * each waiting for a tick per change before it (0.15-plan.md, "As built:
306
+ * PR 4b", measured).
307
+ */
308
+ tick(): Promise<Tick>;
309
+ private queued;
310
+ private tickNow;
311
+ /**
312
+ * Finishes a pending pair: remembered again (the first try may have failed,
313
+ * or not), then held by the database, which may hold it already; else it is
314
+ * written now over the pair the signer holds. A database at neither pair
315
+ * is refused until the checkpointer is reopened, whose rules then apply.
316
+ */
317
+ private finish;
318
+ private commit;
319
+ /**
320
+ * A change answers once its entry is sealed and, where witnesses are set
321
+ * up, witnessed (R51): seal now unless a pair already covers it, then wait
322
+ * for every witness to hold a pair that covers it, whichever tick sealed it,
323
+ * for at most `deadline`. A witness silent that long lets the change answer
324
+ * unwitnessed, alerted: the change is in force either way, and its entry is
325
+ * sealed in the database and in the signer's memory. A tick that cannot seal
326
+ * (an entry failing its MAC) fails the wait rather than spinning.
327
+ *
328
+ * A pair covering the number is not enough: the entry must be in it. An
329
+ * entry deleted before its tick leaves its number a gap the pair calls
330
+ * final, and nothing could ever show the change again; so the change is
331
+ * proved in the pair, and refused, alerted, if it is not there.
332
+ */
333
+ sealThrough(number: bigint, deadline?: number): Promise<Sealed>;
334
+ /**
335
+ * Change `number`'s inclusion proof in a pair this checkpointer signed and wrote, from the tiles its tick wrote, and
336
+ * checked against that pair's root; null if it kept none holding it (T171).
337
+ */
338
+ private proveKept;
339
+ /**
340
+ * Entry `number`'s inclusion proof against the newest pair, checked before
341
+ * it is handed out; null if not sealed. A use's `at`, if known, finds it
342
+ * in one probe; with `tree`, an entry in the other is refused.
343
+ */
344
+ prove(number: bigint, options?: Locating): Promise<InclusionProof | null>;
345
+ private proveAt;
346
+ /** Each witness's pump: one publication in flight at a time, always of the newest pair (a witness needs only that). */
347
+ private pump;
348
+ private publish;
349
+ /** Hands a witness the newest pair: sent now if its pump is idle, else next. A checkpointer closed hands none. */
350
+ private kick;
351
+ private drain;
352
+ private publishAll;
353
+ /** The witnesses that do not hold a pair sealing `upto` within the deadline, each with its last failure. */
354
+ private witnessed;
355
+ }
356
+ //#endregion
357
+ //#region src/log-entry.d.ts
358
+ /** The key that MACs log entries, and its tag: both from the vault's signing seed, each under a label of its own. */
359
+ type LogMacKey = {
360
+ tag: Buffer;
361
+ key: Buffer;
362
+ };
363
+ //#endregion
364
+ export { SealThrough as a, Memory as i, Alarm as n, Tick as o, Checkpointer as r, Witness as s, LogMacKey as t };
package/dist/node.d.ts CHANGED
@@ -1,11 +1,46 @@
1
+ import { a as SealThrough, i as Memory, n as Alarm, r as Checkpointer, s as Witness } from "./log-entry-BYWqj3O5.js";
1
2
  import { a as KekUnavailableError, c as awsKms, d as Kek, f as VaultConfig, i as KekProvider, l as checkpointMessage, n as AwsKmsOptions, o as SecretContext, p as derivedSigningKey, r as KekBadClaimError, s as WrappedDek, t as AwsCredentials, u as verifyCheckpoint } from "./index-DQ-FKVF3.js";
2
- import { Vault, Vault as Vault$1 } from "@coffre/core/vault";
3
- import { Database } from "@coffre/db";
3
+ import { NamedUseInput, Sealing, UseCode, Vault, Vault as Vault$1 } from "@coffre/core/vault";
4
+ import { KekRegistry } from "@coffre/core/kek";
5
+ import { Database, Queryable } from "@coffre/db";
6
+ import { PairNotes, PairVerifiers } from "@coffre/core/merkle";
4
7
  import "@coffre/core/access";
5
- import "@coffre/core/audit";
6
- import "@coffre/db/grants";
7
8
  import "@coffre/db/log";
9
+ import "@coffre/db/grants";
10
+ import "drizzle-orm";
11
+ import "@coffre/core/audit";
8
12
  export * from "@coffre/core/vault";
13
+ //#region src/log-replay.d.ts
14
+ /**
15
+ * The log as stored, read whole and replayed with the seal keys of
16
+ * `signingKeys` (the vault's): each use's decision by its entry number,
17
+ * and what does not hold, an entry that names no object or no purpose
18
+ * among `replay`'s faults. The verifier of plan PR 8 walks the log a
19
+ * stretch at a time; this reads all of it, for the suites that hold every
20
+ * live answer to it (R53).
21
+ */
22
+ export declare function replayStored(db: Queryable, signingKeys: readonly Uint8Array[]): Promise<{
23
+ decided: Map<string, UseCode | null>;
24
+ faults: string[];
25
+ }>;
26
+ //#endregion
27
+ //#region src/use.d.ts
28
+ /**
29
+ * The settings of an isolate's uses: how many calls in flight at once, how long a cover holds, and how many requests'
30
+ * names it maps to ids (`UseCache.names`; 0 maps none, each request then reads its ids: the bench's worst case).
31
+ */
32
+ type UseLimits = {
33
+ inFlight: number;
34
+ coverMs: number;
35
+ names: number;
36
+ /**
37
+ * The most keys a statement of a batch makes uses of (`useMany`): each statement holds the fence for its keys' uses,
38
+ * about 0.4 ms a key, and a change or the checkpointer's barrier waits behind it, every use queued behind that (R44).
39
+ * A batch of more is made in statements of this many, in turn (review r2 of #433): 100 hold the fence about 40 ms.
40
+ */
41
+ batch: number;
42
+ };
43
+ //#endregion
9
44
  //#region src/vault.d.ts
10
45
  type VaultOptions = {
11
46
  /**
@@ -17,6 +52,50 @@ type VaultOptions = {
17
52
  clockOffset?: () => number;
18
53
  /** How far one call walks the log (`WALKS`); tests shorten them. */
19
54
  walks?: Partial<Walks>;
55
+ /**
56
+ * Which log the vault keeps (0.15's plan, "Behind one switch"): 0.15's,
57
+ * the default since its flip (plan PR 10), where each decision that
58
+ * changes an input also logs it as a change of 0.15's log (`changes.ts`),
59
+ * or 0.14's alone, for the suites until 0.16 removes it. Not a
60
+ * deployment's to set: once cut over, the database refuses 0.14's writes.
61
+ */
62
+ log?: '0.14' | '0.15';
63
+ /**
64
+ * Under 0.15's log, what a change waits for before it answers: its entry
65
+ * sealed in a signed checkpoint, and witnessed or past the witnesses'
66
+ * bound (R51): the checkpointer's `sealThrough` (checkpointer.ts). A
67
+ * change whose entry it does not seal fails, though committed. Required
68
+ * under 0.15's log.
69
+ */
70
+ sealThrough?: SealThrough;
71
+ /**
72
+ * Under 0.15's log, the pairs held where no database writer reaches: the
73
+ * signer's memory (`durableHeldOutside` on Workers, the Node runner's
74
+ * `heldOutside`), later a witness's. The verifier holds every walk to
75
+ * them; without, a database rolled back whole to an earlier pair would
76
+ * verify until the signer's next tick refuses it (verifier.ts). Required
77
+ * under 0.15's log, as `sealThrough` is (T151).
78
+ */
79
+ heldOutside?: () => Promise<readonly PairNotes[]>;
80
+ /**
81
+ * Under 0.15's log, whether the checkpointer seals (T169), bounded: what
82
+ * `sealing()` answers, and `/readyz` asks. The Node runner's `sealing`;
83
+ * on Workers the entrypoint asks the object itself. Without, it seals.
84
+ */
85
+ sealing?: () => Promise<Sealing> | Sealing;
86
+ /**
87
+ * 0.15's uses, per isolate (use.ts): how many calls in flight at once, by
88
+ * default the hot path's pool, `connections`, and how long a cover holds.
89
+ */
90
+ uses?: Partial<UseLimits> & {
91
+ connections?: number;
92
+ };
93
+ /**
94
+ * Whether a call's I/O ends with its request, as on Workers, where a call whose request has ended may never settle:
95
+ * then a use's place in admission lapses past its deadline, settled or not (use.ts, `HELD_PAST_DEADLINE_MS`), its
96
+ * connection closed by then (cloudflare.ts). Not a deployment's to set.
97
+ */
98
+ callsEndWithRequest?: boolean;
20
99
  };
21
100
  /** How far one call walks the log, in entries: a checkpoint's walk from the last, its rehash of the past, and a verification's own walk. */
22
101
  type Walks = {
@@ -27,9 +106,84 @@ type Walks = {
27
106
  //#endregion
28
107
  //#region src/local.d.ts
29
108
  type LocalVault = Vault$1 & {
109
+ use: NonNullable<Vault$1['use']>;
30
110
  close(): Promise<void>;
31
111
  };
32
112
  //#endregion
113
+ //#region src/checkpointer-node.d.ts
114
+ /**
115
+ * The 0.15 checkpointer on Node (docs/design/audit-tree.md): one process
116
+ * seals the log every 500 ms, its memory a file beside the vault, synced
117
+ * before the database has the pair it holds (RememberFirst). The vault's
118
+ * process runs it (`keepCheckpointer`, node.ts). Nothing it awaits is
119
+ * unbounded (T169): a tick past its bound stalls the checkpointer, and the
120
+ * vault's process opens another.
121
+ */
122
+ /** The signer's memory in a file: written whole, synced, renamed over the last, and the directory synced, so a power loss keeps it. */
123
+ export declare function fileMemory(path: string): Memory;
124
+ type RunningCheckpointer = {
125
+ checkpointer: Checkpointer;
126
+ /** For the change path (plan PR 4): seal now, then wait for the witnesses (R51). */
127
+ sealThrough: SealThrough;
128
+ /** The alarms raised since `since` (ms), oldest first, from the file beside the memory. */
129
+ alarms(since?: number): Promise<Alarm[]>;
130
+ /** The pair the signer remembers, from its file: what the vault's verifier holds its walks to (`VaultOptions.heldOutside`). */
131
+ heldOutside(): Promise<PairNotes[]>;
132
+ /** The reads' projection run until it stands at the newest pair (T166): for tests and tools, never on a request's path. */
133
+ projected(): Promise<void>;
134
+ /** Whether it seals (`Vault.sealing`): ticking, its last tick ended well, lately. */
135
+ sealing(): Sealing;
136
+ stop(): Promise<void>;
137
+ };
138
+ /** A connection of the checkpointer's own (T169), which its runner cuts once it stalls: `openDatabase(url, { max })`. */
139
+ type Session = {
140
+ db: Database;
141
+ cut(): Promise<void>;
142
+ };
143
+ /**
144
+ * Opens the checkpointer over `database` (the vault's login) and ticks it
145
+ * every `intervalMs`, a tick after the last one ends, each followed by the
146
+ * log's upkeep (days made ahead, retention past `retainDays`, the bulk
147
+ * cap's rows pruned). Each alarm is appended to `<memory>.alarms`, one JSON
148
+ * line each. Given `connect` instead, over a session of its own, cut once
149
+ * it stalls or stops, so that what it still awaits holds no connection of
150
+ * the vault's (review r1).
151
+ */
152
+ export declare function runCheckpointer(options: {
153
+ /** The vault's database, or `connect`, a session of its own. */
154
+ database?: Database;
155
+ connect?: () => Promise<Session>;
156
+ seed: Uint8Array;
157
+ /** The memory's file. */
158
+ memory: string;
159
+ witnesses?: readonly Witness[];
160
+ intervalMs?: number;
161
+ tileRange?: number;
162
+ /** Full days of uses kept before today's; null keeps every day (retention.ts). */
163
+ retainDays?: number | null;
164
+ /** Told once its checkpointer stalls (T169), which ticks no more: `keepCheckpointer` opens another. */
165
+ stalled?: (why: string) => void;
166
+ /** `TICK_BOUND_MS`, which bounds the open and the next days too: for the tests. */
167
+ tickBoundMs?: number;
168
+ /** The memory over its file, `fileMemory` unless a test holds its writes. */
169
+ memoryOf?: (path: string) => Memory;
170
+ /** `UPKEEP_BOUND_MS`, for the tests. */
171
+ upkeepBoundMs?: number;
172
+ }): Promise<RunningCheckpointer>;
173
+ type KeptCheckpointer = Pick<RunningCheckpointer, 'sealThrough' | 'heldOutside' | 'alarms' | 'sealing' | 'stop'>;
174
+ /**
175
+ * The checkpointer as the vault's process keeps it: `runCheckpointer`, tried
176
+ * again every `retryMs` while it cannot open (a log it refuses, as after a
177
+ * restore, docs/restore.md; the database out of reach), each try making the
178
+ * next days of uses first, so that uses go on; its failure an alarm, kept
179
+ * once while it stands. Meanwhile a change's seal fails with why, as on
180
+ * Workers, and the pair held outside is read from the file as ever. One
181
+ * that stalls (T169) is stopped, fenced, and another opened a tick later.
182
+ */
183
+ export declare function keepCheckpointer(options: Parameters<typeof runCheckpointer>[0] & {
184
+ retryMs?: number;
185
+ }): Promise<KeptCheckpointer>;
186
+ //#endregion
33
187
  //#region src/node.d.ts
34
188
  export type NodeVaultConfig = VaultConfig & {
35
189
  /**
@@ -38,8 +192,29 @@ export type NodeVaultConfig = VaultConfig & {
38
192
  * development. Or one already open, which the vault leaves open.
39
193
  */
40
194
  database: string | Database;
195
+ /**
196
+ * 0.15's uses (docs/design/0.15-plan.md, PR 5): the same database as
197
+ * `coffre_vault_hot`, whose lock and statement timeouts bound a use, or
198
+ * one already open; on SQLite, the same `file:` URL. Required.
199
+ */
200
+ hotDatabase?: string | Database;
201
+ /**
202
+ * 0.15's checkpointer, which this process runs: `memory`, the file it
203
+ * keeps the newest pair it signed in, where no database writer reaches,
204
+ * such as `/var/lib/coffre/checkpointer-memory`, writable by the vault's
205
+ * user alone (its alarms go beside it, in `<memory>.alarms`). Required,
206
+ * unless the caller seals through a checkpointer of its own
207
+ * (`VaultOptions.sealThrough` and `heldOutside`).
208
+ */
209
+ checkpointer?: {
210
+ memory: string;
211
+ };
41
212
  };
42
- /** The vault in this process. */
213
+ /**
214
+ * The vault in this process, and under 0.15's log, the default, its
215
+ * checkpointer, opened before the vault serves, so that no call meets a log
216
+ * with no checkpoint yet. A configuration without what 0.15 needs is refused.
217
+ */
43
218
  export declare function localVault(config: NodeVaultConfig, options?: VaultOptions): Promise<LocalVault>;
44
219
  export type VaultServer = {
45
220
  socket: string;
@@ -56,4 +231,4 @@ export declare function serveVault(config: NodeVaultConfig & {
56
231
  /** The vault that `serveVault` runs, from the server's side of its socket. */
57
232
  export declare function connectVault(socket: string): Vault;
58
233
  //#endregion
59
- export { type AwsCredentials, type AwsKmsOptions, type Kek, KekBadClaimError, type KekProvider, KekUnavailableError, type LocalVault, type SecretContext, type Vault, type VaultConfig, type VaultOptions, type WrappedDek, awsKms, checkpointMessage, derivedSigningKey, verifyCheckpoint };
234
+ export { type AwsCredentials, type AwsKmsOptions, type Kek, KekBadClaimError, type KekProvider, KekUnavailableError, type KeptCheckpointer, type LocalVault, type RunningCheckpointer, type SecretContext, type Vault, type VaultConfig, type VaultOptions, type WrappedDek, awsKms, checkpointMessage, derivedSigningKey, verifyCheckpoint };