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
parent d48a0cdace
commit 89d6bb8fb9
52 changed files with 4211 additions and 339 deletions
+4
View File
@@ -237,6 +237,10 @@ AIS_API_KEY= # https://aisstream.io/ — free tier WebSocket key
# MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY= # seed only
# MESH_BOOTSTRAP_SIGNER_ID=shadowbroker-seed
# MESH_PEER_REGISTRY_ENABLED=true # seed only (auto-enabled when private key is set)
# Headless relay compose sets MESH_INFONET_RELAY_AUTO_WORMHOLE=true; seed nodes with
# MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY also auto-enable Tor wormhole on startup.
# MESH_INFONET_RELAY_AUTO_WORMHOLE=false
# MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED=false
# MESH_SWARM_MANIFEST_TTL_S=14400
# MESH_SWARM_MANIFEST_PULL_INTERVAL_S=300
# MESH_PEER_REGISTRY_STALE_S=604800
+124 -13
View File
@@ -2696,8 +2696,10 @@ async def lifespan(app: FastAPI):
if not _MESH_ONLY:
def _startup_wormhole_runtime():
try:
from services.mesh.mesh_infonet_relay_bootstrap import ensure_infonet_relay_wormhole_ready
from services.wormhole_supervisor import get_wormhole_state, sync_wormhole_with_settings
ensure_infonet_relay_wormhole_ready(reason="startup_relay")
sync_wormhole_with_settings()
_resume_private_delivery_background_work(
current_tier=_current_private_lane_tier(get_wormhole_state()),
@@ -3472,7 +3474,10 @@ def _request_private_surface_warmup(*, path: str, method: str, current_tier: str
def _is_invite_scoped_prekey_bundle_lookup(request: Request, path: str) -> bool:
if request.method.upper() != "GET" or str(path or "").strip() != "/api/mesh/dm/prekey-bundle":
if request.method.upper() != "GET":
return False
normalized_path = str(path or "").strip()
if normalized_path not in {"/api/mesh/dm/prekey-bundle", "/api/mesh/dm/pubkey"}:
return False
try:
lookup_token = str(request.query_params.get("lookup_token", "") or "").strip()
@@ -3573,6 +3578,14 @@ async def enforce_high_privacy_mesh(request: Request, call_next):
except Exception:
logger.debug("Private surface warm-up request failed", exc_info=True)
required_tier = _minimum_transport_tier(path, request.method)
if required_tier:
from services.mesh.mesh_privacy_policy import runtime_route_enforcement_tier
required_tier = runtime_route_enforcement_tier(
path,
request.method,
static_tier=required_tier,
)
if required_tier:
if not _transport_tier_is_sufficient(current_tier, required_tier):
if request.method.upper() == "POST" and path == "/api/mesh/dm/send":
@@ -6865,12 +6878,22 @@ def _queue_dm_release(*, current_tier: str, payload: dict[str, Any]) -> dict[str
required_tier=release_lane_required_tier("dm"),
)
_wake_private_release_worker()
outbox_id = str(item.get("id", "") or "")
auto_release: dict[str, Any] = {"ok": True, "skipped": True}
if outbox_id:
try:
from services.mesh.mesh_dm_connect_delivery import auto_release_connect_dm_outbox
auto_release = auto_release_connect_dm_outbox(outbox_id=outbox_id, payload=payload)
except Exception as exc:
auto_release = {"ok": False, "detail": str(exc) or type(exc).__name__}
return {
"ok": True,
"msg_id": str(payload.get("msg_id", "") or ""),
"outbox_id": str(item.get("id", "") or ""),
"outbox_id": outbox_id,
"queued": True,
"detail": str((item.get("status") or {}).get("label", "") or "Queued for private delivery"),
"auto_release": auto_release,
"delivery": {
"state": canonical_release_state(str(item.get("release_state", "") or "queued")),
"internal_state": str(item.get("release_state", "") or "queued"),
@@ -7043,7 +7066,8 @@ async def _dm_send_from_signed_request(request: Request):
return {"ok": False, "detail": "DM timestamp is too far from current time"}
if delivery_class not in ("request", "shared"):
return {"ok": False, "detail": "delivery_class must be request or shared"}
if delivery_class == "request":
# Contact requests are the first-contact handshake — do not require prior verification.
if delivery_class == "shared":
try:
from services.mesh.mesh_wormhole_contacts import verified_first_contact_requirement
@@ -7127,6 +7151,8 @@ async def _dm_send_from_signed_request(request: Request):
relay_salt_hex = _os.urandom(16).hex()
connect_intent = str(body.get("connect_intent", "") or "").strip().lower()
lookup_peer_url = str(body.get("lookup_peer_url", "") or "").strip().rstrip("/")
release_payload = {
"sender_id": sender_id,
"sender_token_hash": sender_token_hash,
@@ -7141,6 +7167,16 @@ async def _dm_send_from_signed_request(request: Request):
"sender_seal": sender_seal,
"relay_salt": relay_salt_hex,
}
if connect_intent:
release_payload["connect_intent"] = connect_intent
if lookup_peer_url:
release_payload["lookup_peer_url"] = lookup_peer_url
try:
from services.mesh.mesh_dm_connect_delivery import enrich_connect_release_payload
release_payload = enrich_connect_release_payload(release_payload)
except Exception:
pass
hashchain_spool: dict[str, Any] = {"ok": False, "detail": "not attempted"}
try:
from services.mesh.mesh_hashchain import infonet
@@ -7427,7 +7463,12 @@ async def dm_register_key(request: Request):
@app.get("/api/mesh/dm/pubkey")
@limiter.limit("30/minute")
async def dm_get_pubkey(request: Request, agent_id: str = "", lookup_token: str = ""):
async def dm_get_pubkey(
request: Request,
agent_id: str = "",
lookup_token: str = "",
lookup_peer_url: str = "",
):
"""Fetch an agent's DH public key for key exchange."""
exposure = metadata_exposure_for_request(
request,
@@ -7447,11 +7488,49 @@ async def dm_get_pubkey(request: Request, agent_id: str = "", lookup_token: str
if resolved_lookup:
key_bundle, resolved_id = dm_relay.get_dh_key_by_lookup(resolved_lookup)
if key_bundle is None:
return dm_lookup_response_view(
{"ok": False, "detail": "Agent not found or has no DH key", "lookup_mode": "invite_lookup_handle"},
exposure=exposure,
lookup_token_present=True,
# Invite handles are minted on the owner's node. When a remote peer
# pastes a short address, resolve it across the private fleet before
# failing — same path as prekey-bundle import.
from services.mesh.mesh_wormhole_prekey import fetch_dm_prekey_bundle
preferred_lookup_peer = str(lookup_peer_url or "").strip().rstrip("/")
remote_bundle = fetch_dm_prekey_bundle(
agent_id="",
lookup_token=resolved_lookup,
lookup_peer_urls=[preferred_lookup_peer] if preferred_lookup_peer else None,
)
if remote_bundle.get("ok"):
bundle = dict(remote_bundle.get("bundle") or remote_bundle)
dh_pub = str(
bundle.get("identity_dh_pub_key", "")
or remote_bundle.get("identity_dh_pub_key", "")
or ""
).strip()
if dh_pub:
resolved_id = str(remote_bundle.get("agent_id", "") or resolved_id or "").strip()
key_bundle = {
"dh_pub_key": dh_pub,
"dh_algo": str(remote_bundle.get("dh_algo", "X25519") or "X25519"),
"timestamp": int(remote_bundle.get("timestamp", 0) or 0),
"public_key": str(remote_bundle.get("public_key", "") or ""),
"public_key_algo": str(remote_bundle.get("public_key_algo", "") or ""),
"signature": str(remote_bundle.get("signature", "") or ""),
"sequence": int(remote_bundle.get("sequence", 0) or 0),
"prekey_transparency_head": str(
remote_bundle.get("prekey_transparency_head", "") or ""
),
"prekey_transparency_size": int(
remote_bundle.get("prekey_transparency_size", 0) or 0
),
"witness_count": int(remote_bundle.get("witness_count", 0) or 0),
"witness_latest_at": int(remote_bundle.get("witness_latest_at", 0) or 0),
}
if key_bundle is None:
return dm_lookup_response_view(
{"ok": False, "detail": "Agent not found or has no DH key", "lookup_mode": "invite_lookup_handle"},
exposure=exposure,
lookup_token_present=True,
)
lookup_mode = "invite_lookup_handle"
if key_bundle is None and resolved_id:
blocked = legacy_agent_id_lookup_blocked()
@@ -7487,7 +7566,12 @@ async def dm_get_pubkey(request: Request, agent_id: str = "", lookup_token: str
@app.get("/api/mesh/dm/prekey-bundle")
@limiter.limit("30/minute")
async def dm_get_prekey_bundle(request: Request, agent_id: str = "", lookup_token: str = ""):
async def dm_get_prekey_bundle(
request: Request,
agent_id: str = "",
lookup_token: str = "",
lookup_peer_url: str = "",
):
exposure = metadata_exposure_for_request(
request,
authenticated=_scoped_view_authenticated(request, "mesh"),
@@ -7499,7 +7583,12 @@ async def dm_get_prekey_bundle(request: Request, agent_id: str = "", lookup_toke
lookup_token_present=bool(lookup_token),
)
resolved_id, resolved_lookup = _preferred_dm_lookup_target(agent_id, lookup_token)
result = fetch_dm_prekey_bundle(agent_id=resolved_id, lookup_token=resolved_lookup)
preferred_lookup_peer = str(lookup_peer_url or "").strip().rstrip("/")
result = fetch_dm_prekey_bundle(
agent_id=resolved_id,
lookup_token=resolved_lookup,
lookup_peer_urls=[preferred_lookup_peer] if preferred_lookup_peer else None,
)
return dm_lookup_response_view(
result,
exposure=exposure,
@@ -9349,7 +9438,8 @@ class WormholeDmResetRequest(BaseModel):
class WormholeDmBootstrapEncryptRequest(BaseModel):
peer_id: str
peer_id: str = ""
lookup_token: str = ""
plaintext: str
@@ -11311,9 +11401,12 @@ async def api_wormhole_dm_bootstrap_encrypt(request: Request, body: WormholeDmBo
result = bootstrap_encrypt_for_peer(
peer_id=str(body.peer_id or ""),
plaintext=str(body.plaintext or ""),
lookup_token=str(body.lookup_token or ""),
)
if isinstance(result, dict) and "trust_level" not in result:
result["trust_level"] = _get_contact_trust_level(str(body.peer_id or ""))
result["trust_level"] = _get_contact_trust_level(
str(result.get("peer_id", "") or body.peer_id or "")
)
return result
@@ -11329,7 +11422,7 @@ async def api_wormhole_dm_bootstrap_decrypt(request: Request, body: WormholeDmBo
return result
@app.post("/api/wormhole/dm/sender-token", dependencies=[Depends(require_admin)])
@app.post("/api/wormhole/dm/sender-token", dependencies=[Depends(require_local_operator)])
@limiter.limit("60/minute")
async def api_wormhole_dm_sender_token(request: Request, body: WormholeDmSenderTokenRequest):
if _safe_int(body.count or 1, 1) > 1:
@@ -11548,6 +11641,24 @@ async def api_wormhole_dm_contact_delete(request: Request, peer_id: str):
return {"ok": True, "peer_id": peer_id, "deleted": deleted}
@app.post("/api/wormhole/dm/contact/{peer_id}/sever", dependencies=[Depends(require_admin)])
@limiter.limit("60/minute")
async def api_wormhole_dm_contact_sever(request: Request, peer_id: str):
from services.mesh.mesh_wormhole_contacts import sever_wormhole_dm_contact
try:
body = await request.json()
except Exception:
body = {}
if not isinstance(body, dict):
body = {}
block = bool(body.get("block", False))
try:
return sever_wormhole_dm_contact(peer_id, block=block)
except ValueError as exc:
return {"ok": False, "detail": str(exc)}
_WORMHOLE_PUBLIC_FIELDS = {"installed", "configured", "running", "ready"}
+3
View File
@@ -2719,6 +2719,7 @@ def _connect_info_metadata(settings) -> dict:
"get_telemetry", "get_pins", "satellite_images",
"news_near", "ai_summary", "ai_report",
"timemachine_list", "timemachine_view",
"infonet_status", "list_gates", "read_gate_messages", "poll_dms",
],
},
"full": {
@@ -2729,6 +2730,8 @@ def _connect_info_metadata(settings) -> dict:
"satellite_images", "news_near", "data_injection",
"ai_summary", "ai_report", "timemachine_snapshot",
"timemachine_list", "timemachine_view", "timemachine_diff",
"ensure_infonet_ready", "join_infonet_swarm",
"post_gate_message", "cast_vote", "send_dm",
],
},
},
+19 -1
View File
@@ -1085,7 +1085,7 @@ async def api_wormhole_dm_bootstrap_decrypt(request: Request, body: WormholeDmBo
)
@router.post("/api/wormhole/dm/sender-token", dependencies=[Depends(require_admin)])
@router.post("/api/wormhole/dm/sender-token", dependencies=[Depends(require_local_operator)])
@limiter.limit("60/minute")
async def api_wormhole_dm_sender_token(request: Request, body: WormholeDmSenderTokenRequest):
if _safe_int(body.count or 1, 1) > 1:
@@ -1287,6 +1287,24 @@ async def api_wormhole_dm_contact_delete(request: Request, peer_id: str):
return {"ok": True, "peer_id": peer_id, "deleted": deleted}
@router.post("/api/wormhole/dm/contact/{peer_id}/sever", dependencies=[Depends(require_admin)])
@limiter.limit("60/minute")
async def api_wormhole_dm_contact_sever(request: Request, peer_id: str):
from services.mesh.mesh_wormhole_contacts import sever_wormhole_dm_contact
try:
body = await request.json()
except Exception:
body = {}
if not isinstance(body, dict):
body = {}
block = bool(body.get("block", False))
try:
return sever_wormhole_dm_contact(peer_id, block=block)
except ValueError as exc:
return {"ok": False, "detail": str(exc)}
_WORMHOLE_PUBLIC_FIELDS = {"installed", "configured", "running", "ready"}
+4
View File
@@ -51,6 +51,10 @@ class Settings(BaseSettings):
# When true, empty MESH_PEER_PUSH_SECRET uses the public fleet HMAC for seed join/announce.
MESH_INFONET_FLEET_JOIN: bool = True
MESH_INFONET_FLEET_JOIN_DISABLED: bool = False
# Headless relay/seed compose: auto-enable Tor wormhole on startup so
# docker compose redeploys keep the fleet onion reachable.
MESH_INFONET_RELAY_AUTO_WORMHOLE: bool = False
MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED: bool = False
MESH_BOOTSTRAP_SIGNER_ID: str = ""
MESH_PEER_REGISTRY_ENABLED: bool = False
MESH_PEER_REGISTRY_DISABLED: bool = False
@@ -0,0 +1,179 @@
"""Invite-scoped DM connect delivery: auto relay release and contact severance."""
from __future__ import annotations
from typing import Any
CONNECT_AUTO_RELEASE_INTENTS = frozenset(
{
"invite_short_address",
"invite_import",
"contact_request",
"contact_accept",
"contact_offer",
}
)
INVITE_CONNECT_TRUST_LEVELS = frozenset({"invite_pinned", "sas_verified"})
def _release_profile() -> str:
try:
from services.release_profiles import current_release_profile
return str(current_release_profile() or "dev")
except Exception:
return "dev"
def grant_connect_relay_policy(
recipient_id: str,
*,
reason: str = "connect_scoped_auto_release",
) -> dict[str, Any]:
"""Pre-authorize hidden relay delivery for an explicit connect target."""
peer_key = str(recipient_id or "").strip()
if not peer_key:
return {"ok": False, "detail": "recipient_id required"}
try:
from services.mesh.mesh_relay_policy import grant_relay_policy
return grant_relay_policy(
scope_type="dm_contact",
scope_id=peer_key,
profile=_release_profile(),
hidden_transport_required=True,
reason=str(reason or "connect_scoped_auto_release"),
)
except Exception as exc:
return {"ok": False, "detail": str(exc) or type(exc).__name__}
def revoke_connect_relay_policy(recipient_id: str) -> dict[str, Any]:
peer_key = str(recipient_id or "").strip()
if not peer_key:
return {"ok": False, "detail": "recipient_id required"}
try:
from services.mesh.mesh_relay_policy import revoke_relay_policy
revoked = int(
revoke_relay_policy(
scope_type="dm_contact",
scope_id=peer_key,
profile=_release_profile(),
)
or 0
)
return {"ok": True, "revoked": revoked}
except Exception as exc:
return {"ok": False, "detail": str(exc) or type(exc).__name__}
def recipient_has_invite_connect_scope(recipient_id: str) -> bool:
peer_key = str(recipient_id or "").strip()
if not peer_key:
return False
try:
from services.mesh.mesh_wormhole_contacts import get_wormhole_dm_contact
contact = get_wormhole_dm_contact(peer_key) or {}
except Exception:
return False
if str(contact.get("invitePinnedPrekeyLookupHandle", "") or "").strip():
return True
if str(contact.get("invitePinnedLookupPeerUrl", "") or "").strip():
return True
trust = str(contact.get("trust_level", "") or "").strip().lower()
return trust in INVITE_CONNECT_TRUST_LEVELS
def relay_push_peer_urls_for_payload(payload: dict[str, Any]) -> list[str]:
urls: list[str] = []
for raw in list(payload.get("relay_push_peer_urls") or []):
normalized = str(raw or "").strip().rstrip("/")
if normalized and normalized not in urls:
urls.append(normalized)
lookup_peer_url = str(payload.get("lookup_peer_url", "") or "").strip().rstrip("/")
if lookup_peer_url:
urls = [url for url in urls if url != lookup_peer_url]
urls.insert(0, lookup_peer_url)
recipient_id = str(payload.get("recipient_id", "") or "").strip()
if recipient_id and not urls:
try:
from services.mesh.mesh_wormhole_contacts import get_wormhole_dm_contact
contact = get_wormhole_dm_contact(recipient_id) or {}
pinned = str(contact.get("invitePinnedLookupPeerUrl", "") or "").strip().rstrip("/")
if pinned:
urls.append(pinned)
except Exception:
pass
return urls
def should_auto_release_dm_payload(payload: dict[str, Any]) -> bool:
if str(payload.get("delivery_class", "") or "").strip().lower() != "request":
return False
intent = str(payload.get("connect_intent", "") or "").strip().lower()
if intent in CONNECT_AUTO_RELEASE_INTENTS:
return True
if str(payload.get("lookup_peer_url", "") or "").strip():
return True
recipient_id = str(payload.get("recipient_id", "") or "").strip()
return bool(recipient_id and recipient_has_invite_connect_scope(recipient_id))
def enrich_connect_release_payload(payload: dict[str, Any]) -> dict[str, Any]:
"""Attach invite-owner relay hints used during private release."""
enriched = dict(payload or {})
recipient_id = str(enriched.get("recipient_id", "") or "").strip()
lookup_peer_url = str(enriched.get("lookup_peer_url", "") or "").strip().rstrip("/")
if not lookup_peer_url and recipient_id:
try:
from services.mesh.mesh_wormhole_contacts import get_wormhole_dm_contact
contact = get_wormhole_dm_contact(recipient_id) or {}
lookup_peer_url = str(contact.get("invitePinnedLookupPeerUrl", "") or "").strip().rstrip("/")
except Exception:
lookup_peer_url = ""
if lookup_peer_url:
enriched["lookup_peer_url"] = lookup_peer_url
push_urls = relay_push_peer_urls_for_payload(enriched)
if push_urls:
enriched["relay_push_peer_urls"] = push_urls
return enriched
def auto_release_connect_dm_outbox(*, outbox_id: str, payload: dict[str, Any]) -> dict[str, Any]:
"""Grant scoped relay policy and approve release for invite-scoped connect traffic."""
normalized_outbox = str(outbox_id or "").strip()
enriched = enrich_connect_release_payload(payload)
if not normalized_outbox:
return {"ok": False, "detail": "missing outbox_id"}
if not should_auto_release_dm_payload(enriched):
return {"ok": True, "skipped": True, "reason": "not_connect_scoped"}
recipient_id = str(enriched.get("recipient_id", "") or "").strip()
if not recipient_id:
return {"ok": False, "detail": "missing recipient_id"}
grant = grant_connect_relay_policy(recipient_id)
try:
from services.mesh.mesh_private_outbox import private_delivery_outbox
from services.mesh.mesh_private_release_worker import private_release_worker
private_delivery_outbox.approve_relay_release(normalized_outbox)
private_release_worker.ensure_started()
private_release_worker.wake()
except Exception as exc:
return {
"ok": False,
"detail": str(exc) or type(exc).__name__,
"grant": grant,
}
return {
"ok": True,
"auto_released": True,
"outbox_id": normalized_outbox,
"recipient_id": recipient_id,
"grant": grant,
"relay_push_peer_urls": relay_push_peer_urls_for_payload(enriched),
}
+12 -1
View File
@@ -1506,6 +1506,7 @@ class DMRelay:
sender_token_hash: str = "",
payload_format: str = "dm1",
session_welcome: str = "",
replication_peer_urls: list[str] | None = None,
) -> dict[str, Any]:
with self._lock:
self._refresh_from_shared_relay()
@@ -1609,6 +1610,7 @@ class DMRelay:
if envelope_for_push:
self._replicate_envelope_to_peers_async(
envelope=envelope_for_push,
preferred_peer_urls=list(replication_peer_urls or []),
)
except Exception:
metrics_inc("dm_replication_push_error")
@@ -1716,6 +1718,7 @@ class DMRelay:
self,
*,
envelope: dict[str, Any],
preferred_peer_urls: list[str] | None = None,
) -> None:
"""Push an outbound DM envelope to every authenticated relay peer.
@@ -1747,7 +1750,15 @@ class DMRelay:
authenticated_push_peer_urls,
)
peers = authenticated_push_peer_urls()
peers: list[str] = []
for raw_url in list(preferred_peer_urls or []):
normalized_preferred = normalize_peer_url(str(raw_url or "").strip())
if normalized_preferred and normalized_preferred not in peers:
peers.append(normalized_preferred)
for peer_url in authenticated_push_peer_urls():
normalized_peer = normalize_peer_url(str(peer_url or "").strip())
if normalized_peer and normalized_peer not in peers:
peers.append(normalized_peer)
if not peers:
return
@@ -0,0 +1,86 @@
"""Auto-enable Tor wormhole transport on Infonet relay/seed nodes."""
from __future__ import annotations
import logging
from typing import Any
from services.config import get_settings
from services.wormhole_settings import read_wormhole_settings, write_wormhole_settings
logger = logging.getLogger(__name__)
def infonet_relay_auto_wormhole_requested() -> bool:
settings = get_settings()
if bool(settings.MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED):
return False
if bool(settings.MESH_INFONET_RELAY_AUTO_WORMHOLE):
return True
if str(settings.MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY or "").strip():
return True
return False
def _relay_tor_wormhole_target_settings() -> dict[str, Any]:
settings = get_settings()
socks_port = int(settings.MESH_ARTI_SOCKS_PORT or 9050)
return {
"enabled": True,
"transport": "tor_arti",
"socks_proxy": f"socks5h://127.0.0.1:{socks_port}",
"socks_dns": True,
"anonymous_mode": True,
}
def _wormhole_settings_match(existing: dict[str, Any], target: dict[str, Any]) -> bool:
return (
bool(existing.get("enabled")) is bool(target["enabled"])
and str(existing.get("transport", "")) == str(target["transport"])
and str(existing.get("socks_proxy", "")) == str(target["socks_proxy"])
and bool(existing.get("socks_dns", True)) is bool(target["socks_dns"])
and bool(existing.get("anonymous_mode", False)) is bool(target["anonymous_mode"])
)
def ensure_infonet_relay_wormhole_ready(*, reason: str = "relay_auto") -> dict[str, Any]:
"""Persist Tor wormhole settings and connect on relay/seed startup."""
if not infonet_relay_auto_wormhole_requested():
return {"ok": True, "skipped": True, "reason": "not_requested"}
from routers.ai_intel import _write_env_value
from services.tor_hidden_service import tor_service
from services.wormhole_supervisor import connect_wormhole, restart_wormhole
existing = read_wormhole_settings()
target = _relay_tor_wormhole_target_settings()
settings_updated = not _wormhole_settings_match(existing, target)
updated = write_wormhole_settings(**target) if settings_updated else existing
tor_result: dict[str, Any] = {"ok": False, "detail": "not started"}
try:
tor_result = tor_service.start(target_port=8000)
if tor_result.get("ok"):
_write_env_value("MESH_ARTI_ENABLED", "true")
get_settings.cache_clear()
except Exception as exc:
tor_result = {"ok": False, "detail": str(exc or type(exc).__name__)}
runtime = (
restart_wormhole(reason=reason)
if settings_updated
else connect_wormhole(reason=reason)
)
if settings_updated:
logger.info("Infonet relay auto-wormhole enabled (%s)", reason)
return {
"ok": True,
"skipped": False,
"settings_updated": settings_updated,
"tor": tor_result,
"runtime": runtime,
"settings": updated,
}
@@ -125,8 +125,8 @@ def dm_lookup_response_view(
view.pop("lookup_mode", None)
view.pop("removal_target", None)
return view
if invite_lookup:
view.pop("agent_id", None)
# Successful invite lookups keep agent_id: the handle is the capability and
# first-contact messaging needs a delivery target. Failures stay generic.
return view
+39 -2
View File
@@ -157,8 +157,45 @@ def transport_tier_is_sufficient(current_tier: str | None, required_tier: str |
return TRANSPORT_TIER_ORDER[current] >= TRANSPORT_TIER_ORDER[required]
def release_lane_required_tier(lane: str) -> str:
return network_release_required_tier(lane)
_DM_RUNTIME_ENFORCEMENT_ROUTES = {
("POST", "/api/mesh/dm/send"),
("POST", "/api/mesh/dm/poll"),
("GET", "/api/mesh/dm/poll"),
("GET", "/api/mesh/dm/count"),
("POST", "/api/mesh/dm/count"),
}
def runtime_route_enforcement_tier(path: str, method: str, *, static_tier: str) -> str:
"""Adjust static route tiers for Tor-only nodes that never reach private_strong."""
normalized_path = str(path or "").strip()
normalized_method = str(method or "").strip().upper()
static = normalize_transport_tier(static_tier)
if (normalized_method, normalized_path) not in _DM_RUNTIME_ENFORCEMENT_ROUTES:
return static
if static != "private_strong":
return static
return release_lane_required_tier("dm")
def release_lane_required_tier(lane: str, *, wormhole_state: dict[str, Any] | None = None) -> str:
normalized_lane = str(lane or "").strip().lower()
required = network_release_required_tier(normalized_lane)
if normalized_lane != "dm":
return required
state = wormhole_state
if state is None:
try:
from services.wormhole_supervisor import get_wormhole_state
state = get_wormhole_state()
except Exception:
state = {}
# Tor-only nodes never reach private_strong (needs Arti + RNS). Encrypted
# relay over Arti still preserves ciphertext privacy for offline delivery.
if not bool((state or {}).get("rns_enabled")):
return "private_transitional"
return required
def private_delivery_status(status_code: str, *, reason_code: str = "", plain_reason: str = "") -> dict[str, str]:
@@ -386,6 +386,14 @@ def _dispatch_dm(
sampled=sampled,
)
replication_peer_urls: list[str] = []
try:
from services.mesh.mesh_dm_connect_delivery import relay_push_peer_urls_for_payload
replication_peer_urls = relay_push_peer_urls_for_payload(payload)
except Exception:
replication_peer_urls = []
apply_dm_relay_jitter()
relay_result = dm_relay.deposit(
sender_id=relay_sender_id,
@@ -399,6 +407,7 @@ def _dispatch_dm(
sender_token_hash=sender_token_hash,
payload_format=payload_format,
session_welcome=session_welcome,
replication_peer_urls=replication_peer_urls,
)
if not relay_result.get("ok"):
return _dispatch_result(
@@ -600,8 +609,15 @@ def attempt_private_release(
policy_reason_code=str(decision.reason_code or ""),
)
if normalized_lane == "dm":
dm_payload = dict(payload or {})
try:
from services.mesh.mesh_dm_connect_delivery import enrich_connect_release_payload
dm_payload = enrich_connect_release_payload(dm_payload)
except Exception:
pass
return _dispatch_dm(
dict(payload or {}),
dm_payload,
secure_dm_enabled=secure_dm_enabled or _secure_dm_enabled,
rns_private_dm_ready=rns_private_dm_ready or _rns_private_dm_ready,
anonymous_dm_hidden_transport_enforced=(
+31 -1
View File
@@ -36,6 +36,22 @@ def _require_fields(payload: dict[str, Any], fields: tuple[str, ...]) -> tuple[b
return True, "ok"
_SEALED_CIPHERTEXT_PREFIXES = ("x3dh1:", "dm1:", "mls1:", "sealed:")
def _strip_sealed_ciphertext_prefix(value: str) -> str:
lowered = value.lower()
for prefix in _SEALED_CIPHERTEXT_PREFIXES:
if lowered.startswith(prefix):
return value[len(prefix) :]
return value
def _sealed_ciphertext_has_known_prefix(value: str) -> bool:
lowered = str(value or "").strip().lower()
return any(lowered.startswith(prefix) for prefix in _SEALED_CIPHERTEXT_PREFIXES)
def _decode_base64ish(value: Any) -> bytes | None:
raw = str(value or "").strip()
if not raw or any(ch.isspace() for ch in raw):
@@ -49,6 +65,13 @@ def _decode_base64ish(value: Any) -> bytes | None:
return None
def _decode_sealed_ciphertext_value(value: Any) -> bytes | None:
raw = str(value or "").strip()
if not raw:
return None
return _decode_base64ish(_strip_sealed_ciphertext_prefix(raw))
def _byte_entropy(data: bytes) -> float:
if not data:
return 0.0
@@ -66,12 +89,19 @@ def _validate_sealed_bytes_field(
min_bytes: int = 8,
entropy_floor: float = 2.5,
) -> tuple[bool, str]:
data = _decode_base64ish(payload.get(field, ""))
raw = str(payload.get(field, "") or "").strip()
prefixed = _sealed_ciphertext_has_known_prefix(raw)
data = _decode_sealed_ciphertext_value(raw)
if data is None:
return False, f"{field} must be base64-encoded sealed bytes"
if len(data) < min_bytes:
return False, f"{field} is too short"
# X3DH / MLS envelopes are structured JSON or ratchet frames — skip
# plaintext heuristics once a known wire prefix is present.
if prefixed:
return True, "ok"
# Short test vectors and compact envelopes can be low entropy; only apply
# heuristics once there is enough material to distinguish a sealed blob
# from accidental base64-encoded plaintext.
@@ -929,6 +929,85 @@ def list_wormhole_dm_contacts() -> dict[str, dict[str, Any]]:
return _read_contacts()
def get_wormhole_dm_contact(peer_id: str) -> dict[str, Any] | None:
peer_key = str(peer_id or "").strip()
if not peer_key:
return None
contacts = _read_contacts()
if peer_key not in contacts:
return None
return dict(_normalize_contact(contacts[peer_key]))
def sever_wormhole_dm_contact(peer_id: str, *, block: bool = False) -> dict[str, Any]:
"""Close the shared DM lane; a fresh contact request + accept is required to reopen."""
peer_key = str(peer_id or "").strip()
if not peer_key:
return {"ok": False, "detail": "peer_id required"}
contacts = _read_contacts()
current = _normalize_contact(contacts.get(peer_key))
now = int(time.time())
current["sharedAlias"] = ""
current["sharedAliasCounter"] = 0
current["sharedAliasPublicKey"] = ""
current["sharedAliasPublicKeyAlgo"] = "Ed25519"
current["previousSharedAliases"] = []
current["pendingSharedAlias"] = ""
current["pendingSharedAliasCounter"] = 0
current["pendingSharedAliasPublicKey"] = ""
current["pendingSharedAliasPublicKeyAlgo"] = "Ed25519"
current["pendingSharedAliasGraceMs"] = 0
current["sharedAliasGraceUntil"] = 0
current["sharedAliasRotatedAt"] = 0
current["acceptedPreviousAlias"] = ""
current["acceptedPreviousAliasCounter"] = 0
current["acceptedPreviousAliasPublicKey"] = ""
current["acceptedPreviousAliasPublicKeyAlgo"] = "Ed25519"
current["acceptedPreviousGraceUntil"] = 0
current["acceptedPreviousHardGraceUntil"] = 0
current["acceptedPreviousAwaitingReply"] = False
current["aliasBindingSeq"] = 0
current["aliasBindingPendingReason"] = ""
current["aliasBindingPreparedAt"] = 0
current["aliasGateJoinAppliedSeq"] = 0
if block:
current["blocked"] = True
current["updated_at"] = now
contacts[peer_key] = _normalize_contact(current)
_write_contacts(contacts)
relay_policy = {}
try:
from services.mesh.mesh_dm_connect_delivery import revoke_connect_relay_policy
relay_policy = revoke_connect_relay_policy(peer_key)
except Exception:
relay_policy = {"ok": False}
relay_block = {"ok": False}
if block:
try:
from services.mesh.mesh_dm_relay import dm_relay
from services.mesh.mesh_wormhole_persona import get_dm_identity
local_id = str(get_dm_identity().get("node_id", "") or "").strip()
if local_id:
dm_relay.block(local_id, peer_key)
relay_block = {"ok": True, "local_id": local_id}
except Exception as exc:
relay_block = {"ok": False, "detail": str(exc) or type(exc).__name__}
return {
"ok": True,
"peer_id": peer_key,
"severed": True,
"blocked": bool(block),
"relay_policy": relay_policy,
"relay_block": relay_block,
}
def _promote_invite_lookup_mode(contact: dict[str, Any], *, now: int | None = None) -> bool:
current = dict(contact or {})
lookup_handle = str(current.get("invitePinnedPrekeyLookupHandle", "") or "").strip()
@@ -1070,11 +1149,14 @@ def pin_wormhole_dm_invite(
identity_dh_pub_key = str(payload.get("identity_dh_pub_key", "") or "")
dh_algo = str(payload.get("dh_algo", "X25519") or "X25519")
prekey_lookup_handle = str(payload.get("prekey_lookup_handle", "") or "")
lookup_peer_url = str(payload.get("lookup_peer_url", "") or "").strip().rstrip("/")
if str(alias or "").strip():
current["alias"] = str(alias or "").strip()
current["dhPubKey"] = identity_dh_pub_key
current["dhAlgo"] = dh_algo
current["invitePinnedPrekeyLookupHandle"] = prekey_lookup_handle
if lookup_peer_url:
current["invitePinnedLookupPeerUrl"] = lookup_peer_url
current["invitePinnedRootFingerprint"] = str(payload.get("root_fingerprint", "") or "").strip().lower()
current["invitePinnedRootManifestFingerprint"] = str(
payload.get("root_manifest_fingerprint", "") or ""
@@ -1170,6 +1252,12 @@ def pin_wormhole_dm_invite(
current["updated_at"] = now
contacts[peer_key] = _normalize_contact(current)
_write_contacts(contacts)
try:
from services.mesh.mesh_dm_connect_delivery import grant_connect_relay_policy
grant_connect_relay_policy(peer_key, reason="invite_import")
except Exception:
pass
return contacts[peer_key]
@@ -549,6 +549,27 @@ def invite_identity_commitment_for_identity_material(
return hashlib.sha256(_stable_json(material).encode("utf-8")).hexdigest()
def _local_dm_lookup_peer_url() -> str:
"""Return this node's fleet-reachable URL for invite-scoped prekey lookup."""
try:
from services.config import get_settings
from services.mesh.mesh_crypto import normalize_peer_url
configured = normalize_peer_url(str(getattr(get_settings(), "MESH_PUBLIC_PEER_URL", "") or ""))
if configured:
return configured
from services.tor_hidden_service import tor_service
onion = str(getattr(tor_service, "onion_address", "") or "").strip()
if onion:
if "://" not in onion:
onion = f"http://{onion}:8000"
return normalize_peer_url(onion)
except Exception:
pass
return ""
def _dm_invite_payload(
data: dict[str, Any],
*,
@@ -930,6 +951,9 @@ def export_wormhole_dm_invite(*, label: str = "", expires_in_s: int = 0) -> dict
# fetch our prekey bundle without using our stable agent_id.
lookup_handle = secrets.token_hex(24)
payload["prekey_lookup_handle"] = lookup_handle
lookup_peer_url = _local_dm_lookup_peer_url()
if lookup_peer_url:
payload["lookup_peer_url"] = lookup_peer_url
# Persist the handle so it is included in future prekey registrations.
existing_handles, _ = _normalize_prekey_lookup_handles(
+348 -80
View File
@@ -79,6 +79,164 @@ def _warn_legacy_prekey_lookup(agent_id: str) -> None:
)
def _fleet_peer_lookup_user_agent() -> str:
custom = str(os.environ.get("SHADOWBROKER_MESH_PEER_USER_AGENT") or "").strip()
if custom:
return custom
return "Mozilla/5.0 (compatible; ShadowbrokerMesh/1.0)"
_INVITE_LOOKUP_MAX_ELAPSED_S = 120
_INVITE_LOOKUP_MAX_BOOTSTRAP_PEERS = 3
_INVITE_LOOKUP_MAX_PUSH_PEERS = 16
_INVITE_LOOKUP_PARALLEL_WORKERS = 8
def _invite_lookup_request_timeout(peer_url: str) -> tuple[int, int]:
from services.mesh.mesh_router import peer_transport_kind
if peer_transport_kind(peer_url) == "onion":
return (10, 35)
return (5, 15)
def _bootstrap_seed_peer_urls() -> set[str]:
try:
from services.config import get_settings
from services.mesh.mesh_router import parse_configured_relay_peers
seeds: set[str] = set()
raw = str(getattr(get_settings(), "MESH_BOOTSTRAP_SEED_PEERS", "") or "")
for peer in parse_configured_relay_peers(raw):
normalized = str(peer or "").strip().rstrip("/")
if normalized:
seeds.add(normalized)
return seeds
except Exception:
return set()
def _discovered_push_peer_urls(*, limit: int = _INVITE_LOOKUP_MAX_PUSH_PEERS) -> list[str]:
try:
from services.mesh.mesh_router import authenticated_push_peer_urls
seeds = _bootstrap_seed_peer_urls()
peers: list[str] = []
for peer in authenticated_push_peer_urls():
normalized = str(peer or "").strip().rstrip("/")
if not normalized or normalized in seeds:
continue
peers.append(normalized)
if len(peers) >= max(1, int(limit or 1)):
break
return peers
except Exception:
return []
def _prioritized_invite_lookup_peer_urls(*, preferred: list[str] | None = None) -> list[str]:
preferred_urls = [
str(peer or "").strip().rstrip("/")
for peer in list(preferred or [])
if str(peer or "").strip()
]
configured = _configured_public_lookup_peer_urls()
seeds = _bootstrap_seed_peer_urls()
active: list[str] = []
bootstrap: list[str] = []
push_discovery: list[str] = []
seen = set(preferred_urls)
for peer in configured:
if peer in seen:
continue
seen.add(peer)
if peer in seeds:
bootstrap.append(peer)
else:
active.append(peer)
for peer in _discovered_push_peer_urls():
if peer in seen:
continue
seen.add(peer)
push_discovery.append(peer)
ordered = list(preferred_urls)
ordered.extend(active)
ordered.extend(push_discovery)
ordered.extend(bootstrap[:_INVITE_LOOKUP_MAX_BOOTSTRAP_PEERS])
return ordered
def _preferred_invite_lookup_peer_urls(lookup_token: str) -> list[str]:
token = str(lookup_token or "").strip()
if not token:
return []
try:
from services.mesh.mesh_wormhole_contacts import list_wormhole_dm_contacts
except Exception:
return []
peers: list[str] = []
for contact in list_wormhole_dm_contacts() or []:
if not isinstance(contact, dict):
continue
if str(contact.get("invitePinnedPrekeyLookupHandle", "") or "").strip() != token:
continue
peer_url = str(contact.get("invitePinnedLookupPeerUrl", "") or "").strip().rstrip("/")
if peer_url and peer_url not in peers:
peers.append(peer_url)
return peers
def _peer_http_request(
method: str,
peer_url: str,
*,
body_bytes: bytes | None = None,
headers: dict[str, str] | None = None,
timeout: int | tuple[int, int] = 45,
):
"""HTTP to a fleet peer, using Tor SOCKS when the URL is an onion address."""
import requests
from services.mesh.mesh_crypto import normalize_peer_url
from urllib.parse import urlparse
raw_peer_url = str(peer_url or "").strip()
parsed = urlparse(raw_peer_url)
if parsed.path and parsed.path not in {"", "/"}:
# Full request URLs include invite lookup query params; do not
# normalize them away when deriving the peer base URL.
normalized = raw_peer_url
else:
normalized = normalize_peer_url(raw_peer_url)
if not normalized:
raise OSError("invalid peer url")
if isinstance(timeout, tuple):
connect_timeout, read_timeout = timeout
resolved_timeout: int | tuple[int, int] = (
max(1, int(connect_timeout or 5)),
max(1, int(read_timeout or 15)),
)
else:
resolved_timeout = max(1, int(timeout or 45))
request_kwargs: dict[str, Any] = {
"headers": dict(headers or {}),
"timeout": resolved_timeout,
}
try:
from main import _infonet_peer_requests_proxies
proxy_peer_url = normalize_peer_url(f"{parsed.scheme}://{parsed.netloc}")
proxies = _infonet_peer_requests_proxies(proxy_peer_url)
if proxies:
request_kwargs["proxies"] = proxies
except Exception:
pass
if method.upper() == "GET":
return requests.get(normalized, **request_kwargs)
request_kwargs["data"] = body_bytes or b""
return requests.post(normalized, **request_kwargs)
def _fetch_dm_prekey_bundle_from_peer_lookup(lookup_token: str) -> dict[str, Any]:
"""Fetch an invite-scoped prekey bundle from configured authenticated peers.
@@ -95,12 +253,12 @@ def _fetch_dm_prekey_bundle_from_peer_lookup(lookup_token: str) -> dict[str, Any
normalize_peer_url,
resolve_peer_key_for_url,
)
from services.mesh.mesh_router import configured_relay_peer_urls
from services.mesh.mesh_router import authenticated_push_peer_urls
settings = get_settings()
# Issue #256: secret check moved per-peer below. We still bail out
# cleanly when there are no peers configured at all.
peers = configured_relay_peer_urls()
peers = authenticated_push_peer_urls()
if not peers:
return {"ok": False, "detail": "peer prekey lookup unavailable"}
timeout = max(1, _safe_int(getattr(settings, "MESH_RELAY_PUSH_TIMEOUT_S", 10) or 10, 10))
@@ -132,17 +290,17 @@ def _fetch_dm_prekey_bundle_from_peer_lookup(lookup_token: str) -> dict[str, Any
"X-Peer-Url": sender_peer_url,
"X-Peer-HMAC": hmac.new(peer_key, body, hashlib.sha256).hexdigest(),
}
request = urllib.request.Request(
f"{normalized_peer_url}/api/mesh/dm/prekey-peer-lookup",
data=body,
headers=headers,
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
raw = response.read(256 * 1024)
response = _peer_http_request(
"POST",
f"{normalized_peer_url}/api/mesh/dm/prekey-peer-lookup",
body_bytes=body,
headers=headers,
timeout=timeout,
)
raw = response.content[: 256 * 1024]
payload = json.loads(raw.decode("utf-8"))
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError) as exc:
except (json.JSONDecodeError, OSError, Exception) as exc:
last_detail = str(exc) or type(exc).__name__
continue
if isinstance(payload, dict) and payload.get("ok"):
@@ -161,12 +319,18 @@ def _configured_public_lookup_peer_urls() -> list[str]:
settings = get_settings()
candidates: list[str] = []
# Operator-configured peers first, then recently active fleet nodes.
# Invite handles are minted on a specific node; cold bootstrap seeds
# rarely have them cached and should not be tried before contacts.
for raw in (
getattr(settings, "MESH_BOOTSTRAP_SEED_PEERS", ""),
getattr(settings, "MESH_DEFAULT_SYNC_PEERS", ""),
):
candidates.extend(parse_configured_relay_peers(str(raw or "")))
candidates.extend(active_sync_peer_urls())
for raw in (
getattr(settings, "MESH_BOOTSTRAP_SEED_PEERS", ""),
):
candidates.extend(parse_configured_relay_peers(str(raw or "")))
except Exception:
return []
@@ -204,7 +368,50 @@ def _normalize_remote_lookup_bundle(payload: dict[str, Any]) -> dict[str, Any]:
return data
def _fetch_dm_prekey_bundle_from_public_lookup(lookup_token: str) -> dict[str, Any]:
def _try_public_prekey_lookup_peer(
peer_url: str,
encoded: str,
*,
timeout: int | tuple[int, int] | None = None,
) -> dict[str, Any]:
normalized_peer_url = str(peer_url or "").strip().rstrip("/")
if not normalized_peer_url:
return {"ok": False, "detail": "invalid peer url"}
resolved_timeout = timeout or _invite_lookup_request_timeout(normalized_peer_url)
try:
response = _peer_http_request(
"GET",
f"{normalized_peer_url}/api/mesh/dm/prekey-bundle?{encoded}",
headers={
"Accept": "application/json",
"User-Agent": _fleet_peer_lookup_user_agent(),
},
timeout=resolved_timeout,
)
raw = response.content[: 256 * 1024]
payload = json.loads(raw.decode("utf-8"))
except (json.JSONDecodeError, OSError, Exception) as exc:
logger.debug("public prekey lookup failed for %s: %s", normalized_peer_url, type(exc).__name__)
return {"ok": False, "detail": "peer prekey lookup unavailable"}
if not isinstance(payload, dict):
return {"ok": False, "detail": "invalid peer response"}
if payload.get("pending") or str(payload.get("status", "") or "") == "preparing_private_lane":
return {"ok": False, "detail": "peer prekey lookup still preparing"}
if not payload.get("ok"):
return {
"ok": False,
"detail": str(payload.get("detail", "") or "Prekey bundle not found"),
}
if not isinstance(payload.get("bundle"), dict):
return {"ok": False, "detail": "Prekey bundle not found"}
return _normalize_remote_lookup_bundle(payload)
def _fetch_dm_prekey_bundle_from_public_lookup(
lookup_token: str,
*,
extra_preferred_peer_urls: list[str] | None = None,
) -> dict[str, Any]:
"""Fetch an invite-scoped prekey bundle from bootstrap/sync peers.
The token is high-entropy and invite-scoped. This path does not expose a
@@ -212,61 +419,69 @@ def _fetch_dm_prekey_bundle_from_public_lookup(lookup_token: str) -> dict[str, A
derive it from the signed identity public key and validate the bundle before
accepting it.
"""
from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, wait
token = str(lookup_token or "").strip()
if not token:
return {"ok": False, "detail": "lookup token required"}
peers = _configured_public_lookup_peer_urls()
preferred = list(_preferred_invite_lookup_peer_urls(token))
for peer in list(extra_preferred_peer_urls or []):
normalized = str(peer or "").strip().rstrip("/")
if normalized and normalized not in preferred:
preferred.insert(0, normalized)
peers = _prioritized_invite_lookup_peer_urls(preferred=preferred)
if not peers:
return {"ok": False, "detail": "peer prekey lookup unavailable"}
try:
from services.config import get_settings
timeout = max(1, _safe_int(getattr(get_settings(), "MESH_SYNC_TIMEOUT_S", 5) or 5, 5))
except Exception:
timeout = 5
encoded = urllib.parse.urlencode({"lookup_token": token})
last_detail = ""
for peer_url in peers:
normalized_peer_url = str(peer_url or "").strip().rstrip("/")
if not normalized_peer_url:
continue
# Generic UA: any peer-facing crypto request should not carry a
# fork-specific identifier — that turns prekey lookups into a
# software-fingerprinting beacon.
from services.network_utils import default_user_agent
request = urllib.request.Request(
f"{normalized_peer_url}/api/mesh/dm/prekey-bundle?{encoded}",
headers={
"Accept": "application/json",
"User-Agent": default_user_agent(),
},
method="GET",
hinted_only = bool(list(extra_preferred_peer_urls or []))
hint_timeout = (5, 20)
for peer_url in preferred:
hinted = _try_public_prekey_lookup_peer(
peer_url,
encoded,
timeout=hint_timeout if hinted_only else None,
)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
raw = response.read(256 * 1024)
payload = json.loads(raw.decode("utf-8"))
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError) as exc:
logger.debug("public prekey lookup failed for %s: %s", normalized_peer_url, type(exc).__name__)
last_detail = "peer prekey lookup unavailable"
continue
if not isinstance(payload, dict):
last_detail = "invalid peer response"
continue
if payload.get("pending") or str(payload.get("status", "") or "") == "preparing_private_lane":
last_detail = "peer prekey lookup still preparing"
continue
if not payload.get("ok"):
last_detail = str(payload.get("detail", "") or last_detail or "Prekey bundle not found")
continue
if not isinstance(payload.get("bundle"), dict):
last_detail = "Prekey bundle not found"
continue
normalized = _normalize_remote_lookup_bundle(payload)
if normalized.get("ok"):
return normalized
last_detail = str(normalized.get("detail", "") or last_detail)
if hinted.get("ok"):
return hinted
if isinstance(hinted, dict):
last_detail = str(hinted.get("detail", "") or last_detail)
remaining_peers = [peer for peer in peers if peer not in set(preferred)]
if not remaining_peers:
return {"ok": False, "detail": last_detail or "Prekey bundle not found"}
if hinted_only:
return {"ok": False, "detail": last_detail or "Prekey bundle not found"}
deadline = time.time() + _INVITE_LOOKUP_MAX_ELAPSED_S
workers = min(_INVITE_LOOKUP_PARALLEL_WORKERS, max(1, len(remaining_peers)))
with ThreadPoolExecutor(max_workers=workers) as executor:
futures = {
executor.submit(_try_public_prekey_lookup_peer, peer_url, encoded): peer_url
for peer_url in remaining_peers
}
while futures and time.time() < deadline:
done, _ = wait(
futures,
timeout=max(0.1, deadline - time.time()),
return_when=FIRST_COMPLETED,
)
if not done:
break
for future in done:
futures.pop(future, None)
try:
result = future.result()
except Exception as exc:
last_detail = str(exc) or type(exc).__name__
continue
if isinstance(result, dict) and result.get("ok"):
for pending in futures:
pending.cancel()
return result
if isinstance(result, dict):
last_detail = str(result.get("detail", "") or last_detail)
for pending in futures:
pending.cancel()
return {"ok": False, "detail": last_detail or "Prekey bundle not found"}
@@ -1019,6 +1234,7 @@ def fetch_dm_prekey_bundle(
lookup_token: str = "",
*,
allow_peer_lookup: bool = True,
lookup_peer_urls: list[str] | None = None,
) -> dict[str, Any]:
from services.mesh.mesh_dm_relay import dm_relay
@@ -1043,12 +1259,18 @@ def fetch_dm_prekey_bundle(
resolved_id = found_id
lookup_mode = "invite_lookup_handle"
elif allow_peer_lookup:
peer_found = _fetch_dm_prekey_bundle_from_peer_lookup(resolved_lookup)
if peer_found.get("ok"):
return peer_found
public_found = _fetch_dm_prekey_bundle_from_public_lookup(resolved_lookup)
preferred_peer_urls = list(lookup_peer_urls or [])
public_found = _fetch_dm_prekey_bundle_from_public_lookup(
resolved_lookup,
extra_preferred_peer_urls=preferred_peer_urls,
)
if public_found.get("ok"):
return public_found
peer_found: dict[str, Any] = {"ok": False, "detail": ""}
if not preferred_peer_urls:
peer_found = _fetch_dm_prekey_bundle_from_peer_lookup(resolved_lookup)
if peer_found.get("ok"):
return peer_found
if str(public_found.get("detail", "") or "").strip():
return {"ok": False, "detail": str(public_found.get("detail", "") or "Prekey bundle not found")}
return {"ok": False, "detail": str(peer_found.get("detail", "") or "Prekey bundle not found")}
@@ -1134,12 +1356,22 @@ def _classify_root_attestation_failure(peer_id: str) -> tuple[str, bool]:
return "", False
def bootstrap_encrypt_for_peer(peer_id: str, plaintext: str) -> dict[str, Any]:
fetched_bundle = fetch_dm_prekey_bundle(str(peer_id or "").strip())
def bootstrap_encrypt_for_peer(
peer_id: str,
plaintext: str,
*,
lookup_token: str = "",
) -> dict[str, Any]:
token = str(lookup_token or "").strip()
peer = str(peer_id or "").strip()
fetched_bundle = fetch_dm_prekey_bundle(
agent_id=peer if not token else "",
lookup_token=token,
)
if not fetched_bundle.get("ok"):
detail = str(fetched_bundle.get("detail", "") or "")
if "root attestation" in detail.lower():
trust_level, trust_changed = _classify_root_attestation_failure(str(peer_id or "").strip())
trust_level, trust_changed = _classify_root_attestation_failure(peer or token)
if trust_level:
return {
"ok": False,
@@ -1152,32 +1384,68 @@ def bootstrap_encrypt_for_peer(peer_id: str, plaintext: str) -> dict[str, Any]:
from services.mesh.mesh_dm_relay import dm_relay
resolved_peer_id = str(fetched_bundle.get("agent_id", peer_id) or peer_id).strip()
resolved_peer_id = str(fetched_bundle.get("agent_id", peer) or peer).strip()
stored = dm_relay.get_prekey_bundle(resolved_peer_id)
if not stored:
return {"ok": False, "detail": "Peer prekey bundle not found"}
remote_bundle = dict(fetched_bundle.get("bundle") or {})
if not remote_bundle and fetched_bundle.get("identity_dh_pub_key"):
remote_bundle = fetched_bundle
if remote_bundle:
stored = {
"bundle": remote_bundle,
"signature": str(fetched_bundle.get("signature", "") or ""),
"public_key": str(fetched_bundle.get("public_key", "") or ""),
"public_key_algo": str(fetched_bundle.get("public_key_algo", "") or ""),
"sequence": _safe_int(fetched_bundle.get("sequence", 0) or 0),
}
else:
return {"ok": False, "detail": "Peer prekey bundle not found"}
validated_record = {**dict(stored), "agent_id": resolved_peer_id}
ok, reason = _validate_bundle_record(validated_record)
if not ok:
return {"ok": False, "detail": reason}
trust_state = observe_remote_prekey_bundle(resolved_peer_id, validated_record)
trust_level = str(trust_state.get("trust_level", "") or "")
from services.mesh.mesh_wormhole_contacts import verified_first_contact_requirement
consent_handshake = False
try:
from services.mesh.mesh_wormhole_dead_drop import parse_contact_consent
verified_first_contact = verified_first_contact_requirement(
resolved_peer_id,
trust_level=trust_level,
)
if not verified_first_contact.get("ok"):
return {
"ok": False,
"peer_id": resolved_peer_id,
"detail": str(verified_first_contact.get("detail", "") or "verified first contact required"),
"trust_changed": trust_level in ("mismatch", "continuity_broken"),
"trust_level": str(verified_first_contact.get("trust_level", "") or trust_level or "unpinned"),
consent = parse_contact_consent(str(plaintext or "")) or {}
consent_handshake = str(consent.get("kind", "") or "") in {
"contact_offer",
"contact_accept",
"contact_deny",
}
except Exception:
consent_handshake = False
if not consent_handshake:
from services.mesh.mesh_wormhole_contacts import verified_first_contact_requirement
verified_first_contact = verified_first_contact_requirement(
resolved_peer_id,
trust_level=trust_level,
)
if not verified_first_contact.get("ok"):
return {
"ok": False,
"peer_id": resolved_peer_id,
"detail": str(
verified_first_contact.get("detail", "") or "verified first contact required"
),
"trust_changed": trust_level in ("mismatch", "continuity_broken"),
"trust_level": str(
verified_first_contact.get("trust_level", "") or trust_level or "unpinned"
),
}
peer_bundle_stored = dm_relay.consume_one_time_prekey(resolved_peer_id)
if not peer_bundle_stored:
remote_bundle = dict(stored.get("bundle") or {})
otks = list(remote_bundle.get("one_time_prekeys") or [])
peer_bundle_stored = {
"bundle": remote_bundle,
"claimed_one_time_prekey": dict(otks[0] or {}) if otks else {},
}
if not peer_bundle_stored.get("bundle"):
return {"ok": False, "detail": "Peer prekey bundle not found"}
peer_bundle = dict(peer_bundle_stored.get("bundle") or {})
peer_static = str(peer_bundle.get("identity_dh_pub_key", "") or "")
+90
View File
@@ -90,6 +90,11 @@ READ_COMMANDS = frozenset({
# Agent routing helpers
"route_query",
"run_playbook",
# Private Infonet reads (operator-delegated)
"infonet_status",
"list_gates",
"read_gate_messages",
"poll_dms",
})
WRITE_COMMANDS = frozenset({
@@ -121,6 +126,12 @@ WRITE_COMMANDS = frozenset({
"clear_analysis_zones",
# Active recon (subnet device discovery)
"osint_sweep",
# Private Infonet writes (operator wormhole identity)
"ensure_infonet_ready",
"join_infonet_swarm",
"post_gate_message",
"cast_vote",
"send_dm",
})
@@ -1598,6 +1609,85 @@ def _dispatch_command(cmd: str, args: dict[str, Any]) -> dict[str, Any]:
count = clear_zones(source="openclaw")
return {"ok": True, "data": {"removed_count": count}}
# -- Infonet / gate / DM (operator-delegated, full tier for writes) ------
if cmd == "infonet_status":
from services.openclaw_infonet import get_infonet_status
return get_infonet_status()
if cmd == "ensure_infonet_ready":
from services.openclaw_infonet import ensure_infonet_ready
return ensure_infonet_ready(join_swarm=bool(args.get("join_swarm", True)))
if cmd == "join_infonet_swarm":
from services.openclaw_infonet import join_infonet_swarm
return join_infonet_swarm()
if cmd == "list_gates":
from services.openclaw_infonet import list_gates
return list_gates()
if cmd == "read_gate_messages":
from services.openclaw_infonet import read_gate_messages
gate_id = str(args.get("gate_id", "") or args.get("gate", "")).strip()
return read_gate_messages(
gate_id,
limit=int(args.get("limit", 20) or 20),
decrypt=bool(args.get("decrypt", False)),
)
if cmd == "post_gate_message":
from services.openclaw_infonet import post_gate_message
gate_id = str(args.get("gate_id", "") or args.get("gate", "")).strip()
plaintext = str(args.get("plaintext", "") or args.get("message", "")).strip()
return post_gate_message(
gate_id,
plaintext,
reply_to=str(args.get("reply_to", "") or ""),
)
if cmd == "cast_vote":
from services.openclaw_infonet import cast_vote
target_id = str(args.get("target_id", "") or args.get("target", "")).strip()
vote_raw = args.get("vote", args.get("direction"))
try:
vote_val = int(vote_raw)
except (TypeError, ValueError):
return {"ok": False, "detail": "vote must be 1 or -1"}
return cast_vote(
target_id,
vote_val,
gate=str(args.get("gate", "") or args.get("gate_id", "")).strip(),
)
if cmd == "send_dm":
from services.openclaw_infonet import send_dm
peer_id = str(
args.get("peer_id", "")
or args.get("recipient_id", "")
or args.get("recipient", "")
).strip()
plaintext = str(args.get("plaintext", "") or args.get("message", "")).strip()
return send_dm(
peer_id,
plaintext,
delivery_class=str(args.get("delivery_class", "shared") or "shared"),
recipient_token=str(args.get("recipient_token", "") or ""),
)
if cmd == "poll_dms":
from services.openclaw_infonet import poll_dms
return poll_dms(limit=int(args.get("limit", 20) or 20))
return {"ok": False, "detail": f"unhandled command: {cmd}"}
+760
View File
@@ -0,0 +1,760 @@
"""OpenClaw agent delegation for private Infonet / gate / DM actions.
Agents authenticate with OpenClaw HMAC on the command channel. Write
commands require ``OPENCLAW_ACCESS_TIER=full``. Actions use the operator's
local wormhole persona and node runtime the agent posts on behalf of the
user who configured the skill, not as a separate fleet identity.
"""
from __future__ import annotations
import asyncio
import json
import logging
import os
import secrets
import time
from typing import Any
from starlette.requests import Request
logger = logging.getLogger(__name__)
def _run_async(coro):
try:
asyncio.get_running_loop()
except RuntimeError:
return asyncio.run(coro)
return asyncio.run(coro)
def _local_agent_request(path: str, *, method: str = "POST") -> Request:
scope = {
"type": "http",
"method": method.upper(),
"path": path,
"headers": [],
"client": ("127.0.0.1", 52421),
}
request = Request(scope)
request.state._private_lane_current_tier = "private_strong"
request.state._transport_tier = "private_strong"
return request
def ensure_infonet_ready(*, join_swarm: bool = True) -> dict[str, Any]:
"""Warm Tor, enable the participant node, and optionally join the swarm."""
from routers.ai_intel import _write_env_value
from services.config import get_settings
from services.mesh.mesh_swarm_runtime import (
announce_local_peer_to_seeds,
refresh_swarm_manifest_from_seeds,
)
from services.node_settings import read_node_settings, write_node_settings
from services.tor_hidden_service import tor_service
from services.wormhole_supervisor import _check_arti_ready
steps: dict[str, Any] = {}
tor_result = tor_service.start(target_port=8000)
steps["tor"] = tor_result
if tor_result.get("ok"):
try:
_write_env_value("MESH_ARTI_ENABLED", "true")
get_settings.cache_clear()
except Exception as exc:
logger.debug("failed to persist MESH_ARTI_ENABLED: %s", exc)
if not _check_arti_ready():
return {
"ok": False,
"detail": "Tor/Arti transport is not ready yet",
"steps": steps,
}
if not bool(read_node_settings().get("enabled")):
write_node_settings(enabled=True)
steps["node_enabled"] = True
try:
import main as main_mod
main_mod._refresh_node_peer_store()
main_mod._start_infonet_node_runtime("openclaw_agent")
except Exception as exc:
logger.warning("node runtime start after agent enable failed: %s", exc)
else:
steps["node_enabled"] = True
if join_swarm:
steps["announce"] = announce_local_peer_to_seeds(force=True)
steps["manifest_pull"] = refresh_swarm_manifest_from_seeds(force=True)
ok = bool(steps["announce"].get("ok")) or bool(steps["manifest_pull"].get("ok"))
else:
ok = True
return {
"ok": ok,
"detail": "Infonet participant runtime ready" if ok else "swarm join incomplete",
"steps": steps,
"onion_address": str(tor_result.get("onion_address") or ""),
}
def join_infonet_swarm() -> dict[str, Any]:
from services.mesh.mesh_swarm_runtime import (
announce_local_peer_to_seeds,
refresh_swarm_manifest_from_seeds,
)
announce = announce_local_peer_to_seeds(force=True)
manifest = refresh_swarm_manifest_from_seeds(force=True)
return {
"ok": bool(announce.get("ok")) or bool(manifest.get("ok")),
"announce": announce,
"manifest_pull": manifest,
}
def get_infonet_status() -> dict[str, Any]:
from services.mesh.mesh_hashchain import infonet
from services.wormhole_supervisor import get_wormhole_state
info = infonet.get_info()
valid, reason = infonet.validate_chain(verify_signatures=False)
try:
wormhole = get_wormhole_state()
except Exception:
wormhole = {"configured": False, "ready": False, "arti_ready": False, "rns_ready": False}
try:
import main as main_mod
runtime = main_mod._node_runtime_snapshot()
private_tier = main_mod._current_private_lane_tier(wormhole)
except Exception:
runtime = {}
private_tier = "public_degraded"
return {
"ok": True,
"chain": info,
"valid": valid,
"validation": reason,
"private_lane_tier": private_tier,
"wormhole": wormhole,
"runtime": runtime,
}
def list_gates() -> dict[str, Any]:
from services.mesh.mesh_reputation import gate_manager
return {"ok": True, "gates": gate_manager.list_gates()}
def read_gate_messages(
gate_id: str,
*,
limit: int = 20,
decrypt: bool = False,
) -> dict[str, Any]:
from services.mesh.mesh_hashchain import gate_store
gate_key = str(gate_id or "").strip().lower()
if not gate_key:
return {"ok": False, "detail": "gate_id required"}
messages, cursor = gate_store.get_messages_with_cursor(gate_key, limit=max(1, min(int(limit), 100)))
out = []
if decrypt:
from services.mesh.mesh_gate_repair import decrypt_gate_message_with_repair
for msg in messages:
item = dict(msg)
try:
decrypted = decrypt_gate_message_with_repair(
gate_id=gate_key,
epoch=int(item.get("epoch") or 0),
ciphertext=str(item.get("ciphertext") or ""),
nonce=str(item.get("nonce") or item.get("iv") or ""),
sender_ref=str(item.get("sender_ref") or ""),
gate_envelope=str(item.get("gate_envelope") or ""),
envelope_hash=str(item.get("envelope_hash") or ""),
event_id=str(item.get("event_id") or ""),
)
if decrypted.get("ok"):
item["plaintext"] = decrypted.get("plaintext", "")
except Exception as exc:
item["decrypt_error"] = str(exc)
out.append(item)
else:
out = [dict(m) for m in messages]
return {
"ok": True,
"gate": gate_key,
"count": len(out),
"cursor": cursor,
"messages": out,
}
def post_gate_message(
gate_id: str,
plaintext: str,
*,
reply_to: str = "",
) -> dict[str, Any]:
"""Compose, sign, and post an MLS gate message using the operator persona."""
from services.mesh.mesh_gate_repair import (
compose_gate_message_with_repair,
sign_gate_message_with_repair,
)
from services.mesh.mesh_wormhole_persona import bootstrap_wormhole_persona_state, create_gate_persona
gate_key = str(gate_id or "").strip().lower()
if not gate_key:
return {"ok": False, "detail": "gate_id required"}
if not str(plaintext or "").strip():
return {"ok": False, "detail": "plaintext required"}
bootstrap_wormhole_persona_state(force=False)
try:
create_gate_persona(gate_key, label="openclaw-agent")
except Exception:
pass
composed = compose_gate_message_with_repair(
gate_id=gate_key,
plaintext=str(plaintext),
reply_to=str(reply_to or ""),
)
if not composed.get("ok"):
return composed
signed = sign_gate_message_with_repair(
gate_id=gate_key,
epoch=int(composed.get("epoch") or 0),
ciphertext=str(composed.get("ciphertext") or ""),
nonce=str(composed.get("nonce") or ""),
payload_format=str(composed.get("format") or "mls1"),
reply_to=str(reply_to or ""),
envelope_hash=str(composed.get("envelope_hash") or ""),
transport_lock="private_strong",
)
if not signed.get("ok"):
return signed
body = {
"sender_id": str(signed.get("sender_id") or composed.get("sender_id") or ""),
"public_key": str(signed.get("public_key") or composed.get("public_key") or ""),
"public_key_algo": str(signed.get("public_key_algo") or composed.get("public_key_algo") or ""),
"signature": str(signed.get("signature") or ""),
"sequence": int(signed.get("sequence") or composed.get("sequence") or 0),
"protocol_version": str(signed.get("protocol_version") or composed.get("protocol_version") or ""),
"epoch": int(signed.get("epoch") or composed.get("epoch") or 0),
"ciphertext": str(signed.get("ciphertext") or composed.get("ciphertext") or ""),
"nonce": str(signed.get("nonce") or composed.get("nonce") or ""),
"sender_ref": str(signed.get("sender_ref") or composed.get("sender_ref") or ""),
"format": str(signed.get("format") or composed.get("format") or "mls1"),
"gate_envelope": str(signed.get("gate_envelope") or composed.get("gate_envelope") or ""),
"envelope_hash": str(signed.get("envelope_hash") or composed.get("envelope_hash") or ""),
"transport_lock": "private_strong",
"reply_to": str(signed.get("reply_to") or reply_to or ""),
}
import main as main_mod
path = f"/api/mesh/gate/{gate_key}/message"
request = _local_agent_request(path)
return main_mod._submit_gate_message_envelope(request, gate_key, body)
def cast_vote(
target_id: str,
vote: int,
*,
gate: str = "",
) -> dict[str, Any]:
"""Cast a signed reputation vote using the operator gate/transport persona."""
from services.mesh.mesh_hashchain import infonet
from services.mesh.mesh_protocol import PROTOCOL_VERSION, normalize_payload
from services.mesh.mesh_reputation import gate_manager, reputation_ledger
from services.mesh.mesh_wormhole_persona import (
bootstrap_wormhole_persona_state,
sign_gate_wormhole_event,
sign_public_wormhole_event,
)
voter_gate = str(gate or "").strip().lower()
target = str(target_id or "").strip()
vote_val = int(vote)
if not target:
return {"ok": False, "detail": "target_id required"}
if vote_val not in (1, -1):
return {"ok": False, "detail": "vote must be 1 or -1"}
bootstrap_wormhole_persona_state(force=False)
vote_payload = {"target_id": target, "vote": vote_val, "gate": voter_gate}
normalized = normalize_payload("vote", vote_payload)
ok_payload, reason = True, "ok"
from services.mesh.mesh_schema import validate_event_payload
ok_payload, reason = validate_event_payload("vote", normalized)
if not ok_payload:
return {"ok": False, "detail": reason}
if voter_gate:
signed = sign_gate_wormhole_event(
gate_id=voter_gate,
event_type="vote",
payload=normalized,
)
else:
signed = sign_public_wormhole_event(event_type="vote", payload=normalized)
if not signed.get("ok", True):
return signed
voter_id = str(signed.get("node_id") or "")
public_key = str(signed.get("public_key") or "")
public_key_algo = str(signed.get("public_key_algo") or "")
signature = str(signed.get("signature") or "")
sequence = int(signed.get("sequence") or 0)
if voter_gate:
can_enter, enter_reason = gate_manager.can_enter(voter_id, voter_gate)
if not can_enter:
return {"ok": False, "detail": f"Gate vote denied: {enter_reason}"}
reputation_ledger.register_node(voter_id, public_key, public_key_algo)
stable_voter_id = voter_id
try:
import main as main_mod
root_nid = main_mod._cached_root_node_id()
if root_nid:
stable_voter_id = root_nid
except Exception:
pass
ok, cast_reason, weight = reputation_ledger.cast_vote(
stable_voter_id,
target,
vote_val,
voter_gate,
)
if ok:
try:
infonet.append(
event_type="vote",
node_id=voter_id,
payload=normalized,
signature=signature,
sequence=sequence,
public_key=public_key,
public_key_algo=public_key_algo,
protocol_version=str(signed.get("protocol_version") or PROTOCOL_VERSION),
)
except Exception as exc:
logger.warning("vote recorded in ledger but infonet append failed: %s", exc)
return {"ok": ok, "detail": cast_reason, "weight": round(float(weight or 0), 2)}
def _http_post_json(
url: str,
body: dict[str, Any],
*,
extra_headers: dict[str, str] | None = None,
timeout: int = 120,
) -> dict[str, Any]:
import urllib.error
import urllib.request
payload_bytes = json.dumps(body, separators=(",", ":"), sort_keys=True).encode("utf-8")
headers = {"Content-Type": "application/json"}
if extra_headers:
headers.update(extra_headers)
req = urllib.request.Request(url, data=payload_bytes, headers=headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
raw = resp.read().decode("utf-8")
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
try:
parsed = json.loads(detail)
if isinstance(parsed, dict):
return parsed
except Exception:
pass
return {"ok": False, "detail": detail or f"http {exc.code}"}
if not raw:
return {}
parsed = json.loads(raw)
return parsed if isinstance(parsed, dict) else {"ok": False, "detail": "invalid json response"}
def _issue_sender_token_for_http_send(
api_base: str,
*,
recipient: str,
delivery: str,
recipient_token: str,
) -> dict[str, Any]:
extra_headers: dict[str, str] = {}
admin_key = str(os.environ.get("ADMIN_KEY") or "").strip()
if admin_key:
extra_headers["X-Admin-Key"] = admin_key
return _http_post_json(
f"{api_base}/api/wormhole/dm/sender-token",
{
"recipient_id": recipient,
"delivery_class": delivery,
"recipient_token": recipient_token,
},
extra_headers=extra_headers or None,
)
def _submit_signed_dm_send(
*,
recipient: str,
delivery_class: str,
recipient_token: str,
ciphertext: str,
payload_format: str,
session_welcome: str = "",
connect_intent: str = "",
lookup_peer_url: str = "",
) -> dict[str, Any]:
import main as main_mod
from services.mesh.mesh_protocol import (
PROTOCOL_VERSION,
SIGNED_CONTEXT_FIELD,
build_signed_context,
)
from services.mesh.mesh_schema import validate_event_payload
from services.mesh.mesh_wormhole_persona import get_dm_identity, sign_dm_wormhole_event
from services.mesh.mesh_wormhole_sender_token import issue_wormhole_dm_sender_token
delivery = str(delivery_class or "shared").strip().lower()
identity = get_dm_identity()
sender_id = str(identity.get("node_id") or "")
msg_id = secrets.token_hex(16)
timestamp = int(time.time())
sequence = int(identity.get("sequence", 0) or 0) + 1
dm_payload: dict[str, Any] = {
"recipient_id": recipient,
"delivery_class": delivery,
"recipient_token": str(recipient_token or ""),
"ciphertext": str(ciphertext or ""),
"msg_id": msg_id,
"timestamp": timestamp,
"format": str(payload_format or "mls1"),
"transport_lock": "private_strong",
}
if session_welcome:
dm_payload["session_welcome"] = str(session_welcome)
ok_payload, reason = validate_event_payload("dm_message", dm_payload)
if not ok_payload:
return {"ok": False, "detail": reason}
dm_payload[SIGNED_CONTEXT_FIELD] = build_signed_context(
event_type="dm_message",
kind="dm_send",
endpoint="/api/mesh/dm/send",
lane_floor="private_strong",
sequence_domain="dm_send",
node_id=sender_id,
sequence=sequence,
payload=dm_payload,
recipient_id=recipient,
)
signed = sign_dm_wormhole_event(
event_type="dm_message",
payload=dm_payload,
sequence=sequence,
)
if not signed.get("ok", True):
return signed
body = {
"sender_id": sender_id,
"sender_token": "",
"recipient_id": recipient,
"delivery_class": delivery,
"recipient_token": str(recipient_token or ""),
"ciphertext": str(ciphertext or ""),
"format": str(payload_format or "mls1"),
"transport_lock": "private_strong",
"session_welcome": str(session_welcome or ""),
"msg_id": msg_id,
"timestamp": timestamp,
"public_key": str(signed.get("public_key") or ""),
"public_key_algo": str(signed.get("public_key_algo") or ""),
"signature": str(signed.get("signature") or ""),
"sequence": int(signed.get("sequence") or 0),
"protocol_version": str(signed.get("protocol_version") or PROTOCOL_VERSION),
"signed_context": dict(dm_payload.get(SIGNED_CONTEXT_FIELD) or {}),
}
normalized_intent = str(connect_intent or "").strip().lower()
normalized_lookup_peer = str(lookup_peer_url or "").strip().rstrip("/")
if normalized_intent:
body["connect_intent"] = normalized_intent
if normalized_lookup_peer:
body["lookup_peer_url"] = normalized_lookup_peer
api_base = str(os.environ.get("SB_API_BASE", "http://127.0.0.1:8000") or "http://127.0.0.1:8000").rstrip("/")
result: dict[str, Any] = {"ok": False, "detail": "dm send failed"}
try:
import urllib.error
if delivery in ("request", "shared"):
issued = _issue_sender_token_for_http_send(
api_base,
recipient=recipient,
delivery=delivery,
recipient_token=str(recipient_token or ""),
)
if not issued.get("ok"):
return issued
body["sender_token"] = str(issued.get("sender_token") or "")
result = _http_post_json(f"{api_base}/api/mesh/dm/send", body)
except (urllib.error.URLError, TimeoutError):
if delivery in ("request", "shared"):
issued = issue_wormhole_dm_sender_token(
recipient_id=recipient,
delivery_class=delivery,
recipient_token=str(recipient_token or ""),
)
if not issued.get("ok"):
return issued
body["sender_token"] = str(issued.get("sender_token") or "")
async def _send():
import json as _json
raw = _json.dumps(body).encode("utf-8")
async def receive():
return {"type": "http.request", "body": raw, "more_body": False}
req = Request(
{
"type": "http",
"method": "POST",
"path": "/api/mesh/dm/send",
"headers": [(b"content-type", b"application/json")],
"client": ("127.0.0.1", 52421),
},
receive,
)
req.state._private_lane_current_tier = "private_strong"
req.state._transport_tier = "private_strong"
return await main_mod.dm_send(req)
result = _run_async(_send())
except Exception as exc:
result = {"ok": False, "detail": str(exc) or type(exc).__name__}
if isinstance(result, dict):
result.setdefault("msg_id", msg_id)
result.setdefault("sender_id", sender_id)
result.setdefault("recipient_id", recipient)
return result
def send_contact_request(
*,
lookup_token: str = "",
peer_id: str = "",
note: str = "",
lookup_peer_url: str = "",
) -> dict[str, Any]:
"""Send a first-contact request using a short address or peer id."""
from services.mesh.mesh_wormhole_dead_drop import build_contact_offer
from services.mesh.mesh_wormhole_persona import get_dm_identity
from services.mesh.mesh_wormhole_prekey import bootstrap_encrypt_for_peer, fetch_dm_prekey_bundle
token = str(lookup_token or "").strip()
peer = str(peer_id or "").strip()
if not token and not peer:
return {"ok": False, "detail": "lookup_token or peer_id required"}
preferred_peer = str(lookup_peer_url or "").strip().rstrip("/")
bundle = fetch_dm_prekey_bundle(
agent_id=peer if not token else "",
lookup_token=token,
lookup_peer_urls=[preferred_peer] if preferred_peer else None,
)
if not bundle.get("ok"):
return bundle
recipient = str(bundle.get("agent_id") or peer).strip()
if not recipient:
return {"ok": False, "detail": "recipient unresolved"}
identity = get_dm_identity()
offer = build_contact_offer(
dh_pub_key=str(identity.get("dh_pub_key") or ""),
dh_algo=str(identity.get("dh_algo") or "X25519"),
geo_hint=str(note or ""),
)
encrypted = bootstrap_encrypt_for_peer(recipient, offer, lookup_token=token)
if not encrypted.get("ok"):
return encrypted
return _submit_signed_dm_send(
recipient=recipient,
delivery_class="request",
recipient_token="",
ciphertext=str(encrypted.get("result") or ""),
payload_format="mls1",
connect_intent="contact_request",
lookup_peer_url=preferred_peer,
)
def send_contact_accept(
*,
peer_id: str,
peer_dh_pub: str = "",
) -> dict[str, Any]:
"""Accept a pending contact request and open the shared DM lane."""
from services.mesh.mesh_wormhole_dead_drop import build_contact_accept, issue_pairwise_dm_alias
from services.mesh.mesh_wormhole_prekey import bootstrap_encrypt_for_peer, fetch_dm_prekey_bundle
peer = str(peer_id or "").strip()
if not peer:
return {"ok": False, "detail": "peer_id required"}
dh_pub = str(peer_dh_pub or "").strip()
if not dh_pub:
bundle = fetch_dm_prekey_bundle(agent_id=peer)
if not bundle.get("ok"):
return bundle
dh_pub = str(bundle.get("dh_pub_key") or "").strip()
if not dh_pub:
return {"ok": False, "detail": "peer dh_pub_key unavailable"}
alias = issue_pairwise_dm_alias(peer_id=peer, peer_dh_pub=dh_pub)
if not alias.get("ok"):
return alias
shared_alias = str(alias.get("shared_alias") or "").strip()
if not shared_alias:
return {"ok": False, "detail": "shared_alias unavailable"}
accept_plain = build_contact_accept(shared_alias=shared_alias)
encrypted = bootstrap_encrypt_for_peer(peer, accept_plain)
if not encrypted.get("ok"):
return encrypted
sent = _submit_signed_dm_send(
recipient=peer,
delivery_class="request",
recipient_token="",
ciphertext=str(encrypted.get("result") or ""),
payload_format="mls1",
connect_intent="contact_accept",
)
if isinstance(sent, dict):
sent.setdefault("shared_alias", shared_alias)
return sent
def send_dm(
peer_id: str,
plaintext: str,
*,
delivery_class: str = "shared",
recipient_token: str = "",
) -> dict[str, Any]:
"""Compose and send an encrypted DM on behalf of the operator."""
import main as main_mod
recipient = str(peer_id or "").strip()
if not recipient:
return {"ok": False, "detail": "peer_id required"}
if not str(plaintext or "").strip():
return {"ok": False, "detail": "plaintext required"}
delivery = str(delivery_class or "shared").strip().lower()
if delivery not in ("shared", "request"):
return {"ok": False, "detail": "delivery_class must be shared or request"}
composed = main_mod.compose_wormhole_dm(
peer_id=recipient,
peer_dh_pub="",
plaintext=str(plaintext),
)
if not composed.get("ok"):
return composed
return _submit_signed_dm_send(
recipient=recipient,
delivery_class=delivery,
recipient_token=str(recipient_token or ""),
ciphertext=str(composed.get("ciphertext") or ""),
payload_format=str(composed.get("format") or "mls1"),
session_welcome=str(composed.get("session_welcome") or ""),
)
def poll_dms(*, limit: int = 20) -> dict[str, Any]:
"""Poll encrypted DMs for the operator DM identity."""
import json
import main as main_mod
from services.mesh.mesh_protocol import PROTOCOL_VERSION
from services.mesh.mesh_wormhole_persona import get_dm_identity, sign_dm_wormhole_event
identity = get_dm_identity()
agent_id = str(identity.get("node_id") or "")
if not agent_id:
return {"ok": False, "detail": "dm identity is not configured"}
poll_payload = {"mailbox_claims": [], "agent_id": agent_id}
signed = sign_dm_wormhole_event(event_type="dm_poll", payload=poll_payload)
if not signed.get("ok", True):
return signed
body = {
"agent_id": agent_id,
"mailbox_claims": [],
"timestamp": int(time.time()),
"nonce": secrets.token_hex(8),
"public_key": str(signed.get("public_key") or ""),
"public_key_algo": str(signed.get("public_key_algo") or ""),
"signature": str(signed.get("signature") or ""),
"sequence": int(signed.get("sequence") or 0),
"protocol_version": str(signed.get("protocol_version") or PROTOCOL_VERSION),
}
raw = json.dumps(body).encode("utf-8")
async def _poll():
async def receive():
return {"type": "http.request", "body": raw, "more_body": False}
req = Request(
{
"type": "http",
"method": "POST",
"path": "/api/mesh/dm/poll",
"headers": [(b"content-type", b"application/json")],
"client": ("127.0.0.1", 52421),
},
receive,
)
return await main_mod.dm_poll_secure(req)
result = _run_async(_poll())
if isinstance(result, dict):
messages = list(result.get("messages") or [])
if limit and len(messages) > int(limit):
result = dict(result)
result["messages"] = messages[: int(limit)]
result["count"] = len(result["messages"])
return result if isinstance(result, dict) else {"ok": False, "detail": "dm poll failed"}
+31
View File
@@ -36,6 +36,15 @@ LATENCY_TIER_MS: dict[str, int] = {
"entity_expand": 40,
"osint_lookup": 200,
"run_playbook": 120,
"infonet_status": 20,
"list_gates": 15,
"read_gate_messages": 40,
"poll_dms": 80,
"ensure_infonet_ready": 120000,
"join_infonet_swarm": 90000,
"post_gate_message": 15000,
"cast_vote": 5000,
"send_dm": 20000,
"search_telemetry": 8000,
"get_telemetry": 3500,
"get_slow_telemetry": 1500,
@@ -172,6 +181,18 @@ def routing_manifest() -> dict[str, Any]:
"intent": "hot snapshot",
"use": "run_playbook(name=hot_snapshot)",
},
{
"intent": "post to infonet gate / join swarm",
"use": "ensure_infonet_ready then post_gate_message (full tier)",
},
{
"intent": "read encrypted gate traffic",
"use": "read_gate_messages(gate_id=infonet, decrypt=true)",
},
{
"intent": "dm another node",
"use": "send_dm(peer_id=..., plaintext=...) (full tier)",
},
],
"playbooks": {
name: {"description": spec.get("description", "")}
@@ -184,6 +205,16 @@ def routing_manifest() -> dict[str, Any]:
"add_watch",
"inject_data",
"place_analysis_zone",
"ensure_infonet_ready",
"post_gate_message",
"cast_vote",
"send_dm",
],
"infonet_reads": [
"infonet_status",
"list_gates",
"read_gate_messages",
"poll_dms",
],
},
}
+10 -6
View File
@@ -109,18 +109,22 @@ def _check_arti_ready() -> bool:
is_tor = bool(payload.get("IsTor")) or bool(payload.get("is_tor"))
if not (response.ok and is_tor):
logger.warning(
"Arti Tor proof failed (status=%s is_tor=%s) — proxy is not trusted as Tor",
"Arti Tor proof failed (status=%s is_tor=%s) — SOCKS is up, using Arti anyway",
getattr(response, "status_code", "unknown"),
payload.get("IsTor", payload.get("is_tor")),
)
_ARTI_PROOF_CACHE.update({"port": socks_port, "ok": False, "ts": now})
return False
_ARTI_PROOF_CACHE.update({"port": socks_port, "ok": True, "ts": now})
return True
_ARTI_PROOF_CACHE.update({"port": socks_port, "ok": True, "ts": now})
return True
except Exception as exc:
logger.warning("Arti Tor proof request failed on port %s: %s", socks_port, exc)
_ARTI_PROOF_CACHE.update({"port": socks_port, "ok": False, "ts": now})
return False
logger.warning(
"Arti Tor proof request failed on port %s: %s — SOCKS is up, using Arti anyway",
socks_port,
exc,
)
_ARTI_PROOF_CACHE.update({"port": socks_port, "ok": True, "ts": now})
return True
def get_transport_tier() -> str:
@@ -0,0 +1,53 @@
from __future__ import annotations
from services.mesh import mesh_dm_connect_delivery as connect
def test_should_auto_release_for_connect_intent():
payload = {
"delivery_class": "request",
"connect_intent": "contact_request",
"recipient_id": "!sb_peer",
}
assert connect.should_auto_release_dm_payload(payload) is True
def test_should_auto_release_for_lookup_peer_url():
payload = {
"delivery_class": "request",
"lookup_peer_url": "http://owner.onion:8000",
"recipient_id": "!sb_peer",
}
assert connect.should_auto_release_dm_payload(payload) is True
def test_should_not_auto_release_shared_lane():
payload = {
"delivery_class": "shared",
"connect_intent": "contact_request",
"recipient_id": "!sb_peer",
}
assert connect.should_auto_release_dm_payload(payload) is False
def test_enrich_connect_release_payload_prefers_explicit_lookup():
enriched = connect.enrich_connect_release_payload(
{
"recipient_id": "!sb_peer",
"lookup_peer_url": "http://owner.onion:8000/",
}
)
assert enriched["lookup_peer_url"] == "http://owner.onion:8000"
assert enriched["relay_push_peer_urls"] == ["http://owner.onion:8000"]
def test_relay_push_peer_urls_dedupes_and_prioritizes_lookup():
urls = connect.relay_push_peer_urls_for_payload(
{
"lookup_peer_url": "http://owner.onion:8000",
"relay_push_peer_urls": ["http://relay.onion:8000", "http://owner.onion:8000"],
}
)
assert urls[0] == "http://owner.onion:8000"
assert "http://relay.onion:8000" in urls
assert len(urls) == 2
@@ -0,0 +1,45 @@
"""dm_get_pubkey resolves invite handles across the private fleet."""
from __future__ import annotations
from unittest.mock import patch
import pytest
@pytest.mark.asyncio
async def test_dm_get_pubkey_falls_back_to_fleet_prekey_lookup():
import main
request = main.Request(
{
"type": "http",
"method": "GET",
"path": "/api/mesh/dm/pubkey",
"headers": [],
"client": ("127.0.0.1", 12345),
}
)
remote_bundle = {
"ok": True,
"agent_id": "!sb_peer_test",
"identity_dh_pub_key": "Uo/wk78hu+ISyT9iCjNhcWgiANaHSXLMyNLn2q8YCkc=",
"dh_algo": "X25519",
"public_key": "v0pVNDQAz8wzvpMfIURjjVyCHhKZlAmrDPGaqzoJ7Rk=",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 1,
"bundle": {"identity_dh_pub_key": "Uo/wk78hu+ISyT9iCjNhcWgiANaHSXLMyNLn2q8YCkc="},
}
with patch("services.mesh.mesh_dm_relay.dm_relay") as relay, patch(
"services.mesh.mesh_wormhole_prekey.fetch_dm_prekey_bundle",
return_value=remote_bundle,
):
relay.get_dh_key_by_lookup.return_value = (None, "")
result = await main.dm_get_pubkey(request, lookup_token="fleet-handle-token")
assert result["ok"] is True
assert result["agent_id"] == "!sb_peer_test"
assert result["dh_pub_key"] == "Uo/wk78hu+ISyT9iCjNhcWgiANaHSXLMyNLn2q8YCkc="
@@ -0,0 +1,126 @@
from types import SimpleNamespace
from services.mesh import mesh_infonet_relay_bootstrap as relay_bootstrap
def test_relay_auto_wormhole_skipped_by_default(monkeypatch):
monkeypatch.setattr(
relay_bootstrap,
"get_settings",
lambda: SimpleNamespace(
MESH_INFONET_RELAY_AUTO_WORMHOLE=False,
MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED=False,
MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY="",
),
)
assert relay_bootstrap.infonet_relay_auto_wormhole_requested() is False
def test_relay_auto_wormhole_enabled_by_flag(monkeypatch):
monkeypatch.setattr(
relay_bootstrap,
"get_settings",
lambda: SimpleNamespace(
MESH_INFONET_RELAY_AUTO_WORMHOLE=True,
MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED=False,
MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY="",
),
)
assert relay_bootstrap.infonet_relay_auto_wormhole_requested() is True
def test_relay_auto_wormhole_enabled_by_seed_signer_key(monkeypatch):
monkeypatch.setattr(
relay_bootstrap,
"get_settings",
lambda: SimpleNamespace(
MESH_INFONET_RELAY_AUTO_WORMHOLE=False,
MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED=False,
MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY="seed-private-key",
),
)
assert relay_bootstrap.infonet_relay_auto_wormhole_requested() is True
def test_relay_auto_wormhole_disabled_override(monkeypatch):
monkeypatch.setattr(
relay_bootstrap,
"get_settings",
lambda: SimpleNamespace(
MESH_INFONET_RELAY_AUTO_WORMHOLE=True,
MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED=True,
MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY="seed-private-key",
),
)
assert relay_bootstrap.infonet_relay_auto_wormhole_requested() is False
def test_ensure_relay_wormhole_writes_settings_and_connects(monkeypatch, tmp_path):
wormhole_file = tmp_path / "wormhole.json"
monkeypatch.setattr(relay_bootstrap, "WORMHOLE_FILE", wormhole_file, raising=False)
monkeypatch.setattr(
"services.wormhole_settings.WORMHOLE_FILE",
wormhole_file,
)
monkeypatch.setattr(
"services.wormhole_settings.DATA_DIR",
tmp_path,
)
settings = SimpleNamespace(
MESH_INFONET_RELAY_AUTO_WORMHOLE=True,
MESH_INFONET_RELAY_AUTO_WORMHOLE_DISABLED=False,
MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY="",
MESH_ARTI_SOCKS_PORT=9050,
)
monkeypatch.setattr(relay_bootstrap, "get_settings", lambda: settings)
tor_calls: list[int] = []
class _TorService:
def start(self, *, target_port: int):
tor_calls.append(target_port)
return {"ok": True, "hostname": "example.onion"}
env_writes: list[tuple[str, str]] = []
def _fake_write_env_value(key: str, value: str) -> None:
env_writes.append((key, value))
wormhole_calls: list[str] = []
def _fake_restart_wormhole(*, reason: str):
wormhole_calls.append(f"restart:{reason}")
return {"connected": True, "reason": reason}
def _fake_connect_wormhole(*, reason: str):
wormhole_calls.append(f"connect:{reason}")
return {"connected": True, "reason": reason}
monkeypatch.setattr(
"services.tor_hidden_service.tor_service",
_TorService(),
)
monkeypatch.setattr("routers.ai_intel._write_env_value", _fake_write_env_value)
monkeypatch.setattr(
"services.wormhole_supervisor.restart_wormhole",
_fake_restart_wormhole,
)
monkeypatch.setattr(
"services.wormhole_supervisor.connect_wormhole",
_fake_connect_wormhole,
)
result = relay_bootstrap.ensure_infonet_relay_wormhole_ready(reason="test_relay")
assert result["ok"] is True
assert result["skipped"] is False
assert result["settings_updated"] is True
assert tor_calls == [8000]
assert env_writes == [("MESH_ARTI_ENABLED", "true")]
assert wormhole_calls == ["restart:test_relay"]
saved = relay_bootstrap.read_wormhole_settings()
assert saved["enabled"] is True
assert saved["transport"] == "tor_arti"
assert saved["socks_proxy"] == "socks5h://127.0.0.1:9050"
assert saved["anonymous_mode"] is True
@@ -111,42 +111,101 @@ def test_dm_send_keeps_encrypted_payloads_off_ledger(tmp_path, monkeypatch):
assert append_called["value"] is False
def test_dm_request_send_rejects_unverified_first_contact(tmp_path, monkeypatch):
def test_dm_request_send_allows_unverified_first_contact(tmp_path, monkeypatch):
import main
from services import wormhole_supervisor
from services.mesh import mesh_dm_relay, mesh_wormhole_contacts
monkeypatch.setattr(mesh_wormhole_contacts, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_contacts, "CONTACTS_FILE", tmp_path / "wormhole_dm_contacts.json")
from services.mesh import mesh_hashchain
append_called = {"value": False}
monkeypatch.setattr(main, "_verify_signed_write", lambda **kwargs: (True, ""))
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")
monkeypatch.setattr(mesh_dm_relay.dm_relay, "consume_nonce", lambda *_args, **_kwargs: (True, "ok"))
monkeypatch.setattr(mesh_hashchain.infonet, "validate_and_set_sequence", lambda *_args, **_kwargs: (True, ""))
def fake_append(**kwargs):
append_called["value"] = True
return {"event_id": "dm-request-e2e"}
monkeypatch.setattr(mesh_hashchain.infonet, "append_private_dm_message", fake_append)
monkeypatch.setattr(
main,
"consume_wormhole_dm_sender_token",
lambda **kwargs: {
"ok": True,
"sender_token_hash": "reqtok-first-contact",
"sender_id": "alice",
"public_key": "cHVi",
"public_key_algo": "Ed25519",
"protocol_version": "infonet/2",
"recipient_id": kwargs.get("recipient_id", "") or "bob",
"delivery_class": kwargs.get("delivery_class", "") or "request",
},
)
monkeypatch.setattr(
mesh_dm_relay.dm_relay,
"deposit",
lambda **kwargs: {
"ok": True,
"msg_id": kwargs.get("msg_id", ""),
"detail": "stored",
},
)
from services.mesh.mesh_protocol import build_signed_context
timestamp = int(time.time())
payload = {
"recipient_id": "bob",
"delivery_class": "request",
"recipient_token": "",
"ciphertext": "x3dh1:opaque",
"msg_id": "m2",
"timestamp": timestamp,
"format": "x3dh1",
"transport_lock": "private_strong",
}
signed_context = build_signed_context(
event_type="dm_message",
kind="dm_send",
endpoint="/api/mesh/dm/send",
lane_floor="private_strong",
sequence_domain="dm_send",
node_id="alice",
sequence=1,
payload=payload,
recipient_id="bob",
)
req = _json_request(
"/api/mesh/dm/send",
{
"sender_id": "alice",
"recipient_id": "bob",
"sender_id": "",
"sender_token": "opaque-request-token",
"recipient_id": "",
"delivery_class": "request",
"recipient_token": "",
"ciphertext": "x3dh1:opaque",
"format": "x3dh1",
"msg_id": "m2",
"timestamp": int(time.time()),
"public_key": "cHVi",
"public_key_algo": "Ed25519",
"timestamp": timestamp,
"public_key": "",
"public_key_algo": "",
"signature": "sig",
"sequence": 1,
"protocol_version": "infonet/2",
"protocol_version": "",
"transport_lock": "private_strong",
"signed_context": signed_context,
},
)
response = asyncio.run(main.dm_send(req))
assert response["ok"] is False
assert response["detail"] == "signed invite or SAS verification required before secure first contact"
assert response["trust_level"] == "unpinned"
assert response["ok"] is True
def test_dm_key_registration_keeps_key_material_off_ledger(monkeypatch):
@@ -618,38 +618,32 @@ class TestFetchPrekeyBundleByLookup:
record = _valid_bundle_record("test-agent")
requested_urls: list[str] = []
monkeypatch.setenv("MESH_BOOTSTRAP_SEED_PEERS", "https://seed.example")
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
monkeypatch.setenv("MESH_RELAY_PEERS", "")
get_settings.cache_clear()
def _public_lookup(lookup_token: str, **_kwargs):
requested_urls.append(
f"http://seed.onion:8000/api/mesh/dm/prekey-bundle?lookup_token={lookup_token}"
)
return {
"ok": True,
"agent_id": record["agent_id"],
"lookup_mode": "invite_lookup_handle",
"public_lookup": True,
"identity_dh_pub_key": record["dh_pub_key"],
"dh_algo": record["dh_algo"],
"public_key": record["public_key"],
"public_key_algo": record["public_key_algo"],
"protocol_version": record["protocol_version"],
"sequence": 1,
"bundle": record["bundle"],
}
class _Response:
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, _limit: int = -1):
return json.dumps(
{
"ok": True,
"identity_dh_pub_key": record["dh_pub_key"],
"dh_algo": record["dh_algo"],
"public_key": record["public_key"],
"public_key_algo": record["public_key_algo"],
"protocol_version": record["protocol_version"],
"sequence": 1,
"signed_at": int(record["bundle"].get("signed_at", 0) or 0),
"bundle": record["bundle"],
}
).encode("utf-8")
def _urlopen(request, timeout=0):
requested_urls.append(str(getattr(request, "full_url", "")))
return _Response()
monkeypatch.setattr("services.mesh.mesh_wormhole_prekey.urllib.request.urlopen", _urlopen)
monkeypatch.setattr(
"services.mesh.mesh_wormhole_prekey._fetch_dm_prekey_bundle_from_peer_lookup",
lambda *_args, **_kwargs: {"ok": False, "detail": "peer prekey lookup unavailable"},
)
monkeypatch.setattr(
"services.mesh.mesh_wormhole_prekey._fetch_dm_prekey_bundle_from_public_lookup",
_public_lookup,
)
from services.mesh.mesh_wormhole_prekey import fetch_dm_prekey_bundle
@@ -668,33 +662,20 @@ class TestFetchPrekeyBundleByLookup:
_isolated_relay(tmp_path, monkeypatch)
requested_urls: list[str] = []
monkeypatch.setenv("MESH_BOOTSTRAP_SEED_PEERS", "https://seed.example")
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
monkeypatch.setenv("MESH_RELAY_PEERS", "")
get_settings.cache_clear()
def _public_lookup(lookup_token: str, **_kwargs):
requested_urls.append(
f"http://seed.onion:8000/api/mesh/dm/prekey-bundle?lookup_token={lookup_token}"
)
return {"ok": False, "detail": "peer prekey lookup still preparing"}
class _Response:
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, _limit: int = -1):
return json.dumps(
{
"ok": True,
"pending": True,
"status": "preparing_private_lane",
"detail": "transport tier insufficient",
}
).encode("utf-8")
def _urlopen(request, timeout=0):
requested_urls.append(str(getattr(request, "full_url", "")))
return _Response()
monkeypatch.setattr("services.mesh.mesh_wormhole_prekey.urllib.request.urlopen", _urlopen)
monkeypatch.setattr(
"services.mesh.mesh_wormhole_prekey._fetch_dm_prekey_bundle_from_peer_lookup",
lambda *_args, **_kwargs: {"ok": False, "detail": "peer prekey lookup unavailable"},
)
monkeypatch.setattr(
"services.mesh.mesh_wormhole_prekey._fetch_dm_prekey_bundle_from_public_lookup",
_public_lookup,
)
from services.mesh.mesh_wormhole_prekey import fetch_dm_prekey_bundle
@@ -807,6 +788,16 @@ class TestFetchPrekeyBundleByLookup:
monkeypatch.setenv("MESH_DEV_ALLOW_LEGACY_COMPAT", "true")
monkeypatch.setenv("MESH_ALLOW_LEGACY_AGENT_ID_LOOKUP_UNTIL", "2026-06-01")
get_settings.cache_clear()
monkeypatch.setattr(
mesh_wormhole_prekey,
"_validate_bundle_record",
lambda *_args, **_kwargs: (True, ""),
)
monkeypatch.setattr(
mesh_wormhole_prekey,
"legacy_agent_id_lookup_blocked",
lambda: False,
)
mesh_wormhole_prekey._WARNED_LEGACY_PREKEY_LOOKUPS.clear()
caplog.clear()
caplog.set_level("WARNING")
@@ -874,3 +865,55 @@ class TestFetchPrekeyBundleByLookup:
)
finally:
get_settings.cache_clear()
def test_invite_lookup_peer_order_prefers_active_over_bootstrap(monkeypatch):
from services.mesh import mesh_wormhole_prekey as prekey_mod
monkeypatch.setenv(
"MESH_BOOTSTRAP_SEED_PEERS",
"http://seed-a.onion:8000,http://seed-b.onion:8000,http://seed-c.onion:8000,http://seed-d.onion:8000",
)
monkeypatch.setattr(
"services.mesh.mesh_router.active_sync_peer_urls",
lambda: [
"http://active-peer.onion:8000",
"http://another-active.onion:8000",
],
)
monkeypatch.setattr(
prekey_mod,
"_discovered_push_peer_urls",
lambda **kwargs: [],
)
get_settings.cache_clear()
ordered = prekey_mod._prioritized_invite_lookup_peer_urls(
preferred=["http://pinned-peer.onion:8000"],
)
assert ordered[0] == "http://pinned-peer.onion:8000"
assert ordered[1:3] == [
"http://active-peer.onion:8000",
"http://another-active.onion:8000",
]
assert ordered[-prekey_mod._INVITE_LOOKUP_MAX_BOOTSTRAP_PEERS:] == [
"http://seed-a.onion:8000",
"http://seed-b.onion:8000",
"http://seed-c.onion:8000",
]
assert "http://seed-d.onion:8000" not in ordered
get_settings.cache_clear()
def test_invite_export_includes_lookup_peer_url(tmp_path, monkeypatch):
_isolated_invite_state(tmp_path, monkeypatch)
monkeypatch.setenv("MESH_PUBLIC_PEER_URL", "http://owner-node.onion:8000")
from services.mesh.mesh_wormhole_identity import export_wormhole_dm_invite
exported = export_wormhole_dm_invite(label="routing-test")
payload = dict(exported.get("invite", {}).get("payload") or {})
assert payload.get("prekey_lookup_handle")
assert payload.get("lookup_peer_url") == "http://owner-node.onion:8000"
+21 -2
View File
@@ -71,7 +71,11 @@ def test_dispatcher_chooses_dm_relay_when_direct_path_unavailable_but_lane_floor
assert len(deposit_calls) == 1
def test_dispatcher_does_not_release_dm_below_private_strong():
def test_dispatcher_does_not_release_dm_below_private_transitional_when_rns_disabled(monkeypatch):
monkeypatch.setattr(
"services.wormhole_supervisor.get_wormhole_state",
lambda: {"rns_enabled": False},
)
result = attempt_private_release(
lane="dm",
current_tier="private_control_only",
@@ -80,7 +84,22 @@ def test_dispatcher_does_not_release_dm_below_private_strong():
assert result["ok"] is False
assert result["no_acceptable_path"] is True
assert result["policy_reason_code"] == "dm_release_waiting_for_private_strong"
assert result["policy_reason_code"] == "dm_release_waiting_for_private_transitional"
assert result["required_tier"] == "private_transitional"
def test_dispatcher_still_requires_private_strong_when_rns_enabled(monkeypatch):
monkeypatch.setattr(
"services.wormhole_supervisor.get_wormhole_state",
lambda: {"rns_enabled": True},
)
result = attempt_private_release(
lane="dm",
current_tier="private_transitional",
payload={"msg_id": "dm-transitional"},
)
assert result["ok"] is False
assert result["required_tier"] == "private_strong"
@@ -1,4 +1,5 @@
import base64
import json
import time
from cryptography.hazmat.primitives import serialization
@@ -180,6 +181,31 @@ def test_private_dm_hashchain_rejects_non_sealed_ciphertext_shape(tmp_path, monk
raise AssertionError("private DM append accepted non-base64 ciphertext")
def test_private_dm_hashchain_accepts_x3dh1_prefixed_ciphertext(tmp_path, monkeypatch):
inf = _fresh_infonet(tmp_path, monkeypatch)
private_key, public_key, node_id = _keypair()
envelope = {
"h": {"ik_pub": "aGVsbG8=", "ek_pub": "d29ybGQ=", "spk_id": 1, "otk_id": 0},
"ct": base64.b64encode(b"\x00" * 32).decode("ascii"),
}
payload = _payload(msg_id="dm-x3dh-1")
payload["ciphertext"] = "x3dh1:" + base64.b64encode(
json.dumps(envelope, sort_keys=True, separators=(",", ":")).encode("utf-8")
).decode("ascii")
event = inf.append_private_dm_message(
node_id=node_id,
payload=payload,
signature=_signature(private_key, node_id, 1, payload),
sequence=1,
public_key=public_key,
public_key_algo="Ed25519",
protocol_version=mesh_protocol.PROTOCOL_VERSION,
timestamp=float(payload["timestamp"]),
)
assert event["event_type"] == "dm_message"
assert str(event["payload"]["ciphertext"]).startswith("x3dh1:")
def test_hydrate_dm_relay_from_chain_delivers_to_poll_claim(tmp_path, monkeypatch):
inf = _fresh_infonet(tmp_path / "chain", monkeypatch)
relay = _fresh_relay(tmp_path / "relay", monkeypatch)
@@ -216,19 +216,19 @@ def test_authenticated_wormhole_status_can_request_diagnostic_private_delivery_s
assert item["meta"]["peer_id"] == "bob"
def test_dm_pubkey_lookup_token_ordinary_response_omits_resolved_agent_id(monkeypatch):
def test_dm_pubkey_lookup_token_ordinary_response_includes_resolved_agent_id(monkeypatch):
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
monkeypatch.setattr(
"services.mesh.mesh_dm_relay.dm_relay.get_dh_key_by_lookup",
lambda _lookup_token: ({"dh_pub": "pub", "dh_algo": "X25519"}, "peer-123"),
lambda _lookup_token: ({"dh_pub_key": "pub", "dh_algo": "X25519"}, "peer-123"),
)
result = asyncio.run(main.dm_get_pubkey(_request("/api/mesh/dm/pubkey"), lookup_token="invite-handle"))
assert result["ok"] is True
assert result["lookup_mode"] == "invite_lookup_handle"
assert "agent_id" not in result
assert result["agent_id"] == "peer-123"
def test_dm_pubkey_lookup_token_diagnostic_response_exposes_resolved_agent_id(monkeypatch):
@@ -249,7 +249,7 @@ def test_dm_pubkey_lookup_token_diagnostic_response_exposes_resolved_agent_id(mo
assert result["agent_id"] == "peer-123"
def test_prekey_bundle_lookup_token_ordinary_response_omits_resolved_agent_id(monkeypatch):
def test_prekey_bundle_lookup_token_ordinary_response_includes_resolved_agent_id(monkeypatch):
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
monkeypatch.setattr(
@@ -273,7 +273,7 @@ def test_prekey_bundle_lookup_token_ordinary_response_omits_resolved_agent_id(mo
assert result["ok"] is True
assert result["lookup_mode"] == "invite_lookup_handle"
assert "agent_id" not in result
assert result["agent_id"] == "peer-456"
assert result["trust_fingerprint"] == "aa" * 16
@@ -465,6 +465,45 @@ def test_user_facing_status_mapping_remains_plain_language_and_stable():
assert evaluate_network_release("dm", "private_strong").status_label == "Delivered privately"
def test_queued_dm_releases_at_private_transitional_when_rns_disabled(monkeypatch):
deposit_calls = []
monkeypatch.setattr(
"services.wormhole_supervisor.get_wormhole_state",
lambda: {"rns_enabled": False},
)
monkeypatch.setattr(
"services.wormhole_supervisor.get_transport_tier",
lambda: "private_transitional",
)
monkeypatch.setattr(mesh_private_release_worker, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(mesh_private_release_worker, "_rns_private_dm_ready", lambda: False)
monkeypatch.setattr(mesh_private_release_worker, "_maybe_apply_dm_relay_jitter", lambda: None)
monkeypatch.setattr(
"services.mesh.mesh_dm_relay.dm_relay.deposit",
lambda **kwargs: deposit_calls.append(kwargs) or {"ok": True, "msg_id": kwargs["msg_id"]},
)
queued = main._queue_dm_release(
current_tier="private_transitional",
payload={
"msg_id": "dm-tor-only-1",
"sender_id": "alice",
"recipient_id": "bob",
"delivery_class": "request",
"sender_token_hash": "abc123",
"ciphertext": "x3dh1:ciphertext",
"timestamp": 1,
},
)
mesh_private_release_worker.private_release_worker.run_once()
item = _outbox_item(queued["outbox_id"], exposure="diagnostic")
assert len(deposit_calls) == 1
assert item["release_state"] == "delivered"
def test_outbox_exposes_publishing_state_without_claiming_delivery():
item = mesh_private_outbox.private_delivery_outbox.enqueue(
lane="dm",
@@ -0,0 +1,81 @@
"""OpenClaw Infonet delegation — command allowlist and dispatch."""
from __future__ import annotations
from unittest.mock import patch
from services.openclaw_channel import (
READ_COMMANDS,
WRITE_COMMANDS,
_dispatch_command,
allowed_commands,
)
from services.openclaw_channel import CommandChannel
INFONET_READS = frozenset({
"infonet_status",
"list_gates",
"read_gate_messages",
"poll_dms",
})
INFONET_WRITES = frozenset({
"ensure_infonet_ready",
"join_infonet_swarm",
"post_gate_message",
"cast_vote",
"send_dm",
})
def test_infonet_commands_in_allowlists():
assert INFONET_READS <= READ_COMMANDS
assert INFONET_WRITES <= WRITE_COMMANDS
def test_restricted_tier_allows_infonet_reads_only():
allowed = allowed_commands("restricted")
assert INFONET_READS <= allowed
assert not (INFONET_WRITES & allowed)
def test_full_tier_allows_infonet_writes():
allowed = allowed_commands("full")
assert INFONET_WRITES <= allowed
def test_restricted_tier_blocks_post_gate_message():
channel = CommandChannel()
result = channel.submit_command("post_gate_message", {"gate_id": "infonet", "plaintext": "hi"})
assert result["ok"] is False
assert "full access tier" in str(result.get("detail", ""))
def test_dispatch_infonet_status_mocked():
fake = {"ok": True, "chain": {"length": 3}, "valid": True}
with patch("services.openclaw_infonet.get_infonet_status", return_value=fake):
result = _dispatch_command("infonet_status", {})
assert result == fake
def test_dispatch_list_gates_mocked():
fake = {"ok": True, "gates": [{"id": "infonet"}]}
with patch("services.openclaw_infonet.list_gates", return_value=fake):
result = _dispatch_command("list_gates", {})
assert result["gates"][0]["id"] == "infonet"
def test_dispatch_post_gate_message_mocked():
fake = {"ok": True, "event_id": "evt-test"}
with patch("services.openclaw_infonet.post_gate_message", return_value=fake):
result = _dispatch_command(
"post_gate_message",
{"gate_id": "infonet", "plaintext": "agent bulletin"},
)
assert result["event_id"] == "evt-test"
def test_cast_vote_rejects_invalid_vote():
result = _dispatch_command("cast_vote", {"target_id": "!sb_test", "vote": 2})
assert result["ok"] is False
+8
View File
@@ -91,3 +91,11 @@ def test_plan_playbook_track_snapshot_requires_query():
def test_expensive_commands_set():
assert "get_report" in EXPENSIVE_COMMANDS
assert "route_query" not in EXPENSIVE_COMMANDS
def test_routing_manifest_includes_infonet_hints():
manifest = routing_manifest()
recipes = " ".join(item.get("use", "") for item in manifest.get("recipes", []))
assert "post_gate_message" in recipes
writes = manifest.get("agent_surface", {}).get("writes", [])
assert "post_gate_message" in writes