Modules
Modules are the core of NixKraken. Together, they form a declarative system that generates GitKraken configuration.
This document explains NixKraken's module organization, how modules compose into the final configuration, and module-specific behaviors. It is intended for contributors extending NixKraken or integrating custom behavior.
WARNING
Contributors should be familiar with the NixOS module system.
Terminology used in this document:
- options: NixKraken module options (input)
- settings: GitKraken configuration options (output)
- module / submodule: NixKraken modules; "submodule" as defined by the NixOS module system
Structure and Organization
Each subdirectory within the modules directory implements a focused, single-purpose module for a specific aspect of GitKraken configuration.
Directory structure of the module system:
.
├── modules
│ ├── datetime
│ ├── git
│ ├── gpg
│ ├── graph
│ ├── notifications
│ ├── profiles
│ ├── ssh
│ ├── tools
│ ├── ui
│ ├── user
│ └── root-options.nix
└── module.nixThe primary entry point is module.nix. It is responsible for:
- importing all submodules from within the
modulesdirectory - merging composed settings into the final GitKraken configuration
- defining Home Manager configuration to install and configure GitKraken, leveraging the gk-configure and gk-theme packages
The root-options.nix file defines top-level options.
This modular approach improves maintainability and clarifies how configuration aspects interact.
Option Scoping
Most GitKraken settings are profile-specific; some are global. To support both scopes, a module may define:
app-options.nix: global (application-wide) optionsprofile-options.nix: profile-specific options
INFO
The profiles module is an intentional exception to this pattern. It aggregates per-profile options across modules and constructs additional profile instances.
This design lets you define global and default-profile behavior in top-level modules (e.g., nixkraken.gpg, nixkraken.user, etc.) and reuse per-profile options for additional profiles under nixkraken.profiles.*.
Example structure of the git module:
modules/git
├── app-options.nix
├── default.nix
└── profile-options.nixapp-options.nixdeclares global optionsprofile-options.nixdeclares per-profile optionsdefault.nixdefines:- a submodule, including both global and per-profile options
- GitKraken settings produced for composition
- Home Manager configuration related to this module
This structure is especially useful with the profiles module, which aggregates all per-profile options into a meta-module to define additional profiles. The following diagram illustrate this:
INFO
Some modules (e.g., the ui module) split options across multiple files for maintainability due to the large number of options.
They still adhere to the global vs. profile option boundaries via app-options.nix and profile-options.nix.
This may evolve into nested submodules.
Settings Composition
To centralize application of GitKraken configuration, each submodule writes its evaluated global settings as an attribute set to an internal option _submoduleSettings.<moduleName>.
The top-level module merges all entries in _submoduleSettings to form the final GitKraken configuration and serializes it to JSON before passing it to the gk-configure package, which writes to the location expected by GitKraken (i.e., ~/.gitkraken/config).
The following diagram illustrates the flow:
Profiles Module
As noted earlier, most GitKraken settings are per-profile. The profiles module manages both the default profile and additional profiles.
The default profile is identified via an internal option isDefault. It uses GitKraken's fixed identifier and sources its name and avatar from the root options defaultProfile.name and defaultProfile.icon.
About profile identifiers
GitKraken uses UUIDs stripped of dashes to identify profiles. The default profile uses a fixed identifier (d6e5a8ca26e14325a4275fc33b17e16f), while additional profiles use randomly generated identifiers.
Because Nix does not natively generate UUIDs, NixKraken derives stable, UUID-shaped identifiers via a utility function that:
- takes a seed string as input
- hashes the seed using the SHA-512 algorithm
- slices the hash into five segments matching UUID lengths (8, 4, 4, 4, 12)
- concatenates the segments with dashes
This produces deterministic identifiers for profiles with unique names. Although we could hash the profile name and truncate to 32 characters, we use this function because it is also needed elsewhere.
A safeguard prevents duplicate profile names, ensuring uniqueness of identifiers.
Because NixKraken supports multiple profiles, a builder function emits GitKraken settings for each profile.
As with global settings, per-profile settings are serialized to JSON and passed, with the profile identifier, to the gk-configure package, which writes them to ~/.gitkraken/profiles/<id>/profile.
The following diagram illustrates the flow:
Inheritance
Additional profiles inherit recursively from the default profile via lib.recursiveUpdate function. Per-profile options override inherited defaults where keys overlap.
Safeguards
To improve the user experience and catch issues early, NixKraken modules make extensive use of warnings and assertions.
Examples of safeguards:
- enabling commit/tag signing without a signing key
- enabling notification topics while notifications are disabled
- mutually exclusive options such as package and version
Use an assertion when a configuration would be invalid or non-deterministic. Use a warning for configurations that are valid but likely unintended.
Messages should be informative and cite the relevant options.
Example (good):
SSH keys (
ssh.privateKey,ssh.publicKey) cannot be set when local SSH agent is used (ssh.useLocalAgent)
As opposed to:
Cannot set
ssh.privateKey