mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-08-24 19:32:42 +02:00
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:
@@ -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)
|
||||
|
||||
@@ -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 ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user