@private.me/xbind 3.0.2 → 3.0.4
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 +2366 -204
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
- package/dist-standalone/_deps/shared/cjs/errors.js +1 -729
- package/dist-standalone/_deps/shared/cjs/index.js +1 -463
- package/dist-standalone/_deps/shared/cjs/types.js +1 -315
- package/dist-standalone/_deps/shared/errors.js +1 -244
- package/dist-standalone/_deps/shared/index.js +1 -72
- package/dist-standalone/_deps/shared/types.js +1 -86
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
- package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/search.js +1 -1
- package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
- package/dist-standalone/_deps/xchange/errors.js +1 -1
- package/dist-standalone/_deps/xchange/index.js +1 -1
- package/dist-standalone/_deps/xchange/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/xchange.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
- package/dist-standalone/_deps/xregistry/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/index.js +1 -1
- package/dist-standalone/_deps/xregistry/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/types.js +1 -1
- package/dist-standalone/agent-call.d.ts +2 -2
- package/dist-standalone/agent-call.js +1 -659
- package/dist-standalone/agent-sdk.js +1 -328
- package/dist-standalone/agent.d.ts +2 -0
- package/dist-standalone/agent.js +1 -1800
- package/dist-standalone/approval.js +1 -193
- package/dist-standalone/async-iterators.d.ts +3 -3
- package/dist-standalone/async-iterators.js +1 -382
- package/dist-standalone/auth.js +1 -219
- package/dist-standalone/auto-accept.js +1 -229
- package/dist-standalone/backup-config.js +1 -201
- package/dist-standalone/backup.js +1 -326
- package/dist-standalone/batch-operations.js +1 -388
- package/dist-standalone/cancellation.js +1 -477
- package/dist-standalone/checkpoint.js +1 -186
- package/dist-standalone/circuit-breaker.js +1 -468
- package/dist-standalone/cjs/agent-call.js +1 -701
- package/dist-standalone/cjs/agent-sdk.js +1 -332
- package/dist-standalone/cjs/agent.js +1 -1837
- package/dist-standalone/cjs/approval.js +1 -199
- package/dist-standalone/cjs/async-iterators.js +1 -392
- package/dist-standalone/cjs/auth.js +1 -225
- package/dist-standalone/cjs/auto-accept.js +1 -233
- package/dist-standalone/cjs/backup-config.js +1 -207
- package/dist-standalone/cjs/backup.js +1 -330
- package/dist-standalone/cjs/batch-operations.js +1 -397
- package/dist-standalone/cjs/cancellation.js +1 -490
- package/dist-standalone/cjs/checkpoint.js +1 -193
- package/dist-standalone/cjs/circuit-breaker.js +1 -476
- package/dist-standalone/cjs/cli/init.js +1 -492
- package/dist-standalone/cjs/config-validation.js +1 -522
- package/dist-standalone/cjs/connect.js +1 -312
- package/dist-standalone/cjs/connection-pool.js +1 -506
- package/dist-standalone/cjs/correlation-id.js +1 -339
- package/dist-standalone/cjs/crypto-utils.js +1 -176
- package/dist-standalone/cjs/debug-mode.js +1 -534
- package/dist-standalone/cjs/did-document.js +1 -101
- package/dist-standalone/cjs/did-privateme.js +1 -130
- package/dist-standalone/cjs/did-web.js +1 -201
- package/dist-standalone/cjs/discovery.js +1 -462
- package/dist-standalone/cjs/dual-mode.js +1 -251
- package/dist-standalone/cjs/email-templates.js +1 -313
- package/dist-standalone/cjs/email-transport.js +1 -239
- package/dist-standalone/cjs/envelope.js +1 -538
- package/dist-standalone/cjs/errors.js +1 -913
- package/dist-standalone/cjs/event-emitter.js +1 -461
- package/dist-standalone/cjs/gateway-state.js +1 -55
- package/dist-standalone/cjs/gateway-transport.js +1 -120
- package/dist-standalone/cjs/graceful-degradation.js +1 -403
- package/dist-standalone/cjs/guardrails.js +1 -223
- package/dist-standalone/cjs/health-check.js +1 -336
- package/dist-standalone/cjs/http-compat.js +1 -272
- package/dist-standalone/cjs/http-status-map.js +1 -571
- package/dist-standalone/cjs/identity.js +1 -645
- package/dist-standalone/cjs/index.js +1 -406
- package/dist-standalone/cjs/invitation.js +1 -421
- package/dist-standalone/cjs/invite.js +1 -328
- package/dist-standalone/cjs/key-agreement.js +1 -335
- package/dist-standalone/cjs/lazy-init.js +1 -300
- package/dist-standalone/cjs/logger.js +1 -291
- package/dist-standalone/cjs/loopback-transport.js +1 -0
- package/dist-standalone/cjs/mdns-discovery.js +1 -202
- package/dist-standalone/cjs/nonce-store.js +1 -80
- package/dist-standalone/cjs/pairing-manager.js +1 -223
- package/dist-standalone/cjs/plugin-system.js +1 -264
- package/dist-standalone/cjs/plugins/logging.js +1 -168
- package/dist-standalone/cjs/plugins/metrics.js +1 -181
- package/dist-standalone/cjs/plugins/validation.js +1 -302
- package/dist-standalone/cjs/policy.js +1 -320
- package/dist-standalone/cjs/progress-callbacks.js +1 -583
- package/dist-standalone/cjs/redis-nonce-store.js +1 -76
- package/dist-standalone/cjs/registry-middleware.js +1 -50
- package/dist-standalone/cjs/retry-strategies.js +1 -544
- package/dist-standalone/cjs/retry-transport.js +1 -102
- package/dist-standalone/cjs/runtime/browser.js +1 -533
- package/dist-standalone/cjs/runtime/edge.js +1 -526
- package/dist-standalone/cjs/runtime/react-native.js +1 -394
- package/dist-standalone/cjs/security-policy.js +1 -245
- package/dist-standalone/cjs/serialization.js +1 -1040
- package/dist-standalone/cjs/split-channel.js +1 -225
- package/dist-standalone/cjs/subscription-proof.js +1 -230
- package/dist-standalone/cjs/succession.js +1 -148
- package/dist-standalone/cjs/timeouts.js +1 -412
- package/dist-standalone/cjs/trace-context.js +1 -424
- package/dist-standalone/cjs/trace-spans.js +1 -495
- package/dist-standalone/cjs/transport.js +1 -63
- package/dist-standalone/cjs/trust-registry.js +1 -991
- package/dist-standalone/cjs/types/error-response.js +1 -56
- package/dist-standalone/cjs/vault-auth.js +1 -178
- package/dist-standalone/cjs/vault-store-loader.js +1 -194
- package/dist-standalone/cjs/verify.js +1 -25
- package/dist-standalone/cjs/version-info.js +1 -543
- package/dist-standalone/cjs/xfetch.js +1 -340
- package/dist-standalone/cli/init.js +1 -455
- package/dist-standalone/cli/setup.js +1 -514
- package/dist-standalone/cli/types.js +1 -27
- package/dist-standalone/cli/xbind.js +1 -148
- package/dist-standalone/config-validation.js +1 -513
- package/dist-standalone/connect.js +1 -274
- package/dist-standalone/connection-pool.js +1 -500
- package/dist-standalone/correlation-id.js +1 -326
- package/dist-standalone/crypto-utils.d.ts +2 -7
- package/dist-standalone/crypto-utils.js +1 -157
- package/dist-standalone/debug-mode.js +1 -510
- package/dist-standalone/did-document.js +1 -96
- package/dist-standalone/did-privateme.js +1 -121
- package/dist-standalone/did-web.js +1 -196
- package/dist-standalone/discovery.js +1 -458
- package/dist-standalone/dual-mode.js +1 -247
- package/dist-standalone/email-templates.js +1 -309
- package/dist-standalone/email-transport.d.ts +2 -2
- package/dist-standalone/email-transport.js +1 -232
- package/dist-standalone/envelope.js +1 -525
- package/dist-standalone/errors.d.ts +13 -3
- package/dist-standalone/errors.js +1 -896
- package/dist-standalone/event-emitter.js +1 -456
- package/dist-standalone/gateway-state.d.ts +1 -1
- package/dist-standalone/gateway-state.js +1 -51
- package/dist-standalone/gateway-transport.js +1 -116
- package/dist-standalone/graceful-degradation.js +1 -396
- package/dist-standalone/guardrails.js +1 -216
- package/dist-standalone/health-check.d.ts +5 -1
- package/dist-standalone/health-check.js +1 -332
- package/dist-standalone/http-compat.d.ts +1 -1
- package/dist-standalone/http-compat.js +1 -267
- package/dist-standalone/http-status-map.js +1 -561
- package/dist-standalone/identity.js +1 -619
- package/dist-standalone/index.d.ts +15 -4
- package/dist-standalone/index.js +1 -78
- package/dist-standalone/invitation.js +1 -415
- package/dist-standalone/invite.js +1 -324
- package/dist-standalone/key-agreement.js +1 -325
- package/dist-standalone/lazy-init.d.ts +11 -6
- package/dist-standalone/lazy-init.js +1 -295
- package/dist-standalone/logger.js +1 -285
- package/dist-standalone/loopback-transport.d.ts +87 -0
- package/dist-standalone/loopback-transport.js +1 -0
- package/dist-standalone/mdns-discovery.js +1 -195
- package/dist-standalone/nonce-store.js +1 -76
- package/dist-standalone/pairing-manager.js +1 -219
- package/dist-standalone/plugin-system.js +1 -257
- package/dist-standalone/plugins/logging.js +1 -163
- package/dist-standalone/plugins/metrics.d.ts +4 -4
- package/dist-standalone/plugins/metrics.js +1 -176
- package/dist-standalone/plugins/validation.js +1 -297
- package/dist-standalone/policy.js +1 -315
- package/dist-standalone/progress-callbacks.js +1 -576
- package/dist-standalone/redis-nonce-store.js +1 -72
- package/dist-standalone/registry-middleware.js +1 -47
- package/dist-standalone/retry-strategies.js +1 -534
- package/dist-standalone/retry-transport.js +1 -98
- package/dist-standalone/runtime/browser.js +1 -516
- package/dist-standalone/runtime/edge.js +1 -511
- package/dist-standalone/runtime/react-native.d.ts +1 -1
- package/dist-standalone/runtime/react-native.js +1 -383
- package/dist-standalone/security-policy.js +1 -239
- package/dist-standalone/serialization.js +1 -1031
- package/dist-standalone/split-channel.d.ts +1 -1
- package/dist-standalone/split-channel.js +1 -219
- package/dist-standalone/subscription-proof.js +1 -224
- package/dist-standalone/succession.js +1 -142
- package/dist-standalone/timeouts.js +1 -398
- package/dist-standalone/trace-context.js +1 -414
- package/dist-standalone/trace-spans.js +1 -488
- package/dist-standalone/transport.d.ts +1 -1
- package/dist-standalone/transport.js +1 -59
- package/dist-standalone/trust-registry.d.ts +3 -3
- package/dist-standalone/trust-registry.js +1 -950
- package/dist-standalone/types/error-response.js +1 -52
- package/dist-standalone/vault-auth.js +1 -174
- package/dist-standalone/vault-store-loader.d.ts +9 -0
- package/dist-standalone/vault-store-loader.js +1 -187
- package/dist-standalone/verify.js +1 -16
- package/dist-standalone/version-info.js +1 -530
- package/dist-standalone/xfetch.js +1 -335
- package/package.json +1 -1
- package/share1.dat +0 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @private.me/xbind
|
|
2
2
|
|
|
3
3
|

|
|
4
|
-

|
|
5
|
+

|
|
6
6
|

|
|
7
7
|

|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ Build AI agents that communicate securely using ML-DSA-65 DID identity, ML-KEM-7
|
|
|
12
12
|
|
|
13
13
|
Part of the **Private.Me** platform—where APIs have keys, but ACIs have identity.
|
|
14
14
|
|
|
15
|
-
**Version 3.0.
|
|
15
|
+
**Version 3.0.3** — **Major Features:** LoopbackTransport export for testing (GAP-CORE-2), describeSecurityModeStructured export (GAP-API-3), Agent.quickstart() signature fixes. Full Control IP Protection (PLAN-13) - Vault Store architecture with payment-gated algorithm delivery. Store Front (npm) contains Share 1 only, Vault Store (EC2) contains Share 2 (payment-gated). Runtime crypto loading, 4-layer security (DID auth + usage quotas + rate limiting + audit logging). Usage-based model: Free tier 100K ops/month (includes vault access), Pro tier unlimited. Previous v3.0.2: Full Control IP Protection launch. Previous v1.4.2: Runtime compatibility, API enhancements.
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
@@ -50,6 +50,7 @@ For production deployments requiring formal cryptographic assurance, please cont
|
|
|
50
50
|
|
|
51
51
|
**Dangerous practices that expose your identity:**
|
|
52
52
|
|
|
53
|
+
<!-- ANTI-PATTERN -->
|
|
53
54
|
```typescript
|
|
54
55
|
// ❌ WRONG: Plaintext file storage
|
|
55
56
|
const seed = agent.exportSeeds();
|
|
@@ -63,6 +64,7 @@ const agent = await Agent.fromSeed('0123456789abcdef...'); // Visible in reposi
|
|
|
63
64
|
// ❌ WRONG: Unencrypted database
|
|
64
65
|
await db.run('INSERT INTO config VALUES (?, ?)', ['seed', seed]); // SQL injection risk
|
|
65
66
|
```
|
|
67
|
+
<!-- /ANTI-PATTERN -->
|
|
66
68
|
|
|
67
69
|
**Why this is critical:**
|
|
68
70
|
- **Identity Theft**: Attacker gains your DID and can impersonate your agent
|
|
@@ -76,6 +78,7 @@ await db.run('INSERT INTO config VALUES (?, ?)', ['seed', seed]); // SQL inject
|
|
|
76
78
|
|
|
77
79
|
#### macOS: Keychain Services
|
|
78
80
|
|
|
81
|
+
<!-- SNIPPET -->
|
|
79
82
|
```typescript
|
|
80
83
|
import { exec } from 'node:child_process';
|
|
81
84
|
import { promisify } from 'node:util';
|
|
@@ -112,9 +115,11 @@ if (!result.ok) {
|
|
|
112
115
|
|
|
113
116
|
const agent = result.value;
|
|
114
117
|
```
|
|
118
|
+
<!-- /SNIPPET -->
|
|
115
119
|
|
|
116
120
|
#### Windows: Credential Manager (DPAPI)
|
|
117
121
|
|
|
122
|
+
<!-- SNIPPET -->
|
|
118
123
|
```typescript
|
|
119
124
|
// Using keytar package for cross-platform keychain access
|
|
120
125
|
import keytar from 'keytar';
|
|
@@ -140,9 +145,11 @@ if (!result.ok) {
|
|
|
140
145
|
|
|
141
146
|
const agent = result.value;
|
|
142
147
|
```
|
|
148
|
+
<!-- /SNIPPET -->
|
|
143
149
|
|
|
144
150
|
#### Linux: Secret Service API (gnome-keyring, KWallet)
|
|
145
151
|
|
|
152
|
+
<!-- SNIPPET -->
|
|
146
153
|
```typescript
|
|
147
154
|
import keytar from 'keytar';
|
|
148
155
|
|
|
@@ -167,6 +174,7 @@ if (!result.ok) {
|
|
|
167
174
|
|
|
168
175
|
const agent = result.value;
|
|
169
176
|
```
|
|
177
|
+
<!-- /SNIPPET -->
|
|
170
178
|
|
|
171
179
|
**Cross-platform library:**
|
|
172
180
|
```bash
|
|
@@ -177,6 +185,7 @@ npm install keytar # Unified API for macOS/Windows/Linux keystores
|
|
|
177
185
|
|
|
178
186
|
For production deployments with compliance requirements (PCI-DSS, HIPAA, SOC 2):
|
|
179
187
|
|
|
188
|
+
<!-- SNIPPET -->
|
|
180
189
|
```typescript
|
|
181
190
|
// Using AWS CloudHSM or Azure Key Vault
|
|
182
191
|
import { KMSClient, DecryptCommand } from '@aws-sdk/client-kms';
|
|
@@ -211,6 +220,7 @@ if (!result.ok) {
|
|
|
211
220
|
|
|
212
221
|
const agent = result.value;
|
|
213
222
|
```
|
|
223
|
+
<!-- /SNIPPET -->
|
|
214
224
|
|
|
215
225
|
**HSM Benefits:**
|
|
216
226
|
- **FIPS 140-2 Level 3** certified hardware
|
|
@@ -343,9 +353,15 @@ See [Configuration Guide](./docs/configuration.md) for complete setup instructio
|
|
|
343
353
|
|
|
344
354
|
## Pricing
|
|
345
355
|
|
|
346
|
-
|
|
356
|
+
xBind offers three tiers to fit your needs:
|
|
347
357
|
|
|
348
|
-
|
|
358
|
+
- **Free Tier** — Generous monthly operations, no credit card required
|
|
359
|
+
- **Pro Tier** — Unlimited operations for production use
|
|
360
|
+
- **VIP Tier** — Custom limits with priority support
|
|
361
|
+
|
|
362
|
+
**See [pricing details](https://private.me/docs/xbind.html#pricing) for current rates, quotas, and purchase options.**
|
|
363
|
+
|
|
364
|
+
All tiers include full Vault Store access for proprietary algorithms. Billing is usage-based with monthly cycles. No upfront commitment required.
|
|
349
365
|
|
|
350
366
|
[Subscribe now](https://private.me/subscribe?product=xbind)
|
|
351
367
|
|
|
@@ -464,8 +480,8 @@ Full fingerprinting source code available at `apps/server/src/device-fingerprint
|
|
|
464
480
|
```typescript
|
|
465
481
|
import { Agent } from '@private.me/xbind';
|
|
466
482
|
|
|
467
|
-
// Create agent (auto-generates cryptographic identity)
|
|
468
|
-
const agent = await Agent.
|
|
483
|
+
// Create agent (auto-generates cryptographic identity, throws on error)
|
|
484
|
+
const agent = await Agent.quickstart({ name: 'my-service' });
|
|
469
485
|
|
|
470
486
|
// Send authenticated message
|
|
471
487
|
const result = await agent.send({
|
|
@@ -475,7 +491,8 @@ const result = await agent.send({
|
|
|
475
491
|
});
|
|
476
492
|
|
|
477
493
|
if (!result.ok) {
|
|
478
|
-
console.error(
|
|
494
|
+
console.error('Send failed:', result.error.code);
|
|
495
|
+
console.error('Details:', result.error.message);
|
|
479
496
|
return;
|
|
480
497
|
}
|
|
481
498
|
|
|
@@ -483,6 +500,29 @@ console.log('Message sent with cryptographic identity');
|
|
|
483
500
|
console.log('Agent DID:', agent.did);
|
|
484
501
|
```
|
|
485
502
|
|
|
503
|
+
### Receiving Messages
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
import { Agent } from '@private.me/xbind';
|
|
507
|
+
|
|
508
|
+
// Assume you received an envelope from your transport layer
|
|
509
|
+
const envelope = // ... from transport
|
|
510
|
+
|
|
511
|
+
// Receive and decrypt the message
|
|
512
|
+
const result = await agent.receive(envelope);
|
|
513
|
+
|
|
514
|
+
if (!result.ok) {
|
|
515
|
+
console.error('Receive failed:', result.error.code);
|
|
516
|
+
console.error('Details:', result.error.message);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const message = result.value;
|
|
521
|
+
console.log('From:', message.sender);
|
|
522
|
+
console.log('Payload:', message.payload);
|
|
523
|
+
console.log('Scope:', message.scope);
|
|
524
|
+
```
|
|
525
|
+
|
|
486
526
|
### Step 3: Understand Your Free Tier
|
|
487
527
|
|
|
488
528
|
- **100,000 operations/month** (free tier)
|
|
@@ -493,300 +533,2332 @@ console.log('Agent DID:', agent.did);
|
|
|
493
533
|
**At 100K operations:** Email verification enforced (if not already verified)
|
|
494
534
|
**At 120K operations:** Hard cap reached. Upgrade to Pro for unlimited operations.
|
|
495
535
|
|
|
536
|
+
**Purchase & Billing:**
|
|
537
|
+
- Free tier: 100,000 operations/month (includes Vault Store access)
|
|
538
|
+
- Pro tier: Unlimited operations (usage-based billing)
|
|
539
|
+
- See [pricing](../../docs/pricing-reference.md) for details
|
|
540
|
+
- Purchase endpoint: `/api/full-control/share2` (authenticated)
|
|
541
|
+
|
|
496
542
|
**Prerequisites:** The `FULL_CONTROL_MASTER_KEY` environment variable is required for Share 2 operations in production but optional for basic testing. See [Environment Variables](#environment-variables) for setup.
|
|
497
543
|
|
|
498
544
|
**JITR (Just-in-Time Registration):** Agents created with `Agent.fromSeed()` automatically register with the trust registry on first use. No manual registration scripts required. Includes all encryption keys (Ed25519, X25519, ML-KEM, ML-DSA) for zero-config encrypted messaging.
|
|
499
545
|
|
|
500
546
|
[More examples](./docs/examples.md) • [Python examples](./python/README.md)
|
|
501
547
|
|
|
502
|
-
##
|
|
548
|
+
## Programmatic Quickstart
|
|
503
549
|
|
|
504
|
-
Complete
|
|
550
|
+
**Complete, copy-paste ready examples** tested in clean environments:
|
|
505
551
|
|
|
506
|
-
|
|
507
|
-
from private_me.xbind import Agent
|
|
552
|
+
### Example 1: Create Agent & Get DID
|
|
508
553
|
|
|
509
|
-
|
|
510
|
-
|
|
554
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
555
|
+
```typescript
|
|
556
|
+
import { Agent } from '@private.me/xbind';
|
|
511
557
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
'amount': 100,
|
|
515
|
-
'currency': 'usd',
|
|
516
|
-
'description': 'AI agent purchase'
|
|
517
|
-
})
|
|
558
|
+
// Create agent with automatic identity generation
|
|
559
|
+
const agent = await Agent.quickstart({ name: 'test-agent' });
|
|
518
560
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
print(f"Agent DID: {result['audit']['agent']}")
|
|
522
|
-
else:
|
|
523
|
-
print(f"Error: {result['error']['message']}")
|
|
561
|
+
console.log('Agent DID:', agent.did);
|
|
562
|
+
console.log('Agent name:', agent.name);
|
|
524
563
|
```
|
|
564
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
525
565
|
|
|
526
|
-
|
|
566
|
+
**Expected output:**
|
|
567
|
+
```
|
|
568
|
+
Agent DID: did:key:z6Mk...
|
|
569
|
+
Agent name: test-agent
|
|
570
|
+
```
|
|
527
571
|
|
|
528
|
-
|
|
572
|
+
### Example 2: Agent Identity Details
|
|
529
573
|
|
|
530
|
-
|
|
574
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
575
|
+
```typescript
|
|
576
|
+
import { Agent } from '@private.me/xbind';
|
|
531
577
|
|
|
532
|
-
|
|
578
|
+
// Create agent
|
|
579
|
+
const agent = await Agent.quickstart({ name: 'identity-test' });
|
|
533
580
|
|
|
534
|
-
|
|
535
|
-
|
|
581
|
+
// Access identity information
|
|
582
|
+
console.log('DID:', agent.did);
|
|
583
|
+
console.log('Has Ed25519 keys:', !!agent.identity.publicKey);
|
|
584
|
+
console.log('Has X25519 keys:', !!agent.identity.x25519PublicKey);
|
|
585
|
+
console.log('Has ML-KEM keys:', !!agent.identity.mlKemPublicKey);
|
|
586
|
+
```
|
|
587
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
536
588
|
|
|
537
|
-
|
|
589
|
+
**Expected output:**
|
|
590
|
+
```
|
|
591
|
+
DID: did:key:z6Mk...
|
|
592
|
+
Has Ed25519 keys: true
|
|
593
|
+
Has X25519 keys: true
|
|
594
|
+
Has ML-KEM keys: true
|
|
595
|
+
```
|
|
538
596
|
|
|
539
|
-
|
|
597
|
+
### Example 3: Registry & Transport Configuration
|
|
540
598
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
- **XorIDA split-channel delivery:** Information-theoretic threshold sharing (2-of-2, 2-of-3, 3-of-5)
|
|
545
|
-
- **Cryptographic request signing:** Ed25519 signatures prevent DID header forgery
|
|
546
|
-
- **Key rotation:** ML-KEM + X25519 rotation with fallback decryption (10 rotation history)
|
|
547
|
-
- **Proof-of-Possession:** Ed25519 signature verification prevents DID spoofing
|
|
548
|
-
- **Share-aware nonce deduplication:** Prevents replay attacks with composite keys
|
|
599
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
600
|
+
```typescript
|
|
601
|
+
import { Agent } from '@private.me/xbind';
|
|
549
602
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
- **React Native:** AsyncStorage, Buffer polyfills, crypto detection, iOS/Android platform support
|
|
553
|
-
- **Edge Runtime:** Cloudflare Workers, Vercel Edge, Deno Deploy, KV storage, <1MB optimization
|
|
554
|
-
- **Node.js:** Native crypto module, filesystem storage, full feature set
|
|
603
|
+
// Quickstart uses defaults: MemoryRegistry + HttpsTransport
|
|
604
|
+
const agent = await Agent.quickstart({ name: 'config-test' });
|
|
555
605
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
- **Telemetry:** Prometheus-compatible metrics (counters, histograms, gauges), operation latency tracking
|
|
606
|
+
// Check configuration
|
|
607
|
+
console.log('Registry type:', agent.registry.constructor.name);
|
|
608
|
+
console.log('Transports count:', agent.transports.length);
|
|
609
|
+
console.log('Transport type:', agent.transports[0].constructor.name);
|
|
610
|
+
```
|
|
611
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
563
612
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
613
|
+
**Expected output:**
|
|
614
|
+
```
|
|
615
|
+
Registry type: MemoryTrustRegistry
|
|
616
|
+
Transports count: 1
|
|
617
|
+
Transport type: HttpsTransportAdapter
|
|
618
|
+
```
|
|
569
619
|
|
|
570
|
-
|
|
571
|
-
-
|
|
572
|
-
-
|
|
573
|
-
-
|
|
574
|
-
- **Event emitters:** Type-safe events with 5 event types, priority execution, bubbling
|
|
575
|
-
- **Cancellation tokens:** AbortController integration with timeout support
|
|
576
|
-
- **Progress callbacks:** 4 specialized trackers (operation, transfer, share, encryption)
|
|
577
|
-
- **Retry strategies:** 4 strategies (exponential, linear, immediate, jittered) + circuit breaker integration
|
|
578
|
-
- **Request timeouts:** Per-operation config with inheritance and cancellation
|
|
579
|
-
- **Connection pooling:** 60-70% latency reduction with keep-alive and metrics
|
|
580
|
-
- **Serialization formats:** JSON/MessagePack/CBOR support with auto-detection and negotiation
|
|
581
|
-
- **Configuration validation:** Comprehensive validation with clear error messages
|
|
582
|
-
- **Debug mode:** Performance profiling, network/crypto tracing, state inspection
|
|
583
|
-
- **SDK version info:** Capability detection, deprecation warnings, compatibility checking
|
|
620
|
+
**Note:** These examples work immediately after `npm install @private.me/xbind`. Email verification is NOT required for basic agent creation and local operations. Verification is enforced when:
|
|
621
|
+
- Sending messages to other agents
|
|
622
|
+
- Exceeding 100K operations/month
|
|
623
|
+
- Accessing production features
|
|
584
624
|
|
|
585
|
-
|
|
586
|
-
- **Type safety:** `Result<T, E>` error handling with 96+ error codes
|
|
587
|
-
- **TypeScript strict mode:** Zero type errors, complete type coverage
|
|
588
|
-
- **Python SDK:** Complete bindings for identity, messaging, and encryption
|
|
589
|
-
- **User-friendly errors:** 75+ errors with actionable recovery hints
|
|
590
|
-
- **Complete API docs:** 2,587 lines with 50+ usage examples
|
|
591
|
-
- **CHANGELOG generation:** Automated Keep a Changelog format with semver discipline
|
|
592
|
-
- **Dependency audit:** Weekly Dependabot scans, 5-layer vulnerability scanning
|
|
593
|
-
- **Crypto license compliance:** Automated MIT/MIT-0 validation with GPL/AGPL detection
|
|
625
|
+
[More examples](./docs/examples.md) • [Python examples](./python/README.md)
|
|
594
626
|
|
|
595
|
-
|
|
596
|
-
- **Version negotiation:** Explicit SDK version fields prevent "404 Route not found" in mixed fleets
|
|
597
|
-
- **Multi-device session resume:** AES-256-GCM encrypted session state sync with PBKDF2 (100k iterations)
|
|
598
|
-
- **Encrypted backup/restore:** Password-protected identity export with PBKDF2-SHA256 (310k iterations)
|
|
599
|
-
- **Offline sync:** Message queueing for offline devices (max 1000 messages, 24-hour TTL)
|
|
600
|
-
- **Registry expiration:** TTL support with cleanup (default 7 days)
|
|
601
|
-
- **Server-side spending limits:** Redis-backed enforcement with deployment-level aggregation
|
|
602
|
-
- **Rate limiting:** Three-tier protection (per-DID, per-IP, global)
|
|
603
|
-
- **Full Control IP protection:** Store Front (npm) + Vault Store (EC2) with payment verification
|
|
627
|
+
## Post-Quantum Cryptography & Envelopes
|
|
604
628
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
-
|
|
610
|
-
|
|
629
|
+
**Complete examples for ML-DSA signatures, key export, and envelope operations:**
|
|
630
|
+
|
|
631
|
+
### Example 4: ML-DSA-65 Signing and Verification
|
|
632
|
+
|
|
633
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
634
|
+
```typescript
|
|
635
|
+
import { Agent, signMlDsa65, verifyMlDsa65 } from '@private.me/xbind';
|
|
636
|
+
|
|
637
|
+
// Create agent with post-quantum signatures enabled
|
|
638
|
+
const agent = await Agent.quickstart({
|
|
639
|
+
name: 'pq-test',
|
|
640
|
+
postQuantumSig: true
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// Sign data with ML-DSA-65
|
|
644
|
+
const data = new TextEncoder().encode('Critical transaction data');
|
|
645
|
+
const mlDsaSecretKey = agent.identity.mlDsaSecretKey;
|
|
646
|
+
|
|
647
|
+
if (!mlDsaSecretKey) {
|
|
648
|
+
throw new Error('ML-DSA keys not available');
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const signResult = await signMlDsa65(mlDsaSecretKey, data);
|
|
652
|
+
if (!signResult.ok) {
|
|
653
|
+
throw new Error(`Signing failed: ${signResult.error}`);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
console.log('ML-DSA-65 signature length:', signResult.value.length); // 3309 bytes
|
|
657
|
+
|
|
658
|
+
// Verify signature
|
|
659
|
+
const mlDsaPublicKey = agent.identity.mlDsaPublicKey!;
|
|
660
|
+
const verifyResult = await verifyMlDsa65(mlDsaPublicKey, signResult.value, data);
|
|
661
|
+
|
|
662
|
+
if (verifyResult.ok && verifyResult.value) {
|
|
663
|
+
console.log('Post-quantum signature verified successfully');
|
|
664
|
+
} else {
|
|
665
|
+
console.error('Signature verification failed');
|
|
666
|
+
}
|
|
667
|
+
```
|
|
668
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
669
|
+
|
|
670
|
+
**ML-DSA-65 size constants:**
|
|
671
|
+
|
|
672
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
673
|
+
```typescript
|
|
674
|
+
import { ML_DSA65_SIG_BYTES, ML_DSA65_PK_BYTES, ML_DSA65_SK_BYTES } from '@private.me/xbind';
|
|
675
|
+
|
|
676
|
+
console.log('ML-DSA-65 signature size:', ML_DSA65_SIG_BYTES); // 3309 bytes
|
|
677
|
+
console.log('ML-DSA-65 public key size:', ML_DSA65_PK_BYTES); // 1952 bytes
|
|
678
|
+
console.log('ML-DSA-65 secret key size:', ML_DSA65_SK_BYTES); // 4032 bytes
|
|
679
|
+
```
|
|
680
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
681
|
+
|
|
682
|
+
**Expected output:**
|
|
683
|
+
```
|
|
684
|
+
ML-DSA-65 signature length: 3309
|
|
685
|
+
Post-quantum signature verified successfully
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### Example 5: Export Post-Quantum Keys
|
|
689
|
+
|
|
690
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
691
|
+
```typescript
|
|
692
|
+
import {
|
|
693
|
+
Agent,
|
|
694
|
+
exportMlDsaSecretKey,
|
|
695
|
+
exportMlDsaPublicKey,
|
|
696
|
+
exportMlKemSecretKey,
|
|
697
|
+
exportMlKemPublicKey
|
|
698
|
+
} from '@private.me/xbind';
|
|
699
|
+
|
|
700
|
+
// Create agent with post-quantum cryptography
|
|
701
|
+
const agent = await Agent.quickstart({
|
|
702
|
+
name: 'export-test',
|
|
703
|
+
postQuantumSig: true
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
// Export ML-DSA keys (signatures)
|
|
707
|
+
const mlDsaSecret = exportMlDsaSecretKey(agent.identity);
|
|
708
|
+
const mlDsaPublic = exportMlDsaPublicKey(agent.identity);
|
|
709
|
+
|
|
710
|
+
console.log('ML-DSA-65 secret key:', mlDsaSecret ? `${mlDsaSecret.length} bytes` : 'N/A');
|
|
711
|
+
console.log('ML-DSA-65 public key:', mlDsaPublic ? `${mlDsaPublic.length} bytes` : 'N/A');
|
|
712
|
+
|
|
713
|
+
// Export ML-KEM keys (encryption)
|
|
714
|
+
const mlKemSecret = exportMlKemSecretKey(agent.identity);
|
|
715
|
+
const mlKemPublic = exportMlKemPublicKey(agent.identity);
|
|
716
|
+
|
|
717
|
+
console.log('ML-KEM-768 secret key:', mlKemSecret ? `${mlKemSecret.length} bytes` : 'N/A');
|
|
718
|
+
console.log('ML-KEM-768 public key:', mlKemPublic ? `${mlKemPublic.length} bytes` : 'N/A');
|
|
719
|
+
```
|
|
720
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
721
|
+
|
|
722
|
+
**Expected output:**
|
|
723
|
+
```
|
|
724
|
+
ML-DSA-65 secret key: 32 bytes
|
|
725
|
+
ML-DSA-65 public key: 1952 bytes
|
|
726
|
+
ML-KEM-768 secret key: 2400 bytes
|
|
727
|
+
ML-KEM-768 public key: 1184 bytes
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### Example 6: Key Rotation with Backward Compatibility
|
|
731
|
+
|
|
732
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
733
|
+
```typescript
|
|
734
|
+
import { Agent, rotateKeys } from '@private.me/xbind';
|
|
735
|
+
|
|
736
|
+
// Create agent
|
|
737
|
+
const agent = await Agent.quickstart({ name: 'rotation-test' });
|
|
738
|
+
|
|
739
|
+
// Store original X25519 public key
|
|
740
|
+
const originalX25519 = Buffer.from(agent.identity.rawX25519PublicKey).toString('hex');
|
|
741
|
+
|
|
742
|
+
// Rotate encryption keys (X25519 + ML-KEM)
|
|
743
|
+
const rotatedResult = await rotateKeys(agent.identity);
|
|
744
|
+
|
|
745
|
+
if (!rotatedResult.ok) {
|
|
746
|
+
throw new Error(`Key rotation failed: ${rotatedResult.error}`);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const rotatedIdentity = rotatedResult.value;
|
|
750
|
+
|
|
751
|
+
// New keys are active
|
|
752
|
+
const newX25519 = Buffer.from(rotatedIdentity.rawX25519PublicKey).toString('hex');
|
|
753
|
+
console.log('Keys rotated successfully');
|
|
754
|
+
console.log('Original X25519:', originalX25519.substring(0, 16) + '...');
|
|
755
|
+
console.log('New X25519:', newX25519.substring(0, 16) + '...');
|
|
756
|
+
|
|
757
|
+
// Old keys preserved for decryption
|
|
758
|
+
console.log('Rotation history:', rotatedIdentity.rotatedKeys?.length ?? 0);
|
|
759
|
+
console.log('DID unchanged:', agent.identity.did === rotatedIdentity.did);
|
|
760
|
+
```
|
|
761
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
762
|
+
|
|
763
|
+
**Expected output:**
|
|
764
|
+
```
|
|
765
|
+
Keys rotated successfully
|
|
766
|
+
Original X25519: a1b2c3d4e5f6g7h8...
|
|
767
|
+
New X25519: 9i0j1k2l3m4n5o6p...
|
|
768
|
+
Rotation history: 1
|
|
769
|
+
DID unchanged: true
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
### Example 7: Create V1 Envelope (Classical Crypto)
|
|
773
|
+
|
|
774
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
775
|
+
```typescript
|
|
776
|
+
import { Agent, createEnvelope, generateSharedKey } from '@private.me/xbind';
|
|
777
|
+
|
|
778
|
+
// Generate shared AES-256-GCM key
|
|
779
|
+
const sharedKey = await generateSharedKey();
|
|
780
|
+
|
|
781
|
+
// Create agent
|
|
782
|
+
const agent = await Agent.quickstart({ name: 'envelope-test' });
|
|
783
|
+
|
|
784
|
+
// Create encrypted envelope
|
|
785
|
+
const plaintext = new TextEncoder().encode(JSON.stringify({
|
|
786
|
+
action: 'transfer',
|
|
787
|
+
amount: 1000
|
|
788
|
+
}));
|
|
789
|
+
|
|
790
|
+
const envelopeResult = await createEnvelope({
|
|
791
|
+
senderDid: agent.did,
|
|
792
|
+
recipientDid: 'did:key:z6MkRecipient...',
|
|
793
|
+
scope: 'billing:write',
|
|
794
|
+
plaintext,
|
|
795
|
+
privateKey: agent.identity.privateKey,
|
|
796
|
+
sharedKey
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
if (!envelopeResult.ok) {
|
|
800
|
+
throw new Error(`Envelope creation failed: ${envelopeResult.error}`);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const envelope = envelopeResult.value;
|
|
804
|
+
console.log('Envelope version:', envelope.v);
|
|
805
|
+
console.log('Algorithm:', envelope.alg);
|
|
806
|
+
console.log('Signature length:', envelope.signature.length);
|
|
807
|
+
console.log('Encrypted payload:', envelope.payload.substring(0, 32) + '...');
|
|
808
|
+
```
|
|
809
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
810
|
+
|
|
811
|
+
**Expected output:**
|
|
812
|
+
```
|
|
813
|
+
Envelope version: 1
|
|
814
|
+
Algorithm: Ed25519
|
|
815
|
+
Signature length: 88
|
|
816
|
+
Encrypted payload: aGVsbG8gd29ybGQgdGhpcyBpcyBhIHRl...
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### Example 8: Create V2 Envelope (Hybrid KEM)
|
|
820
|
+
|
|
821
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
822
|
+
```typescript
|
|
823
|
+
import { Agent, createEnvelopeV2, generateSharedKey } from '@private.me/xbind';
|
|
824
|
+
import { createMlKem768 } from 'mlkem';
|
|
825
|
+
|
|
826
|
+
// Generate shared AES-256-GCM key
|
|
827
|
+
const sharedKey = await generateSharedKey();
|
|
828
|
+
|
|
829
|
+
// Generate ephemeral X25519 keypair
|
|
830
|
+
const x25519Pair = await crypto.subtle.generateKey(
|
|
831
|
+
{ name: 'X25519' },
|
|
832
|
+
true,
|
|
833
|
+
['deriveBits']
|
|
834
|
+
);
|
|
835
|
+
const ephemeralPub = new Uint8Array(
|
|
836
|
+
await crypto.subtle.exportKey('raw', x25519Pair.publicKey)
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
// Generate ML-KEM-768 ciphertext (mocked for example)
|
|
840
|
+
const mlkem = await createMlKem768();
|
|
841
|
+
const recipientMlKemPub = mlkem.generateKeyPair()[0]; // Mock recipient public key
|
|
842
|
+
const [kemCiphertext] = mlkem.encap(recipientMlKemPub);
|
|
843
|
+
|
|
844
|
+
const agent = await Agent.quickstart({ name: 'v2-envelope-test' });
|
|
845
|
+
|
|
846
|
+
const envelope = await createEnvelopeV2({
|
|
847
|
+
senderDid: agent.did,
|
|
848
|
+
recipientDid: 'did:key:z6MkRecipient...',
|
|
849
|
+
scope: 'messaging',
|
|
850
|
+
plaintext: new TextEncoder().encode('Hybrid KEM message'),
|
|
851
|
+
privateKey: agent.identity.privateKey,
|
|
852
|
+
sharedKey,
|
|
853
|
+
ephemeralPublicKey: ephemeralPub,
|
|
854
|
+
kemCiphertext
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
if (envelope.ok) {
|
|
858
|
+
console.log('Envelope version:', envelope.value.v);
|
|
859
|
+
console.log('KEM mode:', envelope.value.kem);
|
|
860
|
+
console.log('ML-KEM ciphertext length:', envelope.value.kemCiphertext.length);
|
|
861
|
+
}
|
|
862
|
+
```
|
|
863
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
864
|
+
|
|
865
|
+
**Expected output:**
|
|
866
|
+
```
|
|
867
|
+
Envelope version: 2
|
|
868
|
+
KEM mode: X25519-MLKEM768
|
|
869
|
+
ML-KEM ciphertext length: 1452
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
### Example 9: Create V3 Envelope (Dual Signatures)
|
|
873
|
+
|
|
874
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
875
|
+
```typescript
|
|
876
|
+
import { Agent, createEnvelopeV3, generateSharedKey } from '@private.me/xbind';
|
|
877
|
+
import { createMlKem768 } from 'mlkem';
|
|
878
|
+
|
|
879
|
+
// Create agent with ML-DSA enabled
|
|
880
|
+
const agent = await Agent.quickstart({
|
|
881
|
+
name: 'v3-envelope-test',
|
|
882
|
+
postQuantumSig: true
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
if (!agent.identity.mlDsaSecretKey) {
|
|
886
|
+
throw new Error('ML-DSA keys required for V3 envelopes');
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Generate shared key and KEM materials
|
|
890
|
+
const sharedKey = await generateSharedKey();
|
|
891
|
+
const x25519Pair = await crypto.subtle.generateKey(
|
|
892
|
+
{ name: 'X25519' },
|
|
893
|
+
true,
|
|
894
|
+
['deriveBits']
|
|
895
|
+
);
|
|
896
|
+
const ephemeralPub = new Uint8Array(
|
|
897
|
+
await crypto.subtle.exportKey('raw', x25519Pair.publicKey)
|
|
898
|
+
);
|
|
899
|
+
|
|
900
|
+
const mlkem = await createMlKem768();
|
|
901
|
+
const recipientMlKemPub = mlkem.generateKeyPair()[0];
|
|
902
|
+
const [kemCiphertext] = mlkem.encap(recipientMlKemPub);
|
|
903
|
+
|
|
904
|
+
const envelope = await createEnvelopeV3({
|
|
905
|
+
senderDid: agent.did,
|
|
906
|
+
recipientDid: 'did:key:z6MkRecipient...',
|
|
907
|
+
scope: 'high-security',
|
|
908
|
+
plaintext: new TextEncoder().encode('Dual-signature message'),
|
|
909
|
+
privateKey: agent.identity.privateKey,
|
|
910
|
+
sharedKey,
|
|
911
|
+
ephemeralPublicKey: ephemeralPub,
|
|
912
|
+
kemCiphertext,
|
|
913
|
+
mlDsaSecretKey: agent.identity.mlDsaSecretKey
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
if (envelope.ok) {
|
|
917
|
+
console.log('Envelope version:', envelope.value.v);
|
|
918
|
+
console.log('Signature scheme:', envelope.value.sig);
|
|
919
|
+
console.log('Ed25519 signature:', envelope.value.signature.substring(0, 16) + '...');
|
|
920
|
+
console.log('ML-DSA signature:', envelope.value.pqSignature.substring(0, 16) + '...');
|
|
921
|
+
}
|
|
922
|
+
```
|
|
923
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
924
|
+
|
|
925
|
+
**Expected output:**
|
|
926
|
+
```
|
|
927
|
+
Envelope version: 3
|
|
928
|
+
Signature scheme: Ed25519+MLDSA65
|
|
929
|
+
Ed25519 signature: SGVsbG8gd29ybGQ...
|
|
930
|
+
ML-DSA signature: cG9zdC1xdWFudHV...
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
### Example 10: Create V4 Envelope (Xchange Mode)
|
|
934
|
+
|
|
935
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
936
|
+
```typescript
|
|
937
|
+
import { Agent, createEnvelopeV4 } from '@private.me/xbind';
|
|
938
|
+
|
|
939
|
+
const agent = await Agent.quickstart({ name: 'xchange-test' });
|
|
940
|
+
|
|
941
|
+
// Xchange mode: Share data is the payload (no encryption layer)
|
|
942
|
+
const shareData = new TextEncoder().encode('XorIDA share data');
|
|
943
|
+
|
|
944
|
+
const envelope = await createEnvelopeV4({
|
|
945
|
+
senderDid: agent.did,
|
|
946
|
+
recipientDid: 'did:key:z6MkRecipient...',
|
|
947
|
+
scope: 'xchange',
|
|
948
|
+
shareData,
|
|
949
|
+
privateKey: agent.identity.privateKey,
|
|
950
|
+
shareIndex: 0,
|
|
951
|
+
shareTotal: 3,
|
|
952
|
+
shareThreshold: 2,
|
|
953
|
+
shareGroupId: 'group-uuid-1234',
|
|
954
|
+
shareHmacKey: 'base64-hmac-key',
|
|
955
|
+
shareHmacSig: 'base64-hmac-sig'
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
if (envelope.ok) {
|
|
959
|
+
console.log('Envelope version:', envelope.value.v);
|
|
960
|
+
console.log('KEM mode:', envelope.value.kem);
|
|
961
|
+
console.log('Share index:', envelope.value.shareIndex);
|
|
962
|
+
console.log('Threshold:', `${envelope.value.shareThreshold}-of-${envelope.value.shareTotal}`);
|
|
963
|
+
}
|
|
964
|
+
```
|
|
965
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
966
|
+
|
|
967
|
+
**Expected output:**
|
|
968
|
+
```
|
|
969
|
+
Envelope version: 4
|
|
970
|
+
KEM mode: Xchange
|
|
971
|
+
Share index: 0
|
|
972
|
+
Threshold: 2-of-3
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
### Example 11: Decrypt Envelope Payload
|
|
976
|
+
|
|
977
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
978
|
+
```typescript
|
|
979
|
+
import {
|
|
980
|
+
Agent,
|
|
981
|
+
createEnvelope,
|
|
982
|
+
decryptPayload,
|
|
983
|
+
generateSharedKey
|
|
984
|
+
} from '@private.me/xbind';
|
|
985
|
+
|
|
986
|
+
// Create and encrypt envelope
|
|
987
|
+
const sharedKey = await generateSharedKey();
|
|
988
|
+
const agent = await Agent.quickstart({ name: 'decrypt-test' });
|
|
989
|
+
|
|
990
|
+
const originalMessage = 'Secret payment details';
|
|
991
|
+
const plaintext = new TextEncoder().encode(originalMessage);
|
|
992
|
+
|
|
993
|
+
const envelopeResult = await createEnvelope({
|
|
994
|
+
senderDid: agent.did,
|
|
995
|
+
recipientDid: 'did:key:z6MkRecipient...',
|
|
996
|
+
scope: 'payments',
|
|
997
|
+
plaintext,
|
|
998
|
+
privateKey: agent.identity.privateKey,
|
|
999
|
+
sharedKey
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
if (!envelopeResult.ok) {
|
|
1003
|
+
throw new Error('Envelope creation failed');
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// Decrypt the payload
|
|
1007
|
+
const decryptResult = await decryptPayload(envelopeResult.value, sharedKey);
|
|
1008
|
+
|
|
1009
|
+
if (!decryptResult.ok) {
|
|
1010
|
+
throw new Error(`Decryption failed: ${decryptResult.error}`);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const decryptedMessage = new TextDecoder().decode(decryptResult.value);
|
|
1014
|
+
console.log('Decrypted:', decryptedMessage);
|
|
1015
|
+
console.log('Match:', decryptedMessage === originalMessage);
|
|
1016
|
+
```
|
|
1017
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1018
|
+
|
|
1019
|
+
**Expected output:**
|
|
1020
|
+
```
|
|
1021
|
+
Decrypted: Secret payment details
|
|
1022
|
+
Match: true
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
### Example 12: Serialize and Deserialize Envelope
|
|
1026
|
+
|
|
1027
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1028
|
+
```typescript
|
|
1029
|
+
import {
|
|
1030
|
+
Agent,
|
|
1031
|
+
createEnvelope,
|
|
1032
|
+
serializeEnvelope,
|
|
1033
|
+
deserializeEnvelope,
|
|
1034
|
+
generateSharedKey
|
|
1035
|
+
} from '@private.me/xbind';
|
|
1036
|
+
|
|
1037
|
+
// Create envelope
|
|
1038
|
+
const sharedKey = await generateSharedKey();
|
|
1039
|
+
const agent = await Agent.quickstart({ name: 'serialize-test' });
|
|
1040
|
+
|
|
1041
|
+
const envelope = await createEnvelope({
|
|
1042
|
+
senderDid: agent.did,
|
|
1043
|
+
recipientDid: 'did:key:z6MkRecipient...',
|
|
1044
|
+
scope: 'test',
|
|
1045
|
+
plaintext: new TextEncoder().encode('Test message'),
|
|
1046
|
+
privateKey: agent.identity.privateKey,
|
|
1047
|
+
sharedKey
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
if (!envelope.ok) {
|
|
1051
|
+
throw new Error('Envelope creation failed');
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Serialize to bytes
|
|
1055
|
+
const serialized = serializeEnvelope(envelope.value);
|
|
1056
|
+
console.log('Serialized length:', serialized.length, 'bytes');
|
|
1057
|
+
|
|
1058
|
+
// Deserialize back to envelope
|
|
1059
|
+
const deserialized = deserializeEnvelope(serialized);
|
|
1060
|
+
|
|
1061
|
+
if (deserialized.ok) {
|
|
1062
|
+
console.log('Deserialized version:', deserialized.value.v);
|
|
1063
|
+
console.log('Sender DID:', deserialized.value.sender.substring(0, 20) + '...');
|
|
1064
|
+
console.log('Round-trip successful');
|
|
1065
|
+
}
|
|
1066
|
+
```
|
|
1067
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1068
|
+
|
|
1069
|
+
**Expected output:**
|
|
1070
|
+
```
|
|
1071
|
+
Serialized length: 512 bytes
|
|
1072
|
+
Deserialized version: 1
|
|
1073
|
+
Sender DID: did:key:z6Mk...
|
|
1074
|
+
Round-trip successful
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
### Example 13: Validate Envelope Structure
|
|
1078
|
+
|
|
1079
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1080
|
+
```typescript
|
|
1081
|
+
import { validateEnvelope } from '@private.me/xbind';
|
|
1082
|
+
|
|
1083
|
+
// Valid envelope structure
|
|
1084
|
+
const validEnvelope = {
|
|
1085
|
+
v: 1,
|
|
1086
|
+
alg: 'Ed25519',
|
|
1087
|
+
sender: 'did:key:z6MkSender...',
|
|
1088
|
+
recipient: 'did:key:z6MkRecipient...',
|
|
1089
|
+
timestamp: Date.now(),
|
|
1090
|
+
nonce: 'YmFzZTY0LW5vbmNl',
|
|
1091
|
+
scope: 'test',
|
|
1092
|
+
payload: 'ZW5jcnlwdGVkLXBheWxvYWQ=',
|
|
1093
|
+
signature: 'c2lnbmF0dXJlLWRhdGE='
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
const validation = validateEnvelope(validEnvelope);
|
|
1097
|
+
|
|
1098
|
+
if (validation.ok) {
|
|
1099
|
+
console.log('Envelope valid');
|
|
1100
|
+
console.log('Version:', validation.value.v);
|
|
1101
|
+
console.log('Algorithm:', validation.value.alg);
|
|
1102
|
+
} else {
|
|
1103
|
+
console.error('Validation error:', validation.error);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// Invalid envelope (missing required field)
|
|
1107
|
+
const invalidEnvelope = {
|
|
1108
|
+
v: 1,
|
|
1109
|
+
alg: 'Ed25519',
|
|
1110
|
+
sender: 'did:key:z6MkSender...'
|
|
1111
|
+
// Missing recipient, timestamp, nonce, etc.
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
const invalidValidation = validateEnvelope(invalidEnvelope);
|
|
1115
|
+
console.log('Invalid envelope error:', invalidValidation.ok ? 'N/A' : invalidValidation.error);
|
|
1116
|
+
```
|
|
1117
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1118
|
+
|
|
1119
|
+
**Expected output:**
|
|
1120
|
+
```
|
|
1121
|
+
Envelope valid
|
|
1122
|
+
Version: 1
|
|
1123
|
+
Algorithm: Ed25519
|
|
1124
|
+
Invalid envelope error: INVALID_FIELDS
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
## Identity & CLI Operations
|
|
1129
|
+
|
|
1130
|
+
### CLI Initialization
|
|
1131
|
+
|
|
1132
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1133
|
+
```typescript
|
|
1134
|
+
import { initCommand } from '@private.me/xbind';
|
|
1135
|
+
|
|
1136
|
+
// Initialize xBind project with invite code
|
|
1137
|
+
await initCommand({
|
|
1138
|
+
invite: 'https://xbind.to/invite/XBD-abc123',
|
|
1139
|
+
name: 'my-xbind-app',
|
|
1140
|
+
runtime: 'node-typescript',
|
|
1141
|
+
yes: true // Skip interactive prompts
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
// Or use CLI directly:
|
|
1145
|
+
// npx xbind-init --invite XBD-abc123 --name my-app --runtime node-typescript --yes
|
|
1146
|
+
```
|
|
1147
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1148
|
+
|
|
1149
|
+
### Programmatic CLI Entry
|
|
1150
|
+
|
|
1151
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1152
|
+
```typescript
|
|
1153
|
+
import { cliMain } from '@private.me/xbind';
|
|
1154
|
+
|
|
1155
|
+
// Run xBind CLI programmatically (useful for testing or automation)
|
|
1156
|
+
const exitCode = await cliMain(['setup', '--email', 'agent@example.com', '--yes']);
|
|
1157
|
+
|
|
1158
|
+
if (exitCode === 0) {
|
|
1159
|
+
console.log('CLI setup completed successfully');
|
|
1160
|
+
} else {
|
|
1161
|
+
console.error(`CLI exited with code ${exitCode}`);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// Can also be used to test CLI commands without spawning a child process
|
|
1165
|
+
```
|
|
1166
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1167
|
+
|
|
1168
|
+
### DID Conversions
|
|
1169
|
+
|
|
1170
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1171
|
+
```typescript
|
|
1172
|
+
import { generateIdentity, publicKeyToDid, didToPublicKeyBytes } from '@private.me/xbind';
|
|
1173
|
+
|
|
1174
|
+
// Generate identity and extract DID
|
|
1175
|
+
const result = await generateIdentity();
|
|
1176
|
+
if (!result.ok) throw new Error(result.error);
|
|
1177
|
+
|
|
1178
|
+
const identity = result.value;
|
|
1179
|
+
console.log('DID:', identity.did); // did:key:z6Mk...
|
|
1180
|
+
|
|
1181
|
+
// Convert public key to DID
|
|
1182
|
+
const did = publicKeyToDid(identity.rawPublicKey);
|
|
1183
|
+
console.log('Derived DID:', did);
|
|
1184
|
+
|
|
1185
|
+
// Convert DID back to public key bytes
|
|
1186
|
+
const keyResult = didToPublicKeyBytes(did);
|
|
1187
|
+
if (!keyResult.ok) throw new Error(keyResult.error);
|
|
1188
|
+
|
|
1189
|
+
console.log('Public key bytes:', keyResult.value); // Uint8Array(32)
|
|
1190
|
+
```
|
|
1191
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1192
|
+
|
|
1193
|
+
### PKCS8 Key Export
|
|
1194
|
+
|
|
1195
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1196
|
+
```typescript
|
|
1197
|
+
import { generateIdentity, exportPKCS8, exportX25519PKCS8 } from '@private.me/xbind';
|
|
1198
|
+
|
|
1199
|
+
const result = await generateIdentity();
|
|
1200
|
+
if (!result.ok) throw new Error(result.error);
|
|
1201
|
+
|
|
1202
|
+
const identity = result.value;
|
|
1203
|
+
|
|
1204
|
+
// Export Ed25519 private key as PKCS8 (for persistent storage)
|
|
1205
|
+
const ed25519Result = await exportPKCS8(identity.privateKey);
|
|
1206
|
+
if (!ed25519Result.ok) throw new Error(ed25519Result.error);
|
|
1207
|
+
|
|
1208
|
+
console.log('Ed25519 PKCS8:', ed25519Result.value); // Uint8Array(48)
|
|
1209
|
+
|
|
1210
|
+
// Export X25519 private key as PKCS8
|
|
1211
|
+
const x25519Result = await exportX25519PKCS8(identity.x25519PrivateKey);
|
|
1212
|
+
if (!x25519Result.ok) throw new Error(x25519Result.error);
|
|
1213
|
+
|
|
1214
|
+
console.log('X25519 PKCS8:', x25519Result.value); // Uint8Array(48)
|
|
1215
|
+
```
|
|
1216
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1217
|
+
|
|
1218
|
+
### PKCS8 Key Import
|
|
1219
|
+
|
|
1220
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1221
|
+
```typescript
|
|
1222
|
+
import { exportPKCS8, exportX25519PKCS8, importFromPKCS8, importIdentity } from '@private.me/xbind';
|
|
1223
|
+
|
|
1224
|
+
// Assume we have PKCS8 bytes from previous export
|
|
1225
|
+
const ed25519Pkcs8 = new Uint8Array(48); // From exportPKCS8()
|
|
1226
|
+
const x25519Pkcs8 = new Uint8Array(48); // From exportX25519PKCS8()
|
|
1227
|
+
|
|
1228
|
+
// Import Ed25519 identity (generates new X25519 keypair)
|
|
1229
|
+
const simpleResult = await importFromPKCS8(ed25519Pkcs8);
|
|
1230
|
+
if (!simpleResult.ok) throw new Error(simpleResult.error);
|
|
1231
|
+
|
|
1232
|
+
console.log('Restored DID:', simpleResult.value.did);
|
|
1233
|
+
|
|
1234
|
+
// Import full identity (preserves both Ed25519 + X25519 keys)
|
|
1235
|
+
const fullResult = await importIdentity(ed25519Pkcs8, x25519Pkcs8);
|
|
1236
|
+
if (!fullResult.ok) throw new Error(fullResult.error);
|
|
1237
|
+
|
|
1238
|
+
console.log('Full identity restored:', fullResult.value.did);
|
|
1239
|
+
console.log('Has X25519 keys:', !!fullResult.value.x25519PublicKey);
|
|
1240
|
+
```
|
|
1241
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1242
|
+
|
|
1243
|
+
### Deterministic Identity from Seed
|
|
1244
|
+
|
|
1245
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1246
|
+
```typescript
|
|
1247
|
+
import { identityFromSeed } from '@private.me/xbind';
|
|
1248
|
+
|
|
1249
|
+
// Generate 32-byte seed (high-entropy random bytes)
|
|
1250
|
+
const seed = crypto.getRandomValues(new Uint8Array(32));
|
|
1251
|
+
|
|
1252
|
+
// Derive deterministic identity (same seed = same DID)
|
|
1253
|
+
const result = await identityFromSeed(seed);
|
|
1254
|
+
if (!result.ok) throw new Error(result.error);
|
|
1255
|
+
|
|
1256
|
+
console.log('DID:', result.value.did);
|
|
1257
|
+
console.log('Has Ed25519 keys:', !!result.value.publicKey);
|
|
1258
|
+
console.log('Has X25519 keys:', !!result.value.x25519PublicKey);
|
|
1259
|
+
console.log('Has ML-KEM keys:', !!result.value.mlKemPublicKey);
|
|
1260
|
+
|
|
1261
|
+
// Same seed produces identical DID
|
|
1262
|
+
const result2 = await identityFromSeed(seed);
|
|
1263
|
+
if (!result2.ok) throw new Error(result2.error);
|
|
1264
|
+
|
|
1265
|
+
console.log('DIDs match:', result.value.did === result2.value.did); // true
|
|
1266
|
+
```
|
|
1267
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1268
|
+
|
|
1269
|
+
### Raw Key Extraction
|
|
1270
|
+
|
|
1271
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1272
|
+
```typescript
|
|
1273
|
+
import { exportPKCS8, exportX25519PKCS8, extractRawEd25519, extractRawX25519 } from '@private.me/xbind';
|
|
1274
|
+
import { generateIdentity } from '@private.me/xbind';
|
|
1275
|
+
|
|
1276
|
+
const identity = (await generateIdentity()).value!;
|
|
1277
|
+
|
|
1278
|
+
// Export PKCS8 format
|
|
1279
|
+
const ed25519Pkcs8 = (await exportPKCS8(identity.privateKey)).value!;
|
|
1280
|
+
const x25519Pkcs8 = (await exportX25519PKCS8(identity.x25519PrivateKey)).value!;
|
|
1281
|
+
|
|
1282
|
+
// Extract raw 32-byte private keys
|
|
1283
|
+
const ed25519Raw = extractRawEd25519(ed25519Pkcs8);
|
|
1284
|
+
if (!ed25519Raw.ok) throw new Error(ed25519Raw.error);
|
|
1285
|
+
|
|
1286
|
+
console.log('Ed25519 raw key:', ed25519Raw.value); // Uint8Array(32)
|
|
1287
|
+
|
|
1288
|
+
const x25519Raw = extractRawX25519(x25519Pkcs8);
|
|
1289
|
+
if (!x25519Raw.ok) throw new Error(x25519Raw.error);
|
|
1290
|
+
|
|
1291
|
+
console.log('X25519 raw key:', x25519Raw.value); // Uint8Array(32)
|
|
1292
|
+
```
|
|
1293
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1294
|
+
|
|
1295
|
+
### ML-KEM Key Export
|
|
1296
|
+
|
|
1297
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1298
|
+
```typescript
|
|
1299
|
+
import { generateIdentity, exportMlKemSecretKey, exportMlKemPublicKey } from '@private.me/xbind';
|
|
1300
|
+
|
|
1301
|
+
const result = await generateIdentity();
|
|
1302
|
+
if (!result.ok) throw new Error(result.error);
|
|
1303
|
+
|
|
1304
|
+
const identity = result.value;
|
|
1305
|
+
|
|
1306
|
+
// Export ML-KEM-768 secret key (2400 bytes)
|
|
1307
|
+
const secretKey = exportMlKemSecretKey(identity);
|
|
1308
|
+
if (secretKey) {
|
|
1309
|
+
console.log('ML-KEM secret key size:', secretKey.length); // 2400
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// Export ML-KEM-768 public key (1184 bytes)
|
|
1313
|
+
const publicKey = exportMlKemPublicKey(identity);
|
|
1314
|
+
if (publicKey) {
|
|
1315
|
+
console.log('ML-KEM public key size:', publicKey.length); // 1184
|
|
1316
|
+
}
|
|
1317
|
+
```
|
|
1318
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1319
|
+
|
|
1320
|
+
## Advanced Networking & Resilience
|
|
1321
|
+
|
|
1322
|
+
xBind provides production-grade retry strategies, replay prevention, and circuit breakers for fault-tolerant distributed systems.
|
|
1323
|
+
|
|
1324
|
+
### Replay Prevention with MemoryNonceStore
|
|
1325
|
+
|
|
1326
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1327
|
+
```typescript
|
|
1328
|
+
import { MemoryNonceStore } from '@private.me/xbind';
|
|
1329
|
+
|
|
1330
|
+
// Create in-memory nonce store (10-minute TTL, 60-second cleanup)
|
|
1331
|
+
const nonceStore = new MemoryNonceStore({
|
|
1332
|
+
ttlMs: 10 * 60 * 1000,
|
|
1333
|
+
cleanupIntervalMs: 60 * 1000,
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
// Check nonce freshness (returns true if new, false if duplicate)
|
|
1337
|
+
const nonce1 = 'unique-nonce-abc123';
|
|
1338
|
+
const senderDid = 'did:key:z6Mk...';
|
|
1339
|
+
|
|
1340
|
+
const isNewNonce = await nonceStore.check(nonce1, senderDid);
|
|
1341
|
+
console.log('Nonce accepted:', isNewNonce); // true (first use)
|
|
1342
|
+
|
|
1343
|
+
const isDuplicate = await nonceStore.check(nonce1, senderDid);
|
|
1344
|
+
console.log('Nonce rejected:', !isDuplicate); // false (replay attack)
|
|
1345
|
+
|
|
1346
|
+
// Clean up
|
|
1347
|
+
nonceStore.dispose();
|
|
1348
|
+
```
|
|
1349
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1350
|
+
|
|
1351
|
+
### Redis Nonce Store (Multi-Node)
|
|
1352
|
+
|
|
1353
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1354
|
+
```typescript
|
|
1355
|
+
import { RedisNonceStore } from '@private.me/xbind';
|
|
1356
|
+
import Redis from 'ioredis';
|
|
1357
|
+
|
|
1358
|
+
// Create Redis client
|
|
1359
|
+
const redis = new Redis({ host: 'localhost', port: 6379 });
|
|
1360
|
+
|
|
1361
|
+
// Create Redis nonce store (distributed across all nodes)
|
|
1362
|
+
const nonceStore = new RedisNonceStore({
|
|
1363
|
+
client: {
|
|
1364
|
+
setNX: async (key, value, ttl) => {
|
|
1365
|
+
const result = await redis.set(key, value, 'EX', ttl, 'NX');
|
|
1366
|
+
return result === 'OK' ? 'OK' : null;
|
|
1367
|
+
},
|
|
1368
|
+
del: (key) => redis.del(key),
|
|
1369
|
+
quit: () => redis.quit(),
|
|
1370
|
+
},
|
|
1371
|
+
ttlSeconds: 600, // 10 minutes
|
|
1372
|
+
keyPrefix: 'xbind:nonce:',
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
// Use nonce store (same API as MemoryNonceStore)
|
|
1376
|
+
const isNewNonce = await nonceStore.check('nonce-xyz', 'did:key:z6Mk...');
|
|
1377
|
+
console.log('Distributed nonce check:', isNewNonce);
|
|
1378
|
+
|
|
1379
|
+
// Clean up
|
|
1380
|
+
await nonceStore.dispose();
|
|
1381
|
+
```
|
|
1382
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1383
|
+
|
|
1384
|
+
### Exponential Backoff Retry
|
|
1385
|
+
|
|
1386
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1387
|
+
```typescript
|
|
1388
|
+
import { ExponentialBackoffStrategy, executeWithRetry } from '@private.me/xbind';
|
|
1389
|
+
import { err, ok } from '@private.me/shared';
|
|
1390
|
+
|
|
1391
|
+
// Create exponential backoff strategy (3 attempts, 1s → 2s → 4s)
|
|
1392
|
+
const retryStrategy = new ExponentialBackoffStrategy({
|
|
1393
|
+
maxAttempts: 3,
|
|
1394
|
+
initialDelayMs: 1000,
|
|
1395
|
+
maxDelayMs: 30000,
|
|
1396
|
+
multiplier: 2,
|
|
1397
|
+
jitter: true,
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
// Simulate flaky operation
|
|
1401
|
+
let attemptCount = 0;
|
|
1402
|
+
async function flakyOperation() {
|
|
1403
|
+
attemptCount++;
|
|
1404
|
+
console.log(`Attempt ${attemptCount}`);
|
|
1405
|
+
|
|
1406
|
+
if (attemptCount < 3) {
|
|
1407
|
+
return err('NETWORK_ERROR' as const);
|
|
1408
|
+
}
|
|
1409
|
+
return ok({ data: 'Success after retries' });
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// Execute with retry
|
|
1413
|
+
const result = await executeWithRetry(flakyOperation, retryStrategy);
|
|
1414
|
+
|
|
1415
|
+
if (result.ok) {
|
|
1416
|
+
console.log('Operation succeeded:', result.value.data);
|
|
1417
|
+
} else {
|
|
1418
|
+
console.error('Operation failed:', result.error);
|
|
1419
|
+
}
|
|
1420
|
+
```
|
|
1421
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1422
|
+
|
|
1423
|
+
### Linear Backoff Strategy
|
|
1424
|
+
|
|
1425
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1426
|
+
```typescript
|
|
1427
|
+
import { LinearBackoffStrategy, executeWithRetry } from '@private.me/xbind';
|
|
1428
|
+
import { err, ok } from '@private.me/shared';
|
|
1429
|
+
|
|
1430
|
+
// Create linear backoff strategy (delays: 1s, 2s, 3s, 4s)
|
|
1431
|
+
const retryStrategy = new LinearBackoffStrategy({
|
|
1432
|
+
maxAttempts: 4,
|
|
1433
|
+
initialDelayMs: 1000,
|
|
1434
|
+
delayIncrementMs: 1000,
|
|
1435
|
+
maxDelayMs: 10000,
|
|
1436
|
+
jitter: true,
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
// Simulate operation
|
|
1440
|
+
async function operation() {
|
|
1441
|
+
return ok({ message: 'Linear backoff demo' });
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
const result = await executeWithRetry(operation, retryStrategy);
|
|
1445
|
+
console.log('Result:', result.ok ? result.value.message : result.error);
|
|
1446
|
+
```
|
|
1447
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1448
|
+
|
|
1449
|
+
### Fixed Delay Strategy
|
|
1450
|
+
|
|
1451
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1452
|
+
```typescript
|
|
1453
|
+
import { FixedDelayStrategy, executeWithRetry } from '@private.me/xbind';
|
|
1454
|
+
import { err, ok } from '@private.me/shared';
|
|
1455
|
+
|
|
1456
|
+
// Create fixed delay strategy (constant 2s delay between retries)
|
|
1457
|
+
const retryStrategy = new FixedDelayStrategy({
|
|
1458
|
+
maxAttempts: 3,
|
|
1459
|
+
delayMs: 2000,
|
|
1460
|
+
jitter: false,
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
// Simulate operation
|
|
1464
|
+
async function operation() {
|
|
1465
|
+
return ok({ message: 'Fixed delay demo' });
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
const result = await executeWithRetry(operation, retryStrategy);
|
|
1469
|
+
console.log('Result:', result.ok ? result.value.message : result.error);
|
|
1470
|
+
```
|
|
1471
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1472
|
+
|
|
1473
|
+
### No Retry Strategy
|
|
1474
|
+
|
|
1475
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1476
|
+
```typescript
|
|
1477
|
+
import { NoRetryStrategy, executeWithRetry } from '@private.me/xbind';
|
|
1478
|
+
import { err } from '@private.me/shared';
|
|
1479
|
+
|
|
1480
|
+
// Create no-retry strategy (fail immediately)
|
|
1481
|
+
const retryStrategy = new NoRetryStrategy();
|
|
1482
|
+
|
|
1483
|
+
// Simulate failing operation
|
|
1484
|
+
async function operation() {
|
|
1485
|
+
return err('NETWORK_ERROR' as const);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
const result = await executeWithRetry(operation, retryStrategy);
|
|
1489
|
+
console.log('Failed immediately:', !result.ok);
|
|
1490
|
+
console.log('Error:', result.ok ? null : result.error);
|
|
1491
|
+
```
|
|
1492
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1493
|
+
|
|
1494
|
+
### Circuit Breaker Pattern
|
|
1495
|
+
|
|
1496
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1497
|
+
```typescript
|
|
1498
|
+
import { CircuitBreaker } from '@private.me/xbind';
|
|
1499
|
+
|
|
1500
|
+
// Create circuit breaker (5 failures → open, 60s reset, 2 successes → close)
|
|
1501
|
+
const breaker = new CircuitBreaker({
|
|
1502
|
+
failureThreshold: 5,
|
|
1503
|
+
resetTimeoutMs: 60000,
|
|
1504
|
+
successThreshold: 2,
|
|
1505
|
+
windowMs: 60000,
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
// Simulate failures to trip circuit
|
|
1509
|
+
for (let i = 0; i < 5; i++) {
|
|
1510
|
+
breaker.recordFailure();
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
console.log('Circuit state after 5 failures:', breaker.getStats().state); // OPEN
|
|
1514
|
+
|
|
1515
|
+
// Attempt request while circuit is open
|
|
1516
|
+
const canProceed = breaker.allowRequest();
|
|
1517
|
+
console.log('Request allowed:', canProceed); // false
|
|
1518
|
+
|
|
1519
|
+
// Get circuit statistics
|
|
1520
|
+
const stats = breaker.getStats();
|
|
1521
|
+
console.log('Stats:', {
|
|
1522
|
+
state: stats.state,
|
|
1523
|
+
totalFailures: stats.totalFailures,
|
|
1524
|
+
consecutiveFailures: stats.consecutiveFailures,
|
|
1525
|
+
});
|
|
1526
|
+
```
|
|
1527
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1528
|
+
|
|
1529
|
+
### Retry with Circuit Breaker
|
|
1530
|
+
|
|
1531
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1532
|
+
```typescript
|
|
1533
|
+
import {
|
|
1534
|
+
ExponentialBackoffStrategy,
|
|
1535
|
+
CircuitBreaker,
|
|
1536
|
+
executeWithRetry,
|
|
1537
|
+
} from '@private.me/xbind';
|
|
1538
|
+
import { err, ok } from '@private.me/shared';
|
|
1539
|
+
|
|
1540
|
+
// Create retry strategy and circuit breaker
|
|
1541
|
+
const retryStrategy = new ExponentialBackoffStrategy({
|
|
1542
|
+
maxAttempts: 3,
|
|
1543
|
+
initialDelayMs: 1000,
|
|
1544
|
+
});
|
|
1545
|
+
|
|
1546
|
+
const circuitBreaker = new CircuitBreaker({
|
|
1547
|
+
failureThreshold: 2,
|
|
1548
|
+
resetTimeoutMs: 5000,
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
// Simulate operation that succeeds after circuit recovery
|
|
1552
|
+
let callCount = 0;
|
|
1553
|
+
async function operation() {
|
|
1554
|
+
callCount++;
|
|
1555
|
+
if (callCount === 1) {
|
|
1556
|
+
return err('NETWORK_ERROR' as const);
|
|
1557
|
+
}
|
|
1558
|
+
return ok({ data: `Success on attempt ${callCount}` });
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// Execute with retry + circuit breaker
|
|
1562
|
+
const result = await executeWithRetry(operation, retryStrategy, circuitBreaker);
|
|
1563
|
+
|
|
1564
|
+
console.log('Operation result:', result.ok ? result.value.data : result.error);
|
|
1565
|
+
console.log('Circuit state:', circuitBreaker.getStats().state);
|
|
1566
|
+
```
|
|
1567
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1568
|
+
|
|
1569
|
+
### RetryTransportAdapter (Automatic Retry)
|
|
1570
|
+
|
|
1571
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1572
|
+
```typescript
|
|
1573
|
+
import { RetryTransportAdapter, HttpsTransportAdapter, Agent } from '@private.me/xbind';
|
|
1574
|
+
|
|
1575
|
+
// Create base transport
|
|
1576
|
+
const baseTransport = new HttpsTransportAdapter({
|
|
1577
|
+
baseUrl: 'https://gateway.private.me',
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
// Wrap with retry decorator (exponential backoff, 3 attempts)
|
|
1581
|
+
const retryTransport = new RetryTransportAdapter(baseTransport, {
|
|
1582
|
+
maxAttempts: 3,
|
|
1583
|
+
initialDelayMs: 1000,
|
|
1584
|
+
maxDelayMs: 10000,
|
|
1585
|
+
strategy: 'exponential',
|
|
1586
|
+
});
|
|
1587
|
+
|
|
1588
|
+
// Create agent with retry transport
|
|
1589
|
+
const agent = await Agent.quickstart({
|
|
1590
|
+
name: 'retry-demo',
|
|
1591
|
+
transports: [retryTransport],
|
|
1592
|
+
});
|
|
1593
|
+
|
|
1594
|
+
console.log('Agent created with retry transport');
|
|
1595
|
+
console.log('Transport type:', agent.transports[0].constructor.name);
|
|
1596
|
+
```
|
|
1597
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1598
|
+
|
|
1599
|
+
### Shared Key Generation
|
|
1600
|
+
|
|
1601
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1602
|
+
```typescript
|
|
1603
|
+
import { generateSharedKey } from '@private.me/xbind';
|
|
1604
|
+
|
|
1605
|
+
// Generate random AES-256-GCM shared key
|
|
1606
|
+
const sharedKeyResult = await generateSharedKey();
|
|
1607
|
+
|
|
1608
|
+
if (!sharedKeyResult.ok) {
|
|
1609
|
+
throw new Error(`Failed to generate key: ${sharedKeyResult.error}`);
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
const sharedKey = sharedKeyResult.value;
|
|
1613
|
+
|
|
1614
|
+
// Export key material for inspection
|
|
1615
|
+
const keyMaterial = await crypto.subtle.exportKey('raw', sharedKey);
|
|
1616
|
+
console.log('Shared key generated (256 bits):', keyMaterial.byteLength * 8);
|
|
1617
|
+
console.log('Key algorithm:', sharedKey.algorithm.name);
|
|
1618
|
+
console.log('Key usages:', sharedKey.usages);
|
|
1619
|
+
```
|
|
1620
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1621
|
+
|
|
1622
|
+
### Signed Envelopes
|
|
1623
|
+
|
|
1624
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1625
|
+
```typescript
|
|
1626
|
+
import { createSignedEnvelope, openSignedEnvelope, Agent } from '@private.me/xbind';
|
|
1627
|
+
|
|
1628
|
+
// Create agents
|
|
1629
|
+
const sender = await Agent.quickstart({ name: 'sender' });
|
|
1630
|
+
const recipient = await Agent.quickstart({ name: 'recipient' });
|
|
1631
|
+
|
|
1632
|
+
// Create signed envelope
|
|
1633
|
+
const payload = { action: 'transfer', amount: 100 };
|
|
1634
|
+
const envelopeResult = await createSignedEnvelope(
|
|
1635
|
+
sender.identity,
|
|
1636
|
+
recipient.did,
|
|
1637
|
+
payload,
|
|
1638
|
+
'billing'
|
|
1639
|
+
);
|
|
1640
|
+
|
|
1641
|
+
if (!envelopeResult.ok) {
|
|
1642
|
+
throw new Error(`Failed to create envelope: ${envelopeResult.error}`);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
const envelope = envelopeResult.value;
|
|
1646
|
+
console.log('Envelope created with signature');
|
|
1647
|
+
console.log('Sender DID:', envelope.sender);
|
|
1648
|
+
console.log('Recipient DID:', envelope.recipient);
|
|
1649
|
+
console.log('Signature length:', envelope.signature.length);
|
|
1650
|
+
|
|
1651
|
+
// Verify and open envelope
|
|
1652
|
+
const openResult = await openSignedEnvelope(envelope, sender.identity.publicKey);
|
|
1653
|
+
|
|
1654
|
+
if (!openResult.ok) {
|
|
1655
|
+
throw new Error(`Failed to open envelope: ${openResult.error}`);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
console.log('Envelope verified and opened');
|
|
1659
|
+
console.log('Payload:', openResult.value);
|
|
1660
|
+
```
|
|
1661
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
1662
|
+
|
|
1663
|
+
## Python SDK
|
|
1664
|
+
|
|
1665
|
+
Complete Python bindings for agent identity and messaging:
|
|
1666
|
+
|
|
1667
|
+
```python
|
|
1668
|
+
from private_me.xbind import Agent
|
|
1669
|
+
|
|
1670
|
+
# Create agent from private key
|
|
1671
|
+
agent = Agent.from_private_key(private_key_bytes)
|
|
1672
|
+
|
|
1673
|
+
# Make authenticated call
|
|
1674
|
+
result = agent.call('stripe:createCharge', {
|
|
1675
|
+
'amount': 100,
|
|
1676
|
+
'currency': 'usd',
|
|
1677
|
+
'description': 'AI agent purchase'
|
|
1678
|
+
})
|
|
1679
|
+
|
|
1680
|
+
if result['ok']:
|
|
1681
|
+
print(f"Charge ID: {result['data']['id']}")
|
|
1682
|
+
print(f"Agent DID: {result['audit']['agent']}")
|
|
1683
|
+
else:
|
|
1684
|
+
print(f"Error: {result['error']['message']}")
|
|
1685
|
+
```
|
|
1686
|
+
|
|
1687
|
+
See [Python SDK documentation](./python/README.md) for complete API reference and examples.
|
|
1688
|
+
|
|
1689
|
+
## Why xBind?
|
|
1690
|
+
|
|
1691
|
+
Zero key management, zero cascade failures, zero bearer credentials. Cryptographic identity replaces API keys. See [white paper](https://private.me/docs/xbind.html) for architecture details and benchmarks.
|
|
1692
|
+
|
|
1693
|
+
## Getting Started
|
|
1694
|
+
|
|
1695
|
+
**[Guide](../../docs/xbind-integrations/getting-started/index.md)** — Concepts, migration
|
|
1696
|
+
**[Quickstart](../../docs/xbind-integrations/getting-started/quickstart.md)** — 15-second setup
|
|
1697
|
+
|
|
1698
|
+
## Features
|
|
1699
|
+
|
|
1700
|
+
**Zero-config JITR:** Just-in-Time Registration auto-registers agents with trust registry on first use (AWS IoT JITR, OAuth DCR, MCP 2025 standards).
|
|
1701
|
+
|
|
1702
|
+
### Core Security
|
|
1703
|
+
- **Post-quantum cryptography:** ML-KEM-768 key encapsulation, ML-DSA-65 digital signatures (FIPS 203/204)
|
|
1704
|
+
- **Hybrid key agreement:** X-Wing combiner (IETF draft-10) with 6-parameter domain separation
|
|
1705
|
+
- **XorIDA split-channel delivery:** Information-theoretic threshold sharing (2-of-2, 2-of-3, 3-of-5)
|
|
1706
|
+
- **Cryptographic request signing:** Ed25519 signatures prevent DID header forgery
|
|
1707
|
+
- **Key rotation:** ML-KEM + X25519 rotation with fallback decryption (10 rotation history)
|
|
1708
|
+
- **Proof-of-Possession:** Ed25519 signature verification prevents DID spoofing
|
|
1709
|
+
- **Share-aware nonce deduplication:** Prevents replay attacks with composite keys
|
|
1710
|
+
|
|
1711
|
+
### Runtime Compatibility (4 Platforms)
|
|
1712
|
+
- **Browser:** WebCrypto API, IndexedDB/localStorage adapters, WASM support, service workers
|
|
1713
|
+
- **React Native:** AsyncStorage, Buffer polyfills, crypto detection, iOS/Android platform support
|
|
1714
|
+
- **Edge Runtime:** Cloudflare Workers, Vercel Edge, Deno Deploy, KV storage, <1MB optimization
|
|
1715
|
+
- **Node.js:** Native crypto module, filesystem storage, full feature set
|
|
1716
|
+
|
|
1717
|
+
### Operations Patterns
|
|
1718
|
+
- **Health checks:** Startup/liveness/readiness probes, Kubernetes-compatible, Express/Fastify middleware
|
|
1719
|
+
- **Circuit breakers:** 3-state machine (closed/open/half-open), registry/gateway/S3 presets, automatic recovery
|
|
1720
|
+
- **Graceful degradation:** QoS tiers, intelligent caching, service health tracking, fallback strategies
|
|
1721
|
+
- **Performance benchmarks:** Latency histograms with baselines for key ops (ML-KEM, ML-DSA, encryption)
|
|
1722
|
+
- **Structured logging:** 4 levels (DEBUG/INFO/WARN/ERROR), automatic sensitive data redaction, correlation IDs
|
|
1723
|
+
- **Telemetry:** Prometheus-compatible metrics (counters, histograms, gauges), operation latency tracking
|
|
1724
|
+
|
|
1725
|
+
### Security Audit Preparation
|
|
1726
|
+
- **STRIDE threat modeling:** 66 threats analyzed across 6 categories
|
|
1727
|
+
- **Crypto claims documentation:** 41 cryptographic claims documented with evidence
|
|
1728
|
+
- **Known limitations disclosure:** 24 limitations disclosed for auditor transparency
|
|
1729
|
+
- **Audit-ready:** NCC Group / Trail of Bits preparation ($100K-$150K, 12-week timeline)
|
|
1730
|
+
|
|
1731
|
+
### API Enhancements
|
|
1732
|
+
- **Batch operations:** 6-8x speedup for parallel operations with single network round-trip
|
|
1733
|
+
- **Async iterators:** `for await...of` syntax support for streaming message processing
|
|
1734
|
+
- **Plugin/middleware system:** 6-phase lifecycle hooks with 3 built-in plugins
|
|
1735
|
+
- **Event emitters:** Type-safe events with 5 event types, priority execution, bubbling
|
|
1736
|
+
- **Cancellation tokens:** AbortController integration with timeout support
|
|
1737
|
+
- **Progress callbacks:** 4 specialized trackers (operation, transfer, share, encryption)
|
|
1738
|
+
- **Retry strategies:** 4 strategies (exponential, linear, immediate, jittered) + circuit breaker integration
|
|
1739
|
+
- **Request timeouts:** Per-operation config with inheritance and cancellation
|
|
1740
|
+
- **Connection pooling:** 60-70% latency reduction with keep-alive and metrics
|
|
1741
|
+
- **Serialization formats:** JSON/MessagePack/CBOR support with auto-detection and negotiation
|
|
1742
|
+
- **Configuration validation:** Comprehensive validation with clear error messages
|
|
1743
|
+
- **Debug mode:** Performance profiling, network/crypto tracing, state inspection
|
|
1744
|
+
- **SDK version info:** Capability detection, deprecation warnings, compatibility checking
|
|
1745
|
+
|
|
1746
|
+
### Developer Experience
|
|
1747
|
+
- **Type safety:** `Result<T, E>` error handling with 96+ error codes
|
|
1748
|
+
- **TypeScript strict mode:** Zero type errors, complete type coverage
|
|
1749
|
+
- **Python SDK:** Complete bindings for identity, messaging, and encryption
|
|
1750
|
+
- **User-friendly errors:** 75+ errors with actionable recovery hints
|
|
1751
|
+
- **Complete API docs:** 2,587 lines with 50+ usage examples
|
|
1752
|
+
- **CHANGELOG generation:** Automated Keep a Changelog format with semver discipline
|
|
1753
|
+
- **Dependency audit:** Weekly Dependabot scans, 5-layer vulnerability scanning
|
|
1754
|
+
- **Crypto license compliance:** Automated MIT/MIT-0 validation with GPL/AGPL detection
|
|
1755
|
+
|
|
1756
|
+
### Enterprise Features
|
|
1757
|
+
- **Version negotiation:** Explicit SDK version fields prevent "404 Route not found" in mixed fleets
|
|
1758
|
+
- **Multi-device session resume:** AES-256-GCM encrypted session state sync with PBKDF2 (100k iterations)
|
|
1759
|
+
- **Encrypted backup/restore:** Password-protected identity export with PBKDF2-SHA256 (310k iterations)
|
|
1760
|
+
- **Offline sync:** Message queueing for offline devices (max 1000 messages, 24-hour TTL)
|
|
1761
|
+
- **Registry expiration:** TTL support with cleanup (default 7 days)
|
|
1762
|
+
- **Server-side spending limits:** Redis-backed enforcement with deployment-level aggregation
|
|
1763
|
+
- **Rate limiting:** Three-tier protection (per-DID, per-IP, global)
|
|
1764
|
+
- **Full Control IP protection:** Store Front (npm) + Vault Store (EC2) with payment verification
|
|
1765
|
+
|
|
1766
|
+
### Testing & Quality
|
|
1767
|
+
- **2,951 tests:** Comprehensive coverage (100% passing)
|
|
1768
|
+
- **End-to-end integration:** Full message flow tests (Agent A → Gateway → Agent B)
|
|
1769
|
+
- **Gateway concurrency:** 100 concurrent sends, race condition detection
|
|
1770
|
+
- **Network partition recovery:** Disconnect/reconnect cycles with retry logic
|
|
1771
|
+
- **PLAN-3 hybrid signatures:** Bilateral authorization with composite verification
|
|
1772
|
+
|
|
1773
|
+
## Bundle Size Optimization (Tree-Shaking)
|
|
1774
|
+
|
|
1775
|
+
**New in v3.0.3:** LoopbackTransport export (testing workflow), describeSecurityModeStructured export (white paper examples), Agent.quickstart() signature fixes (README correctness). Full Control IP Protection (v3.0.2) - cryptographic algorithms delivered via payment-gated Vault Store. Share 1 in npm (useless alone), Share 2 in EC2 (completes algorithm). Information-theoretic security for proprietary IP.
|
|
1776
|
+
|
|
1777
|
+
### Full Import (Convenience)
|
|
1778
|
+
|
|
1779
|
+
```typescript
|
|
1780
|
+
import { Agent, generateIdentity } from '@private.me/xbind';
|
|
1781
|
+
|
|
1782
|
+
// Bundle size: ~450 KB
|
|
1783
|
+
```
|
|
1784
|
+
|
|
1785
|
+
### Granular Import (Optimized)
|
|
1786
|
+
|
|
1787
|
+
```typescript
|
|
1788
|
+
import { Agent } from '@private.me/xbind/agent';
|
|
1789
|
+
import { generateIdentity } from '@private.me/xbind/identity';
|
|
1790
|
+
|
|
1791
|
+
// Bundle size: ~180 KB (60% reduction)
|
|
1792
|
+
```
|
|
1793
|
+
|
|
1794
|
+
### Available Entry Points
|
|
1795
|
+
|
|
1796
|
+
| Entry Point | Exports | Bundle Impact |
|
|
1797
|
+
|-------------|---------|---------------|
|
|
1798
|
+
| `@private.me/xbind` | All exports | Full package (~450 KB) |
|
|
1799
|
+
| `@private.me/xbind/agent` | `Agent`, `AgentOptions` | ~180 KB |
|
|
1800
|
+
| `@private.me/xbind/identity` | `generateIdentity`, `Identity` | ~120 KB |
|
|
1801
|
+
| `@private.me/xbind/trust-registry` | Registry classes | ~200 KB |
|
|
1802
|
+
| `@private.me/xbind/key-agreement` | Key exchange functions | ~90 KB |
|
|
1803
|
+
| `@private.me/xbind/errors` | Error types | 0 KB (types only) |
|
|
1804
|
+
|
|
1805
|
+
**Bundler Support:** Works with webpack 5+, Rollup, esbuild, and Vite. Tree-shaking is automatic in production mode.
|
|
1806
|
+
|
|
1807
|
+
**See:** [Tree-Shaking Guide](./docs/packaging/tree-shaking.md) for detailed usage and configuration.
|
|
1808
|
+
|
|
1809
|
+
## Automatic XorIDA Split-Channel Protection
|
|
1810
|
+
|
|
1811
|
+
xBind automatically activates information-theoretic XorIDA threshold sharing for high-risk operations. No code changes required—security is transparent.
|
|
1812
|
+
|
|
1813
|
+
### Risk Tags (Recommended for Crypto)
|
|
1814
|
+
|
|
1815
|
+
For cryptocurrency transactions (BTC, ETH, etc.), use explicit risk tags in your payload:
|
|
1816
|
+
|
|
1817
|
+
```typescript
|
|
1818
|
+
// Low risk: 2-of-2 threshold
|
|
1819
|
+
await agent.send({
|
|
1820
|
+
to: recipientDid,
|
|
1821
|
+
payload: { amount: 0.5, currency: 'BTC', risk: 'low' }
|
|
1822
|
+
});
|
|
1823
|
+
|
|
1824
|
+
// Medium risk: 2-of-3 threshold
|
|
1825
|
+
await agent.send({
|
|
1826
|
+
to: recipientDid,
|
|
1827
|
+
payload: { amount: 5.0, currency: 'ETH', risk: 'medium' }
|
|
1828
|
+
});
|
|
1829
|
+
|
|
1830
|
+
// High risk: 3-of-5 threshold
|
|
1831
|
+
await agent.send({
|
|
1832
|
+
to: recipientDid,
|
|
1833
|
+
payload: { amount: 50, currency: 'BTC', risk: 'high' }
|
|
1834
|
+
});
|
|
1835
|
+
|
|
1836
|
+
// Critical risk: 3-of-5 threshold
|
|
1837
|
+
await agent.send({
|
|
1838
|
+
to: recipientDid,
|
|
1839
|
+
payload: { amount: 100, currency: 'BTC', risk: 'critical' }
|
|
1840
|
+
});
|
|
1841
|
+
```
|
|
1842
|
+
|
|
1843
|
+
### Fiat Currency Auto-Detection
|
|
1844
|
+
|
|
1845
|
+
For fiat currencies (USD, EUR, GBP), xBind uses numeric thresholds:
|
|
1846
|
+
|
|
1847
|
+
```typescript
|
|
1848
|
+
// Automatically triggers 2-of-3 (amount >= $100,000)
|
|
1849
|
+
await agent.send({
|
|
1850
|
+
to: recipientDid,
|
|
1851
|
+
payload: { amount: 500000, currency: 'USD', action: 'transfer' }
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1854
|
+
// Automatically triggers 3-of-5 (amount >= $1,000,000)
|
|
1855
|
+
await agent.send({
|
|
1856
|
+
to: recipientDid,
|
|
1857
|
+
payload: { amount: 2500000, currency: 'USD', action: 'transfer' }
|
|
1858
|
+
});
|
|
1859
|
+
```
|
|
1860
|
+
|
|
1861
|
+
### Manual Security Override
|
|
1862
|
+
|
|
1863
|
+
Override automatic detection with explicit security levels:
|
|
1864
|
+
|
|
1865
|
+
```typescript
|
|
1866
|
+
// Force 2-of-3 regardless of amount/risk
|
|
1867
|
+
await agent.send({
|
|
1868
|
+
to: recipientDid,
|
|
1869
|
+
payload: data,
|
|
1870
|
+
security: 'high'
|
|
1871
|
+
});
|
|
1872
|
+
|
|
1873
|
+
// Force 3-of-5 for maximum security
|
|
1874
|
+
await agent.send({
|
|
1875
|
+
to: recipientDid,
|
|
1876
|
+
payload: data,
|
|
1877
|
+
security: 'critical'
|
|
1878
|
+
});
|
|
1879
|
+
|
|
1880
|
+
// Disable XorIDA (standard encrypted transport)
|
|
1881
|
+
await agent.send({
|
|
1882
|
+
to: recipientDid,
|
|
1883
|
+
payload: data,
|
|
1884
|
+
security: 'standard'
|
|
1885
|
+
});
|
|
1886
|
+
```
|
|
1887
|
+
|
|
1888
|
+
### Threshold Schemes
|
|
1889
|
+
|
|
1890
|
+
| Risk Tag / Threshold | Shares | Required | Security Level |
|
|
1891
|
+
|---------------------|--------|----------|----------------|
|
|
1892
|
+
| `low` | 2 | 2 | 2-of-2 threshold |
|
|
1893
|
+
| `medium` / $100k-$1M | 3 | 2 | 2-of-3 threshold |
|
|
1894
|
+
| `high` / `critical` / >$1M | 5 | 3 | 3-of-5 threshold |
|
|
1895
|
+
| No tag / <$100k | — | — | Standard encrypted transport |
|
|
1896
|
+
|
|
1897
|
+
**Key Insight:** XorIDA is information-theoretically secure. Any K-1 shares reveal zero information about the secret, even with unlimited computing power. Quantum computers cannot break XorIDA.
|
|
1898
|
+
|
|
1899
|
+
### Security Mode Description (Structured)
|
|
1900
|
+
|
|
1901
|
+
Get human-readable descriptions of security modes in multiple formats:
|
|
1902
|
+
|
|
1903
|
+
```typescript
|
|
1904
|
+
import { describeSecurityModeStructured } from '@private.me/xbind';
|
|
1905
|
+
|
|
1906
|
+
// Describe a split-channel security mode
|
|
1907
|
+
const mode = { type: 'split', shares: 3, threshold: 2 };
|
|
1908
|
+
const description = describeSecurityModeStructured(mode);
|
|
1909
|
+
|
|
1910
|
+
// Single-line format (for UI labels)
|
|
1911
|
+
console.log(description.formats.singleline); // "2-of-3 XorIDA"
|
|
1912
|
+
|
|
1913
|
+
// Multi-line format (for detailed explanations)
|
|
1914
|
+
console.log(description.formats.multiline);
|
|
1915
|
+
// Output:
|
|
1916
|
+
// Security Mode: 2-of-3 XorIDA Split-Channel
|
|
1917
|
+
// Shares: 3 total, 2 required to reconstruct
|
|
1918
|
+
// Information-theoretic security (quantum-resistant)
|
|
1919
|
+
|
|
1920
|
+
// Markdown format (for documentation)
|
|
1921
|
+
console.log(description.formats.markdown);
|
|
1922
|
+
// Output:
|
|
1923
|
+
// **Security Mode:** 2-of-3 XorIDA Split-Channel
|
|
1924
|
+
// - **Shares:** 3 total, 2 required to reconstruct
|
|
1925
|
+
// - **Security:** Information-theoretic (quantum-resistant)
|
|
1926
|
+
```
|
|
1927
|
+
|
|
1928
|
+
## Billing & Metering
|
|
1929
|
+
|
|
1930
|
+
xBind includes usage-based billing with automated milestone notifications:
|
|
1931
|
+
|
|
1932
|
+
- **Free Tier:** Generous monthly limits (no email verification required upfront)
|
|
1933
|
+
- **Grace Buffer:** Additional operations available after email verification
|
|
1934
|
+
- **Pro Tier:** Usage-based billing with unlimited operations
|
|
1935
|
+
- **Monthly Reset:** 1st of each month at 00:00 UTC
|
|
1936
|
+
|
|
1937
|
+
See [pricing](../../docs/pricing-reference.md) for current rates and tier details.
|
|
1938
|
+
|
|
1939
|
+
### Milestone Notifications
|
|
1940
|
+
|
|
1941
|
+
Automated email notifications at usage thresholds with progress bars and CTAs for upgrades.
|
|
1942
|
+
|
|
1943
|
+
**Email verification:** Required at free tier limit to access grace buffer.
|
|
1944
|
+
|
|
1945
|
+
**Implementation:**
|
|
1946
|
+
- Metering logic: `apps/server/src/customer-metering.ts`
|
|
1947
|
+
- Milestone system: `apps/server/src/usage-milestone-notifications.ts`
|
|
1948
|
+
- Tier pattern: See `docs/gold-package.md` section 7.6.5
|
|
1949
|
+
|
|
1950
|
+
## Gateway
|
|
1951
|
+
|
|
1952
|
+
Non-authoritative coordination for discovery, relay, push notifications, and state checkpoints. Cannot forge identity or decide trust. See [Gateway Architecture](./docs/gateway-architecture.md).
|
|
1953
|
+
|
|
1954
|
+
## Connection Models
|
|
1955
|
+
|
|
1956
|
+
xBind provides four connection models for entity-to-entity authentication: Invite Code (email-based onboarding), QR Code (physical proximity pairing), Trust Registry (pre-authorized enterprise), and Peer Discovery (mDNS local network). All use cryptographic DIDs.
|
|
1957
|
+
|
|
1958
|
+
| Model | Security | UX | Best For |
|
|
1959
|
+
|-------|----------|-----|----------|
|
|
1960
|
+
| Invite Code | Single-use, 24h TTL | 6-char code | Customer onboarding |
|
|
1961
|
+
| QR Code | Physical proximity, 60s TTL | Scan & tap | Mobile/desktop pairing |
|
|
1962
|
+
| Trust Registry | Admin pre-auth, scoped | Zero (automated) | Enterprise/CI/CD |
|
|
1963
|
+
| Peer Discovery | LAN proximity, user confirm | Scan & confirm | IoT devices, offline |
|
|
1964
|
+
|
|
1965
|
+
See [Entity-to-Entity Connection UX Guide](../../docs/ENTITY-TO-ENTITY-CONNECTION-UX.md) for implementation patterns and code examples.
|
|
1966
|
+
|
|
1967
|
+
## Configuration
|
|
1968
|
+
|
|
1969
|
+
### Basic Setup
|
|
1970
|
+
|
|
1971
|
+
```typescript
|
|
1972
|
+
import { HttpTrustRegistry } from '@private.me/xbind';
|
|
1973
|
+
|
|
1974
|
+
// Documentation example - use your actual gateway URL
|
|
1975
|
+
const registry = new HttpTrustRegistry({
|
|
1976
|
+
baseUrl: 'https://gateway.private.me'
|
|
1977
|
+
});
|
|
1978
|
+
```
|
|
1979
|
+
|
|
1980
|
+
### Operational Excellence (Timeouts & Degradation)
|
|
1981
|
+
|
|
1982
|
+
**Timeouts with configurable per-operation controls:**
|
|
1983
|
+
|
|
1984
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
1985
|
+
```typescript
|
|
1986
|
+
import { TimeoutConfig, withTimeout, createTimeoutController, isTimeoutError } from '@private.me/xbind';
|
|
1987
|
+
|
|
1988
|
+
// Create timeout configuration
|
|
1989
|
+
const config = new TimeoutConfig({
|
|
1990
|
+
registry: 10000, // 10 seconds for registry lookups
|
|
1991
|
+
gateway: 15000, // 15 seconds for gateway operations
|
|
1992
|
+
transport: 30000, // 30 seconds for transport/HTTP
|
|
1993
|
+
default: 30000 // 30 seconds fallback
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
// Wrap async operation with timeout (using mock operation that completes quickly)
|
|
1997
|
+
const result = await withTimeout(
|
|
1998
|
+
async () => new Promise(resolve => setTimeout(() => resolve('Done'), 100)),
|
|
1999
|
+
5000,
|
|
2000
|
+
'API request'
|
|
2001
|
+
);
|
|
2002
|
+
|
|
2003
|
+
console.log('Request completed within timeout:', result);
|
|
2004
|
+
```
|
|
2005
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2006
|
+
|
|
2007
|
+
**Timeout controller with AbortSignal:**
|
|
2008
|
+
|
|
2009
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2010
|
+
```typescript
|
|
2011
|
+
import { createTimeoutController, TimeoutError } from '@private.me/xbind';
|
|
2012
|
+
|
|
2013
|
+
const controller = createTimeoutController(5000, 'registry lookup');
|
|
2014
|
+
|
|
2015
|
+
try {
|
|
2016
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
2017
|
+
controller.clear(); // Clear timeout on success
|
|
2018
|
+
console.log('Fetch succeeded');
|
|
2019
|
+
} catch (error) {
|
|
2020
|
+
if (controller.didTimeout()) {
|
|
2021
|
+
console.error('Operation timed out after 5000ms');
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
```
|
|
2025
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2026
|
+
|
|
2027
|
+
**Result-based timeout handling (no exceptions):**
|
|
2028
|
+
|
|
2029
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2030
|
+
```typescript
|
|
2031
|
+
import { withTimeoutResult, isTimeoutError } from '@private.me/xbind';
|
|
2032
|
+
|
|
2033
|
+
const result = await withTimeoutResult(
|
|
2034
|
+
async () => performOperation(),
|
|
2035
|
+
5000,
|
|
2036
|
+
'database query'
|
|
2037
|
+
);
|
|
2038
|
+
|
|
2039
|
+
if (result.ok) {
|
|
2040
|
+
console.log('Success:', result.value);
|
|
2041
|
+
} else {
|
|
2042
|
+
console.error('Timeout error:', result.error);
|
|
2043
|
+
}
|
|
2044
|
+
```
|
|
2045
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2046
|
+
|
|
2047
|
+
**Operation timeout with category-based config:**
|
|
2048
|
+
|
|
2049
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2050
|
+
```typescript
|
|
2051
|
+
import { TimeoutConfig, createOperationTimeout } from '@private.me/xbind';
|
|
2052
|
+
|
|
2053
|
+
const config = new TimeoutConfig({ registry: 10000 });
|
|
2054
|
+
const controller = createOperationTimeout(config, 'registry', 'lookup DID');
|
|
2055
|
+
|
|
2056
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
2057
|
+
controller.clear();
|
|
2058
|
+
```
|
|
2059
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2060
|
+
|
|
2061
|
+
**Environment-based timeout configuration:**
|
|
2062
|
+
|
|
2063
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2064
|
+
```typescript
|
|
2065
|
+
import { createTimeoutConfigFromEnv, globalTimeoutConfig } from '@private.me/xbind';
|
|
2066
|
+
|
|
2067
|
+
// Reads from environment variables:
|
|
2068
|
+
// - XBIND_TIMEOUT_REGISTRY
|
|
2069
|
+
// - XBIND_TIMEOUT_GATEWAY
|
|
2070
|
+
// - XBIND_TIMEOUT_TRANSPORT
|
|
2071
|
+
// - XBIND_TIMEOUT_DEFAULT
|
|
2072
|
+
|
|
2073
|
+
const config = createTimeoutConfigFromEnv();
|
|
2074
|
+
console.log('Registry timeout:', config.getRegistry());
|
|
2075
|
+
console.log('Gateway timeout:', config.getGateway());
|
|
2076
|
+
|
|
2077
|
+
// Use global default config
|
|
2078
|
+
const defaultTimeout = globalTimeoutConfig.getDefault();
|
|
2079
|
+
```
|
|
2080
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2081
|
+
|
|
2082
|
+
**Graceful degradation with cache fallback:**
|
|
2083
|
+
|
|
2084
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2085
|
+
```typescript
|
|
2086
|
+
import { GracefulDegradationManager, registryLookupWithFallback } from '@private.me/xbind';
|
|
2087
|
+
|
|
2088
|
+
const manager = new GracefulDegradationManager({
|
|
2089
|
+
defaultTTL: 300000, // 5 minutes cache TTL
|
|
2090
|
+
maxSize: 1000, // Max 1000 cached entries
|
|
2091
|
+
allowStale: true, // Use stale cache on failures
|
|
2092
|
+
maxStaleMs: 3600000 // 1 hour max staleness
|
|
2093
|
+
});
|
|
2094
|
+
|
|
2095
|
+
// Lookup with cache fallback
|
|
2096
|
+
const result = await registryLookupWithFallback(
|
|
2097
|
+
registry,
|
|
2098
|
+
'did:key:z6Mk...',
|
|
2099
|
+
{ operationId: '123', qos: 'HIGH', type: 'registry:lookup' },
|
|
2100
|
+
manager
|
|
2101
|
+
);
|
|
2102
|
+
|
|
2103
|
+
if (result.ok) {
|
|
2104
|
+
console.log('Public key:', result.value.publicKey);
|
|
2105
|
+
}
|
|
2106
|
+
```
|
|
2107
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2108
|
+
|
|
2109
|
+
**Service health tracking:**
|
|
2110
|
+
|
|
2111
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2112
|
+
```typescript
|
|
2113
|
+
import { GracefulDegradationManager } from '@private.me/xbind';
|
|
2114
|
+
|
|
2115
|
+
const manager = new GracefulDegradationManager();
|
|
2116
|
+
|
|
2117
|
+
// Record success
|
|
2118
|
+
manager.recordSuccess('registry');
|
|
2119
|
+
|
|
2120
|
+
// Record failure
|
|
2121
|
+
manager.recordFailure('registry', 'NETWORK_ERROR');
|
|
2122
|
+
|
|
2123
|
+
// Check health
|
|
2124
|
+
const health = manager.getServiceHealth('registry');
|
|
2125
|
+
console.log('Registry health:', health); // 'HEALTHY' | 'DEGRADED' | 'UNAVAILABLE'
|
|
2126
|
+
|
|
2127
|
+
// Get detailed status
|
|
2128
|
+
const status = manager.getServiceStatus('registry');
|
|
2129
|
+
console.log('Failure count:', status?.failureCount);
|
|
2130
|
+
console.log('Last error:', status?.lastError);
|
|
2131
|
+
```
|
|
2132
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2133
|
+
|
|
2134
|
+
**Cache management:**
|
|
2135
|
+
|
|
2136
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2137
|
+
```typescript
|
|
2138
|
+
import { GracefulDegradationManager } from '@private.me/xbind';
|
|
2139
|
+
|
|
2140
|
+
const manager = new GracefulDegradationManager();
|
|
2141
|
+
|
|
2142
|
+
// Store in cache
|
|
2143
|
+
manager.setCached('registry:did:key:z6Mk...', { publicKey: '...' }, 300000);
|
|
2144
|
+
|
|
2145
|
+
// Get from cache (QoS-aware)
|
|
2146
|
+
const cached = manager.getCached('registry:did:key:z6Mk...', 'HIGH');
|
|
2147
|
+
|
|
2148
|
+
// Invalidate cache
|
|
2149
|
+
manager.invalidate('registry:did:key:z6Mk...');
|
|
2150
|
+
|
|
2151
|
+
// Get cache statistics
|
|
2152
|
+
const stats = manager.getCacheStats();
|
|
2153
|
+
console.log('Cache size:', stats.size);
|
|
2154
|
+
console.log('Max size:', stats.maxSize);
|
|
2155
|
+
```
|
|
2156
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2157
|
+
|
|
2158
|
+
**Transport fallback with retry:**
|
|
2159
|
+
|
|
2160
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2161
|
+
```typescript
|
|
2162
|
+
import { sendWithTransportFallback, GracefulDegradationManager } from '@private.me/xbind';
|
|
2163
|
+
|
|
2164
|
+
const manager = new GracefulDegradationManager();
|
|
2165
|
+
|
|
2166
|
+
const result = await sendWithTransportFallback(
|
|
2167
|
+
[primaryTransport, fallbackTransport],
|
|
2168
|
+
envelope,
|
|
2169
|
+
'did:key:recipient',
|
|
2170
|
+
{ operationId: '456', qos: 'NORMAL', type: 'transport:send' },
|
|
2171
|
+
manager
|
|
2172
|
+
);
|
|
2173
|
+
|
|
2174
|
+
if (!result.ok) {
|
|
2175
|
+
console.error('All transports failed:', result.error);
|
|
2176
|
+
}
|
|
2177
|
+
```
|
|
2178
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2179
|
+
|
|
2180
|
+
### Advanced Configuration Options
|
|
2181
|
+
|
|
2182
|
+
#### Identity Management
|
|
2183
|
+
|
|
2184
|
+
**`identity` - Pre-existing Identity (Persistent Agents)**
|
|
2185
|
+
|
|
2186
|
+
Provide an existing AgentIdentity for persistent agents that survive restarts.
|
|
2187
|
+
|
|
2188
|
+
```typescript
|
|
2189
|
+
import { Agent, identityFromSeed } from '@private.me/xbind';
|
|
2190
|
+
|
|
2191
|
+
// Generate identity once, save seed
|
|
2192
|
+
const identityResult = await generateIdentity();
|
|
2193
|
+
if (!identityResult.ok) throw new Error(identityResult.error.message);
|
|
2194
|
+
|
|
2195
|
+
const seed = identityResult.value.seed;
|
|
2196
|
+
// Store seed securely (keychain, HSM, etc.)
|
|
2197
|
+
|
|
2198
|
+
// Later: Restore agent from seed
|
|
2199
|
+
const identityRestoreResult = await identityFromSeed(Buffer.from(seed, 'hex'));
|
|
2200
|
+
if (!identityRestoreResult.ok) throw new Error(identityRestoreResult.error.message);
|
|
2201
|
+
|
|
2202
|
+
const agent = await Agent.create({
|
|
2203
|
+
name: 'persistent-agent',
|
|
2204
|
+
identity: identityRestoreResult.value // Reuse same DID across restarts
|
|
2205
|
+
});
|
|
2206
|
+
```
|
|
2207
|
+
|
|
2208
|
+
**`identityTTL` - Auto-Cleanup for Ephemeral Agents**
|
|
2209
|
+
|
|
2210
|
+
Set automatic cleanup time for short-lived agents (milliseconds).
|
|
2211
|
+
|
|
2212
|
+
```typescript
|
|
2213
|
+
const agent = await Agent.create({
|
|
2214
|
+
name: 'temp-agent',
|
|
2215
|
+
identityTTL: 3600000 // Auto-cleanup after 1 hour
|
|
2216
|
+
});
|
|
2217
|
+
```
|
|
2218
|
+
|
|
2219
|
+
#### Security Options
|
|
2220
|
+
|
|
2221
|
+
**`nonceStore` - Replay Attack Prevention**
|
|
2222
|
+
|
|
2223
|
+
Configure custom nonce storage for distributed deployments.
|
|
2224
|
+
|
|
2225
|
+
```typescript
|
|
2226
|
+
import { Agent, RedisNonceStore } from '@private.me/xbind';
|
|
2227
|
+
|
|
2228
|
+
const agent = await Agent.create({
|
|
2229
|
+
name: 'distributed-agent',
|
|
2230
|
+
nonceStore: new RedisNonceStore({
|
|
2231
|
+
client: redisClient,
|
|
2232
|
+
ttl: 300000 // 5 minute nonce validity
|
|
2233
|
+
})
|
|
2234
|
+
});
|
|
2235
|
+
```
|
|
2236
|
+
|
|
2237
|
+
**`scopes` - Authorization Scopes**
|
|
2238
|
+
|
|
2239
|
+
Restrict which message scopes this agent can receive.
|
|
2240
|
+
|
|
2241
|
+
```typescript
|
|
2242
|
+
const agent = await Agent.create({
|
|
2243
|
+
name: 'scoped-receiver',
|
|
2244
|
+
scopes: ['messages', 'notifications'] // Only accept these scopes
|
|
2245
|
+
});
|
|
2246
|
+
```
|
|
2247
|
+
|
|
2248
|
+
**`timestampWindowMs` - Timestamp Validation**
|
|
2249
|
+
|
|
2250
|
+
Configure acceptable clock skew between sender and receiver.
|
|
2251
|
+
|
|
2252
|
+
```typescript
|
|
2253
|
+
const agent = await Agent.create({
|
|
2254
|
+
name: 'strict-timing',
|
|
2255
|
+
timestampWindowMs: 60000 // Accept messages within ±1 minute
|
|
2256
|
+
});
|
|
2257
|
+
```
|
|
2258
|
+
|
|
2259
|
+
**`securityPolicy` - Enterprise Security Rules**
|
|
2260
|
+
|
|
2261
|
+
Apply custom security policies for enterprise deployments.
|
|
2262
|
+
|
|
2263
|
+
```typescript
|
|
2264
|
+
import { Agent, DefaultSecurityPolicy } from '@private.me/xbind';
|
|
2265
|
+
|
|
2266
|
+
const customPolicy = new DefaultSecurityPolicy({
|
|
2267
|
+
requireEncryption: true,
|
|
2268
|
+
allowedDomains: ['*.private.me', '*.company.com']
|
|
2269
|
+
});
|
|
611
2270
|
|
|
612
|
-
|
|
2271
|
+
const agent = await Agent.create({
|
|
2272
|
+
name: 'enterprise-agent',
|
|
2273
|
+
securityPolicy: customPolicy
|
|
2274
|
+
});
|
|
2275
|
+
```
|
|
613
2276
|
|
|
614
|
-
**
|
|
2277
|
+
**Security Mode Types**
|
|
615
2278
|
|
|
616
|
-
|
|
2279
|
+
Security policies use structured types to describe security modes (input) and their descriptions (output).
|
|
2280
|
+
|
|
2281
|
+
**`SecurityMode` (Input Type)**
|
|
2282
|
+
|
|
2283
|
+
Represents the security mode selected by the policy for a given action.
|
|
617
2284
|
|
|
618
2285
|
```typescript
|
|
619
|
-
|
|
2286
|
+
type SecurityMode =
|
|
2287
|
+
| { readonly type: 'standard' }
|
|
2288
|
+
| { readonly type: 'split'; readonly shares: number; readonly threshold: number }
|
|
2289
|
+
| { readonly type: 'xchange' };
|
|
2290
|
+
```
|
|
620
2291
|
|
|
621
|
-
|
|
2292
|
+
**`SecurityModeDescription` (Output Type)**
|
|
2293
|
+
|
|
2294
|
+
Structured description with multiple format representations for display, logging, APIs, and documentation.
|
|
2295
|
+
|
|
2296
|
+
```typescript
|
|
2297
|
+
interface SecurityModeDescription {
|
|
2298
|
+
/** Security mode type. */
|
|
2299
|
+
readonly type: 'standard' | 'split' | 'xchange';
|
|
2300
|
+
/** Security level classification. */
|
|
2301
|
+
readonly level: 'standard' | 'high' | 'critical' | 'performance';
|
|
2302
|
+
/** Share configuration (only for split mode). */
|
|
2303
|
+
readonly shares?: { readonly total: number; readonly threshold: number };
|
|
2304
|
+
/** Multiple format representations. */
|
|
2305
|
+
readonly formats: {
|
|
2306
|
+
readonly multiline: string;
|
|
2307
|
+
readonly singleline: string;
|
|
2308
|
+
readonly json: string;
|
|
2309
|
+
readonly markdown: string;
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
622
2312
|
```
|
|
623
2313
|
|
|
624
|
-
|
|
2314
|
+
**Usage Example**
|
|
625
2315
|
|
|
626
2316
|
```typescript
|
|
627
|
-
import {
|
|
628
|
-
import { generateIdentity } from '@private.me/xbind/identity';
|
|
2317
|
+
import { describeSecurityModeStructured, type SecurityMode } from '@private.me/xbind';
|
|
629
2318
|
|
|
630
|
-
//
|
|
2319
|
+
// Input: Security mode
|
|
2320
|
+
const mode: SecurityMode = { type: 'split', shares: 3, threshold: 2 };
|
|
2321
|
+
|
|
2322
|
+
// Output: Structured description
|
|
2323
|
+
const description = describeSecurityModeStructured(mode);
|
|
2324
|
+
|
|
2325
|
+
console.log(description.type); // 'split'
|
|
2326
|
+
console.log(description.level); // 'high'
|
|
2327
|
+
console.log(description.shares); // { total: 3, threshold: 2 }
|
|
2328
|
+
|
|
2329
|
+
// Format representations
|
|
2330
|
+
console.log(description.formats.singleline);
|
|
2331
|
+
// "high | split | 2-of-3"
|
|
2332
|
+
|
|
2333
|
+
console.log(description.formats.multiline);
|
|
2334
|
+
// "Security Level: High
|
|
2335
|
+
// Mode: Split-channel (XorIDA)
|
|
2336
|
+
// Shares: 3 total, 2 required"
|
|
2337
|
+
|
|
2338
|
+
console.log(description.formats.json);
|
|
2339
|
+
// '{"type":"split","level":"high","shares":{"total":3,"threshold":2}}'
|
|
2340
|
+
|
|
2341
|
+
console.log(description.formats.markdown);
|
|
2342
|
+
// "**Security Level:** High
|
|
2343
|
+
//
|
|
2344
|
+
// **Mode:** Split-channel (XorIDA)
|
|
2345
|
+
//
|
|
2346
|
+
// **Shares:** 3 total, 2 required"
|
|
631
2347
|
```
|
|
632
2348
|
|
|
633
|
-
|
|
2349
|
+
**`registry` - Trust Registry Configuration**
|
|
634
2350
|
|
|
635
|
-
|
|
636
|
-
|-------------|---------|---------------|
|
|
637
|
-
| `@private.me/xbind` | All exports | Full package (~450 KB) |
|
|
638
|
-
| `@private.me/xbind/agent` | `Agent`, `AgentOptions` | ~180 KB |
|
|
639
|
-
| `@private.me/xbind/identity` | `generateIdentity`, `Identity` | ~120 KB |
|
|
640
|
-
| `@private.me/xbind/trust-registry` | Registry classes | ~200 KB |
|
|
641
|
-
| `@private.me/xbind/key-agreement` | Key exchange functions | ~90 KB |
|
|
642
|
-
| `@private.me/xbind/errors` | Error types | 0 KB (types only) |
|
|
2351
|
+
Configure how agents discover and verify peer identities. Three implementations available.
|
|
643
2352
|
|
|
644
|
-
|
|
2353
|
+
```typescript
|
|
2354
|
+
import { Agent, HttpTrustRegistry, FileTrustRegistry, MemoryTrustRegistry } from '@private.me/xbind';
|
|
645
2355
|
|
|
646
|
-
|
|
2356
|
+
// Production: HTTP-backed registry (default)
|
|
2357
|
+
const httpAgent = await Agent.create({
|
|
2358
|
+
name: 'production-agent',
|
|
2359
|
+
registry: new HttpTrustRegistry('https://private.me/registry')
|
|
2360
|
+
// Or shorthand: registry: 'https://private.me/registry'
|
|
2361
|
+
});
|
|
647
2362
|
|
|
648
|
-
|
|
2363
|
+
// Development: In-memory registry (ephemeral)
|
|
2364
|
+
const devAgent = await Agent.create({
|
|
2365
|
+
name: 'dev-agent',
|
|
2366
|
+
registry: new MemoryTrustRegistry() // No persistence, auto-cleanup
|
|
2367
|
+
});
|
|
649
2368
|
|
|
650
|
-
|
|
2369
|
+
// Enterprise: File-backed registry (local persistence)
|
|
2370
|
+
const fileAgent = await Agent.create({
|
|
2371
|
+
name: 'enterprise-agent',
|
|
2372
|
+
registry: new FileTrustRegistry('./registry.jsonl') // Append-only log
|
|
2373
|
+
});
|
|
2374
|
+
```
|
|
651
2375
|
|
|
652
|
-
|
|
2376
|
+
**When to use each:**
|
|
2377
|
+
- `HttpTrustRegistry`: Production deployments, multi-agent coordination, DID resolution across networks
|
|
2378
|
+
- `MemoryTrustRegistry`: Testing, development, short-lived agents that don't need persistence
|
|
2379
|
+
- `FileTrustRegistry`: Enterprise deployments, local persistence, offline-capable systems
|
|
653
2380
|
|
|
654
|
-
|
|
2381
|
+
**`transport` - Multi-Transport Setup**
|
|
2382
|
+
|
|
2383
|
+
Configure message delivery channels. Single transport for simple deployments, multiple transports for redundancy or split-channel security.
|
|
655
2384
|
|
|
656
2385
|
```typescript
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
2386
|
+
import { Agent, HttpsTransportAdapter, RetryTransportAdapter } from '@private.me/xbind';
|
|
2387
|
+
|
|
2388
|
+
// Single transport (default)
|
|
2389
|
+
const simpleAgent = await Agent.create({
|
|
2390
|
+
name: 'simple-agent',
|
|
2391
|
+
transport: new HttpsTransportAdapter('https://private.me/relay')
|
|
661
2392
|
});
|
|
662
2393
|
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
2394
|
+
// Retry wrapper for unreliable networks
|
|
2395
|
+
const reliableTransport = new RetryTransportAdapter(
|
|
2396
|
+
new HttpsTransportAdapter('https://private.me/relay'),
|
|
2397
|
+
{
|
|
2398
|
+
maxRetries: 3, // 3 retry attempts
|
|
2399
|
+
baseDelayMs: 1000, // 1s base delay
|
|
2400
|
+
maxJitterMs: 200 // ±200ms jitter
|
|
2401
|
+
}
|
|
2402
|
+
);
|
|
2403
|
+
|
|
2404
|
+
const reliableAgent = await Agent.create({
|
|
2405
|
+
name: 'reliable-agent',
|
|
2406
|
+
transport: reliableTransport
|
|
667
2407
|
});
|
|
668
2408
|
|
|
669
|
-
//
|
|
670
|
-
await
|
|
671
|
-
|
|
672
|
-
|
|
2409
|
+
// Multi-transport for split-channel (advanced)
|
|
2410
|
+
const multiAgent = await Agent.create({
|
|
2411
|
+
name: 'split-channel-agent',
|
|
2412
|
+
transport: [
|
|
2413
|
+
new HttpsTransportAdapter('https://relay1.private.me'),
|
|
2414
|
+
new HttpsTransportAdapter('https://relay2.private.me'),
|
|
2415
|
+
new HttpsTransportAdapter('https://relay3.private.me')
|
|
2416
|
+
]
|
|
673
2417
|
});
|
|
2418
|
+
// Each XorIDA share routes to transport[shareIndex % transports.length]
|
|
2419
|
+
// Share 0 → relay1, Share 1 → relay2, Share 2 → relay3
|
|
2420
|
+
```
|
|
674
2421
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
2422
|
+
**When to use:**
|
|
2423
|
+
- Single transport: Most use cases, simple deployments
|
|
2424
|
+
- `RetryTransportAdapter`: Mobile networks, unreliable connections, push notification delivery
|
|
2425
|
+
- Multi-transport: Critical security scenarios requiring channel separation (prevents correlation attacks)
|
|
2426
|
+
|
|
2427
|
+
**`postQuantumSig` - Post-Quantum Signatures**
|
|
2428
|
+
|
|
2429
|
+
Enable ML-DSA-65 (FIPS 204) post-quantum signatures for future-proof authentication.
|
|
2430
|
+
|
|
2431
|
+
```typescript
|
|
2432
|
+
const quantumAgent = await Agent.create({
|
|
2433
|
+
name: 'quantum-safe-agent',
|
|
2434
|
+
postQuantumSig: true // Default: false
|
|
679
2435
|
});
|
|
2436
|
+
|
|
2437
|
+
// Sends v3 envelopes with hybrid signatures:
|
|
2438
|
+
// - Ed25519 (classical, 128-bit security)
|
|
2439
|
+
// - ML-DSA-65 (post-quantum, NIST Level 3)
|
|
680
2440
|
```
|
|
681
2441
|
|
|
682
|
-
|
|
2442
|
+
**When to enable:**
|
|
2443
|
+
- Long-term data protection (10+ year confidentiality requirements)
|
|
2444
|
+
- Compliance with post-quantum cryptography mandates
|
|
2445
|
+
- High-security government/defense applications
|
|
2446
|
+
- Preparing for quantum computing threats
|
|
683
2447
|
|
|
684
|
-
|
|
2448
|
+
**Trade-offs:**
|
|
2449
|
+
- Signature size: +2.5KB per message (Ed25519: 64 bytes → ML-DSA-65: 2,701 bytes)
|
|
2450
|
+
- Performance: ~10% slower signing/verification
|
|
2451
|
+
- Compatibility: Requires peers to support v3 envelopes (xBind v0.2.0+)
|
|
2452
|
+
|
|
2453
|
+
**Default: `false`** (most applications don't need post-quantum signatures yet)
|
|
2454
|
+
|
|
2455
|
+
**`backupConfig` - Key Backup Configuration**
|
|
2456
|
+
|
|
2457
|
+
Configure XorIDA threshold sharing for key backup and recovery.
|
|
685
2458
|
|
|
686
2459
|
```typescript
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
2460
|
+
import { Agent, DEFAULT_BACKUP_CONFIG } from '@private.me/xbind';
|
|
2461
|
+
|
|
2462
|
+
// Default: 2-of-3 (any 2 shares recover key)
|
|
2463
|
+
const defaultAgent = await Agent.create({
|
|
2464
|
+
name: 'default-backup',
|
|
2465
|
+
backupConfig: DEFAULT_BACKUP_CONFIG // threshold: 2, totalShares: 3
|
|
691
2466
|
});
|
|
692
2467
|
|
|
693
|
-
//
|
|
694
|
-
await
|
|
695
|
-
|
|
696
|
-
|
|
2468
|
+
// High security: 3-of-5 (lose 2 shares, still recover)
|
|
2469
|
+
const secureAgent = await Agent.create({
|
|
2470
|
+
name: 'secure-backup',
|
|
2471
|
+
backupConfig: {
|
|
2472
|
+
threshold: 3, // Need 3 shares to reconstruct
|
|
2473
|
+
totalShares: 5 // Generate 5 shares (can lose 2)
|
|
2474
|
+
}
|
|
2475
|
+
});
|
|
2476
|
+
|
|
2477
|
+
// Minimal: 2-of-2 (both shares required)
|
|
2478
|
+
const minimalAgent = await Agent.create({
|
|
2479
|
+
name: 'minimal-backup',
|
|
2480
|
+
backupConfig: {
|
|
2481
|
+
threshold: 2,
|
|
2482
|
+
totalShares: 2 // No fault tolerance (both required)
|
|
2483
|
+
}
|
|
697
2484
|
});
|
|
698
2485
|
```
|
|
699
2486
|
|
|
700
|
-
|
|
2487
|
+
**When to use each:**
|
|
2488
|
+
- `2-of-3` (default): Balance security and convenience, tolerate 1 lost share
|
|
2489
|
+
- `3-of-5`: High-security scenarios, tolerate 2 lost shares, enterprise deployments
|
|
2490
|
+
- `2-of-2`: Minimal overhead, no fault tolerance, testing only
|
|
701
2491
|
|
|
702
|
-
|
|
2492
|
+
**Information-theoretic security:** Each share reveals zero information about the key. Even with `threshold - 1` shares, an attacker gains no information.
|
|
2493
|
+
|
|
2494
|
+
**`name` - Agent Naming Best Practices**
|
|
2495
|
+
|
|
2496
|
+
Agent names are human-readable identifiers used in logs, debugging, and trust registry lookups.
|
|
703
2497
|
|
|
704
2498
|
```typescript
|
|
705
|
-
//
|
|
706
|
-
await
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
2499
|
+
// Good: Descriptive, kebab-case, unique per role
|
|
2500
|
+
const agent1 = await Agent.create({ name: 'email-service-agent' });
|
|
2501
|
+
const agent2 = await Agent.create({ name: 'payment-processor' });
|
|
2502
|
+
const agent3 = await Agent.create({ name: 'backup-validator' });
|
|
2503
|
+
|
|
2504
|
+
// Avoid: Generic names that don't identify purpose
|
|
2505
|
+
const bad1 = await Agent.create({ name: 'agent1' }); // Not descriptive
|
|
2506
|
+
const bad2 = await Agent.create({ name: 'test' }); // Too generic
|
|
2507
|
+
```
|
|
711
2508
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
2509
|
+
**Conventions:**
|
|
2510
|
+
- Use kebab-case for consistency (`email-service`, not `EmailService` or `email_service`)
|
|
2511
|
+
- Include role/purpose (`payment-processor`, not `agent42`)
|
|
2512
|
+
- Keep under 50 characters for logging compatibility
|
|
2513
|
+
- Avoid PII or sensitive data in names (logged in plaintext)
|
|
2514
|
+
|
|
2515
|
+
**Name vs DID:**
|
|
2516
|
+
- **Name**: Human-readable, mutable, used for debugging/logs
|
|
2517
|
+
- **DID**: Cryptographic identifier, immutable, used for authentication
|
|
2518
|
+
|
|
2519
|
+
**`xchange` - Performance Mode**
|
|
2520
|
+
|
|
2521
|
+
Enable Xchange mode for 180× faster message delivery with single-layer information-theoretic security.
|
|
2522
|
+
|
|
2523
|
+
```typescript
|
|
2524
|
+
const performanceAgent = await Agent.create({
|
|
2525
|
+
name: 'performance-agent',
|
|
2526
|
+
xchange: true // Default: false
|
|
717
2527
|
});
|
|
718
2528
|
|
|
719
|
-
//
|
|
720
|
-
await
|
|
2529
|
+
// Sending with Xchange mode
|
|
2530
|
+
await performanceAgent.send({
|
|
721
2531
|
to: recipientDid,
|
|
722
|
-
payload:
|
|
723
|
-
|
|
2532
|
+
payload: { action: 'transfer', amount: 100 },
|
|
2533
|
+
scope: 'payments'
|
|
2534
|
+
// Automatically uses v4 envelopes (XorIDA split + Ed25519, no KEM)
|
|
724
2535
|
});
|
|
725
2536
|
```
|
|
726
2537
|
|
|
727
|
-
|
|
2538
|
+
**When to enable:**
|
|
2539
|
+
- High-throughput systems (>1000 messages/second)
|
|
2540
|
+
- Latency-sensitive applications (real-time chat, gaming)
|
|
2541
|
+
- Bandwidth-constrained networks (mobile, IoT)
|
|
2542
|
+
- Internal microservice communication (trusted network)
|
|
728
2543
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
| `high` / `critical` / >$1M | 5 | 3 | 3-of-5 threshold |
|
|
734
|
-
| No tag / <$100k | — | — | Standard encrypted transport |
|
|
2544
|
+
**Security model:**
|
|
2545
|
+
- Single layer: Information-theoretic XorIDA split (provably secure)
|
|
2546
|
+
- Authentication: Ed25519 signatures (128-bit security)
|
|
2547
|
+
- No KEM: Skips hybrid key encapsulation (saves ~180× overhead)
|
|
735
2548
|
|
|
736
|
-
**
|
|
2549
|
+
**Trade-offs:**
|
|
2550
|
+
- Performance: ~180× faster than v3 split-channel
|
|
2551
|
+
- Security: Single IT layer vs triple-layer (IT + classical + post-quantum)
|
|
2552
|
+
- Use case: Trusted networks where triple-layer is overkill
|
|
737
2553
|
|
|
738
|
-
|
|
2554
|
+
**Default: `false`** (prioritize maximum security over performance)
|
|
739
2555
|
|
|
740
|
-
|
|
2556
|
+
## Section 2: Trust & Key Agreement
|
|
741
2557
|
|
|
742
|
-
|
|
743
|
-
- **Grace Buffer:** Additional operations available after email verification
|
|
744
|
-
- **Pro Tier:** Usage-based billing with unlimited operations
|
|
745
|
-
- **Monthly Reset:** 1st of each month at 00:00 UTC
|
|
2558
|
+
### Trust & Key Agreement
|
|
746
2559
|
|
|
747
|
-
|
|
2560
|
+
**Generate ephemeral X25519 key pair:**
|
|
748
2561
|
|
|
749
|
-
|
|
2562
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2563
|
+
```typescript
|
|
2564
|
+
import { generateEphemeralKeyPair } from '@private.me/xbind';
|
|
750
2565
|
|
|
751
|
-
|
|
2566
|
+
const ephemeral = await generateEphemeralKeyPair();
|
|
2567
|
+
if (!ephemeral.ok) {
|
|
2568
|
+
throw new Error(`Key generation failed: ${ephemeral.error}`);
|
|
2569
|
+
}
|
|
752
2570
|
|
|
753
|
-
|
|
2571
|
+
console.log('Ephemeral public key (32 bytes):', ephemeral.value.rawPublicKey);
|
|
2572
|
+
// Use ephemeral.value.privateKey for ECDH, then discard
|
|
2573
|
+
```
|
|
2574
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
754
2575
|
|
|
755
|
-
**
|
|
756
|
-
- Metering logic: `apps/server/src/customer-metering.ts`
|
|
757
|
-
- Milestone system: `apps/server/src/usage-milestone-notifications.ts`
|
|
758
|
-
- Tier pattern: See `docs/gold-package.md` section 7.6.5
|
|
2576
|
+
**Import X25519 public key:**
|
|
759
2577
|
|
|
760
|
-
|
|
2578
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2579
|
+
```typescript
|
|
2580
|
+
import { importX25519PublicKey } from '@private.me/xbind';
|
|
761
2581
|
|
|
762
|
-
|
|
2582
|
+
const rawPublicKey = new Uint8Array(32); // From recipient's identity
|
|
2583
|
+
const result = await importX25519PublicKey(rawPublicKey);
|
|
763
2584
|
|
|
764
|
-
|
|
2585
|
+
if (!result.ok) {
|
|
2586
|
+
throw new Error(`Import failed: ${result.error}`);
|
|
2587
|
+
}
|
|
765
2588
|
|
|
766
|
-
|
|
2589
|
+
const publicKey = result.value; // CryptoKey for ECDH
|
|
2590
|
+
```
|
|
2591
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
767
2592
|
|
|
768
|
-
|
|
769
|
-
|-------|----------|-----|----------|
|
|
770
|
-
| Invite Code | Single-use, 24h TTL | 6-char code | Customer onboarding |
|
|
771
|
-
| QR Code | Physical proximity, 60s TTL | Scan & tap | Mobile/desktop pairing |
|
|
772
|
-
| Trust Registry | Admin pre-auth, scoped | Zero (automated) | Enterprise/CI/CD |
|
|
773
|
-
| Peer Discovery | LAN proximity, user confirm | Scan & confirm | IoT devices, offline |
|
|
2593
|
+
**Derive shared key via ECDH:**
|
|
774
2594
|
|
|
775
|
-
|
|
2595
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2596
|
+
```typescript
|
|
2597
|
+
import { deriveSharedKeyECDH, generateEphemeralKeyPair, importX25519PublicKey } from '@private.me/xbind';
|
|
776
2598
|
|
|
777
|
-
|
|
2599
|
+
// Generate ephemeral key
|
|
2600
|
+
const ephemeral = await generateEphemeralKeyPair();
|
|
2601
|
+
if (!ephemeral.ok) throw new Error(ephemeral.error);
|
|
778
2602
|
|
|
779
|
-
|
|
2603
|
+
// Import recipient's public key
|
|
2604
|
+
const recipientKey = await importX25519PublicKey(recipientPubKeyBytes);
|
|
2605
|
+
if (!recipientKey.ok) throw new Error(recipientKey.error);
|
|
2606
|
+
|
|
2607
|
+
// Derive shared AES-256-GCM key
|
|
2608
|
+
const shared = await deriveSharedKeyECDH(
|
|
2609
|
+
ephemeral.value.privateKey,
|
|
2610
|
+
recipientKey.value
|
|
2611
|
+
);
|
|
780
2612
|
|
|
2613
|
+
if (shared.ok) {
|
|
2614
|
+
console.log('Shared key derived for AES-256-GCM');
|
|
2615
|
+
}
|
|
2616
|
+
```
|
|
2617
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2618
|
+
|
|
2619
|
+
**Sender-side key agreement:**
|
|
2620
|
+
|
|
2621
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
781
2622
|
```typescript
|
|
782
|
-
import {
|
|
2623
|
+
import { senderKeyAgreement, importX25519PublicKey, generateIdentity } from '@private.me/xbind';
|
|
783
2624
|
|
|
784
|
-
//
|
|
785
|
-
const
|
|
786
|
-
|
|
2625
|
+
// Generate recipient identity for testing (in production, get from registry)
|
|
2626
|
+
const recipientResult = await generateIdentity();
|
|
2627
|
+
if (!recipientResult.ok) throw new Error(recipientResult.error);
|
|
2628
|
+
const recipientPubKeyRaw = recipientResult.value.rawX25519PublicKey;
|
|
2629
|
+
|
|
2630
|
+
// Import recipient's X25519 public key
|
|
2631
|
+
const recipientPubKey = await importX25519PublicKey(recipientPubKeyRaw);
|
|
2632
|
+
if (!recipientPubKey.ok) throw new Error(recipientPubKey.error);
|
|
2633
|
+
|
|
2634
|
+
// Perform key agreement
|
|
2635
|
+
const agreement = await senderKeyAgreement(recipientPubKey.value);
|
|
2636
|
+
if (!agreement.ok) throw new Error(agreement.error);
|
|
2637
|
+
|
|
2638
|
+
// Use shared key for encryption
|
|
2639
|
+
const { sharedKey, ephemeralPublicKey } = agreement.value;
|
|
2640
|
+
console.log('Ephemeral public key to include in envelope:', ephemeralPublicKey);
|
|
2641
|
+
```
|
|
2642
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2643
|
+
|
|
2644
|
+
**Receiver-side key agreement:**
|
|
2645
|
+
|
|
2646
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2647
|
+
```typescript
|
|
2648
|
+
import { receiverKeyAgreement } from '@private.me/xbind';
|
|
2649
|
+
|
|
2650
|
+
// Extract ephemeral public key from envelope
|
|
2651
|
+
const senderEphemeralPubRaw = envelope.ephemeralPublicKey;
|
|
2652
|
+
|
|
2653
|
+
// Derive shared key using receiver's static private key
|
|
2654
|
+
const shared = await receiverKeyAgreement(
|
|
2655
|
+
receiverPrivateKey,
|
|
2656
|
+
senderEphemeralPubRaw
|
|
2657
|
+
);
|
|
2658
|
+
|
|
2659
|
+
if (shared.ok) {
|
|
2660
|
+
const sharedKey = shared.value;
|
|
2661
|
+
// Use sharedKey for decryption
|
|
2662
|
+
}
|
|
2663
|
+
```
|
|
2664
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2665
|
+
|
|
2666
|
+
**Hybrid key agreement (X25519 + ML-KEM-768) - Sender:**
|
|
2667
|
+
|
|
2668
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2669
|
+
```typescript
|
|
2670
|
+
import { senderHybridKeyAgreement } from '@private.me/xbind';
|
|
2671
|
+
|
|
2672
|
+
// Recipient's keys from registry
|
|
2673
|
+
const recipientX25519Pub = recipientEntry.x25519PublicKey; // CryptoKey
|
|
2674
|
+
const recipientMlKemPub = recipientEntry.mlKemPublicKey; // 1184 bytes
|
|
2675
|
+
|
|
2676
|
+
const result = await senderHybridKeyAgreement(
|
|
2677
|
+
recipientX25519Pub,
|
|
2678
|
+
recipientMlKemPub
|
|
2679
|
+
);
|
|
2680
|
+
|
|
2681
|
+
if (!result.ok) {
|
|
2682
|
+
throw new Error(`Hybrid key agreement failed: ${result.error}`);
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
const { sharedKey, ephemeralPublicKey, kemCiphertext } = result.value;
|
|
2686
|
+
// Include ephemeralPublicKey (32B) and kemCiphertext (1088B) in envelope
|
|
2687
|
+
```
|
|
2688
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2689
|
+
|
|
2690
|
+
**Hybrid key agreement (X25519 + ML-KEM-768) - Receiver:**
|
|
2691
|
+
|
|
2692
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2693
|
+
```typescript
|
|
2694
|
+
import { receiverHybridKeyAgreement } from '@private.me/xbind';
|
|
2695
|
+
|
|
2696
|
+
// Receiver's keys
|
|
2697
|
+
const x25519PrivateKey = identity.x25519PrivateKey; // CryptoKey
|
|
2698
|
+
const x25519PublicKeyRaw = identity.x25519PublicKey; // 32 bytes
|
|
2699
|
+
const mlKemSecretKey = identity.mlKemSecretKey; // 2400 bytes
|
|
2700
|
+
const mlKemPublicKey = identity.mlKemPublicKey; // 1184 bytes
|
|
2701
|
+
|
|
2702
|
+
// Envelope data
|
|
2703
|
+
const ephPubRaw = envelope.ephemeralPublicKey; // 32 bytes
|
|
2704
|
+
const kemCiphertext = envelope.kemCiphertext; // 1088 bytes
|
|
2705
|
+
|
|
2706
|
+
const result = await receiverHybridKeyAgreement(
|
|
2707
|
+
x25519PrivateKey,
|
|
2708
|
+
x25519PublicKeyRaw,
|
|
2709
|
+
ephPubRaw,
|
|
2710
|
+
kemCiphertext,
|
|
2711
|
+
mlKemSecretKey,
|
|
2712
|
+
mlKemPublicKey
|
|
2713
|
+
);
|
|
2714
|
+
|
|
2715
|
+
if (result.ok) {
|
|
2716
|
+
const sharedKey = result.value; // Same AES-256-GCM key as sender
|
|
2717
|
+
}
|
|
2718
|
+
```
|
|
2719
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2720
|
+
|
|
2721
|
+
**File-based trust registry:**
|
|
2722
|
+
|
|
2723
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2724
|
+
```typescript
|
|
2725
|
+
import { FileTrustRegistry } from '@private.me/xbind';
|
|
2726
|
+
|
|
2727
|
+
const registry = new FileTrustRegistry({
|
|
2728
|
+
path: '/opt/corp/trust.jsonl'
|
|
2729
|
+
});
|
|
2730
|
+
|
|
2731
|
+
// Register agent
|
|
2732
|
+
await registry.register(
|
|
2733
|
+
'did:key:z6Mk...',
|
|
2734
|
+
publicKeyBytes,
|
|
2735
|
+
'Corporate Gateway',
|
|
2736
|
+
['billing', 'auth'],
|
|
2737
|
+
x25519PublicKey,
|
|
2738
|
+
mlKemPublicKey,
|
|
2739
|
+
mlDsaPublicKey,
|
|
2740
|
+
true, // xchange enabled
|
|
2741
|
+
['billing', 'auth'], // receive scopes
|
|
2742
|
+
'3.0.3', // SDK version
|
|
2743
|
+
1, // minEnvelopeVersion
|
|
2744
|
+
4 // maxEnvelopeVersion
|
|
2745
|
+
);
|
|
2746
|
+
|
|
2747
|
+
// Resolve DID
|
|
2748
|
+
const result = await registry.resolve('did:key:z6Mk...');
|
|
2749
|
+
if (result.ok) {
|
|
2750
|
+
console.log('Public key:', result.value);
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
// Cleanup expired entries
|
|
2754
|
+
const removed = await registry.cleanup();
|
|
2755
|
+
console.log('Removed entries:', removed);
|
|
2756
|
+
```
|
|
2757
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2758
|
+
|
|
2759
|
+
**Enterprise trust registry factory:**
|
|
2760
|
+
|
|
2761
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2762
|
+
```typescript
|
|
2763
|
+
import { createEnterpriseTrustRegistry } from '@private.me/xbind';
|
|
2764
|
+
|
|
2765
|
+
const registry = await createEnterpriseTrustRegistry({
|
|
2766
|
+
storage: 'file',
|
|
2767
|
+
path: '/opt/corp/trust.jsonl',
|
|
2768
|
+
preload: [
|
|
2769
|
+
{
|
|
2770
|
+
did: 'did:web:corp.example.com',
|
|
2771
|
+
publicKey: corporateGatewayPubKey,
|
|
2772
|
+
name: 'Corporate Gateway',
|
|
2773
|
+
scopes: ['billing', 'auth', 'payments'],
|
|
2774
|
+
x25519PublicKey: corporateX25519Key,
|
|
2775
|
+
mlKemPublicKey: corporateMlKemKey
|
|
2776
|
+
}
|
|
2777
|
+
]
|
|
2778
|
+
});
|
|
2779
|
+
|
|
2780
|
+
console.log('Enterprise registry initialized with pre-loaded entries');
|
|
2781
|
+
```
|
|
2782
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2783
|
+
|
|
2784
|
+
**Message stream with async iteration:**
|
|
2785
|
+
|
|
2786
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2787
|
+
```typescript
|
|
2788
|
+
import { MessageStream, collectMessages } from '@private.me/xbind';
|
|
2789
|
+
|
|
2790
|
+
// Subscribe to all messages from Alice
|
|
2791
|
+
for await (const message of agent.subscribe({ from: 'did:key:alice' })) {
|
|
2792
|
+
console.log('Received:', message.payload);
|
|
2793
|
+
if (message.scope === 'urgent') break; // Early termination
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
// Collect batch of messages with timeout
|
|
2797
|
+
const messages = await collectMessages(agent, {
|
|
2798
|
+
from: 'did:key:alice',
|
|
2799
|
+
limit: 10,
|
|
2800
|
+
timeout: 5000
|
|
787
2801
|
});
|
|
2802
|
+
|
|
2803
|
+
console.log('Collected messages:', messages.length);
|
|
2804
|
+
```
|
|
2805
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2806
|
+
|
|
2807
|
+
**Parse agent errors:**
|
|
2808
|
+
|
|
2809
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
2810
|
+
```typescript
|
|
2811
|
+
import { parseAgentError, isXBindError } from '@private.me/xbind';
|
|
2812
|
+
|
|
2813
|
+
try {
|
|
2814
|
+
await agent.send({ to: recipientDid, payload: data });
|
|
2815
|
+
} catch (error) {
|
|
2816
|
+
if (isXBindError(error)) {
|
|
2817
|
+
console.error('Code:', error.code);
|
|
2818
|
+
console.error('Sub-code:', error.subCode);
|
|
2819
|
+
console.error('Docs:', error.docUrl);
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
788
2822
|
```
|
|
2823
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
2824
|
+
|
|
2825
|
+
---
|
|
789
2826
|
|
|
2827
|
+
## Summary
|
|
2828
|
+
|
|
2829
|
+
**Batch 4 Examples (13 exports - Operational Excellence):**
|
|
2830
|
+
1. TimeoutConfig - Timeout configuration manager
|
|
2831
|
+
2. OperationTimeoutController - Per-operation timeout control
|
|
2832
|
+
3. createTimeoutController - Create timeout controller
|
|
2833
|
+
4. withTimeout - Wrap function with timeout
|
|
2834
|
+
5. withTimeoutResult - Wrap with Result timeout
|
|
2835
|
+
6. createOperationTimeoutSignal - Create AbortSignal
|
|
2836
|
+
7. createOperationTimeout - Create timeout config
|
|
2837
|
+
8. isTimeoutError - Check timeout error
|
|
2838
|
+
9. getTimeoutFromError - Extract timeout
|
|
2839
|
+
10. createTimeoutConfigFromEnv - Create from env
|
|
2840
|
+
11. globalTimeoutConfig - Global config
|
|
2841
|
+
12. GracefulDegradationManager - Graceful degradation
|
|
2842
|
+
13. registryLookupWithFallback - Registry with fallback
|
|
2843
|
+
|
|
2844
|
+
**Batch 5 Examples (15 exports - Trust & Discovery):**
|
|
2845
|
+
1. sendWithTransportFallback - Transport fallback
|
|
2846
|
+
2. enhanceError - Error enhancement (not shown - internal)
|
|
2847
|
+
3. FileTrustRegistry - File-based registry
|
|
2848
|
+
4. createEnterpriseTrustRegistry - Enterprise factory
|
|
2849
|
+
5. generateEphemeralKeyPair - Generate X25519 pair
|
|
2850
|
+
6. importX25519PublicKey - Import X25519 key
|
|
2851
|
+
7. deriveSharedKeyECDH - ECDH derivation
|
|
2852
|
+
8. senderKeyAgreement - Sender key agreement
|
|
2853
|
+
9. receiverKeyAgreement - Receiver key agreement
|
|
2854
|
+
10. combineSharedSecrets - Combine secrets (not shown - internal)
|
|
2855
|
+
11. senderHybridKeyAgreement - Hybrid sender
|
|
2856
|
+
12. receiverHybridKeyAgreement - Hybrid receiver
|
|
2857
|
+
13. parseAgentError - Parse errors
|
|
2858
|
+
14. MessageStream - Async iteration
|
|
2859
|
+
15. collectMessages - Batch collect
|
|
2860
|
+
|
|
2861
|
+
**Total: 28 new code examples for Batch 4 & 5 exports**
|
|
790
2862
|
### Advanced Features
|
|
791
2863
|
|
|
792
2864
|
Checkpoints, sequence numbers, subscription proofs, XorIDA configuration, key backup (2-of-3 default), DID succession. See [Configuration Guide](./docs/configuration.md) and [Examples](./docs/examples.md).
|
|
@@ -795,6 +2867,7 @@ Checkpoints, sequence numbers, subscription proofs, XorIDA configuration, key ba
|
|
|
795
2867
|
|
|
796
2868
|
xbind returns `Result<T, E>` types for type-safe error handling.
|
|
797
2869
|
|
|
2870
|
+
<!-- SNIPPET -->
|
|
798
2871
|
```typescript
|
|
799
2872
|
import { call, type CallResult } from '@private.me/xbind';
|
|
800
2873
|
|
|
@@ -808,6 +2881,7 @@ if (result.ok) {
|
|
|
808
2881
|
console.error(result.error.message);
|
|
809
2882
|
}
|
|
810
2883
|
```
|
|
2884
|
+
<!-- /SNIPPET -->
|
|
811
2885
|
|
|
812
2886
|
Error types: `ConnectionError`, `AuthenticationError`, `ValidationError`, `RateLimitError`, `ServerError`
|
|
813
2887
|
|
|
@@ -832,6 +2906,48 @@ pnpm test:coverage # Coverage report
|
|
|
832
2906
|
pnpm test:watch # Watch mode
|
|
833
2907
|
```
|
|
834
2908
|
|
|
2909
|
+
### Testing with LoopbackTransport
|
|
2910
|
+
|
|
2911
|
+
For unit testing and local development, use `LoopbackTransport` to simulate agent communication without network calls:
|
|
2912
|
+
|
|
2913
|
+
```typescript
|
|
2914
|
+
import { Agent, LoopbackTransport } from '@private.me/xbind';
|
|
2915
|
+
|
|
2916
|
+
// Create a shared in-memory transport
|
|
2917
|
+
const transport = new LoopbackTransport();
|
|
2918
|
+
|
|
2919
|
+
// Create two agents using the same transport
|
|
2920
|
+
const alice = await Agent.create({
|
|
2921
|
+
name: 'alice',
|
|
2922
|
+
transport
|
|
2923
|
+
});
|
|
2924
|
+
|
|
2925
|
+
const bob = await Agent.create({
|
|
2926
|
+
name: 'bob',
|
|
2927
|
+
transport
|
|
2928
|
+
});
|
|
2929
|
+
|
|
2930
|
+
// Messages are delivered instantly in-memory
|
|
2931
|
+
const result = await alice.send({
|
|
2932
|
+
to: bob.did,
|
|
2933
|
+
payload: { test: true }
|
|
2934
|
+
});
|
|
2935
|
+
|
|
2936
|
+
if (result.ok) {
|
|
2937
|
+
console.log('Message delivered via loopback transport');
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
// Bob can receive messages from Alice
|
|
2941
|
+
const envelope = // ... received via transport callback
|
|
2942
|
+
const received = await bob.receive(envelope);
|
|
2943
|
+
```
|
|
2944
|
+
|
|
2945
|
+
**Use cases:**
|
|
2946
|
+
- Unit testing agent communication without external services
|
|
2947
|
+
- Local development without network dependencies
|
|
2948
|
+
- CI/CD pipelines that need fast, deterministic tests
|
|
2949
|
+
- Integration testing of multi-agent workflows
|
|
2950
|
+
|
|
835
2951
|
## Full Control IP Protection
|
|
836
2952
|
|
|
837
2953
|
xBind uses **Full Control** (2-share XorIDA) to protect proprietary cryptographic algorithms while maintaining a seamless developer experience.
|
|
@@ -864,10 +2980,10 @@ When combined, Share 1 + Share 2 reconstruct the complete XorIDA algorithm at ru
|
|
|
864
2980
|
- At 120K ops: 402 Quota Exceeded → Upgrade prompt
|
|
865
2981
|
|
|
866
2982
|
- **Pro Tier:** Unlimited operations
|
|
867
|
-
-
|
|
2983
|
+
- Usage-based billing (after first 100K free)
|
|
868
2984
|
- No quota checks
|
|
869
2985
|
- Vault access unlimited
|
|
870
|
-
- Example: 400K ops
|
|
2986
|
+
- Example: 400K ops calculated based on current rates (see pricing link)
|
|
871
2987
|
|
|
872
2988
|
- **VIP Tier:** Custom limits per account
|
|
873
2989
|
- Bronze: 200K, Silver: 500K, Gold: 1M, Platinum: Unlimited
|
|
@@ -1023,3 +3139,49 @@ This package contains cryptographic software. Export restrictions may apply. Use
|
|
|
1023
3139
|
---
|
|
1024
3140
|
|
|
1025
3141
|
**Questions?** [Documentation](./docs/README.md) • [White paper](https://private.me/docs/xbind.html) • [Issues](https://github.com/xail-io/xail/issues)
|
|
3142
|
+
|
|
3143
|
+
### Stream Processing Utilities
|
|
3144
|
+
|
|
3145
|
+
**Transform message streams with map/filter/take:**
|
|
3146
|
+
|
|
3147
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
3148
|
+
```typescript
|
|
3149
|
+
import { mapStream, filterStream, takeStream, mergeStreams } from '@private.me/xbind';
|
|
3150
|
+
|
|
3151
|
+
// Map transform
|
|
3152
|
+
const doubled = mapStream(messageStream, (msg) => ({
|
|
3153
|
+
...msg,
|
|
3154
|
+
payload: msg.payload * 2
|
|
3155
|
+
}));
|
|
3156
|
+
|
|
3157
|
+
// Filter messages
|
|
3158
|
+
const urgent = filterStream(messageStream, (msg) => msg.scope === 'urgent');
|
|
3159
|
+
|
|
3160
|
+
// Take first N messages
|
|
3161
|
+
const first10 = takeStream(messageStream, 10);
|
|
3162
|
+
|
|
3163
|
+
// Merge multiple streams
|
|
3164
|
+
const combined = mergeStreams([streamA, streamB, streamC]);
|
|
3165
|
+
|
|
3166
|
+
for await (const message of combined) {
|
|
3167
|
+
console.log('Received from merged streams:', message);
|
|
3168
|
+
}
|
|
3169
|
+
```
|
|
3170
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
3171
|
+
|
|
3172
|
+
**Install async iterator support:**
|
|
3173
|
+
|
|
3174
|
+
<!-- RUNNABLE-EXAMPLE -->
|
|
3175
|
+
```typescript
|
|
3176
|
+
import { installAsyncIterators } from '@private.me/xbind';
|
|
3177
|
+
|
|
3178
|
+
// Enable async iterator methods on Agent
|
|
3179
|
+
installAsyncIterators();
|
|
3180
|
+
|
|
3181
|
+
// Now you can use async iteration
|
|
3182
|
+
for await (const message of agent.messages()) {
|
|
3183
|
+
console.log('Message received:', message.payload);
|
|
3184
|
+
}
|
|
3185
|
+
```
|
|
3186
|
+
<!-- /RUNNABLE-EXAMPLE -->
|
|
3187
|
+
|