sealkeep 0.9.0 → 0.11.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 (274) hide show
  1. package/ARCHITECTURE.md +163 -14
  2. package/CHANGELOG.md +223 -1
  3. package/CONTROL_PLANE.md +2 -2
  4. package/LICENSE +1 -1
  5. package/README.md +108 -22
  6. package/THIRD_PARTY.md +2 -2
  7. package/THREAT_MODEL.md +23 -4
  8. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/chunk-access.d.ts +26 -4
  9. package/dist/packages/sealkeep-crypto/src/chunk-access.js +219 -0
  10. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.d.ts +11 -1
  11. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.js +58 -21
  12. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.d.ts +2 -2
  13. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.d.ts +1 -0
  14. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.js +1 -0
  15. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.js +4 -4
  16. package/dist/packages/sealkeep-crypto/src/squeeze.d.ts +6 -0
  17. package/dist/packages/sealkeep-crypto/src/squeeze.js +39 -0
  18. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.d.ts +36 -3
  19. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.js +196 -37
  20. package/dist/site/index.html +1808 -1904
  21. package/dist/site/llms.txt +67 -0
  22. package/dist/site/trust/architecture-data-flow.html +53 -0
  23. package/dist/site/trust/audit-roadmap.html +37 -0
  24. package/dist/site/trust/deployment-responsibility.html +11 -0
  25. package/dist/site/trust/dpa-sample.html +30 -0
  26. package/dist/site/trust/release-provenance.html +21 -0
  27. package/dist/site/trust/subprocessors.html +15 -0
  28. package/dist/site/trust/threat-model.html +34 -0
  29. package/dist/site/trust/toms.html +41 -0
  30. package/dist/site/trust-document.css +32 -0
  31. package/dist/site/trust.html +73 -0
  32. package/dist/site/visual/assets/index-8Nxnkc7o.js +207 -0
  33. package/dist/site/visual/assets/index-oxLy1bpy.css +1 -0
  34. package/dist/site/visual/index.html +18 -0
  35. package/dist/site.zip +0 -0
  36. package/dist/src/activity.d.ts +9 -0
  37. package/dist/src/activity.js +90 -1
  38. package/dist/src/adapters.d.ts +175 -5
  39. package/dist/src/adapters.js +961 -73
  40. package/dist/src/agent-context.d.ts +135 -0
  41. package/dist/src/agent-context.js +1059 -0
  42. package/dist/src/archive-copies.d.ts +47 -0
  43. package/dist/src/archive-copies.js +179 -0
  44. package/dist/src/audit.d.ts +1 -1
  45. package/dist/src/audit.js +29 -4
  46. package/dist/src/autopilot.d.ts +52 -7
  47. package/dist/src/autopilot.js +143 -25
  48. package/dist/src/background-bandwidth.d.ts +46 -0
  49. package/dist/src/background-bandwidth.js +301 -0
  50. package/dist/src/background-cpu.d.ts +82 -0
  51. package/dist/src/background-cpu.js +212 -0
  52. package/dist/src/background-worker-error.d.ts +12 -0
  53. package/dist/src/background-worker-error.js +18 -0
  54. package/dist/src/branding.d.ts +24 -1
  55. package/dist/src/branding.js +31 -1
  56. package/dist/src/bridge.d.ts +233 -0
  57. package/dist/src/bridge.js +604 -0
  58. package/dist/src/byte-stream.d.ts +91 -0
  59. package/dist/src/byte-stream.js +385 -0
  60. package/dist/src/chunk-store.d.ts +41 -8
  61. package/dist/src/chunk-store.js +161 -65
  62. package/dist/src/cli.js +1698 -163
  63. package/dist/src/cloud.d.ts +841 -31
  64. package/dist/src/cloud.js +3196 -277
  65. package/dist/src/context-background.d.ts +37 -0
  66. package/dist/src/context-background.js +309 -0
  67. package/dist/src/context-drain-child.d.ts +1 -0
  68. package/dist/src/context-drain-child.js +98 -0
  69. package/dist/src/context-reader.d.ts +118 -0
  70. package/dist/src/context-reader.js +447 -0
  71. package/dist/src/control-plane/auth.d.ts +32 -4
  72. package/dist/src/control-plane/auth.js +85 -24
  73. package/dist/src/control-plane/server.js +19 -6
  74. package/dist/src/control-plane.d.ts +17 -1
  75. package/dist/src/control-plane.js +32 -6
  76. package/dist/src/crypto.d.ts +1 -1
  77. package/dist/src/crypto.js +5 -5
  78. package/dist/src/daemon-lease.d.ts +70 -0
  79. package/dist/src/daemon-lease.js +420 -0
  80. package/dist/src/daemon.d.ts +94 -1
  81. package/dist/src/daemon.js +1082 -105
  82. package/dist/src/darwin-service-policy.d.ts +41 -0
  83. package/dist/src/darwin-service-policy.js +60 -0
  84. package/dist/src/dashboard-cli.js +15 -15
  85. package/dist/src/device-authorization.d.ts +37 -0
  86. package/dist/src/device-authorization.js +199 -0
  87. package/dist/src/device-enrollment.d.ts +91 -0
  88. package/dist/src/device-enrollment.js +349 -0
  89. package/dist/src/disk.d.ts +17 -12
  90. package/dist/src/disk.js +43 -17
  91. package/dist/src/doctor.d.ts +35 -1
  92. package/dist/src/doctor.js +316 -41
  93. package/dist/src/durable-ticket-lock.d.ts +24 -0
  94. package/dist/src/durable-ticket-lock.js +232 -0
  95. package/dist/src/enroll.d.ts +1 -1
  96. package/dist/src/enroll.js +13 -7
  97. package/dist/src/env.d.ts +10 -1
  98. package/dist/src/env.js +11 -3
  99. package/dist/src/errors.d.ts +8 -8
  100. package/dist/src/errors.js +6 -6
  101. package/dist/src/flush.d.ts +12 -0
  102. package/dist/src/flush.js +37 -0
  103. package/dist/src/heartbeat.d.ts +86 -12
  104. package/dist/src/heartbeat.js +415 -29
  105. package/dist/src/index-background-watchdog.d.ts +1 -0
  106. package/dist/src/index-background-watchdog.js +94 -0
  107. package/dist/src/index-background-work.d.ts +21 -0
  108. package/dist/src/index-background-work.js +25 -0
  109. package/dist/src/index-background.d.ts +64 -0
  110. package/dist/src/index-background.js +394 -0
  111. package/dist/src/index-build-child.d.ts +1 -0
  112. package/dist/src/index-build-child.js +109 -0
  113. package/dist/src/index-manifest.d.ts +52 -0
  114. package/dist/src/index-manifest.js +444 -0
  115. package/dist/src/index-publication-proof.d.ts +84 -0
  116. package/dist/src/index-publication-proof.js +380 -0
  117. package/dist/src/index-publication-state.d.ts +149 -0
  118. package/dist/src/index-publication-state.js +696 -0
  119. package/dist/src/index-publication-verifier.d.ts +89 -0
  120. package/dist/src/index-publication-verifier.js +341 -0
  121. package/dist/src/index-publish.d.ts +62 -0
  122. package/dist/src/index-publish.js +540 -0
  123. package/dist/src/index-scratch-cleanup.d.ts +19 -0
  124. package/dist/src/index-scratch-cleanup.js +166 -0
  125. package/dist/src/index-segment-types.d.ts +132 -0
  126. package/dist/src/index-segment-types.js +21 -0
  127. package/dist/src/index-segments.d.ts +9 -0
  128. package/dist/src/index-segments.js +516 -0
  129. package/dist/src/index-store.d.ts +123 -0
  130. package/dist/src/index-store.js +495 -0
  131. package/dist/src/index-sync.d.ts +91 -15
  132. package/dist/src/index-sync.js +286 -53
  133. package/dist/src/index-upgrade-publication.d.ts +30 -0
  134. package/dist/src/index-upgrade-publication.js +179 -0
  135. package/dist/src/integration-manager.d.ts +32 -0
  136. package/dist/src/integration-manager.js +394 -0
  137. package/dist/src/leakscan.js +1 -1
  138. package/dist/src/local-api.d.ts +56 -15
  139. package/dist/src/local-api.js +4979 -541
  140. package/dist/src/machine-settings.d.ts +51 -0
  141. package/dist/src/machine-settings.js +166 -0
  142. package/dist/src/managed-chunks.d.ts +5 -2
  143. package/dist/src/managed-chunks.js +14 -14
  144. package/dist/src/mcp-install.d.ts +11 -9
  145. package/dist/src/mcp-install.js +73 -28
  146. package/dist/src/mcp-workspace.d.ts +18 -0
  147. package/dist/src/mcp-workspace.js +50 -0
  148. package/dist/src/mcp.js +294 -25
  149. package/dist/src/migrate.js +27 -21
  150. package/dist/src/notify.d.ts +1 -1
  151. package/dist/src/notify.js +5 -5
  152. package/dist/src/offload.d.ts +201 -14
  153. package/dist/src/offload.js +1848 -140
  154. package/dist/src/onboarding.d.ts +8 -1
  155. package/dist/src/onboarding.js +4 -4
  156. package/dist/src/packages.d.ts +2 -2
  157. package/dist/src/packages.js +10 -2
  158. package/dist/src/passkey.d.ts +0 -1
  159. package/dist/src/passkey.js +2 -7
  160. package/dist/src/password-lock.d.ts +2 -2
  161. package/dist/src/password-lock.js +6 -6
  162. package/dist/src/paths.d.ts +2 -0
  163. package/dist/src/paths.js +2 -0
  164. package/dist/src/presence.d.ts +86 -0
  165. package/dist/src/presence.js +240 -0
  166. package/dist/src/progress-deadline.d.ts +21 -0
  167. package/dist/src/progress-deadline.js +91 -0
  168. package/dist/src/project-repair.d.ts +55 -0
  169. package/dist/src/project-repair.js +131 -0
  170. package/dist/src/providers/gcs.d.ts +28 -7
  171. package/dist/src/providers/gcs.js +35 -24
  172. package/dist/src/providers/gdrive.d.ts +71 -8
  173. package/dist/src/providers/gdrive.js +223 -50
  174. package/dist/src/providers/index.d.ts +11 -3
  175. package/dist/src/providers/index.js +30 -10
  176. package/dist/src/providers/s3.d.ts +30 -8
  177. package/dist/src/providers/s3.js +41 -30
  178. package/dist/src/providers/safe-storage-fetch.d.ts +12 -0
  179. package/dist/src/providers/safe-storage-fetch.js +72 -0
  180. package/dist/src/queue.d.ts +136 -19
  181. package/dist/src/queue.js +862 -96
  182. package/dist/src/reclaim-transaction.d.ts +156 -0
  183. package/dist/src/reclaim-transaction.js +1027 -0
  184. package/dist/src/recovery-codes.d.ts +32 -0
  185. package/dist/src/recovery-codes.js +338 -0
  186. package/dist/src/recovery.js +12 -9
  187. package/dist/src/rehydrate.d.ts +25 -22
  188. package/dist/src/rehydrate.js +319 -23
  189. package/dist/src/restore.d.ts +57 -4
  190. package/dist/src/restore.js +272 -36
  191. package/dist/src/resume-stub.d.ts +92 -0
  192. package/dist/src/resume-stub.js +417 -0
  193. package/dist/src/retention.d.ts +98 -7
  194. package/dist/src/retention.js +1066 -62
  195. package/dist/src/rotate.js +3 -3
  196. package/dist/src/search.d.ts +566 -8
  197. package/dist/src/search.js +5897 -290
  198. package/dist/src/secrets.d.ts +51 -7
  199. package/dist/src/secrets.js +316 -24
  200. package/dist/src/service.d.ts +49 -11
  201. package/dist/src/service.js +776 -35
  202. package/dist/src/share.js +3 -3
  203. package/dist/src/shared-spaces.d.ts +98 -0
  204. package/dist/src/shared-spaces.js +214 -0
  205. package/dist/src/source-reader.d.ts +73 -0
  206. package/dist/src/source-reader.js +715 -0
  207. package/dist/src/spool.d.ts +1 -1
  208. package/dist/src/spool.js +1 -1
  209. package/dist/src/start-tui.js +2 -1
  210. package/dist/src/start.js +2 -2
  211. package/dist/src/storage-endpoint.d.ts +21 -0
  212. package/dist/src/storage-endpoint.js +122 -0
  213. package/dist/src/storage-setup.js +12 -12
  214. package/dist/src/storage-targets.d.ts +109 -6
  215. package/dist/src/storage-targets.js +975 -67
  216. package/dist/src/stream-to-cloud.d.ts +5 -1
  217. package/dist/src/stream-to-cloud.js +34 -14
  218. package/dist/src/sync-rules.d.ts +31 -6
  219. package/dist/src/sync-rules.js +153 -14
  220. package/dist/src/team-backfill-scheduling.d.ts +8 -0
  221. package/dist/src/team-backfill-scheduling.js +33 -0
  222. package/dist/src/team-backfill.d.ts +116 -0
  223. package/dist/src/team-backfill.js +1429 -0
  224. package/dist/src/team-index-cache.d.ts +16 -0
  225. package/dist/src/team-index-cache.js +152 -0
  226. package/dist/src/team-offboarding.d.ts +38 -0
  227. package/dist/src/team-offboarding.js +1043 -0
  228. package/dist/src/team-presence.d.ts +127 -0
  229. package/dist/src/team-presence.js +904 -0
  230. package/dist/src/team-publication-policy.d.ts +20 -0
  231. package/dist/src/team-publication-policy.js +140 -0
  232. package/dist/src/team-realtime.d.ts +68 -0
  233. package/dist/src/team-realtime.js +816 -0
  234. package/dist/src/team-source-facts-cache.d.ts +23 -0
  235. package/dist/src/team-source-facts-cache.js +255 -0
  236. package/dist/src/trash.d.ts +1 -1
  237. package/dist/src/trash.js +2 -2
  238. package/dist/src/tui.js +11 -12
  239. package/dist/src/types.d.ts +173 -7
  240. package/dist/src/types.js +20 -0
  241. package/dist/src/ui-server.d.ts +163 -35
  242. package/dist/src/ui-server.js +712 -72
  243. package/dist/src/ui.d.ts +1 -2
  244. package/dist/src/ui.js +1 -2
  245. package/dist/src/upload.d.ts +27 -0
  246. package/dist/src/upload.js +383 -43
  247. package/dist/src/vault.d.ts +226 -30
  248. package/dist/src/vault.js +1776 -192
  249. package/dist/src/watcher.d.ts +7 -1
  250. package/dist/src/watcher.js +198 -55
  251. package/dist/src/worker.d.ts +27 -3
  252. package/dist/src/worker.js +274 -55
  253. package/package.json +33 -12
  254. package/scripts/native-reboot-rehearsal.mjs +90 -0
  255. package/web/app.js +6032 -343
  256. package/web/bootstrap.js +17 -0
  257. package/web/index.html +255 -57
  258. package/web/rail.js +317 -40
  259. package/web/retention.html +2 -2
  260. package/web/rules-view.js +188 -16
  261. package/web/sessions-view.js +485 -62
  262. package/web/sessions.html +2 -2
  263. package/web/setup-api.js +152 -29
  264. package/web/setup-logic.js +68 -9
  265. package/web/setup.html +113 -44
  266. package/web/setup.js +604 -71
  267. package/web/style.css +513 -98
  268. package/dist/packages/vaultline-crypto/src/chunk-access.js +0 -93
  269. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.d.ts +0 -0
  270. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.js +0 -0
  271. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.js +0 -0
  272. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.d.ts +0 -0
  273. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.d.ts +0 -0
  274. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.js +0 -0
@@ -1,5 +1,7 @@
1
- import { type BackendName } from "./secrets.js";
2
- import { type Envelope } from "../packages/vaultline-crypto/src/index.js";
1
+ import { type BackendName, type SecretBackend, type SecretRef } from "./secrets.js";
2
+ import { type Envelope } from "../packages/sealkeep-crypto/src/index.js";
3
+ import { type ByteStream } from "./byte-stream.js";
4
+ import { type TeamIndexCacheIdentity } from "./team-index-cache.js";
3
5
  /**
4
6
  * Sealkeep Cloud: the managed tier.
5
7
  *
@@ -10,6 +12,40 @@ import { type Envelope } from "../packages/vaultline-crypto/src/index.js";
10
12
  * control plane is never sent a key that opens it.
11
13
  */
12
14
  export declare const DEFAULT_CLOUD_URL = "https://shared.spala.ai/p04946/api";
15
+ export declare const cloudUrl: (env?: NodeJS.ProcessEnv) => string;
16
+ export declare function signalCloudAuthChange(dataDir: string): Promise<void>;
17
+ /** Cheap generation probe used by long-running services; contains no token. */
18
+ export declare function cloudAuthGeneration(dataDir: string): Promise<string | null>;
19
+ type CloudCredentialMutation = {
20
+ ref: SecretRef;
21
+ value: string | null;
22
+ };
23
+ type CloudAuthMutationDependencies = {
24
+ /** Fault-injection seam. Production always uses the atomic file writer above. */
25
+ writeGeneration?: (dataDir: string, value: string) => Promise<void>;
26
+ /** Compare-and-set fence for a network result produced under an earlier login. */
27
+ expectedAuthGeneration?: string | null;
28
+ };
29
+ /**
30
+ * Changes account credentials without ever publishing them under an old cache
31
+ * scope. The transition marker is durable and written before the first secret:
32
+ * readers refuse it. A second atomic write publishes the new stable scope only
33
+ * after every secret has been read back successfully.
34
+ *
35
+ * If a secret or the final generation write fails, every old secret is restored
36
+ * and a fresh rollback scope is published. If even that last write cannot land
37
+ * (for example a completely full disk), the transition marker deliberately
38
+ * remains: Cloud reads stay closed instead of guessing which account owns the
39
+ * credentials. A later successful login/logout can replace that marker.
40
+ */
41
+ export declare function commitCloudAuthCredentials(dataDir: string, backend: SecretBackend, requested: readonly CloudCredentialMutation[], dependencies?: CloudAuthMutationDependencies): Promise<void>;
42
+ /**
43
+ * Seqlock for credential readers. A reader which overlaps either edge of an
44
+ * auth transaction must retry; otherwise an old token can be paired with the
45
+ * new generation (or the reverse) even though both individual writes are
46
+ * atomic. Exported as a narrow test seam so the overlap is deterministic.
47
+ */
48
+ export declare function readCloudAuthSnapshot<T>(dataDir: string, read: () => Promise<T>): Promise<T>;
13
49
  /**
14
50
  * Where this vault's cloud token lives in the keystore.
15
51
  *
@@ -30,15 +66,184 @@ export declare function tokenRefFor(vaultId: string, env?: NodeJS.ProcessEnv): {
30
66
  service: string;
31
67
  account: string;
32
68
  };
69
+ /**
70
+ * A fresh install has no vault id yet. Its Cloud pairing therefore waits in
71
+ * the native secret backend under a data-directory-scoped bootstrap key until
72
+ * this machine either creates a new vault or adopts an approved existing one.
73
+ * The hash prevents two independent SealKeep homes on the same OS account from
74
+ * inheriting one another's login without putting the local path in keychain UI.
75
+ */
76
+ export declare function bootstrapTokenRefFor(dataDir: string, env?: NodeJS.ProcessEnv): {
77
+ service: string;
78
+ account: string;
79
+ };
80
+ /**
81
+ * Where this machine's own credential lives, beside the account token.
82
+ *
83
+ * The account token says *which account* is calling and nothing more — every
84
+ * laptop paired to a company account presents an identical one. That is fine
85
+ * for reading and for adding, and wrong for destroying: without a second fact
86
+ * the control plane cannot tell one machine's delete from another's, so any
87
+ * laptop could erase a colleague's sealed history and the audit row would name
88
+ * only the account. The machine key is that second fact. It is minted once at
89
+ * pairing, the server keeps only a bcrypt of it, and it rides in the request
90
+ * body rather than a header because this control plane strips request headers
91
+ * before endpoint logic ever sees them — proven, not assumed.
92
+ */
93
+ export declare function machineRefFor(vaultId: string, env?: NodeJS.ProcessEnv): {
94
+ service: string;
95
+ account: string;
96
+ };
97
+ export declare function bootstrapMachineRefFor(dataDir: string, env?: NodeJS.ProcessEnv): {
98
+ service: string;
99
+ account: string;
100
+ };
101
+ export type MachineCredential = {
102
+ device_id: number;
103
+ machine_key: string;
104
+ };
105
+ /**
106
+ * This machine's credential, claiming one if it does not have it yet.
107
+ *
108
+ * Machines that paired before machine keys existed hold only an account token.
109
+ * Left alone they would keep sealing archives nothing can attribute, and the
110
+ * protection would silently never switch on for them — the worst kind of
111
+ * security change, the sort that upgrades cleanly and does nothing. So the
112
+ * first call that needs an identity claims one. `claim` is false on paths
113
+ * where a network round trip would be a surprise.
114
+ */
115
+ export declare function machineIdentity(dataDir: string, preferred?: BackendName, options?: {
116
+ signal?: AbortSignal;
117
+ }): Promise<MachineCredential | null>;
118
+ /** This machine's credential, or null when it paired before machine keys existed. */
119
+ export declare function machineCredential(dataDir: string, preferred?: BackendName): Promise<MachineCredential | null>;
120
+ /**
121
+ * This machine's credential as a read-only snapshot. Unlike
122
+ * `machineCredential`, this never migrates a pre-vault bootstrap alias and
123
+ * therefore cannot advance the Cloud auth generation. Advisory telemetry uses
124
+ * this path; operations which need a durable machine identity keep the normal
125
+ * healing behaviour above.
126
+ */
127
+ export declare function peekMachineCredential(dataDir: string, preferred?: BackendName): Promise<MachineCredential | null>;
128
+ /**
129
+ * Announce this machine and keep the key it is handed.
130
+ *
131
+ * Best-effort on purpose: a machine that cannot register is still signed in and
132
+ * still archives. It is merely anonymous to the control plane, which is exactly
133
+ * what every machine was before this existed — so failing here must never fail
134
+ * a sign-in.
135
+ */
136
+ export declare function registerMachine(dataDir: string, token: string, preferred?: BackendName, options?: {
137
+ signal?: AbortSignal;
138
+ }): Promise<MachineCredential | null>;
139
+ /**
140
+ * Cloud coordinates fresh-machine approval but never receives a private key or
141
+ * vault secret. These wire records contain a target public key and, after an
142
+ * existing device approves, one opaque E2EE package for that key.
143
+ */
144
+ export type CloudDeviceEnrollmentRequest = {
145
+ id: number;
146
+ target_device_id?: number;
147
+ label: string;
148
+ public_key: string;
149
+ state: "pending" | "approved" | "consumed" | "expired";
150
+ created_at: string;
151
+ expires_at: string;
152
+ approved_at?: string | null;
153
+ };
154
+ export declare function createCloudDeviceEnrollment(dataDir: string, input: {
155
+ label: string;
156
+ publicKey: string;
157
+ requestSecret: string;
158
+ }, preferred?: BackendName): Promise<CloudDeviceEnrollmentRequest>;
159
+ export declare function listPendingCloudDeviceEnrollments(dataDir: string, preferred?: BackendName): Promise<CloudDeviceEnrollmentRequest[]>;
160
+ export declare function approveCloudDeviceEnrollment(dataDir: string, requestId: number, bundle: {
161
+ envelope: Envelope;
162
+ sealed: string;
163
+ }, preferred?: BackendName): Promise<{
164
+ approved: true;
165
+ }>;
166
+ export declare function claimCloudDeviceEnrollment(dataDir: string, requestId: number, requestSecret: string, preferred?: BackendName): Promise<{
167
+ envelope: Envelope | string;
168
+ sealed: string;
169
+ }>;
170
+ export declare function completeCloudDeviceEnrollment(dataDir: string, requestId: number, requestSecret: string, preferred?: BackendName): Promise<{
171
+ consumed: true;
172
+ }>;
173
+ /**
174
+ * `seats` is the purchased machine allowance and is absent on accounts that
175
+ * run on their tier's default, so it is optional rather than zero — a zero
176
+ * here would read as "no machines allowed", which is a different thing.
177
+ */
33
178
  export type CloudAccount = {
34
179
  id: number;
35
180
  email: string;
36
181
  label: string;
37
- plan: "free" | "pro_lite" | "pro";
182
+ plan: "free" | "pro_lite" | "pro" | "team";
38
183
  state: string;
39
184
  quota_bytes: number;
40
185
  used_bytes: number;
186
+ seats?: number | null;
187
+ };
188
+ /**
189
+ * Browser-approved authorization for a fresh machine.
190
+ *
191
+ * The verifier is created and retained by the local client. Cloud receives
192
+ * only its S256 challenge until the claim, so an approval URL copied from the
193
+ * browser cannot be exchanged by whoever happens to see it. `public_key` is
194
+ * the target's ephemeral X25519 enrollment key, not an archive or account key.
195
+ */
196
+ export type CloudDeviceAuthorizationStart = {
197
+ request_id: string;
198
+ approval_url: string;
199
+ created_at?: string;
200
+ expires_at: string;
201
+ poll_after_ms?: number;
202
+ };
203
+ export type CloudDeviceAuthorizationMemory = {
204
+ preserved: boolean;
205
+ bytes: number;
206
+ };
207
+ export type CloudDeviceAuthorizationEnrollment = {
208
+ request_id: number;
209
+ request_secret: string;
210
+ created_at: string;
211
+ expires_at: string;
212
+ };
213
+ export type CloudDeviceAuthorizationClaim = {
214
+ /** `denied` is the current hosted contract; `declined` remains readable for
215
+ * an in-flight authorization created by an older hosted build. */
216
+ status: "pending" | "approved" | "denied" | "declined" | "expired";
217
+ expires_at?: string;
218
+ poll_after_ms?: number;
219
+ token?: string;
220
+ account_token?: string;
221
+ machine?: MachineCredential;
222
+ account?: CloudAccount;
223
+ memory?: CloudDeviceAuthorizationMemory;
224
+ enrollment?: CloudDeviceAuthorizationEnrollment | null;
41
225
  };
226
+ export declare function startCloudDeviceAuthorization(input: {
227
+ challenge: string;
228
+ label: string;
229
+ osFamily: "macos" | "linux" | "windows";
230
+ agents: string[];
231
+ publicKey: string;
232
+ }): Promise<CloudDeviceAuthorizationStart>;
233
+ export declare function claimCloudDeviceAuthorization(input: {
234
+ requestId: string;
235
+ verifier: string;
236
+ }): Promise<CloudDeviceAuthorizationClaim>;
237
+ /**
238
+ * Commits an approved hosted authorization to the same native secret backend
239
+ * used by ordinary Cloud login. Neither credential is ever written to config,
240
+ * returned to the browser, or put in a URL. If the second write fails, the
241
+ * first is rolled back so setup never reports a half-paired machine.
242
+ */
243
+ export declare function rememberCloudDeviceAuthorization(dataDir: string, input: {
244
+ accountToken: string;
245
+ machine: MachineCredential;
246
+ }, preferred?: BackendName): Promise<void>;
42
247
  /**
43
248
  * A lease says where this archive goes and how to get it there.
44
249
  *
@@ -94,6 +299,23 @@ export type UploadLease = {
94
299
  };
95
300
  vended?: VendedCredential;
96
301
  };
302
+ export type CloudBackoff = {
303
+ version: 1;
304
+ until: number;
305
+ retryAfterMs: number;
306
+ updatedAt: string;
307
+ scope: "control-plane";
308
+ };
309
+ /**
310
+ * One cooldown shared by every transport that reaches the control plane.
311
+ *
312
+ * REST learns it from Retry-After, while the realtime listener may learn the
313
+ * same fact during a Socket.IO handshake. Keeping the high-water timestamp in
314
+ * one package-owned receipt prevents either transport (and a freshly started
315
+ * daemon) from bypassing the other one's server-requested quiet period.
316
+ */
317
+ export declare function cloudBackoffUntil(dataDir: string, env?: NodeJS.ProcessEnv): Promise<number>;
318
+ export declare function rememberCloudBackoff(dataDir: string, retryAfterMs: number, env?: NodeJS.ProcessEnv): Promise<CloudBackoff>;
97
319
  export declare function register(dataDir: string, input: {
98
320
  email: string;
99
321
  password: string;
@@ -117,8 +339,262 @@ export declare function loginWithCode(dataDir: string, input: {
117
339
  token: string;
118
340
  }, preferred?: BackendName): Promise<CloudAccount>;
119
341
  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>;
342
+ export declare function cloudToken(dataDir: string, preferred?: BackendName, options?: {
343
+ renew?: boolean;
344
+ signal?: AbortSignal;
345
+ }): Promise<string>;
346
+ export declare function accountStatus(dataDir: string, preferred?: BackendName, options?: {
347
+ signal?: AbortSignal;
348
+ renewToken?: boolean;
349
+ }): Promise<CloudAccount>;
350
+ /** Explicit user reconnection reuses the existing device, never a new seat. */
351
+ export declare function reconnectCloudSession(dataDir: string, preferred?: BackendName): Promise<{
352
+ reconnected: boolean;
353
+ }>;
354
+ /**
355
+ * The hosted team lane stores routing facts and ciphertext only. These wire
356
+ * types deliberately contain no plaintext project, task, person label, or
357
+ * recovery material.
358
+ */
359
+ export type TeamIdentityWire = {
360
+ id?: number;
361
+ account_id?: number;
362
+ public_key: string;
363
+ key_box: string;
364
+ updated_at?: string;
365
+ };
366
+ export type TeamSpaceWire = {
367
+ id?: number;
368
+ space_id?: number;
369
+ space_key: string;
370
+ owner_account_id?: number;
371
+ key_version: number;
372
+ membership_version?: number;
373
+ /** Opaque Socket.IO room capability. Rotated with every feed-key epoch. */
374
+ realtime_key?: string;
375
+ role?: "owner" | "member" | string;
376
+ public_key?: string;
377
+ wrapped_key?: string;
378
+ joined_at?: string;
379
+ project_name?: string;
380
+ };
381
+ export type TeamInviteeWire = {
382
+ account_id: number;
383
+ public_key: string;
384
+ key_version?: number;
385
+ };
386
+ export type TeamMemberWire = {
387
+ id?: number;
388
+ space_id?: number;
389
+ space_key?: string;
390
+ account_id: number;
391
+ public_key: string;
392
+ wrapped_key: string;
393
+ key_version: number;
394
+ membership_version?: number;
395
+ realtime_key?: string;
396
+ display_email?: string;
397
+ role: "owner" | "member" | string;
398
+ joined_at?: string;
399
+ project_name?: string;
400
+ };
401
+ export type TeamRosterWire = {
402
+ members: TeamMemberWire[];
403
+ membership_version: number;
404
+ key_version?: number;
405
+ };
406
+ export type TeamPresenceRow = {
407
+ id: number;
408
+ space_id?: number;
409
+ account_id?: number;
410
+ sender: string;
411
+ sequence: number;
412
+ key_version: number;
413
+ ciphertext: string;
414
+ created_at?: string;
415
+ };
416
+ export type TeamRotationWrapWire = {
417
+ account_id: number;
418
+ public_key: string;
419
+ wrapped_key: string;
420
+ };
421
+ export type TeamRotationWire = {
422
+ removed_account_id: number;
423
+ history_fenced?: boolean;
424
+ key_version: number;
425
+ membership_version?: number;
426
+ realtime_key: string;
427
+ event?: TeamPresenceRow;
428
+ };
429
+ export type TeamArchiveWire = {
430
+ vault_ref: string;
431
+ membership_version?: number;
432
+ bytes: number;
433
+ ciphertext_sha256?: string;
434
+ uploader_account_id?: number;
435
+ created_at?: string;
436
+ };
437
+ export type TeamInvitationWire = {
438
+ id: number;
439
+ space_key: string;
440
+ inviter_account_id?: number;
441
+ invitee_account_id?: number;
442
+ invitee_email?: string;
443
+ inviter_email?: string;
444
+ project_name?: string;
445
+ public_key?: string;
446
+ state?: "preparing" | "ready";
447
+ status?: "pending" | "accepted" | "declined";
448
+ created_at?: string;
449
+ wrapped_key?: string;
450
+ key_version?: number;
451
+ history_fenced?: boolean;
452
+ history_membership_version?: number;
453
+ history_index_revision?: number;
454
+ history_archive_refs?: string;
455
+ };
456
+ export type TeamInvitationHistoryFence = {
457
+ expected_membership_version: number;
458
+ expected_index_revision: number;
459
+ expected_archive_refs: string;
460
+ };
461
+ export type TeamAccessRequestWire = {
462
+ id: number;
463
+ space_key: string;
464
+ account_id?: number;
465
+ target_account_id: number;
466
+ target_email?: string;
467
+ project_name?: string;
468
+ state: "requested" | "completed";
469
+ created_at?: string;
470
+ };
471
+ export type TeamProjectRequestWire = {
472
+ id: number;
473
+ account_id?: number;
474
+ project_name: string;
475
+ state: "requested" | "completed";
476
+ created_at?: string;
477
+ completed_at?: string;
478
+ space_key?: string;
479
+ };
480
+ export declare function getTeamIdentity(dataDir: string, preferred?: BackendName): Promise<TeamIdentityWire | null>;
481
+ export declare function putTeamIdentity(dataDir: string, identity: Pick<TeamIdentityWire, "public_key" | "key_box">, preferred?: BackendName): Promise<TeamIdentityWire>;
482
+ export declare function createTeamSpace(dataDir: string, input: {
483
+ space_key: string;
484
+ wrapped_key: string;
485
+ realtime_key: string;
486
+ project_name?: string;
487
+ }, preferred?: BackendName): Promise<TeamSpaceWire>;
488
+ export declare function listTeamSpaces(dataDir: string, preferred?: BackendName): Promise<TeamSpaceWire[]>;
489
+ /** Cloud-owned intent; a connected machine creates and seals the project key. */
490
+ export declare function listTeamProjectRequests(dataDir: string, preferred?: BackendName): Promise<TeamProjectRequestWire[]>;
491
+ /** Create cloud-owned project intent; key creation still happens on a connected machine. */
492
+ export declare function createTeamProjectRequest(dataDir: string, projectName: string, preferred?: BackendName): Promise<TeamProjectRequestWire>;
493
+ /** Closes a project intent only after this account owns the prepared space. */
494
+ export declare function completeTeamProjectRequest(dataDir: string, requestId: number, spaceKey: string, preferred?: BackendName): Promise<TeamProjectRequestWire>;
495
+ export declare function getTeamMembership(dataDir: string, spaceKey: string, preferred?: BackendName): Promise<TeamMemberWire | null>;
496
+ export declare function resolveTeamInvitee(dataDir: string, spaceKey: string, email: string, preferred?: BackendName): Promise<TeamInviteeWire>;
497
+ export declare function addTeamMember(dataDir: string, spaceKey: string, input: {
498
+ email: string;
499
+ wrapped_key: string;
500
+ expected_key_version: number;
501
+ }, preferred?: BackendName): Promise<TeamMemberWire>;
502
+ /** Creates a pending invitation; membership is not granted until acceptance. */
503
+ export declare function createTeamInvitation(dataDir: string, spaceKey: string, input: {
504
+ email: string;
505
+ wrapped_key?: string;
506
+ expected_key_version?: number;
507
+ } & Partial<TeamInvitationHistoryFence>, preferred?: BackendName): Promise<TeamInvitationWire>;
508
+ /** Invitations created in the hosted account that this signed-in account owns. */
509
+ export declare function listOutgoingTeamInvitations(dataDir: string, preferred?: BackendName): Promise<TeamInvitationWire[]>;
510
+ /** Submit ciphertext produced by the installed owner machine; plaintext keys never reach the browser. */
511
+ export declare function prepareTeamInvitation(dataDir: string, invitationId: number, input: {
512
+ wrapped_key: string;
513
+ expected_key_version: number;
514
+ } & TeamInvitationHistoryFence, preferred?: BackendName): Promise<TeamInvitationWire>;
515
+ export declare function listTeamInvitations(dataDir: string, preferred?: BackendName): Promise<TeamInvitationWire[]>;
516
+ export declare function acceptTeamInvitation(dataDir: string, invitationId: number, preferred?: BackendName): Promise<TeamMemberWire>;
517
+ export declare function declineTeamInvitation(dataDir: string, invitationId: number, preferred?: BackendName): Promise<TeamInvitationWire>;
518
+ export declare function listTeamMembers(dataDir: string, spaceKey: string, preferred?: BackendName): Promise<TeamMemberWire[]>;
519
+ /** One server-consistent roster snapshot for CAS-sensitive sharing work. */
520
+ export declare function getTeamRoster(dataDir: string, spaceKey: string, preferred?: BackendName, options?: {
521
+ signal?: AbortSignal;
522
+ renewToken?: boolean;
523
+ }): Promise<TeamRosterWire>;
524
+ /** Cloud-owned intent; a connected owner machine performs the key rotation. */
525
+ export declare function createTeamRemovalRequest(dataDir: string, spaceKey: string, targetAccountId: number, preferred?: BackendName): Promise<TeamAccessRequestWire>;
526
+ export declare function listOutgoingTeamAccessRequests(dataDir: string, preferred?: BackendName): Promise<TeamAccessRequestWire[]>;
527
+ export declare function completeTeamAccessRequest(dataDir: string, requestId: number, preferred?: BackendName): Promise<TeamAccessRequestWire>;
528
+ export declare function registerTeamArchive(dataDir: string, spaceKey: string, vaultRef: string, expectedMembershipVersion: number, preferred?: BackendName, options?: {
529
+ signal?: AbortSignal;
530
+ }): Promise<TeamArchiveWire>;
531
+ export declare function unregisterTeamArchive(dataDir: string, spaceKey: string, vaultRef: string, expectedMembershipVersion: number, preferred?: BackendName, options?: {
532
+ signal?: AbortSignal;
533
+ }): Promise<void>;
534
+ export declare function listTeamArchives(dataDir: string, spaceKey: string, preferred?: BackendName, options?: {
535
+ signal?: AbortSignal;
536
+ renewToken?: boolean;
537
+ }): Promise<TeamArchiveWire[]>;
538
+ export declare function teamArchiveDownload(dataDir: string, spaceKey: string, vaultRef: string, preferred?: BackendName, options?: {
539
+ signal?: AbortSignal;
540
+ }): Promise<DownloadLease>;
541
+ export type TeamReadGuard = {
542
+ assertCurrent(refs?: Iterable<string>): Promise<void>;
543
+ };
544
+ /** Pins local account identity while rechecking live membership and every exact grant.
545
+ * This guard carries no reusable permission: each assertion calls the Team plane. */
546
+ export declare function createTeamReadGuard(dataDir: string, spaceKey: string, refs: Iterable<string>, membershipVersion: number, options?: {
547
+ signal?: AbortSignal;
548
+ preferred?: BackendName;
549
+ }): Promise<TeamReadGuard>;
550
+ /** Strict framed-object range transport. Integrity of a slice additionally
551
+ * requires the caller's authenticated envelope and per-chunk AEAD checks. */
552
+ export declare function pullTeamCiphertextRange(dataDir: string, spaceKey: string, vaultRef: string, storedOffset: number, storedLength: number, preferred?: BackendName, options?: {
553
+ signal?: AbortSignal;
554
+ maxBytes?: number;
555
+ expectedMembershipVersion?: number;
556
+ /** One logical context read may share its identity pin. Its owner must call
557
+ * assertCurrent() after all ranges and before releasing any plaintext. */
558
+ readGuard?: TeamReadGuard;
559
+ }): Promise<Buffer>;
560
+ /** Streaming, integrity-checked read through the current project membership gate.
561
+ * Consumers must exhaust the stream before publishing any downloaded bytes. */
562
+ export declare function pullTeamCiphertextStream(dataDir: string, spaceKey: string, vaultRef: string, preferred?: BackendName, options?: {
563
+ signal?: AbortSignal;
564
+ unframe?: boolean;
565
+ maxBytes?: number;
566
+ }): Promise<ByteStream>;
567
+ /** Download and verify a shared archive through the current membership gate. */
568
+ export declare function pullTeamCiphertext(dataDir: string, spaceKey: string, vaultRef: string, preferred?: BackendName): Promise<{
569
+ ciphertext: Buffer;
570
+ bytes: number;
571
+ }>;
572
+ /**
573
+ * One owner-authorized, compare-and-swap epoch transition. The server removes
574
+ * the target, replaces every remaining wrap, advances the room capability and
575
+ * appends the encrypted discovery marker as one transaction.
576
+ */
577
+ export declare function removeTeamMemberAndRotate(dataDir: string, spaceKey: string, targetAccountId: number, input: {
578
+ expected_key_version: number;
579
+ expected_membership_version?: number;
580
+ expected_index_revision?: number;
581
+ expected_archive_refs?: string;
582
+ realtime_key: string;
583
+ wraps: TeamRotationWrapWire[];
584
+ binding_sender: string;
585
+ binding_sequence: number;
586
+ binding_ciphertext: string;
587
+ }, preferred?: BackendName, options?: {
588
+ signal?: AbortSignal;
589
+ }): Promise<TeamRotationWire>;
590
+ export declare function publishTeamPresence(dataDir: string, spaceKey: string, input: {
591
+ sender: string;
592
+ sequence: number;
593
+ key_version: number;
594
+ ciphertext: string;
595
+ }, preferred?: BackendName): Promise<TeamPresenceRow>;
596
+ export declare function pollTeamPresence(dataDir: string, spaceKey: string, after: number, preferred?: BackendName): Promise<TeamPresenceRow[]>;
597
+ export declare function recentTeamPresence(dataDir: string, spaceKey: string, preferred?: BackendName): Promise<TeamPresenceRow[]>;
122
598
  /**
123
599
  * Recovery without custody.
124
600
  *
@@ -202,10 +678,25 @@ export declare function openEscrowWithPassword(envelope: Envelope, ciphertext: B
202
678
  */
203
679
  export declare function openEscrowWithKey(envelope: Envelope, ciphertext: Buffer, privateKeyRaw: Buffer): Record<string, unknown>;
204
680
  export type EscrowBlob = {
205
- envelope: Envelope;
681
+ envelope: Envelope | string;
206
682
  sealed: string;
207
683
  created_at?: string;
684
+ id?: number;
685
+ };
686
+ export type RecoveryCodeCapsule = {
687
+ recipient_id: string;
688
+ envelope: Envelope | string;
689
+ sealed: string;
208
690
  };
691
+ export type RecoveryKitCloudStatus = {
692
+ available: boolean;
693
+ remaining: number;
694
+ created_at?: string | null;
695
+ };
696
+ /** Upload an already-sealed escrow envelope without ever reconstructing it server-side. */
697
+ export declare function pushEscrowBlob(dataDir: string, envelope: Envelope, ciphertext: Buffer, preferred?: BackendName): Promise<{
698
+ stored: true;
699
+ }>;
209
700
  /**
210
701
  * Hand the sealed blob to the control plane.
211
702
  *
@@ -219,13 +710,60 @@ export declare function pushEscrow(dataDir: string, secretMaterial: Record<strin
219
710
  /** Fetch and open the escrow blob. Requires the phrase; nothing else will do. */
220
711
  /** Whether a sealed copy exists, without needing the phrase to find out. */
221
712
  export declare function hasSealedCopy(dataDir: string, preferred?: BackendName): Promise<boolean>;
713
+ /** Latest append-only escrow revision; still ciphertext to every server involved. */
714
+ export declare function pullEscrowBlob(dataDir: string, preferred?: BackendName): Promise<EscrowBlob | null>;
715
+ /**
716
+ * Atomically replaces the phrase-only escrow copy and all one-time recovery
717
+ * capsules. Each capsule contains one opaque key wrap; Cloud never receives a
718
+ * code or phrase and normal escrow reads never expose a code-decryptable wrap.
719
+ */
720
+ export declare function replaceCloudRecoveryKit(dataDir: string, master: {
721
+ envelope: Envelope;
722
+ ciphertext: Buffer;
723
+ }, capsules: readonly RecoveryCodeCapsule[], preferred?: BackendName): Promise<{
724
+ stored: true;
725
+ remaining: number;
726
+ created_at?: string;
727
+ }>;
728
+ export declare function cloudRecoveryKitStatus(dataDir: string, preferred?: BackendName): Promise<RecoveryKitCloudStatus>;
729
+ /**
730
+ * Atomically awards exactly one encrypted capsule to one random machine claim.
731
+ * A retry with the same claim is idempotent; a different claim gets no capsule.
732
+ */
733
+ export declare function redeemCloudRecoveryCode(dataDir: string, recipientId: string, claimId: string, preferred?: BackendName): Promise<EscrowBlob & {
734
+ remaining: number;
735
+ }>;
736
+ /** Atomically spends one recovery recipient for this account. The code never crosses the wire. */
737
+ export declare function consumeCloudRecoveryCode(dataDir: string, recipientId: string, claimId: string, preferred?: BackendName): Promise<{
738
+ consumed: true;
739
+ }>;
740
+ /**
741
+ * Resolves an ambiguous consume response without making a spent code reusable.
742
+ * Only the same random claim made by this machine is acknowledged.
743
+ */
744
+ export declare function cloudRecoveryCodeClaimStatus(dataDir: string, recipientId: string, claimId: string, preferred?: BackendName): Promise<EscrowBlob & {
745
+ remaining: number;
746
+ }>;
747
+ /** Returns the awarded capsule only to the exact claim that spent the code. */
748
+ export declare function claimCloudRecoveryCodeCapsule(dataDir: string, recipientId: string, claimId: string, preferred?: BackendName): Promise<EscrowBlob & {
749
+ remaining: number;
750
+ }>;
222
751
  export declare function pullEscrow(dataDir: string, rawPhrase: string, preferred?: BackendName): Promise<Record<string, unknown>>;
752
+ /** Process-local hint, never a cached permission or a serializable credential.
753
+ * Its only use is to require a fresh, descriptor-identical pending row before
754
+ * retrying the same frame. A completed object is never authorized by it. */
755
+ export type IncompleteUpload = Readonly<{
756
+ status: "upload_incomplete";
757
+ }>;
223
758
  /** Asks for permission to write exactly one object. The key is chosen server-side. */
224
759
  export declare function requestUpload(dataDir: string, input: {
225
760
  vaultRef: string;
226
761
  ciphertextSha256: string;
227
762
  bytes: number;
228
- }, preferred?: BackendName): Promise<UploadLease>;
763
+ }, preferred?: BackendName, options?: {
764
+ signal?: AbortSignal;
765
+ incompleteUpload?: IncompleteUpload;
766
+ }): Promise<UploadLease>;
229
767
  /**
230
768
  * Straight to storage, whichever way the lease delegated.
231
769
  *
@@ -236,13 +774,29 @@ export declare function requestUpload(dataDir: string, input: {
236
774
  * rather than two. A lease naming a delegation this build does not know is
237
775
  * refused before a single byte or signature leaves the machine.
238
776
  */
777
+ export declare function putCiphertextStream(lease: UploadLease, chunks: ByteStream, bytes: number, signal?: AbortSignal): Promise<{
778
+ bytes: number;
779
+ remoteChecksum: string | null;
780
+ }>;
781
+ /** Small-object compatibility (managed chunks/index); archives use the stream door. */
239
782
  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>;
783
+ type BackgroundObjectReadOptions = {
784
+ signal?: AbortSignal;
785
+ maxBytesPerSecond?: number;
786
+ onProgress?: (bytes: number) => void;
787
+ onYield?: () => void | Promise<void>;
788
+ paceNow?: () => number;
789
+ paceSleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
790
+ };
791
+ export declare function completeUpload(dataDir: string, leaseId: number, bytes: number, preferred?: BackendName, options?: {
792
+ signal?: AbortSignal;
793
+ }): Promise<unknown>;
241
794
  export type PushResult = {
242
795
  archiveId: string;
243
796
  objectKey: string;
244
797
  bytes: number;
245
798
  durable: boolean;
799
+ checksum?: string;
246
800
  note?: string;
247
801
  };
248
802
  /**
@@ -261,6 +815,7 @@ export type PushResult = {
261
815
  * truncated or substituted object is refused rather than decrypted.
262
816
  */
263
817
  export type CloudArchive = {
818
+ id?: number;
264
819
  vault_ref: string;
265
820
  bytes: number;
266
821
  state: string;
@@ -268,6 +823,12 @@ export type CloudArchive = {
268
823
  created_at?: string;
269
824
  object_key?: string;
270
825
  };
826
+ /** Array-compatible catalog result. `complete=false` is non-enumerable so
827
+ * existing JSON/iteration callers keep their contract while completeness-aware
828
+ * surfaces can avoid presenting a legacy capped listing as the whole account. */
829
+ export type CloudArchiveList = CloudArchive[] & {
830
+ complete?: boolean;
831
+ };
271
832
  /** What managed storage is holding for this account. The ref is what `sealkeep open` takes. */
272
833
  /**
273
834
  * The managed index slot: one sealed content index per account, moved through
@@ -318,15 +879,162 @@ export declare function connectGdriveViaCloud(dataDir: string, options?: {
318
879
  timeoutMs?: number;
319
880
  preferred?: BackendName;
320
881
  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[]>;
882
+ /** Test seam; production resolves the opaque Drive permission id. */
883
+ accountIdResolver?: (accessToken: string) => Promise<string>;
884
+ }): Promise<import("./providers/gdrive.js").GdriveCredentials>;
885
+ export declare function pushIndexObject(dataDir: string, kind: "meta" | "blob", body: Buffer, preferred?: BackendName, options?: {
886
+ signal?: AbortSignal;
887
+ maxBytesPerSecond?: number;
888
+ onProgress?: (bytes: number) => void;
889
+ }): Promise<void>;
890
+ /** Publishes a large sealed index without reading the whole ciphertext back
891
+ * into the JS heap. The caller has already atomically committed this file. */
892
+ export declare function pushIndexFile(dataDir: string, kind: "meta" | "blob", path: string, preferred?: BackendName, options?: {
893
+ signal?: AbortSignal;
894
+ maxBytesPerSecond?: number;
895
+ onProgress?: (bytes: number) => void;
896
+ }): Promise<void>;
897
+ export declare function pullIndexObject(dataDir: string, kind: "meta" | "blob", preferred?: BackendName, options?: {
898
+ signal?: AbortSignal;
899
+ renewToken?: boolean;
900
+ maxBytesPerSecond?: number;
901
+ onProgress?: (bytes: number) => void;
902
+ }): Promise<Buffer | null>;
903
+ export declare function pushTeamIndexObject(dataDir: string, spaceKey: string, kind: "meta" | "blob", body: Buffer, preferred?: BackendName, options?: {
904
+ signal?: AbortSignal;
905
+ maxBytesPerSecond?: number;
906
+ onProgress?: (bytes: number) => void;
907
+ }): Promise<void>;
908
+ export declare function pullTeamIndexObject(dataDir: string, spaceKey: string, kind: "meta" | "blob", preferred?: BackendName, options?: {
909
+ signal?: AbortSignal;
910
+ maxBytesPerSecond?: number;
911
+ onProgress?: (bytes: number) => void;
912
+ }): Promise<Buffer | null>;
913
+ /**
914
+ * A committed index generation is an immutable pair. The durable receipt has
915
+ * no signed URLs: those are short-lived bearer capabilities and must never be
916
+ * copied into the publication ledger. `ManagedIndexHead` carries the same
917
+ * receipt plus transient download leases returned by one `/head` request.
918
+ */
919
+ export type ManagedIndexCoverage = Record<string, string>;
920
+ export type ManagedIndexObjectReceipt = {
921
+ objectKey: string;
922
+ bytes: number;
923
+ sha256: string;
924
+ };
925
+ export type ManagedIndexReceipt = {
926
+ revision: number;
927
+ generationId: string;
928
+ blob: ManagedIndexObjectReceipt;
929
+ meta: ManagedIndexObjectReceipt;
930
+ coverageJson: ManagedIndexCoverage;
931
+ membershipVersion?: number;
932
+ committedAt: string;
933
+ };
934
+ export type ManagedIndexDownloadLease = {
935
+ url: string;
936
+ method?: string;
937
+ headers?: Record<string, string>;
938
+ };
939
+ export type ManagedIndexHead = {
940
+ receipt: ManagedIndexReceipt;
941
+ downloads: {
942
+ blob: ManagedIndexDownloadLease;
943
+ meta: ManagedIndexDownloadLease;
944
+ };
945
+ };
946
+ export type ManagedIndexV2Unsupported = {
947
+ status: "unsupported";
948
+ reason: "endpoint_unsupported";
949
+ };
950
+ export type ManagedIndexV2HeadResult = ManagedIndexV2Unsupported | {
951
+ status: "ok";
952
+ head: ManagedIndexHead | null;
953
+ };
954
+ export type ManagedIndexV2Snapshot = {
955
+ receipt: ManagedIndexReceipt;
956
+ /** Transient leases used for this exact snapshot; never persist this field. */
957
+ head: ManagedIndexHead;
958
+ envelope: Envelope;
959
+ ciphertext: Buffer;
960
+ };
961
+ /** Only native current-head snapshots can identify a derived source-facts cache.
962
+ * The identity contains no lease or permission and is independently copied. */
963
+ export declare function teamSnapshotCacheIdentity(snapshot: ManagedIndexV2Snapshot): TeamIndexCacheIdentity | undefined;
964
+ export type ManagedIndexV2SnapshotResult = ManagedIndexV2Unsupported | {
965
+ status: "ok";
966
+ snapshot: ManagedIndexV2Snapshot | null;
967
+ };
968
+ export type ManagedIndexV2PublishResult = ManagedIndexV2Unsupported | {
969
+ status: "conflict";
970
+ head: ManagedIndexHead | null;
971
+ } | {
972
+ status: "committed";
973
+ receipt: ManagedIndexReceipt;
974
+ /** Transient post-commit head; its signed URLs must not be persisted. */
975
+ head: ManagedIndexHead;
976
+ /** Both immutable objects were fetched and hash-checked after commit. */
977
+ verifiedReadBack: true;
978
+ envelope: Envelope;
979
+ };
980
+ export type ManagedIndexV2PublishInput = {
981
+ expectedRevision: number;
982
+ coverageJson: ManagedIndexCoverage;
983
+ envelope: Envelope;
984
+ ciphertext: Buffer | {
985
+ path: string;
986
+ };
987
+ expectedMembershipVersion?: number;
988
+ };
989
+ export declare const INDEX_REVISION_CONFLICT_REMOTE_CODE: "INDEX_REVISION_CONFLICT";
990
+ /** The hosted v2 finalizer currently uses one S3 CopyObject (<5 GiB). */
991
+ export declare const MAX_MANAGED_INDEX_V2_BLOB_BYTES: number;
992
+ export declare function headIndexGenerationV2(dataDir: string, preferred?: BackendName, options?: {
993
+ signal?: AbortSignal;
994
+ renewToken?: boolean;
995
+ }): Promise<ManagedIndexV2HeadResult>;
996
+ export declare function headTeamIndexGenerationV2(dataDir: string, spaceKey: string, preferred?: BackendName, options?: {
997
+ signal?: AbortSignal;
998
+ renewToken?: boolean;
999
+ }): Promise<ManagedIndexV2HeadResult>;
1000
+ export declare function publishIndexGenerationV2(dataDir: string, input: Omit<ManagedIndexV2PublishInput, "expectedMembershipVersion">, preferred?: BackendName, options?: {
1001
+ signal?: AbortSignal;
1002
+ maxBytesPerSecond?: number;
1003
+ onProgress?: (bytes: number) => void;
1004
+ }): Promise<ManagedIndexV2PublishResult>;
1005
+ export declare function publishTeamIndexGenerationV2(dataDir: string, spaceKey: string, input: ManagedIndexV2PublishInput & {
1006
+ expectedMembershipVersion: number;
1007
+ }, preferred?: BackendName, options?: {
1008
+ signal?: AbortSignal;
1009
+ maxBytesPerSecond?: number;
1010
+ onProgress?: (bytes: number) => void;
1011
+ }): Promise<ManagedIndexV2PublishResult>;
1012
+ export declare function pullIndexGenerationV2(dataDir: string, preferred?: BackendName, options?: {
1013
+ signal?: AbortSignal;
1014
+ renewToken?: boolean;
1015
+ maxBytesPerSecond?: number;
1016
+ onProgress?: (bytes: number) => void;
1017
+ maxBytes?: number;
1018
+ }): Promise<ManagedIndexV2SnapshotResult>;
1019
+ export declare function pullTeamIndexGenerationV2(dataDir: string, spaceKey: string, preferred?: BackendName, options?: {
1020
+ signal?: AbortSignal;
1021
+ renewToken?: boolean;
1022
+ maxBytesPerSecond?: number;
1023
+ onProgress?: (bytes: number) => void;
1024
+ maxBytes?: number;
1025
+ }): Promise<ManagedIndexV2SnapshotResult>;
1026
+ export declare function listCloudArchives(dataDir: string, preferred?: BackendName, options?: {
1027
+ signal?: AbortSignal;
1028
+ renewToken?: boolean;
1029
+ token?: string;
1030
+ }): Promise<CloudArchiveList>;
1031
+ /**
1032
+ * Cheap first-run question: does this signed-in account already preserve any
1033
+ * archive? It deliberately fetches only the first cursor page instead of using
1034
+ * `listCloudArchives`, because setup needs a branch, not a thousand-row index.
1035
+ * Older control planes fall back to their capped flat listing.
1036
+ */
1037
+ export declare function hasCloudArchives(dataDir: string, preferred?: BackendName): Promise<boolean>;
330
1038
  /**
331
1039
  * Really deletes one stored object from managed storage.
332
1040
  *
@@ -336,7 +1044,22 @@ export declare function listCloudArchives(dataDir: string, preferred?: BackendNa
336
1044
  * "deleted". The control plane removes the object and its row, and billing
337
1045
  * follows the row.
338
1046
  */
339
- export declare function deleteCloudArchive(dataDir: string, vaultRef: string, preferred?: BackendName): Promise<void>;
1047
+ /** One account/token/device snapshot shared by every object in one destructive
1048
+ * managed operation. It is intentionally not serialisable or returned by an
1049
+ * API: the token remains inside this process. */
1050
+ export type ManagedDeleteContext = {
1051
+ authGeneration: string | null;
1052
+ token: string;
1053
+ machine: MachineCredential | null;
1054
+ };
1055
+ export declare function managedDeleteContext(dataDir: string, preferred?: BackendName, options?: {
1056
+ expectedAuthGeneration?: string | null;
1057
+ signal?: AbortSignal;
1058
+ }): Promise<ManagedDeleteContext>;
1059
+ export declare function deleteCloudArchive(dataDir: string, vaultRef: string, preferred?: BackendName, options?: {
1060
+ context?: ManagedDeleteContext;
1061
+ signal?: AbortSignal;
1062
+ }): Promise<void>;
340
1063
  export type DownloadLease = {
341
1064
  vault_ref: string;
342
1065
  bytes: number;
@@ -349,14 +1072,26 @@ export type DownloadLease = {
349
1072
  };
350
1073
  vended?: VendedCredential;
351
1074
  };
1075
+ /** Immutable native lease identity only; it does not authenticate a partial
1076
+ * object. Range callers still need a trusted envelope and chunk AEAD. */
1077
+ export type PersonalRangeDescriptor = Readonly<{
1078
+ vaultRef: string;
1079
+ bytes: number;
1080
+ ciphertextSha256: string;
1081
+ }>;
352
1082
  /**
353
1083
  * A byte range of a stored object, for chunk-granular reads. The whole-object
354
1084
  * hash cannot be checked on a slice — that is the point of a slice — so the
355
1085
  * 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.
1086
+ * (see sealkeep-crypto/chunk-access). Only the presigned-URL lease shape
1087
+ * supports ranges today. A range request never falls back to a whole object:
1088
+ * neither an unsupported lease nor a storage server's 200 can spend an
1089
+ * unbounded amount of memory/bandwidth on a small search or metadata read.
358
1090
  */
359
- export declare function pullCiphertextRange(dataDir: string, vaultRef: string, storedOffset: number, storedLength: number, preferred?: BackendName): Promise<Buffer>;
1091
+ export declare function pullCiphertextRange(dataDir: string, vaultRef: string, storedOffset: number, storedLength: number, preferred?: BackendName, options?: BackgroundObjectReadOptions & {
1092
+ maxBytes?: number;
1093
+ onDescriptor?: (descriptor: PersonalRangeDescriptor) => void | Promise<void>;
1094
+ }): Promise<Buffer>;
360
1095
  /**
361
1096
  * Existence and stored size of a managed object, proven against STORAGE, not
362
1097
  * the plane's row: the row is minted by metering and a metering row is not an
@@ -365,32 +1100,75 @@ export declare function pullCiphertextRange(dataDir: string, vaultRef: string, s
365
1100
  * count. This is what lets chunk-folder verification and resume treat managed
366
1101
  * storage with exactly the discipline `verifyStored` demands of it.
367
1102
  */
368
- export declare function statCiphertext(dataDir: string, vaultRef: string, preferred?: BackendName): Promise<{
1103
+ export declare function statCiphertext(dataDir: string, vaultRef: string, preferred?: BackendName, options?: {
1104
+ signal?: AbortSignal;
1105
+ allowIncomplete?: boolean;
1106
+ }): Promise<{
369
1107
  exists: boolean;
370
1108
  bytes: number;
1109
+ incompleteUpload?: IncompleteUpload;
371
1110
  }>;
372
- export declare function pullCiphertext(dataDir: string, vaultRef: string, preferred?: BackendName): Promise<{
1111
+ export declare function pullCiphertextStream(dataDir: string, vaultRef: string, preferred?: BackendName, options?: BackgroundObjectReadOptions & {
1112
+ unframe?: boolean;
1113
+ maxBytes?: number;
1114
+ }): Promise<ByteStream>;
1115
+ export declare function pullCiphertext(dataDir: string, vaultRef: string, preferred?: BackendName, options?: {
1116
+ signal?: AbortSignal;
1117
+ }): Promise<{
373
1118
  ciphertext: Buffer;
374
1119
  bytes: number;
375
1120
  }>;
1121
+ export declare function frameObjectHeader(envelope: unknown): Buffer;
376
1122
  export declare function frameObject(envelope: unknown, ciphertext: Buffer): Buffer;
377
1123
  export declare function unframeObject(blob: Buffer): {
378
1124
  envelope: any | null;
379
1125
  ciphertext: Buffer;
380
1126
  };
1127
+ /** Every managed reference `vaultRef` occupies, so a caller can pull the raw parts. */
1128
+ export declare function managedArchiveParts(dataDir: string, vaultRef: string, preferred?: BackendName): Promise<{
1129
+ chunked: boolean;
1130
+ refs: string[];
1131
+ }>;
381
1132
  /**
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.
1133
+ * `unlock` is a phrase, or a recipient's private key — the second is how an
1134
+ * organisation reads what a colleague sealed without anybody sharing a vault
1135
+ * phrase, which is the only workable shape for a team.
389
1136
  */
390
- export declare function openFromCloud(dataDir: string, vaultRef: string, destination: string, rawPhrase: string, preferred?: BackendName): Promise<{
1137
+ export declare function openFromCloud(dataDir: string, vaultRef: string, destination: string, unlock: string | {
1138
+ privateKey: string;
1139
+ }, preferred?: BackendName): Promise<{
391
1140
  output: string;
392
1141
  bytes: number;
393
1142
  }>;
1143
+ /**
1144
+ * Tells the account's security feed what the local scanner found.
1145
+ *
1146
+ * The scan itself happens on this machine, on the plaintext, because this is
1147
+ * the only place the plaintext exists — the plane holds ciphertext and could
1148
+ * not scan it if it wanted to. What travels is the rule that matched, how
1149
+ * often, this machine, and the archive: never the matched text, never the
1150
+ * surrounding line, never the project name. A leak report that repeated the
1151
+ * leak would be a second leak, and one sent to a server that was designed
1152
+ * never to see the first would be worse than that.
1153
+ *
1154
+ * Best-effort by construction. An account that is not on a team plan answers
1155
+ * 402 and we say nothing; a network failure loses a report, not a seal. The
1156
+ * one thing this must never do is prevent an archive from being sealed.
1157
+ */
1158
+ export declare function reportSecurityFindings(dataDir: string, input: {
1159
+ vaultRef?: string;
1160
+ findings: {
1161
+ kind: string;
1162
+ severity: "high" | "medium";
1163
+ }[];
1164
+ }, preferred?: BackendName): Promise<{
1165
+ reported: number;
1166
+ }>;
1167
+ /** What the company's own rules say, for machines to apply before sealing. */
1168
+ export declare function securityPolicy(dataDir: string, preferred?: BackendName): Promise<{
1169
+ blockOnHigh: boolean;
1170
+ rules: unknown[];
1171
+ } | null>;
394
1172
  export type ArchiveCheck = {
395
1173
  vaultRef: string;
396
1174
  bytes: number;
@@ -429,6 +1207,38 @@ export declare function verifyArchives(dataDir: string, options?: {
429
1207
  limit?: number;
430
1208
  onProgress?: (check: ArchiveCheck) => void;
431
1209
  }, preferred?: BackendName): Promise<ArchiveCheck[]>;
1210
+ export type FramedArchiveUpload = {
1211
+ vaultRef: string;
1212
+ envelope: Envelope;
1213
+ ciphertextPath: string;
1214
+ ciphertextSha256: string;
1215
+ ciphertextBytes: number;
1216
+ };
1217
+ /**
1218
+ * Store one already-sealed archive under an explicit managed-storage ref.
1219
+ *
1220
+ * Unlike `pushArchive`, this deliberately does not update a vault record. It
1221
+ * is the narrow primitive used by the team-history backfill: the original
1222
+ * private record and every one of its copies remain untouched while a second,
1223
+ * membership-wrapped object is uploaded, read back, and hash verified. The
1224
+ * caller may publish a team grant only after `durable` is true.
1225
+ */
1226
+ export declare function pushFramedArchive(dataDir: string, input: FramedArchiveUpload, preferred?: BackendName, options?: {
1227
+ signal?: AbortSignal;
1228
+ incompleteUpload?: IncompleteUpload;
1229
+ onProgress?: (bytes: number) => void;
1230
+ onYield?: () => void | Promise<void>;
1231
+ maxBytesPerSecond?: number;
1232
+ paceNow?: () => number;
1233
+ paceSleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
1234
+ }): Promise<PushResult>;
432
1235
  export declare function pushArchive(dataDir: string, archiveId: string, preferred?: BackendName, options?: {
433
1236
  signal?: AbortSignal;
1237
+ ciphertextPath?: string;
1238
+ onProgress?: (bytes: number) => void;
1239
+ onYield?: () => void | Promise<void>;
1240
+ maxBytesPerSecond?: number;
1241
+ paceNow?: () => number;
1242
+ paceSleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
434
1243
  }): Promise<PushResult>;
1244
+ export {};