Summary
A third-party core plugin calling logosAPI->getClient("storage_module")->invokeRemoteMethod(...) silently times out (~40s) even when storage_module is running and healthy. No error is surfaced — LogosAPIClient just returns an invalid QVariant.
cc @dlipicar
Root cause
LogosAPIClient::invokeRemoteMethod auto-provisions module access by calling capability_module.requestModule(origin, target). But that call needs a pre-existing capability_module token in the process-wide TokenManager. Third-party core plugins never receive this bootstrap token at load time — main_ui gets it, arbitrary core plugins do not.
From logos_api_client.cpp:
if (token.isEmpty() && objectName != "capability_module") {
QString capabilityToken = getToken("capability_module"); // empty for third-party plugins
QVariant result = pmc->invokeRemoteMethod(
capabilityToken, // empty string
"capability_module", "requestModule",
QVariantList() << m_origin_module << objectName,
timeout);
token = result.toString();
}
callRemoteMethod in module_proxy.cpp rejects the empty capabilityToken at line 229:
if (authToken.isEmpty()) {
return QVariant(); // silent fail — caller gets invalid QVariant after full timeout
}
Secondary finding: token validation check is inverted
module_proxy.cpp:235:
if (!tokenManager->getToken(authToken).isEmpty()) {
// labelled "INVALID" in debug output
return QVariant();
}
authToken is a UUID token value, but getToken() uses it as a module-name key. UUID strings are never module names, so getToken() always returns empty, and the "INVALID" branch is never taken. Effectively any non-empty string passes the token check. This will cause subtle breakage if fixed without also fixing the bootstrap gap.
Steps to reproduce
- Create a third-party core plugin (type:
core, .so loaded by logos_host)
- In
initLogos(LogosAPI* api): call api->getClient("storage_module")->invokeRemoteMethod("storage_module", "someMethod", {})
- Launch LogosBasecamp with both plugins installed
- Observe: 40s block, then invalid QVariant returned. No error logged by the framework.
Question
Is there an intended mechanism for core plugins to obtain cross-module IPC access?
Options we considered:
- Manifest
"dependencies" field — load-order only, no token provisioning
- Manifest
"capabilities" field — declarative metadata only
logos_core_get_token("capability_module") (C API) — reads the host's TokenManager, but async IPC plumbing still required
informModuleToken — not token-gated (Q_UNUSED(authToken)) so the loader could inject a bootstrap token when loading any core plugin
If core-to-core IPC is not a supported pattern today, that's useful to know too — we'll route through QML instead.
Environment
- LogosBasecamp AppImage:
9b52529-160
- SDK:
logos-cpp-sdk-headers-0.1.0 (nix store)
- Ubuntu 24.04
[Claude Code] — filed on behalf of a notes module developer
Summary
A third-party core plugin calling
logosAPI->getClient("storage_module")->invokeRemoteMethod(...)silently times out (~40s) even whenstorage_moduleis running and healthy. No error is surfaced —LogosAPIClientjust returns an invalidQVariant.cc @dlipicar
Root cause
LogosAPIClient::invokeRemoteMethodauto-provisions module access by callingcapability_module.requestModule(origin, target). But that call needs a pre-existingcapability_moduletoken in the process-wideTokenManager. Third-party core plugins never receive this bootstrap token at load time —main_uigets it, arbitrary core plugins do not.From
logos_api_client.cpp:callRemoteMethodinmodule_proxy.cpprejects the emptycapabilityTokenat line 229:Secondary finding: token validation check is inverted
module_proxy.cpp:235:authTokenis a UUID token value, butgetToken()uses it as a module-name key. UUID strings are never module names, sogetToken()always returns empty, and the "INVALID" branch is never taken. Effectively any non-empty string passes the token check. This will cause subtle breakage if fixed without also fixing the bootstrap gap.Steps to reproduce
core,.soloaded by logos_host)initLogos(LogosAPI* api): callapi->getClient("storage_module")->invokeRemoteMethod("storage_module", "someMethod", {})Question
Is there an intended mechanism for core plugins to obtain cross-module IPC access?
Options we considered:
"dependencies"field — load-order only, no token provisioning"capabilities"field — declarative metadata onlylogos_core_get_token("capability_module")(C API) — reads the host's TokenManager, but async IPC plumbing still requiredinformModuleToken— not token-gated (Q_UNUSED(authToken)) so the loader could inject a bootstrap token when loading any core pluginIf core-to-core IPC is not a supported pattern today, that's useful to know too — we'll route through QML instead.
Environment
9b52529-160logos-cpp-sdk-headers-0.1.0(nix store)