mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-08-08 08:23:12 +00:00
392 lines
11 KiB
Markdown
392 lines
11 KiB
Markdown
# 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 `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.
|
|
|
|
## 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 (`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.
|
|
|
|
## Example Usage
|
|
|
|
```c
|
|
#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 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/`
|