Skip to main content

Build and run a Logos core module

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

Version

This document is accurate for Testnet v0.2.1.

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.

Prerequisites
  • A supported OS:

    • Linux x86_64 or aarch64
    • macOS arm64 or x86_64
  • At least 10 GB of disk space

  • Git

  • logosctl installed.

    • Install it by running curl -fsSL https://raw.githubusercontent.com/logos-co/logos-docs/main/resources/scripts/install-logosctl.sh | sudo sh
  • Nix with flakes enabled.

    • Install from nixos.org, then enable flakes:
    mkdir -p ~/.config/nix
    echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
  • 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 logosctl 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 logosctl 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 -dev variant (for example, linux-amd64-dev) that references /nix/store paths, and .#lgx-portable produces a single self-contained portable variant (for example, linux-amd64). Released builds of logosctl—including the one the install-logosctl.sh helper script downloads—only install portable variants, while dev builds of the tool and of logos-basecamp only install -dev variants. If you need both variants in a single file, 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 logosctl or logos-basecamp, install the LGX package. logosctl package install unpacks it into the current session's modules/ directory (~/.logosctl/modules/ unless --config-dir/LOGOSCTL_CONFIG_DIR says otherwise)—the same session logosctl daemon start reads from later, so no extra flag is needed to make an installed module loadable.

There are two ways to install a module:

  • Install a locally built .lgx package
  • Install a package by name from a registry

Install a locally built .lgx package

  1. Start the logosctl daemon in detached mode.

    logosctl daemon start --detach
  2. Install the .lgx package:

    logosctl package install --file result/logos-<module-name>-module-lib.lgx
    • Use --dir instead of --file to install all LGX packages in a directory at once: logosctl package 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/....
  3. Verify the installed module:

    logosctl package ls

    The session's modules/ directory now contains a subdirectory with manifest.json, the plugin binary (.so or .dylib), and a variant file.

Install a package by name from the catalogue

The Logos module catalogue is hosted on GitHub Releases in the logos-modules repository. logosctl package install fetches a named package straight from the catalogue and installs it in one step.

warning

Catalogue 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 logosctl and into portable builds of logos-basecamp.

  1. Start the logosctl daemon in detached mode.

    logosctl daemon start --detach
  2. Refresh the catalogue, then search it for the module you want to install. Replace <query> with what you're looking for (for example, chat).

    logosctl catalog refresh
    logosctl package search <query>
    tip

    Run logosctl package search with no query to browse all available packages.

  3. Install it by the name from the search results (for example, chat_module). logosctl resolves the name against the catalogue, downloads the package, and unpacks it into the session's modules/ (or plugins/, for a UI module—the destination follows the type declared in the package's metadata) in one step.

    logosctl package install <module-name> --yes
    • Add --version to pin a specific release instead of the newest one, and --root-hash to pin the exact published package identity.

Step 7: Run the module

There are two Logos runtimes, logosctl 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 logosctl

The logosctl 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. Load the module and call a method. Replace <method> and <args> with the method name and arguments you want to call.

    logosctl module load <module-name>
    logosctl call <module-name> <method> <args>
  2. Stop the daemon when finished.

    logosctl daemon 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.

logosctl package install always unpacks into its own session, not an arbitrary directory, so use the standalone lgpm package manager instead to install straight into basecamp's data directory. Build it on demand:

nix build 'github:logos-co/logos-package-manager/0.2.1#cli' --out-link ./pm

./pm/bin/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 logosctl daemon instance supports only one instance of a module, so all dependent apps share that state. To run multiple independent instances from the CLI, start separate daemons under separate sessions—logosctl daemon start --config-dir <dir> for each—so each gets its own installed modules and state. From logos-basecamp, start separate instances with their own user directories; each one runs its own embedded runtime 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 logosctl

Confirm the module is in a subdirectory of the session's modules/ directory (for example, modules/my_module/) and that the subdirectory contains a manifest.json with a main object matching your OS and architecture. Run logosctl package ls to see what the current session actually has installed.

logosctl package install fails

Verify the session's modules/ directory (under --config-dir, default ~/.logosctl) 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 catalogue, check your internet connection and that you've run logosctl catalog refresh recently. To pin a specific version instead of the newest one, pass --version (and optionally --root-hash) to logosctl package install.

If installing into logos-basecamp's own directory with the standalone lgpm, verify that target directory exists and is writable instead.

LGX variant mismatch

If logosctl package 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 -dev variant (for example, linux-amd64-dev) suitable for dev builds of logosctl/logos-basecamp, but rejected by released (portable) builds of logosctl. Use nix build .#lgx-portable for the portable variant those released tools install.
  • 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 installed via logosctl's catalogue currently ship portable variants only.

info

logosctl 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.