Contributing to the catalog#

A catalog contribution makes a module available under an lmx: selector in clients that bundle the updated catalog. For a personal or team module, start with Writing a module and Reusable modules. You do not need to change this repository to use your own NixOS configuration.

This page covers the catalog's file layout and local checks. The release process is documented on the documentation site.

Add a small module first#

The smallest catalog entry has two files. For example, a new jq-tools entry would look like this:

modules/
└── jq-tools/
    ├── default.nix
    └── module.toml

Create modules/jq-tools/default.nix:

{ pkgs, ... }:
{
  environment.systemPackages = [ pkgs.jq ];
}

Create modules/jq-tools/module.toml:

description = "jq for inspecting and transforming JSON."

Here, pkgs.jq uses the consuming VM's Nixpkgs. This example does not introduce a separate package pin or a version selector.

The directory name becomes the catalog name. Once bundled into a client, the entry is selected as lmx:jq-tools:

[nixos]
modules = ["lmx:jq-tools"]

Note

Editing a catalog checkout does not change the catalog embedded in an installed client. To try the module before it is bundled, import its directory as a third-party module using the workflow in Reusable modules.

Follow the metadata contract#

The catalog validator checks the following rules:

Item

Rule

Directory name

At most 63 characters; begins with a lowercase letter; lowercase letters, digits and single separating hyphens

default.nix

Required regular file: the module's default entry point

module.toml

Required regular file

description

Required non-empty string; whitespace alone is invalid

versions

Optional list of unique strings; omitted means no version selectors

Version selector

At most 63 characters; digits separated by dots, such as 24 or 3.14

default

Must name a declared selector when versions is non-empty; otherwise omitted or empty

Version entry point

Every selector requires a regular versions/<selector>.nix file

Extra metadata fields

Rejected; the allowed keys are description, versions and default

Valid names include jq-tools, python and nodejs. Names such as MyModule, my_module, 1tool and my--tool are invalid. Selectors such as 3.14 are valid; v3.14, latest and 3.14-beta are invalid.

Every immediate entry under modules/ must be a module directory. Keep shared catalog documentation outside that directory.

Source: catalog validator.

Add version selectors when needed#

The existing versioned modules share this layout:

modules/go/
├── default.nix
├── module.toml
├── module.nix
├── packages.nix
├── releases.nix
└── versions/
    ├── 1.24.nix
    ├── 1.25.nix
    ├── 1.26.nix
    └── 1.27.nix

File

Responsibility

module.toml

Lists supported selectors and the default

default.nix

Imports the selector named by metadata

versions/<selector>.nix

Passes a selector into the shared module

module.nix

Configures NixOS and installs the selected tools

packages.nix

Loads the selected package source and checks its version

releases.nix

Records source revisions, hashes and package choices

The last three filenames are a repository convention, not additional metadata requirements. The validator requires the entry points and metadata described above.

For example, Go declares:

description = "Go compiler, gopls language server, and Delve debugger."

versions = ["1.24", "1.25", "1.26", "1.27"]
default = "1.27"

Its default.nix reads that choice:

let
  metadata = builtins.fromTOML (builtins.readFile ./module.toml);
in
import (./versions + "/${metadata.default}.nix")

Its versions/1.27.nix supplies the selector:

import ../module.nix "1.27"

The shared module.nix accepts that string first, then the normal NixOS module arguments. This keeps the NixOS configuration in one place.

Pin a package source#

Versioned entries fetch Nixpkgs by a specific Git revision and content hash. packages.nix selects a package from that source and asserts its expected version. Read the existing Go package loader and release map as a complete example.

When adding a selector to that pattern:

  1. Record the Nixpkgs revision and its correct source hash in releases.nix.

  2. Add the selector's package attribute and exact expected package version.

  3. Add versions/<selector>.nix pointing to the shared module.

  4. Add the selector to module.toml; change default only if that is part of the update.

  5. Update the module's examples and the catalog reference.

  6. Run the checks below, then test the behavior in a VM.

Keep version assertions and hashes consistent with the actual source. Changing only a displayed version string does not select a different package.

For modules that support several toolchains together, review the unqualified command priority and versioned wrappers as well. For a service such as Docker, document that only one selected version is supported.

Run the repository checks#

From the repository root, use the existing Taskfile entry points:

task --yes ci/fmt
task --yes ci/lint
task --yes ci/test

Command

Checks

ci/fmt

Nix formatting through nixfmt

ci/lint

Nix code through statix and deadnix

ci/test

Catalog structure and NixOS configuration evaluation

The Taskfile imports the shared Nix tooling at the pinned v0.0.4 revision. It is the entry point for the repository's validation commands.

ci/test evaluates both aarch64-linux and x86_64-linux:

Case

Selected modules

Each default

One module's default.nix

All defaults

Every module's default.nix together

Each version

One declared version entry point

Version with catalog

All defaults, with one module replaced by a declared version

Important

These checks evaluate NixOS derivations. They do not build the packages, boot a VM or run the installed tools. They also do not try every combination of several versions of the same module.

For runtime validation, use the module in a test VM. Check the commands or service it adds. If it supports several versions together, select that combination and check both the ordinary and versioned commands.

Sources: Taskfile, evaluation matrix and test VM configuration.