chore: rename quicproquo → quicprochat in docs, Docker, CI, and packaging
Rename all project references from quicproquo/qpq to quicprochat/qpc across documentation, Docker configuration, CI workflows, packaging scripts, operational configs, and build tooling. - Docker: crate paths, binary names, user/group, data dirs, env vars - CI: workflow crate references, binary names, artifact names - Docs: all markdown files under docs/, SDK READMEs, book.toml - Packaging: OpenWrt Makefile, init script, UCI config (file renames) - Scripts: justfile, dev-shell, screenshot, cross-compile, ai_team - Operations: Prometheus config, alert rules, Grafana dashboard - Config: .env.example (QPQ_* → QPC_*), CODEOWNERS paths - Top-level: README, CONTRIBUTING, ROADMAP, CLAUDE.md
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# C FFI Bindings
|
||||
|
||||
The `quicproquo-ffi` crate provides a synchronous C API for the quicproquo
|
||||
messaging client. It wraps the async `quicproquo-client` library behind an
|
||||
The `quicprochat-ffi` crate provides a synchronous C API for the quicprochat
|
||||
messaging client. It wraps the async `quicprochat-client` library behind an
|
||||
opaque handle, so C, Python, Swift, or any language with C FFI support can
|
||||
connect, authenticate, send messages, and receive messages.
|
||||
|
||||
@@ -13,26 +13,26 @@ internals.
|
||||
|
||||
```bash
|
||||
# Shared library (.so / .dylib / .dll) + static archive (.a)
|
||||
cargo build --release -p quicproquo-ffi
|
||||
cargo build --release -p quicprochat-ffi
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
| Platform | Shared library | Static library |
|
||||
|----------|---------------------------------------------|-----------------------------------------|
|
||||
| Linux | `target/release/libquicproquo_ffi.so` | `target/release/libquicproquo_ffi.a` |
|
||||
| macOS | `target/release/libquicproquo_ffi.dylib` | `target/release/libquicproquo_ffi.a` |
|
||||
| Windows | `target/release/quicproquo_ffi.dll` | `target/release/quicproquo_ffi.lib` |
|
||||
| Linux | `target/release/libquicprochat_ffi.so` | `target/release/libquicprochat_ffi.a` |
|
||||
| macOS | `target/release/libquicprochat_ffi.dylib` | `target/release/libquicprochat_ffi.a` |
|
||||
| Windows | `target/release/quicprochat_ffi.dll` | `target/release/quicprochat_ffi.lib` |
|
||||
|
||||
## Status Codes
|
||||
|
||||
| Code | Constant | Meaning |
|
||||
|------|--------------------|------------------------------------------|
|
||||
| 0 | `QPQ_OK` | Success |
|
||||
| 1 | `QPQ_ERROR` | Generic error (check `qpq_last_error`) |
|
||||
| 2 | `QPQ_AUTH_FAILED` | OPAQUE authentication failed |
|
||||
| 3 | `QPQ_TIMEOUT` | Receive timed out with no messages |
|
||||
| 4 | `QPQ_NOT_CONNECTED`| Handle is null or not logged in |
|
||||
| 0 | `QPC_OK` | Success |
|
||||
| 1 | `QPC_ERROR` | Generic error (check `qpc_last_error`) |
|
||||
| 2 | `QPC_AUTH_FAILED` | OPAQUE authentication failed |
|
||||
| 3 | `QPC_TIMEOUT` | Receive timed out with no messages |
|
||||
| 4 | `QPC_NOT_CONNECTED`| Handle is null or not logged in |
|
||||
|
||||
## C API Reference
|
||||
|
||||
@@ -40,10 +40,10 @@ All functions use the `extern "C"` calling convention. All string parameters
|
||||
must be valid, non-null, null-terminated UTF-8. The opaque handle type is
|
||||
`QpqHandle *`.
|
||||
|
||||
### `qpq_connect`
|
||||
### `qpc_connect`
|
||||
|
||||
```c
|
||||
QpqHandle *qpq_connect(
|
||||
QpqHandle *qpc_connect(
|
||||
const char *server, /* "host:port", e.g. "127.0.0.1:7000" */
|
||||
const char *ca_cert, /* path to CA certificate file (DER) */
|
||||
const char *server_name /* TLS server name, e.g. "localhost" */
|
||||
@@ -54,11 +54,11 @@ Creates a Tokio runtime, performs a health check against the server, and
|
||||
returns a heap-allocated opaque handle. Returns `NULL` on failure (invalid
|
||||
arguments, server unreachable, or runtime creation failed).
|
||||
|
||||
### `qpq_login`
|
||||
### `qpc_login`
|
||||
|
||||
```c
|
||||
int32_t qpq_login(
|
||||
QpqHandle *handle, /* handle from qpq_connect */
|
||||
int32_t qpc_login(
|
||||
QpqHandle *handle, /* handle from qpc_connect */
|
||||
const char *username, /* OPAQUE username */
|
||||
const char *password /* OPAQUE password */
|
||||
);
|
||||
@@ -66,16 +66,16 @@ int32_t qpq_login(
|
||||
|
||||
Authenticates with the server using OPAQUE (password-authenticated key
|
||||
exchange). On success the handle is marked as logged-in and subsequent
|
||||
`qpq_send`/`qpq_receive` calls use the authenticated session.
|
||||
`qpc_send`/`qpc_receive` calls use the authenticated session.
|
||||
|
||||
**Returns:** `QPQ_OK` on success, `QPQ_AUTH_FAILED` on bad credentials,
|
||||
`QPQ_NOT_CONNECTED` if the handle is null, or `QPQ_ERROR` on other failures.
|
||||
**Returns:** `QPC_OK` on success, `QPC_AUTH_FAILED` on bad credentials,
|
||||
`QPC_NOT_CONNECTED` if the handle is null, or `QPC_ERROR` on other failures.
|
||||
|
||||
### `qpq_send`
|
||||
### `qpc_send`
|
||||
|
||||
```c
|
||||
int32_t qpq_send(
|
||||
QpqHandle *handle, /* handle from qpq_connect */
|
||||
int32_t qpc_send(
|
||||
QpqHandle *handle, /* handle from qpc_connect */
|
||||
const char *recipient, /* recipient username (null-terminated) */
|
||||
const uint8_t *message, /* message bytes (UTF-8, not null-terminated) */
|
||||
size_t message_len /* length of message in bytes */
|
||||
@@ -86,14 +86,14 @@ Resolves the recipient by username, then sends an MLS-encrypted message
|
||||
through the server. The `message` buffer must contain valid UTF-8 of at least
|
||||
`message_len` bytes. The handle must be logged in.
|
||||
|
||||
**Returns:** `QPQ_OK` on success, `QPQ_NOT_CONNECTED` if not logged in, or
|
||||
`QPQ_ERROR` on failure (recipient not found, network error, etc.).
|
||||
**Returns:** `QPC_OK` on success, `QPC_NOT_CONNECTED` if not logged in, or
|
||||
`QPC_ERROR` on failure (recipient not found, network error, etc.).
|
||||
|
||||
### `qpq_receive`
|
||||
### `qpc_receive`
|
||||
|
||||
```c
|
||||
int32_t qpq_receive(
|
||||
QpqHandle *handle, /* handle from qpq_connect */
|
||||
int32_t qpc_receive(
|
||||
QpqHandle *handle, /* handle from qpc_connect */
|
||||
uint32_t timeout_ms, /* maximum wait time in milliseconds */
|
||||
char **out_json /* output: heap-allocated JSON string */
|
||||
);
|
||||
@@ -102,48 +102,48 @@ int32_t qpq_receive(
|
||||
Blocks up to `timeout_ms` milliseconds waiting for pending messages. On
|
||||
success, `*out_json` points to a null-terminated JSON string containing an
|
||||
array of decrypted message strings (e.g., `["hello","world"]`). The caller
|
||||
**must** free this string with `qpq_free_string`.
|
||||
**must** free this string with `qpc_free_string`.
|
||||
|
||||
**Returns:** `QPQ_OK` on success (even if the array is empty),
|
||||
`QPQ_TIMEOUT` if the wait expires with no messages, `QPQ_NOT_CONNECTED` if
|
||||
not logged in, or `QPQ_ERROR` on failure.
|
||||
**Returns:** `QPC_OK` on success (even if the array is empty),
|
||||
`QPC_TIMEOUT` if the wait expires with no messages, `QPC_NOT_CONNECTED` if
|
||||
not logged in, or `QPC_ERROR` on failure.
|
||||
|
||||
### `qpq_disconnect`
|
||||
### `qpc_disconnect`
|
||||
|
||||
```c
|
||||
void qpq_disconnect(QpqHandle *handle);
|
||||
void qpc_disconnect(QpqHandle *handle);
|
||||
```
|
||||
|
||||
Shuts down the Tokio runtime and frees the handle. After this call, the
|
||||
handle must not be used again. Passing `NULL` is a safe no-op.
|
||||
|
||||
### `qpq_last_error`
|
||||
### `qpc_last_error`
|
||||
|
||||
```c
|
||||
const char *qpq_last_error(const QpqHandle *handle);
|
||||
const char *qpc_last_error(const QpqHandle *handle);
|
||||
```
|
||||
|
||||
Returns the last error message recorded on the handle, or `NULL` if no error
|
||||
has occurred. The returned pointer is valid **only** until the next FFI call
|
||||
on the same handle. Do **not** free this pointer -- it is owned by the handle.
|
||||
|
||||
### `qpq_free_string`
|
||||
### `qpc_free_string`
|
||||
|
||||
```c
|
||||
void qpq_free_string(char *ptr);
|
||||
void qpc_free_string(char *ptr);
|
||||
```
|
||||
|
||||
Frees a string previously returned by `qpq_receive` via the `out_json`
|
||||
Frees a string previously returned by `qpc_receive` via the `out_json`
|
||||
output parameter. Passing `NULL` is a safe no-op. Do **not** use this to
|
||||
free strings from `qpq_last_error`.
|
||||
free strings from `qpc_last_error`.
|
||||
|
||||
## Memory Management Rules
|
||||
|
||||
1. **`QpqHandle`** is heap-allocated by `qpq_connect` and freed by
|
||||
`qpq_disconnect`. Do not use the handle after disconnecting.
|
||||
2. **`out_json` from `qpq_receive`** is heap-allocated. Free it with
|
||||
`qpq_free_string`.
|
||||
3. **`qpq_last_error`** returns a pointer owned by the handle. Do not free
|
||||
1. **`QpqHandle`** is heap-allocated by `qpc_connect` and freed by
|
||||
`qpc_disconnect`. Do not use the handle after disconnecting.
|
||||
2. **`out_json` from `qpc_receive`** is heap-allocated. Free it with
|
||||
`qpc_free_string`.
|
||||
3. **`qpc_last_error`** returns a pointer owned by the handle. Do not free
|
||||
it; it is valid until the next FFI call on the same handle.
|
||||
4. All `const char *` input parameters are borrowed for the duration of the
|
||||
call and not stored beyond it.
|
||||
@@ -154,11 +154,11 @@ Every function that returns `int32_t` uses the status codes above. The
|
||||
recommended pattern is:
|
||||
|
||||
```c
|
||||
int rc = qpq_login(handle, "alice", "password123");
|
||||
if (rc != QPQ_OK) {
|
||||
const char *err = qpq_last_error(handle);
|
||||
int rc = qpc_login(handle, "alice", "password123");
|
||||
if (rc != QPC_OK) {
|
||||
const char *err = qpc_last_error(handle);
|
||||
fprintf(stderr, "login failed (code %d): %s\n", rc, err ? err : "unknown");
|
||||
qpq_disconnect(handle);
|
||||
qpc_disconnect(handle);
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
@@ -169,48 +169,48 @@ if (rc != QPQ_OK) {
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Link with: -lquicproquo_ffi -lpthread -ldl -lm */
|
||||
/* Link with: -lquicprochat_ffi -lpthread -ldl -lm */
|
||||
|
||||
typedef struct QpqHandle QpqHandle;
|
||||
|
||||
extern QpqHandle *qpq_connect(const char *, const char *, const char *);
|
||||
extern int qpq_login(QpqHandle *, const char *, const char *);
|
||||
extern int qpq_send(QpqHandle *, const char *, const unsigned char *, unsigned long);
|
||||
extern int qpq_receive(QpqHandle *, unsigned int, char **);
|
||||
extern void qpq_disconnect(QpqHandle *);
|
||||
extern const char *qpq_last_error(const QpqHandle *);
|
||||
extern void qpq_free_string(char *);
|
||||
extern QpqHandle *qpc_connect(const char *, const char *, const char *);
|
||||
extern int qpc_login(QpqHandle *, const char *, const char *);
|
||||
extern int qpc_send(QpqHandle *, const char *, const unsigned char *, unsigned long);
|
||||
extern int qpc_receive(QpqHandle *, unsigned int, char **);
|
||||
extern void qpc_disconnect(QpqHandle *);
|
||||
extern const char *qpc_last_error(const QpqHandle *);
|
||||
extern void qpc_free_string(char *);
|
||||
|
||||
#define QPQ_OK 0
|
||||
#define QPC_OK 0
|
||||
|
||||
int main(void) {
|
||||
QpqHandle *h = qpq_connect("127.0.0.1:7000", "server-cert.der", "localhost");
|
||||
QpqHandle *h = qpc_connect("127.0.0.1:7000", "server-cert.der", "localhost");
|
||||
if (!h) {
|
||||
fprintf(stderr, "connection failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (qpq_login(h, "alice", "secret") != QPQ_OK) {
|
||||
fprintf(stderr, "login failed: %s\n", qpq_last_error(h));
|
||||
qpq_disconnect(h);
|
||||
if (qpc_login(h, "alice", "secret") != QPC_OK) {
|
||||
fprintf(stderr, "login failed: %s\n", qpc_last_error(h));
|
||||
qpc_disconnect(h);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Send a message */
|
||||
const char *msg = "hello from C";
|
||||
if (qpq_send(h, "bob", (const unsigned char *)msg, strlen(msg)) != QPQ_OK) {
|
||||
fprintf(stderr, "send failed: %s\n", qpq_last_error(h));
|
||||
if (qpc_send(h, "bob", (const unsigned char *)msg, strlen(msg)) != QPC_OK) {
|
||||
fprintf(stderr, "send failed: %s\n", qpc_last_error(h));
|
||||
}
|
||||
|
||||
/* Receive messages (5 second timeout) */
|
||||
char *json = NULL;
|
||||
int rc = qpq_receive(h, 5000, &json);
|
||||
if (rc == QPQ_OK && json) {
|
||||
int rc = qpc_receive(h, 5000, &json);
|
||||
if (rc == QPC_OK && json) {
|
||||
printf("received: %s\n", json);
|
||||
qpq_free_string(json);
|
||||
qpc_free_string(json);
|
||||
}
|
||||
|
||||
qpq_disconnect(h);
|
||||
qpc_disconnect(h);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
@@ -218,26 +218,26 @@ int main(void) {
|
||||
Compile and link:
|
||||
|
||||
```bash
|
||||
gcc -o qpq_demo qpq_demo.c -L target/release -lquicproquo_ffi -lpthread -ldl -lm
|
||||
LD_LIBRARY_PATH=target/release ./qpq_demo
|
||||
gcc -o qpc_demo qpc_demo.c -L target/release -lquicprochat_ffi -lpthread -ldl -lm
|
||||
LD_LIBRARY_PATH=target/release ./qpc_demo
|
||||
```
|
||||
|
||||
## Python Bindings
|
||||
|
||||
A ready-made Python `ctypes` wrapper is provided in
|
||||
[`examples/python/qpq_client.py`](https://github.com/nickvidal/quicproquo/tree/main/examples/python).
|
||||
[`examples/python/qpc_client.py`](https://github.com/nickvidal/quicprochat/tree/main/examples/python).
|
||||
|
||||
```bash
|
||||
# Build the FFI library first
|
||||
cargo build --release -p quicproquo-ffi
|
||||
cargo build --release -p quicprochat-ffi
|
||||
|
||||
# Run the Python client
|
||||
python examples/python/qpq_client.py \
|
||||
python examples/python/qpc_client.py \
|
||||
--server 127.0.0.1:7000 \
|
||||
--ca-cert server-cert.der \
|
||||
--username alice --password secret \
|
||||
--receive --timeout 5000
|
||||
```
|
||||
|
||||
Set `QPQ_FFI_LIB=/path/to/libquicproquo_ffi.so` to override automatic
|
||||
Set `QPC_FFI_LIB=/path/to/libquicprochat_ffi.so` to override automatic
|
||||
library discovery.
|
||||
|
||||
Reference in New Issue
Block a user