@youam/sdk 0.1.0 → 0.2.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,892 @@
1
+ /**
2
+ * SDK configuration (matching Python config.py).
3
+ *
4
+ * Priority (highest wins): constructor arg > env var > default.
5
+ * TOML config file support is Python-only for now (unnecessary complexity for TS MVP).
6
+ */
7
+ declare class SDKConfig {
8
+ readonly name: string;
9
+ readonly relayUrl: string;
10
+ readonly relayWsUrl: string;
11
+ readonly keyDir: string;
12
+ readonly dataDir: string;
13
+ readonly displayName: string;
14
+ readonly transportType: string;
15
+ readonly trustPolicy: string;
16
+ readonly relayDomain: string;
17
+ constructor(options: {
18
+ name: string;
19
+ relayUrl?: string | null;
20
+ relayWsUrl?: string | null;
21
+ keyDir?: string | null;
22
+ dataDir?: string | null;
23
+ displayName?: string | null;
24
+ transportType?: string;
25
+ trustPolicy?: string;
26
+ relayDomain?: string;
27
+ });
28
+ }
29
+
30
+ /**
31
+ * Core types, constants, and utility functions for UAM protocol.
32
+ */
33
+ /** Protocol version. */
34
+ declare const UAM_VERSION = "0.1";
35
+ /** Maximum envelope size in bytes (64 KB). */
36
+ declare const MAX_ENVELOPE_SIZE = 65536;
37
+ /** All UAM message types. */
38
+ declare enum MessageType {
39
+ MESSAGE = "message",
40
+ HANDSHAKE_REQUEST = "handshake.request",
41
+ HANDSHAKE_ACCEPT = "handshake.accept",
42
+ HANDSHAKE_DENY = "handshake.deny",
43
+ RECEIPT_DELIVERED = "receipt.delivered",
44
+ RECEIPT_READ = "receipt.read",
45
+ RECEIPT_FAILED = "receipt.failed",
46
+ SESSION_REQUEST = "session.request",
47
+ SESSION_ACCEPT = "session.accept",
48
+ SESSION_DECLINE = "session.decline",
49
+ SESSION_END = "session.end"
50
+ }
51
+ /**
52
+ * URL-safe base64 encode, stripping padding.
53
+ */
54
+ declare function b64Encode(data: Uint8Array): string;
55
+ /**
56
+ * URL-safe base64 decode, tolerating missing padding.
57
+ */
58
+ declare function b64Decode(s: string): Uint8Array;
59
+ /**
60
+ * Return a canonical UTC timestamp: YYYY-MM-DDTHH:MM:SS.mmmZ
61
+ */
62
+ declare function utcTimestamp(): string;
63
+
64
+ /**
65
+ * UAM exception hierarchy.
66
+ *
67
+ * All protocol-specific errors inherit from UAMError.
68
+ */
69
+ /** Base error for all UAM protocol errors. */
70
+ declare class UAMError extends Error {
71
+ constructor(message?: string);
72
+ }
73
+ /** Raised when an address string fails validation. */
74
+ declare class InvalidAddressError extends UAMError {
75
+ constructor(message?: string);
76
+ }
77
+ /** Raised when an envelope fails schema validation. */
78
+ declare class InvalidEnvelopeError extends UAMError {
79
+ constructor(message?: string);
80
+ }
81
+ /** Raised when an envelope exceeds the maximum allowed size. */
82
+ declare class EnvelopeTooLargeError extends InvalidEnvelopeError {
83
+ constructor(message?: string);
84
+ }
85
+ /** Raised on signing failures. */
86
+ declare class SignatureError extends UAMError {
87
+ constructor(message?: string);
88
+ }
89
+ /** Raised when a cryptographic signature cannot be verified. */
90
+ declare class SignatureVerificationError extends SignatureError {
91
+ constructor(message?: string);
92
+ }
93
+ /** Raised on encryption failures. */
94
+ declare class EncryptionError extends UAMError {
95
+ constructor(message?: string);
96
+ }
97
+ /** Raised on decryption failures. */
98
+ declare class DecryptionError extends EncryptionError {
99
+ constructor(message?: string);
100
+ }
101
+ /** Raised when a contact card fails validation. */
102
+ declare class InvalidContactCardError extends UAMError {
103
+ constructor(message?: string);
104
+ }
105
+ /** Raised when a pinned contact's public key doesn't match the resolved key. */
106
+ declare class KeyPinningError extends UAMError {
107
+ constructor(message?: string);
108
+ }
109
+
110
+ /**
111
+ * UAM address parsing and validation.
112
+ *
113
+ * A UAM address has the form `agent::domain` (e.g. `alice::youam.network`).
114
+ */
115
+ /**
116
+ * A parsed UAM address (always lowercase).
117
+ */
118
+ interface Address {
119
+ readonly agent: string;
120
+ readonly domain: string;
121
+ readonly full: string;
122
+ }
123
+ /**
124
+ * Parse and validate a UAM address string.
125
+ *
126
+ * Strips whitespace, lowercases, and validates format.
127
+ * Enforces max lengths: agent <= 64 chars, full address <= 128 chars.
128
+ *
129
+ * @throws {InvalidAddressError} If `raw` is not a valid `agent::domain` address.
130
+ */
131
+ declare function parseAddress(raw: string): Address;
132
+
133
+ /**
134
+ * Cryptographic primitives for UAM protocol.
135
+ *
136
+ * Wraps libsodium-wrappers for Ed25519 signing, Curve25519 key exchange,
137
+ * NaCl Box (authenticated encryption), and NaCl SealedBox (anonymous encryption).
138
+ *
139
+ * This module never hand-rolls crypto -- every operation delegates to libsodium.
140
+ */
141
+ /**
142
+ * Promise that resolves when libsodium is ready.
143
+ * Callers must await this before first use of any crypto function.
144
+ */
145
+ declare const sodiumReady: Promise<void>;
146
+ /** 64-byte Ed25519 secret key (seed + public). */
147
+ type SigningKey = Uint8Array;
148
+ /** 32-byte Ed25519 public key. */
149
+ type VerifyKey = Uint8Array;
150
+ /** 32-byte seed. */
151
+ type Seed = Uint8Array;
152
+ interface Keypair {
153
+ signingKey: Uint8Array;
154
+ verifyKey: Uint8Array;
155
+ seed: Uint8Array;
156
+ }
157
+ /**
158
+ * Generate an Ed25519 keypair.
159
+ */
160
+ declare function generateKeypair(): Keypair;
161
+ /**
162
+ * Serialize a signing key to URL-safe base64 (32-byte seed).
163
+ *
164
+ * Matches Python: PyNaCl's SigningKey.encode() returns the 32-byte seed.
165
+ */
166
+ declare function serializeSigningKey(seed: Uint8Array): string;
167
+ /**
168
+ * Restore a signing key from its base64-encoded seed.
169
+ */
170
+ declare function deserializeSigningKey(s: string): Keypair;
171
+ /**
172
+ * Serialize a verify (public) key to URL-safe base64.
173
+ */
174
+ declare function serializeVerifyKey(key: Uint8Array): string;
175
+ /**
176
+ * Restore a verify key from its base64 encoding.
177
+ */
178
+ declare function deserializeVerifyKey(s: string): Uint8Array;
179
+ /**
180
+ * Return the SHA-256 hex digest of the verify key bytes.
181
+ *
182
+ * This 64-character string serves as the agent's identity fingerprint.
183
+ */
184
+ declare function publicKeyFingerprint(verifyKey: Uint8Array): string;
185
+ /**
186
+ * Produce deterministic JSON bytes for signing.
187
+ *
188
+ * - Excludes the "signature" key.
189
+ * - Excludes keys whose value is null or undefined.
190
+ * - Sorts keys, uses compact separators, ensures ASCII encoding.
191
+ */
192
+ declare function canonicalize(data: Record<string, unknown>): Uint8Array;
193
+ /**
194
+ * Sign data with the Ed25519 signing key.
195
+ *
196
+ * @returns The 64-byte signature as a URL-safe base64 string.
197
+ */
198
+ declare function signMessage(data: Uint8Array, signingKey: Uint8Array): string;
199
+ /**
200
+ * Verify an Ed25519 signature.
201
+ *
202
+ * @throws {SignatureVerificationError} If the signature is invalid.
203
+ */
204
+ declare function verifySignature(data: Uint8Array, signatureB64: string, verifyKey: Uint8Array): void;
205
+ /**
206
+ * Generate 24 cryptographically random bytes, returned as base64.
207
+ */
208
+ declare function generateNonce(): string;
209
+ /**
210
+ * Encrypt plaintext using NaCl Box (authenticated encryption).
211
+ *
212
+ * Both parties are known; the sender signs implicitly via key exchange.
213
+ * Format: nonce (24 bytes) || ciphertext, matching PyNaCl Box.encrypt() output.
214
+ *
215
+ * @returns Base64-encoded ciphertext (nonce prepended).
216
+ * @throws {EncryptionError} On any libsodium error.
217
+ */
218
+ declare function encryptPayload(plaintext: Uint8Array, senderSigningKey: Uint8Array, recipientVerifyKey: Uint8Array): string;
219
+ /**
220
+ * Decrypt NaCl Box ciphertext.
221
+ *
222
+ * @returns The original plaintext bytes.
223
+ * @throws {DecryptionError} On any libsodium error (wrong keys, tampered data).
224
+ */
225
+ declare function decryptPayload(ciphertextB64: string, recipientSigningKey: Uint8Array, senderVerifyKey: Uint8Array): Uint8Array;
226
+ /**
227
+ * Encrypt plaintext using NaCl SealedBox (anonymous sender).
228
+ *
229
+ * Only the recipient's public key is required; no sender authentication.
230
+ *
231
+ * @returns Base64-encoded ciphertext.
232
+ */
233
+ declare function encryptPayloadAnonymous(plaintext: Uint8Array, recipientVerifyKey: Uint8Array): string;
234
+ /**
235
+ * Decrypt NaCl SealedBox ciphertext.
236
+ *
237
+ * @returns The original plaintext bytes.
238
+ * @throws {DecryptionError} On any libsodium error.
239
+ */
240
+ declare function decryptPayloadAnonymous(ciphertextB64: string, recipientSigningKey: Uint8Array): Uint8Array;
241
+
242
+ /**
243
+ * UAM message envelope -- creation, signing, verification, wire format.
244
+ *
245
+ * An envelope wraps every UAM message with cryptographic signatures and
246
+ * encrypted payloads, using fromAddress/toAddress naming internally
247
+ * ("from"/"to" on the wire).
248
+ */
249
+
250
+ /**
251
+ * A signed, encrypted UAM message envelope.
252
+ */
253
+ interface MessageEnvelope {
254
+ readonly uamVersion: string;
255
+ readonly messageId: string;
256
+ readonly fromAddress: string;
257
+ readonly toAddress: string;
258
+ readonly timestamp: string;
259
+ readonly type: string;
260
+ readonly nonce: string;
261
+ readonly payload: string;
262
+ readonly signature: string;
263
+ readonly threadId?: string;
264
+ readonly replyTo?: string;
265
+ readonly expires?: string;
266
+ readonly mediaType?: string;
267
+ readonly metadata?: Record<string, unknown>;
268
+ readonly attachments?: Array<Record<string, unknown>>;
269
+ }
270
+ /**
271
+ * Convert an envelope to a wire-format dict.
272
+ *
273
+ * Maps fromAddress -> "from" and toAddress -> "to".
274
+ * Excludes undefined-valued optional fields.
275
+ */
276
+ declare function toWireDict(envelope: MessageEnvelope): Record<string, unknown>;
277
+ /**
278
+ * Restore an envelope from a wire-format dict.
279
+ *
280
+ * @throws {InvalidEnvelopeError} If any required field is missing.
281
+ */
282
+ declare function fromWireDict(d: Record<string, unknown>): MessageEnvelope;
283
+ /**
284
+ * Check that the serialized envelope does not exceed MAX_ENVELOPE_SIZE.
285
+ *
286
+ * @throws {EnvelopeTooLargeError} If the wire JSON exceeds 64 KB.
287
+ */
288
+ declare function validateEnvelopeSize(envelope: MessageEnvelope): void;
289
+ /**
290
+ * Create a signed, encrypted message envelope.
291
+ *
292
+ * @throws {InvalidAddressError} If either address is invalid.
293
+ * @throws {EnvelopeTooLargeError} If the serialized envelope exceeds 64 KB.
294
+ */
295
+ declare function createEnvelope(fromAddress: string, toAddress: string, messageType: MessageType | string, payloadPlaintext: Uint8Array, signingKey: Uint8Array, recipientVerifyKey: Uint8Array, options?: {
296
+ threadId?: string;
297
+ replyTo?: string;
298
+ expires?: string;
299
+ mediaType?: string;
300
+ metadata?: Record<string, unknown>;
301
+ attachments?: Array<Record<string, unknown>>;
302
+ }): Promise<MessageEnvelope>;
303
+ /**
304
+ * Verify the cryptographic signature on an envelope.
305
+ *
306
+ * @throws {SignatureVerificationError} If the signature is invalid.
307
+ */
308
+ declare function verifyEnvelope(envelope: MessageEnvelope, senderVerifyKey: Uint8Array): void;
309
+
310
+ /**
311
+ * UAM contact cards -- self-signed agent identity documents.
312
+ *
313
+ * A contact card advertises an agent's address, public key, relay endpoint,
314
+ * and optional metadata. The card is signed by the agent's own signing key
315
+ * so that any recipient can verify authenticity using the embedded public key.
316
+ */
317
+ /**
318
+ * A self-signed agent identity card.
319
+ */
320
+ interface ContactCard {
321
+ readonly version: string;
322
+ readonly address: string;
323
+ readonly displayName: string;
324
+ readonly description?: string;
325
+ readonly system?: string;
326
+ readonly connectionEndpoint?: string;
327
+ readonly relay: string;
328
+ readonly publicKey: string;
329
+ readonly signature: string;
330
+ readonly verifiedDomain?: string;
331
+ readonly payloadFormats?: string[];
332
+ readonly fingerprint?: string;
333
+ /** CARD-04: Multi-relay support. Outside signature scope. */
334
+ readonly relays?: string[];
335
+ }
336
+ /**
337
+ * Serialize a contact card to a plain dict.
338
+ *
339
+ * Uses snake_case names on the wire, excludes undefined-valued optional fields.
340
+ */
341
+ declare function contactCardToDict(card: ContactCard): Record<string, unknown>;
342
+ /**
343
+ * Deserialize a contact card from a dict.
344
+ *
345
+ * When verify is true (the default), the card's signature is checked
346
+ * immediately after deserialization.
347
+ *
348
+ * @throws {InvalidContactCardError} If required fields are missing.
349
+ * @throws {SignatureVerificationError} If verify is true and the signature is invalid.
350
+ */
351
+ declare function contactCardFromDict(d: Record<string, unknown>, options?: {
352
+ verify?: boolean;
353
+ }): ContactCard;
354
+ /**
355
+ * Create a self-signed contact card.
356
+ *
357
+ * payloadFormats defaults to ["text/plain", "text/markdown"] when not specified.
358
+ * The fingerprint is always auto-computed as the SHA-256 hex digest of the
359
+ * Ed25519 public key bytes.
360
+ *
361
+ * @throws {InvalidAddressError} If address is not valid.
362
+ */
363
+ declare function createContactCard(address: string, displayName: string, relay: string, signingKey: Uint8Array, options?: {
364
+ description?: string;
365
+ system?: string;
366
+ connectionEndpoint?: string;
367
+ verifiedDomain?: string;
368
+ payloadFormats?: string[];
369
+ relays?: string[];
370
+ }): Promise<ContactCard>;
371
+ /**
372
+ * Verify a contact card's signature using its embedded public key.
373
+ *
374
+ * @throws {InvalidContactCardError} If the address is invalid.
375
+ * @throws {SignatureVerificationError} If the signature is invalid.
376
+ */
377
+ declare function verifyContactCard(card: ContactCard): void;
378
+
379
+ /**
380
+ * UAM Protocol -- Universal Agent Messaging protocol library.
381
+ *
382
+ * Public API re-exports for the protocol layer.
383
+ */
384
+
385
+ type index_Address = Address;
386
+ type index_ContactCard = ContactCard;
387
+ type index_DecryptionError = DecryptionError;
388
+ declare const index_DecryptionError: typeof DecryptionError;
389
+ type index_EncryptionError = EncryptionError;
390
+ declare const index_EncryptionError: typeof EncryptionError;
391
+ type index_EnvelopeTooLargeError = EnvelopeTooLargeError;
392
+ declare const index_EnvelopeTooLargeError: typeof EnvelopeTooLargeError;
393
+ type index_InvalidAddressError = InvalidAddressError;
394
+ declare const index_InvalidAddressError: typeof InvalidAddressError;
395
+ type index_InvalidContactCardError = InvalidContactCardError;
396
+ declare const index_InvalidContactCardError: typeof InvalidContactCardError;
397
+ type index_InvalidEnvelopeError = InvalidEnvelopeError;
398
+ declare const index_InvalidEnvelopeError: typeof InvalidEnvelopeError;
399
+ type index_KeyPinningError = KeyPinningError;
400
+ declare const index_KeyPinningError: typeof KeyPinningError;
401
+ type index_Keypair = Keypair;
402
+ declare const index_MAX_ENVELOPE_SIZE: typeof MAX_ENVELOPE_SIZE;
403
+ type index_MessageEnvelope = MessageEnvelope;
404
+ type index_MessageType = MessageType;
405
+ declare const index_MessageType: typeof MessageType;
406
+ type index_Seed = Seed;
407
+ type index_SignatureError = SignatureError;
408
+ declare const index_SignatureError: typeof SignatureError;
409
+ type index_SignatureVerificationError = SignatureVerificationError;
410
+ declare const index_SignatureVerificationError: typeof SignatureVerificationError;
411
+ type index_SigningKey = SigningKey;
412
+ type index_UAMError = UAMError;
413
+ declare const index_UAMError: typeof UAMError;
414
+ declare const index_UAM_VERSION: typeof UAM_VERSION;
415
+ type index_VerifyKey = VerifyKey;
416
+ declare const index_b64Decode: typeof b64Decode;
417
+ declare const index_b64Encode: typeof b64Encode;
418
+ declare const index_canonicalize: typeof canonicalize;
419
+ declare const index_contactCardFromDict: typeof contactCardFromDict;
420
+ declare const index_contactCardToDict: typeof contactCardToDict;
421
+ declare const index_createContactCard: typeof createContactCard;
422
+ declare const index_createEnvelope: typeof createEnvelope;
423
+ declare const index_decryptPayload: typeof decryptPayload;
424
+ declare const index_decryptPayloadAnonymous: typeof decryptPayloadAnonymous;
425
+ declare const index_deserializeSigningKey: typeof deserializeSigningKey;
426
+ declare const index_deserializeVerifyKey: typeof deserializeVerifyKey;
427
+ declare const index_encryptPayload: typeof encryptPayload;
428
+ declare const index_encryptPayloadAnonymous: typeof encryptPayloadAnonymous;
429
+ declare const index_fromWireDict: typeof fromWireDict;
430
+ declare const index_generateKeypair: typeof generateKeypair;
431
+ declare const index_generateNonce: typeof generateNonce;
432
+ declare const index_parseAddress: typeof parseAddress;
433
+ declare const index_publicKeyFingerprint: typeof publicKeyFingerprint;
434
+ declare const index_serializeSigningKey: typeof serializeSigningKey;
435
+ declare const index_serializeVerifyKey: typeof serializeVerifyKey;
436
+ declare const index_signMessage: typeof signMessage;
437
+ declare const index_sodiumReady: typeof sodiumReady;
438
+ declare const index_toWireDict: typeof toWireDict;
439
+ declare const index_utcTimestamp: typeof utcTimestamp;
440
+ declare const index_validateEnvelopeSize: typeof validateEnvelopeSize;
441
+ declare const index_verifyContactCard: typeof verifyContactCard;
442
+ declare const index_verifyEnvelope: typeof verifyEnvelope;
443
+ declare const index_verifySignature: typeof verifySignature;
444
+ declare namespace index {
445
+ export { type index_Address as Address, type index_ContactCard as ContactCard, index_DecryptionError as DecryptionError, index_EncryptionError as EncryptionError, index_EnvelopeTooLargeError as EnvelopeTooLargeError, index_InvalidAddressError as InvalidAddressError, index_InvalidContactCardError as InvalidContactCardError, index_InvalidEnvelopeError as InvalidEnvelopeError, index_KeyPinningError as KeyPinningError, type index_Keypair as Keypair, index_MAX_ENVELOPE_SIZE as MAX_ENVELOPE_SIZE, type index_MessageEnvelope as MessageEnvelope, index_MessageType as MessageType, type index_Seed as Seed, index_SignatureError as SignatureError, index_SignatureVerificationError as SignatureVerificationError, type index_SigningKey as SigningKey, index_UAMError as UAMError, index_UAM_VERSION as UAM_VERSION, type index_VerifyKey as VerifyKey, index_b64Decode as b64Decode, index_b64Encode as b64Encode, index_canonicalize as canonicalize, index_contactCardFromDict as contactCardFromDict, index_contactCardToDict as contactCardToDict, index_createContactCard as createContactCard, index_createEnvelope as createEnvelope, index_decryptPayload as decryptPayload, index_decryptPayloadAnonymous as decryptPayloadAnonymous, index_deserializeSigningKey as deserializeSigningKey, index_deserializeVerifyKey as deserializeVerifyKey, index_encryptPayload as encryptPayload, index_encryptPayloadAnonymous as encryptPayloadAnonymous, index_fromWireDict as fromWireDict, index_generateKeypair as generateKeypair, index_generateNonce as generateNonce, index_parseAddress as parseAddress, index_publicKeyFingerprint as publicKeyFingerprint, index_serializeSigningKey as serializeSigningKey, index_serializeVerifyKey as serializeVerifyKey, index_signMessage as signMessage, index_sodiumReady as sodiumReady, index_toWireDict as toWireDict, index_utcTimestamp as utcTimestamp, index_validateEnvelopeSize as validateEnvelopeSize, index_verifyContactCard as verifyContactCard, index_verifyEnvelope as verifyEnvelope, index_verifySignature as verifySignature };
446
+ }
447
+
448
+ /**
449
+ * Ed25519 key generation, storage, loading, and token persistence.
450
+ *
451
+ * Matches Python key_manager.py: stores 32-byte seed (not 64-byte secret key),
452
+ * uses same file format so keys are cross-language compatible.
453
+ */
454
+
455
+ declare class KeyManager {
456
+ private _keyDir;
457
+ private _keypair;
458
+ constructor(keyDir?: string | null);
459
+ /** The 64-byte Ed25519 signing key. Throws if not loaded. */
460
+ get signingKey(): SigningKey;
461
+ /** The 32-byte Ed25519 verify (public) key. Throws if not loaded. */
462
+ get verifyKey(): VerifyKey;
463
+ /** The 32-byte seed. Throws if not loaded. */
464
+ get seed(): Seed;
465
+ /**
466
+ * Load existing keypair or generate a new one.
467
+ *
468
+ * Checks UAM_SIGNING_KEY env var first (base64-encoded Ed25519 seed).
469
+ * Falls back to file-based storage at {keyDir}/{name}.key.
470
+ * Generates a new keypair if neither source exists.
471
+ */
472
+ loadOrGenerate(name: string): void;
473
+ /**
474
+ * Store the relay token alongside the keypair.
475
+ */
476
+ saveToken(name: string, token: string): void;
477
+ /**
478
+ * Load a previously saved token, or return null.
479
+ *
480
+ * Checks UAM_TOKEN env var first, then file-based storage,
481
+ * then legacy .api_key files for backward compatibility.
482
+ */
483
+ loadToken(name: string): string | null;
484
+ /** Set file permissions to 0o600 (owner read/write only). */
485
+ private _setPermissions;
486
+ /** Warn if key file permissions are too permissive. */
487
+ private _checkPermissions;
488
+ }
489
+
490
+ /**
491
+ * ReceivedMessage -- frozen data object for inbound messages.
492
+ *
493
+ * Matches Python message.py: a readonly interface for decrypted messages.
494
+ * The toString() omission is deliberate prompt-injection isolation (SDK-10).
495
+ */
496
+ /**
497
+ * A decrypted, verified inbound message.
498
+ *
499
+ * This is a DATA OBJECT. Framework integrations must explicitly
500
+ * extract .content to use in prompts. The SDK never concatenates
501
+ * message content into LLM context automatically.
502
+ */
503
+ interface ReceivedMessage {
504
+ readonly messageId: string;
505
+ readonly fromAddress: string;
506
+ readonly toAddress: string;
507
+ readonly content: string;
508
+ readonly timestamp: string;
509
+ readonly type: string;
510
+ readonly threadId?: string;
511
+ readonly replyTo?: string;
512
+ readonly mediaType?: string;
513
+ readonly verified: boolean;
514
+ }
515
+
516
+ /**
517
+ * SQLite-backed local contact storage (HAND-03).
518
+ *
519
+ * Stores known contacts with their public keys and trust state.
520
+ * Provides fast in-memory caches for isKnown() and isBlocked() checks.
521
+ *
522
+ * Uses better-sqlite3 for synchronous SQLite (simpler than Python's aiosqlite).
523
+ */
524
+ /**
525
+ * A row returned from listContacts().
526
+ */
527
+ interface ContactRow {
528
+ address: string;
529
+ displayName: string | null;
530
+ trustState: string;
531
+ firstSeen: string;
532
+ lastSeen: string;
533
+ }
534
+ /**
535
+ * A pending handshake entry.
536
+ */
537
+ interface PendingEntry {
538
+ address: string;
539
+ contactCard: string;
540
+ receivedAt: string;
541
+ }
542
+ /**
543
+ * A blocked pattern entry.
544
+ */
545
+ interface BlockedEntry {
546
+ pattern: string;
547
+ blockedAt: string;
548
+ }
549
+ declare class ContactBook {
550
+ private _dbPath;
551
+ private _db;
552
+ private _knownAddresses;
553
+ private _blockedExact;
554
+ private _blockedDomains;
555
+ constructor(dataDir: string);
556
+ /**
557
+ * Open the database, create tables, run migrations, load caches.
558
+ */
559
+ open(): void;
560
+ /**
561
+ * Run schema migrations using PRAGMA user_version.
562
+ */
563
+ private _migrate;
564
+ /**
565
+ * Close the database connection.
566
+ */
567
+ close(): void;
568
+ /**
569
+ * Check if an address is in the contact book (in-memory, no I/O).
570
+ */
571
+ isKnown(address: string): boolean;
572
+ /**
573
+ * Check if an address matches any blocked pattern (O(1) set lookup).
574
+ */
575
+ isBlocked(address: string): boolean;
576
+ /**
577
+ * Look up the public key for a known contact.
578
+ */
579
+ getPublicKey(address: string): string | null;
580
+ /**
581
+ * Return the relay URLs for a known contact (CARD-04).
582
+ */
583
+ getRelayUrls(address: string): string[] | null;
584
+ /**
585
+ * Add or update a contact (upsert).
586
+ */
587
+ addContact(address: string, publicKey: string, options?: {
588
+ displayName?: string | null;
589
+ trustState?: string;
590
+ trustSource?: string | null;
591
+ relay?: string | null;
592
+ relays?: string[] | null;
593
+ }): void;
594
+ /**
595
+ * Return all contacts sorted by last_seen DESC.
596
+ */
597
+ listContacts(): ContactRow[];
598
+ /**
599
+ * Store a pending handshake request.
600
+ */
601
+ addPending(address: string, contactCardJson: string): void;
602
+ /**
603
+ * Retrieve all pending handshake requests.
604
+ */
605
+ getPending(): PendingEntry[];
606
+ /**
607
+ * Remove a pending handshake request.
608
+ */
609
+ removePending(address: string): void;
610
+ /**
611
+ * Return pending handshakes older than N days.
612
+ */
613
+ getExpiredPending(days?: number): PendingEntry[];
614
+ /**
615
+ * Return the trust_state for an address, or null if unknown.
616
+ */
617
+ getTrustState(address: string): string | null;
618
+ /**
619
+ * Set the pinned_at timestamp and trust_state='pinned' for a contact (TOFU).
620
+ */
621
+ setPinnedAt(address: string): void;
622
+ /**
623
+ * Remove a contact by address. Returns true if a contact was deleted.
624
+ */
625
+ removeContact(address: string): boolean;
626
+ /**
627
+ * Return true if the address has trust_state 'trusted', 'verified', or 'pinned'.
628
+ *
629
+ * Convenience helper for inbound message filtering (CARD-05, TOFU-02).
630
+ */
631
+ isTrustedOrVerified(address: string): boolean;
632
+ /**
633
+ * Block an address or domain pattern (e.g., '*::evil.com').
634
+ */
635
+ addBlock(pattern: string): void;
636
+ /**
637
+ * Remove a block pattern.
638
+ */
639
+ removeBlock(pattern: string): void;
640
+ /**
641
+ * Return all blocked patterns with their timestamps.
642
+ */
643
+ listBlocked(): BlockedEntry[];
644
+ private _cacheBlockPattern;
645
+ private _uncacheBlockPattern;
646
+ }
647
+
648
+ /**
649
+ * First-contact handshake flow manager (HAND-01 through HAND-04).
650
+ *
651
+ * Handles the three-phase handshake:
652
+ * 1. HANDSHAKE_REQUEST: sender sends contact card (encrypted with SealedBox)
653
+ * 2. HANDSHAKE_ACCEPT: recipient stores contact and sends accept back
654
+ * 3. HANDSHAKE_DENY: recipient rejects the handshake
655
+ *
656
+ * The HandshakeManager works with the ContactBook to persist trust decisions.
657
+ */
658
+
659
+ interface AgentLike {
660
+ readonly address: string;
661
+ readonly _config: {
662
+ readonly displayName: string;
663
+ readonly relayWsUrl: string;
664
+ };
665
+ readonly _keyManager: {
666
+ readonly signingKey: Uint8Array;
667
+ };
668
+ readonly _transport: {
669
+ send(envelope: Record<string, unknown>): Promise<void>;
670
+ } | null;
671
+ }
672
+ declare class HandshakeManager {
673
+ private _contactBook;
674
+ readonly _trustPolicy: string;
675
+ constructor(contactBook: ContactBook, trustPolicy: string);
676
+ /**
677
+ * Create a handshake request envelope with embedded contact card.
678
+ *
679
+ * The payload is the JSON-serialized contact card, encrypted with
680
+ * SealedBox (anonymous encryption) to the recipient.
681
+ *
682
+ * Returns wire-format dict ready to send via transport.
683
+ */
684
+ createHandshakeRequest(agent: AgentLike, toAddress: string, recipientVk: VerifyKey): Promise<Record<string, unknown>>;
685
+ /**
686
+ * Handle an inbound handshake message.
687
+ *
688
+ * Returns null -- handshake messages are not user-visible.
689
+ */
690
+ handleInbound(agent: AgentLike, envelope: MessageEnvelope, senderVk: VerifyKey): Promise<ReceivedMessage | null>;
691
+ /**
692
+ * Process a handshake.request: decrypt contact card, apply trust policy.
693
+ */
694
+ private _handleRequest;
695
+ /**
696
+ * Process a handshake.accept: store the sender as pinned (TOFU).
697
+ */
698
+ private _handleAccept;
699
+ /**
700
+ * Send a handshake.accept envelope back to the requester.
701
+ */
702
+ _sendAccept(agent: AgentLike, toAddress: string, recipientVk: VerifyKey): Promise<void>;
703
+ /**
704
+ * Send a handshake.deny envelope to the requester.
705
+ */
706
+ _sendDeny(agent: AgentLike, toAddress: string, recipientVk: VerifyKey): Promise<void>;
707
+ }
708
+
709
+ /**
710
+ * Abstract transport interface for relay communication.
711
+ *
712
+ * Two implementations:
713
+ * - HTTPTransport: stateless polling via REST API
714
+ * - WebSocketTransport: persistent real-time connection
715
+ */
716
+ declare abstract class TransportBase {
717
+ /** Establish connection to the relay. */
718
+ abstract connect(): Promise<void>;
719
+ /** Close the connection. */
720
+ abstract disconnect(): Promise<void>;
721
+ /** Send a message envelope to the relay. */
722
+ abstract send(envelope: Record<string, unknown>): Promise<void>;
723
+ /** Retrieve pending messages from the relay. */
724
+ abstract receive(limit?: number): Promise<Record<string, unknown>[]>;
725
+ /**
726
+ * Start listening for real-time messages.
727
+ *
728
+ * For WebSocket: registers a callback for push delivery.
729
+ * For HTTP: not supported (throws Error).
730
+ */
731
+ abstract listen(callback: (msg: Record<string, unknown>) => Promise<void>): Promise<void>;
732
+ }
733
+
734
+ /**
735
+ * Agent class -- the primary SDK interface (SDK-01).
736
+ *
737
+ * Provides send(), inbox(), handshake flow, contact management,
738
+ * trust policies, approve/deny/block/unblock.
739
+ *
740
+ * Matches Python agent.py API surface.
741
+ */
742
+
743
+ declare class Agent {
744
+ /** @internal */
745
+ readonly _config: SDKConfig;
746
+ /** @internal */
747
+ readonly _keyManager: KeyManager;
748
+ /** @internal */
749
+ _transport: TransportBase | null;
750
+ private _resolver;
751
+ private _address;
752
+ private _token;
753
+ private _connected;
754
+ private _autoRegister;
755
+ /** @internal */
756
+ readonly _contactBook: ContactBook;
757
+ /** @internal */
758
+ readonly _handshake: HandshakeManager;
759
+ /**
760
+ * Create an Agent. No I/O happens here -- call connect() to initialize.
761
+ */
762
+ constructor(name: string, options?: {
763
+ relay?: string | null;
764
+ domain?: string | null;
765
+ keyDir?: string | null;
766
+ autoRegister?: boolean;
767
+ displayName?: string | null;
768
+ transport?: string;
769
+ trustPolicy?: string;
770
+ });
771
+ /** The agent's full UAM address (e.g., 'myagent::youam.network'). */
772
+ get address(): string;
773
+ /** UAM SDK version. */
774
+ get version(): string;
775
+ /** The agent's public key (base64-encoded Ed25519 verify key). */
776
+ get publicKey(): string;
777
+ /** Whether the agent has completed connection setup. */
778
+ get isConnected(): boolean;
779
+ /**
780
+ * Generate and return a signed contact card for this agent.
781
+ */
782
+ contactCard(): Promise<Record<string, unknown>>;
783
+ /**
784
+ * Initialize the agent: load/generate keys, register, connect transport.
785
+ *
786
+ * Idempotent -- calling twice is safe.
787
+ */
788
+ connect(): Promise<void>;
789
+ /**
790
+ * Disconnect the transport and clean up resources.
791
+ */
792
+ close(): Promise<void>;
793
+ /**
794
+ * Send an encrypted, signed message. Returns the message_id.
795
+ */
796
+ send(toAddress: string, message: string, options?: {
797
+ threadId?: string;
798
+ attachments?: Array<Record<string, unknown>>;
799
+ }): Promise<string>;
800
+ /**
801
+ * Retrieve, decrypt, and verify pending messages.
802
+ *
803
+ * Returns a list of ReceivedMessage data objects.
804
+ * Silently drops messages with invalid signatures or decryption errors.
805
+ */
806
+ inbox(limit?: number): Promise<ReceivedMessage[]>;
807
+ /** List pending handshake requests. */
808
+ pending(): Promise<Array<{
809
+ address: string;
810
+ contactCard: string;
811
+ receivedAt: string;
812
+ }>>;
813
+ /**
814
+ * Approve a pending handshake request.
815
+ *
816
+ * Adds the sender to contacts with trust_source='explicit-approval'
817
+ * and sends handshake.accept back.
818
+ */
819
+ approve(address: string): Promise<void>;
820
+ /**
821
+ * Deny a pending handshake request.
822
+ *
823
+ * Removes the entry from pending and sends handshake.deny.
824
+ */
825
+ deny(address: string): Promise<void>;
826
+ /**
827
+ * Block an address or domain pattern (HAND-04).
828
+ */
829
+ block(pattern: string): Promise<void>;
830
+ /**
831
+ * Remove a block pattern (HAND-04).
832
+ */
833
+ unblock(pattern: string): Promise<void>;
834
+ /**
835
+ * Verify domain ownership via relay endpoint (DNS-02).
836
+ *
837
+ * Polls `POST /api/v1/verify-domain` until the relay confirms
838
+ * DNS TXT (or HTTPS fallback) verification succeeds, or timeout
839
+ * elapses.
840
+ *
841
+ * Returns `true` if verified, `false` on timeout.
842
+ */
843
+ verifyDomain(domain: string, options?: {
844
+ timeout?: number;
845
+ pollInterval?: number;
846
+ }): Promise<boolean>;
847
+ /** Multi-relay failover (CARD-06). */
848
+ private _getRelayUrls;
849
+ /**
850
+ * Try sending wire envelope to each relay URL in order (CARD-06).
851
+ *
852
+ * Uses transient fetch POST requests so the envelope can be delivered
853
+ * to any relay that hosts the recipient.
854
+ */
855
+ private _trySendWithFailover;
856
+ private _ensureConnected;
857
+ /**
858
+ * Resolve a recipient's public key, checking contact book first.
859
+ *
860
+ * TOFU-01: Known contacts always use stored key (zero network I/O).
861
+ * TOFU-03: Race-condition double-check after network resolve.
862
+ * Raises KeyPinningError if a pinned contact's key doesn't match.
863
+ */
864
+ private _resolvePublicKey;
865
+ private _initiateHandshake;
866
+ /**
867
+ * Process a single inbound envelope: verify, decrypt, handle handshakes.
868
+ *
869
+ * Returns null for invalid messages or handshake protocol messages.
870
+ */
871
+ private _processInbound;
872
+ /**
873
+ * Sweep expired pending handshakes and send receipt.failed (HAND-03).
874
+ */
875
+ private _sweepExpiredHandshakes;
876
+ /**
877
+ * Send receipt.read back to the sender (RCPT-01).
878
+ *
879
+ * Fire-and-forget: errors are never propagated.
880
+ * Anti-loop guard: receipts, handshakes, and session messages are skipped.
881
+ */
882
+ private _sendReadReceipt;
883
+ /**
884
+ * Register with the relay server.
885
+ *
886
+ * Calls POST /api/v1/register with agent name and public key.
887
+ * Stores the returned API key on disk.
888
+ */
889
+ private _registerWithRelay;
890
+ }
891
+
892
+ export { Agent, type ReceivedMessage, index as protocol };