tinyweb/rns_client.py
Derick Phan 9a9b5e0617
Add Reticulum-native subscriptions and sync-based distributed search
- Subscriptions now use Reticulum destination hashes instead of HTTP URLs
- All subscription syncing happens over encrypted RNS links (rns_client.py)
- Add remote_pages table for synced content from subscriptions
- Search results now include pages from synced subscriptions, grouped by source
- Remove HTTP dependency from subscription handlers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 22:51:22 -07:00

78 lines
2.4 KiB
Python

import time
import RNS
APP_NAME = "tinyweb"
ASPECTS = ["server"]
REQUEST_TIMEOUT = 30
def fetch_remote_sites(dest_hash_hex):
"""
Connect to a remote TinyWeb instance over Reticulum and fetch its
shared sites. Returns the response dict from /api/sites, or raises
an exception on failure.
"""
dest_hash = bytes.fromhex(dest_hash_hex)
# Resolve path if needed
if not RNS.Transport.has_path(dest_hash):
RNS.Transport.request_path(dest_hash)
elapsed = 0
while not RNS.Transport.has_path(dest_hash) and elapsed < 15:
time.sleep(0.5)
elapsed += 0.5
if not RNS.Transport.has_path(dest_hash):
raise ConnectionError(f"Could not find path to {dest_hash_hex}")
server_identity = RNS.Identity.recall(dest_hash)
if server_identity is None:
raise ConnectionError(f"Could not recall identity for {dest_hash_hex}")
destination = RNS.Destination(
server_identity,
RNS.Destination.OUT,
RNS.Destination.SINGLE,
APP_NAME,
*ASPECTS,
)
# Establish link
link = RNS.Link(destination)
elapsed = 0
while link.status == RNS.Link.PENDING and elapsed < 15:
time.sleep(0.25)
elapsed += 0.25
if link.status != RNS.Link.ACTIVE:
raise ConnectionError(f"Could not establish link to {dest_hash_hex}")
try:
# Request /api/sites
request_data = {
"method": "GET",
"path": "/api/sites",
"query": {},
"body": {},
"gateway_host": "",
}
receipt = link.request("/tinyweb", data=request_data, timeout=REQUEST_TIMEOUT)
elapsed = 0
done = (RNS.RequestReceipt.READY, RNS.RequestReceipt.DELIVERED, RNS.RequestReceipt.FAILED)
while receipt.get_status() not in done and elapsed < REQUEST_TIMEOUT:
time.sleep(0.5)
elapsed += 0.5
if receipt.get_status() in (RNS.RequestReceipt.READY, RNS.RequestReceipt.DELIVERED):
resp = receipt.get_response()
if resp["status"] == 403:
raise PermissionError("That instance has sharing disabled.")
if resp["status"] != 200:
raise ConnectionError(f"Remote returned status {resp['status']}")
import json
return json.loads(resp["body"])
else:
raise ConnectionError(f"Request failed or timed out")
finally:
link.teardown()