myceliumail-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -0
- package/assets/icon.png +0 -0
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.js +18 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/crypto.d.ts +26 -0
- package/dist/lib/crypto.js +110 -0
- package/dist/lib/crypto.js.map +1 -0
- package/dist/lib/storage.d.ts +32 -0
- package/dist/lib/storage.js +209 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +306 -0
- package/dist/server.js.map +1 -0
- package/myceliumail-mcp-1.0.0.tgz +0 -0
- package/package.json +49 -0
- package/src/lib/config.ts +21 -0
- package/src/lib/crypto.ts +150 -0
- package/src/lib/storage.ts +257 -0
- package/src/server.ts +387 -0
- package/tsconfig.json +26 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Myceliumail MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Myceliumail messaging as MCP tools for Claude Desktop
|
|
6
|
+
* and other MCP-compatible clients.
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import * as crypto from './lib/crypto.js';
|
|
12
|
+
import * as storage from './lib/storage.js';
|
|
13
|
+
import { getAgentId } from './lib/config.js';
|
|
14
|
+
// Create the MCP server
|
|
15
|
+
const server = new McpServer({
|
|
16
|
+
name: 'myceliumail',
|
|
17
|
+
version: '1.0.0',
|
|
18
|
+
});
|
|
19
|
+
// Tool: check_inbox
|
|
20
|
+
server.tool('check_inbox', 'Check your Myceliumail inbox for messages', {
|
|
21
|
+
unread_only: z.boolean().optional().describe('Only show unread messages'),
|
|
22
|
+
limit: z.number().optional().describe('Maximum number of messages to return'),
|
|
23
|
+
}, async ({ unread_only, limit }) => {
|
|
24
|
+
const agentId = getAgentId();
|
|
25
|
+
const messages = await storage.getInbox(agentId, {
|
|
26
|
+
unreadOnly: unread_only,
|
|
27
|
+
limit: limit || 10,
|
|
28
|
+
});
|
|
29
|
+
if (messages.length === 0) {
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: 'š No messages in inbox' }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const formatted = messages.map(msg => {
|
|
35
|
+
const status = msg.read ? ' ' : 'ā ';
|
|
36
|
+
const encrypted = msg.encrypted ? 'š ' : '';
|
|
37
|
+
return `${status}${encrypted}[${msg.id.slice(0, 8)}] From: ${msg.sender} | ${msg.subject || '(no subject)'} | ${msg.createdAt.toLocaleString()}`;
|
|
38
|
+
}).join('\n');
|
|
39
|
+
return {
|
|
40
|
+
content: [{
|
|
41
|
+
type: 'text',
|
|
42
|
+
text: `š¬ Inbox (${messages.length} messages):\n\n${formatted}`
|
|
43
|
+
}],
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
// Tool: read_message
|
|
47
|
+
server.tool('read_message', 'Read a specific message by ID', {
|
|
48
|
+
message_id: z.string().describe('Message ID (can be partial)'),
|
|
49
|
+
}, async ({ message_id }) => {
|
|
50
|
+
const agentId = getAgentId();
|
|
51
|
+
let message = await storage.getMessage(message_id);
|
|
52
|
+
// Try partial ID match
|
|
53
|
+
if (!message) {
|
|
54
|
+
const inbox = await storage.getInbox(agentId, { limit: 100 });
|
|
55
|
+
message = inbox.find(m => m.id.startsWith(message_id)) || null;
|
|
56
|
+
}
|
|
57
|
+
if (!message) {
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: 'text', text: `ā Message not found: ${message_id}` }],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Mark as read
|
|
63
|
+
await storage.markAsRead(message.id);
|
|
64
|
+
// Decrypt if needed
|
|
65
|
+
let subject = message.subject;
|
|
66
|
+
let body = message.body;
|
|
67
|
+
if (message.encrypted && message.ciphertext && message.nonce && message.senderPublicKey) {
|
|
68
|
+
const keyPair = crypto.loadKeyPair(agentId);
|
|
69
|
+
if (keyPair) {
|
|
70
|
+
try {
|
|
71
|
+
const decrypted = crypto.decryptMessage({
|
|
72
|
+
ciphertext: message.ciphertext,
|
|
73
|
+
nonce: message.nonce,
|
|
74
|
+
senderPublicKey: message.senderPublicKey,
|
|
75
|
+
}, keyPair);
|
|
76
|
+
if (decrypted) {
|
|
77
|
+
const parsed = JSON.parse(decrypted);
|
|
78
|
+
subject = parsed.subject || subject;
|
|
79
|
+
body = parsed.body || body;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
body = '[Failed to decrypt]';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
body = '[Cannot decrypt - no keypair]';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const encrypted = message.encrypted ? '\nš Encrypted: Yes' : '';
|
|
91
|
+
const text = `āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
92
|
+
From: ${message.sender}
|
|
93
|
+
To: ${message.recipient}
|
|
94
|
+
Date: ${message.createdAt.toLocaleString()}
|
|
95
|
+
Subject: ${subject}${encrypted}
|
|
96
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
97
|
+
|
|
98
|
+
${body}
|
|
99
|
+
|
|
100
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
101
|
+
ID: ${message.id}`;
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: 'text', text }],
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
// Tool: send_message
|
|
107
|
+
server.tool('send_message', 'Send a message to another agent', {
|
|
108
|
+
recipient: z.string().describe('Recipient agent ID'),
|
|
109
|
+
subject: z.string().describe('Message subject'),
|
|
110
|
+
body: z.string().describe('Message body'),
|
|
111
|
+
encrypt: z.boolean().optional().describe('Encrypt the message (requires key exchange)'),
|
|
112
|
+
}, async ({ recipient, subject, body, encrypt }) => {
|
|
113
|
+
const sender = getAgentId();
|
|
114
|
+
if (sender === 'anonymous') {
|
|
115
|
+
return {
|
|
116
|
+
content: [{ type: 'text', text: 'ā Agent ID not configured. Set MYCELIUMAIL_AGENT_ID environment variable.' }],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
let messageOptions;
|
|
120
|
+
if (encrypt) {
|
|
121
|
+
const senderKeyPair = crypto.loadKeyPair(sender);
|
|
122
|
+
if (!senderKeyPair) {
|
|
123
|
+
return {
|
|
124
|
+
content: [{ type: 'text', text: 'ā No keypair found. Use generate_keys first.' }],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const recipientPubKeyB64 = crypto.getKnownKey(recipient);
|
|
128
|
+
if (!recipientPubKeyB64) {
|
|
129
|
+
return {
|
|
130
|
+
content: [{ type: 'text', text: `ā No public key found for ${recipient}. Use import_key first.` }],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const recipientPubKey = crypto.decodePublicKey(recipientPubKeyB64);
|
|
134
|
+
const payload = JSON.stringify({ subject, body });
|
|
135
|
+
const encrypted = crypto.encryptMessage(payload, recipientPubKey, senderKeyPair);
|
|
136
|
+
messageOptions = {
|
|
137
|
+
encrypted: true,
|
|
138
|
+
ciphertext: encrypted.ciphertext,
|
|
139
|
+
nonce: encrypted.nonce,
|
|
140
|
+
senderPublicKey: encrypted.senderPublicKey,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const message = await storage.sendMessage(sender, recipient, subject, body, messageOptions);
|
|
145
|
+
const encInfo = encrypt ? ' (š encrypted)' : '';
|
|
146
|
+
return {
|
|
147
|
+
content: [{
|
|
148
|
+
type: 'text',
|
|
149
|
+
text: `ā
Message sent to ${recipient}${encInfo}\nID: ${message.id}`
|
|
150
|
+
}],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
return {
|
|
155
|
+
content: [{ type: 'text', text: `ā Failed to send: ${error}` }],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
// Tool: reply_message
|
|
160
|
+
server.tool('reply_message', 'Reply to a message', {
|
|
161
|
+
message_id: z.string().describe('ID of message to reply to'),
|
|
162
|
+
body: z.string().describe('Reply message body'),
|
|
163
|
+
encrypt: z.boolean().optional().describe('Encrypt the reply'),
|
|
164
|
+
}, async ({ message_id, body, encrypt }) => {
|
|
165
|
+
const agentId = getAgentId();
|
|
166
|
+
// Find original message
|
|
167
|
+
let original = await storage.getMessage(message_id);
|
|
168
|
+
if (!original) {
|
|
169
|
+
const inbox = await storage.getInbox(agentId, { limit: 100 });
|
|
170
|
+
original = inbox.find(m => m.id.startsWith(message_id)) || null;
|
|
171
|
+
}
|
|
172
|
+
if (!original) {
|
|
173
|
+
return {
|
|
174
|
+
content: [{ type: 'text', text: `ā Message not found: ${message_id}` }],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// Send reply to original sender
|
|
178
|
+
const subject = original.subject.startsWith('Re: ')
|
|
179
|
+
? original.subject
|
|
180
|
+
: `Re: ${original.subject}`;
|
|
181
|
+
let messageOptions;
|
|
182
|
+
if (encrypt) {
|
|
183
|
+
const senderKeyPair = crypto.loadKeyPair(agentId);
|
|
184
|
+
const recipientPubKeyB64 = crypto.getKnownKey(original.sender);
|
|
185
|
+
if (senderKeyPair && recipientPubKeyB64) {
|
|
186
|
+
const recipientPubKey = crypto.decodePublicKey(recipientPubKeyB64);
|
|
187
|
+
const payload = JSON.stringify({ subject, body });
|
|
188
|
+
const encrypted = crypto.encryptMessage(payload, recipientPubKey, senderKeyPair);
|
|
189
|
+
messageOptions = {
|
|
190
|
+
encrypted: true,
|
|
191
|
+
ciphertext: encrypted.ciphertext,
|
|
192
|
+
nonce: encrypted.nonce,
|
|
193
|
+
senderPublicKey: encrypted.senderPublicKey,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const message = await storage.sendMessage(agentId, original.sender, subject, body, messageOptions);
|
|
198
|
+
const encInfo = encrypt && messageOptions ? ' (š encrypted)' : '';
|
|
199
|
+
return {
|
|
200
|
+
content: [{
|
|
201
|
+
type: 'text',
|
|
202
|
+
text: `ā
Reply sent to ${original.sender}${encInfo}\nID: ${message.id}`
|
|
203
|
+
}],
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
// Tool: generate_keys
|
|
207
|
+
server.tool('generate_keys', 'Generate encryption keypair for this agent', {
|
|
208
|
+
force: z.boolean().optional().describe('Overwrite existing keypair'),
|
|
209
|
+
}, async ({ force }) => {
|
|
210
|
+
const agentId = getAgentId();
|
|
211
|
+
if (crypto.hasKeyPair(agentId) && !force) {
|
|
212
|
+
const existing = crypto.loadKeyPair(agentId);
|
|
213
|
+
if (existing) {
|
|
214
|
+
const pubKey = crypto.getPublicKeyBase64(existing);
|
|
215
|
+
return {
|
|
216
|
+
content: [{
|
|
217
|
+
type: 'text',
|
|
218
|
+
text: `ā ļø Keypair already exists for ${agentId}\n\nš§ Your public key:\n${pubKey}\n\nUse force=true to regenerate.`
|
|
219
|
+
}],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const keyPair = crypto.generateKeyPair();
|
|
224
|
+
crypto.saveKeyPair(agentId, keyPair);
|
|
225
|
+
const publicKey = crypto.getPublicKeyBase64(keyPair);
|
|
226
|
+
return {
|
|
227
|
+
content: [{
|
|
228
|
+
type: 'text',
|
|
229
|
+
text: `š Keypair generated for ${agentId}\n\nš§ Your public key (share with other agents):\n${publicKey}`
|
|
230
|
+
}],
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
// Tool: list_keys
|
|
234
|
+
server.tool('list_keys', 'List all known encryption keys', {}, async () => {
|
|
235
|
+
const agentId = getAgentId();
|
|
236
|
+
const ownKeys = crypto.listOwnKeys();
|
|
237
|
+
const knownKeys = crypto.loadKnownKeys();
|
|
238
|
+
let output = 'š Encryption Keys\n\nāā Your Keys āā\n';
|
|
239
|
+
if (ownKeys.length === 0) {
|
|
240
|
+
output += 'No keypairs. Use generate_keys to create one.\n';
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
for (const id of ownKeys) {
|
|
244
|
+
const kp = crypto.loadKeyPair(id);
|
|
245
|
+
if (kp) {
|
|
246
|
+
const marker = id === agentId ? ' (active)' : '';
|
|
247
|
+
output += `${id}${marker}: ${crypto.getPublicKeyBase64(kp).slice(0, 20)}...\n`;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
output += '\nāā Peer Keys āā\n';
|
|
252
|
+
const peers = Object.entries(knownKeys);
|
|
253
|
+
if (peers.length === 0) {
|
|
254
|
+
output += 'No peer keys. Use import_key to add one.\n';
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
for (const [id, key] of peers) {
|
|
258
|
+
output += `${id}: ${key.slice(0, 20)}...\n`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
content: [{ type: 'text', text: output }],
|
|
263
|
+
};
|
|
264
|
+
});
|
|
265
|
+
// Tool: import_key
|
|
266
|
+
server.tool('import_key', "Import another agent's public key for encrypted messaging", {
|
|
267
|
+
agent_id: z.string().describe('Agent ID to import key for'),
|
|
268
|
+
public_key: z.string().describe('Base64 encoded public key'),
|
|
269
|
+
}, async ({ agent_id, public_key }) => {
|
|
270
|
+
if (public_key.length < 40) {
|
|
271
|
+
return {
|
|
272
|
+
content: [{ type: 'text', text: 'ā Invalid key format. Expected base64 NaCl public key.' }],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
crypto.saveKnownKey(agent_id, public_key);
|
|
276
|
+
return {
|
|
277
|
+
content: [{
|
|
278
|
+
type: 'text',
|
|
279
|
+
text: `ā
Imported public key for ${agent_id}\n\nš You can now send encrypted messages to this agent.`
|
|
280
|
+
}],
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
// Tool: archive_message
|
|
284
|
+
server.tool('archive_message', 'Archive a message (remove from inbox)', {
|
|
285
|
+
message_id: z.string().describe('Message ID to archive'),
|
|
286
|
+
}, async ({ message_id }) => {
|
|
287
|
+
const success = await storage.archiveMessage(message_id);
|
|
288
|
+
if (success) {
|
|
289
|
+
return {
|
|
290
|
+
content: [{ type: 'text', text: `ā
Message archived: ${message_id}` }],
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
return {
|
|
295
|
+
content: [{ type: 'text', text: `ā Message not found: ${message_id}` }],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
// Start the server
|
|
300
|
+
async function main() {
|
|
301
|
+
const transport = new StdioServerTransport();
|
|
302
|
+
await server.connect(transport);
|
|
303
|
+
console.error('Myceliumail MCP server running');
|
|
304
|
+
}
|
|
305
|
+
main().catch(console.error);
|
|
306
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "myceliumail-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Myceliumail - End-to-End Encrypted Messaging for AI Agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"myceliumail-mcp": "./dist/server.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node dist/server.js",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"claude",
|
|
20
|
+
"ai-agents",
|
|
21
|
+
"messaging",
|
|
22
|
+
"encrypted",
|
|
23
|
+
"e2e",
|
|
24
|
+
"myceliumail"
|
|
25
|
+
],
|
|
26
|
+
"author": "Treebird",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/treebird/myceliumail"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/treebird/myceliumail#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/treebird/myceliumail/issues"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
38
|
+
"tweetnacl": "^1.0.3",
|
|
39
|
+
"tweetnacl-util": "^0.15.1",
|
|
40
|
+
"zod": "^3.22.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^20.10.0",
|
|
44
|
+
"typescript": "^5.3.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Myceliumail MCP - Config Module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function getAgentId(): string {
|
|
6
|
+
return process.env.MYCELIUMAIL_AGENT_ID ||
|
|
7
|
+
process.env.MYCELIUMAIL_AGENT ||
|
|
8
|
+
'anonymous';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getSupabaseUrl(): string | undefined {
|
|
12
|
+
return process.env.SUPABASE_URL;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getSupabaseKey(): string | undefined {
|
|
16
|
+
return process.env.SUPABASE_ANON_KEY;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function hasSupabase(): boolean {
|
|
20
|
+
return !!(getSupabaseUrl() && getSupabaseKey());
|
|
21
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Myceliumail MCP - Crypto Module
|
|
3
|
+
*
|
|
4
|
+
* NaCl encryption for agent messaging.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import nacl from 'tweetnacl';
|
|
8
|
+
import util from 'tweetnacl-util';
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { homedir } from 'os';
|
|
12
|
+
|
|
13
|
+
const KEYS_DIR = join(homedir(), '.myceliumail', 'keys');
|
|
14
|
+
|
|
15
|
+
export interface KeyPair {
|
|
16
|
+
publicKey: Uint8Array;
|
|
17
|
+
secretKey: Uint8Array;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface EncryptedMessage {
|
|
21
|
+
ciphertext: string;
|
|
22
|
+
nonce: string;
|
|
23
|
+
senderPublicKey: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function ensureKeysDir(): void {
|
|
27
|
+
if (!existsSync(KEYS_DIR)) {
|
|
28
|
+
mkdirSync(KEYS_DIR, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function generateKeyPair(): KeyPair {
|
|
33
|
+
return nacl.box.keyPair();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function saveKeyPair(agentId: string, keyPair: KeyPair): void {
|
|
37
|
+
ensureKeysDir();
|
|
38
|
+
const serialized = {
|
|
39
|
+
publicKey: util.encodeBase64(keyPair.publicKey),
|
|
40
|
+
secretKey: util.encodeBase64(keyPair.secretKey),
|
|
41
|
+
};
|
|
42
|
+
const path = join(KEYS_DIR, `${agentId}.key.json`);
|
|
43
|
+
writeFileSync(path, JSON.stringify(serialized, null, 2), { mode: 0o600 });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function loadKeyPair(agentId: string): KeyPair | null {
|
|
47
|
+
const path = join(KEYS_DIR, `${agentId}.key.json`);
|
|
48
|
+
if (!existsSync(path)) return null;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const data = JSON.parse(readFileSync(path, 'utf-8'));
|
|
52
|
+
return {
|
|
53
|
+
publicKey: util.decodeBase64(data.publicKey),
|
|
54
|
+
secretKey: util.decodeBase64(data.secretKey),
|
|
55
|
+
};
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function hasKeyPair(agentId: string): boolean {
|
|
62
|
+
return existsSync(join(KEYS_DIR, `${agentId}.key.json`));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getPublicKeyBase64(keyPair: KeyPair): string {
|
|
66
|
+
return util.encodeBase64(keyPair.publicKey);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function encryptMessage(
|
|
70
|
+
message: string,
|
|
71
|
+
recipientPublicKey: Uint8Array,
|
|
72
|
+
senderKeyPair: KeyPair
|
|
73
|
+
): EncryptedMessage {
|
|
74
|
+
const messageBytes = util.decodeUTF8(message);
|
|
75
|
+
const nonce = nacl.randomBytes(nacl.box.nonceLength);
|
|
76
|
+
|
|
77
|
+
const ciphertext = nacl.box(
|
|
78
|
+
messageBytes,
|
|
79
|
+
nonce,
|
|
80
|
+
recipientPublicKey,
|
|
81
|
+
senderKeyPair.secretKey
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
ciphertext: util.encodeBase64(ciphertext),
|
|
86
|
+
nonce: util.encodeBase64(nonce),
|
|
87
|
+
senderPublicKey: util.encodeBase64(senderKeyPair.publicKey),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function decryptMessage(
|
|
92
|
+
encrypted: EncryptedMessage,
|
|
93
|
+
recipientKeyPair: KeyPair
|
|
94
|
+
): string | null {
|
|
95
|
+
try {
|
|
96
|
+
const ciphertext = util.decodeBase64(encrypted.ciphertext);
|
|
97
|
+
const nonce = util.decodeBase64(encrypted.nonce);
|
|
98
|
+
const senderPublicKey = util.decodeBase64(encrypted.senderPublicKey);
|
|
99
|
+
|
|
100
|
+
const decrypted = nacl.box.open(
|
|
101
|
+
ciphertext,
|
|
102
|
+
nonce,
|
|
103
|
+
senderPublicKey,
|
|
104
|
+
recipientKeyPair.secretKey
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (!decrypted) return null;
|
|
108
|
+
return util.encodeUTF8(decrypted);
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function loadKnownKeys(): Record<string, string> {
|
|
115
|
+
const path = join(KEYS_DIR, 'known_keys.json');
|
|
116
|
+
if (!existsSync(path)) return {};
|
|
117
|
+
try {
|
|
118
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
119
|
+
} catch {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function saveKnownKey(agentId: string, publicKeyBase64: string): void {
|
|
125
|
+
ensureKeysDir();
|
|
126
|
+
const keys = loadKnownKeys();
|
|
127
|
+
keys[agentId] = publicKeyBase64;
|
|
128
|
+
writeFileSync(join(KEYS_DIR, 'known_keys.json'), JSON.stringify(keys, null, 2));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function getKnownKey(agentId: string): string | null {
|
|
132
|
+
const keys = loadKnownKeys();
|
|
133
|
+
return keys[agentId] || null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function listOwnKeys(): string[] {
|
|
137
|
+
ensureKeysDir();
|
|
138
|
+
try {
|
|
139
|
+
const files = readdirSync(KEYS_DIR);
|
|
140
|
+
return files
|
|
141
|
+
.filter(f => f.endsWith('.key.json'))
|
|
142
|
+
.map(f => f.replace('.key.json', ''));
|
|
143
|
+
} catch {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function decodePublicKey(base64: string): Uint8Array {
|
|
149
|
+
return util.decodeBase64(base64);
|
|
150
|
+
}
|