2026-02-17 10:38:35 +01:00
# 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.
2026-08-06 23:53:38 -03:00
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 `LogosDelivery<Name>ReplyFn` and 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:
```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.
2026-02-17 10:38:35 +01:00
## API Functions
### Node Lifecycle
#### `logosdelivery_create_node`
2026-08-06 23:53:38 -03:00
Creates a node from the given configuration JSON.
2026-02-17 10:38:35 +01:00
```c
2026-08-06 23:53:38 -03:00
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
);
2026-02-17 10:38:35 +01:00
void *logosdelivery_create_node(
2026-08-06 23:53:38 -03:00
const CreateNodeCtorReq *req,
LogosDeliveryCreateRawFn onCreated,
2026-02-17 10:38:35 +01:00
void *userData
);
```
**Parameters:**
2026-08-06 23:53:38 -03:00
- `req->configJson` : JSON string containing node configuration
- `onCreated` : Callback that receives the terminal result
2026-02-17 10:38:35 +01:00
- `userData` : User data passed to the callback
2026-08-06 23:53:38 -03:00
**Returns:** the context handle, or `NULL` on failure. Creation is asynchronous:
wait for `onCreated` before you make any other call.
2026-02-17 10:38:35 +01:00
**Example configuration JSON:**
```json
{
"mode": "Core",
2026-03-03 19:17:54 +01:00
"preset": "logos.dev",
2026-07-09 12:21:41 -03:00
"messagingOverrides": {
"listen-address": "0.0.0.0",
"tcp-port": 60000,
"discv5-udp-port": 9000
}
2026-02-17 10:38:35 +01:00
}
```
2026-07-09 12:21:41 -03:00
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.
2026-07-03 13:04:57 +01:00
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 |
2026-03-03 19:17:54 +01:00
2026-02-17 10:38:35 +01:00
#### `logosdelivery_start_node`
Starts the node.
```c
2026-08-06 23:53:38 -03:00
int logosdelivery_start_node(void *ctx, LogosDeliveryScalarRawFn callback, void *userData);
2026-02-17 10:38:35 +01:00
```
#### `logosdelivery_stop_node`
Stops the node.
```c
2026-08-06 23:53:38 -03:00
int logosdelivery_stop_node(void *ctx, LogosDeliveryScalarRawFn callback, void *userData);
2026-02-17 10:38:35 +01:00
```
#### `logosdelivery_destroy`
2026-08-06 23:53:38 -03:00
Destroys a node instance and frees resources. This call is synchronous; do not
use `ctx` afterwards.
2026-02-17 10:38:35 +01:00
```c
2026-08-06 23:53:38 -03:00
int logosdelivery_destroy(void *ctx);
2026-02-17 10:38:35 +01:00
```
### Messaging
#### `logosdelivery_subscribe`
Subscribe to a content topic to receive messages.
```c
2026-08-06 23:53:38 -03:00
typedef struct { const char *contentTopicStr; } SubscribeReq;
2026-02-17 10:38:35 +01:00
int logosdelivery_subscribe(
void *ctx,
2026-08-06 23:53:38 -03:00
LogosDeliverySubscribeReplyFn onReply,
2026-02-17 10:38:35 +01:00
void *userData,
2026-08-06 23:53:38 -03:00
const SubscribeReq *req
2026-02-17 10:38:35 +01:00
);
```
**Parameters:**
2026-08-06 23:53:38 -03:00
- `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
2026-02-17 10:38:35 +01:00
- `userData` : User data passed to the callback
#### `logosdelivery_unsubscribe`
Unsubscribe from a content topic.
```c
2026-08-06 23:53:38 -03:00
typedef struct { const char *contentTopicStr; } UnsubscribeReq;
2026-02-17 10:38:35 +01:00
int logosdelivery_unsubscribe(
void *ctx,
2026-08-06 23:53:38 -03:00
LogosDeliveryUnsubscribeReplyFn onReply,
2026-02-17 10:38:35 +01:00
void *userData,
2026-08-06 23:53:38 -03:00
const UnsubscribeReq *req
2026-02-17 10:38:35 +01:00
);
```
#### `logosdelivery_send`
Send a message.
```c
2026-08-06 23:53:38 -03:00
typedef struct { const char *messageJson; } SendReq;
2026-02-17 10:38:35 +01:00
int logosdelivery_send(
void *ctx,
2026-08-06 23:53:38 -03:00
LogosDeliverySendReplyFn onReply,
2026-02-17 10:38:35 +01:00
void *userData,
2026-08-06 23:53:38 -03:00
const SendReq *req
2026-02-17 10:38:35 +01:00
);
```
**Parameters:**
2026-08-06 23:53:38 -03:00
- `req->messageJson` : JSON string containing the message
2026-02-17 10:38:35 +01:00
**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
2026-07-31 13:50:59 -03:00
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).
2026-02-17 10:38:35 +01:00
```c
2026-07-31 13:50:59 -03:00
uint64_t logosdelivery_add_event_listener(
2026-02-17 10:38:35 +01:00
void *ctx,
2026-07-31 13:50:59 -03:00
const char *eventName,
2026-02-17 10:38:35 +01:00
FFICallBack callback,
void *userData
);
```
2026-07-31 13:50:59 -03:00
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.
2026-02-17 10:38:35 +01:00
## 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:
2026-08-06 23:53:38 -03:00
- `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.
2026-02-17 10:38:35 +01:00
2026-08-06 23:53:38 -03:00
## Callback Functions
2026-02-17 10:38:35 +01:00
2026-08-06 23:53:38 -03:00
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.
2026-02-17 10:38:35 +01:00
```c
2026-08-06 23:53:38 -03:00
// 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).
2026-02-17 10:38:35 +01:00
typedef void (*FFICallBack)(
int callerRet,
const char *msg,
size_t len,
void *userData
);
```
2026-08-06 23:53:38 -03:00
- Reply typedefs (`LogosDelivery<Name>ReplyFn` ): `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.
2026-02-17 10:38:35 +01:00
## Example Usage
```c
#include "liblogosdelivery.h"
#include <stdio.h>
2026-08-06 23:53:38 -03:00
#include <unistd.h>
2026-02-17 10:38:35 +01:00
2026-08-06 23:53:38 -03:00
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) {
2026-02-17 10:38:35 +01:00
if (ret == RET_OK) {
2026-08-06 23:53:38 -03:00
printf("Success: %s\n", reply ? reply : "");
2026-02-17 10:38:35 +01:00
} else {
2026-08-06 23:53:38 -03:00
printf("Error: %s\n", errMsg ? errMsg : "unknown error");
2026-02-17 10:38:35 +01:00
}
}
2026-08-06 23:53:38 -03:00
// 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);
}
2026-02-17 10:38:35 +01:00
int main() {
const char *config = "{"
"\"mode\": \"Core\","
2026-03-03 19:17:54 +01:00
"\"preset\": \"logos.dev\""
2026-02-17 10:38:35 +01:00
"}";
2026-08-06 23:53:38 -03:00
// 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) {
2026-02-17 10:38:35 +01:00
return 1;
}
// Start node
2026-08-06 23:53:38 -03:00
logosdelivery_start_node(node, on_scalar, NULL);
2026-02-17 10:38:35 +01:00
// Subscribe to a topic
2026-08-06 23:53:38 -03:00
SubscribeReq subReq = { .contentTopicStr = "/myapp/1/chat/proto" };
logosdelivery_subscribe(node, on_reply, NULL, &subReq);
2026-02-17 10:38:35 +01:00
// Send a message
const char *msg = "{"
"\"contentTopic\": \"/myapp/1/chat/proto\","
"\"payload\": \"SGVsbG8gV29ybGQ=\","
"\"ephemeral\": false"
"}";
2026-08-06 23:53:38 -03:00
SendReq sendReq = { .messageJson = msg };
logosdelivery_send(node, on_reply, NULL, &sendReq);
2026-02-17 10:38:35 +01:00
2026-08-06 23:53:38 -03:00
// Clean up. logosdelivery_destroy is synchronous.
logosdelivery_stop_node(node, on_scalar, NULL);
logosdelivery_destroy(node);
2026-02-17 10:38:35 +01:00
return 0;
}
```
## Architecture
The library is structured as follows:
2026-08-06 23:53:38 -03:00
- `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)
2026-02-17 10:38:35 +01:00
- `liblogosdelivery.nim` : Main library entry point
- `declare_lib.nim` : Library declaration and initialization
2026-08-06 23:53:38 -03:00
- `logos_delivery_api/node_api.nim` : Node lifecycle API implementation
- `logos_delivery_api/messaging_api.nim` : Subscribe/send API implementation
2026-02-17 10:38:35 +01:00
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/`