Skip to content

vacp2p/de-mls-poc

Repository files navigation

de-mls-poc

License: MIT License: Apache 2.0

Example integration and demo for the de-mls library: a Dioxus desktop chat client that drives de-mls's Conversation handle over a Waku relay, with consensus-based membership. This repo is the runnable proof of concept — the protocol library itself lives in the de-mls repo.

What's Included

Crate / Path Description
crates/de_mls_ds Delivery service — the DeliveryService trait and a Waku relay implementation
crates/de_mls_gateway Integrator layer — per-conversation registry, identity, key-package minting, the OpenMLS provider
crates/de_mls_ui_protocol Shared UI ↔ gateway message types (AppCmd, AppEvent, MemberInfo) + hex display
crates/ui_bridge Bootstrap glue that hosts the async command loop for the desktop client
apps/de_mls_desktop_ui Dioxus desktop UI — login, chat, stewardship, and voting flows

This repo carries no protocol code: the de-mls library is consumed as a git dependency on its main branch (see the workspace Cargo.toml), so the PoC always exercises the latest library API. Run cargo update -p de-mls to pull the newest upstream commit.

Prerequisites

  • Rust (stable toolchain)
  • Nim (only if rebuilding libwaku, see below) — brew install nim

libwaku

The Waku transport links against a local logos-messaging-nim build in libs/. A prebuilt macOS arm64 libwaku.dylib is checked in, so on Apple Silicon the project builds and runs out of the box — no make needed.

Rebuild it (or build libwaku.so on Linux) with:

make          # clones, builds, and copies libwaku into ./libs/

make builds libwaku with --undef:metrics by default to avoid libwaku metrics thread-label errors in embedded-node usage. Override if needed:

make LIBWAKU_NIM_PARAMS=""

The library's install-name is @rpath/libwaku.dylib (make normalizes and re-signs it after every rebuild), and each binary-producing crate carries a build.rs that embeds the workspace libs/ rpath — so test and app binaries load the library from any checkout location.

Feature Flags

Feature Crate Description
waku de-mls-ds Enables the Waku relay transport (WakuDeliveryService) and links to libwaku

The waku feature lives on de-mls-ds; the gateway and desktop crates enable it automatically. Without it, de-mls-ds still provides the transport-agnostic DeliveryService trait and packet types — libwaku is needed only for the concrete WakuDeliveryService.

Delivery Service (crates/de_mls_ds)

The delivery service (DS) is the transport layer between the conversation and the network. It is synchronous, so it can be used from any Rust context.

Module layout

crates/de_mls_ds/src/
├── lib.rs              Public API re-exports
├── transport.rs        DeliveryService trait, OutboundPacket, InboundPacket
├── error.rs            DeliveryServiceError
├── topic_filter.rs     TopicFilter — HashSet-based allowlist for inbound routing
└── waku/               Waku relay implementation (libwaku FFI)
    ├── mod.rs          WakuDeliveryService, WakuConfig, content-topic helpers
    ├── sys.rs          Raw C FFI bindings (trampoline pattern)
    └── wrapper.rs      Safe WakuNodeCtx wrapper (Drop calls waku_stop)

Key types

Type Feature Description
DeliveryService Trait — publish() and subscribe(), both synchronous
OutboundPacket Payload + conversation id + subtopic + app id (self-message filter)
InboundPacket Payload + conversation id + subtopic + app id + timestamp
TopicFilter Allowlist used by the gateway to filter inbound packets by conversation
WakuDeliveryService waku Concrete impl — runs an embedded Waku node on a background std::thread
WakuConfig waku Node port, discv5 settings
WakuStartResult waku Returned by start() — contains the service + optional local ENR

Basic usage

use de_mls_ds::{WakuDeliveryService, WakuConfig, DeliveryService, OutboundPacket};

let result = WakuDeliveryService::start(WakuConfig {
    node_port: 60000,
    discv5: true,
    discv5_udp_port: 61000,
    ..Default::default()
})?;

let mut ds = result.service;

// Open a pull-style inbound channel (multiple receivers allowed).
let rx = ds.inbound_receiver();
std::thread::spawn(move || {
    while let Ok(pkt) = rx.recv() {
        println!("{} bytes from {}", pkt.payload.len(), pkt.conversation_id);
    }
});

// Publish a message.
ds.publish(OutboundPacket::new(
    b"hello".to_vec(), "app_msg", "my-conversation", b"instance-id",
))?;

// Shut down explicitly, or just drop all clones.
ds.shutdown();

Threading model

WakuDeliveryService::start() spawns a "waku-node" thread that owns the libwaku context. Outbound messages are queued via std::sync::mpsc; inbound events are fanned out to all subscribers. The background thread is wrapped in catch_unwind so a panic in the FFI layer won't crash the process.

Content topics

Messages are routed by Waku content topics with the format:

/{conversation_name}/{version}/{subtopic}/proto

Two subtopics are used: app_msg (application messages) and welcome (MLS Welcome messages for conversation joins). The pubsub topic is fixed to /waku/2/rs/15/1 (cluster 15, shard 1).

Self-message filtering

Each User generates a random UUID (app_id) stored in the Waku message meta field. On receive, the application drops packets whose app_id matches the local user's. Waku relay (gossipsub) does not filter self-messages natively.

Quick Start

Environment Variables

Variable Required Description
NODE_PORT Yes TCP port for the embedded Waku node
DISCV5_BOOTSTRAP_ENRS No Comma-separated ENR strings for discv5 bootstrap

discv5 peer discovery is always enabled. The discv5 UDP port is derived automatically as NODE_PORT + 1000. Use a unique NODE_PORT per local client so the embedded Waku nodes do not collide.

Running Multiple Nodes For Example

Nodes on the same clusterId/shard discover each other automatically via discv5. No external relay required — just run multiple local nodes.

Node 1 (bootstrap node):

NODE_PORT=60001 cargo run -p de-mls-desktop-ui

Copy the Local ENR: enr:-QE... line from the logs.

Nodes 2–4 (bootstrap off node 1):

NODE_PORT=60002 DISCV5_BOOTSTRAP_ENRS="enr:-QE..." cargo run -p de-mls-desktop-ui
NODE_PORT=60003 DISCV5_BOOTSTRAP_ENRS="enr:-QE..." cargo run -p de-mls-desktop-ui
NODE_PORT=60004 DISCV5_BOOTSTRAP_ENRS="enr:-QE..." cargo run -p de-mls-desktop-ui

All nodes discover each other via the DHT and form a gossipsub relay mesh automatically.

Using the Desktop UI

  • Login screen – paste an Ethereum-compatible secp256k1 private key (hex, with or without 0x) and click Enter.
    On success the app derives your wallet address, stores it in session state, and navigates to the home layout.

  • Header bar – shows the derived address and allows runtime log-level changes (errortrace).
    Log files rotate daily under apps/de_mls_desktop_ui/logs/.

  • Groups panel – lists every MLS group returned by the gateway.
    Use Create or Join to open a modal, enter the group name, and the UI automatically refreshes the list and opens the group.

  • Chat panel – displays live conversation messages for the active group.
    Compose text messages at the bottom; the UI also offers:

    • Leave group to request a self-ban (the backend fills in your address)
    • Request ban to request ban for another user Member lists are fetched automatically when a group is opened so you can pick existing members from the ban modal.
  • Consensus panel – keeps stewards and members aligned:

    • Shows whether you are a steward for the active group
    • Lists pending steward requests collected during the current epoch
    • Surfaces the proposal currently open for voting with YES/NO buttons
    • Stores the latest proposal decisions with timestamps for quick auditing

How this demo uses de-mls

de-mls is transport- and identity-agnostic: it owns the protocol and exposes a per-conversation de_mls::Conversation handle. The integration that wraps it lives in crates/de_mls_gateway — it keeps a per-conversation registry, builds each Conversation from a wallet-derived credential, owns the OpenMLS provider and key-package minting, and pumps the delivery service. The default backends (consensus over hashgraph-like-consensus, in-memory peer scoring) come from de_mls::defaults; the steward list and its service are owned by the library itself.

For the library API itself, see the de-mls repository.

Development Tips

  • cargo test -p de-mls-gateway – gateway integration tests (in-memory transport)
  • cargo build -p de-mls-gateway – build the gateway with the Waku transport (uses the checked-in libs/libwaku.dylib on macOS arm64; elsewhere run make first)
  • cargo update -p de-mls – pull the latest de-mls main commit
  • cargo fmt --all --check / cargo clippy --all-targets -- -D warnings – CI enforces both
  • RUST_BACKTRACE=full – helpful when debugging state-machine transitions during development

Logs for the desktop UI live in apps/de_mls_desktop_ui/logs/; core logs are emitted to stdout as well.

Contributing

Issues and pull requests are welcome. Please include reproduction steps, relevant logs, and test coverage where possible.

About

Example integration and demo app for the de-mls

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors