33 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 library (logos_host, liblogos_core) |
| logos-logoscore-cli | logos-co/logos-logoscore-cli | Headless CLI runtime (logoscore) |
| 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)
├── module.yaml # Declarative module configuration (~30 lines)
├── CMakeLists.txt # CMake build file (~25 lines)
├── metadata.json # Auto-generated at build time from module.yaml
└── 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.
1.3 The module.yaml Configuration
The module.yaml file is the central configuration for your module:
name: my_module
version: 1.0.0
type: core
category: general
description: "My first Logos module"
dependencies: []
# Nix packages needed at build/runtime (optional)
nix_packages:
build: []
runtime: []
# CMake configuration (optional)
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 |
category |
No | general |
Category (general, network, chat, wallet, integration) |
description |
No | "A Logos module" |
Human-readable description |
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 |
cmake.find_packages |
No | [] |
CMake find_package() calls |
cmake.extra_sources |
No | [] |
Additional source files to compile |
cmake.extra_include_dirs |
No | [] |
Additional include directories |
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 yourmodule.yaml/metadata.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-logoscore-cli) is a headless runtime that can load modules and invoke their methods from the command line.
Building logoscore
nix build 'github:logos-co/logos-logoscore-cli' --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 module.yaml:
name: my_wrapper_module
version: 1.0.0
description: "Wraps libfoo for Logos"
external_libraries:
- name: libfoo
flake_input: "github:example/libfoo"
output_pattern: "lib/libfoo.*"
# Or for a vendored library:
external_libraries:
- name: libfoo
vendor_path: "vendor/libfoo"
build_command: "make"
output_pattern: "build/lib/libfoo.*"
# Or for a Go library:
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()
#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 module.yaml:
name: my_module
dependencies:
- package_manager
- waku_module
Or in metadata.json:
{
"name": "my_module",
"dependencies": ["package_manager", "waku_module"]
}
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 library | logos_host, liblogos_core |
| logos-logoscore-cli | Headless CLI runtime | logoscore (CLI) |
| 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