The Logos C++ SDK (`logos-cpp-sdk`) provides a client-side library and code generation tools for building Logos modules and applications. It abstracts the underlying transport (Qt Remote Objects over local sockets, or a plain-C++ TCP / TCP+TLS RPC stack built on Boost.Asio) and token management, enabling modules to register themselves and call other modules without dealing with sockets or the remote registry directly. The SDK also provides functionality for code generation.
| `cpp-generator` | Code generator that creates type-safe C++ wrappers for Logos modules |
| `cpp` | Client-side SDK that wraps RPC functionality. Modules link against this SDK to call the core and other modules |
| Headers (`include/`) | Public API headers for SDK classes and generated wrappers |
### Other Repository Components
| Component | Purpose |
|-----------|---------|
| `nix/` | Nix build scripts |
## 2. Architecture
### 2.1 High-level Structure
At a high level, the C++ SDK fits into the Logos ecosystem as follows:
**Logos Core**– The core library manages module lifecycle and provides the remote object registry. The SDK connects to this registry to enable inter-module communication.
**Modules**– Modules use the SDK to:
- Register themselves for remote access (via `LogosAPIProvider`)
The C++ SDK (logos-cpp-sdk/cpp) abstracts the transport layer (Qt Remote Objects for local sockets, plain Boost.Asio for TCP / TCP+TLS) and token management so that modules can register themselves and call other modules without dealing with sockets or the remote registry. The SDK exposes `LogosAPI` that owns a provider (`LogosAPIProvider`) and a cache of clients (`LogosAPIClient`) for different target modules. Internally it relies on a TokenManager to authenticate remote calls. The SDK is asynchronous: calls return immediately and results are delivered via callbacks/signals.
Transports are described by `LogosTransportConfig` (protocol = `LocalSocket | Tcp | TcpSsl`, host/port, optional CA/cert/key, codec = `Json | Cbor`). A `LogosTransportSet` (= `std::vector<LogosTransportConfig>`) lets a single provider publish on multiple endpoints simultaneously — e.g. a daemon binding both `LocalSocket` (for in-host modules) and `TcpSsl` (for remote clients). `LogosTransportFactory::createHost(cfg, registryUrl)` chooses between `RemoteTransportHost` (Qt LocalSocket) and `PlainTransportHost` (TCP / TCP+SSL) based on `cfg.protocol`; it returns nullptr on failure (e.g. SSL cert load, TCP bind), and `LogosAPIProvider` skips that transport. `LogosTransportConfigGlobal::setDefault()`/`getDefault()` lets a process override the SDK-wide default.
However under the hood the API abstracts things. In this case the call gets re-routed with the appropriate token and goes to a ModuleProxy object that wraps the actual Object. The ModuleProxy validates the call before forwarding it to the object method.
- `ModuleProxy` is exposed with `QRemoteObjectRegistryHost`
- The call between `LogosAPIClient` and `ModuleProxy` is made using `QRemoteObjectNode`
### 3.1.1 LogosAPI
`LogosAPI` is the entry point for modules and applications. It encapsulates a single provider and a cache of clients and exposes methods to obtain these. A module creates one `LogosAPI` instance during initialisation and passes its own name to it. Internally the constructor constructs a new `LogosAPIProvider` and retrieves a reference to the singleton `TokenManager`. A `QHash` caches `LogosAPIClient` instances keyed by target module so repeated calls reuse the same client.
**Responsibilities**:
- Initialise and own a `LogosAPIProvider` and a `TokenManager`
- Create and cache `LogosAPIClient` objects for calling other modules
- Provide access to the provider and token manager through getters
`LogosAPI` hides the details of registry hosts and consumer connections. Module writers obtain a client via `getClient()` and then call remote methods through that client. They never deal directly with sockets or tokens; the API attaches tokens automatically on calls.
| `explicit LogosAPI(const QString& moduleName, QObject *parent = nullptr)` | Constructs an API for `moduleName` using the process-global default transport. Initialises a provider and token manager. |
| `LogosAPI(const QString& moduleName, LogosTransportSet transports, QObject *parent = nullptr)` | Constructs an API whose provider publishes on every transport in `transports` (one host per entry). Empty set = use the global default. |
| `LogosAPIProvider* getProvider() const` | Returns the provider that modules use to register themselves for remote access. |
| `LogosAPIClient* getClient(const QString& targetModule) const` | Returns a client for calling `targetModule`. If a client for that module does not yet exist, it creates one and caches it. |
| `LogosAPIClient* getClient(const QString& targetModule, const LogosTransportConfig& transport) const` | Returns a client that dials `targetModule` over an explicit transport instead of the global default. Cached per `(target, transport)`. |
| `void setCapabilityModuleTransport(const LogosTransportConfig& transport)` | Sets the transport used by the SDK's auto-`requestModule` flow (which dials `capability_module` to fetch a per-target token). Required when the daemon advertises `capability_module` on a non-default transport. |
| `TokenManager* getTokenManager() const` | Returns the token manager used to store and validate authentication tokens.(note: this is meant to be internal but it's exposed for debug purposes) |
`LogosAPIProvider` runs on the module’s side and exposes local objects through one or more transports. It owns one `LogosTransportHost` per configured transport (created via `LogosTransportFactory::createHost`) plus a `ModuleProxy` wrapping the actual module instance. When a module calls `registerObject(name, object)`, the provider optionally calls `object->initLogos(LogosAPI*)` if that method exists, then wraps the object in a `ModuleProxy` and publishes it over every host. Only one object can be registered per provider; additional attempts return false
- For each configured `LogosTransportConfig`, create a transport host (`RemoteTransportHost` for `LocalSocket` — bound to `local:logos_<moduleName>`; `PlainTransportHost` for `Tcp`/`TcpSsl`). Hosts that fail to bind (e.g. SSL cert load failure) are skipped.
| `~LogosAPIProvider()` | Destructor; `QRemoteObjectRegistryHost` and `ModuleProxy` are deleted as children. |
| `bool registerObject(const QString& name, QObject *object)` | Registers `object` under `name`. If the object defines `initLogos(LogosAPI*)` it is invoked first, then the object is wrapped in a `ModuleProxy` and exposed via the registry. Only one registration per provider is allowed; subsequent calls return false. |
| `bool saveToken(const QString& fromModuleName, const QString& token)` | Persists a token for `fromModuleName` by delegating to the module proxy. |
| `void onEventResponse(QObject *replica, const QString& eventName, const QVariantList& data)` | Emits an event on the subscriber’s replica by invoking its `eventResponse` method. |
**Usage Example**
This API is used internally (by the core or logos host) and is not meant to be used by the Developer directly.
- Call `initLogos` if it exists and pass `LogosAPI` to the module
- Wrap `baseWakuPlugin` with `ModuleProxy`
- Expose the wrapped object with `QRemoteObjectRegistryHost` on `local:logos_<basePlugin->name()>`
### 3.1.2.1 ModuleProxy (internal)
`ModuleProxy` is an internal class used by the provider to expose a module safely. It wraps the real module object and validates every incoming call against the stored authentication tokens. Each proxy keeps a map of tokens keyed by module name.
Modules never instantiate `ModuleProxy` directly; it is created by the provider and published through Qt Remote Objects. Remote callers interact with it implicitly via `LogosAPIClient` and `LogosAPIConsumer`.
**Responsibilities**:
- Validate the authentication token on every remote call. In `callRemoteMethod()` the proxy checks that a non‑empty token is provided and verifies it against the `TokenManager`. Calls with invalid or missing tokens return an empty `QVariant`.
- Dispatch method calls to the underlying module using Qt’s meta‑object system. The proxy locates the requested method by name and argument count, supports up to five arguments, and handles various return types including `void`, `bool`, `int`, `QString`, `QVariant`, `QJsonArray` and `QStringList`
- Introspect the wrapped module’s API via `getPluginMethods()`, returning a `QJsonArray` describing each method (name, signature, return type, parameters, and — when the method has a doc comment in its header — a `description`)
- Introspect the wrapped module’s events via `getPluginEvents()`, returning a `QJsonArray` describing each `logos_events:` declaration (name, signature, parameters, and — when documented — a `description`; no return type, since events are void). `getPluginInterface()` returns both methods and events in one array (each entry tagged with a `"type"`). All three are filtered views over the provider's single `getMethods()` call — there is no separate `getEvents()` vtable method, which keeps the provider ABI stable across SDK versions
- Allow the trusted core / capability module to inform this module of a token via `informModuleToken(authToken, moduleName, token)`. This is a **privileged** operation: planting a token would otherwise let any peer authorize itself (see the security note below), so `informModuleToken` validates `authToken` against this module's own seed secret (stored under the `core` / `capability_module` keys by the host at module init) and rejects the call — failing closed — unless the caller presents that secret
| `QVariant callRemoteMethod(const QString& authToken, const QString& methodName, const QVariantList& args = {})` | Validates `authToken`, locates `methodName` on the module and invokes it. Supports up to five arguments and multiple return types. This will forward the request to the wrapped object. |
| `bool informModuleToken(const QString& authToken, const QString& moduleName, const QString& token)` | Stores `token` for `moduleName` in the global `TokenManager`, letting this module know that another module will communicate using that token. **Privileged**: `authToken` must match this module's seed secret (the value the host stores under the `core` / `capability_module` keys at init), so only the trusted core / capability module can plant a token. Empty or non-matching tokens are rejected (fail-closed) and `false` is returned. |
| `QJsonArray getPluginMethods()` | Enumerates the wrapped module’s methods and returns a JSON array with signatures and parameters. Generated provider/universal modules also include a per-method `description` (from the method's header doc comment); legacy modules introspected via Qt meta‑object have none. |
| `QJsonArray getPluginEvents()` | Enumerates the wrapped module’s `logos_events:` declarations and returns a JSON array with names, signatures, and parameters (plus a per-event `description` from the declaration's doc comment). Universal modules report their declared events; legacy/provider modules return an empty array. |
| `QJsonArray getPluginInterface()` | Returns the module’s whole interface — methods and events together — each entry tagged with a `"type"` (`"method"`/`"event"`). `getPluginMethods`/`getPluginEvents` are the filtered views; all three derive from one `getMethods()` call (no separate `getEvents()` vtable method, so the provider ABI stays stable). |
`LogosAPIClient` provides a high‑level, asynchronous interface for invoking methods on remote modules and subscribing to events. Each client is bound to a single target module and holds two `LogosAPIConsumer`s: one for the target module (`m_consumer`) and one pre-built for `capability_module` (`m_capability_consumer`). The second is needed because the SDK's auto-`requestModule` flow inside `invokeRemoteMethod{,Async}` dials `capability_module` to fetch a per-target token, and the daemon may advertise `capability_module` on a different transport than the target (e.g. target on TCP, capability_module on TCP at a sibling port). Pre-building both consumers in the constructor keeps the hot path free of per-call lookups.
`LogosAPIClient` should be used by modules to perform calls and event subscriptions. It hides the details of connecting, reconnection, token lookup, argument packaging and result deserialization.
**Responsibilities**:
- Manage the connection to the remote registry and acquire remote object replicas via the consumer
- Retrieve and attach authentication tokens for calls. Before every call, the client looks up the token for the target module and passes it to the consumer
- Provide convenience overloads of `invokeRemoteMethod()`` for 0–5 arguments, returning a `QVariant` result
- Register event listeners with optional callbacks and route event responses back to the origin module
- Forward token information to another module by calling `informModuleToken()` on the consumer
| `explicit LogosAPIClient(const QString& moduleToTalkTo, const QString& originModule, TokenManager* tokenManager, QObject *parent = nullptr)` | Constructs a client bound to `moduleToTalkTo`. Both target and `capability_module` consumers use the process-global default transport. |
| `LogosAPIClient(const QString& moduleToTalkTo, const QString& originModule, TokenManager* tokenManager, const LogosTransportConfig& targetTransport, const LogosTransportConfig& capabilityTransport, QObject *parent = nullptr)` | Two-transport constructor: explicit transports for the target module *and* `capability_module`. Use this when the daemon advertises them on different endpoints. |
| `QVariant invokeRemoteMethod(const QString& objectName, const QString& methodName, const QVariantList& args = {}, Timeout timeout = Timeout())` and overloads for 1–5 arguments | Synchronous call: looks up the caller’s auth token and passes it to the consumer. Returns the result or an invalid `QVariant` on failure. |
| `void invokeRemoteMethodAsync(..., AsyncResultCallback callback, Timeout timeout = Timeout())` and overloads for 0–5 arguments | Truly async call: chains an async `requestModule` (to `capability_module` via `m_capability_consumer`) → in its callback, an async invoke of the real method. Returns immediately; the callback delivers the result. |
| `void onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName, std::function<void(const QString&, const QVariantList&)> callback)` | Subscribes to `eventName` emitted by `originObject` and invokes `callback` when triggered. |
| `void onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName)` | Subscribes to an event by connecting `originObject`’s `eventResponse` signal to `destinationObject`’s `onEventResponse` slot. |
| `void onEventResponse(QObject* replica, const QString& eventName, const QVariantList& data)` | Internal helper; emits `eventResponse` on the replica when events arrive. |
| `bool informModuleToken(const QString& authToken, const QString& moduleName, const QString& token)` and `bool informModuleToken_module(const QString& authToken, const QString& originModule, const QString& moduleName, const QString& token)` | Forwards a token to another module via the consumer. |
| `TokenManager* getTokenManager() const` | Returns the token manager used by this client. |
| `QString getToken(const QString& moduleName)` | Helper that retrieves the token for `moduleName` from the token manager. |
**Usage**
This is the most common API that a module developer will use:
`LogosAPIConsumer` is the low‑level component used by `LogosAPIClient`. It owns a `LogosTransportConnection` (built via `LogosTransportFactory::createConnection`) which abstracts over the underlying wire protocol — Qt Remote Objects for `LocalSocket`, plain Boost.Asio for `Tcp`/`TcpSsl`. It acquires remote object handles, invokes methods, and handles event subscription and token propagation. For `LocalSocket` the registry URL is `local:logos_<targetModule>`; for TCP transports it's the host:port from the `LogosTransportConfig`.
`LogosAPIConsumer` should not be used directly by most developers; it is an implementation detail of `LogosAPIClient`. It provides fine‑grained control over remote calls and event handling and encapsulates the chosen transport implementation.
- Manage a `LogosTransportConnection` to the registry and reconnect when needed
- Acquire dynamic handles to remote objects and wait for them to be ready within a timeout
- Invoke remote methods through the transport (via `ModuleProxy` for QRO, or RPC framing for Tcp/TcpSsl)
- Register event listeners: store callbacks per event and connect to the remote object’s `eventResponse` signal. When events arrive, `invokeCallback()` iterates through all registered callbacks and invokes them
- Register simple event subscriptions by connecting the remote `eventResponse` signal directly to a destination slot
- Forward tokens to another module through a remote `informModuleToken` call on the module’s proxy and support informing tokens for modules loaded by the origin module
| `QObject* requestObject(const QString& objectName, int timeoutMs = 20000)` | Acquires a remote object replica and waits for it to be ready. Returns `nullptr` on failure. |
| `bool isConnected() const` | Reports whether the consumer is connected to the registry. |
| `QString registryUrl() const` | Returns the registry URL. |
| `bool reconnect()` | Reconnects by rebuilding the underlying `LogosTransportConnection` and calling `connectToRegistry()`. |
| `bool connectToRegistry()` (private) | Opens the transport connection (QRO `connectToNode` for LocalSocket, TCP/TLS handshake for plain transports) and updates `m_connected`. |
| `QVariant invokeRemoteMethod(const QString& authToken, const QString& objectName, const QString& methodName, const QVariantList& args = {}, int timeoutMs = 20000)` | Invokes a remote method through the transport with the provided token. For QRO this dispatches via the replica's `ModuleProxy::callRemoteMethod`; for plain transports it serializes via the configured wire codec and waits for the response. |
| `void onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName, std::function<void(const QString&, const QVariantList&)> callback)` | Registers a callback for `eventName` by storing it and ensuring the connection to the origin object’s `eventResponse` signal. |
| `void onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName)` | Registers an event listener without a callback by connecting `originObject->eventResponse` to the `destinationObject->onEventResponse` slot. |
| `void invokeCallback(const QString& eventName, const QVariantList& data)` (slot) | Invokes all callbacks registered for `eventName`. |
| `bool informModuleToken(const QString& authToken, const QString& moduleName, const QString& token)` | Informs the capability module’s proxy about a token for `moduleName`. |
| `bool informModuleToken_module(const QString& authToken, const QString& originModule, const QString& moduleName, const QString& token)` | Informs a module loaded by `originModule` about a token via that module’s proxy. |
### 3.1.4 Generated C++ wrappers (logos_sdk)
To simplify calling methods across modules with proper C++ types, a generator produces typed wrappers into `logos-cpp-sdk/cpp/generated/` and an umbrella pair `logos_sdk.h`/`logos_sdk.cpp`. The umbrella aggregates one wrapper class per module and exposes them via a convenience struct `LogosModules`.
`setEventSource()` stores the QObject that actually declares the `eventResponse(QString, QVariantList)` signal—typically the plugin instance itself. The wrapper uses that cached pointer when you call the shorthand `trigger(eventName, data)` so it can emit the signal on the correct sender. If you skip `setEventSource()`, use the explicit overload `trigger(eventName, QObject* source, ...)` to provide the emitting object each time.
Build integration (consumers of wrappers):
- Compile the umbrella source once per binary to avoid duplicate symbols: add `logos-cpp-sdk/cpp/generated/logos_sdk.cpp` to your target sources.
- Add `logos-cpp-sdk/cpp/generated` to your include paths.
- Wrappers are generated during the modules/app build by a custom step; see below.
- Outputs for each dependency module: `<module>_api.h/.cpp`, plus umbrella `logos_sdk.h/.cpp`.
- Always emits `core_manager_api.h/.cpp` and wires `CoreManager` into the umbrella even if the metadata does not list `core_manager`. The core manager plug‑in is built into the core process and therefore cannot be introspected via `QPluginLoader`; generating it unconditionally guarantees SDK consumers can manage the core (initialise, enumerate plug‑ins, load/unload, etc.) without hand‑written bindings.
- Return types are mapped appropriately (e.g., `bool`, `int`, `double`, `float`, `QString`, `QStringList`, `QJsonArray`, or `QVariant`).
CLI flags and behavior:
- **--metadata <file>**: Path to a module's `metadata.json`. The generator parses the `dependencies` array to determine which modules to emit wrappers for.
- **--module-dir <dir>**: Directory containing built module plugins (e.g., `chat_plugin.so/.dylib`, `waku_module_plugin.*`). For each dependency, the generator loads the corresponding plugin to introspect its interface and generate wrappers.
- If only a plugin path is provided (without `--metadata`), the generator produces wrappers for that single plugin.
- Artifacts are written under the repository root’s `logos-cpp-sdk/cpp/generated` directory and include: one `<module>_api.h/.cpp` per dependency and the umbrella `logos_sdk.h/.cpp` that aggregates them into `LogosModules`.
What it does under the hood:
- Loads each dependency plugin via `QPluginLoader`, creates an instance and enumerates its invokable methods using Qt meta‑object reflection.
- For each method, emits a type‑safe C++ wrapper function that marshals arguments and converts results from `QVariant` to the expected C++ types.
- Regenerates an umbrella header/source to include all generated module wrappers and expose a convenience aggregator `LogosModules` with members like `logos.chat` and `logos.core_manager`.
How code generation works (step‑by‑step):
1. Input resolution
- If `--metadata` is provided, the generator parses the JSON and reads the `dependencies` array.
- It combines each dependency name with a platform suffix (e.g., `_plugin.dylib` on macOS, `.so` on Linux, `.dll` on Windows) and looks for those plugin files under `--module-dir`.
- If only a single plugin path is provided (no `--metadata`), it generates wrappers for that one plugin.
2. Plugin loading and introspection
- Each target plugin is loaded with `QPluginLoader` and instantiated.
- The generator walks the plugin instance’s Qt meta‑object (`QMetaObject`) to find invokable methods, capturing name, return type and parameters. This produces a method list the generator uses as its source of truth.
3. Header/source emission per module
- The module name is converted to PascalCase for the wrapper class name (e.g., `chat` → `Chat`).
- A header `<module>_api.h` declares a wrapper class with the typed method wrappers **and** convenience helpers for events (`on(...)`, `setEventSource(...)`, `trigger(...)`).
- A source `<module>_api.cpp` implements each method by:
- Packaging arguments into a `QVariantList` in order.
- Invoking the remote method via the shared `LogosAPI`/client under the hood.
- Converting the `QVariant` result to the declared return type using safe conversions (`toBool`, `toInt`, `toDouble`, `toFloat`, `toString`, `toStringList`, `qvariant_cast<QJsonArray>`), or returning the `QVariant` as‑is for generic cases.
4. Umbrella composition
- `logos_sdk.h` includes all generated `*_api.h` files and defines a convenience aggregator:
- `logos_sdk.cpp` includes all generated `*_api.cpp` sources.
- Consumers compile `logos_sdk.cpp` exactly once per binary and include `logos_sdk.h` to access `logos.<module>.<method>(...)` across modules.
5. Build integration and idempotency
- CMake custom targets call the generator before compiling modules/apps so `logos-cpp-sdk/cpp/generated` is always up‑to‑date.
- The `scripts/clean.sh` script deletes generated files (`*_api.h/.cpp`, `logos_sdk.h/.cpp`) while leaving the directory in place.
6. Scope and limitations
- Wrapper method signatures use Qt types (`QString`, `QStringList`, `QJsonArray`, etc.). For unsupported/complex types, the return falls back to `QVariant`.
- Event helpers ride on top of `LogosAPIClient::onEvent(...)`/`onEventResponse(...)`; call `setEventSource()` once before emitting events from a module.
### 3.2 TokenManager
`TokenManager` is a thread-safe singleton that manages authentication tokens for inter-module communication.
| `instance() → TokenManager*` | Returns the singleton instance |
| `saveToken(key, token)` | Stores a token with the given key |
| `getToken(key) → QString` | Retrieves a token by key |
| `hasToken(key) → bool` | Checks if a token exists for the key |
| `removeToken(key)` | Removes a token |
| `getTokenKeys() → QList<QString>` | Returns all token keys |
**Responsibilities**:
- Store capability-issued tokens keyed by module name and provide thread-safe access.
- Support both module-level tokens and special tokens for core/core_manager/capability flows.
### 3.3 ModuleProxy
`ModuleProxy` is an internal class used by the provider to expose a module safely. It wraps the real module object and validates every incoming call against stored authentication tokens.
| `informModuleToken(authToken, moduleName, token) → bool` | Stores `token` for `moduleName` in the global `TokenManager`. **Privileged**: only the trusted core / capability module channel may call it — `authToken` must match this module's seed secret (the `core` / `capability_module` token the host plants at init); empty or non-matching tokens are rejected and return `false` |
| `getPluginEvents() → QJsonArray` | Enumerates the wrapped module's `logos_events:` declarations (name, signature, parameters, and a per-event `description` for documented universal events); empty for legacy/provider modules |
| `getPluginInterface() → QJsonArray` | Methods and events together, each tagged with a `"type"`; the un-filtered source that `getPluginMethods`/`getPluginEvents` slice (all three come from one `getMethods()` call — no separate `getEvents()` vtable method) |
- Gate `informModuleToken()` so only the trusted core / capability module channel can plant a token (see the security note below).
> **Security note — `informModuleToken` is privileged.** `callRemoteMethod()` authorizes a call when the presented token matches *any* token stored in this module's `TokenManager`. That means whoever can write into the token store effectively controls authorization. `informModuleToken()` is the write path, so it must not be callable by an arbitrary peer — otherwise a peer could plant a token of its own choosing and then present that same token to `callRemoteMethod()` to invoke any method, bypassing the capability gate entirely (finding F-002, CWE-862). To prevent this, `informModuleToken()` validates its `authToken` (using the same constant-time comparison as `callRemoteMethod`) against this module's seed secret — the value the host writes under the `core` and `capability_module` keys at module init. Only the trusted core / capability module knows that secret; every other caller is rejected, and an empty or unseeded secret fails closed.
The code generator emits type-safe wrappers per module plus an umbrella (`logos_sdk.h/.cpp`) that aggregates them into a `LogosModules` helper. Generated wrappers provide:
- Typed method calls (no string-based method names)
- Automatic argument marshalling and `QVariant` conversions
- Event subscription helpers (`on(...)`) and a `trigger(...)` helper for emitting events
- `setEventSource(QObject*)` to cache the QObject that actually emits `eventResponse` when using `trigger(...)`
The installed `logos-cpp-sdkConfig.cmake` calls `find_dependency(Qt6 COMPONENTS Core RemoteObjects)`, `find_dependency(Boost COMPONENTS system)`, `find_dependency(OpenSSL)` and `find_dependency(nlohmann_json)` before importing the target, so consumers don't have to wire transitive dependencies themselves. (The static archive references OpenSSL `SSL_CTX_*`/`X509_*` and Boost `system::error_code`; without the imported target the link step fails.)
**Calling a module over an explicit transport** (e.g. a CLI dialing `core_service` over TCP+SSL while the daemon advertises `capability_module` on a sibling port):
```c++
LogosTransportConfig coreCfg = /* read from daemon.json */;
LogosTransportConfig capCfg = /* sibling port for capability_module */;