Phase 0 gates #2/#3 (two-node LXMF-over-LoRa, external Sideband interop) passed on real hardware (.116's flashed Heltec V3 RNode <-> a phone-flashed RNode running Sideband) — RNS announce, encrypted DM round-trip, and contact binding all verified live. Fixed two bugs found in the process: the Reticulum send path wasn't stamping outbound messages as E2E despite LXMF being unconditionally encrypted, and the per-message transport pill collapsed Meshcore/Meshtastic into one generic "lora" color instead of distinguishing the three radio transports. Built on top of that link: a Columba-style image/file send experience — compression-quality presets with a real transfer-time estimate (mesh.transport-advice, now device-throughput-aware), receive-side thumbnail previews + auto-render for already-local attachments, and async voice messages, all reusing the existing ContentRef/ContentInline attachment pipeline. The headline addition is genuine RNS Resource transfer support (daemon-side RNS.Link + RNS.Resource, Rust-side send_resource/resource_recv plumbing, a new "resource-mesh" transport-advice tier) so compressed photos up to 2MB now actually transfer over LoRa for Reticulum peers instead of always falling back to Tor past the small inline-chunk cap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
65 lines
2.1 KiB
Python
65 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Phase 0 gate #1 — deterministic RNS identity from the Archy seed (NO radio needed).
|
|
|
|
Proves the load-bearing assumption behind the whole "derive RNS identity from Archy
|
|
keys" decision: a node's Reticulum/LXMF destination hash is a stable, reproducible
|
|
function of its 32-byte Archy Ed25519 seed.
|
|
|
|
Run:
|
|
reticulum-daemon/.venv/bin/python reticulum-daemon/spike_identity.py
|
|
|
|
Exits non-zero (and prints FAIL) if any invariant breaks.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
|
|
from archy_rns_identity import lxmf_destination_hash, rns_private_blob
|
|
|
|
# Two fixed, non-secret test seeds (32 bytes each). Real seeds come from node_key.
|
|
SEED_A = bytes(range(32))
|
|
SEED_B = bytes((i * 7 + 3) & 0xFF for i in range(32))
|
|
|
|
|
|
def _hex(b: bytes) -> str:
|
|
return b.hex()
|
|
|
|
|
|
def main() -> int:
|
|
ok = True
|
|
|
|
# 1. The 64-byte private blob is deterministic and well-formed.
|
|
blob1 = rns_private_blob(SEED_A)
|
|
blob2 = rns_private_blob(SEED_A)
|
|
if blob1 != blob2 or len(blob1) != 64:
|
|
print(f"FAIL: private blob not deterministic/64B (len={len(blob1)})")
|
|
ok = False
|
|
else:
|
|
print(f"ok : private blob deterministic, 64B ({_hex(blob1)[:16]}…)")
|
|
|
|
# 2. Same seed -> same LXMF destination hash, across two independent builds.
|
|
h1 = lxmf_destination_hash(SEED_A)
|
|
h2 = lxmf_destination_hash(SEED_A)
|
|
if h1 != h2 or len(h1) != 16:
|
|
print(f"FAIL: destination hash not stable/16B: {_hex(h1)} vs {_hex(h2)}")
|
|
ok = False
|
|
else:
|
|
print(f"ok : destination hash stable, 16B <{_hex(h1)}>")
|
|
|
|
# 3. Different seed -> different destination (no accidental collision/constant).
|
|
h3 = lxmf_destination_hash(SEED_B)
|
|
if h3 == h1:
|
|
print(f"FAIL: distinct seeds produced the same destination <{_hex(h3)}>")
|
|
ok = False
|
|
else:
|
|
print(f"ok : distinct seed -> distinct dest <{_hex(h3)}>")
|
|
|
|
print("\nPASS — RNS identity is deterministic from the Archy seed."
|
|
if ok else "\nFAILED — see above.")
|
|
return 0 if ok else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|