openclaw-p2p 0.1.0__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.
@@ -0,0 +1 @@
1
+ .env
@@ -0,0 +1,333 @@
1
+ Metadata-Version: 2.4
2
+ Name: openclaw-p2p
3
+ Version: 0.1.0
4
+ Summary: Agent-to-Agent P2P communication — encrypted, authenticated, relay-capable
5
+ Project-URL: Homepage, https://github.com/openclaw/openclaw-p2p
6
+ Project-URL: Issues, https://github.com/openclaw/openclaw-p2p/issues
7
+ License-Expression: MIT
8
+ Keywords: agent,ai,ed25519,encryption,noise-protocol,p2p
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Communications
17
+ Classifier: Topic :: Security :: Cryptography
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: cryptography>=42.0
20
+ Requires-Dist: msgpack>=1.0
21
+ Requires-Dist: pydantic>=2.0
22
+ Requires-Dist: websockets>=13.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
25
+ Requires-Dist: pytest>=8.0; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # openclaw-p2p
29
+
30
+ Encrypted agent-to-agent P2P communication. Let your AI agents talk to each other directly — no platform middleman, no message limits, full E2E encryption.
31
+
32
+ Built for the [OpenClaw](https://github.com/openclaw) agent ecosystem, but works with any Python agent framework.
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ pip install openclaw-p2p
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ### 1. Generate your agent's identity
43
+
44
+ ```bash
45
+ p2p-keygen
46
+ ```
47
+
48
+ Output:
49
+ ```
50
+ PeerID: p2p:4Tcthedk3w9vSFTmzecPc8vNqqjn
51
+ Keys: ~/.p2p/identity/
52
+ ```
53
+
54
+ Your PeerID is your agent's public address. Share it with other agent owners to connect.
55
+
56
+ ### 2. Run a relay server (or use someone else's)
57
+
58
+ Agents behind NAT need a relay to find each other. The relay is a thin WebSocket proxy — it forwards encrypted frames but **can't read them** (Noise E2E encryption).
59
+
60
+ ```bash
61
+ p2p-relay --port 8765
62
+ ```
63
+
64
+ That's it. Run this on any machine with a public IP (a $5/month VPS works). The relay:
65
+ - Authenticates agents via Ed25519 signed registration
66
+ - Pins keys on first use (TOFU) to prevent impersonation
67
+ - Rate limits per peer and per IP
68
+ - Sees only ciphertext — zero access to message content
69
+
70
+ ### 3. Connect two agents
71
+
72
+ **Agent A** (your agent):
73
+ ```python
74
+ import asyncio
75
+ from p2p import A2ANode, A2AConfig, PeerIdentity, PeerStore, PeerRecord, MessageType
76
+
77
+ async def main():
78
+ identity = PeerIdentity.load("~/.p2p/identity")
79
+ store = PeerStore("~/.p2p/peers.json")
80
+
81
+ # Add agent B as a peer (get their PeerID + relay from their owner)
82
+ store.save(PeerRecord(
83
+ peer_id="p2p:THEIR_PEER_ID_HERE",
84
+ alias="AgentB",
85
+ addresses=["ws://relay.example.com:8765"],
86
+ ))
87
+
88
+ node = A2ANode(
89
+ identity=identity,
90
+ peer_store=store,
91
+ config=A2AConfig(relay_addresses=["ws://relay.example.com:8765"]),
92
+ agent_name="AgentA",
93
+ owner_display_name="Alice",
94
+ introduction="Alice's personal assistant",
95
+ capabilities=["calendar", "web_search", "memory"],
96
+ )
97
+
98
+ # Handle incoming messages
99
+ async def on_message(envelope):
100
+ print(f"From {envelope.sender_id}: {envelope.payload}")
101
+
102
+ # Handle new peer approval requests
103
+ async def on_approval(peer_id, hello):
104
+ print(f"New peer wants to connect: {hello['agent_name']} ({hello['owner_display_name']})")
105
+ print(f"PeerID: {peer_id}")
106
+ # In production, ask the owner to approve/reject
107
+ await node.approve_handshake(peer_id, trust_level=2)
108
+
109
+ node.on_message(on_message)
110
+ node.on_approval_needed(on_approval)
111
+ await node.start()
112
+
113
+ # Initiate connection (triggers handshake)
114
+ await node.connect_to_peer("p2p:THEIR_PEER_ID_HERE")
115
+
116
+ # Send a message (after handshake completes)
117
+ await node.send("p2p:THEIR_PEER_ID_HERE", MessageType.TEXT, {"text": "Hello from Agent A!"})
118
+
119
+ await asyncio.Future() # run forever
120
+
121
+ asyncio.run(main())
122
+ ```
123
+
124
+ **Agent B** does the same in reverse, with Agent A's PeerID and the same relay address.
125
+
126
+ ## What happens when two agents connect
127
+
128
+ ```
129
+ 1. Both agents connect outbound to the relay (no port forwarding needed)
130
+ 2. Noise_XX handshake — mutual authentication, forward-secret session keys
131
+ 3. Identity binding proof — Ed25519 signature over handshake hash (proves identity)
132
+ 4. HELLO exchange — agent names, capabilities, owner info
133
+ 5. Challenge-response — Ed25519 nonce signing (defense-in-depth)
134
+ 6. Owner approval — BOTH owners must explicitly approve (mandatory)
135
+ 7. ACTIVE — encrypted messaging begins
136
+ ```
137
+
138
+ Reconnections to known peers skip steps 4-6 (just Noise + binding proof).
139
+
140
+ ## Message Types
141
+
142
+ ```python
143
+ from p2p import MessageType
144
+
145
+ # Basic messaging
146
+ await node.send(peer, MessageType.TEXT, {"text": "Hello!"})
147
+
148
+ # Task delegation
149
+ await node.send(peer, MessageType.TASK_REQUEST, {
150
+ "task": "Review this code",
151
+ "context": "PR #42 in the frontend repo",
152
+ })
153
+
154
+ # Queries
155
+ await node.send(peer, MessageType.QUERY, {
156
+ "text": "What's on my calendar tomorrow?",
157
+ })
158
+
159
+ # File transfer (chunked)
160
+ await node.send(peer, MessageType.FILE_OFFER, {
161
+ "filename": "report.pdf",
162
+ "size_bytes": 1024000,
163
+ "mime_type": "application/pdf",
164
+ "sha256": "abc123...",
165
+ })
166
+ ```
167
+
168
+ All messages are signed (Ed25519) and encrypted (AESGCM via Noise session).
169
+
170
+ ## Peer Store
171
+
172
+ Known peers are stored in `~/.p2p/peers.json`:
173
+
174
+ ```json
175
+ {
176
+ "p2p:7xK9mQ...3bZ": {
177
+ "peer_id": "p2p:7xK9mQ...3bZ",
178
+ "alias": "Nova",
179
+ "trust_level": 2,
180
+ "description": "Alex's research assistant. Connected 2026-03-20 via relay.",
181
+ "capabilities": ["code", "research"],
182
+ "scope_instructions": "Help with research tasks, don't share my calendar",
183
+ "last_seen": 1742515200.0
184
+ }
185
+ }
186
+ ```
187
+
188
+ The `description` field is for your agent to write notes about the peer. The `scope_instructions` field controls what your agent should/shouldn't do in conversations with that peer.
189
+
190
+ ## Trust Levels
191
+
192
+ | Level | Name | Meaning |
193
+ |-------|------|---------|
194
+ | 0 | Unknown | Never connected, not verified |
195
+ | 1 | Verified | Handshake complete, key exchange done |
196
+ | 2 | Trusted | Owner explicitly upgraded trust |
197
+ | 3 | Allied | Fully trusted (e.g., your own agents on different machines) |
198
+
199
+ Trust upgrades are always manual. Agents start at level 1 after the first handshake.
200
+
201
+ ## Self-Hosting a Relay
202
+
203
+ ### Quick (for testing)
204
+
205
+ ```bash
206
+ pip install openclaw-p2p
207
+ p2p-relay --port 8765
208
+ ```
209
+
210
+ ### Production (systemd)
211
+
212
+ Create `/etc/systemd/system/p2p-relay.service`:
213
+
214
+ ```ini
215
+ [Unit]
216
+ Description=OpenClaw P2P Relay
217
+ After=network.target
218
+
219
+ [Service]
220
+ Type=simple
221
+ User=p2p
222
+ ExecStart=/usr/local/bin/p2p-relay --port 8765
223
+ Restart=always
224
+ RestartSec=5
225
+
226
+ [Install]
227
+ WantedBy=multi-user.target
228
+ ```
229
+
230
+ ```bash
231
+ sudo systemctl enable --now p2p-relay
232
+ ```
233
+
234
+ ### With TLS (recommended for production)
235
+
236
+ The relay itself runs plain WebSocket. Put it behind a TLS-terminating reverse proxy:
237
+
238
+ **Caddy** (automatic HTTPS):
239
+ ```
240
+ relay.yourdomain.com {
241
+ reverse_proxy localhost:8765
242
+ }
243
+ ```
244
+
245
+ **nginx**:
246
+ ```nginx
247
+ server {
248
+ listen 443 ssl;
249
+ server_name relay.yourdomain.com;
250
+ ssl_certificate /path/to/cert.pem;
251
+ ssl_certificate_key /path/to/key.pem;
252
+
253
+ location / {
254
+ proxy_pass http://127.0.0.1:8765;
255
+ proxy_http_version 1.1;
256
+ proxy_set_header Upgrade $http_upgrade;
257
+ proxy_set_header Connection "upgrade";
258
+ }
259
+ }
260
+ ```
261
+
262
+ Then agents connect via `wss://relay.yourdomain.com`.
263
+
264
+ > Note: Even without TLS, message content is E2E encrypted (Noise protocol). TLS adds protection for metadata (which PeerIDs are connecting, when, how much traffic).
265
+
266
+ ## Security Model
267
+
268
+ | Layer | What it does |
269
+ |-------|-------------|
270
+ | **Noise_XX** | E2E encryption with forward secrecy. Relay sees only ciphertext. |
271
+ | **Identity binding** | Ed25519 signature over Noise handshake hash — proves the PeerID owner is the one in the Noise session |
272
+ | **Challenge-response** | Mutual nonce signing — defense-in-depth on top of Noise |
273
+ | **Message signatures** | Every envelope signed with Ed25519 — verified on receive |
274
+ | **TOFU key pinning** | Relay pins peer_id → public key on first registration |
275
+ | **Timestamped registration** | 60-second freshness window prevents replay of captured registrations |
276
+ | **Owner approval** | Both owners must approve — agents can't autonomously trust each other |
277
+ | **Message dedup** | 10K ring buffer rejects replayed message IDs |
278
+ | **Nonce guard** | Session terminates before AES-GCM nonce can overflow |
279
+
280
+ **What the relay can do**: see which PeerIDs are connected, when, and message sizes (traffic analysis).
281
+ **What the relay can't do**: read messages, forge messages, impersonate agents, or modify traffic without detection.
282
+
283
+ ## API Reference
284
+
285
+ ### `PeerIdentity`
286
+
287
+ ```python
288
+ identity = PeerIdentity.generate() # New random identity
289
+ identity = PeerIdentity.load(path) # Load from ~/.p2p/identity/
290
+ identity.save(path, passphrase="optional") # Save (optionally encrypted)
291
+ identity.peer_id # "p2p:4Tct..."
292
+ identity.sign(data) -> bytes # Ed25519 signature
293
+ PeerIdentity.verify(data, sig, pubkey) -> bool
294
+ ```
295
+
296
+ ### `A2ANode`
297
+
298
+ ```python
299
+ node = A2ANode(identity, peer_store, config,
300
+ agent_name="Name", owner_display_name="Owner",
301
+ capabilities=["list"], introduction="Description")
302
+
303
+ await node.start() # Begin listening + relay registration
304
+ await node.stop() # Graceful shutdown
305
+ await node.send(peer_id, msg_type, payload) # Send message (returns msg ID)
306
+ await node.connect_to_peer(peer_id) # Initiate connection + handshake
307
+ await node.approve_handshake(peer_id, trust_level=1, scope_instructions="")
308
+ await node.reject_handshake(peer_id)
309
+ node.on_message(async_handler) # Register message callback
310
+ node.on_approval_needed(async_handler) # Register approval callback
311
+ node.health() # Returns status dict
312
+ ```
313
+
314
+ ### `PeerStore`
315
+
316
+ ```python
317
+ store = PeerStore("~/.p2p/peers.json")
318
+ store.save(PeerRecord(peer_id="p2p:...", alias="Name", addresses=["ws://..."]))
319
+ store.get(peer_id) -> PeerRecord | None
320
+ store.list_all() -> list[PeerRecord]
321
+ store.remove(peer_id)
322
+ store.update_trust(peer_id, level)
323
+ store.update_description(peer_id, text)
324
+ ```
325
+
326
+ ## Requirements
327
+
328
+ - Python 3.11+
329
+ - Dependencies: `pydantic`, `cryptography`, `websockets`, `msgpack` (all well-maintained, widely used)
330
+
331
+ ## License
332
+
333
+ MIT
@@ -0,0 +1,306 @@
1
+ # openclaw-p2p
2
+
3
+ Encrypted agent-to-agent P2P communication. Let your AI agents talk to each other directly — no platform middleman, no message limits, full E2E encryption.
4
+
5
+ Built for the [OpenClaw](https://github.com/openclaw) agent ecosystem, but works with any Python agent framework.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install openclaw-p2p
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### 1. Generate your agent's identity
16
+
17
+ ```bash
18
+ p2p-keygen
19
+ ```
20
+
21
+ Output:
22
+ ```
23
+ PeerID: p2p:4Tcthedk3w9vSFTmzecPc8vNqqjn
24
+ Keys: ~/.p2p/identity/
25
+ ```
26
+
27
+ Your PeerID is your agent's public address. Share it with other agent owners to connect.
28
+
29
+ ### 2. Run a relay server (or use someone else's)
30
+
31
+ Agents behind NAT need a relay to find each other. The relay is a thin WebSocket proxy — it forwards encrypted frames but **can't read them** (Noise E2E encryption).
32
+
33
+ ```bash
34
+ p2p-relay --port 8765
35
+ ```
36
+
37
+ That's it. Run this on any machine with a public IP (a $5/month VPS works). The relay:
38
+ - Authenticates agents via Ed25519 signed registration
39
+ - Pins keys on first use (TOFU) to prevent impersonation
40
+ - Rate limits per peer and per IP
41
+ - Sees only ciphertext — zero access to message content
42
+
43
+ ### 3. Connect two agents
44
+
45
+ **Agent A** (your agent):
46
+ ```python
47
+ import asyncio
48
+ from p2p import A2ANode, A2AConfig, PeerIdentity, PeerStore, PeerRecord, MessageType
49
+
50
+ async def main():
51
+ identity = PeerIdentity.load("~/.p2p/identity")
52
+ store = PeerStore("~/.p2p/peers.json")
53
+
54
+ # Add agent B as a peer (get their PeerID + relay from their owner)
55
+ store.save(PeerRecord(
56
+ peer_id="p2p:THEIR_PEER_ID_HERE",
57
+ alias="AgentB",
58
+ addresses=["ws://relay.example.com:8765"],
59
+ ))
60
+
61
+ node = A2ANode(
62
+ identity=identity,
63
+ peer_store=store,
64
+ config=A2AConfig(relay_addresses=["ws://relay.example.com:8765"]),
65
+ agent_name="AgentA",
66
+ owner_display_name="Alice",
67
+ introduction="Alice's personal assistant",
68
+ capabilities=["calendar", "web_search", "memory"],
69
+ )
70
+
71
+ # Handle incoming messages
72
+ async def on_message(envelope):
73
+ print(f"From {envelope.sender_id}: {envelope.payload}")
74
+
75
+ # Handle new peer approval requests
76
+ async def on_approval(peer_id, hello):
77
+ print(f"New peer wants to connect: {hello['agent_name']} ({hello['owner_display_name']})")
78
+ print(f"PeerID: {peer_id}")
79
+ # In production, ask the owner to approve/reject
80
+ await node.approve_handshake(peer_id, trust_level=2)
81
+
82
+ node.on_message(on_message)
83
+ node.on_approval_needed(on_approval)
84
+ await node.start()
85
+
86
+ # Initiate connection (triggers handshake)
87
+ await node.connect_to_peer("p2p:THEIR_PEER_ID_HERE")
88
+
89
+ # Send a message (after handshake completes)
90
+ await node.send("p2p:THEIR_PEER_ID_HERE", MessageType.TEXT, {"text": "Hello from Agent A!"})
91
+
92
+ await asyncio.Future() # run forever
93
+
94
+ asyncio.run(main())
95
+ ```
96
+
97
+ **Agent B** does the same in reverse, with Agent A's PeerID and the same relay address.
98
+
99
+ ## What happens when two agents connect
100
+
101
+ ```
102
+ 1. Both agents connect outbound to the relay (no port forwarding needed)
103
+ 2. Noise_XX handshake — mutual authentication, forward-secret session keys
104
+ 3. Identity binding proof — Ed25519 signature over handshake hash (proves identity)
105
+ 4. HELLO exchange — agent names, capabilities, owner info
106
+ 5. Challenge-response — Ed25519 nonce signing (defense-in-depth)
107
+ 6. Owner approval — BOTH owners must explicitly approve (mandatory)
108
+ 7. ACTIVE — encrypted messaging begins
109
+ ```
110
+
111
+ Reconnections to known peers skip steps 4-6 (just Noise + binding proof).
112
+
113
+ ## Message Types
114
+
115
+ ```python
116
+ from p2p import MessageType
117
+
118
+ # Basic messaging
119
+ await node.send(peer, MessageType.TEXT, {"text": "Hello!"})
120
+
121
+ # Task delegation
122
+ await node.send(peer, MessageType.TASK_REQUEST, {
123
+ "task": "Review this code",
124
+ "context": "PR #42 in the frontend repo",
125
+ })
126
+
127
+ # Queries
128
+ await node.send(peer, MessageType.QUERY, {
129
+ "text": "What's on my calendar tomorrow?",
130
+ })
131
+
132
+ # File transfer (chunked)
133
+ await node.send(peer, MessageType.FILE_OFFER, {
134
+ "filename": "report.pdf",
135
+ "size_bytes": 1024000,
136
+ "mime_type": "application/pdf",
137
+ "sha256": "abc123...",
138
+ })
139
+ ```
140
+
141
+ All messages are signed (Ed25519) and encrypted (AESGCM via Noise session).
142
+
143
+ ## Peer Store
144
+
145
+ Known peers are stored in `~/.p2p/peers.json`:
146
+
147
+ ```json
148
+ {
149
+ "p2p:7xK9mQ...3bZ": {
150
+ "peer_id": "p2p:7xK9mQ...3bZ",
151
+ "alias": "Nova",
152
+ "trust_level": 2,
153
+ "description": "Alex's research assistant. Connected 2026-03-20 via relay.",
154
+ "capabilities": ["code", "research"],
155
+ "scope_instructions": "Help with research tasks, don't share my calendar",
156
+ "last_seen": 1742515200.0
157
+ }
158
+ }
159
+ ```
160
+
161
+ The `description` field is for your agent to write notes about the peer. The `scope_instructions` field controls what your agent should/shouldn't do in conversations with that peer.
162
+
163
+ ## Trust Levels
164
+
165
+ | Level | Name | Meaning |
166
+ |-------|------|---------|
167
+ | 0 | Unknown | Never connected, not verified |
168
+ | 1 | Verified | Handshake complete, key exchange done |
169
+ | 2 | Trusted | Owner explicitly upgraded trust |
170
+ | 3 | Allied | Fully trusted (e.g., your own agents on different machines) |
171
+
172
+ Trust upgrades are always manual. Agents start at level 1 after the first handshake.
173
+
174
+ ## Self-Hosting a Relay
175
+
176
+ ### Quick (for testing)
177
+
178
+ ```bash
179
+ pip install openclaw-p2p
180
+ p2p-relay --port 8765
181
+ ```
182
+
183
+ ### Production (systemd)
184
+
185
+ Create `/etc/systemd/system/p2p-relay.service`:
186
+
187
+ ```ini
188
+ [Unit]
189
+ Description=OpenClaw P2P Relay
190
+ After=network.target
191
+
192
+ [Service]
193
+ Type=simple
194
+ User=p2p
195
+ ExecStart=/usr/local/bin/p2p-relay --port 8765
196
+ Restart=always
197
+ RestartSec=5
198
+
199
+ [Install]
200
+ WantedBy=multi-user.target
201
+ ```
202
+
203
+ ```bash
204
+ sudo systemctl enable --now p2p-relay
205
+ ```
206
+
207
+ ### With TLS (recommended for production)
208
+
209
+ The relay itself runs plain WebSocket. Put it behind a TLS-terminating reverse proxy:
210
+
211
+ **Caddy** (automatic HTTPS):
212
+ ```
213
+ relay.yourdomain.com {
214
+ reverse_proxy localhost:8765
215
+ }
216
+ ```
217
+
218
+ **nginx**:
219
+ ```nginx
220
+ server {
221
+ listen 443 ssl;
222
+ server_name relay.yourdomain.com;
223
+ ssl_certificate /path/to/cert.pem;
224
+ ssl_certificate_key /path/to/key.pem;
225
+
226
+ location / {
227
+ proxy_pass http://127.0.0.1:8765;
228
+ proxy_http_version 1.1;
229
+ proxy_set_header Upgrade $http_upgrade;
230
+ proxy_set_header Connection "upgrade";
231
+ }
232
+ }
233
+ ```
234
+
235
+ Then agents connect via `wss://relay.yourdomain.com`.
236
+
237
+ > Note: Even without TLS, message content is E2E encrypted (Noise protocol). TLS adds protection for metadata (which PeerIDs are connecting, when, how much traffic).
238
+
239
+ ## Security Model
240
+
241
+ | Layer | What it does |
242
+ |-------|-------------|
243
+ | **Noise_XX** | E2E encryption with forward secrecy. Relay sees only ciphertext. |
244
+ | **Identity binding** | Ed25519 signature over Noise handshake hash — proves the PeerID owner is the one in the Noise session |
245
+ | **Challenge-response** | Mutual nonce signing — defense-in-depth on top of Noise |
246
+ | **Message signatures** | Every envelope signed with Ed25519 — verified on receive |
247
+ | **TOFU key pinning** | Relay pins peer_id → public key on first registration |
248
+ | **Timestamped registration** | 60-second freshness window prevents replay of captured registrations |
249
+ | **Owner approval** | Both owners must approve — agents can't autonomously trust each other |
250
+ | **Message dedup** | 10K ring buffer rejects replayed message IDs |
251
+ | **Nonce guard** | Session terminates before AES-GCM nonce can overflow |
252
+
253
+ **What the relay can do**: see which PeerIDs are connected, when, and message sizes (traffic analysis).
254
+ **What the relay can't do**: read messages, forge messages, impersonate agents, or modify traffic without detection.
255
+
256
+ ## API Reference
257
+
258
+ ### `PeerIdentity`
259
+
260
+ ```python
261
+ identity = PeerIdentity.generate() # New random identity
262
+ identity = PeerIdentity.load(path) # Load from ~/.p2p/identity/
263
+ identity.save(path, passphrase="optional") # Save (optionally encrypted)
264
+ identity.peer_id # "p2p:4Tct..."
265
+ identity.sign(data) -> bytes # Ed25519 signature
266
+ PeerIdentity.verify(data, sig, pubkey) -> bool
267
+ ```
268
+
269
+ ### `A2ANode`
270
+
271
+ ```python
272
+ node = A2ANode(identity, peer_store, config,
273
+ agent_name="Name", owner_display_name="Owner",
274
+ capabilities=["list"], introduction="Description")
275
+
276
+ await node.start() # Begin listening + relay registration
277
+ await node.stop() # Graceful shutdown
278
+ await node.send(peer_id, msg_type, payload) # Send message (returns msg ID)
279
+ await node.connect_to_peer(peer_id) # Initiate connection + handshake
280
+ await node.approve_handshake(peer_id, trust_level=1, scope_instructions="")
281
+ await node.reject_handshake(peer_id)
282
+ node.on_message(async_handler) # Register message callback
283
+ node.on_approval_needed(async_handler) # Register approval callback
284
+ node.health() # Returns status dict
285
+ ```
286
+
287
+ ### `PeerStore`
288
+
289
+ ```python
290
+ store = PeerStore("~/.p2p/peers.json")
291
+ store.save(PeerRecord(peer_id="p2p:...", alias="Name", addresses=["ws://..."]))
292
+ store.get(peer_id) -> PeerRecord | None
293
+ store.list_all() -> list[PeerRecord]
294
+ store.remove(peer_id)
295
+ store.update_trust(peer_id, level)
296
+ store.update_description(peer_id, text)
297
+ ```
298
+
299
+ ## Requirements
300
+
301
+ - Python 3.11+
302
+ - Dependencies: `pydantic`, `cryptography`, `websockets`, `msgpack` (all well-maintained, widely used)
303
+
304
+ ## License
305
+
306
+ MIT
@@ -0,0 +1,40 @@
1
+ """openclaw-p2p — Agent-to-Agent P2P communication.
2
+
3
+ Encrypted, authenticated, relay-capable communication between AI agents.
4
+ Designed as a pluggable module for OpenClaw and any Python agent framework.
5
+
6
+ Quick start::
7
+
8
+ from p2p import PeerIdentity, A2ANode, PeerStore, A2AConfig
9
+
10
+ identity = PeerIdentity.generate()
11
+ identity.save(Path("~/.p2p/identity"))
12
+
13
+ node = A2ANode(
14
+ identity=identity,
15
+ peer_store=PeerStore("~/.p2p/peers.json"),
16
+ config=A2AConfig(),
17
+ agent_name="MyAgent",
18
+ )
19
+ node.on_message(my_handler)
20
+ await node.start()
21
+ """
22
+
23
+ from p2p.config import A2AConfig
24
+ from p2p.identity import PeerIdentity
25
+ from p2p.node import A2ANode
26
+ from p2p.peer_store import PeerRecord, PeerStore
27
+ from p2p.protocol.envelope import A2AEnvelope
28
+ from p2p.protocol.types import MessageType
29
+
30
+ __all__ = [
31
+ "A2AConfig",
32
+ "A2AEnvelope",
33
+ "A2ANode",
34
+ "MessageType",
35
+ "PeerIdentity",
36
+ "PeerRecord",
37
+ "PeerStore",
38
+ ]
39
+
40
+ __version__ = "0.1.0"