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.
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, andlgpminstalled. To install these tools, use theinstall-node-tools.shhelper script:curl -fsSL https://raw.githubusercontent.com/logos-co/logos-docs/main/resources/scripts/install-node-tools.sh | shexport 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
lmCLI tool or thelogos-module-viewergraphical tool. - You can package, install, and call your module's methods through
logoscoreor load it intologos-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.
-
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 -
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.cppThe template uses
minimalas a placeholder in the source filenames, class names, and identifiers. You replace these placeholders with your module's name in Step 2.infoThe
metadata.jsonfile 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.
-
Edit
metadata.jsonand setname,version,description, andmainto match your module.namemust be a valid C identifier; it is used in filenames, method calls, and module loading.mainmust match the plugin filename without the extension (for example,my_module_pluginresolves tomy_module_plugin.soor.dylib).- Leave the other fields (
type,category,dependencies, and thenixblock withpackages,external_libraries,cmake.find_packages) in place. The bundler relies on thenixblock to build the LGX package.
-
Edit
CMakeLists.txtand update theproject()name and theNAMEandSOURCESvalues inside thelogos_module()call.- The
CMakeLists.txtuses thelogos_module()macro to handle Qt plugin setup. NAMEmust match thenamefield inmetadata.json. A mismatch causes the build to succeed but the install phase to fail.
- The
-
Edit
flake.nixand update thedescriptionfield.- The generated
flake.nixuses an unpinnedlogos-module-builderURL. For reproducible builds, pin it totutorial-v1.
- The generated
-
Rename the source files in
src/to match your module name.mv src/minimal_interface.h src/<module-name>_interface.hmv src/minimal_plugin.h src/<module-name>_plugin.hmv src/minimal_plugin.cpp src/<module-name>_plugin.cpp -
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 virtualand pure-virtual. - The interface ID (for example,
"org.logos.MyModuleInterface") must be unique across all modules.
- Declare each method your module exposes as
-
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_INTERFACESmust list both your interface andPluginInterface.name()must return the same string as thenamefield inmetadata.json.- Declare
initLogos(LogosAPI* api)asQ_INVOKABLEbut notoverride.
-
Edit the plugin implementation (
src/<module-name>_plugin.cpp) and replace the placeholder method bodies with your logic.- In
initLogos, assign theLogosAPI*pointer to the globallogosAPIvariable, not to a class member.
- In
Run grep -ri "minimal" . after editing to catch any remaining placeholder references (minimal, Minimal, MINIMAL_*, MinimalInterface_iid) before building.
Step 3: Build the module
-
Initialise a Git repository.
git init && git add -A -
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.
- Use
-
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.
-
Build the
lmtool from thelogos-modulerepository.nix build 'github:logos-co/logos-module/tutorial-v1#lm' --out-link ./lm -
View the module metadata and confirm the information is correct.
./lm/bin/lm metadata result/lib/<module-name>_plugin.so- Append
--jsonfor JSON output.
The JSON output looks like this:
{"name": "my_module","version": "1.0.0","description": "My first Logos module","author": "","type": "core","dependencies": []} - Append
-
View the module methods.
./lm/bin/lm methods result/lib/<module-name>_plugin.so- Append
--jsonfor 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) withisInvokable: false. - Append
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.
-
Build the viewer.
nix build 'github:logos-co/logos-module-viewer/tutorial-v1#app' --out-link ./logos-viewer -
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.
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 bundlecommand 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).
-
Bundle the module into an LGX package that uses
/nix/storereferences for local development.nix build .#lgx- Use
#lgx-portablefor a self-contained, all dependencies bundled package:nix build .#lgx-portable.
- Use
-
Check the
result/directory and confirm thelogos-<module-name>-module-lib.lgxfile is present.info.#lgxproduces a single variant (for example,linux-amd64) and.#lgx-portableproduces a single portable variant. Neither produces the-devvariant thatlogos-basecampdev builds expect. If you need the dev variant for use withlogos-basecamp, use the#dualbundler 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.
-
Bundle the module into an LGX package using the
nix bundlecommand.nix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3 .#lib- Use
#portablefor a self-contained package with no/nix/storereferences:nix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3#portable .#lib. - Use
#dualto produce both-devand portable variants in a single.lgxfile: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 oflogos-basecamp.
- Use
-
Check the current directory for the bundle output.
nix bundlecreates a symlink directory in the current directory named./logos-<module-name>-module-lib-lgx-<version>/, and the.lgxfile is inside it at./logos-<module-name>-module-lib-lgx-<version>/logos-<module-name>-module-lib.lgx.
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
.lgxpackage - Download and install a
.lgxfile from a registry
Install a locally built .lgx package
-
Create the
modules/directory and install the.lgxpackage.lgpm --modules-dir ./modules install --file result/logos-<module-name>-module-lib.lgx- Use
--dirinstead of--fileto 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.lgxinstead ofresult/....
- Use
-
Verify the installed module directory. The directory contains
manifest.json, the plugin binary (.soor.dylib), and avariantfile.
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.
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.
-
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>tipUse
lgpd listto browse all available packages. -
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
namefield, not the registry name. For example,lgpd download logos-chat-modulewrites./packages/chat_module.lgx.
- Use
-
Create the
modules/directory and install the downloaded package. Replace<downloaded-name>with the actual filename written bylgpd(for example,chat_module.lgx).lgpm --modules-dir ./modules install --file ./packages/<downloaded-name>.lgx- Use
--ui-plugins-dirinstead of--modules-dirwhen installing UI modules.
- Use
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.
-
Start the
logoscoredaemon with themodules/directory.logoscore -D -m ./modules -
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> -
Stop the daemon when finished.
logoscore stop
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.
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.
-
Build the development version of
logos-basecamp.nix build 'github:logos-co/logos-basecamp/tutorial-v1#app' --out-link ./logos-basecamp -
Launch
logos-basecamponce to create its data directory and preinstall bundled modules, then close it../logos-basecamp/bin/logos-basecamp- Look for the directory containing
modules/andplugins/subdirectories at~/Library/Application Support/Logos/LogosBasecamp/(macOS) or~/.config/Logos/LogosBasecamp/(Linux).
- Look for the directory containing
-
Set the
BASECAMP_DIRvariable to your platform's path.# macOSBASECAMP_DIR="$HOME/Library/Application Support/Logos/LogosBasecamp"# LinuxBASECAMP_DIR="$HOME/.config/Logos/LogosBasecamp" -
Install the module's dev LGX package into basecamp's modules directory. The package must contain a
-devvariant for your platform; build it withnix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3#dual .#libas 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
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 .#lgxproduces a single variant (for example,linux-amd64) suitable forlogoscorebut not for a dev build oflogos-basecamp.nix build .#lgx-portableproduces a single portable variant suitable for portable builds oflogos-basecamp.nix bundle --bundler github:logos-co/nix-bundle-lgx/tutorial-v3#dual .#libproduces both-devand portable variants in a single.lgxfile, which works with dev and portable builds oflogos-basecamp.
Registry packages downloaded with lgpd currently ship portable variants only.
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.