@smartledger/keys 1.5.10 → 3.0.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/README.md CHANGED
@@ -1,548 +1,548 @@
1
- # @smartledger/keys SDK Documentation
2
-
3
- **Single source of truth for cryptographic key operations.**
4
-
5
- Version: 1.5.1
6
- Status: ✅ Production Ready
7
- Tests: 52/52 passing
8
-
9
- **New in 1.5.1:** All three ML-DSA security levels now available (44/65/87)
10
-
11
- ---
12
-
13
- ## Why This SDK?
14
-
15
- Keys are the **root of everything** in your crypto architecture:
16
- - Identity
17
- - Signatures
18
- - PQ migration
19
- - Regulatory/audit story
20
-
21
- Without a clean SDK layer, key handling becomes **ad hoc** and **brittle**:
22
- - ❌ Random `generateKeypair()` implementations scattered across repos
23
- - ❌ Slightly different formats (hex here, base64 there)
24
- - ❌ Surprise refactors when adding PQ suites
25
- - ❌ Private keys exposed to calling code
26
-
27
- **With `@smartledger/keys`**, all your agents/modules/bridge code simply ask:
28
- ```typescript
29
- "Give me a signing key of suite X"
30
- "Sign this payload with keyId Y"
31
- ```
32
-
33
- And **don't care** how keys are stored, rotated, or implemented.
34
-
35
- ---
36
-
37
- ## Design Principles
38
-
39
- 1. **Private keys hidden** - Never exposed to most callers
40
- 2. **Crypto agility** - Suite is config, not code
41
- 3. **Single entry point** - Easy to audit
42
- 4. **Idempotent operations** - Safe for retries
43
- 5. **Minimal API surface** - Small, focused, easy to learn
44
-
45
- ---
46
-
47
- ## Installation
48
-
49
- ```bash
50
- npm install @smartledger/keys
51
- ```
52
-
53
- ### Supported ML-DSA Variants
54
-
55
- Choose the right security level for your use case:
56
-
57
- | Variant | NIST Level | Equivalent | Use Case |
58
- |---------|------------|------------|----------|
59
- | **ml-dsa-44** | Level 2 | AES-128 | IoT devices, high-throughput systems |
60
- | **ml-dsa-65** | Level 3 | AES-192 | **General purpose (recommended)** |
61
- | **ml-dsa-87** | Level 5 | AES-256 | High-security, long-term confidential data |
62
-
63
- ---
64
-
65
- ## Quick Start
66
-
67
- ```typescript
68
- import { createKeySDK } from '@smartledger/keys';
69
-
70
- // One line to get a ready-to-use SDK with ML-DSA + ECDSA suites registered
71
- const sdk = createKeySDK();
72
-
73
- // Create both PQ + ECDSA keys for an agent
74
- // Using ml-dsa-65 (Level 3) - recommended for most use cases
75
- const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
76
- 'agent-resonance',
77
- {
78
- primarySignatureSuite: 'ml-dsa-65', // or 'ml-dsa-44', 'ml-dsa-87'
79
- secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
80
- }
81
- );
82
-
83
- // Sign with both suites (private keys stay hidden)
84
- const message = new TextEncoder().encode('agent output');
85
- const { signatures } = await sdk.signWithSuites('agent-resonance', message);
86
-
87
- // Verify both signatures
88
- const verify = await sdk.verifyWithSuites('agent-resonance', message, signatures);
89
- console.log(verify.allValid); // true
90
- ```
91
-
92
- ---
93
-
94
- ## API Reference
95
-
96
- ### Factory Helper
97
-
98
- `createKeySDK(config?)` returns a ready-to-use SDK wired with in-memory storage and the default ML-DSA + ECDSA suites. Pass your own `keyRegistry` or `suiteRegistry` to override defaults.
99
-
100
- ### KeySDK Interface
101
-
102
- #### `createKey(agentId, profile, options?)`
103
-
104
- Create a new key for an agent/module.
105
-
106
- **Parameters:**
107
- - `agentId` (string) - Agent identifier (e.g. `'agent-resonance'`)
108
- - `profile` (CryptoProfile) - Which signature suites to use
109
- - `options` (CreateKeyOptions) - Optional expiration, usage, etc.
110
-
111
- **Returns:** `Promise<KeyRecord>` - KeyRecord with metadata and public key
112
-
113
- **Example:**
114
- ```typescript
115
- const key = await sdk.createKey(
116
- 'agent-schema',
117
- { primarySignatureSuite: 'ml-dsa-65' }, // Recommended: Level 3
118
- {
119
- expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(), // 1 year
120
- usage: ['signing'],
121
- }
122
- );
123
- ```
124
-
125
- ---
126
-
127
- #### `getKey(keyId)`
128
-
129
- Get an existing key by ID.
130
-
131
- **Parameters:**
132
- - `keyId` (string) - Key identifier
133
-
134
- **Returns:** `Promise<KeyRecord | null>` - KeyRecord or null if not found
135
-
136
- **Example:**
137
- ```typescript
138
- const key = await sdk.getKey('agent-resonance:pk-ml-1');
139
- if (key) {
140
- console.log(key.meta.suiteId); // 'ml-dsa-65' or 'ml-dsa-44', 'ml-dsa-87'
141
- console.log(key.publicKey); // Uint8Array
142
- }
143
- ```
144
-
145
- ---
146
-
147
- #### `signWithKey(keyId, message)`
148
-
149
- Sign a message with a key. **Private key never leaves the SDK.**
150
-
151
- **Parameters:**
152
- - `keyId` (string) - Which key to sign with
153
- - `message` (Uint8Array) - Message to sign (will be hashed internally)
154
-
155
- **Returns:** `Promise<Uint8Array>` - Signature bytes
156
-
157
- **Throws:** Error if key not found or not active
158
-
159
- **Example:**
160
- ```typescript
161
- const message = new TextEncoder().encode('agent output');
162
- const signature = await sdk.signWithKey('agent-resonance:pk-ml-1', message);
163
- ```
164
-
165
- ---
166
-
167
- #### `verifySignature(keyId, message, signature)`
168
-
169
- Verify a signature against a public key.
170
-
171
- **Parameters:**
172
- - `keyId` (string) - Which key to verify with
173
- - `message` (Uint8Array) - Original message
174
- - `signature` (Uint8Array) - Signature to verify
175
-
176
- **Returns:** `Promise<boolean>` - true if valid
177
-
178
- **Example:**
179
- ```typescript
180
- const valid = await sdk.verifySignature(
181
- 'agent-resonance:pk-ml-1',
182
- message,
183
- signature
184
- );
185
- ```
186
-
187
- ---
188
-
189
- #### `listKeysForAgent(agentId, activeOnly?)`
190
-
191
- List all keys for an agent.
192
-
193
- **Parameters:**
194
- - `agentId` (string) - Agent identifier
195
- - `activeOnly` (boolean) - If true, only return active keys (default: true)
196
-
197
- **Returns:** `Promise<KeyRecord[]>` - Array of KeyRecords
198
-
199
- **Example:**
200
- ```typescript
201
- const keys = await sdk.listKeysForAgent('agent-schema');
202
- for (const key of keys) {
203
- console.log(`${key.meta.keyId} (${key.meta.suiteId})`);
204
- }
205
- ```
206
-
207
- ---
208
-
209
- #### `getActiveCryptoProfile(agentId)`
210
-
211
- Get the active crypto profile for an agent.
212
-
213
- Looks up the most recent active keys and infers the profile.
214
-
215
- **Parameters:**
216
- - `agentId` (string) - Agent identifier
217
-
218
- **Returns:** `Promise<CryptoProfile | null>` - CryptoProfile or null if no keys found
219
-
220
- **Example:**
221
- ```typescript
222
- const profile = await sdk.getActiveCryptoProfile('agent-schema');
223
- console.log(profile.primarySignatureSuite); // 'ml-dsa-65'
224
- console.log(profile.secondarySignatureSuite); // 'bsv-ecdsa-secp256k1'
225
- ```
226
-
227
- ---
228
-
229
- #### `rotateAgentKeys(agentId)`
230
-
231
- Rotate an agent's keys.
232
-
233
- Generates new keypairs for all active keys, marks old ones as rotated.
234
-
235
- **Parameters:**
236
- - `agentId` (string) - Agent identifier
237
-
238
- **Returns:** `Promise<KeyRecord[]>` - Array of new KeyRecords
239
-
240
- **Example:**
241
- ```typescript
242
- const newKeys = await sdk.rotateAgentKeys('agent-schema');
243
- for (const key of newKeys) {
244
- console.log(`New: ${key.meta.keyId}`);
245
- console.log(`Rotated from: ${key.meta.rotatedFrom}`);
246
- }
247
- ```
248
-
249
- ---
250
-
251
- #### `getOrCreateKey(agentId, profile, options?)`
252
-
253
- Create key if it doesn't exist, otherwise return existing.
254
-
255
- **Idempotent** key creation for agent setup.
256
-
257
- **Parameters:**
258
- - `agentId` (string) - Agent identifier
259
- - `profile` (CryptoProfile) - Crypto configuration
260
- - `options` (CreateKeyOptions) - Creation options
261
-
262
- **Returns:** `Promise<KeyRecord>` - KeyRecord (new or existing)
263
-
264
- **Example:**
265
- ```typescript
266
- // Safe to call multiple times
267
- const key1 = await sdk.getOrCreateKey('agent-validator', {
268
- primarySignatureSuite: 'ml-dsa-65',
269
- });
270
-
271
- const key2 = await sdk.getOrCreateKey('agent-validator', {
272
- primarySignatureSuite: 'ml-dsa-65',
273
- });
274
-
275
- console.log(key1.meta.keyId === key2.meta.keyId); // true
276
- ```
277
-
278
- ---
279
-
280
- #### `createDualSignatureKeys(agentId, profile, options?)`
281
-
282
- Create both primary and secondary keys in one call. Requires `profile.secondarySignatureSuite`.
283
-
284
- **Returns:** `{ primaryKey, secondaryKey }`
285
-
286
- **Example:**
287
- ```typescript
288
- const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
289
- 'agent-bridge',
290
- {
291
- primarySignatureSuite: 'ml-dsa-65', // Recommended
292
- secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
293
- }
294
- );
295
- ```
296
-
297
- ---
298
-
299
- #### `getOrCreateDualSignatureKeys(agentId, profile, options?)`
300
-
301
- Idempotent version of the above; reuses active keys when present.
302
-
303
- **Returns:** `{ primaryKey, secondaryKey }`
304
-
305
- ---
306
-
307
- #### `signWithSuites(agentId, message, suites?)`
308
-
309
- Sign once per suite. When `suites` is omitted, active primary + secondary are used.
310
-
311
- **Returns:** `{ signatures: Array<{ suiteId, keyId, signature }> }`
312
-
313
- **Example:**
314
- ```typescript
315
- const message = new TextEncoder().encode('hybrid payload');
316
- const { signatures } = await sdk.signWithSuites('agent-bridge', message);
317
- ```
318
-
319
- ---
320
-
321
- #### `verifyWithSuites(agentId, message, signatures)`
322
-
323
- Verify multiple signatures and get per-suite results.
324
-
325
- **Returns:** `{ results: Array<{ suiteId, keyId, valid }>, allValid: boolean }`
326
-
327
- **Example:**
328
- ```typescript
329
- const verify = await sdk.verifyWithSuites('agent-bridge', message, signatures);
330
- console.log(verify.allValid); // true when every suite verifies
331
- ```
332
- ---
333
-
334
- ---
335
-
336
- ## Types
337
-
338
- ### KeyRecord
339
-
340
- Public key record (safe to expose, no private key).
341
-
342
- ```typescript
343
- interface KeyRecord {
344
- meta: KeyMeta; // Key metadata
345
- publicKey: Uint8Array; // Public key bytes
346
- }
347
- ```
348
-
349
- ### CreateKeyOptions
350
-
351
- Options for key creation.
352
-
353
- ```typescript
354
- interface CreateKeyOptions {
355
- expiresAt?: string; // ISO 8601
356
- usage?: Array<'signing' | 'encryption' | 'both'>; // Default: ['signing']
357
- cryptoProfileVersion?: string; // Default: '1.0.0'
358
- }
359
- ```
360
-
361
- ### CryptoProfile
362
-
363
- Per-agent/module crypto configuration.
364
-
365
- ```typescript
366
- interface CryptoProfile {
367
- primarySignatureSuite: string; // e.g. 'ml-dsa-65' (recommended), 'ml-dsa-44', 'ml-dsa-87'
368
- secondarySignatureSuite?: string; // e.g. 'bsv-ecdsa-secp256k1' (hybrid)
369
- keyEncapsulationSuite?: string; // e.g. 'ml-kem-768' (future)
370
- }
371
- ```
372
-
373
- ---
374
-
375
- ## Usage Patterns
376
-
377
- ### Single Signature (PQ-only)
378
-
379
- ```typescript
380
- // Agent uses only ML-DSA (recommended: ml-dsa-65)
381
- const key = await sdk.createKey('agent-research', {
382
- primarySignatureSuite: 'ml-dsa-65', // or 'ml-dsa-44' for IoT, 'ml-dsa-87' for max security
383
- });
384
-
385
- const message = new TextEncoder().encode('research results');
386
- const signature = await sdk.signWithKey(key.meta.keyId, message);
387
- ```
388
-
389
- ### Hybrid Signature (ECDSA + PQ)
390
-
391
- ```typescript
392
- // One call to create both keys
393
- await sdk.getOrCreateDualSignatureKeys('agent-bridge', {
394
- primarySignatureSuite: 'ml-dsa-65', // Balanced security (recommended)
395
- secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
396
- });
397
-
398
- // Sign with both suites
399
- const message = new TextEncoder().encode('bridge output');
400
- const { signatures } = await sdk.signWithSuites('agent-bridge', message);
401
-
402
- // Verify both signatures
403
- const verified = await sdk.verifyWithSuites('agent-bridge', message, signatures);
404
- console.log(verified.allValid); // true
405
- ```
406
-
407
- ### Agent Setup (Idempotent)
408
-
409
- ```typescript
410
- // Safe to call on every startup
411
- async function setupAgent(agentId: string) {
412
- const key = await sdk.getOrCreateKey(agentId, {
413
- primarySignatureSuite: 'ml-dsa-65', // Recommended default
414
- }, {
415
- expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(),
416
- });
417
-
418
- return key;
419
- }
420
- ```
421
-
422
- ### Monthly Key Rotation
423
-
424
- ```typescript
425
- // Automated rotation job
426
- async function monthlyRotation() {
427
- const agents = ['agent-resonance', 'agent-schema', 'agent-validator'];
428
-
429
- for (const agentId of agents) {
430
- const newKeys = await sdk.rotateAgentKeys(agentId);
431
- console.log(`✓ Rotated ${agentId}: ${newKeys.length} keys`);
432
- }
433
- }
434
- ```
435
-
436
- ---
437
-
438
- ## Benefits
439
-
440
- ### 🔒 Security
441
-
442
- - **Private keys hidden** - Never exposed to calling code
443
- - **Single audit point** - All key operations go through SDK
444
- - **No key leakage** - Keys stay in KeyRegistry storage
445
- - **Consistent hashing** - Suite-appropriate algorithms
446
-
447
- ### 🔄 Crypto Agility
448
-
449
- - **Suite is config** - Change ML-DSA → Falcon without code changes
450
- - **Profile-driven** - CryptoProfile defines agent's crypto setup
451
- - **Easy migration** - Hybrid mode for ECDSA → PQ transition
452
-
453
- ### 🛠️ Developer Experience
454
-
455
- - **Clean API** - 8 methods, easy to learn
456
- - **Idempotent** - `getOrCreateKey` safe for retries
457
- - **TypeScript** - Full type safety
458
- - **Well-tested** - 23 tests covering all scenarios
459
-
460
- ### 📊 Operations
461
-
462
- - **Centralized** - One SDK for all agents
463
- - **Auditable** - Easy to track key operations
464
- - **Scalable** - Storage backends (in-memory, file, KMS)
465
- - **Rotation-friendly** - Built-in rotation support
466
-
467
- ---
468
-
469
- ## Best Practices
470
-
471
- ### ✅ Do
472
-
473
- - Use SDK for **all** key operations
474
- - Call `getOrCreateKey` for idempotent setup
475
- - Set expiration dates on keys
476
- - Rotate keys regularly (monthly/quarterly)
477
- - Use `signWithKey` (not direct registry access)
478
-
479
- ### ❌ Don't
480
-
481
- - Expose private keys to calling code
482
- - Create keys manually outside SDK
483
- - Hard-code suite IDs in business logic
484
- - Skip key rotation
485
-
486
- ---
487
-
488
- ## Integration with Existing Code
489
-
490
- ### Before (Direct Registry)
491
-
492
- ```typescript
493
- // Scattered key operations
494
- const suite = globalRegistry.getSuite('ml-dsa-87');
495
- const keypair = await suite.generateKeypair();
496
- await keyRegistry.registerKey('agent-1:pk-1', keypair, 'ml-dsa-87');
497
-
498
- const key = await keyRegistry.getKey('agent-1:pk-1');
499
- const sig = await suite.sign(key.keypair.privateKey, message);
500
- ```
501
-
502
- ### After (SDK)
503
-
504
- ```typescript
505
- // Clean, centralized
506
- const key = await sdk.createKey('agent-1', {
507
- primarySignatureSuite: 'ml-dsa-65', // Choose: 'ml-dsa-44', '65', or '87'
508
- });
509
-
510
- const sig = await sdk.signWithKey(key.meta.keyId, message);
511
- ```
512
-
513
- ---
514
-
515
- ## What's Next?
516
-
517
- - **File Storage** - Persistent key storage (not just in-memory)
518
- - **KMS Integration** - AWS KMS, Azure Key Vault, HSM
519
- - **Key Backup** - Encrypted backup/recovery procedures
520
- - **Multi-tenant** - Isolate keys per tenant
521
- - **Hardware Keys** - Support for non-exportable hardware keys
522
-
523
- ---
524
-
525
- ## Summary
526
-
527
- **`@smartledger/keys` is the single source of truth for cryptographic keys:**
528
-
529
- ✅ 23 tests passing
530
- ✅ Private keys hidden from callers
531
- ✅ Crypto-agnostic API (suite is config)
532
- ✅ Idempotent operations
533
- ✅ Easy to audit
534
- ✅ Production-ready
535
-
536
- **Use it for all key operations. Make crypto agility a reality.**
537
-
538
- ---
539
-
540
- **See Also:**
541
- - [Key Management Guide](../docs/KEY_MANAGEMENT_GUIDE.md) - Lower-level KeyRegistry details
542
- - [Algorithm Selection Guide](../docs/ALGORITHM_SELECTION_GUIDE.md) - Which suite to use?
543
- - [ECDSA Implementation Strategy](../docs/ECDSA_IMPLEMENTATION_STRATEGY.md) - Why Noble
544
-
545
- ---
546
-
547
- **Version**: 1.0.0
548
- **Last Updated**: November 28, 2025
1
+ # @smartledger/keys SDK Documentation
2
+
3
+ **Single source of truth for cryptographic key operations.**
4
+
5
+ Version: 1.5.1
6
+ Status: ✅ Production Ready
7
+ Tests: 52/52 passing
8
+
9
+ **New in 1.5.1:** All three ML-DSA security levels now available (44/65/87)
10
+
11
+ ---
12
+
13
+ ## Why This SDK?
14
+
15
+ Keys are the **root of everything** in your crypto architecture:
16
+ - Identity
17
+ - Signatures
18
+ - PQ migration
19
+ - Regulatory/audit story
20
+
21
+ Without a clean SDK layer, key handling becomes **ad hoc** and **brittle**:
22
+ - ❌ Random `generateKeypair()` implementations scattered across repos
23
+ - ❌ Slightly different formats (hex here, base64 there)
24
+ - ❌ Surprise refactors when adding PQ suites
25
+ - ❌ Private keys exposed to calling code
26
+
27
+ **With `@smartledger/keys`**, all your agents/modules/bridge code simply ask:
28
+ ```typescript
29
+ "Give me a signing key of suite X"
30
+ "Sign this payload with keyId Y"
31
+ ```
32
+
33
+ And **don't care** how keys are stored, rotated, or implemented.
34
+
35
+ ---
36
+
37
+ ## Design Principles
38
+
39
+ 1. **Private keys hidden** - Never exposed to most callers
40
+ 2. **Crypto agility** - Suite is config, not code
41
+ 3. **Single entry point** - Easy to audit
42
+ 4. **Idempotent operations** - Safe for retries
43
+ 5. **Minimal API surface** - Small, focused, easy to learn
44
+
45
+ ---
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ npm install @smartledger/keys
51
+ ```
52
+
53
+ ### Supported ML-DSA Variants
54
+
55
+ Choose the right security level for your use case:
56
+
57
+ | Variant | NIST Level | Equivalent | Use Case |
58
+ |---------|------------|------------|----------|
59
+ | **ml-dsa-44** | Level 2 | AES-128 | IoT devices, high-throughput systems |
60
+ | **ml-dsa-65** | Level 3 | AES-192 | **General purpose (recommended)** |
61
+ | **ml-dsa-87** | Level 5 | AES-256 | High-security, long-term confidential data |
62
+
63
+ ---
64
+
65
+ ## Quick Start
66
+
67
+ ```typescript
68
+ import { createKeySDK } from '@smartledger/keys';
69
+
70
+ // One line to get a ready-to-use SDK with ML-DSA + ECDSA suites registered
71
+ const sdk = createKeySDK();
72
+
73
+ // Create both PQ + ECDSA keys for an agent
74
+ // Using ml-dsa-65 (Level 3) - recommended for most use cases
75
+ const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
76
+ 'agent-resonance',
77
+ {
78
+ primarySignatureSuite: 'ml-dsa-65', // or 'ml-dsa-44', 'ml-dsa-87'
79
+ secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
80
+ }
81
+ );
82
+
83
+ // Sign with both suites (private keys stay hidden)
84
+ const message = new TextEncoder().encode('agent output');
85
+ const { signatures } = await sdk.signWithSuites('agent-resonance', message);
86
+
87
+ // Verify both signatures
88
+ const verify = await sdk.verifyWithSuites('agent-resonance', message, signatures);
89
+ console.log(verify.allValid); // true
90
+ ```
91
+
92
+ ---
93
+
94
+ ## API Reference
95
+
96
+ ### Factory Helper
97
+
98
+ `createKeySDK(config?)` returns a ready-to-use SDK wired with in-memory storage and the default ML-DSA + ECDSA suites. Pass your own `keyRegistry` or `suiteRegistry` to override defaults.
99
+
100
+ ### KeySDK Interface
101
+
102
+ #### `createKey(agentId, profile, options?)`
103
+
104
+ Create a new key for an agent/module.
105
+
106
+ **Parameters:**
107
+ - `agentId` (string) - Agent identifier (e.g. `'agent-resonance'`)
108
+ - `profile` (CryptoProfile) - Which signature suites to use
109
+ - `options` (CreateKeyOptions) - Optional expiration, usage, etc.
110
+
111
+ **Returns:** `Promise<KeyRecord>` - KeyRecord with metadata and public key
112
+
113
+ **Example:**
114
+ ```typescript
115
+ const key = await sdk.createKey(
116
+ 'agent-schema',
117
+ { primarySignatureSuite: 'ml-dsa-65' }, // Recommended: Level 3
118
+ {
119
+ expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(), // 1 year
120
+ usage: ['signing'],
121
+ }
122
+ );
123
+ ```
124
+
125
+ ---
126
+
127
+ #### `getKey(keyId)`
128
+
129
+ Get an existing key by ID.
130
+
131
+ **Parameters:**
132
+ - `keyId` (string) - Key identifier
133
+
134
+ **Returns:** `Promise<KeyRecord | null>` - KeyRecord or null if not found
135
+
136
+ **Example:**
137
+ ```typescript
138
+ const key = await sdk.getKey('agent-resonance:pk-ml-1');
139
+ if (key) {
140
+ console.log(key.meta.suiteId); // 'ml-dsa-65' or 'ml-dsa-44', 'ml-dsa-87'
141
+ console.log(key.publicKey); // Uint8Array
142
+ }
143
+ ```
144
+
145
+ ---
146
+
147
+ #### `signWithKey(keyId, message)`
148
+
149
+ Sign a message with a key. **Private key never leaves the SDK.**
150
+
151
+ **Parameters:**
152
+ - `keyId` (string) - Which key to sign with
153
+ - `message` (Uint8Array) - Message to sign (will be hashed internally)
154
+
155
+ **Returns:** `Promise<Uint8Array>` - Signature bytes
156
+
157
+ **Throws:** Error if key not found or not active
158
+
159
+ **Example:**
160
+ ```typescript
161
+ const message = new TextEncoder().encode('agent output');
162
+ const signature = await sdk.signWithKey('agent-resonance:pk-ml-1', message);
163
+ ```
164
+
165
+ ---
166
+
167
+ #### `verifySignature(keyId, message, signature)`
168
+
169
+ Verify a signature against a public key.
170
+
171
+ **Parameters:**
172
+ - `keyId` (string) - Which key to verify with
173
+ - `message` (Uint8Array) - Original message
174
+ - `signature` (Uint8Array) - Signature to verify
175
+
176
+ **Returns:** `Promise<boolean>` - true if valid
177
+
178
+ **Example:**
179
+ ```typescript
180
+ const valid = await sdk.verifySignature(
181
+ 'agent-resonance:pk-ml-1',
182
+ message,
183
+ signature
184
+ );
185
+ ```
186
+
187
+ ---
188
+
189
+ #### `listKeysForAgent(agentId, activeOnly?)`
190
+
191
+ List all keys for an agent.
192
+
193
+ **Parameters:**
194
+ - `agentId` (string) - Agent identifier
195
+ - `activeOnly` (boolean) - If true, only return active keys (default: true)
196
+
197
+ **Returns:** `Promise<KeyRecord[]>` - Array of KeyRecords
198
+
199
+ **Example:**
200
+ ```typescript
201
+ const keys = await sdk.listKeysForAgent('agent-schema');
202
+ for (const key of keys) {
203
+ console.log(`${key.meta.keyId} (${key.meta.suiteId})`);
204
+ }
205
+ ```
206
+
207
+ ---
208
+
209
+ #### `getActiveCryptoProfile(agentId)`
210
+
211
+ Get the active crypto profile for an agent.
212
+
213
+ Looks up the most recent active keys and infers the profile.
214
+
215
+ **Parameters:**
216
+ - `agentId` (string) - Agent identifier
217
+
218
+ **Returns:** `Promise<CryptoProfile | null>` - CryptoProfile or null if no keys found
219
+
220
+ **Example:**
221
+ ```typescript
222
+ const profile = await sdk.getActiveCryptoProfile('agent-schema');
223
+ console.log(profile.primarySignatureSuite); // 'ml-dsa-65'
224
+ console.log(profile.secondarySignatureSuite); // 'bsv-ecdsa-secp256k1'
225
+ ```
226
+
227
+ ---
228
+
229
+ #### `rotateAgentKeys(agentId)`
230
+
231
+ Rotate an agent's keys.
232
+
233
+ Generates new keypairs for all active keys, marks old ones as rotated.
234
+
235
+ **Parameters:**
236
+ - `agentId` (string) - Agent identifier
237
+
238
+ **Returns:** `Promise<KeyRecord[]>` - Array of new KeyRecords
239
+
240
+ **Example:**
241
+ ```typescript
242
+ const newKeys = await sdk.rotateAgentKeys('agent-schema');
243
+ for (const key of newKeys) {
244
+ console.log(`New: ${key.meta.keyId}`);
245
+ console.log(`Rotated from: ${key.meta.rotatedFrom}`);
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ #### `getOrCreateKey(agentId, profile, options?)`
252
+
253
+ Create key if it doesn't exist, otherwise return existing.
254
+
255
+ **Idempotent** key creation for agent setup.
256
+
257
+ **Parameters:**
258
+ - `agentId` (string) - Agent identifier
259
+ - `profile` (CryptoProfile) - Crypto configuration
260
+ - `options` (CreateKeyOptions) - Creation options
261
+
262
+ **Returns:** `Promise<KeyRecord>` - KeyRecord (new or existing)
263
+
264
+ **Example:**
265
+ ```typescript
266
+ // Safe to call multiple times
267
+ const key1 = await sdk.getOrCreateKey('agent-validator', {
268
+ primarySignatureSuite: 'ml-dsa-65',
269
+ });
270
+
271
+ const key2 = await sdk.getOrCreateKey('agent-validator', {
272
+ primarySignatureSuite: 'ml-dsa-65',
273
+ });
274
+
275
+ console.log(key1.meta.keyId === key2.meta.keyId); // true
276
+ ```
277
+
278
+ ---
279
+
280
+ #### `createDualSignatureKeys(agentId, profile, options?)`
281
+
282
+ Create both primary and secondary keys in one call. Requires `profile.secondarySignatureSuite`.
283
+
284
+ **Returns:** `{ primaryKey, secondaryKey }`
285
+
286
+ **Example:**
287
+ ```typescript
288
+ const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
289
+ 'agent-bridge',
290
+ {
291
+ primarySignatureSuite: 'ml-dsa-65', // Recommended
292
+ secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
293
+ }
294
+ );
295
+ ```
296
+
297
+ ---
298
+
299
+ #### `getOrCreateDualSignatureKeys(agentId, profile, options?)`
300
+
301
+ Idempotent version of the above; reuses active keys when present.
302
+
303
+ **Returns:** `{ primaryKey, secondaryKey }`
304
+
305
+ ---
306
+
307
+ #### `signWithSuites(agentId, message, suites?)`
308
+
309
+ Sign once per suite. When `suites` is omitted, active primary + secondary are used.
310
+
311
+ **Returns:** `{ signatures: Array<{ suiteId, keyId, signature }> }`
312
+
313
+ **Example:**
314
+ ```typescript
315
+ const message = new TextEncoder().encode('hybrid payload');
316
+ const { signatures } = await sdk.signWithSuites('agent-bridge', message);
317
+ ```
318
+
319
+ ---
320
+
321
+ #### `verifyWithSuites(agentId, message, signatures)`
322
+
323
+ Verify multiple signatures and get per-suite results.
324
+
325
+ **Returns:** `{ results: Array<{ suiteId, keyId, valid }>, allValid: boolean }`
326
+
327
+ **Example:**
328
+ ```typescript
329
+ const verify = await sdk.verifyWithSuites('agent-bridge', message, signatures);
330
+ console.log(verify.allValid); // true when every suite verifies
331
+ ```
332
+ ---
333
+
334
+ ---
335
+
336
+ ## Types
337
+
338
+ ### KeyRecord
339
+
340
+ Public key record (safe to expose, no private key).
341
+
342
+ ```typescript
343
+ interface KeyRecord {
344
+ meta: KeyMeta; // Key metadata
345
+ publicKey: Uint8Array; // Public key bytes
346
+ }
347
+ ```
348
+
349
+ ### CreateKeyOptions
350
+
351
+ Options for key creation.
352
+
353
+ ```typescript
354
+ interface CreateKeyOptions {
355
+ expiresAt?: string; // ISO 8601
356
+ usage?: Array<'signing' | 'encryption' | 'both'>; // Default: ['signing']
357
+ cryptoProfileVersion?: string; // Default: '1.0.0'
358
+ }
359
+ ```
360
+
361
+ ### CryptoProfile
362
+
363
+ Per-agent/module crypto configuration.
364
+
365
+ ```typescript
366
+ interface CryptoProfile {
367
+ primarySignatureSuite: string; // e.g. 'ml-dsa-65' (recommended), 'ml-dsa-44', 'ml-dsa-87'
368
+ secondarySignatureSuite?: string; // e.g. 'bsv-ecdsa-secp256k1' (hybrid)
369
+ keyEncapsulationSuite?: string; // e.g. 'ml-kem-768' (future)
370
+ }
371
+ ```
372
+
373
+ ---
374
+
375
+ ## Usage Patterns
376
+
377
+ ### Single Signature (PQ-only)
378
+
379
+ ```typescript
380
+ // Agent uses only ML-DSA (recommended: ml-dsa-65)
381
+ const key = await sdk.createKey('agent-research', {
382
+ primarySignatureSuite: 'ml-dsa-65', // or 'ml-dsa-44' for IoT, 'ml-dsa-87' for max security
383
+ });
384
+
385
+ const message = new TextEncoder().encode('research results');
386
+ const signature = await sdk.signWithKey(key.meta.keyId, message);
387
+ ```
388
+
389
+ ### Hybrid Signature (ECDSA + PQ)
390
+
391
+ ```typescript
392
+ // One call to create both keys
393
+ await sdk.getOrCreateDualSignatureKeys('agent-bridge', {
394
+ primarySignatureSuite: 'ml-dsa-65', // Balanced security (recommended)
395
+ secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
396
+ });
397
+
398
+ // Sign with both suites
399
+ const message = new TextEncoder().encode('bridge output');
400
+ const { signatures } = await sdk.signWithSuites('agent-bridge', message);
401
+
402
+ // Verify both signatures
403
+ const verified = await sdk.verifyWithSuites('agent-bridge', message, signatures);
404
+ console.log(verified.allValid); // true
405
+ ```
406
+
407
+ ### Agent Setup (Idempotent)
408
+
409
+ ```typescript
410
+ // Safe to call on every startup
411
+ async function setupAgent(agentId: string) {
412
+ const key = await sdk.getOrCreateKey(agentId, {
413
+ primarySignatureSuite: 'ml-dsa-65', // Recommended default
414
+ }, {
415
+ expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(),
416
+ });
417
+
418
+ return key;
419
+ }
420
+ ```
421
+
422
+ ### Monthly Key Rotation
423
+
424
+ ```typescript
425
+ // Automated rotation job
426
+ async function monthlyRotation() {
427
+ const agents = ['agent-resonance', 'agent-schema', 'agent-validator'];
428
+
429
+ for (const agentId of agents) {
430
+ const newKeys = await sdk.rotateAgentKeys(agentId);
431
+ console.log(`✓ Rotated ${agentId}: ${newKeys.length} keys`);
432
+ }
433
+ }
434
+ ```
435
+
436
+ ---
437
+
438
+ ## Benefits
439
+
440
+ ### 🔒 Security
441
+
442
+ - **Private keys hidden** - Never exposed to calling code
443
+ - **Single audit point** - All key operations go through SDK
444
+ - **No key leakage** - Keys stay in KeyRegistry storage
445
+ - **Consistent hashing** - Suite-appropriate algorithms
446
+
447
+ ### 🔄 Crypto Agility
448
+
449
+ - **Suite is config** - Change ML-DSA → Falcon without code changes
450
+ - **Profile-driven** - CryptoProfile defines agent's crypto setup
451
+ - **Easy migration** - Hybrid mode for ECDSA → PQ transition
452
+
453
+ ### 🛠️ Developer Experience
454
+
455
+ - **Clean API** - 8 methods, easy to learn
456
+ - **Idempotent** - `getOrCreateKey` safe for retries
457
+ - **TypeScript** - Full type safety
458
+ - **Well-tested** - 23 tests covering all scenarios
459
+
460
+ ### 📊 Operations
461
+
462
+ - **Centralized** - One SDK for all agents
463
+ - **Auditable** - Easy to track key operations
464
+ - **Scalable** - Storage backends (in-memory, file, KMS)
465
+ - **Rotation-friendly** - Built-in rotation support
466
+
467
+ ---
468
+
469
+ ## Best Practices
470
+
471
+ ### ✅ Do
472
+
473
+ - Use SDK for **all** key operations
474
+ - Call `getOrCreateKey` for idempotent setup
475
+ - Set expiration dates on keys
476
+ - Rotate keys regularly (monthly/quarterly)
477
+ - Use `signWithKey` (not direct registry access)
478
+
479
+ ### ❌ Don't
480
+
481
+ - Expose private keys to calling code
482
+ - Create keys manually outside SDK
483
+ - Hard-code suite IDs in business logic
484
+ - Skip key rotation
485
+
486
+ ---
487
+
488
+ ## Integration with Existing Code
489
+
490
+ ### Before (Direct Registry)
491
+
492
+ ```typescript
493
+ // Scattered key operations
494
+ const suite = globalRegistry.getSuite('ml-dsa-87');
495
+ const keypair = await suite.generateKeypair();
496
+ await keyRegistry.registerKey('agent-1:pk-1', keypair, 'ml-dsa-87');
497
+
498
+ const key = await keyRegistry.getKey('agent-1:pk-1');
499
+ const sig = await suite.sign(key.keypair.privateKey, message);
500
+ ```
501
+
502
+ ### After (SDK)
503
+
504
+ ```typescript
505
+ // Clean, centralized
506
+ const key = await sdk.createKey('agent-1', {
507
+ primarySignatureSuite: 'ml-dsa-65', // Choose: 'ml-dsa-44', '65', or '87'
508
+ });
509
+
510
+ const sig = await sdk.signWithKey(key.meta.keyId, message);
511
+ ```
512
+
513
+ ---
514
+
515
+ ## What's Next?
516
+
517
+ - **File Storage** - Persistent key storage (not just in-memory)
518
+ - **KMS Integration** - AWS KMS, Azure Key Vault, HSM
519
+ - **Key Backup** - Encrypted backup/recovery procedures
520
+ - **Multi-tenant** - Isolate keys per tenant
521
+ - **Hardware Keys** - Support for non-exportable hardware keys
522
+
523
+ ---
524
+
525
+ ## Summary
526
+
527
+ **`@smartledger/keys` is the single source of truth for cryptographic keys:**
528
+
529
+ ✅ 23 tests passing
530
+ ✅ Private keys hidden from callers
531
+ ✅ Crypto-agnostic API (suite is config)
532
+ ✅ Idempotent operations
533
+ ✅ Easy to audit
534
+ ✅ Production-ready
535
+
536
+ **Use it for all key operations. Make crypto agility a reality.**
537
+
538
+ ---
539
+
540
+ **See Also:**
541
+ - [Key Management Guide](../docs/KEY_MANAGEMENT_GUIDE.md) - Lower-level KeyRegistry details
542
+ - [Algorithm Selection Guide](../docs/ALGORITHM_SELECTION_GUIDE.md) - Which suite to use?
543
+ - [ECDSA Implementation Strategy](../docs/ECDSA_IMPLEMENTATION_STRATEGY.md) - Why Noble
544
+
545
+ ---
546
+
547
+ **Version**: 1.0.0
548
+ **Last Updated**: November 28, 2025