Files
varde/docs/redoal-integration.md
bl 258fa072aa Milestone 6: DiscoveryProvider seam, signed announcements, redoal sketch
The discovery module defines the seam: DiscoveryProvider (subscribe/
announce over 32-byte TopicKeys) and a signed Announcement carrying
root hash, ed25519 author, metadata and provider addresses. Signatures
cover a deterministic postcard encoding including the topic (no cross-
channel replay) and the provider identities (addresses stay refreshable
hints). LanDiscovery conforms to the trait — mdns sightings become
locally-authored announcements, one per pinned root — and the daemon's
auto-sync now runs entirely through it: verify, index unconditionally,
fetch only for trusted authors from allowlisted providers on already-
pinned incomplete roots. The milestone-4 real-mdns sync test passes
unchanged through the new path. Verification unit tests cover round
trip, tampered root, wrong topic, forged author, mismatched signing
key, and serde survival. docs/redoal-integration.md sketches the
gesture-topic gossip provider against this contract.

Also: fix a flaky hang in the socket-activation test (dup2(3,3) leaves
CLOEXEC set when the listener already sits on fd 3; parent's listener
copy masked daemon death), and give the test client a read-timeout hang
guard. Add a top-level README.

Dependencies: ed25519-dalek (Signature type; same implementation iroh
keys use), postcard (deterministic signed encoding, iroh's canonical
compact codec).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:51:57 +02:00

3.8 KiB

redoal integration sketch

How a gesture-derived discovery provider plugs into varde without touching the daemon. Nothing here is implemented; this documents the contract the v1 seam already enforces.

The seam

varde_daemon::discovery defines:

pub trait DiscoveryProvider: Send + Sync {
    fn subscribe(&self, topic: TopicKey) -> BoxStream<SignedAnnouncement>;
    fn announce(&self, topic: TopicKey, ann: SignedAnnouncement) -> Result<()>;
}
  • TopicKey is an opaque 32-byte channel id.
  • Announcement { root, author, meta, providers } is signed (ed25519, the same key type as iroh NodeIds) over a deterministic postcard encoding that includes the topic, so announcements can't be replayed across channels. Provider socket addresses are excluded from the signature (relays may refresh them); provider identities are covered.

The daemon consumes any provider identically (Daemon::on_announcement):

  1. verify the signature — drop on failure;
  2. index the announcement (all of them — discovery is the open tier);
  3. auto-fetch only when all of these hold:
    • the author key is trusted (or is the local daemon),
    • the root is already pinned locally and incomplete,
    • at least one announced provider is on the peer allowlist.

v1 ships one implementation, LanDiscovery: mDNS sightings are turned into locally-authored announcements ("peer N may hold pinned root R"). It proves the seam because the daemon's entire auto-sync path runs through subscribe() — swap the provider and nothing above changes.

The redoal provider (future)

redoal turns a shared physical gesture (two phones shaken together, a tap pattern, ...) into a high-entropy shared secret between the people present. That secret derives a gossip topic:

TopicKey = BLAKE3-derive_key("redoal/v1/topic", gesture_secret)

RedoalDiscovery would:

  • join the iroh-gossip swarm for that TopicId (iroh-gossip rides the same iroh endpoint varde already has — no new transport);
  • subscribe(topic): yield gossip messages decoded as SignedAnnouncements. Verification and consent stay in the daemon — the provider is a dumb pipe by design;
  • announce(topic, ann): broadcast to the swarm. Unlike the LAN provider (where announce is a no-op because mDNS already advertises presence), gossip announce actively publishes — the daemon must only call it for content the user shared to that topic.

Trust bootstrap

The gesture is the consent ceremony. Deriving from the same secret:

author trust:   the gesture exchange includes both parties' public keys
                (signed with the gesture key), so each side adds the
                other to the *author* allowlist for that topic.
peer trust:     announcements carry provider NodeIds; on a gestured
                topic, providers named by a trusted author get scoped
                peer trust (serve/fetch for that topic's roots only).

That last point needs one extension to the v1 model: today peer trust is global (meta.json allowlist). Topic-scoped trust would add trusted_for: {topic → keys}, checked in the same two places the global list is checked now (provider accept gate, fetch candidate filter). The seam anticipates this: both checks already live in the daemon, not in the provider.

What stays true regardless of provider

  • Discovery open, replication explicit: indexing an announcement never moves bytes; only the trust checks above do.
  • All fetched data is BLAKE3-verified by iroh-blobs.
  • Metered/rate-limit posture applies unchanged — providers sit above the transport, the limiters below it.

Sizing

RedoalDiscovery is roughly: iroh-gossip dependency, ~200 lines of provider glue, the topic-scoped trust extension in meta.json, and a varde-ctl topic join <secret> command. No daemon architecture changes.