sealkeep 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 (180) hide show
  1. package/ARCHITECTURE.md +201 -0
  2. package/CHANGELOG.md +218 -0
  3. package/CONTROL_PLANE.md +86 -0
  4. package/LICENSE +34 -0
  5. package/README.md +249 -0
  6. package/THIRD_PARTY.md +22 -0
  7. package/THREAT_MODEL.md +107 -0
  8. package/dist/packages/vaultline-crypto/src/aead.d.ts +12 -0
  9. package/dist/packages/vaultline-crypto/src/aead.js +24 -0
  10. package/dist/packages/vaultline-crypto/src/chunk-access.d.ts +39 -0
  11. package/dist/packages/vaultline-crypto/src/chunk-access.js +93 -0
  12. package/dist/packages/vaultline-crypto/src/envelope.d.ts +71 -0
  13. package/dist/packages/vaultline-crypto/src/envelope.js +188 -0
  14. package/dist/packages/vaultline-crypto/src/format.d.ts +106 -0
  15. package/dist/packages/vaultline-crypto/src/format.js +43 -0
  16. package/dist/packages/vaultline-crypto/src/index.d.ts +5 -0
  17. package/dist/packages/vaultline-crypto/src/index.js +5 -0
  18. package/dist/packages/vaultline-crypto/src/recipients.d.ts +42 -0
  19. package/dist/packages/vaultline-crypto/src/recipients.js +129 -0
  20. package/dist/packages/vaultline-crypto/src/sha256-stream.d.ts +41 -0
  21. package/dist/packages/vaultline-crypto/src/sha256-stream.js +206 -0
  22. package/dist/packages/vaultline-crypto/src/stream.d.ts +139 -0
  23. package/dist/packages/vaultline-crypto/src/stream.js +477 -0
  24. package/dist/site/index.html +1542 -0
  25. package/dist/site.zip +0 -0
  26. package/dist/src/activity.d.ts +22 -0
  27. package/dist/src/activity.js +52 -0
  28. package/dist/src/adapters.d.ts +212 -0
  29. package/dist/src/adapters.js +533 -0
  30. package/dist/src/audit.d.ts +24 -0
  31. package/dist/src/audit.js +41 -0
  32. package/dist/src/autopilot.d.ts +77 -0
  33. package/dist/src/autopilot.js +148 -0
  34. package/dist/src/bip39-wordlist.d.ts +15 -0
  35. package/dist/src/bip39-wordlist.js +272 -0
  36. package/dist/src/branding.d.ts +31 -0
  37. package/dist/src/branding.js +31 -0
  38. package/dist/src/chunk-store.d.ts +142 -0
  39. package/dist/src/chunk-store.js +502 -0
  40. package/dist/src/cli.d.ts +2 -0
  41. package/dist/src/cli.js +2035 -0
  42. package/dist/src/cloud.d.ts +434 -0
  43. package/dist/src/cloud.js +851 -0
  44. package/dist/src/control-plane/auth.d.ts +62 -0
  45. package/dist/src/control-plane/auth.js +123 -0
  46. package/dist/src/control-plane/server.d.ts +31 -0
  47. package/dist/src/control-plane/server.js +263 -0
  48. package/dist/src/control-plane/store.d.ts +101 -0
  49. package/dist/src/control-plane/store.js +82 -0
  50. package/dist/src/control-plane-cli.d.ts +2 -0
  51. package/dist/src/control-plane-cli.js +37 -0
  52. package/dist/src/control-plane-server.d.ts +10 -0
  53. package/dist/src/control-plane-server.js +11 -0
  54. package/dist/src/control-plane.d.ts +78 -0
  55. package/dist/src/control-plane.js +61 -0
  56. package/dist/src/crypto.d.ts +56 -0
  57. package/dist/src/crypto.js +132 -0
  58. package/dist/src/daemon.d.ts +52 -0
  59. package/dist/src/daemon.js +142 -0
  60. package/dist/src/dashboard-cli.d.ts +2 -0
  61. package/dist/src/dashboard-cli.js +20 -0
  62. package/dist/src/disk.d.ts +110 -0
  63. package/dist/src/disk.js +169 -0
  64. package/dist/src/doctor.d.ts +11 -0
  65. package/dist/src/doctor.js +198 -0
  66. package/dist/src/enroll.d.ts +27 -0
  67. package/dist/src/enroll.js +136 -0
  68. package/dist/src/errors.d.ts +26 -0
  69. package/dist/src/errors.js +23 -0
  70. package/dist/src/heartbeat.d.ts +89 -0
  71. package/dist/src/heartbeat.js +120 -0
  72. package/dist/src/index-sync.d.ts +53 -0
  73. package/dist/src/index-sync.js +147 -0
  74. package/dist/src/leakscan.d.ts +48 -0
  75. package/dist/src/leakscan.js +222 -0
  76. package/dist/src/local-api.d.ts +132 -0
  77. package/dist/src/local-api.js +1757 -0
  78. package/dist/src/managed-chunks.d.ts +55 -0
  79. package/dist/src/managed-chunks.js +108 -0
  80. package/dist/src/mcp-install.d.ts +52 -0
  81. package/dist/src/mcp-install.js +140 -0
  82. package/dist/src/mcp.d.ts +1 -0
  83. package/dist/src/mcp.js +59 -0
  84. package/dist/src/migrate.d.ts +35 -0
  85. package/dist/src/migrate.js +88 -0
  86. package/dist/src/mnemonic.d.ts +60 -0
  87. package/dist/src/mnemonic.js +134 -0
  88. package/dist/src/net.d.ts +2 -0
  89. package/dist/src/net.js +16 -0
  90. package/dist/src/notify.d.ts +46 -0
  91. package/dist/src/notify.js +84 -0
  92. package/dist/src/offload.d.ts +117 -0
  93. package/dist/src/offload.js +331 -0
  94. package/dist/src/onboarding.d.ts +10 -0
  95. package/dist/src/onboarding.js +44 -0
  96. package/dist/src/packages.d.ts +126 -0
  97. package/dist/src/packages.js +114 -0
  98. package/dist/src/passkey.d.ts +26 -0
  99. package/dist/src/passkey.js +54 -0
  100. package/dist/src/password-lock.d.ts +19 -0
  101. package/dist/src/password-lock.js +156 -0
  102. package/dist/src/paths.d.ts +9 -0
  103. package/dist/src/paths.js +24 -0
  104. package/dist/src/providers/gcs.d.ts +133 -0
  105. package/dist/src/providers/gcs.js +235 -0
  106. package/dist/src/providers/gdrive.d.ts +156 -0
  107. package/dist/src/providers/gdrive.js +335 -0
  108. package/dist/src/providers/index.d.ts +45 -0
  109. package/dist/src/providers/index.js +74 -0
  110. package/dist/src/providers/s3.d.ts +174 -0
  111. package/dist/src/providers/s3.js +345 -0
  112. package/dist/src/providers/sigv4.d.ts +78 -0
  113. package/dist/src/providers/sigv4.js +112 -0
  114. package/dist/src/queue.d.ts +185 -0
  115. package/dist/src/queue.js +286 -0
  116. package/dist/src/recovery.d.ts +40 -0
  117. package/dist/src/recovery.js +132 -0
  118. package/dist/src/rehydrate.d.ts +43 -0
  119. package/dist/src/rehydrate.js +66 -0
  120. package/dist/src/restore.d.ts +34 -0
  121. package/dist/src/restore.js +80 -0
  122. package/dist/src/retention.d.ts +251 -0
  123. package/dist/src/retention.js +446 -0
  124. package/dist/src/rotate.d.ts +47 -0
  125. package/dist/src/rotate.js +95 -0
  126. package/dist/src/search.d.ts +147 -0
  127. package/dist/src/search.js +677 -0
  128. package/dist/src/secrets.d.ts +86 -0
  129. package/dist/src/secrets.js +220 -0
  130. package/dist/src/service.d.ts +73 -0
  131. package/dist/src/service.js +197 -0
  132. package/dist/src/share.d.ts +34 -0
  133. package/dist/src/share.js +68 -0
  134. package/dist/src/spool.d.ts +97 -0
  135. package/dist/src/spool.js +213 -0
  136. package/dist/src/start-tui.d.ts +17 -0
  137. package/dist/src/start-tui.js +113 -0
  138. package/dist/src/start.d.ts +75 -0
  139. package/dist/src/start.js +101 -0
  140. package/dist/src/storage-setup.d.ts +49 -0
  141. package/dist/src/storage-setup.js +222 -0
  142. package/dist/src/storage-targets.d.ts +40 -0
  143. package/dist/src/storage-targets.js +147 -0
  144. package/dist/src/stream-to-cloud.d.ts +76 -0
  145. package/dist/src/stream-to-cloud.js +820 -0
  146. package/dist/src/sync-rules.d.ts +85 -0
  147. package/dist/src/sync-rules.js +125 -0
  148. package/dist/src/trash.d.ts +15 -0
  149. package/dist/src/trash.js +63 -0
  150. package/dist/src/tui.d.ts +18 -0
  151. package/dist/src/tui.js +179 -0
  152. package/dist/src/types.d.ts +191 -0
  153. package/dist/src/types.js +3 -0
  154. package/dist/src/ui-server.d.ts +187 -0
  155. package/dist/src/ui-server.js +293 -0
  156. package/dist/src/ui.d.ts +41 -0
  157. package/dist/src/ui.js +102 -0
  158. package/dist/src/update.d.ts +30 -0
  159. package/dist/src/update.js +56 -0
  160. package/dist/src/upload.d.ts +46 -0
  161. package/dist/src/upload.js +80 -0
  162. package/dist/src/vault.d.ts +208 -0
  163. package/dist/src/vault.js +812 -0
  164. package/dist/src/watcher.d.ts +34 -0
  165. package/dist/src/watcher.js +121 -0
  166. package/dist/src/worker.d.ts +52 -0
  167. package/dist/src/worker.js +190 -0
  168. package/package.json +65 -0
  169. package/web/app.js +1372 -0
  170. package/web/index.html +476 -0
  171. package/web/rail.js +308 -0
  172. package/web/retention.html +17 -0
  173. package/web/rules-view.js +249 -0
  174. package/web/sessions-view.js +448 -0
  175. package/web/sessions.html +17 -0
  176. package/web/setup-api.js +181 -0
  177. package/web/setup-logic.js +394 -0
  178. package/web/setup.html +419 -0
  179. package/web/setup.js +697 -0
  180. package/web/style.css +990 -0
@@ -0,0 +1,434 @@
1
+ import { type BackendName } from "./secrets.js";
2
+ import { type Envelope } from "../packages/vaultline-crypto/src/index.js";
3
+ /**
4
+ * Sealkeep Cloud: the managed tier.
5
+ *
6
+ * The trade for paying is that you configure nothing — no bucket, no IAM, no
7
+ * credentials. What you do not trade away is control of your data: this module
8
+ * asks the control plane for permission to write one object, then talks to
9
+ * storage directly. Ciphertext never passes through our servers, and the
10
+ * control plane is never sent a key that opens it.
11
+ */
12
+ export declare const DEFAULT_CLOUD_URL = "https://shared.spala.ai/p04946/api";
13
+ /**
14
+ * Where this vault's cloud token lives in the keystore.
15
+ *
16
+ * Keyed by vault as well as by cloud URL. Keying on the URL alone made the
17
+ * token machine-global: every vault on the machine inherited whichever account
18
+ * happened to be signed in, so a brand-new free-tier vault reported itself
19
+ * signed in as someone else, showed a green sealed-key check it had no claim
20
+ * to, and — because `resolveSyncRules` reads that account — silently applied a
21
+ * stranger's sync rules. On this machine that held back 659 of 662 sessions on
22
+ * a vault that had never logged in to anything.
23
+ *
24
+ * Exported because `enroll` stores a token at this same location. Two copies of
25
+ * a key format that must agree is how `access_key_id` drifted from `akid` and
26
+ * would have signed every managed upload with an undefined credential — one
27
+ * definition, imported, so a change here cannot leave the other behind.
28
+ */
29
+ export declare function tokenRefFor(vaultId: string, env?: NodeJS.ProcessEnv): {
30
+ service: string;
31
+ account: string;
32
+ };
33
+ export type CloudAccount = {
34
+ id: number;
35
+ email: string;
36
+ label: string;
37
+ plan: "free" | "pro_lite" | "pro";
38
+ state: string;
39
+ quota_bytes: number;
40
+ used_bytes: number;
41
+ };
42
+ /**
43
+ * A lease says where this archive goes and how to get it there.
44
+ *
45
+ * Two shapes, because the two families of provider delegate differently:
46
+ *
47
+ * - `upload_url` — the control plane presigned a single object. GCS works this
48
+ * way, and so would S3 if we signed centrally.
49
+ * - `vended` — the control plane handed back a short-lived credential scoped to
50
+ * this account's own prefix, and this machine signs its own request. That is
51
+ * how B2 and R2 are reached: their scoping lives in the credential rather
52
+ * than in a URL, so tenant isolation is the provider's job instead of ours.
53
+ *
54
+ * The client does not care which arrives. Nothing above `putCiphertext` knows
55
+ * or needs to know which provider is holding the bytes, which is the whole
56
+ * point — we can move managed storage between providers without shipping a new
57
+ * CLI, because an archive already carries everything needed to open it.
58
+ */
59
+ export type VendedCredential = {
60
+ provider: "s3" | "r2" | "b2";
61
+ bucket: string;
62
+ region: string;
63
+ /** Omitted for AWS, where the host is derived from bucket and region. */
64
+ host?: string;
65
+ /** For self-hosted S3-compatible endpoints. Managed leases are always https on 443. */
66
+ port?: number;
67
+ protocol?: "https" | "http";
68
+ /**
69
+ * Named `akid`, not `access_key_id`, because Spala's response filter redacts
70
+ * the latter — it replaces the value with "[REDACTED]" while passing
71
+ * `secret_access_key` and `session_token` through untouched. The client would
72
+ * receive a literal "[REDACTED]" to sign with. See SPALA_BACKEND.md issue 27.
73
+ */
74
+ akid: string;
75
+ secret_access_key: string;
76
+ session_token?: string;
77
+ /**
78
+ * When the vended key stops working, ISO-8601. Checked before every signing
79
+ * so a stalled or resumed run re-leases instead of shipping requests the
80
+ * provider will 403 — absent (older planes) falls back to the lease's own
81
+ * expires_at, which every lease already carries.
82
+ */
83
+ expires_at?: string;
84
+ };
85
+ export type UploadLease = {
86
+ lease_id: number;
87
+ vault_ref: string;
88
+ object_key: string;
89
+ expires_at: string;
90
+ upload_url?: {
91
+ url: string;
92
+ method: string;
93
+ headers: Record<string, string>;
94
+ };
95
+ vended?: VendedCredential;
96
+ };
97
+ export declare function register(dataDir: string, input: {
98
+ email: string;
99
+ password: string;
100
+ label: string;
101
+ }, preferred?: BackendName): Promise<CloudAccount>;
102
+ /** The token lives in the OS keychain, never in a config file on disk. */
103
+ export declare function login(dataDir: string, input: {
104
+ email: string;
105
+ password: string;
106
+ }, preferred?: BackendName): Promise<CloudAccount>;
107
+ /**
108
+ * Sign in with a one-time code from the web panel instead of a password.
109
+ *
110
+ * An account created through Google has no password anybody knows — the server
111
+ * stores a bcrypt of a random string it never shows. Without this, every Google
112
+ * customer would have a working dashboard and an unusable CLI. The code is
113
+ * single-use and the server clears it on redemption.
114
+ */
115
+ export declare function loginWithCode(dataDir: string, input: {
116
+ email: string;
117
+ token: string;
118
+ }, preferred?: BackendName): Promise<CloudAccount>;
119
+ export declare function logout(dataDir: string, preferred?: BackendName): Promise<void>;
120
+ export declare function cloudToken(dataDir: string, preferred?: BackendName): Promise<string>;
121
+ export declare function accountStatus(dataDir: string, preferred?: BackendName): Promise<CloudAccount>;
122
+ /**
123
+ * Recovery without custody.
124
+ *
125
+ * The escrow blob is the vault's secret material sealed with the recovery
126
+ * phrase, exactly like an archive. We store it so a lost machine is not a lost
127
+ * vault, but it is ciphertext to us: without the phrase it is noise, and the
128
+ * phrase is never transmitted. Losing the phrase still loses the data — that is
129
+ * the point, and the signup flow has to say so plainly.
130
+ */
131
+ /**
132
+ * A way back into the escrow blob, beyond the recovery phrase.
133
+ *
134
+ * Each one is an independent recipient: any single unlock opens the blob, so
135
+ * adding one is adding a key, not a factor. That is worth stating in those
136
+ * words because it is the thing most easily got wrong — "password *and* phrase"
137
+ * sounds stronger than either alone, and as separate recipients it is exactly
138
+ * as strong as the weaker of the two.
139
+ *
140
+ * - `password` derives its key with scrypt, like the phrase does. It makes a
141
+ * new machine reachable with something a person remembers, and it makes the
142
+ * password the whole key. A weak one is a weak vault, and no amount of
143
+ * wording elsewhere changes that.
144
+ * - `publicKey` is an X25519 recipient: a passkey's PRF-derived identity (see
145
+ * src/passkey.ts), or an organisation's escrow key. Nothing secret is needed
146
+ * to *add* one, only to use it.
147
+ *
148
+ * What is never here is anything we could derive ourselves. The server stores
149
+ * the envelope as an opaque string, so recipients can be added without it
150
+ * learning anything, and without a server change.
151
+ */
152
+ export type EscrowUnlock = {
153
+ kind: "password";
154
+ password: string;
155
+ } | {
156
+ kind: "publicKey";
157
+ publicKeyBase64: string;
158
+ label?: string;
159
+ };
160
+ /**
161
+ * Refuses an escrow password that is also the account password.
162
+ *
163
+ * `login` sends the account password to the control plane, where it is bcrypted
164
+ * — which is ordinary and fine for authentication, and fatal here. An escrow
165
+ * password is a key that opens the blob on its own, so if it is the same string
166
+ * the account is authenticated with, then we have been handed that key, and so
167
+ * has anyone who takes the database or compels us to produce it. The product's
168
+ * central claim — that we cannot open your archives — would be false, and
169
+ * false in a way no customer could detect.
170
+ *
171
+ * Enforced rather than documented, because a rule that only exists in prose is
172
+ * a rule people break by pasting the password they already use. The comparison
173
+ * is deliberately naive: this is a mistake-catcher, not a policy engine, and
174
+ * the caller is the only place both strings are ever in hand at once.
175
+ *
176
+ * The real fix is for authentication to stop sending the raw password at all —
177
+ * send one derivation to log in, keep another for encryption, so reuse becomes
178
+ * harmless instead of forbidden. That is a control-plane change; until it
179
+ * happens, this keeps the guarantee true.
180
+ */
181
+ export declare function assertEscrowPasswordDistinct(accountPassword: string, escrowPassword: string): void;
182
+ export declare function sealEscrow(secretMaterial: Record<string, unknown>, rawPhrase: string, also?: readonly EscrowUnlock[]): {
183
+ envelope: Envelope;
184
+ ciphertext: Buffer;
185
+ };
186
+ export declare function openEscrow(envelope: Envelope, ciphertext: Buffer, rawPhrase: string): Record<string, unknown>;
187
+ /**
188
+ * Opens the blob with a password that was registered as an unlock.
189
+ *
190
+ * Separate from `openEscrow` so the two cannot be confused at a call site: this
191
+ * one is reached by typing something memorable, which is the convenience the
192
+ * paid tier is for and also the weaker of the two doors. The failure message
193
+ * says password rather than phrase, because being told your recovery phrase is
194
+ * wrong when you typed a password is how people conclude they have lost
195
+ * everything.
196
+ */
197
+ export declare function openEscrowWithPassword(envelope: Envelope, ciphertext: Buffer, password: string): Record<string, unknown>;
198
+ /**
199
+ * Opens the blob with a key: a passkey's derived identity, or an org's escrow
200
+ * key. The private half is never stored by us and never travels — it is
201
+ * re-derived from the authenticator, or held by whoever owns the org key.
202
+ */
203
+ export declare function openEscrowWithKey(envelope: Envelope, ciphertext: Buffer, privateKeyRaw: Buffer): Record<string, unknown>;
204
+ export type EscrowBlob = {
205
+ envelope: Envelope;
206
+ sealed: string;
207
+ created_at?: string;
208
+ };
209
+ /**
210
+ * Hand the sealed blob to the control plane.
211
+ *
212
+ * What crosses the wire is ciphertext plus the envelope needed to unwrap it
213
+ * *given the phrase*. The phrase itself is never sent, so possession of this
214
+ * record — by us, or by anyone who takes the database — does not open it.
215
+ */
216
+ export declare function pushEscrow(dataDir: string, secretMaterial: Record<string, unknown>, rawPhrase: string, preferred?: BackendName, also?: readonly EscrowUnlock[]): Promise<{
217
+ stored: true;
218
+ }>;
219
+ /** Fetch and open the escrow blob. Requires the phrase; nothing else will do. */
220
+ /** Whether a sealed copy exists, without needing the phrase to find out. */
221
+ export declare function hasSealedCopy(dataDir: string, preferred?: BackendName): Promise<boolean>;
222
+ export declare function pullEscrow(dataDir: string, rawPhrase: string, preferred?: BackendName): Promise<Record<string, unknown>>;
223
+ /** Asks for permission to write exactly one object. The key is chosen server-side. */
224
+ export declare function requestUpload(dataDir: string, input: {
225
+ vaultRef: string;
226
+ ciphertextSha256: string;
227
+ bytes: number;
228
+ }, preferred?: BackendName): Promise<UploadLease>;
229
+ /**
230
+ * Straight to storage, whichever way the lease delegated.
231
+ *
232
+ * A presigned lease is obeyed verbatim — the signer chose the method and the
233
+ * headers, and changing either invalidates the signature. A vended lease is
234
+ * signed here with the same SigV4 code the bring-your-own-bucket path uses, so
235
+ * managed B2 and a customer's own S3 bucket travel through one tested signer
236
+ * rather than two. A lease naming a delegation this build does not know is
237
+ * refused before a single byte or signature leaves the machine.
238
+ */
239
+ export declare function putCiphertext(lease: UploadLease, ciphertext: Buffer, signal?: AbortSignal): Promise<void>;
240
+ export declare function completeUpload(dataDir: string, leaseId: number, bytes: number, preferred?: BackendName): Promise<unknown>;
241
+ export type PushResult = {
242
+ archiveId: string;
243
+ objectKey: string;
244
+ bytes: number;
245
+ durable: boolean;
246
+ note?: string;
247
+ };
248
+ /**
249
+ * Push one already-encrypted archive to managed storage.
250
+ *
251
+ * Completion is reported separately from the upload because the bytes are
252
+ * already safe in storage by then. If metering fails we say so rather than
253
+ * claiming the archive is not stored, which would be false and would invite a
254
+ * pointless re-upload.
255
+ */
256
+ /**
257
+ * Fetch an archive's ciphertext back from managed storage.
258
+ *
259
+ * The server hands out a short-lived signed GET and never sees the plaintext;
260
+ * the hash it records is verified here before anything is written, so a
261
+ * truncated or substituted object is refused rather than decrypted.
262
+ */
263
+ export type CloudArchive = {
264
+ vault_ref: string;
265
+ bytes: number;
266
+ state: string;
267
+ durable_at?: string;
268
+ created_at?: string;
269
+ object_key?: string;
270
+ };
271
+ /** What managed storage is holding for this account. The ref is what `sealkeep open` takes. */
272
+ /**
273
+ * The managed index slot: one sealed content index per account, moved through
274
+ * control-plane-signed URLs exactly like archive objects — known lengths, the
275
+ * signer the plane already has. `kind` is "meta" (the tiny envelope, read
276
+ * first to decide freshness) or "blob" (the sealed index itself). The plane
277
+ * sees ciphertext sizes and update times; tokens never leave the machines.
278
+ */
279
+ /**
280
+ * Drive OAuth on the account's shared Google client, Paul's way: reuse the
281
+ * client the backend already owns, keep its secret THERE, and let this
282
+ * machine hold only its own tokens. The CLI opens the consent URL, a loopback
283
+ * listener on the registered fixed port catches the code, and the plane —
284
+ * authenticated as this account — exchanges it. Nothing is stored server-side.
285
+ */
286
+ export declare function gdriveOauthConfig(dataDir: string, preferred?: BackendName): Promise<{
287
+ client_id: string;
288
+ redirect_uri: string;
289
+ scope: string;
290
+ }>;
291
+ export declare function gdriveExchange(dataDir: string, input: {
292
+ code: string;
293
+ redirectUri: string;
294
+ }, preferred?: BackendName): Promise<{
295
+ refresh_token: string;
296
+ access_token: string;
297
+ expires_in: number;
298
+ }>;
299
+ export declare function gdriveRefresh(dataDir: string, refreshToken: string, preferred?: BackendName): Promise<{
300
+ accessToken: string;
301
+ expiresIn: number;
302
+ }>;
303
+ export declare function gdriveClaim(dataDir: string, state: string, preferred?: BackendName): Promise<{
304
+ refresh_token: string;
305
+ access_token: string;
306
+ expires_in: number;
307
+ } | null>;
308
+ /**
309
+ * The consent flow for the shared client. Two shapes, decided by the server's
310
+ * registered redirect: a REMOTE https callback (the plane exchanges and parks
311
+ * the tokens; this machine polls its one-time state — works even when the
312
+ * browser lives on another device), or a loopback listener that catches the
313
+ * code here and exchanges through the authed endpoint.
314
+ */
315
+ export declare function connectGdriveViaCloud(dataDir: string, options?: {
316
+ onConsentUrl?: (url: string) => void;
317
+ opener?: (url: string) => unknown;
318
+ timeoutMs?: number;
319
+ preferred?: BackendName;
320
+ pollMs?: number;
321
+ }): Promise<{
322
+ kind: "gdrive";
323
+ refreshToken: string;
324
+ clientId: string;
325
+ exchange: "cloud";
326
+ }>;
327
+ export declare function pushIndexObject(dataDir: string, kind: "meta" | "blob", body: Buffer, preferred?: BackendName): Promise<void>;
328
+ export declare function pullIndexObject(dataDir: string, kind: "meta" | "blob", preferred?: BackendName): Promise<Buffer | null>;
329
+ export declare function listCloudArchives(dataDir: string, preferred?: BackendName): Promise<CloudArchive[]>;
330
+ /**
331
+ * Really deletes one stored object from managed storage.
332
+ *
333
+ * Goes through the control plane rather than storage directly, because it is
334
+ * the same call the account panel's Archives section makes — one delete path,
335
+ * so the product and the panel cannot come to mean different things by
336
+ * "deleted". The control plane removes the object and its row, and billing
337
+ * follows the row.
338
+ */
339
+ export declare function deleteCloudArchive(dataDir: string, vaultRef: string, preferred?: BackendName): Promise<void>;
340
+ export type DownloadLease = {
341
+ vault_ref: string;
342
+ bytes: number;
343
+ ciphertext_sha256: string;
344
+ object_key?: string;
345
+ download?: {
346
+ url: string;
347
+ method?: string;
348
+ headers?: Record<string, string>;
349
+ };
350
+ vended?: VendedCredential;
351
+ };
352
+ /**
353
+ * A byte range of a stored object, for chunk-granular reads. The whole-object
354
+ * hash cannot be checked on a slice — that is the point of a slice — so the
355
+ * caller's integrity comes from the per-chunk hashes and AEAD tags inside
356
+ * (see vaultline-crypto/chunk-access). Only the presigned-URL lease shape
357
+ * supports ranges today; a vended-credential lease falls back to whole-object.
358
+ */
359
+ export declare function pullCiphertextRange(dataDir: string, vaultRef: string, storedOffset: number, storedLength: number, preferred?: BackendName): Promise<Buffer>;
360
+ /**
361
+ * Existence and stored size of a managed object, proven against STORAGE, not
362
+ * the plane's row: the row is minted by metering and a metering row is not an
363
+ * object. A one-byte ranged GET on the download URL answers both questions
364
+ * for the cost of a header — the Content-Range total is the provider's own
365
+ * count. This is what lets chunk-folder verification and resume treat managed
366
+ * storage with exactly the discipline `verifyStored` demands of it.
367
+ */
368
+ export declare function statCiphertext(dataDir: string, vaultRef: string, preferred?: BackendName): Promise<{
369
+ exists: boolean;
370
+ bytes: number;
371
+ }>;
372
+ export declare function pullCiphertext(dataDir: string, vaultRef: string, preferred?: BackendName): Promise<{
373
+ ciphertext: Buffer;
374
+ bytes: number;
375
+ }>;
376
+ export declare function frameObject(envelope: unknown, ciphertext: Buffer): Buffer;
377
+ export declare function unframeObject(blob: Buffer): {
378
+ envelope: any | null;
379
+ ciphertext: Buffer;
380
+ };
381
+ /**
382
+ * Fetch one archive and open it, on any machine.
383
+ *
384
+ * This is the whole disaster-recovery story in one call: the recovery phrase
385
+ * unwraps the archive key from the envelope the object carries, so a fresh
386
+ * laptop with nothing but the phrase can read the archive back. The plaintext
387
+ * hash recorded at seal time is checked inside decryptArchive, after the AEAD
388
+ * has authenticated every chunk.
389
+ */
390
+ export declare function openFromCloud(dataDir: string, vaultRef: string, destination: string, rawPhrase: string, preferred?: BackendName): Promise<{
391
+ output: string;
392
+ bytes: number;
393
+ }>;
394
+ export type ArchiveCheck = {
395
+ vaultRef: string;
396
+ bytes: number;
397
+ ok: boolean;
398
+ problem?: string;
399
+ };
400
+ /**
401
+ * The judgement, kept separate from the fetching so it can be tested honestly.
402
+ *
403
+ * `recorded` is what the control plane says the archive is; `got` is what
404
+ * storage actually returned, or the error it failed with. A raw status code is
405
+ * not an explanation, so a missing object is named in words a customer can act
406
+ * on.
407
+ */
408
+ export declare function classifyArchive(recorded: {
409
+ vault_ref: string;
410
+ bytes: number;
411
+ }, got: {
412
+ length: number;
413
+ } | {
414
+ error: string;
415
+ }): ArchiveCheck;
416
+ /**
417
+ * Proves the archives really are retrievable.
418
+ *
419
+ * A backup nobody has tested is a hope, not a backup. This fetches each durable
420
+ * archive back and checks it against the hash recorded at upload, so the two
421
+ * ways an archive can quietly rot are both caught: the object is missing from
422
+ * storage, or the bytes no longer match what was sealed.
423
+ *
424
+ * It deliberately re-downloads rather than trusting a HEAD. Size alone would
425
+ * pass an object that was truncated and re-padded, and the whole point of the
426
+ * exercise is to distrust the happy path.
427
+ */
428
+ export declare function verifyArchives(dataDir: string, options?: {
429
+ limit?: number;
430
+ onProgress?: (check: ArchiveCheck) => void;
431
+ }, preferred?: BackendName): Promise<ArchiveCheck[]>;
432
+ export declare function pushArchive(dataDir: string, archiveId: string, preferred?: BackendName, options?: {
433
+ signal?: AbortSignal;
434
+ }): Promise<PushResult>;