diff --git a/README.md b/README.md index 2f38a3a..897db65 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,25 @@ lynx aims to have similar goals to nixpkgs, providing documentation, testing, an }; outputs = inputs@{self, parts, nixpkgs, lynx, ...}: - parts.lib.mkFlake { inherit inputs; } + let + lynx' = import lynx.lib { flake-parts-lib=parts.lib; }; + # mkFlake with config.assertions and + # config.warnings support + ## parts.lib.mkFlake can be used instead aswell. + mkFlake = lynx'.mkFlakeWithAssertions; + in + mkFlake { inherit inputs; } (_: # https://flake.parts/module-arguments { systems = ["x86_64-linux"]; - imports = [ ]; - + imports = with lynx.flakeModules; [ + flake-guard # define a wireguard network once, and use it everywhere. + deploy-rs # types for deploy-rs + domains # evaluate flake modules in their own namespace + # "builtins" # include this if you're using `parts.lib.mkFlake` + # instead `of `mkFlakeWithAssertions` + ]; + flake.nixosConfigurations.default = nixpkgs.lib.nixosSystem { specialArgs = { inherit inputs self; } modules = [ diff --git a/docs/contributor.md b/docs/contributor.md index f854c93..88e50ff 100644 --- a/docs/contributor.md +++ b/docs/contributor.md @@ -18,10 +18,6 @@ Lynx does **not contain**: - Nixos Configurations (Share your modules with us instead) Instead, you may write tests which we run on our CI - - - Overlays (They're just cursed at scale.) - Everyone seems to hate them, and I've never needed them. - So they're not included here. - Does not use `self` (flake modules isolated) Availability to `self` isn't nessicary. We instead prefer you use flake-modules `options` to declare namespaced variables. diff --git a/flake-modules/builtins/assertions.nix b/flake-modules/builtins/assertions.nix new file mode 100644 index 0000000..dc14e14 --- /dev/null +++ b/flake-modules/builtins/assertions.nix @@ -0,0 +1,30 @@ +{ lib, ... }: +with lib; +{ + options = { + assertions = mkOption { + type = types.listOf types.unspecified; + internal = true; + default = []; + example = [ { assertion = false; message = "you can't enable this for that reason"; } ]; + description = '' + This option allows modules to express conditions that must + hold for the evaluation of the system configuration to + succeed, along with associated error messages for the user. + ''; + }; + + warnings = mkOption { + internal = true; + default = []; + type = types.listOf types.str; + example = [ "The `foo' service is deprecated and will go away soon!" ]; + description = '' + This option allows modules to show warnings to users during + the evaluation of the system configuration. + ''; + }; + }; + + # impl of this is in lib.nix:evalFlakeModules +} diff --git a/flake-modules/builtins/default.nix b/flake-modules/builtins/default.nix new file mode 100644 index 0000000..c0f8ad9 --- /dev/null +++ b/flake-modules/builtins/default.nix @@ -0,0 +1,4 @@ +{ imports = [ + ./assertions.nix + ]; +} diff --git a/flake-modules/domains/default.nix b/flake-modules/domains/default.nix new file mode 100644 index 0000000..21e676c --- /dev/null +++ b/flake-modules/domains/default.nix @@ -0,0 +1,63 @@ +{ + inputs + , config + , stdlib + , lib + , lynxlib + , flake-parts-lib + , ... +}: +{ + options.domains = lib.mkOption { + default = {}; + + description = '' + evaluate flake modules as their own namespace, + seperate from the parent. These options are built on their + respective names in `config.build.domains` + ''; + + example = '' + domains."hello-world".specialArgs = { }; + domains."hello-world".modules = [ + ({inputs, config, lib, ...}: { + systems = ["x86_64-linux"]; + imports = [ inputs.lynx.flakeModules.flake-guard ]; + + wireguard.enable = true; + wireguard.networks.vxlan = { + sopsLookup = "wg-vxlan"; + peers.by-name.gateway = { + publicKey = "nwDPjwn9KPKw2wYNMe0CHP5oIJBJHFruRy62EoTjU1A="; + ipv4 = ["172.16.1.1"]; + }; + }; + }) + ]; + ''; + + type = with lib.types; attrsOf (submodule { + options.modules = lib.mkOption { + type = listOf deferredModule; + default = []; + }; + + options.specialArgs = lib.mkOption { + type = attrsOf raw; + default = {}; + }; + }); + }; + + options.build.domains = lib.mkOption { + type = with lib.types; lazyAttrsOf raw; + default = {}; + }; + + config.build.domains = builtins.mapAttrs(domain: toplevel: + (lynxlib.evalFlakeModuleWithAssertions { + inherit inputs; + inherit (toplevel) specialArgs; + } { imports = toplevel.modules; }) + ) config.domains; +} diff --git a/flake-modules/flake-guard/default.nix b/flake-modules/flake-guard/default.nix index 7d2e03f..2ac879c 100644 --- a/flake-modules/flake-guard/default.nix +++ b/flake-modules/flake-guard/default.nix @@ -11,6 +11,7 @@ let mkOption mkEnableOption mkIf + mkRemovedOptionModule types optionalString optionals @@ -20,7 +21,17 @@ let ; in { - imports = [ ./options.nix ]; + imports = [ + (mkRemovedOptionModule [ "wireguard" "enable" ] '' + wireguard.enable was removed because it often causes user errors + where `wireguard.enable` was set to `false` but users had enabled + the nixos options `autoConfig.interface`. + This lead to errors messages which were hard to understand. + '') + + ./options.nix + ]; + flake.nixosModules.flake-guard-host = {config, ...}: let cfg = config.networking.wireguard.networks; @@ -30,7 +41,6 @@ in default = {}; type = types.attrsOf (types.submodule { options = { - autoConfig = { interface = mkEnableOption "automatically generate the underlying network interface"; peers = mkEnableOption "automatically generate the peers -- this will add all peers in the network to the interface."; @@ -58,19 +68,16 @@ in }; privateKeyFile = mkOption { - type = types.str; + type = types.unspecified; }; }; }; }); }; - config = mkIf rootConfig.enable - { - - networking.wireguard.networks = mapAttrs (net-name: network: + config.networking.wireguard.networks = + (mapAttrs (net-name: network: let - self-name = builtins.head (builtins.filter (x: x == config.networking.hostName) (builtins.attrNames network.peers.by-name)); @@ -108,10 +115,11 @@ in inherit self; peers.by-name = mapAttrs (pname: peer: (toPeer peer)) network.peers.by-name; peers.list = map toPeer (builtins.attrValues network.peers.by-name); - }) rootConfig.networks; + }) rootConfig.networks); - networking.wireguard.interfaces = mapAttrs (net-name: network: - mkIf network.autoConfig.interface { + config.networking.wireguard.interfaces = mapAttrs (net-name: network: + mkIf network.autoConfig.interface + { inherit (config.networking.wireguard.networks.${net-name}.self) listenPort privateKeyFile @@ -123,6 +131,8 @@ in ); }) config.networking.wireguard.networks; - }; }; + + + } diff --git a/flake-modules/flake-guard/lib.nix b/flake-modules/flake-guard/lib.nix index cc239f0..6bf02f3 100644 --- a/flake-modules/flake-guard/lib.nix +++ b/flake-modules/flake-guard/lib.nix @@ -19,7 +19,9 @@ inherit (lib) in rec { toPeer = p: { - inherit (p) publicKey; + inherit (p) + publicKey + persistentKeepalive; allowedIPs = p.ipv4 ++ p.ipv6; endpoint = p.selfEndpoint; }; diff --git a/flake-modules/flake-guard/options.nix b/flake-modules/flake-guard/options.nix index 9a4c44d..4e579fb 100644 --- a/flake-modules/flake-guard/options.nix +++ b/flake-modules/flake-guard/options.nix @@ -55,6 +55,11 @@ inherit (lib) default = null; }; + persistentKeepalive= mkOption { + type = types.nullOr types.int; + default = null; + }; + # module = mkOption { # type = types.nullOr types.unspecified; # default = null; @@ -69,8 +74,6 @@ inherit (lib) in { options.wireguard = { - enable = mkEnableOption "Enable wireguard"; - networks = mkOption { type = types.attrsOf (types.submodule { options = { @@ -127,14 +130,14 @@ in }; config.wireguard.build.networks = - mapAttrs (net-name: network: - { - peers.by-name = mapAttrs (peer-name: peer: - peer // { - sopsLookup = if peer.sopsLookup != null - then peer.sopsLookup - else network.sopsLookup; - } - ) network.peers.by-name; - }) config.wireguard.networks; + (mapAttrs (net-name: network: + { + peers.by-name = mapAttrs (peer-name: peer: + peer // { + sopsLookup = if peer.sopsLookup != null + then peer.sopsLookup + else network.sopsLookup; + } + ) network.peers.by-name; + }) config.wireguard.networks); } diff --git a/flake-modules/flake-guard/readme.md b/flake-modules/flake-guard/readme.md index c1a58df..f57c03a 100644 --- a/flake-modules/flake-guard/readme.md +++ b/flake-modules/flake-guard/readme.md @@ -10,7 +10,6 @@ flake guard allows you to define your wireguard network once, and use it across { imports = [ inputs.lynx.flakeModules.flake-guard ]; - wireguard.enable = true; wireguard.networks.my-network = { # assumes same sop keys for all hosts. # this also works with agenix diff --git a/flake.lock b/flake.lock deleted file mode 100644 index d3ed9bd..0000000 --- a/flake.lock +++ /dev/null @@ -1,63 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1706386369, - "narHash": "sha256-iL18ktG4tNbJVYaV3h9155N4X5cvfNTgQXhVug1g0yI=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "30b34ac00788d0dce98a992b6d92779b9eb6bc19", - "type": "github" - }, - "original": { - "owner": "nixos", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "dir": "lib", - "lastModified": 1703961334, - "narHash": "sha256-M1mV/Cq+pgjk0rt6VxoyyD+O8cOUiai8t9Q6Yyq4noY=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "b0d36bd0a420ecee3bc916c91886caca87c894e9", - "type": "github" - }, - "original": { - "dir": "lib", - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1704982712, - "narHash": "sha256-2Ptt+9h8dczgle2Oo6z5ni5rt/uLMG47UFTR1ry/wgg=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "07f6395285469419cf9d078f59b5b49993198c00", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs", - "parts": "parts" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix index aa85efb..c9958c3 100644 --- a/flake.nix +++ b/flake.nix @@ -2,18 +2,22 @@ description = "Repository of shared modules"; outputs = _: { flakeModules = { - deploy-rs = import ./flake-modules/deploy-rs; - lynx-docs = import ./flake-modules/lynx-docs; - flake-guard = import ./flake-modules/flake-guard; - profile-parts-homexts = import ./flake-modules/profile-parts-homext.nix; + "builtins" = ./flake-modules/builtins; + deploy-rs = ./flake-modules/deploy-rs; + lynx-docs = ./flake-modules/lynx-docs; + flake-guard = ./flake-modules/flake-guard; + domains = ./flake-modules/domains; + profile-parts-homexts = ./flake-modules/profile-parts-homext.nix; }; nixosModules = { - globals = import ./nixos-modules/globals.nix; + globals = ./nixos-modules/globals.nix; fs.zfs = { - encrypted-ephemeral = import ./nixos-modules/fs/zfs/encrypted-ephemeral.nix; - reuse-password-prompt = import ./nixos-modules/fs/zfs/reuse-password-prompt.nix; + encrypted-ephemeral = ./nixos-modules/fs/zfs/encrypted-ephemeral.nix; + reuse-password-prompt = ./nixos-modules/fs/zfs/reuse-password-prompt.nix; }; }; + + lib = ./lib.nix; }; } diff --git a/lib.nix b/lib.nix new file mode 100644 index 0000000..3f462f1 --- /dev/null +++ b/lib.nix @@ -0,0 +1,39 @@ +{ flake-parts-lib, ... }: +with builtins; +let + inherit (flake-parts-lib) evalFlakeModule; + + singleModuleBase = x: { + imports = [ + ./flake-modules/builtins/assertions.nix + x + ]; + }; + + evalAssertions = eval: + let + failedAssertions = map (x: x.message) (filter (x: !x.assertion) eval.config.assertions); + warnings = eval.config.warnings; + in + if (failedAssertions != []) + then + builtins.abort (concatStringsSep "\n\n" failedAssertions) + else + if (warnings != []) + then + builtins.trace (concatStringsSep "\n\n" warnings) + eval + else eval; + + + evalFlakeModuleWithAssertions = a: m: + evalAssertions (evalFlakeModule a (singleModuleBase m)); +in +{ + inherit evalFlakeModuleWithAssertions; + mkFlakeWithAssertions = args: module: + let + eval = evalFlakeModuleWithAssertions args module; + in + eval.config.flake; +}