@voidly/agent-sdk 3.2.6 → 3.2.7
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/examples/post-quantum.mjs +99 -0
- package/examples/sse-streaming.mjs +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Post-Quantum + Ratchet Persistence — Future-proof encrypted messaging.
|
|
4
|
+
*
|
|
5
|
+
* Run: node examples/post-quantum.mjs
|
|
6
|
+
*
|
|
7
|
+
* Features demonstrated:
|
|
8
|
+
* 1. ML-KEM-768 hybrid key exchange (NIST FIPS 203) — quantum-resistant
|
|
9
|
+
* 2. Double Ratchet forward secrecy — compromise now can't decrypt past messages
|
|
10
|
+
* 3. Ratchet persistence — session survives agent restarts
|
|
11
|
+
* 4. Sealed sender — relay can't see who sent the message
|
|
12
|
+
* 5. Deniable auth — both parties can produce the signature (plausible deniability)
|
|
13
|
+
*/
|
|
14
|
+
import { VoidlyAgent } from '@voidly/agent-sdk';
|
|
15
|
+
|
|
16
|
+
// ─── Register with full security config ─────────────────────────────────────
|
|
17
|
+
const alice = await VoidlyAgent.register(
|
|
18
|
+
{ name: 'pq-alice', capabilities: ['chat', 'intel'] },
|
|
19
|
+
{
|
|
20
|
+
pq: true, // Enable ML-KEM-768 post-quantum hybrid
|
|
21
|
+
padding: true, // Pad all messages to power-of-2 (traffic analysis resistance)
|
|
22
|
+
sealedSender: true, // Hide sender DID from relay metadata
|
|
23
|
+
deniable: true, // HMAC signatures (both parties can produce — deniable)
|
|
24
|
+
persist: 'memory', // Ratchet state persists in memory (use 'file' or 'relay' for disk)
|
|
25
|
+
autoPin: true, // TOFU: pin first-seen keys, warn on change
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const bob = await VoidlyAgent.register(
|
|
30
|
+
{ name: 'pq-bob', capabilities: ['chat', 'analysis'] },
|
|
31
|
+
{
|
|
32
|
+
pq: true,
|
|
33
|
+
padding: true,
|
|
34
|
+
sealedSender: true,
|
|
35
|
+
deniable: true,
|
|
36
|
+
persist: 'memory',
|
|
37
|
+
autoPin: true,
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
console.log(`Alice: ${alice.did} (PQ + sealed + deniable)`);
|
|
42
|
+
console.log(`Bob: ${bob.did} (PQ + sealed + deniable)\n`);
|
|
43
|
+
|
|
44
|
+
// ─── Verify post-quantum is active ──────────────────────────────────────────
|
|
45
|
+
const threat = alice.threatModel();
|
|
46
|
+
console.log('Active protections:');
|
|
47
|
+
threat.protections.forEach(p => console.log(` ✓ ${p}`));
|
|
48
|
+
console.log('Known gaps:');
|
|
49
|
+
threat.gaps.forEach(g => console.log(` ⚠ ${g}`));
|
|
50
|
+
|
|
51
|
+
// ─── Send messages with full protection stack ───────────────────────────────
|
|
52
|
+
console.log('\nSending with PQ hybrid + Double Ratchet + sealed sender + padding...');
|
|
53
|
+
await alice.send(bob.did, 'Quantum-resistant hello from Alice', { threadId: 'pq-demo' });
|
|
54
|
+
await alice.send(bob.did, 'Even a quantum computer cannot decrypt this retroactively');
|
|
55
|
+
|
|
56
|
+
// Bob receives and decrypts
|
|
57
|
+
const messages = await bob.receive({ limit: 10 });
|
|
58
|
+
for (const msg of messages) {
|
|
59
|
+
console.log(`\n Bob received: "${msg.content}"`);
|
|
60
|
+
console.log(` From: ${msg.from.slice(0, 30)}...`);
|
|
61
|
+
console.log(` Signature valid: ${msg.signatureValid}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─── Demonstrate forward secrecy ────────────────────────────────────────────
|
|
65
|
+
console.log('\n─── Forward Secrecy Demo ───');
|
|
66
|
+
console.log('Ratchet advances with each message — past keys are deleted.');
|
|
67
|
+
console.log('Even if an attacker compromises the agent NOW, they cannot');
|
|
68
|
+
console.log('decrypt messages that were already received and processed.\n');
|
|
69
|
+
|
|
70
|
+
// Bob replies — DH ratchet advances
|
|
71
|
+
await bob.send(alice.did, 'Reply from Bob — ratchet advanced');
|
|
72
|
+
const replies = await alice.receive({ limit: 5 });
|
|
73
|
+
for (const r of replies) {
|
|
74
|
+
console.log(` Alice received: "${r.content}"`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Credential export (includes ratchet state) ─────────────────────────────
|
|
78
|
+
console.log('\n─── Credential Export ───');
|
|
79
|
+
const creds = alice.exportCredentials();
|
|
80
|
+
console.log(` DID: ${creds.did}`);
|
|
81
|
+
console.log(` Signing key: ${creds.signingSecretKey.slice(0, 16)}...`);
|
|
82
|
+
console.log(` PQ enabled: ${!!creds.mlkemSecretKey}`);
|
|
83
|
+
|
|
84
|
+
// Restore agent from credentials (e.g., after restart)
|
|
85
|
+
const restored = VoidlyAgent.fromCredentials(creds, {
|
|
86
|
+
pq: true, padding: true, sealedSender: true, deniable: true, persist: 'memory',
|
|
87
|
+
});
|
|
88
|
+
console.log(` Restored DID: ${restored.did} (matches: ${restored.did === alice.did})`);
|
|
89
|
+
|
|
90
|
+
// ─── Flush ratchet state ────────────────────────────────────────────────────
|
|
91
|
+
await alice.flushRatchetState();
|
|
92
|
+
console.log('\n Ratchet state flushed to persistence backend.');
|
|
93
|
+
|
|
94
|
+
// Clean shutdown
|
|
95
|
+
alice.stopAll();
|
|
96
|
+
bob.stopAll();
|
|
97
|
+
restored.stopAll();
|
|
98
|
+
|
|
99
|
+
console.log('\n✓ Done — post-quantum hybrid encryption with forward secrecy.');
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SSE Streaming — Real-time message delivery via Server-Sent Events.
|
|
4
|
+
*
|
|
5
|
+
* Run: node examples/sse-streaming.mjs
|
|
6
|
+
*
|
|
7
|
+
* Instead of polling, Bob opens an SSE connection to the relay.
|
|
8
|
+
* Messages arrive in near-real-time (~1s latency) with automatic reconnection.
|
|
9
|
+
* All decryption still happens client-side.
|
|
10
|
+
*/
|
|
11
|
+
import { VoidlyAgent } from '@voidly/agent-sdk';
|
|
12
|
+
|
|
13
|
+
const alice = await VoidlyAgent.register({ name: 'sse-alice' });
|
|
14
|
+
const bob = await VoidlyAgent.register(
|
|
15
|
+
{ name: 'sse-bob' },
|
|
16
|
+
{ transport: ['sse', 'long-poll'] } // Prefer SSE, fall back to long-poll
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
console.log(`Alice: ${alice.did}`);
|
|
20
|
+
console.log(`Bob: ${bob.did} (SSE transport)\n`);
|
|
21
|
+
|
|
22
|
+
// Bob listens via SSE — messages arrive in near-real-time
|
|
23
|
+
const handle = bob.listen(
|
|
24
|
+
(msg) => {
|
|
25
|
+
console.log(` ← Bob received: "${msg.content}" from ${msg.from.slice(0, 24)}...`);
|
|
26
|
+
console.log(` Signature valid: ${msg.signatureValid}`);
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
interval: 2000, // Reconnect interval if SSE drops
|
|
30
|
+
adaptive: true, // Back off when idle
|
|
31
|
+
heartbeat: false, // Don't send pings
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Wait for SSE connection to establish
|
|
36
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
37
|
+
|
|
38
|
+
// Alice sends a burst of messages
|
|
39
|
+
console.log('Alice sending 3 messages...');
|
|
40
|
+
await alice.send(bob.did, 'Message 1 — SSE delivery');
|
|
41
|
+
await alice.send(bob.did, 'Message 2 — near-real-time');
|
|
42
|
+
await alice.send(bob.did, 'Message 3 — all encrypted');
|
|
43
|
+
|
|
44
|
+
// Wait for delivery
|
|
45
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
46
|
+
|
|
47
|
+
// Clean shutdown
|
|
48
|
+
handle.stop();
|
|
49
|
+
alice.stopAll();
|
|
50
|
+
bob.stopAll();
|
|
51
|
+
|
|
52
|
+
console.log('\n✓ Done — SSE streaming with E2E encryption.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voidly/agent-sdk",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.7",
|
|
4
4
|
"description": "E2E encrypted agent-to-agent communication SDK — Double Ratchet, X3DH, deniable auth, ML-KEM-768 post-quantum, SSE streaming, ratchet persistence, multi-relay federation",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|