From f623faa0ef7226023e872cdd71cdaf4ca8d6d9f0 Mon Sep 17 00:00:00 2001 From: atagen Date: Wed, 14 Aug 2024 01:01:53 +1000 Subject: [PATCH] cosmic checkpoint --- TODO | 22 +- flake.lock | 261 +++++++++----------- flake.nix | 102 ++++---- home/bolt.nix | 9 +- home/home.nix | 40 ++- home/icons/ollama.png | Bin 0 -> 37402 bytes home/modules/chat.nix | 4 +- home/modules/cosmic.nix | 163 +++++++++++++ home/modules/desktop.nix | 5 +- home/modules/documents.nix | 11 +- home/modules/niri.nix | 7 + home/modules/webapps.nix | 4 +- home/programs/ollama.nix | 52 ++-- home/programs/openwebui.nix | 40 +++ home/programs/stable-diffusion.nix | 9 +- home/util/containers.nix | 365 ++++++++++++++++++++++++++++ {util => home/util}/cosmic.nix | 137 ++++++----- home/util/firefox-webapp.nix | 21 +- home/util/local-webapp.nix | 191 +++++++++++---- system/adrift.nix | 3 + system/configuration.nix | 33 +-- system/modules/cosmic.nix | 110 +-------- system/modules/network.nix | 3 +- system/modules/niri.nix | 6 + system/modules/stable-diffusion.nix | 21 ++ system/quiver.nix | 82 ++++--- 26 files changed, 1163 insertions(+), 538 deletions(-) create mode 100644 home/icons/ollama.png create mode 100644 home/modules/cosmic.nix create mode 100644 home/modules/niri.nix create mode 100644 home/programs/openwebui.nix create mode 100644 home/util/containers.nix rename {util => home/util}/cosmic.nix (51%) create mode 100644 system/modules/niri.nix create mode 100644 system/modules/stable-diffusion.nix diff --git a/TODO b/TODO index 7d833fe..a7e71b0 100644 --- a/TODO +++ b/TODO @@ -1,35 +1,23 @@ implement agenix -separate stable-diffusion and ollama backends from their UIs - figure out a way to get firefox policies and plugins set up in webapps -is theme gen relevant with cosmic? -it appears that cosmic-theme is a thing but is it functional yet? - -concept: -a function that flake-ifies and overrides the sources for regular nixpkgs -derivations to allow you to keep them utd by yourself -^ not possible to make flake inputs based on a function, - just pull source as flake and override the source attribute.. - concept: a nixos-like OS with standardised option sets that nonetheless uses composable flake -pieces to construct the whole OS, thus breaking the monorepo syndrome +pieces to construct the whole OS, breaking the monorepo syndrome concept: direnv+flake with services possibilities: -* make own systemd user slice and run services out of it +* flake-containers, but with rootless nspawn? + +* make per project systemd user slice and run services out of it - can we adapt nixos service modules directly? - most lightweight option - can share services between projects by sharing slice -* run a command directly inside systemd-nspawn - - fully virtualised without needing an image or anything - - should be very fast - * roll an oci image and run inside systemd-nspawn - this allows reuse of the entire nixos module system, including service config - docker-like port forwarding + - doesn't fucking work, hates nix oci images, fake and bad diff --git a/flake.lock b/flake.lock index 3ae0772..6f64fb8 100644 --- a/flake.lock +++ b/flake.lock @@ -24,11 +24,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", "type": "github" }, "original": { @@ -54,22 +54,6 @@ } }, "flake-compat_3": { - "flake": false, - "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_4": { "flake": false, "locked": { "lastModified": 1717312683, @@ -140,24 +124,6 @@ "inputs": { "systems": "systems_2" }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_3": { - "inputs": { - "systems": "systems_3" - }, "locked": { "lastModified": 1681202837, "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", @@ -172,31 +138,13 @@ "type": "github" } }, - "flake-utils_4": { - "inputs": { - "systems": "systems_4" - }, - "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "flatpaks": { "locked": { - "lastModified": 1711997375, - "narHash": "sha256-KvU4gOtuFMS9Il67glRGtdNfguAINT9pCaXtvCL8uI8=", + "lastModified": 1721549352, + "narHash": "sha256-nlXJa8RSOX0kykrIYW33ukoHYq+FOSNztHLLgqKwOp8=", "owner": "gmodena", "repo": "nix-flatpak", - "rev": "45bf66f7068db79b552da864c0e87452be624d6c", + "rev": "dbce39ea8664820ba9037caaf1e2fad365ed6b4b", "type": "github" }, "original": { @@ -235,11 +183,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1718241864, - "narHash": "sha256-/XOumFymqlUSS2OZZSOIUL7z1vQyxOEpuOqynH85aYI=", + "lastModified": 1723549983, + "narHash": "sha256-8lya0y9tR3dtVk++nUjVaPbSb5+Ah+vKgcX+3R556BQ=", "owner": "helix-editor", "repo": "helix", - "rev": "9c479e6d2de3bca9dec304f9182cee2b1c0ad766", + "rev": "f65ec32a1c2e09b3b32b521617f4a3ef19bc71c5", "type": "github" }, "original": { @@ -255,15 +203,16 @@ ] }, "locked": { - "lastModified": 1718243258, - "narHash": "sha256-abBpj2VU8p6qlRzTU8o22q68MmOaZ4v8zZ4UlYl5YRU=", - "owner": "nix-community", + "lastModified": 1723535926, + "narHash": "sha256-XCQ/IGVRwhM0m2jDYkTKySEKpdI5mRcrFCkEhkn2+K4=", + "owner": "n-hass", "repo": "home-manager", - "rev": "8d5e27b4807d25308dfe369d5a923d87e7dbfda3", + "rev": "db192f855b550c5e5cb25cef7d41be2081b32a3f", "type": "github" }, "original": { - "owner": "nix-community", + "owner": "n-hass", + "ref": "podman-module", "repo": "home-manager", "type": "github" } @@ -284,52 +233,43 @@ "type": "github" } }, - "nix-index-database": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, + "madness": { "locked": { - "lastModified": 1718011381, - "narHash": "sha256-sFXI+ZANp/OC+MwfJoZgPSf4xMdtzQMe1pS3FGti4C8=", - "owner": "Mic92", - "repo": "nix-index-database", - "rev": "88ad3d7501e22b2401dd72734b032b7baa794434", + "lastModified": 1720637547, + "narHash": "sha256-5MGG0iRBvP35VlcHvxVrapuLygZwHwMB3g0M0fWxz58=", + "owner": "antithesishq", + "repo": "madness", + "rev": "c22c9c03579b7175d94f63e44ee0e518bb5ccdba", "type": "github" }, "original": { - "owner": "Mic92", - "repo": "nix-index-database", + "owner": "antithesishq", + "repo": "madness", "type": "github" } }, - "nix-ld-rs": { + "nix-index-database": { "inputs": { - "flake-compat": "flake-compat", - "flake-utils": "flake-utils_2", - "nixpkgs": [ - "nixpkgs" - ] + "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1718260327, - "narHash": "sha256-P2PsdY2kLin3S0AW7dYWWI+1/a/+onp3KaAz8O32EnY=", - "owner": "nix-community", - "repo": "nix-ld-rs", - "rev": "753a1539846e6f75c88583777a3f6e40f4064302", + "lastModified": 1723352546, + "narHash": "sha256-WTIrvp0yV8ODd6lxAq4F7EbrPQv0gscBnyfn559c3k8=", + "owner": "Mic92", + "repo": "nix-index-database", + "rev": "ec78079a904d7d55e81a0468d764d0fffb50ac06", "type": "github" }, "original": { - "owner": "nix-community", - "repo": "nix-ld-rs", + "owner": "Mic92", + "repo": "nix-index-database", "type": "github" } }, "nix-rice": { "inputs": { - "flake-compat": "flake-compat_2", - "flake-utils": "flake-utils_3", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils_2", "kitty-themes-src": "kitty-themes-src", "nixpkgs-lib": "nixpkgs-lib", "pre-commit-hooks": "pre-commit-hooks" @@ -352,14 +292,15 @@ "inputs": { "flake-parts": "flake-parts", "flake-root": "flake-root", - "nixpkgs": "nixpkgs_3" + "nixpkgs": "nixpkgs_4", + "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1718200260, - "narHash": "sha256-YcifM/i8wMzZHjyY9FNoruDb5Arm6Xw4RKfdvZBLdQU=", + "lastModified": 1723381807, + "narHash": "sha256-tBvNlNvI3xRjmfUuzwgwWFrk+SO50wlrmAGRuG3Yzi4=", "owner": "nix-community", "repo": "nixd", - "rev": "6811dcf03ac055752a3f28cbabf90bd0b0cee417", + "rev": "c9d8970a646dbaa82981d050d905637a29bbdd21", "type": "github" }, "original": { @@ -370,16 +311,17 @@ }, "nixos-cosmic": { "inputs": { - "flake-compat": "flake-compat_4", - "nixpkgs": "nixpkgs_4", - "nixpkgs-stable": "nixpkgs-stable_2" + "flake-compat": "flake-compat_3", + "nixpkgs": "nixpkgs_5", + "nixpkgs-stable": "nixpkgs-stable_2", + "rust-overlay": "rust-overlay_2" }, "locked": { - "lastModified": 1718332244, - "narHash": "sha256-91RPJQ00iEtqvY21lPtPQNRWtips9X/iuIXOCS8qeZI=", + "lastModified": 1723512951, + "narHash": "sha256-XZMqVka80UyX9JB6qnsb8TVERWgHSaKo0IAw9rFTTIU=", "owner": "lilyinstarlight", "repo": "nixos-cosmic", - "rev": "aed79d6d7e67d4229378c9812fe464ceeb90473f", + "rev": "c2e0c9b3ef4ed20ea59031c6187b408d560cf874", "type": "github" }, "original": { @@ -455,11 +397,11 @@ }, "nixpkgs-stable_2": { "locked": { - "lastModified": 1718208800, - "narHash": "sha256-US1tAChvPxT52RV8GksWZS415tTS7PV42KTc2PNDBmc=", + "lastModified": 1723282977, + "narHash": "sha256-oTK91aOlA/4IsjNAZGMEBz7Sq1zBS0Ltu4/nIQdYDOg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "cc54fb41d13736e92229c21627ea4f22199fee6b", + "rev": "a781ff33ae258bbcfd4ed6e673860c3e923bf2cc", "type": "github" }, "original": { @@ -470,6 +412,22 @@ } }, "nixpkgs_2": { + "locked": { + "lastModified": 1723175592, + "narHash": "sha256-M0xJ3FbDUc4fRZ84dPGx5VvgFsOzds77KiBMW/mMTnI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5e0ca22929f3342b19569b21b2f3462f053e497b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { "locked": { "lastModified": 1681303793, "narHash": "sha256-JEdQHsYuCfRL2PICHlOiH/2ue3DwoxUX7DJ6zZxZXFk=", @@ -485,7 +443,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_4": { "locked": { "lastModified": 1714562304, "narHash": "sha256-Mr3U37Rh6tH0FbaDFu0aZDwk9mPAe7ASaqDOGgLqqLU=", @@ -501,13 +459,13 @@ "type": "github" } }, - "nixpkgs_4": { + "nixpkgs_5": { "locked": { - "lastModified": 1718160348, - "narHash": "sha256-9YrUjdztqi4Gz8n3mBuqvCkMo4ojrA6nASwyIKWMpus=", + "lastModified": 1723175592, + "narHash": "sha256-M0xJ3FbDUc4fRZ84dPGx5VvgFsOzds77KiBMW/mMTnI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "57d6973abba7ea108bac64ae7629e7431e0199b6", + "rev": "5e0ca22929f3342b19569b21b2f3462f053e497b", "type": "github" }, "original": { @@ -519,11 +477,11 @@ }, "nur": { "locked": { - "lastModified": 1718337354, - "narHash": "sha256-gbkVNpb2cnt5uVrxWykzIO4xCC/FtUhtKNPJ/792Uh8=", + "lastModified": 1723554177, + "narHash": "sha256-c874Bx8Hi6NGEt+PZQ88tgay2eyZ9Zly6rDHFhKFRJk=", "owner": "nix-community", "repo": "NUR", - "rev": "6a09d5a34d9f535b2b03c62ea6e4b23d12d5ea83", + "rev": "494fb37109715b5e3498c6a85532d5be16bdf10e", "type": "github" }, "original": { @@ -534,13 +492,13 @@ }, "pre-commit-hooks": { "inputs": { - "flake-compat": "flake-compat_3", + "flake-compat": "flake-compat_2", "flake-utils": [ "nix-rice", "flake-utils" ], "gitignore": "gitignore", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs_3", "nixpkgs-stable": "nixpkgs-stable" }, "locked": { @@ -562,8 +520,8 @@ "flatpaks": "flatpaks", "helix": "helix", "home-manager": "home-manager", + "madness": "madness", "nix-index-database": "nix-index-database", - "nix-ld-rs": "nix-ld-rs", "nix-rice": "nix-rice", "nixd": "nixd", "nixos-cosmic": "nixos-cosmic", @@ -572,7 +530,7 @@ "nixpkgs" ], "nur": "nur", - "rust-overlay": "rust-overlay_2" + "rust-overlay": "rust-overlay_3" } }, "rust-overlay": { @@ -602,17 +560,37 @@ }, "rust-overlay_2": { "inputs": { - "flake-utils": "flake-utils_4", + "nixpkgs": [ + "nixos-cosmic", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1723429325, + "narHash": "sha256-4x/32xTCd+xCwFoI/kKSiCr5LQA2ZlyTRYXKEni5HR8=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "65e3dc0fe079fe8df087cd38f1fe6836a0373aad", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "rust-overlay_3": { + "inputs": { "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1718331519, - "narHash": "sha256-6Ru37wS8uec626nHVIh6hSpCYB7eNc3RPFa2U//bhw4=", + "lastModified": 1723515680, + "narHash": "sha256-nHdKymsHCVIh0Wdm4MvSgxcTTg34FJIYHRQkQYaSuvk=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "419e7fae2731f41dd9b3e34dfe8802be68558b92", + "rev": "4ee3d9e9569f70d7bb40f28804d6fe950c81eab3", "type": "github" }, "original": { @@ -651,33 +629,24 @@ "type": "github" } }, - "systems_3": { + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixd", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "lastModified": 1722330636, + "narHash": "sha256-uru7JzOa33YlSRwf9sfXpJG+UAV+bnBEYMjrzKrQZFw=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "768acdb06968e53aa1ee8de207fd955335c754b7", "type": "github" }, "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_4": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", + "owner": "numtide", + "repo": "treefmt-nix", "type": "github" } } diff --git a/flake.nix b/flake.nix index c965009..b78c2fa 100644 --- a/flake.nix +++ b/flake.nix @@ -12,36 +12,27 @@ inputs.nixpkgs.follows = "nixpkgs"; }; - helix = { - url = "github:helix-editor/helix"; - # inputs.nixpkgs.follows = "nixpkgs"; - }; + helix.url = "github:helix-editor/helix"; home-manager = { - url = "github:nix-community/home-manager"; + # url = "github:nix-community/home-manager"; + url = "github:n-hass/home-manager/podman-module"; inputs.nixpkgs.follows = "nixpkgs"; }; - nix-index-database = { - url = "github:Mic92/nix-index-database"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + nix-index-database.url = "github:Mic92/nix-index-database"; nix-rice.url = "github:bertof/nix-rice"; nixd.url = "github:nix-community/nixd"; - nix-ld-rs = { - url = "github:nix-community/nix-ld-rs"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - flatpaks.url = "github:gmodena/nix-flatpak"; nixos-cosmic = { url = "github:lilyinstarlight/nixos-cosmic"; - # inputs.nixpkgs.follows = "nixpkgs"; }; + + madness.url = "github:antithesishq/madness"; }; outputs = { @@ -50,58 +41,65 @@ nixos-cosmic, home-manager, nur, - helix, flatpaks, - nixd, - nix-ld-rs, nix-index-database, - nix-rice, + madness, ... } @ inputs: let inherit (self) outputs; + sharedModules = [ + madness.nixosModules.madness + nur.nixosModules.nur + nix-index-database.nixosModules.nix-index + ./system/cachix.nix + ]; + system = "x86_64-linux"; in { overlays = import ./util/overlay.nix {inherit inputs;}; nixosConfigurations = { "quiver" = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; + inherit system; specialArgs = {inherit inputs outputs;}; - modules = [ - nixos-cosmic.nixosModules.default - nur.nixosModules.nur - nix-index-database.nixosModules.nix-index - ./system/quiver.nix - ./system/cachix.nix - home-manager.nixosModules.home-manager - { - home-manager.useGlobalPkgs = true; - home-manager.extraSpecialArgs = {inherit inputs outputs;}; - home-manager.users.bolt.imports = [ - flatpaks.homeManagerModules.nix-flatpak - ./home/bolt.nix - ]; - } - ]; + modules = + [ + ./system/quiver.nix + nixos-cosmic.nixosModules.default + home-manager.nixosModules.home-manager + { + home-manager = { + useGlobalPkgs = true; + extraSpecialArgs = {inherit inputs outputs;}; + users.bolt.imports = [ + flatpaks.homeManagerModules.nix-flatpak + ./home/bolt.nix + ]; + }; + } + ] + ++ sharedModules; }; "adrift" = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; + inherit system; specialArgs = {inherit inputs outputs;}; - modules = [ - nur.nixosModules.nur - nix-index-database.nixosModules.nix-index - ./system/adrift.nix - ./system/cachix.nix - home-manager.nixosModules.home-manager - { - home-manager.useGlobalPkgs = true; - home-manager.extraSpecialArgs = {inherit inputs outputs;}; - home-manager.users.plank.imports = [ - flatpaks.homeManagerModules.nix-flatpak - ./home/plank.nix - ]; - } - ]; + modules = + [ + ./system/adrift.nix + nixos-cosmic.nixosModules.default + home-manager.nixosModules.home-manager + { + home-manager = { + useGlobalPkgs = true; + extraSpecialArgs = {inherit inputs outputs;}; + users.plank.imports = [ + flatpaks.homeManagerModules.nix-flatpak + ./home/plank.nix + ]; + }; + } + ] + ++ sharedModules; }; }; }; diff --git a/home/bolt.nix b/home/bolt.nix index 544bcdf..6c77923 100644 --- a/home/bolt.nix +++ b/home/bolt.nix @@ -2,17 +2,18 @@ imports = [ ./home.nix ./util/local-webapp.nix - ./programs/stable-diffusion.nix - ./programs/ollama.nix + ./util/containers.nix + # ./programs/stable-diffusion.nix + ./programs/openwebui.nix ]; - home = { + home = rec { username = "bolt"; homeDirectory = "/home/bolt"; + sessionVariables.FLAKE = "${homeDirectory}/.nix"; }; home.packages = with pkgs; [ nicotine-plus - private-gpt ]; } diff --git a/home/home.nix b/home/home.nix index a15560c..6d97200 100644 --- a/home/home.nix +++ b/home/home.nix @@ -15,6 +15,7 @@ in { ./modules/theming.nix ./util/ez.nix ./util/flatpak.nix + ./util/cosmic.nix inputs.nix-index-database.hmModules.nix-index ]; @@ -24,7 +25,32 @@ in { options = "--delete-older-than 3d"; }; - xdg.enable = true; + xdg = { + enable = true; + # mimeApps = { + # enable = true; + # # TODO? + # # also figure out how cosmic detects default terminal + # }; + portal = let + xdg-cosmic = pkgs.xdg-desktop-portal-cosmic; + in { + enable = true; + config = { + common = { + default = [ + "cosmic" + ]; + }; + }; + configPackages = [ + xdg-cosmic + ]; + extraPortals = [ + xdg-cosmic + ]; + }; + }; home.packages = with pkgs; rice.fonts.pkgs @@ -42,6 +68,18 @@ in { }; services.udiskie.enable = true; + # fix reliance on nonexistent graphical-session-pre.target + systemd.user.services.udiskie = lib.mkForce { + Unit = { + Description = "udiskie mount daemon"; + After = []; + PartOf = ["graphical-session.target"]; + }; + + Service.ExecStart = ["${pkgs.udiskie}/bin/udiskie --appindicator"]; + + Install.WantedBy = ["graphical-session.target"]; + }; # programs.nix-index-database.comma.enable = true; # programs.nix-index = { diff --git a/home/icons/ollama.png b/home/icons/ollama.png new file mode 100644 index 0000000000000000000000000000000000000000..45f7dd9fe461c25cabbec5b41b98144e742aaa15 GIT binary patch literal 37402 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 Ve%`DwQKbuHvZt$`%Q~loCIFWu@CE /dev/null; printf $?) -ne 0 ]]; then - echo Not found. Creating OpenWebUI/Ollama container.. - ${docker} create -e PORT=${toString port} --restart no --network host --gpus all -v ollama:/root/.ollama -v open-webui:/app/backend/data --name openwebui --restart always ghcr.io/open-webui/open-webui:ollama - fi - echo Check complete. - ''}"; - ExecStart = "${docker} start -a openwebui"; - ExecStop = "${docker} stop openwebui"; - }; +}: { + services.podman = { + containers.ollama = { + # serviceName = "ollama"; + image = "ollama/ollama:latest"; + # ports = ["11434:11434"]; + devices = ["nvidia.com/gpu=all"]; + autostart = false; + networks = ["ollama"]; + extraOptions = [ + "--health-cmd" + (lib.escapeShellArg "bash -c 'cat < /dev/null > /dev/tcp/localhost/11434'") + ]; + }; + + networks.ollama = { + Subnet = "192.168.10.0/24"; + Gateway = "192.168.10.1"; }; }; + + # containers.ollama = { + # image = "ollama/ollama:latest"; + # # TODO: volume for models! + # extraOptions = ["--network=host" "--device=nvidia.com/gpu=all"]; + # }; } diff --git a/home/programs/openwebui.nix b/home/programs/openwebui.nix new file mode 100644 index 0000000..4b035a1 --- /dev/null +++ b/home/programs/openwebui.nix @@ -0,0 +1,40 @@ +{ + lib, + pkgs, + ... +}: let + port = 3021; +in { + imports = [./ollama.nix]; + + localWebApps = { + openwebui = { + name = "OpenWebUI"; + genericName = "LLM"; + icon = ../icons/openwebui.png; + inherit port; + requires.containers = ["openwebui" "ollama"]; + }; + }; + + services.podman = { + containers.openwebui = let + str = builtins.toString; + in { + # serviceName = "openwebui"; + image = "ghcr.io/open-webui/open-webui:main"; + ports = ["${str port}:${str port}"]; + environment = { + WEBUI_AUTH = "False"; + PORT = "${str port}"; + }; + autostart = false; + networks = ["ollama"]; + unitConfig = {Requires = ["podman-ollama.service"];}; + extraOptions = [ + "--health-cmd" + (lib.escapeShellArg "bash -c 'cat < /dev/null > /dev/tcp/localhost/${str port}'") + ]; + }; + }; +} diff --git a/home/programs/stable-diffusion.nix b/home/programs/stable-diffusion.nix index 8fe7b6f..051a37f 100644 --- a/home/programs/stable-diffusion.nix +++ b/home/programs/stable-diffusion.nix @@ -9,15 +9,12 @@ in { localWebApps = { stable-diffusion = { - name = "Stable Diffusion (ComfyUI)"; - genericName = "Stable Diffusion"; + name = "Stable Diffusion"; + genericName = "Image Generator"; icon = ../icons/comfyui.png; id = 5; port = 7860; - service = { - WorkingDirectory = "${homeDirectory}/code/etc/stable-diffusion-webui-docker"; - ExecStart = "${getExe pkgs.docker} compose --profile comfy up --build"; - }; + requires = ["stable-diffusion"]; }; }; } diff --git a/home/util/containers.nix b/home/util/containers.nix new file mode 100644 index 0000000..83dcddd --- /dev/null +++ b/home/util/containers.nix @@ -0,0 +1,365 @@ +{ + config, + lib, + pkgs, + ... +}: let + containerOptions = {...}: { + options = { + pullPolicy = mkOption { + type = with types; str; + description = "Podman container pulling policy"; + default = "newer"; + }; + image = mkOption { + type = with types; str; + description = "OCI image to run."; + example = "library/hello-world"; + }; + + imageFile = mkOption { + type = with types; nullOr package; + default = null; + description = '' + Path to an image file to load before running the image. This can + be used to bypass pulling the image from the registry. + + The `image` attribute must match the name and + tag of the image contained in this file, as they will be used to + run the container with that image. If they do not match, the + image will be pulled from the registry as usual. + ''; + example = literalExpression "pkgs.dockerTools.buildImage {...};"; + }; + + login = { + username = mkOption { + type = with types; nullOr str; + default = null; + description = "Username for login."; + }; + + passwordFile = mkOption { + type = with types; nullOr str; + default = null; + description = "Path to file containing password."; + example = "/etc/nixos/dockerhub-password.txt"; + }; + + registry = mkOption { + type = with types; nullOr str; + default = null; + description = "Registry where to login to."; + example = "https://docker.pkg.github.com"; + }; + }; + + cmd = mkOption { + type = with types; listOf str; + default = []; + description = "Commandline arguments to pass to the image's entrypoint."; + example = literalExpression '' + ["--port=9000"] + ''; + }; + + labels = mkOption { + type = with types; attrsOf str; + default = {}; + description = "Labels to attach to the container at runtime."; + example = literalExpression '' + { + "traefik.https.routers.example.rule" = "Host(`example.container`)"; + } + ''; + }; + + entrypoint = mkOption { + type = with types; nullOr str; + description = "Override the default entrypoint of the image."; + default = null; + example = "/bin/my-app"; + }; + + environment = mkOption { + type = with types; attrsOf str; + default = {}; + description = "Environment variables to set for this container."; + example = literalExpression '' + { + DATABASE_HOST = "db.example.com"; + DATABASE_PORT = "3306"; + } + ''; + }; + + environmentFiles = mkOption { + type = with types; listOf path; + default = []; + description = "Environment files for this container."; + example = literalExpression '' + [ + /path/to/.env + /path/to/.env.secret + ] + ''; + }; + + log-driver = mkOption { + type = types.str; + default = "journald"; + description = '' + Logging driver for the container. The default of + `"journald"` means that the container's logs will be + handled as part of the systemd unit. + + For more details and a full list of logging drivers, refer to podman documentation. + + For Docker: + [Docker engine documentation](https://docs.docker.com/engine/reference/run/#logging-drivers---log-driver) + + For Podman: + Refer to the docker-run(1) man page. + ''; + }; + + ports = mkOption { + type = with types; listOf str; + default = []; + description = '' + Network ports to publish from the container to the outer host. + + Valid formats: + - `::` + - `::` + - `:` + - `` + + Both `hostPort` and `containerPort` can be specified as a range of + ports. When specifying ranges for both, the number of container + ports in the range must match the number of host ports in the + range. Example: `1234-1236:1234-1236/tcp` + + When specifying a range for `hostPort` only, the `containerPort` + must *not* be a range. In this case, the container port is published + somewhere within the specified `hostPort` range. + Example: `1234-1236:1234/tcp` + + Refer to the + [Docker engine documentation](https://docs.docker.com/engine/reference/run/#expose-incoming-ports) for full details. + ''; + example = literalExpression '' + [ + "8080:9000" + ] + ''; + }; + + user = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Override the username or UID (and optionally groupname or GID) used + in the container. + ''; + example = "nobody:nogroup"; + }; + + volumes = mkOption { + type = with types; listOf str; + default = []; + description = '' + List of volumes to attach to this container. + + Note that this is a list of `"src:dst"` strings to + allow for `src` to refer to `/nix/store` paths, which + would be difficult with an attribute set. There are + also a variety of mount options available as a third + field; please refer to the + [docker engine documentation](https://docs.docker.com/engine/reference/run/#volume-shared-filesystems) for details. + ''; + example = literalExpression '' + [ + "volume_name:/path/inside/container" + "/path/on/host:/path/inside/container" + ] + ''; + }; + + workdir = mkOption { + type = with types; nullOr str; + default = null; + description = "Override the default working directory for the container."; + example = "/var/lib/hello_world"; + }; + + dependsOn = mkOption { + type = with types; listOf str; + default = []; + description = '' + Define which other containers this one depends on. They will be added to both After and Requires for the unit. + + Use the same name as the attribute under `virtualisation.oci-containers.containers`. + ''; + example = literalExpression '' + containers = { + node1 = {}; + node2 = { + dependsOn = [ "node1" ]; + } + } + ''; + }; + + hostname = mkOption { + type = with types; nullOr str; + default = null; + description = "The hostname of the container."; + example = "hello-world"; + }; + + preRunExtraOptions = mkOption { + type = with types; listOf str; + default = []; + description = "Extra options for podman that go before the `run` argument."; + example = ["--runtime" "runsc"]; + }; + + extraOptions = mkOption { + type = with types; listOf str; + default = []; + description = "Extra options for podman run`."; + example = literalExpression '' + ["--network=host"] + ''; + }; + + autoStart = mkOption { + type = types.bool; + default = false; + description = '' + When enabled, the container is automatically started on boot. + If this option is set to false, the container has to be started on-demand via its service. + ''; + }; + }; + }; + + mkService = name: container: let + podman = lib.getExe pkgs.podman; + rm = lib.getExe' pkgs.coreutils "rm"; + printf = lib.getExe' pkgs.coreutils "printf"; + getId = ''"$(${lib.getExe' pkgs.coreutils "id"} -u)"''; + dependsOn = map (x: "podman-${x}.service") container.dependsOn; + escapedName = lib.escapeShellArg name; + preStartScript = pkgs.writeShellApplication { + name = "pre-start"; + runtimeInputs = []; + text = '' + ${printf} "Running pre-start script.." + ${podman} rm -f ${name} || true + ${lib.optionalString (container.imageFile != null) '' + ${podman} load -i ${container.imageFile} + ''} + ${rm} -f /run/user/${getId}/podman-${escapedName}.ctr-id + ${printf} " success.\nStarting Podman...\n" + ''; + }; + script = concatStringsSep " \\\n " ( + [ + "exec ${podman} " + ] + ++ map escapeShellArg container.preRunExtraOptions + ++ [ + "run" + "--log-level=debug" + "--rm" + "--name=${escapedName}" + "--log-driver=${container.log-driver}" + ] + ++ optional (container.entrypoint != null) + "--entrypoint=${escapeShellArg container.entrypoint}" + ++ optional (container.hostname != null) + "--hostname=${escapeShellArg container.hostname}" + ++ optional (container.pullPolicy != null) + "--pull=${container.pullPolicy}" + ++ [ + "--cidfile=/run/user/${getId}/podman-${escapedName}.ctr-id" + # "--sdnotify=ignore" + "--cgroups=no-conmon" + "--sdnotify=conmon" + "-d" + "--replace" + ] + ++ (mapAttrsToList (k: v: "-e ${escapeShellArg k}=${escapeShellArg v}") container.environment) + ++ map (f: "--env-file ${escapeShellArg f}") container.environmentFiles + ++ map (p: "-p ${escapeShellArg p}") container.ports + ++ optional (container.user != null) "-u ${escapeShellArg container.user}" + ++ map (v: "-v ${escapeShellArg v}") container.volumes + ++ (mapAttrsToList (k: v: "-l ${escapeShellArg k}=${escapeShellArg v}") container.labels) + ++ optional (container.workdir != null) "-w ${escapeShellArg container.workdir}" + ++ map escapeShellArg container.extraOptions + ++ [container.image] + ++ map escapeShellArg container.cmd + ); + + inherit (lib) concatStringsSep escapeShellArg optional optionalAttrs optionalString mapAttrsToList; + in { + Unit = { + WantedBy = [] ++ lib.optional (container.autoStart) "default.target"; # graphical-session instead maybe? + After = dependsOn; + Requires = dependsOn; + # StopWhenUnneeded = true; + }; + # TODO make network target.. + # wants = lib.optional (container.imageFile == null) "network-online.target"; + # after = lib.optionals (container.imageFile == null) [ "network-online.target" ] + # ++ dependsOn; + # environment = proxy_env; + + Service = { + ### There is no generalized way of supporting `reload` for docker + ### containers. Some containers may respond well to SIGHUP sent to their + ### init process, but it is not guaranteed; some apps have other reload + ### mechanisms, some don't have a reload signal at all, and some docker + ### images just have broken signal handling. The best compromise in this + ### case is probably to leave ExecReload undefined, so `systemctl reload` + ### will at least result in an error instead of potentially undefined + ### behaviour. + ### + ### Advanced users can still override this part of the unit to implement + ### a custom reload handler, since the result of all this is a normal + ### systemd service from the perspective of the NixOS module system. + ### + # ExecReload = ...; + ### + ExecStartPre = ["${preStartScript}/bin/pre-start"]; + ExecStart = [ + "${pkgs.writeShellScript "start" script}" + ]; + ExecStop = [ + "${podman} stop --ignore --cidfile=/run/user/${getId}/podman-${escapedName}.ctr-id" + "${podman} rm -f --ignore --cidfile=/run/user/${getId}/podman-${escapedName}.ctr-id" + ]; + # TimeoutStartSec = 0; + # TimeoutStopSec = 120; + # Restart = "always"; + Environment = ["PODMAN_SYSTEMD_UNIT=podman-${name}.service"]; + Type = "notify"; + NotifyAccess = "all"; + # Type = "exec"; + }; + }; + + cfg = config.containers; + inherit (lib) mapAttrs' nameValuePair mkOption types mkIf literalExpression; +in { + options.containers = mkOption { + default = {}; + type = with types; attrsOf (submodule containerOptions); + }; + + config = mkIf (cfg != {}) { + systemd.user.services = mapAttrs' (k: v: nameValuePair "podman-${k}" (mkService k v)) cfg; + }; +} diff --git a/util/cosmic.nix b/home/util/cosmic.nix similarity index 51% rename from util/cosmic.nix rename to home/util/cosmic.nix index e5f50aa..81e9b60 100644 --- a/util/cosmic.nix +++ b/home/util/cosmic.nix @@ -3,7 +3,7 @@ config, ... }: let - inherit (lib) filterAttrs concatStrings concatStringsSep mapAttrsToList concatLists foldlAttrs concatMapAttrs mapAttrs' nameValuePair boolToString; + inherit (lib) filterAttrs concatStrings concatStringsSep mapAttrsToList concatLists foldlAttrs concatMapAttrs mapAttrs mapAttrs' nameValuePair boolToString; inherit (builtins) typeOf toString stringLength; # build up serialisation machinery from here for various types @@ -79,75 +79,98 @@ then concatStrings [s.type "(" (toString s.data) ")"] else s; - # set up boilerplate for keybinding config file - cosmic-bindings = a: - struct { - key_bindings = mapBindings a; - data_control_enabled = false; - }; mapCosmicSettings = application: options: mapAttrs' (k: v: - nameValuePair "xdg/cosmic/${application}/v${options.version}/${k}" { + nameValuePair "cosmic/${application}/v${options.version}/${k}" { enable = true; text = serialise.${typeOf v} v; }) options.option; + # deconstructKeybindAttr = attr: mapAttrs' ( + # name: value: + # ) attr; + # rec_attr = lib.types.attrsOf (lib.types.oneOf lib.types.string rec_attr); in { - options.services.desktopManager.cosmic.keybindings = with lib; - mkOption { - default = []; - type = with types; - listOf (submodule { - options = { - modifiers = mkOption { - type = listOf str; - default = []; - }; - key = mkOption { - type = nullOr str; - default = null; - }; - action = mkOption { - type = either str (submodule { - options = { - type = mkOption { - type = str; - }; - data = mkOption { - type = oneOf [str int]; - default = ""; - }; - }; - }); - }; - }; - }); - }; + options.cosmic = { + enable = with lib; + mkOption { + default = false; + type = types.bool; + }; - options.services.desktopManager.cosmic.otherSettings = with lib; - mkOption { - default = {}; - type = with types; - attrsOf (submodule { - options = { - version = mkOption { - type = str; - default = "1"; - }; - option = mkOption { - type = attrsOf anything; - }; - }; - }); - }; + defaultKeybindings = with lib; + mkOption { + default = true; + type = types.bool; + }; - config.environment.etc = + # binds = with lib; + # mkOption { + # default = {}; + # type = with types; rec_attr; + # }; + + keybindings = with lib; + mkOption { + default = []; + type = with types; + listOf (submodule { + options = { + modifiers = mkOption { + type = listOf str; + default = []; + }; + key = mkOption { + type = nullOr str; + default = null; + }; + action = mkOption { + type = either str (submodule { + options = { + type = mkOption { + type = str; + }; + data = mkOption { + type = oneOf [str int]; + default = ""; + }; + }; + }); + }; + }; + }); + }; + + otherSettings = with lib; + mkOption { + default = {}; + type = with types; + attrsOf (submodule { + options = { + version = mkOption { + type = str; + default = "1"; + }; + option = mkOption { + type = attrsOf anything; + }; + }; + }); + }; + }; + + # TODO we need to split between system_actions, workspaces, custom + config.xdg.configFile = { - "cosmic-comp/config.ron".text = cosmic-bindings config.services.desktopManager.cosmic.keybindings; + "cosmic/com.system76.CosmicSettings.Shortcuts/v1/custom".text = mapBindings config.cosmic.keybindings; + "cosmic/com.system76.CosmicSettings.Shortcuts/v1/defaults" = { + text = "{}"; + enable = !config.cosmic.defaultKeybindings; + }; } // concatMapAttrs ( application: options: mapCosmicSettings application options ) - config.services.desktopManager.cosmic.otherSettings; + config.cosmic.otherSettings; } diff --git a/home/util/firefox-webapp.nix b/home/util/firefox-webapp.nix index f76cdb0..1eeece3 100644 --- a/home/util/firefox-webapp.nix +++ b/home/util/firefox-webapp.nix @@ -4,15 +4,22 @@ ... }: let inherit (builtins) getAttr stringLength substring; - inherit (lib) mkOption getExe; + inherit (lib) mkOption getExe listToAttrs attrsToList imap; inherit (lib.attrsets) filterAttrs mapAttrs mapAttrs' nameValuePair; inherit (lib.strings) concatStringsSep toUpper; + enumerate = a: + listToAttrs (imap (id: { + name, + value, + }: { + inherit name; + value = value // {inherit id;}; + }) (attrsToList a)); make-app-profiles = cfg: mapAttrs' (name: cfg: nameValuePair "home-manager-webapp-${name}" { inherit (cfg) id; - userChrome = '' @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); @@ -82,10 +89,10 @@ in { description = "The URL of the webapp to launch."; }; - id = mkOption { - type = int; - description = "The Firefox profile ID to set."; - }; + # id = mkOption { + # type = int; + # description = "The Firefox profile ID to set."; + # }; hidden = mkOption { type = bool; @@ -174,7 +181,7 @@ in { }; config = { - programs.firefox.profiles = make-app-profiles config.programs.firefox.webapps; + programs.firefox.profiles = make-app-profiles (enumerate config.programs.firefox.webapps); xdg.desktopEntries = mapAttrs (name: cfg: { diff --git a/home/util/local-webapp.nix b/home/util/local-webapp.nix index c546e82..b78e5af 100644 --- a/home/util/local-webapp.nix +++ b/home/util/local-webapp.nix @@ -7,32 +7,84 @@ inherit (lib) mkOption; inherit (lib.attrsets) mapAttrs mapAttrs' nameValuePair; inherit (lib) getExe getExe'; - # make a firefox webapp entry for the client app + # make a firefox webapp + hidden .desktop entry for the client app make-firefox = cfg: mapAttrs' ( name: cfg: nameValuePair "${name}-client" { - inherit (cfg) name id; + inherit (cfg) name; url = "http://127.0.0.1:${builtins.toString cfg.port}"; extraSettings = config.programs.firefox.profiles.default.settings; hidden = true; } ) cfg; - # make a systemd service for running the backend - make-systemd = cfg: + # make a systemd service for running the frontend + make-systemd-service = cfg: + mapAttrs' ( + name: cfg: + if + (cfg.service + != null) + then + nameValuePair "${cfg.name}-frontend" { + Unit = { + Description = "${cfg.name} Frontend"; + WantedBy = lib.mkForce []; + }; + + Service = cfg.service; + } + else nameValuePair "" {} + ) + cfg; + # modify systemd units to be PartOf this target + modify-systemd-services = cfg: + lib.listToAttrs (lib.flatten (lib.mapAttrsToList ( + name: cfg: (map ( + req: { + name = "${req}"; + value = { + Unit = { + PartOf = "${lib.toLower cfg.name}.target"; + }; + }; + } + ) + cfg.requires.services) + ) + cfg)); + modify-quadlets = cfg: + lib.listToAttrs (lib.flatten (lib.mapAttrsToList ( + name: cfg: (map ( + req: { + name = "${req}"; + value = { + unitConfig = { + PartOf = "${lib.toLower cfg.name}.target"; + }; + }; + } + ) + cfg.requires.containers) + ) + cfg)); + # make a systemd target to collate dependencies + make-systemd-target = cfg: mapAttrs ( name: cfg: { Unit = { - Description = "${cfg.name} Backend"; + Description = "${cfg.name} Target"; WantedBy = lib.mkForce []; + Requires = + (map (req: req + ".service") cfg.requires.services) + ++ (map (req: "podman-" + req + ".service") cfg.requires.containers); }; - Service = cfg.service; } ) cfg; - # make desktop shortcuts and a script which will handle starting both the above + # make desktop shortcuts and a script which will handle starting everything make-xdg = cfg: mapAttrs ( name: cfg: { @@ -42,45 +94,72 @@ notify-send = "${getExe' pkgs.libnotify "notify-send"} -a \"${cfg.name}\""; systemctl = "${getExe' pkgs.systemd "systemctl"}"; dex = "${getExe pkgs.dex}"; - curl = "${getExe pkgs.curl}"; + podman = "${getExe pkgs.podman}"; + makeContainerCheck = container: ''[ "$(${podman} inspect -f {{.State.Health.Status}} ${container})" == "healthy" ]''; + # makeContainerCheck = container: '' + # [ ${podman} inspect -f {{.State.Status}} ${container})" != "running" ] + # ''; + containerChecks = + if (cfg.requires.containers != []) + then + '' + container_checks() { + if '' + + (lib.concatMapStringsSep " && " + (container: makeContainerCheck container) + cfg.requires.containers) + + '' + ; then + return 0 + else + return 1 + fi + } + '' + else '' + container_checks() { + return 0 + } + ''; in pkgs.writeShellScript "${name}" '' set -euo pipefail - ${notify-send} "Launching backend.." "Please be patient." - ${systemctl} --user start ${name} - attempts=0 - success=false - while [[ $attempts -lt $((20*9)) ]]; do - if [[ $(${curl} -sf http://127.0.0.1:${builtins.toString cfg.port} --output /dev/null; printf $?) -eq 0 ]]; then - ${notify-send} "Backend up." "Launching client.." - success=true - break - else - attempts=$((attempts+1)) - if [[ $(($attempts % 20)) -eq 0 ]]; then - ${notify-send} "Launching backend.." "Still launching.. ($((attempts/2))s)" - fi + exit_error() { + ${notify-send} -w "Failure" $1 + exit 1 + } + + ${containerChecks} + + ${notify-send} "Launching ${name} backend.." "Please be patient." + ${systemctl} --user start ${name}.target || exit_error "Failed to launch!" + + checks=0 + until container_checks; do + sleep 2 + checks=$((checks+1)) + if [ $((checks%10)) -eq 0 ]; then + ${notify-send} "Waiting for backend." + fi + if [ $checks -ge 60 ]; then + ${systemctl} --no-block --user stop ${name}.target + exit_error "Failed to launch!" fi - sleep 0.5 done - if [[ ! $success ]]; then - ${notify-send} "Failure" "Failed to launch backend!" - ${systemctl} --user kill ${name} - exit 1 - fi - + ${notify-send} "Launching ${name}.." ${dex} -w ~/.nix-profile/share/applications/${name}-client.desktop ${notify-send} "Goodbye" "Shutting down." - ${systemctl} --user stop ${name} + ${systemctl} --user stop ${name}.target exit 0 ''}"; } ) cfg; + cfg = config.localWebApps; in { options.localWebApps = mkOption { default = {}; @@ -104,29 +183,59 @@ in { default = null; }; - id = mkOption { - type = int; - description = "Firefox profile ID for the webapp's client"; + requires = mkOption { + type = nullOr (submodule { + options = { + containers = mkOption { + type = listOf str; + default = []; + }; + services = mkOption { + type = listOf str; + default = []; + }; + }; + }); + default = null; + description = "Containers or services this app requires."; + }; + + service = mkOption { + type = nullOr (submodule { + options = { + execStartPre = mkOption { + type = nullOr str; + default = null; + }; + execStart = mkOption { + type = nullOr str; + default = null; + }; + execStop = mkOption { + type = nullOr str; + default = null; + }; + }; + }); + default = null; + description = "Submodule containing exec[StartPre/Start/Stop] commands for any required systemd service"; }; port = mkOption { type = int; description = "Local port the webapp should host on."; }; - - service = mkOption { - type = attrsOf str; - description = "The service settings for systemd user service. Requires at least ExecStart."; - }; }; }); }; config = { - programs.firefox.webapps = make-firefox config.localWebApps; + programs.firefox.webapps = make-firefox cfg; - systemd.user.services = make-systemd config.localWebApps; + systemd.user.targets = make-systemd-target cfg; + systemd.user.services = (make-systemd-service cfg) // (modify-systemd-services cfg); + services.podman.containers = modify-quadlets cfg; - xdg.desktopEntries = make-xdg config.localWebApps; + xdg.desktopEntries = make-xdg cfg; }; } diff --git a/system/adrift.nix b/system/adrift.nix index b3fad37..fb6e2db 100644 --- a/system/adrift.nix +++ b/system/adrift.nix @@ -12,6 +12,9 @@ ./configuration.nix ]; + nix.settings.trusted-users = ["plank"]; + programs.nh.flake = "/home/plank/.nix"; + boot.kernelParams = [ "mitigations=off" "quiet" diff --git a/system/configuration.nix b/system/configuration.nix index 8c5bb93..a85951e 100644 --- a/system/configuration.nix +++ b/system/configuration.nix @@ -17,7 +17,6 @@ outputs.overlays.rice inputs.helix.overlays.default inputs.nixd.overlays.default - inputs.nix-ld-rs.overlays.default ]; config = { allowUnfree = true; @@ -29,7 +28,7 @@ # add flake inputs to our registry to allow global use registry = lib.mapAttrs (_: value: {flake = value;}) inputs; settings = { - trusted-users = ["bolt" "plank"]; + # trusted-users = ["bolt" "plank"]; experimental-features = "nix-command flakes"; substitute = true; }; @@ -37,11 +36,6 @@ keep-outputs = true keep-derivations = true ''; - gc = { - automatic = true; - dates = "daily"; - options = "--delete-older-than 3d"; - }; optimise.automatic = true; package = pkgs.lix; }; @@ -64,8 +58,11 @@ services.libinput.enable = true; - hardware.opengl.enable = true; - hardware.opengl.driSupport32Bit = true; + # busted as hell + # services.usbmuxd.enable = true; # for iphone + + hardware.graphics.enable = true; + hardware.graphics.enable32Bit = true; services.udisks2.enable = true; @@ -82,10 +79,20 @@ curl eza git + + # broken \/ + # libimobiledevice + # ifuse ]; services.atuin.enable = true; + programs.nh = { + enable = true; + clean.enable = true; + clean.extraArgs = "--keep-since 3d --keep 3"; + }; + programs.nix-index-database.comma.enable = true; programs.nix-index.enableZshIntegration = false; programs.nix-index.enableBashIntegration = false; @@ -94,13 +101,7 @@ programs.dconf.enable = true; services.gvfs.enable = true; - programs.nix-ld = { - enable = true; - package = pkgs.nix-ld-rs; - }; - # give cpuset to user systemd.services."user@".serviceConfig.Delegate = "memory pids cpu cpuset"; - - system.stateVersion = "22.11"; # Did you read the comment? + systemd.user.extraConfig = "LogLevel=debug"; } diff --git a/system/modules/cosmic.nix b/system/modules/cosmic.nix index c626157..e95fd7e 100644 --- a/system/modules/cosmic.nix +++ b/system/modules/cosmic.nix @@ -1,114 +1,6 @@ -{ - pkgs, - lib, - ... -}: let - inherit (lib) range mapAttrsToList; - inherit (builtins) toString; - workspaceRange = range 1 6; - makeWorkspaceBinding = modifiers: action: - map (i: { - inherit modifiers; - key = toString i; - action = { - type = action; - data = i; - }; - }) - workspaceRange; - focusWsBindings = makeWorkspaceBinding ["Super"] "Workspace"; - moveWsBindings = makeWorkspaceBinding ["Super" "Shift"] "SendToWorkspace"; - - hjkl = { - "h" = "Left"; - "j" = "Down"; - "k" = "Up"; - "l" = "Right"; - }; - makeDirBinding = modifiers: action: - mapAttrsToList ( - key: dir: { - inherit key modifiers; - action = { - type = action; - data = dir; - }; - } - ) - hjkl; - focusBindings = makeDirBinding ["Super"] "Focus"; - moveBindings = makeDirBinding ["Super" "Shift"] "Move"; - winManagementBindings = - focusWsBindings - ++ moveWsBindings - ++ focusBindings - ++ moveBindings; - genBinding = key: modifiers: action: { - inherit key modifiers action; - }; - genSpawnBinding = key: modifiers: app: { - inherit key modifiers; - action = { - type = "Spawn"; - data = app; - }; - }; -in { - imports = [ - ../../util/cosmic.nix - ]; +{pkgs, ...}: { services.desktopManager.cosmic = { enable = true; - otherSettings = { - "com.system76.CosmicPanel.Dock" = { - option.opacity = 0.8; - }; - }; - keybindings = - winManagementBindings - ++ [ - (genBinding "q" ["Super" "Shift"] "Close") - (genBinding "w" ["Super"] "ToggleStacking") - (genBinding "s" ["Super"] "ToggleOrientation") - (genBinding "space" ["Super"] "ToggleWindowFloating") - (genBinding "space" ["Super" "Shift"] "Maximize") - (genBinding "minus" ["Super"] "Minimize") - (genBinding "r" ["Super"] { - type = "Resizing"; - data = "Outwards"; - }) - (genBinding "r" ["Super" "Shift"] { - type = "Resizing"; - data = "Inwards"; - }) - (genBinding "tab" ["Super"] "NextOutput") - (genBinding "tab" ["Super" "Shift"] "MoveToNextOutput") - (genBinding "grave" ["Super"] "PreviousOutput") - (genBinding "grave" ["Super" "Shift"] "MoveToPreviousOutput") - (genSpawnBinding "f" ["Super"] "firefox") - (genSpawnBinding "e" ["Super"] "nautilus") - (genSpawnBinding "equal" ["Super"] "keepassxc") - (genSpawnBinding "return" ["Super"] "kitty") - (genSpawnBinding "s" ["Super" "Shift"] "cosmic-screenshot") - (genSpawnBinding null ["Super"] "cosmic-launcher") - (genSpawnBinding "d" ["Super"] "cosmic-app-library") - (genSpawnBinding "XF86AudioRaiseVolume" [] "amixer sset Master 5%+") - (genSpawnBinding "XF86AudioLowerVolume" [] "amixer sset Master 5%-") - (genSpawnBinding "XF86AudioMute" [] "amixer sset Master toggle") - (genSpawnBinding "XF86AudioNext" [] "playerctl next") - (genSpawnBinding "XF86AudioPrev" [] "playerctl previous") - (genSpawnBinding "XF86AudioPlay" [] "playerctl play-pause") - (genSpawnBinding "XF86AudioStop" [] "playerctl stop") - ( - genSpawnBinding "XF86MonBrightnessUp" [] - "busctl --user call com.system76.CosmicSettingsDaemon /com/system76/CosmicSettingsDaemon com.system76.CosmicSettingsDaemon IncreaseDisplayBrightness" - ) - ( - genSpawnBinding "XF86MonBrightnessDown" [] - "busctl --user call com.system76.CosmicSettingsDaemon /com/system76/CosmicSettingsDaemon com.system76.CosmicSettingsDaemon DecreaseDisplayBrightness" - ) - (genSpawnBinding "e" ["Super" "Shift"] "wlogout") - ]; }; environment.cosmic.excludePackages = with pkgs; [ cosmic-store diff --git a/system/modules/network.nix b/system/modules/network.nix index a56a39b..b8a49bc 100644 --- a/system/modules/network.nix +++ b/system/modules/network.nix @@ -6,7 +6,8 @@ fallbackDns = ["103.1.206.179" "168.138.8.38" "168.138.12.137"]; dnssec = "false"; }; - services.mullvad-vpn.enable = true; + # services.mullvad-vpn.enable = true; + # systemd.services.mullvad-daemon.environment.TALPID_NET_CLS_MOUNT_DIR = "/opt/net-cls-v1"; networking.firewall = { checkReversePath = "loose"; }; diff --git a/system/modules/niri.nix b/system/modules/niri.nix new file mode 100644 index 0000000..beedda4 --- /dev/null +++ b/system/modules/niri.nix @@ -0,0 +1,6 @@ +{pkgs, ...}: { + programs.niri = { + enable = true; + package = pkgs.niri-unstable; + }; +} diff --git a/system/modules/stable-diffusion.nix b/system/modules/stable-diffusion.nix new file mode 100644 index 0000000..999a5de --- /dev/null +++ b/system/modules/stable-diffusion.nix @@ -0,0 +1,21 @@ +{ + lib, + pkgs, + stable-diffusion, + automatic-webui, + ... +}: { + systemd.services.stable-diffusion = { + description = "stable diffusion + AUTOMATIC1111 ui"; + serviceConfig = { + }; + }; + # virtualisation.oci-containers.containers = { + # # this is only the backend + # stable-diffusion = { + # image = "holaflenain/stable-diffusion:latest"; + # ports = ["7860:7860"]; + # autoStart = false; + # }; + # }; +} diff --git a/system/quiver.nix b/system/quiver.nix index c9b5d05..33baefc 100644 --- a/system/quiver.nix +++ b/system/quiver.nix @@ -8,9 +8,16 @@ ./configuration.nix ]; + system.stateVersion = "22.11"; # Did you read the comment? + + networking.hostName = "quiver"; + users.users.bolt = { isNormalUser = true; - extraGroups = ["wheel" "podman" "docker"]; # Enable ‘sudo’ for the user. + extraGroups = [ + "wheel" + "podman" + ]; }; boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "usbcore" "sd_mod"]; @@ -29,7 +36,6 @@ "vt.global_cursor_default=0" ]; - # boot.kernelPackages = pkgs.linuxPackages_cachyos; boot.kernelPackages = pkgs.linuxPackages_xanmod_latest; boot.supportedFilesystems = { ntfs = true; @@ -39,42 +45,41 @@ boot.loader.systemd-boot.configurationLimit = 5; boot.loader.efi.canTouchEfiVariables = true; - # boot.plymouth = with pkgs.rice.plymouth; { - # inherit font theme themePackages; - # enable = true; - # }; - security.tpm2.enable = true; networking.useDHCP = lib.mkDefault true; hardware.cpu.amd.updateMicrocode = true; + environment.pathsToLink = ["/share/xdg-desktop-portal" "/share/applications"]; environment.sessionVariables = { NIXOS_OZONE_WL = "1"; ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE = "fg=5"; - GBM_BACKEND = "nvidia-drm"; - NVD_BACKEND = "direct"; - __GLX_VENDOR_LIBRARY_NAME = "nvidia"; - LIBVA_DRIVER_NAME = "nvidia"; - __GL_GSYNC_ALLOWED = "1"; - __GL_VRR_ALLOWED = "1"; + # GBM_BACKEND = "nvidia-drm"; + # NVD_BACKEND = "direct"; + # __GLX_VENDOR_LIBRARY_NAME = "nvidia"; + # LIBVA_DRIVER_NAME = "nvidia"; + # __GL_GSYNC_ALLOWED = "1"; + # __GL_VRR_ALLOWED = "1"; EDITOR = "hx"; }; + nix.settings.trusted-users = ["bolt"]; + + virtualisation.oci-containers.backend = "podman"; + hardware.nvidia-container-toolkit.enable = true; virtualisation = { podman = { enable = true; - # dockerSocket.enable = true; - # enableNvidia = true; + dockerSocket.enable = true; defaultNetwork.settings.dns_enabled = true; }; - docker = { - enable = true; - enableNvidia = true; - enableOnBoot = true; - extraOptions = "--add-runtime nvidia=/run/current-system/sw/bin/nvidia-container-runtime"; - }; + # docker = { + # enable = true; + # enableOnBoot = true; + # enableNvidia = true; + # extraOptions = "--add-runtime nvidia=/run/current-system/sw/bin/nvidia-container-runtime"; + # }; }; services.minidlna = { @@ -87,12 +92,13 @@ }; services.xserver.videoDrivers = ["nvidia"]; + # TODO figure out why i can't resume properly hardware.nvidia = { - package = config.boot.kernelPackages.nvidiaPackages.stable; + package = config.boot.kernelPackages.nvidiaPackages.latest; modesetting.enable = true; - powerManagement.enable = true; + # powerManagement.enable = true; open = false; - nvidiaPersistenced = true; + # nvidiaPersistenced = true; }; programs.xwayland.enable = true; @@ -118,22 +124,22 @@ xclip ]; - networking.hostName = "quiver"; # Define your hostname. + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/b993b463-c131-4ef1-9aba-0e3eadaa2f9a"; + fsType = "btrfs"; + }; - fileSystems."/" = { - device = "/dev/disk/by-uuid/b993b463-c131-4ef1-9aba-0e3eadaa2f9a"; - fsType = "btrfs"; - }; + "/boot" = { + device = "/dev/disk/by-uuid/6B75-AF9F"; + fsType = "vfat"; + }; - fileSystems."/boot" = { - device = "/dev/disk/by-uuid/6B75-AF9F"; - fsType = "vfat"; - }; - - fileSystems."/data" = { - device = "/dev/disk/by-uuid/39D4F78C658E8B56"; - fsType = "ntfs"; - options = ["rw" "uid=1000" "gid=100"]; + "/data" = { + device = "/dev/disk/by-uuid/39D4F78C658E8B56"; + fsType = "ntfs"; + options = ["rw" "uid=1000" "gid=100"]; + }; }; swapDevices = [