From 59d1028e19d9ac5f43122d08119e90d77e56a7da Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 12:41:53 +1000 Subject: [PATCH 1/9] add CHANGELOG.md --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1c7a9a3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog + +## v0.2.0-alpha +Initial "stable" release. Cerulean is currently usable and supports: +1. local & remote deployment configuration +2. nixos/homemanager module-level support for any number of nixpkg branches +3. use of the [nix-systems standard](https://github.com/nix-systems/nix-systems), the introduction of the `snow/flake` standard, and the introduction of the `nixpkgs.nix` standard module. +4. hierarchical groups for NixOS hosts via `snow.nix` + +This is still a alpha-build of Cerulean. Everything will break in the future as I change the internals a bunch. I'll aim to write documentation in future cause currently there's no guide. + +## v0.2.1-alpha +Minor patches +- cerulean no longer has a `inputs.nixpkgs-unstable` (the `nixpkgs.nix` is the new alternative) +- `home-manager.nixosModules.default` and `microvm.nixosModules.microvm` are added as default modules +- fixed `groups.all` not being added to nodes with `groups = []` + +## v0.2.2-alpha +Minor patches +- fixed `nexus.groups.all` not added to empty `nexus.nodes.*.groups` declarations +- fixed bad propagation of inputs +- forced system architecture to be specified per node +- cerulean no longer depends on `nixpkgs`, `base` package set should be set instead +- rename `extraModules` -> `modules` +- rename `specialArgs` -> `args` From d85a6b963f85ae81904c94789167ae337891c6d2 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 17:00:09 +1000 Subject: [PATCH 2/9] restruct args into specialArgs._cerulean --- TODO.md | 4 +++ cerulean/nexus/nexus.nix | 55 +++++++++++++++++++++------------ cerulean/nexus/nodes.nix | 4 +-- cerulean/nixos/default.nix | 6 ++-- cerulean/nixos/home-manager.nix | 9 ++---- 5 files changed, 46 insertions(+), 32 deletions(-) diff --git a/TODO.md b/TODO.md index a12b8c3..5536d7f 100755 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,7 @@ +- [ ] base should automatically be set as the default (dont do anything with the default) +- [ ] try to remove common foot guns, ie abort if the user provides the home-manager or microvm nixosModules + since cerulean ALREADY provides these + - [ ] deploy port should default to the first port given to `services.openssh` - [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 22424ba..b30647d 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -20,6 +20,7 @@ }: let inherit (builtins) + all attrNames concatLists concatStringsSep @@ -184,33 +185,47 @@ in { outputs = rec { nixosConfigurations = mapNodes nexus ( { + base, lib, nodeName, node, ... }: let - nixosDecl = lib.nixosSystem rec { - system = node.system; - specialArgs = - nexus.args - // node.args - // { - inherit root specialArgs; - inherit (node) system; - _deploy-rs = inputs.deploy-rs; + nixosDecl = let + userArgs = nexus.args // node.args; + ceruleanArgs = { + inherit root base; + inherit (node) system; + _cerulean = { + inherit inputs userArgs; + args = ceruleanArgs; }; - modules = - [ - self.nixosModules.default - (findImport (root + "/hosts/${nodeName}")) + }; + specialArgs = assert (userArgs + |> attrNames + |> all (argName: + ! ceruleanArgs ? argName + || abort '' + `specialArgs` are like super important to Cerulean my love... filter (x: pathExists (root + "/homes/${x}")) |> (x: lib.genAttrs x (y: import (root + "/homes/${y}"))); - extraSpecialArgs = {inherit root system;} // (specialArgs.inputs or {}); + extraSpecialArgs = _cerulean.args; sharedModules = [ # user configuration (import (root + "/nixpkgs.nix")) # options declarations (import ./nixpkgs.nix (args // {contextName = "homes";})) ]; - - # disable home-manager trying anything fancy - # we control the pkgs now!! - # useGlobalPkgs = true; }; }; } From 0c5387fd61b55266831f488d3e9c5f329c8508f6 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 17:27:31 +1000 Subject: [PATCH 3/9] nixpkgs.channels disregards contextName --- cerulean/nixos/nixpkgs.nix | 47 +++++++++++++++----------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix index 65db5a5..41ee0ab 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -26,46 +26,38 @@ cfg = config.nixpkgs.channels; in { options.nixpkgs.channels = lib.mkOption { - type = lib.types.attrsOf (lib.types.attrs); + type = lib.types.attrs; default = {}; - description = "Declare package repositories per module context (nixos, home-manager, etc)"; + description = "Declare package repositories"; example = { - "homes" = { - "pkgs" = { - source = "inputs.nixpkgs"; - system = "x86-64-linux"; - config = { - allowUnfree = true; - allowBroken = false; - }; + "pkgs" = { + source = "inputs.nixpkgs"; + system = "x86-64-linux"; + config = { + allowUnfree = true; + allowBroken = false; }; - "upkgs" = { - source = "inputs.nixpkgs-unstable"; - system = "x86-64-linux"; - config = { - allowUnfree = true; - allowBroken = false; - }; + }; + "upkgs" = { + source = "inputs.nixpkgs-unstable"; + system = "x86-64-linux"; + config = { + allowUnfree = false; + allowBroken = true; }; }; }; }; config = let - # TODO: use lib.types.submodule to restrict what options - # TODO: can be given to `nixpkgs.channels.${moduleName}.${name}` - decl = - cfg.${contextName} or cfg.default; - repos = - decl + cfg |> mapAttrs ( name: args: lib.mkForce ( assert args ? source || abort '' - ${toString ./.} - `nixpkgs.channels.${contextName}.${name}` missing required attribute "source" + `nixpkgs.channels.${name}` missing required attribute "source" ''; ((removeAttrs args ["source"]) // {inherit system;}) @@ -78,8 +70,9 @@ in { _module.args = repos; nixpkgs = let + # XXX: TODO: would it work to use `base` instead of having default? defaultPkgs = - decl.default or (throw '' + cfg.default or (throw '' Your `nixpkgs.nix` file does not declare a default package source. Ensure you set `nixpkgs.channels.*.default = ...;` ''); @@ -91,9 +84,7 @@ in { } else if contextName == "homes" then { - # XXX: XXX: XXX: OH OH OH OMG, its because aurora never defines pkgs config = lib.mkOverride 200 (defaultPkgs.config or {}); - # XXX: WARNING: TODO: modify options so overlays must always be given as the correct type overlays = lib.mkOverride 200 (defaultPkgs.overlays or []); } else {}; From 870bbb1f3738736ea248c171685bffdb242e294a Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Feb 2026 17:24:28 +1000 Subject: [PATCH 4/9] i cried --- cerulean/nexus/nexus.nix | 6 +++--- cerulean/nixos/home-manager.nix | 30 +++++++++++++++--------------- cerulean/nixos/nixpkgs.nix | 32 +++++++++++++++++--------------- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index b30647d..f3cf38a 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -197,8 +197,8 @@ in { inherit root base; inherit (node) system; _cerulean = { - inherit inputs userArgs; - args = ceruleanArgs; + inherit inputs userArgs ceruleanArgs; + specialArgs = userArgs // ceruleanArgs; }; }; specialArgs = assert (userArgs @@ -209,7 +209,7 @@ in { `specialArgs` are like super important to Cerulean my love... attrNames - |> filter (x: pathExists (root + "/homes/${x}")) - |> (x: lib.genAttrs x (y: import (root + "/homes/${y}"))); + home-manager = { + users = + config.users.users + |> attrNames + |> filter (x: pathExists (root + "/homes/${x}")) + |> (x: + lib.genAttrs x (y: + import (root + "/homes/${y}"))); - extraSpecialArgs = _cerulean.args; - sharedModules = [ - # user configuration - (import (root + "/nixpkgs.nix")) - # options declarations - (import ./nixpkgs.nix (args // {contextName = "homes";})) - ]; - }; + extraSpecialArgs = _cerulean.specialArgs; + sharedModules = [ + # user configuration + (import (root + "/nixpkgs.nix")) + # options declarations + (import ./nixpkgs.nix (args // {contextName = "homes";})) + ]; }; } diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix index 41ee0ab..b43dbbc 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -52,6 +52,7 @@ in { config = let repos = cfg + |> (xs: removeAttrs xs ["default"]) |> mapAttrs ( name: args: lib.mkForce ( @@ -59,29 +60,30 @@ in { || abort '' `nixpkgs.channels.${name}` missing required attribute "source" ''; - ((removeAttrs args ["source"]) - // {inherit system;}) - |> import args.source + import args.source ({inherit system;} // (removeAttrs args ["source"])) ) ); + + # XXX: TODO: would it work to use `base` instead of having default? + defaultPkgs = + cfg.default or (throw '' + Your `nixpkgs.nix` file does not declare a default package source. + Ensure you set `nixpkgs.channels.*.default = ...;` + ''); in { # NOTE: _module.args is a special option that allows us to # NOTE: set extend specialArgs from inside the modules. _module.args = repos; - nixpkgs = let - # XXX: TODO: would it work to use `base` instead of having default? - defaultPkgs = - cfg.default or (throw '' - Your `nixpkgs.nix` file does not declare a default package source. - Ensure you set `nixpkgs.channels.*.default = ...;` - ''); - in + nixpkgs = if contextName == "hosts" - then { - flake.source = lib.mkOverride 200 defaultPkgs.source; - config = lib.mkOverride 200 defaultPkgs.config; - } + then + # DEBUG: defaultPkgs + { + flake.source = lib.mkOverride 200 (defaultPkgs.source or null); + overlays = lib.mkOverride 200 (defaultPkgs.overlays or {}); + config = lib.mkOverride 200 (defaultPkgs.config or {}); + } else if contextName == "homes" then { config = lib.mkOverride 200 (defaultPkgs.config or {}); From f34c2fba39acd2544635e2b847214ce3aa2c1a27 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Feb 2026 20:12:58 +1000 Subject: [PATCH 5/9] rename base -> decl (easily confusable) --- cerulean/nexus/nexus.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index f3cf38a..ea8997e 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -93,13 +93,13 @@ Cerulean Nexus config must be provided as an attribute set, got "${typeOf nexus}" instead! Ensure the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`. ''; let - base = nt.projectOnto templateNexus nexus; + decl = nt.projectOnto templateNexus nexus; in # XXX: TODO: create a different version of nt.projectOnto that can actually # XXX: TODO: handle applying a transformation to the result of each datapoint - base + decl // { - groups = parseGroupDecl base.groups; + groups = parseGroupDecl decl.groups; }; parseDecl = outputsBuilder: let From 232837d34adcab3dc70e08fc492c158d83b63d20 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Feb 2026 20:13:05 +1000 Subject: [PATCH 6/9] update TODO --- TODO.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 5536d7f..1dde662 100755 --- a/TODO.md +++ b/TODO.md @@ -27,8 +27,8 @@ - [ ] allow multiple privesc methods, the standard is pam_ssh_agent_auth ## Low Priority -- [ ] rename extraModules to modules? -- [ ] rename specialArgs to args? +- [X] rename extraModules to modules? +- [X] rename specialArgs to args? - [ ] make an extension to the nix module system (different to mix) that allows transformations (ie a stop post config, ie outputs, which @@ -44,6 +44,8 @@ - [ ] rewrite the ceru cli in rust - [ ] make `ceru` do local and remote deployments +- [ ] support `legacyImports` + ```nix # REF: foxora vms = { From 18e1a2c8909b84cc2199533f3d5b76ddcefc6b0f Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Feb 2026 20:13:36 +1000 Subject: [PATCH 7/9] don't add pkgs to _module.args --- cerulean/nixos/nixpkgs.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix index b43dbbc..5475e0b 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -73,7 +73,8 @@ in { in { # NOTE: _module.args is a special option that allows us to # NOTE: set extend specialArgs from inside the modules. - _module.args = repos; + # WARNING: pkgs is a reserved specialArg + _module.args = removeAttrs repos ["pkgs"]; nixpkgs = if contextName == "hosts" From d5211287bd3cb96078f2053488d67d557848a8f2 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Feb 2026 20:13:43 +1000 Subject: [PATCH 8/9] TEMP: use base --- cerulean/nixos/nixpkgs.nix | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix index 5475e0b..0376f47 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. { + base, lib, system, config, @@ -78,13 +79,11 @@ in { nixpkgs = if contextName == "hosts" - then - # DEBUG: defaultPkgs - { - flake.source = lib.mkOverride 200 (defaultPkgs.source or null); - overlays = lib.mkOverride 200 (defaultPkgs.overlays or {}); - config = lib.mkOverride 200 (defaultPkgs.config or {}); - } + then { + flake.source = lib.mkOverride 200 base; # DEBUG: temp while getting base to work + overlays = lib.mkOverride 200 (defaultPkgs.overlays or {}); + config = lib.mkOverride 200 (defaultPkgs.config or {}); + } else if contextName == "homes" then { config = lib.mkOverride 200 (defaultPkgs.config or {}); From 087f679e67005b4c8fda93f139eb05c78c644130 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Feb 2026 23:56:05 +1000 Subject: [PATCH 9/9] add modifiable homeManager --- cerulean/nexus/nexus.nix | 10 ++++- cerulean/nexus/nodes.nix | 2 + cerulean/nixos/default.nix | 20 ++++++---- flake.lock | 81 +++++++++++++++++++++----------------- flake.nix | 5 --- 5 files changed, 66 insertions(+), 52 deletions(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index ea8997e..5d0ca02 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -54,6 +54,7 @@ base = null; modules = []; args = Terminal {}; + homeManager = null; groups = Terminal {}; nodes = Terminal {}; @@ -192,12 +193,17 @@ in { ... }: let nixosDecl = let + homeManager = + if node.homeManager != null + then node.homeManager + else nexus.homeManager; + userArgs = nexus.args // node.args; ceruleanArgs = { inherit root base; inherit (node) system; _cerulean = { - inherit inputs userArgs ceruleanArgs; + inherit inputs userArgs ceruleanArgs homeManager; specialArgs = userArgs // ceruleanArgs; }; }; @@ -219,9 +225,9 @@ in { self.nixosModules.default (findImport (root + "/hosts/${nodeName}")) - inputs.home-manager.nixosModules.default # inputs.microvm.nixosModules.microvm ] + ++ (homeManager.nixosModules.default or []) ++ (getGroupModules root nodeName node) ++ node.modules ++ nexus.modules; diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index 25b4bb3..13ced2f 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -38,6 +38,8 @@ in rec { modules = []; args = Terminal {}; + homeManager = null; + base = null; deploy = { diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index bd8d03d..ea7c359 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -17,14 +17,18 @@ _cerulean, ... } @ args: { - imports = [ - # user configuration - (import (root + "/nixpkgs.nix")) - # options declarations - (import ./nixpkgs.nix (args // {contextName = "hosts";})) - - ./home-manager.nix - ]; + imports = + [ + # user configuration + (import (root + "/nixpkgs.nix")) + # options declarations + (import ./nixpkgs.nix (args // {contextName = "hosts";})) + ] + ++ ( + if _cerulean.homeManager != null + then [./home-manager.nix] + else [] + ); environment.systemPackages = with _cerulean.inputs; [ deploy-rs.packages.${system}.default diff --git a/flake.lock b/flake.lock index 0dd06b1..fc8e402 100644 --- a/flake.lock +++ b/flake.lock @@ -3,7 +3,9 @@ "deploy-rs": { "inputs": { "flake-compat": "flake-compat", - "nixpkgs": "nixpkgs", + "nixpkgs": [ + "nixpkgs" + ], "utils": "utils" }, "locked": { @@ -58,6 +60,27 @@ "type": "github" } }, + "microvm": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "spectrum": "spectrum" + }, + "locked": { + "lastModified": 1771365290, + "narHash": "sha256-1XJOslVyF7yzf6yd/yl1VjGLywsbtwmQh3X1LuJcLI4=", + "owner": "microvm-nix", + "repo": "microvm.nix", + "rev": "789c90b164b55b4379e7a94af8b9c01489024c18", + "type": "github" + }, + "original": { + "owner": "microvm-nix", + "repo": "microvm.nix", + "type": "github" + } + }, "nix-github-actions": { "inputs": { "nixpkgs": [ @@ -105,38 +128,6 @@ } }, "nixpkgs": { - "locked": { - "lastModified": 1743014863, - "narHash": "sha256-jAIUqsiN2r3hCuHji80U7NNEafpIMBXiwKlSrjWMlpg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "bd3bac8bfb542dbde7ffffb6987a1a1f9d41699f", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-unstable": { - "locked": { - "lastModified": 1768305791, - "narHash": "sha256-AIdl6WAn9aymeaH/NvBj0H9qM+XuAuYbGMZaP0zcXAQ=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "1412caf7bf9e660f2f962917c14b1ea1c3bc695e", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { "locked": { "lastModified": 1768323494, "narHash": "sha256-yBXJLE6WCtrGo7LKiB6NOt6nisBEEkguC/lq/rP3zRQ=", @@ -152,7 +143,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_2": { "locked": { "lastModified": 1767313136, "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=", @@ -171,7 +162,7 @@ "nt": { "inputs": { "nix-unit": "nix-unit", - "nixpkgs": "nixpkgs_3", + "nixpkgs": "nixpkgs_2", "systems": "systems_2" }, "locked": { @@ -191,12 +182,28 @@ "root": { "inputs": { "deploy-rs": "deploy-rs", - "nixpkgs": "nixpkgs_2", - "nixpkgs-unstable": "nixpkgs-unstable", + "microvm": "microvm", + "nixpkgs": "nixpkgs", "nt": "nt", "systems": "systems_3" } }, + "spectrum": { + "flake": false, + "locked": { + "lastModified": 1759482047, + "narHash": "sha256-H1wiXRQHxxPyMMlP39ce3ROKCwI5/tUn36P8x6dFiiQ=", + "ref": "refs/heads/main", + "rev": "c5d5786d3dc938af0b279c542d1e43bce381b4b9", + "revCount": 996, + "type": "git", + "url": "https://spectrum-os.org/git/spectrum" + }, + "original": { + "type": "git", + "url": "https://spectrum-os.org/git/spectrum" + } + }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index 89ce6b4..c1a5498 100644 --- a/flake.nix +++ b/flake.nix @@ -23,11 +23,6 @@ nt.url = "github:cry128/nt"; - home-manager = { - url = "github:nix-community/home-manager/release-25.11"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - deploy-rs = { url = "github:serokell/deploy-rs"; inputs.nixpkgs.follows = "nixpkgs";