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.
- package/ARCHITECTURE.md +201 -0
- package/CHANGELOG.md +218 -0
- package/CONTROL_PLANE.md +86 -0
- package/LICENSE +34 -0
- package/README.md +249 -0
- package/THIRD_PARTY.md +22 -0
- package/THREAT_MODEL.md +107 -0
- package/dist/packages/vaultline-crypto/src/aead.d.ts +12 -0
- package/dist/packages/vaultline-crypto/src/aead.js +24 -0
- package/dist/packages/vaultline-crypto/src/chunk-access.d.ts +39 -0
- package/dist/packages/vaultline-crypto/src/chunk-access.js +93 -0
- package/dist/packages/vaultline-crypto/src/envelope.d.ts +71 -0
- package/dist/packages/vaultline-crypto/src/envelope.js +188 -0
- package/dist/packages/vaultline-crypto/src/format.d.ts +106 -0
- package/dist/packages/vaultline-crypto/src/format.js +43 -0
- package/dist/packages/vaultline-crypto/src/index.d.ts +5 -0
- package/dist/packages/vaultline-crypto/src/index.js +5 -0
- package/dist/packages/vaultline-crypto/src/recipients.d.ts +42 -0
- package/dist/packages/vaultline-crypto/src/recipients.js +129 -0
- package/dist/packages/vaultline-crypto/src/sha256-stream.d.ts +41 -0
- package/dist/packages/vaultline-crypto/src/sha256-stream.js +206 -0
- package/dist/packages/vaultline-crypto/src/stream.d.ts +139 -0
- package/dist/packages/vaultline-crypto/src/stream.js +477 -0
- package/dist/site/index.html +1542 -0
- package/dist/site.zip +0 -0
- package/dist/src/activity.d.ts +22 -0
- package/dist/src/activity.js +52 -0
- package/dist/src/adapters.d.ts +212 -0
- package/dist/src/adapters.js +533 -0
- package/dist/src/audit.d.ts +24 -0
- package/dist/src/audit.js +41 -0
- package/dist/src/autopilot.d.ts +77 -0
- package/dist/src/autopilot.js +148 -0
- package/dist/src/bip39-wordlist.d.ts +15 -0
- package/dist/src/bip39-wordlist.js +272 -0
- package/dist/src/branding.d.ts +31 -0
- package/dist/src/branding.js +31 -0
- package/dist/src/chunk-store.d.ts +142 -0
- package/dist/src/chunk-store.js +502 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +2035 -0
- package/dist/src/cloud.d.ts +434 -0
- package/dist/src/cloud.js +851 -0
- package/dist/src/control-plane/auth.d.ts +62 -0
- package/dist/src/control-plane/auth.js +123 -0
- package/dist/src/control-plane/server.d.ts +31 -0
- package/dist/src/control-plane/server.js +263 -0
- package/dist/src/control-plane/store.d.ts +101 -0
- package/dist/src/control-plane/store.js +82 -0
- package/dist/src/control-plane-cli.d.ts +2 -0
- package/dist/src/control-plane-cli.js +37 -0
- package/dist/src/control-plane-server.d.ts +10 -0
- package/dist/src/control-plane-server.js +11 -0
- package/dist/src/control-plane.d.ts +78 -0
- package/dist/src/control-plane.js +61 -0
- package/dist/src/crypto.d.ts +56 -0
- package/dist/src/crypto.js +132 -0
- package/dist/src/daemon.d.ts +52 -0
- package/dist/src/daemon.js +142 -0
- package/dist/src/dashboard-cli.d.ts +2 -0
- package/dist/src/dashboard-cli.js +20 -0
- package/dist/src/disk.d.ts +110 -0
- package/dist/src/disk.js +169 -0
- package/dist/src/doctor.d.ts +11 -0
- package/dist/src/doctor.js +198 -0
- package/dist/src/enroll.d.ts +27 -0
- package/dist/src/enroll.js +136 -0
- package/dist/src/errors.d.ts +26 -0
- package/dist/src/errors.js +23 -0
- package/dist/src/heartbeat.d.ts +89 -0
- package/dist/src/heartbeat.js +120 -0
- package/dist/src/index-sync.d.ts +53 -0
- package/dist/src/index-sync.js +147 -0
- package/dist/src/leakscan.d.ts +48 -0
- package/dist/src/leakscan.js +222 -0
- package/dist/src/local-api.d.ts +132 -0
- package/dist/src/local-api.js +1757 -0
- package/dist/src/managed-chunks.d.ts +55 -0
- package/dist/src/managed-chunks.js +108 -0
- package/dist/src/mcp-install.d.ts +52 -0
- package/dist/src/mcp-install.js +140 -0
- package/dist/src/mcp.d.ts +1 -0
- package/dist/src/mcp.js +59 -0
- package/dist/src/migrate.d.ts +35 -0
- package/dist/src/migrate.js +88 -0
- package/dist/src/mnemonic.d.ts +60 -0
- package/dist/src/mnemonic.js +134 -0
- package/dist/src/net.d.ts +2 -0
- package/dist/src/net.js +16 -0
- package/dist/src/notify.d.ts +46 -0
- package/dist/src/notify.js +84 -0
- package/dist/src/offload.d.ts +117 -0
- package/dist/src/offload.js +331 -0
- package/dist/src/onboarding.d.ts +10 -0
- package/dist/src/onboarding.js +44 -0
- package/dist/src/packages.d.ts +126 -0
- package/dist/src/packages.js +114 -0
- package/dist/src/passkey.d.ts +26 -0
- package/dist/src/passkey.js +54 -0
- package/dist/src/password-lock.d.ts +19 -0
- package/dist/src/password-lock.js +156 -0
- package/dist/src/paths.d.ts +9 -0
- package/dist/src/paths.js +24 -0
- package/dist/src/providers/gcs.d.ts +133 -0
- package/dist/src/providers/gcs.js +235 -0
- package/dist/src/providers/gdrive.d.ts +156 -0
- package/dist/src/providers/gdrive.js +335 -0
- package/dist/src/providers/index.d.ts +45 -0
- package/dist/src/providers/index.js +74 -0
- package/dist/src/providers/s3.d.ts +174 -0
- package/dist/src/providers/s3.js +345 -0
- package/dist/src/providers/sigv4.d.ts +78 -0
- package/dist/src/providers/sigv4.js +112 -0
- package/dist/src/queue.d.ts +185 -0
- package/dist/src/queue.js +286 -0
- package/dist/src/recovery.d.ts +40 -0
- package/dist/src/recovery.js +132 -0
- package/dist/src/rehydrate.d.ts +43 -0
- package/dist/src/rehydrate.js +66 -0
- package/dist/src/restore.d.ts +34 -0
- package/dist/src/restore.js +80 -0
- package/dist/src/retention.d.ts +251 -0
- package/dist/src/retention.js +446 -0
- package/dist/src/rotate.d.ts +47 -0
- package/dist/src/rotate.js +95 -0
- package/dist/src/search.d.ts +147 -0
- package/dist/src/search.js +677 -0
- package/dist/src/secrets.d.ts +86 -0
- package/dist/src/secrets.js +220 -0
- package/dist/src/service.d.ts +73 -0
- package/dist/src/service.js +197 -0
- package/dist/src/share.d.ts +34 -0
- package/dist/src/share.js +68 -0
- package/dist/src/spool.d.ts +97 -0
- package/dist/src/spool.js +213 -0
- package/dist/src/start-tui.d.ts +17 -0
- package/dist/src/start-tui.js +113 -0
- package/dist/src/start.d.ts +75 -0
- package/dist/src/start.js +101 -0
- package/dist/src/storage-setup.d.ts +49 -0
- package/dist/src/storage-setup.js +222 -0
- package/dist/src/storage-targets.d.ts +40 -0
- package/dist/src/storage-targets.js +147 -0
- package/dist/src/stream-to-cloud.d.ts +76 -0
- package/dist/src/stream-to-cloud.js +820 -0
- package/dist/src/sync-rules.d.ts +85 -0
- package/dist/src/sync-rules.js +125 -0
- package/dist/src/trash.d.ts +15 -0
- package/dist/src/trash.js +63 -0
- package/dist/src/tui.d.ts +18 -0
- package/dist/src/tui.js +179 -0
- package/dist/src/types.d.ts +191 -0
- package/dist/src/types.js +3 -0
- package/dist/src/ui-server.d.ts +187 -0
- package/dist/src/ui-server.js +293 -0
- package/dist/src/ui.d.ts +41 -0
- package/dist/src/ui.js +102 -0
- package/dist/src/update.d.ts +30 -0
- package/dist/src/update.js +56 -0
- package/dist/src/upload.d.ts +46 -0
- package/dist/src/upload.js +80 -0
- package/dist/src/vault.d.ts +208 -0
- package/dist/src/vault.js +812 -0
- package/dist/src/watcher.d.ts +34 -0
- package/dist/src/watcher.js +121 -0
- package/dist/src/worker.d.ts +52 -0
- package/dist/src/worker.js +190 -0
- package/package.json +65 -0
- package/web/app.js +1372 -0
- package/web/index.html +476 -0
- package/web/rail.js +308 -0
- package/web/retention.html +17 -0
- package/web/rules-view.js +249 -0
- package/web/sessions-view.js +448 -0
- package/web/sessions.html +17 -0
- package/web/setup-api.js +181 -0
- package/web/setup-logic.js +394 -0
- package/web/setup.html +419 -0
- package/web/setup.js +697 -0
- 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>;
|