# 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: ```rust pub trait DiscoveryProvider: Send + Sync { fn subscribe(&self, topic: TopicKey) -> BoxStream; 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 `SignedAnnouncement`s. 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 ` command. No daemon architecture changes.