diff --git a/TODO.md b/TODO.md index a12b8c3..c43e8c6 100755 --- a/TODO.md +++ b/TODO.md @@ -1,58 +1,5 @@ -- [ ] deploy port should default to the first port given to `services.openssh` +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` -- [ ] 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!! - -- [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? - -- [ ] 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) - - -- [ ] 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 - -- [ ] rewrite the ceru cli in rust -- [ ] make `ceru` do local and remote deployments - -```nix -# REF: foxora -vms = { - home-assistant = { - autostart = true; - # matches in vms/* - image = "home-assistant"; - options = { - mem = 2048; - }; - }; - equinox = { - image = "home-assistant"; - }; -}; -``` +Using `flake-parts` ensures Cerulean is usable without restricting +yourself only to the Cerulean ecosystem. diff --git a/cerulean/default.nix b/cerulean/default.nix index f24bb5a..68846bf 100644 --- a/cerulean/default.nix +++ b/cerulean/default.nix @@ -13,24 +13,23 @@ # limitations under the License. { mix, - inputs, + deploy-rs, ... -} @ args: -mix.newMixture args (mixture: { +} @ inputs: +mix.newMixture inputs (mixture: { includes.public = [ ./nexus ]; - version = "0.2.1"; - - nixosModules = rec { - default = cerulean; - cerulean = ./nixos; - }; - 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; + # }; + # }) ]; }) diff --git a/cerulean/nexus/default.nix b/cerulean/nexus/default.nix index 65495bf..d8e0f7c 100644 --- a/cerulean/nexus/default.nix +++ b/cerulean/nexus/default.nix @@ -11,8 +11,8 @@ # 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: { +{mix, ...} @ inputs: +mix.newMixture inputs (mixture: { includes.public = [ ./nodes.nix ./nexus.nix diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 24a0b75..8b65fdd 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -14,8 +14,11 @@ { self, this, + nixpkgs, + nixpkgs-unstable, nt, - inputs, + lib, + deploy-rs, ... }: let inherit @@ -39,22 +42,20 @@ mapNodes ; - inherit - (nt) - findImport - ; - templateNexus = let inherit (nt.naive.terminal) Terminal ; - in { - base = null; - extraModules = []; - specialArgs = 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 { groups = Terminal {}; + overlays = []; nodes = Terminal {}; }; @@ -65,8 +66,7 @@ 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` + Ensure all the `groups` definitions are attribute sets under your call to `cerulean.mkNexus`. ''; delegate = parent: gName: g: let result = @@ -90,7 +90,7 @@ 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`. + Ensure all the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`. ''; let base = nt.projectOnto templateNexus nexus; in @@ -121,56 +121,11 @@ 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; + # XXX: TODO: create a function in NixTypes that handles this instead + findImport = path: + if pathExists path + then path + else path + ".nix"; in { mkNexus = root: outputsBuilder: let decl = parseDecl outputsBuilder; @@ -182,47 +137,118 @@ in { customOutputs = removeAttrs decl ["nexus"]; outputs = rec { - nixosConfigurations = mapNodes nexus ( - { - lib, - nodeName, - node, - ... - }: let - nixosDecl = lib.nixosSystem { + nixosConfigurations = mapNodes nexus.nodes ( + nodeName: node: + lib.nixosSystem { system = node.system; - specialArgs = let - specialArgs = - nexus.specialArgs - // node.specialArgs - // { - inherit root specialArgs; - inherit (node) system; - _deploy-rs = inputs.deploy-rs; - }; + modules = let + host = findImport (root + "/hosts/${nodeName}"); + # XXX: TODO: don't use a naive type for this (ie _name property) + # XXX: TODO: i really need NixTypes to be stable and use that instead + # groups = + # node.groups + # |> map (group: + # assert group ? _name + # || throw (let + # got = + # if ! isAttrs group + # then toString group + # else + # group + # |> attrNames + # |> map (name: "${name} = <${typeOf (getAttr name group)}>;") + # |> concatStringsSep " " + # |> (x: "{ ${x} }"); + # in '' + # Cerulean Nexus node "${nodeName}" is a member of a nonexistent group. + # Got "${got}" of primitive type "${typeOf group}". + # NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP` + # ''); + # findImport (root + "/groups/${group._name}")) + # |> nt.prim.unique; # filter by uniqueness + groups = 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: let + got = + if ! isAttrs g + then toString g + else + g + |> attrNames + |> map (name: "${name} = <${typeOf (getAttr name g)}>;") + |> concatStringsSep " " + |> (x: "{ ${x} }"); + in + assert g ? _parent + || throw '' + Cerulean Nexus node "${nodeName}" is a member of an incorrectly structured group. + Got "${got}" of primitive type "${typeOf g}". + NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP` + ''; + 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 - specialArgs; - modules = - [ - self.nixosModules.default - (findImport (root + "/hosts/${nodeName}")) + [../nixos-module host] ++ groups ++ node.extraModules; - inputs.home-manager.nixosModules.default - # inputs.microvm.nixosModules.microvm - ] - ++ (getGroupModules root nodeName node) - ++ node.extraModules - ++ nexus.extraModules; - }; - in - nixosDecl + # nix passes these to every single module + specialArgs = let + pkgConfig = + { + inherit (node) system; + # XXX: WARNING: TODO: i've stopped caring + # XXX: WARNING: TODO: just figure out a better solution to pkgConfig + config.allowUnfree = true; + overlays = self.overlays ++ nexus.overlays ++ node.overlays; + } + // node.extraPkgConfig; + in + node.specialArgs + // { + inherit root; + pkgs = import nixpkgs pkgConfig; + upkgs = import nixpkgs-unstable pkgConfig; + }; + } ); - deploy.nodes = mapNodes nexus ({ - nodeName, - node, - ... - }: let + deploy.nodes = mapNodes nexus.nodes (nodeName: node: let inherit (node.deploy) activationTimeout @@ -236,7 +262,7 @@ in { user ; - nixosFor = system: inputs.deploy-rs.lib.${system}.activate.nixos; + nixosFor = system: deploy-rs.lib.${system}.activate.nixos; in { hostname = ssh.host; @@ -272,7 +298,7 @@ in { }; }); - checks = mapAttrs (system: deployLib: deployLib.deployChecks deploy) inputs.deploy-rs.lib; + 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 index ccb5c20..25a1d02 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -25,20 +25,14 @@ 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 = missing "its system architecture" "system"; + system = "x86_64-linux"; # sane default (i hope...) groups = []; extraModules = []; specialArgs = Terminal {}; - - base = null; + overlays = []; + # XXX: WARNING: extraPkgConfig is a terrible solution (but im lazy for now) + extraPkgConfig = Terminal {}; deploy = { user = "root"; @@ -75,25 +69,7 @@ in rec { in nt.projectOnto templateAttrs 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; - }); + mapNodes = nodes: f: + nodes + |> mapAttrs (nodeName: nodeAttrs: f nodeName (parseNode nodeName nodeAttrs)); } diff --git a/cerulean/nexus/snow.nix b/cerulean/nexus/snow.nix deleted file mode 100644 index 1316ee3..0000000 --- a/cerulean/nexus/snow.nix +++ /dev/null @@ -1,108 +0,0 @@ -# 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-parent.nix b/cerulean/nixos-module/config.nix similarity index 98% rename from cerulean/nixos/microvm-parent.nix rename to cerulean/nixos-module/config.nix index 2b6a12e..26d459c 100644 --- a/cerulean/nixos/microvm-parent.nix +++ b/cerulean/nixos-module/config.nix @@ -11,3 +11,4 @@ # 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-child.nix b/cerulean/nixos-module/default.nix similarity index 85% rename from cerulean/nixos/microvm-child.nix rename to cerulean/nixos-module/default.nix index 2b6a12e..cc92212 100644 --- a/cerulean/nixos/microvm-child.nix +++ b/cerulean/nixos-module/default.nix @@ -11,3 +11,8 @@ # 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: { + options = import ./options.nix inputs; + + config = import ./config.nix inputs; +} diff --git a/cerulean/nixos/default.nix b/cerulean/nixos-module/options.nix similarity index 64% rename from cerulean/nixos/default.nix rename to cerulean/nixos-module/options.nix index f15b236..8fb6a46 100644 --- a/cerulean/nixos/default.nix +++ b/cerulean/nixos-module/options.nix @@ -11,22 +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, - 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 - ]; +{lib, ...}: { + cerulean.nexus.node = { + group = lib.mkOption { + type = lib.types.enum; + }; + }; } diff --git a/cerulean/nixos/home-manager.nix b/cerulean/nixos/home-manager.nix deleted file mode 100644 index 21ee78c..0000000 --- a/cerulean/nixos/home-manager.nix +++ /dev/null @@ -1,50 +0,0 @@ -# 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, - system, - config, - lib, - specialArgs, - ... -} @ args: let - inherit - (builtins) - attrNames - filter - pathExists - ; -in { - config = { - home-manager = { - users = - config.users.users - |> attrNames - |> filter (x: pathExists (root + "/homes/${x}")) - |> (x: lib.genAttrs x (y: import (root + "/homes/${y}"))); - - extraSpecialArgs = {inherit root system;} // (specialArgs.inputs or {}); - 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; - }; - }; -} diff --git a/cerulean/nixos/nixpkgs.nix b/cerulean/nixos/nixpkgs.nix deleted file mode 100644 index 65db5a5..0000000 --- a/cerulean/nixos/nixpkgs.nix +++ /dev/null @@ -1,101 +0,0 @@ -# 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; - }; - }; - }; - }; - }; - - 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 ( - assert args ? source - || abort '' - ${toString ./.} - `nixpkgs.channels.${contextName}.${name}` missing required attribute "source" - ''; - ((removeAttrs args ["source"]) - // {inherit system;}) - |> import args.source - ) - ); - 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 - 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.mkOverride 200 defaultPkgs.source; - config = lib.mkOverride 200 defaultPkgs.config; - } - 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 {}; - }; -} diff --git a/flake.lock b/flake.lock index 0dd06b1..e9a0074 100644 --- a/flake.lock +++ b/flake.lock @@ -36,71 +36,43 @@ "type": "github" } }, - "flake-parts": { + "mix": { "inputs": { - "nixpkgs-lib": [ - "nt", - "nix-unit", - "nixpkgs" + "nib": [ + "nib" ] }, "locked": { - "lastModified": 1762440070, - "narHash": "sha256-xxdepIcb39UJ94+YydGP221rjnpkDZUlykKuF54PsqI=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "26d05891e14c88eb4a5d5bee659c0db5afb609d8", + "lastModified": 1768525804, + "narHash": "sha256-jlpNb7Utqfdq2HESAB1mtddWHOsxKlTjPiLFRLd35r8=", + "owner": "emilelcb", + "repo": "mix", + "rev": "617d8915a6518a3d4e375b87c50ae34d9daee6c6", "type": "github" }, "original": { - "owner": "hercules-ci", - "repo": "flake-parts", + "owner": "emilelcb", + "repo": "mix", "type": "github" } }, - "nix-github-actions": { + "nib": { "inputs": { - "nixpkgs": [ - "nt", - "nix-unit", - "nixpkgs" + "systems": [ + "systems" ] }, "locked": { - "lastModified": 1737420293, - "narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=", - "owner": "nix-community", - "repo": "nix-github-actions", - "rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9", + "lastModified": 1768472076, + "narHash": "sha256-bdVRCDy6oJx/CZiyxkke783FgtBW//wDuOAITUsQcNc=", + "owner": "emilelcb", + "repo": "nib", + "rev": "42ac66dfc180a13af1cc8850397db66ec5556991", "type": "github" }, "original": { - "owner": "nix-community", - "repo": "nix-github-actions", - "type": "github" - } - }, - "nix-unit": { - "inputs": { - "flake-parts": "flake-parts", - "nix-github-actions": "nix-github-actions", - "nixpkgs": [ - "nt", - "nixpkgs" - ], - "treefmt-nix": "treefmt-nix" - }, - "locked": { - "lastModified": 1762774186, - "narHash": "sha256-hRADkHjNt41+JUHw2EiSkMaL4owL83g5ZppjYUdF/Dc=", - "owner": "nix-community", - "repo": "nix-unit", - "rev": "1c9ab50554eed0b768f9e5b6f646d63c9673f0f7", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nix-unit", + "owner": "emilelcb", + "repo": "nib", "type": "github" } }, @@ -152,49 +124,14 @@ "type": "github" } }, - "nixpkgs_3": { - "locked": { - "lastModified": 1767313136, - "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-25.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nt": { - "inputs": { - "nix-unit": "nix-unit", - "nixpkgs": "nixpkgs_3", - "systems": "systems_2" - }, - "locked": { - "lastModified": 1770975056, - "narHash": "sha256-ZXTz/P3zUbbM6lNXzt91u8EwfNqhXpYMu8+wvFZqQHE=", - "owner": "cry128", - "repo": "nt", - "rev": "f42dcdd49a7921a7f433512e83d5f93696632412", - "type": "github" - }, - "original": { - "owner": "cry128", - "repo": "nt", - "type": "github" - } - }, "root": { "inputs": { "deploy-rs": "deploy-rs", + "mix": "mix", + "nib": "nib", "nixpkgs": "nixpkgs_2", "nixpkgs-unstable": "nixpkgs-unstable", - "nt": "nt", - "systems": "systems_3" + "systems": "systems_2" } }, "systems": { @@ -227,43 +164,6 @@ "type": "github" } }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": [ - "nt", - "nix-unit", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1762410071, - "narHash": "sha256-aF5fvoZeoXNPxT0bejFUBXeUjXfHLSL7g+mjR/p5TEg=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "97a30861b13c3731a84e09405414398fbf3e109f", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - }, "utils": { "inputs": { "systems": "systems" diff --git a/flake.nix b/flake.nix index 89ce6b4..94f4b33 100644 --- a/flake.nix +++ b/flake.nix @@ -17,36 +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"; + nt.url = "github:emilelcb/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"; - }; + deploy-rs.url = "github:serokell/deploy-rs"; }; outputs = { - self, + nixpkgs, nt, ... } @ inputs: import ./cerulean - { - inherit inputs self nt; - inherit (nt) mix; - }; + (inputs + // { + inherit (nixpkgs) lib; + inherit (nt) mix; + }); } diff --git a/lib/cdesktop.nix b/lib/cdesktop.nix new file mode 100644 index 0000000..ef2b205 --- /dev/null +++ b/lib/cdesktop.nix @@ -0,0 +1,149 @@ +# 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. +{ + lib, + config, + pkgs, + pkgs-unstable, + ... +} @ args: let + getModule = name: "../modules/homemanager/${name}.nix"; + getModules = map (x: getModule x); +in { + imports = getModules [ + "term/foot" + "editor/vscode" + + "wm/hyprland" + "wm/hyprland/hyprlock" + + "dm/sddm" + "dm/sddm/themes/corners" + + "apps/firefox" + "apps/thunderbird" + "apps/obsidian" + "apps/rider" + "apps/winbox" + "apps/gitkraken" + "apps/thunar" + + "wm/kanshi" + "wm/mako" + ]; + + home = { + pointerCursor = { + gtk.enable = true; + # x11.enable = true # dont enable since im on hyprland + package = pkgs.bibata-cursors; + name = "Bibata-Modern-Ice"; + size = 16; + }; + + packages = with pkgs; [ + # for services.gnome-keyring + ( + if config.cerulean.isGraphical + then seahorse # gui + else null + ) + + fuzzel + ]; + }; + + gtk = { + enable = true; + font.name = "Victor Mono SemiBold 12"; + theme = { + name = "Dracula"; + package = pkgs.dracula-theme; + }; + iconTheme = { + name = "kora"; + package = pkgs.kora-icon-theme; + }; + # TODO: use a variable to mirror this cursor size + # with the `home.pointerCurser.size` + cursorTheme = { + package = pkgs.bibata-cursors; + name = "Bibata-Modern-Ice"; + size = 16; + }; + }; + + qt = { + enable = true; + platformTheme.name = "gtk2"; + style.name = "gtk2"; + }; + + services = { + # Set display manager (login screen) + displayManager = { + # sddm relies on pkgs.libsForQt5.qt5.qtgraphicaleffects + sddm = { + enable = true; + wayland.enable = true; # experimental + theme = "corners"; + }; + defaultSession = + "hyprland" + + ( + if config.programs.hyprland.withUWSM + then "-uwsm" + else null + ); + }; + + # Multimedia Framework + # With backwards compatability for alsa/pulseaudio/jack + pipewire = { + enable = true; + wireplumber.enable = true; + + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + jack.enable = true; + }; + }; + + # ---- ENVIRONMENT ---- + environment = { + sessionVariables = { + # Hint Electron apps to use support Wayland + NIXOS_OZONE_WL = "1"; + }; + }; + + # ---- SYSTEM PACKAGES ---- + environment.systemPackages = with pkgs; [ + # User Environment + swww + helvum + easyeffects + pavucontrol + hyprpicker # colour picking utility + hyprshot # screenshot utility + qbittorrent + signal-desktop # MAKE THIS ONLY FOR THE DESKTOP FOR END USERS, NOT SERVERS + kdePackages.gwenview # image viewer + libreoffice + wl-clipboard # clipboard for wayland + ]; + + security.rtkit.enable = true; # I *think* this is for pipewire +} diff --git a/lib/csystem.nix b/lib/csystem.nix new file mode 100644 index 0000000..64e3aa3 --- /dev/null +++ b/lib/csystem.nix @@ -0,0 +1,330 @@ +# 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. +{ + inputs, + lib, + config, + pkgs, + pkgs-unstable, + homemanager, + cerulean, + ... +} @ args: let + getModule = name: "../modules/nixos/${name}.nix"; + getModules = map (x: getModule x); + + getHostModule = name: "TODO"; +in { + imports = getModules [ + (getHostModule "hardware-configuration") + (import "${homemanager}/nixos") + + "shell/bash" + "shell/bash/bashistrans.nix" + "shell/zsh" + "shell/fish" + + "cli/git" + "cli/bat" + "cli/btop" + "cli/tmux" + "cli/nvim" + + "lang/asm" + "lang/bash" # TODO: (YES THIS IS DIFFERENT TO shell/bash, this provides language support ie pkgs.shellcheck) + "lang/c-family" + "lang/dotnet" + # "lang/go" + # "lang/haskell" + # "lang/java" + # "lang/nim" + "lang/python" + # "lang/rust" + # "lang/sage" + + "editor/helix" + ]; + + nix.settings = { + # REF: https://nix.dev/manual/nix/2.24/development/experimental-features + experimental-features = [ + # Significant + "flakes" + "nix-command" + "pipe-operators" + + # Minor + "no-url-literals" + "parse-toml-timestamps" + "recursive-nix" + ]; + + download-buffer-size = 524288000; # 500 MiB + + # making wheel group members "trusted users" allows + # them to import packages not signed by a trusted key + # (aka super duper easier to remote deploy) + trusted-users = ["root" "@wheel"]; + }; + + nixpkgs = { + overlays = cerulean.lib.importOverlaysNixOS; + + config = if config.cerulean.allowUnfreeWhitelist != [] + then { + allowUnfreePredicate = + pkg: builtins.elem + (lib.getName pkg) + config.cerulean.allowUnfreeWhitelist; + } + else { + allowUnfree = config.cerulean.allowUnfree; + }; + }; + + # colmena deployment configuration + deployment = { + targetHost = config.cerulean.domain ?? config.cerulean.ip; + targetUser = "cerulean"; + targetPort = "22"; + sshOptions = [ + "-A" # forward ssh-agent + ]; + buildOnTarget = false; # build locally then deploy + }; + + + time.timeZone = config.cerulean.timeZone; + i18n.defaultLocale = "en_US.UTF-8"; + + # Enable initrd hook for virtual console customisation + # aka cool colours when booting yay!! + console = { + enable = true; + earlySetup = true; # initrd pre hook + keyMap = "us"; + font = "Lat2-Terminus16"; + # ANSI 24-bit color definitions (theme: dracula) + colors = [ + "21222c" + "ff5555" + "50fa7b" + "f1fa8c" + "bd93f9" + "ff79c6" + "8be9fd" + "f8f8f2" + "6272a4" + "ff6e6e" + "69ff94" + "ffffa5" + "d6acff" + "ff92df" + "a4ffff" + "ffffff" + ]; + }; + + # super duper minimum grub2 config + boot.loader = { + efi = { + canTouchEfiVariables = true; + efiSysMountPoint = "/boot/efi"; + }; + + grub = { + enable = true; + device = "nodev"; + }; + + # GitHub: vinceliuice/grub2-themes + grub2-theme = { + enable = true; + theme = "whitesur"; # stylish, vimix, or whitesur + footer = true; + # TODO: switch my cables to switch default grub display + customResolution = "3840x2160"; + }; + }; + + networking = { + hostName = config.cerulean.hostname; + networkmanager.enable = true; + + firewall = { + enable = true; + allowedTCPPorts = [ + 22 # sshd + 80 # nginx (http) + 443 # nginx (https) + # 5678 # MikroTik WinBox + ]; + }; + }; + + # ------- USERS ------- + security.sudo.wheelNeedsPassword = true; + users = { + defaultUserShell = pkgs.bash; + + users = cerulean.lib.importUsersNixOS; + }; + + home-manager = { + users = cerulean.lib.importUsersHomeManager; + + extraSpecialArgs = { inherit inputs pkgs pkgs-unstable; }; + sharedModules = []; + }; + + # ---- ENVIRONMENT ---- + environment = { + # always install "dev"/"man" derivation outputs + extraOutputsToInstall = ["dev" "man"]; + + systemPackages = with pkgs; [ + # User Environment + bluetui + + # Shell + bash + fish + shellcheck + grc # colorise command outputs + moreutils + + # Systems Programming & Compilation + qemu # Fellice Bellard's Quick Emulator + # GNU Utils + gnumake + # Binaries + binutils + strace + ltrace + perf-tools # ftrace + perf + radare2 + gdb + # ASM + nasm + (callPackage ../packages/x86-manpages {}) + # C Family + gcc + clang + clang-tools + + # Rust + cargo + rustc + # Go + go + # Nim + nim + nimble + # Haskell + ghc + ghcid + haskell-language-server + ormolu + + # Python + python312 # I use 3.12 since it's in a pretty stable state now + python314 # also 3.14 for latest features + poetry + + openvpn + inetutils + + # security tools + nmap + + httpie + curlie + zoxide + doggo + tldr + btop + eza + yazi + lazygit + ripgrep + viddy # modern `watch` command + thefuck + + # TODO: once upgraded past Nix-24.07 this line won't be necessary (I think) + # helix will support nixd by default + # SOURCE: https://github.com/nix-community/nixd/blob/main/nixd/docs/editor-setup.md#Helix + # nixd # lsp for nix # DEBUG + + # Pretty necessary + nix-prefetch-git + brightnessctl + acpi + powertop + imagemagick + + # "Standard" Unix Commands + vim + file + wget + tree + pstree + unzip + unrar-free + lz4 + man-pages + man-pages-posix + + # Cryptography + gnupg + openssl + libargon2 + ]; + }; + + programs = { + nix-ld.enable = true; + }; + + documentation = { + enable = true; + doc.enable = true; # install /share/doc packages + man.enable = true; # install manpages + info.enable = true; # install GNU info + dev.enable = true; # install docs intended for developers + nixos = { + enable = true; # install NixOS documentation (ie man -k nix, & nixos-help) + options.splitBuild = true; + # includeAllModules = true; + }; + }; + + virtualisation.docker.enable = true; + + + hardware = { + graphics = { + enable = true; + enable32Bit = true; + }; + + bluetooth = let + btSupported = config.cerulean.bluetoothSupported; + in { + enable = btSupported; + powerOnBoot = btSupported; + }; + }; + + system.stateVersion = config.cerulean.stateVersion; # DO NOT MODIFY +} diff --git a/lib/cuser.nix b/lib/cuser.nix new file mode 100644 index 0000000..f602e03 --- /dev/null +++ b/lib/cuser.nix @@ -0,0 +1,102 @@ +# 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. +{ + lib, + config, + pkgs, + pkgs-unstable, + ... +} @ args: let + getModule = name: "../modules/homemanager/${name}.nix"; + getModules = map (x: getModule x); +in { + imports = getModules [ + "shell/fish" + + "cli/git" + "cli/bat" + "cli/btop" + "cli/tmux" + + "editor/helix" + ]; + + nixpkgs.config.allowUnfreePredicate = pkg: + builtins.elem (lib.GetName pkg) [ + "vscode-extension-ms-dotnettools-csharp" + ]; + + home = { + stateVersion = config.cerulean.stateVersion; # DO NOT MODIFY + + username = config.cerulean.username; + homeDirectory = "/home/${config.cerulean.username}"; + + shellAliases = { + rg = "batgrep"; # bat + ripgrep + man = "batman"; # bat + man + }; + + sessionVariables = { + NIX_SHELL_PRESERVE_PROMPT = 1; + }; + + packages = with pkgs; [ + # for services.gnome-keyring + gcr # provides org.gnome.keyring.SystemPrompter + speedtest-cli + ]; + }; + + programs = { + home-manager.enable = true; + + zsh = { + enable = true; + enableCompletion = true; + autosuggestion.enable = true; + syntaxHighlighting.enable = true; + + history = { + size = 10000; + ignoreAllDups = true; + path = "$HOME/.zsh_history"; + ignorePatterns = [ + "rm *" + ]; + }; + }; + + # set ssh profiles + # NOTE: (IMPORTANT) this DOES NOT start the ssh-agent + # for that you need to use `services.ssh-agent.enable` + ssh = { + enable = true; + forwardAgent = false; + addKeysToAgent = "no"; + }; + }; + + services = { + # enable OpenSSH private key agent + ssh-agent.enable = true; + + gnome-keyring.enable = true; + }; + + # the ssh-agent won't set this for itself... + systemd.user.sessionVariables.SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/ssh-agent"; + # Nicely reload system units when changing configs + systemd.user.startServices = "sd-switch"; +}