mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-06 04:17:57 +02:00
Ship DM connect delivery, fleet pubkey lookup, OpenClaw Infonet agent, and relay auto-wormhole.
Auto-relay connect DMs with End Contact severing, signed fleet prekey lookup, OpenClaw private Infonet channel intents, headless relay Tor bootstrap on redeploy, and swarm/DM live verification scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user