Discovery
Discovery lets a Softadastra C++ SDK client find peers.
Discovery is optional. A client can use local storage, persistence, restart recovery, sync state, manual ticks, and transport without enabling discovery.
Use discovery when your application needs to discover nearby or known Softadastra nodes without manually hardcoding every peer.
Include
#include <softadastra/sdk.hpp>What discovery provides
Discovery gives the SDK a peer discovery layer.
With discovery enabled, an application can:
start the discovery service
check if discovery is running
list discovered peers
stop discoveryThe public API is simple:
client.start_discovery();
client.discovery_running();
client.peers();
client.stop_discovery();Discovery is optional
A client created without discovery can still write, read, persist, recover, inspect sync state, and use transport manually.
Client client{
ClientOptions::persistent(
"my-app",
"data/my-app.wal"
)
};Discovery methods require discovery to be enabled in ClientOptions.
If discovery is not enabled, discovery methods return an error.
discovery_error: discovery is disabledEnable discovery
Use with_local_discovery() for local development.
ClientOptions options = ClientOptions::memory_only("node-1").with_local_discovery(5051);This enables discovery on:
127.0.0.1:5051Then create and open the client.
Client client{options};
const auto opened = client.open();
if (opened.is_err())
{
return 1;
}Start discovery
After opening the client, call start_discovery().
const auto started = client.start_discovery();
if (started.is_err())
{
std::cerr << started.error().code_string() << ": " << started.error().message() << "\n";
return 1;
}Check the running state:
if (client.discovery_running())
{
std::cout << "discovery is running\n";
}Stop discovery
Use stop_discovery() to stop the discovery service.
client.stop_discovery();You can check the state again:
std::cout << "discovery running: " << (client.discovery_running() ? "yes" : "no") << "\n";List discovered peers
Use peers() to read the discovered peer list.
const auto peers = client.peers();
if (peers.is_err())
{
std::cerr << peers.error().code_string() << ": " << peers.error().message() << "\n";
return 1;
}
for (const auto &peer : peers.value())
{
std::cout << peer.node_id() << " " << peer.host() << ":" << peer.port() << "\n";
}peers() returns:
Result<std::vector<Peer>, Error>Peer format
Each discovered peer is exposed as:
softadastra::sdk::PeerA peer contains:
node_id
host
portExample:
for (const auto &peer : peers.value())
{
std::cout << "node_id: " << peer.node_id() << "\n";
std::cout << "host: " << peer.host() << "\n";
std::cout << "port: " << peer.port() << "\n";
}Discovery and transport
Discovery depends on the transport runtime internally.
When discovery is enabled, the SDK also builds the transport layer internally so discovered peers can be represented as connectable nodes.
For normal usage, this is enough:
ClientOptions options = ClientOptions::memory_only("node-1").with_local_discovery(5051);Then:
Client client{options};
client.open();
client.start_discovery();Discovery address
with_local_discovery() binds discovery to localhost.
ClientOptions options = ClientOptions::memory_only("node-1").with_local_discovery(5051);For a custom bind host, use with_discovery().
ClientOptions options = ClientOptions::memory_only("node-1").with_discovery("0.0.0.0", 5051);Announced address
When discovery is enabled, the SDK announces the node address so other peers can find it.
If a transport port is configured, discovery announces the transport port.
If no transport port is configured, discovery falls back to the discovery port.
This keeps local discovery usable even when an application only enables discovery.
Basic discovery example
#include <softadastra/sdk.hpp>
#include <iostream>
namespace
{
void print_peer(const softadastra::sdk::Peer &peer)
{
std::cout << " - node_id : " << peer.node_id() << "\n";
std::cout << " host : " << peer.host() << "\n";
std::cout << " port : " << peer.port() << "\n";
}
}
int main()
{
using namespace softadastra::sdk;
ClientOptions options = ClientOptions::memory_only("example-discovery").with_local_discovery(5051);
Client client{options};
const auto opened = client.open();
if (opened.is_err())
{
std::cerr << "open failed: " << opened.error().code_string() << ": "
<< opened.error().message() << "\n";
return 1;
}
const auto started = client.start_discovery();
if (started.is_err())
{
std::cerr << "start_discovery failed: " << started.error().code_string() << ": "
<< started.error().message() << "\n";
client.close();
return 1;
}
std::cout << "discovery_running : " << (client.discovery_running() ? "yes" : "no") << "\n";
const auto peers = client.peers();
if (peers.is_err())
{
std::cerr << "peers failed: " << peers.error().code_string() << ": "
<< peers.error().message() << "\n";
client.stop_discovery();
client.close();
return 1;
}
std::cout << "discovered_peers : " << peers.value().size() << "\n";
if (peers.value().empty())
{
std::cout << " no peers discovered\n";
}
else
{
for (const auto &peer : peers.value())
{
print_peer(peer);
}
}
client.stop_discovery();
std::cout << "discovery_running : " << (client.discovery_running() ? "yes" : "no") << "\n";
client.close();
return 0;
}Expected output
If no other peers are running, the output can look like this:
discovery_running : yes
discovered_peers : 0
no peers discovered
discovery_running : noThis is valid. It means discovery started successfully, but no peers were found yet.
Discovery with metadata
You can give the node a human-readable name.
ClientOptions options = ClientOptions::memory_only("node-1")
.with_local_discovery(5051)
.with_metadata("Discovery Node", "0.1.0");Then read the node metadata:
const auto info = client.node_info();
if (info.is_ok())
{
std::cout << info.value().label() << "\n";
}Discovery with transport
You can enable both transport and discovery.
ClientOptions options = ClientOptions::memory_only("node-1")
.with_local_transport(9100)
.with_local_discovery(5051);This gives the node:
async TCP transport on 127.0.0.1:9100
discovery on 127.0.0.1:5051
metadata service
peer listingWhen peers are discovered, the application can connect to them with transport.
const auto discovered = client.peers();
if (discovered.is_ok())
{
for (const auto &peer : discovered.value())
{
client.connect(peer);
}
}Common errors
Discovery is disabled
This happens when a discovery method is called without enabling discovery.
discovery_error: discovery is disabledFix:
ClientOptions options = ClientOptions::memory_only("node-1").with_local_discovery(5051);Discovery is not initialized
This means discovery was enabled but the runtime was not built correctly.
discovery_error: discovery is not initializedCheck that client.open() succeeded.
Client is not open
Discovery requires an open client.
invalid_state: SDK client is not openFix:
const auto opened = client.open();
if (opened.is_err())
{
return 1;
}Invalid discovery port
A discovery port of 0 is invalid when discovery is enabled.
ClientOptions options = ClientOptions::memory_only("node-1").with_local_discovery(0);
if (!options.is_valid())
{
std::cout << "invalid options\n";
}Use a non-zero port.
ClientOptions options = ClientOptions::memory_only("node-1").with_local_discovery(5051);When to use discovery
Use discovery when your application needs peer discovery.
Good use cases:
local peer discovery
LAN-first applications
node-to-node experiments
tools that should find nearby peers
applications that should avoid manual peer configurationDo not enable discovery if your application only needs local storage, restart recovery, or manual peer configuration.
Summary
Discovery is optional.
Enable it with:
ClientOptions::memory_only("node-1").with_local_discovery(5051);Start it with:
client.start_discovery();List peers with:
client.peers();Stop it with:
client.stop_discovery();Next, continue with Metadata.