From 2b619a2be2306d6f6b4bf44568f1f0e97c9830b8 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 14 Feb 2026 14:06:29 +1000 Subject: [PATCH 01/68] add extraSpecialArgs --- cerulean/nixos-module/home-manager.nix | 39 ++++++++++++++------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/cerulean/nixos-module/home-manager.nix b/cerulean/nixos-module/home-manager.nix index e693ac9..97e4530 100644 --- a/cerulean/nixos-module/home-manager.nix +++ b/cerulean/nixos-module/home-manager.nix @@ -13,6 +13,7 @@ # limitations under the License. { root, + system, config, lib, specialArgs, @@ -25,25 +26,27 @@ pathExists ; in { - home-manager = { - users = - config.users.users - |> attrNames - |> filter (x: pathExists (root + "/homes/${x}")) - |> (x: lib.genAttrs x (y: import (root + "/homes/${y}"))); + config = { + home-manager = { + users = + config.users.users + |> attrNames + |> filter (x: pathExists (root + "/homes/${x}")) + |> (x: lib.genAttrs x (y: import (root + "/homes/${y}"))); - # extraSpecialArgs = specialArgs; - sharedModules = [ - # user configuration - # (import (root + "/nixpkgs.nix")) - (import (root + "/nixpkgs.nix")) - # options declarations - # (import ./nixpkgs.nix (args // {contextName = "homes";})) - (import ./nixpkgs.nix (args // {contextName = "homes";})) - ]; + extraSpecialArgs = {inherit root system;} // (specialArgs.inputs or {}); + sharedModules = [ + # user configuration + # (import (root + "/nixpkgs.nix")) + (import (root + "/nixpkgs.nix")) + # options declarations + # (import ./nixpkgs.nix (args // {contextName = "homes";})) + (import ./nixpkgs.nix (args // {contextName = "homes";})) + ]; - # disable home-manager trying anything fancy - # we control the pkgs now!! - # useGlobalPkgs = true; + # disable home-manager trying anything fancy + # we control the pkgs now!! + # useGlobalPkgs = true; + }; }; } From b7838874d89671d7a42d070f2366b35c00d8c0aa Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 14 Feb 2026 14:42:02 +1000 Subject: [PATCH 02/68] fix overlays bad type --- cerulean/nixos-module/nixpkgs.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cerulean/nixos-module/nixpkgs.nix b/cerulean/nixos-module/nixpkgs.nix index c5a97f7..5bf925f 100644 --- a/cerulean/nixos-module/nixpkgs.nix +++ b/cerulean/nixos-module/nixpkgs.nix @@ -21,6 +21,7 @@ inherit (builtins) mapAttrs + typeOf ; cfg = config.nixpkgs.channels; @@ -107,7 +108,8 @@ in { else if contextName == "homes" then { config = decl.pkgs.config or {}; - overlays = decl.pkgs.overlays or {}; + # XXX: WARNING: TODO: modify options so overlays must always be given as the correct type + overlays = decl.pkgs.overlays or []; } else {}; }; From 93aa29dc1038cf679547cd0975c4d9ae15491dde Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 14 Feb 2026 16:00:34 +1000 Subject: [PATCH 03/68] support nixpkgs.channels.*.default --- cerulean/nixos-module/home-manager.nix | 2 -- cerulean/nixos-module/nixpkgs.nix | 43 +++++++++----------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/cerulean/nixos-module/home-manager.nix b/cerulean/nixos-module/home-manager.nix index 97e4530..21ee78c 100644 --- a/cerulean/nixos-module/home-manager.nix +++ b/cerulean/nixos-module/home-manager.nix @@ -37,10 +37,8 @@ in { extraSpecialArgs = {inherit root system;} // (specialArgs.inputs or {}); sharedModules = [ # user configuration - # (import (root + "/nixpkgs.nix")) (import (root + "/nixpkgs.nix")) # options declarations - # (import ./nixpkgs.nix (args // {contextName = "homes";})) (import ./nixpkgs.nix (args // {contextName = "homes";})) ]; diff --git a/cerulean/nixos-module/nixpkgs.nix b/cerulean/nixos-module/nixpkgs.nix index 5bf925f..65db5a5 100644 --- a/cerulean/nixos-module/nixpkgs.nix +++ b/cerulean/nixos-module/nixpkgs.nix @@ -21,7 +21,6 @@ inherit (builtins) mapAttrs - typeOf ; cfg = config.nixpkgs.channels; @@ -52,11 +51,6 @@ in { }; }; - # or abort '' - # `nixpkgs.channels.${contextName}` does not exist, but neither does `nixpkgs.channels.default`! - # A channel configuration must be declared for module context "${contextName}". - # '' - config = let # TODO: use lib.types.submodule to restrict what options # TODO: can be given to `nixpkgs.channels.${moduleName}.${name}` @@ -68,7 +62,6 @@ in { |> mapAttrs ( name: args: lib.mkForce ( - # builtins.trace "SAVE ME GOT NAME: ${name}" ( assert args ? source || abort '' ${toString ./.} @@ -77,39 +70,31 @@ in { ((removeAttrs args ["source"]) // {inherit system;}) |> import args.source - # DEBUG: |> lib.mkOverride 200 ) - # ) ); in { # NOTE: _module.args is a special option that allows us to # NOTE: set extend specialArgs from inside the modules. - # "pkgs" is unique since the nix module system already handles it - # DEBUG: _module.args = lib.mkOverride 200 ( - # _module.args = ( - # if contextName == "hosts" - # then repos - # else - # assert ( - # repos - # |> builtins.attrNames - # |> map (x: "\"${x}\"") - # |> builtins.concatStringsSep " " - # |> (x: "FUCK YOU SO BAD: { ${x} }") - # |> abort - # ); - # removeAttrs repos ["pkgs"] - # ); _module.args = repos; - nixpkgs = + nixpkgs = let + defaultPkgs = + decl.default or (throw '' + Your `nixpkgs.nix` file does not declare a default package source. + Ensure you set `nixpkgs.channels.*.default = ...;` + ''); + in if contextName == "hosts" - then {flake.source = lib.mkIf (decl ? pkgs) (lib.mkOverride 200 decl.pkgs.source);} + then { + flake.source = lib.mkOverride 200 defaultPkgs.source; + config = lib.mkOverride 200 defaultPkgs.config; + } else if contextName == "homes" then { - config = decl.pkgs.config or {}; + # 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 = decl.pkgs.overlays or []; + overlays = lib.mkOverride 200 (defaultPkgs.overlays or []); } else {}; }; From 07ad32475ff2bf25f14060dd368fa005361dcc74 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 13:43:19 +1000 Subject: [PATCH 04/68] only inherit inputs --- cerulean/default.nix | 10 ++-------- cerulean/nexus/nexus.nix | 8 ++++---- flake.nix | 15 +++++++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/cerulean/default.nix b/cerulean/default.nix index d7797c4..6ec21cc 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -13,7 +13,7 @@ # limitations under the License. { mix, - deploy-rs, + inputs, ... } @ args: mix.newMixture args (mixture: { @@ -31,12 +31,6 @@ mix.newMixture args (mixture: { overlays = [ # build deploy-rs as a package not from the flake input, # hence we can rely on a nixpkg binary cache. - deploy-rs.overlays.default - # (self: super: { - # deploy-rs = { - # inherit (super) deploy-rs; - # lib = super.deploy-rs.lib; - # }; - # }) + inputs.deploy-rs.overlays.default ]; }) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 7466bb8..29d7387 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -16,7 +16,7 @@ this, nt, lib, - deploy-rs, + inputs, ... }: let inherit @@ -192,7 +192,7 @@ in { // { inherit root specialArgs; inherit (node) system; - _deploy-rs = deploy-rs; + _deploy-rs = inputs.deploy-rs; }; in specialArgs; @@ -220,7 +220,7 @@ in { user ; - nixosFor = system: deploy-rs.lib.${system}.activate.nixos; + nixosFor = system: inputs.deploy-rs.lib.${system}.activate.nixos; in { hostname = ssh.host; @@ -256,7 +256,7 @@ in { }; }); - checks = mapAttrs (system: deployLib: deployLib.deployChecks deploy) deploy-rs.lib; + checks = mapAttrs (system: deployLib: deployLib.deployChecks deploy) inputs.deploy-rs.lib; }; in outputs // customOutputs; diff --git a/flake.nix b/flake.nix index ff430bc..1a3f46e 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,10 @@ nt.url = "github:cry128/nt"; - deploy-rs.url = "github:serokell/deploy-rs"; + deploy-rs = { + url = "github:serokell/deploy-rs"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { @@ -31,9 +34,9 @@ ... } @ inputs: import ./cerulean - (inputs - // { - inherit (nixpkgs) lib; - inherit (nt) mix; - }); + { + inherit inputs; + inherit (nixpkgs) lib; + inherit (nt) mix; + }; } From c244c9791984eebb49bd65f36b9a81968a966bda Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 13:43:37 +1000 Subject: [PATCH 05/68] remove nixpkgs-unstable --- flake.nix | 3 --- 1 file changed, 3 deletions(-) diff --git a/flake.nix b/flake.nix index 1a3f46e..24c43ac 100644 --- a/flake.nix +++ b/flake.nix @@ -16,10 +16,7 @@ inputs = { systems.url = "github:nix-systems/default"; - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; - nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; - nt.url = "github:cry128/nt"; deploy-rs = { From b33730c06852ffb57e957c8aa6f743b326ecfee5 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 13:45:39 +1000 Subject: [PATCH 06/68] auto-import home-manager and microvm nixosModules --- cerulean/nexus/nexus.nix | 8 +++++++- flake.nix | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 29d7387..88f2863 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -197,7 +197,13 @@ in { in specialArgs; modules = - [self.nixosModules.default (findImport (root + "/hosts/${nodeName}"))] + [ + self.nixosModules.default + (findImport (root + "/hosts/${nodeName}")) + + inputs.home-manager.nixosModules.default + inputs.microvm.nixosModules.microvm + ] ++ (getGroupModules root nodeName node) ++ node.extraModules ++ nexus.extraModules; diff --git a/flake.nix b/flake.nix index 24c43ac..6d911c5 100644 --- a/flake.nix +++ b/flake.nix @@ -19,10 +19,20 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; 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"; }; + + microvm = { + url = "github:microvm-nix/microvm.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { From b5c2338456a2f326ff56fae9454b5f24e5a74b88 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 17:34:12 +1000 Subject: [PATCH 07/68] remove overlays overlays is no longer configured this way --- cerulean/nexus/nodes.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index 3d49434..ed1c8ad 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -30,7 +30,6 @@ in rec { groups = []; extraModules = []; specialArgs = Terminal {}; - overlays = []; deploy = { user = "root"; From 536d48c8c3cdad1542e67b4c85e348e85fdbc1d3 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 17:34:29 +1000 Subject: [PATCH 08/68] add new TODO.md --- TODO.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index 25f6f1b..f5e657d 100755 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,14 @@ -Allow `Cerulean.mkNexus` to be an alias for `flake-parts.lib.mkFlake` -also rename `Cerulean` to `cerulean` in Nix to maintain the naming convention. +- [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` +- [ ] find an alternative to `nix.settings.trusted-users` probably +- [ ] add support for github:microvm-nix/microvm.nix +- [ ] add support for sops-nix -Using `flake-parts` ensures Cerulean is usable without restricting -yourself only to the Cerulean ecosystem. +- [ ] create an alternative to nixos-install called cerulean-install that + allows people to easily bootstrap new machines -- [ ] extend the options.nixpkgs to allow any number of package repositories!! -- [x] auto-propagate the same specialArgs for hosts to home-manager + +- [ ] rename nixos-modules/ to nixos/ + + +- [ ] add the ceru-build user +- [ ] ensure all machines are in groups.all by default From 16d9062e694e6a1de802beabc26731687ba596fd Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 17:34:35 +1000 Subject: [PATCH 09/68] v0.2.1-alpha --- cerulean/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cerulean/default.nix b/cerulean/default.nix index 6ec21cc..449028b 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -21,7 +21,7 @@ mix.newMixture args (mixture: { ./nexus ]; - version = "0.2.0"; + version = "0.2.1"; nixosModules = rec { default = cerulean; From c5c867b43fd25d0aceb12e393d01c3d621a98796 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 17:35:18 +1000 Subject: [PATCH 10/68] always use root group --- cerulean/nexus/nexus.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 88f2863..41ef28b 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -133,7 +133,12 @@ Cerulean Nexus node "${nodeName}" does not declare group membership as a list, got "${typeOf node.groups}" instead! Ensure `nexus.nodes.${nodeName}.groups` is a list under your call to `cerulean.mkNexus`. ''; - node.groups + # ensure root group is always added + (node.groups + ++ { + _parent = null; + _name = ROOT_GROUP_NAME; + }) # ensure all members are actually groups |> map (group: let got = From e07047ab50d5328701c628c56d8ef24635b03f6b Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 17:46:37 +1000 Subject: [PATCH 11/68] move nixos-modules/ -> nixos/ --- cerulean/default.nix | 2 +- cerulean/{nixos-module => nixos}/default.nix | 0 cerulean/{nixos-module => nixos}/home-manager.nix | 0 cerulean/{nixos-module => nixos}/nixpkgs.nix | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename cerulean/{nixos-module => nixos}/default.nix (100%) rename cerulean/{nixos-module => nixos}/home-manager.nix (100%) rename cerulean/{nixos-module => nixos}/nixpkgs.nix (100%) diff --git a/cerulean/default.nix b/cerulean/default.nix index 449028b..f24bb5a 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -25,7 +25,7 @@ mix.newMixture args (mixture: { nixosModules = rec { default = cerulean; - cerulean = ./nixos-module; + cerulean = ./nixos; }; overlays = [ diff --git a/cerulean/nixos-module/default.nix b/cerulean/nixos/default.nix similarity index 100% rename from cerulean/nixos-module/default.nix rename to cerulean/nixos/default.nix diff --git a/cerulean/nixos-module/home-manager.nix b/cerulean/nixos/home-manager.nix similarity index 100% rename from cerulean/nixos-module/home-manager.nix rename to cerulean/nixos/home-manager.nix diff --git a/cerulean/nixos-module/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix similarity index 100% rename from cerulean/nixos-module/nixpkgs.nix rename to cerulean/nixos/nixpkgs.nix From 9127e64fedbc488ed9140b9c9acbf1e389035f4b Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 15 Feb 2026 23:29:41 +1000 Subject: [PATCH 12/68] more TODO.md --- TODO.md | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index f5e657d..995ed69 100755 --- a/TODO.md +++ b/TODO.md @@ -1,14 +1,39 @@ - [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` +- [ ] create an alternative to nixos-install called cerulean-install that + allows people to easily bootstrap new machines (and host it on dobutterfliescry.net) + - [ ] find an alternative to `nix.settings.trusted-users` probably +- [ ] add the ceru-build user, - [ ] add support for github:microvm-nix/microvm.nix - [ ] add support for sops-nix -- [ ] create an alternative to nixos-install called cerulean-install that - allows people to easily bootstrap new machines +- [ ] it would be cool to enable/disable groups and hosts +- [ ] find a standard for how nixpkgs.nix can have a different base per group + +- [X] rename nixos-modules/ to nixos/ +- [X] ensure all machines are in groups.all by default + +## Low Priority +- [ ] rename extraModules to modules? +- [ ] 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 + it then returns instead of config) -- [ ] rename nixos-modules/ to nixos/ - - -- [ ] add the ceru-build user -- [ ] ensure all machines are in groups.all by default +``` +vms = { + home-assistant = { + autostart = true; + # matches in vms/* + image = "home-assistant"; + options = { + mem = 2048; + }; + }; + equinox = { + image = "home-assistant"; + }; +}; +``` From 2cff3119cd2b9ab29a0936ecf5598eb639b8a477 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Mon, 16 Feb 2026 08:17:33 +1000 Subject: [PATCH 13/68] fix nt not propagated --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 6d911c5..220e732 100644 --- a/flake.nix +++ b/flake.nix @@ -42,7 +42,7 @@ } @ inputs: import ./cerulean { - inherit inputs; + inherit inputs nt; inherit (nixpkgs) lib; inherit (nt) mix; }; From 803f759472aa34d7a4bdbb5d763655893833640b Mon Sep 17 00:00:00 2001 From: _cry64 Date: Mon, 16 Feb 2026 08:19:07 +1000 Subject: [PATCH 14/68] fix self not propagated --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 220e732..8fcd0e0 100644 --- a/flake.nix +++ b/flake.nix @@ -36,13 +36,14 @@ }; outputs = { + self, nixpkgs, nt, ... } @ inputs: import ./cerulean { - inherit inputs nt; + inherit inputs self nt; inherit (nixpkgs) lib; inherit (nt) mix; }; From c24cc9d46f92d2eaa6dc55f43574201aec579708 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Mon, 16 Feb 2026 08:20:24 +1000 Subject: [PATCH 15/68] fix [] ++ {} (im oopid) --- cerulean/nexus/nexus.nix | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 41ef28b..d17cfdb 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -96,7 +96,7 @@ assert isAttrs nexus || abort '' Cerulean Nexus config must be provided as an attribute set, got "${typeOf nexus}" instead! - Ensure all the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`. + Ensure the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`. ''; let base = nt.projectOnto templateNexus nexus; in @@ -135,10 +135,12 @@ ''; # ensure root group is always added (node.groups - ++ { - _parent = null; - _name = ROOT_GROUP_NAME; - }) + ++ [ + { + _parent = null; + _name = ROOT_GROUP_NAME; + } + ]) # ensure all members are actually groups |> map (group: let got = From 8dc975fc89c543c9e98cfe3c6dfcd98a41531419 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Mon, 16 Feb 2026 08:21:50 +1000 Subject: [PATCH 16/68] fix dont use microvm.nixosModules.microvm by default --- TODO.md | 6 ++ cerulean/nexus/nexus.nix | 2 +- cerulean/nexus/nodes.nix | 1 + cerulean/nexus/snow.nix | 108 ++++++++++++++++++++++++++++++ cerulean/nixos/microvm-child.nix | 13 ++++ cerulean/nixos/microvm-parent.nix | 13 ++++ 6 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 cerulean/nexus/snow.nix create mode 100644 cerulean/nixos/microvm-child.nix create mode 100644 cerulean/nixos/microvm-parent.nix diff --git a/TODO.md b/TODO.md index 995ed69..b80f03c 100755 --- a/TODO.md +++ b/TODO.md @@ -22,6 +22,12 @@ it then returns instead of config) +- [ ] what if we automated the process of replacing windows with Nix?? + then push this to nixos-anywhere or nix-infect lmaooo + +- [ ] patch microvm so that acpi=off https://github.com/microvm-nix/microvm.nix/commit/b59a26962bb324cc0a134756a323f3e164409b72 + cause otherwise 2GB causes a failure + ``` vms = { home-assistant = { diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index d17cfdb..f03d1ce 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -209,7 +209,7 @@ in { (findImport (root + "/hosts/${nodeName}")) inputs.home-manager.nixosModules.default - inputs.microvm.nixosModules.microvm + # inputs.microvm.nixosModules.microvm ] ++ (getGroupModules root nodeName node) ++ node.extraModules diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index ed1c8ad..1687e46 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -26,6 +26,7 @@ in rec { Terminal ; in { + enabled = true; system = "x86_64-linux"; # sane default (i hope...) groups = []; extraModules = []; diff --git a/cerulean/nexus/snow.nix b/cerulean/nexus/snow.nix new file mode 100644 index 0000000..1316ee3 --- /dev/null +++ b/cerulean/nexus/snow.nix @@ -0,0 +1,108 @@ +# Copyright 2026 Emile Clark-Boman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{ + inputs, + lib, + ... +}: { + # nexus + options = let + inherit + (lib) + mkOption + types + ; + in { + extraModules = mkOption { + type = types.listOf types.path; + }; + specialArgs = mkOption { + type = types.attrs; + }; + + groups = mkOption { + type = types.attrs; + }; + + nodes = mkOption { + type = types.attrsOf (types.submoduleWith ({...}: { + options = { + enabled = mkOption { + type = types.bool; + default = true; + }; + system = mkOption { + type = types.enum inputs.systems; + }; + groups = mkOption { + type = types.list; + }; + modules = mkOption { + type = types.list; + }; + args = mkOption { + type = types.attrs; + }; + + deploy = { + user = mkOption { + type = types.str; + }; + sudoCmd = mkOption { + type = types.str; + }; + interactiveSudo = mkOption { + type = types.bool; + }; + + remoteBuild = mkOption { + type = types.bool; + }; + autoRollback = mkOption { + type = types.bool; + }; + magicRollback = mkOption { + type = types.bool; + }; + + activationTimeout = mkOption { + type = types.int; + }; + confirmTimeout = mkOption { + type = types.int; + }; + + ssh = { + host = mkOption { + type = types.str; + }; + user = mkOption { + type = types.str; + }; + port = mkOption { + type = types.int; + }; + opts = mkOption { + type = types.listOf types.str; + }; + }; + }; + }; + })); + }; + }; + + config = { + }; +} diff --git a/cerulean/nixos/microvm-child.nix b/cerulean/nixos/microvm-child.nix new file mode 100644 index 0000000..2b6a12e --- /dev/null +++ b/cerulean/nixos/microvm-child.nix @@ -0,0 +1,13 @@ +# Copyright 2026 Emile Clark-Boman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/cerulean/nixos/microvm-parent.nix b/cerulean/nixos/microvm-parent.nix new file mode 100644 index 0000000..2b6a12e --- /dev/null +++ b/cerulean/nixos/microvm-parent.nix @@ -0,0 +1,13 @@ +# Copyright 2026 Emile Clark-Boman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. From dc37c482a8c71e63a7a10704e1c170abd8f1c7c3 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Mon, 16 Feb 2026 18:56:34 +1000 Subject: [PATCH 17/68] remove nixpkgs dependency YIPPIE YIPPIE YIPPIE --- TODO.md | 3 +++ cerulean/nexus/nexus.nix | 23 +++++++++++++---------- cerulean/nexus/nodes.nix | 32 +++++++++++++++++++++++++++++--- flake.nix | 4 +--- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/TODO.md b/TODO.md index b80f03c..e86d610 100755 --- a/TODO.md +++ b/TODO.md @@ -13,6 +13,9 @@ - [X] rename nixos-modules/ to nixos/ - [X] ensure all machines are in groups.all by default +- [X] fix nixpkgs.nix not working (default not respected) +- [X] remove dependence on nixpkgs + ## Low Priority - [ ] rename extraModules to modules? - [ ] rename specialArgs to args? diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index f03d1ce..24a0b75 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -15,7 +15,6 @@ self, this, nt, - lib, inputs, ... }: let @@ -50,13 +49,8 @@ (nt.naive.terminal) Terminal ; - - missing = msg: path: - Terminal (abort '' - Each Cerulean Nexus node is required to specify ${msg}! - Ensure `nexus.${path}` exists under your call to `cerulean.mkNexus`. - ''); in { + base = null; extraModules = []; specialArgs = Terminal {}; @@ -188,8 +182,13 @@ in { customOutputs = removeAttrs decl ["nexus"]; outputs = rec { - nixosConfigurations = mapNodes nexus.nodes ( - nodeName: node: let + nixosConfigurations = mapNodes nexus ( + { + lib, + nodeName, + node, + ... + }: let nixosDecl = lib.nixosSystem { system = node.system; specialArgs = let @@ -219,7 +218,11 @@ in { nixosDecl ); - deploy.nodes = mapNodes nexus.nodes (nodeName: node: let + deploy.nodes = mapNodes nexus ({ + nodeName, + node, + ... + }: let inherit (node.deploy) activationTimeout diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index 1687e46..fa0bc0b 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -25,6 +25,12 @@ in rec { (nt.naive.terminal) Terminal ; + + missing = msg: path: + Terminal (abort '' + Each Cerulean Nexus node is required to specify ${msg}! + Ensure `nexus.${path}` exists under your call to `cerulean.mkNexus`. + ''); in { enabled = true; system = "x86_64-linux"; # sane default (i hope...) @@ -32,6 +38,8 @@ in rec { extraModules = []; specialArgs = Terminal {}; + base = null; + deploy = { user = "root"; sudo = "sudo -u"; @@ -67,7 +75,25 @@ in rec { in nt.projectOnto templateAttrs nodeAttrs; - mapNodes = nodes: f: - nodes - |> mapAttrs (nodeName: nodeAttrs: f nodeName (parseNode nodeName nodeAttrs)); + mapNodes = nexus: f: + nexus.nodes + |> mapAttrs (nodeName: nodeAttrs: let + node = parseNode nodeName nodeAttrs; + + # use per-node base or default to nexus base + base = + if node.base != null + then node.base + else if nexus.base != null + then nexus.base + else + abort '' + Cerulean cannot construct nexus node "${nodeName}" without a base package source. + Ensure `nexus.nodes.*.base` or `nexus.base` is a flake reference to the github:NixOS/nixpkgs repository. + ''; + in + f { + inherit nodeName node; + lib = base.lib; + }); } diff --git a/flake.nix b/flake.nix index 8fcd0e0..6682de0 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,7 @@ inputs = { systems.url = "github:nix-systems/default"; - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + # nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; nt.url = "github:cry128/nt"; home-manager = { @@ -37,14 +37,12 @@ outputs = { self, - nixpkgs, nt, ... } @ inputs: import ./cerulean { inherit inputs self nt; - inherit (nixpkgs) lib; inherit (nt) mix; }; } From 272b03aa6efff55b2ed98b9f6d881c8248f3a788 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Mon, 16 Feb 2026 18:56:51 +1000 Subject: [PATCH 18/68] update TODO --- TODO.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index e86d610..a12b8c3 100755 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,5 @@ +- [ ] deploy port should default to the first port given to `services.openssh` + - [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` - [ ] create an alternative to nixos-install called cerulean-install that allows people to easily bootstrap new machines (and host it on dobutterfliescry.net) @@ -10,12 +12,16 @@ - [ ] it would be cool to enable/disable groups and hosts - [ ] find a standard for how nixpkgs.nix can have a different base per group +- [ ] go through all flake inputs (recursively) and ENSURE we remove all duplicates by using follows!! + - [X] rename nixos-modules/ to nixos/ - [X] ensure all machines are in groups.all by default - [X] fix nixpkgs.nix not working (default not respected) - [X] remove dependence on nixpkgs +- [ ] allow multiple privesc methods, the standard is pam_ssh_agent_auth + ## Low Priority - [ ] rename extraModules to modules? - [ ] rename specialArgs to args? @@ -31,7 +37,11 @@ - [ ] patch microvm so that acpi=off https://github.com/microvm-nix/microvm.nix/commit/b59a26962bb324cc0a134756a323f3e164409b72 cause otherwise 2GB causes a failure -``` +- [ ] rewrite the ceru cli in rust +- [ ] make `ceru` do local and remote deployments + +```nix +# REF: foxora vms = { home-assistant = { autostart = true; From fd3b11c7cf88684544df6b90decf80fcbd8456cf Mon Sep 17 00:00:00 2001 From: _cry64 Date: Mon, 16 Feb 2026 19:25:23 +1000 Subject: [PATCH 19/68] force system to be specified --- cerulean/nexus/nodes.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index fa0bc0b..ccb5c20 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -33,7 +33,7 @@ in rec { ''); in { enabled = true; - system = "x86_64-linux"; # sane default (i hope...) + system = missing "its system architecture" "system"; groups = []; extraModules = []; specialArgs = Terminal {}; From a2ac6b5da35cd6031e2e8c24bcda070d6d9be4fc Mon Sep 17 00:00:00 2001 From: _cry64 Date: Mon, 16 Feb 2026 19:37:21 +1000 Subject: [PATCH 20/68] ok maybe leave nixpkgs... we won't depend on it (i force that) but it's good to allow following --- flake.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 6682de0..89ce6b4 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,11 @@ inputs = { systems.url = "github:nix-systems/default"; - # nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + + # WARNING: nixpkgs is ONLY included so flakes using Cerulean can + # WARNING: force Cerulean's inputs to follow a specific revision. + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + nt.url = "github:cry128/nt"; home-manager = { From c5ef764e27581d7049df51f42fa95833143e4acc Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 11:12:21 +1000 Subject: [PATCH 21/68] update README --- README.md | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b62396e..474239b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +![enbyware](https://pride-badges.pony.workers.dev/static/v1?label=enbyware&labelColor=%23555&stripeWidth=8&stripeColors=FCF434%2CFFFFFF%2C9C59D1%2C2C2C2C) +![repo size](https://img.shields.io/github/repo-size/cry128/cerulean) + >[!WARNING] > ✨ **Under Construction** ✨ > Cerulean has lived rent free in my head for the last 12 months. @@ -5,15 +8,41 @@ > for use at my workplace. **Be not afraid!** It's only a matter > of time until Cerulean is ready for use! +# 🌌 🚀 Cerulean Nexus +The culmination of 2 years designing better Nix flakes. Cerulean removes the boilerplate of managing +NixOS infrastructure by declaring each machine as a **node** and their relationships as *"Nexus Networks"*, +virtual networks of servers that Cerulean can manage. Each Nexus is **very powerful**. Allowing for simple +distributed computing, automatic construction of VPNs, DNS for local hostnames, and that's just scratching the surface... + +- Is your node a VPS? Set `deploy.ssh.host = "example.com"` and Cerulean will configure custom build users, + ssh deployment via custom PAM modules, etc etc +- Is your node a VM? Set `vms = [ nodes.VM_NODE ]` on your host node, and Cerulean will configure + all the bridging, NAT, and other networking you so desire! + +## 🩷💜 Motivation +Nix is intended as a non-restrictive & unopinionated system, which is amazing, but it also means +every user develops their own standards to simplify their config. Cerulean however is very much +opinionated and contains all the standards I personally believe should be sane defaults for every NixOS machine. + +> Flakes are not designed for NixOS, they're designed for Nix, and that's an important distinction. + +Flakes and NixOS don't offer anything to simplify managing interconnected nodes of machines. +But this ends with *extremely messy configs* with **a lot of footguns**. You shouldn't have to spend +days reading about networking and learning to work with other peoples' modules. + +Finally, the Nix module system assumes you only use one channel of `github:NixOS/nixpkgs` but this +just isn't realistic. Most people have both `inputs.nixpkgs` and `inputs.nixpkgs-unstable` defined. +So cerulean declares the `nixpkgs.channels.*` option so you don't have to import your channels +manually! + ## 💙 Same Colour, More Control -Cerulean is what you wish Azure could be. An expansive collection of microservices, pre-configured systems, +>[!NOTE] +> This section is *mostly* for the business minded people. + +Cerulean is what you wish Azure could be. Providing an expansive collection of microservices, pre-configured systems, and entirely self-hosted! Cerulean is built using NixOS as a foundation so you know it's never going to break randomly. NixOS backing makes Cerulean **extremely scalable**! Just rent a new VPS and Cerulean will build an ISO of your configuration. No stress, no hassle! Say goodbye to Azure! And say goodbye to Kubernetes! You're taking life into your own hands 💙 -### 🌌 🚀 Nexus -Cerulean allows you to declare *"Nexus Networks"*, virtual networks of servers that Cerulean can manage. -Each Nexus is **very powerful**. Allowing for simple distributed computing, automatic construction of a wireguard -VPN, distributed DNS for local hostnames, and that's just scratching the surface... From ce996f99562a86e81f5dc17185d4bd5715493e7e Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 11:18:11 +1000 Subject: [PATCH 22/68] update construction README --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 474239b..b1362ae 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,13 @@ >[!WARNING] > ✨ **Under Construction** ✨ -> Cerulean has lived rent free in my head for the last 12 months. -> I'm developing this project for personal use and especially -> for use at my workplace. **Be not afraid!** It's only a matter -> of time until Cerulean is ready for use! +> +> Cerulean is in an **experimental alpha release stage**.
+> **Be not afraid!** It's only a matter of time until Cerulean is ready for use! +> +> If you're curious about or want to use Cerulean then
+> *please please please* contact me on Bluesky [@dobutterfliescry.net](https://bsky.app/profile/dobutterfliescry.net).
+> (i would actually *melt* and fall in love with you...) # 🌌 🚀 Cerulean Nexus The culmination of 2 years designing better Nix flakes. Cerulean removes the boilerplate of managing From af1da3dbc383872836864d8595e596ddf707d0f6 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 11:42:59 +1000 Subject: [PATCH 23/68] rename specialArgs -> args --- cerulean/nexus/nexus.nix | 23 ++++++++++------------- cerulean/nexus/nodes.nix | 2 +- cerulean/nexus/snow.nix | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 24a0b75..2424cd2 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -52,7 +52,7 @@ in { base = null; extraModules = []; - specialArgs = Terminal {}; + args = Terminal {}; groups = Terminal {}; nodes = Terminal {}; @@ -189,19 +189,16 @@ in { node, ... }: let - nixosDecl = lib.nixosSystem { + nixosDecl = lib.nixosSystem rec { system = node.system; - specialArgs = let - specialArgs = - nexus.specialArgs - // node.specialArgs - // { - inherit root specialArgs; - inherit (node) system; - _deploy-rs = inputs.deploy-rs; - }; - in - specialArgs; + specialArgs = + nexus.args + // node.args + // { + inherit root specialArgs; + inherit (node) system; + _deploy-rs = inputs.deploy-rs; + }; modules = [ self.nixosModules.default diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index ccb5c20..46b9add 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -36,7 +36,7 @@ in rec { system = missing "its system architecture" "system"; groups = []; extraModules = []; - specialArgs = Terminal {}; + args = Terminal {}; base = null; diff --git a/cerulean/nexus/snow.nix b/cerulean/nexus/snow.nix index 1316ee3..6704710 100644 --- a/cerulean/nexus/snow.nix +++ b/cerulean/nexus/snow.nix @@ -27,7 +27,7 @@ extraModules = mkOption { type = types.listOf types.path; }; - specialArgs = mkOption { + args = mkOption { type = types.attrs; }; From 0d85fe39a678701e1419f0ee750725255029e374 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 11:46:59 +1000 Subject: [PATCH 24/68] rename extraModules -> modules --- cerulean/nexus/nexus.nix | 6 +++--- cerulean/nexus/nodes.nix | 2 +- cerulean/nexus/snow.nix | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 2424cd2..22424ba 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -51,7 +51,7 @@ ; in { base = null; - extraModules = []; + modules = []; args = Terminal {}; groups = Terminal {}; @@ -208,8 +208,8 @@ in { # inputs.microvm.nixosModules.microvm ] ++ (getGroupModules root nodeName node) - ++ node.extraModules - ++ nexus.extraModules; + ++ node.modules + ++ nexus.modules; }; in nixosDecl diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index 46b9add..a1b6117 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -35,7 +35,7 @@ in rec { enabled = true; system = missing "its system architecture" "system"; groups = []; - extraModules = []; + modules = []; args = Terminal {}; base = null; diff --git a/cerulean/nexus/snow.nix b/cerulean/nexus/snow.nix index 6704710..28496ca 100644 --- a/cerulean/nexus/snow.nix +++ b/cerulean/nexus/snow.nix @@ -24,7 +24,7 @@ types ; in { - extraModules = mkOption { + modules = mkOption { type = types.listOf types.path; }; args = mkOption { From 04d6bd04c3988d5719a7906879e11f180570f0d1 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 11:47:49 +1000 Subject: [PATCH 25/68] v0.2.2-alpha --- cerulean/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cerulean/default.nix b/cerulean/default.nix index f24bb5a..202fdf9 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -21,7 +21,7 @@ mix.newMixture args (mixture: { ./nexus ]; - version = "0.2.1"; + version = "0.2.2"; nixosModules = rec { default = cerulean; From 59d1028e19d9ac5f43122d08119e90d77e56a7da Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 17 Feb 2026 12:41:53 +1000 Subject: [PATCH 26/68] 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 27/68] 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 28/68] 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 29/68] 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 30/68] 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 31/68] 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 32/68] 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 33/68] 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 34/68] 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"; From d0780c1ff4bb15dfd3c3271082667436535e89eb Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Feb 2026 20:13:43 +1000 Subject: [PATCH 35/68] TEMP: use base --- cerulean/default.nix | 2 +- cerulean/nixos/nixpkgs.nix | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cerulean/default.nix b/cerulean/default.nix index 202fdf9..4639fa4 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -21,12 +21,12 @@ mix.newMixture args (mixture: { ./nexus ]; - version = "0.2.2"; nixosModules = rec { default = cerulean; cerulean = ./nixos; }; + version = "0.2.3"; overlays = [ # build deploy-rs as a package not from the flake input, 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 53914a77e27689fa8992ddc43c686ac251bccf6c Mon Sep 17 00:00:00 2001 From: _cry64 Date: Wed, 18 Feb 2026 23:56:05 +1000 Subject: [PATCH 36/68] 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"; From 80ec35ebbc86ff9b01ddacc1e8d668a9a9aa0fba Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Feb 2026 13:47:51 +1000 Subject: [PATCH 37/68] update TODO --- TODO.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index 1dde662..355863e 100755 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,11 @@ -- [ ] 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 +- [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` + +- [X] base should automatically be set as the default (dont do anything with the default) +- [X] 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` - [ ] create an alternative to nixos-install called cerulean-install that allows people to easily bootstrap new machines (and host it on dobutterfliescry.net) @@ -34,10 +35,6 @@ that allows transformations (ie a stop post config, ie outputs, which it then returns instead of config) - -- [ ] what if we automated the process of replacing windows with Nix?? - then push this to nixos-anywhere or nix-infect lmaooo - - [ ] patch microvm so that acpi=off https://github.com/microvm-nix/microvm.nix/commit/b59a26962bb324cc0a134756a323f3e164409b72 cause otherwise 2GB causes a failure From 56184d62fb66e060960ee8d403c829d5225f5019 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Feb 2026 14:38:52 +1000 Subject: [PATCH 38/68] clarify license --- NOTICE | 26 ++++++++++++++++++++++++++ ceru/ceru | 2 +- ceru/libceru.sh | 2 +- ceru/subcmds/new/cache-key | 2 +- ceru/subcmds/new/default.sh | 2 +- ceru/subcmds/new/password | 2 +- ceru/subcmds/new/ssh-key | 2 +- ceru/subcmds/new/wg-key | 2 +- cerulean/default.nix | 2 +- cerulean/nexus/default.nix | 2 +- cerulean/nexus/nexus.nix | 2 +- cerulean/nexus/nodes.nix | 2 +- cerulean/nexus/snow.nix | 2 +- cerulean/nixos/default.nix | 2 +- cerulean/nixos/home-manager.nix | 2 +- cerulean/nixos/microvm-child.nix | 2 +- cerulean/nixos/microvm-parent.nix | 2 +- cerulean/nixos/nixpkgs.nix | 2 +- flake.nix | 2 +- 19 files changed, 44 insertions(+), 18 deletions(-) create mode 100644 NOTICE diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..f85b4fa --- /dev/null +++ b/NOTICE @@ -0,0 +1,26 @@ +Cerulean (https://dobutterfliescry.net/cerulean) +Copyright 2025-2026 _cry64 (Emile Clark-Boman) + +This product includes software developed by +_cry64 (Emile Clark-Boman) (https://github.com/cry128/nt) under the MIT license. +Copyright 2025-2026 _cry64 (Emile Clark-Boman) + +This product includes software developed by +Eelco Dolstra and the Nixpkgs/NixOS contributors under the MIT license. +Copyright 2003-2026 Eelco Dolstra and the Nixpkgs/NixOS contributors + +This product includes software developed by +the Home Manager contributors (https://github.com/nix-community/home-manager) under the MIT license. +Copyright 2017-2026 Home Manager contributors + +This product includes software developed by +Serokell (https://serokell.io) under the MPL-2.0 license. +Copyright 2020-2026 Serokell + +This product includes software developed by +nix-systems (https://github.com/nix-systems) under the MIT license. +Copyright 2023 nix-systems + +This product includes software developed by +Astro (https://github.com/astro) and the MicroVM.nix contributors (https://github.com/microvm-nix/microvm.nix) under the MIT license. +Copyright 2021 Astro, and MicroVM.nix contributors diff --git a/ceru/ceru b/ceru/ceru index f407960..5630489 100755 --- a/ceru/ceru +++ b/ceru/ceru @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/ceru/libceru.sh b/ceru/libceru.sh index 95aba51..ed1b6c3 100755 --- a/ceru/libceru.sh +++ b/ceru/libceru.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/ceru/subcmds/new/cache-key b/ceru/subcmds/new/cache-key index 0b5aa13..e193e83 100755 --- a/ceru/subcmds/new/cache-key +++ b/ceru/subcmds/new/cache-key @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/ceru/subcmds/new/default.sh b/ceru/subcmds/new/default.sh index af34920..88175fa 100755 --- a/ceru/subcmds/new/default.sh +++ b/ceru/subcmds/new/default.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/ceru/subcmds/new/password b/ceru/subcmds/new/password index d5cd795..232539b 100755 --- a/ceru/subcmds/new/password +++ b/ceru/subcmds/new/password @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2026 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/ceru/subcmds/new/ssh-key b/ceru/subcmds/new/ssh-key index d0aa524..651aadb 100755 --- a/ceru/subcmds/new/ssh-key +++ b/ceru/subcmds/new/ssh-key @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/ceru/subcmds/new/wg-key b/ceru/subcmds/new/wg-key index 2efa85c..bab6773 100755 --- a/ceru/subcmds/new/wg-key +++ b/ceru/subcmds/new/wg-key @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/default.nix b/cerulean/default.nix index 4639fa4..a2dba7d 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -1,4 +1,4 @@ -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nexus/default.nix b/cerulean/nexus/default.nix index 65495bf..7a756aa 100644 --- a/cerulean/nexus/default.nix +++ b/cerulean/nexus/default.nix @@ -1,4 +1,4 @@ -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 5d0ca02..6f79b26 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -1,4 +1,4 @@ -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index 13ced2f..0e3d03d 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -1,4 +1,4 @@ -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nexus/snow.nix b/cerulean/nexus/snow.nix index 28496ca..736779f 100644 --- a/cerulean/nexus/snow.nix +++ b/cerulean/nexus/snow.nix @@ -1,4 +1,4 @@ -# Copyright 2026 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index ea7c359..02bbe8b 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -1,4 +1,4 @@ -# Copyright 2026 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nixos/home-manager.nix b/cerulean/nixos/home-manager.nix index 1d281cb..7c87a0c 100644 --- a/cerulean/nixos/home-manager.nix +++ b/cerulean/nixos/home-manager.nix @@ -1,4 +1,4 @@ -# Copyright 2026 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nixos/microvm-child.nix b/cerulean/nixos/microvm-child.nix index 2b6a12e..d13e217 100644 --- a/cerulean/nixos/microvm-child.nix +++ b/cerulean/nixos/microvm-child.nix @@ -1,4 +1,4 @@ -# Copyright 2026 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nixos/microvm-parent.nix b/cerulean/nixos/microvm-parent.nix index 2b6a12e..d13e217 100644 --- a/cerulean/nixos/microvm-parent.nix +++ b/cerulean/nixos/microvm-parent.nix @@ -1,4 +1,4 @@ -# Copyright 2026 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix index 0376f47..f07fc35 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -1,4 +1,4 @@ -# Copyright 2026 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/flake.nix b/flake.nix index c1a5498..0e6d711 100644 --- a/flake.nix +++ b/flake.nix @@ -1,4 +1,4 @@ -# Copyright 2025 Emile Clark-Boman +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 8f7eb8ff3fdb61e821ce5bdae98857af7c033705 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Feb 2026 22:41:08 +1000 Subject: [PATCH 39/68] finish snow nodes module --- cerulean/nexus/snow.nix | 108 ------------------- cerulean/snow/default.nix | 18 ++++ cerulean/snow/nodes/default.nix | 49 +++++++++ cerulean/snow/nodes/shared.nix | 82 +++++++++++++++ cerulean/snow/nodes/submodule.nix | 166 ++++++++++++++++++++++++++++++ 5 files changed, 315 insertions(+), 108 deletions(-) delete mode 100644 cerulean/nexus/snow.nix create mode 100644 cerulean/snow/default.nix create mode 100644 cerulean/snow/nodes/default.nix create mode 100644 cerulean/snow/nodes/shared.nix create mode 100644 cerulean/snow/nodes/submodule.nix diff --git a/cerulean/nexus/snow.nix b/cerulean/nexus/snow.nix deleted file mode 100644 index 736779f..0000000 --- a/cerulean/nexus/snow.nix +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -{ - inputs, - lib, - ... -}: { - # nexus - options = let - inherit - (lib) - mkOption - types - ; - in { - modules = mkOption { - type = types.listOf types.path; - }; - args = mkOption { - type = types.attrs; - }; - - groups = mkOption { - type = types.attrs; - }; - - nodes = mkOption { - type = types.attrsOf (types.submoduleWith ({...}: { - options = { - enabled = mkOption { - type = types.bool; - default = true; - }; - system = mkOption { - type = types.enum inputs.systems; - }; - groups = mkOption { - type = types.list; - }; - modules = mkOption { - type = types.list; - }; - args = mkOption { - type = types.attrs; - }; - - deploy = { - user = mkOption { - type = types.str; - }; - sudoCmd = mkOption { - type = types.str; - }; - interactiveSudo = mkOption { - type = types.bool; - }; - - remoteBuild = mkOption { - type = types.bool; - }; - autoRollback = mkOption { - type = types.bool; - }; - magicRollback = mkOption { - type = types.bool; - }; - - activationTimeout = mkOption { - type = types.int; - }; - confirmTimeout = mkOption { - type = types.int; - }; - - ssh = { - host = mkOption { - type = types.str; - }; - user = mkOption { - type = types.str; - }; - port = mkOption { - type = types.int; - }; - opts = mkOption { - type = types.listOf types.str; - }; - }; - }; - }; - })); - }; - }; - - config = { - }; -} diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix new file mode 100644 index 0000000..1fe87f2 --- /dev/null +++ b/cerulean/snow/default.nix @@ -0,0 +1,18 @@ +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{...}: { + imports = [ + ./nodes + ]; +} diff --git a/cerulean/snow/nodes/default.nix b/cerulean/snow/nodes/default.nix new file mode 100644 index 0000000..c458a41 --- /dev/null +++ b/cerulean/snow/nodes/default.nix @@ -0,0 +1,49 @@ +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{lib, ...}: { + options.nodes = let + inherit + (lib) + mkOption + types + ; + in + mkOption { + description = '' + Cerulean node declarations. + ''; + type = types.submoduleWith { + imports = [./shared.nix]; + + options = { + groups = mkOption { + type = types.attrs; + default = {}; + example = lib.literalExpression "{ servers = { staging = {}; production = {}; }; }"; + description = '' + Hierarchical groups that nodes can be a member of. + ''; + }; + + nodes = mkOption { + type = types.attrsOf (types.submoduleWith (import ./submodule.nix)); + # example = { ... }; # TODO + description = '' + Node (host systems) declarations. + ''; + }; + }; + }; + }; +} diff --git a/cerulean/snow/nodes/shared.nix b/cerulean/snow/nodes/shared.nix new file mode 100644 index 0000000..c840d22 --- /dev/null +++ b/cerulean/snow/nodes/shared.nix @@ -0,0 +1,82 @@ +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{lib, ...}: let + inherit + (lib) + mkOption + types + ; + + flakeRef = types.either types.str types.path; +in { + options = { + base = lib.mkOption { + # In newer Nix versions, particularly with lazy trees, outPath of + # flakes becomes a Nix-language path object. We deliberately allow this + # to gracefully come through the interface in discussion with @roberth. + # + # See: https://github.com/NixOS/nixpkgs/pull/278522#discussion_r1460292639 + type = types.nullOr flakeRef; + + default = null; + defaultText = "if (using nixpkgsFlake.lib.nixosSystem) then self.outPath else null"; + + example = lib.literalExpression "inputs.nixpkgs"; + + description = '' + The path to the nixpkgs source used to build a system. A `base` package set + is required to be set, and can be specified via either: + 1. `options.nodes.base` (default `base` used for all systems) + 2. `options.nodes.nodes..base` (takes prescedence over `options.nodes.base`) + + This can also be optionally set if the NixOS system is not built with a flake but still uses + pinned sources: set this to the store path for the nixpkgs sources used to build the system, + as may be obtained by `fetchTarball`, for example. + + Note: the name of the store path must be "source" due to + . + ''; + }; + + modules = mkOption { + type = types.listOf types.raw; + default = []; + example = lib.literalExpression "[ { environment.systemPackages = [ pkgs.git ]; } ]"; + description = '' + Shared modules to import; equivalent to the NixOS module system's `extraModules`. + ''; + }; + + args = mkOption { + type = types.attrs; + default = {}; + example = lib.literalExpression "{ inherit inputs; }"; + description = '' + Shared args to provided for each node; equivalent to the NixOS module system's `specialArgs`. + ''; + }; + + homeManager = mkOption { + type = types.nullOr flakeRef; + default = null; + example = lib.literalExpression "inputs.home-manager"; + description = '' + The path to the home-manager source. A `homeManager` flake reference + is required to be set for `homes/` to be evaluated, and can be specified via either: + 1. `options.nodes.homeManager` (default `homManager` used for all systems) + 2. `options.nodes.nodes..homeManager` (takes prescedence over `options.nodes.homeManager`) + ''; + }; + }; +} diff --git a/cerulean/snow/nodes/submodule.nix b/cerulean/snow/nodes/submodule.nix new file mode 100644 index 0000000..5d7ffe6 --- /dev/null +++ b/cerulean/snow/nodes/submodule.nix @@ -0,0 +1,166 @@ +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{ + inputs, + lib, + ... +}: { + imports = [./shared.nix]; + + options = let + inherit + (lib) + mkOption + types + ; + in { + enabled = lib.mkOption { + type = types.bool; + default = true; + example = true; + description = '' + Whether to enable this node. Nodes are enabled by default. + ''; + }; + + system = types.nullOr mkOption { + type = types.enum inputs.systems; + default = null; + example = "x86_64-linux"; + description = '' + The target system architecture to compile for. + ''; + }; + + groups = mkOption { + type = types.functionTo types.list; + default = []; + example = lib.literalExpression "( groups: [ groups.servers groups.secure-boot ] )"; + description = '' + A function from the `groups` hierarchy to a list of groups this node inherits from. + ''; + }; + + deploy = { + user = mkOption { + type = types.str; + default = "root"; + example = "admin"; + description = '' + The user that the system derivation will be deployed to. The command specified in + `.deploy.sudoCmd` will be used if `.deploy.user` is not the + same as `.deploy.ssh.user` the same as above). + ''; + }; + + sudoCmd = mkOption { + type = types.str; + default = "sudo -u"; + example = "doas -u"; + description = '' + Which sudo command to use. Must accept at least two arguments: + 1. the user name to execute commands as + 2. the rest is the command to execute + ''; + }; + + interactiveSudo = mkOption { + type = types.bool; + default = false; + example = false; + description = '' + Whether to enable interactive sudo (password based sudo). + NOT RECOMMENDED. Use one of Cerulean's recommended auth methods instead. + ''; + }; + + remoteBuild = mkOption { + type = types.bool; + default = false; + example = false; + description = '' + Whether to build the system derivation on the target system. + Will also fetch all external dependencies from the target system's substituters. + ''; + }; + + autoRollback = mkOption { + type = types.bool; + default = true; + example = true; + description = '' + If the previous system derivation should be re-activated if activation fails. + ''; + }; + + activationTimeout = mkOption { + type = types.int; + default = 500; + example = 30; + description = '' + Time window in seconds allowed for system derivation activation. + If timeout occurs, remote deployment is considered to have failed. + ''; + }; + + confirmTimeout = mkOption { + type = types.int; + default = 30; + example = 15; + description = '' + Time window in seconds allowed for activation confirmation. + If timeout occurs, remote deployment is considered to have failed. + ''; + }; + + ssh = { + host = mkOption { + type = types.str; + default = ""; + example = "dobutterfliescry.net"; + description = '' + The host to connect to over ssh during deployment + ''; + }; + + user = mkOption { + type = types.str; + default = "cerubld"; + example = "custom-user"; + description = '' + The user to connect to over ssh during deployment. + ''; + }; + + port = mkOption { + type = types.int; + default = 22; + example = 2222; + description = '' + The port to connect to over ssh during deployment. + ''; + }; + + opts = mkOption { + type = types.listOf types.str; + default = []; + example = ["-i" "~/.ssh/id_rsa"]; + description = '' + Extra ssh arguments to use during deployment. + ''; + }; + }; + }; + }; +} From 43f5c8581ee7701479f82269d314b9ffb834ad4c Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Feb 2026 22:41:17 +1000 Subject: [PATCH 40/68] use lib.optional --- cerulean/nixos/default.nix | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index 02bbe8b..99325ef 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -13,6 +13,7 @@ # limitations under the License. { root, + lib, system, _cerulean, ... @@ -24,11 +25,7 @@ # options declarations (import ./nixpkgs.nix (args // {contextName = "hosts";})) ] - ++ ( - if _cerulean.homeManager != null - then [./home-manager.nix] - else [] - ); + ++ lib.optional (_cerulean.homeManager != null) [./home-manager.nix]; environment.systemPackages = with _cerulean.inputs; [ deploy-rs.packages.${system}.default From e4ab4f4b5a81716fcb01a146c6a36e3bc99c62da Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 19 Feb 2026 22:43:57 +1000 Subject: [PATCH 41/68] update TODO --- TODO.md | 4 ++++ cerulean/nixos/default.nix | 3 +++ 2 files changed, 7 insertions(+) diff --git a/TODO.md b/TODO.md index 355863e..2f4edac 100755 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,9 @@ +## Next - [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` +- [ ] add `options.experimental` for snowflake +- [ ] add `legacyImports` support +## Queued - [X] base should automatically be set as the default (dont do anything with the default) - [X] try to remove common foot guns, ie abort if the user provides the home-manager or microvm nixosModules since cerulean ALREADY provides these diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index 99325ef..ae593ef 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -20,6 +20,9 @@ } @ args: { imports = [ + # add support for `options.legacyImports` + # ./legacy-imports.nix + # user configuration (import (root + "/nixpkgs.nix")) # options declarations From 45c53f025add7c7c9e76e6632877f66d05193bbf Mon Sep 17 00:00:00 2001 From: _cry64 Date: Fri, 20 Feb 2026 02:08:04 +1000 Subject: [PATCH 42/68] support snow.flake --- cerulean/default.nix | 13 ++- cerulean/nixos/default.nix | 6 +- cerulean/nixos/home-manager.nix | 4 + cerulean/nixos/nixpkgs.nix | 12 +- cerulean/snow/default.nix | 176 +++++++++++++++++++++++++++++- cerulean/snow/lib/nodes.nix | 96 ++++++++++++++++ cerulean/snow/module.nix | 23 ++++ cerulean/snow/nodes/default.nix | 51 +++++---- cerulean/snow/nodes/submodule.nix | 11 +- flake.nix | 1 + 10 files changed, 352 insertions(+), 41 deletions(-) create mode 100644 cerulean/snow/lib/nodes.nix create mode 100644 cerulean/snow/module.nix diff --git a/cerulean/default.nix b/cerulean/default.nix index a2dba7d..065621a 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -20,12 +20,10 @@ mix.newMixture args (mixture: { includes.public = [ ./nexus ]; + submods.public = [ + ./snow + ]; - - nixosModules = rec { - default = cerulean; - cerulean = ./nixos; - }; version = "0.2.3"; overlays = [ @@ -33,4 +31,9 @@ mix.newMixture args (mixture: { # hence we can rely on a nixpkg binary cache. inputs.deploy-rs.overlays.default ]; + + nixosModules = rec { + default = cerulean; + cerulean = ./nixos; + }; }) diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index ae593ef..9e28aa5 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -28,7 +28,11 @@ # options declarations (import ./nixpkgs.nix (args // {contextName = "hosts";})) ] - ++ lib.optional (_cerulean.homeManager != null) [./home-manager.nix]; + ++ ( + if _cerulean.homeManager != null + then [./home-manager.nix] + else [] + ); environment.systemPackages = with _cerulean.inputs; [ deploy-rs.packages.${system}.default diff --git a/cerulean/nixos/home-manager.nix b/cerulean/nixos/home-manager.nix index 7c87a0c..8c1aa8b 100644 --- a/cerulean/nixos/home-manager.nix +++ b/cerulean/nixos/home-manager.nix @@ -25,6 +25,10 @@ pathExists ; in { + imports = [ + _cerulean.homeManager.nixosModules.default + ]; + home-manager = { users = config.users.users diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix index f07fc35..03925c8 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -75,19 +75,19 @@ in { # NOTE: _module.args is a special option that allows us to # NOTE: set extend specialArgs from inside the modules. # WARNING: pkgs is a reserved specialArg - _module.args = removeAttrs repos ["pkgs"]; + _module.args = removeAttrs repos ["pkgs" "default"]; nixpkgs = if contextName == "hosts" 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 {}); + flake.source = lib.mkForce base; # DEBUG: temp while getting base to work + overlays = lib.mkForce (defaultPkgs.overlays or {}); + config = lib.mkForce (defaultPkgs.config or {}); } else if contextName == "homes" then { - config = lib.mkOverride 200 (defaultPkgs.config or {}); - overlays = lib.mkOverride 200 (defaultPkgs.overlays or []); + config = lib.mkForce (defaultPkgs.config or {}); + overlays = lib.mkForce (defaultPkgs.overlays or []); } else {}; }; diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix index 1fe87f2..7cd0b18 100644 --- a/cerulean/snow/default.nix +++ b/cerulean/snow/default.nix @@ -11,8 +11,174 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -{...}: { - imports = [ - ./nodes - ]; -} +{ + this, + self, + inputs, + systems, + nt, + mix, + ... +} @ args: let + inherit + (builtins) + all + attrNames + elem + mapAttrs + warn + ; + + inherit (inputs.nixpkgs) lib; + + inherit (nt) findImport; +in + mix.newMixture args (mixture: let + inherit (mixture) mapNodes; + in { + includes.private = [ + ./lib/nodes.nix + ]; + + inherit findImport; + + # snow.flake + flake = flakeInputs: root: let + module = lib.evalModules { + class = "snowflake"; + # TODO: abort if inputs contains reserved names + specialArgs = + flakeInputs + // { + inherit root; + inherit systems; + inherit (this) snow; # please don't be infinite recursion... + inputs = flakeInputs; + }; + + modules = [ + ./module.nix + ]; + }; + + nodes = module.config.nodes; + in rec { + nixosConfigurations = mapNodes nodes ( + { + base, + lib, + name, + node, + groupModules, + ... + }: let + homeManager = + if node.homeManager != null + then node.homeManager + else if nodes.homeManager != null + then nodes.homeManager + else + warn '' + [snowflake] Neither `nodes.homeManager` nor `nodes.nodes.${name}.homeManager` were specified! + [snowflake] home-manager will NOT be used! User configuration will be ignored! + '' + null; + + userArgs = nodes.args // node.args; + ceruleanArgs = { + inherit systems root base; + inherit (node) system; + inherit (this) snow; + + _cerulean = { + inherit inputs userArgs ceruleanArgs homeManager; + specialArgs = userArgs // ceruleanArgs; + }; + }; + specialArgs = assert (userArgs + |> attrNames + |> all (argName: + ! ceruleanArgs ? argName + || abort '' + `specialArgs` are like super important to Cerulean my love... rollback; + magicRollback = magicRollback -> rollback; + activationTimeout = activationTimeout; + confirmTimeout = confirmTimeout; + + remoteBuild = remoteBuild; + sshUser = ssh.user; + sshOpts = + ssh.opts + ++ ( + if elem "-p" ssh.opts + then [] + else ["-p" (toString ssh.port)] + ) + ++ ( + if elem "-A" ssh.opts + then [] + else ["-A"] + ); + }; + }); + + checks = + inputs.deploy-rs.lib + |> mapAttrs (system: deployLib: + deployLib.deployChecks deploy); + }; + }) diff --git a/cerulean/snow/lib/nodes.nix b/cerulean/snow/lib/nodes.nix new file mode 100644 index 0000000..7f1a21b --- /dev/null +++ b/cerulean/snow/lib/nodes.nix @@ -0,0 +1,96 @@ +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{nt, ...}: let + inherit + (builtins) + concatLists + elem + filter + isAttrs + mapAttrs + pathExists + typeOf + ; + + rootGroupName = "all"; + + parseGroupsDecl = groups: let + validGroup = g: + isAttrs g + || throw '' + Cerulean Nexus groups must be provided as attribute sets, got "${typeOf g}" instead! + Ensure all the group definitions are attribute sets under your call to `cerulean.mkNexus`. + NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP` + ''; + delegate = parent: gName: g: let + result = + (g + // { + _name = gName; + _parent = parent; + }) + |> mapAttrs (name: value: + if elem name ["_name" "_parent"] + # ignore metadata fields + then value + else assert validGroup value; (delegate result name value)); + in + result; + in + assert validGroup groups; + delegate null rootGroupName groups; + + getGroupModules = root: groups: + # ensure root group is always added + groups + # add all inherited groups via _parent + |> map (let + delegate = g: + if g._parent == null + then [g] + else [g] ++ delegate (g._parent); + in + delegate) + # flatten recursion result + |> concatLists + # find import location + |> map (group: nt.findImport (root + "/groups/${group._name}")) + # filter by uniqueness + |> nt.prim.unique + # ignore missing groups + |> filter pathExists; +in { + mapNodes = nodes: f: + nodes.nodes + |> mapAttrs (name: node: let + # use per-node base or default to nodes' base + base = + if node.base != null + then node.base + else if nodes.base != null + then nodes.base + else + abort '' + Cerulean cannot construct nodes node "${name}" without a base package source. + Ensure `nodes.nodes.*.base` or `nodes.base` is a flake reference to the github:NixOS/nixpkgs repository. + ''; + in + f rec { + inherit name node base; + inherit (base) lib; + + groups = node.groups (parseGroupsDecl nodes.groups); + groupModules = root: getGroupModules root groups; + }); +} diff --git a/cerulean/snow/module.nix b/cerulean/snow/module.nix new file mode 100644 index 0000000..d45b35a --- /dev/null +++ b/cerulean/snow/module.nix @@ -0,0 +1,23 @@ +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{ + root, + snow, + ... +}: { + imports = [ + ./nodes + (snow.findImport (root + "/snow")) + ]; +} diff --git a/cerulean/snow/nodes/default.nix b/cerulean/snow/nodes/default.nix index c458a41..d3bc9b7 100644 --- a/cerulean/snow/nodes/default.nix +++ b/cerulean/snow/nodes/default.nix @@ -11,7 +11,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -{lib, ...}: { +{ + lib, + specialArgs, + ... +}: { options.nodes = let inherit (lib) @@ -24,26 +28,35 @@ Cerulean node declarations. ''; type = types.submoduleWith { - imports = [./shared.nix]; + inherit specialArgs; - options = { - groups = mkOption { - type = types.attrs; - default = {}; - example = lib.literalExpression "{ servers = { staging = {}; production = {}; }; }"; - description = '' - Hierarchical groups that nodes can be a member of. - ''; - }; + modules = [ + { + imports = [./shared.nix]; - nodes = mkOption { - type = types.attrsOf (types.submoduleWith (import ./submodule.nix)); - # example = { ... }; # TODO - description = '' - Node (host systems) declarations. - ''; - }; - }; + options = { + groups = mkOption { + type = types.attrs; + default = {}; + example = lib.literalExpression "{ servers = { staging = {}; production = {}; }; }"; + description = '' + Hierarchical groups that nodes can be a member of. + ''; + }; + + nodes = mkOption { + type = types.attrsOf (types.submoduleWith { + inherit specialArgs; + modules = [(import ./submodule.nix)]; + }); + # example = { ... }; # TODO + description = '' + Node (host systems) declarations. + ''; + }; + }; + } + ]; }; }; } diff --git a/cerulean/snow/nodes/submodule.nix b/cerulean/snow/nodes/submodule.nix index 5d7ffe6..3f2e59e 100644 --- a/cerulean/snow/nodes/submodule.nix +++ b/cerulean/snow/nodes/submodule.nix @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. { - inputs, lib, + systems, ... }: { imports = [./shared.nix]; @@ -34,8 +34,8 @@ ''; }; - system = types.nullOr mkOption { - type = types.enum inputs.systems; + system = mkOption { + type = types.nullOr (types.enum systems); default = null; example = "x86_64-linux"; description = '' @@ -44,8 +44,9 @@ }; groups = mkOption { - type = types.functionTo types.list; - default = []; + # TODO: write a custom group type that validates better than types.attrs lol + type = types.functionTo (types.listOf types.attrs); + default = groups: []; example = lib.literalExpression "( groups: [ groups.servers groups.secure-boot ] )"; description = '' A function from the `groups` hierarchy to a list of groups this node inherits from. diff --git a/flake.nix b/flake.nix index 0e6d711..f5abab0 100644 --- a/flake.nix +++ b/flake.nix @@ -43,5 +43,6 @@ { inherit inputs self nt; inherit (nt) mix; + systems = import inputs.systems; }; } From be45d2a4d4b1c0e536b75eb0898698fefb5eb093 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 22 Feb 2026 02:39:44 +1000 Subject: [PATCH 43/68] remove legacy nexus --- cerulean/default.nix | 6 +- cerulean/nexus/default.nix | 20 --- cerulean/nexus/nexus.nix | 297 ------------------------------------- cerulean/nexus/nodes.nix | 101 ------------- 4 files changed, 3 insertions(+), 421 deletions(-) delete mode 100644 cerulean/nexus/default.nix delete mode 100644 cerulean/nexus/nexus.nix delete mode 100644 cerulean/nexus/nodes.nix diff --git a/cerulean/default.nix b/cerulean/default.nix index 065621a..47fcdfd 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -17,15 +17,15 @@ ... } @ args: mix.newMixture args (mixture: { - includes.public = [ - ./nexus - ]; submods.public = [ ./snow ]; version = "0.2.3"; + # WARNING: legacy + mkFlake = mixture.snow.flake; + overlays = [ # build deploy-rs as a package not from the flake input, # hence we can rely on a nixpkg binary cache. diff --git a/cerulean/nexus/default.nix b/cerulean/nexus/default.nix deleted file mode 100644 index 7a756aa..0000000 --- a/cerulean/nexus/default.nix +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -{mix, ...} @ args: -mix.newMixture args (mixture: { - includes.public = [ - ./nodes.nix - ./nexus.nix - ]; -}) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix deleted file mode 100644 index 6f79b26..0000000 --- a/cerulean/nexus/nexus.nix +++ /dev/null @@ -1,297 +0,0 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -{ - self, - this, - nt, - inputs, - ... -}: let - inherit - (builtins) - all - attrNames - concatLists - concatStringsSep - elem - filter - getAttr - isAttrs - isFunction - isList - mapAttrs - pathExists - typeOf - ; - - inherit - (this) - mapNodes - ; - - inherit - (nt) - findImport - ; - - templateNexus = let - inherit - (nt.naive.terminal) - Terminal - ; - in { - base = null; - modules = []; - args = Terminal {}; - homeManager = null; - - groups = Terminal {}; - nodes = Terminal {}; - }; - - ROOT_GROUP_NAME = "all"; - - parseGroupDecl = groups: let - validGroup = g: - isAttrs g - || throw '' - Cerulean Nexus groups must be provided as attribute sets, got "${typeOf g}" instead! - Ensure all the group definitions are attribute sets under your call to `cerulean.mkNexus`. - NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP` - ''; - delegate = parent: gName: g: let - result = - (g - // { - _name = gName; - _parent = parent; - }) - |> mapAttrs (name: value: - if elem name ["_name" "_parent"] - # ignore metadata fields - then value - else assert validGroup value; (delegate result name value)); - in - result; - in - assert validGroup groups; - delegate null ROOT_GROUP_NAME groups; - - parseNexus = nexus: - assert isAttrs nexus - || abort '' - 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 - 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 - decl - // { - groups = parseGroupDecl decl.groups; - }; - - parseDecl = outputsBuilder: let - decl = ( - if isFunction outputsBuilder - then outputsBuilder final # provide `self` - else - assert (isAttrs outputsBuilder) - || abort '' - Cerulean declaration must be provided as an attribute set, got "${typeOf outputsBuilder}" instead! - Ensure your declaration is an attribute set or function under your call to `cerulean.mkNexus`. - ''; outputsBuilder - ); - - final = - decl - // { - nexus = parseNexus (decl.nexus or {}); - }; - in - final; - - getGroupModules = root: nodeName: node: - assert isList node.groups - || throw '' - Cerulean Nexus node "${nodeName}" does not declare group membership as a list, got "${typeOf node.groups}" instead! - Ensure `nexus.nodes.${nodeName}.groups` is a list under your call to `cerulean.mkNexus`. - ''; - # ensure root group is always added - (node.groups - ++ [ - { - _parent = null; - _name = ROOT_GROUP_NAME; - } - ]) - # ensure all members are actually groups - |> map (group: let - got = - if ! isAttrs group - then toString group - else - group - |> attrNames - |> map (name: "${name} = <${typeOf (getAttr name group)}>;") - |> concatStringsSep " " - |> (x: "{ ${x} }"); - in - if group ? _name - then group - else - throw '' - Cerulean Nexus node "${nodeName}" is a member of an incorrectly structured group. - Got "${got}" of primitive type "${typeOf group}". - NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP` - '') - # add all inherited groups via _parent - |> map (let - delegate = g: - if g._parent == null - then [g] - else [g] ++ delegate (g._parent); - in - delegate) - # flatten recursion result - |> concatLists - # find import location - |> map (group: findImport (root + "/groups/${group._name}")) - # filter by uniqueness - |> nt.prim.unique - # ignore missing groups - |> filter pathExists; -in { - mkNexus = root: outputsBuilder: let - decl = parseDecl outputsBuilder; - - inherit - (decl) - nexus - ; - customOutputs = removeAttrs decl ["nexus"]; - - outputs = rec { - nixosConfigurations = mapNodes nexus ( - { - base, - lib, - nodeName, - node, - ... - }: 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 homeManager; - specialArgs = userArgs // ceruleanArgs; - }; - }; - specialArgs = assert (userArgs - |> attrNames - |> all (argName: - ! ceruleanArgs ? argName - || abort '' - `specialArgs` are like super important to Cerulean my love... mapAttrs (nodeName: nodeAttrs: let - node = parseNode nodeName nodeAttrs; - - # use per-node base or default to nexus base - base = - if node.base != null - then node.base - else if nexus.base != null - then nexus.base - else - abort '' - Cerulean cannot construct nexus node "${nodeName}" without a base package source. - Ensure `nexus.nodes.*.base` or `nexus.base` is a flake reference to the github:NixOS/nixpkgs repository. - ''; - in - f { - inherit nodeName node base; - inherit (base) lib; - }); -} From 35571f6af1977b8461ba2ce9dc356b39227e1b63 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 22 Feb 2026 02:40:09 +1000 Subject: [PATCH 44/68] add missing deploy.rollback option --- cerulean/snow/nodes/submodule.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cerulean/snow/nodes/submodule.nix b/cerulean/snow/nodes/submodule.nix index 3f2e59e..340a395 100644 --- a/cerulean/snow/nodes/submodule.nix +++ b/cerulean/snow/nodes/submodule.nix @@ -96,6 +96,15 @@ ''; }; + rollback = mkOption { + type = types.bool; + default = true; + example = true; + description = '' + Enables both `autoRollback` and `magicRollback`. + ''; + }; + autoRollback = mkOption { type = types.bool; default = true; From 8928134f12c39bb32bed6d6e5f94df4774eea09a Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 24 Feb 2026 17:28:28 +1000 Subject: [PATCH 45/68] add magicRollback (tf did it go??) --- cerulean/snow/nodes/submodule.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cerulean/snow/nodes/submodule.nix b/cerulean/snow/nodes/submodule.nix index 340a395..ea30c4f 100644 --- a/cerulean/snow/nodes/submodule.nix +++ b/cerulean/snow/nodes/submodule.nix @@ -114,6 +114,15 @@ ''; }; + magicRollback = mkOption { + type = types.bool; + default = true; + example = true; + description = '' + TODO: im fucking lazy + ''; + }; + activationTimeout = mkOption { type = types.int; default = 500; From fe1537735d5ebd8432bace71242a257cd000c1a4 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 26 Feb 2026 14:52:20 +1000 Subject: [PATCH 46/68] fix rename sudo -> sudoCmd --- cerulean/snow/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix index 7cd0b18..b1ea663 100644 --- a/cerulean/snow/default.nix +++ b/cerulean/snow/default.nix @@ -130,7 +130,7 @@ in (node.deploy) ssh user - sudo + sudoCmd interactiveSudo remoteBuild rollback @@ -149,7 +149,7 @@ in path = nixosFor node.system nixosConfigurations.${name}; user = user; - sudo = sudo; + sudo = sudoCmd; interactiveSudo = interactiveSudo; fastConnection = false; From e1f8c6cc3bf7bb3f48f635d6b0c3af59dd2430b1 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 26 Feb 2026 15:21:23 +1000 Subject: [PATCH 47/68] add sops-nix dependency --- cerulean/nixos/default.nix | 6 ++++-- cerulean/snow/default.nix | 2 -- flake.nix | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index 9e28aa5..d585cc5 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -13,12 +13,11 @@ # limitations under the License. { root, - lib, system, _cerulean, ... } @ args: { - imports = + imports = with _cerulean.inputs; [ # add support for `options.legacyImports` # ./legacy-imports.nix @@ -27,6 +26,9 @@ (import (root + "/nixpkgs.nix")) # options declarations (import ./nixpkgs.nix (args // {contextName = "hosts";})) + + sops-nix.nixosModules.sops + # microvm.nixosModules.microvm ] ++ ( if _cerulean.homeManager != null diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix index b1ea663..048572b 100644 --- a/cerulean/snow/default.nix +++ b/cerulean/snow/default.nix @@ -112,8 +112,6 @@ in [ self.nixosModules.default (findImport (root + "/hosts/${name}")) - - # inputs.microvm.nixosModules.microvm ] ++ (groupModules root) ++ node.modules diff --git a/flake.nix b/flake.nix index f5abab0..80faf5c 100644 --- a/flake.nix +++ b/flake.nix @@ -32,6 +32,11 @@ url = "github:microvm-nix/microvm.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; + + sops-nix = { + url = "github:Mic92/sops-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { From 8508b61b9224f409dc85562da4260dfbf9ab52cf Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 26 Feb 2026 15:21:33 +1000 Subject: [PATCH 48/68] update CHANGELOG (todo) --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c7a9a3..dd559bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,3 +23,11 @@ Minor patches - cerulean no longer depends on `nixpkgs`, `base` package set should be set instead - rename `extraModules` -> `modules` - rename `specialArgs` -> `args` + +## v0.2.3-alpha +>[!TODO] +> I've been too focused on upcoming changes... + +## v0.2.4-alpha +- `homeManager` flake reference may now be specified in snowflake +- `` From 0651bd0118e73a5c8a9581dfe0313ecaa5ed1e0a Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 26 Feb 2026 20:43:04 +1000 Subject: [PATCH 49/68] default install sops --- cerulean/nixos/default.nix | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index d585cc5..8d96f08 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -13,6 +13,7 @@ # limitations under the License. { root, + pkgs, system, _cerulean, ... @@ -36,7 +37,11 @@ else [] ); - environment.systemPackages = with _cerulean.inputs; [ - deploy-rs.packages.${system}.default - ]; + environment.systemPackages = + (with pkgs; [ + sops + ]) + ++ (with _cerulean.inputs; [ + deploy-rs.packages.${system}.default + ]); } From 9bab917d8cc3b16f3c73b1980fcb55a4b5ce5239 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Tue, 3 Mar 2026 11:45:15 +1000 Subject: [PATCH 50/68] ignore rust artefacts --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3828286..6cdd71c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /hidden +target/ From 930eafa8189ec040269c9f36a52baa8622ecc65e Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 11:09:00 +1000 Subject: [PATCH 51/68] ignore target --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3828286..6cdd71c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /hidden +target/ From d9432d87a460c4f6dfb2602bba7e98267c2a0132 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 11:17:27 +1000 Subject: [PATCH 52/68] v0.2.4-alpha --- cerulean/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cerulean/default.nix b/cerulean/default.nix index 47fcdfd..502db05 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -21,7 +21,7 @@ mix.newMixture args (mixture: { ./snow ]; - version = "0.2.3"; + version = "0.2.4-alpha"; # WARNING: legacy mkFlake = mixture.snow.flake; From ef5bc338566670380ceec75eb692697c9e81fb7e Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 11:57:49 +1000 Subject: [PATCH 53/68] enable hm --- TODO.md | 3 +++ cerulean/nixos/home-manager.nix | 2 ++ cerulean/nixos/home.nix | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 cerulean/nixos/home.nix diff --git a/TODO.md b/TODO.md index 2f4edac..beb6a6c 100755 --- a/TODO.md +++ b/TODO.md @@ -3,10 +3,13 @@ - [ ] add `options.experimental` for snowflake - [ ] add `legacyImports` support +- [ ] support hs system per dir, ie hosts//overlays or hosts//nixpkgs.nix + ## Queued - [X] base should automatically be set as the default (dont do anything with the default) - [X] try to remove common foot guns, ie abort if the user provides the home-manager or microvm nixosModules since cerulean ALREADY provides these +- [ ] per node home configuration is a lil jank rn - [ ] deploy port should default to the first port given to `services.openssh` diff --git a/cerulean/nixos/home-manager.nix b/cerulean/nixos/home-manager.nix index 8c1aa8b..0927bbf 100644 --- a/cerulean/nixos/home-manager.nix +++ b/cerulean/nixos/home-manager.nix @@ -44,6 +44,8 @@ in { (import (root + "/nixpkgs.nix")) # options declarations (import ./nixpkgs.nix (args // {contextName = "homes";})) + + ./home.nix ]; }; } diff --git a/cerulean/nixos/home.nix b/cerulean/nixos/home.nix new file mode 100644 index 0000000..417e2b7 --- /dev/null +++ b/cerulean/nixos/home.nix @@ -0,0 +1,3 @@ +{...}: { + programs.home-manager.enable = true; # DEBUG: why didn't i enable it already? +} From ba7763801f3bd95b4edeba12fbb8b5c670284aa7 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 16:48:35 +1000 Subject: [PATCH 54/68] use better path notation --- cerulean/nixos/default.nix | 2 +- cerulean/nixos/home-manager.nix | 6 +++--- cerulean/snow/default.nix | 2 +- cerulean/snow/lib/nodes.nix | 2 +- cerulean/snow/module.nix | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index 8d96f08..edc85cd 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -24,7 +24,7 @@ # ./legacy-imports.nix # user configuration - (import (root + "/nixpkgs.nix")) + (import /${root}/nixpkgs.nix) # options declarations (import ./nixpkgs.nix (args // {contextName = "hosts";})) diff --git a/cerulean/nixos/home-manager.nix b/cerulean/nixos/home-manager.nix index 0927bbf..d4d8748 100644 --- a/cerulean/nixos/home-manager.nix +++ b/cerulean/nixos/home-manager.nix @@ -33,15 +33,15 @@ in { users = config.users.users |> attrNames - |> filter (x: pathExists (root + "/homes/${x}")) + |> filter (x: pathExists /${root}/homes/${x}) |> (x: lib.genAttrs x (y: - import (root + "/homes/${y}"))); + import /${root}/homes/${y})); extraSpecialArgs = _cerulean.specialArgs; sharedModules = [ # user configuration - (import (root + "/nixpkgs.nix")) + (import /${root}/nixpkgs.nix) # options declarations (import ./nixpkgs.nix (args // {contextName = "homes";})) diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix index 048572b..bad3c1b 100644 --- a/cerulean/snow/default.nix +++ b/cerulean/snow/default.nix @@ -111,7 +111,7 @@ in modules = [ self.nixosModules.default - (findImport (root + "/hosts/${name}")) + (findImport /${root}/hosts/${name}) ] ++ (groupModules root) ++ node.modules diff --git a/cerulean/snow/lib/nodes.nix b/cerulean/snow/lib/nodes.nix index 7f1a21b..f9b6537 100644 --- a/cerulean/snow/lib/nodes.nix +++ b/cerulean/snow/lib/nodes.nix @@ -65,7 +65,7 @@ # flatten recursion result |> concatLists # find import location - |> map (group: nt.findImport (root + "/groups/${group._name}")) + |> map (group: nt.findImport (/${root}/groups/${group._name}) # filter by uniqueness |> nt.prim.unique # ignore missing groups diff --git a/cerulean/snow/module.nix b/cerulean/snow/module.nix index d45b35a..79b8804 100644 --- a/cerulean/snow/module.nix +++ b/cerulean/snow/module.nix @@ -18,6 +18,6 @@ }: { imports = [ ./nodes - (snow.findImport (root + "/snow")) + (snow.findImport /${root}/snow) ]; } From f985e7ee7049197ca5dc35bcaac900fede642e1c Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 17:54:10 +1000 Subject: [PATCH 55/68] rename channels.default -> channels.base --- cerulean/nixos/nixpkgs.nix | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix index 03925c8..40a3d57 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -31,7 +31,7 @@ in { default = {}; description = "Declare package repositories"; example = { - "pkgs" = { + "npkgs" = { source = "inputs.nixpkgs"; system = "x86-64-linux"; config = { @@ -53,7 +53,7 @@ in { config = let repos = cfg - |> (xs: removeAttrs xs ["default"]) + |> (xs: removeAttrs xs ["base"]) |> mapAttrs ( name: args: lib.mkForce ( @@ -65,30 +65,27 @@ in { ) ); - # 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 = ...;` - ''); + basePkgs = cfg.base or {}; in { # NOTE: _module.args is a special option that allows us to # NOTE: set extend specialArgs from inside the modules. # WARNING: pkgs is a reserved specialArg - _module.args = removeAttrs repos ["pkgs" "default"]; + _module.args = removeAttrs repos ["pkgs" "base"]; - nixpkgs = + nixpkgs = let + nixpkgConfig = { + config = lib.mkForce (basePkgs.config or {}); + overlays = lib.mkForce (basePkgs.overlays or []); + }; + in if contextName == "hosts" - then { - flake.source = lib.mkForce base; # DEBUG: temp while getting base to work - overlays = lib.mkForce (defaultPkgs.overlays or {}); - config = lib.mkForce (defaultPkgs.config or {}); - } + then + nixpkgConfig + // { + flake.source = lib.mkForce base; + } else if contextName == "homes" - then { - config = lib.mkForce (defaultPkgs.config or {}); - overlays = lib.mkForce (defaultPkgs.overlays or []); - } + then nixpkgConfig else {}; }; } From c49fdc9769b8f919206d93746a4e59c036daf9f4 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 18:18:10 +1000 Subject: [PATCH 56/68] rename home-manager.nix -> home.nix add options.users.users..manageHome --- cerulean/nixos/default.nix | 2 +- cerulean/nixos/home-manager.nix | 51 ------------------------ cerulean/nixos/home.nix | 70 ++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 54 deletions(-) delete mode 100644 cerulean/nixos/home-manager.nix diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index edc85cd..664a10c 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -33,7 +33,7 @@ ] ++ ( if _cerulean.homeManager != null - then [./home-manager.nix] + then [./home.nix] else [] ); diff --git a/cerulean/nixos/home-manager.nix b/cerulean/nixos/home-manager.nix deleted file mode 100644 index d4d8748..0000000 --- a/cerulean/nixos/home-manager.nix +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -{ - root, - config, - lib, - _cerulean, - ... -} @ args: let - inherit - (builtins) - attrNames - filter - pathExists - ; -in { - imports = [ - _cerulean.homeManager.nixosModules.default - ]; - - home-manager = { - users = - config.users.users - |> attrNames - |> filter (x: pathExists /${root}/homes/${x}) - |> (x: - lib.genAttrs x (y: - import /${root}/homes/${y})); - - extraSpecialArgs = _cerulean.specialArgs; - sharedModules = [ - # user configuration - (import /${root}/nixpkgs.nix) - # options declarations - (import ./nixpkgs.nix (args // {contextName = "homes";})) - - ./home.nix - ]; - }; -} diff --git a/cerulean/nixos/home.nix b/cerulean/nixos/home.nix index 417e2b7..a63b6e8 100644 --- a/cerulean/nixos/home.nix +++ b/cerulean/nixos/home.nix @@ -1,3 +1,69 @@ -{...}: { - programs.home-manager.enable = true; # DEBUG: why didn't i enable it already? +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{ + root, + config, + lib, + _cerulean, + ... +} @ args: let + inherit + (builtins) + attrNames + filter + pathExists + ; +in { + imports = [ + _cerulean.homeManager.nixosModules.default + ]; + + options = { + users.users = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule { + options.manageHome = lib.mkOption { + type = lib.types.bool; + default = true; + example = false; + description = '' + Whether Cerulean should automatically enable home-manager for this user, + and manage their home configuration declaratively. + + Enabled by default, but can be disabled if necessary. + ''; + }; + }); + }; + }; + + config = { + home-manager = { + users = + config.users.users + |> attrNames + |> filter (x: pathExists (root + "/homes/${x}")) + |> (x: + lib.genAttrs x (y: + import (root + "/homes/${y}"))); + + extraSpecialArgs = _cerulean.specialArgs; + sharedModules = [ + # user configuration + (import (root + "/nixpkgs.nix")) + # options declarations + (import ./nixpkgs.nix (args // {contextName = "homes";})) + ]; + }; + }; } From 23449396f7e9a9ffc07ed1f72691bc33997bb88d Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 18:18:17 +1000 Subject: [PATCH 57/68] clean TODO --- TODO.md | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index beb6a6c..a9f40e1 100755 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,4 @@ ## Next -- [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` - [ ] add `options.experimental` for snowflake - [ ] add `legacyImports` support @@ -26,29 +25,19 @@ - [ ] go through all flake inputs (recursively) and ENSURE we remove all duplicates by using follows!! -- [X] rename nixos-modules/ to nixos/ -- [X] ensure all machines are in groups.all by default - -- [X] fix nixpkgs.nix not working (default not respected) -- [X] remove dependence on nixpkgs - - [ ] allow multiple privesc methods, the standard is pam_ssh_agent_auth ## Low Priority -- [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 it then returns instead of config) +- [ ] support `legacyImports` (?) - [ ] patch microvm so that acpi=off https://github.com/microvm-nix/microvm.nix/commit/b59a26962bb324cc0a134756a323f3e164409b72 cause otherwise 2GB causes a failure -- [ ] rewrite the ceru cli in rust -- [ ] make `ceru` do local and remote deployments +- [ ] write the cerulean cli -- [ ] support `legacyImports` ```nix # REF: foxora From 169bf2bf482cbdedeabddc566f55ef54bea0182d Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 18:52:06 +1000 Subject: [PATCH 58/68] ACTUALLY use users.users..manageHome --- cerulean/nixos/home.nix | 8 +++++--- cerulean/snow/lib/nodes.nix | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cerulean/nixos/home.nix b/cerulean/nixos/home.nix index a63b6e8..0e91e8b 100644 --- a/cerulean/nixos/home.nix +++ b/cerulean/nixos/home.nix @@ -52,15 +52,17 @@ in { users = config.users.users |> attrNames - |> filter (x: pathExists (root + "/homes/${x}")) + |> filter (x: x.manageHome && pathExists /${root}/homes/${x}) |> (x: lib.genAttrs x (y: - import (root + "/homes/${y}"))); + import /${root}/homes/${y})); extraSpecialArgs = _cerulean.specialArgs; sharedModules = [ + ../home + # user configuration - (import (root + "/nixpkgs.nix")) + (import /${root}/nixpkgs.nix) # options declarations (import ./nixpkgs.nix (args // {contextName = "homes";})) ]; diff --git a/cerulean/snow/lib/nodes.nix b/cerulean/snow/lib/nodes.nix index f9b6537..48a583d 100644 --- a/cerulean/snow/lib/nodes.nix +++ b/cerulean/snow/lib/nodes.nix @@ -65,7 +65,7 @@ # flatten recursion result |> concatLists # find import location - |> map (group: nt.findImport (/${root}/groups/${group._name}) + |> map (group: nt.findImport /${root}/groups/${group._name}) # filter by uniqueness |> nt.prim.unique # ignore missing groups From 34a8c23537ddf0c0efd8793c4225dae97e913e13 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 19:29:26 +1000 Subject: [PATCH 59/68] provide per-user args (ie username) --- cerulean/nixos/home.nix | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/cerulean/nixos/home.nix b/cerulean/nixos/home.nix index 0e91e8b..0a3b983 100644 --- a/cerulean/nixos/home.nix +++ b/cerulean/nixos/home.nix @@ -12,18 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. { - root, - config, - lib, _cerulean, + config, + root, + lib, ... } @ args: let inherit (builtins) - attrNames - filter pathExists ; + + inherit + (lib) + filterAttrs + mapAttrs + ; in { imports = [ _cerulean.homeManager.nixosModules.default @@ -49,13 +53,21 @@ in { config = { home-manager = { + useUserPackages = lib.mkDefault false; + useGlobalPkgs = lib.mkDefault true; + + overwriteBackup = lib.mkDefault false; + backupFileExtension = lib.mkDefault "bak"; + users = config.users.users - |> attrNames - |> filter (x: x.manageHome && pathExists /${root}/homes/${x}) - |> (x: - lib.genAttrs x (y: - import /${root}/homes/${y})); + |> filterAttrs (name: value: value.manageHome && pathExists /${root}/homes/${name}) + |> mapAttrs (name: _: { + imports = [import /${root}/homes/${name}]; + + # per-user arguments + _module.args.username = name; + }); extraSpecialArgs = _cerulean.specialArgs; sharedModules = [ From 1c68485dcf0ba4e813a4b72a4f3d71f88137f6c2 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 19:39:57 +1000 Subject: [PATCH 60/68] cerulean now manages trivial home-manager options --- cerulean/home/default.nix | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 cerulean/home/default.nix diff --git a/cerulean/home/default.nix b/cerulean/home/default.nix new file mode 100644 index 0000000..8ae23c6 --- /dev/null +++ b/cerulean/home/default.nix @@ -0,0 +1,20 @@ +# NOTE: you can access the system configuration via the `osConfig` arg +{ + username, + lib, + ... +}: { + # WARNING: required for home-manager to work + programs.home-manager.enable = true; # user must apply lib.mkForce + # Nicely reload systemd units when changing configs + systemd.user.startServices = lib.mkDefault "sd-switch"; + + home = { + username = lib.mkDefault username; + homeDirectory = lib.mkDefault "/home/${username}"; + + sessionVariables = { + NIX_SHELL_PRESERVE_PROMPT = lib.mkDefault 1; + }; + }; +} From 39ec2e62d07d2173373db7b59be24851f7104bb9 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 20:10:36 +1000 Subject: [PATCH 61/68] fix nixpkgs disabled when home-manager.useGlobalPkgs --- cerulean/nixos/home.nix | 5 ++--- cerulean/nixos/nixpkgs.nix | 18 +++++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cerulean/nixos/home.nix b/cerulean/nixos/home.nix index 0a3b983..82117d8 100644 --- a/cerulean/nixos/home.nix +++ b/cerulean/nixos/home.nix @@ -62,8 +62,8 @@ in { users = config.users.users |> filterAttrs (name: value: value.manageHome && pathExists /${root}/homes/${name}) - |> mapAttrs (name: _: { - imports = [import /${root}/homes/${name}]; + |> mapAttrs (name: _: {...}: { + imports = [/${root}/homes/${name}]; # per-user arguments _module.args.username = name; @@ -73,7 +73,6 @@ in { sharedModules = [ ../home - # 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 40a3d57..946748b 100644 --- a/cerulean/nixos/nixpkgs.nix +++ b/cerulean/nixos/nixpkgs.nix @@ -73,19 +73,23 @@ in { _module.args = removeAttrs repos ["pkgs" "base"]; nixpkgs = let - nixpkgConfig = { + nixpkgsConfig = { config = lib.mkForce (basePkgs.config or {}); overlays = lib.mkForce (basePkgs.overlays or []); }; - in - if contextName == "hosts" - then - nixpkgConfig + + nixpkgsHostsConfig = + nixpkgsConfig // { flake.source = lib.mkForce base; - } + }; + + nixpkgsHomesConfig = lib.mkIf (!config.home-manager.useGlobalPkgs) nixpkgsConfig; + in + if contextName == "hosts" + then nixpkgsHostsConfig else if contextName == "homes" - then nixpkgConfig + then nixpkgsHomesConfig else {}; }; } From aec16966ae8b5be33c3739ab1c58921eaeb9e29f Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 20:21:26 +1000 Subject: [PATCH 62/68] add missing license --- cerulean/home/default.nix | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cerulean/home/default.nix b/cerulean/home/default.nix index 8ae23c6..e854221 100644 --- a/cerulean/home/default.nix +++ b/cerulean/home/default.nix @@ -1,9 +1,23 @@ -# NOTE: you can access the system configuration via the `osConfig` arg +# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. { username, lib, ... }: { + # NOTE: you can access the system configuration via the `osConfig` arg + # WARNING: required for home-manager to work programs.home-manager.enable = true; # user must apply lib.mkForce # Nicely reload systemd units when changing configs From b486ee8cb765f967fc0067356a13d453e7ab768b Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 23:46:02 +1000 Subject: [PATCH 63/68] propagate node config and hostname to nixos modules --- TODO.md | 3 --- cerulean/snow/default.nix | 11 +++++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index a9f40e1..5fff9a2 100755 --- a/TODO.md +++ b/TODO.md @@ -5,9 +5,6 @@ - [ ] support hs system per dir, ie hosts//overlays or hosts//nixpkgs.nix ## Queued -- [X] base should automatically be set as the default (dont do anything with the default) -- [X] try to remove common foot guns, ie abort if the user provides the home-manager or microvm nixosModules - since cerulean ALREADY provides these - [ ] per node home configuration is a lil jank rn - [ ] deploy port should default to the first port given to `services.openssh` diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix index bad3c1b..c1199a6 100644 --- a/cerulean/snow/default.nix +++ b/cerulean/snow/default.nix @@ -86,9 +86,10 @@ in userArgs = nodes.args // node.args; ceruleanArgs = { - inherit systems root base; + inherit systems root base node; inherit (node) system; inherit (this) snow; + hostname = name; _cerulean = { inherit inputs userArgs ceruleanArgs homeManager; @@ -128,7 +129,6 @@ in (node.deploy) ssh user - sudoCmd interactiveSudo remoteBuild rollback @@ -140,14 +140,17 @@ in nixosFor = system: inputs.deploy-rs.lib.${system}.activate.nixos; in { - hostname = ssh.host; + hostname = + if ssh.host != null + then ssh.host + else ""; profilesOrder = ["default"]; # profiles priority profiles.default = { path = nixosFor node.system nixosConfigurations.${name}; user = user; - sudo = sudoCmd; + sudo = "sudo -u"; interactiveSudo = interactiveSudo; fastConnection = false; From 6b579dff1e78038c7c62851854e0d95ba38c172f Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 23:46:51 +1000 Subject: [PATCH 64/68] set hostname by default --- cerulean/nixos/default.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index 664a10c..09975e5 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -37,6 +37,8 @@ else [] ); + networking.hostName = lib.mkDefault hostname; + environment.systemPackages = (with pkgs; [ sops From 630389a5989c15d92d883d62862850316322f8fa Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sat, 7 Mar 2026 23:48:38 +1000 Subject: [PATCH 65/68] migrate to cerubld user --- cerulean/nixos/default.nix | 27 +++++---- cerulean/nixos/remote-deploy/default.nix | 72 ++++++++++++++++++++++++ cerulean/snow/nodes/submodule.nix | 39 +++++++++---- 3 files changed, 116 insertions(+), 22 deletions(-) create mode 100644 cerulean/nixos/remote-deploy/default.nix diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix index 09975e5..a716c2f 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos/default.nix @@ -13,29 +13,32 @@ # limitations under the License. { root, - pkgs, system, + hostname, + node, + pkgs, + lib, _cerulean, ... } @ args: { - imports = with _cerulean.inputs; + imports = [ + _cerulean.inputs.sops-nix.nixosModules.sops + # _cerulean.inputs.microvm.nixosModules.microvm + # add support for `options.legacyImports` # ./legacy-imports.nix - # user configuration - (import /${root}/nixpkgs.nix) - # options declarations + # nixos options declarations (import ./nixpkgs.nix (args // {contextName = "hosts";})) - sops-nix.nixosModules.sops - # microvm.nixosModules.microvm + # user's nixpkg configuration + (import /${root}/nixpkgs.nix) ] - ++ ( - if _cerulean.homeManager != null - then [./home.nix] - else [] - ); + # homemanager options declarations + ++ (lib.optional (_cerulean.homeManager != null) ./home.nix) + # remote deployment configuration + ++ (lib.optional (node.deploy.ssh.host != null) ./remote-deploy); networking.hostName = lib.mkDefault hostname; diff --git a/cerulean/nixos/remote-deploy/default.nix b/cerulean/nixos/remote-deploy/default.nix new file mode 100644 index 0000000..f2f4a90 --- /dev/null +++ b/cerulean/nixos/remote-deploy/default.nix @@ -0,0 +1,72 @@ +{ + config, + node, + lib, + pkgs, + hostname, + ... +}: let + user = node.deploy.ssh.user; + cfg = config.users.users.${user}; + + DEFAULT_USER = "cerubld"; + + isStandardDeployUser = user == DEFAULT_USER; +in { + assertions = [ + { + assertion = builtins.length node.deploy.ssh.publicKeys != 0; + message = '' + The Cerulean deployment user `${user}` for node `${hostname}` must have at least + one publicKey authorized for ssh deployment! Try setting `nodes.nodes..deploy.ssh.publicKeys = [ ... ]` <3 + ''; + } + { + assertion = cfg.isSystemUser && !cfg.isNormalUser; + message = '' + The Cerulean deployment user `${user}` for node `${hostname}` has been configured incorrectly. + Ensure `users.users.${user}.isSystemUser == true` and `users.users.${user}.isNormalUser == false`. + ''; + } + ]; + + warnings = lib.optional (node.deploy.warnNonstandardDeployUser && !isStandardDeployUser) '' + The Cerulean deplyment user `${user}` for node `${hostname}` has been overriden. + It is recommended to leave this user as `${DEFAULT_USER}` unless you TRULY understand what you are doing! + This message can be disabled by setting `.deploy.warnNonstandardBuildUser = false`. + ''; + + # prefer sudo-rs over sudo + security.sudo-rs = { + enable = true; + wheelNeedsPassword = true; + + # allow the build user to run nix commands + extraRules = [ + { + users = [user]; + runAs = "${node.deploy.user}:ALL"; + commands = [ + "${pkgs.nix}/bin/nix" + ]; + } + ]; + }; + + # ensure deployment user has SSH permissions + services.openssh.settings.AllowUsers = [user]; + + users = lib.mkIf isStandardDeployUser { + groups.${user} = {}; + + users.${user} = { + enable = true; + isSystemUser = true; + group = user; + description = "Cerulean's user for building and remote deployment."; + + shell = pkgs.bash; + openssh.authorizedKeys.keys = node.deploy.ssh.publicKeys; + }; + }; +} diff --git a/cerulean/snow/nodes/submodule.nix b/cerulean/snow/nodes/submodule.nix index ea30c4f..6b4ae05 100644 --- a/cerulean/snow/nodes/submodule.nix +++ b/cerulean/snow/nodes/submodule.nix @@ -59,23 +59,32 @@ default = "root"; example = "admin"; description = '' - The user that the system derivation will be deployed to. The command specified in + The user that the system derivation will be built with. The command specified in `.deploy.sudoCmd` will be used if `.deploy.user` is not the same as `.deploy.ssh.user` the same as above). ''; }; - sudoCmd = mkOption { - type = types.str; - default = "sudo -u"; - example = "doas -u"; + warnNonstandardDeployUser = mkOption { + type = types.bool; + default = true; + example = false; description = '' - Which sudo command to use. Must accept at least two arguments: - 1. the user name to execute commands as - 2. the rest is the command to execute + Disables the warning that shows when `deploy.ssh.user` is set to a non-standard value. ''; }; + # sudoCmd = mkOption { + # type = types.str; + # default = "sudo -u"; + # example = "doas -u"; + # description = '' + # Which sudo command to use. Must accept at least two arguments: + # 1. the user name to execute commands as + # 2. the rest is the command to execute + # ''; + # }; + interactiveSudo = mkOption { type = types.bool; default = false; @@ -145,8 +154,8 @@ ssh = { host = mkOption { - type = types.str; - default = ""; + type = types.nullOr types.str; + default = null; example = "dobutterfliescry.net"; description = '' The host to connect to over ssh during deployment @@ -171,6 +180,16 @@ ''; }; + publicKeys = mkOption { + type = types.listOf types.str; + default = []; + example = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIeyZuUUmyUYrYaEJwEMvcXqZFYm1NaZab8klOyK6Imr me@puter"]; + description = '' + SSH public keys that will be authorized to the deployment user. + This key is intended solely for deployment, allowing for fine-grained permission control. + ''; + }; + opts = mkOption { type = types.listOf types.str; default = []; From 02ded5d4f0d53eaeb68f1b68469926fd21acc6db Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 8 Mar 2026 02:21:51 +1000 Subject: [PATCH 66/68] TEMP fix for cerubld not having permissions --- cerulean/nixos/remote-deploy/default.nix | 30 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/cerulean/nixos/remote-deploy/default.nix b/cerulean/nixos/remote-deploy/default.nix index f2f4a90..4aa39fd 100644 --- a/cerulean/nixos/remote-deploy/default.nix +++ b/cerulean/nixos/remote-deploy/default.nix @@ -21,13 +21,13 @@ in { one publicKey authorized for ssh deployment! Try setting `nodes.nodes..deploy.ssh.publicKeys = [ ... ]` <3 ''; } - { - assertion = cfg.isSystemUser && !cfg.isNormalUser; - message = '' - The Cerulean deployment user `${user}` for node `${hostname}` has been configured incorrectly. - Ensure `users.users.${user}.isSystemUser == true` and `users.users.${user}.isNormalUser == false`. - ''; - } + # { + # assertion = cfg.isSystemUser && !cfg.isNormalUser; + # message = '' + # The Cerulean deployment user `${user}` for node `${hostname}` has been configured incorrectly. + # Ensure `users.users.${user}.isSystemUser == true` and `users.users.${user}.isNormalUser == false`. + # ''; + # } ]; warnings = lib.optional (node.deploy.warnNonstandardDeployUser && !isStandardDeployUser) '' @@ -47,12 +47,16 @@ in { users = [user]; runAs = "${node.deploy.user}:ALL"; commands = [ - "${pkgs.nix}/bin/nix" + # "${pkgs.nix}/bin/nix" + "ALL" # XXX: WARNING: FIX: TODO: DO NOT FUCKING USE `ALL` ]; } ]; }; + # XXX: WARNING: FIX: TODO: use `trusted-public-keys` instead + nix.settings.trusted-users = [user]; + # ensure deployment user has SSH permissions services.openssh.settings.AllowUsers = [user]; @@ -61,11 +65,17 @@ in { users.${user} = { enable = true; - isSystemUser = true; - group = user; description = "Cerulean's user for building and remote deployment."; + isSystemUser = true; + group = user; + + createHome = true; + home = "/var/lib/cerulean/cerubld"; + + useDefaultShell = false; shell = pkgs.bash; + openssh.authorizedKeys.keys = node.deploy.ssh.publicKeys; }; }; From 902f9d75082772a98a745de5c747f0d27f8c53e3 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 8 Mar 2026 17:13:37 +1000 Subject: [PATCH 67/68] provide systems and snow as flake inputs --- cerulean/snow/default.nix | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix index c1199a6..6993ff1 100644 --- a/cerulean/snow/default.nix +++ b/cerulean/snow/default.nix @@ -48,16 +48,22 @@ in class = "snowflake"; # TODO: abort if inputs contains reserved names specialArgs = - flakeInputs - // { - inherit root; - inherit systems; - inherit (this) snow; # please don't be infinite recursion... - inputs = flakeInputs; - }; + (flakeInputs + // { + inherit systems root; + inherit (this) snow; + inputs = flakeInputs; + }) + |> (x: builtins.removeAttrs x ["self" "nodes"]); modules = [ ./module.nix + ({config, ...}: { + _module.args = { + self = config; + nodes = config.nodes.nodes; + }; + }) ]; }; @@ -86,7 +92,7 @@ in userArgs = nodes.args // node.args; ceruleanArgs = { - inherit systems root base node; + inherit systems root base nodes node; inherit (node) system; inherit (this) snow; hostname = name; From 6c7f335fbde5b6f1931d79d287bf03ee642ba7f5 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Sun, 8 Mar 2026 17:14:06 +1000 Subject: [PATCH 68/68] features for v0.2.6-alpha --- TODO.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO.md b/TODO.md index 5fff9a2..7a23e43 100755 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,7 @@ ## Next +- [ ] formalize how the snow flake system compiles outputs, this would remove the need for `mapNodes` +- [ ] groups should allow you to set node configuration defaults + - [ ] add `options.experimental` for snowflake - [ ] add `legacyImports` support