v0.9.6: InfoNet hashchain, Wormhole gate encryption, mesh reputation, 16 community contributors

Gate messages now propagate via the Infonet hashchain as encrypted blobs — every node syncs them
through normal chain sync while only Gate members with MLS keys can decrypt. Added mesh reputation
system, peer push workers, voluntary Wormhole opt-in for node participation, fork recovery,
killwormhole scripts, obfuscated terminology, and hardened the self-updater to protect encryption
keys and chain state during updates.

New features: Shodan search, train tracking, Sentinel Hub imagery, 8 new intelligence layers,
CCTV expansion to 11,000+ cameras across 6 countries, Mesh Terminal CLI, prediction markets,
desktop-shell scaffold, and comprehensive mesh test suite (215 frontend + backend tests passing).

Community contributors: @wa1id, @AlborzNazari, @adust09, @Xpirix, @imqdcr, @csysp, @suranyami,
@chr0n1x, @johan-martensson, @singularfailure, @smithbh, @OrfeoTerkuci, @deuza, @tm-const,
@Elhard1, @ttulttul
This commit is contained in:
anoracleofra-code
2026-03-26 05:58:04 -06:00
parent d363013742
commit 668ce16dc7
363 changed files with 170430 additions and 23203 deletions
+8 -6
View File
@@ -5,12 +5,14 @@ from unittest.mock import patch, MagicMock
@pytest.fixture(autouse=True)
def _suppress_background_services():
"""Prevent real scheduler/stream/tracker from starting during tests."""
with patch("services.data_fetcher.start_scheduler"), \
patch("services.data_fetcher.stop_scheduler"), \
patch("services.ais_stream.start_ais_stream"), \
patch("services.ais_stream.stop_ais_stream"), \
patch("services.carrier_tracker.start_carrier_tracker"), \
patch("services.carrier_tracker.stop_carrier_tracker"):
with (
patch("services.data_fetcher.start_scheduler"),
patch("services.data_fetcher.stop_scheduler"),
patch("services.ais_stream.start_ais_stream"),
patch("services.ais_stream.stop_ais_stream"),
patch("services.carrier_tracker.start_carrier_tracker"),
patch("services.carrier_tracker.stop_carrier_tracker"),
):
yield
+4
View File
@@ -0,0 +1,4 @@
[
{"id": "KDEN", "name": "Denver Intl", "iata": "DEN", "lat": 39.8561, "lng": -104.6737, "type": "airport"},
{"id": "KJFK", "name": "John F Kennedy Intl", "iata": "JFK", "lat": 40.6413, "lng": -73.7781, "type": "airport"}
]
View File
@@ -0,0 +1,437 @@
import asyncio
import json
from starlette.requests import Request
from starlette.responses import Response
def _request(path: str, method: str = "POST") -> Request:
return Request(
{
"type": "http",
"headers": [],
"client": ("test", 12345),
"method": method,
"path": path,
}
)
def test_anonymous_mode_blocks_public_mesh_write_without_hidden_transport(monkeypatch):
import main
from services import wormhole_settings, wormhole_status
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "direct",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": True,
"ready": True,
"transport_active": "direct",
},
)
async def call_next(_request: Request) -> Response:
return Response(status_code=200)
response = asyncio.run(main.enforce_high_privacy_mesh(_request("/api/mesh/send"), call_next))
payload = json.loads(response.body.decode("utf-8"))
assert response.status_code == 428
assert "hidden Wormhole transport" in payload["detail"]
def test_anonymous_mode_allows_public_mesh_write_when_hidden_transport_ready(monkeypatch):
import main
from services import wormhole_settings, wormhole_status
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "tor",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": True,
"ready": True,
"transport_active": "tor",
},
)
called = {"value": False}
async def call_next(_request: Request) -> Response:
called["value"] = True
return Response(status_code=200)
response = asyncio.run(main.enforce_high_privacy_mesh(_request("/api/mesh/send"), call_next))
assert response.status_code == 200
assert called["value"] is True
def test_anonymous_mode_treats_tor_arti_as_hidden_transport(monkeypatch):
import main
from services import wormhole_settings, wormhole_status
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "tor_arti",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": True,
"ready": True,
"transport_active": "tor_arti",
},
)
called = {"value": False}
async def call_next(_request: Request) -> Response:
called["value"] = True
return Response(status_code=200)
response = asyncio.run(main.enforce_high_privacy_mesh(_request("/api/mesh/send"), call_next))
assert response.status_code == 200
assert called["value"] is True
def test_anonymous_mode_does_not_block_read_only_mesh_requests(monkeypatch):
import main
from services import wormhole_settings, wormhole_status
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "direct",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": False,
"ready": False,
"transport_active": "direct",
},
)
called = {"value": False}
async def call_next(_request: Request) -> Response:
called["value"] = True
return Response(status_code=200)
response = asyncio.run(
main.enforce_high_privacy_mesh(_request("/api/mesh/status", method="GET"), call_next)
)
assert response.status_code == 200
assert called["value"] is True
def test_anonymous_mode_blocks_private_dm_actions_without_hidden_transport(monkeypatch):
import main
from services import wormhole_settings, wormhole_status, wormhole_supervisor
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "direct",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": True,
"ready": True,
"transport_active": "direct",
},
)
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {
"configured": True,
"ready": True,
"arti_ready": True,
"rns_ready": True,
},
)
async def call_next(_request: Request) -> Response:
return Response(status_code=200)
response = asyncio.run(main.enforce_high_privacy_mesh(_request("/api/mesh/dm/send"), call_next))
payload = json.loads(response.body.decode("utf-8"))
assert response.status_code == 428
assert "private DM activity" in payload["detail"]
def test_anonymous_mode_allows_private_dm_actions_when_hidden_transport_ready(monkeypatch):
import main
from services import wormhole_settings, wormhole_status, wormhole_supervisor
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "tor",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": True,
"ready": True,
"transport_active": "tor",
},
)
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {
"configured": True,
"ready": True,
"arti_ready": True,
"rns_ready": True,
},
)
called = {"value": False}
async def call_next(_request: Request) -> Response:
called["value"] = True
return Response(status_code=200)
response = asyncio.run(main.enforce_high_privacy_mesh(_request("/api/mesh/dm/poll"), call_next))
assert response.status_code == 200
assert called["value"] is True
def test_anonymous_mode_blocks_dm_witness_and_block_without_hidden_transport(monkeypatch):
import main
from services import wormhole_settings, wormhole_status, wormhole_supervisor
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "direct",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": True,
"ready": True,
"transport_active": "direct",
},
)
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {
"configured": True,
"ready": True,
"arti_ready": True,
"rns_ready": True,
},
)
async def call_next(_request: Request) -> Response:
return Response(status_code=200)
block_response = asyncio.run(
main.enforce_high_privacy_mesh(_request("/api/mesh/dm/block"), call_next)
)
witness_response = asyncio.run(
main.enforce_high_privacy_mesh(_request("/api/mesh/dm/witness"), call_next)
)
assert block_response.status_code == 428
assert witness_response.status_code == 428
def test_anonymous_mode_blocks_public_vouch_without_hidden_transport(monkeypatch):
import main
from services import wormhole_settings, wormhole_status, wormhole_supervisor
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "direct",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": True,
"ready": True,
"transport_active": "direct",
},
)
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {
"configured": True,
"ready": True,
"arti_ready": True,
"rns_ready": False,
},
)
async def call_next(_request: Request) -> Response:
return Response(status_code=200)
response = asyncio.run(
main.enforce_high_privacy_mesh(_request("/api/mesh/trust/vouch"), call_next)
)
payload = json.loads(response.body.decode("utf-8"))
assert response.status_code == 428
assert "hidden Wormhole transport" in payload["detail"]
def test_private_infonet_gate_write_requires_wormhole_ready_but_not_rns(monkeypatch):
import main
from services import wormhole_supervisor
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {
"configured": True,
"ready": True,
"arti_ready": True,
"rns_ready": False,
},
)
async def call_next(_request: Request) -> Response:
return Response(status_code=200)
response = asyncio.run(
main.enforce_high_privacy_mesh(_request("/api/mesh/gate/test-gate/message"), call_next)
)
assert response.status_code == 200
def test_private_infonet_gate_write_blocks_when_wormhole_not_ready(monkeypatch):
import main
from services import wormhole_supervisor
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {
"configured": True,
"ready": False,
"rns_ready": True,
},
)
async def call_next(_request: Request) -> Response:
return Response(status_code=200)
response = asyncio.run(
main.enforce_high_privacy_mesh(_request("/api/mesh/gate/test-gate/message"), call_next)
)
payload = json.loads(response.body.decode("utf-8"))
assert response.status_code == 428
assert payload == {
"ok": False,
"detail": "transport tier insufficient",
"required": "private_transitional",
"current": "public_degraded",
}
def test_private_dm_send_blocks_at_transitional_tier(monkeypatch):
import main
from services import wormhole_settings, wormhole_supervisor
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "direct",
"anonymous_mode": False,
},
)
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {
"configured": True,
"ready": True,
"arti_ready": True,
"rns_ready": False,
},
)
async def call_next(_request: Request) -> Response:
return Response(status_code=200)
response = asyncio.run(main.enforce_high_privacy_mesh(_request("/api/mesh/dm/send"), call_next))
payload = json.loads(response.body.decode("utf-8"))
assert response.status_code == 428
assert payload == {
"ok": False,
"detail": "transport tier insufficient",
"required": "private_strong",
"current": "private_transitional",
}
@@ -0,0 +1,193 @@
import base64
import json
import pytest
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
from services.mesh.mesh_bootstrap_manifest import (
BOOTSTRAP_MANIFEST_VERSION,
BootstrapManifestError,
bootstrap_signer_public_key_b64,
build_bootstrap_manifest_payload,
generate_bootstrap_signer,
load_bootstrap_manifest,
write_signed_bootstrap_manifest,
)
from services.mesh.mesh_crypto import canonical_json
def _write_signed_manifest(
path,
*,
private_key,
peers,
issued_at=1_700_000_000,
valid_until=1_800_000_000,
signer_id="bootstrap-test",
):
payload = {
"version": BOOTSTRAP_MANIFEST_VERSION,
"issued_at": issued_at,
"valid_until": valid_until,
"signer_id": signer_id,
"peers": peers,
}
signature = base64.b64encode(private_key.sign(canonical_json(payload).encode("utf-8"))).decode("utf-8")
manifest = dict(payload)
manifest["signature"] = signature
path.write_text(json.dumps(manifest), encoding="utf-8")
return manifest
def test_load_bootstrap_manifest_roundtrip(tmp_path):
private_key = ed25519.Ed25519PrivateKey.generate()
public_key_b64 = base64.b64encode(
private_key.public_key().public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
)
).decode("utf-8")
manifest_path = tmp_path / "bootstrap.json"
_write_signed_manifest(
manifest_path,
private_key=private_key,
peers=[
{
"peer_url": "https://seed.example",
"transport": "clearnet",
"role": "seed",
"label": "Primary seed",
},
{
"peer_url": "http://alphaexample.onion",
"transport": "onion",
"role": "relay",
},
],
)
manifest = load_bootstrap_manifest(
manifest_path,
signer_public_key_b64=public_key_b64,
now=1_750_000_000,
)
assert manifest.signer_id == "bootstrap-test"
assert [peer.peer_url for peer in manifest.peers] == [
"https://seed.example",
"http://alphaexample.onion",
]
assert [peer.transport for peer in manifest.peers] == ["clearnet", "onion"]
def test_load_bootstrap_manifest_fails_on_tamper(tmp_path):
private_key = ed25519.Ed25519PrivateKey.generate()
public_key_b64 = base64.b64encode(
private_key.public_key().public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
)
).decode("utf-8")
manifest_path = tmp_path / "bootstrap.json"
manifest = _write_signed_manifest(
manifest_path,
private_key=private_key,
peers=[
{
"peer_url": "https://seed.example",
"transport": "clearnet",
"role": "seed",
}
],
)
manifest["peers"][0]["peer_url"] = "https://evil.example"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
with pytest.raises(BootstrapManifestError, match="signature invalid"):
load_bootstrap_manifest(
manifest_path,
signer_public_key_b64=public_key_b64,
now=1_750_000_000,
)
def test_load_bootstrap_manifest_rejects_expired_manifest(tmp_path):
private_key = ed25519.Ed25519PrivateKey.generate()
public_key_b64 = base64.b64encode(
private_key.public_key().public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
)
).decode("utf-8")
manifest_path = tmp_path / "bootstrap.json"
_write_signed_manifest(
manifest_path,
private_key=private_key,
peers=[
{
"peer_url": "https://seed.example",
"transport": "clearnet",
"role": "seed",
}
],
issued_at=100,
valid_until=200,
)
with pytest.raises(BootstrapManifestError, match="expired"):
load_bootstrap_manifest(
manifest_path,
signer_public_key_b64=public_key_b64,
now=500,
)
def test_generate_bootstrap_signer_roundtrip():
signer = generate_bootstrap_signer()
assert signer["private_key_b64"]
assert signer["public_key_b64"]
assert bootstrap_signer_public_key_b64(signer["private_key_b64"]) == signer["public_key_b64"]
def test_write_signed_bootstrap_manifest_roundtrip(tmp_path):
signer = generate_bootstrap_signer()
manifest_path = tmp_path / "bootstrap.json"
manifest = write_signed_bootstrap_manifest(
manifest_path,
signer_id="seed-alpha",
signer_private_key_b64=signer["private_key_b64"],
peers=[
{
"peer_url": "https://seed.example",
"transport": "clearnet",
"role": "seed",
"label": "Primary seed",
}
],
valid_for_hours=24,
)
loaded = load_bootstrap_manifest(
manifest_path,
signer_public_key_b64=signer["public_key_b64"],
now=manifest.issued_at + 60,
)
assert loaded.signer_id == "seed-alpha"
assert [peer.peer_url for peer in loaded.peers] == ["https://seed.example"]
def test_build_bootstrap_manifest_payload_rejects_invalid_peers():
with pytest.raises(BootstrapManifestError, match="clearnet bootstrap peers must use https://"):
build_bootstrap_manifest_payload(
signer_id="seed-alpha",
peers=[
{
"peer_url": "http://seed.example",
"transport": "clearnet",
"role": "seed",
}
],
)
+19
View File
@@ -0,0 +1,19 @@
import json
from pathlib import Path
from services.mesh.mesh_crypto import build_signature_payload
def test_mesh_canonical_fixtures() -> None:
root = Path(__file__).resolve().parents[3]
fixtures_path = root / "docs" / "mesh" / "mesh-canonical-fixtures.json"
fixtures = json.loads(fixtures_path.read_text(encoding="utf-8"))
for case in fixtures:
result = build_signature_payload(
event_type=case["event_type"],
node_id=case["node_id"],
sequence=case["sequence"],
payload=case["payload"],
)
assert result == case["expected"], f"fixture mismatch: {case['name']}"
+51
View File
@@ -0,0 +1,51 @@
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, ed25519
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from services.mesh.mesh_crypto import build_signature_payload, verify_signature
def test_ed25519_signature_roundtrip():
key = ed25519.Ed25519PrivateKey.generate()
pub_raw = key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
public_key_b64 = base64.b64encode(pub_raw).decode("utf-8")
payload = {"message": "hello", "destination": "broadcast", "channel": "LongFast"}
sig_payload = build_signature_payload(
event_type="message",
node_id="!sb_test",
sequence=1,
payload=payload,
)
signature = key.sign(sig_payload.encode("utf-8")).hex()
assert verify_signature(
public_key_b64=public_key_b64,
public_key_algo="Ed25519",
signature_hex=signature,
payload=sig_payload,
)
def test_ecdsa_signature_roundtrip():
key = ec.generate_private_key(ec.SECP256R1())
pub_raw = key.public_key().public_bytes(Encoding.X962, PublicFormat.UncompressedPoint)
public_key_b64 = base64.b64encode(pub_raw).decode("utf-8")
payload = {"target_id": "!sb_abc12345", "vote": 1, "gate": ""}
sig_payload = build_signature_payload(
event_type="vote",
node_id="!sb_test",
sequence=5,
payload=payload,
)
signature = key.sign(sig_payload.encode("utf-8"), ec.ECDSA(hashes.SHA256())).hex()
assert verify_signature(
public_key_b64=public_key_b64,
public_key_algo="ECDSA_P256",
signature_hex=signature,
payload=sig_payload,
)
@@ -0,0 +1,280 @@
import asyncio
import json
import time
from starlette.requests import Request
def _json_request(path: str, body: dict) -> Request:
payload = json.dumps(body).encode("utf-8")
sent = {"value": False}
async def receive():
if sent["value"]:
return {"type": "http.request", "body": b"", "more_body": False}
sent["value"] = True
return {"type": "http.request", "body": payload, "more_body": False}
return Request(
{
"type": "http",
"headers": [(b"content-type", b"application/json")],
"client": ("test", 12345),
"method": "POST",
"path": path,
},
receive,
)
def test_dm_send_keeps_encrypted_payloads_off_ledger(monkeypatch):
import main
from services import wormhole_supervisor
from services.mesh import mesh_hashchain, mesh_dm_relay
append_called = {"value": False}
monkeypatch.setattr(
main,
"_verify_signed_event",
lambda **kwargs: (True, ""),
)
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")
def fake_append(**kwargs):
append_called["value"] = True
return {"event_id": "unexpected"}
monkeypatch.setattr(mesh_hashchain.infonet, "append", fake_append)
monkeypatch.setattr(mesh_hashchain.infonet, "validate_and_set_sequence", lambda *_args, **_kwargs: (True, ""))
monkeypatch.setattr(mesh_dm_relay.dm_relay, "consume_nonce", lambda *_args, **_kwargs: (True, "ok"))
monkeypatch.setattr(
mesh_dm_relay.dm_relay,
"deposit",
lambda **kwargs: {
"ok": True,
"msg_id": kwargs.get("msg_id", ""),
"detail": "stored",
},
)
req = _json_request(
"/api/mesh/dm/send",
{
"sender_id": "alice",
"recipient_id": "bob",
"delivery_class": "request",
"recipient_token": "",
"ciphertext": "x3dh1:opaque",
"msg_id": "m1",
"timestamp": int(time.time()),
"public_key": "cHVi",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 1,
"protocol_version": "infonet/2",
},
)
response = asyncio.run(main.dm_send(req))
assert response["ok"] is True
assert append_called["value"] is False
def test_dm_key_registration_keeps_key_material_off_ledger(monkeypatch):
import main
from services.mesh import mesh_hashchain, mesh_dm_relay
append_called = {"value": False}
monkeypatch.setattr(
main,
"_verify_signed_event",
lambda **kwargs: (True, ""),
)
def fake_append(**kwargs):
append_called["value"] = True
return {"event_id": "unexpected"}
monkeypatch.setattr(mesh_hashchain.infonet, "append", fake_append)
monkeypatch.setattr(
mesh_dm_relay.dm_relay,
"register_dh_key",
lambda *args, **kwargs: (True, "ok", {"bundle_fingerprint": "bf", "accepted_sequence": 1}),
)
req = _json_request(
"/api/mesh/dm/register",
{
"agent_id": "alice",
"dh_pub_key": "dhpub",
"dh_algo": "X25519",
"timestamp": int(time.time()),
"public_key": "cHVi",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 1,
"protocol_version": "infonet/2",
},
)
response = asyncio.run(main.dm_register_key(req))
assert response["ok"] is True
assert append_called["value"] is False
def test_wormhole_dm_key_registration_keeps_key_material_off_ledger(tmp_path, monkeypatch):
import main
from services.mesh import (
mesh_hashchain,
mesh_secure_storage,
mesh_wormhole_persona,
)
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
monkeypatch.setattr(mesh_wormhole_persona, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_persona, "PERSONA_FILE", tmp_path / "wormhole_persona.json")
monkeypatch.setattr(
mesh_wormhole_persona,
"LEGACY_DM_IDENTITY_FILE",
tmp_path / "wormhole_identity.json",
)
append_called = {"value": False}
def fake_append(**kwargs):
append_called["value"] = True
return {"event_id": "unexpected"}
monkeypatch.setattr(mesh_hashchain.infonet, "append", fake_append)
monkeypatch.setattr(
main,
"register_wormhole_prekey_bundle",
lambda *args, **kwargs: {"ok": True, "bundle": {}},
)
response = asyncio.run(main.api_wormhole_dm_register_key(_json_request("/api/wormhole/dm/register-key", {})))
assert response["ok"] is True
assert append_called["value"] is False
def test_dead_drop_contact_consent_helpers_round_trip():
from services.mesh.mesh_wormhole_dead_drop import (
build_contact_accept,
build_contact_deny,
build_contact_offer,
parse_contact_consent,
)
offer = build_contact_offer(dh_pub_key="dhpub", dh_algo="X25519", geo_hint="40.12,-105.27")
accept = build_contact_accept(shared_alias="dmx_pairwise")
deny = build_contact_deny(reason="declined")
assert parse_contact_consent(offer) == {
"kind": "contact_offer",
"dh_pub_key": "dhpub",
"dh_algo": "X25519",
"geo_hint": "40.12,-105.27",
}
assert parse_contact_consent(accept) == {
"kind": "contact_accept",
"shared_alias": "dmx_pairwise",
}
assert parse_contact_consent(deny) == {
"kind": "contact_deny",
"reason": "declined",
}
def test_pairwise_alias_is_separate_from_gate_identities(tmp_path, monkeypatch):
from services.mesh import (
mesh_secure_storage,
mesh_wormhole_contacts,
mesh_wormhole_dead_drop,
mesh_wormhole_persona,
)
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
monkeypatch.setattr(mesh_wormhole_persona, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_persona, "PERSONA_FILE", tmp_path / "wormhole_persona.json")
monkeypatch.setattr(
mesh_wormhole_persona,
"LEGACY_DM_IDENTITY_FILE",
tmp_path / "wormhole_identity.json",
)
monkeypatch.setattr(mesh_wormhole_contacts, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_contacts, "CONTACTS_FILE", tmp_path / "wormhole_dm_contacts.json")
gate_session = mesh_wormhole_persona.enter_gate_anonymously("infonet", rotate=True)["identity"]
gate_persona = mesh_wormhole_persona.create_gate_persona("infonet", label="watcher")["identity"]
dm_identity = mesh_wormhole_persona.get_dm_identity()
issued = mesh_wormhole_dead_drop.issue_pairwise_dm_alias(
peer_id="peer_alpha",
peer_dh_pub="dhpub_alpha",
)
assert issued["ok"] is True
assert issued["identity_scope"] == "dm_alias"
assert issued["shared_alias"].startswith("dmx_")
assert issued["shared_alias"] != gate_session["node_id"]
assert issued["shared_alias"] != gate_persona["node_id"]
assert issued["shared_alias"] != dm_identity["node_id"]
assert issued["dm_identity_id"] == dm_identity["node_id"]
assert issued["contact"]["sharedAlias"] == issued["shared_alias"]
assert issued["contact"]["dhPubKey"] == "dhpub_alpha"
def test_pairwise_alias_rotation_promotes_after_grace(tmp_path, monkeypatch):
from services.mesh import (
mesh_secure_storage,
mesh_wormhole_contacts,
mesh_wormhole_dead_drop,
mesh_wormhole_persona,
)
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
monkeypatch.setattr(mesh_wormhole_persona, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_persona, "PERSONA_FILE", tmp_path / "wormhole_persona.json")
monkeypatch.setattr(
mesh_wormhole_persona,
"LEGACY_DM_IDENTITY_FILE",
tmp_path / "wormhole_identity.json",
)
monkeypatch.setattr(mesh_wormhole_contacts, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_contacts, "CONTACTS_FILE", tmp_path / "wormhole_dm_contacts.json")
initial = mesh_wormhole_dead_drop.issue_pairwise_dm_alias(
peer_id="peer_beta",
peer_dh_pub="dhpub_beta",
)
rotated = mesh_wormhole_dead_drop.rotate_pairwise_dm_alias(
peer_id="peer_beta",
peer_dh_pub="dhpub_beta",
grace_ms=5_000,
)
assert rotated["ok"] is True
assert rotated["active_alias"] == initial["shared_alias"]
assert rotated["pending_alias"].startswith("dmx_")
assert rotated["pending_alias"] != initial["shared_alias"]
assert rotated["contact"]["sharedAlias"] == initial["shared_alias"]
assert rotated["contact"]["pendingSharedAlias"] == rotated["pending_alias"]
assert rotated["contact"]["sharedAliasGraceUntil"] >= rotated["grace_until"]
future = rotated["grace_until"] / 1000.0 + 1
monkeypatch.setattr(mesh_wormhole_contacts.time, "time", lambda: future)
promoted = mesh_wormhole_contacts.list_wormhole_dm_contacts()["peer_beta"]
assert promoted["sharedAlias"] == rotated["pending_alias"]
assert promoted["pendingSharedAlias"] == ""
assert promoted["sharedAliasGraceUntil"] == 0
assert initial["shared_alias"] in promoted["previousSharedAliases"]
+318
View File
@@ -0,0 +1,318 @@
import asyncio
import time
REQUEST_CLAIMS = [{"type": "requests", "token": "request-claim-token"}]
def _fresh_dm_mls_state(tmp_path, monkeypatch):
from services import wormhole_supervisor
from services.mesh import mesh_dm_mls, mesh_dm_relay, mesh_secure_storage, mesh_wormhole_persona
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
monkeypatch.setattr(mesh_wormhole_persona, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_persona, "PERSONA_FILE", tmp_path / "wormhole_persona.json")
monkeypatch.setattr(
mesh_wormhole_persona,
"LEGACY_DM_IDENTITY_FILE",
tmp_path / "wormhole_identity.json",
)
monkeypatch.setattr(mesh_dm_mls, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_dm_mls, "STATE_FILE", tmp_path / "wormhole_dm_mls.json")
monkeypatch.setattr(mesh_dm_relay, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_dm_relay, "RELAY_FILE", tmp_path / "dm_relay.json")
monkeypatch.setattr(
mesh_dm_mls,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False},
)
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False},
)
relay = mesh_dm_relay.DMRelay()
monkeypatch.setattr(mesh_dm_relay, "dm_relay", relay)
mesh_dm_mls.reset_dm_mls_state(clear_privacy_core=True, clear_persistence=True)
return mesh_dm_mls, relay
def test_dm_mls_initiate_accept_encrypt_decrypt_round_trip(tmp_path, monkeypatch):
dm_mls, _relay = _fresh_dm_mls_state(tmp_path, monkeypatch)
bob_bundle = dm_mls.export_dm_key_package_for_alias("bob")
assert bob_bundle["ok"] is True
assert bob_bundle["welcome_dh_pub"]
initiated = dm_mls.initiate_dm_session("alice", "bob", bob_bundle)
assert initiated["ok"] is True
assert initiated["welcome"]
accepted = dm_mls.accept_dm_session("bob", "alice", initiated["welcome"])
assert accepted["ok"] is True
encrypted = dm_mls.encrypt_dm("alice", "bob", "hello bob")
assert encrypted["ok"] is True
decrypted = dm_mls.decrypt_dm("bob", "alice", encrypted["ciphertext"], encrypted["nonce"])
assert decrypted == {
"ok": True,
"plaintext": "hello bob",
"session_id": accepted["session_id"],
"nonce": encrypted["nonce"],
}
encrypted_back = dm_mls.encrypt_dm("bob", "alice", "hello alice")
assert encrypted_back["ok"] is True
decrypted_back = dm_mls.decrypt_dm(
"alice",
"bob",
encrypted_back["ciphertext"],
encrypted_back["nonce"],
)
assert decrypted_back["ok"] is True
assert decrypted_back["plaintext"] == "hello alice"
def test_dm_mls_lock_rejects_legacy_dm1_decrypt(tmp_path, monkeypatch):
import main
dm_mls, _relay = _fresh_dm_mls_state(tmp_path, monkeypatch)
bob_bundle = dm_mls.export_dm_key_package_for_alias("bob")
initiated = dm_mls.initiate_dm_session("alice", "bob", bob_bundle)
accepted = dm_mls.accept_dm_session("bob", "alice", initiated["welcome"])
assert accepted["ok"] is True
encrypted = dm_mls.encrypt_dm("alice", "bob", "lock me in")
assert encrypted["ok"] is True
first_decrypt = main.decrypt_wormhole_dm_envelope(
peer_id="alice-agent",
local_alias="bob",
remote_alias="alice",
ciphertext=encrypted["ciphertext"],
payload_format="mls1",
nonce=encrypted["nonce"],
)
assert first_decrypt["ok"] is True
assert dm_mls.is_dm_locked_to_mls("bob", "alice") is True
locked = main.decrypt_wormhole_dm_envelope(
peer_id="alice-agent",
local_alias="bob",
remote_alias="alice",
ciphertext="legacy-ciphertext",
payload_format="dm1",
nonce="legacy-nonce",
)
assert locked == {
"ok": False,
"detail": "DM session is locked to MLS format",
"required_format": "mls1",
"current_format": "dm1",
}
def test_dm_mls_refuses_public_degraded_transport(tmp_path, monkeypatch):
dm_mls, _relay = _fresh_dm_mls_state(tmp_path, monkeypatch)
monkeypatch.setattr(
dm_mls,
"get_wormhole_state",
lambda: {"configured": False, "ready": False, "rns_ready": False},
)
result = dm_mls.initiate_dm_session(
"alice",
"bob",
{"mls_key_package": "ZmFrZQ=="},
)
assert result == {"ok": False, "detail": "DM MLS requires PRIVATE transport tier"}
def test_dm_mls_session_persistence_survives_same_process_restart(tmp_path, monkeypatch):
dm_mls, _relay = _fresh_dm_mls_state(tmp_path, monkeypatch)
bob_bundle = dm_mls.export_dm_key_package_for_alias("bob")
initiated = dm_mls.initiate_dm_session("alice", "bob", bob_bundle)
accepted = dm_mls.accept_dm_session("bob", "alice", initiated["welcome"])
dm_mls.reset_dm_mls_state(clear_privacy_core=False, clear_persistence=False)
encrypted = dm_mls.encrypt_dm("alice", "bob", "persisted hello")
assert encrypted["ok"] is True
decrypted = dm_mls.decrypt_dm("bob", "alice", encrypted["ciphertext"], encrypted["nonce"])
assert decrypted["ok"] is True
assert decrypted["plaintext"] == "persisted hello"
assert decrypted["session_id"] == accepted["session_id"]
def test_dm_mls_encrypt_detects_stale_session_after_privacy_core_reset(tmp_path, monkeypatch):
dm_mls, _relay = _fresh_dm_mls_state(tmp_path, monkeypatch)
bob_bundle = dm_mls.export_dm_key_package_for_alias("bob")
initiated = dm_mls.initiate_dm_session("alice", "bob", bob_bundle)
accepted = dm_mls.accept_dm_session("bob", "alice", initiated["welcome"])
assert accepted["ok"] is True
dm_mls.reset_dm_mls_state(clear_privacy_core=True, clear_persistence=False)
expired = dm_mls.encrypt_dm("alice", "bob", "stale handle")
assert expired == {
"ok": False,
"detail": "session_expired",
"session_id": "alice::bob",
}
assert dm_mls.has_dm_session("alice", "bob") == {
"ok": True,
"exists": False,
"session_id": "alice::bob",
}
def test_dm_mls_recreates_alias_identity_when_binding_proof_is_tampered(tmp_path, monkeypatch, caplog):
import logging
from services.mesh.mesh_secure_storage import read_domain_json, write_domain_json
dm_mls, _relay = _fresh_dm_mls_state(tmp_path, monkeypatch)
first_bundle = dm_mls.export_dm_key_package_for_alias("alice")
assert first_bundle["ok"] is True
stored = read_domain_json(dm_mls.STATE_DOMAIN, dm_mls.STATE_FILENAME, dm_mls._default_state)
original_handle = int(stored["aliases"]["alice"]["handle"])
stored["aliases"]["alice"]["binding_proof"] = "00" * 64
write_domain_json(dm_mls.STATE_DOMAIN, dm_mls.STATE_FILENAME, stored)
dm_mls.reset_dm_mls_state(clear_privacy_core=False, clear_persistence=False)
with caplog.at_level(logging.WARNING):
second_bundle = dm_mls.export_dm_key_package_for_alias("alice")
reloaded = read_domain_json(dm_mls.STATE_DOMAIN, dm_mls.STATE_FILENAME, dm_mls._default_state)
assert second_bundle["ok"] is True
assert "dm mls alias binding invalid for alice" in caplog.text.lower()
assert int(reloaded["aliases"]["alice"]["handle"]) != original_handle
def test_dm_mls_http_compose_store_poll_decrypt_round_trip(tmp_path, monkeypatch):
import main
from httpx import ASGITransport, AsyncClient
from services.mesh import mesh_hashchain
from services import wormhole_supervisor
dm_mls, relay = _fresh_dm_mls_state(tmp_path, monkeypatch)
bob_bundle = dm_mls.export_dm_key_package_for_alias("bob")
assert bob_bundle["ok"] is True
monkeypatch.setattr(main, "_current_admin_key", lambda: "test-admin")
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, "ok"))
monkeypatch.setattr(
main,
"_verify_dm_mailbox_request",
lambda **_kwargs: (
True,
"ok",
{"mailbox_claims": REQUEST_CLAIMS},
),
)
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(
dm_mls,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": True},
)
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": True},
)
monkeypatch.setattr(mesh_hashchain.infonet, "validate_and_set_sequence", lambda *_args, **_kwargs: (True, "ok"))
admin_headers = {"X-Admin-Key": main._current_admin_key()}
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
now = int(time.time())
compose_response = await ac.post(
"/api/wormhole/dm/compose",
json={
"peer_id": "bob-agent",
"plaintext": "hello through http",
"local_alias": "alice",
"remote_alias": "bob",
"remote_prekey_bundle": bob_bundle,
},
headers=admin_headers,
)
composed = compose_response.json()
send_response = await ac.post(
"/api/mesh/dm/send",
json={
"sender_id": "alice-agent",
"recipient_id": "bob-agent",
"delivery_class": "request",
"ciphertext": composed["ciphertext"],
"format": composed["format"],
"session_welcome": composed["session_welcome"],
"msg_id": "dm-mls-http-1",
"timestamp": now,
"nonce": "http-mls-nonce-1",
"public_key": "cHVi",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 11,
"protocol_version": "infonet/2",
},
)
poll_response = await ac.post(
"/api/mesh/dm/poll",
json={
"agent_id": "bob-agent",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": now + 1,
"nonce": "http-mls-nonce-2",
"public_key": "cHVi",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 12,
"protocol_version": "infonet/2",
},
)
polled = poll_response.json()
decrypt_response = await ac.post(
"/api/wormhole/dm/decrypt",
json={
"peer_id": "alice-agent",
"local_alias": "bob",
"remote_alias": "alice",
"ciphertext": polled["messages"][0]["ciphertext"],
"format": polled["messages"][0]["format"],
"nonce": "",
"session_welcome": polled["messages"][0]["session_welcome"],
},
headers=admin_headers,
)
return composed, send_response.json(), polled, decrypt_response.json()
composed, sent, polled, decrypted = asyncio.run(_run())
assert composed["ok"] is True
assert composed["format"] == "mls1"
assert sent["ok"] is True
assert sent["msg_id"] == "dm-mls-http-1"
assert polled["ok"] is True
assert polled["count"] == 1
assert polled["messages"][0]["format"] == "mls1"
assert polled["messages"][0]["session_welcome"] == composed["session_welcome"]
assert decrypted == {
"ok": True,
"peer_id": "alice-agent",
"local_alias": "bob",
"remote_alias": "alice",
"plaintext": "hello through http",
"format": "mls1",
}
assert relay.count_claims("bob-agent", REQUEST_CLAIMS) == 0
+343
View File
@@ -0,0 +1,343 @@
import json
import time
from services.config import get_settings
from services.mesh import mesh_dm_relay, mesh_schema, mesh_secure_storage
REQUEST_CLAIM = [{"type": "requests", "token": "request-claim-token"}]
def _fresh_relay(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_dm_relay, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_dm_relay, "RELAY_FILE", tmp_path / "dm_relay.json")
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
get_settings.cache_clear()
return mesh_dm_relay.DMRelay()
def test_dm_key_registration_is_monotonic(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
ok, reason, meta = relay.register_dh_key(
"alice",
"pub1",
"X25519",
100,
"sig1",
"nodepub",
"Ed25519",
"infonet/2",
1,
)
assert ok, reason
assert meta["accepted_sequence"] == 1
assert meta["bundle_fingerprint"]
ok, reason, _ = relay.register_dh_key(
"alice",
"pub1",
"X25519",
100,
"sig1",
"nodepub",
"Ed25519",
"infonet/2",
1,
)
assert not ok
assert "rollback" in reason.lower() or "replay" in reason.lower()
ok, reason, _ = relay.register_dh_key(
"alice",
"pub2",
"X25519",
99,
"sig2",
"nodepub",
"Ed25519",
"infonet/2",
2,
)
assert not ok
assert "older" in reason.lower()
ok, reason, meta = relay.register_dh_key(
"alice",
"pub3",
"X25519",
101,
"sig3",
"nodepub",
"Ed25519",
"infonet/2",
2,
)
assert ok, reason
assert meta["accepted_sequence"] == 2
def test_secure_mailbox_claims_split_requests_and_shared(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
request_result = relay.deposit(
sender_id="alice",
recipient_id="bob",
ciphertext="cipher_req",
msg_id="msg_req",
delivery_class="request",
)
shared_result = relay.deposit(
sender_id="carol",
recipient_id="bob",
ciphertext="cipher_shared",
msg_id="msg_shared",
delivery_class="shared",
recipient_token="sharedtoken",
)
assert request_result["ok"]
assert shared_result["ok"]
assert relay.count_legacy(agent_id="bob") == 0
request_claims = REQUEST_CLAIM
shared_claims = [{"type": "shared", "token": "sharedtoken"}]
assert relay.count_claims("bob", request_claims) == 1
assert relay.count_claims("bob", shared_claims) == 1
request_messages = relay.collect_claims("bob", request_claims)
assert [msg["msg_id"] for msg in request_messages] == ["msg_req"]
assert request_messages[0]["delivery_class"] == "request"
assert relay.count_claims("bob", request_claims) == 0
assert relay.count_claims("bob", [{"type": "requests"}]) == 0
shared_messages = relay.collect_claims("bob", shared_claims)
assert [msg["msg_id"] for msg in shared_messages] == ["msg_shared"]
assert shared_messages[0]["delivery_class"] == "shared"
assert relay.count_claims("bob", shared_claims) == 0
def test_legacy_collect_and_count_require_agent_token(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
relay._mailboxes["legacy-token"].append(
mesh_dm_relay.DMMessage(
sender_id="alice",
ciphertext="cipher",
timestamp=time.time(),
msg_id="legacy-1",
delivery_class="request",
)
)
assert relay.collect_legacy(agent_id="bob") == []
assert relay.count_legacy(agent_id="bob") == 0
assert relay.count_legacy(agent_token="legacy-token") == 1
def test_nonce_replay_and_memory_only_spool(tmp_path, monkeypatch):
monkeypatch.setenv("MESH_DM_PERSIST_SPOOL", "false")
relay = _fresh_relay(tmp_path, monkeypatch)
result = relay.deposit(
sender_id="alice",
recipient_id="bob",
ciphertext="cipher",
msg_id="msg1",
delivery_class="request",
)
assert result["ok"]
assert mesh_dm_relay.RELAY_FILE.exists()
payload = json.loads(mesh_dm_relay.RELAY_FILE.read_text(encoding="utf-8"))
assert payload.get("kind") == "sb_secure_json"
restored = mesh_secure_storage.read_secure_json(mesh_dm_relay.RELAY_FILE, lambda: {})
assert "mailboxes" not in restored
ok, reason = relay.consume_nonce("bob", "nonce-1", 100)
assert ok, reason
ok, reason = relay.consume_nonce("bob", "nonce-1", 100)
assert not ok
assert reason == "nonce replay detected"
def test_request_mailbox_token_binding_requires_presented_token(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
legacy_key = relay.mailbox_key_for_delivery(
recipient_id="bob",
delivery_class="request",
)
presented_token = "mailbox-token-bob"
hashed = relay._hashed_mailbox_token(presented_token)
assert legacy_key != hashed
claimed = relay.claim_mailbox_keys("bob", [{"type": "requests", "token": presented_token}])
assert claimed[0] == hashed
assert legacy_key in claimed
assert relay.mailbox_key_for_delivery(recipient_id="bob", delivery_class="request") == hashed
def test_shared_delivery_uses_hashed_mailbox_token(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
result = relay.deposit(
sender_id="alice",
recipient_id="",
ciphertext="cipher_shared",
msg_id="msg_shared_hash",
delivery_class="shared",
recipient_token="shared-mailbox-token",
sender_token_hash="abc123",
)
assert result["ok"] is True
mailbox_key = relay._hashed_mailbox_token("shared-mailbox-token")
assert list(relay._mailboxes.keys()) == [mailbox_key]
assert relay._mailboxes[mailbox_key][0].sender_id == "sender_token:abc123"
def test_request_and_shared_claims_freeze_current_sender_identity_contract(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
request_result = relay.deposit(
sender_id="alice",
recipient_id="bob",
ciphertext="cipher-req",
msg_id="msg-req-1",
delivery_class="request",
)
shared_result = relay.deposit(
sender_id="alice",
recipient_id="",
ciphertext="cipher-shared",
msg_id="msg-shared-1",
delivery_class="shared",
recipient_token="shared-mailbox-token",
sender_token_hash="abc123",
sender_seal="v3:sealed",
)
assert request_result["ok"] is True
assert shared_result["ok"] is True
request_messages = relay.collect_claims("bob", [{"type": "requests", "token": "request-claim-token"}])
shared_messages = relay.collect_claims("bob", [{"type": "shared", "token": "shared-mailbox-token"}])
assert request_messages == [
{
"sender_id": "alice",
"ciphertext": "cipher-req",
"timestamp": request_messages[0]["timestamp"],
"msg_id": "msg-req-1",
"delivery_class": "request",
"sender_seal": "",
"format": "dm1",
"session_welcome": "",
}
]
assert shared_messages == [
{
"sender_id": "sender_token:abc123",
"ciphertext": "cipher-shared",
"timestamp": shared_messages[0]["timestamp"],
"msg_id": "msg-shared-1",
"delivery_class": "shared",
"sender_seal": "v3:sealed",
"format": "dm1",
"session_welcome": "",
}
]
def test_block_purges_and_rejects_reduced_sender_handles(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
first = relay.deposit(
sender_id="sealed:first",
raw_sender_id="alice",
recipient_id="bob",
ciphertext="cipher-req",
msg_id="msg-sealed-1",
delivery_class="request",
sender_seal="v3:test-seal",
)
assert first["ok"] is True
relay.block("bob", "alice")
assert relay.count_claims("bob", REQUEST_CLAIM) == 0
second = relay.deposit(
sender_id="sealed:second",
raw_sender_id="alice",
recipient_id="bob",
ciphertext="cipher-req-2",
msg_id="msg-sealed-2",
delivery_class="request",
sender_seal="v3:test-seal",
)
assert second == {"ok": False, "detail": "Recipient is not accepting your messages"}
assert relay.count_claims("bob", REQUEST_CLAIM) == 0
def test_nonce_cache_is_bounded_and_expires_entries(tmp_path, monkeypatch):
monkeypatch.setenv("MESH_DM_NONCE_CACHE_MAX", "2")
relay = _fresh_relay(tmp_path, monkeypatch)
current = {"value": 1_000.0}
monkeypatch.setattr(mesh_dm_relay.time, "time", lambda: current["value"])
assert relay.consume_nonce("bob", "nonce-1", 1_000)[0] is True
assert relay.consume_nonce("bob", "nonce-2", 1_000)[0] is True
assert len(relay._nonce_cache) == 2
ok, reason = relay.consume_nonce("bob", "nonce-3", 1_000)
assert ok is False
assert reason == "nonce cache at capacity"
assert len(relay._nonce_cache) == 2
assert "bob:nonce-1" in relay._nonce_cache
assert "bob:nonce-2" in relay._nonce_cache
current["value"] = 1_000.0 + 301.0
assert relay.consume_nonce("bob", "nonce-2", 1_000)[0] is True
def test_dm_schema_requires_tokens_for_all_mailbox_claims():
ok, reason = mesh_schema.validate_event_payload(
"dm_poll",
{
"mailbox_claims": [{"type": "requests", "token": ""}],
"timestamp": 123,
"nonce": "abc",
},
)
assert not ok
assert "token" in reason.lower()
ok, reason = mesh_schema.validate_event_payload(
"dm_count",
{
"mailbox_claims": [{"type": "shared", "token": ""}],
"timestamp": 123,
"nonce": "abc",
},
)
assert not ok
assert "token" in reason.lower()
ok, reason = mesh_schema.validate_event_payload(
"dm_message",
{
"recipient_id": "bob",
"delivery_class": "shared",
"recipient_token": "",
"ciphertext": "cipher",
"format": "mls1",
"msg_id": "m1",
"timestamp": 123,
},
)
assert not ok
assert "recipient_token" in reason.lower()
@@ -0,0 +1,86 @@
"""Tests verifying the /api/mesh/dm/witness endpoint uses correct identifiers.
The bug: _preflight_signed_event_integrity was called with event_type="trust_vouch"
and node_id=voucher_id (undefined NameError). Fixed to event_type="dm_key_witness"
and node_id=witness_id.
"""
import ast
import textwrap
from pathlib import Path
MAIN_PY = Path(__file__).resolve().parents[2] / "main.py"
def _find_dm_key_witness_func(source: str) -> ast.AsyncFunctionDef | None:
"""Parse main.py AST and find the dm_key_witness POST handler."""
tree = ast.parse(source)
for node in ast.walk(tree):
if isinstance(node, ast.AsyncFunctionDef) and node.name == "dm_key_witness":
return node
return None
def test_witness_endpoint_uses_correct_event_type():
"""The preflight call must use event_type='dm_key_witness', not 'trust_vouch'."""
source = MAIN_PY.read_text(encoding="utf-8")
func = _find_dm_key_witness_func(source)
assert func is not None, "dm_key_witness function not found in main.py"
# Extract the function source and look for _preflight_signed_event_integrity call
func_source = ast.get_source_segment(source, func)
assert func_source is not None
assert 'event_type="dm_key_witness"' in func_source, (
"preflight call should use event_type='dm_key_witness', "
f"but the function contains: {func_source[:500]}"
)
assert 'event_type="trust_vouch"' not in func_source, (
"preflight call still uses the wrong event_type='trust_vouch'"
)
def test_witness_endpoint_uses_witness_id_not_voucher_id():
"""The preflight call must use node_id=witness_id, not voucher_id."""
source = MAIN_PY.read_text(encoding="utf-8")
func = _find_dm_key_witness_func(source)
assert func is not None, "dm_key_witness function not found in main.py"
func_source = ast.get_source_segment(source, func)
assert func_source is not None
# Find all _preflight_signed_event_integrity calls in the function
lines = func_source.splitlines()
in_preflight = False
preflight_block = []
for line in lines:
if "_preflight_signed_event_integrity" in line:
in_preflight = True
if in_preflight:
preflight_block.append(line)
if ")" in line and line.strip().endswith(")"):
break
preflight_text = "\n".join(preflight_block)
assert "node_id=witness_id" in preflight_text, (
f"preflight call should use node_id=witness_id, got:\n{preflight_text}"
)
assert "node_id=voucher_id" not in preflight_text, (
"preflight call still references undefined voucher_id"
)
def test_verify_signed_event_also_uses_dm_key_witness():
"""The _verify_signed_event call should also use event_type='dm_key_witness'."""
source = MAIN_PY.read_text(encoding="utf-8")
func = _find_dm_key_witness_func(source)
assert func is not None
func_source = ast.get_source_segment(source, func)
assert func_source is not None
# Count occurrences of the correct event_type
assert func_source.count('event_type="dm_key_witness"') == 2, (
"Both _verify_signed_event and _preflight_signed_event_integrity "
"should use event_type='dm_key_witness'"
)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,178 @@
"""Tests for security config guardrails in env_check._audit_security_config."""
import logging
import os
from unittest.mock import patch
import pytest
# Reset pydantic settings cache before importing, so env overrides take effect
os.environ.pop("MESH_DM_TOKEN_PEPPER", None)
from services.config import get_settings, Settings
@pytest.fixture(autouse=True)
def _clear_settings_cache():
"""Bust the lru_cache so each test gets fresh Settings."""
get_settings.cache_clear()
yield
get_settings.cache_clear()
@pytest.fixture(autouse=True)
def _clean_pepper_env():
"""Remove any auto-generated pepper between tests."""
os.environ.pop("MESH_DM_TOKEN_PEPPER", None)
yield
os.environ.pop("MESH_DM_TOKEN_PEPPER", None)
class TestInsecureAdminWarning:
def test_allow_insecure_admin_without_key_logs_critical(self, caplog):
with patch.dict(os.environ, {"ALLOW_INSECURE_ADMIN": "true", "ADMIN_KEY": ""}):
get_settings.cache_clear()
from services.env_check import _audit_security_config
with caplog.at_level(logging.CRITICAL):
_audit_security_config(get_settings())
assert "ALLOW_INSECURE_ADMIN=true with no ADMIN_KEY" in caplog.text
assert "completely unauthenticated" in caplog.text
def test_admin_key_present_no_warning(self, caplog):
with patch.dict(
os.environ, {"ALLOW_INSECURE_ADMIN": "true", "ADMIN_KEY": "secret123"}
):
get_settings.cache_clear()
from services.env_check import _audit_security_config
with caplog.at_level(logging.CRITICAL):
_audit_security_config(get_settings())
assert "ALLOW_INSECURE_ADMIN=true with no ADMIN_KEY" not in caplog.text
class TestSignatureConfigWarnings:
def test_non_strict_logs_warning(self, caplog):
with patch.dict(os.environ, {"MESH_STRICT_SIGNATURES": "false"}):
get_settings.cache_clear()
from services.env_check import _audit_security_config
with caplog.at_level(logging.WARNING):
_audit_security_config(get_settings())
assert "MESH_STRICT_SIGNATURES=false" in caplog.text
class TestTokenPepperAutoGeneration:
def test_empty_pepper_auto_generates(self, caplog):
os.environ.pop("MESH_DM_TOKEN_PEPPER", None)
get_settings.cache_clear()
from services.env_check import _audit_security_config
with caplog.at_level(logging.WARNING):
_audit_security_config(get_settings())
generated = os.environ.get("MESH_DM_TOKEN_PEPPER", "")
assert len(generated) == 64 # 32 bytes hex
assert "Auto-generated a random pepper" in caplog.text
def test_existing_pepper_preserved(self, caplog):
os.environ["MESH_DM_TOKEN_PEPPER"] = "my-secret-pepper"
get_settings.cache_clear()
from services.env_check import _audit_security_config
with caplog.at_level(logging.WARNING):
_audit_security_config(get_settings())
assert os.environ["MESH_DM_TOKEN_PEPPER"] == "my-secret-pepper"
assert "Auto-generated" not in caplog.text
class TestPeerSecretWarnings:
def test_missing_peer_secret_only_warns_and_does_not_fail_validation(self, caplog):
with patch.dict(
os.environ,
{
"MESH_RELAY_PEERS": "https://peer.example",
"MESH_PEER_PUSH_SECRET": "",
},
clear=False,
):
get_settings.cache_clear()
from services.env_check import validate_env
with caplog.at_level(logging.WARNING):
result = validate_env(strict=True)
assert result is True
assert "MESH_PEER_PUSH_SECRET is invalid (empty)" in caplog.text
def test_security_posture_warnings_include_missing_peer_secret(self):
with patch.dict(
os.environ,
{
"MESH_RELAY_PEERS": "https://peer.example",
"MESH_PEER_PUSH_SECRET": "",
},
clear=False,
):
get_settings.cache_clear()
from services.env_check import get_security_posture_warnings
warnings = get_security_posture_warnings(get_settings())
assert any("MESH_PEER_PUSH_SECRET is invalid (empty)" in item for item in warnings)
def test_placeholder_peer_secret_is_flagged(self, caplog):
with patch.dict(
os.environ,
{
"MESH_RELAY_PEERS": "https://peer.example",
"MESH_PEER_PUSH_SECRET": "change-me",
},
clear=False,
):
get_settings.cache_clear()
from services.env_check import _audit_security_config
with caplog.at_level(logging.WARNING):
_audit_security_config(get_settings())
assert "MESH_PEER_PUSH_SECRET is invalid (placeholder)" in caplog.text
class TestCoverTrafficWarnings:
def test_disabled_cover_traffic_logs_warning_when_rns_enabled(self, caplog):
with patch.dict(
os.environ,
{
"MESH_RNS_ENABLED": "true",
"MESH_RNS_COVER_INTERVAL_S": "0",
},
clear=False,
):
get_settings.cache_clear()
from services.env_check import _audit_security_config
with caplog.at_level(logging.WARNING):
_audit_security_config(get_settings())
assert "MESH_RNS_COVER_INTERVAL_S<=0 disables background RNS cover traffic" in caplog.text
def test_security_posture_warnings_include_disabled_cover_traffic(self):
with patch.dict(
os.environ,
{
"MESH_RNS_ENABLED": "true",
"MESH_RNS_COVER_INTERVAL_S": "0",
},
clear=False,
):
get_settings.cache_clear()
from services.env_check import get_security_posture_warnings
warnings = get_security_posture_warnings(get_settings())
assert any("MESH_RNS_COVER_INTERVAL_S<=0" in item for item in warnings)
@@ -0,0 +1,177 @@
import asyncio
import json
from starlette.requests import Request
from httpx import ASGITransport, AsyncClient
def _json_request(path: str, body: dict) -> Request:
raw = json.dumps(body).encode("utf-8")
sent = {"value": False}
async def receive():
if sent["value"]:
return {"type": "http.request", "body": b"", "more_body": False}
sent["value"] = True
return {"type": "http.request", "body": raw, "more_body": False}
return Request(
{
"type": "http",
"headers": [(b"content-type", b"application/json")],
"client": ("test", 12345),
"method": "POST",
"path": path,
},
receive,
)
def test_fixed_launch_gate_catalog_contains_private_rooms():
from services.mesh.mesh_reputation import gate_manager
gate_ids = [gate["gate_id"] for gate in gate_manager.list_gates()]
assert "infonet" in gate_ids
assert "finance" in gate_ids
assert "prediction-markets" in gate_ids
assert "cryptography" in gate_ids
assert "opsec-lab" in gate_ids
assert "public-square" not in gate_ids
def test_fixed_launch_gate_catalog_exposes_descriptions_and_welcome_text():
from services.mesh.mesh_reputation import gate_manager
finance = next(g for g in gate_manager.list_gates() if g["gate_id"] == "finance")
assert finance["fixed"] is True
assert "Macro" in finance["description"]
assert "WELCOME TO FINANCE" in finance["welcome"]
def test_gate_create_endpoint_is_disabled_for_fixed_launch_catalog():
import main
response = asyncio.run(
main.gate_create(
_json_request(
"/api/mesh/gate/create",
{
"creator_id": "!sb_test",
"gate_id": "new-gate",
"display_name": "New Gate",
"rules": {"min_overall_rep": 0},
},
)
)
)
assert response["ok"] is False
assert "fixed private launch catalog" in response["detail"]
def test_infonet_messages_returns_seed_notice_for_empty_fixed_gate(monkeypatch):
import main
from services.mesh import mesh_hashchain
monkeypatch.setattr(mesh_hashchain.infonet, "get_messages", lambda **kwargs: [])
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (True, "ok"))
response = asyncio.run(
main.infonet_messages(
Request(
{
"type": "http",
"headers": [(b"x-admin-key", b"test-admin")],
"client": ("test", 12345),
"method": "GET",
"path": "/api/mesh/infonet/messages",
}
),
gate="finance",
limit=20,
offset=0,
)
)
assert response["count"] == 1
assert response["messages"][0]["system_seed"] is True
assert response["messages"][0]["gate"] == "finance"
assert "WELCOME TO FINANCE" in response["messages"][0]["message"]
def test_gate_scoped_vote_rejects_unknown_gate(monkeypatch):
import main
from services import wormhole_supervisor
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, "ok"))
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True},
)
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
response = await ac.post(
"/api/mesh/vote",
json={
"voter_id": "!sb_voter",
"target_id": "!sb_target",
"vote": 1,
"gate": "nonexistent-gate",
"voter_pubkey": "pub",
"public_key_algo": "Ed25519",
"voter_sig": "sig",
"sequence": 1,
"protocol_version": "1",
},
)
return response.json()
result = asyncio.run(_run())
assert result["ok"] is False
assert "does not exist" in result["detail"]
def test_gate_scoped_vote_requires_voter_gate_access(monkeypatch):
import main
from services import wormhole_supervisor
from services.mesh.mesh_reputation import gate_manager
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, "ok"))
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True},
)
monkeypatch.setattr(
gate_manager,
"can_enter",
lambda voter_id, gate_id: (False, "Need 10 overall rep (you have 0)"),
)
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
response = await ac.post(
"/api/mesh/vote",
json={
"voter_id": "!sb_voter",
"target_id": "!sb_target",
"vote": -1,
"gate": "finance",
"voter_pubkey": "pub",
"public_key_algo": "Ed25519",
"voter_sig": "sig",
"sequence": 1,
"protocol_version": "1",
},
)
return response.json()
result = asyncio.run(_run())
assert result["ok"] is False
assert "Gate vote denied" in result["detail"]
+667
View File
@@ -0,0 +1,667 @@
import asyncio
import base64
import json
def _embedded_gate_event_wire_size(gate_mls_mod, persona_id: str, gate_id: str, plaintext: str) -> int:
from services.mesh.mesh_hashchain import build_gate_wire_ref
from services.mesh.mesh_rns import RNSMessage
binding = gate_mls_mod._sync_binding(gate_id)
member = binding.members[persona_id]
proof = {
"proof_version": "embedded-proof-v1",
"node_id": "!sb_embeddedproof",
"public_key": "A" * 44,
"public_key_algo": "Ed25519",
"sequence": 7,
"protocol_version": "infonet/2",
"content_hash": "b" * 64,
"transport_hash": "c" * 64,
"signature": "d" * 128,
}
plaintext_with_proof = json.dumps(
{
"m": plaintext,
"e": int(binding.epoch),
"proof": proof,
},
separators=(",", ":"),
ensure_ascii=False,
)
ciphertext = gate_mls_mod._privacy_client().encrypt_group_message(
member.group_handle,
plaintext_with_proof.encode("utf-8"),
)
padded = gate_mls_mod._pad_ciphertext_raw(ciphertext)
event = {
"gate_contract_version": "gate-v2-embedded-origin-v1",
"event_type": "gate_message",
"timestamp": 1710000000,
"event_id": "e" * 64,
"payload": {
"ciphertext": gate_mls_mod._b64(padded),
"format": gate_mls_mod.MLS_GATE_FORMAT,
"nonce": "n" * 16,
"sender_ref": "s" * 16,
"epoch": int(binding.epoch),
},
}
event["payload"]["gate_ref"] = build_gate_wire_ref(gate_id, event)
return len(
RNSMessage(
msg_type="gate_event",
body={"event": event},
meta={"message_id": "mid", "dandelion": {"phase": "stem", "hops": 0, "max_hops": 3}},
).encode()
)
def _fresh_gate_state(tmp_path, monkeypatch):
from services import wormhole_supervisor
from services.mesh import mesh_gate_mls, mesh_secure_storage, mesh_wormhole_persona
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
monkeypatch.setattr(mesh_gate_mls, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_gate_mls, "STATE_FILE", tmp_path / "wormhole_gate_mls.json")
monkeypatch.setattr(mesh_wormhole_persona, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_persona, "PERSONA_FILE", tmp_path / "wormhole_persona.json")
monkeypatch.setattr(
mesh_wormhole_persona,
"LEGACY_DM_IDENTITY_FILE",
tmp_path / "wormhole_identity.json",
)
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False},
)
mesh_gate_mls.reset_gate_mls_state()
return mesh_gate_mls, mesh_wormhole_persona
def test_gate_message_schema_accepts_mls1_format():
from services.mesh.mesh_protocol import normalize_payload
from services.mesh.mesh_schema import validate_event_payload
payload = normalize_payload(
"gate_message",
{
"gate": "infonet",
"epoch": 1,
"ciphertext": "ZmFrZQ==",
"nonce": "bWxzMS1lbnZlbG9wZQ==",
"sender_ref": "persona-1",
"format": "mls1",
},
)
assert validate_event_payload("gate_message", payload) == (True, "ok")
def test_compose_and_decrypt_gate_message_round_trip_via_mls(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.create_gate_persona("finance", label="scribe")
composed = gate_mls_mod.compose_encrypted_gate_message("finance", "hello mls gate")
decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id="finance",
epoch=int(composed["epoch"]),
ciphertext=str(composed["ciphertext"]),
nonce=str(composed["nonce"]),
sender_ref=str(composed["sender_ref"]),
)
assert composed["ok"] is True
assert composed["format"] == "mls1"
assert composed["ciphertext"] != "hello mls gate"
assert decrypted == {
"ok": True,
"gate_id": "finance",
"epoch": 1,
"plaintext": "hello mls gate",
"identity_scope": "persona",
}
def test_anonymous_gate_session_can_compose_and_decrypt_round_trip(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.enter_gate_anonymously("finance", rotate=True)
status = gate_mls_mod.get_local_gate_key_status("finance")
composed = gate_mls_mod.compose_encrypted_gate_message("finance", "hello from anonymous gate")
decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id="finance",
epoch=int(composed["epoch"]),
ciphertext=str(composed["ciphertext"]),
nonce=str(composed["nonce"]),
sender_ref=str(composed["sender_ref"]),
)
assert status["ok"] is True
assert status["identity_scope"] == "anonymous"
assert status["has_local_access"] is True
assert composed["ok"] is True
assert composed["identity_scope"] == "anonymous"
assert decrypted == {
"ok": True,
"gate_id": "finance",
"epoch": 1,
"plaintext": "hello from anonymous gate",
"identity_scope": "anonymous",
}
def test_self_echo_decrypt_uses_local_plaintext_cache_fast_path(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.create_gate_persona("finance", label="scribe")
composed = gate_mls_mod.compose_encrypted_gate_message("finance", "cache hit")
def fail_sync(_gate_id: str):
raise AssertionError("self-echo cache should bypass MLS sync/decrypt")
monkeypatch.setattr(gate_mls_mod, "_sync_binding", fail_sync)
decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id="finance",
epoch=int(composed["epoch"]),
ciphertext=str(composed["ciphertext"]),
nonce=str(composed["nonce"]),
sender_ref=str(composed["sender_ref"]),
)
assert decrypted == {
"ok": True,
"gate_id": "finance",
"epoch": 1,
"plaintext": "cache hit",
"identity_scope": "persona",
}
def test_verifier_open_does_not_require_active_gate_persona(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "finance"
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona(gate_id, label="first")
second = persona_mod.create_gate_persona(gate_id, label="second")
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "verifier open")
assert composed["ok"] is True
persona_mod.enter_gate_anonymously(gate_id, rotate=True)
opened = gate_mls_mod.open_gate_ciphertext_for_verifier(
gate_id=gate_id,
epoch=int(composed["epoch"]),
ciphertext=str(composed["ciphertext"]),
format=str(composed["format"]),
)
assert opened["ok"] is True
assert opened["plaintext"] == "verifier open"
assert opened["identity_scope"] == "verifier"
assert opened["opened_by_persona_id"] in {
first["identity"]["persona_id"],
second["identity"]["persona_id"],
}
def test_verifier_open_does_not_use_self_echo_cache(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "finance"
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona(gate_id, label="first")
second = persona_mod.create_gate_persona(gate_id, label="second")
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "no cache authority")
assert composed["ok"] is True
monkeypatch.setattr(
gate_mls_mod,
"_peek_cached_plaintext",
lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("verifier must not peek cache")),
)
monkeypatch.setattr(
gate_mls_mod,
"_consume_cached_plaintext",
lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("verifier must not consume cache")),
)
monkeypatch.setattr(gate_mls_mod, "_active_gate_persona", lambda *_args, **_kwargs: None)
opened = gate_mls_mod.open_gate_ciphertext_for_verifier(
gate_id=gate_id,
epoch=int(composed["epoch"]),
ciphertext=str(composed["ciphertext"]),
format=str(composed["format"]),
)
assert opened == {
"ok": True,
"gate_id": gate_id,
"epoch": 1,
"plaintext": "no cache authority",
"opened_by_persona_id": second["identity"]["persona_id"],
"identity_scope": "verifier",
}
def test_removed_member_cannot_decrypt_new_messages(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "opsec-lab"
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona(gate_id, label="first")
second = persona_mod.create_gate_persona(gate_id, label="second")
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
before_removal = gate_mls_mod.compose_encrypted_gate_message(gate_id, "before removal")
persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"])
readable_before = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id=gate_id,
epoch=int(before_removal["epoch"]),
ciphertext=str(before_removal["ciphertext"]),
nonce=str(before_removal["nonce"]),
sender_ref=str(before_removal["sender_ref"]),
)
persona_mod.retire_gate_persona(gate_id, second["identity"]["persona_id"])
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
after_removal = gate_mls_mod.compose_encrypted_gate_message(gate_id, "after removal")
persona_mod.enter_gate_anonymously(gate_id, rotate=True)
blocked_after = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id=gate_id,
epoch=int(after_removal["epoch"]),
ciphertext=str(after_removal["ciphertext"]),
nonce=str(after_removal["nonce"]),
sender_ref=str(after_removal["sender_ref"]),
)
assert readable_before["ok"] is True
assert readable_before["plaintext"] == "before removal"
assert blocked_after == {
"ok": True,
"gate_id": gate_id,
"epoch": int(after_removal["epoch"]),
"plaintext": "after removal",
"identity_scope": "anonymous",
}
def test_gate_mls_state_survives_simulated_restart(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "infonet"
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona(gate_id, label="first")
second = persona_mod.create_gate_persona(gate_id, label="second")
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
initial = gate_mls_mod.compose_encrypted_gate_message(gate_id, "before restart")
gate_mls_mod.reset_gate_mls_state()
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
after_restart = gate_mls_mod.compose_encrypted_gate_message(gate_id, "after restart")
persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"])
decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id=gate_id,
epoch=int(after_restart["epoch"]),
ciphertext=str(after_restart["ciphertext"]),
nonce=str(after_restart["nonce"]),
sender_ref=str(after_restart["sender_ref"]),
)
assert initial["ok"] is True
assert after_restart["ok"] is True
assert after_restart["epoch"] == initial["epoch"]
assert decrypted["ok"] is True
assert decrypted["plaintext"] == "after restart"
def test_pre_restart_gate_message_fails_to_decrypt_after_reset(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "restart-blackout"
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona(gate_id, label="first")
second = persona_mod.create_gate_persona(gate_id, label="second")
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
before_reset = gate_mls_mod.compose_encrypted_gate_message(gate_id, "before reset")
assert before_reset["ok"] is True
persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"])
readable_before = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id=gate_id,
epoch=int(before_reset["epoch"]),
ciphertext=str(before_reset["ciphertext"]),
nonce=str(before_reset["nonce"]),
sender_ref=str(before_reset["sender_ref"]),
)
assert readable_before["ok"] is True
assert readable_before["plaintext"] == "before reset"
gate_mls_mod.reset_gate_mls_state()
persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"])
blocked_after = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id=gate_id,
epoch=int(before_reset["epoch"]),
ciphertext=str(before_reset["ciphertext"]),
nonce=str(before_reset["nonce"]),
sender_ref=str(before_reset["sender_ref"]),
)
assert blocked_after == {
"ok": False,
"detail": "gate_mls_decrypt_failed",
}
def test_embedded_proof_budget_exceeds_rns_limit_before_6144_bucket_for_large_messages(tmp_path, monkeypatch):
from services.config import get_settings
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "budget-gate"
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona(gate_id, label="first")
persona_id = first["identity"]["persona_id"]
persona_mod.activate_gate_persona(gate_id, persona_id)
medium_wire = _embedded_gate_event_wire_size(gate_mls_mod, persona_id, gate_id, "x" * 1000)
large_wire = _embedded_gate_event_wire_size(gate_mls_mod, persona_id, gate_id, "x" * 2000)
assert medium_wire < get_settings().MESH_RNS_MAX_PAYLOAD
assert large_wire > get_settings().MESH_RNS_MAX_PAYLOAD
def test_sync_binding_skips_persist_when_membership_is_unchanged(tmp_path, monkeypatch):
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "quiet-room"
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona(gate_id, label="first")
second = persona_mod.create_gate_persona(gate_id, label="second")
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "steady state")
persist_calls = []
original_persist = gate_mls_mod._persist_binding
def track_persist(binding):
persist_calls.append(binding.gate_id)
return original_persist(binding)
monkeypatch.setattr(gate_mls_mod, "_persist_binding", track_persist)
persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"])
decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id=gate_id,
epoch=int(composed["epoch"]),
ciphertext=str(composed["ciphertext"]),
nonce=str(composed["nonce"]),
sender_ref=str(composed["sender_ref"]),
)
assert decrypted["ok"] is True
assert decrypted["plaintext"] == "steady state"
assert persist_calls == []
def test_tampered_binding_is_rejected_on_sync(tmp_path, monkeypatch, caplog):
from services.mesh.mesh_secure_storage import read_domain_json, write_domain_json
import logging
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "cryptography"
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona = persona_mod.create_gate_persona(gate_id, label="scribe")
composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "tamper target")
assert composed["ok"] is True
stored = read_domain_json(
gate_mls_mod.STATE_DOMAIN,
gate_mls_mod.STATE_FILENAME,
gate_mls_mod._default_binding_store,
)
persona_id = persona["identity"]["persona_id"]
stored["gates"][gate_id]["members"][persona_id]["binding_signature"] = "00" * 64
write_domain_json(gate_mls_mod.STATE_DOMAIN, gate_mls_mod.STATE_FILENAME, stored)
gate_mls_mod.reset_gate_mls_state()
with caplog.at_level(logging.WARNING):
retry = gate_mls_mod.compose_encrypted_gate_message(gate_id, "should rebuild")
assert retry["ok"] is True
assert "corrupted binding for gate#" in caplog.text.lower()
assert "member persona#" in caplog.text.lower()
def test_mls_compose_refuses_public_degraded_transport(tmp_path, monkeypatch):
from services import wormhole_supervisor
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.create_gate_persona("finance", label="scribe")
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "public_degraded")
result = gate_mls_mod.compose_encrypted_gate_message("finance", "should fail closed")
assert result == {
"ok": False,
"detail": "MLS gate compose requires PRIVATE transport tier",
}
def test_compose_endpoint_can_use_mls_without_changing_gate_post_envelope(tmp_path, monkeypatch):
import main
from httpx import ASGITransport, AsyncClient
from services.mesh import mesh_hashchain, mesh_reputation
from services import wormhole_supervisor
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.create_gate_persona("infonet", label="scribe")
monkeypatch.setattr(main, "_debug_mode_enabled", lambda: True)
class _Ledger:
def __init__(self):
self.registered = []
def register_node(self, *args):
self.registered.append(args)
class _GateManager:
def __init__(self):
self.recorded = []
self.enter_checks = []
def can_enter(self, sender_id, gate_id):
self.enter_checks.append((sender_id, gate_id))
return True, "ok"
def record_message(self, gate_id):
self.recorded.append(gate_id)
fake_ledger = _Ledger()
fake_gate_manager = _GateManager()
append_calls = []
def fake_append(gate_id, event):
append_calls.append({"gate_id": gate_id, "event": event})
return event
admin_headers = {"X-Admin-Key": main._current_admin_key()}
monkeypatch.setattr(main, "_preflight_signed_event_integrity", lambda **_: (True, "ok"))
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False},
)
monkeypatch.setattr(mesh_reputation, "reputation_ledger", fake_ledger, raising=False)
monkeypatch.setattr(mesh_reputation, "gate_manager", fake_gate_manager, raising=False)
monkeypatch.setattr(mesh_hashchain.gate_store, "append", fake_append)
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
compose_response = await ac.post(
"/api/wormhole/gate/message/compose",
json={"gate_id": "infonet", "plaintext": "field report"},
headers=admin_headers,
)
composed = compose_response.json()
send_response = await ac.post(
"/api/wormhole/gate/message/post",
json={"gate_id": "infonet", "plaintext": "field report"},
headers=admin_headers,
)
decrypt_response = await ac.post(
"/api/wormhole/gate/message/decrypt",
json={
"gate_id": "infonet",
"epoch": composed["epoch"],
"ciphertext": composed["ciphertext"],
"nonce": composed["nonce"],
"sender_ref": composed["sender_ref"],
"format": composed["format"],
},
headers=admin_headers,
)
return composed, send_response.json(), decrypt_response.json()
try:
composed, sent, decrypted = asyncio.run(_run())
finally:
gate_mls_mod.reset_gate_mls_state()
assert composed["ok"] is True
assert composed["format"] == "mls1"
assert len(base64.b64decode(composed["nonce"])) == 12
assert sent["ok"] is True
assert sent["detail"] == "Message posted to gate 'infonet'"
assert sent["gate_id"] == "infonet"
assert sent["event_id"] == append_calls[0]["event"]["event_id"]
assert decrypted["ok"] is True
assert decrypted["plaintext"] == "field report"
assert fake_gate_manager.enter_checks == [(append_calls[0]["event"]["node_id"], "infonet")]
assert fake_gate_manager.recorded == ["infonet"]
assert fake_ledger.registered == [
(
append_calls[0]["event"]["node_id"],
append_calls[0]["event"]["public_key"],
append_calls[0]["event"]["public_key_algo"],
)
]
assert append_calls[0]["gate_id"] == "infonet"
assert append_calls[0]["event"]["payload"]["gate"] == "infonet"
assert append_calls[0]["event"]["payload"]["format"] == "mls1"
assert append_calls[0]["event"]["payload"]["ciphertext"]
assert append_calls[0]["event"]["payload"]["nonce"]
assert append_calls[0]["event"]["payload"]["sender_ref"]
def test_receive_only_mls_decrypt_locks_gate_format(tmp_path, monkeypatch):
from services.mesh.mesh_secure_storage import read_domain_json, write_domain_json
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "receive-only-lab"
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona(gate_id, label="sender")
second = persona_mod.create_gate_persona(gate_id, label="receiver")
persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"])
composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "receiver should lock gate")
stored = read_domain_json(
gate_mls_mod.STATE_DOMAIN,
gate_mls_mod.STATE_FILENAME,
gate_mls_mod._default_binding_store,
)
stored.setdefault("gate_format_locks", {}).pop(gate_id, None)
write_domain_json(gate_mls_mod.STATE_DOMAIN, gate_mls_mod.STATE_FILENAME, stored)
assert gate_mls_mod.is_gate_locked_to_mls(gate_id) is True
persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"])
decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity(
gate_id=gate_id,
epoch=int(composed["epoch"]),
ciphertext=str(composed["ciphertext"]),
nonce=str(composed["nonce"]),
sender_ref=str(composed["sender_ref"]),
)
assert decrypted["ok"] is True
assert decrypted["plaintext"] == "receiver should lock gate"
assert gate_mls_mod.is_gate_locked_to_mls(gate_id) is True
def test_mls_locked_gate_rejects_legacy_g1_decrypt(tmp_path, monkeypatch):
import main
from httpx import ASGITransport, AsyncClient
from services import wormhole_supervisor
gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch)
gate_id = "lockout-lab"
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.create_gate_persona(gate_id, label="scribe")
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False},
)
composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "mls only")
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
response = await ac.post(
"/api/wormhole/gate/message/decrypt",
json={
"gate_id": gate_id,
"epoch": composed["epoch"],
"ciphertext": composed["ciphertext"],
"nonce": composed["nonce"],
"sender_ref": composed["sender_ref"],
"format": "g1",
},
headers={"X-Admin-Key": main._current_admin_key()},
)
return response.json()
try:
result = asyncio.run(_run())
finally:
gate_mls_mod.reset_gate_mls_state()
assert composed["ok"] is True
assert gate_mls_mod.is_gate_locked_to_mls(gate_id) is True
assert result == {
"ok": False,
"detail": "gate is locked to MLS format",
"gate_id": gate_id,
"required_format": "mls1",
"current_format": "g1",
}
@@ -0,0 +1,122 @@
import pytest
import base64
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
from services.mesh import mesh_crypto, mesh_hashchain, mesh_protocol
def _signed_event_fields(event_type: str, payload: dict, sequence: int, private_key=None):
priv = private_key or ed25519.Ed25519PrivateKey.generate()
pub = priv.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
public_key = base64.b64encode(pub).decode("utf-8")
node_id = mesh_crypto.derive_node_id(public_key)
normalized = mesh_protocol.normalize_payload(event_type, payload)
sig_payload = mesh_crypto.build_signature_payload(
event_type=event_type,
node_id=node_id,
sequence=sequence,
payload=normalized,
)
signature = priv.sign(sig_payload.encode("utf-8")).hex()
return {
"node_id": node_id,
"payload": normalized,
"signature": signature,
"public_key": public_key,
"public_key_algo": "Ed25519",
"protocol_version": mesh_protocol.PROTOCOL_VERSION,
"private_key": priv,
}
def test_infonet_sequence_enforced(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
inf = mesh_hashchain.Infonet()
evt1_fields = _signed_event_fields(
"message",
{"message": "hello", "destination": "broadcast"},
1,
)
evt = inf.append(
event_type="message",
node_id=evt1_fields["node_id"],
payload=evt1_fields["payload"],
signature=evt1_fields["signature"],
sequence=1,
public_key=evt1_fields["public_key"],
public_key_algo=evt1_fields["public_key_algo"],
protocol_version=evt1_fields["protocol_version"],
)
assert evt["payload"]["channel"] == "LongFast"
assert evt["payload"]["priority"] == "normal"
replay_fields = _signed_event_fields(
"message",
{"message": "replay", "destination": "broadcast", "channel": "LongFast"},
1,
)
with pytest.raises(ValueError):
inf.append(
event_type="message",
node_id=evt1_fields["node_id"],
payload=replay_fields["payload"],
signature=replay_fields["signature"],
sequence=1,
public_key=evt1_fields["public_key"],
public_key_algo=evt1_fields["public_key_algo"],
protocol_version=evt1_fields["protocol_version"],
)
out_of_order_fields = _signed_event_fields(
"message",
{"message": "out-of-order", "destination": "broadcast", "channel": "LongFast"},
1,
)
with pytest.raises(ValueError):
inf.append(
event_type="message",
node_id=evt1_fields["node_id"],
payload=out_of_order_fields["payload"],
signature=out_of_order_fields["signature"],
sequence=1,
public_key=evt1_fields["public_key"],
public_key_algo=evt1_fields["public_key_algo"],
protocol_version=evt1_fields["protocol_version"],
)
evt2_fields = _signed_event_fields(
"message",
{"message": "next", "destination": "broadcast", "channel": "LongFast"},
2,
private_key=evt1_fields["private_key"],
)
inf.append(
event_type="message",
node_id=evt1_fields["node_id"],
payload=evt2_fields["payload"],
signature=evt2_fields["signature"],
sequence=2,
public_key=evt1_fields["public_key"],
public_key_algo=evt1_fields["public_key_algo"],
protocol_version=evt1_fields["protocol_version"],
)
with pytest.raises(ValueError):
inf.append(
event_type="not_valid",
node_id=evt1_fields["node_id"],
payload={"message": "nope", "destination": "broadcast"},
signature=evt1_fields["signature"],
sequence=1,
public_key=evt1_fields["public_key"],
public_key_algo=evt1_fields["public_key_algo"],
protocol_version=evt1_fields["protocol_version"],
)
+38
View File
@@ -0,0 +1,38 @@
import hashlib
from services.mesh.mesh_ibf import IBLT, build_iblt
def _key(seed: str) -> bytes:
return hashlib.sha256(seed.encode("utf-8")).digest()
def test_iblt_reconcile_diff() -> None:
keys_a = [_key(f"a{i}") for i in range(20)]
keys_b = [_key(f"a{i}") for i in range(12)] + [_key(f"b{i}") for i in range(6)]
iblt_a = build_iblt(keys_a, size=64)
iblt_b = build_iblt(keys_b, size=64)
diff = iblt_a.subtract(iblt_b)
ok, plus, minus = diff.decode()
assert ok
plus_set = {p for p in plus}
minus_set = {m for m in minus}
assert plus_set == set(keys_a) - set(keys_b)
assert minus_set == set(keys_b) - set(keys_a)
def test_iblt_compact_roundtrip() -> None:
keys = [_key(f"x{i}") for i in range(15)]
iblt = build_iblt(keys, size=64)
packed = iblt.to_compact_dict()
iblt2 = IBLT.from_compact_dict(packed)
diff = iblt.subtract(iblt2)
ok, plus, minus = diff.decode()
assert ok
assert plus == []
assert minus == []
@@ -0,0 +1,263 @@
import base64
import json
import pytest
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
from services.mesh import mesh_hashchain, mesh_crypto, mesh_protocol, mesh_schema
def test_infonet_ingest_accepts_valid_event(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
inf = mesh_hashchain.Infonet()
priv = ed25519.Ed25519PrivateKey.generate()
pub = priv.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
pub_b64 = base64.b64encode(pub).decode("utf-8")
node_id = mesh_crypto.derive_node_id(pub_b64)
payload = mesh_protocol.normalize_payload(
"message",
{"message": "hello", "destination": "broadcast", "channel": "LongFast", "priority": "normal", "ephemeral": False},
)
sig_payload = mesh_crypto.build_signature_payload(
event_type="message", node_id=node_id, sequence=1, payload=payload
)
signature = priv.sign(sig_payload.encode("utf-8")).hex()
evt = mesh_hashchain.ChainEvent(
prev_hash=mesh_hashchain.GENESIS_HASH,
event_type="message",
node_id=node_id,
payload=payload,
sequence=1,
signature=signature,
public_key=pub_b64,
public_key_algo="Ed25519",
protocol_version=mesh_protocol.PROTOCOL_VERSION,
network_id=mesh_protocol.NETWORK_ID,
)
result = inf.ingest_events([evt.to_dict()])
assert result["accepted"] == 1
assert inf.head_hash == evt.event_id
def test_verify_node_binding_rejects_legacy_and_accepts_current_ids():
priv = ed25519.Ed25519PrivateKey.generate()
pub = priv.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
pub_b64 = base64.b64encode(pub).decode("utf-8")
current = mesh_crypto.derive_node_id(pub_b64)
legacy = f"{mesh_crypto.NODE_ID_PREFIX}{current[len(mesh_crypto.NODE_ID_PREFIX):len(mesh_crypto.NODE_ID_PREFIX) + 8]}"
assert mesh_crypto.verify_node_binding(current, pub_b64)
assert not mesh_crypto.verify_node_binding(legacy, pub_b64)
def test_infonet_append_rejects_missing_signature_fields(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
inf = mesh_hashchain.Infonet()
payload = mesh_protocol.normalize_payload(
"message",
{"message": "hello", "destination": "broadcast", "channel": "LongFast", "priority": "normal", "ephemeral": False},
)
try:
inf.append(
event_type="message",
node_id="!sb_test",
payload=payload,
sequence=1,
)
assert False, "Expected missing signature fields to be rejected"
except ValueError as exc:
assert "signature" in str(exc).lower()
def test_infonet_load_fails_closed_on_hash_mismatch(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
priv = ed25519.Ed25519PrivateKey.generate()
pub = priv.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
pub_b64 = base64.b64encode(pub).decode("utf-8")
node_id = mesh_crypto.derive_node_id(pub_b64)
payload = mesh_protocol.normalize_payload(
"message",
{"message": "hello", "destination": "broadcast", "channel": "LongFast", "priority": "normal", "ephemeral": False},
)
sig_payload = mesh_crypto.build_signature_payload(
event_type="message", node_id=node_id, sequence=1, payload=payload
)
signature = priv.sign(sig_payload.encode("utf-8")).hex()
evt = mesh_hashchain.ChainEvent(
prev_hash=mesh_hashchain.GENESIS_HASH,
event_type="message",
node_id=node_id,
payload=payload,
sequence=1,
signature=signature,
public_key=pub_b64,
public_key_algo="Ed25519",
protocol_version=mesh_protocol.PROTOCOL_VERSION,
network_id=mesh_protocol.NETWORK_ID,
).to_dict()
evt["event_id"] = "tampered"
mesh_hashchain.CHAIN_FILE.write_text(
json.dumps({"events": [evt], "head_hash": "tampered", "node_sequences": {node_id: 1}}),
encoding="utf-8",
)
with pytest.raises(ValueError, match="Hash mismatch on event load"):
mesh_hashchain.Infonet()
def test_validate_gate_message_payload_rejects_plaintext_shape():
payload = mesh_protocol.normalize_payload(
"gate_message",
{"gate": "infonet", "message": "plaintext should fail"},
)
valid, reason = mesh_schema.validate_event_payload("gate_message", payload)
assert valid is False
assert reason == "epoch must be a positive integer"
def test_gate_store_accepts_encrypted_gate_payload(tmp_path, monkeypatch):
priv = ed25519.Ed25519PrivateKey.generate()
pub = priv.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
pub_b64 = base64.b64encode(pub).decode("utf-8")
node_id = mesh_crypto.derive_node_id(pub_b64)
payload = mesh_protocol.normalize_payload(
"gate_message",
{
"gate": "infonet",
"epoch": 2,
"ciphertext": "opaque-ciphertext",
"nonce": "nonce-2",
"sender_ref": "persona-ops-1",
"format": "mls1",
},
)
sig_payload = mesh_crypto.build_signature_payload(
event_type="gate_message", node_id=node_id, sequence=1, payload=payload
)
signature = priv.sign(sig_payload.encode("utf-8")).hex()
store = mesh_hashchain.GateMessageStore(data_dir=str(tmp_path / "gate_messages"))
evt = store.append(
"infonet",
{
"event_id": "evt_gate_cipher",
"event_type": "gate_message",
"node_id": node_id,
"payload": payload,
"timestamp": 1_700_000_000.0,
"sequence": 1,
"signature": signature,
"public_key": pub_b64,
"public_key_algo": "Ed25519",
"protocol_version": mesh_protocol.PROTOCOL_VERSION,
},
)
assert evt["payload"]["gate"] == "infonet"
assert evt["payload"]["ciphertext"] == "opaque-ciphertext"
assert evt["payload"]["epoch"] == 2
assert evt["payload"]["nonce"] == "nonce-2"
assert evt["payload"]["sender_ref"] == "persona-ops-1"
def test_gate_store_rejects_replayed_ciphertext_across_append_and_peer_ingest(tmp_path):
store = mesh_hashchain.GateMessageStore(data_dir=str(tmp_path / "gate_messages"))
gate_id = "infonet"
first = store.append(
gate_id,
{
"event_id": "a" * 64,
"event_type": "gate_message",
"payload": {
"gate": gate_id,
"ciphertext": "opaque-ciphertext",
"format": "mls1",
},
"timestamp": float(int(mesh_hashchain.time.time() / 60) * 60),
},
)
replayed = store.append(
gate_id,
{
"event_id": "b" * 64,
"event_type": "gate_message",
"payload": {
"gate": gate_id,
"ciphertext": "opaque-ciphertext",
"format": "mls1",
},
"timestamp": float(int(mesh_hashchain.time.time() / 60) * 60) + 60.0,
},
)
peer_result = store.ingest_peer_events(
gate_id,
[
{
"event_type": "gate_message",
"timestamp": mesh_hashchain.time.time(),
"payload": {
"gate": gate_id,
"ciphertext": "opaque-ciphertext",
"format": "mls1",
},
}
],
)
assert replayed is first
assert peer_result == {"accepted": 0, "duplicates": 1, "rejected": 0}
assert len(store.get_messages(gate_id, limit=10)) == 1
def test_gate_store_prunes_stale_replay_fingerprints(tmp_path):
store = mesh_hashchain.GateMessageStore(data_dir=str(tmp_path / "gate_messages"))
old_ts = mesh_hashchain.time.time() - mesh_hashchain.GATE_REPLAY_WINDOW_S - 10
store.append(
"infonet",
{
"event_id": "c" * 64,
"event_type": "gate_message",
"payload": {
"gate": "infonet",
"ciphertext": "old-cipher",
"format": "mls1",
},
"timestamp": old_ts,
},
)
assert len(store._replay_index) == 1
removed = store._prune_replay_index(now=mesh_hashchain.time.time())
assert removed == 1
assert store._replay_index == {}
@@ -0,0 +1,75 @@
from services.mesh.mesh_infonet_sync_support import (
SyncWorkerState,
begin_sync,
eligible_sync_peers,
finish_sync,
should_run_sync,
)
from services.mesh.mesh_peer_store import make_bootstrap_peer_record, make_sync_peer_record
def test_eligible_sync_peers_filters_bucket_and_cooldown():
records = [
make_bootstrap_peer_record(
peer_url="https://seed.example",
transport="clearnet",
role="seed",
signer_id="bootstrap-a",
now=100,
),
make_sync_peer_record(
peer_url="https://active.example",
transport="clearnet",
now=100,
),
make_sync_peer_record(
peer_url="https://cooldown.example",
transport="clearnet",
now=100,
),
]
cooled = records[2]
records[2] = type(cooled)(**{**cooled.to_dict(), "cooldown_until": 500})
candidates = eligible_sync_peers(records, now=200)
assert [record.peer_url for record in candidates] == ["https://active.example"]
def test_finish_sync_success_updates_schedule():
state = begin_sync(SyncWorkerState(), peer_url="https://seed.example", now=100)
finished = finish_sync(
state,
ok=True,
peer_url="https://seed.example",
current_head="abc123",
now=110,
interval_s=300,
)
assert finished.last_outcome == "ok"
assert finished.last_sync_ok_at == 110
assert finished.next_sync_due_at == 410
assert finished.current_head == "abc123"
assert not finished.fork_detected
def test_finish_sync_failure_surfaces_fork_without_auto_merging():
state = begin_sync(SyncWorkerState(), peer_url="https://seed.example", now=100)
finished = finish_sync(
state,
ok=False,
peer_url="https://seed.example",
error="fork detected",
fork_detected=True,
now=120,
failure_backoff_s=45,
)
assert finished.last_outcome == "fork"
assert finished.fork_detected is True
assert finished.last_error == "fork detected"
assert finished.consecutive_failures == 1
assert finished.next_sync_due_at == 165
assert should_run_sync(finished, now=150) is False
assert should_run_sync(finished, now=165) is True
+163
View File
@@ -0,0 +1,163 @@
import base64
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
from services.mesh import mesh_crypto, mesh_hashchain, mesh_protocol
def _signed_event_fields(
event_type: str,
payload: dict,
sequence: int,
*,
private_key: ed25519.Ed25519PrivateKey | None = None,
):
priv = private_key or ed25519.Ed25519PrivateKey.generate()
pub = priv.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
pub_b64 = base64.b64encode(pub).decode("utf-8")
node_id = mesh_crypto.derive_node_id(pub_b64)
normalized = mesh_protocol.normalize_payload(event_type, payload)
sig_payload = mesh_crypto.build_signature_payload(
event_type=event_type,
node_id=node_id,
sequence=sequence,
payload=normalized,
)
signature = priv.sign(sig_payload.encode("utf-8")).hex()
return {
"node_id": node_id,
"payload": normalized,
"signature": signature,
"public_key": pub_b64,
"public_key_algo": "Ed25519",
"protocol_version": mesh_protocol.PROTOCOL_VERSION,
}
def test_chain_linkage_and_head(tmp_path, monkeypatch) -> None:
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
inf = mesh_hashchain.Infonet()
evt1_fields = _signed_event_fields(
"message",
{"message": "one", "destination": "broadcast", "channel": "LongFast", "priority": "normal", "ephemeral": False},
1,
)
evt1 = inf.append(
event_type="message",
node_id=evt1_fields["node_id"],
payload=evt1_fields["payload"],
signature=evt1_fields["signature"],
sequence=1,
public_key=evt1_fields["public_key"],
public_key_algo=evt1_fields["public_key_algo"],
protocol_version=evt1_fields["protocol_version"],
)
evt2_fields = _signed_event_fields(
"message",
{"message": "two", "destination": "broadcast", "channel": "LongFast", "priority": "normal", "ephemeral": False},
2,
)
evt2 = inf.append(
event_type="message",
node_id=evt2_fields["node_id"],
payload=evt2_fields["payload"],
signature=evt2_fields["signature"],
sequence=2,
public_key=evt2_fields["public_key"],
public_key_algo=evt2_fields["public_key_algo"],
protocol_version=evt2_fields["protocol_version"],
)
assert evt1["prev_hash"] == mesh_hashchain.GENESIS_HASH
assert evt2["prev_hash"] == evt1["event_id"]
assert inf.head_hash == evt2["event_id"]
def test_ingest_rejects_non_normalized_payload(tmp_path, monkeypatch) -> None:
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
inf = mesh_hashchain.Infonet()
evt = mesh_hashchain.ChainEvent(
prev_hash=mesh_hashchain.GENESIS_HASH,
event_type="message",
node_id="!sb_test",
payload={"message": "hi", "destination": "broadcast"},
sequence=1,
signature="deadbeef",
public_key="pub",
public_key_algo="Ed25519",
protocol_version=mesh_protocol.PROTOCOL_VERSION,
network_id=mesh_protocol.NETWORK_ID,
)
result = inf.ingest_events([evt.to_dict()])
assert result["accepted"] == 0
assert result["rejected"]
assert "normalized" in result["rejected"][0]["reason"].lower()
def test_revoked_key_rejects_future_events(tmp_path, monkeypatch) -> None:
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
inf = mesh_hashchain.Infonet()
now = int(mesh_hashchain.time.time())
revoke_priv = ed25519.Ed25519PrivateKey.generate()
revoked_payload = {
"revoked_public_key": "",
"revoked_public_key_algo": "Ed25519",
"revoked_at": now - 10,
"grace_until": now - 10,
"reason": "compromised",
}
revoke_fields = _signed_event_fields(
"key_revoke",
revoked_payload,
1,
private_key=revoke_priv,
)
revoked_payload["revoked_public_key"] = revoke_fields["public_key"]
revoke_fields = _signed_event_fields(
"key_revoke",
revoked_payload,
1,
private_key=revoke_priv,
)
inf.append(
event_type="key_revoke",
node_id=revoke_fields["node_id"],
payload=revoked_payload,
signature=revoke_fields["signature"],
sequence=1,
public_key=revoke_fields["public_key"],
public_key_algo=revoke_fields["public_key_algo"],
protocol_version=revoke_fields["protocol_version"],
)
msg_fields = _signed_event_fields(
"message",
{"message": "blocked", "destination": "broadcast", "channel": "LongFast", "priority": "normal", "ephemeral": False},
2,
private_key=revoke_priv,
)
try:
inf.append(
event_type="message",
node_id=revoke_fields["node_id"],
payload=msg_fields["payload"],
signature=msg_fields["signature"],
sequence=2,
public_key=revoke_fields["public_key"],
public_key_algo=revoke_fields["public_key_algo"],
protocol_version=revoke_fields["protocol_version"],
)
assert False, "Expected revocation to block new events"
except ValueError as exc:
assert "revoked" in str(exc).lower()
+79
View File
@@ -0,0 +1,79 @@
import base64
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
from services.mesh import mesh_crypto, mesh_hashchain, mesh_protocol
def _signed_event_fields(event_type: str, payload: dict, sequence: int, private_key=None):
priv = private_key or ed25519.Ed25519PrivateKey.generate()
pub = priv.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
public_key = base64.b64encode(pub).decode("utf-8")
node_id = mesh_crypto.derive_node_id(public_key)
normalized = mesh_protocol.normalize_payload(event_type, payload)
sig_payload = mesh_crypto.build_signature_payload(
event_type=event_type,
node_id=node_id,
sequence=sequence,
payload=normalized,
)
signature = priv.sign(sig_payload.encode("utf-8")).hex()
return {
"node_id": node_id,
"payload": normalized,
"signature": signature,
"public_key": public_key,
"public_key_algo": "Ed25519",
"protocol_version": mesh_protocol.PROTOCOL_VERSION,
"private_key": priv,
}
def test_locator_sync(tmp_path, monkeypatch) -> None:
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
inf = mesh_hashchain.Infonet()
evt1_fields = _signed_event_fields(
"message",
{"message": "hello", "destination": "broadcast"},
1,
)
evt1 = inf.append(
event_type="message",
node_id=evt1_fields["node_id"],
payload=evt1_fields["payload"],
signature=evt1_fields["signature"],
sequence=1,
public_key=evt1_fields["public_key"],
public_key_algo=evt1_fields["public_key_algo"],
protocol_version=evt1_fields["protocol_version"],
)
evt2_fields = _signed_event_fields(
"message",
{"message": "world", "destination": "broadcast"},
2,
private_key=evt1_fields["private_key"],
)
evt2 = inf.append(
event_type="message",
node_id=evt1_fields["node_id"],
payload=evt2_fields["payload"],
signature=evt2_fields["signature"],
sequence=2,
public_key=evt1_fields["public_key"],
public_key_algo=evt1_fields["public_key_algo"],
protocol_version=evt1_fields["protocol_version"],
)
locator = inf.get_locator()
assert locator[0] == evt2["event_id"]
matched, _start, events = inf.get_events_after_locator([evt1["event_id"]], limit=10)
assert matched == evt1["event_id"]
assert len(events) == 1
assert events[0]["event_id"] == evt2["event_id"]
+18
View File
@@ -0,0 +1,18 @@
import json
from pathlib import Path
from services.mesh.mesh_merkle import merkle_root, verify_merkle_proof
def test_merkle_fixtures() -> None:
root_dir = Path(__file__).resolve().parents[3]
fixture_path = root_dir / "docs" / "mesh" / "mesh-merkle-fixtures.json"
fixtures = json.loads(fixture_path.read_text(encoding="utf-8"))
leaves = fixtures["leaves"]
root = fixtures["root"]
assert merkle_root(leaves) == root
for idx_str, proof in fixtures["proofs"].items():
idx = int(idx_str)
assert verify_merkle_proof(leaves[idx], idx, proof, root)
@@ -0,0 +1,136 @@
import asyncio
import base64
import json
from types import SimpleNamespace
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
from httpx import ASGITransport, AsyncClient
def _write_signed_manifest(path, *, private_key):
from services.mesh.mesh_bootstrap_manifest import BOOTSTRAP_MANIFEST_VERSION
from services.mesh.mesh_crypto import canonical_json
payload = {
"version": BOOTSTRAP_MANIFEST_VERSION,
"issued_at": 1_700_000_000,
"valid_until": 1_800_000_000,
"signer_id": "bootstrap-a",
"peers": [
{
"peer_url": "https://seed.example",
"transport": "clearnet",
"role": "seed",
"label": "Seed A",
}
],
}
signature = base64.b64encode(private_key.sign(canonical_json(payload).encode("utf-8"))).decode("utf-8")
path.write_text(json.dumps({**payload, "signature": signature}), encoding="utf-8")
def test_refresh_node_peer_store_promotes_manifest_peers_to_sync_only(tmp_path, monkeypatch):
import main
from services.config import get_settings
from services.mesh import mesh_bootstrap_manifest as manifest_mod
from services.mesh import mesh_peer_store as peer_store_mod
manifest_key = ed25519.Ed25519PrivateKey.generate()
manifest_pub = base64.b64encode(
manifest_key.public_key().public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
)
).decode("utf-8")
manifest_path = tmp_path / "bootstrap.json"
peer_store_path = tmp_path / "peer_store.json"
_write_signed_manifest(manifest_path, private_key=manifest_key)
monkeypatch.setattr(manifest_mod, "DEFAULT_BOOTSTRAP_MANIFEST_PATH", manifest_path)
monkeypatch.setattr(peer_store_mod, "DEFAULT_PEER_STORE_PATH", peer_store_path)
monkeypatch.setenv("MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", manifest_pub)
monkeypatch.setenv("MESH_BOOTSTRAP_MANIFEST_PATH", str(manifest_path))
monkeypatch.setenv("MESH_RELAY_PEERS", "https://operator.example")
get_settings.cache_clear()
try:
snapshot = main._refresh_node_peer_store(now=1_750_000_000)
store = peer_store_mod.PeerStore(peer_store_path)
store.load()
finally:
get_settings.cache_clear()
assert snapshot["manifest_loaded"] is True
assert snapshot["bootstrap_peer_count"] == 1
assert snapshot["sync_peer_count"] == 2
assert snapshot["push_peer_count"] == 1
assert [record.peer_url for record in store.records_for_bucket("bootstrap")] == ["https://seed.example"]
assert sorted(record.peer_url for record in store.records_for_bucket("sync")) == [
"https://operator.example",
"https://seed.example",
]
assert [record.peer_url for record in store.records_for_bucket("push")] == ["https://operator.example"]
def test_verify_peer_push_hmac_requires_allowlisted_peer(monkeypatch):
import hashlib
import hmac
import main
from services.config import get_settings
from services.mesh.mesh_crypto import _derive_peer_key
monkeypatch.setenv("MESH_PEER_PUSH_SECRET", "shared-secret")
get_settings.cache_clear()
monkeypatch.setattr(main, "authenticated_push_peer_urls", lambda *args, **kwargs: ["https://good.example"])
try:
body = b'{"events":[]}'
peer_url = "https://bad.example"
peer_key = _derive_peer_key("shared-secret", peer_url)
signature = hmac.new(peer_key, body, hashlib.sha256).hexdigest()
request = SimpleNamespace(
headers={"x-peer-url": peer_url, "x-peer-hmac": signature},
url=SimpleNamespace(scheme="https", netloc="bad.example"),
)
assert main._verify_peer_push_hmac(request, body) is False
finally:
get_settings.cache_clear()
def test_infonet_status_includes_node_runtime_snapshot(monkeypatch):
import main
from services import wormhole_supervisor
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (True, "ok"))
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False},
)
monkeypatch.setattr(
main,
"_node_runtime_snapshot",
lambda: {
"node_mode": "participant",
"node_enabled": True,
"bootstrap": {"sync_peer_count": 2, "push_peer_count": 1},
"sync_runtime": {"last_outcome": "ok"},
"push_runtime": {"last_event_id": "evt-1"},
},
)
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
response = await ac.get("/api/mesh/infonet/status")
return response.json()
result = asyncio.run(_run())
assert result["node_mode"] == "participant"
assert result["node_enabled"] is True
assert result["bootstrap"]["sync_peer_count"] == 2
assert result["bootstrap"]["push_peer_count"] == 1
assert result["sync_runtime"]["last_outcome"] == "ok"
assert result["push_runtime"]["last_event_id"] == "evt-1"
@@ -0,0 +1,98 @@
from services.mesh.mesh_peer_store import (
PeerStore,
make_bootstrap_peer_record,
make_push_peer_record,
make_sync_peer_record,
)
def test_peer_store_preserves_provenance_across_buckets(tmp_path):
store = PeerStore(tmp_path / "peer_store.json")
bootstrap = make_bootstrap_peer_record(
peer_url="https://seed.example",
transport="clearnet",
role="seed",
signer_id="bootstrap-a",
now=100,
)
sync_peer = make_sync_peer_record(
peer_url="https://seed.example",
transport="clearnet",
role="seed",
source="bootstrap_promoted",
signer_id="bootstrap-a",
now=101,
)
push_peer = make_push_peer_record(
peer_url="https://seed.example",
transport="clearnet",
role="seed",
now=102,
)
store.upsert(bootstrap)
store.upsert(sync_peer)
store.upsert(push_peer)
assert [record.bucket for record in store.records()] == ["bootstrap", "push", "sync"]
assert [record.source for record in store.records_for_bucket("sync")] == ["bootstrap_promoted"]
assert [record.source for record in store.records_for_bucket("push")] == ["operator"]
def test_peer_store_save_load_roundtrip(tmp_path):
path = tmp_path / "peer_store.json"
store = PeerStore(path)
store.upsert(
make_bootstrap_peer_record(
peer_url="https://seed.example",
transport="clearnet",
role="seed",
signer_id="bootstrap-a",
now=100,
)
)
store.upsert(
make_sync_peer_record(
peer_url="http://alphaexample.onion",
transport="onion",
role="relay",
source="operator",
now=101,
)
)
store.save()
loaded = PeerStore(path)
records = loaded.load()
assert len(records) == 2
assert [record.bucket for record in records] == ["bootstrap", "sync"]
assert records[0].signer_id == "bootstrap-a"
assert records[1].peer_url == "http://alphaexample.onion"
def test_peer_store_failure_and_success_lifecycle(tmp_path):
store = PeerStore(tmp_path / "peer_store.json")
store.upsert(
make_sync_peer_record(
peer_url="https://sync.example",
transport="clearnet",
now=100,
)
)
failed = store.mark_failure(
"https://sync.example",
"sync",
error="timeout",
cooldown_s=30,
now=200,
)
assert failed.failure_count == 1
assert failed.cooldown_until == 230
assert failed.last_error == "timeout"
recovered = store.mark_sync_success("https://sync.example", now=250)
assert recovered.failure_count == 0
assert recovered.cooldown_until == 0
assert recovered.last_error == ""
assert recovered.last_sync_ok_at == 250
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,85 @@
import asyncio
def test_x3dh_hkdf_uses_nonzero_ff_salt():
from services.mesh.mesh_wormhole_prekey import _hkdf
derived = _hkdf(b"input-material", "SB-TEST")
assert derived
assert derived != _hkdf(b"input-material", "SB-TEST-ALT")
def test_ratchet_padding_extends_large_payloads():
from services.mesh.mesh_wormhole_ratchet import _build_padded_payload, PAD_MAGIC, PAD_STEP
plaintext = "x" * 5000
padded = _build_padded_payload(plaintext)
assert padded[:4].decode("utf-8") == PAD_MAGIC
assert len(padded) > len(plaintext.encode("utf-8"))
assert len(padded) % PAD_STEP == 0
def test_dead_drop_epoch_shortens_in_high_privacy(monkeypatch):
from services.mesh import mesh_wormhole_dead_drop
monkeypatch.setattr(
mesh_wormhole_dead_drop,
"read_wormhole_settings",
lambda: {"privacy_profile": "high"},
)
assert mesh_wormhole_dead_drop.mailbox_epoch_seconds() == 2 * 60 * 60
def test_relay_jitter_only_applies_in_high_privacy(monkeypatch):
import main
sleeps: list[float] = []
async def fake_sleep(delay: float):
sleeps.append(delay)
monkeypatch.setattr(main, "_high_privacy_profile_enabled", lambda: True)
monkeypatch.setattr(main.asyncio, "sleep", fake_sleep)
asyncio.run(main._maybe_apply_dm_relay_jitter())
assert len(sleeps) == 1
assert 0.05 <= sleeps[0] <= 0.5
def test_high_privacy_refuses_private_tier_clearnet_fallback(monkeypatch):
from services.mesh.mesh_router import MeshEnvelope, MeshRouter, Priority, TransportResult
router = MeshRouter()
internet_attempts: list[str] = []
monkeypatch.setattr(
"services.mesh.mesh_router._high_privacy_profile_blocks_clearnet_fallback",
lambda: True,
)
monkeypatch.setattr(router.tor_arti, "can_reach", lambda _envelope: False)
monkeypatch.setattr(
router.internet,
"send",
lambda *_args, **_kwargs: (
internet_attempts.append("internet"),
TransportResult(True, "internet", "sent"),
)[1],
)
results = router.route(
MeshEnvelope(
sender_id="!sb_sender",
destination="!sb_dest",
payload="ciphertext",
trust_tier="private_transitional",
priority=Priority.NORMAL,
),
{},
)
assert internet_attempts == []
assert len(results) == 1
assert results[0].transport == "policy"
assert "clearnet fallback refused" in results[0].detail
@@ -0,0 +1,296 @@
import asyncio
import time
from collections import deque
from types import SimpleNamespace
class _DummyBreaker:
def check_and_record(self, _priority):
return True, "ok"
class _FakeMeshtasticTransport:
NAME = "meshtastic"
def __init__(self, can_reach: bool = True, send_ok: bool = True):
self._can_reach = can_reach
self._send_ok = send_ok
self.sent = []
def can_reach(self, _envelope):
return self._can_reach
def send(self, envelope, _credentials):
from services.mesh.mesh_router import TransportResult
self.sent.append(envelope)
return TransportResult(self._send_ok, self.NAME, "sent")
class _FakeMeshRouter:
def __init__(self, meshtastic):
self.meshtastic = meshtastic
self.breakers = {"meshtastic": _DummyBreaker()}
self.route_called = False
def route(self, _envelope, _credentials):
self.route_called = True
return []
def _valid_body(**overrides):
body = {
"destination": "!a0cc7a80",
"message": "hello mesh",
"sender_id": "!sb_sender",
"node_id": "!sb_sender",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 1,
"protocol_version": "1",
"channel": "LongFast",
"priority": "normal",
"ephemeral": False,
"transport_lock": "meshtastic",
"credentials": {"mesh_region": "US"},
}
body.update(overrides)
return body
def test_meshtastic_transport_lock_stays_on_public_direct_path(monkeypatch):
import main
from services.mesh import mesh_router as mesh_router_mod
from services.sigint_bridge import sigint_grid
from httpx import ASGITransport, AsyncClient
fake_meshtastic = _FakeMeshtasticTransport(can_reach=True, send_ok=True)
fake_router = _FakeMeshRouter(fake_meshtastic)
fake_bridge = SimpleNamespace(messages=deque(maxlen=10))
monkeypatch.setattr(main, "_verify_signed_event", lambda **_: (True, "ok"))
monkeypatch.setattr(main, "_preflight_signed_event_integrity", lambda **_: (True, "ok"))
monkeypatch.setattr(main, "_check_throttle", lambda *_: (True, "ok"))
monkeypatch.setattr(mesh_router_mod, "mesh_router", fake_router)
monkeypatch.setattr(sigint_grid, "mesh", fake_bridge)
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
response = await ac.post("/api/mesh/send", json=_valid_body())
return response.json()
result = asyncio.run(_run())
assert result["ok"] is True
assert result["routed_via"] == "meshtastic"
assert "public node-targeted path" in result["route_reason"]
assert fake_router.route_called is False
assert len(fake_meshtastic.sent) == 1
assert fake_meshtastic.sent[0].destination == "!a0cc7a80"
assert fake_bridge.messages[0]["from"] == "!0779e8b8"
def test_meshtastic_transport_lock_does_not_fallback_when_unreachable(monkeypatch):
import main
from services.mesh import mesh_router as mesh_router_mod
from httpx import ASGITransport, AsyncClient
fake_meshtastic = _FakeMeshtasticTransport(can_reach=False, send_ok=False)
fake_router = _FakeMeshRouter(fake_meshtastic)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_: (True, "ok"))
monkeypatch.setattr(main, "_preflight_signed_event_integrity", lambda **_: (True, "ok"))
monkeypatch.setattr(main, "_check_throttle", lambda *_: (True, "ok"))
monkeypatch.setattr(mesh_router_mod, "mesh_router", fake_router)
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
response = await ac.post("/api/mesh/send", json=_valid_body(message="x" * 10))
return response.json()
result = asyncio.run(_run())
assert result["ok"] is False
assert result["routed_via"] == ""
assert fake_router.route_called is False
assert fake_meshtastic.sent == []
assert result["results"] == [
{
"ok": False,
"transport": "meshtastic",
"detail": "Message exceeds Meshtastic payload limit",
}
]
def test_meshtastic_transport_lock_allows_two_messages_per_minute(monkeypatch):
import main
node_id = "!sb_meshrate"
now = time.time()
main._node_throttle[node_id] = {
"last_send": now - 31,
"daily_count": 0,
"daily_reset": now,
"first_seen": now,
}
ok_first, _reason_first = main._check_throttle(node_id, "normal", "meshtastic")
ok_second, reason_second = main._check_throttle(node_id, "normal", "meshtastic")
assert ok_first is True
assert ok_second is False
assert "1 message per 30s" in reason_second
def test_private_trust_tier_skips_public_transports():
from services.mesh.mesh_router import MeshEnvelope, MeshRouter, Priority, TransportResult
class _FakeTransport:
def __init__(self, name):
self.NAME = name
self.sent = []
def can_reach(self, _envelope):
return True
def send(self, envelope, _credentials):
self.sent.append(envelope)
return TransportResult(True, self.NAME, "sent")
router = MeshRouter()
router.aprs = _FakeTransport("aprs")
router.meshtastic = _FakeTransport("meshtastic")
router.internet = _FakeTransport("internet")
router.transports = [router.aprs, router.meshtastic, router.internet]
envelope = MeshEnvelope(
sender_id="!sb_sender",
destination="broadcast",
priority=Priority.NORMAL,
payload="private payload",
trust_tier="private_strong",
)
results = router.route(envelope, {})
assert [r.transport for r in results] == ["policy"]
assert router.aprs.sent == []
assert router.meshtastic.sent == []
assert len(router.internet.sent) == 0
def test_private_route_recognizes_tor_arti_and_falls_back_to_internet():
from services.mesh.mesh_router import MeshEnvelope, MeshRouter, Priority, TransportResult
class _FakeTransport:
def __init__(self, name, ok=True):
self.NAME = name
self.ok = ok
self.sent = []
def can_reach(self, _envelope):
return True
def send(self, envelope, _credentials):
self.sent.append(envelope)
return TransportResult(self.ok, self.NAME, "sent" if self.ok else "stub")
router = MeshRouter()
router.aprs = _FakeTransport("aprs")
router.meshtastic = _FakeTransport("meshtastic")
router.tor_arti = _FakeTransport("tor_arti", ok=False)
router.internet = _FakeTransport("internet", ok=True)
router.transports = [router.aprs, router.meshtastic, router.tor_arti, router.internet]
envelope = MeshEnvelope(
sender_id="!sb_sender",
destination="broadcast",
priority=Priority.NORMAL,
payload="private payload",
trust_tier="private_strong",
)
results = router.route(envelope, {})
assert [r.transport for r in results] == ["tor_arti", "policy"]
assert router.aprs.sent == []
assert router.meshtastic.sent == []
assert len(router.tor_arti.sent) == 1
assert len(router.internet.sent) == 0
def test_private_tier_blocks_meshtastic_transport_lock(monkeypatch):
"""C-2 fix: transport_lock=meshtastic must be rejected when trust_tier is private."""
import main
from services.mesh import mesh_router as mesh_router_mod
from services import wormhole_supervisor
from httpx import ASGITransport, AsyncClient
fake_meshtastic = _FakeMeshtasticTransport(can_reach=True, send_ok=True)
fake_router = _FakeMeshRouter(fake_meshtastic)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_: (True, "ok"))
monkeypatch.setattr(main, "_preflight_signed_event_integrity", lambda **_: (True, "ok"))
monkeypatch.setattr(main, "_check_throttle", lambda *_: (True, "ok"))
monkeypatch.setattr(mesh_router_mod, "mesh_router", fake_router)
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
response = await ac.post("/api/mesh/send", json=_valid_body())
return response.json()
result = asyncio.run(_run())
assert result["ok"] is False
assert "Private-tier content cannot be sent over Meshtastic" in result["results"][0]["detail"]
assert fake_meshtastic.sent == []
assert fake_router.route_called is False
def test_envelope_trust_tier_set_from_wormhole_state(monkeypatch):
"""C-1 fix: MeshEnvelope.trust_tier must reflect actual Wormhole transport tier."""
import main
from services.mesh import mesh_router as mesh_router_mod
from services import wormhole_supervisor
from services.sigint_bridge import sigint_grid
from httpx import ASGITransport, AsyncClient
captured_envelopes = []
class _CapturingRouter:
def __init__(self):
self.meshtastic = _FakeMeshtasticTransport(can_reach=True, send_ok=True)
self.breakers = {"meshtastic": _DummyBreaker()}
def route(self, envelope, _credentials):
from services.mesh.mesh_router import TransportResult
captured_envelopes.append(envelope)
return [TransportResult(True, "internet", "sent")]
fake_router = _CapturingRouter()
fake_bridge = SimpleNamespace(messages=deque(maxlen=10))
monkeypatch.setattr(main, "_verify_signed_event", lambda **_: (True, "ok"))
monkeypatch.setattr(main, "_preflight_signed_event_integrity", lambda **_: (True, "ok"))
monkeypatch.setattr(main, "_check_throttle", lambda *_: (True, "ok"))
monkeypatch.setattr(mesh_router_mod, "mesh_router", fake_router)
monkeypatch.setattr(sigint_grid, "mesh", fake_bridge)
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")
body = _valid_body()
del body["transport_lock"] # no lock — use normal routing
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
response = await ac.post("/api/mesh/send", json=body)
return response.json()
result = asyncio.run(_run())
assert result["ok"] is True
assert len(captured_envelopes) == 1
assert captured_envelopes[0].trust_tier == "private_transitional"
@@ -0,0 +1,158 @@
import json
import time
from services.mesh import mesh_reputation, mesh_secure_storage
def test_identity_link_merges_reputation(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_reputation, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_reputation, "LEDGER_FILE", tmp_path / "rep.json")
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
ledger = mesh_reputation.ReputationLedger()
now = time.time()
ledger.votes = [
{
"voter_id": "!sb_v1",
"target_id": "!sb_old",
"vote": 1,
"gate": "",
"timestamp": now,
"weight": 1.0,
"agent_verify": False,
},
{
"voter_id": "!sb_v2",
"target_id": "!sb_new",
"vote": 1,
"gate": "",
"timestamp": now,
"weight": 1.0,
"agent_verify": False,
},
]
ledger._scores_dirty = True
ok, _ = ledger.link_identities("!sb_old", "!sb_new")
assert ok is True
rep = ledger.get_reputation("!sb_new")
assert rep["overall"] == 2
assert "linked_from" not in rep
assert ledger.aliases["!sb_new"] == "!sb_old"
def test_reputation_ledger_is_encrypted_at_rest(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_reputation, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_reputation, "LEDGER_FILE", tmp_path / "reputation_ledger.json")
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
ledger = mesh_reputation.ReputationLedger()
ledger.register_node("!sb_voter")
ledger.register_node("!sb_target")
ok, _reason = ledger.cast_vote("!sb_voter", "!sb_target", 1)
assert ok is True
ledger._flush()
domain_path = tmp_path / mesh_reputation.LEDGER_DOMAIN / mesh_reputation.LEDGER_FILE.name
raw = domain_path.read_text(encoding="utf-8")
assert '"kind": "sb_secure_json"' in raw
assert domain_path.exists()
assert not mesh_reputation.LEDGER_FILE.exists()
assert "!sb_voter" not in raw
assert "!sb_target" not in raw
def test_reputation_votes_are_blinded_inside_encrypted_ledger(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_reputation, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_reputation, "LEDGER_FILE", tmp_path / "reputation_ledger.json")
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
ledger = mesh_reputation.ReputationLedger()
ledger.register_node("!sb_voter")
ledger.register_node("!sb_target")
ok, _reason = ledger.cast_vote("!sb_voter", "!sb_target", 1)
assert ok is True
ledger._flush()
stored = mesh_secure_storage.read_domain_json(
mesh_reputation.LEDGER_DOMAIN,
mesh_reputation.LEDGER_FILE.name,
lambda: {},
)
vote = stored["votes"][0]
assert "voter_id" not in vote
assert vote["blinded_voter_id"]
def test_reputation_duplicate_same_direction_vote_is_rejected(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_reputation, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_reputation, "LEDGER_FILE", tmp_path / "reputation_ledger.json")
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
ledger = mesh_reputation.ReputationLedger()
ledger.register_node("!sb_voter")
ledger.register_node("!sb_target")
ok, reason = ledger.cast_vote("!sb_voter", "!sb_target", 1, "infonet")
assert ok is True
assert "Voted up" in reason
assert len(ledger.votes) == 1
ok, reason = ledger.cast_vote("!sb_voter", "!sb_target", 1, "infonet")
assert ok is False
assert reason == "Vote already set to up on !sb_target in gate 'infonet'"
assert len(ledger.votes) == 1
def test_reputation_vote_direction_can_change_without_creating_duplicates(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_reputation, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_reputation, "LEDGER_FILE", tmp_path / "reputation_ledger.json")
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
ledger = mesh_reputation.ReputationLedger()
ledger.register_node("!sb_voter")
ledger.register_node("!sb_target")
ok, _reason = ledger.cast_vote("!sb_voter", "!sb_target", 1, "infonet")
assert ok is True
assert len(ledger.votes) == 1
ok, reason = ledger.cast_vote("!sb_voter", "!sb_target", -1, "infonet")
assert ok is True
assert "Voted down" in reason
assert len(ledger.votes) == 1
assert ledger.votes[0]["vote"] == -1
def test_gate_catalog_is_domain_encrypted_with_legacy_migration(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_reputation, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_reputation, "GATES_FILE", tmp_path / "gates.json")
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
legacy = {"ops": {"display_name": "Ops", "fixed": False}}
mesh_reputation.GATES_FILE.write_text(json.dumps(legacy), encoding="utf-8")
manager = mesh_reputation.GateManager(mesh_reputation.ReputationLedger())
domain_path = tmp_path / mesh_reputation.GATES_DOMAIN / mesh_reputation.GATES_FILE.name
stored = mesh_secure_storage.read_domain_json(
mesh_reputation.GATES_DOMAIN,
mesh_reputation.GATES_FILE.name,
lambda: {},
)
assert domain_path.exists()
assert not mesh_reputation.GATES_FILE.exists()
assert stored["ops"]["display_name"] == "Ops"
assert manager.gates["ops"]["display_name"] == "Ops"
@@ -0,0 +1,421 @@
import base64
import json
import random
import threading
import time
import uuid
from types import SimpleNamespace
from services.mesh.mesh_hashchain import infonet
from services.mesh.mesh_rns import RNSBridge
def _xor_parity(data: list[bytes]) -> bytes:
parity = data[0]
for shard in data[1:]:
parity = bytes(a ^ b for a, b in zip(parity, shard))
return parity
def _make_shard_body(
shard_id: str,
index: int,
total: int,
data_shards: int,
parity_shards: int,
size: int,
length: int,
parity: bool,
fec: str,
blob: bytes,
) -> dict:
return {
"shard_id": shard_id,
"index": index,
"total": total,
"data_shards": data_shards,
"parity_shards": parity_shards,
"size": size,
"length": length,
"parity": parity,
"fec": fec,
"data": base64.b64encode(blob).decode("ascii"),
}
def test_rns_quorum_thread_safety(monkeypatch) -> None:
bridge = RNSBridge()
sync_id = "sync-test"
head_hash = infonet.head_hash or "head"
with bridge._sync_lock:
bridge._pending_sync[sync_id] = {
"created": time.time(),
"expected": set(),
"quorum": 2,
"responses": {},
"responders": set(),
}
ingested: list[list[dict]] = []
def _fake_ingest(events: list[dict]) -> None:
ingested.append(events)
monkeypatch.setattr(bridge, "_ingest_ordered", _fake_ingest)
threads = []
for idx in range(4):
meta = {"sync_id": sync_id, "head_hash": head_hash, "reply_to": f"peer-{idx}"}
t = threading.Thread(
target=bridge._ingest_with_quorum, args=([{"event_id": f"e{idx}"}], meta)
)
threads.append(t)
t.start()
for t in threads:
t.join()
assert sync_id not in bridge._pending_sync
assert ingested
def test_rns_shard_reassembly_thread_safety(monkeypatch) -> None:
bridge = RNSBridge()
payload = b"mesh-concurrency" * 256
data_shards = 4
data, length = bridge._split_payload(payload, data_shards)
size = len(data[0])
parity = _xor_parity(data)
shard_id = uuid.uuid4().hex
total = data_shards + 1
bodies = [
_make_shard_body(
shard_id,
idx,
total,
data_shards,
1,
size,
length,
False,
"xor",
shard,
)
for idx, shard in enumerate(data)
]
bodies.append(
_make_shard_body(
shard_id,
data_shards,
total,
data_shards,
1,
size,
length,
True,
"xor",
parity,
)
)
random.shuffle(bodies)
assembled: list[bytes] = []
def _fake_on_packet(data: bytes, packet=None) -> None:
assembled.append(data)
monkeypatch.setattr(bridge, "_on_packet", _fake_on_packet)
threads = [threading.Thread(target=bridge._handle_infonet_shard, args=(body,)) for body in bodies]
for t in threads:
t.start()
for t in threads:
t.join()
assert assembled
assert assembled[-1] == payload
def test_rns_shard_reassembly_with_loss_and_delay(monkeypatch) -> None:
bridge = RNSBridge()
payload = b"mesh-loss-delay" * 256
data_shards = 5
data, length = bridge._split_payload(payload, data_shards)
size = len(data[0])
parity = _xor_parity(data)
shard_id = uuid.uuid4().hex
total = data_shards + 1
bodies = [
_make_shard_body(
shard_id,
idx,
total,
data_shards,
1,
size,
length,
False,
"xor",
shard,
)
for idx, shard in enumerate(data)
]
bodies.append(
_make_shard_body(
shard_id,
data_shards,
total,
data_shards,
1,
size,
length,
True,
"xor",
parity,
)
)
rng = random.Random(1337)
drop_index = rng.randrange(data_shards)
bodies = [b for b in bodies if not (not b["parity"] and b["index"] == drop_index)]
rng.shuffle(bodies)
assembled: list[bytes] = []
def _fake_on_packet(data: bytes, packet=None) -> None:
assembled.append(data)
monkeypatch.setattr(bridge, "_on_packet", _fake_on_packet)
def _deliver(body: dict) -> None:
time.sleep(rng.uniform(0.0, 0.03))
bridge._handle_infonet_shard(body)
threads = [threading.Thread(target=_deliver, args=(body,)) for body in bodies]
for t in threads:
t.start()
for t in threads:
t.join()
assert assembled
assert assembled[-1] == payload
def test_rns_publish_gate_event_freezes_current_v1_signer_bundle(monkeypatch) -> None:
from services import config as config_mod
from services.mesh import mesh_rns as mesh_rns_mod
bridge = RNSBridge()
sent: list[tuple[bytes, str | None]] = []
settings = SimpleNamespace(
MESH_PEER_PUSH_SECRET="peer-secret",
MESH_RNS_MAX_PAYLOAD=8192,
MESH_RNS_DANDELION_DELAY_MS=0,
)
monkeypatch.setattr(config_mod, "get_settings", lambda: settings)
monkeypatch.setattr(mesh_rns_mod, "get_settings", lambda: settings)
monkeypatch.setattr(bridge, "enabled", lambda: True)
monkeypatch.setattr(bridge, "_maybe_rotate_session", lambda: None)
monkeypatch.setattr(bridge, "_seen", lambda _message_id: False)
monkeypatch.setattr(bridge, "_make_message_id", lambda prefix: f"{prefix}-wire-id")
monkeypatch.setattr(bridge, "_dandelion_hops", lambda: 3)
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: None)
monkeypatch.setattr(
bridge,
"_send_diffuse",
lambda payload, exclude=None: sent.append((payload, exclude)),
)
bridge.publish_gate_event(
"finance",
{
"event_type": "gate_message",
"timestamp": 1710000000,
"event_id": "gate-evt-1",
"node_id": "!gate-persona-1",
"sequence": 19,
"signature": "deadbeef",
"public_key": "pubkey-1",
"public_key_algo": "Ed25519",
"protocol_version": "infonet/2",
"payload": {
"gate": "finance",
"ciphertext": "abc123",
"format": "mls1",
"nonce": "nonce-7",
"sender_ref": "sender-ref-7",
"epoch": 4,
},
},
)
assert len(sent) == 1
message, exclude = sent[0]
decoded = json.loads(message.decode("utf-8"))
event = decoded["body"]["event"]
assert exclude is None
assert decoded["type"] == "gate_event"
assert decoded["meta"] == {
"message_id": "gate-wire-id",
"dandelion": {"phase": "stem", "hops": 0, "max_hops": 3},
}
assert set(event.keys()) == {
"event_type",
"timestamp",
"payload",
"event_id",
"node_id",
"sequence",
"signature",
"public_key",
"public_key_algo",
"protocol_version",
}
assert event["event_id"] == "gate-evt-1"
assert event["node_id"] == "!gate-persona-1"
assert event["sequence"] == 19
assert event["signature"] == "deadbeef"
assert event["public_key"] == "pubkey-1"
assert event["public_key_algo"] == "Ed25519"
assert event["protocol_version"] == "infonet/2"
assert set(event["payload"].keys()) == {"ciphertext", "format", "gate_ref", "nonce", "sender_ref", "epoch"}
assert event["payload"]["ciphertext"] == "abc123"
assert event["payload"]["format"] == "mls1"
assert event["payload"]["nonce"] == "nonce-7"
assert event["payload"]["sender_ref"] == "sender-ref-7"
assert event["payload"]["epoch"] == 4
assert event["payload"]["gate_ref"]
assert "gate" not in event["payload"]
def test_rns_inbound_gate_event_resolves_gate_ref_before_local_ingest(monkeypatch) -> None:
from services import config as config_mod
from services.mesh import mesh_hashchain as mesh_hashchain_mod, mesh_rns as mesh_rns_mod
bridge = RNSBridge()
ingested: list[tuple[str, list[dict]]] = []
settings = SimpleNamespace(MESH_RNS_DANDELION_HOPS=3)
monkeypatch.setattr(config_mod, "get_settings", lambda: settings)
monkeypatch.setattr(mesh_rns_mod, "get_settings", lambda: settings)
monkeypatch.setattr(bridge, "_seen", lambda _message_id: False)
monkeypatch.setattr(mesh_hashchain_mod, "resolve_gate_wire_ref", lambda gate_ref, event: "finance")
monkeypatch.setattr(
mesh_hashchain_mod.gate_store,
"ingest_peer_events",
lambda gate_id, events: ingested.append((gate_id, events)) or {"accepted": 1, "duplicates": 0, "rejected": 0},
)
packet = mesh_rns_mod.RNSMessage(
msg_type="gate_event",
body={
"event": {
"event_type": "gate_message",
"timestamp": 1710000000,
"event_id": "gate-evt-inbound",
"node_id": "!gate-persona-1",
"sequence": 9,
"signature": "deadbeef",
"public_key": "pubkey-1",
"public_key_algo": "Ed25519",
"protocol_version": "infonet/2",
"payload": {
"ciphertext": "abc123",
"format": "mls1",
"nonce": "nonce-7",
"sender_ref": "sender-ref-7",
"epoch": 4,
"gate_ref": "opaque-ref-1",
},
}
},
meta={"message_id": "gate-inbound-1", "dandelion": {"phase": "diffuse"}},
).encode()
bridge._on_packet(packet)
assert len(ingested) == 1
gate_id, events = ingested[0]
assert gate_id == "finance"
assert len(events) == 1
event = events[0]
assert event["event_id"] == "gate-evt-inbound"
assert event["node_id"] == "!gate-persona-1"
assert event["sequence"] == 9
assert event["signature"] == "deadbeef"
assert event["public_key"] == "pubkey-1"
assert event["public_key_algo"] == "Ed25519"
assert event["protocol_version"] == "infonet/2"
assert event["payload"]["gate"] == "finance"
assert event["payload"]["gate_ref"] == "opaque-ref-1"
assert event["payload"]["ciphertext"] == "abc123"
assert event["payload"]["nonce"] == "nonce-7"
assert event["payload"]["sender_ref"] == "sender-ref-7"
assert event["payload"]["epoch"] == 4
def test_rns_inbound_gate_event_blind_forwards_when_gate_cannot_be_resolved(monkeypatch) -> None:
from services import config as config_mod
from services.mesh import mesh_hashchain as mesh_hashchain_mod, mesh_rns as mesh_rns_mod
bridge = RNSBridge()
forwarded: list[tuple[str, dict]] = []
ingested: list[tuple[str, list[dict]]] = []
settings = SimpleNamespace(MESH_RNS_DANDELION_HOPS=3)
monkeypatch.setattr(config_mod, "get_settings", lambda: settings)
monkeypatch.setattr(mesh_rns_mod, "get_settings", lambda: settings)
monkeypatch.setattr(bridge, "_seen", lambda _message_id: False)
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: "peer-stem")
monkeypatch.setattr(
bridge,
"_send_to_peer",
lambda peer, payload: forwarded.append((peer, json.loads(payload.decode("utf-8")))),
)
monkeypatch.setattr(mesh_hashchain_mod, "resolve_gate_wire_ref", lambda gate_ref, event: "")
monkeypatch.setattr(
mesh_hashchain_mod.gate_store,
"ingest_peer_events",
lambda gate_id, events: ingested.append((gate_id, events)) or {"accepted": 1, "duplicates": 0, "rejected": 0},
)
original_event = {
"event_type": "gate_message",
"timestamp": 1710000000,
"event_id": "gate-evt-blind",
"node_id": "!gate-persona-1",
"sequence": 9,
"signature": "deadbeef",
"public_key": "pubkey-1",
"public_key_algo": "Ed25519",
"protocol_version": "infonet/2",
"payload": {
"ciphertext": "abc123",
"format": "mls1",
"nonce": "nonce-7",
"sender_ref": "sender-ref-7",
"epoch": 4,
"gate_ref": "opaque-ref-1",
},
}
packet = mesh_rns_mod.RNSMessage(
msg_type="gate_event",
body={"event": original_event},
meta={"message_id": "gate-inbound-2", "dandelion": {"phase": "stem", "hops": 0, "max_hops": 2}},
).encode()
bridge._on_packet(packet)
assert ingested == []
assert len(forwarded) == 1
peer, forwarded_msg = forwarded[0]
assert peer == "peer-stem"
assert forwarded_msg["type"] == "gate_event"
assert forwarded_msg["meta"] == {
"message_id": "gate-inbound-2",
"dandelion": {"phase": "stem", "hops": 1, "max_hops": 2},
}
assert forwarded_msg["body"]["event"] == original_event
@@ -0,0 +1,909 @@
import asyncio
import base64
import hashlib
import hmac
import time
from httpx import ASGITransport, AsyncClient
import main
from services.config import get_settings
from services.mesh.mesh_crypto import derive_node_id
from services.mesh import mesh_dm_relay, mesh_hashchain, mesh_rns
def _fresh_relay(tmp_path, monkeypatch):
from services import wormhole_supervisor
monkeypatch.setattr(mesh_dm_relay, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_dm_relay, "RELAY_FILE", tmp_path / "dm_relay.json")
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": True},
)
get_settings.cache_clear()
relay = mesh_dm_relay.DMRelay()
monkeypatch.setattr(mesh_dm_relay, "dm_relay", relay)
return relay
def _post(path: str, payload: dict):
async def _run():
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
return await ac.post(path, json=payload)
return asyncio.run(_run())
class _FakeInfonet:
def __init__(self):
self.appended = []
self.sequences = {}
def append(self, **kwargs):
self.appended.append(kwargs)
def validate_and_set_sequence(self, node_id, sequence):
last = self.sequences.get(node_id, 0)
if sequence <= last:
return False, f"Replay detected: sequence {sequence} <= last {last}"
self.sequences[node_id] = sequence
return True, ""
class _DirectRNS:
def __init__(self, send_result=True, direct_messages=None, direct_ids=None):
self.send_result = send_result
self.sent = []
self.direct_messages = list(direct_messages or [])
self.direct_ids_value = set(direct_ids or [])
def send_private_dm(self, *, mailbox_key, envelope):
self.sent.append({"mailbox_key": mailbox_key, "envelope": envelope})
return self.send_result
def collect_private_dm(self, mailbox_keys):
return list(self.direct_messages)
def private_dm_ids(self, mailbox_keys):
return set(self.direct_ids_value)
def count_private_dm(self, mailbox_keys):
return len(self.direct_ids_value)
TEST_PUBLIC_KEY = base64.b64encode(b"0" * 32).decode("ascii")
TEST_SENDER_ID = derive_node_id(TEST_PUBLIC_KEY)
REQUEST_CLAIMS = [{"type": "requests", "token": "request-claim-token"}]
NOW_TS = lambda: int(time.time())
def test_secure_dm_send_prefers_reticulum(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
direct_rns = _DirectRNS(send_result=True)
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: True)
monkeypatch.setattr(main, "_rns_private_dm_ready", lambda: True)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
response = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"msg_id": "msg-reticulum-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 7,
"protocol_version": "infonet/2",
},
)
body = response.json()
assert response.status_code == 200
assert body["ok"] is True
assert body["transport"] == "reticulum"
assert relay.count_claims("!sb_recipient1234", REQUEST_CLAIMS) == 0
assert len(direct_rns.sent) == 1
assert direct_rns.sent[0]["envelope"]["msg_id"] == "msg-reticulum-1"
assert len(infonet.appended) == 0
def test_secure_dm_send_falls_back_to_relay(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
direct_rns = _DirectRNS(send_result=False)
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: True)
monkeypatch.setattr(main, "_rns_private_dm_ready", lambda: True)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
response = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"msg_id": "msg-relay-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 8,
"protocol_version": "infonet/2",
},
)
body = response.json()
assert response.status_code == 200
assert body["ok"] is True
assert body["transport"] == "relay"
assert "relay fallback" in body["detail"].lower()
assert relay.count_claims("!sb_recipient1234", REQUEST_CLAIMS) == 1
assert len(infonet.appended) == 0
def test_request_sender_seal_reduces_relay_sender_handle_on_fallback(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
direct_rns = _DirectRNS(send_result=False)
relay_salt = "0123456789abcdef0123456789abcdef"
expected_sender = "sealed:" + hmac.new(
bytes.fromhex(relay_salt), TEST_SENDER_ID.encode("utf-8"), hashlib.sha256
).hexdigest()[:16]
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: True)
monkeypatch.setattr(main, "_rns_private_dm_ready", lambda: True)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
response = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"sender_seal": "v3:test-seal",
"relay_salt": relay_salt,
"msg_id": "msg-relay-sealed-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 18,
"protocol_version": "infonet/2",
},
)
body = response.json()
assert response.status_code == 200
assert body["ok"] is True
assert body["transport"] == "relay"
messages = relay.collect_claims("!sb_recipient1234", REQUEST_CLAIMS)
assert [msg["msg_id"] for msg in messages] == ["msg-relay-sealed-1"]
assert messages[0]["sender_id"] == expected_sender
assert messages[0]["sender_id"] != TEST_SENDER_ID
assert messages[0]["sender_seal"] == "v3:test-seal"
def test_request_sender_seal_reduces_direct_rns_sender_handle(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
direct_rns = _DirectRNS(send_result=True)
relay_salt = "fedcba9876543210fedcba9876543210"
expected_sender = "sealed:" + hmac.new(
bytes.fromhex(relay_salt), TEST_SENDER_ID.encode("utf-8"), hashlib.sha256
).hexdigest()[:16]
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: True)
monkeypatch.setattr(main, "_rns_private_dm_ready", lambda: True)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
response = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"sender_seal": "v3:test-seal",
"relay_salt": relay_salt,
"msg_id": "msg-direct-sealed-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 19,
"protocol_version": "infonet/2",
},
)
body = response.json()
assert response.status_code == 200
assert body["ok"] is True
assert body["transport"] == "reticulum"
assert len(direct_rns.sent) == 1
assert direct_rns.sent[0]["envelope"]["sender_id"] == expected_sender
assert direct_rns.sent[0]["envelope"]["sender_id"] != TEST_SENDER_ID
assert direct_rns.sent[0]["envelope"]["sender_seal"] == "v3:test-seal"
assert relay.count_claims("!sb_recipient1234", REQUEST_CLAIMS) == 0
def test_request_sender_block_prevents_direct_rns_delivery(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
direct_rns = _DirectRNS(send_result=True)
relay.block("!sb_recipient1234", TEST_SENDER_ID)
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: True)
monkeypatch.setattr(main, "_rns_private_dm_ready", lambda: True)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
response = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"sender_seal": "v3:test-seal",
"relay_salt": "00112233445566778899aabbccddeeff",
"msg_id": "msg-direct-blocked-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 20,
"protocol_version": "infonet/2",
},
)
assert response.status_code == 200
assert response.json() == {"ok": False, "detail": "Recipient is not accepting your messages"}
assert len(direct_rns.sent) == 0
assert relay.count_claims("!sb_recipient1234", REQUEST_CLAIMS) == 0
def test_request_sender_seal_respects_raw_sender_block_on_relay_send_path(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
relay.block("!sb_recipient1234", TEST_SENDER_ID)
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
response = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"sender_seal": "v3:test-seal",
"relay_salt": "00112233445566778899aabbccddeeff",
"msg_id": "msg-blocked-sealed-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 20,
"protocol_version": "infonet/2",
},
)
assert response.status_code == 200
assert response.json() == {"ok": False, "detail": "Recipient is not accepting your messages"}
assert relay.count_claims("!sb_recipient1234", REQUEST_CLAIMS) == 0
def test_secure_dm_send_rejects_replayed_msg_id_nonce(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
payload = {
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"msg_id": "msg-replay-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 14,
"protocol_version": "infonet/2",
}
first = _post("/api/mesh/dm/send", payload)
second = _post("/api/mesh/dm/send", payload)
assert first.status_code == 200
assert first.json()["ok"] is True
assert second.status_code == 200
assert second.json() == {"ok": False, "detail": "nonce replay detected"}
assert relay.count_claims("!sb_recipient1234", REQUEST_CLAIMS) == 1
def test_secure_dm_send_rejects_replayed_sequence_with_new_nonce(tmp_path, monkeypatch):
_fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
first = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"msg_id": "msg-seq-1",
"nonce": "nonce-seq-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 15,
"protocol_version": "infonet/2",
},
)
second = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext-again",
"msg_id": "msg-seq-2",
"nonce": "nonce-seq-2",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 15,
"protocol_version": "infonet/2",
},
)
assert first.status_code == 200
assert first.json()["ok"] is True
assert second.status_code == 200
assert second.json() == {"ok": False, "detail": "Replay detected: sequence 15 <= last 15"}
def test_secure_dm_send_does_not_consume_nonce_before_signature_verification(tmp_path, monkeypatch):
_fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
consumed = {"count": 0}
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (False, "Invalid signature"))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(
mesh_dm_relay.dm_relay,
"consume_nonce",
lambda *_args, **_kwargs: consumed.__setitem__("count", consumed["count"] + 1) or (True, "ok"),
)
response = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"msg_id": "msg-invalid-sig",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 16,
"protocol_version": "infonet/2",
},
)
assert response.status_code == 200
assert response.json() == {"ok": False, "detail": "Invalid signature"}
assert consumed["count"] == 0
def test_anonymous_mode_dm_send_stays_off_reticulum(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
infonet = _FakeInfonet()
direct_rns = _DirectRNS(send_result=True)
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: True)
monkeypatch.setattr(main, "_rns_private_dm_ready", lambda: True)
monkeypatch.setattr(main, "_anonymous_dm_hidden_transport_enforced", lambda: True)
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
response = _post(
"/api/mesh/dm/send",
{
"sender_id": TEST_SENDER_ID,
"recipient_id": "!sb_recipient1234",
"delivery_class": "request",
"ciphertext": "ciphertext",
"msg_id": "msg-anon-relay-1",
"timestamp": NOW_TS(),
"public_key": TEST_PUBLIC_KEY,
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 9,
"protocol_version": "infonet/2",
},
)
body = response.json()
assert response.status_code == 200
assert body["ok"] is True
assert body["transport"] == "relay"
assert "off direct transport" in body["detail"].lower()
assert relay.count_claims("!sb_recipient1234", REQUEST_CLAIMS) == 1
assert len(direct_rns.sent) == 0
assert len(infonet.appended) == 0
def test_secure_dm_poll_and_count_merge_relay_and_reticulum(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
relay.deposit(
sender_id="alice",
recipient_id="bob",
ciphertext="cipher-relay-dup",
msg_id="dup",
delivery_class="request",
)
relay.deposit(
sender_id="alice",
recipient_id="bob",
ciphertext="cipher-relay-only",
msg_id="relay-only",
delivery_class="request",
)
direct_rns = _DirectRNS(
direct_messages=[
{
"sender_id": "sealed:1234",
"ciphertext": "cipher-direct-dup",
"timestamp": 100.0,
"msg_id": "dup",
"delivery_class": "request",
"sender_seal": "",
"transport": "reticulum",
},
{
"sender_id": "sealed:1234",
"ciphertext": "cipher-direct-only",
"timestamp": 101.0,
"msg_id": "direct-only",
"delivery_class": "request",
"sender_seal": "",
"transport": "reticulum",
},
],
direct_ids={"dup", "direct-only"},
)
infonet = _FakeInfonet()
monkeypatch.setattr(
main,
"_verify_dm_mailbox_request",
lambda **_kwargs: (True, "", {"mailbox_claims": REQUEST_CLAIMS}),
)
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
poll_response = _post(
"/api/mesh/dm/poll",
{
"agent_id": "bob",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": NOW_TS(),
"nonce": "nonce-poll",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 10,
"protocol_version": "infonet/2",
},
)
poll_body = poll_response.json()
assert poll_response.status_code == 200
assert poll_body["ok"] is True
assert poll_body["count"] == 3
assert {msg["msg_id"] for msg in poll_body["messages"]} == {"dup", "relay-only", "direct-only"}
dup_message = next(msg for msg in poll_body["messages"] if msg["msg_id"] == "dup")
assert dup_message["sender_id"] == "alice"
assert dup_message["ciphertext"] == "cipher-relay-dup"
count_response = _post(
"/api/mesh/dm/count",
{
"agent_id": "bob",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": NOW_TS(),
"nonce": "nonce-count",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 11,
"protocol_version": "infonet/2",
},
)
count_body = count_response.json()
assert count_response.status_code == 200
assert count_body["ok"] is True
assert count_body["count"] == 2
def test_secure_dm_poll_marks_reduced_v3_request_recovery_fields(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
relay.deposit(
sender_id="sealed:relayv3",
raw_sender_id="alice",
recipient_id="bob",
ciphertext="cipher-relay-v3",
msg_id="relay-v3",
delivery_class="request",
sender_seal="v3:relay-seal",
)
relay.deposit(
sender_id="alice",
recipient_id="bob",
ciphertext="cipher-legacy",
msg_id="legacy-raw",
delivery_class="request",
)
direct_rns = _DirectRNS(
direct_messages=[
{
"sender_id": "sealed:directv3",
"ciphertext": "cipher-direct-v3",
"timestamp": 101.0,
"msg_id": "direct-v3",
"delivery_class": "request",
"sender_seal": "v3:direct-seal",
"transport": "reticulum",
}
],
direct_ids={"direct-v3"},
)
infonet = _FakeInfonet()
monkeypatch.setattr(
main,
"_verify_dm_mailbox_request",
lambda **_kwargs: (True, "", {"mailbox_claims": REQUEST_CLAIMS}),
)
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
poll_response = _post(
"/api/mesh/dm/poll",
{
"agent_id": "bob",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": NOW_TS(),
"nonce": "nonce-poll-markers",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 12,
"protocol_version": "infonet/2",
},
)
poll_body = poll_response.json()
assert poll_response.status_code == 200
assert poll_body["ok"] is True
assert poll_body["count"] == 3
by_id = {msg["msg_id"]: msg for msg in poll_body["messages"]}
assert by_id["relay-v3"]["request_contract_version"] == "request-v2-reduced-v3"
assert by_id["relay-v3"]["sender_recovery_required"] is True
assert by_id["relay-v3"]["sender_recovery_state"] == "pending"
assert by_id["direct-v3"]["request_contract_version"] == "request-v2-reduced-v3"
assert by_id["direct-v3"]["sender_recovery_required"] is True
assert by_id["direct-v3"]["sender_recovery_state"] == "pending"
assert "request_contract_version" not in by_id["legacy-raw"]
assert "sender_recovery_required" not in by_id["legacy-raw"]
assert "sender_recovery_state" not in by_id["legacy-raw"]
def test_secure_dm_poll_prefers_canonical_v2_duplicate_over_legacy_raw(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
relay.deposit(
sender_id="alice",
recipient_id="bob",
ciphertext="cipher-relay-raw",
msg_id="dup-v2-over-raw",
delivery_class="request",
)
direct_rns = _DirectRNS(
direct_messages=[
{
"sender_id": "sealed:directv3",
"ciphertext": "cipher-direct-v3",
"timestamp": 101.0,
"msg_id": "dup-v2-over-raw",
"delivery_class": "request",
"sender_seal": "v3:direct-seal",
"transport": "reticulum",
}
],
direct_ids={"dup-v2-over-raw"},
)
infonet = _FakeInfonet()
monkeypatch.setattr(
main,
"_verify_dm_mailbox_request",
lambda **_kwargs: (True, "", {"mailbox_claims": REQUEST_CLAIMS}),
)
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
poll_response = _post(
"/api/mesh/dm/poll",
{
"agent_id": "bob",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": NOW_TS(),
"nonce": "nonce-poll-v2-over-raw",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 13,
"protocol_version": "infonet/2",
},
)
poll_body = poll_response.json()
assert poll_response.status_code == 200
assert poll_body["ok"] is True
assert poll_body["count"] == 1
message = poll_body["messages"][0]
assert message["msg_id"] == "dup-v2-over-raw"
assert message["sender_id"] == "sealed:directv3"
assert message["ciphertext"] == "cipher-direct-v3"
assert message["transport"] == "reticulum"
assert message["request_contract_version"] == "request-v2-reduced-v3"
assert message["sender_recovery_required"] is True
assert message["sender_recovery_state"] == "pending"
def test_secure_dm_poll_prefers_legacy_raw_duplicate_over_legacy_sealed(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
relay.deposit(
sender_id="sealed:relaylegacy",
raw_sender_id="alice",
recipient_id="bob",
ciphertext="cipher-relay-sealed",
msg_id="dup-raw-over-sealed",
delivery_class="request",
sender_seal="v2:legacy-seal",
)
direct_rns = _DirectRNS(
direct_messages=[
{
"sender_id": "alice",
"ciphertext": "cipher-direct-raw",
"timestamp": 101.0,
"msg_id": "dup-raw-over-sealed",
"delivery_class": "request",
"sender_seal": "",
"transport": "reticulum",
}
],
direct_ids={"dup-raw-over-sealed"},
)
infonet = _FakeInfonet()
monkeypatch.setattr(
main,
"_verify_dm_mailbox_request",
lambda **_kwargs: (True, "", {"mailbox_claims": REQUEST_CLAIMS}),
)
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
poll_response = _post(
"/api/mesh/dm/poll",
{
"agent_id": "bob",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": NOW_TS(),
"nonce": "nonce-poll-raw-over-sealed",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 14,
"protocol_version": "infonet/2",
},
)
poll_body = poll_response.json()
assert poll_response.status_code == 200
assert poll_body["ok"] is True
assert poll_body["count"] == 1
message = poll_body["messages"][0]
assert message["msg_id"] == "dup-raw-over-sealed"
assert message["sender_id"] == "alice"
assert message["ciphertext"] == "cipher-direct-raw"
assert message["transport"] == "reticulum"
assert "request_contract_version" not in message
assert "sender_recovery_required" not in message
assert "sender_recovery_state" not in message
def test_secure_dm_poll_keeps_relay_copy_for_same_contract_v2_duplicate(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
relay.deposit(
sender_id="sealed:sharedv3",
raw_sender_id="alice",
recipient_id="bob",
ciphertext="cipher-relay-v3-dup",
msg_id="dup-v2-tie",
delivery_class="request",
sender_seal="v3:relay-seal",
)
direct_rns = _DirectRNS(
direct_messages=[
{
"sender_id": "sealed:sharedv3",
"ciphertext": "cipher-direct-v3-dup",
"timestamp": 101.0,
"msg_id": "dup-v2-tie",
"delivery_class": "request",
"sender_seal": "v3:relay-seal",
"transport": "reticulum",
}
],
direct_ids={"dup-v2-tie"},
)
infonet = _FakeInfonet()
monkeypatch.setattr(
main,
"_verify_dm_mailbox_request",
lambda **_kwargs: (True, "", {"mailbox_claims": REQUEST_CLAIMS}),
)
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
poll_response = _post(
"/api/mesh/dm/poll",
{
"agent_id": "bob",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": NOW_TS(),
"nonce": "nonce-poll-v2-tie",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 15,
"protocol_version": "infonet/2",
},
)
poll_body = poll_response.json()
assert poll_response.status_code == 200
assert poll_body["ok"] is True
assert poll_body["count"] == 1
message = poll_body["messages"][0]
assert message["msg_id"] == "dup-v2-tie"
assert message["sender_id"] == "sealed:sharedv3"
assert message["ciphertext"] == "cipher-relay-v3-dup"
assert "transport" not in message
assert message["request_contract_version"] == "request-v2-reduced-v3"
assert message["sender_recovery_required"] is True
assert message["sender_recovery_state"] == "pending"
def test_anonymous_mode_poll_and_count_ignore_reticulum(tmp_path, monkeypatch):
relay = _fresh_relay(tmp_path, monkeypatch)
relay.deposit(
sender_id="alice",
recipient_id="bob",
ciphertext="cipher-relay-only",
msg_id="relay-only",
delivery_class="request",
)
direct_rns = _DirectRNS(
direct_messages=[
{
"sender_id": "sealed:1234",
"ciphertext": "cipher-direct-only",
"timestamp": 101.0,
"msg_id": "direct-only",
"delivery_class": "request",
"sender_seal": "",
"transport": "reticulum",
},
],
direct_ids={"direct-only"},
)
infonet = _FakeInfonet()
monkeypatch.setattr(
main,
"_verify_dm_mailbox_request",
lambda **_kwargs: (True, "", {"mailbox_claims": REQUEST_CLAIMS}),
)
monkeypatch.setattr(main, "_anonymous_dm_hidden_transport_enforced", lambda: True)
monkeypatch.setattr(mesh_hashchain, "infonet", infonet)
monkeypatch.setattr(mesh_rns, "rns_bridge", direct_rns)
poll_response = _post(
"/api/mesh/dm/poll",
{
"agent_id": "bob",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": NOW_TS(),
"nonce": "nonce-poll-anon",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 12,
"protocol_version": "infonet/2",
},
)
poll_body = poll_response.json()
assert poll_response.status_code == 200
assert poll_body["ok"] is True
assert poll_body["count"] == 1
assert {msg["msg_id"] for msg in poll_body["messages"]} == {"relay-only"}
count_response = _post(
"/api/mesh/dm/count",
{
"agent_id": "bob",
"mailbox_claims": REQUEST_CLAIMS,
"timestamp": NOW_TS(),
"nonce": "nonce-count-anon",
"public_key": "pub",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 13,
"protocol_version": "infonet/2",
},
)
count_body = count_response.json()
assert count_response.status_code == 200
assert count_body["ok"] is True
assert count_body["count"] == 0
@@ -0,0 +1,225 @@
import json
import os
import subprocess
import sys
from types import SimpleNamespace
def _reset_secure_storage_state(mesh_secure_storage) -> None:
mesh_secure_storage._MASTER_KEY_CACHE = None
mesh_secure_storage._DOMAIN_KEY_CACHE.clear()
def test_secure_storage_encrypts_and_reads_json(tmp_path, monkeypatch):
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
_reset_secure_storage_state(mesh_secure_storage)
path = tmp_path / "secret.json"
mesh_secure_storage.write_secure_json(path, {"alpha": 1, "bravo": "two"})
raw = json.loads(path.read_text(encoding="utf-8"))
assert raw["kind"] == "sb_secure_json"
assert "alpha" not in path.read_text(encoding="utf-8")
data = mesh_secure_storage.read_secure_json(path, lambda: {})
assert data == {"alpha": 1, "bravo": "two"}
def test_secure_storage_migrates_plaintext_json(tmp_path, monkeypatch):
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
_reset_secure_storage_state(mesh_secure_storage)
path = tmp_path / "legacy.json"
path.write_text(json.dumps({"legacy": True}), encoding="utf-8")
data = mesh_secure_storage.read_secure_json(path, lambda: {})
assert data == {"legacy": True}
migrated = json.loads(path.read_text(encoding="utf-8"))
assert migrated["kind"] == "sb_secure_json"
def test_secure_storage_fails_closed_on_decrypt_error(tmp_path, monkeypatch):
import pytest
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
_reset_secure_storage_state(mesh_secure_storage)
path = tmp_path / "corrupt.json"
mesh_secure_storage.write_secure_json(path, {"secret": "value"})
payload = json.loads(path.read_text(encoding="utf-8"))
payload["ciphertext"] = payload["ciphertext"][:-4] + "AAAA"
path.write_text(json.dumps(payload), encoding="utf-8")
with pytest.raises(mesh_secure_storage.SecureStorageError):
mesh_secure_storage.read_secure_json(path, lambda: {})
def test_secure_storage_round_trips_across_process_boundary(tmp_path, monkeypatch):
if os.name != "nt":
return
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
_reset_secure_storage_state(mesh_secure_storage)
path = tmp_path / "cross-process.json"
mesh_secure_storage.write_secure_json(path, {"alpha": 7, "bravo": "cross-process"})
backend_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
script = f"""
import json
from pathlib import Path
from services.mesh import mesh_secure_storage
mesh_secure_storage.DATA_DIR = Path(r"{tmp_path}")
mesh_secure_storage.MASTER_KEY_FILE = Path(r"{tmp_path / 'wormhole_secure_store.key'}")
print(json.dumps(mesh_secure_storage.read_secure_json(r"{path}", lambda: {{}})))
"""
result = subprocess.run(
[sys.executable, "-c", script],
cwd=backend_root,
capture_output=True,
text=True,
env={**os.environ.copy(), "PYTHONPATH": backend_root},
check=True,
)
assert json.loads(result.stdout.strip()) == {"alpha": 7, "bravo": "cross-process"}
def test_domain_storage_isolation_keeps_gate_and_dm_data_separate(tmp_path, monkeypatch):
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
_reset_secure_storage_state(mesh_secure_storage)
mesh_secure_storage.write_domain_json("gate_persona", "gate.json", {"gate": "alpha"})
mesh_secure_storage.write_domain_json("dm_alias", "dm.json", {"alias": "bravo"})
gate_data = mesh_secure_storage.read_domain_json("gate_persona", "gate.json", lambda: {})
dm_data = mesh_secure_storage.read_domain_json("dm_alias", "dm.json", lambda: {})
assert gate_data == {"gate": "alpha"}
assert dm_data == {"alias": "bravo"}
assert (tmp_path / "gate_persona" / "gate.json").exists()
assert (tmp_path / "dm_alias" / "dm.json").exists()
def test_domain_storage_uses_independent_domain_key_files(tmp_path, monkeypatch):
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
_reset_secure_storage_state(mesh_secure_storage)
mesh_secure_storage.write_domain_json("gate_persona", "gate.json", {"gate": "alpha"})
mesh_secure_storage.write_domain_json("dm_alias", "dm.json", {"alias": "bravo"})
gate_key = tmp_path / "_domain_keys" / "gate_persona.key"
dm_key = tmp_path / "_domain_keys" / "dm_alias.key"
assert gate_key.exists()
assert dm_key.exists()
assert gate_key.read_text(encoding="utf-8") != dm_key.read_text(encoding="utf-8")
assert not mesh_secure_storage.MASTER_KEY_FILE.exists()
def test_domain_storage_migrates_legacy_master_derived_ciphertext(tmp_path, monkeypatch):
import pytest
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
_reset_secure_storage_state(mesh_secure_storage)
domain = "gate_persona"
filename = "legacy.json"
payload = {"legacy": True}
file_path = tmp_path / domain / filename
file_path.parent.mkdir(parents=True, exist_ok=True)
nonce = os.urandom(12)
ciphertext = AESGCM(mesh_secure_storage._derive_legacy_domain_key(domain)).encrypt(
nonce,
mesh_secure_storage._stable_json(payload),
mesh_secure_storage._domain_aad(domain, filename),
)
envelope = mesh_secure_storage._secure_envelope(file_path, nonce, ciphertext)
file_path.write_text(json.dumps(envelope), encoding="utf-8")
_reset_secure_storage_state(mesh_secure_storage)
data = mesh_secure_storage.read_domain_json(domain, filename, lambda: {})
assert data == payload
assert (tmp_path / "_domain_keys" / f"{domain}.key").exists()
migrated = json.loads(file_path.read_text(encoding="utf-8"))
with pytest.raises(Exception):
AESGCM(mesh_secure_storage._derive_legacy_domain_key(domain)).decrypt(
mesh_secure_storage._unb64(migrated["nonce"]),
mesh_secure_storage._unb64(migrated["ciphertext"]),
mesh_secure_storage._domain_aad(domain, filename),
)
def test_domain_storage_rejects_path_traversal(tmp_path, monkeypatch):
import pytest
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
_reset_secure_storage_state(mesh_secure_storage)
with pytest.raises(mesh_secure_storage.SecureStorageError):
mesh_secure_storage._domain_file_path("../../etc", "passwd")
def test_raw_fallback_requires_explicit_opt_in_not_debug(monkeypatch):
from services import config as config_mod
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "_is_windows", lambda: False)
monkeypatch.delenv("PYTEST_CURRENT_TEST", raising=False)
monkeypatch.setattr(
config_mod,
"get_settings",
lambda: SimpleNamespace(
MESH_ALLOW_RAW_SECURE_STORAGE_FALLBACK=False,
MESH_DEBUG_MODE=True,
),
)
assert mesh_secure_storage._raw_fallback_allowed() is False
def test_raw_fallback_allows_explicit_opt_in(monkeypatch):
from services import config as config_mod
from services.mesh import mesh_secure_storage
monkeypatch.setattr(mesh_secure_storage, "_is_windows", lambda: False)
monkeypatch.delenv("PYTEST_CURRENT_TEST", raising=False)
monkeypatch.setattr(
config_mod,
"get_settings",
lambda: SimpleNamespace(
MESH_ALLOW_RAW_SECURE_STORAGE_FALLBACK=True,
MESH_DEBUG_MODE=False,
),
)
assert mesh_secure_storage._raw_fallback_allowed() is True
@@ -0,0 +1,73 @@
import asyncio
class TestSensitiveBackendNoStore:
def test_mesh_status_sets_privacy_security_headers(self, client):
r = client.get("/api/mesh/infonet/status")
assert r.status_code == 200
assert "default-src 'self'" in (r.headers.get("content-security-policy") or "")
assert (r.headers.get("x-frame-options") or "").upper() == "DENY"
assert (r.headers.get("x-content-type-options") or "").lower() == "nosniff"
assert (r.headers.get("referrer-policy") or "").lower() == "no-referrer"
def test_wormhole_status_is_no_store(self, client):
r = client.get("/api/wormhole/status")
assert r.status_code == 200
assert "no-store" in (r.headers.get("cache-control") or "").lower()
def test_settings_privacy_profile_is_no_store(self, client):
r = client.get("/api/settings/privacy-profile")
assert r.status_code == 200
assert "no-store" in (r.headers.get("cache-control") or "").lower()
def test_settings_wormhole_is_no_store(self, client):
r = client.get("/api/settings/wormhole")
assert r.status_code == 200
assert "no-store" in (r.headers.get("cache-control") or "").lower()
def test_settings_wormhole_status_is_no_store(self, client):
r = client.get("/api/settings/wormhole-status")
assert r.status_code == 200
assert "no-store" in (r.headers.get("cache-control") or "").lower()
def test_dm_pubkey_is_no_store_even_on_failure(self, client):
r = client.get("/api/mesh/dm/pubkey?agent_id=missing")
assert r.status_code == 200
body = r.json()
assert body["ok"] is False
assert "no-store" in (r.headers.get("cache-control") or "").lower()
def test_anonymous_mode_blocked_dm_send_is_no_store(self, client, monkeypatch):
import main
from services import wormhole_settings, wormhole_status
monkeypatch.setattr(
wormhole_settings,
"read_wormhole_settings",
lambda: {
"enabled": True,
"privacy_profile": "default",
"transport": "direct",
"anonymous_mode": True,
},
)
monkeypatch.setattr(
wormhole_status,
"read_wormhole_status",
lambda: {
"running": True,
"ready": True,
"transport_active": "direct",
},
)
async def _post():
from httpx import ASGITransport, AsyncClient
transport = ASGITransport(app=main.app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
return await ac.post("/api/mesh/dm/send", json={})
response = asyncio.run(_post())
assert response.status_code == 428
assert "no-store" in (response.headers.get("cache-control") or "").lower()
@@ -0,0 +1,63 @@
def test_wormhole_identity_allows_local_operator_without_admin_key(client, monkeypatch):
import main
monkeypatch.setattr(main, "_current_admin_key", lambda: "test-key")
monkeypatch.setattr(main, "_allow_insecure_admin", lambda: False)
monkeypatch.setattr(
main,
"get_transport_identity",
lambda: {
"node_id": "transport-node",
"public_key": "pub",
"public_key_algo": "Ed25519",
},
)
allowed = client.get("/api/wormhole/identity")
assert allowed.status_code == 200
assert allowed.json()["node_id"] == "transport-node"
def test_wormhole_gate_identity_allows_local_operator_without_admin_key(client, monkeypatch):
import main
monkeypatch.setattr(main, "_current_admin_key", lambda: "test-key")
monkeypatch.setattr(main, "_allow_insecure_admin", lambda: False)
monkeypatch.setattr(
main,
"get_active_gate_identity",
lambda gate_id: {
"ok": True,
"gate_id": gate_id,
"identity": {"node_id": "gate-node", "scope": "gate_session"},
},
)
allowed = client.get("/api/wormhole/gate/journalists/identity")
assert allowed.status_code == 200
body = allowed.json()
assert body["gate_id"] == "journalists"
assert body["identity"]["node_id"] == "gate-node"
def test_wormhole_gate_personas_allows_local_operator_without_admin_key(client, monkeypatch):
import main
monkeypatch.setattr(main, "_current_admin_key", lambda: "test-key")
monkeypatch.setattr(main, "_allow_insecure_admin", lambda: False)
monkeypatch.setattr(
main,
"list_gate_personas",
lambda gate_id: {
"ok": True,
"gate_id": gate_id,
"active_persona_id": "",
"personas": [{"node_id": "persona-node", "scope": "gate_persona"}],
},
)
allowed = client.get("/api/wormhole/gate/journalists/personas")
assert allowed.status_code == 200
body = allowed.json()
assert body["gate_id"] == "journalists"
assert body["personas"][0]["node_id"] == "persona-node"
@@ -0,0 +1,480 @@
import base64
import asyncio
import json
import time
import pytest
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption, PrivateFormat, PublicFormat
from starlette.requests import Request
def _fresh_mesh_state(tmp_path, monkeypatch):
from services.mesh import (
mesh_dm_relay,
mesh_secure_storage,
mesh_wormhole_identity,
mesh_wormhole_persona,
)
monkeypatch.setattr(mesh_dm_relay, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_dm_relay, "RELAY_FILE", tmp_path / "dm_relay.json")
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
monkeypatch.setattr(mesh_wormhole_persona, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_persona, "PERSONA_FILE", tmp_path / "wormhole_persona.json")
monkeypatch.setattr(
mesh_wormhole_persona,
"LEGACY_DM_IDENTITY_FILE",
tmp_path / "wormhole_identity.json",
)
relay = mesh_dm_relay.DMRelay()
monkeypatch.setattr(mesh_dm_relay, "dm_relay", relay)
return relay, mesh_wormhole_identity
def _json_request(path: str, body: dict) -> Request:
payload = json.dumps(body).encode("utf-8")
sent = {"value": False}
async def receive():
if sent["value"]:
return {"type": "http.request", "body": b"", "more_body": False}
sent["value"] = True
return {"type": "http.request", "body": payload, "more_body": False}
return Request(
{
"type": "http",
"headers": [(b"content-type", b"application/json")],
"client": ("test", 12345),
"method": "POST",
"path": path,
},
receive,
)
def test_sender_token_can_resolve_recipient_without_clear_recipient_id(tmp_path, monkeypatch):
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh.mesh_wormhole_sender_token import (
consume_wormhole_dm_sender_token,
issue_wormhole_dm_sender_token,
)
issued = issue_wormhole_dm_sender_token(
recipient_id="peer123",
delivery_class="shared",
recipient_token="tok123",
)
assert issued["ok"]
consumed = consume_wormhole_dm_sender_token(
sender_token=issued["sender_token"],
recipient_id="",
delivery_class="shared",
recipient_token="tok123",
)
assert consumed["ok"]
assert consumed["recipient_id"] == "peer123"
assert consumed["sender_token_hash"]
def test_signed_prekey_rotation_preserves_old_bootstrap_decrypt(tmp_path, monkeypatch):
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh.mesh_wormhole_prekey import (
SIGNED_PREKEY_ROTATE_AFTER_S,
bootstrap_decrypt_from_sender,
bootstrap_encrypt_for_peer,
register_wormhole_prekey_bundle,
)
reg1 = register_wormhole_prekey_bundle(force_signed_prekey=True)
assert reg1["ok"]
agent_id = reg1["agent_id"]
old_envelope = bootstrap_encrypt_for_peer(agent_id, "ACCESS_REQUEST:X25519:testpub|geo=1,2")
assert old_envelope["ok"]
data = identity_mod.read_wormhole_identity()
data["signed_prekey_generated_at"] = int(time.time()) - SIGNED_PREKEY_ROTATE_AFTER_S - 10
identity_mod._write_identity(data)
reg2 = register_wormhole_prekey_bundle()
assert reg2["ok"]
assert reg2["bundle"]["signed_prekey_id"] != reg1["bundle"]["signed_prekey_id"]
refreshed = identity_mod.read_wormhole_identity()
history = list(refreshed.get("signed_prekey_history") or [])
assert any(int(item.get("signed_prekey_id", 0) or 0) == reg1["bundle"]["signed_prekey_id"] for item in history)
dec = bootstrap_decrypt_from_sender(agent_id, old_envelope["result"])
assert dec["ok"]
assert dec["result"] == "ACCESS_REQUEST:X25519:testpub|geo=1,2"
def test_prekey_bundle_fetch_rejects_stale_or_tampered_bundle(tmp_path, monkeypatch):
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh import mesh_wormhole_prekey as prekey_mod
registered = prekey_mod.register_wormhole_prekey_bundle(force_signed_prekey=True)
assert registered["ok"] is True
agent_id = registered["agent_id"]
fresh = prekey_mod.fetch_dm_prekey_bundle(agent_id)
assert fresh["ok"] is True
assert int(fresh["signed_at"]) > 0
assert fresh["bundle_signature"]
stored = relay.get_prekey_bundle(agent_id)
stale_bundle = dict(stored.get("bundle") or {})
stale_bundle["signed_at"] = int(time.time()) - prekey_mod._max_prekey_bundle_age_s() - 10
stale_bundle = prekey_mod._attach_bundle_signature(stale_bundle, signed_at=stale_bundle["signed_at"])
relay._prekey_bundles[agent_id]["bundle"] = stale_bundle
stale = prekey_mod.fetch_dm_prekey_bundle(agent_id)
assert stale == {"ok": False, "detail": "Prekey bundle is stale"}
tampered_bundle = dict(stale_bundle)
tampered_bundle["signed_at"] = int(time.time())
tampered_bundle = prekey_mod._attach_bundle_signature(tampered_bundle, signed_at=tampered_bundle["signed_at"])
tampered_bundle["bundle_signature"] = "00" * 64
relay._prekey_bundles[agent_id]["bundle"] = tampered_bundle
tampered = prekey_mod.fetch_dm_prekey_bundle(agent_id)
assert tampered == {"ok": False, "detail": "Prekey bundle signature invalid"}
def test_prekey_bundle_fetch_rejects_future_dated_bundle(tmp_path, monkeypatch):
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh import mesh_wormhole_prekey as prekey_mod
registered = prekey_mod.register_wormhole_prekey_bundle(force_signed_prekey=True)
assert registered["ok"] is True
agent_id = registered["agent_id"]
stored = relay.get_prekey_bundle(agent_id)
future_bundle = dict(stored.get("bundle") or {})
future_bundle["signed_at"] = int(time.time()) + 301
future_bundle = prekey_mod._attach_bundle_signature(future_bundle, signed_at=future_bundle["signed_at"])
relay._prekey_bundles[agent_id]["bundle"] = future_bundle
future = prekey_mod.fetch_dm_prekey_bundle(agent_id)
assert future == {"ok": False, "detail": "Prekey bundle signed_at is in the future"}
def test_remote_prekey_identity_is_pinned_and_detects_mismatch(tmp_path, monkeypatch):
_relay, _identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
from services.mesh import 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")
pinned = mesh_wormhole_contacts.observe_remote_prekey_identity(
"peer-alpha",
fingerprint="aa" * 32,
sequence=3,
signed_at=111,
)
same = mesh_wormhole_contacts.observe_remote_prekey_identity(
"peer-alpha",
fingerprint="aa" * 32,
sequence=4,
signed_at=222,
)
changed = mesh_wormhole_contacts.observe_remote_prekey_identity(
"peer-alpha",
fingerprint="bb" * 32,
sequence=5,
signed_at=333,
)
assert pinned["trust_changed"] is False
assert same["trust_changed"] is False
assert changed["trust_changed"] is True
stored = mesh_wormhole_contacts.list_wormhole_dm_contacts()["peer-alpha"]
assert stored["remotePrekeyFingerprint"] == "aa" * 32
assert stored["remotePrekeyObservedFingerprint"] == "bb" * 32
assert stored["remotePrekeyMismatch"] is True
def test_compose_wormhole_dm_rejects_remote_prekey_identity_change(tmp_path, monkeypatch):
_relay, _identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
import main
from services.mesh import 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")
monkeypatch.setattr(main, "has_mls_dm_session", lambda *_args, **_kwargs: {"ok": True, "exists": False})
monkeypatch.setattr(main, "initiate_mls_dm_session", lambda *_args, **_kwargs: {"ok": True, "welcome": "welcome"})
monkeypatch.setattr(main, "encrypt_mls_dm", lambda *_args, **_kwargs: {"ok": True, "ciphertext": "ct", "nonce": "n"})
initial = {
"ok": True,
"agent_id": "peer-alpha",
"mls_key_package": "ZmFrZQ==",
"identity_dh_pub_key": "peer-dh-pub",
"public_key": "peer-signing-pub",
"public_key_algo": "Ed25519",
"protocol_version": "infonet/2",
"sequence": 2,
"signed_at": int(time.time()),
"trust_fingerprint": "11" * 32,
}
changed = {
**initial,
"sequence": 3,
"signed_at": int(time.time()) + 1,
"trust_fingerprint": "22" * 32,
}
first = main.compose_wormhole_dm(
peer_id="peer-alpha",
peer_dh_pub="peer-dh-pub",
plaintext="hello",
remote_prekey_bundle=initial,
)
second = main.compose_wormhole_dm(
peer_id="peer-alpha",
peer_dh_pub="peer-dh-pub",
plaintext="hello again",
remote_prekey_bundle=changed,
)
assert first["ok"] is True
assert second == {
"ok": False,
"peer_id": "peer-alpha",
"detail": "remote prekey identity changed; verification required",
"trust_changed": True,
}
def test_prekey_bundle_registration_rejects_invalid_bundle(tmp_path, monkeypatch):
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity = identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh import mesh_wormhole_prekey as prekey_mod
bundle = prekey_mod.ensure_wormhole_prekeys(force_signed_prekey=True)
bundle = prekey_mod._attach_bundle_signature(bundle, signed_at=int(time.time()) + 301)
ok, reason, meta = relay.register_prekey_bundle(
identity["node_id"],
bundle,
"sig",
identity["public_key"],
identity["public_key_algo"],
"infonet/2",
1,
)
assert ok is False
assert reason == "Prekey bundle signed_at is in the future"
assert meta is None
assert relay.get_prekey_bundle(identity["node_id"]) is None
def test_dm_mailbox_token_derivation_and_shared_sender_token_routing(tmp_path, monkeypatch):
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity = identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh.mesh_wormhole_identity import derive_dm_mailbox_token
from services.mesh.mesh_wormhole_sender_token import issue_wormhole_dm_sender_token
import main
from services import wormhole_supervisor
from services.mesh import mesh_dm_relay, mesh_hashchain
mailbox_token = derive_dm_mailbox_token(identity["node_id"])
assert mailbox_token
issued = issue_wormhole_dm_sender_token(
recipient_id="peer123",
delivery_class="shared",
recipient_token=mailbox_token,
)
assert issued["ok"] is True
monkeypatch.setattr(main, "_verify_signed_event", lambda **_kwargs: (True, ""))
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_strong")
monkeypatch.setattr(mesh_hashchain.infonet, "validate_and_set_sequence", lambda *_args, **_kwargs: (True, ""))
monkeypatch.setattr(mesh_dm_relay, "dm_relay", relay)
response = asyncio.run(
main.dm_send(
_json_request(
"/api/mesh/dm/send",
{
"sender_token": issued["sender_token"],
"recipient_id": "",
"delivery_class": "shared",
"recipient_token": mailbox_token,
"ciphertext": "cipher-shared",
"sender_seal": "v3:test-seal",
"msg_id": "shared-msg-1",
"timestamp": int(time.time()),
"public_key": "",
"public_key_algo": "Ed25519",
"signature": "sig",
"sequence": 1,
"protocol_version": "infonet/2",
},
)
)
)
assert response["ok"] is True
hashed_mailbox = relay._hashed_mailbox_token(mailbox_token)
assert list(relay._mailboxes.keys()) == [hashed_mailbox]
assert relay._mailboxes[hashed_mailbox][0].sender_id.startswith("sender_token:")
assert relay._mailboxes[hashed_mailbox][0].sender_id != identity["node_id"]
delivered = relay.collect_claims(identity["node_id"], [{"type": "shared", "token": mailbox_token}])
assert [msg["msg_id"] for msg in delivered] == ["shared-msg-1"]
def test_open_sender_seal_verifies_in_wormhole(tmp_path, monkeypatch):
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity = identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh.mesh_wormhole_seal import build_sender_seal, open_sender_seal
msg_id = "dm_test_1"
timestamp = 1234567890
built = build_sender_seal(
recipient_id=identity["node_id"],
recipient_dh_pub=identity["dh_pub_key"],
msg_id=msg_id,
timestamp=timestamp,
)
assert built["ok"]
assert str(built["sender_seal"]).startswith("v3:")
opened = open_sender_seal(
sender_seal=built["sender_seal"],
candidate_dh_pub=identity["dh_pub_key"],
recipient_id=identity["node_id"],
expected_msg_id=msg_id,
)
assert opened["ok"]
assert opened["sender_id"] == identity["node_id"]
assert opened["seal_verified"] is True
def test_open_sender_seal_still_accepts_legacy_format(tmp_path, monkeypatch):
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity = identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh.mesh_wormhole_identity import sign_wormhole_message
from services.mesh.mesh_wormhole_seal import open_sender_seal
sender_priv = x25519.X25519PrivateKey.generate()
sender_pub = sender_priv.public_key()
recipient_pub = x25519.X25519PublicKey.from_public_bytes(base64.b64decode(identity["dh_pub_key"]))
shared = sender_priv.exchange(recipient_pub)
msg_id = "dm_test_legacy"
timestamp = 1234567890
signed = sign_wormhole_message(f"seal|{msg_id}|{timestamp}|{identity['node_id']}")
seal_payload = {
"sender_id": signed["node_id"],
"public_key": signed["public_key"],
"public_key_algo": signed["public_key_algo"],
"msg_id": msg_id,
"timestamp": timestamp,
"signature": signed["signature"],
}
iv = b"\x00" * 12
ciphertext = AESGCM(shared).encrypt(iv, json.dumps(seal_payload).encode("utf-8"), None)
sender_seal = base64.b64encode(iv + ciphertext).decode("ascii")
candidate_dh_pub = base64.b64encode(
sender_pub.public_bytes(Encoding.Raw, PublicFormat.Raw)
).decode("ascii")
opened = open_sender_seal(
sender_seal=sender_seal,
candidate_dh_pub=candidate_dh_pub,
recipient_id=identity["node_id"],
expected_msg_id=msg_id,
)
assert opened["ok"]
assert opened["sender_id"] == identity["node_id"]
assert opened["seal_verified"] is True
def test_legacy_sender_seal_rejected_in_hardened_mode(tmp_path, monkeypatch):
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
identity = identity_mod.bootstrap_wormhole_identity(force=True)
from services.mesh.mesh_wormhole_identity import sign_wormhole_message
from services.mesh import mesh_wormhole_seal
monkeypatch.setattr(
mesh_wormhole_seal,
"read_wormhole_settings",
lambda: {"enabled": True, "anonymous_mode": True},
)
sender_priv = x25519.X25519PrivateKey.generate()
sender_pub = sender_priv.public_key()
recipient_pub = x25519.X25519PublicKey.from_public_bytes(base64.b64decode(identity["dh_pub_key"]))
shared = sender_priv.exchange(recipient_pub)
msg_id = "dm_test_legacy_hardened"
timestamp = 1234567890
signed = sign_wormhole_message(f"seal|{msg_id}|{timestamp}|{identity['node_id']}")
seal_payload = {
"sender_id": signed["node_id"],
"public_key": signed["public_key"],
"public_key_algo": signed["public_key_algo"],
"msg_id": msg_id,
"timestamp": timestamp,
"signature": signed["signature"],
}
iv = b"\x00" * 12
ciphertext = AESGCM(shared).encrypt(iv, json.dumps(seal_payload).encode("utf-8"), None)
sender_seal = base64.b64encode(iv + ciphertext).decode("ascii")
candidate_dh_pub = base64.b64encode(
sender_pub.public_bytes(Encoding.Raw, PublicFormat.Raw)
).decode("ascii")
opened = mesh_wormhole_seal.open_sender_seal(
sender_seal=sender_seal,
candidate_dh_pub=candidate_dh_pub,
recipient_id=identity["node_id"],
expected_msg_id=msg_id,
)
assert opened["ok"] is False
assert "Legacy sender seals" in opened["detail"]
def test_require_admin_no_longer_trusts_loopback_without_override(monkeypatch):
from fastapi import HTTPException
from starlette.requests import Request
import main
monkeypatch.setattr(main, "_current_admin_key", lambda: "")
monkeypatch.setattr(main, "_allow_insecure_admin", lambda: False)
request = Request(
{
"type": "http",
"headers": [],
"client": ("127.0.0.1", 12345),
"method": "GET",
"path": "/api/wormhole/status",
}
)
with pytest.raises(HTTPException) as exc:
main.require_admin(request)
assert exc.value.status_code == 403
@@ -0,0 +1,307 @@
import asyncio
from starlette.requests import Request
def _fresh_persona_state(tmp_path, monkeypatch):
from services.mesh import mesh_secure_storage, mesh_wormhole_identity, mesh_wormhole_persona
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
monkeypatch.setattr(mesh_wormhole_persona, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_wormhole_persona, "PERSONA_FILE", tmp_path / "wormhole_persona.json")
monkeypatch.setattr(
mesh_wormhole_persona,
"LEGACY_DM_IDENTITY_FILE",
tmp_path / "wormhole_identity.json",
)
return mesh_wormhole_persona, mesh_wormhole_identity
def _request(path: str) -> Request:
return Request(
{
"type": "http",
"headers": [],
"client": ("test", 12345),
"method": "POST",
"path": path,
}
)
def test_transport_identity_is_separate_from_dm_identity(tmp_path, monkeypatch):
persona_mod, identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
dm_identity = identity_mod.bootstrap_wormhole_identity(force=True)
persona_state = persona_mod.bootstrap_wormhole_persona_state(force=True)
transport_identity = persona_state["transport_identity"]
assert dm_identity["node_id"]
assert transport_identity["node_id"]
assert dm_identity["node_id"] != transport_identity["node_id"]
assert dm_identity["public_key"] != transport_identity["public_key"]
def test_gate_anonymous_session_differs_from_transport_identity(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
transport_identity = persona_mod.get_transport_identity()
gate_identity = persona_mod.enter_gate_anonymously("journalists", rotate=True)
assert gate_identity["ok"] is True
assert gate_identity["identity"]["scope"] == "gate_session"
assert gate_identity["identity"]["gate_id"] == "journalists"
assert gate_identity["identity"]["node_id"] != transport_identity["node_id"]
def test_gate_identities_are_separate_from_root_identity(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
state = persona_mod.read_wormhole_persona_state()
root_identity = state["root_identity"]
gate_session = persona_mod.enter_gate_anonymously("journalists", rotate=True)["identity"]
gate_persona = persona_mod.create_gate_persona("journalists", label="source-a")["identity"]
assert root_identity["node_id"]
assert gate_session["node_id"] != root_identity["node_id"]
assert gate_session["public_key"] != root_identity["public_key"]
assert gate_persona["node_id"] != root_identity["node_id"]
assert gate_persona["public_key"] != root_identity["public_key"]
def test_gate_persona_activation_is_gate_local(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona("sources", label="source-a")
second = persona_mod.create_gate_persona("leaks", label="source-a")
assert first["ok"] is True
assert second["ok"] is True
assert first["identity"]["gate_id"] == "sources"
assert second["identity"]["gate_id"] == "leaks"
assert first["identity"]["node_id"] != second["identity"]["node_id"]
active_sources = persona_mod.get_active_gate_identity("sources")
active_leaks = persona_mod.get_active_gate_identity("leaks")
assert active_sources["identity"]["persona_id"] == first["identity"]["persona_id"]
assert active_leaks["identity"]["persona_id"] == second["identity"]["persona_id"]
def test_gate_persona_duplicate_labels_get_unique_suffixes(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.create_gate_persona("sources", label="source-a")
second = persona_mod.create_gate_persona("sources", label="source-a")
third = persona_mod.create_gate_persona("sources", label="Source-A")
assert first["ok"] is True
assert second["ok"] is True
assert third["ok"] is True
assert first["identity"]["label"] == "source-a"
assert second["identity"]["label"] == "source-a-2"
assert third["identity"]["label"] == "Source-A-3"
def test_sign_public_event_uses_transport_identity(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
transport_identity = persona_mod.get_transport_identity()
signed = persona_mod.sign_public_wormhole_event(
event_type="message",
payload={
"message": "hello",
"destination": "broadcast",
"channel": "LongFast",
"priority": "normal",
"ephemeral": False,
},
)
assert signed["identity_scope"] == "transport"
assert signed["node_id"] == transport_identity["node_id"]
assert signed["public_key"] == transport_identity["public_key"]
def test_sign_gate_event_uses_gate_session_identity(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
transport_identity = persona_mod.get_transport_identity()
signed = persona_mod.sign_gate_wormhole_event(
gate_id="journalists",
event_type="gate_message",
payload={
"gate": "journalists",
"epoch": 1,
"ciphertext": "opaque-source-drop",
"nonce": "nonce-j1",
"sender_ref": "gate-session-j1",
},
)
gate_identity = persona_mod.get_active_gate_identity("journalists")
assert signed["identity_scope"] == "gate_session"
assert signed["gate_id"] == "journalists"
assert signed["node_id"] == gate_identity["identity"]["node_id"]
assert signed["node_id"] != transport_identity["node_id"]
def test_leave_gate_forces_new_anonymous_session_on_reentry(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.enter_gate_anonymously("sources", rotate=True)
persona_mod.leave_gate("sources")
second = persona_mod.enter_gate_anonymously("sources", rotate=False)
assert first["identity"]["node_id"]
assert second["identity"]["node_id"]
assert first["identity"]["node_id"] != second["identity"]["node_id"]
def test_gate_session_rotation_uses_jitter_window_before_auto_swap(tmp_path, monkeypatch):
from services.config import get_settings
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
monkeypatch.setenv("MESH_GATE_SESSION_ROTATE_MSGS", "1")
monkeypatch.setenv("MESH_GATE_SESSION_ROTATE_JITTER_S", "120")
get_settings.cache_clear()
try:
now = {"value": 1_000.0}
monkeypatch.setattr(persona_mod.time, "time", lambda: now["value"])
monkeypatch.setattr(persona_mod.random, "uniform", lambda *_args, **_kwargs: 45.0)
persona_mod.bootstrap_wormhole_persona_state(force=True)
first = persona_mod.enter_gate_anonymously("sources", rotate=True)
persona_mod.sign_gate_wormhole_event(
gate_id="sources",
event_type="gate_message",
payload={
"gate": "sources",
"epoch": 1,
"ciphertext": "opaque",
"nonce": "nonce-1",
"sender_ref": "sender-ref-1",
"format": "mls1",
},
)
same = persona_mod.enter_gate_anonymously("sources", rotate=False)
scheduled = persona_mod.read_wormhole_persona_state()["gate_sessions"]["sources"]["_rotate_after"]
assert same["identity"]["node_id"] == first["identity"]["node_id"]
assert scheduled == 1_045.0
now["value"] = 1_046.0
rotated = persona_mod.enter_gate_anonymously("sources", rotate=False)
assert rotated["identity"]["node_id"] != first["identity"]["node_id"]
finally:
get_settings.cache_clear()
def test_gate_enter_leave_do_not_emit_public_breadcrumbs(tmp_path, monkeypatch):
import main
from services.mesh import mesh_hashchain
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
append_called = {"count": 0}
def fake_append(**kwargs):
append_called["count"] += 1
return {"event_id": "unexpected"}
monkeypatch.setattr(mesh_hashchain.infonet, "append", fake_append)
body = main.WormholeGateRequest(gate_id="sources", rotate=True)
entered = asyncio.run(main.api_wormhole_gate_enter(_request("/api/wormhole/gate/enter"), body))
left = asyncio.run(
main.api_wormhole_gate_leave(
_request("/api/wormhole/gate/leave"),
main.WormholeGateRequest(gate_id="sources"),
)
)
assert entered["ok"] is True
assert left["ok"] is True
assert append_called["count"] == 0
def test_clear_active_persona_reverts_gate_to_anonymous_session(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.enter_gate_anonymously("evidence", rotate=True)
created = persona_mod.create_gate_persona("evidence", label="reporter")
cleared = persona_mod.clear_active_gate_persona("evidence")
active = persona_mod.get_active_gate_identity("evidence")
assert created["identity"]["scope"] == "gate_persona"
assert cleared["ok"] is True
assert cleared["identity"]["scope"] == "gate_session"
assert active["source"] == "anonymous"
def test_sign_gate_event_uses_active_persona_when_selected(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.enter_gate_anonymously("ops", rotate=True)
created = persona_mod.create_gate_persona("ops", label="scribe")
signed = persona_mod.sign_gate_wormhole_event(
gate_id="ops",
event_type="gate_message",
payload={
"gate": "ops",
"epoch": 1,
"ciphertext": "opaque-persona-post",
"nonce": "nonce-o1",
"sender_ref": "persona-ops-1",
},
)
assert created["identity"]["persona_id"]
assert signed["identity_scope"] == "gate_persona"
assert signed["node_id"] == created["identity"]["node_id"]
def test_enter_gate_anonymously_clears_existing_active_persona(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
created = persona_mod.create_gate_persona("ops", label="scribe")
entered = persona_mod.enter_gate_anonymously("ops", rotate=True)
active = persona_mod.get_active_gate_identity("ops")
assert created["identity"]["scope"] == "gate_persona"
assert entered["identity"]["scope"] == "gate_session"
assert active["source"] == "anonymous"
assert active["identity"]["node_id"] == entered["identity"]["node_id"]
assert active["identity"]["node_id"] != created["identity"]["node_id"]
def test_sign_gate_event_rejects_cross_gate_payload_mismatch(tmp_path, monkeypatch):
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
persona_mod.bootstrap_wormhole_persona_state(force=True)
persona_mod.enter_gate_anonymously("ops", rotate=True)
signed = persona_mod.sign_gate_wormhole_event(
gate_id="ops",
event_type="gate_message",
payload={
"gate": "finance",
"epoch": 1,
"ciphertext": "opaque-cross-gate-post",
"nonce": "nonce-cross-1",
"sender_ref": "persona-finance-1",
},
)
assert signed["ok"] is False
assert signed["detail"] == "gate payload mismatch"
+15
View File
@@ -0,0 +1,15 @@
def test_node_settings_roundtrip(tmp_path, monkeypatch):
from services import node_settings
settings_path = tmp_path / "node.json"
monkeypatch.setattr(node_settings, "NODE_FILE", settings_path)
monkeypatch.setattr(node_settings, "_cache", None)
monkeypatch.setattr(node_settings, "_cache_ts", 0.0)
initial = node_settings.read_node_settings()
updated = node_settings.write_node_settings(enabled=True)
reread = node_settings.read_node_settings()
assert initial["enabled"] is False
assert updated["enabled"] is True
assert reread["enabled"] is True
@@ -0,0 +1,74 @@
from __future__ import annotations
import base64
import shutil
from pathlib import Path
import pytest
from services.privacy_core_client import (
PrivacyCoreError,
PrivacyCoreClient,
PrivacyCoreUnavailable,
candidate_library_paths,
)
def _built_library_path() -> Path:
for candidate in candidate_library_paths():
if candidate.exists():
return candidate
raise PrivacyCoreUnavailable("privacy-core shared library not found")
def _isolated_client(tmp_path: Path, name: str) -> PrivacyCoreClient:
source = _built_library_path()
target = tmp_path / f"{name}{source.suffix}"
shutil.copy2(source, target)
return PrivacyCoreClient.load(target)
# NOTE: This test runs both clients in the same process. It validates key-package
# serialization/deserialization correctness but does not prove cross-process isolation.
# True cross-process testing deferred — see BUILD_TRACKER S3-F4 note.
def test_cross_client_key_package_serialization_round_trip(tmp_path):
try:
client_a = _isolated_client(tmp_path, "privacy_core_node_a")
client_b = _isolated_client(tmp_path, "privacy_core_node_b")
except PrivacyCoreUnavailable:
pytest.skip("privacy-core shared library not found")
assert client_a.reset_all_state() is True
assert client_b.reset_all_state() is True
alice = client_a.create_identity()
group = client_a.create_group(alice)
throwaway = client_b.create_identity()
bob = client_b.create_identity()
exported = client_b.export_key_package(bob)
transported = base64.b64decode(base64.b64encode(exported))
imported = client_a.import_key_package(transported)
commit = client_a.add_member(group, imported)
assert client_a.commit_message_bytes(commit)
assert client_a.commit_welcome_message_bytes(commit)
assert client_a.release_commit(commit) is True
assert client_a.release_key_package(imported) is True
assert client_a.release_group(group) is True
assert client_a.release_identity(alice) is True
assert client_b.release_identity(throwaway) is True
assert client_b.release_identity(bob) is True
def test_import_key_package_rejects_oversized_payload(tmp_path):
try:
client = _isolated_client(tmp_path, "privacy_core_oversized")
except PrivacyCoreUnavailable:
pytest.skip("privacy-core shared library not found")
assert client.reset_all_state() is True
with pytest.raises(PrivacyCoreError, match="maximum size"):
client.import_key_package(b"x" * 65_537)
@@ -0,0 +1,335 @@
"""Sprint 1 security-review invariant tests for privacy-core FFI.
These tests exercise the FFI boundary, handle lifecycle, buffer ownership,
and MLS correctness of the Rust privacy-core as accessed through the Python
ctypes bridge.
Test IDs map to the S1 Security Review findings:
S1-T1 Use-after-release: freed handle must produce error, no ciphertext
S1-T2 Double-release: second release returns False, no crash
S1-T3 Public-bundle key-material: exported JSON contains no private key
S1-T4 MLS round-trip: encrypt decrypt produces original plaintext
S1-T5 Removed member cannot decrypt post-removal messages
Requires a compiled privacy-core shared library. If unavailable, tests are
skipped rather than failed.
"""
from __future__ import annotations
import json
import os
import sys
import unittest
from pathlib import Path
# Ensure the backend package is importable.
_backend = Path(__file__).resolve().parents[2]
if str(_backend) not in sys.path:
sys.path.insert(0, str(_backend))
from services.privacy_core_client import (
PrivacyCoreClient,
PrivacyCoreError,
PrivacyCoreUnavailable,
)
_client: PrivacyCoreClient | None = None
def _get_client() -> PrivacyCoreClient:
"""Lazy-load the privacy-core library; skip if unavailable."""
global _client # noqa: PLW0603
if _client is not None:
return _client
try:
_client = PrivacyCoreClient.load()
except PrivacyCoreUnavailable:
raise unittest.SkipTest(
"privacy-core shared library not found — skipping FFI invariant tests"
)
return _client
class TestUseAfterRelease(unittest.TestCase):
"""S1-T1: Operations on a released handle must fail cleanly."""
def test_encrypt_after_release_group(self) -> None:
"""Releasing a group handle then encrypting with it must raise, not encrypt."""
client = _get_client()
identity = client.create_identity()
group = client.create_group(identity)
# Sanity: encryption works before release.
ciphertext = client.encrypt_group_message(group, b"pre-release")
self.assertIsInstance(ciphertext, bytes)
self.assertGreater(len(ciphertext), 0)
# Release the group handle.
released = client.release_group(group)
self.assertTrue(released)
# Post-release: must raise, must NOT return ciphertext.
with self.assertRaises(PrivacyCoreError) as ctx:
client.encrypt_group_message(group, b"post-release-secret")
self.assertIn("unknown group handle", str(ctx.exception).lower())
# Cleanup.
client.release_identity(identity)
def test_decrypt_after_release_group(self) -> None:
"""Decrypting with a freed group handle must fail."""
client = _get_client()
identity = client.create_identity()
group = client.create_group(identity)
ciphertext = client.encrypt_group_message(group, b"test")
client.release_group(group)
with self.assertRaises(PrivacyCoreError):
client.decrypt_group_message(group, ciphertext)
client.release_identity(identity)
class TestDoubleRelease(unittest.TestCase):
"""S1-T2: Double-releasing a handle must not crash and must return False."""
def test_double_release_identity(self) -> None:
client = _get_client()
identity = client.create_identity()
first = client.release_identity(identity)
second = client.release_identity(identity)
self.assertTrue(first)
self.assertFalse(second)
def test_double_release_group(self) -> None:
client = _get_client()
identity = client.create_identity()
group = client.create_group(identity)
first = client.release_group(group)
second = client.release_group(group)
self.assertTrue(first)
self.assertFalse(second)
client.release_identity(identity)
def test_double_release_commit(self) -> None:
client = _get_client()
alice = client.create_identity()
bob = client.create_identity()
group = client.create_group(alice)
kp_bytes = client.export_key_package(bob)
kp_handle = client.import_key_package(kp_bytes)
commit = client.add_member(group, kp_handle)
first = client.release_commit(commit)
second = client.release_commit(commit)
self.assertTrue(first)
self.assertFalse(second)
# Cleanup.
client.release_group(group)
client.release_key_package(kp_handle)
client.release_identity(alice)
client.release_identity(bob)
class TestPublicBundleNoPrivateKey(unittest.TestCase):
"""S1-T3: Exported public bundle must not contain private key material."""
def test_bundle_contains_only_public_fields(self) -> None:
client = _get_client()
identity = client.create_identity()
bundle_bytes = client.export_public_bundle(identity)
bundle = json.loads(bundle_bytes)
# Expected fields only.
allowed_keys = {"label", "cipher_suite", "signing_public_key", "credential"}
self.assertEqual(set(bundle.keys()), allowed_keys)
# The signing_public_key field must be present and non-empty (it's the PUBLIC key).
self.assertIsInstance(bundle["signing_public_key"], list)
self.assertGreater(len(bundle["signing_public_key"]), 0)
# Verify no field name suggests private material.
for key in bundle:
self.assertNotIn("private", key.lower(), f"Field '{key}' suggests private material")
self.assertNotIn("secret", key.lower(), f"Field '{key}' suggests secret material")
client.release_identity(identity)
def test_bundle_for_unknown_identity_fails(self) -> None:
client = _get_client()
with self.assertRaises(PrivacyCoreError):
client.export_public_bundle(0xDEAD)
class TestMLSRoundTrip(unittest.TestCase):
"""S1-T4: Encrypt → decrypt must produce original plaintext."""
def test_two_member_encrypt_decrypt(self) -> None:
client = _get_client()
alice_id = client.create_identity()
bob_id = client.create_identity()
# Alice creates a group.
alice_group = client.create_group(alice_id)
# Bob exports a key package; Alice imports it and adds Bob.
kp_bytes = client.export_key_package(bob_id)
kp_handle = client.import_key_package(kp_bytes)
commit = client.add_member(alice_group, kp_handle)
# Get Bob's joined group handle.
bob_group = client.commit_joined_group_handle(commit)
# Alice encrypts; Bob decrypts.
plaintext = b"hello from alice"
ciphertext = client.encrypt_group_message(alice_group, plaintext)
self.assertNotEqual(ciphertext, plaintext)
decrypted = client.decrypt_group_message(bob_group, ciphertext)
self.assertEqual(decrypted, plaintext)
# Bob encrypts; Alice decrypts.
plaintext2 = b"hello from bob"
ciphertext2 = client.encrypt_group_message(bob_group, plaintext2)
decrypted2 = client.decrypt_group_message(alice_group, ciphertext2)
self.assertEqual(decrypted2, plaintext2)
# Cleanup.
client.release_commit(commit)
client.release_key_package(kp_handle)
client.release_group(alice_group)
client.release_group(bob_group)
client.release_identity(alice_id)
client.release_identity(bob_id)
def test_old_epoch_ciphertext_fails_after_membership_change(self) -> None:
client = _get_client()
alice_id = client.create_identity()
bob_id = client.create_identity()
charlie_id = client.create_identity()
alice_group = client.create_group(alice_id)
bob_kp = client.export_key_package(bob_id)
bob_kp_handle = client.import_key_package(bob_kp)
commit1 = client.add_member(alice_group, bob_kp_handle)
bob_group = client.commit_joined_group_handle(commit1)
old_epoch_ct = client.encrypt_group_message(alice_group, b"epoch one")
self.assertEqual(client.decrypt_group_message(bob_group, old_epoch_ct), b"epoch one")
charlie_kp = client.export_key_package(charlie_id)
charlie_kp_handle = client.import_key_package(charlie_kp)
commit2 = client.add_member(alice_group, charlie_kp_handle)
charlie_group = client.commit_joined_group_handle(commit2)
with self.assertRaises(PrivacyCoreError):
client.decrypt_group_message(alice_group, old_epoch_ct)
with self.assertRaises(PrivacyCoreError):
client.decrypt_group_message(bob_group, old_epoch_ct)
new_epoch_ct = client.encrypt_group_message(alice_group, b"epoch two")
self.assertEqual(client.decrypt_group_message(charlie_group, new_epoch_ct), b"epoch two")
for handle in (commit1, commit2):
client.release_commit(handle)
for handle in (bob_kp_handle, charlie_kp_handle):
client.release_key_package(handle)
for handle in (alice_group, bob_group, charlie_group):
client.release_group(handle)
for handle in (alice_id, bob_id, charlie_id):
client.release_identity(handle)
class TestRemovedMemberCannotDecrypt(unittest.TestCase):
"""S1-T5: A removed member must fail to decrypt post-removal messages."""
def test_removed_member_decryption_fails(self) -> None:
client = _get_client()
alice_id = client.create_identity()
bob_id = client.create_identity()
charlie_id = client.create_identity()
# Alice creates group, adds Bob.
alice_group = client.create_group(alice_id)
bob_kp = client.export_key_package(bob_id)
bob_kp_h = client.import_key_package(bob_kp)
commit1 = client.add_member(alice_group, bob_kp_h)
bob_group = client.commit_joined_group_handle(commit1)
# Alice adds Charlie.
charlie_kp = client.export_key_package(charlie_id)
charlie_kp_h = client.import_key_package(charlie_kp)
commit2 = client.add_member(alice_group, charlie_kp_h)
charlie_group = client.commit_joined_group_handle(commit2)
# Verify all three can communicate.
ct = client.encrypt_group_message(alice_group, b"all three")
self.assertEqual(client.decrypt_group_message(bob_group, ct), b"all three")
# Alice removes Bob (member_ref=1, since Alice is 0, Bob is 1).
# Note: member indices depend on insertion order in mls-rs.
# We try member_ref=1 for Bob. If it fails we try 2.
try:
remove_commit = client.remove_member(alice_group, 1)
except PrivacyCoreError:
remove_commit = client.remove_member(alice_group, 2)
# Post-removal: Alice encrypts a new message.
post_removal_ct = client.encrypt_group_message(alice_group, b"bob is gone")
# Charlie should still be able to decrypt.
post_removal_plain = client.decrypt_group_message(charlie_group, post_removal_ct)
self.assertEqual(post_removal_plain, b"bob is gone")
# Bob's group handle should have been removed by the remove_member
# operation's family cleanup. Attempting to decrypt should fail.
with self.assertRaises(PrivacyCoreError):
client.decrypt_group_message(bob_group, post_removal_ct)
# Cleanup.
for c in (commit1, commit2, remove_commit):
client.release_commit(c)
for kp in (bob_kp_h, charlie_kp_h):
client.release_key_package(kp)
for g in (alice_group, charlie_group):
client.release_group(g)
for i in (alice_id, bob_id, charlie_id):
client.release_identity(i)
class TestPrivacyCoreLimits(unittest.TestCase):
"""S7-T5: privacy-core must enforce handle limits and report stats."""
def test_identity_limit_and_handle_stats(self) -> None:
client = _get_client()
client.reset_all_state()
stats_before = client.handle_stats()
self.assertEqual(stats_before["identities"], 0)
self.assertEqual(stats_before["max_identities"], 1024)
handles = []
for _ in range(stats_before["max_identities"]):
handles.append(client.create_identity())
stats_full = client.handle_stats()
self.assertEqual(stats_full["identities"], stats_full["max_identities"])
with self.assertRaises(PrivacyCoreError):
client.create_identity()
for handle in handles:
client.release_identity(handle)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,43 @@
import os
from types import SimpleNamespace
from unittest.mock import patch
def test_wormhole_subprocess_env_whitelists_runtime_and_mesh_vars():
from services import wormhole_supervisor
settings = {
"transport": "tor",
"socks_proxy": "127.0.0.1:9050",
"socks_dns": True,
}
config_snapshot = SimpleNamespace(MESH_RNS_ENABLED=False)
with patch.dict(
os.environ,
{
"PATH": "C:\\Python;C:\\Windows\\System32",
"SYSTEMROOT": "C:\\Windows",
"PYTHONPATH": "F:\\Codebase\\Oracle\\live-risk-dashboard\\backend",
"ADMIN_KEY": "admin-secret",
"MESH_PEER_PUSH_SECRET": "peer-secret-value",
"UNRELATED_SECRET": "should-not-leak",
},
clear=True,
):
env = wormhole_supervisor._wormhole_subprocess_env(
settings,
settings_obj=config_snapshot,
)
assert env["PATH"] == "C:\\Python;C:\\Windows\\System32"
assert env["SYSTEMROOT"] == "C:\\Windows"
assert env["PYTHONPATH"] == "F:\\Codebase\\Oracle\\live-risk-dashboard\\backend"
assert env["ADMIN_KEY"] == "admin-secret"
assert env["MESH_PEER_PUSH_SECRET"] == "peer-secret-value"
assert env["MESH_ONLY"] == "true"
assert env["MESH_RNS_ENABLED"] == "false"
assert env["WORMHOLE_TRANSPORT"] == "tor"
assert env["WORMHOLE_SOCKS_PROXY"] == "127.0.0.1:9050"
assert env["WORMHOLE_SOCKS_DNS"] == "true"
assert "UNRELATED_SECRET" not in env
+103
View File
@@ -1,4 +1,7 @@
"""Smoke tests for all API endpoints — verifies routes exist and return valid responses."""
import asyncio
import pytest
@@ -51,6 +54,92 @@ class TestLiveDataEndpoints:
for key in ("news", "stocks", "weather", "earthquakes"):
assert key in data, f"Missing key: {key}"
def test_fast_returns_full_world_payload_and_filters_disabled_sigint_sources(self, client, monkeypatch):
from services.fetchers import _store
ships = [{"lat": float(i % 80), "lng": float((i % 360) - 180), "id": i} for i in range(2000)]
sigint = (
[{"source": "aprs", "lat": 1.0, "lng": 1.0, "id": f"a-{i}"} for i in range(50)]
+ [{"source": "meshtastic", "lat": 2.0, "lng": 2.0, "id": f"m-{i}"} for i in range(50)]
+ [{"source": "meshtastic", "from_api": True, "lat": 3.0, "lng": 3.0, "id": f"mm-{i}"} for i in range(50)]
+ [{"source": "js8call", "lat": 4.0, "lng": 4.0, "id": f"j-{i}"} for i in range(50)]
)
monkeypatch.setitem(_store.latest_data, "ships", ships)
monkeypatch.setitem(_store.latest_data, "sigint", sigint)
monkeypatch.setitem(_store.active_layers, "sigint_aprs", False)
monkeypatch.setitem(_store.active_layers, "sigint_meshtastic", True)
r = client.get("/api/live-data/fast")
assert r.status_code == 200
data = r.json()
assert len(data["ships"]) == len(ships)
assert all(item["source"] != "aprs" for item in data["sigint"])
assert data["sigint_totals"]["aprs"] == 0
assert data["sigint_totals"]["meshtastic"] == 100
assert data["sigint_totals"]["meshtastic_map"] == 50
assert data["sigint_totals"]["js8call"] == 50
def test_slow_omits_disabled_power_plants_and_returns_full_world_datacenters(self, client, monkeypatch):
from services.fetchers import _store
datacenters = [{"lat": float(i % 80), "lng": float((i % 360) - 180), "id": i} for i in range(2000)]
power_plants = [{"lat": float(i % 80), "lng": float((i % 360) - 180), "id": i} for i in range(4000)]
monkeypatch.setitem(_store.latest_data, "datacenters", datacenters)
monkeypatch.setitem(_store.latest_data, "power_plants", power_plants)
monkeypatch.setitem(_store.active_layers, "datacenters", True)
monkeypatch.setitem(_store.active_layers, "power_plants", False)
r = client.get("/api/live-data/slow")
assert r.status_code == 200
data = r.json()
assert len(data["datacenters"]) == len(datacenters)
assert data["power_plants"] == []
def test_slow_handles_geojson_incidents_without_crashing(self, client, monkeypatch):
from services.fetchers import _store
gdelt = [
{
"type": "Feature",
"properties": {"name": "Incident A"},
"geometry": {"type": "Point", "coordinates": [10.0, 20.0]},
}
]
monkeypatch.setitem(_store.latest_data, "gdelt", gdelt)
monkeypatch.setitem(_store.active_layers, "global_incidents", True)
r = client.get("/api/live-data/slow")
assert r.status_code == 200
data = r.json()
assert data["gdelt"] == gdelt
def test_enabling_viirs_layer_queues_immediate_refresh(self, monkeypatch):
import main
from httpx import ASGITransport, AsyncClient
from services.fetchers import _store
queued = {"called": False}
monkeypatch.setitem(_store.active_layers, "viirs_nightlights", False)
monkeypatch.setattr(main, "_queue_viirs_change_refresh", lambda: queued.__setitem__("called", True))
async def _exercise():
transport = ASGITransport(app=main.app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
return await ac.post("/api/layers", json={"layers": {"viirs_nightlights": True}})
response = asyncio.run(_exercise())
assert response.status_code == 200
assert response.json()["status"] == "ok"
assert queued["called"] is True
class TestDebugEndpoint:
def test_debug_latest_returns_list(self, client):
@@ -74,6 +163,20 @@ class TestSettingsEndpoints:
assert isinstance(data, list)
class TestAdminProtection:
def test_refresh_requires_admin_key(self, client, monkeypatch):
import main
monkeypatch.setattr(main, "_ADMIN_KEY", "test-key")
monkeypatch.setattr(main, "_ALLOW_INSECURE_ADMIN", False)
r = client.get("/api/refresh")
assert r.status_code == 403
r_ok = client.get("/api/refresh", headers={"X-Admin-Key": "test-key"})
assert r_ok.status_code in (200, 202)
class TestRadioEndpoints:
def test_radio_top_returns_200(self, client):
r = client.get("/api/radio/top")
+15
View File
@@ -0,0 +1,15 @@
from services.fetch_health import record_success, record_failure, get_health_snapshot
def test_record_success_and_failure():
record_success("unit_test_source", duration_s=0.1, count=3)
record_failure("unit_test_source", error=Exception("boom"), duration_s=0.2)
snap = get_health_snapshot()
assert "unit_test_source" in snap
entry = snap["unit_test_source"]
assert entry["ok_count"] >= 1
assert entry["error_count"] >= 1
assert entry["last_ok"] is not None
assert entry["last_error"] is not None
assert entry["last_duration_ms"] is not None
+18
View File
@@ -0,0 +1,18 @@
import json
from services.fetchers import geo
def test_find_nearest_airport_from_fixture():
with open("tests/fixtures/airports.json", "r", encoding="utf-8") as f:
airports = json.load(f)
geo.cached_airports = airports
# Near Denver
result = geo.find_nearest_airport(39.74, -104.99, max_distance_nm=200)
assert result is not None
assert result["iata"] == "DEN"
# Far away (middle of the ocean)
result_far = geo.find_nearest_airport(0.0, -140.0, max_distance_nm=50)
assert result_far is None
@@ -0,0 +1,100 @@
import zipfile
from unittest.mock import patch
import pytest
from services import geopolitics, updater
@pytest.fixture(autouse=True)
def _clear_gdelt_caches():
geopolitics._article_title_cache.clear()
geopolitics._article_url_safety_cache.clear()
yield
geopolitics._article_title_cache.clear()
geopolitics._article_url_safety_cache.clear()
class TestGdeltArticleUrlSafety:
def test_safe_public_article_url_allows_public_dns(self, monkeypatch):
monkeypatch.setattr(
geopolitics.socket,
"getaddrinfo",
lambda *args, **kwargs: [(0, 0, 0, "", ("93.184.216.34", 443))],
)
allowed, reason = geopolitics._is_safe_public_article_url("https://example.com/story")
assert allowed is True
assert reason == ""
def test_safe_public_article_url_blocks_private_dns(self, monkeypatch):
monkeypatch.setattr(
geopolitics.socket,
"getaddrinfo",
lambda *args, **kwargs: [(0, 0, 0, "", ("10.0.0.7", 443))],
)
allowed, reason = geopolitics._is_safe_public_article_url("https://example.com/story")
assert allowed is False
assert reason == "private_dns"
def test_fetch_article_title_refuses_private_ip_without_request(self):
with patch("services.geopolitics.requests.get") as mock_get:
title = geopolitics._fetch_article_title("http://127.0.0.1/story")
assert title is None
mock_get.assert_not_called()
class TestUpdaterHardening:
def test_validate_update_url_rejects_untrusted_host(self):
with pytest.raises(RuntimeError, match="untrusted release host"):
updater._validate_update_url("https://evil.example.com/update.zip")
def test_extract_and_copy_rejects_zip_path_traversal(self, tmp_path):
zip_path = tmp_path / "bad.zip"
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
zf.writestr("../escape.txt", "nope")
with pytest.raises(RuntimeError, match="path traversal entry"):
updater._extract_and_copy(str(zip_path), str(tmp_path / "project"), str(tmp_path / "work"))
def test_perform_update_returns_manual_url_on_failure(self, monkeypatch, tmp_path):
def _boom(_temp_dir):
raise RuntimeError("update exploded")
monkeypatch.setattr(updater, "_download_release", _boom)
result = updater.perform_update(str(tmp_path))
assert result["status"] == "error"
assert result["manual_url"] == updater.GITHUB_RELEASES_PAGE_URL
assert "update exploded" in result["message"]
def test_perform_update_surfaces_release_metadata(self, monkeypatch, tmp_path):
release_url = "https://github.com/BigBodyCobain/Shadowbroker/releases/tag/v1.2.3"
download_url = (
"https://github.com/BigBodyCobain/Shadowbroker/releases/download/v1.2.3/update.zip"
)
backup_path = tmp_path / "backup.zip"
monkeypatch.setattr(
updater,
"_download_release",
lambda _temp_dir: ("dummy.zip", "v1.2.3", download_url, release_url),
)
monkeypatch.setattr(updater, "_validate_zip_hash", lambda _zip_path: None)
monkeypatch.setattr(updater, "_backup_current", lambda *_args: str(backup_path))
monkeypatch.setattr(updater, "_extract_and_copy", lambda *_args: 42)
result = updater.perform_update(str(tmp_path))
assert result["status"] == "ok"
assert result["version"] == "v1.2.3"
assert result["files_updated"] == 42
assert result["backup_path"] == str(backup_path)
assert result["manual_url"] == release_url
assert result["release_url"] == release_url
assert result["download_url"] == download_url
+20
View File
@@ -0,0 +1,20 @@
from unittest.mock import patch
def test_geocode_search_proxy(client):
with patch("main.search_geocode") as mock_search:
mock_search.return_value = [{"label": "Denver, CO, USA", "lat": 39.7392, "lng": -104.9903}]
r = client.get("/api/geocode/search?q=denver&limit=1")
assert r.status_code == 200
data = r.json()
assert data["count"] == 1
assert data["results"][0]["label"] == "Denver, CO, USA"
def test_geocode_reverse_proxy(client):
with patch("main.reverse_geocode") as mock_reverse:
mock_reverse.return_value = {"label": "Boulder, CO, USA"}
r = client.get("/api/geocode/reverse?lat=40.01499&lng=-105.27055")
assert r.status_code == 200
data = r.json()
assert data["label"] == "Boulder, CO, USA"
+37
View File
@@ -0,0 +1,37 @@
from services.mesh.meshtastic_topics import (
build_subscription_topics,
normalize_root,
parse_topic_metadata,
)
def test_normalize_root_accepts_custom_subroots():
assert normalize_root("msh/US/rob/snd/#") == "US/rob/snd"
assert normalize_root(" PL ") == "PL"
def test_build_subscription_topics_keeps_defaults_and_extras():
topics = build_subscription_topics(extra_roots="PL,US/rob/snd", extra_topics="msh/+/2/json/#")
assert "msh/US/#" in topics
assert "msh/PL/#" in topics
assert "msh/US/rob/snd/#" in topics
assert "msh/+/2/json/#" in topics
def test_parse_topic_metadata_preserves_root_and_channel():
meta = parse_topic_metadata("msh/US/rob/snd/2/e/LongFast/!abcd1234")
assert meta == {
"region": "US",
"root": "US/rob/snd",
"channel": "LongFast",
"mode": "e",
"version": "2",
}
def test_parse_topic_metadata_handles_json_topics():
meta = parse_topic_metadata("msh/PL/2/json/PKI/!cafefeed")
assert meta["region"] == "PL"
assert meta["root"] == "PL"
assert meta["channel"] == "PKI"
assert meta["mode"] == "json"
+24 -2
View File
@@ -40,12 +40,34 @@ class TestMilitaryBasesData:
for entry in raw:
assert entry["branch"] in known_branches, f"{entry['name']} has unknown branch: {entry['branch']}"
def test_adversary_bases_present(self):
def test_multi_country_bases_present(self):
raw = json.loads(BASES_PATH.read_text(encoding="utf-8"))
countries = {entry["country"] for entry in raw}
for expected in ("China", "Russia", "North Korea", "Taiwan"):
for expected in (
"China", "Russia", "North Korea", "Taiwan", "Japan", "Guam",
"Israel", "France", "Germany", "India", "Pakistan",
"United States", "United Kingdom", "Iran", "Italy",
"South Korea", "Australia", "Philippines", "Greece",
"Netherlands", "Spain", "Poland",
):
assert expected in countries, f"Missing bases for {expected}"
def test_nuclear_sites_present(self):
raw = json.loads(BASES_PATH.read_text(encoding="utf-8"))
nuclear = [e for e in raw if e["branch"] == "nuclear"]
countries_with_nuclear = {e["country"] for e in nuclear}
for expected in ("China", "Russia", "North Korea", "Iran", "Israel",
"India", "Pakistan", "United Kingdom", "France"):
assert expected in countries_with_nuclear, f"Missing nuclear sites for {expected}"
def test_missile_sites_present(self):
raw = json.loads(BASES_PATH.read_text(encoding="utf-8"))
missiles = [e for e in raw if e["branch"] == "missile"]
countries_with_missiles = {e["country"] for e in missiles}
for expected in ("China", "Russia", "North Korea", "Iran", "Israel",
"India", "Pakistan", "Taiwan", "South Korea", "Poland"):
assert expected in countries_with_missiles, f"Missing missile sites for {expected}"
def test_no_duplicate_names(self):
raw = json.loads(BASES_PATH.read_text(encoding="utf-8"))
names = [entry["name"] for entry in raw]
+15 -6
View File
@@ -1,8 +1,15 @@
"""Tests for network_utils — fetch_with_curl, circuit breaker, domain fail cache."""
import time
import pytest
from unittest.mock import patch, MagicMock
from services.network_utils import fetch_with_curl, _circuit_breaker, _domain_fail_cache, _cb_lock, _DummyResponse
from services.network_utils import (
fetch_with_curl,
_circuit_breaker,
_domain_fail_cache,
_cb_lock,
_DummyResponse,
)
class TestDummyResponse:
@@ -81,7 +88,7 @@ class TestCircuitBreaker:
mock_result = MagicMock()
mock_result.returncode = 0
mock_result.stdout = '{"data": true}\n200'
mock_result.stderr = ''
mock_result.stderr = ""
with patch("subprocess.run", return_value=mock_result) as mock_run:
result = fetch_with_curl("https://skip-to-curl.com/api")
@@ -138,8 +145,9 @@ class TestFetchWithCurl:
with patch("services.network_utils._session") as mock_session:
mock_session.post.return_value = mock_resp
result = fetch_with_curl("https://api.example.com/create",
method="POST", json_data={"name": "test"})
result = fetch_with_curl(
"https://api.example.com/create", method="POST", json_data={"name": "test"}
)
assert result.status_code == 200
mock_session.post.assert_called_once()
@@ -151,8 +159,9 @@ class TestFetchWithCurl:
with patch("services.network_utils._session") as mock_session:
mock_session.get.return_value = mock_resp
fetch_with_curl("https://api.example.com/data",
headers={"Authorization": "Bearer token123"})
fetch_with_curl(
"https://api.example.com/data", headers={"Authorization": "Bearer token123"}
)
call_args = mock_session.get.call_args
headers = call_args.kwargs.get("headers", {})
assert "Authorization" in headers
+48
View File
@@ -0,0 +1,48 @@
import json
import importlib.util
from pathlib import Path
import pytest
_HELPER_PATH = Path(__file__).resolve().parents[1] / "scripts" / "release_helper.py"
_SPEC = importlib.util.spec_from_file_location("release_helper", _HELPER_PATH)
assert _SPEC and _SPEC.loader
release_helper = importlib.util.module_from_spec(_SPEC)
_SPEC.loader.exec_module(release_helper)
def test_normalize_version_accepts_plain_and_prefixed():
assert release_helper._normalize_version("0.9.6") == "0.9.6"
assert release_helper._normalize_version("v0.9.6") == "0.9.6"
def test_normalize_version_rejects_non_semver_triplet():
with pytest.raises(ValueError, match="X.Y.Z"):
release_helper._normalize_version("0.9")
def test_expected_release_names():
assert release_helper.expected_tag("0.9.6") == "v0.9.6"
assert release_helper.expected_asset("0.9.6") == "ShadowBroker_v0.9.6.zip"
def test_set_version_updates_package_json(monkeypatch, tmp_path):
package_json = tmp_path / "package.json"
package_json.write_text(json.dumps({"name": "frontend", "version": "0.9.5"}) + "\n", encoding="utf-8")
monkeypatch.setattr(release_helper, "PACKAGE_JSON", package_json)
version = release_helper.set_version("0.9.6")
assert version == "0.9.6"
data = json.loads(package_json.read_text(encoding="utf-8"))
assert data["version"] == "0.9.6"
def test_sha256_file(tmp_path):
payload = tmp_path / "payload.zip"
payload.write_bytes(b"shadowbroker")
digest = release_helper.sha256_file(payload)
assert digest == "153f774fe47e71734bf608e20fd59d9ee0ad522811dc9a121bbfd3dbd79a4229"
+4 -8
View File
@@ -1,4 +1,5 @@
"""Tests for Pydantic response schemas."""
import pytest
from pydantic import ValidationError
from services.schemas import HealthResponse, RefreshResponse, AisFeedResponse, RouteResponse
@@ -11,7 +12,7 @@ class TestHealthResponse:
"last_updated": "2024-01-01T00:00:00",
"sources": {"flights": 150, "ships": 42},
"freshness": {"flights": "2024-01-01T00:00:00", "ships": "2024-01-01T00:00:00"},
"uptime_seconds": 3600
"uptime_seconds": 3600,
}
resp = HealthResponse(**data)
assert resp.status == "ok"
@@ -19,12 +20,7 @@ class TestHealthResponse:
assert resp.uptime_seconds == 3600
def test_health_response_optional_last_updated(self):
data = {
"status": "ok",
"sources": {},
"freshness": {},
"uptime_seconds": 0
}
data = {"status": "ok", "sources": {}, "freshness": {}, "uptime_seconds": 0}
resp = HealthResponse(**data)
assert resp.last_updated is None
@@ -59,7 +55,7 @@ class TestRouteResponse:
orig_loc=[40.6413, -73.7781],
dest_loc=[51.4700, -0.4543],
origin_name="JFK",
dest_name="LHR"
dest_name="LHR",
)
assert resp.origin_name == "JFK"
assert len(resp.orig_loc) == 2
+557
View File
@@ -0,0 +1,557 @@
from types import SimpleNamespace
import pytest
def test_build_sigint_snapshot_merges_map_nodes_without_duplicate_meshtastic(monkeypatch):
from services.fetchers import sigint as sigint_fetcher
from services.fetchers._store import _data_lock, latest_data
from services import sigint_bridge as sigint_bridge_mod
fake_grid = SimpleNamespace(
get_all_signals=lambda: [
{
"callsign": "!live1",
"source": "meshtastic",
"timestamp": "2026-03-22T18:00:00+00:00",
"region": "US",
},
{
"callsign": "K1ABC",
"source": "aprs",
"timestamp": "2026-03-22T17:59:00+00:00",
},
],
get_mesh_channel_stats=lambda api_nodes=None: {"total_api": len(api_nodes or [])},
)
monkeypatch.setattr(sigint_bridge_mod, "sigint_grid", fake_grid)
with _data_lock:
latest_data["meshtastic_map_nodes"] = [
{
"callsign": "!live1",
"source": "meshtastic",
"timestamp": "2026-03-22T17:40:00+00:00",
"from_api": True,
},
{
"callsign": "!map2",
"source": "meshtastic",
"timestamp": "2026-03-22T17:58:00+00:00",
"from_api": True,
},
]
signals, channel_stats, totals = sigint_fetcher.build_sigint_snapshot()
assert [sig["callsign"] for sig in signals] == ["!live1", "K1ABC", "!map2"]
assert channel_stats == {"total_api": 2}
assert totals == {
"total": 3,
"meshtastic": 2,
"meshtastic_live": 1,
"meshtastic_map": 1,
"aprs": 1,
"js8call": 0,
}
def test_rewrite_cctv_hls_playlist_proxies_relative_segments_and_keys():
import main
playlist = """#EXTM3U
#EXT-X-VERSION:3
#EXT-X-KEY:METHOD=AES-128,URI="keys/key.bin"
#EXTINF:5.0,
segment-001.ts
"""
rewritten = main._rewrite_cctv_hls_playlist(
"https://navigator-c2c.dot.ga.gov/live/cam/index.m3u8",
playlist,
)
assert '/api/cctv/media?url=https%3A%2F%2Fnavigator-c2c.dot.ga.gov%2Flive%2Fcam%2Fkeys%2Fkey.bin' in rewritten
assert '/api/cctv/media?url=https%3A%2F%2Fnavigator-c2c.dot.ga.gov%2Flive%2Fcam%2Fsegment-001.ts' in rewritten
def test_cctv_proxy_allows_known_state_dot_media_hosts():
import main
allowed_hosts = {
"wzmedia.dot.ca.gov",
"511ga.org",
"cctv.travelmidwest.com",
"micamerasimages.net",
"tripcheck.com",
}
for host in allowed_hosts:
assert main._cctv_host_allowed(host)
def test_fetch_satnogs_keeps_last_good_snapshot_on_error(monkeypatch):
from services.fetchers import infrastructure
from services.fetchers._store import _data_lock, latest_data
from services.fetchers import _store as store_mod
from services import satnogs_fetcher
with _data_lock:
latest_data["satnogs_stations"] = [{"id": "station-1"}]
latest_data["satnogs_observations"] = [{"id": "obs-1"}]
monkeypatch.setattr(store_mod, "is_any_active", lambda *args: True)
monkeypatch.setattr(satnogs_fetcher, "fetch_satnogs_stations", lambda: (_ for _ in ()).throw(ValueError("boom")))
monkeypatch.setattr(satnogs_fetcher, "fetch_satnogs_observations", lambda: [])
infrastructure.fetch_satnogs()
with _data_lock:
assert latest_data["satnogs_stations"] == [{"id": "station-1"}]
assert latest_data["satnogs_observations"] == [{"id": "obs-1"}]
def test_fetch_tinygs_keeps_last_good_snapshot_on_error(monkeypatch):
from services.fetchers import infrastructure
from services.fetchers._store import _data_lock, latest_data
from services.fetchers import _store as store_mod
from services import tinygs_fetcher
with _data_lock:
latest_data["tinygs_satellites"] = [{"norad_id": 12345}]
monkeypatch.setattr(store_mod, "is_any_active", lambda *args: True)
monkeypatch.setattr(tinygs_fetcher, "fetch_tinygs_satellites", lambda: (_ for _ in ()).throw(ValueError("boom")))
infrastructure.fetch_tinygs()
with _data_lock:
assert latest_data["tinygs_satellites"] == [{"norad_id": 12345}]
def test_caltrans_ingestor_prefers_static_image_when_stream_url_is_not_browser_safe(monkeypatch):
from services import cctv_pipeline
class _Response:
status_code = 200
def json(self):
return {
"data": [
{
"cctv": {
"location": {
"latitude": "34.123",
"longitude": "-118.456",
"locationName": "I-5 @ Main",
"route": "I-5",
},
"inService": "true",
"imageData": {
"streamingVideoURL": "viewer?id=123",
"static": {"currentImageURL": "/images/cam123.jpg"},
},
"index": 123,
}
}
]
}
monkeypatch.setattr(cctv_pipeline, "fetch_with_curl", lambda *args, **kwargs: _Response())
cameras = cctv_pipeline.CaltransIngestor().fetch_data()
assert cameras[0]["media_url"] == "https://cwwp2.dot.ca.gov/images/cam123.jpg"
assert cameras[0]["media_type"] == "image"
def test_georgia_ingestor_uses_public_511ga_feed_and_paginates(monkeypatch):
from services import cctv_pipeline
class _Response:
def __init__(self, payload):
self.status_code = 200
self._payload = payload
def json(self):
return self._payload
responses = [
{
"recordsTotal": 2,
"data": [
{
"id": 14968,
"location": "ALPH-0050: Rucker Rd at Charlotte Dr (Alpharetta)",
"latLng": {
"geography": {
"wellKnownText": "POINT (-84.33039 34.076298)",
}
},
"images": [
{
"id": 22378,
"imageUrl": "/map/Cctv/22378",
"blocked": False,
}
],
}
],
},
{
"recordsTotal": 2,
"data": [
{
"id": 14969,
"location": "BARR-0034: SR 211 at Pinot Nior Dr (Barrow)",
"latLng": {
"geography": {
"wellKnownText": "POINT (-83.81524 34.10526)",
}
},
"images": [
{
"id": 22379,
"imageUrl": "/map/Cctv/22379",
"blocked": False,
}
],
}
],
},
]
calls = []
def _fake_fetch(url, method="GET", json_data=None, timeout=15, headers=None):
calls.append(
{
"url": url,
"method": method,
"json_data": json_data,
"headers": headers,
}
)
return _Response(responses.pop(0))
monkeypatch.setattr(cctv_pipeline.GeorgiaDOTIngestor, "PAGE_SIZE", 1)
monkeypatch.setattr(cctv_pipeline, "fetch_with_curl", _fake_fetch)
cameras = cctv_pipeline.GeorgiaDOTIngestor().fetch_data()
assert [cam["id"] for cam in cameras] == ["GDOT-14968", "GDOT-14969"]
assert cameras[0]["media_url"] == "https://511ga.org/map/Cctv/22378"
assert cameras[0]["media_type"] == "image"
assert cameras[0]["lat"] == pytest.approx(34.076298)
assert cameras[0]["lon"] == pytest.approx(-84.33039)
assert len(calls) == 2
assert all(call["url"] == "https://511ga.org/List/GetData/Cameras" for call in calls)
assert all(call["method"] == "POST" for call in calls)
assert calls[0]["json_data"] == {"draw": 1, "start": 0, "length": 1}
assert calls[1]["json_data"] == {"draw": 2, "start": 1, "length": 1}
assert calls[0]["headers"]["Referer"] == "https://511ga.org/cctv"
def test_michigan_ingestor_absolutizes_relative_image_urls(monkeypatch):
from services import cctv_pipeline
class _Response:
status_code = 200
def json(self):
return [
{
"county": "id=42&lat=42.3314&lon=-83.0458",
"image": '<img src="/MiDrive/camera/image/42.jpg">',
"route": "I-94",
"location": "Downtown",
}
]
monkeypatch.setattr(cctv_pipeline, "fetch_with_curl", lambda *args, **kwargs: _Response())
cameras = cctv_pipeline.MichiganDOTIngestor().fetch_data()
assert cameras[0]["media_url"] == "https://mdotjboss.state.mi.us/MiDrive/camera/image/42.jpg"
assert cameras[0]["media_type"] == "image"
def test_austin_ingestor_prefers_source_screenshot_address_and_filters_disabled(monkeypatch):
from services import cctv_pipeline
class _Response:
def raise_for_status(self):
return None
def json(self):
return [
{
"camera_id": "316",
"camera_status": "TURNED_ON",
"location_name": "Austin Camera 316",
"screenshot_address": "https://cctv.austinmobility.io/image/316.jpg",
"location": {"coordinates": [-97.74, 30.24]},
},
{
"camera_id": "317",
"camera_status": "TURNED_OFF",
"location_name": "Austin Camera 317",
"screenshot_address": "https://cctv.austinmobility.io/image/317.jpg",
"location": {"coordinates": [-97.75, 30.25]},
},
]
monkeypatch.setattr(cctv_pipeline, "fetch_with_curl", lambda *args, **kwargs: _Response())
cameras = cctv_pipeline.AustinTXIngestor().fetch_data()
assert len(cameras) == 1
assert cameras[0]["id"] == "ATX-316"
assert cameras[0]["media_url"] == "https://cctv.austinmobility.io/image/316.jpg"
assert cameras[0]["media_type"] == "image"
def test_cctv_proxy_profiles_are_source_specific():
import main
tfl = main._cctv_proxy_profile_for_url("https://s3-eu-west-1.amazonaws.com/jamcams.tfl.gov.uk/00001.mp4")
austin = main._cctv_proxy_profile_for_url("https://cctv.austinmobility.io/image/316.jpg")
georgia = main._cctv_proxy_profile_for_url("https://511ga.org/map/Cctv/22378")
spain = main._cctv_proxy_profile_for_url("https://infocar.dgt.es/etraffic/data/camaras/1050.jpg")
assert tfl.name == "tfl-jamcam"
assert tfl.headers["Accept"].startswith("video/mp4")
assert austin.name == "austin-mobility"
assert austin.headers["Origin"] == "https://data.mobility.austin.gov"
assert georgia.name == "gdot-511ga-image"
assert georgia.timeout == (5.0, 12.0)
assert georgia.headers["Referer"] == "https://511ga.org/cctv"
assert spain.name == "dgt-spain"
assert spain.headers["Referer"] == "https://infocar.dgt.es/"
def test_cctv_proxy_preserves_upstream_http_status(monkeypatch):
import main
class _Response:
status_code = 404
headers = {}
def close(self):
return None
monkeypatch.setattr("requests.get", lambda *args, **kwargs: _Response())
request = SimpleNamespace(headers={})
profile = main._cctv_proxy_profile_for_url("https://infocar.dgt.es/etraffic/data/camaras/1050.jpg")
with pytest.raises(main.HTTPException) as exc:
main._fetch_cctv_upstream_response(request, "https://infocar.dgt.es/etraffic/data/camaras/1050.jpg", profile)
assert exc.value.status_code == 404
assert exc.value.detail == "Upstream returned 404"
def test_colorado_ingestor_prefers_preview_image_with_hls_fallback(monkeypatch):
from services import cctv_pipeline
class _Response:
status_code = 200
def json(self):
return [
{
"id": 1,
"public": True,
"active": True,
"name": "I-70 EB",
"location": {"latitude": 39.7, "longitude": -105.2, "routeId": "I-70"},
"cameraOwner": {"name": "Colorado DOT"},
"views": [
{
"url": "https://publicstreamer2.cotrip.org/rtplive/test/playlist.m3u8",
"videoPreviewUrl": "https://cocam.carsprogram.org/Snapshots/test.flv.png",
}
],
},
{
"id": 2,
"public": True,
"active": True,
"name": "US-285 NB",
"location": {"latitude": 39.6, "longitude": -105.1, "routeId": "US-285"},
"cameraOwner": {"name": "Colorado DOT"},
"views": [
{
"url": "",
"videoPreviewUrl": "https://cocam.carsprogram.org/Snapshots/test2.flv.png",
}
],
},
]
monkeypatch.setattr(cctv_pipeline, "fetch_with_curl", lambda *args, **kwargs: _Response())
cameras = cctv_pipeline.ColoradoDOTIngestor().fetch_data()
assert cameras[0]["media_url"] == "https://cocam.carsprogram.org/Snapshots/test.flv.png"
assert cameras[0]["media_type"] == "image"
assert cameras[1]["media_url"] == "https://cocam.carsprogram.org/Snapshots/test2.flv.png"
assert cameras[1]["media_type"] == "image"
def test_caltrans_ingestor_prefers_static_image_over_flaky_hls(monkeypatch):
from services import cctv_pipeline
class _Response:
status_code = 200
def json(self):
return {
"data": [
{
"cctv": {
"index": "1",
"inService": "true",
"location": {
"latitude": "37.82539",
"longitude": "-122.27291",
"locationName": "TV102 -- I-580 : West of SR-24",
},
"imageData": {
"streamingVideoURL": "https://wzmedia.dot.ca.gov/D4/W580_JWO_24_IC.stream/playlist.m3u8",
"static": {
"currentImageURL": "https://cwwp2.dot.ca.gov/data/d4/cctv/image/tv102i580westofsr24/tv102i580westofsr24.jpg"
},
},
}
}
]
}
monkeypatch.setattr(cctv_pipeline, "fetch_with_curl", lambda *args, **kwargs: _Response())
monkeypatch.setattr(cctv_pipeline.CaltransIngestor, "DISTRICTS", [4])
cameras = cctv_pipeline.CaltransIngestor().fetch_data()
assert len(cameras) == 1
assert cameras[0]["media_type"] == "image"
assert cameras[0]["media_url"].endswith("tv102i580westofsr24.jpg")
def test_dgt_ingestor_skips_dead_seed_urls(monkeypatch):
from services import cctv_pipeline
monkeypatch.setattr(
cctv_pipeline,
"_media_url_reachable",
lambda url, **kwargs: url.endswith("/1001.jpg"),
)
cameras = cctv_pipeline.DGTNationalIngestor().fetch_data()
assert len(cameras) == 1
assert cameras[0]["id"] == "DGT-1001"
def test_base_ingestor_prunes_stale_rows_for_successful_source_refresh(tmp_path, monkeypatch):
import sqlite3
from services import cctv_pipeline
db_path = tmp_path / "cctv.db"
monkeypatch.setattr(cctv_pipeline, "DB_PATH", db_path)
cctv_pipeline.init_db()
rows = [
{
"id": "DGT-1001",
"source_agency": "DGT Spain",
"lat": 40.4,
"lon": -3.7,
"direction_facing": "A-6 Madrid",
"media_url": "https://infocar.dgt.es/etraffic/data/camaras/1001.jpg",
"media_type": "image",
"refresh_rate_seconds": 300,
},
{
"id": "DGT-1002",
"source_agency": "DGT Spain",
"lat": 40.45,
"lon": -3.68,
"direction_facing": "A-2 Madrid",
"media_url": "https://infocar.dgt.es/etraffic/data/camaras/1002.jpg",
"media_type": "image",
"refresh_rate_seconds": 300,
},
]
class _Ingestor(cctv_pipeline.BaseCCTVIngestor):
def fetch_data(self):
return list(rows)
_Ingestor().ingest()
rows.pop()
_Ingestor().ingest()
conn = sqlite3.connect(db_path)
try:
stored_ids = [row[0] for row in conn.execute("select id from cameras order by id")]
finally:
conn.close()
assert stored_ids == ["DGT-1001"]
def test_osm_ingestor_keeps_only_direct_media_urls(monkeypatch):
from services import cctv_pipeline
class _Response:
status_code = 200
def json(self):
return {
"elements": [
{
"id": 101,
"lat": 39.7392,
"lon": -104.9903,
"tags": {
"camera:type": "traffic_monitoring",
"camera:url": "https://example.gov/cam101/playlist.m3u8",
"camera:direction": "270",
"operator": "Colorado DOT",
},
},
{
"id": 102,
"lat": 39.7400,
"lon": -104.9910,
"tags": {
"camera:type": "traffic_monitoring",
"website": "https://example.gov/traffic/cameras",
},
},
]
}
monkeypatch.setattr(cctv_pipeline, "fetch_with_curl", lambda *args, **kwargs: _Response())
cameras = cctv_pipeline.OSMTrafficCameraIngestor().fetch_data()
assert len(cameras) == 1
assert cameras[0]["id"] == "OSM-101"
assert cameras[0]["media_type"] == "hls"
assert cameras[0]["direction_facing"] == "270"
def test_cctv_proxy_allows_colorado_media_hosts():
import main
assert main._cctv_host_allowed("publicstreamer2.cotrip.org")
assert main._cctv_host_allowed("cocam.carsprogram.org")
def test_data_fetcher_cctv_scheduler_includes_colorado_and_osm():
from pathlib import Path
source = Path("backend/services/data_fetcher.py").read_text(encoding="utf-8")
assert "ColoradoDOTIngestor()" in source
assert "OSMTrafficCameraIngestor()" in source
+37 -9
View File
@@ -1,4 +1,5 @@
"""Tests for the shared in-memory data store."""
import threading
import time
import pytest
@@ -10,19 +11,46 @@ class TestLatestDataStructure:
def test_has_all_required_keys(self):
expected_keys = {
"last_updated", "news", "stocks", "oil", "flights", "ships",
"military_flights", "tracked_flights", "cctv", "weather",
"earthquakes", "uavs", "frontlines", "gdelt", "liveuamap",
"kiwisdr", "space_weather", "internet_outages", "firms_fires",
"datacenters"
"last_updated",
"news",
"stocks",
"oil",
"flights",
"ships",
"military_flights",
"tracked_flights",
"cctv",
"weather",
"earthquakes",
"uavs",
"frontlines",
"gdelt",
"liveuamap",
"kiwisdr",
"space_weather",
"internet_outages",
"firms_fires",
"datacenters",
}
assert expected_keys.issubset(set(latest_data.keys()))
def test_list_keys_default_to_empty_list(self):
list_keys = ["news", "flights", "ships", "military_flights",
"tracked_flights", "cctv", "earthquakes", "uavs",
"gdelt", "liveuamap", "kiwisdr", "internet_outages",
"firms_fires", "datacenters"]
list_keys = [
"news",
"flights",
"ships",
"military_flights",
"tracked_flights",
"cctv",
"earthquakes",
"uavs",
"gdelt",
"liveuamap",
"kiwisdr",
"internet_outages",
"firms_fires",
"datacenters",
]
for key in list_keys:
assert isinstance(latest_data[key], list), f"{key} should default to list"
+150
View File
@@ -0,0 +1,150 @@
from services.fetchers import trains as train_fetcher
from services.fetchers._store import _data_lock, latest_data
def setup_function():
train_fetcher._TRAIN_TRACK_CACHE.clear()
with _data_lock:
latest_data["trains"] = []
def test_merge_nonredundant_trains_prefers_higher_priority_source_and_backfills_fields():
lower_fidelity = train_fetcher._normalize_train(
source="amtrak",
raw_id="AMTK-8",
number="8",
lat=40.0000,
lng=-75.0000,
route="New York -> Chicago",
status="On Time",
operator="Shared Rail",
country="US",
observed_at="2026-03-22T18:00:00Z",
)
higher_fidelity = train_fetcher._normalize_train(
source="digitraffic",
raw_id="FIN-8",
number="8",
lat=40.0080,
lng=-75.0020,
speed_kmh=128.4,
status="Active",
operator="Shared Rail",
country="US",
observed_at="2026-03-22T18:00:05Z",
)
merged = train_fetcher._merge_nonredundant_trains([lower_fidelity], [higher_fidelity])
assert len(merged) == 1
train = merged[0]
assert train["source"] == "digitraffic"
assert train["speed_kmh"] == 128.4
assert train["route"] == "New York -> Chicago"
assert train["source_label"] == "Digitraffic Finland"
def test_motion_estimates_infer_speed_and_heading_from_previous_position():
first = train_fetcher._normalize_train(
source="amtrak",
raw_id="AMTK-14",
number="14",
lat=40.0000,
lng=-75.0000,
observed_at=1_000,
)
assert first is not None
second = train_fetcher._normalize_train(
source="amtrak",
raw_id="AMTK-14",
number="14",
lat=40.0000,
lng=-74.9900,
observed_at=1_060,
)
assert second is not None
assert second["speed_kmh"] is not None
assert 40.0 <= second["speed_kmh"] <= 80.0
assert second["heading"] is not None
assert 80.0 <= second["heading"] <= 100.0
def test_fetch_trains_merges_sources_into_store(monkeypatch):
def _batch_one():
return [
train_fetcher._normalize_train(
source="amtrak",
raw_id="AMTK-22",
number="22",
lat=41.0000,
lng=-87.0000,
route="Chicago -> St. Louis",
operator="Shared Rail",
country="US",
observed_at="2026-03-22T19:00:00Z",
)
]
def _batch_two():
return [
train_fetcher._normalize_train(
source="digitraffic",
raw_id="FIN-22",
number="22",
lat=41.0040,
lng=-87.0010,
speed_kmh=96.0,
operator="Shared Rail",
country="US",
observed_at="2026-03-22T19:00:05Z",
)
]
monkeypatch.setattr(
train_fetcher,
"_TRAIN_FETCHERS",
(("amtrak", _batch_one), ("digitraffic", _batch_two)),
)
train_fetcher.fetch_trains()
with _data_lock:
trains = list(latest_data["trains"])
assert len(trains) == 1
assert trains[0]["source"] == "digitraffic"
assert trains[0]["route"] == "Chicago -> St. Louis"
assert trains[0]["speed_kmh"] == 96.0
def test_fetch_trains_preserves_last_good_snapshot_when_refresh_fails(monkeypatch):
with _data_lock:
latest_data["trains"] = [
{
"id": "AMTK-1",
"name": "Sunset Limited",
"number": "1",
"source": "amtrak",
"lat": 32.33,
"lng": -109.76,
"speed_kmh": None,
"heading": None,
"status": "On Time",
"route": "New Orleans -> Los Angeles",
}
]
monkeypatch.setattr(
train_fetcher,
"_TRAIN_FETCHERS",
(("amtrak", lambda: []), ("digitraffic", lambda: [])),
)
train_fetcher.fetch_trains()
with _data_lock:
trains = list(latest_data["trains"])
assert len(trains) == 1
assert trains[0]["id"] == "AMTK-1"