Files
logos-protocol/cpp/logos_module_impl.h
Dario Gabriel LipicarandClaude Opus 5 b218f4c8e5 feat: the host-services C ABI a trust-root module needs
capability_module is the last legacy Qt Q_INVOKABLE provider, and it cannot
become an ordinary `interface: universal` module while the two things it does
have no C entry point: reading the token store, and pushing a token to an
ARBITRARY target. This adds both, plus the grant that gates them. Purely
additive — no existing symbol changes behaviour.

  lp_token_keys()               the module names THIS image's TokenManager
                                holds. NULL means REFUSED, never "empty" — a
                                granted call with no tokens answers "[]", and a
                                known-caller gate needs to tell those apart.
  lp_inform_module_token_to()   routes to LogosAPIClient::informModuleToken_module,
                                the 5-arg form. Note the existing
                                lp_inform_module_token is the WRONG DIRECTION
                                for this: it reaches a consumer path that
                                hardcodes requestObject("capability_module"),
                                i.e. core -> capability, not capability ->
                                target. That 5-arg method had no C entry point.
  lp_grant_host_services()      sets the in-image grant over the closed set
                                {token_registry, token_delivery}. Replaces
                                rather than merges; NULL/""/"[]" clears. An
                                unknown name is rejected wholesale and leaves
                                the existing grant untouched, so a typo can
                                never silently drop a service.

Why the gate is per-IMAGE, which looks like an odd choice until it doesn't:
the host binary and a module's cdylib each link their own copy of this library,
so they have separate process-global state. A gate "simplified" into the host
would be checked against state the calling image can never set, and would read
as ungranted forever. The grant therefore crosses the module-impl C ABI the
same way the auth token already does — hence the logos_module_grant_host_services
declaration added to logos_module_impl.h, whose generated body and host-side
call land in logos-cpp-sdk and logos-module-loader-qt respectively.

MINOR 2 -> 3; MAJOR unchanged, so the equal-MAJOR compatibility rule is
unaffected. 387/387 tests pass, including 6 new ones covering both gates
closed, both opened, clearing re-closing them, and the unknown-name rejection.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 20:43:08 -03:00

119 lines
5.7 KiB
C

#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":"<base64url>"} 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 */