#ifndef LOGOS_MODULE_IMPL_H #define LOGOS_MODULE_IMPL_H /* =========================================================================== * logos_module_impl.h — the COMMON module-impl C ABI. * * ONE contract for module implementations in every language: a Logos module * compiles to a cdylib exporting exactly these symbols. The C++ SDK emits * this wrapper around a universal C++ impl class; the Rust SDK emits it * around a Rust impl. The uniform generated Qt-plugin glue (and, later, a * no-Qt host) talks to the cdylib ONLY through this ABI — the glue is * identical regardless of the module's source language, which is what makes * the eventual Qt-glue removal a host swap instead of a per-language change. * * Data model mirrors the lp_* consumer ABI (logos_protocol.h): * - method args / event payloads: JSON array (UTF-8 const char*) * - results: JSON value * - bytes: the canonical {"_bytes":""} tagged form * - errors from dispatch: NULL return, or a canonical error object * {"code","message","origin"} returned as the result of a failed call * when the implementation prefers structured errors. * * Ownership: every char* RETURNED by the module is heap-allocated and the * CALLER frees it with logos_module_string_free (exported by the module so * allocator domains never mix). Every const char* passed IN is borrowed. * * Threading: the host serializes dispatch calls (one at a time) unless a * future capability negotiates otherwise. The emit callback may be invoked * from any module thread; the host marshals. * * Versioning: logos_module_get_protocol_version() returns the * logos-protocol semver the module was COMPILED against (forwarded from * LOGOS_PROTOCOL_VERSION_STRING, never minted). Hosts apply the same rule * as the metadata stamp: equal MAJOR ⇔ compatible. This runtime handshake * complements the build-time metadata stamp and is what a no-Qt host (no * Qt plugin metadata) negotiates with. * =========================================================================== */ #ifdef __cplusplus extern "C" { #endif #if defined(_WIN32) #define LOGOS_MODULE_IMPL_EXPORT __declspec(dllexport) #else #define LOGOS_MODULE_IMPL_EXPORT __attribute__((visibility("default"))) #endif /* Event-emission callback installed by the host/glue. `data_json` is a JSON * array payload, borrowed for the duration of the call. */ typedef void (*logos_module_emit_cb)(const char* event_name, const char* data_json, void* user_data); /* --------------------------------------------------------------------------- * Exported by every module cdylib (generated by the SDK of the module's * language; module authors never write these by hand). * ------------------------------------------------------------------------- */ /* Dispatch a method call. Returns the result JSON value as a heap string * (free with logos_module_string_free), or NULL when the method is unknown * or dispatch failed structurally. */ LOGOS_MODULE_IMPL_EXPORT char* logos_module_dispatch(const char* method, const char* args_json); /* The module's method/event metadata as a JSON array — same shape as * LogosProviderObject::getMethods() (entries tagged "method"/"event"). */ LOGOS_MODULE_IMPL_EXPORT char* logos_module_get_methods(void); /* Module identity/context, stamped by the host before the first dispatch: * module path, instance id, per-instance persistence path. Mirrors * LogosModuleContext / RustModuleContext. Any argument may be NULL. */ LOGOS_MODULE_IMPL_EXPORT void logos_module_set_context( const char* module_path, const char* instance_id, const char* instance_persistence_path); /* Install the host's event-emission callback. The module keeps (cb, * user_data) and invokes cb once per emitted event. Passing NULL clears it; * after the clearing call returns, the module must not invoke the old cb. */ LOGOS_MODULE_IMPL_EXPORT void logos_module_set_emit_callback( logos_module_emit_cb cb, void* user_data); /* Deliver an auth token for `module_name` (the provider-side * informModuleToken). Returns 0 on acceptance. */ LOGOS_MODULE_IMPL_EXPORT int logos_module_accept_token(const char* module_name, const char* token); /* Grant the module the privileged host services named in `services_json` (a * JSON array from the closed set lp_grant_host_services documents). Returns 0 * on acceptance; the generated implementation simply forwards to * lp_grant_host_services. * * Called by the host AFTER it has verified the module's identity, and only for * the modules its policy designates as a trust root — nothing about this ABI * decides who deserves the grant. * * It has to travel this way, and that is the subtle part: the host binary and * the module cdylib each link their own copy of logos-protocol, so each has its * own process-global grant state, exactly as each has its own TokenManager. A * grant the host records for itself is invisible to the gate the cdylib checks. * Pushing it in over this ABI — the same route the auth token above already * takes — is what puts the grant in the image whose gates it must open. */ LOGOS_MODULE_IMPL_EXPORT int logos_module_grant_host_services( const char* services_json); /* The logos-protocol semver this module was compiled against. Static * string — do NOT free. */ LOGOS_MODULE_IMPL_EXPORT const char* logos_module_get_protocol_version(void); /* Free a string returned by this module. Safe on NULL. */ LOGOS_MODULE_IMPL_EXPORT void logos_module_string_free(char* s); #ifdef __cplusplus } #endif #endif /* LOGOS_MODULE_IMPL_H */