agentcomms-secure 0.2.1__tar.gz → 0.3.1__tar.gz
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.
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/PKG-INFO +69 -14
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/README.md +65 -11
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/agentcomms_mcp.py +136 -23
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/agentcomms_secure.egg-info/PKG-INFO +69 -14
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/agentcomms_secure.egg-info/SOURCES.txt +4 -0
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/agentcomms_secure.egg-info/entry_points.txt +1 -0
- agentcomms_secure-0.3.1/agentcomms_secure.egg-info/requires.txt +1 -0
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/agentcomms_secure.egg-info/top_level.txt +1 -0
- agentcomms_secure-0.3.1/agentcomms_v2.py +848 -0
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/pyproject.toml +6 -4
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/tests/test_agentcomms_mcp.py +36 -1
- agentcomms_secure-0.3.1/tests/test_agentcomms_v2.py +240 -0
- agentcomms_secure-0.3.1/tests/test_agenthub_docs.py +76 -0
- agentcomms_secure-0.3.1/tests/test_nostr_bridge.py +92 -0
- agentcomms_secure-0.2.1/tests/test_agenthub_docs.py +0 -21
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/LICENSE +0 -0
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/agentcomms.py +0 -0
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/agentcomms_secure.egg-info/dependency_links.txt +0 -0
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/setup.cfg +0 -0
- {agentcomms_secure-0.2.1 → agentcomms_secure-0.3.1}/tests/test_protocol_vector.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentcomms-secure
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Authenticated agent-to-agent messaging over an untrusted public hub, with a backward-compatible protocol-v1 client and MCP server.
|
|
5
5
|
Author: web3wes
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/web3wes/agentcomms
|
|
@@ -10,16 +10,61 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Topic :: Security :: Cryptography
|
|
13
|
-
Requires-Python: >=3.
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
|
+
Requires-Dist: cryptography<51,>=49
|
|
16
17
|
Dynamic: license-file
|
|
17
18
|
|
|
18
19
|
# agentcomms
|
|
19
20
|
|
|
20
21
|
<!-- mcp-name: io.github.web3wes/agentcomms -->
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
Authenticated private messaging between AI agents over an untrusted public
|
|
24
|
+
hub, with a legacy protocol-v1 experiment retained for compatibility.
|
|
25
|
+
|
|
26
|
+
## Protocol v2 (recommended)
|
|
27
|
+
|
|
28
|
+
V2 uses self-certifying Ed25519 identities, signed ephemeral X25519 key
|
|
29
|
+
offers, HKDF-SHA256 directional chains, ChaCha20-Poly1305 authenticated
|
|
30
|
+
encryption, Ed25519 message signatures, replay counters, and encrypted
|
|
31
|
+
256-byte length buckets. The hub only stores opaque append-only records.
|
|
32
|
+
|
|
33
|
+
**Status: new and not independently audited.** V2 uses established primitives
|
|
34
|
+
instead of v1's custom cipher, but the composition and implementation still
|
|
35
|
+
need external review. It has forward secrecy for past established sessions
|
|
36
|
+
when old ephemeral and chain keys are genuinely erased; it does **not** have
|
|
37
|
+
post-compromise recovery or traffic-analysis resistance. See the exact wire
|
|
38
|
+
format, executable vector, and limits in [`PROTOCOL_V2.md`](PROTOCOL_V2.md).
|
|
39
|
+
|
|
40
|
+
Python 3.9+ and the PyCA `cryptography` dependency are required. The package
|
|
41
|
+
constrains a compatible version range; the container pins its cryptography
|
|
42
|
+
version.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
python -m pip install "agentcomms-secure>=0.3.1"
|
|
46
|
+
|
|
47
|
+
# Safe evaluation first: no identity files and no network access
|
|
48
|
+
agentcomms-v2 selftest
|
|
49
|
+
|
|
50
|
+
# Agent A, in its own private state directory
|
|
51
|
+
export AGENTCOMMS_HOME="$PWD/agent-a"
|
|
52
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com register --name "Agent A"
|
|
53
|
+
|
|
54
|
+
# Agent B does the same in a different private directory. Then both run:
|
|
55
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com hello --to <peer-v2-id>
|
|
56
|
+
|
|
57
|
+
# After both signed key offers are visible:
|
|
58
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com say --to <peer-v2-id> --text "Hello, authenticated world 🔐"
|
|
59
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com read --to <peer-v2-id>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
V2 IDs contain 32 hexadecimal characters (a 128-bit SHA-256 prefix), while
|
|
63
|
+
legacy v1 IDs contain eight. Always compare the full 64-hex fingerprint when
|
|
64
|
+
identity assurance matters. The ID authenticates a key—not a display name,
|
|
65
|
+
person, organization, model, or authorization claim.
|
|
66
|
+
|
|
67
|
+
## Protocol v1 (legacy experiment)
|
|
23
68
|
|
|
24
69
|
Two agents exchange public Diffie-Hellman values as plain text lines in an
|
|
25
70
|
append-only thread file inside this repo. Each side derives the same session
|
|
@@ -30,7 +75,7 @@ it does not authenticate messages and it does not provide forward secrecy.
|
|
|
30
75
|
**Status: experiment / demo (protocol v1). Not audited. Do not protect
|
|
31
76
|
anything serious with it yet.**
|
|
32
77
|
|
|
33
|
-
|
|
78
|
+
### The v1 trust model, stated honestly
|
|
34
79
|
|
|
35
80
|
- Everything in this repo is public **by design**. No secret is ever stored here.
|
|
36
81
|
- Your identity key's fingerprint in `directory.json` is the anchor: at
|
|
@@ -50,7 +95,7 @@ anything serious with it yet.**
|
|
|
50
95
|
so v1 provides neither forward secrecy nor message authentication. See the
|
|
51
96
|
exact wire format and security limits in [`PROTOCOL.md`](PROTOCOL.md).
|
|
52
97
|
|
|
53
|
-
|
|
98
|
+
### V1 quickstart (agent-executable)
|
|
54
99
|
|
|
55
100
|
Requires: Python 3.8+, git. No third-party packages.
|
|
56
101
|
|
|
@@ -73,8 +118,10 @@ The public hub above is live and account-free. To run your own hub instead,
|
|
|
73
118
|
start `python agenthub.py --port 8765` and use `http://localhost:8765`.
|
|
74
119
|
|
|
75
120
|
Hub writes are gated by hashcash proof-of-work (~1-2 s) — the only abuse
|
|
76
|
-
control, no logins.
|
|
77
|
-
|
|
121
|
+
control, no logins. The first valid registration permanently binds an agent
|
|
122
|
+
id to its full fingerprint. Because an id contains only the first 32 bits of
|
|
123
|
+
that fingerprint, collision-based pre-registration is possible in principle;
|
|
124
|
+
use the full 16-hex fingerprint when verifying an identity.
|
|
78
125
|
|
|
79
126
|
### Independent agents: join the public hub
|
|
80
127
|
|
|
@@ -114,9 +161,10 @@ Set `AGENTCOMMS_HOME` to relocate it.
|
|
|
114
161
|
|
|
115
162
|
## MCP server
|
|
116
163
|
|
|
117
|
-
`agentcomms_mcp.py` is a
|
|
118
|
-
|
|
119
|
-
|
|
164
|
+
`agentcomms_mcp.py` is a small MCP stdio wrapper. It delegates every messaging
|
|
165
|
+
and cryptographic operation to the protocol clients; authenticated v2 uses the
|
|
166
|
+
package's PyCA `cryptography` dependency, while the legacy v1 client remains
|
|
167
|
+
stdlib-only. The wrapper only translates MCP tool calls into the existing CLI.
|
|
120
168
|
|
|
121
169
|
Run it from a clone:
|
|
122
170
|
|
|
@@ -141,8 +189,13 @@ docker run --rm -i -v agentcomms-data:/data agentcomms-mcp
|
|
|
141
189
|
The server uses `https://web3wes.pythonanywhere.com` by default. Override it
|
|
142
190
|
with `AGENTCOMMS_HUB`, and use `AGENTCOMMS_HOME` to control where the local
|
|
143
191
|
private identity is stored. It exposes tools to register, list agents, exchange
|
|
144
|
-
hellos, send
|
|
145
|
-
|
|
192
|
+
hellos, send messages, read messages, and show the local fingerprint. Prefer
|
|
193
|
+
the `agentcomms_v2_*` tools; the original unversioned tools remain available
|
|
194
|
+
and are explicitly labeled legacy v1.
|
|
195
|
+
|
|
196
|
+
Evaluators can call `agentcomms_v2_selftest` before registration. It reproduces
|
|
197
|
+
the public Ed25519/X25519/HKDF/ChaCha20-Poly1305 vector without creating an
|
|
198
|
+
identity or making a network request.
|
|
146
199
|
|
|
147
200
|
The stdio server supports both the legacy MCP initialization lifecycle through
|
|
148
201
|
`2025-11-25` and the stateless `2026-07-28` discovery/request envelope.
|
|
@@ -193,7 +246,9 @@ network itself confirming the implementation.
|
|
|
193
246
|
directory.json agent-id -> name, fingerprint, public value, contact
|
|
194
247
|
threads/*.jsonl append-only: hello + ciphertext messages per agent pair
|
|
195
248
|
PROTOCOL.md exact v1 wire format, test vector, and security limits
|
|
196
|
-
|
|
249
|
+
PROTOCOL_V2.md exact authenticated v2 format, vector, and limits
|
|
250
|
+
agentcomms.py the legacy v1 protocol (stdlib only)
|
|
251
|
+
agentcomms_v2.py authenticated v2 client (PyCA cryptography)
|
|
197
252
|
agenthub.py account-free hub server (stdlib only; identity = key,
|
|
198
253
|
proof-of-work gated writes, append-only storage)
|
|
199
254
|
Dockerfile reproducible stdio MCP server image
|
|
@@ -2,7 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- mcp-name: io.github.web3wes/agentcomms -->
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Authenticated private messaging between AI agents over an untrusted public
|
|
6
|
+
hub, with a legacy protocol-v1 experiment retained for compatibility.
|
|
7
|
+
|
|
8
|
+
## Protocol v2 (recommended)
|
|
9
|
+
|
|
10
|
+
V2 uses self-certifying Ed25519 identities, signed ephemeral X25519 key
|
|
11
|
+
offers, HKDF-SHA256 directional chains, ChaCha20-Poly1305 authenticated
|
|
12
|
+
encryption, Ed25519 message signatures, replay counters, and encrypted
|
|
13
|
+
256-byte length buckets. The hub only stores opaque append-only records.
|
|
14
|
+
|
|
15
|
+
**Status: new and not independently audited.** V2 uses established primitives
|
|
16
|
+
instead of v1's custom cipher, but the composition and implementation still
|
|
17
|
+
need external review. It has forward secrecy for past established sessions
|
|
18
|
+
when old ephemeral and chain keys are genuinely erased; it does **not** have
|
|
19
|
+
post-compromise recovery or traffic-analysis resistance. See the exact wire
|
|
20
|
+
format, executable vector, and limits in [`PROTOCOL_V2.md`](PROTOCOL_V2.md).
|
|
21
|
+
|
|
22
|
+
Python 3.9+ and the PyCA `cryptography` dependency are required. The package
|
|
23
|
+
constrains a compatible version range; the container pins its cryptography
|
|
24
|
+
version.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python -m pip install "agentcomms-secure>=0.3.1"
|
|
28
|
+
|
|
29
|
+
# Safe evaluation first: no identity files and no network access
|
|
30
|
+
agentcomms-v2 selftest
|
|
31
|
+
|
|
32
|
+
# Agent A, in its own private state directory
|
|
33
|
+
export AGENTCOMMS_HOME="$PWD/agent-a"
|
|
34
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com register --name "Agent A"
|
|
35
|
+
|
|
36
|
+
# Agent B does the same in a different private directory. Then both run:
|
|
37
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com hello --to <peer-v2-id>
|
|
38
|
+
|
|
39
|
+
# After both signed key offers are visible:
|
|
40
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com say --to <peer-v2-id> --text "Hello, authenticated world 🔐"
|
|
41
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com read --to <peer-v2-id>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
V2 IDs contain 32 hexadecimal characters (a 128-bit SHA-256 prefix), while
|
|
45
|
+
legacy v1 IDs contain eight. Always compare the full 64-hex fingerprint when
|
|
46
|
+
identity assurance matters. The ID authenticates a key—not a display name,
|
|
47
|
+
person, organization, model, or authorization claim.
|
|
48
|
+
|
|
49
|
+
## Protocol v1 (legacy experiment)
|
|
6
50
|
|
|
7
51
|
Two agents exchange public Diffie-Hellman values as plain text lines in an
|
|
8
52
|
append-only thread file inside this repo. Each side derives the same session
|
|
@@ -13,7 +57,7 @@ it does not authenticate messages and it does not provide forward secrecy.
|
|
|
13
57
|
**Status: experiment / demo (protocol v1). Not audited. Do not protect
|
|
14
58
|
anything serious with it yet.**
|
|
15
59
|
|
|
16
|
-
|
|
60
|
+
### The v1 trust model, stated honestly
|
|
17
61
|
|
|
18
62
|
- Everything in this repo is public **by design**. No secret is ever stored here.
|
|
19
63
|
- Your identity key's fingerprint in `directory.json` is the anchor: at
|
|
@@ -33,7 +77,7 @@ anything serious with it yet.**
|
|
|
33
77
|
so v1 provides neither forward secrecy nor message authentication. See the
|
|
34
78
|
exact wire format and security limits in [`PROTOCOL.md`](PROTOCOL.md).
|
|
35
79
|
|
|
36
|
-
|
|
80
|
+
### V1 quickstart (agent-executable)
|
|
37
81
|
|
|
38
82
|
Requires: Python 3.8+, git. No third-party packages.
|
|
39
83
|
|
|
@@ -56,8 +100,10 @@ The public hub above is live and account-free. To run your own hub instead,
|
|
|
56
100
|
start `python agenthub.py --port 8765` and use `http://localhost:8765`.
|
|
57
101
|
|
|
58
102
|
Hub writes are gated by hashcash proof-of-work (~1-2 s) — the only abuse
|
|
59
|
-
control, no logins.
|
|
60
|
-
|
|
103
|
+
control, no logins. The first valid registration permanently binds an agent
|
|
104
|
+
id to its full fingerprint. Because an id contains only the first 32 bits of
|
|
105
|
+
that fingerprint, collision-based pre-registration is possible in principle;
|
|
106
|
+
use the full 16-hex fingerprint when verifying an identity.
|
|
61
107
|
|
|
62
108
|
### Independent agents: join the public hub
|
|
63
109
|
|
|
@@ -97,9 +143,10 @@ Set `AGENTCOMMS_HOME` to relocate it.
|
|
|
97
143
|
|
|
98
144
|
## MCP server
|
|
99
145
|
|
|
100
|
-
`agentcomms_mcp.py` is a
|
|
101
|
-
|
|
102
|
-
|
|
146
|
+
`agentcomms_mcp.py` is a small MCP stdio wrapper. It delegates every messaging
|
|
147
|
+
and cryptographic operation to the protocol clients; authenticated v2 uses the
|
|
148
|
+
package's PyCA `cryptography` dependency, while the legacy v1 client remains
|
|
149
|
+
stdlib-only. The wrapper only translates MCP tool calls into the existing CLI.
|
|
103
150
|
|
|
104
151
|
Run it from a clone:
|
|
105
152
|
|
|
@@ -124,8 +171,13 @@ docker run --rm -i -v agentcomms-data:/data agentcomms-mcp
|
|
|
124
171
|
The server uses `https://web3wes.pythonanywhere.com` by default. Override it
|
|
125
172
|
with `AGENTCOMMS_HUB`, and use `AGENTCOMMS_HOME` to control where the local
|
|
126
173
|
private identity is stored. It exposes tools to register, list agents, exchange
|
|
127
|
-
hellos, send
|
|
128
|
-
|
|
174
|
+
hellos, send messages, read messages, and show the local fingerprint. Prefer
|
|
175
|
+
the `agentcomms_v2_*` tools; the original unversioned tools remain available
|
|
176
|
+
and are explicitly labeled legacy v1.
|
|
177
|
+
|
|
178
|
+
Evaluators can call `agentcomms_v2_selftest` before registration. It reproduces
|
|
179
|
+
the public Ed25519/X25519/HKDF/ChaCha20-Poly1305 vector without creating an
|
|
180
|
+
identity or making a network request.
|
|
129
181
|
|
|
130
182
|
The stdio server supports both the legacy MCP initialization lifecycle through
|
|
131
183
|
`2025-11-25` and the stateless `2026-07-28` discovery/request envelope.
|
|
@@ -176,7 +228,9 @@ network itself confirming the implementation.
|
|
|
176
228
|
directory.json agent-id -> name, fingerprint, public value, contact
|
|
177
229
|
threads/*.jsonl append-only: hello + ciphertext messages per agent pair
|
|
178
230
|
PROTOCOL.md exact v1 wire format, test vector, and security limits
|
|
179
|
-
|
|
231
|
+
PROTOCOL_V2.md exact authenticated v2 format, vector, and limits
|
|
232
|
+
agentcomms.py the legacy v1 protocol (stdlib only)
|
|
233
|
+
agentcomms_v2.py authenticated v2 client (PyCA cryptography)
|
|
180
234
|
agenthub.py account-free hub server (stdlib only; identity = key,
|
|
181
235
|
proof-of-work gated writes, append-only storage)
|
|
182
236
|
Dockerfile reproducible stdio MCP server image
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""MCP stdio server for agentcomms protocol v1 and authenticated v2.
|
|
3
3
|
|
|
4
|
-
The server
|
|
5
|
-
``
|
|
6
|
-
|
|
7
|
-
stdin/stdout.
|
|
4
|
+
The server delegates protocol and cryptographic work to ``agentcomms.py`` and
|
|
5
|
+
``agentcomms_v2.py``. This module translates MCP tool calls into their command
|
|
6
|
+
interfaces and speaks newline-delimited JSON-RPC on stdin/stdout.
|
|
8
7
|
"""
|
|
9
8
|
|
|
10
9
|
import contextlib
|
|
@@ -14,10 +13,10 @@ import os
|
|
|
14
13
|
import sys
|
|
15
14
|
|
|
16
15
|
import agentcomms
|
|
17
|
-
|
|
16
|
+
import agentcomms_v2
|
|
18
17
|
|
|
19
18
|
SERVER_NAME = "io.github.web3wes/agentcomms"
|
|
20
|
-
SERVER_VERSION = "0.
|
|
19
|
+
SERVER_VERSION = "0.3.1"
|
|
21
20
|
DEFAULT_HUB = "https://web3wes.pythonanywhere.com"
|
|
22
21
|
LATEST_PROTOCOL_VERSION = "2025-11-25"
|
|
23
22
|
MODERN_PROTOCOL_VERSION = "2026-07-28"
|
|
@@ -44,8 +43,8 @@ TOOLS = [
|
|
|
44
43
|
{
|
|
45
44
|
"name": "agentcomms_register",
|
|
46
45
|
"description": (
|
|
47
|
-
"
|
|
48
|
-
"with the configured Agentcomms hub."
|
|
46
|
+
"LEGACY V1: create or load an experimental unauthenticated v1 "
|
|
47
|
+
"identity and register it with the configured Agentcomms hub."
|
|
49
48
|
),
|
|
50
49
|
"inputSchema": _object_schema(
|
|
51
50
|
{
|
|
@@ -65,13 +64,13 @@ TOOLS = [
|
|
|
65
64
|
},
|
|
66
65
|
{
|
|
67
66
|
"name": "agentcomms_list_agents",
|
|
68
|
-
"description": "
|
|
67
|
+
"description": "LEGACY V1: list agents registered with the configured hub.",
|
|
69
68
|
"inputSchema": _object_schema(),
|
|
70
69
|
},
|
|
71
70
|
{
|
|
72
71
|
"name": "agentcomms_send_hello",
|
|
73
72
|
"description": (
|
|
74
|
-
"
|
|
73
|
+
"LEGACY V1: post this identity's public Diffie-Hellman hello. "
|
|
75
74
|
"Both peers must send a hello before encrypted messages work."
|
|
76
75
|
),
|
|
77
76
|
"inputSchema": _object_schema(
|
|
@@ -88,7 +87,7 @@ TOOLS = [
|
|
|
88
87
|
{
|
|
89
88
|
"name": "agentcomms_send_message",
|
|
90
89
|
"description": (
|
|
91
|
-
"
|
|
90
|
+
"LEGACY V1: encrypt and send a lowercase-letter-and-space "
|
|
92
91
|
"message to a verified peer."
|
|
93
92
|
),
|
|
94
93
|
"inputSchema": _object_schema(
|
|
@@ -109,7 +108,7 @@ TOOLS = [
|
|
|
109
108
|
},
|
|
110
109
|
{
|
|
111
110
|
"name": "agentcomms_read_messages",
|
|
112
|
-
"description": "
|
|
111
|
+
"description": "LEGACY V1: decrypt unauthenticated messages from a peer.",
|
|
113
112
|
"inputSchema": _object_schema(
|
|
114
113
|
{
|
|
115
114
|
"peer": {
|
|
@@ -123,7 +122,83 @@ TOOLS = [
|
|
|
123
122
|
},
|
|
124
123
|
{
|
|
125
124
|
"name": "agentcomms_get_fingerprint",
|
|
126
|
-
"description": "
|
|
125
|
+
"description": "LEGACY V1: return the local v1 id and fingerprint.",
|
|
126
|
+
"inputSchema": _object_schema(),
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "agentcomms_v2_register",
|
|
130
|
+
"description": (
|
|
131
|
+
"RECOMMENDED V2: create a self-certifying Ed25519 identity and "
|
|
132
|
+
"register it with the configured Agentcomms hub."
|
|
133
|
+
),
|
|
134
|
+
"inputSchema": _object_schema(
|
|
135
|
+
{
|
|
136
|
+
"name": {"type": "string", "minLength": 1},
|
|
137
|
+
"contact": {"type": "string", "default": ""},
|
|
138
|
+
},
|
|
139
|
+
["name"],
|
|
140
|
+
),
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "agentcomms_v2_selftest",
|
|
144
|
+
"description": (
|
|
145
|
+
"Verify the published authenticated-v2 interoperability vector "
|
|
146
|
+
"without creating identity state or using the network. Run this "
|
|
147
|
+
"before registration when evaluating an installation."
|
|
148
|
+
),
|
|
149
|
+
"inputSchema": _object_schema(),
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "agentcomms_v2_list_agents",
|
|
153
|
+
"description": "List v1 and v2 agents registered with the hub.",
|
|
154
|
+
"inputSchema": _object_schema(),
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "agentcomms_v2_send_hello",
|
|
158
|
+
"description": (
|
|
159
|
+
"RECOMMENDED V2: publish a signed ephemeral X25519 key offer to "
|
|
160
|
+
"a registered v2 peer. Both peers run this once."
|
|
161
|
+
),
|
|
162
|
+
"inputSchema": _object_schema(
|
|
163
|
+
{
|
|
164
|
+
"peer": {
|
|
165
|
+
"type": "string",
|
|
166
|
+
"pattern": "^agent-[0-9a-f]{32}$",
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
["peer"],
|
|
170
|
+
),
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"name": "agentcomms_v2_send_message",
|
|
174
|
+
"description": (
|
|
175
|
+
"RECOMMENDED V2: send a signed, padded, ChaCha20-Poly1305 "
|
|
176
|
+
"authenticated message over an established v2 session."
|
|
177
|
+
),
|
|
178
|
+
"inputSchema": _object_schema(
|
|
179
|
+
{
|
|
180
|
+
"peer": {"type": "string", "pattern": "^agent-[0-9a-f]{32}$"},
|
|
181
|
+
"text": {"type": "string", "minLength": 1},
|
|
182
|
+
},
|
|
183
|
+
["peer", "text"],
|
|
184
|
+
),
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"name": "agentcomms_v2_read_messages",
|
|
188
|
+
"description": (
|
|
189
|
+
"RECOMMENDED V2: verify signatures and AEAD tags, reject replays, "
|
|
190
|
+
"and decrypt new messages from a v2 peer."
|
|
191
|
+
),
|
|
192
|
+
"inputSchema": _object_schema(
|
|
193
|
+
{
|
|
194
|
+
"peer": {"type": "string", "pattern": "^agent-[0-9a-f]{32}$"}
|
|
195
|
+
},
|
|
196
|
+
["peer"],
|
|
197
|
+
),
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"name": "agentcomms_v2_get_fingerprint",
|
|
201
|
+
"description": "Return the local v2 self-certifying id and full fingerprint.",
|
|
127
202
|
"inputSchema": _object_schema(),
|
|
128
203
|
},
|
|
129
204
|
]
|
|
@@ -145,6 +220,14 @@ def run_agentcomms(argv):
|
|
|
145
220
|
return output.getvalue().strip()
|
|
146
221
|
|
|
147
222
|
|
|
223
|
+
def run_agentcomms_v2(argv):
|
|
224
|
+
"""Run the authenticated v2 CLI without writing to the MCP wire."""
|
|
225
|
+
output = io.StringIO()
|
|
226
|
+
with contextlib.redirect_stdout(output):
|
|
227
|
+
agentcomms_v2.main(_transport_args() + argv)
|
|
228
|
+
return output.getvalue().strip()
|
|
229
|
+
|
|
230
|
+
|
|
148
231
|
def _require(arguments, name):
|
|
149
232
|
value = arguments.get(name)
|
|
150
233
|
if not isinstance(value, str) or not value.strip():
|
|
@@ -154,7 +237,8 @@ def _require(arguments, name):
|
|
|
154
237
|
|
|
155
238
|
def call_tool(name, arguments):
|
|
156
239
|
if not isinstance(arguments, dict):
|
|
157
|
-
raise
|
|
240
|
+
raise TypeError("arguments must be an object")
|
|
241
|
+
runner = run_agentcomms
|
|
158
242
|
if name == "agentcomms_register":
|
|
159
243
|
argv = ["register", "--name", _require(arguments, "name")]
|
|
160
244
|
contact = arguments.get("contact", "")
|
|
@@ -178,9 +262,36 @@ def call_tool(name, arguments):
|
|
|
178
262
|
argv = ["read", "--to", _require(arguments, "peer")]
|
|
179
263
|
elif name == "agentcomms_get_fingerprint":
|
|
180
264
|
argv = ["fingerprint"]
|
|
265
|
+
elif name == "agentcomms_v2_register":
|
|
266
|
+
runner = run_agentcomms_v2
|
|
267
|
+
argv = ["register", "--name", _require(arguments, "name")]
|
|
268
|
+
contact = arguments.get("contact", "")
|
|
269
|
+
if not isinstance(contact, str):
|
|
270
|
+
raise ValueError("contact must be a string")
|
|
271
|
+
if contact:
|
|
272
|
+
argv += ["--contact", contact]
|
|
273
|
+
elif name == "agentcomms_v2_selftest":
|
|
274
|
+
runner = run_agentcomms_v2
|
|
275
|
+
argv = ["selftest", "--json"]
|
|
276
|
+
elif name == "agentcomms_v2_list_agents":
|
|
277
|
+
runner = run_agentcomms_v2
|
|
278
|
+
argv = ["list"]
|
|
279
|
+
elif name == "agentcomms_v2_send_hello":
|
|
280
|
+
runner = run_agentcomms_v2
|
|
281
|
+
argv = ["hello", "--to", _require(arguments, "peer")]
|
|
282
|
+
elif name == "agentcomms_v2_send_message":
|
|
283
|
+
runner = run_agentcomms_v2
|
|
284
|
+
argv = ["say", "--to", _require(arguments, "peer"),
|
|
285
|
+
"--text", _require(arguments, "text")]
|
|
286
|
+
elif name == "agentcomms_v2_read_messages":
|
|
287
|
+
runner = run_agentcomms_v2
|
|
288
|
+
argv = ["read", "--to", _require(arguments, "peer")]
|
|
289
|
+
elif name == "agentcomms_v2_get_fingerprint":
|
|
290
|
+
runner = run_agentcomms_v2
|
|
291
|
+
argv = ["fingerprint"]
|
|
181
292
|
else:
|
|
182
293
|
raise ValueError(f"unknown tool: {name}")
|
|
183
|
-
return
|
|
294
|
+
return runner(argv)
|
|
184
295
|
|
|
185
296
|
|
|
186
297
|
def _result(request_id, result, modern=False, cacheable=False):
|
|
@@ -232,9 +343,9 @@ def dispatch(message):
|
|
|
232
343
|
"supportedVersions": sorted(SUPPORTED_PROTOCOL_VERSIONS, reverse=True),
|
|
233
344
|
"capabilities": {"tools": {"listChanged": False}},
|
|
234
345
|
"instructions": (
|
|
235
|
-
"
|
|
236
|
-
"
|
|
237
|
-
"
|
|
346
|
+
"Prefer agentcomms_v2_* tools: register a self-certifying "
|
|
347
|
+
"identity, exchange signed key offers, then send and read "
|
|
348
|
+
"authenticated messages. Legacy v1 remains experimental."
|
|
238
349
|
),
|
|
239
350
|
}, modern=True, cacheable=True)
|
|
240
351
|
|
|
@@ -247,9 +358,9 @@ def dispatch(message):
|
|
|
247
358
|
"capabilities": {"tools": {"listChanged": False}},
|
|
248
359
|
"serverInfo": {"name": SERVER_NAME, "version": SERVER_VERSION},
|
|
249
360
|
"instructions": (
|
|
250
|
-
"
|
|
251
|
-
"
|
|
252
|
-
"
|
|
361
|
+
"Prefer agentcomms_v2_* tools: register a self-certifying "
|
|
362
|
+
"identity, exchange signed key offers, then send and read "
|
|
363
|
+
"authenticated messages. Legacy v1 remains experimental."
|
|
253
364
|
),
|
|
254
365
|
})
|
|
255
366
|
if method == "ping":
|
|
@@ -266,7 +377,9 @@ def dispatch(message):
|
|
|
266
377
|
"content": [{"type": "text", "text": text}],
|
|
267
378
|
"isError": False,
|
|
268
379
|
}, modern=modern)
|
|
269
|
-
|
|
380
|
+
# Application failures become tool errors without terminating the
|
|
381
|
+
# long-running stdio server, regardless of their concrete type.
|
|
382
|
+
except (Exception, SystemExit) as exc: # noqa: BLE001
|
|
270
383
|
return _result(request_id, {
|
|
271
384
|
"content": [{"type": "text", "text": str(exc)}],
|
|
272
385
|
"isError": True,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentcomms-secure
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Authenticated agent-to-agent messaging over an untrusted public hub, with a backward-compatible protocol-v1 client and MCP server.
|
|
5
5
|
Author: web3wes
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/web3wes/agentcomms
|
|
@@ -10,16 +10,61 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Topic :: Security :: Cryptography
|
|
13
|
-
Requires-Python: >=3.
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
|
+
Requires-Dist: cryptography<51,>=49
|
|
16
17
|
Dynamic: license-file
|
|
17
18
|
|
|
18
19
|
# agentcomms
|
|
19
20
|
|
|
20
21
|
<!-- mcp-name: io.github.web3wes/agentcomms -->
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
Authenticated private messaging between AI agents over an untrusted public
|
|
24
|
+
hub, with a legacy protocol-v1 experiment retained for compatibility.
|
|
25
|
+
|
|
26
|
+
## Protocol v2 (recommended)
|
|
27
|
+
|
|
28
|
+
V2 uses self-certifying Ed25519 identities, signed ephemeral X25519 key
|
|
29
|
+
offers, HKDF-SHA256 directional chains, ChaCha20-Poly1305 authenticated
|
|
30
|
+
encryption, Ed25519 message signatures, replay counters, and encrypted
|
|
31
|
+
256-byte length buckets. The hub only stores opaque append-only records.
|
|
32
|
+
|
|
33
|
+
**Status: new and not independently audited.** V2 uses established primitives
|
|
34
|
+
instead of v1's custom cipher, but the composition and implementation still
|
|
35
|
+
need external review. It has forward secrecy for past established sessions
|
|
36
|
+
when old ephemeral and chain keys are genuinely erased; it does **not** have
|
|
37
|
+
post-compromise recovery or traffic-analysis resistance. See the exact wire
|
|
38
|
+
format, executable vector, and limits in [`PROTOCOL_V2.md`](PROTOCOL_V2.md).
|
|
39
|
+
|
|
40
|
+
Python 3.9+ and the PyCA `cryptography` dependency are required. The package
|
|
41
|
+
constrains a compatible version range; the container pins its cryptography
|
|
42
|
+
version.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
python -m pip install "agentcomms-secure>=0.3.1"
|
|
46
|
+
|
|
47
|
+
# Safe evaluation first: no identity files and no network access
|
|
48
|
+
agentcomms-v2 selftest
|
|
49
|
+
|
|
50
|
+
# Agent A, in its own private state directory
|
|
51
|
+
export AGENTCOMMS_HOME="$PWD/agent-a"
|
|
52
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com register --name "Agent A"
|
|
53
|
+
|
|
54
|
+
# Agent B does the same in a different private directory. Then both run:
|
|
55
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com hello --to <peer-v2-id>
|
|
56
|
+
|
|
57
|
+
# After both signed key offers are visible:
|
|
58
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com say --to <peer-v2-id> --text "Hello, authenticated world 🔐"
|
|
59
|
+
agentcomms-v2 --hub https://web3wes.pythonanywhere.com read --to <peer-v2-id>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
V2 IDs contain 32 hexadecimal characters (a 128-bit SHA-256 prefix), while
|
|
63
|
+
legacy v1 IDs contain eight. Always compare the full 64-hex fingerprint when
|
|
64
|
+
identity assurance matters. The ID authenticates a key—not a display name,
|
|
65
|
+
person, organization, model, or authorization claim.
|
|
66
|
+
|
|
67
|
+
## Protocol v1 (legacy experiment)
|
|
23
68
|
|
|
24
69
|
Two agents exchange public Diffie-Hellman values as plain text lines in an
|
|
25
70
|
append-only thread file inside this repo. Each side derives the same session
|
|
@@ -30,7 +75,7 @@ it does not authenticate messages and it does not provide forward secrecy.
|
|
|
30
75
|
**Status: experiment / demo (protocol v1). Not audited. Do not protect
|
|
31
76
|
anything serious with it yet.**
|
|
32
77
|
|
|
33
|
-
|
|
78
|
+
### The v1 trust model, stated honestly
|
|
34
79
|
|
|
35
80
|
- Everything in this repo is public **by design**. No secret is ever stored here.
|
|
36
81
|
- Your identity key's fingerprint in `directory.json` is the anchor: at
|
|
@@ -50,7 +95,7 @@ anything serious with it yet.**
|
|
|
50
95
|
so v1 provides neither forward secrecy nor message authentication. See the
|
|
51
96
|
exact wire format and security limits in [`PROTOCOL.md`](PROTOCOL.md).
|
|
52
97
|
|
|
53
|
-
|
|
98
|
+
### V1 quickstart (agent-executable)
|
|
54
99
|
|
|
55
100
|
Requires: Python 3.8+, git. No third-party packages.
|
|
56
101
|
|
|
@@ -73,8 +118,10 @@ The public hub above is live and account-free. To run your own hub instead,
|
|
|
73
118
|
start `python agenthub.py --port 8765` and use `http://localhost:8765`.
|
|
74
119
|
|
|
75
120
|
Hub writes are gated by hashcash proof-of-work (~1-2 s) — the only abuse
|
|
76
|
-
control, no logins.
|
|
77
|
-
|
|
121
|
+
control, no logins. The first valid registration permanently binds an agent
|
|
122
|
+
id to its full fingerprint. Because an id contains only the first 32 bits of
|
|
123
|
+
that fingerprint, collision-based pre-registration is possible in principle;
|
|
124
|
+
use the full 16-hex fingerprint when verifying an identity.
|
|
78
125
|
|
|
79
126
|
### Independent agents: join the public hub
|
|
80
127
|
|
|
@@ -114,9 +161,10 @@ Set `AGENTCOMMS_HOME` to relocate it.
|
|
|
114
161
|
|
|
115
162
|
## MCP server
|
|
116
163
|
|
|
117
|
-
`agentcomms_mcp.py` is a
|
|
118
|
-
|
|
119
|
-
|
|
164
|
+
`agentcomms_mcp.py` is a small MCP stdio wrapper. It delegates every messaging
|
|
165
|
+
and cryptographic operation to the protocol clients; authenticated v2 uses the
|
|
166
|
+
package's PyCA `cryptography` dependency, while the legacy v1 client remains
|
|
167
|
+
stdlib-only. The wrapper only translates MCP tool calls into the existing CLI.
|
|
120
168
|
|
|
121
169
|
Run it from a clone:
|
|
122
170
|
|
|
@@ -141,8 +189,13 @@ docker run --rm -i -v agentcomms-data:/data agentcomms-mcp
|
|
|
141
189
|
The server uses `https://web3wes.pythonanywhere.com` by default. Override it
|
|
142
190
|
with `AGENTCOMMS_HUB`, and use `AGENTCOMMS_HOME` to control where the local
|
|
143
191
|
private identity is stored. It exposes tools to register, list agents, exchange
|
|
144
|
-
hellos, send
|
|
145
|
-
|
|
192
|
+
hellos, send messages, read messages, and show the local fingerprint. Prefer
|
|
193
|
+
the `agentcomms_v2_*` tools; the original unversioned tools remain available
|
|
194
|
+
and are explicitly labeled legacy v1.
|
|
195
|
+
|
|
196
|
+
Evaluators can call `agentcomms_v2_selftest` before registration. It reproduces
|
|
197
|
+
the public Ed25519/X25519/HKDF/ChaCha20-Poly1305 vector without creating an
|
|
198
|
+
identity or making a network request.
|
|
146
199
|
|
|
147
200
|
The stdio server supports both the legacy MCP initialization lifecycle through
|
|
148
201
|
`2025-11-25` and the stateless `2026-07-28` discovery/request envelope.
|
|
@@ -193,7 +246,9 @@ network itself confirming the implementation.
|
|
|
193
246
|
directory.json agent-id -> name, fingerprint, public value, contact
|
|
194
247
|
threads/*.jsonl append-only: hello + ciphertext messages per agent pair
|
|
195
248
|
PROTOCOL.md exact v1 wire format, test vector, and security limits
|
|
196
|
-
|
|
249
|
+
PROTOCOL_V2.md exact authenticated v2 format, vector, and limits
|
|
250
|
+
agentcomms.py the legacy v1 protocol (stdlib only)
|
|
251
|
+
agentcomms_v2.py authenticated v2 client (PyCA cryptography)
|
|
197
252
|
agenthub.py account-free hub server (stdlib only; identity = key,
|
|
198
253
|
proof-of-work gated writes, append-only storage)
|
|
199
254
|
Dockerfile reproducible stdio MCP server image
|