Files
logos-tutorial/logos-developer-guide.md
T

32 KiB

Logos Module Developer Guide

A comprehensive guide to creating, building, testing, packaging, and distributing modules for the Logos platform.

Table of Contents


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
    
  • 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:

  1. Inherit from QObject and implement the PluginInterface
  2. Declare an interface with Q_INTERFACES
  3. Embed metadata with Q_PLUGIN_METADATA
  4. 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_INVOKABLE method 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 eventResponse signal is used for event forwarding between modules
  • name() must match the name field in your module.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-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 main has a corresponding directory and file
  • Every variant directory has a corresponding main entry
  • 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:

  1. Resolves transitive dependencies
  2. Downloads the correct platform variant for your OS/architecture
  3. Extracts the LGX package
  4. 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 logos bridge: 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:

  1. Implement the IComponent interface
  2. Set "type": "ui" in your metadata
  3. Return a QWidget* from createWidget()
#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 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:

  1. The module binary is in the correct directory (modules dir for core, plugins dir for UI)
  2. The metadata.json file is present alongside the binary
  3. The name field in metadata matches the binary name (e.g., my_module_plugin.so for module named my_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