What the user achieves
A developer builds a Logos Core module that calls the libp2p_module Service Discovery API to advertise a named service to the network and discover other peers offering that same service.
Why it matters
Applications on the Logos network need a protocol-agnostic way to find peers offering specific services — mix nodes, relay nodes, storage providers — at runtime without hard-coding topology or peer lists. The Service Discovery API (LIP-160, built over Logos Service Discovery LIP-107 / Kad-DHT) enables any Logos Core module to perform typed, service-keyed peer lookups that work from lightweight client nodes that do not participate in DHT routing, unblocking any app that needs to wire itself into a live Logos network service.
Key components
logos-libp2p-module: A Logos Core module (type: core, interface: universal) wrapping nim-libp2p C bindings. Exposes all Service Discovery API methods (disco*) that the dependent module calls via LogosAPIClient.
Libp2pModuleOptions { .mountServiceDiscovery = true }: The struct field that activates the discovery subsystem at libp2p_module construction time; without it all disco* calls fail at runtime. Set in the libp2p_module's own config, not in the dependent module.
LogosModules / LogosAPIClient: The inter-module call interface injected into your module via setLogosModules. Use m_modules->api->getClient("libp2p_module") to obtain a client, then invokeRemoteMethod to make calls.
my_service_module: The new universal Logos Core module you write. Declares libp2p_module as a dependency, receives LogosModules* via setLogosModules, and wraps the disco* calls in typed methods.
examples/service_discovery.cpp: A 97-line self-contained two-node demo (direct Libp2pModuleImpl usage, not the Logos module pattern) that serves as a smoke test before building your own module.
Repository
https://github.com/logos-co/logos-libp2p-module
Runtime target
testnet v0.2
Prerequisites
- OS: Linux (Ubuntu 22.04+) or macOS 14+
- Hardware: 2 GB RAM sufficient for a local two-module test
- Tools: Nix with flakes enabled (required); CMake ≥ 3.22, Ninja, Qt 6, GCC 12+/Clang 15+ are provided inside
nix develop — do not install them separately
Commands and expected outputs
### Phase A — Smoke-test the example binary first
Build and run the bundled two-node demo to confirm the module is working before writing your own module.
git clone https://github.com/logos-co/logos-libp2p-module
cd logos-libp2p-module
# Build the module (first run fetches Nix deps — can take 5–20 min)
nix build -L
# Enter the dev shell, build examples
nix develop
cmake -B examples/build -S examples -DCMAKE_PREFIX_PATH="$(realpath result)"
cmake --build examples/build -j
./examples/build/service_discovery
# Starting nodes...
# Node A: starting advertising demo-service
# Node B: registering interest in demo-service
# Node B: looking up demo-service
# Node B found 1 peer(s) advertising demo-service
# peer: 12D3KooW... seq: 1 addrs: 1
# Node A: random lookup
# Random lookup returned 1 peer(s)
# Node B: unregistering interest in demo-service
# Node A: stopping advertising demo-service
# Done
Note: peer IDs are non-deterministic
### Phase B — Create your Logos Core module
cd ..
nix run github:logos-co/logos-dev-boost -- init my_service_module --type module
# The scaffold tool prefixes the output directory with logos-:
cd logos-my-service-module
**`metadata.json`** — declare the `libp2p_module` dependency:
{
"name": "my_service_module",
"version": "1.0.0",
"description": "Service discovery demo module",
"type": "core",
"interface": "universal",
"main": "my_service_module_plugin",
"dependencies": ["libp2p_module"]
}
**`src/my_service_module_impl.h`**
#pragma once
#include <string>
struct LogosModules; // forward declaration — keeps header Qt-free
class MyServiceModuleImpl {
public:
// The code generator detects setLogosModules by name and calls it in the
// generated onInit. Do not declare onInit in this header — the generator
// handles it entirely in the glue layer.
void setLogosModules(LogosModules* m) { m_modules = m; }
// serviceData: opaque bytes — any string is valid.
// Recommended: Extensible Peer Record (LIP-74) for interoperability.
// A plain string like "version=1" is sufficient for simple/internal use.
std::string advertise(const std::string& serviceId, const std::string& serviceData);
std::string discover(const std::string& serviceId);
private:
LogosModules* m_modules = nullptr;
};
**`src/my_service_module_impl.cpp`**
#include "my_service_module_impl.h"
#include "logos_sdk.h"
#include "logos_api_client.h"
#include "logos_types.h"
#include <QString>
#include <QVariantList>
#include <thread>
#include <chrono>
std::string MyServiceModuleImpl::advertise(const std::string& serviceId,
const std::string& serviceData) {
if (!m_modules) return "error: not initialized";
auto* client = m_modules->api->getClient("libp2p_module");
// start() initialises the libp2p node; use a generous timeout
auto qv = client->invokeRemoteMethod("libp2p_module", "start",
QVariantList{}, Timeout(15000));
auto r = qv.value<LogosResult>();
if (!r.success) return "start failed: " + r.getError().toStdString();
qv = client->invokeRemoteMethod("libp2p_module", "discoStart");
r = qv.value<LogosResult>();
if (!r.success) return "discoStart failed: " + r.getError().toStdString();
qv = client->invokeRemoteMethod("libp2p_module", "discoStartAdvertising",
QVariantList{QString::fromStdString(serviceId),
QString::fromStdString(serviceData)});
r = qv.value<LogosResult>();
if (!r.success) return "advertise failed: " + r.getError().toStdString();
return "advertising " + serviceId;
}
std::string MyServiceModuleImpl::discover(const std::string& serviceId) {
if (!m_modules) return "error: not initialized";
auto* client = m_modules->api->getClient("libp2p_module");
auto qv = client->invokeRemoteMethod("libp2p_module", "discoRegisterInterest",
QVariantList{QString::fromStdString(serviceId)});
auto r = qv.value<LogosResult>();
if (!r.success) return "registerInterest failed: " + r.getError().toStdString();
// DHT walk is async — allow time to propagate before querying
std::this_thread::sleep_for(std::chrono::milliseconds(500));
qv = client->invokeRemoteMethod("libp2p_module", "discoLookup",
QVariantList{QString::fromStdString(serviceId), QString("")});
r = qv.value<LogosResult>();
if (!r.success) return "lookup failed: " + r.getError().toStdString();
return r.value.toString().toStdString();
}
**`flake.nix`** — add `logos-libp2p-module` as a flake input:
inputs = {
logos-module-builder.url = "github:logos-co/logos-module-builder";
logos-libp2p-module.url = "github:logos-co/logos-libp2p-module";
# Local dev: override at build time without editing this file:
# nix build --override-input logos-libp2p-module path:../logos-libp2p-module
};
### Phase C — Build both modules
The `.#install` target runs `lgpm` internally and produces the directory structure `logoscore` requires. Developers only write `metadata.json`; the install target generates `manifest.json`, `variant`, and co-locates all `.so` files automatically.
# In logos-my-service-module:
git init && git add -A
nix build .#install -L
# Produces: result/modules/my_service_module/
# ├── manifest.json (generated from metadata.json by lgpm)
# ├── variant
# └── my_service_module_plugin.so
# In logos-libp2p-module:
cd ../logos-libp2p-module
nix build .#install -L
# Produces: result/modules/libp2p_module/
# ├── manifest.json
# ├── variant
# ├── libp2p_module_plugin.so
# └── libp2p.so (co-located automatically so $ORIGIN RUNPATH resolves)
### Phase D — Load both modules with logoscore
The `-m` flag can be repeated — each instance points at a `modules/` directory containing one or more module subdirectories.
cd ../logos-my-service-module
logoscore \
-m ../logos-libp2p-module/result/modules \
-m ./result/modules \
-l libp2p_module,my_service_module \
-c "my_service_module.advertise(logos/mix/v1, version=1)" \
-c "my_service_module.discover(logos/mix/v1)" \
--quit-on-finish
# advertising logos/mix/v1 ← from advertise()
# <peer record JSON> ← from discover()
Note: `start()` requires bootstrap peers to complete. In a local environment without network peers, `advertise()` will time out at the logoscore client level — this is expected. The build and load are correct.
Success command
./examples/build/service_discovery
Expected result
Starting nodes...
Node A: starting advertising demo-service
Node B: registering interest in demo-service
Node B: looking up demo-service
Node B found 1 peer(s) advertising demo-service
peer: 12D3KooW... seq: 1 addrs: 1
Node A: random lookup
Random lookup returned 1 peer(s)
Node B: unregistering interest in demo-service
Node A: stopping advertising demo-service
Done
Exit code 0. Peer IDs non-deterministic; count and field names stable. Any stderr or non-zero exit is a failure
Configuration details
Failure modes and limits
No response
GitHub handle
@gmelodie
Discord handle
gmelodie
Existing docs or specs
No response
Hardware requirements
Additional context
Repository: https://github.com/logos-co/logos-libp2p-module @ 5b1773ecbeda94e8b7ef72da99bb5fc14d0393e9
Canonical example: examples/service_discovery.cpp — 97-line self-contained two-node advertiser/discoverer demo
LIP-160 (Service Discovery API): logos-co/logos-lips → docs/anoncomms/raw/service-discovery-api.md — defines disco* method signatures and Advertisement/AdvertisementList C types; editor: Simon-Pierre Vivier
LIP-107 (Logos Service Discovery Protocol): docs/anoncomms/raw/logos-service-discovery.md — defines Advertiser, Discoverer, Registrar roles and client/server mode distinction; editor: Arunima Chaudhuri
LIP-74 (Extensible Peer Records): docs/anoncomms/raw/extensible-peer-records.md — recommended protobuf encoding for serviceData; extends the standard libp2p peer record with a services field in a signed envelope; compliance is not required but strongly recommended for interoperability; editor: Hanno Cornelius
Estimated time to complete
15–25 minutes (Nix build dominates on first run; cached thereafter)
Security notes
No response
What the user achieves
A developer builds a Logos Core module that calls the
libp2p_moduleService Discovery API to advertise a named service to the network and discover other peers offering that same service.Why it matters
Applications on the Logos network need a protocol-agnostic way to find peers offering specific services — mix nodes, relay nodes, storage providers — at runtime without hard-coding topology or peer lists. The Service Discovery API (LIP-160, built over Logos Service Discovery LIP-107 / Kad-DHT) enables any Logos Core module to perform typed, service-keyed peer lookups that work from lightweight client nodes that do not participate in DHT routing, unblocking any app that needs to wire itself into a live Logos network service.
Key components
logos-libp2p-module: A Logos Core module (type: core,interface: universal) wrapping nim-libp2p C bindings. Exposes all Service Discovery API methods (disco*) that the dependent module calls viaLogosAPIClient.Libp2pModuleOptions { .mountServiceDiscovery = true }: The struct field that activates the discovery subsystem atlibp2p_moduleconstruction time; without it alldisco*calls fail at runtime. Set in thelibp2p_module's own config, not in the dependent module.LogosModules/LogosAPIClient: The inter-module call interface injected into your module viasetLogosModules. Usem_modules->api->getClient("libp2p_module")to obtain a client, theninvokeRemoteMethodto make calls.my_service_module: The new universal Logos Core module you write. Declareslibp2p_moduleas a dependency, receivesLogosModules*viasetLogosModules, and wraps thedisco*calls in typed methods.examples/service_discovery.cpp: A 97-line self-contained two-node demo (directLibp2pModuleImplusage, not the Logos module pattern) that serves as a smoke test before building your own module.Repository
https://github.com/logos-co/logos-libp2p-module
Runtime target
testnet v0.2
Prerequisites
nix develop— do not install them separatelyCommands and expected outputs
Success command
./examples/build/service_discoveryExpected result
Configuration details
Failure modes and limits
No response
GitHub handle
@gmelodie
Discord handle
gmelodie
Existing docs or specs
No response
Hardware requirements
Additional context
Repository: https://github.com/logos-co/logos-libp2p-module @
5b1773ecbeda94e8b7ef72da99bb5fc14d0393e9Canonical example:
examples/service_discovery.cpp— 97-line self-contained two-node advertiser/discoverer demoLIP-160 (Service Discovery API):
logos-co/logos-lips→docs/anoncomms/raw/service-discovery-api.md— definesdisco*method signatures andAdvertisement/AdvertisementListC types; editor: Simon-Pierre VivierLIP-107 (Logos Service Discovery Protocol):
docs/anoncomms/raw/logos-service-discovery.md— defines Advertiser, Discoverer, Registrar roles and client/server mode distinction; editor: Arunima ChaudhuriLIP-74 (Extensible Peer Records):
docs/anoncomms/raw/extensible-peer-records.md— recommended protobuf encoding forserviceData; extends the standard libp2p peer record with aservicesfield in a signed envelope; compliance is not required but strongly recommended for interoperability; editor: Hanno CorneliusEstimated time to complete
15–25 minutes (Nix build dominates on first run; cached thereafter)
Security notes
No response