For the complete documentation index, see llms.txt. This page is also available as Markdown.

Minimal integration example

The Quickstart shows what a DeRecProtocol instance looks like in isolation. This page shows the lifecycle: two in-process actors pair, cross-confirm their fingerprints, and run one ProtectSecret round end-to-end. Every println! corresponds to an event you would surface to your application.

This example covers the Owner ↔ Helper case only — enough to see the start / process / accept triangle in action. The pattern below is a stripped-down version of the canonical fixture at bindings/rust/src/protocol.rs — once you've understood this page, that file is where you go next for the fuller worked example (multi-helper sharing rounds, verification, recovery, replica-mode pairings and Secret sync, UpdateChannelInfo, unpairing).

What we are building

Two actors live in the same process:

  • Alice — Owner. Creates a contact, accepts Bob's pairing request, eventually publishes a secret via DeRecFlow::ProtectSecret.

  • Bob — Helper. Scans Alice's contact, drives DeRecFlow::Pairing, accepts the inbound share when it arrives.

The "transport" is an Arc<Mutex<VecDeque<_>>> per peer: each peer's send enqueues (endpoint, bytes); a driver loop drains the queues and feeds bytes into the destination peer's protocol.process(...). In a real deployment this would be HTTPS, WebSockets, or a message bus — the library doesn't care.

Project setup

[dependencies]
derec-library = "0.0.1-alpha.7"
derec-proto = "0.0.1-alpha.7"
tokio = { version = "1", features = ["macros", "rt"] }

The store traits (in-memory)

Four tiny HashMap-backed stores. Every method returns Box::pin(std::future::ready(...)) — sync work, async-shaped signature.

use std::collections::HashMap;
use derec_library::protocol::{
    ChannelStoreFuture, DeRecChannelStore, DeRecSecretStore, DeRecShareStore,
    DeRecUserSecretStore, MissingPolicy, SecretKind, SecretStoreFuture, SecretValue,
    Share, ShareStoreFuture,
};
use derec_library::protocol::types::{Channel, UserSecrets};
use derec_library::types::ChannelId;

#[derive(Default)]
struct ChannelStore { data: HashMap<(u64, u64), Channel> }

impl DeRecChannelStore for ChannelStore {
    fn load(&self, sid: u64, cid: ChannelId) -> ChannelStoreFuture<'_, Option<Channel>> {
        Box::pin(std::future::ready(Ok(self.data.get(&(sid, cid.0)).cloned())))
    }
    fn save(&mut self, sid: u64, ch: Channel) -> ChannelStoreFuture<'_, ()> {
        self.data.insert((sid, ch.id.0), ch);
        Box::pin(std::future::ready(Ok(())))
    }
    fn remove(&mut self, sid: u64, cid: ChannelId) -> ChannelStoreFuture<'_, bool> {
        Box::pin(std::future::ready(Ok(self.data.remove(&(sid, cid.0)).is_some())))
    }
    fn channels(&self, sid: u64) -> ChannelStoreFuture<'_, Vec<Channel>> {
        let out = self.data.iter()
            .filter(|((s, _), _)| *s == sid)
            .map(|(_, c)| c.clone())
            .collect();
        Box::pin(std::future::ready(Ok(out)))
    }
    fn link_channel(&mut self, _: u64, _: ChannelId, _: ChannelId) -> ChannelStoreFuture<'_, ()> {
        Box::pin(std::future::ready(Ok(())))
    }
    fn linked_channels(&self, _: u64, cid: ChannelId) -> ChannelStoreFuture<'_, Vec<ChannelId>> {
        Box::pin(std::future::ready(Ok(vec![cid])))
    }
}

ShareStore, SecretStore, and UserSecretStore follow the same shape — see bindings/rust/src/protocol.rs for the full code (~70 lines per trait).

Why so terse? The store traits are the application's responsibility, not the library's. The terser the better — they only need to persist values verbatim and return them by key.

The transport

A per-peer outbox the test driver drains and routes by URI.

Wiring a Peer

Bundles a DeRecProtocol instance with the transport handle so the driver can route bytes by URI.

The event loop

deliver feeds bytes into a peer's process() and auto-accepts every ActionRequired. pump drains one peer's outbox into the other's process() until the network is quiescent — that's all the "transport" we need for an in-process demo.

The full handshake + protect round

What you should see

What this leaves out

The example above covers pairing through one publish round. The canonical fixture at bindings/rust/src/protocol.rs extends it with:

  • multi-helper sharing with the threshold actually met;

  • a DeRecFlow::VerifyShares round and ShareVerified events;

  • DeRecFlow::Discovery followed by DeRecFlow::RecoverSecret and the SecretRecovered event;

  • replica-mode pairings, ReplicaSecretReceived, and ReplicaSecretAcked;

  • DeRecFlow::Unpair with both UnpairAck::Required and UnpairAck::NotRequired;

  • DeRecFlow::UpdateChannelInfo with mid-flow endpoint changeover.

Read it as the next step. Every flow follows the same start / process / accept triangle this example demonstrates.

Last updated