34 KiB
Logos Module Developer Guide
A comprehensive guide to creating, building, testing, packaging, and distributing modules for the Logos platform.
Table of Contents
- Overview
- Architecture
- Prerequisites
- Part 1: Creating a Module
- Part 2: Inspecting and Testing Your Module
- Part 3: Packaging Your Module
- Part 4: Installing and Managing Modules
- Part 5: Running in logos-basecamp
- Part 6: Inter-Module Communication
- Part 7: Advanced Topics
- Reference: Repository Map
- Reference: CLI Tools Summary
- Troubleshooting
Overview
The Logos platform is a modular application framework built in C++ on top of Qt 6. Applications are composed of dynamically loaded modules (plugins) that communicate via an IPC layer. The platform provides:
- Process isolation -- each module runs in its own host process (on desktop), communicating via Qt Remote Objects
- Cross-platform support -- macOS (arm64, x86_64) and Linux (arm64, x86_64)
- A package format (
.lgx) for distributing modules with platform-specific variants - A desktop application shell (
logos-basecamp) with a sidebar, tabbed workspace, and plugin management UI - A CLI runtime (
logoscore) for running modules headlessly
Architecture
+---------------------------------------------------------------+
| Application Layer |
| logos-basecamp (Desktop GUI) or logoscore (CLI Runtime) |
+---------------------------------------------------------------+
| | |
v v v
+---------------+ +------------------+ +------------------+
| Module A | | Module B | | Package Manager |
| (logos_host) | | (logos_host) | | Module |
+-------+-------+ +--------+---------+ +--------+---------+
| | |
| Qt Remote Objects (IPC) |
+--------------------------------------------+
|
+--------v---------+
| liblogos | (Core Runtime)
| logos-liblogos |
+--------+---------+
|
+--------v---------+
| logos-cpp-sdk | (SDK: LogosAPI,
| | Code Generator,
| | Types, IPC)
+------------------+
Key components:
| Component | Repository | Role |
|---|---|---|
| logos-module-builder | logos-co/logos-module-builder | Scaffolding and build system for new modules |
| logos-module | logos-co/logos-module | Plugin loading/introspection library + lm CLI |
| logos-cpp-sdk | logos-co/logos-cpp-sdk | C++ SDK, types, IPC layer, code generator |
| logos-liblogos | logos-co/logos-liblogos | Core runtime (logoscore, logos_host, liblogos_core) |
| logos-package | logos-co/logos-package | LGX package format library + lgx CLI |
| logos-package-manager-module | logos-co/logos-package-manager-module | Package manager module + lgpm CLI |
| logos-basecamp | logos-co/logos-basecamp | Desktop application shell |
Prerequisites
Required
-
Nix with flakes enabled. This is the primary build tool for the entire ecosystem. Install Nix from nixos.org, then enable flakes:
# If you need experimental features enabled per-command: nix --extra-experimental-features "nix-command flakes" <command> # Or enable globally in ~/.config/nix/nix.conf: experimental-features = nix-command flakes
Recommended Knowledge
- C++ (C++17)
- Qt 6 basics (
QObject,Q_INVOKABLE,Q_PLUGIN_METADATA, signals/slots) - Basic CMake
- Basic Nix concepts (flakes, derivations)
Part 1: Creating a Module
1.1 Scaffold with logos-module-builder
The fastest way to create a new module is using the logos-module-builder template:
# Create a new directory for your module
mkdir logos-my-module && cd logos-my-module
# Scaffold a minimal module (no external dependencies)
nix flake init -t github:logos-co/logos-module-builder
# Or scaffold a module that wraps an external C/C++ library
nix flake init -t github:logos-co/logos-module-builder#with-external-lib
This generates a ready-to-build project with all the boilerplate handled for you.
1.2 Project Structure
After scaffolding, your module directory looks like this:
logos-my-module/
├── flake.nix # Nix flake (build config, ~15 lines)
├── metadata.json # Single source of truth: module metadata + build config (~30 lines)
├── CMakeLists.txt # CMake build file (~25 lines)
└── src/
├── my_module_interface.h # Qt interface definition
├── my_module_plugin.h # Plugin header
└── my_module_plugin.cpp # Plugin implementation
The key insight: logos-module-builder reduces ~600 lines of configuration across 5+ files down to ~70 lines across 2-3 files. metadata.json serves as the single source of truth — it contains both the runtime metadata (embedded into the plugin binary by Qt) and the build configuration (read by the builder via the nix section).
1.3 The metadata.json Configuration
The metadata.json file is the single source of truth for your module. It contains both the runtime metadata and the build configuration (read by logos-module-builder via the nix section).
{
"name": "my_module",
"version": "1.0.0",
"type": "core",
"category": "general",
"description": "My first Logos module",
"main": "my_module_plugin",
"dependencies": [],
"nix": {
"packages": {
"build": [],
"runtime": []
},
"external_libraries": [],
"cmake": {
"find_packages": [],
"extra_sources": [],
"extra_include_dirs": [],
"extra_link_libraries": []
}
}
}
Field reference:
| Field | Required | Default | Description |
|---|---|---|---|
name |
Yes | -- | Module name (used for filenames and identifiers) |
version |
No | 1.0.0 |
Semantic version |
type |
No | core |
Module type (core, ui, ui_qml) |
category |
No | general |
Category (general, network, chat, wallet, integration) |
description |
No | "A Logos module" |
Human-readable description |
main |
Yes | -- | Plugin entry point (plugin name for core/ui, Main.qml for QML) |
dependencies |
No | [] |
Other Logos module names this depends on |
nix.packages.build |
No | [] |
Nix packages for build time |
nix.packages.runtime |
No | [] |
Nix packages for runtime |
nix.external_libraries |
No | [] |
External C/C++ libraries to link |
nix.cmake.find_packages |
No | [] |
CMake find_package() calls |
nix.cmake.extra_sources |
No | [] |
Additional source files to compile |
nix.cmake.extra_include_dirs |
No | [] |
Additional include directories |
nix.cmake.extra_link_libraries |
No | [] |
Additional libraries to link |
1.4 Writing Module Code
A Logos module is a Qt plugin. It must:
- Inherit from
QObjectand implement thePluginInterface - Declare an interface with
Q_INTERFACES - Embed metadata with
Q_PLUGIN_METADATA - Mark callable methods with
Q_INVOKABLE
The Interface Header (src/my_module_interface.h)
#pragma once
#include <QtPlugin>
#include <QString>
#include <interface.h> // From logos-cpp-sdk: provides PluginInterface
class MyModuleInterface : public PluginInterface
{
public:
virtual ~MyModuleInterface() {}
// Declare your module's public methods here
virtual QString doSomething(const QString& input) = 0;
virtual int compute(int a, int b) = 0;
};
#define MyModuleInterface_iid "com.logos.MyModuleInterface"
Q_DECLARE_INTERFACE(MyModuleInterface, MyModuleInterface_iid)
The Plugin Header (src/my_module_plugin.h)
#pragma once
#include <QObject>
#include "my_module_interface.h"
class MyModulePlugin : public QObject, public MyModuleInterface
{
Q_OBJECT
Q_INTERFACES(MyModuleInterface PluginInterface)
Q_PLUGIN_METADATA(IID MyModuleInterface_iid FILE "metadata.json")
public:
explicit MyModulePlugin(QObject* parent = nullptr);
~MyModulePlugin();
// PluginInterface
QString name() const override { return "my_module"; }
QString version() const override { return "1.0.0"; }
// Your methods -- mark with Q_INVOKABLE for remote access
Q_INVOKABLE void initLogos(LogosAPI* logosAPIInstance);
Q_INVOKABLE QString doSomething(const QString& input) override;
Q_INVOKABLE int compute(int a, int b) override;
signals:
// For event forwarding to other modules
void eventResponse(const QString& eventName, const QVariantList& data);
};
The Plugin Implementation (src/my_module_plugin.cpp)
#include "my_module_plugin.h"
#include <QDebug>
MyModulePlugin::MyModulePlugin(QObject* parent) : QObject(parent)
{
qDebug() << "MyModulePlugin: created";
}
MyModulePlugin::~MyModulePlugin()
{
qDebug() << "MyModulePlugin: destroyed";
}
void MyModulePlugin::initLogos(LogosAPI* logosAPIInstance)
{
// Store the API pointer for inter-module communication
logosAPI = logosAPIInstance;
qDebug() << "MyModulePlugin: LogosAPI initialized";
}
QString MyModulePlugin::doSomething(const QString& input)
{
return "Processed: " + input;
}
int MyModulePlugin::compute(int a, int b)
{
return a + b;
}
Key rules:
- Every
Q_INVOKABLEmethod is discoverable and callable by other modules at runtime initLogos(LogosAPI*)is called by the host when your module is loaded -- store the pointer for later use- The
eventResponsesignal is used for event forwarding between modules name()must match thenamefield in yourmetadata.json
1.5 Building Your Module
# Build everything (library + generated SDK headers)
nix build
# Build just the plugin shared library (.so / .dylib)
nix build .#lib
# Build just the generated SDK headers (for other modules to use)
nix build .#include
# Enter the development shell (provides cmake, ninja, Qt, etc.)
nix develop
# Inside the dev shell, you can also build directly with CMake:
cmake -B build -GNinja
cmake --build build
Build outputs:
result/
├── lib/
│ └── my_module_plugin.so # (or .dylib on macOS)
├── include/
│ └── ... # Generated SDK headers
└── share/
└── metadata.json # Runtime metadata
Part 2: Inspecting and Testing Your Module
2.1 The lm CLI Tool
The lm tool (from logos-module) lets you inspect compiled module binaries without loading them into the full runtime. It reads metadata and enumerates methods via Qt's meta-object system.
Building lm
nix build 'github:logos-co/logos-module#lm' --out-link ./lm
Viewing Metadata
# Human-readable metadata
./lm/bin/lm metadata ./result/lib/my_module_plugin.so
# JSON output
./lm/bin/lm metadata ./result/lib/my_module_plugin.so --json
Example JSON output:
{
"name": "my_module",
"version": "1.0.0",
"description": "My first Logos module",
"author": "",
"type": "core",
"dependencies": []
}
Viewing Methods
# Human-readable method list
./lm/bin/lm methods ./result/lib/my_module_plugin.so
# JSON output
./lm/bin/lm methods ./result/lib/my_module_plugin.so --json
Example JSON output:
[
{
"name": "initLogos",
"signature": "initLogos(LogosAPI*)",
"returnType": "void",
"isInvokable": true,
"parameters": [
{ "name": "logosAPIInstance", "type": "LogosAPI*" }
]
},
{
"name": "doSomething",
"signature": "doSomething(QString)",
"returnType": "QString",
"isInvokable": true,
"parameters": [
{ "name": "input", "type": "QString" }
]
}
]
2.2 Running with logoscore
The logoscore CLI (from logos-liblogos) is a headless runtime that can load modules and invoke their methods from the command line.
Building logoscore
nix build 'github:logos-co/logos-liblogos' --out-link ./logos
Running a Module
# Load a module from a directory
./logos/bin/logoscore \
-m ./modules \
--load-modules my_module
# Load a module and call a method
./logos/bin/logoscore \
-m ./modules \
--load-modules my_module \
-c "my_module.doSomething(hello)"
# Load a module and call a method with a JSON config file
./logos/bin/logoscore \
-m ./modules \
--load-modules my_module \
-c "my_module.configure(@config.json)"
Flags:
| Flag | Description |
|---|---|
-m <dir> |
Directory containing module libraries |
--load-modules <name1,name2> |
Comma-separated list of modules to load |
-c "<module>.<method>(args)" |
Command to execute after loading |
@file.json |
Pass a JSON file as a method argument |
2.3 The logos-module-viewer
The logos-module-viewer is a graphical tool for inspecting loaded modules.
# Build the viewer
nix build 'github:logos-co/logos-module-viewer#app' --out-link ./logos-viewer
# Run it with your module
./logos-viewer/bin/logos-module-viewer -m ./result/lib/my_module_plugin.so
This opens a window showing the module's metadata, methods, and allows interactive method invocation.
Part 3: Packaging Your Module
3.1 The LGX Package Format
Logos modules are distributed as .lgx packages. An LGX file is a gzip-compressed tar archive with a specific internal structure:
manifest.json # Package metadata (required)
manifest.cose # Optional cryptographic signature
variants/ # Platform-specific builds (required)
linux-x86_64/
my_module_plugin.so
darwin-arm64/
my_module_plugin.dylib
docs/ # Optional documentation
licenses/ # Optional license files
The manifest.json declares the package name, version, and maps each variant to its main entry point (the shared library file):
{
"name": "my_module",
"version": "1.0.0",
"description": "My first Logos module",
"author": "Developer Name",
"type": "core",
"category": "general",
"manifestVersion": "0.1",
"main": {
"linux-x86_64": "my_module_plugin.so",
"darwin-arm64": "my_module_plugin.dylib"
},
"dependencies": []
}
3.2 Creating a Package with lgx
The lgx CLI tool (from logos-package) creates and manages LGX packages.
Building lgx
nix build 'github:logos-co/logos-package#lgx' --out-link ./lgx
Creating a New Package
# Create an empty package skeleton
./lgx/bin/lgx create my_module.lgx --name my_module
Adding Platform Variants
# Add a single-file variant (the library binary)
./lgx/bin/lgx add-variant my_module.lgx \
--variant linux-x86_64 \
--files ./result/lib/my_module_plugin.so
# Add a macOS variant
./lgx/bin/lgx add-variant my_module.lgx \
--variant darwin-arm64 \
--files ./result-macos/lib/my_module_plugin.dylib
# Add a directory variant (if your module has multiple files)
./lgx/bin/lgx add-variant my_module.lgx \
--variant linux-x86_64 \
--files ./result/lib/ \
--main my_module_plugin.so
Variant naming convention: <os>-<arch> (lowercase). Common variants:
| Variant | Platform |
|---|---|
linux-x86_64 |
Linux Intel/AMD 64-bit |
linux-arm64 |
Linux ARM 64-bit |
darwin-arm64 |
macOS Apple Silicon |
darwin-x86_64 |
macOS Intel |
Removing a Variant
./lgx/bin/lgx remove-variant my_module.lgx --variant linux-x86_64
Listing Package Contents
./lgx/bin/lgx list my_module.lgx
Extracting a Package
# Extract a specific variant
./lgx/bin/lgx extract my_module.lgx --variant linux-x86_64 --output ./extracted/
# Extract all variants
./lgx/bin/lgx extract my_module.lgx --all --output ./extracted/
3.3 Verifying Packages
./lgx/bin/lgx verify my_module.lgx
This checks:
- Package structure is valid (manifest.json exists, variants/ directory exists)
- Manifest fields are present and valid
- Every variant listed in
mainhas a corresponding directory and file - Every variant directory has a corresponding
mainentry - No forbidden files (symlinks, special files) are present
- All paths are valid (no
..traversal, no absolute paths)
Part 4: Installing and Managing Modules
4.1 The lgpm CLI
The lgpm CLI (Logos Package Manager) installs, searches, and manages module packages.
Building lgpm
nix build 'github:logos-co/logos-package-manager-module#cli' --out-link ./package-manager
Commands
# Search for packages
lgpm search waku
# List all available packages
lgpm list
# List only installed packages
lgpm list --installed
# List packages in a category
lgpm list --category networking
# Show package details
lgpm info my_module
# List available categories
lgpm categories
# Install a package (with dependency resolution)
lgpm install my_module
# Install multiple packages
lgpm install my_module another_module
# Install from a local .lgx file
lgpm install --file ./my_module.lgx
Global Options
| Option | Description |
|---|---|
--modules-dir <path> |
Target directory for installed core modules |
--ui-plugins-dir <path> |
Target directory for UI plugins |
--release <tag> |
GitHub release tag to use (default: latest) |
--json |
Output in JSON format |
-h, --help |
Show help |
4.2 Installing from Local Files
# Install a locally built .lgx package
./package-manager/bin/lgpm --modules-dir ./modules install --file ./my_module.lgx
4.3 Installing from a Registry
# Install a published package (lgpm fetches from GitHub Releases)
./package-manager/bin/lgpm --modules-dir ./modules install my_module
# Install from a specific release
./package-manager/bin/lgpm --modules-dir ./modules --release v2.0.0 install my_module
The package manager automatically:
- Resolves transitive dependencies
- Downloads the correct platform variant for your OS/architecture
- Extracts the LGX package
- Copies the library to the target directory
Part 5: Running in logos-basecamp
5.1 Building logos-basecamp
# Build the full application
nix build 'github:logos-co/logos-basecamp#app' --out-link ./logos-basecamp
# Run it
./logos-basecamp/bin/logos-basecamp
# Or build platform-specific distributions:
nix build 'github:logos-co/logos-basecamp#bin-appimage' # Linux AppImage
nix build 'github:logos-co/logos-basecamp#bin-macos-app' # macOS .app bundle
nix build 'github:logos-co/logos-basecamp#bin-macos-dmg' # macOS DMG
5.2 Module Types in logos-basecamp
The application supports three types of modules:
Core Modules (Backend)
These are non-UI modules that provide backend functionality. They run in isolated logos_host processes and communicate via Qt Remote Objects.
- Loaded via
logos_core_load_plugin() - Placed in the modules directory (
--modules-dir) - Have
"type": "core"in metadata
C++ UI Modules (Native Widgets)
These provide native Qt widget UIs. They implement the IComponent interface:
class IComponent {
public:
virtual ~IComponent() = default;
virtual QWidget* createWidget(LogosAPI* logosAPI = nullptr) = 0;
virtual void destroyWidget(QWidget* widget) = 0;
};
- Loaded via
QPluginLoader - Placed in the plugins directory (
--ui-plugins-dir) - Their widget appears as a tab in the MDI workspace
QML UI Modules (Sandboxed)
These provide QML-based UIs in a sandboxed environment:
- Have
"type": "ui_qml"in their manifest - Entry point is
Main.qml - Network access is denied
- Filesystem access is restricted to the module's own directory
- Can call core modules via the
logosbridge:logos.callModule("module", "method", [args])
5.3 Development Mode
For rapid iteration on QML UI modules, use the development mode launcher:
# Build once
nix build 'github:logos-co/logos-basecamp'
# Run with live QML reloading (edits to .qml files take effect immediately)
./run-dev.sh
This sets QML_UI to point to the source directory and disables QML caching, so you can edit QML files and see changes without rebuilding.
Part 6: Inter-Module Communication
6.1 The LogosAPI
Every module receives a LogosAPI* pointer when initLogos() is called. This is your gateway to communicating with other modules.
void MyModulePlugin::initLogos(LogosAPI* logosAPIInstance)
{
logosAPI = logosAPIInstance;
// Get a client for calling another module
LogosAPIClient* client = logosAPI->getClient("other_module");
// Call a method on that module
QVariant result = client->invokeRemoteMethod(
"other_module", // target module name
"someMethod", // method name
arg1, arg2 // arguments (up to 5 positional args)
);
}
6.2 The C++ SDK Code Generator
The logos-cpp-generator tool (from logos-cpp-sdk) inspects a compiled module and generates typed C++ wrapper classes, so you get compile-time type safety instead of raw invokeRemoteMethod calls.
Generating Wrappers
# Generate wrappers for a single module
logos-cpp-generator /path/to/my_module_plugin.so --output-dir ./generated
# Generate wrappers for all dependencies listed in metadata.json
logos-cpp-generator --metadata metadata.json --module-dir /path/to/modules --output-dir ./generated
# Generate only module files (no umbrella headers)
logos-cpp-generator /path/to/plugin.so --module-only --output-dir ./generated
# Generate only umbrella SDK files (assumes module files exist)
logos-cpp-generator --metadata metadata.json --general-only --output-dir ./generated
Using Generated Wrappers
After generation, you get typed wrapper classes:
#include "logos_sdk.h" // Umbrella header
// In your module's initLogos():
void MyModulePlugin::initLogos(LogosAPI* api) {
logosAPI = api;
// Create the typed SDK wrapper
LogosModules* logos = new LogosModules(api);
// Call other modules with type safety
QString result = logos->other_module.doSomething("hello");
bool ok = logos->core_manager.loadPlugin("another_module");
}
The generated LogosModules struct provides a member for each module, with methods matching the module's Q_INVOKABLE methods.
6.3 LogosResult
Many module methods return LogosResult for structured success/error handling:
LogosResult result = logos->my_module.someMethod();
if (result.success) {
// Access the value
QString value = result.getString();
int number = result.getInt();
QVariantMap map = result.getMap();
QVariantList list = result.getList();
// Access nested values
QString name = result.getString("name");
int count = result.getInt("count", 0); // with default
} else {
// Access the error
QString error = result.getError();
}
To return a LogosResult from your module:
Q_INVOKABLE LogosResult MyModulePlugin::fetchData(const QString& id) {
if (id.isEmpty()) {
return {false, QVariant(), "ID cannot be empty"};
}
QVariantMap data;
data["id"] = id;
data["name"] = "Example";
data["count"] = 42;
return {true, data};
}
6.4 Communication Modes
The SDK supports two communication modes:
| Mode | Use Case | Mechanism |
|---|---|---|
| Remote (default) | Desktop apps | Qt Remote Objects (IPC between processes) |
| Local | Mobile apps, single-process | In-process PluginRegistry |
Set the mode before creating any LogosAPI instances:
// For mobile / embedded (all modules in one process)
LogosModeConfig::setMode(LogosMode::Local);
// For desktop (each module in its own process) -- this is the default
LogosModeConfig::setMode(LogosMode::Remote);
Part 7: Advanced Topics
7.1 Wrapping External Libraries
To create a module that wraps an external C/C++ library, use the external library template:
nix flake init -t github:logos-co/logos-module-builder#with-external-lib
Then configure the external library in the nix section of metadata.json:
{
"name": "my_wrapper_module",
"version": "1.0.0",
"description": "Wraps libfoo for Logos",
"main": "my_wrapper_module_plugin",
"dependencies": [],
"nix": {
"external_libraries": [
{
"name": "libfoo",
"flake_input": "github:example/libfoo",
"output_pattern": "lib/libfoo.*"
}
]
}
}
For a vendored library, use vendor_path and build_command:
{
"nix": {
"external_libraries": [
{
"name": "libfoo",
"vendor_path": "vendor/libfoo",
"build_command": "make",
"output_pattern": "build/lib/libfoo.*"
}
]
}
}
For a Go library with C bindings:
{
"nix": {
"external_libraries": [
{
"name": "libfoo",
"vendor_path": "vendor/libfoo",
"go_build": true,
"output_pattern": "libfoo.*"
}
]
}
}
The builder handles downloading, building, and linking the external library into your module.
7.2 UI Modules (C++ Widgets)
To create a module with a native Qt widget UI:
- Implement the
IComponentinterface - Set
"type": "ui"in your metadata - Return a
QWidget*fromcreateWidget()
IComponent.h is not part of the SDK — each UI module vendors its own copy in interfaces/IComponent.h:
// interfaces/IComponent.h (copy verbatim into your module)
#pragma once
#include <QObject>
#include <QWidget>
#include <QtPlugin>
class LogosAPI;
class IComponent {
public:
virtual ~IComponent() = default;
virtual QWidget* createWidget(LogosAPI* logosAPI = nullptr) = 0;
virtual void destroyWidget(QWidget* widget) = 0;
};
#define IComponent_iid "com.logos.component.IComponent"
Q_DECLARE_INTERFACE(IComponent, IComponent_iid)
Expose it via INCLUDE_DIRS in your CMakeLists.txt:
logos_module(
NAME my_ui_module
SOURCES src/my_ui_plugin.h src/my_ui_plugin.cpp
INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/interfaces
)
Then implement the plugin:
#include <IComponent.h>
class MyUIPlugin : public QObject, public IComponent
{
Q_OBJECT
Q_INTERFACES(IComponent)
Q_PLUGIN_METADATA(IID IComponent_iid FILE "metadata.json")
public:
Q_INVOKABLE QWidget* createWidget(LogosAPI* logosAPI = nullptr) override {
auto* widget = new QWidget();
// Build your UI here
return widget;
}
void destroyWidget(QWidget* widget) override {
delete widget;
}
};
7.3 UI Modules (QML)
For a QML-based UI module, create a directory with:
my_qml_module/
├── manifest.json
├── metadata.json
└── Main.qml
manifest.json:
{
"type": "ui_qml",
"main": "Main.qml",
"name": "my_qml_module",
"version": "1.0.0"
}
Main.qml:
import QtQuick 2.15
import QtQuick.Controls 2.15
Item {
width: 400
height: 300
Button {
text: "Call Core Module"
onClicked: {
// logos bridge is injected by the host
var result = logos.callModule("my_module", "doSomething", ["hello"])
console.log("Result:", result)
}
}
}
QML modules are sandboxed: no network access, no filesystem access outside the module directory.
7.4 Module Dependencies
Declare dependencies in your metadata.json:
{
"name": "my_module",
"dependencies": ["package_manager", "waku_module"]
}
Each entry in dependencies must match the name field in that module's own metadata.json. When adding a dependency as a flake input, the input attribute name must also match the dependency name — e.g., waku_module.url = "github:logos-co/logos-waku-module". The URL can point to any repo, but the attribute name is how the builder resolves dependencies.
When your module is installed via lgpm, its dependencies are automatically resolved and installed first. When loaded via logos-basecamp, core module dependencies are loaded before your module.
Reference: Repository Map
| Repository | What It Provides | Key Outputs |
|---|---|---|
| logos-module-builder | Build system / scaffolding | mkLogosModule Nix function, LogosModule.cmake, templates |
| logos-module | Plugin introspection | liblogos_module.a (static lib), lm (CLI) |
| logos-cpp-sdk | SDK + code generator | LogosAPI, LogosResult, logos-cpp-generator, PluginInterface |
| logos-liblogos | Core runtime | logoscore (CLI), logos_host, liblogos_core |
| logos-package | Package format | lgx (CLI), liblgx (library) |
| logos-package-manager-module | Package management | lgpm (CLI), package_manager_plugin |
| logos-basecamp | Desktop app shell | LogosApp (GUI), MDI workspace, plugin loader |
Reference: CLI Tools Summary
lm -- Module Inspector
lm metadata <plugin-file> [--json] # View module metadata
lm methods <plugin-file> [--json] # List Q_INVOKABLE methods
logoscore -- Headless Runtime
logoscore -m <modules-dir> --load-modules <name> [-c "<module>.<method>(args)"]
lgx -- Package Tool
lgx create <output.lgx> --name <name> # Create empty package
lgx add-variant <pkg.lgx> --variant <name> --files <path> [--main <file>]
lgx remove-variant <pkg.lgx> --variant <name>
lgx list <pkg.lgx> # List contents
lgx verify <pkg.lgx> # Validate structure
lgx extract <pkg.lgx> --variant <name> --output <dir> # Extract
lgpm -- Package Manager
lgpm search <query> # Search packages
lgpm list [--category <cat>] [--installed] # List packages
lgpm install <pkg> [pkgs...] # Install with dependency resolution
lgpm install --file <path.lgx> # Install local file
lgpm info <pkg> # Package details
lgpm categories # List categories
logos-cpp-generator -- SDK Code Generator
logos-cpp-generator <plugin-file> [--output-dir <dir>] [--module-only]
logos-cpp-generator --metadata <metadata.json> --module-dir <dir> [--output-dir <dir>]
logos-cpp-generator --metadata <metadata.json> --general-only [--output-dir <dir>]
Troubleshooting
"experimental features" error with Nix
If you see errors about experimental features, either pass the flag:
nix --extra-experimental-features "nix-command flakes" build
Or add to ~/.config/nix/nix.conf:
experimental-features = nix-command flakes
Module loads but LogosAPI is not available
This happens when running a module outside the full Logos runtime (e.g., in the module viewer). The LogosAPI is only available when the module is loaded by logoscore or logos-basecamp.
Build fails finding Qt
Ensure you're building inside the Nix environment:
nix develop # Enter dev shell with all dependencies
cmake -B build -GNinja && cmake --build build
Module not discovered by logos-basecamp
Check that:
- The module binary is in the correct directory (modules dir for core, plugins dir for UI)
- The
metadata.jsonfile is present alongside the binary - The
namefield in metadata matches the binary name (e.g.,my_module_plugin.sofor module namedmy_module)
lgpm install fails
- Check your internet connection (lgpm fetches from GitHub Releases)
- Try specifying a release:
lgpm --release v1.0.0 install my_module - For local files:
lgpm install --file ./my_module.lgx - Check the target directory is writable:
lgpm --modules-dir ./modules install my_module
Cross-platform builds
Build on each target platform separately, then add each binary as a variant to the same .lgx package:
# On Linux x86_64:
nix build .#lib
lgx add-variant my_module.lgx --variant linux-x86_64 --files ./result/lib/my_module_plugin.so
# On macOS arm64:
nix build .#lib
lgx add-variant my_module.lgx --variant darwin-arm64 --files ./result/lib/my_module_plugin.dylib