# Logos Delivery — Protocol Stack Architecture Study > A top-to-bottom study of how the logos-delivery (nwaku/Waku v2 fork) stack works: > the layered API, the core protocols, how they interconnect, their dependencies, > the messaging API, the reliable channel API, and the C FFI library. --- ## 0. The big picture `logos-delivery` is a Nim implementation of a libp2p messaging stack (a rebrand/fork of nwaku — Waku v2). It is organized as **three stacked API layers** sitting on top of a **suite of libp2p protocols**, with a **C FFI library** wrapping the whole thing. ``` ┌──────────────────────────────────────────────────────┐ C / mobile │ library/ (C ABI: liblogosdelivery.{so,dylib,a}) │ applications │ - logosdelivery_* (stable, messaging tier) │ │ - waku_* (kernel tier, raw protocols) │ └───────────────────────────┬──────────────────────────┘ │ one worker thread + chronos loop ┌───────────────────────────▼──────────────────────────┐ │ LogosDelivery (logos_delivery/logos_delivery.nim) │ │ pure concentrator: wires 3 layers, drives lifecycle │ └───────────────────────────┬──────────────────────────┘ ┌──────────────────────┬─────────────┴───────────────┐ ▼ ▼ ▼ ReliableChannelManager → MessagingClient ──────────────► Waku (WakuNode) (E2E ordered, dedup, (send/recv with delivery (relay, lightpush, segmentation, repair) confirmation + offline backfill) filter, store, sync, discovery, RLN, mix) ``` The root type `LogosDelivery` (`logos_delivery/logos_delivery.nim`) is a **pure concentrator**: it owns exactly one instance of each layer and chains them `Waku ← MessagingClient ← ReliableChannelManager`, each layer driving the one below. `new` builds bottom-up; `start` runs bottom-up; `stop` runs top-down so higher layers drain first. A recurring architectural device is the **broker context** (`brokers/broker_context`): an in-process event/request bus that lets layers communicate without hard imports. - `EventBroker` — fire-and-forget pub/sub (`MessageSentEvent`, `MessageReceivedEvent`, `ReadyToSendEvent`, …). - `RequestBroker` — request/response with a single installable provider (`Encrypt`/`Decrypt`, `RequestRelayShard`, `RequestGenerateRlnProof`, shard/topic health). No provider ⇒ the request errors. This bus is what keeps the reliable-channel layer fully decoupled from the layers below it, and what lets RLN proof generation and shard resolution cross module boundaries. --- ## 1. Core data model (`waku/waku_core/`) Every protocol imports these types. They are the universal vocabulary. ### WakuMessage — the universal envelope `waku_core/message/message.nim` | field | wire# | meaning | |---|---|---| | `payload: seq[byte]` | 1 | the application bytes (required) | | `contentTopic: string` | 2 | logical topic (required) | | `version: uint32` | 3 | legacy encryption discriminator | | `timestamp: int64` | 10 | sender-set, **nanoseconds** (zigzag-encoded) | | `meta: seq[byte]` | 11 | opaque marker, ≤ 64 bytes (used to route to the reliable-channel layer) | | `proof: seq[byte]` | 21 | RLN rate-limit proof (RFC 17), opaque at core layer | | `ephemeral: bool` | 31 | if true ⇒ never stored/archived | Max message size: `DefaultMaxWakuMessageSize = 150 KiB`. ### Content topics & sharding - **Content topic** (`topics/content_topic.nim`): `////` (optionally with a leading generation). A string; structured form is `NsContentTopic`. - **Pubsub topic / shard** (`topics/pubsub_topic.nim`): `RelayShard{clusterId, shardId}` rendered as `/waku/2/rs//`. Default = `/waku/2/rs/0/0`. - **Auto-sharding** (`topics/sharding.nim`): a content topic deterministically maps to a shard via `sha256(application & version)` → last 64 bits mod shardCount. This is how a publish that supplies *only* a content topic deduces its pubsub topic. ### Deterministic hashing `waku_core/message/digest.nim` — `computeMessageHash(pubsubTopic, msg) = SHA256(pubsubTopic ++ payload ++ contentTopic ++ meta ++ BE(timestamp))`. **`version`, `ephemeral`, and `proof` are deliberately excluded**, so the hash is a stable content identity even after an RLN proof is attached. This single 32-byte hash is used everywhere: relay message ID basis, store cursor, archive primary key, store-sync fingerprint, dedup keys. ### Protocol codecs (one place: `waku_core/codecs.nim`) | Protocol | Codec | |---|---| | Relay | `/vac/waku/relay/2.0.0` | | Lightpush v3 / legacy | `/vac/waku/lightpush/3.0.0` · `/vac/waku/lightpush/2.0.0-beta1` | | Filter subscribe / push | `/vac/waku/filter-subscribe/2.0.0-beta1` · `/vac/waku/filter-push/2.0.0-beta1` | | Store query | `/vac/waku/store-query/3.0.0` | | Store-sync reconciliation / transfer | `/vac/waku/reconciliation/1.0.0` · `/vac/waku/transfer/1.0.0` | | Metadata | `WakuMetadataCodec` | | Peer exchange | `/vac/waku/peer-exchange/2.0.0-alpha1` | | Rendezvous | `/vac/waku/rendezvous/1.0.0` | --- ## 2. The core protocols (`waku/`) ### 2.1 Relay — the gossipsub mesh (the hub) `waku_relay/protocol.nim` — `WakuRelay = ref object of GossipSub` (subclasses libp2p GossipSub directly, RFC 29 tuning: `d=6, dLow=4, dHigh=8`, flood-publish, peer scoring, bad-peer disconnect). - **Validators are the key extension point.** `addValidator` appends an app-level validator; `generateOrderedValidator` wraps them into one libp2p validator registered per topic. Any non-`Accept` short-circuits. **RLN is just one such validator.** The same validator list also gates lightpush via `validateMessage`. - `subscribe` / `publish` / `unsubscribe`; `publish` returns peer count or a `PublishOutcome` error (`NoPeersToPublish`, `DuplicateMessage`, …). - **Message ID** = `sha256(message.data)` (raw payload, implementation-agnostic). - Per-topic **health** (UNHEALTHY / MINIMALLY / SUFFICIENTLY based on mesh peer count) is computed on a loop and emitted on the broker. **Relay is the hub:** when a full node subscribes to a shard, `node/subscription_manager.nim` installs one handler that fans every received message through, in order: `trace(metrics) → filter.handleMessage → archive.handleMessage → storeSync.messageIngress → MessageSeenEvent(broker) → legacy app handler`. That single chain is how relayed traffic reaches filter clients, the store, the sync mirror, and the app. ### 2.2 Lightpush — publish without running relay `waku_lightpush/` (v3) and `waku_lightpush_legacy/` (v2). A light **client** sends one `WakuMessage` to a **server** (a relay-running service node), which validates and publishes it on the mesh. - v3 wire: `LightpushRequest{requestId, pubSubTopic?, message}` → `LightPushResponse{requestId, statusCode, statusDesc?, relayPeerCount?}` with HTTP-like codes (200, 400, 413, 420, 429, 503, **504 OUT_OF_RLN_PROOF**, **505 NO_PEERS_TO_RELAY**). Auto-sharding can fill in the pubsub topic. - The **relay bridge** is `callbacks.nim:getRelayPushHandler(relay, rlnPeer)`: attach RLN proof if needed → `relay.validateMessage` → `relay.publish`. So a lightpush request *becomes* a relay publish; the client never joins the mesh. - Legacy v2 differences: single `PushRPC` envelope, boolean-only `PushResponse`, required pubsub topic, no auto-sharding, errors collapsed to "not published to any peer." ### 2.3 Filter v2 — receive without running relay `waku_filter_v2/`. A light client registers `(pubsubTopic, contentTopic)` criteria with a server and receives only matching messages. - Two codecs: a **subscribe/request** channel (client→server: PING/SUBSCRIBE/UNSUBSCRIBE/UNSUBSCRIBE_ALL) and a **push** channel (server→client `MessagePush`, no response). - Subscriptions are **soft-state with a 5-minute TTL** kept alive by client PINGs; the server prunes via a 1-minute maintenance loop and dedups pushes with a 2-minute cache. - The filter *server* only has messages to push because it runs relay and is fed by the `subscription_manager` fan-out (`handleMessage`). The filter module has **no compile-time dependency on relay** — coupling is only at node assembly. **Node types:** a **full/service node** mounts relay (joins the mesh) and optionally lightpush/filter/store *servers* to serve light clients. A **light node** runs no mesh: it publishes via the lightpush *client* and receives via the filter *client*. ### 2.4 Store / Archive / Store-sync — history & durability - **Archive** (`waku_archive/`): local persistence behind an `ArchiveDriver` (driver pattern). Backends: **SQLite** (keyset-cursor pagination, schema v10), **Postgres** (partitioned, production/high-volume, `when defined(postgres)`), and an in-memory bounded `SortedSet` queue (25k cap). Ingests via `handleMessage` (computes hash, validates ±20 s timestamp drift, drops ephemeral, `driver.put`). Retention policies: time / capacity / size, on a 30-min loop. - **Store** (`waku_store/`): the request/response query protocol over the archive. `StoreQueryRequest` carries content-topic/time filters, explicit `messageHashes`, and a **content-addressed cursor** (a `WakuMessageHash`, not an offset) for stable keyset pagination. The server is a thin shell: `requestHandler → archive.findMessages`. The client has `query` (one peer) and `queryToAny` (random peers, retry). `resume.nim` lets a node catch up on reconnect by querying everything since `max(lastOnline, now−6h)`. - **Store-sync** (`waku_store_sync/`): peer-to-peer **Range-Based Set Reconciliation** (Negentropy-family). Two sub-protocols: **reconciliation** (recursive 8-way range splitting with XOR fingerprints, escalating to explicit item-set exchange below a 100-element threshold) figures out the diff; **transfer** ships the missing messages. Seeds an in-memory `SeqStorage` mirror from the archive; transferred messages re-enter via `syncMessageIngress` (skips the timestamp validator — and inbound transfer messages are **not yet RLN-verified**, a known gap). ### 2.5 Networking, discovery & peers (`waku/net/`, `discovery/`, `waku_enr/`, `waku_metadata/`, `waku_peer_exchange/`, `waku_rendezvous/`) - **PeerManager** (`node/peer_manager/`): the connectivity engine. Wraps the libp2p `Switch` and an extended **PeerStore** (custom "books": ENR, Shard, Source/origin, Connection, failure/backoff). Distinguishes **relay peers** (managed in bulk to in/out target counts, ~⅔ inbound) from **service peers** (store/filter/lightpush/px, individually pinned in `serviceSlots`). A 30 s **connectivity loop** maintains relay targets — shard- and capability-aware in sharded mode — with exponential backoff (120 s × 4ⁿ), parallel-dial caps, IP-colocation limits, and periodic store pruning + SQLite persistence. - **Discovery** all funnels into `peerManager.addPeer(peer, origin)`: - **discv5** (`waku_discv5.nim`): Ethereum node discovery, filters ENRs by a shard/cluster predicate, `searchLoop` feeds peers. - **DNS** (EIP-1459 `enrtree://`): resolves bootstrap ENRs fed into discv5 setup. - **Kademlia** (libp2p DHT): specialized here for **Mix**-capable peer/service discovery. - **Rendezvous** (libp2p): cluster-scoped `rs//mix` namespace; distributes signed `WakuPeerRecord`s carrying mix pubkeys. - **Peer exchange** (RFC 34): light nodes (no discv5) ask full nodes for a reservoir-sampled set of discv5-origin ENRs. - **ENR** (`waku_enr/`): encodes a `waku2` **capability bitfield** (Relay=0, Store=1, Filter=2, Lightpush=3, **Sync=4, Mix=5** — the last two extend RFC 31), a len-prefixed `multiaddrs` field, and sharding (`rs` indices list < 64 shards, `rsv` 128-byte bit vector ≥ 64). - **Metadata** (`waku_metadata/`): exchanged immediately on each connection; carries `clusterId` + live shards. **Gating** lives in the peer manager (`refreshPeerMetadata` on peer-join): **cluster mismatch ⇒ immediate disconnect+delete** — this hard-partitions the network by cluster. Shard info only refines selection, it doesn't gate. ### 2.6 RLN — Rate Limiting Nullifier (live anti-spam) `waku/rln/`. Enforces "N messages per epoch per member" cryptographically (RFC 17) and **plugs into relay as a gossipsub validator** (`mountRlnRelay` → `relay.addValidator`). - **Validation** (`rln.nim:validateMessage`): parse `msg.proof` → timestamp within 20 s → epoch matches → Merkle **root in the accepted window (≤ 50 roots)** → **zk-SNARK verify** of the signal (`payload ++ contentTopic ++ BE(timestamp)`) → **double-signal check** against the nullifier log (`Spam ⇒ Reject`). - **Membership** comes from an on-chain Ethereum contract via web3 (`OnchainGroupManager`): no local Merkle tree; it caches this node's Merkle proof and refreshes accepted roots from `root()`/`getRecentRoots()`. A static/off-chain mode also exists. - **Rate-limit enforcement is dual**: client-side `NonceManager` (monotonic message-id per epoch, `NonceLimitReached` at the limit) *and* cryptographic — exceeding the limit reuses a slot, producing two Shamir shares on one line under the same nullifier, which (a) flags spam and (b) lets anyone interpolate the offender's identity secret (slashing primitive; on-chain slashing is a TODO). - Proof **generation** at publish is broker-mediated (`RequestGenerateRlnProof`); the lightpush server attaches a proof if one is missing. ### 2.7 Mix — sender anonymity (opt-in) `waku/waku_mix/protocol.nim` — `WakuMix = ref object of MixProtocol`, a thin wrapper over the external `libp2p_mix` **Sphinx mixnet** library. Sending is not a custom publish: the caller wraps a normal lightpush stream via `wakuMix.toConnection(MixDestination.exitNode(peer), WakuLightPushCodec, MixParameters(...))` — per-hop exponential delay (mean 50) defeats timing correlation, SURBs enable anonymous replies. The mix pubkey (Curve25519) is advertised via rendezvous peer records, `WakuInfo`, and Kademlia. Disabled by default; min pool size 4. ### 2.8 Incentivization (PoC, **not wired in**) `waku/incentivization/` — RFC 73 proof-of-concept: on-chain ETH-transfer txid eligibility verification (`EligibilityManager`) + a ternary peer reputation table. Present and tested but **not connected to any live path** (verified by grep). --- ## 3. The node integration hub (`waku/node/` + `waku/factory/`) - **`WakuNode`** (`node/waku_node.nim`): a `ref object` holding *every* protocol instance (relay, archive, store(+client/resume/sync/transfer), filter(+client), rln, lightpush(+legacy+clients), peer exchange, metadata, mix, kademlia, rendezvous, libp2p ping, peer manager, switch, broker context, subscription manager). Each protocol is attached by a `mount*` proc; `start`/`stop` cascade the lifecycle. Publish/subscribe/query APIs live in `node/waku_node/{relay,lightpush,filter,store,…}.nim`. - **`WakuConf`** (`factory/waku_conf.nim`): one validated config object. Convention: **`Option[...]Conf` being `some` enables that protocol.** Assembled by per-concern `conf_builder/*` builders. - **`node_factory.nim`**: `setupNode → builder.build (switch/peerManager/WakuNode.new) → setupProtocols (conditional mount*, order matters: metadata → mix → kademlia → store → autosharding → **relay** → rendezvous/ping → **RLN (after relay)** → lightpush/filter/px) → startNode (connect bootstrap, start peer manager)`. --- ## 4. The messaging API (`logos_delivery/messaging/` + `logos_delivery/api/`) This layer adds **delivery confirmation** and **offline backfill** on top of raw transport. - **`MessageEnvelope`** (`api/types.nim`): `{contentTopic, payload, ephemeral, meta}` → `toWakuMessage` stamps the timestamp. **`RequestId`** is a correlation handle returned by `send`, and it is **shared across all three layers** — that is what lets the channel layer correlate its segments with messaging-layer events. - **`MessagingClient.send`**: auto-subscribes to the content topic (so the node sees its own broadcast), mints a `RequestId`, builds a `DeliveryTask`, fires it at the `SendService`, and returns the id immediately (fire-and-forget; completion arrives as events). - **SendService** (reinforced publish): a fallback **processor chain** — `RelaySendProcessor` (primary, gossipsub) → `LightpushSendProcessor` (fallback for edge/light nodes) — plus a 1 s service loop that retries and, when `useP2PReliability` is on, **validates delivery by querying a store node** for the message hash. State→event mapping: - `SuccessfullyPropagated` → **`MessagePropagatedEvent`** (reached neighbors) - `SuccessfullyValidated` → **`MessageSentEvent`** (confirmed archived in a store node) - `FailedToDeliver` → **`MessageErrorEvent`** - **RecvService**: dedups inbound (by hash), emits **`MessageReceivedEvent`**, and on offline→online reconnect **backfills** missed messages from a store node over the offline gap. --- ## 5. The reliable channel API (`logos_delivery/channels/`) A `ReliableChannel` gives an **end-to-end ordered, de-duplicated, gap-repairing** channel on top of the messaging client. Spec: *reliable-channel-api*. One channel = one `ChannelId` (= SDS channel id). **Egress pipeline:** `segmentation → SDS (reliability) → rate-limit → encryption → dispatch`. **Ingress pipeline:** the reverse. The manager builds a default `SendHandler` over `MessagingClient.send`, so callers never wire transport. ### The reliability mechanism (SDS) The actual state machine is the external **nim-sds** library (`ReliabilityManager`); `channels/scalable_data_sync/` is the adapter mapping one manager to one channel. The scheme: - **Outgoing**: each message gets a unique `SdsMessageID = keccak256(participantId ++ wrap-time-ns ++ content)`. `wrapOutgoingMessage` attaches a **Lamport timestamp** (channel clock) and a **causal history** (the last N delivered message IDs, N = `causalHistorySize`, default 2), and registers it in the outgoing buffer awaiting acknowledgement. - **Acknowledgement** is implicit: a sent message is acked when it is **observed as a causal dependency of some peer's later message**. Unacked messages are **retransmitted** every `acknowledgementTimeoutMs` (default 5 s) up to `maxRetransmissions` (default 5). - **Incoming**: deserialize, drop foreign channels, dedup against history (bloom/history), then: - duplicate ⇒ consumed; - **missing causal dependencies ⇒ park** the segment in `pendingContent` (bounded 32) until deps arrive; - otherwise ⇒ deliver this message *plus* any parked segments that just became deliverable — **in causal order** (ingress is serialized by a lock). - **SDS-R repair**: an `onRepairReady` callback rebroadcasts a full SDS envelope (skips the rate-limit queue, always `ephemeral`) to heal gaps for peers. - **Persistence** (`waku/persistency/sds_persistency.nim`): snapshot model — lamport clock + outgoing/incoming + repair buffers persisted to SQLite; history reconstructed by sorting on `(lamportTimestamp, messageId)` — the same total order SDS uses for delivery. Channel state survives restart. ### Per-message-send bookkeeping `ReliableChannel.send` segments the payload, SDS-wraps each segment, records a `ChannelReqState{totalExpectedSegments, awaitingDispatch, inflightMessagingIds, confirmed, failed}`, and enqueues. On dispatch each segment is encrypted (`Encrypt` request broker), tagged with `meta = "RELIABLE-CHANNEL-API/1"` (the ingress-routing marker), and sent via the messaging client. The channel listens for the messaging-layer `MessageSentEvent`/`MessageErrorEvent` (correlated by the shared `RequestId`) and, when all segments resolve, emits **`ChannelMessageSentEvent`** or **`ChannelMessageErrorEvent`**. Inbound, it filters by the `meta` marker + content topic, then `Decrypt → SDS handleIncoming → reassemble → ChannelMessageReceivedEvent`. > **Component maturity (as of this study):** `segmentation` is currently a skeleton (one segment > = whole payload; Reed-Solomon parity planned), `rate_limit_manager` is a pass-through (RLN-epoch > budgeting planned), and `encryption` ships a no-op provider you must opt into. The SDS reliability > core is real (delegated to nim-sds). Encryption requires installing an `Encrypt`/`Decrypt` > provider on the broker or the request errors. --- ## 6. The C FFI library (`library/`) Wraps `LogosDelivery` as a shared/static C library (`liblogosdelivery.{so,dylib,a}`). All FFI plumbing (threading, request marshalling, callbacks, Nim runtime init) is delegated to the external **nim-ffi** framework (pinned, not vendored in this checkout). ### Two-tier C API - **Stable tier** `logosdelivery_*` (`library/logos_delivery_api/`, header `liblogosdelivery.h`): `create_node`, `start_node`, `stop_node`, `destroy`, `subscribe`/`unsubscribe`, `send`, `set_event_callback`. Calls into the **high-level** messaging client; protocol selection is hidden. - **Kernel tier** `waku_*` (`library/kernel_api/`, header `liblogosdelivery_kernel.h`, "use at your own risk"): ~45 functions reaching **straight into the node's protocols** — relay pub/sub, filter, lightpush, store query, peer manager, discovery, ping. Raw `WakuMessage`/pubsub-level access. Both tiers are `include`d into a single compilation unit and share one `FFIContext`; the split is header/stability, not a binary boundary. The tiers mirror `LogosDelivery`'s own composition (kernel→Waku node, stable→MessagingClient, events surface the reliable-channel layer). ### Calling & threading model - Universal callback: `void (*FFICallBack)(int callerRet, const char *msg, size_t len, void *userData)`; return codes `RET_OK=0 / RET_ERR=1 / RET_MISSING_CALLBACK=2`. - Universal shape: `fn(void *ctx, FFICallBack cb, void *userData, …args)` returns a synchronous dispatch status; the **real result/JSON/error arrives asynchronously via the callback**. - **One worker thread, one chronos event loop.** `ctx` is an `FFIContext[LogosDelivery]` owning a dedicated thread + watchdog + an SPSC request channel + signals. A C call packs args into an `FFIThreadRequest`, hands it across the channel, and the worker runs the async body and fires the callback **on the worker thread** (so callbacks must be fast, non-blocking, thread-safe). The C example bridges back with `volatile` flags + polling. ### Event system Two callback channels: (1) per-call result callbacks, and (2) a single **event callback** (`set_event_callback`) fired repeatedly for the node's lifetime. Events are JSON strings. Two families: - **Low-level handlers** (wired at node creation): `message`, `relay_topic_health_change`, `connection_change`, `node_health_change`. - **High-level broker events** (registered in `start_node`): `message_sent`, `message_error`, `message_propagated`, `message_received`, `connection_status_change`. > Known rough edges flagged during the study: connection status surfaces under two different > `eventType` strings (`node_health_change` vs `connection_status_change`), and `MESSAGE_EVENTS.md` > documents only three of the message events. --- ## 7. End-to-end: the life of a message **Sending (full app stack):** 1. App calls `ReliableChannel.send(channelId, payload)`. 2. Segmentation → SDS wrap (lamport ts + causal history, registered for ack) → rate-limit queue → encrypt → tag `meta="RELIABLE-CHANNEL-API/1"`. 3. `MessagingClient.send` → auto-subscribe + `DeliveryTask` → `SendService` chain: `relay.publish` (RLN proof attached via broker), or lightpush fallback. 4. Relay validators (incl. RLN) accept → gossipsub propagates → `MessagePropagatedEvent`. 5. SendService later queries a store node for the hash → `MessageSentEvent` (confirmed durable). 6. Channel tallies segment confirmations → `ChannelMessageSentEvent`. **Receiving:** 1. Relay delivers the message; `subscription_manager` fan-out → filter, **archive (persist)**, store-sync mirror, and `MessageSeenEvent`. 2. `RecvService` dedups → `MessageReceivedEvent`. 3. Channel (matching `meta` + content topic) → decrypt → SDS `handleIncoming` (causal ordering, parks gaps, repairs) → reassemble → `ChannelMessageReceivedEvent`. 4. If the node was offline, `RecvService`/store-resume backfills the gap from a store node; SDS-R repairs any causal holes. **Light node variant:** steps 3–4 of sending use the **lightpush client** instead of joining the mesh; receiving uses the **filter client** (with PINGs to keep the 5-min subscription alive), plus store backfill. --- ## 8. Dependency summary - Everything depends on **`waku_core`** (WakuMessage, topics, sharding, digest, codecs). - **relay** is the hub; **lightpush** depends on relay (via the push handler) and RLN; **filter** is decoupled at compile time and coupled only at node assembly. - **store / store-sync / store-resume** all depend on **archive**; archive depends only on its driver + core. - **discovery** (discv5/DNS/kademlia/rendezvous/PX) all converge on `peerManager.addPeer`; **metadata** gates connections by cluster. - **RLN** plugs into relay as a validator; reads membership from an on-chain contract. - **mix** layers under lightpush; advertised via rendezvous/kademlia. - The **broker context** decouples cross-layer calls (encryption, shard/health resolution, RLN proof gen) and carries the events the upper layers and the FFI subscribe to. - The three API layers stack `Waku ← MessagingClient ← ReliableChannelManager`; the **FFI** wraps all three and runs them on a single dedicated worker thread. --- ## Appendix — where to look | Concern | Start here | |---|---| | Top-level wiring & lifecycle | `logos_delivery/logos_delivery.nim` | | Messaging API | `logos_delivery/messaging/messaging_client.nim`, `api/types.nim` | | Send reliability / store validation | `logos_delivery/messaging/delivery_service/` | | Reliable channels | `logos_delivery/channels/reliable_channel.nim`, `…/reliable_channel_manager.nim` | | SDS adapter | `logos_delivery/channels/scalable_data_sync/scalable_data_sync.nim` | | Core data types | `logos_delivery/waku/waku_core/` | | Node hub & mounting | `logos_delivery/waku/node/waku_node.nim`, `factory/node_factory.nim` | | Relay | `logos_delivery/waku/waku_relay/protocol.nim` | | Lightpush / Filter | `logos_delivery/waku/waku_lightpush/`, `waku_filter_v2/` | | Store / Archive / Sync | `logos_delivery/waku/waku_store/`, `waku_archive/`, `waku_store_sync/` | | Peers & discovery | `logos_delivery/waku/node/peer_manager/`, `discovery/`, `waku_enr/` | | RLN anti-spam | `logos_delivery/waku/rln/` | | Mix privacy | `logos_delivery/waku/waku_mix/protocol.nim` | | C FFI | `library/` (+ `library/README.md`, `MESSAGE_EVENTS.md`) |