@tinycloud/sdk-services 2.2.0-beta.7 → 2.2.1-beta.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.
@@ -0,0 +1,802 @@
1
+ import { c as IService, e as Result, B as BaseService } from '../BaseService-C_iXlTeN.js';
2
+
3
+ /**
4
+ * Canonical JSON serialization and content hashing for TinyCloud
5
+ * encryption requests/responses.
6
+ *
7
+ * The node and SDK must agree byte-for-byte on the canonical form so
8
+ * that body-hash bindings (`bodyHash`, `encryptedSymmetricKeyHash`,
9
+ * `receiverPublicKeyHash`) verify on both sides.
10
+ *
11
+ * Canonical rules:
12
+ * - Object keys are sorted lexicographically by code point.
13
+ * - Strings are encoded with the JSON.stringify default (RFC 8259).
14
+ * - Numbers are emitted via JSON.stringify; callers should restrict to
15
+ * integers or use string fields for high-precision values.
16
+ * - `undefined` properties are dropped. `null` is preserved.
17
+ * - Arrays preserve element order.
18
+ *
19
+ * Hashing uses SHA-256 supplied by the caller (via an `EncryptionCrypto`
20
+ * binding) so this module stays platform-agnostic.
21
+ */
22
+ type Json = null | boolean | number | string | Json[] | {
23
+ [key: string]: Json | undefined;
24
+ };
25
+ /**
26
+ * Produce the canonical JSON string for {@link value}. Object keys are
27
+ * sorted, `undefined` properties are dropped, and primitive types are
28
+ * encoded by `JSON.stringify`.
29
+ */
30
+ declare function canonicalize(value: Json | undefined): string;
31
+ declare function hexEncode(bytes: Uint8Array): string;
32
+ declare function hexDecode(hex: string): Uint8Array;
33
+ declare function base64Encode(bytes: Uint8Array): string;
34
+ declare function base64Decode(s: string): Uint8Array;
35
+ declare function utf8Encode(s: string): Uint8Array;
36
+ declare function utf8Decode(b: Uint8Array): string;
37
+ /**
38
+ * Compute `hexEncode(sha256(canonicalize(value)))`. The SHA-256 binding
39
+ * is injected so the module remains usable in both the WASM and pure-JS
40
+ * paths.
41
+ */
42
+ declare function canonicalHashHex(sha256: (bytes: Uint8Array) => Uint8Array, value: Json): string;
43
+
44
+ /**
45
+ * Type definitions for TinyCloud one-of-one encryption.
46
+ *
47
+ * Wire shapes mirror the protocol described in the encryption
48
+ * architecture: inline envelopes carry the encrypted symmetric key
49
+ * alongside the ciphertext; decrypt requests are short-lived UCAN
50
+ * invocations against a target node plus networkId.
51
+ */
52
+
53
+ /** Default ciphersuite identifier for v1 envelopes. */
54
+ declare const DEFAULT_ENCRYPTION_ALG: "x25519-aes256gcm/v1";
55
+ /** Inline-envelope schema version. */
56
+ declare const ENVELOPE_VERSION: 1;
57
+ /** Default key version on freshly-created networks. */
58
+ declare const DEFAULT_KEY_VERSION: 1;
59
+ /** Decrypt-invocation fact type. */
60
+ declare const DECRYPT_FACT_TYPE: "tinycloud.encryption.decrypt/v1";
61
+ /** Decrypt response type. */
62
+ declare const DECRYPT_RESULT_TYPE: "tinycloud.encryption.decrypt-result/v1";
63
+ /** Encryption service identifier (manifest long form). */
64
+ declare const ENCRYPTION_SERVICE: "tinycloud.encryption";
65
+ /** Short form used in recap/abilities maps. */
66
+ declare const ENCRYPTION_SERVICE_SHORT: "encryption";
67
+ /** Decrypt ability URN. */
68
+ declare const DECRYPT_ACTION: "tinycloud.encryption/decrypt";
69
+ /**
70
+ * Inline encrypted envelope persisted in KV/SQL records.
71
+ *
72
+ * - `encryptedSymmetricKey` is opaque to the SDK; only the network can
73
+ * unwrap it.
74
+ * - `encryptedSymmetricKeyHash` is the canonical hash of the wrapped key
75
+ * bytes (hex-encoded sha-256 of the base64 string's canonical JSON
76
+ * form). The node recomputes this on every decrypt request.
77
+ * - `ciphertext` and `aad` are payload bytes only; the node never sees
78
+ * them.
79
+ */
80
+ interface InlineEncryptedEnvelope {
81
+ /** Schema version. */
82
+ v: typeof ENVELOPE_VERSION;
83
+ /** Network id URN. */
84
+ networkId: string;
85
+ /** Ciphersuite identifier. */
86
+ alg: string;
87
+ /** Network key version that was used to wrap the symmetric key. */
88
+ keyVersion: number;
89
+ /** Base64-encoded wrapped symmetric key / capsule. */
90
+ encryptedSymmetricKey: string;
91
+ /** Hex sha-256 of the canonical encryptedSymmetricKey string. */
92
+ encryptedSymmetricKeyHash: string;
93
+ /** Base64-encoded payload ciphertext. */
94
+ ciphertext: string;
95
+ /** Base64-encoded associated data, if any. */
96
+ aad?: string;
97
+ /** Caller-supplied metadata. Not authenticated against the node. */
98
+ metadata?: Record<string, string>;
99
+ }
100
+ /**
101
+ * Node-published network descriptor. The node DB is authoritative; a
102
+ * cached copy may also live under
103
+ * `.well-known/encryption/network/<name>` in the principal's account
104
+ * space (a discovery record only).
105
+ */
106
+ interface NetworkDescriptor {
107
+ networkId: string;
108
+ principal: string;
109
+ name: string;
110
+ members: ReadonlyArray<{
111
+ nodeId: string;
112
+ role: "primary" | "share";
113
+ }>;
114
+ threshold: {
115
+ n: number;
116
+ t: number;
117
+ };
118
+ state: "pending" | "generating" | "active" | "rotating" | "revoked" | "failed";
119
+ /** Base64-encoded network public key. */
120
+ publicEncryptionKey: string;
121
+ alg: string;
122
+ keyVersion: number;
123
+ keyBackend: "local-one-of-one" | "dstack" | "threshold";
124
+ createdAt: string;
125
+ updatedAt: string;
126
+ }
127
+ /**
128
+ * Decrypt-request body sent over the wire. Hashed (canonically) and
129
+ * bound to the UCAN invocation via `facts.bodyHash`.
130
+ */
131
+ interface DecryptRequestBody {
132
+ type: typeof DECRYPT_FACT_TYPE;
133
+ targetNode: string;
134
+ networkId: string;
135
+ alg: string;
136
+ keyVersion: number;
137
+ /** Base64-encoded wrapped symmetric key from the envelope. */
138
+ encryptedSymmetricKey: string;
139
+ /** Recomputed hash of the wrapped key. */
140
+ encryptedSymmetricKeyHash: string;
141
+ /** Base64-encoded per-request receiver public key. */
142
+ receiverPublicKey: string;
143
+ /** Hash of the receiver public key. */
144
+ receiverPublicKeyHash: string;
145
+ }
146
+ /**
147
+ * Decrypt-response body returned by the node. The SDK verifies the
148
+ * signature, recomputes hashes, then unwraps `wrappedKey` with the
149
+ * per-request receiver private key before decrypting the payload.
150
+ */
151
+ interface DecryptResponseBody {
152
+ type: typeof DECRYPT_RESULT_TYPE;
153
+ targetNode: string;
154
+ networkId: string;
155
+ invocationCid: string;
156
+ encryptedSymmetricKeyHash: string;
157
+ receiverPublicKeyHash: string;
158
+ /** Base64-encoded symmetric key re-encrypted to receiverPublicKey. */
159
+ wrappedKey: string;
160
+ alg: string;
161
+ keyVersion: number;
162
+ requestHash: string;
163
+ nodeId: string;
164
+ /** Base64-encoded ed25519 signature over canonical(response - signature field). */
165
+ nodeSignature: string;
166
+ }
167
+ /**
168
+ * Decrypt-invocation facts attached to the UCAN. Verifiers recompute
169
+ * `bodyHash`, `encryptedSymmetricKeyHash`, and `receiverPublicKeyHash`
170
+ * from the request body and reject any mismatch.
171
+ */
172
+ interface DecryptInvocationFact {
173
+ type: typeof DECRYPT_FACT_TYPE;
174
+ targetNode: string;
175
+ networkId: string;
176
+ bodyHash: string;
177
+ encryptedSymmetricKeyHash: string;
178
+ receiverPublicKeyHash: string;
179
+ alg: string;
180
+ keyVersion: number;
181
+ }
182
+ /**
183
+ * Per-request receiver key pair (x25519). The private key never
184
+ * leaves the SDK; the public key is sent to the node so the node can
185
+ * rewrap the symmetric key.
186
+ */
187
+ interface ReceiverKeyPair {
188
+ publicKey: Uint8Array;
189
+ privateKey: Uint8Array;
190
+ }
191
+ /**
192
+ * Crypto primitives injected into the encryption module. The SDK
193
+ * provides these via WASM bindings; tests provide simple in-memory
194
+ * implementations.
195
+ */
196
+ interface EncryptionCrypto {
197
+ /** SHA-256 → 32-byte digest. */
198
+ sha256(data: Uint8Array): Uint8Array;
199
+ /** Cryptographically secure random bytes. */
200
+ randomBytes(length: number): Uint8Array;
201
+ /** Derive an x25519 key pair from a 32-byte seed. */
202
+ x25519FromSeed(seed: Uint8Array): ReceiverKeyPair;
203
+ /** Compute the x25519 ECDH shared secret. */
204
+ x25519Dh(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;
205
+ /** Authenticated symmetric encryption (the node's symmetric scheme). */
206
+ authEncrypt(key: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array): Uint8Array;
207
+ /** Authenticated symmetric decryption (matched to authEncrypt). */
208
+ authDecrypt(key: Uint8Array, ciphertext: Uint8Array, aad?: Uint8Array): Uint8Array;
209
+ /**
210
+ * Wrap a symmetric key for the network's public encryption key using
211
+ * a sealed-box / hpke / x25519+symmetric construction. Implementation
212
+ * is opaque; only the node can unwrap.
213
+ */
214
+ sealToNetworkKey(networkPublicKey: Uint8Array, symmetricKey: Uint8Array): Uint8Array;
215
+ /**
216
+ * Open a wrapped symmetric key that was re-encrypted to the
217
+ * per-request receiver public key. The matching private key must be
218
+ * supplied here; the SDK never sends it to the node.
219
+ */
220
+ openWithReceiverKey(receiverPrivateKey: Uint8Array, wrappedKey: Uint8Array): Uint8Array;
221
+ /**
222
+ * Verify an ed25519 signature over `message` produced by the node
223
+ * identified by `nodeId` (the public-key DID).
224
+ */
225
+ verifyNodeSignature(nodeId: string, message: Uint8Array, signature: Uint8Array): boolean;
226
+ }
227
+ /**
228
+ * Signer interface used to derive a receiver key pair from a wallet or
229
+ * session signer. The signature is HKDF-extracted into the receiver
230
+ * seed so the public key is reproducible given the same context.
231
+ */
232
+ interface ReceiverKeySigner {
233
+ signMessage(message: string): Promise<string>;
234
+ }
235
+ /** Capability proof material accompanying a decrypt invocation. */
236
+ interface DecryptCapabilityProof {
237
+ /** Delegation chain CIDs rooted at the network principal. */
238
+ proofs: ReadonlyArray<string>;
239
+ /** Optional Authorization header value to use instead of building one. */
240
+ authorization?: string;
241
+ }
242
+ /**
243
+ * Inputs to the decrypt invocation builder.
244
+ */
245
+ interface BuildDecryptInvocationInput {
246
+ /** Target node DID — also the UCAN audience. */
247
+ targetNode: string;
248
+ /** Network id URN — also the recap resource. */
249
+ networkId: string;
250
+ /** Canonical body that will be POSTed. */
251
+ body: DecryptRequestBody;
252
+ /** Facts include hashes bound to the canonical body. */
253
+ facts: DecryptInvocationFact;
254
+ /** Capability proof chain. */
255
+ proof: DecryptCapabilityProof;
256
+ /** Optional `nbf` UCAN field as an ISO date string. */
257
+ notBefore?: string;
258
+ /** Optional `exp` UCAN field as an ISO date string. */
259
+ expiration?: string;
260
+ }
261
+ /**
262
+ * The output of {@link buildDecryptInvocation}.
263
+ */
264
+ interface BuiltDecryptInvocation {
265
+ /** HTTP `Authorization` header value. */
266
+ authorization: string;
267
+ /** CID of the invocation (used by the node response binding). */
268
+ invocationCid: string;
269
+ /** Canonical body string the node will hash. */
270
+ canonicalBody: string;
271
+ }
272
+ /**
273
+ * Signer interface for producing the decrypt invocation. WASM bindings
274
+ * implement this with the same session signer used for KV/SQL
275
+ * invocations; tests can stub it.
276
+ */
277
+ interface DecryptInvocationSigner {
278
+ signDecryptInvocation(input: BuildDecryptInvocationInput): Promise<BuiltDecryptInvocation>;
279
+ }
280
+ /**
281
+ * Errors thrown / returned from the encryption module.
282
+ */
283
+ type EncryptionErrorInput = {
284
+ code: "NETWORK_NOT_FOUND";
285
+ networkId?: string;
286
+ name?: string;
287
+ message?: string;
288
+ } | {
289
+ code: "NETWORK_NOT_ACTIVE";
290
+ state: string;
291
+ message?: string;
292
+ } | {
293
+ code: "INVALID_NETWORK_ID";
294
+ message: string;
295
+ } | {
296
+ code: "INVALID_ENVELOPE";
297
+ message: string;
298
+ } | {
299
+ code: "DECRYPT_DENIED";
300
+ message: string;
301
+ } | {
302
+ code: "INVALID_RESPONSE";
303
+ message: string;
304
+ } | {
305
+ code: "RESPONSE_SIGNATURE_INVALID";
306
+ message?: string;
307
+ } | {
308
+ code: "RESPONSE_BINDING_MISMATCH";
309
+ field: string;
310
+ message?: string;
311
+ } | {
312
+ code: "TRANSPORT_ERROR";
313
+ cause: Error;
314
+ message?: string;
315
+ } | {
316
+ code: "INVALID_INPUT";
317
+ message: string;
318
+ };
319
+ type EncryptionError = EncryptionErrorInput & {
320
+ service: "encryption";
321
+ message: string;
322
+ };
323
+ declare function encryptionError(input: EncryptionErrorInput): EncryptionError;
324
+ /** Re-export for ergonomic typing of canonical payloads. */
325
+ type CanonicalJson = Json;
326
+
327
+ /**
328
+ * Network-descriptor discovery.
329
+ *
330
+ * Resolution order (per architecture):
331
+ *
332
+ * 1. The node's authoritative endpoint
333
+ * `GET /encryption/networks/<networkId>` returns the current
334
+ * descriptor (`state`, `publicEncryptionKey`, `keyVersion`, ...).
335
+ * 2. If the node is unreachable, fall back to the cached discovery
336
+ * record at `.well-known/encryption/network/<name>` inside the
337
+ * principal's public space.
338
+ *
339
+ * The node DB is authoritative on conflict; cached records are
340
+ * advisory only.
341
+ */
342
+
343
+ type DiscoverySource = "node" | "well-known";
344
+ interface DiscoveredNetwork {
345
+ descriptor: NetworkDescriptor;
346
+ source: DiscoverySource;
347
+ }
348
+ interface NodeDescriptorFetcher {
349
+ /** Fetch the descriptor by full networkId URN. */
350
+ fetchByNetworkId(networkId: string): Promise<NetworkDescriptor | null>;
351
+ }
352
+ interface WellKnownDescriptorFetcher {
353
+ /**
354
+ * Read the cached well-known descriptor by principal + network name.
355
+ * Returns null if no record exists or the record is unreadable.
356
+ */
357
+ fetchWellKnown(principal: string, discoveryKey: string): Promise<NetworkDescriptor | null>;
358
+ }
359
+ interface DiscoverNetworkInput {
360
+ /** Either a networkId URN or a bare network name (paired with `principal`). */
361
+ identifier: string;
362
+ /** Required when identifier is a bare name. */
363
+ principal?: string;
364
+ node?: NodeDescriptorFetcher;
365
+ wellKnown?: WellKnownDescriptorFetcher;
366
+ }
367
+ /**
368
+ * Resolve a network descriptor. The node fetcher is preferred; the
369
+ * well-known fallback is used only on transport failure.
370
+ *
371
+ * The returned descriptor is sanity-checked: `networkId`, `principal`,
372
+ * and `name` must agree with the URN, and the public key field must
373
+ * be non-empty.
374
+ */
375
+ declare function discoverNetwork(input: DiscoverNetworkInput): Promise<{
376
+ ok: true;
377
+ data: DiscoveredNetwork;
378
+ } | {
379
+ ok: false;
380
+ error: EncryptionError;
381
+ }>;
382
+ /**
383
+ * Reject a descriptor that is not in a state that accepts decrypt
384
+ * requests. Only `active` and `rotating` networks may decrypt; revoked
385
+ * or pending networks reject.
386
+ */
387
+ declare function ensureNetworkUsableForDecrypt(descriptor: NetworkDescriptor): {
388
+ ok: true;
389
+ data: NetworkDescriptor;
390
+ } | {
391
+ ok: false;
392
+ error: EncryptionError;
393
+ };
394
+
395
+ /**
396
+ * IEncryptionService - Interface for the TinyCloud encryption service.
397
+ *
398
+ * Provides:
399
+ * - Network discovery
400
+ * - Local encrypt-to-network (no node round-trip)
401
+ * - Node-mediated decrypt (one-of-one or threshold; v1 is one-of-one)
402
+ *
403
+ * The service does NOT own KV/SQL access. Callers fetch encrypted
404
+ * envelopes from their data service of choice and pass them in.
405
+ */
406
+
407
+ interface EncryptToNetworkOptions {
408
+ /** Optional associated authenticated data (bytes). */
409
+ aad?: Uint8Array;
410
+ /** Ciphersuite override. Defaults to the network's alg. */
411
+ alg?: string;
412
+ /** Key version override. Defaults to the network's current key version. */
413
+ keyVersion?: number;
414
+ /** Caller-supplied envelope metadata. */
415
+ metadata?: Record<string, string>;
416
+ }
417
+ interface DecryptEnvelopeOptions {
418
+ /**
419
+ * Pre-discovered descriptor for the envelope's network. When omitted
420
+ * the service performs discovery internally.
421
+ */
422
+ descriptor?: NetworkDescriptor;
423
+ /** Optional associated data; must match what was used at encryption time. */
424
+ aad?: Uint8Array;
425
+ /** Override the target node id when the descriptor advertises many. */
426
+ targetNode?: string;
427
+ }
428
+ interface IEncryptionService extends IService {
429
+ /**
430
+ * Look up a network's descriptor.
431
+ *
432
+ * `identifier` may be either a full networkId URN
433
+ * (`urn:tinycloud:encryption:did:key:...:default`) or a bare network
434
+ * name. The bare name form requires the principal to be supplied via
435
+ * `principal`.
436
+ */
437
+ discoverNetwork(identifier: string, principal?: string): Promise<Result<NetworkDescriptor, EncryptionError>>;
438
+ /**
439
+ * Encrypt plaintext to the network's public key locally. The result
440
+ * is an inline envelope ready to persist (KV/SQL).
441
+ */
442
+ encryptToNetwork(networkId: string, plaintext: Uint8Array, options?: EncryptToNetworkOptions): Promise<Result<InlineEncryptedEnvelope, EncryptionError>>;
443
+ /**
444
+ * Decrypt an inline envelope via the node.
445
+ *
446
+ * Steps: generate per-request receiver key, build & sign the
447
+ * decrypt invocation, POST it to the node, verify the signed
448
+ * response, open `wrappedKey`, and decrypt the payload locally.
449
+ *
450
+ * The capability proof must root at the network principal embedded
451
+ * in the envelope's networkId.
452
+ */
453
+ decryptEnvelope(envelope: InlineEncryptedEnvelope, capabilityProof: DecryptCapabilityProof, options?: DecryptEnvelopeOptions): Promise<Result<Uint8Array, EncryptionError>>;
454
+ }
455
+
456
+ /**
457
+ * EncryptionService — TinyCloud one-of-one encryption service.
458
+ *
459
+ * Responsibilities:
460
+ * - Network discovery (`discoverNetwork`).
461
+ * - Local envelope encryption (`encryptToNetwork`).
462
+ * - Node-mediated decrypt with full request/response binding
463
+ * verification (`decryptEnvelope`).
464
+ *
465
+ * Non-responsibilities:
466
+ * - KV/SQL access. Callers fetch encrypted envelopes from their data
467
+ * service of choice and pass them in.
468
+ * - Network onboarding/ceremony. Those routes are node-only.
469
+ */
470
+
471
+ /**
472
+ * Transport for posting decrypt requests to a TinyCloud node.
473
+ *
474
+ * Implementations supply the `Authorization` header (built by the
475
+ * decrypt-invocation signer) and POST the canonical body. The
476
+ * response body is JSON-decoded into a {@link DecryptResponseBody}.
477
+ */
478
+ interface DecryptTransport {
479
+ postDecrypt(input: {
480
+ targetNode: string;
481
+ networkId: string;
482
+ authorization: string;
483
+ canonicalBody: string;
484
+ }): Promise<DecryptResponseBody>;
485
+ }
486
+ interface EncryptionServiceConfig {
487
+ crypto: EncryptionCrypto;
488
+ signer: DecryptInvocationSigner;
489
+ transport: DecryptTransport;
490
+ node?: NodeDescriptorFetcher;
491
+ wellKnown?: WellKnownDescriptorFetcher;
492
+ [key: string]: unknown;
493
+ }
494
+ declare class EncryptionService extends BaseService implements IEncryptionService {
495
+ static readonly serviceName = "encryption";
496
+ protected _config: EncryptionServiceConfig;
497
+ constructor(config: EncryptionServiceConfig);
498
+ get config(): EncryptionServiceConfig;
499
+ private get crypto();
500
+ discoverNetwork(identifier: string, principal?: string): Promise<Result<NetworkDescriptor, EncryptionError>>;
501
+ encryptToNetwork(networkId: string, plaintext: Uint8Array, options?: EncryptToNetworkOptions): Promise<Result<InlineEncryptedEnvelope, EncryptionError>>;
502
+ decryptEnvelope(envelope: InlineEncryptedEnvelope, capabilityProof: DecryptCapabilityProof, options?: DecryptEnvelopeOptions): Promise<Result<Uint8Array, EncryptionError>>;
503
+ }
504
+
505
+ /**
506
+ * TinyCloud encryption network identifiers.
507
+ *
508
+ * A network id is `urn:tinycloud:encryption:<principal>:<network>` where
509
+ * `principal` is a DID (typically `did:key:...`) and `network` is a
510
+ * non-empty label drawn from `[a-z0-9][a-z0-9-]*`.
511
+ *
512
+ * The embedded principal is the root authority for the network: any
513
+ * delegation chain ending in a `tinycloud.encryption/decrypt` grant on
514
+ * the network must root at this principal.
515
+ */
516
+ interface ParsedNetworkId {
517
+ /** The full URN string. */
518
+ networkId: string;
519
+ /** Principal DID embedded in the URN (the network's root authority). */
520
+ principal: string;
521
+ /** Network label (the suffix after the principal). */
522
+ name: string;
523
+ }
524
+ declare class NetworkIdError extends Error {
525
+ constructor(message: string);
526
+ }
527
+ /**
528
+ * Parse a network id string into its principal and name components.
529
+ *
530
+ * Throws {@link NetworkIdError} when the input does not match
531
+ * `urn:tinycloud:encryption:<did>:<name>`, when the embedded DID is
532
+ * malformed, or when the network name fails {@link NETWORK_NAME_RE}.
533
+ */
534
+ declare function parseNetworkId(networkId: string): ParsedNetworkId;
535
+ /**
536
+ * Construct a network id URN from a principal DID and a network name.
537
+ * Validates inputs and throws {@link NetworkIdError} on bad shape.
538
+ */
539
+ declare function buildNetworkId(principal: string, name: string): string;
540
+ /**
541
+ * Returns true when {@link networkId} is a syntactically valid network URN.
542
+ */
543
+ declare function isNetworkId(networkId: unknown): networkId is string;
544
+ /**
545
+ * Resolve the discovery key used to look up a network's descriptor under
546
+ * a principal's public account-space.
547
+ *
548
+ * Format: `.well-known/encryption/network/<name>`.
549
+ */
550
+ declare function networkDiscoveryKey(name: string): string;
551
+ declare const ENCRYPTION_NETWORK_URN_PREFIX = "urn:tinycloud:encryption:";
552
+ declare const NETWORK_NAME_PATTERN: RegExp;
553
+
554
+ /**
555
+ * Inline-envelope encrypt / decrypt helpers.
556
+ *
557
+ * Encryption is fully local: the SDK generates a per-record symmetric
558
+ * key, encrypts the payload, then wraps the symmetric key against the
559
+ * network's public encryption key. The wrapped key (and key hash)
560
+ * travels alongside the ciphertext inside an
561
+ * {@link InlineEncryptedEnvelope}.
562
+ *
563
+ * Decryption is split: the node unwraps the symmetric key to the
564
+ * per-request receiver public key (see {@link buildDecryptInvocation}
565
+ * and the decrypt route); this module is responsible for the local
566
+ * payload decryption once the symmetric key is available.
567
+ */
568
+
569
+ interface EncryptToNetworkInput {
570
+ /** Target network id URN. */
571
+ networkId: string;
572
+ /** Network public key bytes (already discovered). */
573
+ networkPublicKey: Uint8Array;
574
+ /** Payload bytes to encrypt. Callers serialize objects to bytes themselves. */
575
+ plaintext: Uint8Array;
576
+ /** Optional associated authenticated data. */
577
+ aad?: Uint8Array;
578
+ /** Ciphersuite identifier. Defaults to {@link DEFAULT_ENCRYPTION_ALG}. */
579
+ alg?: string;
580
+ /** Key version. Defaults to {@link DEFAULT_KEY_VERSION}. */
581
+ keyVersion?: number;
582
+ /** Caller-supplied envelope metadata. */
583
+ metadata?: Record<string, string>;
584
+ }
585
+ interface EncryptToNetworkResult {
586
+ envelope: InlineEncryptedEnvelope;
587
+ /** Symmetric key returned for caller bookkeeping; do NOT persist. */
588
+ symmetricKey: Uint8Array;
589
+ }
590
+ /**
591
+ * Local-only encrypt: generates a symmetric key, encrypts the payload,
592
+ * wraps the key against the network public key, and returns the
593
+ * inline envelope.
594
+ */
595
+ declare function encryptToNetwork(crypto: EncryptionCrypto, input: EncryptToNetworkInput): EncryptToNetworkResult;
596
+ /**
597
+ * Validate an inline envelope shape. Returns an error if the envelope
598
+ * is missing required fields or fails internal hash recomputation.
599
+ */
600
+ declare function validateEnvelope(crypto: EncryptionCrypto, envelope: unknown): {
601
+ ok: true;
602
+ data: InlineEncryptedEnvelope;
603
+ } | {
604
+ ok: false;
605
+ error: EncryptionError;
606
+ };
607
+ /**
608
+ * Decrypt an inline envelope given the unwrapped symmetric key.
609
+ *
610
+ * Callers typically obtain the symmetric key via the node decrypt
611
+ * endpoint plus the per-request receiver key pair (see
612
+ * `EncryptionService.decryptEnvelope`).
613
+ */
614
+ declare function decryptEnvelopeWithKey(crypto: EncryptionCrypto, envelope: InlineEncryptedEnvelope, symmetricKey: Uint8Array): Uint8Array;
615
+
616
+ /**
617
+ * Per-request receiver key generation.
618
+ *
619
+ * The receiver key pair is generated for a single decrypt request: the
620
+ * SDK sends `receiverPublicKey` to the node so the node can rewrap the
621
+ * symmetric key; the matching private key never leaves the SDK.
622
+ *
623
+ * Two derivation modes are supported:
624
+ *
625
+ * 1. **Random** — `crypto.randomBytes(32)` seeds a fresh x25519 pair.
626
+ * This is the default for one-of-one flows.
627
+ *
628
+ * 2. **Signed-context** — the caller provides a signer that signs a
629
+ * deterministic context message (network id + nonce + invocation
630
+ * intent). The signature bytes are SHA-256'd into the seed. This is
631
+ * useful when callers want the receiver key to be reproducible by
632
+ * the same session signer (e.g. for delegated reads where the
633
+ * signer is the only stable secret).
634
+ */
635
+
636
+ interface RandomReceiverKeyInput {
637
+ crypto: EncryptionCrypto;
638
+ }
639
+ declare function generateRandomReceiverKey(input: RandomReceiverKeyInput): ReceiverKeyPair;
640
+ interface SignedReceiverKeyInput {
641
+ crypto: EncryptionCrypto;
642
+ signer: ReceiverKeySigner;
643
+ networkId: string;
644
+ /** Optional extra context (e.g. invocation nonce) folded into the message. */
645
+ context?: string;
646
+ }
647
+ /**
648
+ * Deterministic receiver-key derivation: signs a context string with
649
+ * the supplied signer, then SHA-256s the signature bytes into the
650
+ * x25519 seed.
651
+ *
652
+ * The context message is:
653
+ * `tinycloud.encryption.receiver-key/v1:<networkId>:<context>`
654
+ *
655
+ * Callers MUST include unique context (e.g. a fresh nonce) on every
656
+ * request unless they explicitly want reproducibility.
657
+ */
658
+ declare function deriveSignedReceiverKey(input: SignedReceiverKeyInput): Promise<ReceiverKeyPair>;
659
+
660
+ /**
661
+ * TinyCloud encryption decrypt-invocation builder.
662
+ *
663
+ * The decrypt invocation is a UCAN-style TinyCloud invocation against
664
+ * a node plus a network id. It is structurally distinct from the
665
+ * existing space-shaped invocations:
666
+ *
667
+ * - `aud` (audience) is the **target node DID** (not a space owner).
668
+ * - `att` (attenuation) uses the **networkId URN** as the resource
669
+ * key (not a `tinycloud:pkh:...:<space>` URI).
670
+ * - `fct` (facts) binds `bodyHash`, `encryptedSymmetricKeyHash`, and
671
+ * `receiverPublicKeyHash` so the node can recompute them from the
672
+ * POST body.
673
+ *
674
+ * Production callers inject a `DecryptInvocationSigner` backed by the
675
+ * WASM UCAN/session signer. Tests pass a deterministic stub.
676
+ */
677
+
678
+ interface CanonicalDecryptRequest {
679
+ /** The canonical body that the node will hash. */
680
+ canonicalBody: string;
681
+ /** Hex sha-256 of the canonical body bytes. */
682
+ bodyHash: string;
683
+ /** Hex sha-256 of the receiver public key bytes. */
684
+ receiverPublicKeyHash: string;
685
+ }
686
+ interface BuildCanonicalDecryptRequestInput {
687
+ crypto: EncryptionCrypto;
688
+ body: DecryptRequestBody;
689
+ receiverPublicKey: Uint8Array;
690
+ }
691
+ /**
692
+ * Build the canonical body string and its bound hashes for a decrypt
693
+ * request. The output is what gets POSTed to the node, and what the
694
+ * node will hash to verify `facts.bodyHash`.
695
+ */
696
+ declare function buildCanonicalDecryptRequest(input: BuildCanonicalDecryptRequestInput): CanonicalDecryptRequest;
697
+ interface BuildDecryptFactsInput {
698
+ crypto: EncryptionCrypto;
699
+ body: DecryptRequestBody;
700
+ /** Encrypted symmetric key hash from the envelope (already canonical). */
701
+ encryptedSymmetricKeyHash: string;
702
+ /** Receiver public key bytes. */
703
+ receiverPublicKey: Uint8Array;
704
+ /** Canonical body string used to derive bodyHash. */
705
+ canonicalBody?: string;
706
+ }
707
+ /**
708
+ * Build the {@link DecryptInvocationFact} that will be embedded in the
709
+ * UCAN `fct` field. Hashes are recomputed here so callers cannot drift
710
+ * from the canonical body without the node noticing.
711
+ */
712
+ declare function buildDecryptFacts(input: BuildDecryptFactsInput): DecryptInvocationFact;
713
+ /**
714
+ * Recap-shaped attenuation for the decrypt invocation. The resource
715
+ * key is the networkId URN; the ability is the long-form
716
+ * `tinycloud.encryption/decrypt`. This is intentionally distinct from
717
+ * the existing space-shaped invocation map so callers cannot
718
+ * accidentally fake a space prefix.
719
+ */
720
+ declare function buildDecryptAttenuation(networkId: string): Record<string, Record<string, Record<string, never>>>;
721
+ /**
722
+ * Validate a {@link BuildDecryptInvocationInput} payload — the body
723
+ * shape, the facts bindings, and the audience contract — without
724
+ * actually signing. Returns either the input (typed) or a structured
725
+ * error so callers can short-circuit before calling into WASM.
726
+ */
727
+ declare function checkDecryptInvocationInput(crypto: EncryptionCrypto, input: BuildDecryptInvocationInput): {
728
+ ok: true;
729
+ data: BuildDecryptInvocationInput;
730
+ canonicalBody: string;
731
+ } | {
732
+ ok: false;
733
+ error: EncryptionError;
734
+ };
735
+ /**
736
+ * Compose the high-level decrypt invocation and hand it to the signer
737
+ * for UCAN minting. The signer returns the Authorization header value
738
+ * plus the invocation CID; this function only validates and
739
+ * orchestrates — it does not perform crypto signing itself.
740
+ */
741
+ declare function buildDecryptInvocation(crypto: EncryptionCrypto, signer: DecryptInvocationSigner, input: BuildDecryptInvocationInput): Promise<{
742
+ ok: true;
743
+ data: BuiltDecryptInvocation;
744
+ } | {
745
+ ok: false;
746
+ error: EncryptionError;
747
+ }>;
748
+
749
+ /**
750
+ * Decrypt-response verification.
751
+ *
752
+ * The node returns the unwrapped symmetric key re-encrypted to the
753
+ * per-request receiver public key. Before the SDK uses the wrapped
754
+ * key, it must:
755
+ *
756
+ * 1. Verify the node signature over the canonical response (excluding
757
+ * the signature field).
758
+ * 2. Recompute every binding hash and reject any mismatch.
759
+ * 3. Confirm `targetNode`, `networkId`, `alg`, and `keyVersion` echo
760
+ * what the SDK sent.
761
+ *
762
+ * After verification, the SDK opens `wrappedKey` with the per-request
763
+ * receiver private key and uses the resulting symmetric key to
764
+ * decrypt the inline envelope payload.
765
+ */
766
+
767
+ /**
768
+ * Construct the canonical bytes that the node signed.
769
+ *
770
+ * The signature covers the response with `nodeSignature` removed. We
771
+ * canonicalize and hash the same way the node does so the binding is
772
+ * stable.
773
+ */
774
+ declare function canonicalSignedResponse(response: DecryptResponseBody): string;
775
+ interface VerifyDecryptResponseInput {
776
+ crypto: EncryptionCrypto;
777
+ request: DecryptRequestBody;
778
+ facts: DecryptInvocationFact;
779
+ /** CID of the signed invocation that produced this response. */
780
+ invocationCid: string;
781
+ /** Hex bodyHash that was bound in `facts.bodyHash`. */
782
+ requestBodyHash: string;
783
+ response: DecryptResponseBody;
784
+ }
785
+ /**
786
+ * Verify the node's decrypt response. Returns the response on success;
787
+ * a structured error on signature, binding, or shape failure.
788
+ */
789
+ declare function verifyDecryptResponse(input: VerifyDecryptResponseInput): {
790
+ ok: true;
791
+ data: DecryptResponseBody;
792
+ } | {
793
+ ok: false;
794
+ error: EncryptionError;
795
+ };
796
+ /**
797
+ * After verification, open the response's `wrappedKey` with the
798
+ * per-request receiver private key and return the symmetric key.
799
+ */
800
+ declare function openWrappedKey(crypto: EncryptionCrypto, receiverPrivateKey: Uint8Array, response: DecryptResponseBody): Uint8Array;
801
+
802
+ export { type BuildCanonicalDecryptRequestInput, type BuildDecryptFactsInput, type BuildDecryptInvocationInput, type BuiltDecryptInvocation, type CanonicalDecryptRequest, type CanonicalJson, DECRYPT_ACTION, DECRYPT_FACT_TYPE, DECRYPT_RESULT_TYPE, DEFAULT_ENCRYPTION_ALG, DEFAULT_KEY_VERSION, type DecryptCapabilityProof, type DecryptEnvelopeOptions, type DecryptInvocationFact, type DecryptInvocationSigner, type DecryptRequestBody, type DecryptResponseBody, type DecryptTransport, type DiscoverNetworkInput, type DiscoveredNetwork, type DiscoverySource, ENCRYPTION_NETWORK_URN_PREFIX, ENCRYPTION_SERVICE, ENCRYPTION_SERVICE_SHORT, ENVELOPE_VERSION, type EncryptToNetworkInput, type EncryptToNetworkOptions, type EncryptToNetworkResult, type EncryptionCrypto, type EncryptionError, type EncryptionErrorInput, EncryptionService, type EncryptionServiceConfig, type IEncryptionService, type InlineEncryptedEnvelope, type Json, NETWORK_NAME_PATTERN, type NetworkDescriptor, NetworkIdError, type NodeDescriptorFetcher, type ParsedNetworkId, type RandomReceiverKeyInput, type ReceiverKeyPair, type ReceiverKeySigner, type SignedReceiverKeyInput, type VerifyDecryptResponseInput, type WellKnownDescriptorFetcher, base64Decode, base64Encode, buildCanonicalDecryptRequest, buildDecryptAttenuation, buildDecryptFacts, buildDecryptInvocation, buildNetworkId, canonicalHashHex, canonicalSignedResponse, canonicalize, checkDecryptInvocationInput, decryptEnvelopeWithKey, deriveSignedReceiverKey, discoverNetwork, encryptToNetwork, encryptionError, ensureNetworkUsableForDecrypt, generateRandomReceiverKey, hexDecode, hexEncode, isNetworkId, networkDiscoveryKey, openWrappedKey, parseNetworkId, utf8Decode, utf8Encode, validateEnvelope, verifyDecryptResponse };