# Logos Messaging API (LMAPI) Library A C FFI library providing a simplified interface to Logos Messaging functionality. ## Overview This library wraps the high-level API functions from `waku/api/api.nim` and exposes them via a C FFI interface, making them accessible from C, C++, and other languages that support C FFI. The call surface is generated by nim-ffi from the `{.ffi.}` annotations in `library/*.nim`. `make liblogosdelivery` writes it to `library/generated/logosdelivery.h` on every build, so it can never drift from the Nim signatures. It is a build artifact and is not checked in: build the library before you compile anything against it. Include `library/liblogosdelivery.h`, which pulls in the generated header and adds the event-listener ABI. Every entry point takes the context handle (`void *ctx`) first, except the constructor. The rest of the signature depends on the call: - No-argument calls (`start_node`, `stop_node`, `get_available_configs`, `get_available_node_info_ids`) take a raw `LogosDeliveryScalarRawFn`: `(void *ctx, LogosDeliveryScalarRawFn cb, void *userData)`. - Argument-taking calls (`subscribe`, `unsubscribe`, `send`, `get_node_info`) take a per-call `LogosDeliveryReplyFn` and pass their arguments last, in a request struct: `(void *ctx, LogosDeliveryReplyFn onReply, void *userData, const Req *req)`. The generator emits one reply typedef per call (e.g. `LogosDeliverySubscribeReplyFn`), all with the same shape: ```c typedef void (*LogosDeliveryScalarRawFn)(int callerRet, char *msg, size_t len, void *userData); typedef void (*LogosDeliverySubscribeReplyFn)(int errCode, const char *reply, const char *errMsg, void *userData); ``` `reply`, `errMsg` and `msg` are borrowed: copy them if you need them after the callback returns. ## API Functions ### Node Lifecycle #### `logosdelivery_create_node` Creates a node from the given configuration JSON. ```c typedef struct { const char *configJson; } CreateNodeCtorReq; typedef void (*LogosDeliveryCreateRawFn)( int errCode, const char *ctxAddr, // context address as decimal text, on success const char *errMsg, void *userData ); void *logosdelivery_create_node( const CreateNodeCtorReq *req, LogosDeliveryCreateRawFn onCreated, void *userData ); ``` **Parameters:** - `req->configJson`: JSON string containing node configuration - `onCreated`: Callback that receives the terminal result - `userData`: User data passed to the callback **Returns:** the context handle, or `NULL` on failure. Creation is asynchronous: wait for `onCreated` before you make any other call. **Example configuration JSON:** ```json { "mode": "Core", "preset": "logos.dev", "messagingOverrides": { "listen-address": "0.0.0.0", "tcp-port": 60000, "discv5-udp-port": 9000 } } ``` The configuration object has four optional top-level keys: `mode` (`"Core"` or `"Edge"`, defaults to `"Core"`), `preset`, `messagingOverrides` (per-field node config overrides), and `channelsOverrides` (reliable-channel overrides). Override keys accept the config field name or its CLI switch name (e.g. `"clusterId"` or `"cluster-id"`); unknown keys are rejected. Use `"preset"` to select a network preset (e.g., `"twn"`, `"logos.dev"`, `"status.prod"`) which auto-configures entry nodes, cluster ID, sharding, and other network-specific settings. Available presets: | Preset | Cluster ID | RLN | Sharding | Network | | --- | --- | --- | --- | --- | | `twn` | 1 | on | auto (8 shards) | The Waku Network | | `logos.dev` | 2 | off | auto (8 shards) | Logos Dev Network | | `logos.test` | 2 | off | auto (8 shards) | Logos Test Network | | `status.prod` | 16 | off | auto (1 shard) | Status Production Network | #### `logosdelivery_start_node` Starts the node. ```c int logosdelivery_start_node(void *ctx, LogosDeliveryScalarRawFn callback, void *userData); ``` #### `logosdelivery_stop_node` Stops the node. ```c int logosdelivery_stop_node(void *ctx, LogosDeliveryScalarRawFn callback, void *userData); ``` #### `logosdelivery_destroy` Destroys a node instance and frees resources. This call is synchronous; do not use `ctx` afterwards. ```c int logosdelivery_destroy(void *ctx); ``` ### Messaging #### `logosdelivery_subscribe` Subscribe to a content topic to receive messages. ```c typedef struct { const char *contentTopicStr; } SubscribeReq; int logosdelivery_subscribe( void *ctx, LogosDeliverySubscribeReplyFn onReply, void *userData, const SubscribeReq *req ); ``` **Parameters:** - `ctx`: Context handle returned by `logosdelivery_create_node` - `req->contentTopicStr`: Content topic string (e.g., "/myapp/1/chat/proto") - `onReply`: Callback function to receive the result - `userData`: User data passed to the callback #### `logosdelivery_unsubscribe` Unsubscribe from a content topic. ```c typedef struct { const char *contentTopicStr; } UnsubscribeReq; int logosdelivery_unsubscribe( void *ctx, LogosDeliveryUnsubscribeReplyFn onReply, void *userData, const UnsubscribeReq *req ); ``` #### `logosdelivery_send` Send a message. ```c typedef struct { const char *messageJson; } SendReq; int logosdelivery_send( void *ctx, LogosDeliverySendReplyFn onReply, void *userData, const SendReq *req ); ``` **Parameters:** - `req->messageJson`: JSON string containing the message **Example message JSON:** ```json { "contentTopic": "/myapp/1/chat/proto", "payload": "SGVsbG8gV29ybGQ=", "ephemeral": false } ``` Note: The `payload` field should be base64-encoded. **Returns:** Request ID in the callback message that can be used to track message delivery. ### Events Events are delivered through a per-event listener registry: register one callback per event name you care about. A registration returns a listener id you can later pass to remove it. #### `logosdelivery_add_event_listener` Registers `callback` for the named event and returns a non-zero listener id (0 on an invalid context). ```c uint64_t logosdelivery_add_event_listener( void *ctx, const char *eventName, FFICallBack callback, void *userData ); ``` Event names: `onMessageSent`, `onMessageError`, `onMessagePropagated`, `onMessageReceived`, `onConnectionStatusChange`, `onTopicHealthChange`, `onConnectionChange`, `onReceivedMessage`, `onChannelMessageReceived`, `onChannelMessageSent`, `onChannelMessageError`. #### `logosdelivery_remove_event_listener` Removes a previously registered listener. Returns `0` on success, `1` if the listener id was not found or the context is invalid. ```c int logosdelivery_remove_event_listener( void *ctx, uint64_t listenerId ); ``` **Important:** Callbacks run on a dedicated event thread and should be fast, non-blocking, and thread-safe. ## Building The library follows the same build system as the main Logos Messaging project. ### Build the library ```bash make liblogosdeliveryStatic # Build static library # or make liblogosdeliveryDynamic # Build dynamic library ``` ## Return Codes All functions that return `int` use the following return codes: - `NIMFFI_RET_OK` / `RET_OK` (0): Success - `NIMFFI_RET_ERR` / `RET_ERR` (1): Error - `NIMFFI_RET_MISSING_CALLBACK` / `RET_MISSING_CALLBACK` (2): Missing callback function - `NIMFFI_RET_STALE_WARN` (3): Non-terminal progress tick, always followed by a terminal code. Ignore it unless you want progress. ## Callback Functions Results come back through one of four callback shapes. The generated names carry the library prefix (`LogosDelivery`); the reply typedef is emitted once per call. ```c // Argument-taking calls: one typedef per call, all this shape. typedef void (*LogosDeliverySubscribeReplyFn)( int errCode, const char *reply, const char *errMsg, void *userData ); // No-argument calls (start/stop/get_available_*). typedef void (*LogosDeliveryScalarRawFn)( int callerRet, char *msg, size_t len, void *userData ); // Constructor. typedef void (*LogosDeliveryCreateRawFn)( int errCode, const char *ctxAddr, const char *errMsg, void *userData ); // Event listeners (declared by liblogosdelivery.h, not the generated header). typedef void (*FFICallBack)( int callerRet, const char *msg, size_t len, void *userData ); ``` - Reply typedefs (`LogosDeliveryReplyFn`): `reply` is the result on success (NUL-terminated, may be empty); `errMsg` is the message on failure. - `LogosDeliveryScalarRawFn` and `FFICallBack`: `msg` holds `len` bytes and is not NUL-terminated. - `LogosDeliveryCreateRawFn`: `ctxAddr` is the context address as decimal text on success. All of these strings are borrowed and valid only for the duration of the call. Copy them if you need them afterwards. ## Example Usage ```c #include "liblogosdelivery.h" #include #include static volatile int created = -1; static void *node = NULL; // The argument-taking calls share this reply shape. void on_reply(int ret, const char *reply, const char *errMsg, void *userData) { if (ret == RET_OK) { printf("Success: %s\n", reply ? reply : ""); } else { printf("Error: %s\n", errMsg ? errMsg : "unknown error"); } } // The no-argument calls (start/stop) take the raw callback. void on_scalar(int ret, char *msg, size_t len, void *userData) { if (ret == RET_STALE_WARN) return; // progress tick, ignore printf("%.*s\n", (int)len, msg ? msg : ""); } void on_created(int ret, const char *ctxAddr, const char *errMsg, void *userData) { created = (ret == RET_OK); } int main() { const char *config = "{" "\"mode\": \"Core\"," "\"preset\": \"logos.dev\"" "}"; // Create the node. The return value is the context handle; wait for // on_created before making any other call. CreateNodeCtorReq createReq = { .configJson = config }; node = logosdelivery_create_node(&createReq, on_created, NULL); for (int i = 0; i < 100 && created == -1; i++) { usleep(100000); } if (created != 1 || node == NULL) { return 1; } // Start node logosdelivery_start_node(node, on_scalar, NULL); // Subscribe to a topic SubscribeReq subReq = { .contentTopicStr = "/myapp/1/chat/proto" }; logosdelivery_subscribe(node, on_reply, NULL, &subReq); // Send a message const char *msg = "{" "\"contentTopic\": \"/myapp/1/chat/proto\"," "\"payload\": \"SGVsbG8gV29ybGQ=\"," "\"ephemeral\": false" "}"; SendReq sendReq = { .messageJson = msg }; logosdelivery_send(node, on_reply, NULL, &sendReq); // Clean up. logosdelivery_destroy is synchronous. logosdelivery_stop_node(node, on_scalar, NULL); logosdelivery_destroy(node); return 0; } ``` ## Architecture The library is structured as follows: - `liblogosdelivery.h`: Public C header; includes the generated header and adds the event ABI - `generated/logosdelivery.h`: Generated call surface, emitted by `make liblogosdelivery` (not checked in) - `liblogosdelivery.nim`: Main library entry point - `declare_lib.nim`: Library declaration and initialization - `logos_delivery_api/node_api.nim`: Node lifecycle API implementation - `logos_delivery_api/messaging_api.nim`: Subscribe/send API implementation The library uses the nim-ffi framework for FFI infrastructure, which handles: - Thread-safe request processing - Async operation management - Memory management between C and Nim - Callback marshaling ## See Also - Main API documentation: `waku/api/api.nim` - Original libwaku library: `library/libwaku.nim` - nim-ffi framework: `vendor/nim-ffi/`