@morseai/sdk 0.1.0-beta.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0-beta.3] - 2025-01-XX
9
+
10
+ ### Changed
11
+ - Updated package name to @morseai/sdk
12
+ - Updated repository URL to https://github.com/morseaitech/sdk.git
13
+
14
+ ## [0.1.0-beta.1] - 2025-01-XX
15
+
16
+ ### Added
17
+ - Initial beta release of MORSE SDK
18
+ - TypeScript SDK for creating and accessing encrypted signals
19
+ - Support for private and shared wallet signals
20
+ - X25519 + XChaCha20-Poly1305 encryption for shared signals
21
+ - AES-GCM-256 encryption for private signals
22
+ - Wallet authentication (browser, private key, custom)
23
+ - Automatic encryption/decryption
24
+ - Rate limiting support
25
+ - Request retry logic
26
+ - Comprehensive error handling
27
+ - Input validation
28
+ - Expiration utilities and constants
29
+ - File upload support
30
+ - Key certificate management (EIP-712)
31
+ - Deterministic key derivation from wallet signatures
32
+
33
+ ### Security
34
+ - Zero-knowledge architecture (server never sees plaintext)
35
+ - One-time access enforcement (signals burned after opening)
36
+ - Atomic operations prevent race conditions
37
+ - Certificate signature verification prevents key substitution attacks
38
+
package/README.md ADDED
@@ -0,0 +1,588 @@
1
+ # MORSE SDK
2
+
3
+ TypeScript SDK for creating and accessing encrypted signals in the MORSE platform.
4
+
5
+ **Version:** 0.1.0-beta.3 (Beta Release)
6
+
7
+ > ⚠️ **Beta Notice**: This is a beta release. The API is stable but may have minor changes before the 1.0.0 release. Please report any issues you encounter.
8
+
9
+ ## Features
10
+
11
+ - ✅ Full TypeScript support with autocomplete
12
+ - ✅ Automatic encryption/decryption (AES-GCM and X25519)
13
+ - ✅ Wallet authentication (browser, private key, custom)
14
+ - ✅ X25519 + XChaCha20-Poly1305 for shared signals
15
+ - ✅ Rate limiting (configurable)
16
+ - ✅ Request retry logic
17
+ - ✅ Comprehensive error handling
18
+ - ✅ Input validation
19
+ - ✅ Security-first design
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @morseai/sdk
25
+ # or
26
+ pnpm add @morseai/sdk
27
+ # or
28
+ yarn add @morseai/sdk
29
+ ```
30
+
31
+ ## Prerequisites
32
+
33
+ This SDK requires `ethers` v6+ as a peer dependency for wallet signature functionality.
34
+
35
+ ```bash
36
+ npm install ethers
37
+ ```
38
+
39
+ ## Authentication
40
+
41
+ MORSE uses **wallet signature authentication** - you sign a message with your Ethereum wallet to authenticate requests. See [AUTHENTICATION.md](./docs/AUTHENTICATION.md) for complete details.
42
+
43
+ **Quick summary:**
44
+ - ✅ API Key (required for SDK usage)
45
+ - ✅ Wallet signature (for frontend/operations)
46
+ - ✅ Private key (for backend/server)
47
+ - ✅ Custom implementation
48
+
49
+ ## Quick Start
50
+
51
+ ```typescript
52
+ import { MorseSDK, createWalletFromPrivateKey, Expiration } from "@morseai/sdk";
53
+
54
+ // Initialize SDK (only apiKey is required)
55
+ const sdk = new MorseSDK({
56
+ apiKey: process.env.MORSE_API_KEY!,
57
+ });
58
+
59
+ // Create wallet from private key
60
+ const wallet = createWalletFromPrivateKey({
61
+ privateKey: process.env.PRIVATE_KEY!,
62
+ });
63
+
64
+ // Create a shared signal (X25519 encryption)
65
+ const result = await sdk.createSignalEncrypted(wallet, {
66
+ walletTarget: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
67
+ mode: "shared_wallet",
68
+ message: "Secret message! 🔐",
69
+ expiresIn: Expiration.ONE_DAY, // or "24h", "7d", etc.
70
+ });
71
+
72
+ console.log("Signal ID:", result.signalId);
73
+ console.log("Shareable link:", result.shareableLink);
74
+
75
+ // Open and decrypt signal
76
+ const decrypted = await sdk.openSignalDecrypted(wallet, result.signalId);
77
+ console.log("Message:", decrypted.message);
78
+ ```
79
+
80
+ ## Configuration
81
+
82
+ ### Basic Setup
83
+
84
+ ```typescript
85
+ import { MorseSDK } from "@morseai/sdk";
86
+
87
+ // Simple initialization (only apiKey is required)
88
+ const sdk = new MorseSDK({
89
+ apiKey: "sk_your_api_key_here", // REQUIRED
90
+ });
91
+ ```
92
+
93
+ **Note:** `baseUrl` and `frontendUrl` are internal constants. The SDK automatically uses the correct API endpoints.
94
+
95
+ ### Advanced Configuration
96
+
97
+ ```typescript
98
+ const sdk = new MorseSDK({
99
+ apiKey: "sk_your_api_key_here", // REQUIRED
100
+ apiVersion: "v1", // Optional, defaults to "v1"
101
+ timeout: 30000, // Request timeout in ms (default: 30000)
102
+ retries: 3, // Number of retries on failure (default: 0)
103
+ retryDelay: 1000, // Delay between retries in ms (default: 1000)
104
+ rateLimit: {
105
+ enabled: true, // Enable rate limiting (default: true)
106
+ maxRequests: 100, // Maximum requests per window (default: 100)
107
+ windowMs: 60000, // Time window in milliseconds (default: 60000 = 1 minute)
108
+ },
109
+ onRequest: (url, options) => {
110
+ console.log("Making request to:", url);
111
+ },
112
+ onResponse: (url, response) => {
113
+ console.log("Response received from:", url, response.status);
114
+ },
115
+ onError: (error) => {
116
+ console.error("Request error:", error);
117
+ },
118
+ });
119
+ ```
120
+
121
+ ## Wallet Authentication
122
+
123
+ The SDK supports multiple ways to authenticate, depending on your use case:
124
+
125
+ ### 1. Browser/Web3 Wallet (Frontend)
126
+
127
+ ```typescript
128
+ import { MorseSDK, createBrowserWallet } from "@morseai/sdk";
129
+
130
+ const sdk = new MorseSDK({
131
+ apiKey: process.env.MORSE_API_KEY!,
132
+ });
133
+
134
+ // For MetaMask or other browser wallets
135
+ const wallet = await createBrowserWallet(window.ethereum);
136
+ ```
137
+
138
+ ### 2. Private Key (Backend/Server)
139
+
140
+ ```typescript
141
+ import { MorseSDK, createWalletFromPrivateKey } from "@morseai/sdk";
142
+
143
+ const sdk = new MorseSDK({
144
+ apiKey: process.env.MORSE_API_KEY!,
145
+ });
146
+
147
+ // For backend applications with a private key
148
+ const wallet = createWalletFromPrivateKey({
149
+ privateKey: process.env.PRIVATE_KEY!, // Keep this secure!
150
+ });
151
+ ```
152
+
153
+ ### 3. Custom Implementation
154
+
155
+ ```typescript
156
+ import { MorseSDK, type WalletAuth } from "@morseai/sdk";
157
+
158
+ const sdk = new MorseSDK({
159
+ apiKey: process.env.MORSE_API_KEY!,
160
+ });
161
+
162
+ // Implement your own wallet auth
163
+ const wallet: WalletAuth = {
164
+ address: "0x...",
165
+ signMessage: async (message: string) => {
166
+ // Your custom signing logic
167
+ return signature;
168
+ },
169
+ };
170
+ ```
171
+
172
+ ## Creating Signals
173
+
174
+ ### Automatic Encryption (Recommended)
175
+
176
+ The SDK can handle encryption automatically. This is the recommended approach:
177
+
178
+ ```typescript
179
+ import { MorseSDK, Expiration } from "@morseai/sdk";
180
+
181
+ // Create a private signal (AES-GCM, key in URL)
182
+ const privateSignal = await sdk.createSignalEncrypted(wallet, {
183
+ mode: "private",
184
+ message: "Private message - key will be in URL",
185
+ expiresIn: Expiration.ONE_DAY,
186
+ });
187
+
188
+ // Create a shared signal (X25519, recipient decrypts with wallet)
189
+ const sharedSignal = await sdk.createSignalEncrypted(wallet, {
190
+ walletTarget: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
191
+ mode: "shared_wallet", // shareWithRecipient is automatically true
192
+ message: "Shared message - recipient decrypts with wallet",
193
+ expiresIn: Expiration.ONE_DAY,
194
+ });
195
+ ```
196
+
197
+ ### Signal Modes
198
+
199
+ - **`mode: "private"`** - Private signal, key stored in URL fragment (`#k=...`)
200
+ - Uses AES-GCM encryption
201
+ - `shareWithRecipient` is automatically `false`
202
+ - Key is derived from wallet (deterministic) or random (for URL sharing)
203
+
204
+ - **`mode: "shared_wallet"`** - Shared signal, recipient decrypts with their wallet
205
+ - Uses X25519 + XChaCha20-Poly1305 encryption
206
+ - `shareWithRecipient` is automatically `true`
207
+ - Requires `walletTarget` (recipient's wallet address)
208
+ - No key in URL needed
209
+
210
+ ### Expiration
211
+
212
+ You can specify expiration in two ways:
213
+
214
+ #### Option 1: Relative Time (`expiresIn`)
215
+
216
+ ```typescript
217
+ import { Expiration } from "@morseai/sdk";
218
+
219
+ await sdk.createSignalEncrypted(wallet, {
220
+ mode: "shared_wallet",
221
+ walletTarget: "0x...",
222
+ message: "...",
223
+ expiresIn: Expiration.ONE_DAY, // Use constants for autocomplete
224
+ // Or use string format: "24h", "7d", "1h", "30m", "5s"
225
+ });
226
+ ```
227
+
228
+ **Available Constants:**
229
+ - `Expiration.FIVE_SECONDS` → `"5s"`
230
+ - `Expiration.ONE_MINUTE` → `"1m"`
231
+ - `Expiration.ONE_HOUR` → `"1h"`
232
+ - `Expiration.ONE_DAY` → `"24h"`
233
+ - `Expiration.ONE_WEEK` → `"7d"`
234
+ - `Expiration.ONE_MONTH` → `"30d"`
235
+ - And more...
236
+
237
+ #### Option 2: Specific Date (`expiresAt`)
238
+
239
+ ```typescript
240
+ // Specific date and time
241
+ const customDate = new Date("2026-12-31T23:59:59.000Z");
242
+ await sdk.createSignalEncrypted(wallet, {
243
+ mode: "shared_wallet",
244
+ walletTarget: "0x...",
245
+ message: "...",
246
+ expiresAt: customDate.toISOString(), // ISO 8601 format
247
+ });
248
+
249
+ // Or calculate from now
250
+ const futureDate = new Date();
251
+ futureDate.setHours(futureDate.getHours() + 2); // 2 hours from now
252
+ await sdk.createSignalEncrypted(wallet, {
253
+ mode: "shared_wallet",
254
+ walletTarget: "0x...",
255
+ message: "...",
256
+ expiresAt: futureDate.toISOString(),
257
+ });
258
+ ```
259
+
260
+ **Note:** Either `expiresIn` OR `expiresAt` must be provided (not both).
261
+
262
+ ### Creating Signals with Files
263
+
264
+ ```typescript
265
+ import * as fs from "fs";
266
+
267
+ const fileData = fs.readFileSync("./document.pdf");
268
+
269
+ const result = await sdk.createSignalEncrypted(wallet, {
270
+ walletTarget: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
271
+ mode: "shared_wallet",
272
+ message: "Check out this document!",
273
+ file: {
274
+ data: fileData,
275
+ originalName: "document.pdf",
276
+ mimeType: "application/pdf",
277
+ },
278
+ expiresIn: Expiration.ONE_DAY,
279
+ });
280
+ ```
281
+
282
+ ## Opening Signals
283
+
284
+ ### Automatic Decryption
285
+
286
+ The SDK automatically detects the encryption type and decrypts accordingly:
287
+
288
+ ```typescript
289
+ // Open and decrypt signal (automatic detection)
290
+ const decrypted = await sdk.openSignalDecrypted(wallet, signalId);
291
+
292
+ console.log("Message:", decrypted.message);
293
+ console.log("File:", decrypted.file);
294
+ console.log("Key source:", decrypted.keySource); // "derived" (X25519) or "provided" (URL key)
295
+ ```
296
+
297
+ **For X25519 signals:**
298
+ - No key needed - recipient decrypts with their wallet
299
+ - SDK automatically derives the key from wallet signature
300
+
301
+ **For private signals (AES-GCM):**
302
+ - Key is in the URL fragment (`#k=...`)
303
+ - Or you can provide it manually:
304
+
305
+ ```typescript
306
+ const decrypted = await sdk.openSignalDecrypted(wallet, signalId, keyFromUrl);
307
+ ```
308
+
309
+ ### Raw Encrypted Data
310
+
311
+ If you need the raw encrypted data (for manual decryption):
312
+
313
+ ```typescript
314
+ const encrypted = await sdk.openSignal(wallet, signalId);
315
+
316
+ console.log("Encrypted text:", encrypted.encryptedText);
317
+ console.log("Payload nonce:", encrypted.payloadNonce);
318
+ console.log("Cipher version:", encrypted.cipherVersion);
319
+ ```
320
+
321
+ ## Listing Signals
322
+
323
+ ```typescript
324
+ const mySignals = await sdk.listMySignals(wallet);
325
+
326
+ console.log(`You have ${mySignals.count} signals`);
327
+ mySignals.signals.forEach((signal) => {
328
+ console.log(`- ${signal.signalId}: ${signal.status}`);
329
+ console.log(` Created: ${signal.createdAt}`);
330
+ console.log(` Expires: ${signal.expiresAt}`);
331
+ });
332
+ ```
333
+
334
+ ## Burning Signals
335
+
336
+ ```typescript
337
+ await sdk.burnSignal(wallet, "signal-id-here");
338
+ ```
339
+
340
+ **Note:** Only the signal creator or recipient can burn a signal.
341
+
342
+ ## Encryption Methods
343
+
344
+ ### X25519 (Shared Signals)
345
+
346
+ For `mode: "shared_wallet"` signals:
347
+ - **Encryption:** X25519 ECDH + XChaCha20-Poly1305
348
+ - **Key Exchange:** Ephemeral sender key + recipient's static key
349
+ - **Decryption:** Recipient uses their wallet to derive key
350
+ - **No key in URL:** Recipient decrypts with their wallet signature
351
+
352
+ ### AES-GCM (Private Signals)
353
+
354
+ For `mode: "private"` signals:
355
+ - **Encryption:** AES-GCM-256
356
+ - **Key:** Derived from wallet or random (for URL sharing)
357
+ - **Key in URL:** For shareable links, key is in URL fragment (`#k=...`)
358
+
359
+ ## Error Handling
360
+
361
+ The SDK throws specific error types for better error handling:
362
+
363
+ ```typescript
364
+ import {
365
+ MorseSDKError,
366
+ SignalNotFoundError,
367
+ SignalExpiredError,
368
+ SignalAlreadyUsedError,
369
+ WalletNotAllowedError,
370
+ ValidationError,
371
+ NetworkError,
372
+ RateLimitError,
373
+ } from "@morseai/sdk";
374
+
375
+ try {
376
+ const signal = await sdk.openSignalDecrypted(wallet, signalId);
377
+ } catch (error) {
378
+ if (error instanceof SignalExpiredError) {
379
+ console.log("Signal expired");
380
+ } else if (error instanceof SignalAlreadyUsedError) {
381
+ console.log("Signal already used");
382
+ } else if (error instanceof WalletNotAllowedError) {
383
+ console.log("Wallet not allowed");
384
+ } else if (error instanceof RateLimitError) {
385
+ console.log(`Rate limit exceeded. Retry after ${error.retryAfterMs}ms`);
386
+ } else {
387
+ console.error("Unknown error:", error);
388
+ }
389
+ }
390
+ ```
391
+
392
+ ## Examples
393
+
394
+ See the `examples/` directory for complete working examples:
395
+
396
+ - `create-signal-example.ts` - Creating signals
397
+ - `open-signal-example.ts` - Opening and decrypting signals
398
+ - `backend-to-backend.ts` - Backend-to-backend communication
399
+ - `browser-example.ts` - Browser/Web3 wallet usage
400
+ - `custom-expiration-example.ts` - Custom expiration dates
401
+ - `advanced-config.ts` - Advanced configuration options
402
+
403
+ **Running examples:**
404
+
405
+ ```bash
406
+ # Set environment variables
407
+ export MORSE_API_KEY=sk_your_api_key
408
+ export PRIVATE_KEY=your_private_key_hex
409
+
410
+ # Run examples
411
+ npx tsx examples/create-signal-example.ts
412
+ npx tsx examples/open-signal-example.ts <signalId>
413
+ ```
414
+
415
+ ## API Reference
416
+
417
+ ### `MorseSDK`
418
+
419
+ The main SDK class for creating and accessing encrypted signals.
420
+
421
+ #### Constructor
422
+
423
+ ```typescript
424
+ new MorseSDK(config: MorseSDKConfig)
425
+ ```
426
+
427
+ **Config Options:**
428
+
429
+ | Option | Type | Default | Description |
430
+ |--------|------|---------|-------------|
431
+ | `apiKey` | `string` | **Required** | Your MORSE API key (starts with `sk_`) |
432
+ | `apiVersion` | `string` | `"v1"` | API version to use |
433
+ | `timeout` | `number` | `30000` | Request timeout in milliseconds |
434
+ | `retries` | `number` | `0` | Number of retries on failure |
435
+ | `retryDelay` | `number` | `1000` | Delay between retries in ms |
436
+ | `rateLimit` | `RateLimitConfig` | `{ enabled: true, maxRequests: 100, windowMs: 60000 }` | Rate limiting config |
437
+ | `onRequest` | `function` | - | Callback before each request |
438
+ | `onResponse` | `function` | - | Callback after each response |
439
+ | `onError` | `function` | - | Callback on errors |
440
+
441
+ #### Methods
442
+
443
+ ##### `createSignalEncrypted(wallet, options): Promise<CreateSignalResponseEncrypted>`
444
+
445
+ Create a signal with automatic encryption. **Recommended method.**
446
+
447
+ **Parameters:**
448
+ - `wallet: WalletAuth` - Wallet authentication object
449
+ - `options: CreateSignalOptionsEncrypted` - Signal creation options
450
+
451
+ **Returns:** `Promise<CreateSignalResponseEncrypted>` - Created signal with shareable link
452
+
453
+ **Example:**
454
+ ```typescript
455
+ const result = await sdk.createSignalEncrypted(wallet, {
456
+ walletTarget: "0x...", // Required for shared_wallet mode
457
+ mode: "shared_wallet", // or "private"
458
+ message: "Secret message",
459
+ expiresIn: Expiration.ONE_DAY,
460
+ });
461
+
462
+ console.log("Signal ID:", result.signalId);
463
+ console.log("Shareable link:", result.shareableLink);
464
+ console.log("Key (for private signals):", result.keyBase64);
465
+ ```
466
+
467
+ ##### `openSignalDecrypted(wallet, signalId, keyBase64?): Promise<OpenSignalResponseDecrypted>`
468
+
469
+ Open and decrypt a signal automatically.
470
+
471
+ **Parameters:**
472
+ - `wallet: WalletAuth` - Wallet authentication object
473
+ - `signalId: string` - Signal ID to open
474
+ - `keyBase64?: string` - Optional key for private signals (from URL fragment)
475
+
476
+ **Returns:** `Promise<OpenSignalResponseDecrypted>` - Decrypted signal data
477
+
478
+ **Example:**
479
+ ```typescript
480
+ const decrypted = await sdk.openSignalDecrypted(wallet, signalId);
481
+ console.log("Message:", decrypted.message);
482
+ console.log("File:", decrypted.file);
483
+ ```
484
+
485
+ ##### `createSignal(wallet, options): Promise<CreateSignalResponse>`
486
+
487
+ Create a signal with pre-encrypted data (manual encryption).
488
+
489
+ **Parameters:**
490
+ - `wallet: WalletAuth` - Wallet authentication object
491
+ - `options: CreateSignalOptions` - Signal creation options with encrypted data
492
+
493
+ **Returns:** `Promise<CreateSignalResponse>` - Created signal info
494
+
495
+ ##### `openSignal(wallet, signalId): Promise<OpenSignalResponse>`
496
+
497
+ Open a signal and return encrypted data (for manual decryption).
498
+
499
+ **Parameters:**
500
+ - `wallet: WalletAuth` - Wallet authentication object
501
+ - `signalId: string` - Signal ID to open
502
+
503
+ **Returns:** `Promise<OpenSignalResponse>` - Encrypted signal data
504
+
505
+ ##### `listMySignals(wallet): Promise<ListMySignalsResponse>`
506
+
507
+ List all signals accessible by the wallet.
508
+
509
+ **Parameters:**
510
+ - `wallet: WalletAuth` - Wallet authentication object
511
+
512
+ **Returns:** `Promise<ListMySignalsResponse>` - List of signals
513
+
514
+ ##### `burnSignal(wallet, signalId): Promise<{ success: boolean }>`
515
+
516
+ Manually burn a signal (mark as used).
517
+
518
+ **Parameters:**
519
+ - `wallet: WalletAuth` - Wallet authentication object
520
+ - `signalId: string` - Signal ID to burn
521
+
522
+ **Returns:** `Promise<{ success: boolean }>`
523
+
524
+ ## TypeScript Support
525
+
526
+ The SDK is fully typed. All types are exported:
527
+
528
+ ```typescript
529
+ import type {
530
+ CreateSignalOptions,
531
+ CreateSignalOptionsEncrypted,
532
+ CreateSignalResponse,
533
+ CreateSignalResponseEncrypted,
534
+ OpenSignalResponse,
535
+ OpenSignalResponseDecrypted,
536
+ SignalMode,
537
+ SignalStatus,
538
+ WalletAuth,
539
+ MorseSDKConfig,
540
+ ExpirationValue,
541
+ } from "@morseai/sdk";
542
+
543
+ import { Expiration } from "@morseai/sdk";
544
+ ```
545
+
546
+ ## Helper Functions
547
+
548
+ ### Signal Validation
549
+
550
+ ```typescript
551
+ import { isValidSignalId, isValidWalletAddress } from "@morseai/sdk";
552
+
553
+ isValidSignalId("abc123"); // true
554
+ isValidWalletAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"); // true
555
+ ```
556
+
557
+ ### Expiration Utilities
558
+
559
+ ```typescript
560
+ import {
561
+ formatExpiration,
562
+ isSignalExpired,
563
+ getTimeUntilExpiration,
564
+ parseExpiresIn,
565
+ } from "@morseai/sdk";
566
+
567
+ formatExpiration("2025-12-31T23:59:59Z");
568
+ isSignalExpired("2025-12-31T23:59:59Z");
569
+ getTimeUntilExpiration("2025-12-31T23:59:59Z");
570
+ parseExpiresIn("24h");
571
+ ```
572
+
573
+ ### Expiration Constants
574
+
575
+ ```typescript
576
+ import { Expiration } from "@morseai/sdk";
577
+
578
+ // Use constants for autocomplete and type safety
579
+ Expiration.ONE_DAY // "24h"
580
+ Expiration.ONE_WEEK // "7d"
581
+ Expiration.ONE_HOUR // "1h"
582
+ Expiration.ONE_MONTH // "30d"
583
+ // ... and more
584
+ ```
585
+
586
+ ## License
587
+
588
+ MIT