11 KiB
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 rawLogosDeliveryScalarRawFn:(void *ctx, LogosDeliveryScalarRawFn cb, void *userData). - Argument-taking calls (
subscribe,unsubscribe,send,get_node_info) take a per-callLogosDelivery<Name>ReplyFnand pass their arguments last, in a request struct:(void *ctx, LogosDelivery<Name>ReplyFn onReply, void *userData, const <Name>Req *req).
The generator emits one reply typedef per call (e.g. LogosDeliverySubscribeReplyFn),
all with the same shape:
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.
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 configurationonCreated: Callback that receives the terminal resultuserData: 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:
{
"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.
int logosdelivery_start_node(void *ctx, LogosDeliveryScalarRawFn callback, void *userData);
logosdelivery_stop_node
Stops the node.
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.
int logosdelivery_destroy(void *ctx);
Messaging
logosdelivery_subscribe
Subscribe to a content topic to receive messages.
typedef struct { const char *contentTopicStr; } SubscribeReq;
int logosdelivery_subscribe(
void *ctx,
LogosDeliverySubscribeReplyFn onReply,
void *userData,
const SubscribeReq *req
);
Parameters:
ctx: Context handle returned bylogosdelivery_create_nodereq->contentTopicStr: Content topic string (e.g., "/myapp/1/chat/proto")onReply: Callback function to receive the resultuserData: User data passed to the callback
logosdelivery_unsubscribe
Unsubscribe from a content topic.
typedef struct { const char *contentTopicStr; } UnsubscribeReq;
int logosdelivery_unsubscribe(
void *ctx,
LogosDeliveryUnsubscribeReplyFn onReply,
void *userData,
const UnsubscribeReq *req
);
logosdelivery_send
Send a message.
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:
{
"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).
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.
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
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): SuccessNIMFFI_RET_ERR/RET_ERR(1): ErrorNIMFFI_RET_MISSING_CALLBACK/RET_MISSING_CALLBACK(2): Missing callback functionNIMFFI_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.
// 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 (
LogosDelivery<Name>ReplyFn):replyis the result on success (NUL-terminated, may be empty);errMsgis the message on failure. LogosDeliveryScalarRawFnandFFICallBack:msgholdslenbytes and is not NUL-terminated.LogosDeliveryCreateRawFn:ctxAddris 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
#include "liblogosdelivery.h"
#include <stdio.h>
#include <unistd.h>
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 ABIgenerated/logosdelivery.h: Generated call surface, emitted bymake liblogosdelivery(not checked in)liblogosdelivery.nim: Main library entry pointdeclare_lib.nim: Library declaration and initializationlogos_delivery_api/node_api.nim: Node lifecycle API implementationlogos_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/