diff --git a/.gitignore b/.gitignore index 6cdd71c..3828286 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /hidden -target/ diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index dd559bf..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,33 +0,0 @@ -# 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` - -## 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 -- `` diff --git a/NOTICE b/NOTICE deleted file mode 100644 index f85b4fa..0000000 --- a/NOTICE +++ /dev/null @@ -1,26 +0,0 @@ -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/README.md b/README.md index b1362ae..b62396e 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,19 @@ -![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 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 -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! +> 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! ## 💙 Same Colour, More Control ->[!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, +Cerulean is what you wish Azure could be. 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... diff --git a/TODO.md b/TODO.md index 7a23e43..25f6f1b 100755 --- a/TODO.md +++ b/TODO.md @@ -1,57 +1,8 @@ -## 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 +Allow `Cerulean.mkNexus` to be an alias for `flake-parts.lib.mkFlake` +also rename `Cerulean` to `cerulean` in Nix to maintain the naming convention. -- [ ] add `options.experimental` for snowflake -- [ ] add `legacyImports` support +Using `flake-parts` ensures Cerulean is usable without restricting +yourself only to the Cerulean ecosystem. -- [ ] support hs system per dir, ie hosts//overlays or hosts//nixpkgs.nix - -## Queued -- [ ] per node home configuration is a lil jank rn - -- [ ] deploy port should default to the first port given to `services.openssh` - -- [ ] 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 - -- [ ] 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!! - -- [ ] allow multiple privesc methods, the standard is pam_ssh_agent_auth - -## Low Priority -- [ ] 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 - -- [ ] write the cerulean cli - - -```nix -# REF: foxora -vms = { - home-assistant = { - autostart = true; - # matches in vms/* - image = "home-assistant"; - options = { - mem = 2048; - }; - }; - equinox = { - image = "home-assistant"; - }; -}; -``` +- [ ] extend the options.nixpkgs to allow any number of package repositories!! +- [x] auto-propagate the same specialArgs for hosts to home-manager diff --git a/ceru/ceru b/ceru/ceru index 5630489..f407960 100755 --- a/ceru/ceru +++ b/ceru/ceru @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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 ed1b6c3..95aba51 100755 --- a/ceru/libceru.sh +++ b/ceru/libceru.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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 e193e83..0b5aa13 100755 --- a/ceru/subcmds/new/cache-key +++ b/ceru/subcmds/new/cache-key @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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 88175fa..af34920 100755 --- a/ceru/subcmds/new/default.sh +++ b/ceru/subcmds/new/default.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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 232539b..d5cd795 100755 --- a/ceru/subcmds/new/password +++ b/ceru/subcmds/new/password @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# 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. diff --git a/ceru/subcmds/new/ssh-key b/ceru/subcmds/new/ssh-key index 651aadb..d0aa524 100755 --- a/ceru/subcmds/new/ssh-key +++ b/ceru/subcmds/new/ssh-key @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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 bab6773..2efa85c 100755 --- a/ceru/subcmds/new/wg-key +++ b/ceru/subcmds/new/wg-key @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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 80240c0..d7797c4 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -1,4 +1,4 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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. @@ -13,27 +13,30 @@ # limitations under the License. { mix, - inputs, + deploy-rs, ... } @ args: mix.newMixture args (mixture: { - submods.public = [ - ./snow + includes.public = [ + ./nexus ]; - version = "0.2.5-alpha"; + version = "0.2.0"; - # WARNING: legacy - mkFlake = mixture.snow.flake; + nixosModules = rec { + default = cerulean; + cerulean = ./nixos-module; + }; overlays = [ # build deploy-rs as a package not from the flake input, # hence we can rely on a nixpkg binary cache. - inputs.deploy-rs.overlays.default + deploy-rs.overlays.default + # (self: super: { + # deploy-rs = { + # inherit (super) deploy-rs; + # lib = super.deploy-rs.lib; + # }; + # }) ]; - - nixosModules = rec { - default = cerulean; - cerulean = ./nixos; - }; }) diff --git a/cerulean/home/default.nix b/cerulean/home/default.nix deleted file mode 100644 index e854221..0000000 --- a/cerulean/home/default.nix +++ /dev/null @@ -1,34 +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. -{ - 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 - 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; - }; - }; -} diff --git a/cerulean/snow/module.nix b/cerulean/nexus/default.nix similarity index 79% rename from cerulean/snow/module.nix rename to cerulean/nexus/default.nix index 79b8804..65495bf 100644 --- a/cerulean/snow/module.nix +++ b/cerulean/nexus/default.nix @@ -1,4 +1,4 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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. @@ -11,13 +11,10 @@ # 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) +{mix, ...} @ args: +mix.newMixture args (mixture: { + includes.public = [ + ./nodes.nix + ./nexus.nix ]; -} +}) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix new file mode 100644 index 0000000..7466bb8 --- /dev/null +++ b/cerulean/nexus/nexus.nix @@ -0,0 +1,263 @@ +# Copyright 2025 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, + lib, + deploy-rs, + ... +}: let + inherit + (builtins) + 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 + ; + + 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 { + extraModules = []; + specialArgs = Terminal {}; + + 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 all the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`. + ''; let + base = 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 + // { + groups = parseGroupDecl base.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`. + ''; + node.groups + # 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.nodes ( + nodeName: node: let + nixosDecl = lib.nixosSystem { + system = node.system; + specialArgs = let + specialArgs = + nexus.specialArgs + // node.specialArgs + // { + inherit root specialArgs; + inherit (node) system; + _deploy-rs = deploy-rs; + }; + in + specialArgs; + modules = + [self.nixosModules.default (findImport (root + "/hosts/${nodeName}"))] + ++ (getGroupModules root nodeName node) + ++ node.extraModules + ++ nexus.extraModules; + }; + in + nixosDecl + ); + + deploy.nodes = mapNodes nexus.nodes (nodeName: node: let + inherit + (node.deploy) + activationTimeout + autoRollback + confirmTimeout + interactiveSudo + magicRollback + remoteBuild + ssh + sudo + user + ; + + nixosFor = system: deploy-rs.lib.${system}.activate.nixos; + in { + hostname = ssh.host; + + profilesOrder = ["default"]; # profiles priority + profiles.default = { + path = nixosFor node.system nixosConfigurations.${nodeName}; + + user = user; + sudo = sudo; + interactiveSudo = interactiveSudo; + + fastConnection = false; + + autoRollback = autoRollback; + magicRollback = magicRollback; + 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 = mapAttrs (system: deployLib: deployLib.deployChecks deploy) deploy-rs.lib; + }; + in + outputs // customOutputs; +} diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix new file mode 100644 index 0000000..3d49434 --- /dev/null +++ b/cerulean/nexus/nodes.nix @@ -0,0 +1,73 @@ +# Copyright 2025 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) + isAttrs + mapAttrs + typeOf + ; +in rec { + # abstract node instance that stores all default values + templateNode = name: system: let + inherit + (nt.naive.terminal) + Terminal + ; + in { + system = "x86_64-linux"; # sane default (i hope...) + groups = []; + extraModules = []; + specialArgs = Terminal {}; + overlays = []; + + deploy = { + user = "root"; + sudo = "sudo -u"; + interactiveSudo = false; + + remoteBuild = false; # prefer local builds for remote deploys + + autoRollback = true; # reactivate previous profile if activation fails + magicRollback = true; + + activationTimeout = 500; # timeout in seconds for profile activation + confirmTimeout = 30; # timeout in seconds for profile activation confirmation + + ssh = { + host = name; + user = "ceru-build"; # ceru-build is the default connection user + port = 22; + opts = []; + }; + }; + }; + + parseNode = name: nodeAttrs: + if !(isAttrs nodeAttrs) + then + # fail if node is not an attribute set + abort '' + Cerulean Nexus nodes must be provided as an attribute set, got "${typeOf nodeAttrs}" instead! + Ensure all `cerulean.nexus.nodes.${name}` declarations are attribute sets under your call to `cerulean.mkNexus`. + '' + else let + templateAttrs = templateNode name nodeAttrs.system; + in + nt.projectOnto templateAttrs nodeAttrs; + + mapNodes = nodes: f: + nodes + |> mapAttrs (nodeName: nodeAttrs: f nodeName (parseNode nodeName nodeAttrs)); +} diff --git a/cerulean/nixos/microvm-child.nix b/cerulean/nixos-module/default.nix similarity index 59% rename from cerulean/nixos/microvm-child.nix rename to cerulean/nixos-module/default.nix index d13e217..f15b236 100644 --- a/cerulean/nixos/microvm-child.nix +++ b/cerulean/nixos-module/default.nix @@ -1,4 +1,4 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# 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. @@ -11,3 +11,22 @@ # 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, + system, + _deploy-rs, + ... +} @ args: { + imports = [ + # user configuration + (import (root + "/nixpkgs.nix")) + # options declarations + (import ./nixpkgs.nix (args // {contextName = "hosts";})) + + ./home-manager.nix + ]; + + environment.systemPackages = [ + _deploy-rs.packages.${system}.default + ]; +} diff --git a/cerulean/nixos-module/home-manager.nix b/cerulean/nixos-module/home-manager.nix new file mode 100644 index 0000000..e693ac9 --- /dev/null +++ b/cerulean/nixos-module/home-manager.nix @@ -0,0 +1,49 @@ +# 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. +{ + root, + config, + lib, + specialArgs, + ... +} @ args: let + inherit + (builtins) + attrNames + filter + pathExists + ; +in { + 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";})) + ]; + + # disable home-manager trying anything fancy + # we control the pkgs now!! + # useGlobalPkgs = true; + }; +} diff --git a/cerulean/nixos-module/nixpkgs.nix b/cerulean/nixos-module/nixpkgs.nix new file mode 100644 index 0000000..c5a97f7 --- /dev/null +++ b/cerulean/nixos-module/nixpkgs.nix @@ -0,0 +1,114 @@ +# 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. +{ + lib, + system, + config, + contextName, + ... +}: let + inherit + (builtins) + mapAttrs + ; + + cfg = config.nixpkgs.channels; +in { + options.nixpkgs.channels = lib.mkOption { + type = lib.types.attrsOf (lib.types.attrs); + default = {}; + description = "Declare package repositories per module context (nixos, home-manager, etc)"; + example = { + "homes" = { + "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; + }; + }; + }; + }; + }; + + # 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}` + decl = + cfg.${contextName} or cfg.default; + + repos = + decl + |> mapAttrs ( + name: args: + lib.mkForce ( + # builtins.trace "SAVE ME GOT NAME: ${name}" ( + assert args ? source + || abort '' + ${toString ./.} + `nixpkgs.channels.${contextName}.${name}` missing required attribute "source" + ''; + ((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 = + if contextName == "hosts" + then {flake.source = lib.mkIf (decl ? pkgs) (lib.mkOverride 200 decl.pkgs.source);} + else if contextName == "homes" + then { + config = decl.pkgs.config or {}; + overlays = decl.pkgs.overlays or {}; + } + else {}; + }; +} diff --git a/cerulean/nixos/default.nix b/cerulean/nixos/default.nix deleted file mode 100644 index a716c2f..0000000 --- a/cerulean/nixos/default.nix +++ /dev/null @@ -1,52 +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, - system, - hostname, - node, - pkgs, - lib, - _cerulean, - ... -} @ args: { - imports = - [ - _cerulean.inputs.sops-nix.nixosModules.sops - # _cerulean.inputs.microvm.nixosModules.microvm - - # add support for `options.legacyImports` - # ./legacy-imports.nix - - # nixos options declarations - (import ./nixpkgs.nix (args // {contextName = "hosts";})) - - # user's nixpkg configuration - (import /${root}/nixpkgs.nix) - ] - # 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; - - environment.systemPackages = - (with pkgs; [ - sops - ]) - ++ (with _cerulean.inputs; [ - deploy-rs.packages.${system}.default - ]); -} diff --git a/cerulean/nixos/home.nix b/cerulean/nixos/home.nix deleted file mode 100644 index 82117d8..0000000 --- a/cerulean/nixos/home.nix +++ /dev/null @@ -1,82 +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. -{ - _cerulean, - config, - root, - lib, - ... -} @ args: let - inherit - (builtins) - pathExists - ; - - inherit - (lib) - filterAttrs - mapAttrs - ; -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 = { - useUserPackages = lib.mkDefault false; - useGlobalPkgs = lib.mkDefault true; - - overwriteBackup = lib.mkDefault false; - backupFileExtension = lib.mkDefault "bak"; - - users = - config.users.users - |> filterAttrs (name: value: value.manageHome && pathExists /${root}/homes/${name}) - |> mapAttrs (name: _: {...}: { - imports = [/${root}/homes/${name}]; - - # per-user arguments - _module.args.username = name; - }); - - extraSpecialArgs = _cerulean.specialArgs; - sharedModules = [ - ../home - - (import /${root}/nixpkgs.nix) - # options declarations - (import ./nixpkgs.nix (args // {contextName = "homes";})) - ]; - }; - }; -} diff --git a/cerulean/nixos/microvm-parent.nix b/cerulean/nixos/microvm-parent.nix deleted file mode 100644 index d13e217..0000000 --- a/cerulean/nixos/microvm-parent.nix +++ /dev/null @@ -1,13 +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. diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix deleted file mode 100644 index 946748b..0000000 --- a/cerulean/nixos/nixpkgs.nix +++ /dev/null @@ -1,95 +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. -{ - base, - lib, - system, - config, - contextName, - ... -}: let - inherit - (builtins) - mapAttrs - ; - - cfg = config.nixpkgs.channels; -in { - options.nixpkgs.channels = lib.mkOption { - type = lib.types.attrs; - default = {}; - description = "Declare package repositories"; - example = { - "npkgs" = { - source = "inputs.nixpkgs"; - 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 - repos = - cfg - |> (xs: removeAttrs xs ["base"]) - |> mapAttrs ( - name: args: - lib.mkForce ( - assert args ? source - || abort '' - `nixpkgs.channels.${name}` missing required attribute "source" - ''; - import args.source ({inherit system;} // (removeAttrs args ["source"])) - ) - ); - - 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" "base"]; - - nixpkgs = let - nixpkgsConfig = { - config = lib.mkForce (basePkgs.config or {}); - overlays = lib.mkForce (basePkgs.overlays or []); - }; - - 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 nixpkgsHomesConfig - else {}; - }; -} diff --git a/cerulean/nixos/remote-deploy/default.nix b/cerulean/nixos/remote-deploy/default.nix deleted file mode 100644 index 4aa39fd..0000000 --- a/cerulean/nixos/remote-deploy/default.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ - 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" - "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]; - - users = lib.mkIf isStandardDeployUser { - groups.${user} = {}; - - users.${user} = { - enable = true; - 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; - }; - }; -} diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix deleted file mode 100644 index 6993ff1..0000000 --- a/cerulean/snow/default.nix +++ /dev/null @@ -1,191 +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. -{ - 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 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; - }; - }) - ]; - }; - - 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 nodes node; - inherit (node) system; - inherit (this) snow; - hostname = name; - - _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 deleted file mode 100644 index 48a583d..0000000 --- a/cerulean/snow/lib/nodes.nix +++ /dev/null @@ -1,96 +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. -{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/nodes/default.nix b/cerulean/snow/nodes/default.nix deleted file mode 100644 index d3bc9b7..0000000 --- a/cerulean/snow/nodes/default.nix +++ /dev/null @@ -1,62 +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. -{ - lib, - specialArgs, - ... -}: { - options.nodes = let - inherit - (lib) - mkOption - types - ; - in - mkOption { - description = '' - Cerulean node declarations. - ''; - type = types.submoduleWith { - inherit specialArgs; - - modules = [ - { - 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 { - inherit specialArgs; - modules = [(import ./submodule.nix)]; - }); - # example = { ... }; # TODO - description = '' - Node (host systems) declarations. - ''; - }; - }; - } - ]; - }; - }; -} diff --git a/cerulean/snow/nodes/shared.nix b/cerulean/snow/nodes/shared.nix deleted file mode 100644 index c840d22..0000000 --- a/cerulean/snow/nodes/shared.nix +++ /dev/null @@ -1,82 +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. -{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 deleted file mode 100644 index 6b4ae05..0000000 --- a/cerulean/snow/nodes/submodule.nix +++ /dev/null @@ -1,204 +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. -{ - lib, - systems, - ... -}: { - 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 = mkOption { - type = types.nullOr (types.enum systems); - default = null; - example = "x86_64-linux"; - description = '' - The target system architecture to compile for. - ''; - }; - - groups = mkOption { - # 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. - ''; - }; - - deploy = { - user = mkOption { - type = types.str; - default = "root"; - example = "admin"; - description = '' - 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). - ''; - }; - - warnNonstandardDeployUser = mkOption { - type = types.bool; - default = true; - example = false; - description = '' - 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; - 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. - ''; - }; - - rollback = mkOption { - type = types.bool; - default = true; - example = true; - description = '' - Enables both `autoRollback` and `magicRollback`. - ''; - }; - - autoRollback = mkOption { - type = types.bool; - default = true; - example = true; - description = '' - If the previous system derivation should be re-activated if activation fails. - ''; - }; - - magicRollback = mkOption { - type = types.bool; - default = true; - example = true; - description = '' - TODO: im fucking lazy - ''; - }; - - 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.nullOr types.str; - default = null; - 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. - ''; - }; - - 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 = []; - example = ["-i" "~/.ssh/id_rsa"]; - description = '' - Extra ssh arguments to use during deployment. - ''; - }; - }; - }; - }; -} diff --git a/flake.lock b/flake.lock index fc8e402..0dd06b1 100644 --- a/flake.lock +++ b/flake.lock @@ -3,9 +3,7 @@ "deploy-rs": { "inputs": { "flake-compat": "flake-compat", - "nixpkgs": [ - "nixpkgs" - ], + "nixpkgs": "nixpkgs", "utils": "utils" }, "locked": { @@ -60,27 +58,6 @@ "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": [ @@ -128,6 +105,38 @@ } }, "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=", @@ -143,7 +152,7 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgs_3": { "locked": { "lastModified": 1767313136, "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=", @@ -162,7 +171,7 @@ "nt": { "inputs": { "nix-unit": "nix-unit", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs_3", "systems": "systems_2" }, "locked": { @@ -182,28 +191,12 @@ "root": { "inputs": { "deploy-rs": "deploy-rs", - "microvm": "microvm", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", + "nixpkgs-unstable": "nixpkgs-unstable", "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 80faf5c..ff430bc 100644 --- a/flake.nix +++ b/flake.nix @@ -1,4 +1,4 @@ -# Copyright 2025-2026 _cry64 (Emile Clark-Boman) +# Copyright 2025 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. @@ -17,37 +17,23 @@ inputs = { systems.url = "github:nix-systems/default"; - # 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"; + nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; nt.url = "github:cry128/nt"; - deploy-rs = { - url = "github:serokell/deploy-rs"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - - microvm = { - url = "github:microvm-nix/microvm.nix"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - - sops-nix = { - url = "github:Mic92/sops-nix"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + deploy-rs.url = "github:serokell/deploy-rs"; }; outputs = { - self, + nixpkgs, nt, ... } @ inputs: import ./cerulean - { - inherit inputs self nt; - inherit (nt) mix; - systems = import inputs.systems; - }; + (inputs + // { + inherit (nixpkgs) lib; + inherit (nt) mix; + }); }