A shared Nix flake library that provides reusable functions for building Logos modules with minimal boilerplate.
Instead of duplicating ~600 lines of build configuration across every module, this library lets you define a module with a single metadata.json file and your source code.
| Without Builder | With Builder | Reduction |
|---|---|---|
| ~600 lines config | ~70 lines config | 88% |
| 5 config files | 2 config files | 60% |
my-module/
├── metadata.json # Single config file (~30 lines)
├── flake.nix # Minimal flake (~10 lines)
├── CMakeLists.txt # CMake config (~25 lines)
└── src/ # Source files
├── my_module_interface.h
├── my_module_plugin.h
└── my_module_plugin.cpp
{
"name": "my_module",
"version": "1.0.0",
"type": "core",
"category": "general",
"description": "My custom Logos module",
"main": "my_module_plugin",
"dependencies": ["waku_module"],
"nix": {
"packages": {
"build": ["protobuf"],
"runtime": ["zstd"]
},
"external_libraries": [],
"cmake": { "find_packages": [], "extra_sources": [] }
}
}{
inputs = {
logos-module-builder.url = "github:logos-co/logos-module-builder";
};
outputs = inputs@{ logos-module-builder, ... }:
logos-module-builder.lib.mkLogosModule {
src = ./.;
configFile = ./metadata.json;
flakeInputs = inputs;
};
}git init && git add -A # Nix needs files tracked by git
nix build # Build everything
nix build .#lib # Build just the library
nix build .#lgx # Build .lgx package
nix build .#lgx-portable # Build portable .lgx package
nix build .#install # Build, package, and install (dev)
nix build .#install-portable # Build, package, and install (portable)For type: "ui_qml" modules, logos-module-builder automatically wires up apps.default so nix run . launches the module in logos-standalone-app. No separate logos-standalone-app input is needed — it is bundled inside logos-module-builder.
With C++ backend (mkLogosQmlModule — validates "type": "ui_qml" + "view" field, compiles backend when "main" is set):
The C++ plugin runs in a separate ui-host process (process-isolated), and the QML view is loaded in the host application. Communication happens via Qt Remote Objects over a private socket. Use logos.module() from QML to access the backend replica.
{
inputs = {
logos-module-builder.url = "github:logos-co/logos-module-builder";
# Add backend dependencies as inputs:
# calc_module.url = "github:logos-co/logos-tutorial?dir=logos-calc-module";
};
outputs = inputs@{ logos-module-builder, ... }:
logos-module-builder.lib.mkLogosQmlModule {
src = ./.;
configFile = ./metadata.json;
flakeInputs = inputs;
};
}QML-only (mkLogosQmlModule — no C++ compilation, runs in-process):
{
inputs = {
logos-module-builder.url = "github:logos-co/logos-module-builder";
};
outputs = inputs@{ logos-module-builder, ... }:
logos-module-builder.lib.mkLogosQmlModule {
src = ./.;
configFile = ./metadata.json;
flakeInputs = inputs;
};
}Then nix run . launches the module in logos-standalone-app. Dependencies listed in metadata.json are automatically bundled from their LGX packages and loaded at runtime.
See templates/ui-qml-backend, templates/ui-qml, and lib/mkLogosQmlModule.nix.
For ui_qml modules, mkLogosQmlModule auto-detects .mjs test files in the tests/ directory and wires up integration testing using logos-qt-mcp's test framework. No extra flake inputs needed.
# Run tests hermetically (builds everything, launches headless, runs tests)
nix build .#integration-test -L
# Build the test framework for interactive use (one-time)
nix build .#test-framework -o result-mcp
# Run tests interactively (app must be running with inspector on :3768)
node tests/ui-tests.mjsTests use the QML inspector to interact with the running UI — finding elements, clicking buttons, verifying text. Example test file (tests/ui-tests.mjs):
import { resolve } from "node:path";
// CI sets LOGOS_QT_MCP automatically; for interactive use: nix build .#test-framework -o result-mcp
const root = process.env.LOGOS_QT_MCP || new URL("../result-mcp", import.meta.url).pathname;
const { test, run } = await import(resolve(root, "test-framework/framework.mjs"));
test("my_module: loads UI", async (app) => {
await app.waitFor(
async () => { await app.expectTexts(["Hello"]); },
{ timeout: 15000, interval: 500, description: "UI to load" }
);
});
run();See the logos-qt-mcp test framework for available assertions and helpers.
- ~90% reduction in boilerplate per module
- Single source of truth via
metadata.json— used by Nix build and embedded into Qt plugins at compile time - Automatic CMake configuration via
LogosModule.cmake - External library support (vendor pre-built or flake-input source)
- Cross-platform (macOS, Linux)
- Auto-resolved module dependencies from
flakeInputs - Built-in LGX packaging —
nix build .#lgxandnix build .#lgx-portableincluded automatically - Built-in install outputs —
nix build .#installandnix build .#install-portablebundle and install via lgpm in one step - Auto-detected UI integration tests — put
.mjstest files intests/and getnix build .#integration-testfor free
| Document | Description |
|---|---|
| Getting Started | Create your first module |
| Quick Reference | Cheat sheet for common tasks |
| Configuration Reference | Complete metadata.json specification |
| CMake Reference | LogosModule.cmake functions |
| Nix API Reference | mkLogosModule and other functions |
| External Libraries Guide | Wrap C/C++ libraries |
| Migration Guide | Migrate existing modules |
| Troubleshooting | Common issues and solutions |
| Example | Description |
|---|---|
| minimal-module | Basic module with no external dependencies |
| waku-module-migrated | Example migration showing 91% config reduction |
Use nix flake init with our templates:
# Minimal core module (backend/logic, no UI)
nix flake init -t github:logos-co/logos-module-builder
# C++ UI module — view module with C++ backend + QML view (process-isolated)
nix flake init -t github:logos-co/logos-module-builder#ui-qml-backend
# QML-only UI module (no C++ backend, in-process)
nix flake init -t github:logos-co/logos-module-builder#ui-qml
# Module with external library
nix flake init -t github:logos-co/logos-module-builder#with-external-libFor AI assistants (Claude, Cursor, etc.), we provide skill files:
| Skill | Description |
|---|---|
| create-logos-module | Step-by-step guide to create a new core module |
| create-ui-module | Create a ui_qml module with C++ backend + QML view (process-isolated) |
| create-qml-module | Create a ui_qml module (QML-only, in-process) |
| update-logos-module | Guide to update/modify existing modules |
The builder has a pure Nix evaluation test suite (no compilation required). Tests cover metadata parsing, utility functions, external library helpers, and template validity.
# Run tests via nix
nix build '.#checks.x86_64-linux.default'
# Or use nix flake check (runs all checks for the current system)
nix flake check
# From the logos-workspace
ws test logos-module-builderTests are in tests/ and are organized into:
| File | What it tests |
|---|---|
test-parse-metadata.nix |
metadata.json parsing, defaults, required fields, type coercion |
test-common.nix |
Name formats, platform helpers, recursive merge, dependency collection |
test-external-lib.nix |
External library detection, name extraction, vendor build scripts |
test-templates.nix |
All 4 templates parse correctly, expected files exist, field consistency |
logos-module-builder/
├── lib/ # Nix library functions
│ ├── mkLogosModule.nix # Builder for core + legacy UI widget modules
│ ├── mkLogosQmlModule.nix # Builder for ui_qml modules (QML view + optional C++ backend)
│ ├── buildCppPlugin.nix # Shared C++ plugin build pipeline
│ ├── mkStandaloneApp.nix # apps.default for logos-standalone-app
│ ├── mkModuleLib.nix # Library builder
│ ├── mkModuleInclude.nix # Header generator
│ ├── mkExternalLib.nix # External library handler
│ └── parseMetadata.nix # metadata.json parser
├── cmake/
│ └── LogosModule.cmake # Reusable CMake module
├── templates/ # Module templates
├── examples/ # Working examples
├── docs/ # Documentation
└── skills/ # AI assistant skills
MIT