From 09c2b6de63a6ff35348fcb2c2eb57b974f0a8a52 Mon Sep 17 00:00:00 2001 From: Kleidi Bujari Date: Wed, 2 Apr 2025 21:59:20 -0400 Subject: automate project structure --- flake.lock | 68 +- flake.nix | 96 +- lib/depot-tools.nix | 84 ++ machines/radon/pxeboot.nix | 1 + machines/t1/default.nix | 6 +- machines/t480/default.nix | 38 +- machines/xnet/README | 18 - machines/xnet/default.nix | 114 -- machines/xnet/desktop/default.nix | 208 --- machines/xnet/disk.nix | 154 -- machines/xnet/gitserver/default.nix | 49 - machines/xnet/gitserver/gitweb.nix | 70 - machines/xnet/monitoring/22604_rev2.json | 1565 -------------------- machines/xnet/monitoring/default.nix | 58 - machines/xnet/monitoring/grafana.nix | 78 - machines/xnet/net/default.nix | 128 -- machines/xnet/net/dns.nix | 38 - machines/xnet/net/sshd.nix | 46 - machines/xnet/nginx.nix | 27 - machines/xnet/persist.nix | 61 - mod/code/cses/.envrc | 1 - mod/code/cses/default.nix | 13 - mod/code/cses/problems/1068.cpp | 17 - mod/code/cses/problems/1069.cpp | 23 - mod/code/cses/problems/1070.cpp | 15 - mod/code/cses/problems/1071.cpp | 19 - mod/code/cses/problems/1083.cpp | 20 - mod/code/cses/problems/1094.cpp | 27 - mod/misc/cv/cv.typ | 250 ---- mod/misc/cv/default.nix | 6 - mod/nix/readTree/default.nix | 326 ---- mod/tools/perf-flamegraph.nix | 12 - mod/tools/typst/default.nix | 34 - mod/tools/typst/main.typ | 1 - mod/tools/typst/packages/tmu/template.typ | 37 - mod/tools/typst/packages/tmu/typst.toml | 7 - mod/users/kle/default.nix | 56 - mod/web/blog/.gitignore | 2 - mod/web/blog/config.toml | 8 - mod/web/blog/content/posts/_index.md | 6 - .../blog/content/posts/deterministic-hostnames.md | 80 - mod/web/blog/content/posts/nohup.md | 26 - .../content/posts/stateless-compute-networks.md | 41 - mod/web/blog/content/posts/vim-compilers.md | 20 - mod/web/blog/default.nix | 30 - mod/web/blog/templates/404.html | 9 - mod/web/blog/templates/base.html | 88 -- mod/web/blog/templates/index.html | 16 - mod/web/blog/templates/post.html | 14 - mod/web/resistors/default.nix | 12 - mod/web/resistors/index.html | 339 ----- mod/web/resistors/styles.css | 127 -- modules/dns.nix | 38 + modules/users.nix | 48 + modules/xnet/README | 18 + modules/xnet/default.nix | 114 ++ modules/xnet/desktop/default.nix | 208 +++ modules/xnet/disk.nix | 155 ++ modules/xnet/gitserver/default.nix | 49 + modules/xnet/gitserver/gitweb.nix | 70 + modules/xnet/monitoring/22604_rev2.json | 1565 ++++++++++++++++++++ modules/xnet/monitoring/default.nix | 58 + modules/xnet/monitoring/grafana.nix | 78 + modules/xnet/net/default.nix | 128 ++ modules/xnet/net/dns.nix | 38 + modules/xnet/net/sshd.nix | 46 + modules/xnet/nginx.nix | 27 + modules/xnet/persist.nix | 61 + packages/blog/.gitignore | 2 + packages/blog/config.toml | 8 + packages/blog/content/posts/_index.md | 6 + .../blog/content/posts/deterministic-hostnames.md | 80 + packages/blog/content/posts/nohup.md | 26 + .../content/posts/stateless-compute-networks.md | 41 + packages/blog/content/posts/vim-compilers.md | 20 + packages/blog/default.nix | 21 + packages/blog/templates/404.html | 9 + packages/blog/templates/base.html | 88 ++ packages/blog/templates/index.html | 16 + packages/blog/templates/post.html | 14 + packages/cses/default.nix | 13 + packages/cses/problems/1068.cpp | 17 + packages/cses/problems/1069.cpp | 23 + packages/cses/problems/1070.cpp | 15 + packages/cses/problems/1071.cpp | 19 + packages/cses/problems/1083.cpp | 20 + packages/cses/problems/1094.cpp | 27 + packages/cv/cv.typ | 250 ++++ packages/cv/default.nix | 5 + packages/perf-flamegraph.nix | 12 + packages/resistors/default.nix | 7 + packages/resistors/index.html | 339 +++++ packages/resistors/styles.css | 127 ++ 93 files changed, 4044 insertions(+), 4451 deletions(-) create mode 100644 lib/depot-tools.nix delete mode 100644 machines/xnet/README delete mode 100644 machines/xnet/default.nix delete mode 100644 machines/xnet/desktop/default.nix delete mode 100644 machines/xnet/disk.nix delete mode 100644 machines/xnet/gitserver/default.nix delete mode 100644 machines/xnet/gitserver/gitweb.nix delete mode 100644 machines/xnet/monitoring/22604_rev2.json delete mode 100644 machines/xnet/monitoring/default.nix delete mode 100644 machines/xnet/monitoring/grafana.nix delete mode 100644 machines/xnet/net/default.nix delete mode 100644 machines/xnet/net/dns.nix delete mode 100644 machines/xnet/net/sshd.nix delete mode 100644 machines/xnet/nginx.nix delete mode 100644 machines/xnet/persist.nix delete mode 100644 mod/code/cses/.envrc delete mode 100644 mod/code/cses/default.nix delete mode 100644 mod/code/cses/problems/1068.cpp delete mode 100644 mod/code/cses/problems/1069.cpp delete mode 100644 mod/code/cses/problems/1070.cpp delete mode 100644 mod/code/cses/problems/1071.cpp delete mode 100644 mod/code/cses/problems/1083.cpp delete mode 100644 mod/code/cses/problems/1094.cpp delete mode 100644 mod/misc/cv/cv.typ delete mode 100644 mod/misc/cv/default.nix delete mode 100644 mod/nix/readTree/default.nix delete mode 100644 mod/tools/perf-flamegraph.nix delete mode 100644 mod/tools/typst/default.nix delete mode 100644 mod/tools/typst/main.typ delete mode 100644 mod/tools/typst/packages/tmu/template.typ delete mode 100644 mod/tools/typst/packages/tmu/typst.toml delete mode 100644 mod/users/kle/default.nix delete mode 100644 mod/web/blog/.gitignore delete mode 100644 mod/web/blog/config.toml delete mode 100644 mod/web/blog/content/posts/_index.md delete mode 100644 mod/web/blog/content/posts/deterministic-hostnames.md delete mode 100644 mod/web/blog/content/posts/nohup.md delete mode 100644 mod/web/blog/content/posts/stateless-compute-networks.md delete mode 100644 mod/web/blog/content/posts/vim-compilers.md delete mode 100644 mod/web/blog/default.nix delete mode 100644 mod/web/blog/templates/404.html delete mode 100644 mod/web/blog/templates/base.html delete mode 100644 mod/web/blog/templates/index.html delete mode 100644 mod/web/blog/templates/post.html delete mode 100644 mod/web/resistors/default.nix delete mode 100644 mod/web/resistors/index.html delete mode 100644 mod/web/resistors/styles.css create mode 100644 modules/dns.nix create mode 100644 modules/users.nix create mode 100644 modules/xnet/README create mode 100644 modules/xnet/default.nix create mode 100644 modules/xnet/desktop/default.nix create mode 100644 modules/xnet/disk.nix create mode 100644 modules/xnet/gitserver/default.nix create mode 100644 modules/xnet/gitserver/gitweb.nix create mode 100644 modules/xnet/monitoring/22604_rev2.json create mode 100644 modules/xnet/monitoring/default.nix create mode 100644 modules/xnet/monitoring/grafana.nix create mode 100644 modules/xnet/net/default.nix create mode 100644 modules/xnet/net/dns.nix create mode 100644 modules/xnet/net/sshd.nix create mode 100644 modules/xnet/nginx.nix create mode 100644 modules/xnet/persist.nix create mode 100644 packages/blog/.gitignore create mode 100644 packages/blog/config.toml create mode 100644 packages/blog/content/posts/_index.md create mode 100644 packages/blog/content/posts/deterministic-hostnames.md create mode 100644 packages/blog/content/posts/nohup.md create mode 100644 packages/blog/content/posts/stateless-compute-networks.md create mode 100644 packages/blog/content/posts/vim-compilers.md create mode 100644 packages/blog/default.nix create mode 100644 packages/blog/templates/404.html create mode 100644 packages/blog/templates/base.html create mode 100644 packages/blog/templates/index.html create mode 100644 packages/blog/templates/post.html create mode 100644 packages/cses/default.nix create mode 100644 packages/cses/problems/1068.cpp create mode 100644 packages/cses/problems/1069.cpp create mode 100644 packages/cses/problems/1070.cpp create mode 100644 packages/cses/problems/1071.cpp create mode 100644 packages/cses/problems/1083.cpp create mode 100644 packages/cses/problems/1094.cpp create mode 100644 packages/cv/cv.typ create mode 100644 packages/cv/default.nix create mode 100644 packages/perf-flamegraph.nix create mode 100644 packages/resistors/default.nix create mode 100644 packages/resistors/index.html create mode 100644 packages/resistors/styles.css diff --git a/flake.lock b/flake.lock index ea444a3..b40bfe5 100644 --- a/flake.lock +++ b/flake.lock @@ -1,28 +1,5 @@ { "nodes": { - "agenix": { - "inputs": { - "darwin": [], - "home-manager": "home-manager", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1703089996, - "narHash": "sha256-ipqShkBmHKC9ft1ZAsA6aeKps32k7+XZSPwfxeHLsAU=", - "owner": "ryantm", - "repo": "agenix", - "rev": "564595d0ad4be7277e07fa63b5a991b3c645655d", - "type": "github" - }, - "original": { - "owner": "ryantm", - "ref": "0.15.0", - "repo": "agenix", - "type": "github" - } - }, "disko": { "inputs": { "nixpkgs": [ @@ -44,27 +21,6 @@ "type": "github" } }, - "home-manager": { - "inputs": { - "nixpkgs": [ - "agenix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1682203081, - "narHash": "sha256-kRL4ejWDhi0zph/FpebFYhzqlOBrk0Pl3dzGEKSAlEw=", - "owner": "nix-community", - "repo": "home-manager", - "rev": "32d3e39c491e2f91152c84f8ad8b003420eab0a1", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "home-manager", - "type": "github" - } - }, "naersk": { "inputs": { "nixpkgs": [ @@ -119,32 +75,10 @@ }, "root": { "inputs": { - "agenix": "agenix", "disko": "disko", "naersk": "naersk", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs", - "sops-nix": "sops-nix" - } - }, - "sops-nix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1732186149, - "narHash": "sha256-N9JGWe/T8BC0Tss2Cv30plvZUYoiRmykP7ZdY2on2b0=", - "owner": "Mic92", - "repo": "sops-nix", - "rev": "53c853fb1a7e4f25f68805ee25c83d5de18dc699", - "type": "github" - }, - "original": { - "owner": "Mic92", - "repo": "sops-nix", - "type": "github" + "nixpkgs": "nixpkgs" } } }, diff --git a/flake.nix b/flake.nix index 3cda153..703ed9c 100644 --- a/flake.nix +++ b/flake.nix @@ -1,19 +1,11 @@ { description = "Personal monorepo"; - inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; disko.url = "github:nix-community/disko/v1.6.1"; disko.inputs.nixpkgs.follows = "nixpkgs"; - sops-nix.url = "github:Mic92/sops-nix"; - sops-nix.inputs.nixpkgs.follows = "nixpkgs"; - - agenix.url = "github:ryantm/agenix/0.15.0"; - agenix.inputs.nixpkgs.follows = "nixpkgs"; - agenix.inputs.darwin.follows = ""; - nixos-hardware.url = "github:nixos/nixos-hardware/master"; # Rust projects @@ -25,70 +17,71 @@ let inherit (builtins) attrNames - filter listToAttrs map readDir ; + inherit (nixpkgs) lib; + inherit (nixpkgs.lib) + mapAttrs nixosSystem ; - readTree = import ./mod/nix/readTree { }; + systems = [ "x86_64-linux" "aarch64-linux" ]; - # Recursively converts subdirectories under ./mod into an attrset - # that can be passed to other parts of the depot. - readDepot = depotArgs: readTree { - args = depotArgs; - path = ./mod; - }; + inherit (import ./lib/depot-tools.nix { inherit inputs systems lib; }) + importDir + eachSystem + systemArgs + ; - # Named arguments to be made available to any nix expression in - # the depot module. - depot = readTree.fix (self: readDepot { - depot = self; + packages = eachSystem ( + { newScope, ... }: + mapAttrs (pname: { path, ... }: newScope { inherit pname; } path { }) + (importDir ./packages) + ); + publisherArgs = { + flake = self; inherit inputs; + }; - # x86_64-linux is hardcoded as the package arch only for the - # mod directory. This workaround keeps the flake pure, - # at the expense of only supporting one system. This can - # be circumvented by retrieving the system during evaluation, - # but flakes disallow this by design. - # - # This monorepo currently depends on functionality from flakes, - # but this may change in the future, perhaps shifting to an - # impure base with certain packages exposed from the flake in a - # pure way. - # - # In practice, this means that any configuration or package - # exposed from the depot flake that makes use of internal - # derivations is limited to x86_64-linux. For now this is fine, - # but ideally any architecture supported by nix should be - # allowed to evaluate projects hosted here. - pkgs = nixpkgs.legacyPackages."x86_64-linux"; - - # Expose lib attribute to modules. - lib = nixpkgs.lib; - }); - - machines = filter (m: m != "xnet") - (attrNames (readDir ./machines)); + expectsPublisherArgs = + module: + builtins.isFunction module + && builtins.all (arg: builtins.elem arg (builtins.attrNames publisherArgs)) ( + builtins.attrNames (builtins.functionArgs module) + ); + + injectPublisherArgs = + modulePath: + let + module = import modulePath; + in + if expectsPublisherArgs module then + lib.setDefaultModuleLocation modulePath (module publisherArgs) + else + modulePath; + + nixosModules = mapAttrs (_: moduleDir: injectPublisherArgs moduleDir) + (mapAttrs (_: { path, ... }: path) (importDir ./modules)); in { - inherit depot; - - nixosModules.xnet.imports = - [ ./machines/xnet inputs.disko.nixosModules.disko ]; + inherit packages nixosModules; nixosConfigurations = listToAttrs ( map (name: { inherit name; - value = nixosSystem { + value = nixosSystem rec { system = "x86_64-linux"; - specialArgs = { inherit inputs depot; }; + specialArgs = { + inherit inputs; + inherit (self) outputs; + inherit (systemArgs.${system}) flake perSystem; + }; modules = [ # Machine's specific configuration ./machines/${name} @@ -105,6 +98,7 @@ ]; }; }) - machines); + (attrNames (readDir ./machines)) + ); }; } diff --git a/lib/depot-tools.nix b/lib/depot-tools.nix new file mode 100644 index 0000000..448055a --- /dev/null +++ b/lib/depot-tools.nix @@ -0,0 +1,84 @@ +{ inputs +, systems +, nixpkgs ? inputs.nixpkgs +, ... +}: + +let + inherit (builtins) + match + head + readDir + ; + + inherit (nixpkgs.lib) + callPackageWith + filterAttrs + genAttrs + makeScope + mapAttrs + mapAttrs' + ; + + importDir = + path: + let + entries = readDir path; + + # Get paths to directories + onlyDirs = filterAttrs (_name: type: type == "directory") entries; + dirPaths = mapAttrs + (name: type: { + path = path + "/${name}"; + inherit type; + }) + onlyDirs; + + # Get paths to nix files, where the name is the basename of the file without the .nix extension + nixPaths = removeAttrs + (mapAttrs' + ( + name: type: + let + nixName = match "(.*)\\.nix" name; + in + { + name = if type == "directory" || nixName == null then "__junk" else (head nixName); + value = { + path = path + "/${name}"; + type = type; + }; + } + ) + entries) [ "__junk" ]; + in + dirPaths // nixPaths; + + # Memoize the args per system + systemArgs = genAttrs systems ( + system: + let + # Resolve the packages for each input. + perSystem = mapAttrs + (_: flake: flake.legacyPackages.${system} or { } // flake.packages.${system} or { }) + inputs; + + # Handle nixpkgs specially. + pkgs = + if (nixpkgs.config or { }) == { } && (nixpkgs.overlays or [ ]) == [ ] then + perSystem.nixpkgs + else + import inputs.nixpkgs { + inherit system; + config = nixpkgs.config or { }; + overlays = nixpkgs.overlays or [ ]; + }; + + flake = inputs.self; + in + makeScope callPackageWith (_: { inherit inputs perSystem flake pkgs system; }) + ); + + eachSystem = f: genAttrs systems (system: f systemArgs.${system}); +in +{ inherit importDir systemArgs eachSystem; } diff --git a/machines/radon/pxeboot.nix b/machines/radon/pxeboot.nix index 4d897c3..4655990 100644 --- a/machines/radon/pxeboot.nix +++ b/machines/radon/pxeboot.nix @@ -24,6 +24,7 @@ let users.users.root.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP7T2uWJFUu8aFZZgQusGKyEMocb2pKbHLDad2eIJus9" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEOw8YEbHsKy38JHp9W1wcxxZgWCDgnabOXccZUN5ddd" ]; }) ]; diff --git a/machines/t1/default.nix b/machines/t1/default.nix index 68bc7b9..fa6f6e3 100644 --- a/machines/t1/default.nix +++ b/machines/t1/default.nix @@ -1,4 +1,4 @@ -{ inputs, depot, pkgs, ... }: +{ inputs, flake, pkgs, perSystem, ... }: let inherit (inputs.nixos-hardware.nixosModules) @@ -6,7 +6,7 @@ let common-gpu-amd ; - inherit (depot.users) kle; + inherit (import flake.outputs.nixosModules.users { inherit perSystem pkgs; }) kle; in { imports = [ @@ -16,7 +16,7 @@ in system.stateVersion = "24.11"; - users.users.kle = kle.nixos; + users.users.kle = kle; xnet = { desktop.enable = true; diff --git a/machines/t480/default.nix b/machines/t480/default.nix index e726670..24b6bc6 100644 --- a/machines/t480/default.nix +++ b/machines/t480/default.nix @@ -1,12 +1,10 @@ -{ inputs, depot, ... }: +{ inputs, flake, perSystem, pkgs, ... }: let inherit (inputs.nixos-hardware.nixosModules) lenovo-thinkpad-t480 ; - inherit (depot.users) - kle - ; + inherit (import flake.outputs.nixosModules.users { inherit perSystem pkgs; }) kle; in { imports = [ @@ -23,16 +21,15 @@ in }; }; - users.users.kle = kle.nixos; + users.users.kle = kle; networking = { hostName = "t480"; useDHCP = false; dhcpcd.enable = false; - nameservers = [ "127.0.0.1" ]; networkmanager = { enable = true; - dns = "none"; + # dns = "none"; wifi.powersave = true; }; }; @@ -40,32 +37,5 @@ in programs.nm-applet.enable = true; services.fwupd.enable = true; - services.unbound = { - enable = true; - settings.server = { - interface = [ "127.0.0.1" ]; - port = 53; - access-control = [ "127.0.0.1 allow" ]; - harden-glue = true; - harden-dnssec-stripped = true; - use-caps-for-id = false; - prefetch = true; - edns-buffer-size = 1232; - hide-identity = true; - hide-version = true; - }; - - settings.forward-zone = [ - { - name = "."; - forward-tls-upstream = true; - forward-addr = [ - "9.9.9.9#dns.quad9.net" - "149.112.112.112#dns.quad9.net" - ]; - } - ]; - }; - xnet.persist = [ "/etc/NetworkManager/system-connections" ]; } diff --git a/machines/xnet/README b/machines/xnet/README deleted file mode 100644 index ab1c28c..0000000 --- a/machines/xnet/README +++ /dev/null @@ -1,18 +0,0 @@ -xnet -==== - -Base configuration for any machines running a standard xnet -configuration. - -Notable features: - - - Root filesystem running in RAM - - Automated ZFS partitioning of disk - - Hardened SSH and web services - -Usage -===== - -The entire module is exposed from the top-level flake as a nixosModule, -but it changes too often to be considered stable. Options are made -available when imported under the 'xnet' attribute set. diff --git a/machines/xnet/default.nix b/machines/xnet/default.nix deleted file mode 100644 index 9d224cf..0000000 --- a/machines/xnet/default.nix +++ /dev/null @@ -1,114 +0,0 @@ -{ pkgs, lib, config, ... }: -let - inherit (builtins) - fetchurl - filter - readFile - ; - - inherit (lib) - mkDefault - mkOption - types - splitString - ; - - gitKeys = fetchurl { - url = "https://github.com/kbujari.keys"; - sha256 = "1kskbiyqvjz1wsmcrgh9v0iryf33y70zk503z0m96wmzdjllmc94"; - }; - -in -{ - imports = [ - ./disk.nix - ./nginx.nix - ./persist.nix - ./net - ./desktop - ./gitserver - ./monitoring - ]; - - options.xnet = { - pubKeys = mkOption { - type = types.listOf types.str; - default = filter (s: s != "") - (splitString "\n" (readFile gitKeys)); - }; - }; - - config = { - i18n.defaultLocale = mkDefault "en_US.UTF-8"; - time.timeZone = mkDefault "America/Toronto"; - - boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; - - nix = { - settings = { - auto-optimise-store = true; - experimental-features = [ "nix-command" "flakes" ]; - warn-dirty = false; - - # timeout fast from binary cache - connect-timeout = 5; - }; - gc = { - automatic = true; - options = mkDefault "--delete-older-than 30d"; - }; - }; - - documentation = { - doc.enable = mkDefault false; - info.enable = mkDefault false; - }; - - users.mutableUsers = false; - users.users.root = { - openssh.authorizedKeys.keys = config.xnet.pubKeys; - initialPassword = "hello"; - }; - - security.sudo = { - execWheelOnly = true; - extraConfig = "Defaults lecture = never"; - }; - - nixpkgs.config.allowUnfree = true; - - programs.ssh = { - knownHosts = { - "github.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl"; - "gitlab.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf"; - "git.sr.ht".publicKey = " ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZvRd4EtM7R+IHVMWmDkVU3VLQTSwQDSAvW0t2Tkj60"; - "pascal.ee.ryerson.ca".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFmWInQNT6EoU1NtUYzTs5jtpfbO/m6yvCckOiEGjvDc"; - }; - extraConfig = '' - Host github - HostName github.com - User git - PreferredAuthentications publickey - - Host ee - HostName pascal.ee.ryerson.ca - User kbujari - ''; - }; - - programs.git = { - enable = true; - config = { - init.defaultBranch = "master"; - fetch.prune = true; - core.excludesFile = pkgs.writeText "gitignore" '' - # dev shell caching - .direnv/ - .envrc - ''; - push.default = "upstream"; - push.autoSetupRemote = true; - }; - }; - }; -} diff --git a/machines/xnet/desktop/default.nix b/machines/xnet/desktop/default.nix deleted file mode 100644 index 995381f..0000000 --- a/machines/xnet/desktop/default.nix +++ /dev/null @@ -1,208 +0,0 @@ -{ config, lib, pkgs, ... }: -let - cfg = config.xnet.desktop; - inherit (lib) mkDefault mkOption mkForce mkIf types; - inherit (builtins) listToAttrs; -in -{ - options.xnet.desktop = { - enable = mkOption { - type = types.bool; - default = false; - description = "Enable graphical desktop."; - }; - }; - - config = mkIf cfg.enable { - security.rtkit.enable = true; - services.pipewire = { - enable = true; - pulse.enable = true; - alsa.enable = true; - }; - - systemd.user.services.waybar = { - enable = true; - after = [ "graphical-session.target" ]; - partOf = [ "graphical-session.target" ]; - wantedBy = [ "graphical-session.target" ]; - serviceConfig = { - ExecStart = "${pkgs.waybar}/bin/waybar"; - Type = "simple"; - }; - }; - - programs.sway = { - enable = true; - wrapperFeatures = { - gtk = true; - base = true; - }; - xwayland.enable = true; - extraPackages = with pkgs; [ - foot - fuzzel - imv - mako - mpv - niri - playerctl - pop-icon-theme - pwvucontrol - sway-contrib.grimshot - swayidle - swaylock - tigervnc - udiskie - waybar - wl-clipboard - zathura - ]; - extraSessionCommands = '' - export MOZ_ENABLE_WAYLAND=1 - export MOZ_USE_XINPUT2=1 - export MOZ_WEBRENDER=1 - export XDG_CURRENT_DESKTOP=sway - export XDG_SESSION_TYPE=wayland - ''; - }; - - qt = { - enable = true; - style = "adwaita-dark"; - platformTheme = "gnome"; - }; - - fonts.packages = with pkgs; [ - departure-mono - iosevka - noto-fonts - noto-fonts-cjk-sans - noto-fonts-emoji - nerd-fonts.symbols-only - terminus_font - ]; - - xdg.portal = { - enable = true; - extraPortals = with pkgs; [ - xdg-desktop-portal-wlr - xdg-desktop-portal-gtk - ]; - }; - - environment.etc."xdg/user-dirs.defaults".text = '' - DOWNLOAD=/tmp/downloads - ''; - - environment.systemPackages = [ pkgs.man-pages pkgs.man-pages-posix ]; - - programs = { - fish = { - enable = true; - interactiveShellInit = '' - # disable greeting - set fish_greeting - ''; - }; - light.enable = mkDefault true; - direnv = { - enable = true; - nix-direnv.enable = true; - }; - - # GTK settings - dconf = { - enable = true; - profiles.user.databases = [{ - lockAll = true; - settings = { - "org/gnome/desktop/interface" = { - color-scheme = "prefer-dark"; - gtk-font-name = "System-ui 10"; - icon-theme = "Pop"; - theme-name = "Adwaita-dark"; - }; - }; - }]; - }; - }; - - # Helper for managing dotfiles - environment.shellAliases.dots = - "git --git-dir=$HOME/.local/cfg/ --work-tree=$HOME"; - - # Enable yubikey for SSH and more - services = { - yubikey-agent.enable = true; - pcscd.enable = true; - udev.packages = with pkgs; [ yubikey-personalization ]; - udisks2.enable = true; - }; - - programs.firefox = { - enable = true; - preferences = { - # Enable hardware transcoding - "media.ffmpeg.vaapi.enabled" = true; - - # Disable controlling media with keyboard - "media.hardwaremediakeys.enabled" = false; - # Enable legacy compact mode - "browser.compactmode.show" = true; - "browser.uidensity" = 1; - - # Disable ctrl+q closing browser - "browser.quitShortcut.disabled" = true; - - # Hardcode theme to dark mode - "ui.systemUsesDarkTheme" = 1; - }; - policies = { - DefaultDownloadDirectory = "/tmp/firefox"; - DisableTelemetry = true; - DisableFirefoxStudies = true; - EnableTrackingProtection = { - Value = true; - Locked = true; - Cryptomining = true; - Fingerprinting = true; - }; - DisableAccounts = true; - DisableFirefoxAccounts = true; - DisableFirefoxScreenshots = true; - DisablePocket = true; - DisplayBookmarksToolbar = "never"; - DisplayMenuBar = "default-off"; - DontCheckDefaultBrowser = true; - Homepage = { - URL = "about:blank"; - StartPage = "homepage"; - }; - HttpsOnlyMode = "enabled"; - DNSOverHTTPS = false; - NewTabPage = false; - OfferToSaveLogins = false; - PasswordManagerEnabled = false; - SearchBar = "unified"; - SearchEngines.Default = "DuckDuckGo"; - SearchSuggestEnabled = false; - ExtensionSettings = - let - extension = shortId: uuid: { - name = uuid; - value = { - install_url = "https://addons.mozilla.org/en-US/firefox/downloads/latest/${shortId}/latest.xpi"; - installation_mode = "force_installed"; - }; - }; - in - listToAttrs [ - (extension "ublock-origin" "uBlock0@raymondhill.net") - (extension "bitwarden-password-manager" "{446900e4-71c2-419f-a6a7-df9c091e268b}") - (extension "darkreader" "addon@darkreader.org") - ]; - }; - }; - }; -} diff --git a/machines/xnet/disk.nix b/machines/xnet/disk.nix deleted file mode 100644 index 26b6909..0000000 --- a/machines/xnet/disk.nix +++ /dev/null @@ -1,154 +0,0 @@ -{ config, lib, modulesPath, ... }: -let - cfg = config.xnet.disk; - commonOpts = { - acltype = "posixacl"; - atime = "off"; - compression = "on"; - normalization = "formD"; - relatime = "off"; - xattr = "sa"; - "com.sun:auto-snapshot" = "false"; - }; - - inherit (lib) mkOption mkDefault mkIf types; -in -{ - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - options.xnet.disk = { - enable = mkOption { - type = types.bool; - default = false; - description = "Apply xnet-standard ZFS disk layout."; - }; - - device = mkOption { - type = types.str; - description = "Device used for zroot ZFS pool."; - }; - }; - - config = mkIf cfg.enable { - networking.hostId = builtins.substring 0 8 - (builtins.hashString "md5" config.networking.hostName); - - services.zfs = { - autoScrub.enable = true; - trim.enable = true; - }; - - # With root running in memory, swap should be required unless - # otherwise specified - zramSwap.enable = mkDefault true; - - boot = { - kernelParams = [ "nohibernate" "elevator=none" ]; - supportedFilesystems = [ "vfat" "zfs" ]; - zfs.devNodes = mkDefault "/dev/disk/by-partuuid"; - loader = { - systemd-boot.enable = true; - efi.canTouchEfiVariables = true; - }; - initrd = { - systemd.enable = true; - availableKernelModules = [ - "xhci_pci" - "ahci" - "nvme" - "usb_storage" - "sd_mod" - "sdhci_pci" - ]; - }; - tmp.cleanOnBoot = mkDefault true; - }; - - disko.devices.disk.main = { - type = "disk"; - device = cfg.device; - content = { - type = "gpt"; - partitions.ESP = { - size = "1G"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - }; - }; - partitions.ZFS = { - size = "100%"; - content = { - type = "zfs"; - pool = "zroot"; - }; - }; - }; - }; - - disko.devices = { - nodev."/" = { - fsType = "tmpfs"; - mountOptions = [ "defaults" "size=2G" "mode=755" ]; - }; - - zpool.zroot = { - type = "zpool"; - options = { - ashift = "12"; - autotrim = "on"; - }; - - datasets = { - "local" = { - type = "zfs_fs"; - options = commonOpts // { - mountpoint = "none"; - }; - }; - - "local/nix" = { - type = "zfs_fs"; - mountpoint = "/nix"; - options.mountpoint = "legacy"; - }; - - "local/reserved" = { - type = "zfs_fs"; - options = { - refreservation = "10G"; - mountpoint = "none"; - }; - }; - - "persist" = { - type = "zfs_fs"; - mountpoint = "/persist"; - options = commonOpts // { - mountpoint = "legacy"; - }; - }; - }; - }; - }; - - services.sanoid = { - enable = true; - templates.default = { - autosnap = true; - autoprune = true; - hourly = 24; - daily = 14; - monthly = 1; - }; - datasets."zroot/persist" = { - useTemplate = [ "default" ]; - recursive = true; - }; - }; - }; -} diff --git a/machines/xnet/gitserver/default.nix b/machines/xnet/gitserver/default.nix deleted file mode 100644 index 2152ebd..0000000 --- a/machines/xnet/gitserver/default.nix +++ /dev/null @@ -1,49 +0,0 @@ -{ config, lib, pkgs, ... }: -let - cfg = config.xnet.gitServer; - inherit (lib) mkOption mkIf types; -in -{ - imports = [ ./gitweb.nix ]; - - options.xnet.gitServer = { - enable = mkOption { - type = types.bool; - default = false; - description = "Serve git repos over SSH."; - }; - - path = mkOption { - type = types.path; - default = "/persist/repo/git"; - description = "Directory where repos will be stored."; - }; - }; - - config = mkIf cfg.enable { - users.users.git = { - group = "git"; - initialPassword = ""; - isSystemUser = true; - home = cfg.path; - homeMode = "755"; - createHome = true; - shell = "${pkgs.git}/bin/git-shell"; - openssh.authorizedKeys.keys = config.xnet.pubKeys; - }; - - users.groups.git = { }; - - programs.git = { - enable = true; - config = { - init = { - defaultBranch = "master"; - }; - safe = { - directory = "*"; - }; - }; - }; - }; -} diff --git a/machines/xnet/gitserver/gitweb.nix b/machines/xnet/gitserver/gitweb.nix deleted file mode 100644 index da9a5ee..0000000 --- a/machines/xnet/gitserver/gitweb.nix +++ /dev/null @@ -1,70 +0,0 @@ -{ config, lib, pkgs, ... }: -let - cfg = config.xnet.gitServer.gitweb; - inherit (lib) mkOption mkIf types; -in -{ - options.xnet.gitServer.gitweb = { - enable = mkOption { - type = types.bool; - default = false; - description = "Enable web interface to git repos."; - }; - - hostName = mkOption { - type = types.str; - default = "src.web.4kb.net"; - description = "Hostname the webUI is served from."; - }; - }; - - config = mkIf cfg.enable { - xnet.nginx.enable = true; - - users.users.nginx.extraGroups = [ "git" ]; - services.cgit.main = { - enable = true; - scanPath = config.xnet.gitServer.path; - package = pkgs.cgit-pink; - nginx = { - virtualHost = cfg.hostName; - location = "/"; - }; - extraConfig = '' - mimetype.gif=image/gif - mimetype.html=text/html - mimetype.jpeg=image/jpeg - mimetype.jpg=image/jpeg - mimetype.pdf=application/pdf - mimetype.png=image/png - mimetype.svg=image/svg+xml - readme=:readme - readme=:readme.md - readme=:readme.txt - readme=:README - readme=:README.md - readme=:README.txt - ''; - settings = { - about-filter = "${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh"; - source-filter = "${pkgs.cgit-pink}/lib/cgit/filters/syntax-highlighting.py"; - clone-url = "https://${cfg.hostName}/$CGIT_REPO_URL git@${cfg.hostName}:$CGIT_REPO_URL"; - enable-commit-graph = true; - enable-http-clone = false; - enable-index-links = true; - enable-remote-branches = true; - remove-suffix = true; - robots = "noindex, nofollow"; - root-desc = "What I cannot create, I do not understand"; - root-title = cfg.hostName; - section-from-path = true; - snapshots = "tar.gz tar.bz2 zip"; - }; - }; - - # services.nginx.virtualHosts."${cfg.gitweb.hostName}" = { - # useACMEHost = "4kb.net"; - # addSSL = true; - # }; - }; -} diff --git a/machines/xnet/monitoring/22604_rev2.json b/machines/xnet/monitoring/22604_rev2.json deleted file mode 100644 index 0adeb22..0000000 --- a/machines/xnet/monitoring/22604_rev2.json +++ /dev/null @@ -1,1565 +0,0 @@ -{ - "__inputs": [ - { - "name": "DS_PROMETHEUS", - "label": "Prometheus", - "description": "", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__elements": {}, - "__requires": [ - { - "type": "panel", - "id": "gauge", - "name": "Gauge", - "version": "" - }, - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "11.4.0" - }, - { - "type": "panel", - "id": "piechart", - "name": "Pie chart", - "version": "" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "stat", - "name": "Stat", - "version": "" - }, - { - "type": "panel", - "id": "table", - "name": "Table", - "version": "" - }, - { - "type": "panel", - "id": "timeseries", - "name": "Time series", - "version": "" - } - ], - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "description": "Extended smartctl-exporter Dashboard", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": null, - "links": [], - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "links": [], - "mappings": [], - "min": 20, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "celsius" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 13, - "x": 0, - "y": 0 - }, - "id": 1, - "options": { - "legend": { - "calcs": ["mean", "lastNotNull", "max"], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "expr": "avg(smartctl_device_temperature{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (instance,device,model_name)", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "1m", - "intervalFactor": 1, - "legendFormat": "{{instance}} {{device}} {{model_name}}", - "refId": "B" - } - ], - "title": "Disk Temperature", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 2, - "x": 13, - "y": 0 - }, - "id": 13, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(smartctl_device{instance=~\"$node\", device=~\"$disk\", interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"})", - "instant": false, - "range": true, - "refId": "A" - } - ], - "title": "Devices", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - } - }, - "fieldMinMax": false, - "mappings": [], - "min": 0, - "noValue": "0", - "unit": "none" - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": ["Value"], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 6, - "w": 2, - "x": 15, - "y": 0 - }, - "id": 16, - "options": { - "displayLabels": ["percent"], - "legend": { - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "values": ["value"] - }, - "pieType": "pie", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "/^Value$/", - "values": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(smartctl_device) by (interface)", - "format": "table", - "instant": true, - "intervalFactor": 1, - "legendFormat": "__auto", - "range": false, - "refId": "A" - } - ], - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], - "type": "piechart" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 7, - "x": 17, - "y": 0 - }, - "id": 2, - "options": { - "legend": { - "calcs": ["lastNotNull"], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "exemplar": false, - "expr": "smartctl_device_media_errors{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"} > 0", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}} {{device}} {{model_name}}", - "range": true, - "refId": "A" - } - ], - "title": "Media Errors", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "min": 0, - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 1 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 2, - "x": 13, - "y": 4 - }, - "id": 12, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "expr": "count((smartctl_device_smart_status{instance=~\"$node\",device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device) == 0)", - "format": "table", - "instant": true, - "intervalFactor": 1, - "legendFormat": "__auto", - "refId": "A" - } - ], - "title": "Unhealthy", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "bars", - "fillOpacity": 100, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "stepAfter", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "links": [], - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 1 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byValue", - "options": { - "op": "gte", - "reducer": "allIsZero", - "value": 0 - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": true, - "tooltip": true, - "viz": false - } - } - ] - } - ] - }, - "gridPos": { - "h": 4, - "w": 7, - "x": 17, - "y": 4 - }, - "id": 5, - "options": { - "legend": { - "calcs": ["lastNotNull"], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "expr": "smartctl_device_critical_warning{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"} > 0", - "format": "time_series", - "interval": "", - "intervalFactor": 2, - "legendFormat": "Critical Warnings {{device}}", - "range": true, - "refId": "A" - } - ], - "title": "Critical Warnings", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 0 - }, - { - "color": "green", - "value": 1 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 2, - "w": 2, - "x": 15, - "y": 6 - }, - "id": 15, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "center", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "exemplar": false, - "expr": "count(count(smartctl_device) by (instance))", - "instant": true, - "legendFormat": "nodes", - "range": false, - "refId": "A" - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "filterable": false, - "inspect": false, - "minWidth": 150 - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "unit", - "value": "dateTimeFromNow" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.align" - }, - { - "id": "displayName", - "value": "Last Checked" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Value #A" - }, - "properties": [ - { - "id": "displayName", - "value": "Power On Time" - }, - { - "id": "unit", - "value": "s" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "model_name" - }, - "properties": [ - { - "id": "displayName", - "value": "Device Model" - }, - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "device" - }, - "properties": [ - { - "id": "displayName", - "value": "Device" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Value #B" - }, - "properties": [ - { - "id": "displayName", - "value": "Power Cycles" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "instance" - }, - "properties": [ - { - "id": "displayName", - "value": "Node" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Node" - }, - "properties": [ - { - "id": "custom.width", - "value": 80 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Device" - }, - "properties": [ - { - "id": "custom.width", - "value": 107 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Last Checked" - }, - "properties": [ - { - "id": "custom.width", - "value": 142 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Device Model" - }, - "properties": [ - { - "id": "custom.width", - "value": 291 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Power On Time" - }, - "properties": [ - { - "id": "custom.width", - "value": 162 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Power Cycles" - }, - "properties": [ - { - "id": "custom.width", - "value": 117 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 13, - "x": 0, - "y": 8 - }, - "id": 10, - "interval": "1h", - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "enablePagination": true, - "fields": "", - "reducer": ["sum"], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Power On Time" - } - ] - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "expr": "max(smartctl_device_power_on_seconds{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (instance, device, model_name)", - "format": "table", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "__auto", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "expr": "max(smartctl_device_power_cycle_count{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (instance, device, model_name)", - "format": "table", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "Power Cycles", - "refId": "B" - } - ], - "title": "Disk Lifetime", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Total Written" - }, - "properties": [ - { - "id": "displayName", - "value": "Total Written" - }, - { - "id": "unit", - "value": "bytes" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "unit", - "value": "dateTimeFromNow" - }, - { - "id": "custom.align" - }, - { - "id": "displayName", - "value": "Last Checked" - }, - { - "id": "custom.hidden", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "model_name" - }, - "properties": [ - { - "id": "displayName", - "value": "Device Model" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "device" - }, - "properties": [ - { - "id": "displayName", - "value": "Device" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "instance" - }, - "properties": [ - { - "id": "displayName", - "value": "Node" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "serial_number" - }, - "properties": [ - { - "id": "displayName", - "value": "Serial Number" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Device" - }, - "properties": [ - { - "id": "custom.width", - "value": 115 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "instance" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Serial Number" - }, - "properties": [ - { - "id": "custom.width", - "value": 177 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "model_name" - }, - "properties": [ - { - "id": "custom.width", - "value": 277 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Total Written" - }, - "properties": [ - { - "id": "custom.width", - "value": 123 - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 11, - "x": 13, - "y": 8 - }, - "id": 3, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "enablePagination": true, - "fields": "", - "reducer": ["sum"], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Total Written" - } - ] - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "expr": "max(smartctl_device_bytes_written{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (device, instance, model_name, serial_number)", - "format": "table", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "__auto", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "exemplar": false, - "expr": "max((smartctl_device_attribute{instance=~\"$node\", device=~\"$disk\", attribute_name=\"Total_LBAs_Written\", attribute_value_type=\"raw\"} * on (instance,device) smartctl_device_block_size{instance=~\"$node\", device=~\"$disk\",blocks_type=\"physical\"}) * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (device, instance, model_name, serial_number)\n", - "format": "table", - "hide": false, - "instant": true, - "legendFormat": "__auto", - "range": false, - "refId": "A" - } - ], - "title": "Total Data Written on SSDs", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - }, - { - "id": "calculateField", - "options": { - "alias": "Total Written", - "binary": { - "left": { - "fixed": "" - }, - "right": { - "fixed": "" - } - }, - "mode": "reduceRow", - "reduce": { - "include": ["Value #B", "Value #A"], - "reducer": "sum" - }, - "replaceFields": false, - "window": { - "reducer": "mean", - "windowAlignment": "trailing", - "windowSize": 0.1, - "windowSizeMode": "percentage" - } - } - }, - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "Time", - "device", - "instance", - "model_name", - "serial_number", - "Total Written" - ] - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "displayName": "", - "mappings": [], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 75 - }, - { - "color": "red", - "value": 90 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 11, - "w": 13, - "x": 0, - "y": 18 - }, - "id": 8, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": ["last"], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "sizing": "auto" - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "expr": "sort_desc(100 - smartctl_device_attribute{instance=~\"$node\", device=~\"$disk\", attribute_name=\"Wear_Leveling_Count\",attribute_value_type=\"value\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"sat\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"})", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}:{{device}}", - "refId": "A" - } - ], - "title": "Wear Level on SATA Devices", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "displayName": "", - "mappings": [], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 75 - }, - { - "color": "red", - "value": 90 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 11, - "w": 11, - "x": 13, - "y": 18 - }, - "id": 14, - "options": { - "minVizHeight": 75, - "minVizWidth": 75, - "orientation": "auto", - "reduceOptions": { - "calcs": ["last"], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "sizing": "auto" - }, - "pluginVersion": "11.4.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "editorMode": "code", - "expr": "sort_desc(smartctl_device_percentage_used{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"nvme\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"})\n", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}:{{device}}", - "refId": "A" - } - ], - "title": "Wear Level on NVMe Devices", - "type": "gauge" - } - ], - "refresh": "1m", - "schemaVersion": 40, - "tags": ["prometheus", "node_exporter", "smartmon"], - "templating": { - "list": [ - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "definition": "label_values(smartctl_version,instance)", - "includeAll": true, - "multi": true, - "name": "node", - "options": [], - "query": { - "qryType": 1, - "query": "label_values(smartctl_version,instance)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "sort": 1, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "definition": "label_values(smartctl_device,device)", - "includeAll": true, - "multi": true, - "name": "disk", - "options": [], - "query": { - "qryType": 1, - "query": "label_values(smartctl_device,device)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "sort": 1, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "definition": "label_values(smartctl_device,interface)", - "includeAll": true, - "multi": true, - "name": "type", - "options": [], - "query": { - "qryType": 1, - "query": "label_values(smartctl_device,interface)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "sort": 1, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "definition": "label_values(smartctl_device,model_name)", - "includeAll": true, - "multi": true, - "name": "device_model", - "options": [], - "query": { - "qryType": 1, - "query": "label_values(smartctl_device,model_name)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "sort": 1, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "Prometheus" - }, - "definition": "label_values(smartctl_device,serial_number)", - "includeAll": true, - "multi": true, - "name": "serial_number", - "options": [], - "query": { - "qryType": 1, - "query": "label_values(smartctl_device,serial_number)", - "refId": "PrometheusVariableQueryEditor-VariableQuery" - }, - "refresh": 1, - "regex": "", - "sort": 1, - "type": "query" - } - ] - }, - "time": { - "from": "now-1h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": ["1m", "5m", "15m", "30m", "1h", "2h", "1d"] - }, - "timezone": "browser", - "title": "SMARTctl Exporter Dashboard", - "uid": "ce8j0dmrrej9cc", - "version": 47, - "weekStart": "", - "gnetId": 22604 -} diff --git a/machines/xnet/monitoring/default.nix b/machines/xnet/monitoring/default.nix deleted file mode 100644 index eed4963..0000000 --- a/machines/xnet/monitoring/default.nix +++ /dev/null @@ -1,58 +0,0 @@ -{ config, ... }: -let - inherit (builtins) - map - ; - - inherit (config.services.prometheus) exporters; -in -{ - imports = [ ./grafana.nix ]; - - - config = { - services.prometheus = { - enable = config.xnet.monitoring.hostGrafana; - globalConfig.scrape_interval = "1m"; - - exporters = { - node = { - enable = true; - openFirewall = true; - enabledCollectors = [ "processes" "systemd" ]; - }; - - # systemd = { - # enable = true; - # openFirewall = true; - # extraFlags = [ - # "--systemd.collector.enable-ip-accounting" - # "--systemd.collector.enable-restart-count" - # ]; - # }; - - smartctl = { - enable = true; - openFirewall = true; - }; - }; - - scrapeConfigs = - let - hosts = [ "iridium" "radon" ]; - in - [{ - job_name = "node"; - static_configs = [{ - targets = map (h: "${h}:${toString exporters.node.port}") hosts; - }]; - } - { - job_name = "smartctl"; - static_configs = [{ - targets = map (h: "${h}:${toString exporters.smartctl.port}") hosts; - }]; - }]; - }; - }; -} diff --git a/machines/xnet/monitoring/grafana.nix b/machines/xnet/monitoring/grafana.nix deleted file mode 100644 index e95318d..0000000 --- a/machines/xnet/monitoring/grafana.nix +++ /dev/null @@ -1,78 +0,0 @@ -{ config, pkgs, lib, ... }: -let - inherit (lib) mkOption mkIf types; - - inherit (config.xnet.monitoring) - hostGrafana - ; -in -{ - options.xnet.monitoring = { - hostGrafana = mkOption { - type = types.bool; - default = false; - description = "Scrape hosts and host the data."; - }; - }; - - config = mkIf hostGrafana { - services.grafana.provision = { - enable = true; - datasources.settings.datasources = [{ - name = "Prometheus"; - type = "prometheus"; - url = "http://localhost:9090"; - access = "proxy"; - editable = false; - }]; - - dashboards.settings.providers = [{ - name = "Fetched Dashboards"; - options.path = "/etc/grafana/dashboards"; - }]; - }; - - environment.etc = { - "grafana/dashboards/node-exporter.json" = { - user = "grafana"; - group = "grafana"; - source = pkgs.fetchurl { - url = "https://grafana.com/api/dashboards/1860/revisions/37/download"; - hash = "sha256-1DE1aaanRHHeCOMWDGdOS1wBXxOF84UXAjJzT5Ek6mM="; - }; - }; - "grafana/dashboards/smartctl-exporter.json" = { - user = "grafana"; - group = "grafana"; - source = ./grafana.nix; - # source = pkgs.fetchurl { - # url = "https://grafana.com/api/dashboards/22604/revisions/2/download"; - # hash = "sha256-ci8WE23fZ+ltEKFoUdNNVXsUIV0jqtas79ia2lYIo88="; - # }; - }; - }; - - services.grafana = { - enable = true; - settings.server = { - domain = "grafana.plaza.4kb.net"; - protocol = "socket"; - }; - settings."auth.anonymous" = { - enabled = true; - org_role = "Admin"; - }; - }; - - users.groups.grafana.members = [ "nginx" ]; - systemd.services.nginx.serviceConfig.ProtectHome = false; - - services.nginx.virtualHosts."${config.services.grafana.settings.server.domain}" = { - # useACMEHost = "4kb.net"; - # addSSL = true; - locations."/" = { - proxyPass = "http://unix:/${toString config.services.grafana.settings.server.socket}"; - }; - }; - }; -} diff --git a/machines/xnet/net/default.nix b/machines/xnet/net/default.nix deleted file mode 100644 index 7242855..0000000 --- a/machines/xnet/net/default.nix +++ /dev/null @@ -1,128 +0,0 @@ -{ config, lib, ... }: -let - cfg = config.xnet.net; - - inherit (builtins) - attrNames - attrValues - map - length - ; - - inherit (lib) - concatLines - genAttrs - mapAttrsToList - mergeAttrsList - mkOption - mkIf - types - ; - - # Networks are logical groupings of hosts wired together. This is - # accomplished using VLANS on the same layer 2 network. - # - # Hosts can hard code their spot on a network here. This module aims - # to generate network information for a host using this set of facts. - # The module should gracefully fall back to defaults if a given fact - # is not specified for the host consuming the module. - # - # A host will have it's /etc/hosts populated with all other hosts in - # a network to avoid a dynamic DNS server. - networks = { - - # Open network for xnet hosts - plaza = { - mask = 24; - vlan = 88; - hosts = { - t1 = "10.88.88.1"; - radon = "10.88.88.2"; - iridium = "10.88.88.3"; - }; - }; - - # Reserved network for kubernetes related nodes. A distinct network - # is created here for hosts that provide resources to workers. - # Generally, nodes on this network are spawned dynamically and are - # not a part of this NixOS config directly, although they may boot - # from assets served from a node here. - kubenet = { - mask = 24; - vlan = 42; - hosts = { - iridium = "10.88.42.254"; - }; - }; - }; -in -{ - imports = [ - ./sshd.nix - ./dns.nix - ]; - - options.xnet.net = { - join = mkOption { - type = types.listOf (types.enum (attrNames networks)); - default = [ ]; - description = "Available networks."; - }; - - interface = mkOption { - type = types.str; - default = ""; - description = "Network interface connecting to xnet."; - }; - }; - - config = mkIf ((length cfg.join) > 0) { - # networkd will handle this - networking.useDHCP = false; - - systemd.network = - let - name = config.networking.hostName; - - genConfig = net: { - netdevs."${toString networks.${net}.vlan}-${net}" = { - netdevConfig.Kind = "vlan"; - netdevConfig.Name = net; - vlanConfig.Id = networks.${net}.vlan; - }; - - networks."10-${cfg.interface}" = { - matchConfig.Name = cfg.interface; - vlan = cfg.join; - - # Accept DHCP on physical interface - DHCP = "ipv4"; - - # Network is up when xnet physical interface gets carrier - linkConfig.RequiredForOnline = "carrier"; - - # Allow resolved to resolve mDNS - networkConfig.MulticastDNS = true; - }; - - networks."${toString networks.${net}.vlan}-${net}" = { - matchConfig.Name = net; - - # Set up address for known host - address = [ "${networks.${net}.hosts.${name}}/${toString networks.${net}.mask}" ]; - }; - }; - - values = attrValues (genAttrs cfg.join genConfig); - in - { enable = true; } // mergeAttrsList values; - - networking.extraHosts = - let - genHosts = net: concatLines - (mapAttrsToList (name: value: "${value} ${name} ${name}.${net}.4kb.net") - networks.${net}.hosts); - in - concatLines (map (net: genHosts net) cfg.join); - }; -} diff --git a/machines/xnet/net/dns.nix b/machines/xnet/net/dns.nix deleted file mode 100644 index 0632fa9..0000000 --- a/machines/xnet/net/dns.nix +++ /dev/null @@ -1,38 +0,0 @@ -{ lib, ... }: -let - inherit (lib) - mkDefault - ; - - quad9 = [ - "9.9.9.9#dns.quad9.net" - "149.112.112.112#dns.quad9.net" - "2620:fe::fe#dns.quad9.net" - "2620:fe::9#dns.quad9.net" - ]; - - cloudflare = [ - "1.1.1.1#one.one.one.one" - "1.0.0.1#one.one.one.one" - "2606:4700:4700::1111#one.one.one.one" - "2606:4700:4700::1001#one.one.one.one" - ]; -in -{ - - networking.nameservers = mkDefault quad9; - - services.resolved = { - enable = true; - llmnr = "false"; - dnssec = "false"; - dnsovertls = mkDefault "opportunistic"; - fallbackDns = quad9 ++ cloudflare; - extraConfig = '' - MulticastDNS=yes - ''; - }; - - # Allow mDNS resolution - networking.firewall.allowedTCPPorts = [ 5353 ]; -} diff --git a/machines/xnet/net/sshd.nix b/machines/xnet/net/sshd.nix deleted file mode 100644 index 9d7976e..0000000 --- a/machines/xnet/net/sshd.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ config, lib, ... }: -let - cfg = config.xnet.net.sshd; - inherit (lib) mkOption mkIf types; -in -{ - options.xnet.net.sshd = { - enable = mkOption { - type = types.bool; - default = false; - description = "Enable hardened SSH service."; - }; - }; - - config = mkIf cfg.enable { - services.openssh = { - enable = true; - startWhenNeeded = true; - openFirewall = true; - hostKeys = [{ - path = "/persist/certs/ssh/ssh_host_ed25519_key"; - type = "ed25519"; - }]; - settings = { - UsePAM = false; - X11Forwarding = false; - PermitRootLogin = "prohibit-password"; - PasswordAuthentication = false; - Ciphers = [ "chacha20-poly1305@openssh.com" ]; - Macs = [ "hmac-sha2-512-etm@openssh.com" ]; - KexAlgorithms = [ "curve25519-sha256@libssh.org" ]; - }; - sftpServerExecutable = "internal-sftp"; - sftpFlags = [ "-f AUTHPRIV" "-l INFO" ]; - extraConfig = - let - pubkeyTypes = lib.strings.concatStringsSep "," [ - "sk-ssh-ed25519-cert-v01@openssh.com" - "ssh-ed25519-cert-v01@openssh.com" - "ssh-ed25519" - ]; - in - "PubkeyAcceptedKeyTypes ${pubkeyTypes}"; - }; - }; -} diff --git a/machines/xnet/nginx.nix b/machines/xnet/nginx.nix deleted file mode 100644 index eadd794..0000000 --- a/machines/xnet/nginx.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ config, lib, ... }: -let - cfg = config.xnet.nginx; - - inherit (lib) mkOption mkIf types; -in -{ - options.xnet.nginx = { - enable = mkOption { - type = types.bool; - default = false; - description = "Enable optimized nginx."; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = [ 80 443 ]; - - services.nginx = { - enable = true; - recommendedGzipSettings = true; - recommendedOptimisation = true; - recommendedProxySettings = true; - recommendedTlsSettings = true; - }; - }; -} diff --git a/machines/xnet/persist.nix b/machines/xnet/persist.nix deleted file mode 100644 index 10eb883..0000000 --- a/machines/xnet/persist.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ config, lib, ... }: -let - inherit (lib) - concatLists - isString - mkOption - types - ; - -in -{ - options.xnet.persist = mkOption { - type = types.listOf (types.either - types.str - (types.submodule { - options = { - path = mkOption { - type = types.str; - description = "Path to persist"; - }; - user = mkOption { - type = types.nullOr types.str; - default = null; - description = "User ID to set on the persisted directory"; - }; - group = mkOption { - type = types.nullOr types.str; - default = null; - description = "Group ID to set on the persisted directory"; - }; - - mode = mkOption { - type = types.nullOr types.str; - default = null; - description = "Permissions to set"; - }; - }; - })); - default = [ ]; - description = "Automatically persisted state."; - }; - - config.systemd.tmpfiles.rules = - let - mkEntry = entry: - let - path = if isString entry then entry else entry.path; - user = if isString entry then "-" else entry.user; - group = if isString entry then "-" else entry.group; - mode = if isString entry then "-" else entry.mode; - - dent = "d /persist${path} ${mode} ${user} ${group} - -"; - link = "L+ ${path} - - - - /persist${path}"; - perm = "Z ${mode} ${user} ${group} - -"; - in - [ dent link perm ]; - - entries = map mkEntry config.xnet.persist; - in - concatLists entries; -} diff --git a/mod/code/cses/.envrc b/mod/code/cses/.envrc deleted file mode 100644 index 1d953f4..0000000 --- a/mod/code/cses/.envrc +++ /dev/null @@ -1 +0,0 @@ -use nix diff --git a/mod/code/cses/default.nix b/mod/code/cses/default.nix deleted file mode 100644 index 6538eb8..0000000 --- a/mod/code/cses/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs ? import { }, ... }: - -pkgs.mkShell { - packages = with pkgs; [ - clang-tools - (writeShellScriptBin "cpprun" '' - TEMP=".tmp.cpp" - g++ -std=c++20 -O3 ./problems/"$1.cpp" -o $TEMP - ./$TEMP - rm $TEMP - '') - ]; -} diff --git a/mod/code/cses/problems/1068.cpp b/mod/code/cses/problems/1068.cpp deleted file mode 100644 index 8d05936..0000000 --- a/mod/code/cses/problems/1068.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -int main(int argc, char *argv[]) { - unsigned long long n; - std::cin >> n; - - while (n != 1) { - std::cout << n << " "; - - if (n % 2 == 0) - n /= 2; - else - n = (n * 3) + 1; - } - - std::cout << 1; -} diff --git a/mod/code/cses/problems/1069.cpp b/mod/code/cses/problems/1069.cpp deleted file mode 100644 index 232d2f7..0000000 --- a/mod/code/cses/problems/1069.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include - -int main() { - std::string input; - std::cin >> input; - - auto max = 0; - auto curr = '-'; - auto count = 0; - - for (auto const ch : input) { - if (curr != ch) { - curr = ch; - count = 0; - } - - count += 1; - max = std::max(max, count); - } - - std::cout << max; - return 0; -} diff --git a/mod/code/cses/problems/1070.cpp b/mod/code/cses/problems/1070.cpp deleted file mode 100644 index 131a644..0000000 --- a/mod/code/cses/problems/1070.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -int main() { - int n; - std::cin >> n; - - if (n == 1) - std::cout << 1; - else if (n < 4) - std::cout << "NO SOLUTION"; - else { - for (auto i = 2; i <= n; i += 2) std::cout << i << " "; - for (auto i = 1; i <= n; i += 2) std::cout << i << " "; - } -} diff --git a/mod/code/cses/problems/1071.cpp b/mod/code/cses/problems/1071.cpp deleted file mode 100644 index 778cc86..0000000 --- a/mod/code/cses/problems/1071.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#include - -using ull = unsigned long long; - -int main() { - int n; - std::cin >> n; - - while (n--) { - ull x, y; - std::cin >> y >> x; - - auto area = y * y; - auto perimeter = y + y + 1; - auto max = area + perimeter; - } -} diff --git a/mod/code/cses/problems/1083.cpp b/mod/code/cses/problems/1083.cpp deleted file mode 100644 index a3cd253..0000000 --- a/mod/code/cses/problems/1083.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include - -using ull = unsigned long long; - -int main(int argc, char *argv[]) { - ull n; - std::cin >> n; - - auto set = std::set{}; - - std::string numstr; - while (std::getline(std::cin, numstr, ' ')) set.insert(std::stoi(numstr)); - - for (auto i = 1; i <= n; i++) { - if (set.contains(i)) continue; - std::cout << i; - break; - } -} diff --git a/mod/code/cses/problems/1094.cpp b/mod/code/cses/problems/1094.cpp deleted file mode 100644 index 56f3886..0000000 --- a/mod/code/cses/problems/1094.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include - -using ull = unsigned long long; - -int main() { - ull n; - auto arr = std::vector{}; - - std::cin >> n; - - std::string type; - while (std::cin >> type) arr.push_back(std::stoi(type)); - - ull count = 0; - auto last = arr[0]; - - for (auto const num : arr) { - if (num > last) { - last = num; - continue; - } - count += last - num; - } - - std::cout << count; -} diff --git a/mod/misc/cv/cv.typ b/mod/misc/cv/cv.typ deleted file mode 100644 index 1120e39..0000000 --- a/mod/misc/cv/cv.typ +++ /dev/null @@ -1,250 +0,0 @@ -#let resume( - author: "", - email: "", - github: "", - personal-site: "", - accent-color: "#000000", - font: "New Computer Modern", - body, -) = { - set document(author: author, title: author) - set text( - // LaTeX style font - font: font, - size: 10pt, - lang: "en", - ligatures: false - ) - - set page( - margin: (0.5in), - paper: "us-letter", - ) - - show link: underline - show link: set text( - fill: rgb(accent-color), - ) - - show heading.where(level: 2): it => [ - #pad(top: 0pt, bottom: -10pt, [#smallcaps(it.body)]) - #line(length: 100%, stroke: 1pt) - ] - - // Accent Color Styling - show heading: set text( - fill: rgb(accent-color), - ) - - show heading.where(level: 1): it => [ - #set align(left) - #set text( - weight: 700, - size: 20pt, - ) - #pad(it.body) - ] - - [= #(author)] - - let contact-item(value, prefix: "", link-type: "") = { - if value != "" { - if link-type != "" { - link(link-type + value)[#(prefix + value)] - } else { - value - } - } - } - - // Personal Info - pad( - top: 0.25em, - align(left)[ - #{ - let items = ( - contact-item(email, link-type: "mailto:"), - contact-item(github, link-type: "https://"), - contact-item(personal-site, link-type: "https://"), - ) - items.filter(x => x != none).join(" | ") - } - ], - ) - - set par(justify: true) - - body -} - -#let generic-two-by-two( - top-left: "", - top-right: "", - bottom-left: "", - bottom-right: "", -) = { - [ - #top-left #h(1fr) #top-right \ - #bottom-left #h(1fr) #bottom-right - ] -} - -#let dates-helper( - start-date: "", - end-date: "", -) = { - start-date + " " + $dash.em$ + " " + end-date -} - -#let edu( - institution: "", - dates: "", - degree: "", - location: "", -) = { - generic-two-by-two( - top-left: strong(institution), - top-right: location, - bottom-left: emph(degree), - bottom-right: emph(dates), - ) -} - -#let work( - title: "", - dates: "", - company: "", - location: "", -) = { - generic-two-by-two( - top-left: strong(title), - top-right: dates, - bottom-left: company, - bottom-right: emph(location), - ) -} - -#show: resume.with( - author: "Kleidi Bujari", - email: "mail@4kb.net", - github: "github.com/kbujari", - personal-site: "4kb.net", -) - -== Education - -#edu( - institution: "Toronto Metropolitan University", - dates: dates-helper(start-date: "Sep 2020", end-date: "Apr 2025"), - location: "Ontario, Canada", - degree: "Bachelor's of Engineering, Computer Engineering", -) - -- *Relevant Coursework*: - Data Structures, Embedded Programming, Compilers, Digital Systems, Computer Networks -- *Extracurriculars*: - Member of student robotics design team, - teaching assistant for micro-processor courses. - -== Experience - -#work( - company: "Toronto Metropolitan University", - title: "Graduate Research Assistant", - dates: dates-helper(start-date: "May 2024", end-date: "Sep 2024"), - location: "Toronto, Canada", -) - -- Implemented transformations for hundreds of media files, - using ffmpeg and unix primitives to parallelize workload. -- Designed frontend with AstroJS to generate only static HTML, - ensuring compatibility with many hosting providers. - -#work( - company: "Canadian Broadcasting Corporation", - title: "Network Engineering Intern", - dates: dates-helper(start-date: "May 2023", end-date: "Apr 2024"), - location: "Toronto, Canada", -) - -- Designed custom PXE-boot implementation for hundreds of devices using NetBox, - eliminating manual configuration. -- Configured Hyper-Converged Proxmox cluster for 2024 Olympics, - saving \$250k+ with reused hardware and open software. -- Deployed vendor-agnostic routing observability from scratch, - with Prometheus metrics and Grafana dashboards. -- Mentored junior application developers in modern C++23 programming, - aiding in performance design and memory safety. - -#work( - company: "WSP Canada", - title: "Student Engineer", - dates: dates-helper(start-date: "May", end-date: "Aug") + ", 2021, 2022", - location: "Toronto, Canada", -) - -- Contributed to subway car control systems with modern C++20, - replacing legacy code with newer STL functions. -- Extended internal distributed filesystem with support for deduplication and compression, - reclaiming 30% of storage. -- Validated new electrical designs for power consumption, - cost efficiency, and viability with existing systems. -- Participated in reviewing and adjusting large scale electrical and structural engineering designs. - -== Projects - -*Custom Linux Distribution* --- -Designed entire Linux distribution used for hosting production servers, -daily desktop use, and embedded programming on a Raspberry Pi. -Using NixOS, it supports enabling only required functionality at build time. -Features systemd, -an in-memory root filesystem, -ZFS persistent storage with backups, -and extremely hardened networking. - -*ICER Compressor* --- -Image compression library written in Rust, -designed for deep-space communication. -Hand tuned for speed and portability by using only integer arithmetic, -no heap allocations, and no standard library by default. -Achieves practically instant compressions, even on microprocessors. - -*Kubernetes Cluster* --- -Bare-metal compute cluster managed with GitOps to be completely reproducible. -Uses Cilium CNI for fast eBPF networking and BGP load balancing, -FluxCD for cluster management, -and CEPH distributed storage for stateful workloads. -Running Prometheus, Loki and AlertManager for complete observability. - -*Mirrorlist Generator* --- -Fetches Arch Linux package mirrors, -filtering them based on user parameters. -Sorts and outputs formatted data compliant with the pacman package manager. -Heavily outperforms default Python implementation. - -*Toronto Metropolitan Robotics* --- -Member of university design team working on space focused automated robotics. -Designed custom STM32 hardware with various interfaces (SPI, I2C, etc.) for controlling motor functions on robot. -Worked alongside various subteams to deliver a competition ready autonomous system. - -== Skills - -- *Languages*: #( - "Rust", - "C++", - "Nix", - "Haskell", - "TypeScript", - "Lua", - "Python", - ).join(", ") - -- *Technologies*: #( - "Linux", - "Compilers", - "Virtualisation", - "Terraform", - "Ansible", - "Computer Networks", - "Frontend (Svelte, Astro)" - ).join(", ") diff --git a/mod/misc/cv/default.nix b/mod/misc/cv/default.nix deleted file mode 100644 index b663475..0000000 --- a/mod/misc/cv/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ pkgs, ... }: - -pkgs.runCommand "cv" { } '' - mkdir -p $out/share - ${pkgs.typst}/bin/typst compile ${./cv.typ} $out/share/cv.pdf -'' diff --git a/mod/nix/readTree/default.nix b/mod/nix/readTree/default.nix deleted file mode 100644 index 4a745ce..0000000 --- a/mod/nix/readTree/default.nix +++ /dev/null @@ -1,326 +0,0 @@ -# Copyright (c) 2019 Vincent Ambo -# Copyright (c) 2020-2021 The TVL Authors -# SPDX-License-Identifier: MIT -# -# Provides a function to automatically read a filesystem structure -# into a Nix attribute set. -# -# Called with an attribute set taking the following arguments: -# -# path: Path to a directory from which to start reading the tree. -# -# args: Argument set to pass to each imported file. -# -# filter: Function to filter `args` based on the tree location. This should -# be a function of the form `args -> location -> args`, where the -# location is a list of strings representing the path components of -# the current readTree target. Optional. -{ ... }: - -let - inherit (builtins) - attrNames - concatMap - concatStringsSep - elem - elemAt - filter - hasAttr - head - isAttrs - listToAttrs - map - match - readDir - substring; - - argsWithPath = args: parts: - let meta.locatedAt = parts; - in meta // (if isAttrs args then args else args meta); - - readDirVisible = path: - let - children = readDir path; - # skip hidden files, except for those that contain special instructions to readTree - isVisible = f: f == ".skip-subtree" || f == ".skip-tree" || (substring 0 1 f) != "."; - names = filter isVisible (attrNames children); - in - listToAttrs (map - (name: { - inherit name; - value = children.${name}; - }) - names); - - # Create a mark containing the location of this attribute and - # a list of all child attribute names added by readTree. - marker = parts: children: { - __readTree = parts; - __readTreeChildren = builtins.attrNames children; - }; - - # Create a label from a target's tree location. - mkLabel = target: - let label = concatStringsSep "/" target.__readTree; - in if target ? __subtarget - then "${label}:${target.__subtarget}" - else label; - - # Merge two attribute sets, but place attributes in `passthru` via - # `overrideAttrs` for derivation targets that support it. - merge = a: b: - if a ? overrideAttrs - then - a.overrideAttrs - (prev: { - passthru = (prev.passthru or { }) // b; - }) - else a // b; - - # Import a file and enforce our calling convention - importFile = args: scopedArgs: path: parts: filter: - let - importedFile = - if scopedArgs != { } && builtins ? scopedImport # For tvix - then builtins.scopedImport scopedArgs path - else import path; - pathType = builtins.typeOf importedFile; - in - if pathType != "lambda" - then throw "readTree: trying to import ${toString path}, but it’s a ${pathType}, you need to make it a function like { depot, pkgs, ... }" - else importedFile (filter parts (argsWithPath args parts)); - - nixFileName = file: - let res = match "(.*)\\.nix" file; - in if res == null then null else head res; - - # Internal implementation of readTree, which handles things like the - # skipping of trees and subtrees. - # - # This method returns an attribute sets with either of two shapes: - # - # { ok = ...; } # a tree was read successfully - # { skip = true; } # a tree was skipped - # - # The higher-level `readTree` method assembles the final attribute - # set out of these results at the top-level, and the internal - # `children` implementation unwraps and processes nested trees. - readTreeImpl = { args, initPath, rootDir, parts, argsFilter, scopedArgs }: - let - dir = readDirVisible initPath; - - # Determine whether any part of this tree should be skipped. - # - # Adding a `.skip-subtree` file will still allow the import of - # the current node's "default.nix" file, but stop recursion - # there. - # - # Adding a `.skip-tree` file will completely ignore the folder - # in which this file is located. - skipTree = hasAttr ".skip-tree" dir; - skipSubtree = skipTree || hasAttr ".skip-subtree" dir; - - joinChild = c: initPath + ("/" + c); - - self = - if rootDir - then { __readTree = [ ]; } - else importFile args scopedArgs initPath parts argsFilter; - - # Import subdirectories of the current one, unless any skip - # instructions exist. - # - # This file can optionally contain information on why the tree - # should be ignored, but its content is not inspected by - # readTree - filterDir = f: dir."${f}" == "directory"; - filteredChildren = map - (c: { - name = c; - value = readTreeImpl { - inherit argsFilter scopedArgs; - args = args; - initPath = (joinChild c); - rootDir = false; - parts = (parts ++ [ c ]); - }; - }) - (filter filterDir (attrNames dir)); - - # Remove skipped children from the final set, and unwrap the - # result set. - children = - if skipSubtree then [ ] - else map ({ name, value }: { inherit name; value = value.ok; }) (filter (child: child.value ? ok) filteredChildren); - - # Import Nix files - nixFiles = - if skipSubtree then [ ] - else filter (f: f != null) (map nixFileName (attrNames dir)); - nixChildren = map - (c: - let - p = joinChild (c + ".nix"); - childParts = parts ++ [ c ]; - imported = importFile args scopedArgs p childParts argsFilter; - in - { - name = c; - value = - if isAttrs imported - then merge imported (marker childParts { }) - else imported; - }) - nixFiles; - - nodeValue = if dir ? "default.nix" then self else { }; - - allChildren = listToAttrs ( - if dir ? "default.nix" - then children - else nixChildren ++ children - ); - - in - if skipTree - then { skip = true; } - else { - ok = - if isAttrs nodeValue - then merge nodeValue (allChildren // (marker parts allChildren)) - else nodeValue; - }; - - # Top-level implementation of readTree itself. - readTree = args: - let - tree = readTreeImpl args; - in - if tree ? skip - then throw "Top-level folder has a .skip-tree marker and could not be read by readTree!" - else tree.ok; - - # Helper function to fetch subtargets from a target. This is a - # temporary helper to warn on the use of the `meta.targets` - # attribute, which is deprecated in favour of `meta.ci.targets`. - subtargets = node: - let targets = (node.meta.targets or [ ]) ++ (node.meta.ci.targets or [ ]); - in if node ? meta.targets then - builtins.trace '' - Warning: The meta.targets attribute is deprecated. - - Please move the subtargets of //${mkLabel node} to the - meta.ci.targets attribute. -  - '' - targets else targets; - - # Function which can be used to find all readTree targets within an - # attribute set. - # - # This function will gather physical targets, that is targets which - # correspond directly to a location in the repository, as well as - # subtargets (specified in the meta.ci.targets attribute of a node). - # - # This can be used to discover targets for inclusion in CI - # pipelines. - # - # Called with the arguments: - # - # eligible: Function to determine whether the given derivation - # should be included in the build. - gather = eligible: node: - if node ? __readTree then - # Include the node itself if it is eligible. - (if eligible node then [ node ] else [ ]) - # Include eligible children of the node - ++ concatMap (gather eligible) (map (attr: node."${attr}") node.__readTreeChildren) - # Include specified sub-targets of the node - ++ filter eligible (map - (k: (node."${k}" or { }) // { - # Keep the same tree location, but explicitly mark this - # node as a subtarget. - __readTree = node.__readTree; - __readTreeChildren = [ ]; - __subtarget = k; - }) - (subtargets node)) - else [ ]; - - # Determine whether a given value is a derivation. - # Copied from nixpkgs/lib for cases where lib is not available yet. - isDerivation = x: isAttrs x && x ? type && x.type == "derivation"; -in -{ - inherit gather mkLabel; - - __functor = _: - { path - , args - , filter ? (_parts: x: x) - , scopedArgs ? { } - }: - readTree { - inherit args scopedArgs; - argsFilter = filter; - initPath = path; - rootDir = true; - parts = [ ]; - }; - - # In addition to readTree itself, some functionality is exposed that - # is useful for users of readTree. - - # Create a readTree filter disallowing access to the specified - # top-level folder in the repository, except for specific exceptions - # specified by their (full) paths. - # - # Called with the arguments: - # - # folder: Name of the restricted top-level folder (e.g. 'experimental') - # - # exceptions: List of readTree parts (e.g. [ [ "services" "some-app" ] ]), - # which should be able to access the restricted folder. - # - # reason: Textual explanation for the restriction (included in errors) - restrictFolder = { folder, exceptions ? [ ], reason }: parts: args: - if (elemAt parts 0) == folder || elem parts exceptions - then args - else args // { - depot = args.depot // { - "${folder}" = throw '' - Access to targets under //${folder} is not permitted from - other repository paths. Specific exceptions are configured - at the top-level. - - ${reason} - At location: ${builtins.concatStringsSep "." parts} - ''; - }; - }; - - # This definition of fix is identical to .lib.fix, but is - # provided here for cases where readTree is used before nixpkgs can - # be imported. - # - # It is often required to create the args attribute set. - fix = f: let x = f x; in x; - - # Takes an attribute set and adds a meta.ci.targets attribute to it - # which contains all direct children of the attribute set which are - # derivations. - # - # Type: attrs -> attrs - drvTargets = attrs: - attrs // { - # preserve .meta from original attrs - meta = (attrs.meta or { }) // { - # preserve .meta.ci (except .targets) from original attrs - ci = (attrs.meta.ci or { }) // { - targets = builtins.filter - (x: isDerivation attrs."${x}") - (builtins.attrNames attrs); - }; - }; - }; -} diff --git a/mod/tools/perf-flamegraph.nix b/mod/tools/perf-flamegraph.nix deleted file mode 100644 index b472b74..0000000 --- a/mod/tools/perf-flamegraph.nix +++ /dev/null @@ -1,12 +0,0 @@ -# Script that collects perf timing for the execution of a command and writes a -# flamegraph to stdout -{ pkgs, ... }: - -pkgs.writeShellScriptBin "perf-flamegraph" '' - set -euo pipefail - - ${pkgs.linuxPackages.perf}/bin/perf record -g --call-graph dwarf -F max "$@" - ${pkgs.linuxPackages.perf}/bin/perf script \ - | ${pkgs.flamegraph}/bin/stackcollapse-perf.pl \ - | ${pkgs.flamegraph}/bin/flamegraph.pl -'' diff --git a/mod/tools/typst/default.nix b/mod/tools/typst/default.nix deleted file mode 100644 index 1871cc6..0000000 --- a/mod/tools/typst/default.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ pkgs, ... }: -let - version = "0.0.0"; - - depotPackages = pkgs.stdenvNoCC.mkDerivation { - pname = "depot-typst-packages"; - inherit version; - src = ./packages; - phases = [ "installPhase" ]; - - installPhase = '' - for pkg in $src/*; do - name=$(basename $pkg) - mkdir -p $out/share/typst/packages/depot/$name/${version} - cp -rv $pkg/* $out/share/typst/packages/depot/$name/${version}/ - done - ''; - }; -in -{ - inherit depotPackages; - - writeEnv = pkgs.mkShellNoCC { - packages = [ - pkgs.typst - pkgs.typstyle - pkgs.tinymist - depotPackages - ]; - - shellHook = "export TYPST_PACKAGE_PATH=${depotPackages}/share/typst"; - # shellHook = "alias typst='XDG_DATA_HOME=${depotPackages}/share typst'"; - }; -} diff --git a/mod/tools/typst/main.typ b/mod/tools/typst/main.typ deleted file mode 100644 index c6be2b4..0000000 --- a/mod/tools/typst/main.typ +++ /dev/null @@ -1 +0,0 @@ -#import "@depot/tmu:0.0.0": * diff --git a/mod/tools/typst/packages/tmu/template.typ b/mod/tools/typst/packages/tmu/template.typ deleted file mode 100644 index 721e7b6..0000000 --- a/mod/tools/typst/packages/tmu/template.typ +++ /dev/null @@ -1,37 +0,0 @@ -#let report( - title: [], - course: [], - semester: [], - authors: (), - doc, -) = { - set page( - paper: "us-letter", - margin: 1.5in, - numbering: "1", - header: align(horizon, strong(course + h(1fr) + semester)), - ) - - set align(center) - text(18pt, strong(title)) - linebreak() - text(12pt, "Toronto Metropolitan University") - - v(24pt) - - grid( - columns: (1fr,) * calc.min(authors.len(), 4), - row-gutter: 12pt, - ..authors.map(a => [ - #a.name \ - #a.id \ - #link("mailto:" + a.email) - ]) - ) - - set align(left) - set par(justify: true) - v(24pt) - - doc -} diff --git a/mod/tools/typst/packages/tmu/typst.toml b/mod/tools/typst/packages/tmu/typst.toml deleted file mode 100644 index caf96d7..0000000 --- a/mod/tools/typst/packages/tmu/typst.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "tmu" -version = "0.0.0" -entrypoint = "template.typ" -authors = ["Kleidi Bujari"] -license = "MIT" -description = "TMU engineering report template" diff --git a/mod/users/kle/default.nix b/mod/users/kle/default.nix deleted file mode 100644 index fca1ec7..0000000 --- a/mod/users/kle/default.nix +++ /dev/null @@ -1,56 +0,0 @@ -{ pkgs, depot, ... }: -let - inherit (builtins) - attrValues - ; - - inherit (depot.tools) - perf-flamegraph - ; - - - gitKeys = builtins.fetchurl { - url = "https://github.com/kbujari.keys"; - sha256 = "1kskbiyqvjz1wsmcrgh9v0iryf33y70zk503z0m96wmzdjllmc94"; - }; - - keys = { - t480 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEOw8YEbHsKy38JHp9W1wcxxZgWCDgnabOXccZUN5ddd"; - t1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP7T2uWJFUu8aFZZgQusGKyEMocb2pKbHLDad2eIJus9"; - }; -in -{ - inherit keys; - inherit gitKeys; - - nixos = { - initialHashedPassword = "$6$R4dDhaftX.vapGMd$.An36hlp3DXfkIC7bPZ0MDPo6Zvpk8JRrhy2LES.lZZj6JDa74oJkcMW3DCsIySvLJxOPXSShos0TpgJ/w0fH/"; - isNormalUser = true; - shell = pkgs.fish; - home = "/persist/usr/kle"; - createHome = true; - extraGroups = [ "wheel" "users" "networkmanager" "video" "corectrl" ]; - packages = with pkgs; [ - btop - curl - fzf - guvcview - jq - lynx - neovim - perf-flamegraph - ranger - ripgrep - rsync - sshfs - tree - zip - - # nix debugging - nixd - nixpkgs-fmt - ]; - - openssh.authorizedKeys.keys = attrValues keys; - }; -} diff --git a/mod/web/blog/.gitignore b/mod/web/blog/.gitignore deleted file mode 100644 index decc3f8..0000000 --- a/mod/web/blog/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -public/ -nohup.out diff --git a/mod/web/blog/config.toml b/mod/web/blog/config.toml deleted file mode 100644 index 69c389b..0000000 --- a/mod/web/blog/config.toml +++ /dev/null @@ -1,8 +0,0 @@ -base_url = "https://4kb.net" -build_search_index = false -compile_sass = false -generate_feeds = true -minify_html = true - -[markdown] -highlight_code = false diff --git a/mod/web/blog/content/posts/_index.md b/mod/web/blog/content/posts/_index.md deleted file mode 100644 index 9efc62e..0000000 --- a/mod/web/blog/content/posts/_index.md +++ /dev/null @@ -1,6 +0,0 @@ -+++ -title = "Posts" -sort_by = "date" -page_template = "post.html" -redirect_to="/" -+++ diff --git a/mod/web/blog/content/posts/deterministic-hostnames.md b/mod/web/blog/content/posts/deterministic-hostnames.md deleted file mode 100644 index 43dc7d8..0000000 --- a/mod/web/blog/content/posts/deterministic-hostnames.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: "Deterministic and unique network hostnames" -date: "2024-11-10" ---- - -As part of building out a Kubernetes cluster, I wanted to build and distribute a -single OS image to create stateless worker nodes. Using network booting, and -some clever tricks to differentiate nodes, we can create a scaleable and -efficient farm of workers for a cluster that don't even need disks. - -The idea came from a plan to build a cluster using the -[compute blade](https://computeblade.com/), and a few Raspberry Pi SBCs I -already own. Running the cluster from an SD card is not recommended due to the -not-so-great reliability of the flash used by most manufacturers, so I wanted to -try PXE booting each Pi to save money rather than purchasing an SSD for each -one. The compute blades do support an NVMe disk, but I plan to use those for a -storage cluster later, so they need to remain empty. - -## Base image - -Alpine Linux has been my preferred server OS for a long time. It provides a very -lightweight base system, and bundles an excellent bootstrapping system, -[apkovl](https://wiki.alpinelinux.org/wiki/Alpine_local_backup), that allows the -user to save a set of customisations to an system as an overlay to a stock -Alpine live image. In other words, we can create our image once, save the -changes as an `apkovl.tar.gz` file, and apply the same changes to a base system -on boot. This file can even be provided as a -[kernel parameter](https://wiki.alpinelinux.org/wiki/PXE_boot#Guide_to_options) -and will be fetched from a remote webserver automatically! - -Since the image and configuration will be shipped to the node via the network, -an added benefit of using Alpine is its tiny space consumption. I'm not using -enough nodes for this to really matter, but it's a cool optimization regardless. - -## Differentiating the nodes - -One of the main goals of this project is that there should be no persistent -storage required outside the boot image itself. Since every node will download -and generate the same root file-system on startup, the first problem that arises -is how the nodes will identify themselves both on the network and the cluster, -given that it's not possible to name them ahead of time. In other words, any -given node has to generate a unique hostname that won't collide with other -workers, and that will be the same each time that node boots. - -Since these nodes will not have a predefined name, we have to rely on -characteristics of the hardware to differentiate each one. The hardware MAC -address is perfect for this, since it's unique to to each node and will not be -wiped away after the node reboots. On a system like Linux that exposes its -hardware through a _sysfs_, we can find a file containing the address at -`/sys/class/net/eth0/address`. I don't really like the idea of attaching the -literal MAC address of the node to its network hostname, since it's a security -risk, and a bit too verbose. Instead, we can transform it into something safer -using a `sha1sum`, which is already present on our Alpine base system: - -```console -sha1sum /sys/class/net/eth0/address | head -c 6 | awk '{print "worker-" $0}' -``` - -### Applying the new name - -Ideally, the node should apply its generated hostname before reaching out for an -address over DHCP or joining the cluster. We can make sure it happens before any -traffic is sent out by adding a `pre-up` command to the right interface in -`/etc/network/interfaces`: - -``` - -... - -auto eth0 -iface eth0 inet dhcp - pre-up sha1sum /sys/class/net/eth0/address | head -c 6 | awk '{print "worker-" $0}' > /etc/hostname - -... -``` - -The VM I tested with looks outputs `worker-e2fae8`. Pretty clean result, and if -you want to know the physical node that maps to each hostname, you can take note -of the MAC address beforehand and generate the same hash on another computer to -match them up. diff --git a/mod/web/blog/content/posts/nohup.md b/mod/web/blog/content/posts/nohup.md deleted file mode 100644 index 64f7983..0000000 --- a/mod/web/blog/content/posts/nohup.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "Spawning background processes" -date: "2024-11-17" ---- - -Working in a terminal, -I often pair my editor with a background process watching files. -Before reaching for terminal multiplexers, -see if you can get away with simple tty job control. -Spawn the background process, -still attached to the terminal instance: - -``` -program args & -``` - -Also redirect its output to a file, -for when the process writes to the tty from the background: - -``` -nohup program args & -``` - -Extra reading: - -- diff --git a/mod/web/blog/content/posts/stateless-compute-networks.md b/mod/web/blog/content/posts/stateless-compute-networks.md deleted file mode 100644 index bac9e5d..0000000 --- a/mod/web/blog/content/posts/stateless-compute-networks.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: "On stateless compute networks" -date: "2024-11-10" -draft: true ---- - -On the topic of distributed systems and clustering, -I am quite invested in the idea of compute nodes that rely entirely on the network for configuration. -Arbitrary nodes can join a pre-existing cluster, -offering their CPU time and memory for computation without relying on any pre-existing configuration on the node itself. -In other words, any computer could pick up work, -only needing power and a network connection to the cluster. - -Perhaps this eventually leads into a "self-healing" cluster where only one node is manually bootstrapped, -which then serves a _configuration endpoint_ for other stateless nodes to reach out to for their instructions, -which they will then also serve once they are themselves ready. - -Early revisions of these notes mention Kubernetes, -but I am also trying to achieve similar results with NixOS on a custom project. -In any case, these are my ever-updating notes towards a general implementation of a stateless distributed systems architecture. - -## Self healing cluster - -Assuming control of an external DHCP server, -a self healing Kubernetes cluster would be feasible, -with the PXE boot artifacts supplied by the cluster itself. -That is, as long as one node is running the pod hosting the artifacts on a given endpoint, -other nodes can boot those artifacts and join the cluster, -thereby being able to host the artifacts as well. - -## Configuration endpoint - -The nodes shouldn't require a disk installed to be able to join the network. -Rather, the lofty goal of zero-configuration compute nodes passes the job of node initialization to the supporting network. -This is accomplished with PXE boot instructions supplied over DHCP. - -I delegate the following tasks to a single node in the subnet: - -- Gateway: Optional outbound connections if required -- DHCP server: Cluster IPAM -- TFTP and HTTP server: Serves iPXE firmware and kernel/initrd artifacts diff --git a/mod/web/blog/content/posts/vim-compilers.md b/mod/web/blog/content/posts/vim-compilers.md deleted file mode 100644 index f735228..0000000 --- a/mod/web/blog/content/posts/vim-compilers.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: "Neovim's built-in compilers" -date: "2025-01-17" ---- - -Today I learned that neovim's `:make` comes with many -[backends](https://neovim.io/doc/user/quickfix.html#_6.-selecting-a-compiler) -already configured. -This works for checking c/cpp and python files, among other, but I was -most interested to see [Typst] and [Pandoc] listed as well. - -I was looking to add this functionality with a plugin or implementing it -manually, -where a document can be compiled on the fly from the editor. -After setting `:compiler pandoc`, -generating a pdf is done with `:make pdf`, -with other pandoc options just appended afterwards if needed. - -[typst]: https://typst.app -[pandoc]: https://github.com/jgm/pandoc diff --git a/mod/web/blog/default.nix b/mod/web/blog/default.nix deleted file mode 100644 index 1f91a68..0000000 --- a/mod/web/blog/default.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ pkgs, depot, ... }: -let - inherit (pkgs) - symlinkJoin - ; - - posts = pkgs.stdenvNoCC.mkDerivation { - pname = "4kb.net"; - version = "1.0"; - src = ./.; - - nativeBuildInputs = with pkgs; [ - zola - ]; - - buildPhase = "zola build"; - installPhase = '' - mkdir -p $out - cp -r public/* $out/ - ''; - }; - -in -symlinkJoin { - name = "site"; - paths = [ - posts - depot.misc.cv - ]; -} diff --git a/mod/web/blog/templates/404.html b/mod/web/blog/templates/404.html deleted file mode 100644 index a4669df..0000000 --- a/mod/web/blog/templates/404.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} - -{% block content %} - - Error 404! - - -404! -{% endblock content %} diff --git a/mod/web/blog/templates/base.html b/mod/web/blog/templates/base.html deleted file mode 100644 index 5a91df5..0000000 --- a/mod/web/blog/templates/base.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - {% block title %}4kb.net{% endblock title %} - - - -
- Kleidi Bujari <mail@4kb.net> - -
-
{% block content %} {% endblock %}
- - diff --git a/mod/web/blog/templates/index.html b/mod/web/blog/templates/index.html deleted file mode 100644 index 6e8a2af..0000000 --- a/mod/web/blog/templates/index.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "base.html" %} {% block content %} - -
- {% set section = get_section(path="posts/_index.md") %} - -
    - {% for page in section.pages %} -
  • - {{ page.date }} - {{ page.title }} -
  • - {% endfor %} -
- - {% endblock content %} -
diff --git a/mod/web/blog/templates/post.html b/mod/web/blog/templates/post.html deleted file mode 100644 index b819520..0000000 --- a/mod/web/blog/templates/post.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "base.html" %} - -{% block title%} - {{ page.title }} - {{ super() }} -{% endblock title %} - -{% block content %} - -

{{ page.title }}

-
- {{ page.content | safe }} -
- -{% endblock content %} diff --git a/mod/web/resistors/default.nix b/mod/web/resistors/default.nix deleted file mode 100644 index 5fca68b..0000000 --- a/mod/web/resistors/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ pkgs, ... }: - -pkgs.stdenvNoCC.mkDerivation { - pname = "resistors"; - version = "0.0"; - src = ./.; - - buildInputs = [ pkgs.nodePackages.prettier ]; - - phases = [ "installPhase" ]; - installPhase = "mkdir -p $out; cp -r $src/*.{css,html} $out/"; -} diff --git a/mod/web/resistors/index.html b/mod/web/resistors/index.html deleted file mode 100644 index 0683f49..0000000 --- a/mod/web/resistors/index.html +++ /dev/null @@ -1,339 +0,0 @@ - - - - rs-calc - - - - - -

rs-calc

- - - - - - -
-

- forked from - this, with removed analytics, cleaned up code. - my github -

-
- - - - diff --git a/mod/web/resistors/styles.css b/mod/web/resistors/styles.css deleted file mode 100644 index e2823e4..0000000 --- a/mod/web/resistors/styles.css +++ /dev/null @@ -1,127 +0,0 @@ -* { - /*font-family: 'firacode';*/ - text-rendering: optimizeLegibility; -} - -body { - margin: 10px 20px; - color: #cccccc; - background: #252525; - background-attachment: fixed !important; -} - -h1 { - font-weight: normal; - font-size: 50px; - margin: -10px 0 10px 0; -} - -h2 { - font-weight: normal; - font-size: 15px; -} - -strong { - font-weight: normal; -} - -footer { - position: absolute; - font-size: 12px; - color: #555; - bottom: 0; - left: 0; - right: 0; - padding: 20px; -} - -a { - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -footer a { - color: #775; -} - -#resistor-query { - font-size: 20px; - border: 1px solid #999; - color: #fff; - background-color: #444; - outline: none; - width: 180px; -} - -.resistor-stripe { - width: 80px; - height: 150px; - display: inline-block; - padding: 4px 4px; -} - -.resistor-smt { - display: inline-block; - padding: 0px 10px; - font-size: 35px; - background-color: #000; - border-left: 15px solid silver; - border-right: 15px solid silver; - border-top: 1px solid silver; - border-bottom: 1px solid silver; - margin-right: 10px; -} - -@media print, screen and (max-width: 520px) { - .resistor-stripe { - width: 55px; - font-size: 80%; - height: 100px; - } - .resistor-smt { - font-size: 25px; - border-left: 12px solid silver; - border-right: 12px solid silver; - } -} - -@media print, screen and (max-width: 410px) { - .resistor-stripe { - width: 35px; - font-size: 55%; - height: 70px; - } - .resistor-smt { - font-size: 18px; - border-left: 8px solid silver; - border-right: 8px solid silver; - } -} - -@media print, screen and (max-width: 300px) { - .resistor-stripe { - width: 25px; - font-size: 50%; - height: 60px; - padding: 2px; - } - .resistor-smt { - font-size: 13px; - border-left: 6px solid silver; - border-right: 6px solid silver; - padding: 2px 4px; - } -} - -@media print, screen and (max-height: 660px) { - .resistor-stripe { - height: 55px; - } - footer { - position: inherit; - padding: 10px 0; - } -} diff --git a/modules/dns.nix b/modules/dns.nix new file mode 100644 index 0000000..0632fa9 --- /dev/null +++ b/modules/dns.nix @@ -0,0 +1,38 @@ +{ lib, ... }: +let + inherit (lib) + mkDefault + ; + + quad9 = [ + "9.9.9.9#dns.quad9.net" + "149.112.112.112#dns.quad9.net" + "2620:fe::fe#dns.quad9.net" + "2620:fe::9#dns.quad9.net" + ]; + + cloudflare = [ + "1.1.1.1#one.one.one.one" + "1.0.0.1#one.one.one.one" + "2606:4700:4700::1111#one.one.one.one" + "2606:4700:4700::1001#one.one.one.one" + ]; +in +{ + + networking.nameservers = mkDefault quad9; + + services.resolved = { + enable = true; + llmnr = "false"; + dnssec = "false"; + dnsovertls = mkDefault "opportunistic"; + fallbackDns = quad9 ++ cloudflare; + extraConfig = '' + MulticastDNS=yes + ''; + }; + + # Allow mDNS resolution + networking.firewall.allowedTCPPorts = [ 5353 ]; +} diff --git a/modules/users.nix b/modules/users.nix new file mode 100644 index 0000000..f9f7df3 --- /dev/null +++ b/modules/users.nix @@ -0,0 +1,48 @@ +{ pkgs, perSystem, ... }: +let + inherit (builtins) + attrValues; + + inherit (perSystem.self) + perf-flamegraph + ; + + keys = { + t480 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEOw8YEbHsKy38JHp9W1wcxxZgWCDgnabOXccZUN5ddd"; + t1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP7T2uWJFUu8aFZZgQusGKyEMocb2pKbHLDad2eIJus9"; + }; +in +{ + kle = { + initialHashedPassword = "$6$R4dDhaftX.vapGMd$.An36hlp3DXfkIC7bPZ0MDPo6Zvpk8JRrhy2LES.lZZj6JDa74oJkcMW3DCsIySvLJxOPXSShos0TpgJ/w0fH/"; + isNormalUser = true; + shell = pkgs.fish; + home = "/persist/usr/kle"; + createHome = true; + extraGroups = [ "wheel" "users" "networkmanager" "video" "corectrl" ]; + packages = with pkgs; [ + btop + curl + emacs + fzf + guvcview + jq + jujutsu + lynx + neovim + perf-flamegraph + ranger + ripgrep + rsync + sshfs + tree + zip + + # nix debugging + nixd + nixpkgs-fmt + ]; + + openssh.authorizedKeys.keys = attrValues keys; + }; +} diff --git a/modules/xnet/README b/modules/xnet/README new file mode 100644 index 0000000..ab1c28c --- /dev/null +++ b/modules/xnet/README @@ -0,0 +1,18 @@ +xnet +==== + +Base configuration for any machines running a standard xnet +configuration. + +Notable features: + + - Root filesystem running in RAM + - Automated ZFS partitioning of disk + - Hardened SSH and web services + +Usage +===== + +The entire module is exposed from the top-level flake as a nixosModule, +but it changes too often to be considered stable. Options are made +available when imported under the 'xnet' attribute set. diff --git a/modules/xnet/default.nix b/modules/xnet/default.nix new file mode 100644 index 0000000..9d224cf --- /dev/null +++ b/modules/xnet/default.nix @@ -0,0 +1,114 @@ +{ pkgs, lib, config, ... }: +let + inherit (builtins) + fetchurl + filter + readFile + ; + + inherit (lib) + mkDefault + mkOption + types + splitString + ; + + gitKeys = fetchurl { + url = "https://github.com/kbujari.keys"; + sha256 = "1kskbiyqvjz1wsmcrgh9v0iryf33y70zk503z0m96wmzdjllmc94"; + }; + +in +{ + imports = [ + ./disk.nix + ./nginx.nix + ./persist.nix + ./net + ./desktop + ./gitserver + ./monitoring + ]; + + options.xnet = { + pubKeys = mkOption { + type = types.listOf types.str; + default = filter (s: s != "") + (splitString "\n" (readFile gitKeys)); + }; + }; + + config = { + i18n.defaultLocale = mkDefault "en_US.UTF-8"; + time.timeZone = mkDefault "America/Toronto"; + + boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; + + nix = { + settings = { + auto-optimise-store = true; + experimental-features = [ "nix-command" "flakes" ]; + warn-dirty = false; + + # timeout fast from binary cache + connect-timeout = 5; + }; + gc = { + automatic = true; + options = mkDefault "--delete-older-than 30d"; + }; + }; + + documentation = { + doc.enable = mkDefault false; + info.enable = mkDefault false; + }; + + users.mutableUsers = false; + users.users.root = { + openssh.authorizedKeys.keys = config.xnet.pubKeys; + initialPassword = "hello"; + }; + + security.sudo = { + execWheelOnly = true; + extraConfig = "Defaults lecture = never"; + }; + + nixpkgs.config.allowUnfree = true; + + programs.ssh = { + knownHosts = { + "github.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl"; + "gitlab.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf"; + "git.sr.ht".publicKey = " ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZvRd4EtM7R+IHVMWmDkVU3VLQTSwQDSAvW0t2Tkj60"; + "pascal.ee.ryerson.ca".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFmWInQNT6EoU1NtUYzTs5jtpfbO/m6yvCckOiEGjvDc"; + }; + extraConfig = '' + Host github + HostName github.com + User git + PreferredAuthentications publickey + + Host ee + HostName pascal.ee.ryerson.ca + User kbujari + ''; + }; + + programs.git = { + enable = true; + config = { + init.defaultBranch = "master"; + fetch.prune = true; + core.excludesFile = pkgs.writeText "gitignore" '' + # dev shell caching + .direnv/ + .envrc + ''; + push.default = "upstream"; + push.autoSetupRemote = true; + }; + }; + }; +} diff --git a/modules/xnet/desktop/default.nix b/modules/xnet/desktop/default.nix new file mode 100644 index 0000000..995381f --- /dev/null +++ b/modules/xnet/desktop/default.nix @@ -0,0 +1,208 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.xnet.desktop; + inherit (lib) mkDefault mkOption mkForce mkIf types; + inherit (builtins) listToAttrs; +in +{ + options.xnet.desktop = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable graphical desktop."; + }; + }; + + config = mkIf cfg.enable { + security.rtkit.enable = true; + services.pipewire = { + enable = true; + pulse.enable = true; + alsa.enable = true; + }; + + systemd.user.services.waybar = { + enable = true; + after = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + wantedBy = [ "graphical-session.target" ]; + serviceConfig = { + ExecStart = "${pkgs.waybar}/bin/waybar"; + Type = "simple"; + }; + }; + + programs.sway = { + enable = true; + wrapperFeatures = { + gtk = true; + base = true; + }; + xwayland.enable = true; + extraPackages = with pkgs; [ + foot + fuzzel + imv + mako + mpv + niri + playerctl + pop-icon-theme + pwvucontrol + sway-contrib.grimshot + swayidle + swaylock + tigervnc + udiskie + waybar + wl-clipboard + zathura + ]; + extraSessionCommands = '' + export MOZ_ENABLE_WAYLAND=1 + export MOZ_USE_XINPUT2=1 + export MOZ_WEBRENDER=1 + export XDG_CURRENT_DESKTOP=sway + export XDG_SESSION_TYPE=wayland + ''; + }; + + qt = { + enable = true; + style = "adwaita-dark"; + platformTheme = "gnome"; + }; + + fonts.packages = with pkgs; [ + departure-mono + iosevka + noto-fonts + noto-fonts-cjk-sans + noto-fonts-emoji + nerd-fonts.symbols-only + terminus_font + ]; + + xdg.portal = { + enable = true; + extraPortals = with pkgs; [ + xdg-desktop-portal-wlr + xdg-desktop-portal-gtk + ]; + }; + + environment.etc."xdg/user-dirs.defaults".text = '' + DOWNLOAD=/tmp/downloads + ''; + + environment.systemPackages = [ pkgs.man-pages pkgs.man-pages-posix ]; + + programs = { + fish = { + enable = true; + interactiveShellInit = '' + # disable greeting + set fish_greeting + ''; + }; + light.enable = mkDefault true; + direnv = { + enable = true; + nix-direnv.enable = true; + }; + + # GTK settings + dconf = { + enable = true; + profiles.user.databases = [{ + lockAll = true; + settings = { + "org/gnome/desktop/interface" = { + color-scheme = "prefer-dark"; + gtk-font-name = "System-ui 10"; + icon-theme = "Pop"; + theme-name = "Adwaita-dark"; + }; + }; + }]; + }; + }; + + # Helper for managing dotfiles + environment.shellAliases.dots = + "git --git-dir=$HOME/.local/cfg/ --work-tree=$HOME"; + + # Enable yubikey for SSH and more + services = { + yubikey-agent.enable = true; + pcscd.enable = true; + udev.packages = with pkgs; [ yubikey-personalization ]; + udisks2.enable = true; + }; + + programs.firefox = { + enable = true; + preferences = { + # Enable hardware transcoding + "media.ffmpeg.vaapi.enabled" = true; + + # Disable controlling media with keyboard + "media.hardwaremediakeys.enabled" = false; + # Enable legacy compact mode + "browser.compactmode.show" = true; + "browser.uidensity" = 1; + + # Disable ctrl+q closing browser + "browser.quitShortcut.disabled" = true; + + # Hardcode theme to dark mode + "ui.systemUsesDarkTheme" = 1; + }; + policies = { + DefaultDownloadDirectory = "/tmp/firefox"; + DisableTelemetry = true; + DisableFirefoxStudies = true; + EnableTrackingProtection = { + Value = true; + Locked = true; + Cryptomining = true; + Fingerprinting = true; + }; + DisableAccounts = true; + DisableFirefoxAccounts = true; + DisableFirefoxScreenshots = true; + DisablePocket = true; + DisplayBookmarksToolbar = "never"; + DisplayMenuBar = "default-off"; + DontCheckDefaultBrowser = true; + Homepage = { + URL = "about:blank"; + StartPage = "homepage"; + }; + HttpsOnlyMode = "enabled"; + DNSOverHTTPS = false; + NewTabPage = false; + OfferToSaveLogins = false; + PasswordManagerEnabled = false; + SearchBar = "unified"; + SearchEngines.Default = "DuckDuckGo"; + SearchSuggestEnabled = false; + ExtensionSettings = + let + extension = shortId: uuid: { + name = uuid; + value = { + install_url = "https://addons.mozilla.org/en-US/firefox/downloads/latest/${shortId}/latest.xpi"; + installation_mode = "force_installed"; + }; + }; + in + listToAttrs [ + (extension "ublock-origin" "uBlock0@raymondhill.net") + (extension "bitwarden-password-manager" "{446900e4-71c2-419f-a6a7-df9c091e268b}") + (extension "darkreader" "addon@darkreader.org") + ]; + }; + }; + }; +} diff --git a/modules/xnet/disk.nix b/modules/xnet/disk.nix new file mode 100644 index 0000000..82e9c87 --- /dev/null +++ b/modules/xnet/disk.nix @@ -0,0 +1,155 @@ +{ config, lib, modulesPath, inputs, ... }: +let + cfg = config.xnet.disk; + commonOpts = { + acltype = "posixacl"; + atime = "off"; + compression = "on"; + normalization = "formD"; + relatime = "off"; + xattr = "sa"; + "com.sun:auto-snapshot" = "false"; + }; + + inherit (lib) mkOption mkDefault mkIf types; +in +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + inputs.disko.nixosModules.default + ]; + + options.xnet.disk = { + enable = mkOption { + type = types.bool; + default = false; + description = "Apply xnet-standard ZFS disk layout."; + }; + + device = mkOption { + type = types.str; + description = "Device used for zroot ZFS pool."; + }; + }; + + config = mkIf cfg.enable { + networking.hostId = builtins.substring 0 8 + (builtins.hashString "md5" config.networking.hostName); + + services.zfs = { + autoScrub.enable = true; + trim.enable = true; + }; + + # With root running in memory, swap should be required unless + # otherwise specified + zramSwap.enable = mkDefault true; + + boot = { + kernelParams = [ "nohibernate" "elevator=none" ]; + supportedFilesystems = [ "vfat" "zfs" ]; + zfs.devNodes = mkDefault "/dev/disk/by-partuuid"; + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + initrd = { + systemd.enable = true; + availableKernelModules = [ + "xhci_pci" + "ahci" + "nvme" + "usb_storage" + "sd_mod" + "sdhci_pci" + ]; + }; + tmp.cleanOnBoot = mkDefault true; + }; + + disko.devices.disk.main = { + type = "disk"; + device = cfg.device; + content = { + type = "gpt"; + partitions.ESP = { + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + partitions.ZFS = { + size = "100%"; + content = { + type = "zfs"; + pool = "zroot"; + }; + }; + }; + }; + + disko.devices = { + nodev."/" = { + fsType = "tmpfs"; + mountOptions = [ "defaults" "size=2G" "mode=755" ]; + }; + + zpool.zroot = { + type = "zpool"; + options = { + ashift = "12"; + autotrim = "on"; + }; + + datasets = { + "local" = { + type = "zfs_fs"; + options = commonOpts // { + mountpoint = "none"; + }; + }; + + "local/nix" = { + type = "zfs_fs"; + mountpoint = "/nix"; + options.mountpoint = "legacy"; + }; + + "local/reserved" = { + type = "zfs_fs"; + options = { + refreservation = "10G"; + mountpoint = "none"; + }; + }; + + "persist" = { + type = "zfs_fs"; + mountpoint = "/persist"; + options = commonOpts // { + mountpoint = "legacy"; + }; + }; + }; + }; + }; + + services.sanoid = { + enable = true; + templates.default = { + autosnap = true; + autoprune = true; + hourly = 24; + daily = 14; + monthly = 1; + }; + datasets."zroot/persist" = { + useTemplate = [ "default" ]; + recursive = true; + }; + }; + }; +} diff --git a/modules/xnet/gitserver/default.nix b/modules/xnet/gitserver/default.nix new file mode 100644 index 0000000..2152ebd --- /dev/null +++ b/modules/xnet/gitserver/default.nix @@ -0,0 +1,49 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.xnet.gitServer; + inherit (lib) mkOption mkIf types; +in +{ + imports = [ ./gitweb.nix ]; + + options.xnet.gitServer = { + enable = mkOption { + type = types.bool; + default = false; + description = "Serve git repos over SSH."; + }; + + path = mkOption { + type = types.path; + default = "/persist/repo/git"; + description = "Directory where repos will be stored."; + }; + }; + + config = mkIf cfg.enable { + users.users.git = { + group = "git"; + initialPassword = ""; + isSystemUser = true; + home = cfg.path; + homeMode = "755"; + createHome = true; + shell = "${pkgs.git}/bin/git-shell"; + openssh.authorizedKeys.keys = config.xnet.pubKeys; + }; + + users.groups.git = { }; + + programs.git = { + enable = true; + config = { + init = { + defaultBranch = "master"; + }; + safe = { + directory = "*"; + }; + }; + }; + }; +} diff --git a/modules/xnet/gitserver/gitweb.nix b/modules/xnet/gitserver/gitweb.nix new file mode 100644 index 0000000..da9a5ee --- /dev/null +++ b/modules/xnet/gitserver/gitweb.nix @@ -0,0 +1,70 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.xnet.gitServer.gitweb; + inherit (lib) mkOption mkIf types; +in +{ + options.xnet.gitServer.gitweb = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable web interface to git repos."; + }; + + hostName = mkOption { + type = types.str; + default = "src.web.4kb.net"; + description = "Hostname the webUI is served from."; + }; + }; + + config = mkIf cfg.enable { + xnet.nginx.enable = true; + + users.users.nginx.extraGroups = [ "git" ]; + services.cgit.main = { + enable = true; + scanPath = config.xnet.gitServer.path; + package = pkgs.cgit-pink; + nginx = { + virtualHost = cfg.hostName; + location = "/"; + }; + extraConfig = '' + mimetype.gif=image/gif + mimetype.html=text/html + mimetype.jpeg=image/jpeg + mimetype.jpg=image/jpeg + mimetype.pdf=application/pdf + mimetype.png=image/png + mimetype.svg=image/svg+xml + readme=:readme + readme=:readme.md + readme=:readme.txt + readme=:README + readme=:README.md + readme=:README.txt + ''; + settings = { + about-filter = "${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh"; + source-filter = "${pkgs.cgit-pink}/lib/cgit/filters/syntax-highlighting.py"; + clone-url = "https://${cfg.hostName}/$CGIT_REPO_URL git@${cfg.hostName}:$CGIT_REPO_URL"; + enable-commit-graph = true; + enable-http-clone = false; + enable-index-links = true; + enable-remote-branches = true; + remove-suffix = true; + robots = "noindex, nofollow"; + root-desc = "What I cannot create, I do not understand"; + root-title = cfg.hostName; + section-from-path = true; + snapshots = "tar.gz tar.bz2 zip"; + }; + }; + + # services.nginx.virtualHosts."${cfg.gitweb.hostName}" = { + # useACMEHost = "4kb.net"; + # addSSL = true; + # }; + }; +} diff --git a/modules/xnet/monitoring/22604_rev2.json b/modules/xnet/monitoring/22604_rev2.json new file mode 100644 index 0000000..0adeb22 --- /dev/null +++ b/modules/xnet/monitoring/22604_rev2.json @@ -0,0 +1,1565 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.4.0" + }, + { + "type": "panel", + "id": "piechart", + "name": "Pie chart", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Extended smartctl-exporter Dashboard", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "links": [], + "mappings": [], + "min": 20, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 13, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "avg(smartctl_device_temperature{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (instance,device,model_name)", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "1m", + "intervalFactor": 1, + "legendFormat": "{{instance}} {{device}} {{model_name}}", + "refId": "B" + } + ], + "title": "Disk Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 2, + "x": 13, + "y": 0 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(smartctl_device{instance=~\"$node\", device=~\"$disk\", interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"})", + "instant": false, + "range": true, + "refId": "A" + } + ], + "title": "Devices", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "fieldMinMax": false, + "mappings": [], + "min": 0, + "noValue": "0", + "unit": "none" + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": ["Value"], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 2, + "x": 15, + "y": 0 + }, + "id": 16, + "options": { + "displayLabels": ["percent"], + "legend": { + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "values": ["value"] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/^Value$/", + "values": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(smartctl_device) by (interface)", + "format": "table", + "instant": true, + "intervalFactor": 1, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + } + ], + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 17, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": ["lastNotNull"], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "smartctl_device_media_errors{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"} > 0", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{instance}} {{device}} {{model_name}}", + "range": true, + "refId": "A" + } + ], + "title": "Media Errors", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 2, + "x": 13, + "y": 4 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "count((smartctl_device_smart_status{instance=~\"$node\",device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device) == 0)", + "format": "table", + "instant": true, + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "A" + } + ], + "title": "Unhealthy", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "bars", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 17, + "y": 4 + }, + "id": 5, + "options": { + "legend": { + "calcs": ["lastNotNull"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "smartctl_device_critical_warning{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"} > 0", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "Critical Warnings {{device}}", + "range": true, + "refId": "A" + } + ], + "title": "Critical Warnings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 2, + "x": 15, + "y": 6 + }, + "id": 15, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(count(smartctl_device) by (instance))", + "instant": true, + "legendFormat": "nodes", + "range": false, + "refId": "A" + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false, + "minWidth": 150 + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [ + { + "id": "unit", + "value": "dateTimeFromNow" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + }, + { + "id": "displayName", + "value": "Last Checked" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value #A" + }, + "properties": [ + { + "id": "displayName", + "value": "Power On Time" + }, + { + "id": "unit", + "value": "s" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "model_name" + }, + "properties": [ + { + "id": "displayName", + "value": "Device Model" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "device" + }, + "properties": [ + { + "id": "displayName", + "value": "Device" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value #B" + }, + "properties": [ + { + "id": "displayName", + "value": "Power Cycles" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "displayName", + "value": "Node" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Node" + }, + "properties": [ + { + "id": "custom.width", + "value": 80 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "custom.width", + "value": 107 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Last Checked" + }, + "properties": [ + { + "id": "custom.width", + "value": 142 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device Model" + }, + "properties": [ + { + "id": "custom.width", + "value": 291 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Power On Time" + }, + "properties": [ + { + "id": "custom.width", + "value": 162 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Power Cycles" + }, + "properties": [ + { + "id": "custom.width", + "value": 117 + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 13, + "x": 0, + "y": 8 + }, + "id": 10, + "interval": "1h", + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "enablePagination": true, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Power On Time" + } + ] + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "max(smartctl_device_power_on_seconds{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (instance, device, model_name)", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "max(smartctl_device_power_cycle_count{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (instance, device, model_name)", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Power Cycles", + "refId": "B" + } + ], + "title": "Disk Lifetime", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Total Written" + }, + "properties": [ + { + "id": "displayName", + "value": "Total Written" + }, + { + "id": "unit", + "value": "bytes" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [ + { + "id": "unit", + "value": "dateTimeFromNow" + }, + { + "id": "custom.align" + }, + { + "id": "displayName", + "value": "Last Checked" + }, + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "model_name" + }, + "properties": [ + { + "id": "displayName", + "value": "Device Model" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "device" + }, + "properties": [ + { + "id": "displayName", + "value": "Device" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "displayName", + "value": "Node" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "serial_number" + }, + "properties": [ + { + "id": "displayName", + "value": "Serial Number" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "custom.width", + "value": 115 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Serial Number" + }, + "properties": [ + { + "id": "custom.width", + "value": 177 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "model_name" + }, + "properties": [ + { + "id": "custom.width", + "value": 277 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Total Written" + }, + "properties": [ + { + "id": "custom.width", + "value": 123 + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 11, + "x": 13, + "y": 8 + }, + "id": 3, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "enablePagination": true, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Total Written" + } + ] + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "max(smartctl_device_bytes_written{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (device, instance, model_name, serial_number)", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "max((smartctl_device_attribute{instance=~\"$node\", device=~\"$disk\", attribute_name=\"Total_LBAs_Written\", attribute_value_type=\"raw\"} * on (instance,device) smartctl_device_block_size{instance=~\"$node\", device=~\"$disk\",blocks_type=\"physical\"}) * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"$type\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"}) by (device, instance, model_name, serial_number)\n", + "format": "table", + "hide": false, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Total Data Written on SSDs", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "calculateField", + "options": { + "alias": "Total Written", + "binary": { + "left": { + "fixed": "" + }, + "right": { + "fixed": "" + } + }, + "mode": "reduceRow", + "reduce": { + "include": ["Value #B", "Value #A"], + "reducer": "sum" + }, + "replaceFields": false, + "window": { + "reducer": "mean", + "windowAlignment": "trailing", + "windowSize": 0.1, + "windowSizeMode": "percentage" + } + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "device", + "instance", + "model_name", + "serial_number", + "Total Written" + ] + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "", + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 75 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 13, + "x": 0, + "y": 18 + }, + "id": 8, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "sort_desc(100 - smartctl_device_attribute{instance=~\"$node\", device=~\"$disk\", attribute_name=\"Wear_Leveling_Count\",attribute_value_type=\"value\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"sat\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"})", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{instance}}:{{device}}", + "refId": "A" + } + ], + "title": "Wear Level on SATA Devices", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "", + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 75 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 11, + "x": 13, + "y": 18 + }, + "id": 14, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "sort_desc(smartctl_device_percentage_used{instance=~\"$node\", device=~\"$disk\"} * on(instance, device) group_left(interface,serial_number,model_family,model_name) smartctl_device{interface=~\"nvme\",serial_number=~\"$serial_number\",model_name=~\"$device_model\"})\n", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{instance}}:{{device}}", + "refId": "A" + } + ], + "title": "Wear Level on NVMe Devices", + "type": "gauge" + } + ], + "refresh": "1m", + "schemaVersion": 40, + "tags": ["prometheus", "node_exporter", "smartmon"], + "templating": { + "list": [ + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "definition": "label_values(smartctl_version,instance)", + "includeAll": true, + "multi": true, + "name": "node", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(smartctl_version,instance)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "definition": "label_values(smartctl_device,device)", + "includeAll": true, + "multi": true, + "name": "disk", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(smartctl_device,device)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "definition": "label_values(smartctl_device,interface)", + "includeAll": true, + "multi": true, + "name": "type", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(smartctl_device,interface)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "definition": "label_values(smartctl_device,model_name)", + "includeAll": true, + "multi": true, + "name": "device_model", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(smartctl_device,model_name)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "definition": "label_values(smartctl_device,serial_number)", + "includeAll": true, + "multi": true, + "name": "serial_number", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(smartctl_device,serial_number)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": ["1m", "5m", "15m", "30m", "1h", "2h", "1d"] + }, + "timezone": "browser", + "title": "SMARTctl Exporter Dashboard", + "uid": "ce8j0dmrrej9cc", + "version": 47, + "weekStart": "", + "gnetId": 22604 +} diff --git a/modules/xnet/monitoring/default.nix b/modules/xnet/monitoring/default.nix new file mode 100644 index 0000000..eed4963 --- /dev/null +++ b/modules/xnet/monitoring/default.nix @@ -0,0 +1,58 @@ +{ config, ... }: +let + inherit (builtins) + map + ; + + inherit (config.services.prometheus) exporters; +in +{ + imports = [ ./grafana.nix ]; + + + config = { + services.prometheus = { + enable = config.xnet.monitoring.hostGrafana; + globalConfig.scrape_interval = "1m"; + + exporters = { + node = { + enable = true; + openFirewall = true; + enabledCollectors = [ "processes" "systemd" ]; + }; + + # systemd = { + # enable = true; + # openFirewall = true; + # extraFlags = [ + # "--systemd.collector.enable-ip-accounting" + # "--systemd.collector.enable-restart-count" + # ]; + # }; + + smartctl = { + enable = true; + openFirewall = true; + }; + }; + + scrapeConfigs = + let + hosts = [ "iridium" "radon" ]; + in + [{ + job_name = "node"; + static_configs = [{ + targets = map (h: "${h}:${toString exporters.node.port}") hosts; + }]; + } + { + job_name = "smartctl"; + static_configs = [{ + targets = map (h: "${h}:${toString exporters.smartctl.port}") hosts; + }]; + }]; + }; + }; +} diff --git a/modules/xnet/monitoring/grafana.nix b/modules/xnet/monitoring/grafana.nix new file mode 100644 index 0000000..e95318d --- /dev/null +++ b/modules/xnet/monitoring/grafana.nix @@ -0,0 +1,78 @@ +{ config, pkgs, lib, ... }: +let + inherit (lib) mkOption mkIf types; + + inherit (config.xnet.monitoring) + hostGrafana + ; +in +{ + options.xnet.monitoring = { + hostGrafana = mkOption { + type = types.bool; + default = false; + description = "Scrape hosts and host the data."; + }; + }; + + config = mkIf hostGrafana { + services.grafana.provision = { + enable = true; + datasources.settings.datasources = [{ + name = "Prometheus"; + type = "prometheus"; + url = "http://localhost:9090"; + access = "proxy"; + editable = false; + }]; + + dashboards.settings.providers = [{ + name = "Fetched Dashboards"; + options.path = "/etc/grafana/dashboards"; + }]; + }; + + environment.etc = { + "grafana/dashboards/node-exporter.json" = { + user = "grafana"; + group = "grafana"; + source = pkgs.fetchurl { + url = "https://grafana.com/api/dashboards/1860/revisions/37/download"; + hash = "sha256-1DE1aaanRHHeCOMWDGdOS1wBXxOF84UXAjJzT5Ek6mM="; + }; + }; + "grafana/dashboards/smartctl-exporter.json" = { + user = "grafana"; + group = "grafana"; + source = ./grafana.nix; + # source = pkgs.fetchurl { + # url = "https://grafana.com/api/dashboards/22604/revisions/2/download"; + # hash = "sha256-ci8WE23fZ+ltEKFoUdNNVXsUIV0jqtas79ia2lYIo88="; + # }; + }; + }; + + services.grafana = { + enable = true; + settings.server = { + domain = "grafana.plaza.4kb.net"; + protocol = "socket"; + }; + settings."auth.anonymous" = { + enabled = true; + org_role = "Admin"; + }; + }; + + users.groups.grafana.members = [ "nginx" ]; + systemd.services.nginx.serviceConfig.ProtectHome = false; + + services.nginx.virtualHosts."${config.services.grafana.settings.server.domain}" = { + # useACMEHost = "4kb.net"; + # addSSL = true; + locations."/" = { + proxyPass = "http://unix:/${toString config.services.grafana.settings.server.socket}"; + }; + }; + }; +} diff --git a/modules/xnet/net/default.nix b/modules/xnet/net/default.nix new file mode 100644 index 0000000..7242855 --- /dev/null +++ b/modules/xnet/net/default.nix @@ -0,0 +1,128 @@ +{ config, lib, ... }: +let + cfg = config.xnet.net; + + inherit (builtins) + attrNames + attrValues + map + length + ; + + inherit (lib) + concatLines + genAttrs + mapAttrsToList + mergeAttrsList + mkOption + mkIf + types + ; + + # Networks are logical groupings of hosts wired together. This is + # accomplished using VLANS on the same layer 2 network. + # + # Hosts can hard code their spot on a network here. This module aims + # to generate network information for a host using this set of facts. + # The module should gracefully fall back to defaults if a given fact + # is not specified for the host consuming the module. + # + # A host will have it's /etc/hosts populated with all other hosts in + # a network to avoid a dynamic DNS server. + networks = { + + # Open network for xnet hosts + plaza = { + mask = 24; + vlan = 88; + hosts = { + t1 = "10.88.88.1"; + radon = "10.88.88.2"; + iridium = "10.88.88.3"; + }; + }; + + # Reserved network for kubernetes related nodes. A distinct network + # is created here for hosts that provide resources to workers. + # Generally, nodes on this network are spawned dynamically and are + # not a part of this NixOS config directly, although they may boot + # from assets served from a node here. + kubenet = { + mask = 24; + vlan = 42; + hosts = { + iridium = "10.88.42.254"; + }; + }; + }; +in +{ + imports = [ + ./sshd.nix + ./dns.nix + ]; + + options.xnet.net = { + join = mkOption { + type = types.listOf (types.enum (attrNames networks)); + default = [ ]; + description = "Available networks."; + }; + + interface = mkOption { + type = types.str; + default = ""; + description = "Network interface connecting to xnet."; + }; + }; + + config = mkIf ((length cfg.join) > 0) { + # networkd will handle this + networking.useDHCP = false; + + systemd.network = + let + name = config.networking.hostName; + + genConfig = net: { + netdevs."${toString networks.${net}.vlan}-${net}" = { + netdevConfig.Kind = "vlan"; + netdevConfig.Name = net; + vlanConfig.Id = networks.${net}.vlan; + }; + + networks."10-${cfg.interface}" = { + matchConfig.Name = cfg.interface; + vlan = cfg.join; + + # Accept DHCP on physical interface + DHCP = "ipv4"; + + # Network is up when xnet physical interface gets carrier + linkConfig.RequiredForOnline = "carrier"; + + # Allow resolved to resolve mDNS + networkConfig.MulticastDNS = true; + }; + + networks."${toString networks.${net}.vlan}-${net}" = { + matchConfig.Name = net; + + # Set up address for known host + address = [ "${networks.${net}.hosts.${name}}/${toString networks.${net}.mask}" ]; + }; + }; + + values = attrValues (genAttrs cfg.join genConfig); + in + { enable = true; } // mergeAttrsList values; + + networking.extraHosts = + let + genHosts = net: concatLines + (mapAttrsToList (name: value: "${value} ${name} ${name}.${net}.4kb.net") + networks.${net}.hosts); + in + concatLines (map (net: genHosts net) cfg.join); + }; +} diff --git a/modules/xnet/net/dns.nix b/modules/xnet/net/dns.nix new file mode 100644 index 0000000..0632fa9 --- /dev/null +++ b/modules/xnet/net/dns.nix @@ -0,0 +1,38 @@ +{ lib, ... }: +let + inherit (lib) + mkDefault + ; + + quad9 = [ + "9.9.9.9#dns.quad9.net" + "149.112.112.112#dns.quad9.net" + "2620:fe::fe#dns.quad9.net" + "2620:fe::9#dns.quad9.net" + ]; + + cloudflare = [ + "1.1.1.1#one.one.one.one" + "1.0.0.1#one.one.one.one" + "2606:4700:4700::1111#one.one.one.one" + "2606:4700:4700::1001#one.one.one.one" + ]; +in +{ + + networking.nameservers = mkDefault quad9; + + services.resolved = { + enable = true; + llmnr = "false"; + dnssec = "false"; + dnsovertls = mkDefault "opportunistic"; + fallbackDns = quad9 ++ cloudflare; + extraConfig = '' + MulticastDNS=yes + ''; + }; + + # Allow mDNS resolution + networking.firewall.allowedTCPPorts = [ 5353 ]; +} diff --git a/modules/xnet/net/sshd.nix b/modules/xnet/net/sshd.nix new file mode 100644 index 0000000..9d7976e --- /dev/null +++ b/modules/xnet/net/sshd.nix @@ -0,0 +1,46 @@ +{ config, lib, ... }: +let + cfg = config.xnet.net.sshd; + inherit (lib) mkOption mkIf types; +in +{ + options.xnet.net.sshd = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable hardened SSH service."; + }; + }; + + config = mkIf cfg.enable { + services.openssh = { + enable = true; + startWhenNeeded = true; + openFirewall = true; + hostKeys = [{ + path = "/persist/certs/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + }]; + settings = { + UsePAM = false; + X11Forwarding = false; + PermitRootLogin = "prohibit-password"; + PasswordAuthentication = false; + Ciphers = [ "chacha20-poly1305@openssh.com" ]; + Macs = [ "hmac-sha2-512-etm@openssh.com" ]; + KexAlgorithms = [ "curve25519-sha256@libssh.org" ]; + }; + sftpServerExecutable = "internal-sftp"; + sftpFlags = [ "-f AUTHPRIV" "-l INFO" ]; + extraConfig = + let + pubkeyTypes = lib.strings.concatStringsSep "," [ + "sk-ssh-ed25519-cert-v01@openssh.com" + "ssh-ed25519-cert-v01@openssh.com" + "ssh-ed25519" + ]; + in + "PubkeyAcceptedKeyTypes ${pubkeyTypes}"; + }; + }; +} diff --git a/modules/xnet/nginx.nix b/modules/xnet/nginx.nix new file mode 100644 index 0000000..eadd794 --- /dev/null +++ b/modules/xnet/nginx.nix @@ -0,0 +1,27 @@ +{ config, lib, ... }: +let + cfg = config.xnet.nginx; + + inherit (lib) mkOption mkIf types; +in +{ + options.xnet.nginx = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable optimized nginx."; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + }; + }; +} diff --git a/modules/xnet/persist.nix b/modules/xnet/persist.nix new file mode 100644 index 0000000..10eb883 --- /dev/null +++ b/modules/xnet/persist.nix @@ -0,0 +1,61 @@ +{ config, lib, ... }: +let + inherit (lib) + concatLists + isString + mkOption + types + ; + +in +{ + options.xnet.persist = mkOption { + type = types.listOf (types.either + types.str + (types.submodule { + options = { + path = mkOption { + type = types.str; + description = "Path to persist"; + }; + user = mkOption { + type = types.nullOr types.str; + default = null; + description = "User ID to set on the persisted directory"; + }; + group = mkOption { + type = types.nullOr types.str; + default = null; + description = "Group ID to set on the persisted directory"; + }; + + mode = mkOption { + type = types.nullOr types.str; + default = null; + description = "Permissions to set"; + }; + }; + })); + default = [ ]; + description = "Automatically persisted state."; + }; + + config.systemd.tmpfiles.rules = + let + mkEntry = entry: + let + path = if isString entry then entry else entry.path; + user = if isString entry then "-" else entry.user; + group = if isString entry then "-" else entry.group; + mode = if isString entry then "-" else entry.mode; + + dent = "d /persist${path} ${mode} ${user} ${group} - -"; + link = "L+ ${path} - - - - /persist${path}"; + perm = "Z ${mode} ${user} ${group} - -"; + in + [ dent link perm ]; + + entries = map mkEntry config.xnet.persist; + in + concatLists entries; +} diff --git a/packages/blog/.gitignore b/packages/blog/.gitignore new file mode 100644 index 0000000..decc3f8 --- /dev/null +++ b/packages/blog/.gitignore @@ -0,0 +1,2 @@ +public/ +nohup.out diff --git a/packages/blog/config.toml b/packages/blog/config.toml new file mode 100644 index 0000000..69c389b --- /dev/null +++ b/packages/blog/config.toml @@ -0,0 +1,8 @@ +base_url = "https://4kb.net" +build_search_index = false +compile_sass = false +generate_feeds = true +minify_html = true + +[markdown] +highlight_code = false diff --git a/packages/blog/content/posts/_index.md b/packages/blog/content/posts/_index.md new file mode 100644 index 0000000..9efc62e --- /dev/null +++ b/packages/blog/content/posts/_index.md @@ -0,0 +1,6 @@ ++++ +title = "Posts" +sort_by = "date" +page_template = "post.html" +redirect_to="/" ++++ diff --git a/packages/blog/content/posts/deterministic-hostnames.md b/packages/blog/content/posts/deterministic-hostnames.md new file mode 100644 index 0000000..43dc7d8 --- /dev/null +++ b/packages/blog/content/posts/deterministic-hostnames.md @@ -0,0 +1,80 @@ +--- +title: "Deterministic and unique network hostnames" +date: "2024-11-10" +--- + +As part of building out a Kubernetes cluster, I wanted to build and distribute a +single OS image to create stateless worker nodes. Using network booting, and +some clever tricks to differentiate nodes, we can create a scaleable and +efficient farm of workers for a cluster that don't even need disks. + +The idea came from a plan to build a cluster using the +[compute blade](https://computeblade.com/), and a few Raspberry Pi SBCs I +already own. Running the cluster from an SD card is not recommended due to the +not-so-great reliability of the flash used by most manufacturers, so I wanted to +try PXE booting each Pi to save money rather than purchasing an SSD for each +one. The compute blades do support an NVMe disk, but I plan to use those for a +storage cluster later, so they need to remain empty. + +## Base image + +Alpine Linux has been my preferred server OS for a long time. It provides a very +lightweight base system, and bundles an excellent bootstrapping system, +[apkovl](https://wiki.alpinelinux.org/wiki/Alpine_local_backup), that allows the +user to save a set of customisations to an system as an overlay to a stock +Alpine live image. In other words, we can create our image once, save the +changes as an `apkovl.tar.gz` file, and apply the same changes to a base system +on boot. This file can even be provided as a +[kernel parameter](https://wiki.alpinelinux.org/wiki/PXE_boot#Guide_to_options) +and will be fetched from a remote webserver automatically! + +Since the image and configuration will be shipped to the node via the network, +an added benefit of using Alpine is its tiny space consumption. I'm not using +enough nodes for this to really matter, but it's a cool optimization regardless. + +## Differentiating the nodes + +One of the main goals of this project is that there should be no persistent +storage required outside the boot image itself. Since every node will download +and generate the same root file-system on startup, the first problem that arises +is how the nodes will identify themselves both on the network and the cluster, +given that it's not possible to name them ahead of time. In other words, any +given node has to generate a unique hostname that won't collide with other +workers, and that will be the same each time that node boots. + +Since these nodes will not have a predefined name, we have to rely on +characteristics of the hardware to differentiate each one. The hardware MAC +address is perfect for this, since it's unique to to each node and will not be +wiped away after the node reboots. On a system like Linux that exposes its +hardware through a _sysfs_, we can find a file containing the address at +`/sys/class/net/eth0/address`. I don't really like the idea of attaching the +literal MAC address of the node to its network hostname, since it's a security +risk, and a bit too verbose. Instead, we can transform it into something safer +using a `sha1sum`, which is already present on our Alpine base system: + +```console +sha1sum /sys/class/net/eth0/address | head -c 6 | awk '{print "worker-" $0}' +``` + +### Applying the new name + +Ideally, the node should apply its generated hostname before reaching out for an +address over DHCP or joining the cluster. We can make sure it happens before any +traffic is sent out by adding a `pre-up` command to the right interface in +`/etc/network/interfaces`: + +``` + +... + +auto eth0 +iface eth0 inet dhcp + pre-up sha1sum /sys/class/net/eth0/address | head -c 6 | awk '{print "worker-" $0}' > /etc/hostname + +... +``` + +The VM I tested with looks outputs `worker-e2fae8`. Pretty clean result, and if +you want to know the physical node that maps to each hostname, you can take note +of the MAC address beforehand and generate the same hash on another computer to +match them up. diff --git a/packages/blog/content/posts/nohup.md b/packages/blog/content/posts/nohup.md new file mode 100644 index 0000000..64f7983 --- /dev/null +++ b/packages/blog/content/posts/nohup.md @@ -0,0 +1,26 @@ +--- +title: "Spawning background processes" +date: "2024-11-17" +--- + +Working in a terminal, +I often pair my editor with a background process watching files. +Before reaching for terminal multiplexers, +see if you can get away with simple tty job control. +Spawn the background process, +still attached to the terminal instance: + +``` +program args & +``` + +Also redirect its output to a file, +for when the process writes to the tty from the background: + +``` +nohup program args & +``` + +Extra reading: + +- diff --git a/packages/blog/content/posts/stateless-compute-networks.md b/packages/blog/content/posts/stateless-compute-networks.md new file mode 100644 index 0000000..bac9e5d --- /dev/null +++ b/packages/blog/content/posts/stateless-compute-networks.md @@ -0,0 +1,41 @@ +--- +title: "On stateless compute networks" +date: "2024-11-10" +draft: true +--- + +On the topic of distributed systems and clustering, +I am quite invested in the idea of compute nodes that rely entirely on the network for configuration. +Arbitrary nodes can join a pre-existing cluster, +offering their CPU time and memory for computation without relying on any pre-existing configuration on the node itself. +In other words, any computer could pick up work, +only needing power and a network connection to the cluster. + +Perhaps this eventually leads into a "self-healing" cluster where only one node is manually bootstrapped, +which then serves a _configuration endpoint_ for other stateless nodes to reach out to for their instructions, +which they will then also serve once they are themselves ready. + +Early revisions of these notes mention Kubernetes, +but I am also trying to achieve similar results with NixOS on a custom project. +In any case, these are my ever-updating notes towards a general implementation of a stateless distributed systems architecture. + +## Self healing cluster + +Assuming control of an external DHCP server, +a self healing Kubernetes cluster would be feasible, +with the PXE boot artifacts supplied by the cluster itself. +That is, as long as one node is running the pod hosting the artifacts on a given endpoint, +other nodes can boot those artifacts and join the cluster, +thereby being able to host the artifacts as well. + +## Configuration endpoint + +The nodes shouldn't require a disk installed to be able to join the network. +Rather, the lofty goal of zero-configuration compute nodes passes the job of node initialization to the supporting network. +This is accomplished with PXE boot instructions supplied over DHCP. + +I delegate the following tasks to a single node in the subnet: + +- Gateway: Optional outbound connections if required +- DHCP server: Cluster IPAM +- TFTP and HTTP server: Serves iPXE firmware and kernel/initrd artifacts diff --git a/packages/blog/content/posts/vim-compilers.md b/packages/blog/content/posts/vim-compilers.md new file mode 100644 index 0000000..f735228 --- /dev/null +++ b/packages/blog/content/posts/vim-compilers.md @@ -0,0 +1,20 @@ +--- +title: "Neovim's built-in compilers" +date: "2025-01-17" +--- + +Today I learned that neovim's `:make` comes with many +[backends](https://neovim.io/doc/user/quickfix.html#_6.-selecting-a-compiler) +already configured. +This works for checking c/cpp and python files, among other, but I was +most interested to see [Typst] and [Pandoc] listed as well. + +I was looking to add this functionality with a plugin or implementing it +manually, +where a document can be compiled on the fly from the editor. +After setting `:compiler pandoc`, +generating a pdf is done with `:make pdf`, +with other pandoc options just appended afterwards if needed. + +[typst]: https://typst.app +[pandoc]: https://github.com/jgm/pandoc diff --git a/packages/blog/default.nix b/packages/blog/default.nix new file mode 100644 index 0000000..204388f --- /dev/null +++ b/packages/blog/default.nix @@ -0,0 +1,21 @@ +{ pkgs, perSystem, ... }: +let + inherit (perSystem.self) + cv + ; +in +pkgs.stdenvNoCC.mkDerivation { + name = "4kb.net"; + src = ./.; + + nativeBuildInputs = with pkgs; [ + zola + ]; + + buildPhase = "zola build"; + installPhase = '' + mkdir -p $out/share + cp -r public/* $out/ + cp ${cv} $out/share/cv.pdf + ''; +} diff --git a/packages/blog/templates/404.html b/packages/blog/templates/404.html new file mode 100644 index 0000000..a4669df --- /dev/null +++ b/packages/blog/templates/404.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% block content %} + + Error 404! + + +404! +{% endblock content %} diff --git a/packages/blog/templates/base.html b/packages/blog/templates/base.html new file mode 100644 index 0000000..5a91df5 --- /dev/null +++ b/packages/blog/templates/base.html @@ -0,0 +1,88 @@ + + + + + + {% block title %}4kb.net{% endblock title %} + + + +
+ Kleidi Bujari <mail@4kb.net> + +
+
{% block content %} {% endblock %}
+ + diff --git a/packages/blog/templates/index.html b/packages/blog/templates/index.html new file mode 100644 index 0000000..6e8a2af --- /dev/null +++ b/packages/blog/templates/index.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} {% block content %} + +
+ {% set section = get_section(path="posts/_index.md") %} + +
    + {% for page in section.pages %} +
  • + {{ page.date }} + {{ page.title }} +
  • + {% endfor %} +
+ + {% endblock content %} +
diff --git a/packages/blog/templates/post.html b/packages/blog/templates/post.html new file mode 100644 index 0000000..b819520 --- /dev/null +++ b/packages/blog/templates/post.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% block title%} + {{ page.title }} - {{ super() }} +{% endblock title %} + +{% block content %} + +

{{ page.title }}

+
+ {{ page.content | safe }} +
+ +{% endblock content %} diff --git a/packages/cses/default.nix b/packages/cses/default.nix new file mode 100644 index 0000000..6538eb8 --- /dev/null +++ b/packages/cses/default.nix @@ -0,0 +1,13 @@ +{ pkgs ? import { }, ... }: + +pkgs.mkShell { + packages = with pkgs; [ + clang-tools + (writeShellScriptBin "cpprun" '' + TEMP=".tmp.cpp" + g++ -std=c++20 -O3 ./problems/"$1.cpp" -o $TEMP + ./$TEMP + rm $TEMP + '') + ]; +} diff --git a/packages/cses/problems/1068.cpp b/packages/cses/problems/1068.cpp new file mode 100644 index 0000000..8d05936 --- /dev/null +++ b/packages/cses/problems/1068.cpp @@ -0,0 +1,17 @@ +#include + +int main(int argc, char *argv[]) { + unsigned long long n; + std::cin >> n; + + while (n != 1) { + std::cout << n << " "; + + if (n % 2 == 0) + n /= 2; + else + n = (n * 3) + 1; + } + + std::cout << 1; +} diff --git a/packages/cses/problems/1069.cpp b/packages/cses/problems/1069.cpp new file mode 100644 index 0000000..232d2f7 --- /dev/null +++ b/packages/cses/problems/1069.cpp @@ -0,0 +1,23 @@ +#include + +int main() { + std::string input; + std::cin >> input; + + auto max = 0; + auto curr = '-'; + auto count = 0; + + for (auto const ch : input) { + if (curr != ch) { + curr = ch; + count = 0; + } + + count += 1; + max = std::max(max, count); + } + + std::cout << max; + return 0; +} diff --git a/packages/cses/problems/1070.cpp b/packages/cses/problems/1070.cpp new file mode 100644 index 0000000..131a644 --- /dev/null +++ b/packages/cses/problems/1070.cpp @@ -0,0 +1,15 @@ +#include + +int main() { + int n; + std::cin >> n; + + if (n == 1) + std::cout << 1; + else if (n < 4) + std::cout << "NO SOLUTION"; + else { + for (auto i = 2; i <= n; i += 2) std::cout << i << " "; + for (auto i = 1; i <= n; i += 2) std::cout << i << " "; + } +} diff --git a/packages/cses/problems/1071.cpp b/packages/cses/problems/1071.cpp new file mode 100644 index 0000000..778cc86 --- /dev/null +++ b/packages/cses/problems/1071.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +using ull = unsigned long long; + +int main() { + int n; + std::cin >> n; + + while (n--) { + ull x, y; + std::cin >> y >> x; + + auto area = y * y; + auto perimeter = y + y + 1; + auto max = area + perimeter; + } +} diff --git a/packages/cses/problems/1083.cpp b/packages/cses/problems/1083.cpp new file mode 100644 index 0000000..a3cd253 --- /dev/null +++ b/packages/cses/problems/1083.cpp @@ -0,0 +1,20 @@ +#include +#include + +using ull = unsigned long long; + +int main(int argc, char *argv[]) { + ull n; + std::cin >> n; + + auto set = std::set{}; + + std::string numstr; + while (std::getline(std::cin, numstr, ' ')) set.insert(std::stoi(numstr)); + + for (auto i = 1; i <= n; i++) { + if (set.contains(i)) continue; + std::cout << i; + break; + } +} diff --git a/packages/cses/problems/1094.cpp b/packages/cses/problems/1094.cpp new file mode 100644 index 0000000..56f3886 --- /dev/null +++ b/packages/cses/problems/1094.cpp @@ -0,0 +1,27 @@ +#include +#include + +using ull = unsigned long long; + +int main() { + ull n; + auto arr = std::vector{}; + + std::cin >> n; + + std::string type; + while (std::cin >> type) arr.push_back(std::stoi(type)); + + ull count = 0; + auto last = arr[0]; + + for (auto const num : arr) { + if (num > last) { + last = num; + continue; + } + count += last - num; + } + + std::cout << count; +} diff --git a/packages/cv/cv.typ b/packages/cv/cv.typ new file mode 100644 index 0000000..1120e39 --- /dev/null +++ b/packages/cv/cv.typ @@ -0,0 +1,250 @@ +#let resume( + author: "", + email: "", + github: "", + personal-site: "", + accent-color: "#000000", + font: "New Computer Modern", + body, +) = { + set document(author: author, title: author) + set text( + // LaTeX style font + font: font, + size: 10pt, + lang: "en", + ligatures: false + ) + + set page( + margin: (0.5in), + paper: "us-letter", + ) + + show link: underline + show link: set text( + fill: rgb(accent-color), + ) + + show heading.where(level: 2): it => [ + #pad(top: 0pt, bottom: -10pt, [#smallcaps(it.body)]) + #line(length: 100%, stroke: 1pt) + ] + + // Accent Color Styling + show heading: set text( + fill: rgb(accent-color), + ) + + show heading.where(level: 1): it => [ + #set align(left) + #set text( + weight: 700, + size: 20pt, + ) + #pad(it.body) + ] + + [= #(author)] + + let contact-item(value, prefix: "", link-type: "") = { + if value != "" { + if link-type != "" { + link(link-type + value)[#(prefix + value)] + } else { + value + } + } + } + + // Personal Info + pad( + top: 0.25em, + align(left)[ + #{ + let items = ( + contact-item(email, link-type: "mailto:"), + contact-item(github, link-type: "https://"), + contact-item(personal-site, link-type: "https://"), + ) + items.filter(x => x != none).join(" | ") + } + ], + ) + + set par(justify: true) + + body +} + +#let generic-two-by-two( + top-left: "", + top-right: "", + bottom-left: "", + bottom-right: "", +) = { + [ + #top-left #h(1fr) #top-right \ + #bottom-left #h(1fr) #bottom-right + ] +} + +#let dates-helper( + start-date: "", + end-date: "", +) = { + start-date + " " + $dash.em$ + " " + end-date +} + +#let edu( + institution: "", + dates: "", + degree: "", + location: "", +) = { + generic-two-by-two( + top-left: strong(institution), + top-right: location, + bottom-left: emph(degree), + bottom-right: emph(dates), + ) +} + +#let work( + title: "", + dates: "", + company: "", + location: "", +) = { + generic-two-by-two( + top-left: strong(title), + top-right: dates, + bottom-left: company, + bottom-right: emph(location), + ) +} + +#show: resume.with( + author: "Kleidi Bujari", + email: "mail@4kb.net", + github: "github.com/kbujari", + personal-site: "4kb.net", +) + +== Education + +#edu( + institution: "Toronto Metropolitan University", + dates: dates-helper(start-date: "Sep 2020", end-date: "Apr 2025"), + location: "Ontario, Canada", + degree: "Bachelor's of Engineering, Computer Engineering", +) + +- *Relevant Coursework*: + Data Structures, Embedded Programming, Compilers, Digital Systems, Computer Networks +- *Extracurriculars*: + Member of student robotics design team, + teaching assistant for micro-processor courses. + +== Experience + +#work( + company: "Toronto Metropolitan University", + title: "Graduate Research Assistant", + dates: dates-helper(start-date: "May 2024", end-date: "Sep 2024"), + location: "Toronto, Canada", +) + +- Implemented transformations for hundreds of media files, + using ffmpeg and unix primitives to parallelize workload. +- Designed frontend with AstroJS to generate only static HTML, + ensuring compatibility with many hosting providers. + +#work( + company: "Canadian Broadcasting Corporation", + title: "Network Engineering Intern", + dates: dates-helper(start-date: "May 2023", end-date: "Apr 2024"), + location: "Toronto, Canada", +) + +- Designed custom PXE-boot implementation for hundreds of devices using NetBox, + eliminating manual configuration. +- Configured Hyper-Converged Proxmox cluster for 2024 Olympics, + saving \$250k+ with reused hardware and open software. +- Deployed vendor-agnostic routing observability from scratch, + with Prometheus metrics and Grafana dashboards. +- Mentored junior application developers in modern C++23 programming, + aiding in performance design and memory safety. + +#work( + company: "WSP Canada", + title: "Student Engineer", + dates: dates-helper(start-date: "May", end-date: "Aug") + ", 2021, 2022", + location: "Toronto, Canada", +) + +- Contributed to subway car control systems with modern C++20, + replacing legacy code with newer STL functions. +- Extended internal distributed filesystem with support for deduplication and compression, + reclaiming 30% of storage. +- Validated new electrical designs for power consumption, + cost efficiency, and viability with existing systems. +- Participated in reviewing and adjusting large scale electrical and structural engineering designs. + +== Projects + +*Custom Linux Distribution* --- +Designed entire Linux distribution used for hosting production servers, +daily desktop use, and embedded programming on a Raspberry Pi. +Using NixOS, it supports enabling only required functionality at build time. +Features systemd, +an in-memory root filesystem, +ZFS persistent storage with backups, +and extremely hardened networking. + +*ICER Compressor* --- +Image compression library written in Rust, +designed for deep-space communication. +Hand tuned for speed and portability by using only integer arithmetic, +no heap allocations, and no standard library by default. +Achieves practically instant compressions, even on microprocessors. + +*Kubernetes Cluster* --- +Bare-metal compute cluster managed with GitOps to be completely reproducible. +Uses Cilium CNI for fast eBPF networking and BGP load balancing, +FluxCD for cluster management, +and CEPH distributed storage for stateful workloads. +Running Prometheus, Loki and AlertManager for complete observability. + +*Mirrorlist Generator* --- +Fetches Arch Linux package mirrors, +filtering them based on user parameters. +Sorts and outputs formatted data compliant with the pacman package manager. +Heavily outperforms default Python implementation. + +*Toronto Metropolitan Robotics* --- +Member of university design team working on space focused automated robotics. +Designed custom STM32 hardware with various interfaces (SPI, I2C, etc.) for controlling motor functions on robot. +Worked alongside various subteams to deliver a competition ready autonomous system. + +== Skills + +- *Languages*: #( + "Rust", + "C++", + "Nix", + "Haskell", + "TypeScript", + "Lua", + "Python", + ).join(", ") + +- *Technologies*: #( + "Linux", + "Compilers", + "Virtualisation", + "Terraform", + "Ansible", + "Computer Networks", + "Frontend (Svelte, Astro)" + ).join(", ") diff --git a/packages/cv/default.nix b/packages/cv/default.nix new file mode 100644 index 0000000..9de6903 --- /dev/null +++ b/packages/cv/default.nix @@ -0,0 +1,5 @@ +{ pkgs, ... }: + +pkgs.runCommand "cv" { } '' + ${pkgs.typst}/bin/typst compile --format pdf ${./cv.typ} $out +'' diff --git a/packages/perf-flamegraph.nix b/packages/perf-flamegraph.nix new file mode 100644 index 0000000..b472b74 --- /dev/null +++ b/packages/perf-flamegraph.nix @@ -0,0 +1,12 @@ +# Script that collects perf timing for the execution of a command and writes a +# flamegraph to stdout +{ pkgs, ... }: + +pkgs.writeShellScriptBin "perf-flamegraph" '' + set -euo pipefail + + ${pkgs.linuxPackages.perf}/bin/perf record -g --call-graph dwarf -F max "$@" + ${pkgs.linuxPackages.perf}/bin/perf script \ + | ${pkgs.flamegraph}/bin/stackcollapse-perf.pl \ + | ${pkgs.flamegraph}/bin/flamegraph.pl +'' diff --git a/packages/resistors/default.nix b/packages/resistors/default.nix new file mode 100644 index 0000000..6a038ea --- /dev/null +++ b/packages/resistors/default.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: + +pkgs.runCommandNoCC "resisto-rs" { } '' + mkdir -p $out + cp ${./index.html} $out/index.html + cp ${./styles.css} $out/styles.css +'' diff --git a/packages/resistors/index.html b/packages/resistors/index.html new file mode 100644 index 0000000..0683f49 --- /dev/null +++ b/packages/resistors/index.html @@ -0,0 +1,339 @@ + + + + rs-calc + + + + + +

rs-calc

+ + + + + + +
+

+ forked from + this, with removed analytics, cleaned up code. + my github +

+
+ + + + diff --git a/packages/resistors/styles.css b/packages/resistors/styles.css new file mode 100644 index 0000000..e2823e4 --- /dev/null +++ b/packages/resistors/styles.css @@ -0,0 +1,127 @@ +* { + /*font-family: 'firacode';*/ + text-rendering: optimizeLegibility; +} + +body { + margin: 10px 20px; + color: #cccccc; + background: #252525; + background-attachment: fixed !important; +} + +h1 { + font-weight: normal; + font-size: 50px; + margin: -10px 0 10px 0; +} + +h2 { + font-weight: normal; + font-size: 15px; +} + +strong { + font-weight: normal; +} + +footer { + position: absolute; + font-size: 12px; + color: #555; + bottom: 0; + left: 0; + right: 0; + padding: 20px; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +footer a { + color: #775; +} + +#resistor-query { + font-size: 20px; + border: 1px solid #999; + color: #fff; + background-color: #444; + outline: none; + width: 180px; +} + +.resistor-stripe { + width: 80px; + height: 150px; + display: inline-block; + padding: 4px 4px; +} + +.resistor-smt { + display: inline-block; + padding: 0px 10px; + font-size: 35px; + background-color: #000; + border-left: 15px solid silver; + border-right: 15px solid silver; + border-top: 1px solid silver; + border-bottom: 1px solid silver; + margin-right: 10px; +} + +@media print, screen and (max-width: 520px) { + .resistor-stripe { + width: 55px; + font-size: 80%; + height: 100px; + } + .resistor-smt { + font-size: 25px; + border-left: 12px solid silver; + border-right: 12px solid silver; + } +} + +@media print, screen and (max-width: 410px) { + .resistor-stripe { + width: 35px; + font-size: 55%; + height: 70px; + } + .resistor-smt { + font-size: 18px; + border-left: 8px solid silver; + border-right: 8px solid silver; + } +} + +@media print, screen and (max-width: 300px) { + .resistor-stripe { + width: 25px; + font-size: 50%; + height: 60px; + padding: 2px; + } + .resistor-smt { + font-size: 13px; + border-left: 6px solid silver; + border-right: 6px solid silver; + padding: 2px 4px; + } +} + +@media print, screen and (max-height: 660px) { + .resistor-stripe { + height: 55px; + } + footer { + position: inherit; + padding: 10px 0; + } +} -- cgit v1.3.1