Skip to main content

Build and run a Logos core module

Scaffold, build, package, and test a core module on Logos.

Logos is a modular application framework built on Qt 6. Applications are composed of dynamically loaded modules (Qt plugins) that provide features and functionality.

Logos core modules are non-UI modules that provide backend functionality. Core modules run in isolated logos_host processes and communicate via Qt Remote Objects.

info

For other module types, check out Wrap a C Library as a Logos core module and Build a Logos C++ UI module. These guides — along with the LGX package format and bundling reference and the Logos CLI Reference — are still being written; the linked pages are placeholders for now.

Before you start, make sure you have the following:

  • Linux (x86_64 or aarch64) or macOS (arm64 or x86_64)

  • At least 10 GB of disk space

  • Nix with flakes enabled

  • Git

  • logoscore, and lgpm installed. To install these tools, use the install-node-tools.sh helper script:

    curl -fsSL https://raw.githubusercontent.com/logos-co/logos-docs/main/resources/scripts/install-node-tools.sh | sh
    export PATH="$PWD/bin:$PATH"
  • Basic familiarity with C++ (C++17), Qt 6 (QObject, Q_INVOKABLE, signals/slots), CMake, and Nix concepts

What to expect

  • You can scaffold, configure, and build a Logos core module using the templates provided by logos-module-builder.
  • You can inspect the compiled module's metadata and methods using the lm CLI tool or the logos-module-viewer graphical tool.
  • You can package, install, and call your module's methods through logoscore or load it into logos-basecamp.

Step 1: Scaffold the module project

The logos-module-builder provides four scaffolding templates for different module types. To create a Logos core module, choose the default template that produces a minimal core module with a C++ backend and no UI.

  1. Create a new directory using your module's name and initialise it from the module builder template. Replace <module-name> with your module's name, for example, my_module.

    mkdir <module-name> && cd <module-name>
    nix flake init -t github:logos-co/logos-module-builder/tutorial-v1
  2. Review the project directory. The generated project structure looks like this:

    <module-name>/
    ├── flake.nix
    ├── metadata.json
    ├── CMakeLists.txt
    └── src/
    ├── minimal_interface.h
    ├── minimal_plugin.h
    └── minimal_plugin.cpp

    The template uses minimal as a placeholder in the source filenames, class names, and identifiers. You replace these placeholders with your module's name in Step 2.

    info

    The metadata.json file is the single source of truth for your module. Read LGX package format and bundling reference for more details.

Step 2: Adapt the template for your module

The template generates files with placeholder names like minimal/Minimal and doSomething. Replace these in every generated file to match your module's name and methods.

  1. Edit metadata.json and set name, version, description, and main to match your module.

    • name must be a valid C identifier; it is used in filenames, method calls, and module loading.
    • main must match the plugin filename without the extension (for example, my_module_plugin resolves to my_module_plugin.so or .dylib).
    • Leave the other fields (type, category, dependencies, and the nix block with packages, external_libraries, cmake.find_packages) in place. The bundler relies on the nix block to build the LGX package.
  2. Edit CMakeLists.txt and update the project() name and the NAME and SOURCES values inside the logos_module() call.

    • The CMakeLists.txt uses the logos_module() macro to handle Qt plugin setup.
    • NAME must match the name field in metadata.json. A mismatch causes the build to succeed but the install phase to fail.
  3. Edit flake.nix and update the description field.

    • The generated flake.nix uses an unpinned logos-module-builder URL. For reproducible builds, pin it to tutorial-v1.
  4. Rename the source files in src/ to match your module name.

    mv src/minimal_interface.h src/<module-name>_interface.h
    mv src/minimal_plugin.h src/<module-name>_plugin.h
    mv src/minimal_plugin.cpp src/<module-name>_plugin.cpp
  5. Edit the interface header (src/<module-name>_interface.h) and replace the class name, include guard, and interface ID string.

    • Declare each method your module exposes as Q_INVOKABLE virtual and pure-virtual.
    • The interface ID (for example, "org.logos.MyModuleInterface") must be unique across all modules.
  6. Edit the plugin header (src/<module-name>_plugin.h) and replace the class name, include guard, and interface references.

    • Q_PLUGIN_METADATA(IID ... FILE "metadata.json") embeds the metadata into the binary.
    • Q_INTERFACES must list both your interface and PluginInterface.
    • name() must return the same string as the name field in metadata.json.
    • Declare initLogos(LogosAPI* api) as Q_INVOKABLE but not override.
  7. Edit the plugin implementation (src/<module-name>_plugin.cpp) and replace the placeholder method bodies with your logic.

    • In initLogos, assign the LogosAPI* pointer to the global logosAPI variable, not to a class member.
tip

Run grep -ri "minimal" . after editing to catch any remaining placeholder references (minimal, Minimal, MINIMAL_*, MinimalInterface_iid) before building.

Step 3: Build the module

  1. Initialise a Git repository.

    git init && git add -A
  2. Build the full module output (library and generated SDK headers).

    nix build
    • Use nix build '.#lib' to build only the plugin shared library.
    • Use nix build '.#include' to build only the generated SDK headers.
  3. Verify the build output contains the plugin binary and generated headers:

    result/
    ├── lib/
    │ └── <module-name>_plugin.so # (or .dylib on macOS)
    └── include/
    ├── <module-name>_api.h # Generated type-safe wrapper header
    └── <module-name>_api.cpp # Generated wrapper implementation

Step 4: Inspect your module

You can inspect the compiled module binary to verify the embedded metadata and exposed methods using the lm CLI tool or the logos-module-viewer graphical tool.

Inspect with the 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.

  1. Build the lm tool from the logos-module repository.

    nix build 'github:logos-co/logos-module/tutorial-v1#lm' --out-link ./lm
  2. View the module metadata and confirm the information is correct.

    ./lm/bin/lm metadata result/lib/<module-name>_plugin.so
    • Append --json for JSON output.

    The JSON output looks like this:

    {
    "name": "my_module",
    "version": "1.0.0",
    "description": "My first Logos module",
    "author": "",
    "type": "core",
    "dependencies": []
    }
  3. View the module methods.

    ./lm/bin/lm methods result/lib/<module-name>_plugin.so
    • Append --json for JSON output.

    The JSON output looks like this:

    [
    {
    "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" }
    ]
    }
    ]

    The output also includes any Qt signals declared in the module (for example, eventResponse) with isInvokable: false.

Inspect with the graphical tool

The logos-module-viewer is a graphical tool for inspecting loaded modules. It displays the module's metadata and methods in a graphical interface and lets you call methods interactively.

  1. Build the viewer.

    nix build 'github:logos-co/logos-module-viewer/tutorial-v1#app' --out-link ./logos-viewer
  2. Launch the viewer with the module binary.

    ./logos-viewer/bin/logos-module-viewer -m ./result/lib/<module-name>_plugin.so

Step 5: Package the module

Before you can run your module with logoscore or install it into logos-basecamp, you need to package the build output into an .lgx package and install it into a modules/ directory. Check out the LGX package format and bundling reference for more details on the format and bundling options.

info

The manifest.json is auto-generated from your module's metadata.json by the bundler. It maps each variant to its main entry point.

There are two ways to create .lgx packages:

  • Use the built-in Nix derivation that comes with logos-module-builder (preferred).
  • Use the nix bundle command directly.

Use the Nix derivation

When your module uses logos-module-builder, LGX package outputs are automatically available as part of your flake (the builder includes nix-bundle-lgx internally).

  1. Bundle the module into an LGX package that uses /nix/store references for local development.

    nix build .#lgx
    • Use #lgx-portable for a self-contained, all dependencies bundled package: nix build .#lgx-portable.
  2. Check the result/ directory and confirm the logos-<module-name>-module-lib.lgx file is present.

    info

    .#lgx produces a single variant (for example, linux-amd64) and .#lgx-portable produces a single portable variant. Neither produces the -dev variant that logos-basecamp dev builds expect. If you need the dev variant for use with logos-basecamp, use the #dual bundler described in the next section.

Use the nix bundle command

The nix bundle command is useful if your module does not use logos-module-builder, or if you need the dual bundling mode (both dev and portable in a single .lgx file) which is only available via the nix bundle command.

  1. Bundle the module into an LGX package using the nix bundle command.

    nix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3 .#lib
    • Use #portable for a self-contained package with no /nix/store references: nix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3#portable .#lib.
    • Use #dual to produce both -dev and portable variants in a single .lgx file: nix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3#dual .#lib. Use this mode when you need to install the module into a dev build of logos-basecamp.
  2. Check the current directory for the bundle output. nix bundle creates a symlink directory in the current directory named ./logos-<module-name>-module-lib-lgx-<version>/, and the .lgx file is inside it at ./logos-<module-name>-module-lib-lgx-<version>/logos-<module-name>-module-lib.lgx.

tip

Check out LGX package format and bundling reference for more details on the format and bundling options.

Step 6: Install the module

Before you can run your module with logoscore or logos-basecamp, install the LGX package into a modules/ directory that the runtime can load from.

There are two ways to install .lgx packages:

  • Install a locally built .lgx package
  • Download and install a .lgx file from a registry

Install a locally built .lgx package

  1. Create the modules/ directory and install the .lgx package.

    lgpm --modules-dir ./modules install --file result/logos-<module-name>-module-lib.lgx
    • Use --dir instead of --file to install all LGX packages in a directory at once: ./package-manager/bin/lgpm --modules-dir ./modules install --dir ./packages/
    • If you bundled with nix bundle, the path is ./logos-<module-name>-module-lib-lgx-<version>/logos-<module-name>-module-lib.lgx instead of result/....
  2. Verify the installed module directory. The directory contains manifest.json, the plugin binary (.so or .dylib), and a variant file.

Download and install a .lgx file from a registry

The Logos module catalogue is hosted on GitHub Releases in the logos-modules repository. Use lgpd to search and download packages, then lgpm to install them locally.

warning

Registry packages currently ship portable variants only (for example, linux-amd64, darwin-arm64). They cannot be installed into a dev build of logos-basecamp, which expects -dev variants. To use a registry module with a dev build, you must build the module from source and bundle it with #dual. They install cleanly into logoscore and into portable builds of logos-basecamp.

  1. Search the catalogue for the module you want to install. Replace <registry-name> with the registry name of the module you want to find (for example, logos-chat-module).

    lgpd search <registry-name>
    tip

    Use lgpd list to browse all available packages.

  2. Download the LGX package to a local directory.

    lgpd download <registry-name> -o ./packages/
    • Use --release <tag> to download from a specific release version. For example: ./downloader/bin/lgpd --release v2.0.0 download <registry-name> -o ./packages/
    • The downloaded file is named after the module's internal name field, not the registry name. For example, lgpd download logos-chat-module writes ./packages/chat_module.lgx.
  3. Create the modules/ directory and install the downloaded package. Replace <downloaded-name> with the actual filename written by lgpd (for example, chat_module.lgx).

    lgpm --modules-dir ./modules install --file ./packages/<downloaded-name>.lgx
    • Use --ui-plugins-dir instead of --modules-dir when installing UI modules.

Step 7: Run the module

There are two Logos runtimes, logoscore and logos-basecamp, that can load and run your module. However, to interact with your module directly through the logos-basecamp interface, you need to provide a UI module.

Run with logoscore

The logoscore CLI (from logos-liblogos) is a headless runtime that can load modules and invoke their methods from the command line. It runs as a daemon that stays alive to host modules.

  1. Start the logoscore daemon with the modules/ directory.

    logoscore -D -m ./modules
  2. From another terminal, load the module and call a method. Replace <method> and <args> with the method name and arguments you want to call.

    logoscore load-module <module-name>
    logoscore call <module-name> <method> <args>
  3. Stop the daemon when finished.

    logoscore stop
tip

Check out Logos CLI Reference for more details on available commands and options.

Run with logos-basecamp

Logos Basecamp is a desktop shell that provides a graphical interface for managing and running modules. Core modules run as background services in logos-basecamp. Other UI modules can call them through LogosAPI or the logos.callModule() bridge once they are installed.

warning

The LGX variant type must match the basecamp build type. Dev builds of basecamp expect dev LGX variants (for example, darwin-arm64-dev), and portable builds expect portable variants (for example, darwin-arm64). Check out the LGX package format and bundling reference for more details.

  1. Build the development version of logos-basecamp.

    nix build 'github:logos-co/logos-basecamp/tutorial-v1#app' --out-link ./logos-basecamp
  2. Launch logos-basecamp once to create its data directory and preinstall bundled modules, then close it.

    ./logos-basecamp/bin/logos-basecamp
    • Look for the directory containing modules/ and plugins/ subdirectories at ~/Library/Application Support/Logos/LogosBasecamp/ (macOS) or ~/.config/Logos/LogosBasecamp/ (Linux).
  3. Set the BASECAMP_DIR variable to your platform's path.

    # macOS
    BASECAMP_DIR="$HOME/Library/Application Support/Logos/LogosBasecamp"

    # Linux
    BASECAMP_DIR="$HOME/.config/Logos/LogosBasecamp"
  4. Install the module's dev LGX package into basecamp's modules directory. The package must contain a -dev variant for your platform; build it with nix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3#dual .#lib as described in Step 5.

    lgpm --modules-dir "$BASECAMP_DIR/modules" install --file ./logos-<module-name>-module-lib-lgx-<version>/logos-<module-name>-module-lib.lgx
tip

Try running the Blockchain module, Storage module or Chat module or browse the full list of Logos modules.

Troubleshooting

Known constraints

A single logoscore instance supports only one instance of a module, so all dependent apps share that state. To run multiple independent instances, start separate logos-basecamp instances with their own user directories, and each logos-basecamp instance runs its own logoscore and module instances in isolation.

Nix reports an "experimental features" error

If you see errors about experimental features, either pass the flag:

nix --extra-experimental-features "nix-command flakes" build

Or add the following to ~/.config/nix/nix.conf:

experimental-features = nix-command flakes

Module not discovered by logos-basecamp

Confirm the module is in a subdirectory of the modules/ directory (for example, modules/my_module/) and that the subdirectory contains the module binary and manifest.json. The name field in manifest.json must match the binary name (for example, my_module for my_module_plugin.so).

Module not discovered by logoscore

Confirm the module is in a subdirectory of the modules/ directory (for example, modules/my_module/) and that the subdirectory contains a manifest.json with a main object matching your OS and architecture.

lgpm fails to install a module

Verify the target directory exists and is writable. If installing from a local .lgx file, confirm the file path is correct (the bundler writes logos-<module-name>-module-lib.lgx, not <module-name>.lgx). If installing from the registry, check your internet connection — lgpd fetches packages from GitHub Releases. To pin a specific release version, pass --release <tag> to lgpd (not lgpm) when downloading.

LGX variant mismatch

If lgpm install fails with Package does not contain variant for platform: <platform>-dev, the LGX file does not include a -dev variant for your platform.

  • nix build .#lgx produces a single variant (for example, linux-amd64) suitable for logoscore but not for a dev build of logos-basecamp.
  • nix build .#lgx-portable produces a single portable variant suitable for portable builds of logos-basecamp.
  • nix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3#dual .#lib produces both -dev and portable variants in a single .lgx file, which works with dev and portable builds of logos-basecamp.

Registry packages downloaded with lgpd currently ship portable variants only.

info

lgpm error messages report the platform as linux-x86_64 while LGX manifests label it linux-amd64. These refer to the same architecture.

nix build .#lib does nothing or fails silently

Some shells (notably zsh) treats # as a comment character outside quotes. Quote the flake reference so the # reaches nix intact.

nix build '.#lib'

First build is slow

The first nix build downloads Qt 6, the Logos C++ SDK, the code generator, and the rest of the build dependencies. This is a one-time cost, subsequent builds reuse the cache and typically complete in under 30 seconds.