Merge declarative Nix patches into mutable config files at activation time.
# flake.nix
inputs.patchix.url = "github:y0usaf/patchix";
inputs.patchix.inputs.nixpkgs.follows = "nixpkgs";# configuration.nix
{ inputs, ... }: {
imports = [ inputs.patchix.nixosModules.default ];
patchix.enable = true;
patchix.users.alice.patches = {
".config/starship.toml" = {
format = "toml";
value.character.success_symbol = "[>](bold green)";
};
# clobber = false: only write keys not already present on disk
".config/Code/User/settings.json" = {
format = "json";
clobber = false;
value = {
"editor.fontSize" = 14;
"editor.tabSize" = 2;
};
};
# __global__ holds sectionless keys.
".config/foot/config.ini" = {
format = "ini";
value = {
__global__ = { font = "monospace:size=12"; };
colors-dark = { alpha = 0.85; };
};
};
".wine/user.reg" = {
format = "reg";
value = {
"HKEY_CURRENT_USER\\Software\\Wine\\Direct3D" = {
"UseGLSL" = { type = "sz"; value = "enabled"; };
"VideoMemorySize" = { type = "dword"; value = 512; };
};
};
};
};
}Each user gets a systemd oneshot that runs patchix merge per file on
activation.
patchix.users.<name>.patches.<path>:
| Option | Default | |
|---|---|---|
format |
(required) | "json" "toml" "yaml" "ini" "reg" |
value |
{} |
Patch content as Nix attrset |
clobber |
true |
true: overwrite existing values. false: only fill in missing keys |
defaultArrayStrategy |
"replace" |
"replace" "append" "prepend" "union" |
arrayStrategies |
{} |
Per-path overrides (dot-separated) |
enable |
true |
Toggle this patch |
Both modes recurse into nested objects. Setting a key to null deletes it (RFC
7396). Under --no-clobber, null patch values are ignored — they do not delete
the key.
| Strategy | [a, b] + [c] |
|---|---|
replace (default) |
[c] |
append |
[a, b, c] |
prepend |
[c, a, b] |
union |
[a, b, c] (deduplicated) |
Per-path: arrayStrategies."editor.formatters" = "append";
Auto-detected from file extension. Supported: json, toml, yaml/yml,
ini/conf/cfg, reg.
TOML datetimes round-trip as strings. INI sections map to top-level keys;
sectionless (global) keys are grouped under __global__. YAML uses implicit
typing: unquoted yes/no/true/false become booleans and bare numbers
become numeric — quote values to preserve them as strings.
Registry patches use typed values because .reg entries are not plain JSON
scalars:
{
"HKEY_CURRENT_USER\\Software\\Wine\\Direct3D" = {
"UseGLSL" = { type = "sz"; value = "enabled"; };
"VideoMemorySize" = { type = "dword"; value = 512; };
"BinaryBlob" = { type = "hex"; value = "01,02,ff"; };
"(default)" = { type = "sz"; value = "default text"; };
"DeprecatedValue" = null;
};
"HKEY_CURRENT_USER\\Software\\Wine\\ObsoleteKey" = null;
}patchix merge -e config.json -p patch.json
patchix merge -e config.json -p patch.json --no-clobber
patchix merge -e config.json -p patch.json --array-strategy 'plugins=append'
patchix merge -e config.toml -p patch.toml -o merged.toml
patchix merge -e config.yml -p patch.yml --default-array append
patchix merge -e config -p patch.json --format json
patchix merge -e user.reg -p patch.json --format reg --patch-format jsonAGPL-3.0-or-later