Ship DM connect delivery, fleet pubkey lookup, OpenClaw Infonet agent, and relay auto-wormhole.

Auto-relay connect DMs with End Contact severing, signed fleet prekey lookup,
OpenClaw private Infonet channel intents, headless relay Tor bootstrap on redeploy,
and swarm/DM live verification scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
BigBodyCobain
2026-06-12 02:15:56 -06:00
co-authored by Cursor
parent d48a0cdace
commit 89d6bb8fb9
52 changed files with 4211 additions and 339 deletions
+33 -13
View File
@@ -365,25 +365,45 @@ layers (e.g., "add this CCTV camera I found", "add this military base").
### 8. Wormhole / InfoNet / Mesh Network
OpenClaw can participate as a full two-way agent in the decentralized network:
OpenClaw agents participate in the private Infonet **on behalf of the operator**
who configured the skill. All traffic uses the operator's wormhole persona and
local node runtime (MLS gate crypto, Ed25519 signing, Tor onion transport) —
the agent does not get a separate fleet identity.
**Access tiers**
- `restricted` (default): read Infonet status, list gates, read gate messages,
poll DMs.
- `full` (`OPENCLAW_ACCESS_TIER=full`): also warm Tor, join the swarm, post
gate messages, cast votes, and send DMs when the user commands it.
Remote agents authenticate with HMAC on `/api/ai/channel/command`; loopback
uses the local operator lane.
```python
# Join the Wormhole network (creates Ed25519 identity)
await sb.join_wormhole()
# Warm Tor, enable the node, announce to fleet seed (full tier)
await sb.ensure_infonet_ready(join_swarm=True)
# Post to the InfoNet (signed, chain-verified)
await sb.post_to_infonet("Intelligence bulletin: 3 carriers underway in Med")
# Status snapshot (chain health, wormhole, runtime)
status = await sb.infonet_status()
# Read InfoNet messages
messages = await sb.read_infonet(limit=20)
# Read the public Infonet gate (MLS-encrypted, decrypt with operator keys)
messages = await sb.read_gate_messages("infonet", limit=20, decrypt=True)
# Post on behalf of the operator (full tier) — propagates via peer-push
await sb.post_to_gate("infonet", "Intelligence bulletin: 3 carriers underway in Med")
# Legacy alias:
await sb.post_to_infonet("same as post_to_gate on infonet gate")
# Upvote / downvote a node (full tier)
await sb.cast_vote("!sb_peer_id_or_pubkey", vote=1, gate="infonet")
# Encrypted DMs (peer_id / !sb_... recipient)
await sb.send_encrypted_dm("!sb_recipient", "Eyes only: carrier update")
dms = await sb.read_encrypted_dms(limit=20)
# Join encrypted gate channels
gates = await sb.list_gates()
await sb.post_to_gate("gate_id", "Classified intel for gate members")
# Send/receive encrypted DMs
await sb.send_encrypted_dm("recipient_pubkey", "Eyes only: carrier update")
dms = await sb.read_encrypted_dms()
await sb.join_infonet_swarm() # re-announce + refresh manifest
# Meshtastic radio
signals = await sb.listen_mesh(region="US", limit=20)
+94 -57
View File
@@ -583,56 +583,81 @@ class ShadowBrokerClient:
r = await self._delete("/api/ai/inject", params=params)
return r.json()
# ── Wormhole / InfoNet ────────────────────────────────────────────
# ── Wormhole / InfoNet (operator-delegated via command channel) ───
async def ensure_infonet_ready(self, *, join_swarm: bool = True) -> dict:
"""Warm Tor, enable the node, and join the private Infonet swarm."""
resp = await self.send_command(
"ensure_infonet_ready",
{"join_swarm": join_swarm},
)
return resp.get("result") if isinstance(resp.get("result"), dict) else resp
async def join_infonet_swarm(self) -> dict:
"""Announce to the fleet seed and pull the signed peer manifest."""
resp = await self.send_command("join_infonet_swarm", {})
return resp.get("result") if isinstance(resp.get("result"), dict) else resp
async def infonet_status(self) -> dict:
"""Participant node + hashchain status snapshot."""
return self.unwrap_channel_result(await self.send_command("infonet_status", {}))
async def list_gates(self) -> dict:
"""List encrypted gate channels."""
return self.unwrap_channel_result(await self.send_command("list_gates", {}))
async def read_gate_messages(
self,
gate_id: str,
*,
limit: int = 20,
decrypt: bool = False,
) -> dict:
"""Read gate messages (optionally decrypt with the operator MLS persona)."""
return self.unwrap_channel_result(
await self.send_command(
"read_gate_messages",
{"gate_id": gate_id, "limit": limit, "decrypt": decrypt},
)
)
async def post_to_gate(self, gate_id: str, message: str, *, reply_to: str = "") -> dict:
"""Post an MLS-encrypted gate message on behalf of the operator."""
resp = await self.send_command(
"post_gate_message",
{
"gate_id": gate_id,
"plaintext": message,
"reply_to": reply_to,
},
)
return resp.get("result") if isinstance(resp.get("result"), dict) else resp
async def cast_vote(
self,
target_id: str,
vote: int,
*,
gate: str = "",
) -> dict:
"""Upvote (+1) or downvote (-1) a node; optional gate scope."""
resp = await self.send_command(
"cast_vote",
{"target_id": target_id, "vote": vote, "gate": gate},
)
return resp.get("result") if isinstance(resp.get("result"), dict) else resp
# Legacy aliases — prefer command-channel methods above
async def join_wormhole(self) -> dict:
"""Create a Wormhole identity and join the network."""
r = await self._post("/api/wormhole/join")
return r.json()
async def sign_event(self, event_type: str, payload: dict) -> dict:
"""Sign an event with the Wormhole Ed25519 key."""
r = await self._post("/api/wormhole/sign", json={
"event_type": event_type,
"payload": payload,
})
r.raise_for_status()
return r.json()
return await self.ensure_infonet_ready(join_swarm=True)
async def post_to_infonet(self, message: str, event_type: str = "message") -> dict:
"""Post a signed event to the InfoNet ledger."""
signed = await self.sign_event(event_type, {"message": message})
r = await self._post("/api/mesh/infonet/ingest", json={
"events": [signed],
})
r.raise_for_status()
return r.json()
if event_type != "message":
raise RuntimeError("use post_to_gate for encrypted gate traffic")
return await self.post_to_gate("infonet", message)
async def read_infonet(self, limit: int = 20, gate: str = "") -> dict:
"""Read recent InfoNet messages."""
params = {"limit": limit}
if gate:
params["gate"] = gate
r = await self._get("/api/mesh/infonet/messages", params=params)
return r.json()
async def list_gates(self) -> list:
"""List available encrypted gate channels."""
r = await self._get("/api/mesh/gate/list")
return r.json()
async def post_to_gate(self, gate_id: str, message: str) -> dict:
"""Compose and post an MLS-encrypted message to a gate."""
compose = await self._post("/api/wormhole/gate/message/compose", json={
"gate_id": gate_id,
"plaintext": message,
})
compose.raise_for_status()
envelope = compose.json()
post = await self._post(f"/api/mesh/gate/{gate_id}/message", json=envelope)
post.raise_for_status()
return post.json()
async def read_infonet(self, limit: int = 20, gate: str = "infonet") -> dict:
return await self.read_gate_messages(gate or "infonet", limit=limit, decrypt=True)
# ── Meshtastic ────────────────────────────────────────────────────
@@ -830,19 +855,31 @@ class ShadowBrokerClient:
# ── Encrypted DMs ─────────────────────────────────────────────
async def send_encrypted_dm(self, recipient_pubkey: str, message: str) -> dict:
"""Send an E2E encrypted direct message to another Wormhole identity."""
r = await self._post("/api/wormhole/dm/send", json={
"recipient": recipient_pubkey,
"plaintext": message,
})
r.raise_for_status()
return r.json()
async def send_encrypted_dm(
self,
peer_id: str,
message: str,
*,
delivery_class: str = "shared",
recipient_token: str = "",
) -> dict:
"""Send an E2E encrypted DM to another node (peer_id / !sb_...)."""
resp = await self.send_command(
"send_dm",
{
"peer_id": peer_id,
"plaintext": message,
"delivery_class": delivery_class,
"recipient_token": recipient_token,
},
)
return resp.get("result") if isinstance(resp.get("result"), dict) else resp
async def read_encrypted_dms(self, limit: int = 20) -> list:
"""Read received encrypted direct messages."""
r = await self._get("/api/wormhole/dm/inbox", params={"limit": limit})
return r.json()
async def read_encrypted_dms(self, limit: int = 20) -> dict:
"""Poll encrypted DMs for the operator identity."""
return self.unwrap_channel_result(
await self.send_command("poll_dms", {"limit": limit})
)
# ── Dead Drop ─────────────────────────────────────────────────
+8
View File
@@ -31,9 +31,17 @@ agent_surface:
- sb_query.ShadowBrokerClient.run_playbook
- sb_query.ShadowBrokerClient.send_batch
- sb_query.ShadowBrokerClient.channel_status
- sb_query.ShadowBrokerClient.infonet_status
- sb_query.ShadowBrokerClient.list_gates
- sb_query.ShadowBrokerClient.read_gate_messages
- sb_query.ShadowBrokerClient.poll_dms
writes:
- sb_query.ShadowBrokerClient.place_pin
- sb_query.ShadowBrokerClient.place_pins_batch
- sb_query.ShadowBrokerClient.ensure_infonet_ready
- sb_query.ShadowBrokerClient.post_to_gate
- sb_query.ShadowBrokerClient.cast_vote
- sb_query.ShadowBrokerClient.send_encrypted_dm
blocked_without_confirm:
- search_telemetry
- get_telemetry