Vix.cpp docs moved to vixcpp.com
Skip to content

Transport

Transport lets a Softadastra C++ SDK client start an async TCP network runtime and connect to peers. Transport is optional. A client can use the local store, persistence, sync state, and manual ticks without enabling transport. Use transport when your application needs to connect one Softadastra node to another.

Include

cpp
#include <softadastra/sdk.hpp>

What transport provides

Transport gives the SDK a network layer for peer communication.

With transport enabled, an application can:

txt
start the transport runtime
check if transport is running
connect to a peer
disconnect from a peer
process queued transport events
advance transport work during tick()

The public API stays simple:

cpp
client.start_transport();
client.transport_running();
client.connect(peer);
client.disconnect(peer);
client.process_transport_events();
client.stop_transport();

Transport is optional

A client created without transport can still write, read, persist, recover, inspect sync state, and run manual ticks.

cpp
Client client{
    ClientOptions::persistent(
        "my-app",
        "data/my-app.wal"
    )
};

Transport methods require transport to be enabled in ClientOptions.

If transport is not enabled, transport methods return an error.

txt
transport_error: transport is disabled

Enable transport

Use with_local_transport() for local development.

cpp
ClientOptions options = ClientOptions::memory_only("node-1").with_local_transport(9100);

This enables transport on:

txt
127.0.0.1:9100

Then create and open the client.

cpp
Client client{options};

const auto opened = client.open();

if (opened.is_err())
{
    return 1;
}

Start transport

After opening the client, call start_transport().

cpp
const auto started = client.start_transport();

if (started.is_err())
{
    std::cerr << started.error().code_string() << ": "
              << started.error().message() << "\n";
    return 1;
}

Check the running state:

cpp
if (client.transport_running())
{
    std::cout << "transport is running\n";
}

Stop transport

Use stop_transport() to stop the transport service.

cpp
client.stop_transport();

You can check the state again:

cpp
std::cout << "transport running: " << (client.transport_running() ? "yes" : "no") << "\n";

Peer

A peer represents another Softadastra node.

cpp
const Peer peer = Peer::local("node-2", 9101);

A valid peer must have:

txt
non-empty node id
non-empty host
non-zero port

For localhost development, use:

cpp
Peer::local("node-2", 9101);

This creates a peer pointing to:

txt
127.0.0.1:9101

Connect to a peer

Use connect() to connect to another peer.

cpp
const Peer peer = Peer::local("node-2", 9101);

const auto connected = client.connect(peer);

if (connected.is_err())
{
    std::cerr << connected.error().code_string() << ": " << connected.error().message();
    if (connected.error().has_context())
    {
        std::cerr << " (" << connected.error().context() << ")";
    }

    std::cerr << "\n";
}

If the peer is invalid, the SDK returns:

txt
invalid_argument: invalid peer

If the connection fails, the SDK returns a transport error.

Disconnect from a peer

Use disconnect() to disconnect from a peer.

cpp
const auto disconnected = client.disconnect(peer);

if (disconnected.is_err())
{
    std::cerr << disconnected.error().code_string() << ": " << disconnected.error().message() << "\n";
}

Process transport events

The async transport backend can queue events.

Use process_transport_events() to let the SDK process them explicitly.

cpp
const auto processed = client.process_transport_events(64);

if (processed.is_ok())
{
    std::cout << "processed events: " << processed.value() << "\n";
}

This is useful after:

txt
start_transport()
connect()
disconnect()
tick()
a wait loop
a manual event loop

Transport and tick

When transport is enabled, tick() also processes a small number of queued transport events.

cpp
const auto tick = client.tick();

if (tick.is_err())
{
    return 1;
}

This makes transport usable in applications that manually drive their runtime loop.

Basic transport example

cpp
#include <softadastra/sdk.hpp>
#include <iostream>

int main()
{
    using namespace softadastra::sdk;

    ClientOptions options = ClientOptions::memory_only("example-transport").with_local_transport(9100);
    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_transport();

    if (started.is_err())
    {
        std::cerr << "start_transport failed: " << started.error().code_string() << ": "
                  << started.error().message() << "\n";
        client.close();
        return 1;
    }

    std::cout << "transport_running : " << (client.transport_running() ? "yes" : "no") << "\n";

    const Peer peer = Peer::local("example-peer", 9101);
    const auto connected = client.connect(peer);

    if (connected.is_err())
    {
        std::cerr << "connect failed: " << connected.error().code_string() << ": " << connected.error().message();
        if (connected.error().has_context())
        {
            std::cerr << " (" << connected.error().context() << ")";
        }

        std::cerr << "\n";
    }
    else
    {
        std::cout << "connected peer: " << peer.node_id() << " " << peer.host() << ":" << peer.port() << "\n";
    }

    client.stop_transport();
    std::cout << "transport_running : " << (client.transport_running() ? "yes" : "no") << "\n";
    client.close();

    return 0;
}

Two-node local example

Transport is easiest to understand with two local nodes.

Node 1 listens on port 9100.

cpp
ClientOptions node1_options = ClientOptions::memory_only("node-1").with_local_transport(9100);
Client node1{node1_options};

node1.open();
node1.start_transport();

Node 2 listens on port 9101.

cpp
ClientOptions node2_options = ClientOptions::memory_only("node-2").with_local_transport(9101);
Client node2{node2_options};

node2.open();
node2.start_transport();

Node 1 can connect to node 2.

cpp
const Peer node2_peer = Peer::local("node-2", 9101);
const auto connected = node1.connect(node2_peer);

After connect, process transport events.

cpp
node1.process_transport_events();
node2.process_transport_events();

Complete two-node example

cpp
#include <softadastra/sdk.hpp>
#include <iostream>

int main()
{
    using namespace softadastra::sdk;

    Client node1{
        ClientOptions::memory_only("node-1")
            .with_local_transport(9100)
    };

    Client node2{
        ClientOptions::memory_only("node-2")
            .with_local_transport(9101)
    };

    const auto opened1 = node1.open();

    if (opened1.is_err())
    {
        std::cerr << "node1 open failed: " << opened1.error().message() << "\n";
        return 1;
    }

    const auto opened2 = node2.open();

    if (opened2.is_err())
    {
        std::cerr << "node2 open failed: " << opened2.error().message() << "\n";
        node1.close();
        return 1;
    }

    const auto started1 = node1.start_transport();

    if (started1.is_err())
    {
        std::cerr << "node1 transport failed: " << started1.error().message() << "\n";

        node2.close();
        node1.close();
        return 1;
    }

    const auto started2 = node2.start_transport();

    if (started2.is_err())
    {
        std::cerr << "node2 transport failed: " << started2.error().message() << "\n";

        node1.stop_transport();
        node2.close();
        node1.close();
        return 1;
    }

    const Peer node2_peer = Peer::local("node-2", 9101);
    const auto connected = node1.connect(node2_peer);

    if (connected.is_err())
    {
        std::cerr << "connect failed: " << connected.error().code_string() << ": "
                  << connected.error().message() << "\n";
    }
    else
    {
        std::cout << "node-1 connected to node-2\n";
    }

    node1.process_transport_events();
    node2.process_transport_events();

    node2.stop_transport();
    node1.stop_transport();

    node2.close();
    node1.close();

    return 0;
}

Transport with persistent storage

Transport can be used with persistent storage.

cpp
ClientOptions options = ClientOptions::persistent(
        "node-1",
        "data/node-1.wal"
    ).with_local_transport(9100);

This gives the node:

txt
WAL-backed local storage
sync pipeline
async TCP transport
node metadata

Transport with metadata

You can give the node a human-readable display name.

cpp
ClientOptions options = ClientOptions::memory_only("node-1")
        .with_local_transport(9100)
        .with_metadata("Local Node 1", "0.1.0");

Then read it later:

cpp
const auto info = client.node_info();

if (info.is_ok())
{
    std::cout << info.value().label() << "\n";
}

Common errors

Transport is disabled

This happens when a transport method is called without enabling transport.

txt
transport_error: transport is disabled

Fix:

cpp
ClientOptions options = ClientOptions::memory_only("node-1").with_local_transport(9100);

Transport is not initialized

This means transport was enabled but the runtime was not built correctly.

txt
transport_error: transport is not initialized

Check that client.open() succeeded.

Client is not open

Transport requires an open client.

txt
invalid_state: SDK client is not open

Fix:

cpp
const auto opened = client.open();

if (opened.is_err())
{
    return 1;
}

Invalid peer

This happens when the peer has an empty node id, empty host, or zero port.

txt
invalid_argument: invalid peer

Fix:

cpp
const Peer peer =
    Peer::local("node-2", 9101);

When to use transport

Use transport when your application needs network communication between Softadastra nodes.

Good use cases:

txt
local peer-to-peer experiments
node-to-node synchronization
LAN-first applications
manual sync runtimes
tools that need explicit peer connections

Do not enable transport if your application only needs local storage and restart recovery.

Summary

Transport is optional.

Enable it with:

cpp
ClientOptions::memory_only("node-1").with_local_transport(9100);

Start it with:

cpp
client.start_transport();

Connect to peers with:

cpp
client.connect(Peer::local("node-2", 9101));

Process events with:

cpp
client.process_transport_events();

Stop it with:

cpp
client.stop_transport();

Next, continue with Discovery.

Released under the Apache License 2.0.