Dario LipicarandClaude Opus 5 ee5c553497 chore: bump logos-protocol to the off-strand socket close fixes (#172)
4ee85b26 -> 3a31c91d. liblogos_core statically links logos_protocol, so the
plain-transport code in this library is a copy of whatever this pin says —
which is why the pin matters here and not just downstream.

The two fixes this carries are the same race on two different asio objects:

  #38 RpcConnection::fail() closed the socket on the caller's thread while
      every other access to m_stream was serialized on m_strand. Teardown
      (~RpcClient -> ~PlainTransportConnection -> stop() -> fail()) ran
      close() -> cleanup_descriptor_data(), nulling impl.reactor_data_, while
      the io worker was inside reactive_socket_service_base::start_op() for a
      just-posted write. SIGSEGV at +0x98 on the IoContextPool thread.

  #39 The acceptor half: RpcServerTcp/Ssl::stop() closed m_acceptor on the
      host thread (via ~PlainTransportHost) while doAccept() re-armed
      async_accept from its own completion handler on the io worker.

Both now hand the close to a strand with dispatch(). Measured upstream at
~0.45% of calls over tcp and tcp_ssl, 0 over local/QtRO.

It is a false negative, not a lost result: the RPC completes and prints the
right answer, then teardown crashes, so anything that checks the exit code
before parsing stdout reports a healthy call as failed.

Also swept up between the two pins: #35 jsonToLogosResult and
#37 Codec<std::optional<T>>. Both additive.

Verification on aarch64-darwin:
  - nix build .#checks.<sys>.tests: 181 tests, 16 suites, 0 failures —
    identical to origin/master (181/16/0), so no regression and no new
    coverage; protocol's own regression tests live in logos-protocol.
  - the fix is present in the artifact, not just the lock: the new private
    methods appear in the built liblogos_core.dylib —
    closeStreamOnStrand x16 (#38), RpcServerTcp/Ssl::closeAcceptorOnStrand
    (#39) — and are absent from the same symbol table built at origin/master.

Every logos-protocol edge on the strand that feeds liblogos_core now agrees
at 3a31c91d: root, logos-cpp-sdk, logos-qt-sdk and default-module-loader
(logos-module-loader-qt). One sibling still carries its own older protocol —
see the PR description for logos-capability-module.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 16:15:23 -03:00
2026-06-01 10:33:03 -04:00

logos-liblogos

The core runtime library for the Logos modular application platform. Provides liblogos_core (a C-API shared library) and logos_host (the module subprocess host binary).

logos-liblogos is a library. It is consumed by two frontends:

Composed from

The runtime pulls its module-loading behaviour from a few separate repos so each piece can be swapped independently:

How to Build

The project uses a Nix flake for reproducible builds with a modular structure:

Build Complete Library (Binaries + Libraries + Headers)

# Build everything (default)
nix build

# Or explicitly
nix build '.#logos-liblogos'
nix build '.#default'

The result will include:

  • /bin/ - Host binary (logos_host)
  • /lib/ - Core library (liblogos_core)
  • /include/ - Headers (logos_core.h, interface.h)

Build Individual Components

# Build only the binaries (outputs to /bin)
nix build '.#logos-liblogos-bin'

# Build only the libraries (outputs to /lib)
nix build '.#logos-liblogos-lib'

# Build only the headers (outputs to /include)
nix build '.#logos-liblogos-include'

# Build and run tests
nix build '.#logos-liblogos-tests'

# Build portable variant (selects portable LGX variants instead of dev)
nix build '.#portable'

Running Tests

# Build and run tests (tests run automatically during build)
nix build '.#logos-liblogos-tests'

# To run tests manually after building:
./result/bin/logos_core_tests

# Run specific tests
./result/bin/logos_core_tests --gtest_filter=AppLifecycleTest.*

# List all available tests
./result/bin/logos_core_tests --gtest_list_tests

Development Shell

# Enter development shell with all dependencies
nix develop

Note: In zsh, you need to quote targets with # to prevent glob expansion.

If you don't have flakes enabled globally, add experimental flags:

nix build '.#logos-liblogos' --extra-experimental-features 'nix-command flakes'

The compiled artifacts can be found at result/

Modular Architecture

The nix build system is organized into modular files in the /nix directory:

  • nix/default.nix - Common configuration (dependencies, flags, metadata)
  • nix/build.nix - Shared build that compiles everything once
  • nix/bin.nix - Extracts binaries (logos_host, includes libraries for runtime linking)
  • nix/lib.nix - Extracts libraries only
  • nix/include.nix - Header installation
  • nix/tests.nix - Test suite build and execution

Note: The logos-liblogos-bin package includes both the logos_host binary and its required libraries to ensure proper runtime linking.

Local Development

To build against a local checkout of a dependency, override its input. For example the SDK:

nix build --override-input logos-cpp-sdk path:../logos-cpp-sdk

The container and format-loader abstractions live in their own repos, consumed as inputs the same way process-stats is. To build against local checkouts:

nix build \
  --override-input logos-container path:../logos-container \
  --override-input logos-module-loader path:../logos-module-loader \
  --override-input default-container path:../logos-container-subprocess \
  --override-input default-module-loader path:../logos-module-loader-qt

(default-container / default-module-loader are the input slots for the built-in default implementations; they point at the subprocess / qt-plugin repos.)

Library API

logos-liblogos exposes a C API via logos_core.h:

// Lifecycle
void logos_core_init(int argc, char *argv[]);
void logos_core_start();
void logos_core_cleanup();

// Module directory management
void logos_core_add_modules_dir(const char* dir);

// Instance persistence
void logos_core_set_persistence_base_path(const char* path);

// Per-module transport configuration (forwarded to the module's
// child subprocess so its LogosAPIProvider binds every listener
// instead of only the global default LocalSocket). Must be called
// before the module is loaded.
void logos_core_set_module_transports(const char* name, const char* transport_set_json);

// Inter-module access policy (per-target allowed-caller allowlists).
// Core parses it and registers the per-target restrictions with
// capability_module, which then denies token issuance for disallowed
// (caller, target) pairs. Call before logos_core_start(); NULL/"" clears.
void logos_core_set_access_policy(const char* policy_json);

// Module management
int  logos_core_load_module(const char* name, bool with_dependencies);
int  logos_core_unload_module(const char* name, bool with_dependents);
char* logos_core_process_module(const char* path);
void logos_core_refresh_modules();

// Dependency graph queries (forward + reverse edges; recursive walks BFS)
char** logos_core_get_module_dependencies(const char* name, bool recursive);
char** logos_core_get_module_dependents(const char* name, bool recursive);

// Module queries
char** logos_core_get_loaded_modules();
char** logos_core_get_known_modules();

// Module stats and tokens
char* logos_core_get_module_stats();
char* logos_core_get_token(const char* key);

See src/logos_core/logos_core.h for the full API.

Thread safety

Module load/unload operations (logos_core_load_module, logos_core_unload_module) are serialised internally by a single mutex. It is safe to call them concurrently from multiple threads, including rapid and repeated load/unload cycles on the same module — each call waits for its turn and the process management layer handles teardown cleanly before the next launch. logos_core_unload_module with with_dependents=true in particular holds the lock for its entire leaves-first teardown so a late-arriving load can't interleave between tearing down a dependent and its parent.

logos_core_refresh_modules is synchronised through the module registry's reader-writer lock — it is safe to call concurrently with other registry accesses, but it is not serialised against load/unload by the same mutex as above.

Read-only accessors (logos_core_get_known_modules, logos_core_get_loaded_modules) use that shared reader-writer lock and are safe to call concurrently with each other and with logos_core_refresh_modules.

Dev vs Portable Builds

The library supports two build modes controlled by the LOGOS_PORTABLE_BUILD CMake flag:

  • Dev build (default): Module loading looks for LGX variants with -dev suffix (e.g., linux-amd64-dev). Used in Nix/development environments.
  • Portable build (-DLOGOS_PORTABLE_BUILD=ON): Looks for portable variants without suffix (e.g., linux-amd64). Used in self-contained distributed applications.

Build the portable variant with nix build '.#portable'.

Supported Platforms

  • macOS (aarch64-darwin, x86_64-darwin)
  • Linux (aarch64-linux, x86_64-linux)

Building with a different container or module loader

The container and format-loader are selected by which package liblogos links — not by its C++ or CMake. To use your own, build a package that:

  • implements the contract interface (LogosCore::ModuleContainer from logos-container, or LogosCore::ModuleFormatLoader from logos-module-loader),
  • defines the factory symbol (LogosCore::makeContainer() / LogosCore::makeFormatLoader()), and
  • ships the generic CMake config (LogosContainerImpl / LogosFormatLoaderImpl, exposing the …::impl target).

(Copy the structure from logos-container-subprocess / logos-module-loader-qt.) Then point liblogos at it by overriding the input slot:

# different container
nix build '.#logos-liblogos' --override-input default-container <flake-ref>
# different format loader
nix build '.#logos-liblogos' --override-input default-module-loader <flake-ref>

<flake-ref> is e.g. github:you/your-impl or path:../your-impl; it must expose packages.<system>.default carrying that config + factory symbol. For a permanent default, add it as an input and set containerImpl / formatLoaderImpl in flake.nix. Container and loader are independent — swap either or both.

Note: the format-loader package also provides the logos_host binary that bin.nix re-exports, so a replacement loader must ship its own host binary.

Disclaimer

This repository is part of an experimental development environment. Components are under active development and may be incomplete, unstable, modified or discontinued at any time.

The software is provided for development and testing purposes only and is not intended for production use.

The code and related materials are made available on an open-source, “as-is” basis without warranties or guarantees of any kind, express or implied, including warranties of correctness, security, performance or fitness for a particular purpose. Use at your own risk.

S
Description
No description provided
Readme
5.3 MiB
Languages
C++ 77.7%
Nix 11%
CMake 7.8%
C 2.3%
Shell 1.2%