diff --git a/.gitignore b/.gitignore index b2be92b7..141b99d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +result-* result +repl-result-* diff --git a/faucet/auth/pub/diag.pub b/faucet/auth/pub/diag.pub deleted file mode 100644 index b29d4af3..00000000 --- a/faucet/auth/pub/diag.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICvi83M2V/DHUthDPzoiYgLhc8wlWNBFaBsYOWi115f4 diag diff --git a/faucet/fs/btrfs.nix b/faucet/fs/btrfs.nix deleted file mode 100644 index f8252be3..00000000 --- a/faucet/fs/btrfs.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ pkgs -, lib -, config -, ... }: with lib; let - cfg = config.faucet.fs; -in { - options.faucet.fs.btrfs = { - options = mkOption { - type = with types; listOf str; - default = [ "noatime" "compress=zstd" ]; - description = "btrfs mount options"; - }; - }; - - config = mkIf (cfg.type == "btrfs") { - fileSystems."/nix" = - { inherit (cfg.btrfs) options; - device = "/dev/disk/by-uuid/${cfg.store}"; - fsType = "btrfs"; - }; - }; -} diff --git a/faucet/gui/default.nix b/faucet/gui/default.nix deleted file mode 100644 index 7844d6ed..00000000 --- a/faucet/gui/default.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ pkgs -, lib -, config -, ... }: with lib; let - cfg = config.faucet.gui; -in { - imports = [ - ./plymouth.nix - ./greetd.nix - ]; - - options.faucet.gui = { - enable = mkEnableOption "various setup required for GUI and support software"; - session = mkEnableOption "software required for a graphical session" // { default = true; }; - type = mkOption { - type = with types; enum [ "intel" "amdgpu" "nvidia" "prime" ]; - description = "type of graphics acceleration used"; - }; - prime = { - integrated = mkOption { - type = with types; str; - default = "i915"; - description = "integrated gpu driver"; - }; - }; - }; - - config = mkIf cfg.enable { - hardware.opengl = { - enable = true; - driSupport = true; - driSupport32Bit = true; - }; - - services.xserver.videoDrivers = - optional ((cfg.type == "nvidia") || (cfg.type == "prime")) "nvidia" ++ - optional (cfg.type == "amdgpu") "amdgpu"; - # inhibits default display manager - services.xserver.displayManager.startx.enable = mkDefault true; - - hardware.nvidia = mkIf ((cfg.type == "nvidia") || (cfg.type == "prime")) { - modesetting.enable = true; - nvidiaSettings = true; - - prime = mkIf (cfg.type == "prime") { - offload = { - enable = true; - enableOffloadCmd = true; - }; - }; - - powerManagement.enable = false; - powerManagement.finegrained = false; - open = true; - }; - - boot.initrd.kernelModules = - optional (cfg.type == "amdgpu") "amdgpu" ++ - optional (cfg.type == "prime") cfg.prime.integrated; - }; -} diff --git a/faucet/gui/greetd.nix b/faucet/gui/greetd.nix deleted file mode 100644 index ef6143ef..00000000 --- a/faucet/gui/greetd.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ pkgs -, lib -, config -, ... }: with lib; let - cfg = config.faucet.gui; -in mkIf (cfg.enable && cfg.session) { - programs.regreet = { - enable = true; - cageArgs = [ "-s" "-d" "-m" "last" ]; - settings = { - background.path = ../../share/54345906_p0.jpg; - background.fit = "Fill"; - GTK = { - application_prefer_dark_theme = true; - cursor_theme_name = "Bibata-Modern-Classic"; - icon_theme_name = "WhiteSur-dark"; - theme_name = "WhiteSur-Dark"; - }; - }; - }; - - environment.persistence."/nix/persist/fhs".directories = [ "/var/cache/regreet" ]; -} diff --git a/faucet/util/default.nix b/faucet/util/default.nix deleted file mode 100644 index 6f3b29f5..00000000 --- a/faucet/util/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs -, lib -, config -, ... }: with lib; let - cfg = config.faucet.util; -in { - options.faucet.util = { }; - - config = { - programs.zsh.enable = true; - environment.shells = singleton pkgs.zsh; - }; -} diff --git a/flake.lock b/flake.lock index 7a3c7d69..a38f2578 100644 --- a/flake.lock +++ b/flake.lock @@ -1,30 +1,33 @@ { "nodes": { + "catppuccin": { + "locked": { + "lastModified": 1734057772, + "narHash": "sha256-waF/2Y39JXJ4kG3zawmw1J1GxPHopyoOkJKJhfJ7RBs=", + "owner": "catppuccin", + "repo": "nix", + "rev": "20b6328df20ae45752c81311d225fd47cba32483", + "type": "github" + }, + "original": { + "owner": "catppuccin", + "repo": "nix", + "type": "github" + } + }, "crane": { "inputs": { - "flake-compat": [ - "lanzaboote", - "flake-compat" - ], - "flake-utils": [ - "lanzaboote", - "flake-utils" - ], "nixpkgs": [ "lanzaboote", "nixpkgs" - ], - "rust-overlay": [ - "lanzaboote", - "rust-overlay" ] }, "locked": { - "lastModified": 1681177078, - "narHash": "sha256-ZNIjBDou2GOabcpctiQykEQVkI8BDwk7TyvlWlI4myE=", + "lastModified": 1717535930, + "narHash": "sha256-1hZ/txnbd/RmiBPNUs7i8UQw2N89uAK3UzrGAWdnFfU=", "owner": "ipetkov", "repo": "crane", - "rev": "0c9f468ff00576577d83f5019a66c557ede5acf6", + "rev": "55e7754ec31dac78980c8be45f8a28e80e370946", "type": "github" }, "original": { @@ -36,11 +39,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -56,11 +59,11 @@ ] }, "locked": { - "lastModified": 1704152458, - "narHash": "sha256-DS+dGw7SKygIWf9w4eNBUZsK+4Ug27NwEWmn2tnbycg=", + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "88a2cd8166694ba0b6cb374700799cec53aef527", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", "type": "github" }, "original": { @@ -77,11 +80,11 @@ ] }, "locked": { - "lastModified": 1680392223, - "narHash": "sha256-n3g7QFr85lDODKt250rkZj2IFS3i4/8HBU2yKHO3tqw=", + "lastModified": 1717285511, + "narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "dcc36e45d054d7bb554c9cdab69093debd91a0b5", + "rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8", "type": "github" }, "original": { @@ -95,11 +98,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -113,11 +116,11 @@ "systems": "systems_2" }, "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -135,11 +138,11 @@ ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -155,11 +158,11 @@ ] }, "locked": { - "lastModified": 1704383912, - "narHash": "sha256-Be7O73qoOj/z+4ZCgizdLlu+5BkVvO2KO299goZ9cW8=", + "lastModified": 1734093295, + "narHash": "sha256-hSwgGpcZtdDsk1dnzA0xj5cNaHgN9A99hRF/mxMtwS4=", "owner": "nix-community", "repo": "home-manager", - "rev": "26b8adb300e50efceb51fff6859a1a6ba1ade4f7", + "rev": "66c5d8b62818ec4c1edb3e941f55ef78df8141a8", "type": "github" }, "original": { @@ -170,11 +173,11 @@ }, "impermanence": { "locked": { - "lastModified": 1703656108, - "narHash": "sha256-hCSUqdFJKHHbER8Cenf5JRzjMlBjIdwdftGQsO0xoJs=", + "lastModified": 1731242966, + "narHash": "sha256-B3C3JLbGw0FtLSWCjBxU961gLNv+BOOBC6WvstKLYMw=", "owner": "nix-community", "repo": "impermanence", - "rev": "033643a45a4a920660ef91caa391fbffb14da466", + "rev": "3ed3f0eaae9fcc0a8331e77e9319c8a4abd8a71a", "type": "github" }, "original": { @@ -184,6 +187,25 @@ "type": "github" } }, + "jovian": { + "inputs": { + "nix-github-actions": "nix-github-actions", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1734162608, + "narHash": "sha256-m2AX+3eiVqIK6uO7GbGY7SFnkkYOlR5fQiNI0eRvWOQ=", + "owner": "Jovian-Experiments", + "repo": "Jovian-NixOS", + "rev": "31bdf4c7c91204d65afbde01146deee0259a8fb7", + "type": "github" + }, + "original": { + "owner": "Jovian-Experiments", + "repo": "Jovian-NixOS", + "type": "github" + } + }, "lanzaboote": { "inputs": { "crane": "crane", @@ -197,27 +219,49 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1682802423, - "narHash": "sha256-Fb5TeRTdvUlo/5Yi2d+FC8a6KoRLk2h1VE0/peMhWPs=", + "lastModified": 1718178907, + "narHash": "sha256-eSZyrQ9uoPB9iPQ8Y5H7gAmAgAvCw3InStmU3oEjqsE=", "owner": "nix-community", "repo": "lanzaboote", - "rev": "64b903ca87d18cef2752c19c098af275c6e51d63", + "rev": "b627ccd97d0159214cee5c7db1412b75e4be6086", "type": "github" }, "original": { "owner": "nix-community", - "ref": "v0.3.0", + "ref": "v0.4.1", "repo": "lanzaboote", "type": "github" } }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "jovian", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1729697500, + "narHash": "sha256-VFTWrbzDlZyFHHb1AlKRiD/qqCJIripXKiCSFS8fAOY=", + "owner": "zhaofengli", + "repo": "nix-github-actions", + "rev": "e418aeb728b6aa5ca8c5c71974e7159c2df1d8cf", + "type": "github" + }, + "original": { + "owner": "zhaofengli", + "ref": "matrix-name", + "repo": "nix-github-actions", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1704194953, - "narHash": "sha256-RtDKd8Mynhe5CFnVT8s0/0yqtWFMM9LmCzXv/YKxnq4=", + "lastModified": 1733392399, + "narHash": "sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG+cBns=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "bd645e8668ec6612439a9ee7e71f7eac4099d4f6", + "rev": "d0797a04b81caeae77bcff10a9dde78bc17f5661", "type": "github" }, "original": { @@ -229,30 +273,65 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1678872516, - "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-22.11", + "ref": "nixos-23.11", "repo": "nixpkgs", "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1733940404, + "narHash": "sha256-Pj39hSoUA86ZePPF/UXiYHHM7hMIkios8TYG29kQT4g=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5d67ea6b4b63378b9c13be21e2ec9d1afc921713", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "plasma-manager": { + "inputs": { + "home-manager": [ + "home-manager" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733858086, + "narHash": "sha256-h2BDIDKiqgMpA6E+mu0RgMGy3FeM6k+EuJ9xgOQ1+zw=", + "owner": "pjones", + "repo": "plasma-manager", + "rev": "7e2010249529931a3848054d5ff0dbf24675ab68", + "type": "github" + }, + "original": { + "owner": "pjones", + "repo": "plasma-manager", + "type": "github" + } + }, "pre-commit-hooks-nix": { "inputs": { "flake-compat": [ "lanzaboote", "flake-compat" ], - "flake-utils": [ - "lanzaboote", - "flake-utils" - ], "gitignore": "gitignore", "nixpkgs": [ "lanzaboote", @@ -261,11 +340,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1681413034, - "narHash": "sha256-/t7OjNQcNkeWeSq/CFLYVBfm+IEnkjoSm9iKvArnUUI=", + "lastModified": 1717664902, + "narHash": "sha256-7XfBuLULizXjXfBYy/VV+SpYMHreNRHk9nKMsm1bgb4=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "d3de8f69ca88fb6f8b09e5b598be5ac98d28ede5", + "rev": "cc4d466cb1254af050ff7bdf47f6d404a7c646d1", "type": "github" }, "original": { @@ -276,12 +355,15 @@ }, "root": { "inputs": { + "catppuccin": "catppuccin", "flake-parts": "flake-parts", "flake-utils": "flake-utils", "home-manager": "home-manager", "impermanence": "impermanence", + "jovian": "jovian", "lanzaboote": "lanzaboote", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs_2", + "plasma-manager": "plasma-manager" } }, "rust-overlay": { @@ -296,11 +378,11 @@ ] }, "locked": { - "lastModified": 1682129965, - "narHash": "sha256-1KRPIorEL6pLpJR04FwAqqnt4Tzcm4MqD84yhlD+XSk=", + "lastModified": 1717813066, + "narHash": "sha256-wqbRwq3i7g5EHIui0bIi84mdqZ/It1AXBSLJ5tafD28=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "2c417c0460b788328220120c698630947547ee83", + "rev": "6dc3e45fe4aee36efeed24d64fc68b1f989d5465", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 37bfbfbf..d50b4480 100644 --- a/flake.nix +++ b/flake.nix @@ -3,24 +3,21 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - flake-parts = { - url = "github:hercules-ci/flake-parts"; - inputs.nixpkgs-lib.follows = "nixpkgs"; - }; - + flake-parts.url = "github:hercules-ci/flake-parts"; + flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; impermanence.url = "github:nix-community/impermanence/master"; + home-manager.url = "github:nix-community/home-manager"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; + plasma-manager.url = "github:pjones/plasma-manager"; + plasma-manager.inputs.nixpkgs.follows = "nixpkgs"; + plasma-manager.inputs.home-manager.follows = "home-manager"; + catppuccin.url = "github:catppuccin/nix"; + lanzaboote.url = "github:nix-community/lanzaboote/v0.4.1"; + lanzaboote.inputs.nixpkgs.follows = "nixpkgs"; - home-manager = { - url = "github:nix-community/home-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - - lanzaboote = { - url = "github:nix-community/lanzaboote/v0.3.0"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + # steamdeck + jovian.url = "github:Jovian-Experiments/Jovian-NixOS"; }; outputs = inputs: diff --git a/global/acme/default.nix b/global/acme/default.nix new file mode 100644 index 00000000..dacf367e --- /dev/null +++ b/global/acme/default.nix @@ -0,0 +1,20 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.acme; +in { + options.global.acme = { + enable = mkEnableOption "ACME SSL certificates"; + }; + + config = mkIf cfg.enable { + security.acme = { + acceptTerms = true; + defaults.email = mkDefault "koishi@514fpv.one"; + defaults.group = config.services.nginx.group; + }; + + environment.persistence."/nix/persist/fhs".directories = [ "/var/lib/acme" ]; + }; +} diff --git a/global/android/default.nix b/global/android/default.nix new file mode 100644 index 00000000..a7f94c17 --- /dev/null +++ b/global/android/default.nix @@ -0,0 +1,17 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.android; +in { + options.global.android = { + enable = mkEnableOption "android tools"; + }; + + config = mkIf cfg.enable { + programs.adb.enable = true; + + # allow device access by admin users + users.adminGroups = [ "adbusers" ]; + }; +} diff --git a/global/asusd/default.nix b/global/asusd/default.nix new file mode 100644 index 00000000..00b4af2e --- /dev/null +++ b/global/asusd/default.nix @@ -0,0 +1,18 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.asusd; +in { + options.global.asusd = { + enable = mkEnableOption "ASUS laptop userland support daemon"; + }; + + config = mkIf cfg.enable { + services.asusd.enable = true; + + environment.persistence."/nix/persist/fhs".directories = [ + "/etc/asusd" + ]; + }; +} diff --git a/faucet/auth/default.nix b/global/auth/default.nix similarity index 80% rename from faucet/auth/default.nix rename to global/auth/default.nix index 1c433054..1c0572a6 100644 --- a/faucet/auth/default.nix +++ b/global/auth/default.nix @@ -2,20 +2,22 @@ , lib , config , ... }: with lib; let - cfg = config.faucet.auth; + cfg = config.global.auth; pub = lib.pipe ./pub [ builtins.readDir (lib.filterAttrs (n: ty: ty == "regular")) (lib.mapAttrsToList (n: _: builtins.readFile ./pub/${n})) + (foldr (payload: keys: (splitString "\n" payload) ++ keys) [ ]) + (foldr (candidate: keys: keys ++ (if candidate == "" then [ ] else [ candidate ])) [ ]) ]; in { - options.faucet.auth = { + options.global.auth = { enable = mkEnableOption "identity authentication in various software" // { default = true; }; openssh = { enable = mkEnableOption "openssh server"; password = mkEnableOption "password authentication"; publicKeys = mkOption { - type = with types; listOf str; + type = with types; listOf singleLineStr; default = pub; description = "list of trusted openssh keys"; }; @@ -40,7 +42,7 @@ in { settings.PasswordAuthentication = cfg.openssh.password; }; - networking.firewall.allowedTCPPorts = [ ] ++ + networking.firewall.allowedTCPPorts = [ 1300 ] ++ # utility port optional (cfg.openssh.enable && (cfg.openssh.port != null)) cfg.openssh.port; environment.persistence."/nix/persist/fhs".directories = [ ] ++ diff --git a/faucet/auth/pub/chireiden.pub b/global/auth/pub/chireiden.pub similarity index 100% rename from faucet/auth/pub/chireiden.pub rename to global/auth/pub/chireiden.pub diff --git a/global/auth/pub/eientei.pub b/global/auth/pub/eientei.pub new file mode 100644 index 00000000..cfe7e155 --- /dev/null +++ b/global/auth/pub/eientei.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJnhMCGSLMY+QldeCTaRovmfuzKdJsllQy9XinN2JU2z koishi@eientei diff --git a/global/auth/pub/hakugyokurou.pub b/global/auth/pub/hakugyokurou.pub new file mode 100644 index 00000000..4fa391d2 --- /dev/null +++ b/global/auth/pub/hakugyokurou.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHKCA0/6dsdVyLEgzWt8+u5lWVc0o6A3MY4M2Hf2BT8h koishi@hakugyokurou diff --git a/global/auth/pub/koumakyou.pub b/global/auth/pub/koumakyou.pub new file mode 100644 index 00000000..305fe6a9 --- /dev/null +++ b/global/auth/pub/koumakyou.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINJOoXrfB4D8Vi6HH4E7RqHHIWhPPqEiiOeLRfggW1XZ koishi@koumakyou diff --git a/global/auth/pub/reimaden.pub b/global/auth/pub/reimaden.pub new file mode 100644 index 00000000..0c845b69 --- /dev/null +++ b/global/auth/pub/reimaden.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGIZq1mD3J1cgWK61okXx3hQSe+5g3UTBfAf4RHkkFVd koishi@reimaden diff --git a/global/auth/pub/shinkirou.pub b/global/auth/pub/shinkirou.pub new file mode 100644 index 00000000..8da18c48 --- /dev/null +++ b/global/auth/pub/shinkirou.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDwV7Z+PDC8ARRj1LxUJlv59gJ3A84LCMMyMSqLtRtuQ koishi@shinkirou diff --git a/global/auth/pub/yume.pub b/global/auth/pub/yume.pub new file mode 100644 index 00000000..444ab0db --- /dev/null +++ b/global/auth/pub/yume.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPdzq2g13LEyxTZnA0HQ5hMEp4XNh0TOB/KY1bRwjsaq koishi@yume diff --git a/faucet/boot/default.nix b/global/boot/default.nix similarity index 73% rename from faucet/boot/default.nix rename to global/boot/default.nix index 7c197629..fe27a997 100644 --- a/faucet/boot/default.nix +++ b/global/boot/default.nix @@ -2,12 +2,17 @@ , lib , config , ... }: with lib; let - cfg = config.faucet.boot; + cfg = config.global.boot; in { - options.faucet.boot = { + options.global.boot = { enable = mkEnableOption "bootloader installation and maintenance" // { default = true; }; systemd-boot = mkEnableOption "generation selection via systemd-boot" // { default = !cfg.lanzaboote; }; lanzaboote = mkEnableOption "secure boot maintenance via lanzaboote"; + memtest = mkOption { + type = with types; nullOr int; + default = null; + description = "memtest passes to perform on boot"; + }; }; config = let @@ -20,6 +25,7 @@ in { loader.systemd-boot.enable = cfg.systemd-boot; loader.efi.canTouchEfiVariables = true; tmp.cleanOnBoot = true; + kernelParams = optional (cfg.memtest != null) "memtest=${toString cfg.memtest}"; }; # symlink for sbctl diff --git a/faucet/default.nix b/global/default.nix similarity index 100% rename from faucet/default.nix rename to global/default.nix diff --git a/global/flatpak/default.nix b/global/flatpak/default.nix new file mode 100644 index 00000000..a3b77f76 --- /dev/null +++ b/global/flatpak/default.nix @@ -0,0 +1,17 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.flatpak; +in { + options.global.flatpak = { + enable = mkEnableOption "flatpak sandbox"; + }; + + config = mkIf cfg.enable { + services.flatpak.enable = true; + xdg.portal.enable = true; + users.home.persist.directories = [ ".local/share/flatpak" ".var" ]; + environment.persistence."/nix/persist/fhs".directories = [ "/var/lib/flatpak" ]; + }; +} diff --git a/global/fs/bcachefs.nix b/global/fs/bcachefs.nix new file mode 100644 index 00000000..a779d7bd --- /dev/null +++ b/global/fs/bcachefs.nix @@ -0,0 +1,22 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.fs; +in { + options.global.fs.bcachefs = { + options = mkOption { + type = with types; listOf str; + default = [ "noatime" "compression=zstd" ]; + description = "bcachefs mount options"; + }; + }; + + config = mkIf (cfg.type == "bcachefs") { + fileSystems."/nix" = + { inherit (cfg.bcachefs) options; + device = "/dev/disk/by-uuid/${cfg.store}"; + fsType = "bcachefs"; + }; + }; +} diff --git a/faucet/fs/default.nix b/global/fs/default.nix similarity index 83% rename from faucet/fs/default.nix rename to global/fs/default.nix index 084e6177..0871b015 100644 --- a/faucet/fs/default.nix +++ b/global/fs/default.nix @@ -2,23 +2,25 @@ , lib , config , ... }: with lib; let - cfg = config.faucet.fs; + cfg = config.global.fs; in { imports = [ ./ext4.nix + ./f2fs.nix ./xfs.nix - #./bcachefs.nix - ./btrfs.nix + ./bcachefs.nix + ./zfs ]; - options.faucet.fs = { + options.global.fs = { type = mkOption { - type = with types; enum [ "ext4" "xfs" "bcachefs" "btrfs" ]; + type = with types; enum [ "ext4" "f2fs" "xfs" "zfs" "bcachefs" ]; default = "bcachefs"; description = "filesystem type to use for persistent state storage"; }; store = mkOption { type = with types; str; + default = config.networking.hostName; description = "UUID/dataset of nix store backing device"; }; esp = { @@ -29,7 +31,7 @@ in { description = "vfat serial number of EFI system partition"; }; }; - extPersist = { + external = { enable = mkEnableOption "external persist filesystem"; # this wraps the standard fileSystems module # since some attrs have to be unconditionally set @@ -70,8 +72,8 @@ in { { device = "/dev/disk/by-uuid/${cfg.esp.uuid}"; fsType = "vfat"; }; - fileSystems."/nix/persist" = mkIf cfg.extPersist.enable - { inherit (cfg.extPersist) device fsType options; + fileSystems."/nix/persist" = mkIf cfg.external.enable + { inherit (cfg.external) device fsType options; neededForBoot = true; depends = [ "/nix" ]; }; @@ -88,5 +90,10 @@ in { inherit (cfg.cryptsetup) allowDiscards bypassWorkqueues; device = "/dev/disk/by-uuid/${uuid}"; }) cfg.cryptsetup.uuids); + + environment.persistence."/nix/persist/fhs".files = [ { + file = "/var/lib/private/mode"; + parentDirectory.mode = "0700"; + } ]; }; } diff --git a/faucet/fs/ext4.nix b/global/fs/ext4.nix similarity index 75% rename from faucet/fs/ext4.nix rename to global/fs/ext4.nix index aeaafb18..109c29cc 100644 --- a/faucet/fs/ext4.nix +++ b/global/fs/ext4.nix @@ -1,10 +1,11 @@ { lib , config , ... }: with lib; let - cfg = config.faucet.fs; + cfg = config.global.fs; in mkIf (cfg.type == "ext4") { fileSystems."/nix" = { device = "/dev/disk/by-uuid/${cfg.store}"; fsType = "ext4"; + options = [ "noatime" ]; }; } diff --git a/global/fs/f2fs.nix b/global/fs/f2fs.nix new file mode 100644 index 00000000..f9043a39 --- /dev/null +++ b/global/fs/f2fs.nix @@ -0,0 +1,10 @@ +{ lib +, config +, ... }: with lib; let + cfg = config.global.fs; +in mkIf (cfg.type == "f2fs") { + fileSystems."/nix" = + { device = "/dev/disk/by-uuid/${cfg.store}"; + fsType = "f2fs"; + }; +} diff --git a/faucet/fs/xfs.nix b/global/fs/xfs.nix similarity index 77% rename from faucet/fs/xfs.nix rename to global/fs/xfs.nix index 03025993..42c8c7ea 100644 --- a/faucet/fs/xfs.nix +++ b/global/fs/xfs.nix @@ -1,11 +1,12 @@ { lib , config , ... }: with lib; let - cfg = config.faucet.fs; + cfg = config.global.fs; in mkIf (cfg.type == "xfs") { # NOTE: -m reflink=1 fileSystems."/nix" = { device = "/dev/disk/by-uuid/${cfg.store}"; fsType = "xfs"; + options = [ "noatime" ]; }; } diff --git a/global/fs/zfs/alert.nix b/global/fs/zfs/alert.nix new file mode 100644 index 00000000..9af1317d --- /dev/null +++ b/global/fs/zfs/alert.nix @@ -0,0 +1,122 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.fs.zfs.alert; + + backend = { + text = pkgs.writeShellScript "telegram-text" '' + set -e + source ${cfg.secret} + + ${pkgs.curl}/bin/curl -sG \ + --data-urlencode "chat_id=$CHATID" \ + --data-urlencode "text=$ALERT" \ + $CURL_EXTRA_ARGS \ + "https://api.telegram.org/bot$APIKEY/sendMessage" + ''; + image = pkgs.writeShellScript "telegram-image" '' + set -e + source ${cfg.secret} + + ${pkgs.curl}/bin/curl -sG \ + -F "chat_id=$CHATID" \ + -F "caption=$ALERT" \ + -F "photo=@-" \ + $CURL_EXTRA_ARGS \ + "https://api.telegram.org/bot$APIKEY/sendPhoto" + ''; + }; + + zedAlert = pkgs.writeShellScript "zed-alert" '' + set -e + export BODY="$(cat)" + + # add tag + ALERT="$1 #zfs" + + export ALERT + echo -e "$BODY" | \ + ${pkgs.imagemagick}/bin/convert \ + -size 1500x2000 xc:black \ + -font "${pkgs.freefont_ttf}/share/fonts/truetype/FreeMono.ttf" \ + -pointsize 16 \ + -fill white -annotate +15+80 "@-" \ + -trim -bordercolor "#000" \ + -border 32 +repage \ + png:- | \ + ${backend.image} + ''; + + mdadmAlert = pkgs.writeShellScript "mdadm-alert" '' + set -e + + EVENT="$1" + ARRAY="$2" + DEVICE="$3" + + # fallback alert + ALERT="$EVENT | $ARRAY | $DEVICE" + + case $EVENT in + DegradedArray) + ALERT="Array $ARRAY is in a degraded state" + ;; + DeviceDisappeared) + ALERT="Array $ARRAY disappeared" + ;; + Fail) + ALERT="Array $ARRAY encountered failure of component $DEVICE" + ;; + FailSpare) + ALERT="Array $ARRAY encountered failure of spare component $DEVICE during rebuild" + ;; + MoveSpare) + ALERT="Spare $DEVICE moved to array $ARRAY" + ;; + NewArray) + ALERT="Array $ARRAY appeared" + ;; + Rebuild??) + ALERT="Array $ARRAY rebuild is now $(echo $EVENT | ${pkgs.sedutil}/bin/sed 's/Rebuild//')% complete" + ;; + RebuildFinished) + ALERT="Rebuild of array $ARRAY has concluded" + ;; + RebuildStarted) + ALERT="Rebuild of array $ARRAY has started" + ;; + SpareActive) + ALERT="Spare $DEVICE activated in array $ARRAY" + ;; + SparesMissing) + ALERT="Array $ARRAY missing one or more spares" + ;; + TestMessage) + ALERT="Test message generated for array $ARRAY" + ;; + esac + + # add tag + ALERT="$ALERT #swraid" + + export ALERT + exec ${backend.text} + ''; +in mkIf (cfg.secret != null) { + services.zfs.zed = mkIf cfg.zed { + settings = { + ZED_EMAIL_ADDR = [ "root" ]; + ZED_EMAIL_PROG = toString zedAlert; + ZED_EMAIL_OPTS = "'@SUBJECT@'"; + + ZED_NOTIFY_INTERVAL_SECS = 3600; + ZED_NOTIFY_VERBOSE = false; + + ZED_USE_ENCLOSURE_LEDS = true; + ZED_SCRUB_AFTER_RESILVER = false; + }; + }; + + global.fs.zfs.split.mdProg = mkIf cfg.swraid (toString mdadmAlert); +} diff --git a/global/fs/zfs/default.nix b/global/fs/zfs/default.nix new file mode 100644 index 00000000..61976560 --- /dev/null +++ b/global/fs/zfs/default.nix @@ -0,0 +1,105 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.fs; +in { + imports = [ + ./alert.nix + ./split.nix + ./replication.nix + ]; + + # -o ashift=12 + # -O encryption=on -O keyformat=passphrase -O keylocation=prompt + # -O compression=on -O mountpoint=none -O xattr=sa -O acltype=posixacl + options.global.fs.zfs = { + alert = { + zed = mkEnableOption "zfs event alerts" // { default = true; }; + swraid = mkEnableOption "software raid alerts" // { default = true; }; + secret = mkOption { + type = with types; nullOr str; + default = null; + description = "path to alert secrets"; + }; + }; + + persist = mkOption { + type = with types; str; + default = cfg.store; + description = '' + pool for persist dataset + defaults to nix store dataset + ''; + }; + mountpoints = mkOption { + type = with types; attrsOf str; + description = "zfs dataset mountpoints"; + }; + externalStore = mkEnableOption "external nix store filesystem"; + + split = { + enable = mkEnableOption "zfs state with split nix store"; + mdProg = mkOption { + type = with types; str; + default = "/usr/bin/true"; + description = "mdadm PROGRAM config value"; + }; + secret = mkOption { + type = with types; str; + description = "UUID of secret filesystem"; + }; + store = mkOption { + type = with types; str; + description = "UUID of store filesystem"; + }; + }; + + replication = { + enable = mkEnableOption "zfs replication to remote"; + remote = mkOption { + type = with types; str; + description = "remote host as replication destination"; + }; + port = mkOption { + type = with types; port; + description = "ssh port of replication target"; + default = 22; + }; + datasets = mkOption { + type = with types; listOf str; + default = [ "persist" "service" "storage" ]; + description = "list of filesystems to perform replication for"; + }; + sendOptions = mkOption { + type = with types; str; + default = "w"; + description = "send options for all datasets"; + }; + }; + }; + + config = mkIf (cfg.type == "zfs") { + fileSystems = (mapAttrs (path: dataset: { + device = "${cfg.zfs.persist}/${dataset}"; + fsType = "zfs"; + options = [ "zfsutil" ]; + # required by impermanence + neededForBoot = true; + }) cfg.zfs.mountpoints) // { + "/nix" = (if !cfg.zfs.externalStore then + { device = "${cfg.store}/nix"; + fsType = "zfs"; + } else + { inherit (cfg.external) device fsType options; }); + }; + global.fs.zfs.mountpoints."/nix/persist" = "persist"; + + services.zfs.trim.enable = true; + services.zfs.autoSnapshot.enable = true; + services.zfs.autoScrub.enable = true; + boot.zfs.devNodes = mkDefault "/dev/disk/by-partuuid"; + #boot.kernelPackages = mkDefault config.boot.zfs.package.latestCompatibleLinuxPackages; + global.kernel.lts = mkDefault true; + }; +} diff --git a/global/fs/zfs/replication.nix b/global/fs/zfs/replication.nix new file mode 100644 index 00000000..1650c118 --- /dev/null +++ b/global/fs/zfs/replication.nix @@ -0,0 +1,30 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.fs.zfs.replication; +in mkIf cfg.enable { + services.syncoid = { + enable = mkDefault true; + interval = mkDefault "daily"; + sshKey = mkDefault "/var/lib/syncoid/.ssh/id_ed25519"; + commonArgs = [ + "--recursive" + "--mbuffer-size=128M" + "--delete-target-snapshots" + "--sshport=${toString cfg.port}" + ]; + localSourceAllow = mkOptionDefault [ "mount" ]; + + commands = (lists.foldr (name: commands: commands // { + "${config.global.fs.store}/${name}" = { + inherit (cfg) sendOptions; + target = "${cfg.remote}/${name}"; + }; + }) { }) cfg.datasets; + }; + + users.users.syncoid.uid = 82; + users.groups.syncoid.gid = 82; + environment.persistence."/nix/persist/fhs".directories = [ "/var/lib/syncoid" ]; +} diff --git a/global/fs/zfs/split.nix b/global/fs/zfs/split.nix new file mode 100644 index 00000000..22f9231f --- /dev/null +++ b/global/fs/zfs/split.nix @@ -0,0 +1,35 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.fs.zfs.split; +in mkIf cfg.enable { + # unconditionally enable fstrim for xfs and ext4 + services.fstrim.enable = mkDefault true; + + # enable swraid for split raid1 system array + boot.swraid.enable = mkDefault true; + boot.swraid.mdadmConf = mkDefault '' + PROGRAM ${cfg.mdProg} + ''; + + # secret filesystem backed by swraid + fileSystems."/nix/var/secret" = + { device = "/dev/disk/by-uuid/${cfg.secret}"; + fsType = "ext4"; + options = [ "noatime" ]; + neededForBoot = true; + depends = [ "/nix/var" ]; + }; + + # external store backed by swraid + global.fs = { + zfs.externalStore = mkDefault true; + external.device = "/dev/disk/by-uuid/${cfg.store}"; + external.fsType = "xfs"; + external.options = [ "noatime" ]; + }; + + # import system state pool after encrypted filesystems become available for key loading + boot.initrd.systemd.services."zfs-import-${config.global.fs.store}".after = [ "sysroot-nix-var-secret.mount" "cryptsetup.target" ]; +} diff --git a/global/gpu/default.nix b/global/gpu/default.nix new file mode 100644 index 00000000..3b950361 --- /dev/null +++ b/global/gpu/default.nix @@ -0,0 +1,111 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.gpu; + + intel = cfg.type == "intel" || (cfg.type == "prime" && config.hardware.nvidia.prime.intelBusId != ""); + amdgpu = cfg.type == "amdgpu" || (cfg.type == "prime" && config.hardware.nvidia.prime.amdgpuBusId != ""); + nvidia = cfg.type == "nvidia" || cfg.type == "prime"; +in { + imports = [ + ./plymouth.nix + ./greetd.nix + ]; + + options.global.gpu = { + enable = mkEnableOption "various setup required for GUI and support software"; + session = mkEnableOption "software required for a graphical session" // { default = true; }; + type = mkOption { + type = with types; nullOr (enum [ "intel" "amdgpu" "nvidia" "prime" ]); + default = null; + description = "type of graphics acceleration used"; + }; + arc = mkOption { + type = with types; nullOr str; + default = null; + description = "intel arc PCI ID if installed, enables toggling the arc before boot"; + }; + }; + + config = mkIf cfg.enable { + hardware.graphics = { + enable = true; + enable32Bit = true; + + # https://nixos.wiki/wiki/Accelerated_Video_Playback + extraPackages = with pkgs; optionals intel [ + intel-media-driver # LIBVA_DRIVER_NAME=iHD + vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium) + vaapiVdpau + libvdpau-va-gl + intel-compute-runtime + ] ++ + optional nvidia nvidia-vaapi-driver ++ + optional (cfg.type == "nvidia") vulkan-validation-layers; + }; + + services.xserver = mkIf cfg.session { + videoDrivers = + optional nvidia "nvidia" ++ + optional (cfg.type == "amdgpu") "amdgpu"; + # inhibits default display manager + + displayManager.startx.enable = mkDefault true; + }; + + hardware.nvidia = mkIf nvidia { + modesetting.enable = true; + nvidiaSettings = true; + + prime = mkIf (cfg.type == "prime") { + offload = { + enable = true; + enableOffloadCmd = true; + }; + }; + + powerManagement.enable = false; + powerManagement.finegrained = false; + open = false; + }; + + environment.variables = { + # work around broken nvidia hw cursor on wayland + WLR_NO_HARDWARE_CURSORS = mkIf (cfg.type == "nvidia") "1"; + # work around wlroots flickering on pure nvidia + #WLR_RENDERER = mkIf (cfg.type == "nvidia") "vulkan"; + }; + + specialisation.integratedGraphics = mkIf (cfg.type == "prime") { + configuration = { + global.gpu.type = mkForce (if intel then "intel" else if amdgpu then "amdgpu" else "prime"); + boot.blacklistedKernelModules = [ "nouveau" ]; + }; + }; + + specialisation.withArc = mkIf (cfg.arc != null) { + configuration = { + global.gpu.arc = mkForce null; + powerManagement.cpuFreqGovernor = mkForce "performance"; + }; + }; + + boot.initrd.kernelModules = + optional amdgpu "amdgpu" ++ + optional (intel && cfg.arc == null) "i915" ++ + optionals nvidia [ "nvidia" "nvidia_drm" "nvidia_modeset" "nvidia_uvm" ] ++ + optional (cfg.arc != null) "vfio-pci"; + + boot.extraModulePackages = optional nvidia config.boot.kernelPackages.nvidia_x11; + + boot.extraModprobeConfig = mkIf (cfg.arc != null) '' + softdep drm pre: vfio-pci + options vfio-pci ids=${cfg.arc} + ''; + + boot.kernelParams = + optional intel "i915.fastboot=1" ++ + optionals nvidia [ "nvidia_drm.modeset=1" "nvidia_drm.fbdev=1" ]; + }; +} diff --git a/global/gpu/greetd.nix b/global/gpu/greetd.nix new file mode 100644 index 00000000..d31f6c5c --- /dev/null +++ b/global/gpu/greetd.nix @@ -0,0 +1,24 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.gpu; + gui = with cfg; enable && session; +in mkIf gui { + programs.regreet = { + enable = mkDefault true; + cageArgs = [ "-s" "-d" "-m" "last" ]; + settings = { + background.path = mkDefault ../../share/54345906_p0.jpg; + background.fit = "Fill"; + GTK = { + application_prefer_dark_theme = mkDefault true; + cursor_theme_name = mkDefault "Bibata-Modern-Classic"; + icon_theme_name = mkDefault "Papirus-Dark"; + theme_name = mkDefault "WhiteSur-Dark"; + }; + }; + }; + + environment.persistence."/nix/persist/fhs".directories = [ "/var/cache/regreet" ]; +} diff --git a/faucet/gui/plymouth.nix b/global/gpu/plymouth.nix similarity index 75% rename from faucet/gui/plymouth.nix rename to global/gpu/plymouth.nix index 2408af6d..0705fd8a 100644 --- a/faucet/gui/plymouth.nix +++ b/global/gpu/plymouth.nix @@ -2,10 +2,11 @@ , lib , config , ... }: with lib; let - cfg = config.faucet.gui; -in mkIf cfg.enable { + cfg = config.global.gpu; + gui = with cfg; enable && session; +in mkIf gui { boot = { - loader.timeout = lib.mkDefault 0; + loader.timeout = mkDefault 0; consoleLogLevel = 0; initrd.verbose = false; initrd.systemd.enable = true; @@ -13,7 +14,6 @@ in mkIf cfg.enable { kernelParams = [ "quiet" "splash" - "i915.fastboot=1" "loglevel=3" "rd.systemd.show_status=false" "rd.udev.log_level=3" diff --git a/faucet/id/default.nix b/global/id/default.nix similarity index 80% rename from faucet/id/default.nix rename to global/id/default.nix index 075efe43..c225845e 100644 --- a/faucet/id/default.nix +++ b/global/id/default.nix @@ -2,9 +2,9 @@ , lib , config , ... }: with lib; let - cfg = config.faucet.id; + cfg = config.global.id; in { - options.faucet.id = mkOption { + options.global.id = mkOption { type = with types; str; description = "systemd machine id"; }; diff --git a/faucet/io/default.nix b/global/io/default.nix similarity index 60% rename from faucet/io/default.nix rename to global/io/default.nix index 381554db..407a3de2 100644 --- a/faucet/io/default.nix +++ b/global/io/default.nix @@ -2,41 +2,61 @@ , lib , config , ... }: with lib; let - cfg = config.faucet.io; + cfg = config.global.io; + gui = with config.global.gpu; enable && session; in { - options.faucet.io = { - betaflight = mkEnableOption "betaflight udev rules" // { default = true; }; - bluetooth = mkEnableOption "bluetooth daemons and state persistence" // { default = true; }; - audio = mkEnableOption "pulseaudio server configuration" // { default = true; }; + options.global.io = { + betaflight = mkEnableOption "betaflight udev rules" // { default = gui; }; + bluetooth = mkEnableOption "bluetooth daemons and state persistence" // { default = gui; }; + audio = mkEnableOption "pulseaudio server configuration" // { default = gui; }; coredump = mkEnableOption "save coredumps handled by systemd"; }; config = { - services.udev.extraRules = "" + (if cfg.betaflight then '' + services.udev.extraRules = '' + # ignore zvols + KERNEL=="zd*", ENV{UDISKS_IGNORE}="1" + '' + (if cfg.betaflight then '' # DFU (Internal bootloader for STM32 and AT32 MCUs) SUBSYSTEM=="usb", ATTRS{idVendor}=="2e3c", ATTRS{idProduct}=="df11", MODE="0664", GROUP="dialout" SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0664", GROUP="dialout" '' else ""); networking.networkmanager.enable = mkDefault true; + networking.hosts = { + "10.5.14.0" = [ "codec" ]; + "10.5.14.1" = [ "redir" ]; + "10.5.14.2" = [ "compat" ]; + + "192.168.123.1" = [ "netvm" ]; + }; + networking.firewall.logRefusedConnections = true; hardware.bluetooth.enable = mkDefault cfg.bluetooth; - hardware.pulseaudio = mkIf cfg.audio { + # rtkit is optional but recommended + security.rtkit.enable = cfg.audio; + services.pipewire = mkIf cfg.audio { enable = true; - support32Bit = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + jack.enable = true; }; - #nixpkgs.config.pulseaudio = mkIf cfg.audio; security.pam.loginLimits = mkIf (!cfg.coredump) (singleton { domain = "*"; item = "core"; type = "hard"; value = "0"; }); systemd.coredump.extraConfig = mkIf (!cfg.coredump) "Storage=none"; environment.persistence."/nix/persist/fhs".directories = [ "/var/log" + "/var/lib/nixos" "/var/lib/systemd/backlight" ] ++ optional config.networking.networkmanager.enable "/etc/NetworkManager/system-connections" ++ optional cfg.bluetooth "/var/lib/bluetooth" ++ optional cfg.coredump "/var/lib/systemd/coredump"; environment.persistence."/nix/persist/fhs".hideMounts = true; + + users.home.persist.directories = [ ] ++ + optional cfg.audio ".local/state/wireplumber"; }; } diff --git a/faucet/kernel/default.nix b/global/kernel/default.nix similarity index 80% rename from faucet/kernel/default.nix rename to global/kernel/default.nix index 9a3e7e08..0fc6c935 100644 --- a/faucet/kernel/default.nix +++ b/global/kernel/default.nix @@ -2,9 +2,9 @@ , lib , config , ... }: with lib; let - cfg = config.faucet.kernel; + cfg = config.global.kernel; in { - options.faucet.kernel = { + options.global.kernel = { enable = mkEnableOption "kernel version and configuration" // { default = true; }; lts = mkEnableOption "longterm kernel releases"; sysctl = { @@ -23,6 +23,6 @@ in { "kernel.dmesg_restrict" = mkIf cfg.sysctl.harden 1; "vm.swappiness" = cfg.sysctl.swappiness; }; - boot.kernelPackages = with pkgs; mkDefault (if cfg.lts then linuxPackages else linuxPackages_latest); + boot.kernelPackages = with pkgs; mkOverride 1001 (if cfg.lts then linuxPackages else linuxPackages_latest); }; } diff --git a/faucet/libvirt/default.nix b/global/libvirt/default.nix similarity index 77% rename from faucet/libvirt/default.nix rename to global/libvirt/default.nix index 48dacbbf..b8149719 100644 --- a/faucet/libvirt/default.nix +++ b/global/libvirt/default.nix @@ -2,9 +2,9 @@ , lib , config , ... }: with lib; let - cfg = config.faucet.libvirt; + cfg = config.global.libvirt; in { - options.faucet.libvirt = { + options.global.libvirt = { enable = mkEnableOption "libvirt virtualisation daemon" // { default = true; }; }; @@ -20,12 +20,15 @@ in { parallelShutdown = 5; }; + environment.systemPackages = with pkgs; [ virtiofsd ]; + # USB redirection requires a setuid wrapper virtualisation.spiceUSBRedirection.enable = true; environment.persistence."/nix/persist/fhs".directories = [ "/var/lib/libvirt" ]; + global.fs.zfs.mountpoints."/nix/persist/service/libvirt" = "service/libvirt"; # allow management by admin users users.adminGroups = [ "libvirtd" ]; diff --git a/global/lowmem/default.nix b/global/lowmem/default.nix new file mode 100644 index 00000000..8a3ca395 --- /dev/null +++ b/global/lowmem/default.nix @@ -0,0 +1,25 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.lowmem; +in { + options.global.lowmem = { + enable = mkEnableOption "low memory optimisations"; + swapsize = mkOption { + type = with types; int; + default = 8 * 1024; + description = "automatic swap file size"; + }; + }; + + config = mkIf cfg.enable { + # enables remote nixos-rebuild + nix.settings.trusted-users = [ "koishi" ]; + + swapDevices = [ { + device = "/nix/persist/secret/swap"; + size = cfg.swapsize; + } ]; + }; +} diff --git a/global/netdata/default.nix b/global/netdata/default.nix new file mode 100644 index 00000000..fb9bebf6 --- /dev/null +++ b/global/netdata/default.nix @@ -0,0 +1,52 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.netdata; +in { + options.global.netdata = { + enable = mkEnableOption "netdata"; + host = mkOption { + type = with types; str; + default = "localhost"; + description = "hostname of netdata web interface"; + }; + addSSL = mkEnableOption "add SSL to netdata proxy"; + useACMEHost = mkOption { + type = with types; nullOr str; + default = null; + description = "existing acme host"; + }; + basicAuthFile = mkOption { + type = with types; nullOr path; + default = "/nix/persist/secret/netdata"; + description = "path to passwd file"; + }; + }; + + config = mkIf cfg.enable { + services.netdata = { + enable = true; + config = { + global = { + "error log" = "syslog"; + "access log" = "none"; + "debug log" = "syslog"; + }; + web."bind to" = "unix:/var/run/netdata/netdata.sock"; + }; + }; + + users.users.netdata.uid = 287; + users.groups.netdata.gid = 287; + + services.nginx.enable = mkDefault true; + services.nginx.virtualHosts.${cfg.host} = { + inherit (cfg) addSSL useACMEHost basicAuthFile; + locations."/".proxyPass = "http://unix:/var/run/netdata/netdata.sock"; + }; + users.users.nginx.extraGroups = [ "netdata" ]; + + environment.persistence."/nix/persist/fhs".directories = [ "/var/lib/netdata" ]; + }; +} diff --git a/global/oci/default.nix b/global/oci/default.nix new file mode 100644 index 00000000..60e5e4d1 --- /dev/null +++ b/global/oci/default.nix @@ -0,0 +1,21 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.oci; +in { + options.global.oci = { + enable = mkEnableOption "oci container runtime"; + }; + + config = mkIf cfg.enable { + virtualisation.podman = { + enable = true; + enableNvidia = with config.global.gpu; mkDefault type == "prime" || type == "nvidia"; + dockerCompat = true; + }; + + users.home.persist.directories = [ ".local/share/containers" ]; + environment.persistence."/nix/persist/fhs".directories = [ "/var/lib/containers" ]; + }; +} diff --git a/global/virtualbox/default.nix b/global/virtualbox/default.nix new file mode 100644 index 00000000..bf705e14 --- /dev/null +++ b/global/virtualbox/default.nix @@ -0,0 +1,34 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.global.virtualbox; +in { + options.global.virtualbox = { + enable = mkEnableOption "virtualbox host (kvm)"; + }; + + config = mkIf cfg.enable { + virtualisation.virtualbox.host = { + enable = true; + enableKvm = true; + enableExtensionPack = true; + + enableHardening = false; + addNetworkInterface = false; + }; + + # allow virtualbox USB passthrough + users.adminGroups = [ "vboxusers" ]; + + users.home.persist.directories = [ + ".config/VirtualBox" + ]; + + users.homeModules = [ { + wayland.windowManager.sway.config.window.commands = [ + { criteria.class = "VirtualBox Manager"; command = "floating enable"; } + ]; + } ]; + }; +} diff --git a/home/app/nixos.nix b/home/app/nixos.nix new file mode 100644 index 00000000..4cfb6191 --- /dev/null +++ b/home/app/nixos.nix @@ -0,0 +1,28 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.users; +in { + options.users.home.persistApp = { + files = mkOption { + type = with types; listOf (oneOf [ str (attrsOf str) ]); + default = [ ]; + }; + directories = mkOption { + type = with types; listOf (oneOf [ str (attrsOf str) ]); + default = [ ]; + }; + }; + + config = { + users.profiles.app = { + uid = 5800; + description = "Insecure Applications"; + picture = ../picture/app.png; + }; + + # extra persistence specific to the app user + environment.persistence."/nix/persist".users.app = cfg.home.persistApp; + }; +} diff --git a/home/auth/home.nix b/home/auth/home.nix index 890628c3..1e86f8e1 100644 --- a/home/auth/home.nix +++ b/home/auth/home.nix @@ -17,9 +17,21 @@ # compiled from trusted keys in auth module ssh.allowedSignersFile = toString (pkgs.writeText "allowed_signers" (foldr (key: folded: - folded + "koishi@514fpv.one ${key}") "" + folded + "koishi@514fpv.one ${key}\n") "" config.passthrough.publicKeys)); }; }; }; + + programs.ssh = { + enable = true; + matchBlocks = { + "edge.514fpv.io".port = 8086; + "sf.514fpv.io".port = 8087; + }; + }; + + wayland.windowManager.sway.config.window.commands = mkIf config.passthrough.gui [ + { criteria.title = "Bitwarden"; command = "floating enable"; } + ]; } diff --git a/home/auth/nixos.nix b/home/auth/nixos.nix index d57ea2e3..55984071 100644 --- a/home/auth/nixos.nix +++ b/home/auth/nixos.nix @@ -2,6 +2,6 @@ , ... }: { # this module passes openssh public keys to home-manager users.homeModules = [ { - passthrough.publicKeys = config.faucet.auth.openssh.publicKeys; + passthrough.publicKeys = config.global.auth.openssh.publicKeys; } ]; } diff --git a/home/btop/home.nix b/home/btop/home.nix new file mode 100644 index 00000000..f435fee1 --- /dev/null +++ b/home/btop/home.nix @@ -0,0 +1,13 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.btop; +in mkIf cfg.enable { + programs.btop = { + enable = true; + settings = { + theme_background = false; + }; + }; +} diff --git a/home/btop/nixos.nix b/home/btop/nixos.nix new file mode 100644 index 00000000..3379a317 --- /dev/null +++ b/home/btop/nixos.nix @@ -0,0 +1,17 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.btop; +in { + options.home.btop = { + enable = mkEnableOption "btop" // { default = !config.home.util.minimal; }; + }; + + config = { + users.homeModules = [ + # this module passes gyroflow configuration to home-manager + { passthrough.btop = cfg; } + ]; + }; +} diff --git a/home/catppuccin/flake.png b/home/catppuccin/flake.png new file mode 100644 index 00000000..ea098e87 Binary files /dev/null and b/home/catppuccin/flake.png differ diff --git a/home/catppuccin/gui.nix b/home/catppuccin/gui.nix new file mode 100644 index 00000000..c52a3b96 --- /dev/null +++ b/home/catppuccin/gui.nix @@ -0,0 +1,62 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.catppuccin; + palette = (lib.importJSON "${config.catppuccin.sources.palette}/palette.json").${config.catppuccin.flavor}.colors; +in mkIf cfg.enable { + gtk.theme = { inherit (cfg.gtk) package name; }; + qt.style.name = "kvantum"; + qt.platformTheme.name = "kvantum"; + home.pointerCursor = { inherit (cfg.cursor) package name; }; + + # sway colour palette override + wayland.windowManager.sway.config = { + colors = { + focused = { border = "$lavender"; background = "$base"; text = "$text"; indicator = "$rosewater"; childBorder = "$lavender"; }; + focusedInactive = { border = "$overlay0"; background = "$base"; text = "$text"; indicator = "$rosewater"; childBorder = "$overlay0"; }; + unfocused = { border = "$overlay0"; background = "$base"; text = "$text"; indicator = "$rosewater"; childBorder = "$overlay0"; }; + urgent = { border = "$peach"; background = "$base"; text = "$peach"; indicator = "$overlay0"; childBorder = "$peach"; }; + placeholder = { border = "$overlay0"; background = "$base"; text = "$text"; indicator = "$overlay0"; childBorder = "$overlay0"; }; + background = "$base"; + }; + + bars = mkForce [ { + colors = { + background = "$base"; + statusline = "$text"; + focusedStatusline = "$text"; + focusedSeparator = "$base"; + focusedWorkspace = { border = "$base"; background = "$base"; text = "$green"; }; + activeWorkspace = { border = "$base"; background = "$base"; text = "$blue"; }; + inactiveWorkspace = { border = "$base"; background = "$base"; text = "$surface1"; }; + urgentWorkspace = { border = "$base"; background = "$base"; text = "$surface1"; }; + bindingMode = { border = "$base"; background = "$base"; text = "$surface1"; }; + }; + + mode = "dock"; + position = "bottom"; + workspaceButtons = true; + workspaceNumbers = true; + statusCommand = "${pkgs.i3status}/bin/i3status"; + fonts = { + names = [ "monospace" ]; + size = 8.0; + }; + trayOutput = "primary"; + } ]; + + output."*".bg = mkForce "${./flake.png} fill"; + gaps.inner = 12; + gaps.outer = 5; + # dodge the status bar + gaps.bottom = 0; + }; + + # i3status colour palette override + programs.i3status.general = with palette; { + color_good = lavender.hex; + color_degraded = yellow.hex; + color_bad = red.hex; + }; +} diff --git a/home/catppuccin/home.nix b/home/catppuccin/home.nix new file mode 100644 index 00000000..01950f15 --- /dev/null +++ b/home/catppuccin/home.nix @@ -0,0 +1,9 @@ +{ + catppuccin = { + enable = true; + accent = "pink"; + flavor = "mocha"; + }; + + imports = [ ./gui.nix ]; +} diff --git a/home/catppuccin/nixos.nix b/home/catppuccin/nixos.nix new file mode 100644 index 00000000..dbdf05cc --- /dev/null +++ b/home/catppuccin/nixos.nix @@ -0,0 +1,79 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + gui = with config.global.gpu; enable && session; + cfg = config.home.catppuccin; +in { + options.home.catppuccin = { + enable = mkEnableOption "catppuccin colour scheme" // { default = gui; }; + + gtk = { + package = mkOption { + type = with types; package; + default = (pkgs.catppuccin-gtk.overrideAttrs { + src = pkgs.fetchFromGitHub { + owner = "catppuccin"; + repo = "gtk"; + rev = "v1.0.3"; + fetchSubmodules = true; + hash = "sha256-q5/VcFsm3vNEw55zq/vcM11eo456SYE5TQA3g2VQjGc="; + }; + + postUnpack = ""; + }).override { + accents = [ "pink" ]; + size = "compact"; + #tweaks = [ "rimless" "black" ]; + variant = "mocha"; + }; + description = "catppuccin gtk theme package"; + }; + name = mkOption { + type = with types; str; + default = "catppuccin-mocha-pink-compact"; + description = "name of catppuccin gtk theme"; + }; + }; + + cursor = { + package = mkOption { + type = with types; package; + default = pkgs.catppuccin-cursors.mochaDark; + description = "catppuccin cursor theme package"; + }; + name = mkOption { + type = with types; str; + default = "catppuccin-mocha-dark-cursors"; + description = "name of catppuccin cursor theme"; + }; + }; + }; + + config = { + users.homeModules = [ + # this module passes catppuccin configuration to home-manager + { passthrough.catppuccin = cfg; } + ]; + + catppuccin.enable = cfg.enable; + + # gtk and cursor themes + environment.systemPackages = with cfg; mkIf enable [ + gtk.package cursor.package + ]; + + # override greetd theme + programs.regreet = mkIf cfg.enable { + theme = { + inherit (cfg.gtk) name package; + }; + cursorTheme = { + inherit (cfg.cursor) name package; + }; + settings = { + background.path = ./solid.png; + }; + }; + }; +} diff --git a/home/catppuccin/solid.png b/home/catppuccin/solid.png new file mode 100644 index 00000000..81bd01e2 Binary files /dev/null and b/home/catppuccin/solid.png differ diff --git a/home/chrome/nixos.nix b/home/chrome/nixos.nix index 7726bc31..44a2b5c0 100644 --- a/home/chrome/nixos.nix +++ b/home/chrome/nixos.nix @@ -2,8 +2,9 @@ , lib , config , ... }: with lib; let - gui = with config.faucet.gui; ( enable && session ); + gui = with config.global.gpu; enable && session; in { users.home.persist.directories = mkIf gui [ ".config/google-chrome" ]; security.chromiumSuidSandbox.enable = mkIf gui true; + environment.sessionVariables.NIXOS_OZONE_WL = "1"; } diff --git a/home/foot/home.nix b/home/foot/home.nix index d0d7bf86..af10a83a 100644 --- a/home/foot/home.nix +++ b/home/foot/home.nix @@ -5,5 +5,7 @@ programs.foot = { enable = true; settings.main.term = "xterm-256color"; + settings.main.font = "DejaVu Sans Mono:size=11"; + #settings.colors.alpha = 0.8; }; } diff --git a/home/gnome/home.nix b/home/gnome/home.nix new file mode 100644 index 00000000..fa7c5948 --- /dev/null +++ b/home/gnome/home.nix @@ -0,0 +1,11 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.gnome; +in { + imports = [ + ./impl/home.nix + ./impl/dconf.nix + ]; +} diff --git a/home/gnome/impl/dconf.nix b/home/gnome/impl/dconf.nix new file mode 100644 index 00000000..b9d88c18 --- /dev/null +++ b/home/gnome/impl/dconf.nix @@ -0,0 +1,286 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.gnome; + bg = ../../../share/54345906_p0.jpg; +in mkIf cfg.enable { + dconf.settings = let + p = "org/gnome"; + pd = "${p}/desktop"; + ps = "${p}/shell"; + pse = "${ps}/extensions"; + ptl = "${p}/terminal/legacy"; + ptlp = "${ptl}/profiles:"; + in { + "${pd}/peripherals/mouse".natural-scroll = true; + "${pd}/peripherals/touchpad".tap-to-click = true; + "${p}/epiphany".ask-for-default = false; + "${p}/evolution-data-server".migrated = true; + + "${p}/nautilus/preferences" = { + default-folder-viewer = "icon-view"; + migrated-gtk-settings = true; + search-filter-time-type = "last_modified"; + }; + + "${pd}/background" = { + color-shading-type = "solid"; + picture-options = "zoom"; + picture-uri = "file://${bg}"; + picture-uri-dark = "file://${bg}"; + primary-color = "#000000000000"; + secondary-color = "#000000000000"; + }; + + "${pd}/interface" = { + color-scheme = "prefer-dark"; + cursor-theme = "Bibata-Modern-Classic"; + font-antialiasing = "grayscale"; + font-hinting = "slight"; + gtk-theme = "adw-gtk3-dark"; + icon-theme = "Papirus-Dark"; + }; + + "${pd}/screensaver" = { + color-shading-type = "solid"; + lock-enabled = false; + picture-options = "zoom"; + picture-uri = "file://${bg}"; + primary-color = "#000000000000"; + secondary-color = "#000000000000"; + }; + + "${pd}/wm/preferences" = { + action-double-click-titlebar = "toggle-maximize"; + action-middle-click-titlebar = "minimize"; + button-layout = "close:appmenu"; + resize-with-right-button = true; + }; + + "${pd}/wm/keybindings" = { + panel-run-dialog = [ ]; + begin-resize = [ "r" ]; + close = [ "q" ]; + minimize = [ "BackSpace" ]; + move-to-workspace-1 = [ "1" ]; + move-to-workspace-2 = [ "2" ]; + move-to-workspace-3 = [ "3" ]; + move-to-workspace-4 = [ "4" ]; + move-to-workspace-left = [ "h" ]; + move-to-workspace-right = [ "l" ]; + switch-to-workspace-1 = [ "1" ]; + switch-to-workspace-2 = [ "2" ]; + switch-to-workspace-3 = [ "3" ]; + switch-to-workspace-4 = [ "4" ]; + toggle-maximized = [ "f" ]; + }; + + "${ps}/keybindings" = { + switch-to-application-1 = [ ]; + switch-to-application-2 = [ ]; + switch-to-application-3 = [ ]; + switch-to-application-4 = [ ]; + switch-to-application-5 = [ ]; + switch-to-application-6 = [ ]; + switch-to-application-7 = [ ]; + switch-to-application-8 = [ ]; + switch-to-application-9 = [ ]; + toggle-application-view = [ "d" ]; + }; + + "${p}/settings-daemon/plugins/media-keys" = { + custom-keybindings = [ + "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/" + "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/" + ]; + logout = [ ]; + screensaver = [ "l" ]; + }; + + "${p}/settings-daemon/plugins/media-keys/custom-keybindings/custom0" = { + binding = "Return"; + command = "kgx"; + name = "Launch console"; + }; + + "${p}/settings-daemon/plugins/media-keys/custom-keybindings/custom1" = { + binding = "q"; + command = "google-chrome-stable"; + name = "Launch Google Chrome"; + }; + + "${ptlp}" = { + #default = "95894cfd-82f7-430d-af6e-84d168bc34f5"; + list = [ + "de8a9081-8352-4ce4-9519-5de655ad9361" + "71a9971e-e829-43a9-9b2f-4565c855d664" + "5083e06b-024e-46be-9cd2-892b814f1fc8" + "95894cfd-82f7-430d-af6e-84d168bc34f5" + ]; + }; + + "${ptlp}/:5083e06b-024e-46be-9cd2-892b814f1fc8" = { + background-color = "#24273a"; + cursor-background-color = "#f4dbd6"; + cursor-colors-set = true; + cursor-foreground-color = "#24273a"; + foreground-color = "#cad3f5"; + highlight-background-color = "#24273a"; + highlight-colors-set = true; + highlight-foreground-color = "#5b6078"; + palette = [ + "#494d64" "#ed8796" "#a6da95" "#eed49f" + "#8aadf4" "#f5bde6" "#8bd5ca" "#b8c0e0" + "#5b6078" "#ed8796" "#a6da95" "#eed49f" + "#8aadf4" "#f5bde6" "#8bd5ca" "#a5adcb" + ]; + use-theme-colors = false; + visible-name = "Catppuccin Macchiato"; + }; + + "${ptlp}/:71a9971e-e829-43a9-9b2f-4565c855d664" = { + background-color = "#303446"; + cursor-background-color = "#f2d5cf"; + cursor-colors-set = true; + cursor-foreground-color = "#303446"; + default-size-columns = 150; + default-size-rows = 35; + foreground-color = "#c6d0f5"; + highlight-background-color = "#303446"; + highlight-colors-set = true; + highlight-foreground-color = "#626880"; + palette = [ + "#51576d" "#e78284" "#a6d189" "#e5c890" + "#8caaee" "#f4b8e4" "#81c8be" "#b5bfe2" + "#626880" "#e78284" "#a6d189" "#e5c890" + "#8caaee" "#f4b8e4" "#81c8be" "#a5adce" + ]; + use-theme-colors = false; + visible-name = "Catppuccin Frappe"; + }; + + "${ptlp}/:95894cfd-82f7-430d-af6e-84d168bc34f5" = { + background-color = "#1e1e2e"; + cursor-background-color = "#f5e0dc"; + cursor-colors-set = true; + cursor-foreground-color = "#1e1e2e"; + foreground-color = "#cdd6f4"; + highlight-background-color = "#1e1e2e"; + highlight-colors-set = true; + highlight-foreground-color = "#585b70"; + palette = [ + "#45475a" "#f38ba8" "#a6e3a1" "#f9e2af" + "#89b4fa" "#f5c2e7" "#94e2d5" "#bac2de" + "#585b70" "#f38ba8" "#a6e3a1" "#f9e2af" + "#89b4fa" "#f5c2e7" "#94e2d5" "#a6adc8" + ]; + use-theme-colors = false; + visible-name = "Catppuccin Mocha"; + }; + + "${ptlp}/:de8a9081-8352-4ce4-9519-5de655ad9361" = { + background-color = "#eff1f5"; + cursor-background-color = "#dc8a78"; + cursor-colors-set = true; + cursor-foreground-color = "#eff1f5"; + foreground-color = "#4c4f69"; + highlight-background-color = "#eff1f5"; + highlight-colors-set = true; + highlight-foreground-color = "#acb0be"; + palette = [ + "#5c5f77" "#d20f39" "#40a02b" "#df8e1d" + "#1e66f5" "#ea76cb" "#179299" "#acb0be" + "#6c6f85" "#d20f39" "#40a02b" "#df8e1d" + "#1e66f5" "#ea76cb" "#179299" "#bcc0cc" + ]; + use-theme-colors = false; + visible-name = "Catppuccin Latte"; + }; + + "${ps}" = { + disabled-extensions = [ + "light-style@gnome-shell-extensions.gcampax.github.com" + "places-menu@gnome-shell-extensions.gcampax.github.com" + "windowsNavigator@gnome-shell-extensions.gcampax.github.com" + "window-list@gnome-shell-extensions.gcampax.github.com" + "workspace-indicator@gnome-shell-extensions.gcampax.github.com" + "dash-to-dock@micxgx.gmail.com" + ]; + enabled-extensions = [ + "user-theme@gnome-shell-extensions.gcampax.github.com" + "apps-menu@gnome-shell-extensions.gcampax.github.com" + "drive-menu@gnome-shell-extensions.gcampax.github.com" + "appindicatorsupport@rgcjonas.gmail.com" + "dash-to-panel@jderose9.github.com" + "caffeine@patapon.info" + "PrivacyMenu@stuarthayhurst" + ]; + last-selected-power-profile = "performance"; + welcome-dialog-last-shown-version = "45.3"; + }; + + #"${pse}/user-theme".name = "catppuccin-mocha-pink-compact"; + + "${pse}/caffeine" = { + screen-blank = "never"; + }; + + "${pse}/dash-to-dock" = { + background-opacity = 0.80000000000000004; + dash-max-icon-size = 48; + dock-position = "BOTTOM"; + height-fraction = 0.90000000000000002; + multi-monitor = false; + running-indicator-style = "DOTS"; + custom-theme-shrink = true; + }; + + "${pse}/dash-to-panel" = { + animate-appicon-hover = false; + animate-appicon-hover-animation-type = "SIMPLE"; + appicon-margin = 0; + appicon-padding = 4; + appicon-style= "NORMAL"; + available-monitors = [ 0 ]; + dot-position = "BOTTOM"; + dot-style-focused = "METRO"; + dot-style-unfocused = "DOTS"; + group-apps = true; + hide-overview-on-startup = true; + hotkeys-overlay-combo = "TEMPORARILY"; + intellihide = true; + intellihide-behaviour = "FOCUSED_WINDOWS"; + intellihide-hide-from-windows = true; + isolate-workspaces = false; + leftbox-padding = -1; + overview-click-to-exit = true; + panel-anchors = ''{"0":"MIDDLE"}''; + panel-element-positions = ''{"0":[{"element":"showAppsButton","visible":true,"position":"stackedTL"},{"element":"activitiesButton","visible":true,"position":"stackedTL"},{"element":"leftBox","visible":false,"position":"stackedTL"},{"element":"taskbar","visible":true,"position":"centerMonitor"},{"element":"centerBox","visible":true,"position":"stackedBR"},{"element":"rightBox","visible":true,"position":"stackedBR"},{"element":"dateMenu","visible":true,"position":"stackedBR"},{"element":"systemMenu","visible":true,"position":"stackedBR"},{"element":"desktopButton","visible":false,"position":"stackedBR"}]}''; + panel-lengths = ''{"0":100}''; + panel-positions = ''{"0":"BOTTOM"}''; + panel-sizes = ''{"0":42}''; + primary-monitor = 0; + secondarymenu-contains-showdetails = true; + show-showdesktop-hover = true; + status-icon-padding = -1; + stockgs-force-hotcorner = false; + stockgs-keep-dash = false; + stockgs-keep-top-panel = false; + stockgs-panelbtn-click-only = false; + trans-bg-color = "#2a2a2a"; + trans-dynamic-anim-target = 1.0; + trans-dynamic-behavior = "MAXIMIZED_WINDOWS"; + trans-gradient-bottom-color = "#000000"; + trans-gradient-bottom-opacity = 0.5; + trans-gradient-top-opacity = 0.0; + trans-panel-opacity = 0.0; + trans-use-custom-bg = true; + trans-use-custom-gradient = true; + trans-use-custom-opacity = true; + trans-use-dynamic-opacity = true; + tray-padding = -1; + window-preview-title-position = "TOP"; + }; + }; +} diff --git a/home/gnome/impl/home.nix b/home/gnome/impl/home.nix new file mode 100644 index 00000000..ff5b9eba --- /dev/null +++ b/home/gnome/impl/home.nix @@ -0,0 +1,32 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.gnome; +in mkIf cfg.enable { + home.packages = + with pkgs; + with gnome; + with gnomeExtensions; [ + # gtk3 theme + adw-gtk3 + + # gnomeExtensions + caffeine + dash-to-panel + dash-to-dock + appindicator + privacy-settings-menu + ]; + + catppuccin.enable = mkForce false; + home.pointerCursor = mkForce null; + gtk.enable = false; + + home.persistence."/nix/persist/home/${config.home.username}" = { + removePrefixDirectory = true; + files = [ + (if config.specialisation != {} then "gnome/.config/monitors.xml" else "extern/.config/monitors.xml") + ]; + }; +} diff --git a/home/gnome/impl/nixos.nix b/home/gnome/impl/nixos.nix new file mode 100644 index 00000000..e49c6ad2 --- /dev/null +++ b/home/gnome/impl/nixos.nix @@ -0,0 +1,49 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.gnome; +in mkIf cfg.enable { + global.flatpak.enable = mkDefault true; + home.catppuccin.enable = mkDefault false; + catppuccin.enable = false; + programs.regreet.enable = false; + services.xserver.enable = true; + services.xserver.displayManager.startx.enable = false; + services.xserver.displayManager.gdm.enable = true; + services.xserver.desktopManager.gnome.enable = true; + services.udev.packages = with pkgs; [ gnome-settings-daemon ]; + services.hardware.bolt.enable = true; + xdg.portal.configPackages = with pkgs; [ gnome-session ]; + hardware.pulseaudio.enable = false; + + environment.gnome.excludePackages = (with pkgs; [ + snapshot + gnome-tour + ] ++ optionals config.global.flatpak.enable [ + baobab + simple-scan + evince + file-roller + geary + loupe + seahorse + totem + epiphany + gnome-calculator + gnome-calendar + gnome-connections + gnome-font-viewer + gnome-text-editor + gnome-characters + gnome-clocks + gnome-contacts + gnome-logs + gnome-maps + gnome-music + gnome-weather + ]) ++ (with pkgs.gnome; [ ] ++ optionals config.global.flatpak.enable [ + ]); + + users.home.persist.directories = [ ".config/dconf" ]; +} diff --git a/home/gnome/nixos.nix b/home/gnome/nixos.nix new file mode 100644 index 00000000..c7a3bd26 --- /dev/null +++ b/home/gnome/nixos.nix @@ -0,0 +1,25 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.gnome; +in { + imports = [ ./impl/nixos.nix ]; + + options.home.gnome = { + enable = mkEnableOption "GNOME desktop environment"; + }; + + config = { + users.homeModules = [ + # this module passes gnome configuration to home-manager + { passthrough.gnome = cfg; } + ]; + + specialisation.nognome = with cfg; mkIf enable { + configuration = { + home.gnome.enable = mkForce false; + }; + }; + }; +} diff --git a/home/gui/home.nix b/home/gui/home.nix index 152d48a4..f8423161 100644 --- a/home/gui/home.nix +++ b/home/gui/home.nix @@ -1,25 +1,35 @@ { pkgs , lib , config -, ... }: with lib; mkIf config.passthrough.gui { - # cursor theme - home.pointerCursor = { - package = pkgs.bibata-cursors; - name = "Bibata-Modern-Classic"; - size = 24; - }; +, ... }: with lib; let + catppuccin = config.passthrough.catppuccin.enable; +in { + config = mkIf config.passthrough.gui { + # cursor theme + home.pointerCursor = { + package = mkDefault pkgs.bibata-cursors; + name = mkDefault "Bibata-Modern-Classic"; + size = 24; + x11.enable = true; + gtk.enable = true; + }; - # gtk theme - gtk.theme = { - package = pkgs.whitesur-gtk-theme; - name = "WhiteSur-Dark"; - }; + # gtk theme + gtk.theme = mkDefault { + package = pkgs.whitesur-gtk-theme; + name = "WhiteSur-Dark"; + }; - # gtk icons - gtk.iconTheme = { - package = pkgs.whitesur-icon-theme; - name = "WhiteSur-dark"; - }; + # gtk icons + gtk.iconTheme = mkDefault { + package = pkgs.papirus-icon-theme; + name = "Papirus-Dark"; + }; - gtk.enable = true; + # unify qt theme + qt.platformTheme.name = mkDefault "gtk"; + + gtk.enable = mkDefault true; + qt.enable = mkDefault true; + }; } diff --git a/home/gui/nixos.nix b/home/gui/nixos.nix index 35370e5a..0e655654 100644 --- a/home/gui/nixos.nix +++ b/home/gui/nixos.nix @@ -2,7 +2,8 @@ , lib , config , ... }: with lib; let - gui = with config.faucet.gui; ( enable && session ); + gui = with config.global.gpu; enable && session; + catppuccin = config.home.catppuccin; in { config = { users.homeModules = [ @@ -12,17 +13,24 @@ in { users.adminGroups = mkIf gui [ "video" ]; # themes and icons - environment.systemPackages = with pkgs; mkIf gui [ - whitesur-gtk-theme whitesur-icon-theme + environment.systemPackages = with pkgs; mkIf gui ([ + papirus-icon-theme + ] ++ optionals (!catppuccin.enable) [ + whitesur-gtk-theme + whitesur-icon-theme bibata-cursors - ]; + ]); + + fonts.enableDefaultPackages = mkIf gui true; security = mkIf gui { polkit.enable = true; }; - fonts.enableDefaultPackages = mkIf gui true; programs = mkIf gui { dconf.enable = true; }; + services = mkIf gui { + blueman.enable = !config.global.flatpak.enable; + }; }; } diff --git a/home/gyroflow/home.nix b/home/gyroflow/home.nix new file mode 100644 index 00000000..abff2f37 --- /dev/null +++ b/home/gyroflow/home.nix @@ -0,0 +1,13 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.gyroflow; +in mkIf cfg.enable { + # temporarily gone until regression is fixed + #home.packages = [ cfg.package ]; + + wayland.windowManager.sway.config.window.commands = [ + { criteria.app_id = "xyz.gyroflow.gyroflow"; command = "floating enable"; } + ]; +} diff --git a/home/gyroflow/nixos.nix b/home/gyroflow/nixos.nix new file mode 100644 index 00000000..02b46551 --- /dev/null +++ b/home/gyroflow/nixos.nix @@ -0,0 +1,26 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.gyroflow; +in { + options.home.gyroflow = { + enable = mkEnableOption "gyroflow stabilisation software"; + package = mkOption { + type = with types; package; + default = pkgs.gyroflow.overrideAttrs (finalAttrs: previousAttrs: { + buildInputs = previousAttrs.buildInputs ++ [ pkgs.qt6Packages.qtwayland ]; + }); + description = "gyroflow package"; + }; + }; + + config = { + users.homeModules = [ + # this module passes gyroflow configuration to home-manager + { passthrough.gyroflow = cfg; } + ]; + + users.home.persist.directories = mkIf cfg.enable [ ".config/Gyroflow" ]; + }; +} diff --git a/home/headless/home.nix b/home/headless/home.nix new file mode 100644 index 00000000..e30334d8 --- /dev/null +++ b/home/headless/home.nix @@ -0,0 +1,24 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.headless; +in mkIf (cfg.enable != null) { + wayland.windowManager.sway.config = { + output = { + ${cfg.enable}.pos = "0 0"; + HEADLESS-1 = cfg.output; + }; + + startup = [ { command = "swaymsg create_output && swaymsg output HEADLESS-1 disable"; } ]; + }; + + home.packages = [ (pkgs.writeShellScriptBin "headless" '' + swaymsg output HEADLESS-1 enable + ${pkgs.wayvnc}/bin/wayvnc \ + --output=HEADLESS-1 \ + ${cfg.extraArgs} \ + ${cfg.host} ${toString cfg.port} + swaymsg output HEADLESS-1 disable + '') ]; +} diff --git a/home/headless/nixos.nix b/home/headless/nixos.nix new file mode 100644 index 00000000..77521f05 --- /dev/null +++ b/home/headless/nixos.nix @@ -0,0 +1,51 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.headless; +in { + options.home.headless = { + enable = mkOption { + type = with types; nullOr str; + default = null; + description = "a headless, remotely viewed sway display"; + }; + + output = mkOption { + type = with types; attrsOf str; + default = { + # pixel tablet + mode = "2560x1600"; + scale = "2"; + pos = "1920 0"; + }; + description = "headless display configuration"; + }; + + host = mkOption { + type = with types; str; + default = "0.0.0.0"; + description = "wayvnc listen host"; + }; + + port = mkOption { + type = with types; port; + # utility port + default = 1300; + description = "wayvnc listen port"; + }; + + extraArgs = mkOption { + type = with types; str; + default = "--max-fps=60"; + description = "extra wayvnc args"; + }; + }; + + config = { + users.homeModules = [ + # this module passes headless configuration to home-manager + { passthrough.headless = cfg; } + ]; + }; +} diff --git a/home/imv/home.nix b/home/imv/home.nix new file mode 100644 index 00000000..e50dc321 --- /dev/null +++ b/home/imv/home.nix @@ -0,0 +1,6 @@ +{ pkgs +, lib +, config +, ... }: with lib; mkIf config.passthrough.gui { + programs.imv.enable = true; +} diff --git a/home/jetbrains/home.nix b/home/jetbrains/home.nix new file mode 100644 index 00000000..637dc689 --- /dev/null +++ b/home/jetbrains/home.nix @@ -0,0 +1,11 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.jetbrains; +in mkIf cfg.enable { + home.packages = with pkgs.jetbrains; [ pkgs.go ] ++ + optional cfg.idea idea-community ++ + optional cfg.clion clion ++ + optional cfg.goland goland; +} diff --git a/home/jetbrains/nixos.nix b/home/jetbrains/nixos.nix new file mode 100644 index 00000000..e2d01f60 --- /dev/null +++ b/home/jetbrains/nixos.nix @@ -0,0 +1,27 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.jetbrains; +in { + options.home.jetbrains = { + enable = mkEnableOption "jetbrains text editor"; + idea = mkEnableOption "intellij idea"; + clion = mkEnableOption "clion ide"; + goland = mkEnableOption "goland ide" // { default = true; }; + }; + + config = { + users.homeModules = [ + # this module passes jetbrains configuration to home-manager + { passthrough.jetbrains = cfg; } + ]; + + users.home.persist.directories = mkIf cfg.enable [ + "go" + ".java/.userPrefs" + ".config/JetBrains" + ".local/share/JetBrains" + ]; + }; +} diff --git a/home/libreoffice/home.nix b/home/libreoffice/home.nix new file mode 100644 index 00000000..628782af --- /dev/null +++ b/home/libreoffice/home.nix @@ -0,0 +1,9 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.libreoffice; + enable = cfg.enable && (cfg.allUsers || (config.home.username == "app")); +in mkIf enable { + home.packages = with pkgs; [ libreoffice ]; +} diff --git a/home/libreoffice/nixos.nix b/home/libreoffice/nixos.nix new file mode 100644 index 00000000..9e911164 --- /dev/null +++ b/home/libreoffice/nixos.nix @@ -0,0 +1,22 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.libreoffice; + persist = [ ".config/libreoffice" ]; +in { + options.home.libreoffice = { + enable = mkEnableOption "open source office suite"; + allUsers = mkEnableOption "set up for all users"; + }; + + config = { + users.homeModules = [ + # this module passes minecraft configuration to home-manager + { passthrough.libreoffice = cfg; } + ]; + + users.home.persist.directories = with cfg; mkIf (enable && allUsers) persist; + users.home.persistApp.directories = with cfg; mkIf (enable && !allUsers) persist; + }; +} diff --git a/home/minecraft/home.nix b/home/minecraft/home.nix new file mode 100644 index 00000000..410eef4c --- /dev/null +++ b/home/minecraft/home.nix @@ -0,0 +1,16 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + inherit (config.passthrough) gui; + cfg = config.passthrough.minecraft; + enable = cfg.enable && config.home.username == cfg.user; +in mkIf enable { + home.packages = with pkgs; [ + jdk8 + ] ++ optional gui prismlauncher; + + wayland.windowManager.sway.config.window.commands = mkIf gui [ + { criteria.app_id = "org.prismlauncher.PrismLauncher"; command = "floating enable"; } + ]; +} diff --git a/home/minecraft/nixos.nix b/home/minecraft/nixos.nix new file mode 100644 index 00000000..ebbdab5d --- /dev/null +++ b/home/minecraft/nixos.nix @@ -0,0 +1,33 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.minecraft; + gui = with config.global.gpu; enable && session; +in { + options.home.minecraft = { + enable = mkEnableOption "minecraft game launcher and jvm"; + user = mkOption { + type = with types; str; + default = "minecraft"; + description = "username which minecraft game client runs under"; + }; + }; + + config = { + users.homeModules = [ + # this module passes minecraft configuration to home-manager + { passthrough.minecraft = cfg; } + ]; + + users.profiles.minecraft = mkIf (cfg.enable && cfg.user == "minecraft") { + uid = 5801; + description = "Minecraft"; + picture = ../picture/aux.png; + }; + + environment.persistence."/nix/persist".users.${cfg.user} = mkIf (cfg.enable && gui) { + directories = [ ".local/share/PrismLauncher" ]; + }; + }; +} diff --git a/home/mpv/home.nix b/home/mpv/home.nix new file mode 100644 index 00000000..b95bb44c --- /dev/null +++ b/home/mpv/home.nix @@ -0,0 +1,14 @@ +{ pkgs +, lib +, config +, ... }: with lib; mkIf config.passthrough.gui { + programs.mpv = { + enable = true; + config = { + hwdec = "auto-safe"; + vo = "gpu"; + profile = "gpu-hq"; + gpu-context = "wayland"; + }; + }; +} diff --git a/home/picture/app.png b/home/picture/app.png new file mode 100644 index 00000000..bd1b86c3 Binary files /dev/null and b/home/picture/app.png differ diff --git a/home/picture/aux.png b/home/picture/aux.png new file mode 100644 index 00000000..8262154f Binary files /dev/null and b/home/picture/aux.png differ diff --git a/home/picture/koishi.png b/home/picture/koishi.png new file mode 100644 index 00000000..8f44f8e0 Binary files /dev/null and b/home/picture/koishi.png differ diff --git a/home/picture/staging.png b/home/picture/staging.png new file mode 100644 index 00000000..1702e24e Binary files /dev/null and b/home/picture/staging.png differ diff --git a/home/plasma/config.nix b/home/plasma/config.nix new file mode 100644 index 00000000..207bba57 --- /dev/null +++ b/home/plasma/config.nix @@ -0,0 +1,13 @@ +{ + programs.plasma = { + workspace = { + lookAndFeel = "org.kde.breezedark.desktop"; + #clickItemTo = "select"; + }; + + configFile = { + baloofilerc."Basic Settings"."Indexing-Enabled" = false; + kcminputrc.Libinput."2362"."597"."UNIW0001:00 093A:0255 Touchpad".NaturalScroll = true; + }; + }; +} diff --git a/home/plasma/home.nix b/home/plasma/home.nix new file mode 100644 index 00000000..7c329a8b --- /dev/null +++ b/home/plasma/home.nix @@ -0,0 +1,51 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.plasma; + image = ../../share/54345906_p0.jpg; +in mkIf cfg.enable { + programs.plasma = { + # https://github.com/pjones/plasma-manager + enable = true; + #overrideConfig = true; + + workspace = { + lookAndFeel = "org.kde.breezedark.desktop"; + wallpaper = image; + }; + + hotkeys.commands = { + launch-konsole = { + name = "Launch Konsole"; + key = "Meta+Enter"; + command = "konsole"; + }; + }; + + configFile = { + baloofilerc."Basic Settings"."Indexing-Enabled" = false; + kscreenlockerrc.Greeter.Wallpaper."org.kde.image".General.Image = image; + kscreenlockerrc.Greeter.Wallpaper."org.kde.image".General.PreviewImage = image; + }; + } // cfg.extraConfig; + + home.activation.gtkCleanup = hm.dag.entryAfter [ "writeBoundary" ] '' + $DRY_RUN_CMD rm -f $HOME/.gtkrc-2.0.old + ''; + + qt.enable = false; + qt.platformTheme.name = null; + + # gtk theme + gtk.theme = { + package = pkgs.kdePackages.breeze-gtk; + name = "Breeze-Dark"; + }; + + # gtk icons + gtk.iconTheme = { + package = pkgs.kdePackages.breeze-icons; + name = "breeze-dark"; + }; +} diff --git a/home/plasma/nixos.nix b/home/plasma/nixos.nix new file mode 100644 index 00000000..b1a642cc --- /dev/null +++ b/home/plasma/nixos.nix @@ -0,0 +1,52 @@ +{ pkgs +, lib +, config +, plasma-manager +, ... }: with lib; let + cfg = config.home.plasma; +in { + options.home.plasma = { + enable = mkEnableOption "plasma desktop and configuration"; + specialise = mkEnableOption "enable plasma in a specialisation"; + extraConfig = mkOption { + type = with types; anything; + default = { }; + description = "extra plasma-manager configuration"; + }; + }; + + config = { + users.homeModules = [ + # this module passes plasma configuration to home-manager + { passthrough.plasma = cfg; } + ]; + + users.home.persist.files = mkIf cfg.enable [ + ".config/kwinoutputconfig.json" + ]; + users.home.persist.directories = mkIf cfg.enable [ + ".local/share/kwalletd" + ]; + + services.desktopManager.plasma6 = mkIf cfg.enable { + enable = true; + }; + + home-manager.backupFileExtension = mkIf cfg.enable "old"; + home-manager.sharedModules = [ + plasma-manager.homeManagerModules.plasma-manager + ]; + + services.blueman = mkIf cfg.enable { + enable = mkForce false; + }; + + home = mkIf cfg.enable { + catppuccin.enable = mkForce false; + }; + + specialisation.plasma = mkIf cfg.specialise { + configuration.home.plasma.enable = true; + }; + }; +} diff --git a/home/profile.nix b/home/profile.nix index d65672f8..ee64c439 100644 --- a/home/profile.nix +++ b/home/profile.nix @@ -1,6 +1,7 @@ { pkgs , lib , config +, inputs , ... }: with lib; let cfg = config.users; in { @@ -28,6 +29,11 @@ in { default = false; description = "enable ssh authorized keys for user"; }; + picture = mkOption { + type = with types; nullOr path; + default = null; + description = "path to user profile picture"; + }; }; }); description = "preconfigured users with profile options"; @@ -67,9 +73,9 @@ in { users = mapAttrs (name: opts: { inherit (opts) uid; description = with opts; mkIf (description != null) description; - extraGroups = mkIf opts.admin cfg.adminGroups; + extraGroups = [ "dialout" ] ++ optionals opts.admin cfg.adminGroups; openssh.authorizedKeys.keys = mkIf (opts.sshLogin && config.services.openssh.enable) - config.faucet.auth.openssh.publicKeys; + config.global.auth.openssh.publicKeys; hashedPasswordFile = "/nix/persist/shadow/${name}"; shell = pkgs.zsh; isNormalUser = mkIf (name != "root") true; @@ -79,8 +85,9 @@ in { # base groups adminGroups = [ - "wheel" "dialout" "kvm" + "wheel" "kvm" "systemd-journal" + "networkmanager" ]; # base home modules in current directory @@ -124,25 +131,64 @@ in { neededForBoot = true; }) cfg.profiles; + global.fs.zfs.mountpoints = mapAttrs' + (name: opts: nameValuePair + "/nix/persist/home/${name}" + "home/${name}") + (filterAttrs (n: _: n != "root") config.users.profiles); + home-manager.users = mapAttrs (name: opts: { - imports = cfg.homeModules; + imports = with inputs; cfg.homeModules ++ [ + impermanence.homeManagerModules.impermanence + catppuccin.homeManagerModules.catppuccin + ]; + home.file.".face" = mkIf (opts.picture != null) { + source = opts.picture; + }; home.stateVersion = "23.11"; }) cfg.profiles; + system.activationScripts = mapAttrs' + (name: opts: nameValuePair + "${name}-profile-icon" + { + deps = [ "users" ]; + text = let + iconDest = "/var/lib/AccountsService/icons/${name}"; + userConf = pkgs.writeText "${name}-config" '' + [User] + Session= + Icon=${iconDest} + SystemAccount=false + ''; + in '' + install -Dm 0444 ${opts.picture} ${iconDest} + install -Dm 0400 ${userConf} /var/lib/AccountsService/users/${name} + ''; + }) + (filterAttrs (n: _: n != "root") config.users.profiles); + # set up standard persistence for users # this is registered internally for each software's configuration environment.persistence."/nix/persist" = { - users = mapAttrs (name: _: cfg.home.persist // { + users = (mapAttrs (name: _: cfg.home.persist // { # root workaround, ugly but necessary # cannot get it properly for the same reason # mentioned above in fileSystems home = mkIf (name == "root") "/root"; - }) cfg.profiles; + }) cfg.profiles); + hideMounts = true; }; # enable passwordless sudo security.sudo.wheelNeedsPassword = false; + + # enable access in build-vm + virtualisation.vmVariant = { + users.users.koishi.password = "passwd"; + users.users.koishi.hashedPasswordFile = mkForce null; + }; }; # this is for home components that need to extend nixos diff --git a/home/steam/config.nix b/home/steam/config.nix new file mode 100644 index 00000000..0b696a43 --- /dev/null +++ b/home/steam/config.nix @@ -0,0 +1,14 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.steam; +in mkIf cfg.enable { + hardware.steam-hardware.enable = true; + + networking.firewall = { + allowedTCPPorts = [ 27015 27036 ]; + allowedUDPPorts = [ 27015 ]; + allowedUDPPortRanges = [ { from = 27031; to = 27036; } ]; + }; +} diff --git a/home/steam/home.nix b/home/steam/home.nix new file mode 100644 index 00000000..89fa75bb --- /dev/null +++ b/home/steam/home.nix @@ -0,0 +1,17 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.steam; + enable = cfg.enable && (cfg.allUsers || (config.home.username == "app")); + package = config.programs.steam.package; +in mkIf enable { + home.packages = with pkgs; [ + cfg.package + cfg.package.run + ]; + + wayland.windowManager.sway.config.window.commands = [ + { criteria.class = "steam"; command = "floating enable"; } + ]; +} diff --git a/home/steam/nixos.nix b/home/steam/nixos.nix new file mode 100644 index 00000000..cccf99b3 --- /dev/null +++ b/home/steam/nixos.nix @@ -0,0 +1,29 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.steam; + persist = [ ".steam" ".local/share/Steam" ]; +in { + imports = [ ./config.nix ]; + + options.home.steam = { + enable = mkEnableOption "steam software and environment"; + package = mkOption { + type = with types; package; + default = config.programs.steam.package; + description = "steam package"; + }; + allUsers = mkEnableOption "set up for all users"; + }; + + config = { + users.homeModules = [ + # this module passes steam configuration to home-manager + { passthrough.steam = cfg; } + ]; + + users.home.persist.directories = with cfg; mkIf (enable && allUsers) persist; + users.home.persistApp.directories = with cfg; mkIf (enable && !allUsers) persist; + }; +} diff --git a/home/sway/home.nix b/home/sway/home.nix index bd05fbf6..0f389fe4 100644 --- a/home/sway/home.nix +++ b/home/sway/home.nix @@ -10,6 +10,7 @@ swaynag.enable = true; config = { + defaultWorkspace = "workspace number 1"; modifier = "Mod4"; keybindings = let modifier = config.wayland.windowManager.sway.config.modifier; @@ -17,10 +18,11 @@ XF86MonBrightnessUp = "light -A 5"; XF86MonBrightnessDown = "light -U 5"; - "Control+Alt+l" = "exec ${pkgs.swaylock}/bin/swaylock -c 000000"; + "Control+Alt+l" = "exec swaylock -f --grace 0"; "Print" = "exec ${pkgs.grim}/bin/grim - | ${pkgs.wl-clipboard}/bin/wl-copy"; "${modifier}+Print" = "exec ${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp)\" - | ${pkgs.wl-clipboard}/bin/wl-copy"; "${modifier}+q" = "exec google-chrome-stable"; + "${modifier}+Home" = "exec ${pkgs.pavucontrol}/bin/pavucontrol"; }; bars = [ { @@ -37,14 +39,10 @@ # sets transparency colors.background = "00000000"; } ]; - gaps.inner = 12; - gaps.outer = 5; - # dodge the status bar - gaps.bottom = 0; input."*".natural_scroll = "enabled"; input."type:touchpad".tap = "enabled"; - output."*".bg = "${../../share/54345906_p0.jpg} fill"; + output."*".bg = "#000000 solid_color"; }; }; } diff --git a/home/sway/nixos.nix b/home/sway/nixos.nix index f6900dd3..44b3e32c 100644 --- a/home/sway/nixos.nix +++ b/home/sway/nixos.nix @@ -2,9 +2,10 @@ , lib , config , ... }: with lib; let - gui = with config.faucet.gui; ( enable && session ); + gui = with config.global.gpu; enable && session; + nvidia = with config.global.gpu; type == "nvidia" || type == "prime"; in mkIf gui { - services.xserver.displayManager.sessionPackages = [ + services.displayManager.sessionPackages = [ (pkgs.writeTextFile { name = "sway-session"; destination = "/share/wayland-sessions/sway.desktop"; @@ -12,12 +13,25 @@ in mkIf gui { [Desktop Entry] Name=Sway Comment=An i3-compatible Wayland compositor - Exec=sway --unsupported-gpu + Exec=${pkgs.writeTextFile { + name = "sway-wrapper"; + executable = true; + text = '' + #!${pkgs.zsh}/bin/zsh + SHLVL=0 + for profile in ''${(z)NIX_PROFILES}; do + fpath+=($profile/share/zsh/site-functions $profile/share/zsh/$ZSH_VERSION/functions $profile/share/zsh/vendor-completions) + done + exec sway${if nvidia then " --unsupported-gpu" else ""} 2>&1 >> $XDG_CACHE_HOME/sway + ''; + checkPhase = '' + ${pkgs.stdenv.shellDryRun} "$target" + ''; + }} Type=Application ''; } // { providedSessions = [ pkgs.sway.meta.mainProgram ]; }) ]; - security.pam.services.swaylock = { }; programs.light.enable = true; } diff --git a/home/swayidle/home.nix b/home/swayidle/home.nix new file mode 100644 index 00000000..c7d0daef --- /dev/null +++ b/home/swayidle/home.nix @@ -0,0 +1,24 @@ +{ pkgs +, lib +, config +, ... }: with lib; mkIf config.passthrough.gui { + services.swayidle = let + sway = config.wayland.windowManager.sway.package; + swaymsg = "${sway}/bin/swaymsg"; + swaylock = "${config.programs.swaylock.package}/bin/swaylock"; + in { + enable = true; + systemdTarget = "sway-session.target"; + timeouts = [ + { timeout = 600; command = "${swaymsg} 'output * dpms off'"; resumeCommand = "${swaymsg} 'output * dpms on'"; } + ]; + events = [ + { event = "before-sleep"; command = "${swaylock} -f --grace 0"; } + ]; + }; + + # fullscreen as simple idle inhibitor shortcut + wayland.windowManager.sway.config.window.commands = [ + { criteria.shell = ".*"; command = "inhibit_idle fullscreen"; } + ]; +} diff --git a/home/swaylock/home.nix b/home/swaylock/home.nix new file mode 100644 index 00000000..25a72c88 --- /dev/null +++ b/home/swaylock/home.nix @@ -0,0 +1,24 @@ +{ pkgs +, lib +, config +, ... }: with lib; mkIf config.passthrough.gui { + programs.swaylock = { + enable = true; + package = pkgs.swaylock-effects; + settings = { + indicator-caps-lock = true; + font-size = 20; + ignore-empty-password = true; + show-failed-attempts = true; + color = mkDefault "#00000000"; + + # Ring + indicator-radius = 115; + + # Swaylock-effects specific settings + clock = true; + timestr = "%r"; + grace = 2; + }; + }; +} diff --git a/home/swaylock/nixos.nix b/home/swaylock/nixos.nix new file mode 100644 index 00000000..3891904b --- /dev/null +++ b/home/swaylock/nixos.nix @@ -0,0 +1,8 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + gui = with config.global.gpu; enable && session; +in mkIf gui { + security.pam.services.swaylock = { }; +} diff --git a/home/user.nix b/home/user.nix index 82342a93..6f61a442 100644 --- a/home/user.nix +++ b/home/user.nix @@ -5,11 +5,13 @@ description = "Koishi"; admin = true; sshLogin = true; + picture = ./picture/koishi.png; }; staging = { uid = 1000; description = "Staging Environment"; + picture = ./picture/staging.png; }; root.uid = 0; diff --git a/home/util/home.nix b/home/util/home.nix index 3d37a054..2c377008 100644 --- a/home/util/home.nix +++ b/home/util/home.nix @@ -1,13 +1,33 @@ { pkgs , lib , config -, ... }: with lib; { +, ... }: with lib; let + cfg = config.passthrough.util; +in { home.packages = with pkgs; [ - pciutils usbutils lsscsi + pv file wget e2fsprogs + ] ++ optionals (!cfg.minimal) [ + tio mbuffer sedutil + lsscsi zip unzip + nix-index dnsutils whois + pciutils usbutils nvme-cli ] ++ optionals config.passthrough.gui [ + gtk-engine-murrine + gnome-themes-extra + mission-planner - betaflight-configurator inav-configurator inav-blackbox-tools + (blhelisuite32.override { workdir = "${config.home.homeDirectory}/.blhelisuite32"; }) + ] ++ optionals (config.passthrough.gui && !config.passthrough.flatpak.enable) [ + xfce.thunar gimp + jellyfin-media-player + betaflight-configurator + expresslrs-configurator + ]; + + wayland.windowManager.sway.config.window.commands = mkIf config.passthrough.gui [ + { criteria.class = "BLHeliSuite32xl"; command = "floating enable"; } + { criteria.app_id = "thunar"; command = "floating enable"; } ]; } diff --git a/home/util/nixos.nix b/home/util/nixos.nix index c93ca39a..990d9e61 100644 --- a/home/util/nixos.nix +++ b/home/util/nixos.nix @@ -2,12 +2,43 @@ , lib , config , ... }: with lib; let - gui = with config.faucet.gui; ( enable && session ); + cfg = config.home.util; + gui = with config.global.gpu; enable && session; in { - users.home.persist.directories = [ ] ++ - optionals gui [ - ".local/share/Mission Planner" - ".config/betaflight-configurator" - ".config/inav-configurator" - ]; + options.home.util = { + minimal = mkEnableOption "minimal system environment with less packages"; + }; + + config = { + users.homeModules = [ + # this module passes util configuration to home-manager + { passthrough.util = cfg; } + # this module passes flatpak configuration to home-manager + { passthrough.flatpak = config.global.flatpak; } + ]; + + programs.zsh.enable = true; + environment.shells = singleton pkgs.zsh; + + users.home.persist.directories = [ ] ++ + optionals (!cfg.minimal) [ + ".cache/nix-index" + ] ++ + optionals gui [ + # mission-planner + ".local/share/Mission Planner" + # inav-configurator + ".config/inav-configurator" + ] ++ + optionals (gui && !config.global.flatpak.enable) [ + # jellyfin-media-player + ".config/jellyfin.org" + ".local/share/jellyfinmediaplayer" + ".local/share/Jellyfin Media Player" + # expresslrs-configurator + ".config/ExpressLRS Configurator" + # betaflight-configurator + ".config/betaflight-configurator" + ]; + }; } diff --git a/home/virt-manager/home.nix b/home/virt-manager/home.nix index e9a4ca71..b5bf3e67 100644 --- a/home/virt-manager/home.nix +++ b/home/virt-manager/home.nix @@ -49,4 +49,10 @@ }; "${p}/conns/qemu:system".pretty-name = "KVM"; }; + + # floating other than main window + wayland.windowManager.sway.config.window.commands = [ + { criteria.app_id = "virt-manager"; command = "floating enable"; } + { criteria.app_id = "virt-manager"; criteria.title = "Virtual Machine Manager"; command = "floating disable"; } + ]; } diff --git a/home/virt-manager/nixos.nix b/home/virt-manager/nixos.nix index 30ff9fbf..fc865a12 100644 --- a/home/virt-manager/nixos.nix +++ b/home/virt-manager/nixos.nix @@ -2,7 +2,7 @@ , lib , config , ... }: with lib; let - gui = with config.faucet.gui; ( enable && session ); + gui = with config.global.gpu; enable && session; in mkIf gui { programs.virt-manager.enable = true; } diff --git a/home/vscode/home.nix b/home/vscode/home.nix new file mode 100644 index 00000000..b6ab9c29 --- /dev/null +++ b/home/vscode/home.nix @@ -0,0 +1,24 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.passthrough.vscode; + theme = config.passthrough.catppuccin.enable; +in mkIf cfg.enable { + programs.vscode = { + enable = true; + mutableExtensionsDir = false; + enableUpdateCheck = false; + enableExtensionUpdateCheck = false; + package = pkgs.vscodium; + extensions = with pkgs.vscode-extensions; [ + catppuccin.catppuccin-vsc catppuccin.catppuccin-vsc-icons + bbenoist.nix golang.go rust-lang.rust-analyzer + ]; + userSettings = { + "workbench.colorTheme" = mkIf theme "Catppuccin Mocha"; + "workbench.iconTheme" = mkIf theme "catppuccin-mocha"; + "[nix]"."editor.tabSize" = 2; + }; + }; +} diff --git a/home/vscode/nixos.nix b/home/vscode/nixos.nix new file mode 100644 index 00000000..7ecffe47 --- /dev/null +++ b/home/vscode/nixos.nix @@ -0,0 +1,19 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.home.vscode; +in { + options.home.vscode = { + enable = mkEnableOption "vscode text editor"; + }; + + config = { + users.homeModules = [ + # this module passes vscode configuration to home-manager + { passthrough.vscode = cfg; } + ]; + + users.home.persist.directories = mkIf cfg.enable [ ".config/VSCodium" ]; + }; +} diff --git a/package/blhelisuite32/default.nix b/package/blhelisuite32/default.nix new file mode 100644 index 00000000..65f3b509 --- /dev/null +++ b/package/blhelisuite32/default.nix @@ -0,0 +1,92 @@ +{ lib +, libicns +, p7zip +, fetchzip +, stdenvNoCC +, makeDesktopItem +, buildFHSUserEnv +, workdir ? "/tmp/blhelisuite32" }: let + name = "BLHeliSuite32"; + pname = "blhelisuite32"; + version = "32.10"; + suffix = "1044"; + + dist = fetchzip { + name = "${pname}-dist"; + url = "https://github.com/bitdump/BLHeli/releases/download/Rev${version}/${name}xLinux64_${suffix}.zip"; + hash = "sha256-y4S824s9Ipxb1M1IeD6Lo6k7hmm8CEmPflvhaqZz+84="; + }; + + desktopItem = makeDesktopItem { + name = pname; + exec = pname; + icon = pname; + comment = "This Application may flash and configure BLHeli_32 based ESCs"; + desktopName = name; + genericName = "BLHeli for brushless ESC firmware"; + }; + + icons = stdenvNoCC.mkDerivation { + pname = "${pname}-icons"; + inherit version; + src = fetchzip { + name = "${pname}-macos"; + url = "https://github.com/bitdump/BLHeli/releases/download/Rev${version}/${name}xm_MacOS64_${suffix}.zip"; + hash = "sha256-StRnrVI8p51vNsTMO1LtaZvENbG7XZ1V/mKHe4pO7kU="; + }; + + nativeBuildInputs = [ libicns p7zip ]; + + configurePhase = '' + 7z x *.dmg + ''; + + buildPhase = '' + icns2png -x ${name}xm_MacOS64_${suffix}/${name}xm.app/Contents/Resources/${name}xm.icns + ''; + + installPhase = '' + mkdir -p "$out" + cp -r ${name}*.png "$out" + ''; + }; + + linked = stdenvNoCC.mkDerivation { + inherit pname version; + phases = [ "unpackPhase" "patchPhase" "installPhase" ]; + src = dist; + + installPhase = '' + cp -r . "$out" + + # BLHeliSuite32 tries to write next to its binary + ln -s ${workdir}/settings $out/Settings + ln -s ${workdir}/music $out/Music + ''; + }; +in buildFHSUserEnv { + inherit pname version; + targetPkgs = pkgs: (with pkgs; [ + glib libGL curl + libgcc gtk3 + zlib systemdLibs + ]); + extraInstallCommands = let + mkIconScale = scale: + "install -m 444 -D ${icons}/${name}xm_${scale}x${scale}x32.png $out/share/icons/hicolor/${scale}x${scale}/apps/${pname}.png"; + in '' + ${mkIconScale "16"} + ${mkIconScale "32"} + ${mkIconScale "64"} + ${mkIconScale "128"} + ${mkIconScale "256"} + ${mkIconScale "512"} + ${mkIconScale "1024"} + cp -r ${desktopItem}/share/applications $out/share/ + ''; + + runScript = "sh -c '" + + "mkdir -p ${workdir}/settings && " + + "mkdir -p ${workdir}/music && " + + "exec ${linked}/${name}xl'"; +} diff --git a/package/default.nix b/package/default.nix new file mode 100644 index 00000000..3f21f9d6 --- /dev/null +++ b/package/default.nix @@ -0,0 +1,19 @@ +{ lib, ... }: { + nixpkgs.overlays = [ (final: prev: + builtins.listToAttrs (map (pkg: { + name = pkg; + value = let + argsPath = ./${pkg}/args.nix; + in final.callPackage ./${pkg} + (if (builtins.pathExists argsPath) then + (import argsPath final) + else { }); + }) (builtins.attrNames (builtins.readDir ./.))) + ) ]; + + imports = lib.pipe ./. [ + builtins.readDir + (lib.filterAttrs (n: ty: ty == "directory" && builtins.pathExists ./${n}/nixos.nix)) + (lib.mapAttrsToList (n: _: ./${n}/nixos.nix)) + ]; +} diff --git a/package/expresslrs-configurator/default.nix b/package/expresslrs-configurator/default.nix new file mode 100644 index 00000000..affbbe58 --- /dev/null +++ b/package/expresslrs-configurator/default.nix @@ -0,0 +1,51 @@ +{ lib +, fetchzip +, makeDesktopItem +, imagemagick +, buildFHSUserEnv }: let + name = "ExpressLRS-Configurator"; + pname = "expresslrs-configurator"; + version = "1.7.2"; + + dist = fetchzip { + name = "${pname}-dist"; + url = "https://github.com/ExpressLRS/ExpressLRS-Configurator/releases/download/v${version}/${pname}-${version}.zip"; + stripRoot = false; + hash = "sha256-pXmJ420HeJaMjAZCzlIriuFrTK5xabxTrSy3PDVisgU="; + }; + + desktopItem = makeDesktopItem { + name = pname; + exec = pname; + icon = pname; + comment = "Cross platform configuration & build tool for the ExpressLRS radio link"; + desktopName = "ExpressLRS Configurator"; + genericName = "radio link configuration & build tool"; + }; +in buildFHSUserEnv { + inherit pname version; + # copied from chromium deps + targetPkgs = pkgs: (with pkgs; [ + glib fontconfig freetype pango cairo xorg.libX11 xorg.libXi atk nss nspr + xorg.libXcursor xorg.libXext xorg.libXfixes xorg.libXrender + xorg.libXScrnSaver xorg.libXcomposite xorg.libxcb + alsa-lib xorg.libXdamage xorg.libXtst xorg.libXrandr xorg.libxshmfence expat cups + dbus gdk-pixbuf gcc-unwrapped.lib + systemd + libexif pciutils + liberation_ttf curl util-linux wget + flac harfbuzz icu libpng libopus snappy speechd + bzip2 libcap at-spi2-atk at-spi2-core + libkrb5 libdrm libglvnd mesa coreutils + libxkbcommon pipewire wayland + libva gtk3 gtk4 + ]); + extraInstallCommands = '' + mkdir -p $out/share + ${imagemagick}/bin/convert ${dist}/resources/assets/icon.png -resize 128x128 icon-128.png + install -m 444 -D icon-128.png $out/share/icons/hicolor/128x128/apps/${pname}.png + cp -r ${desktopItem}/share/applications $out/share/ + ''; + + runScript = "${dist}/expresslrs-configurator"; +} diff --git a/package/photoview/default.nix b/package/photoview/default.nix new file mode 100644 index 00000000..be549841 --- /dev/null +++ b/package/photoview/default.nix @@ -0,0 +1,74 @@ +{ lib +, pkg-config +, libjpeg_turbo +, libheif +, lapack +, dlib +, blas +, ffmpeg +, exiftool +, darktable +, fetchFromGitHub +, buildGoModule +, buildNpmPackage +, runCommandLocal +, makeWrapper }: buildGoModule rec { + pname = "photoview"; + version = "2.4.0"; + + source = fetchFromGitHub { + name = "${pname}-src"; + owner = pname; + repo = pname; + rev = "refs/tags/v${version}"; + hash = "sha256-ZfvBdQlyqONsrviZGL22Kt+AiPaVWwdoREDUrHDYyIs="; + }; + + src = source + "/api"; + vendorHash = "sha256-Tn4OxSV41s/4n2Q3teJRJNc39s6eKW4xE9wW/CIR5Fg="; + nativeBuildInputs = [ + pkg-config + makeWrapper + ]; + buildInputs = [ + libjpeg_turbo + libheif + lapack + dlib + blas + ]; + + ui = buildNpmPackage { + pname = "${pname}-ui"; + inherit version; + src = source + "/ui"; + + #REACT_APP_API_ENDPOINT = ""; + REACT_APP_BUILD_VERSION = version; + REACT_APP_BUILD_DATE = builtins.readFile (runCommandLocal "date" { } "date -uI > $out" ); + REACT_APP_BUILD_COMMIT_SHA = "nix"; + + npmDepsHash = "sha256-wUbfq+7SuJUBxfy9TxHVda8A0g4mmYCbzJT64XBN2mI="; + }; + + postInstall = '' + mkdir -p "$out/lib/${pname}" + + mv "$out/bin/api" "$out/bin/${pname}" + wrapProgram $out/bin/${pname} \ + --prefix PATH ":" "${lib.makeBinPath [ + ffmpeg + exiftool + darktable + ]}" \ + --set PHOTOVIEW_SERVE_UI 1 \ + --set PHOTOVIEW_UI_PATH "$out/lib/${pname}/ui" + cp -r \ + "${src}/data/" \ + "$out/lib/${pname}/data/" + + cp -r \ + "${ui}/lib/node_modules/photoview-ui/dist" \ + "$out/lib/${pname}/ui" + ''; +} diff --git a/package/photoview/nixos.nix b/package/photoview/nixos.nix new file mode 100644 index 00000000..08c7d167 --- /dev/null +++ b/package/photoview/nixos.nix @@ -0,0 +1,148 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.services.photoview; +in { + options.services.photoview = { + enable = mkEnableOption "photoview server"; + + package = mkOption { + type = with types; package; + default = pkgs.photoview; + description = "photoview package"; + }; + + user = mkOption { + type = with types; str; + default = "photoview"; + description = "user under which photoview runs"; + }; + group = mkOption { + type = with types; str; + default = "photoview"; + description = "group under which photoview runs"; + }; + + listen = { + host = mkOption { + type = with types; str; + default = "127.0.0.1"; + description = "host to listen on"; + }; + port = mkOption { + type = with types; port; + default = 8000; + description = "port to listen on"; + }; + }; + + database = { + driver = mkOption { + type = with types; enum [ "sqlite" "mysql" "postgres" ]; + default = "sqlite"; + description = "database driver"; + }; + string = mkOption { + type = with types; str; + description = "database connection string"; + }; + }; + + stateDir = mkOption { + type = with types; str; + default = "/var/lib/photoview"; + description = "path to photoview state directory"; + }; + + cacheDir = mkOption { + type = with types; str; + default = "${cfg.stateDir}/media_cache"; + description = "path to photoview media cache"; + }; + + secrets = mkOption { + type = with types; nullOr str; + default = null; + description = "path to secrets environment file"; + example = '' + MAPBOX_TOKEN= + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.photoview = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "Photoview - Photo gallery for self-hosted personal servers"; + environment = with cfg; { + PHOTOVIEW_LISTEN_IP = listen.host; + PHOTOVIEW_LISTEN_PORT = toString listen.port; + PHOTOVIEW_MEDIA_CACHE = cacheDir; + PHOTOVIEW_DATABASE_DRIVER = database.driver; + "PHOTOVIEW_${toUpper database.driver}_URL" = database.string; + }; + serviceConfig = { + ExecStartPre = pkgs.writeShellScript "photoview-pre" '' + rm -f "${cfg.stateDir}/data" + ln -s "${cfg.package}/lib/photoview/data" "${cfg.stateDir}/data" + ''; + ExecStart = "${cfg.package}/bin/photoview"; + WorkingDirectory = cfg.stateDir; + User = cfg.user; + Group = cfg.group; + EnvironmentFile = mkIf (cfg.secrets != null) cfg.secrets; + + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProcSubset = "all"; + ProtectSystem = "strict"; + RemoveIPC = true; + ReadWritePaths = [ cfg.stateDir ]; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + UMask = "0077"; + }; + }; + + users.users = mkIf (cfg.user == "photoview") { + photoview = { + description = "photoview service account"; + group = cfg.group; + uid = config.ids.uids.photoview; + }; + }; + + users.groups = mkIf (cfg.group == "photoview") { + photoview.gid = config.ids.gids.photoview; + }; + + ids.uids.photoview = 287; + ids.gids.photoview = 287; + }; +} diff --git a/package/tubesync/database-local-socket.patch b/package/tubesync/database-local-socket.patch new file mode 100644 index 00000000..b422f3ed --- /dev/null +++ b/package/tubesync/database-local-socket.patch @@ -0,0 +1,27 @@ +diff --git a/tubesync/tubesync/local_settings.py.container b/tubesync/tubesync/local_settings.py.container +index a7a07ab..7564138 100644 +--- a/tubesync/tubesync/local_settings.py.container ++++ b/tubesync/tubesync/local_settings.py.container +@@ -34,14 +34,20 @@ if database_connection_env: + database_dict = parse_database_connection_string(database_connection_env) + + ++database_host = database_dict.get("HOST") ++if database_host == "localhost": ++ database_dict["HOST"] = None ++ database_dict["PASSWORD"] = None ++ ++ + if database_dict: + log.info(f'Using database connection: {database_dict["ENGINE"]}://' +- f'{database_dict["USER"]}:[hidden]@{database_dict["HOST"]}:' ++ f'{database_dict["USER"]}:[hidden]@{database_host}:' + f'{database_dict["PORT"]}/{database_dict["NAME"]}') + DATABASES = { + 'default': database_dict, + } +- DATABASE_CONNECTION_STR = (f'{database_dict["DRIVER"]} at "{database_dict["HOST"]}:' ++ DATABASE_CONNECTION_STR = (f'{database_dict["DRIVER"]} at "{database_host}:' + f'{database_dict["PORT"]}" database ' + f'"{database_dict["NAME"]}"') + else: diff --git a/package/tubesync/default.nix b/package/tubesync/default.nix new file mode 100644 index 00000000..5318f882 --- /dev/null +++ b/package/tubesync/default.nix @@ -0,0 +1,139 @@ +{ lib +, stdenvNoCC +, ffmpeg +, rtmpdump +, atomicparsley +, callPackage +, fetchFromGitHub +, fetchPypi +, fetchpatch +, makeWrapper +, python3Packages }: with python3Packages; let + mkPypi = pname: version: src: format: buildPythonPackage { + inherit pname version src format; + doCheck = false; + nativeBuildInputs = [ setuptools ]; + }; + + mkPypi' = pname: version: hash: format: mkPypi pname version + (fetchPypi { + inherit pname version hash; + }) format; + + mkPypi'' = pname: version: hash: mkPypi' pname version hash + "setuptools"; + + django-compat = mkPypi'' "django-compat" "1.0.15" "sha256-OsmjvtxWuTZdnrJBvFFX0MGTdpv5lfmnjcG8JOfCMxs="; + django-appconf = mkPypi'' "django-appconf" "1.0.6" "sha256-z+h+qCfE7gS5pw+rkLhtcEywLymB+J2oQjyw+r+I778="; + django-basicauth = mkPypi'' "django-basicauth" "0.5.3" "sha256-FenjZvaY9TxxseeU2v6gYPmQoqxVa65rczDdJTJKCRw="; + django-sass-processor = mkPypi'' "django-sass-processor" "1.4" "sha256-sX850H06dRCuxCXBkZN+IwUC3ut8pr9pUKGt+LS3wcM="; + django-background-tasks = mkPypi'' "django-background-tasks" "1.2.5" "sha256-4bGejUlaJ2ydZMWh/4tBEy910vWORb5xt4ZQ2tWa+d4="; + + django-compressor = let + pname = "django-compressor"; + version = "4.4"; + in mkPypi pname version (fetchFromGitHub { + owner = pname; + repo = pname; + rev = "refs/tags/${version}"; + hash = "sha256-c9uS5Z077b23Aj8jV30XNsshbEfrLRX3ozXasitQ6UQ="; + }) "setuptools"; + + app = buildPythonApplication rec { + pname = "tubesync"; + version = "0.13.6"; + format = "other"; + + src = fetchFromGitHub { + name = "${pname}-src"; + owner = "meeb"; + repo = pname; + rev = "v${version}"; + hash = "sha256-5l1HkMoTn99rNeeK5u+KoFejTFi2LfLyt8upq7xwrj0="; + }; + + patches = [ + ./gunicorn-env.patch + ./state-dir-env.patch + ./database-local-socket.patch + ./ensure-fragments.patch + ]; + + propagatedBuildInputs = [ + yt-dlp requests + httptools pillow + gunicorn whitenoise + psycopg2 mysqlclient + redis hiredis + libsass six + ] ++ [ + django_3 + django-compat + django-appconf + django-compressor + django-basicauth + django-sass-processor + django-background-tasks + ]; + + buildPhase = '' + mv "tubesync/tubesync/local_settings.py.container" "tubesync/tubesync/local_settings.py" + rm "tubesync/tubesync/local_settings.py.example" + rm "tubesync/tubesync/local_settings.py.container.orig" + + python3 tubesync/manage.py compilescss + python3 tubesync/manage.py collectstatic --no-input + ''; + + installPhase = '' + mkdir -p "$out" + cp -r "tubesync" "$out/app" + + FFMPEG_VERSION=$(${ffmpegFix}/bin/ffmpeg -version | head -n 1 | awk '{ print $3 }') + echo "ffmpeg_version = '$FFMPEG_VERSION'" >> "$out/app/common/third_party_versions.py" + + mv "$out/app/static" "$out/static" + ln -s "/tmp/tubesync/static" "$out/app/static" + ''; + }; + + ffmpegFix = ffmpeg.overrideAttrs (finalAttrs: previousAttrs: { + patches = previousAttrs.patches ++ [ (fetchpatch { + name = "avformat-webvttdec-skip-style-region.patch"; + url = "https://git.ffmpeg.org/gitweb/ffmpeg.git/patch/51d303e20cbb0874172f50b5172c515a973587d4"; + hash = "sha256-dxCjKZUz2H2QyktsprkzyfNp5aDG6X8deF4ZGdvhvfk="; + }) ]; + }); +in stdenvNoCC.mkDerivation { + pname = "${app.pname}-wrapped"; + inherit (app) version; + + nativeBuildInputs = [ makeWrapper ]; + unpackPhase = "true"; + installPhase = let + prefix = ''--prefix PATH : "${lib.makeBinPath [ + ffmpegFix rtmpdump atomicparsley + ]}"''; + in '' + mkdir -p "$out/bin" + + makeWrapper "${python}/bin/python3" "$out/bin/tubesync-worker" \ + --chdir ${app}/app --add-flags \ + "${app}/app/manage.py process_tasks" \ + ${prefix} + + makeWrapper "${gunicorn}/bin/gunicorn" "$out/bin/tubesync-gunicorn" \ + --chdir ${app}/app --add-flags \ + "-c ${app}/app/tubesync/gunicorn.py --capture-output tubesync.wsgi:application" \ + ${prefix} + + makeWrapper "${python}/bin/python3" "$out/bin/tubesync-migrate" \ + --chdir "${app}/app" --add-flags \ + "${app}/app/manage.py migrate" + ''; + + passthru = { + inherit app; + pythonPath = makePythonPath app.propagatedBuildInputs; + }; +} diff --git a/package/tubesync/ensure-fragments.patch b/package/tubesync/ensure-fragments.patch new file mode 100644 index 00000000..f25534ea --- /dev/null +++ b/package/tubesync/ensure-fragments.patch @@ -0,0 +1,15 @@ +diff --git a/tubesync/sync/youtube.py b/tubesync/sync/youtube.py +index 4ac6e83..192b031 100644 +--- a/tubesync/sync/youtube.py ++++ b/tubesync/sync/youtube.py +@@ -119,6 +119,10 @@ def download_media(url, media_format, extension, output_file, info_json, + 'writesubtitles': write_subtitles, + 'writeautomaticsub': auto_subtitles, + 'subtitleslangs': sub_langs.split(','), ++ 'progress_with_newline': True, ++ 'fragment_retries': 65536, ++ 'skip_unavailable_fragments': False, ++ 'continue_dl': False, + } + + sbopt = { diff --git a/package/tubesync/gunicorn-env.patch b/package/tubesync/gunicorn-env.patch new file mode 100644 index 00000000..d1ee0965 --- /dev/null +++ b/package/tubesync/gunicorn-env.patch @@ -0,0 +1,19 @@ +diff --git a/tubesync/tubesync/gunicorn.py b/tubesync/tubesync/gunicorn.py +index d59c138..341af25 100644 +--- a/tubesync/tubesync/gunicorn.py ++++ b/tubesync/tubesync/gunicorn.py +@@ -23,11 +23,10 @@ def get_bind(): + + workers = get_num_workers() + timeout = 30 +-chdir = '/app' + daemon = False +-pidfile = '/run/app/gunicorn.pid' +-user = 'app' +-group = 'app' ++pidfile = os.getenv('GUNICORN_PID_FILE', '/var/run/tubesync/gunicorn.pid') ++user = os.getenv('GUNICORN_USER', 'tubesync') ++group = os.getenv('GUNICORN_GROUP', 'tubesync') + loglevel = 'info' + errorlog = '-' + accesslog = '/dev/null' # Access logs are printed to stdout from nginx diff --git a/package/tubesync/nixos.nix b/package/tubesync/nixos.nix new file mode 100644 index 00000000..383503a9 --- /dev/null +++ b/package/tubesync/nixos.nix @@ -0,0 +1,188 @@ +{ pkgs +, lib +, config +, ... }: with lib; let + cfg = config.services.tubesync; +in { + options.services.tubesync = { + enable = mkEnableOption "tubesync stack"; + debug = mkEnableOption "debug logging"; + package = mkOption { + type = with types; package; + default = pkgs.tubesync; + description = "tubesync launcher package"; + }; + + workers = mkOption { + type = with types; int; + default = 1; + description = "maximum amount of concurrent workers"; + }; + + user = mkOption { + type = with types; str; + default = "tubesync"; + description = "user under which tubesync runs"; + }; + group = mkOption { + type = with types; str; + default = "tubesync"; + description = "group under which tubesync runs"; + }; + + listen = { + host = mkOption { + type = with types; str; + default = "127.0.0.1"; + description = "host to listen on"; + }; + port = mkOption { + type = with types; port; + default = 8080; + description = "port to listen on"; + }; + }; + + stateDir = mkOption { + type = with types; str; + default = "/var/lib/tubesync"; + description = "path to tubesync state storage directory"; + }; + + dataDir = mkOption { + type = with types; str; + default = "${cfg.stateDir}/downloads"; + description = "path to tubesync video downloads"; + }; + + database = mkOption { + type = with types; str; + default = "postgresql://tubesync:@localhost:5432/tubesync"; + description = "database connection string"; + }; + }; + + config = mkIf cfg.enable { + systemd.services = let + env = { + GUNICORN_PID_FILE = "${cfg.stateDir}/run/gunicorn.pid"; + GUNICORN_USER = cfg.user; + GUNICORN_GROUP = cfg.group; + DATABASE_CONNECTION = cfg.database; + CONFIG_BASE_DIR = cfg.stateDir; + DOWNLOADS_BASE_DIR = cfg.dataDir; + TUBESYNC_DEBUG = mkIf cfg.debug "True"; + TUBESYNC_WORKERS = toString cfg.workers; + + PYTHONPATH = cfg.package.pythonPath; + REDIS_CONNECTION = "redis+socket://" + + "${cfg.stateDir}/run/redis.sock"; + }; + + base = description: { + description = "tubesync: ${description}"; + wantedBy = [ "multi-user.target" ]; + environment = env; + path = [ cfg.package ]; + serviceConfig = { + WorkingDirectory = cfg.stateDir; + User = cfg.user; + Group = cfg.group; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateUsers = false; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProcSubset = "all"; + ProtectSystem = "strict"; + RemoveIPC = true; + ReadWritePaths = with cfg; [ stateDir dataDir ]; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" + "AF_UNIX" + ]; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "@chown" + ]; + #UMask = "0077"; + }; + }; + + base' = description: (base description) // { + after = [ "tubesync.service" ]; + partOf = [ "tubesync.service" ]; + }; + in { + tubesync = recursiveUpdate (base "gunicorn") ({ + after = [ "network.target" ]; + serviceConfig = { + PIDFile = env.GUNICORN_PID_FILE; + ExecStartPre = pkgs.writeShellScript "tubesync-setup" '' + set -xe + tubesync-migrate + mkdir -p "${cfg.stateDir}/run" + + mkdir -p "/tmp/tubesync" + cp -r "${cfg.package.app}/static/." "/tmp/tubesync/static" + chmod +w -R "/tmp/tubesync/static" + ''; + ExecStart = "${cfg.package}/bin/tubesync-gunicorn"; + ExecReload = "/usr/bin/env kill -s HUP $MAINPID"; + ExecStop = "/usr/bin/env kill -s TERM $MAINPID"; + ExecStopPost = pkgs.writeShellScript "tubesync-cleanup" '' + rm -f "$GUNICORN_PID_FILE" + rm -rf "/tmp/tubesync" + ''; + }; + }); + + tubesync-worker = recursiveUpdate (base' "worker") ({ + serviceConfig.ExecStart = "${cfg.package}/bin/tubesync-worker"; + }); + + # allow binding to unix socket + redis-tubesync-celery.serviceConfig.ReadWritePaths = [ "${cfg.stateDir}/run" ]; + }; + + services.redis.servers.tubesync-celery = { + enable = true; + inherit (cfg) user; + unixSocket = "${cfg.stateDir}/run/redis.sock"; + save = [ ]; + }; + + users.users = mkIf (cfg.user == "tubesync") { + tubesync = { + description = "tubesync service account"; + group = cfg.group; + uid = config.ids.uids.tubesync; + }; + }; + + users.groups = mkIf (cfg.group == "tubesync") { + tubesync.gid = config.ids.gids.tubesync; + }; + + ids.uids.tubesync = 101; + ids.gids.tubesync = 101; + + # package is local-only, will allow this for now + nixpkgs.config.permittedInsecurePackages = [ + "python3.12-django-3.2.25" + ]; + }; +} diff --git a/package/tubesync/state-dir-env.patch b/package/tubesync/state-dir-env.patch new file mode 100644 index 00000000..f7bb1d2d --- /dev/null +++ b/package/tubesync/state-dir-env.patch @@ -0,0 +1,16 @@ +diff --git a/tubesync/tubesync/local_settings.py.container b/tubesync/tubesync/local_settings.py.container +index a7a07ab..9207c7f 100644 +--- a/tubesync/tubesync/local_settings.py.container ++++ b/tubesync/tubesync/local_settings.py.container +@@ -6,9 +6,8 @@ from common.utils import parse_database_connection_string + + + BASE_DIR = Path(__file__).resolve().parent.parent +-ROOT_DIR = Path('/') +-CONFIG_BASE_DIR = ROOT_DIR / 'config' +-DOWNLOADS_BASE_DIR = ROOT_DIR / 'downloads' ++CONFIG_BASE_DIR = Path(os.getenv('CONFIG_BASE_DIR', "/var/lib/tubesync")) ++DOWNLOADS_BASE_DIR = Path(os.getenv('DOWNLOADS_BASE_DIR', f"{CONFIG_BASE_DIR}/downloads")) + DJANGO_URL_PREFIX = os.getenv('DJANGO_URL_PREFIX', None) + STATIC_URL = str(os.getenv('DJANGO_STATIC_URL', '/static/')) + if DJANGO_URL_PREFIX and STATIC_URL: diff --git a/share/catppuccin-nix.png b/share/catppuccin-nix.png new file mode 100644 index 00000000..7b5094e1 Binary files /dev/null and b/share/catppuccin-nix.png differ diff --git a/share/catppuccin.png b/share/catppuccin.png new file mode 100644 index 00000000..cae96bd5 Binary files /dev/null and b/share/catppuccin.png differ diff --git a/spec/chireiden/default.nix b/spec/chireiden/default.nix index 7fa774e4..f59fbf08 100644 --- a/spec/chireiden/default.nix +++ b/spec/chireiden/default.nix @@ -1,26 +1,44 @@ -{ - faucet = { +{ pkgs, ... }: { + global = { id = "5d3c16fe58444e12ad621600039f10af"; fs.esp.uuid = "32A5-6257"; fs.type = "xfs"; fs.store = "c0408dd5-dfc1-42e3-b925-2f32407d320a"; - fs.extPersist.enable = true; - fs.extPersist.device = "/dev/disk/by-uuid/af2b8ec5-62d8-4b14-8ba8-083cc4fb33f4"; - fs.extPersist.fsType = "btrfs"; - fs.extPersist.options = [ "noatime" "compress=zstd" ]; + fs.external.enable = true; + fs.external.device = "/dev/disk/by-uuid/af2b8ec5-62d8-4b14-8ba8-083cc4fb33f4"; + fs.external.fsType = "btrfs"; + fs.external.options = [ "noatime" "compress=zstd" ]; fs.cryptsetup.enable = true; fs.cryptsetup.uuids.nix = "3905f87f-7b11-49ef-8ddf-f4202c555ffd"; fs.cryptsetup.uuids.persist = "b2322f01-1c44-433f-a995-4ddce5845cf9"; boot.lanzaboote = true; - gui.enable = true; - gui.type = "prime"; + gpu.enable = true; + gpu.type = "prime"; + android.enable = true; }; + home = { + gnome.enable = true; + vscode.enable = true; + libreoffice.enable = true; + minecraft.enable = true; + steam.enable = true; + }; + + powerManagement.enable = true; + powerManagement.cpuFreqGovernor = "powersave"; + powerManagement.resumeCommands = '' + # trackpad driver resume workaround + ${pkgs.kmod}/bin/modprobe -r psmouse + ${pkgs.kmod}/bin/modprobe psmouse + ''; + hardware.nvidia.prime = { intelBusId = "PCI:0:2:0"; nvidiaBusId = "PCI:5:0:0"; }; + boot.plymouth.catppuccin.enable = false; hardware.enableRedistributableFirmware = true; boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod" ]; boot.initrd.kernelModules = [ ]; diff --git a/spec/default.nix b/spec/default.nix index 15dbcba6..021aa4c1 100644 --- a/spec/default.nix +++ b/spec/default.nix @@ -4,13 +4,15 @@ system = "x86_64-linux"; specialArgs = inputs // { inherit inputs; }; modules = [ - ../faucet + ../global ../home/profile.nix ../home/user.nix + ../package ./constant.nix ./channel.nix impermanence.nixosModules.impermanence home-manager.nixosModules.home-manager + catppuccin.nixosModules.catppuccin { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; diff --git a/spec/eientei/acme.nix b/spec/eientei/acme.nix new file mode 100644 index 00000000..7fec425c --- /dev/null +++ b/spec/eientei/acme.nix @@ -0,0 +1,11 @@ +{ + security.acme.certs = let + cloudflare = { + dnsProvider = "cloudflare"; + credentialsFile = "/nix/persist/secret/cloudflare"; + }; + in { + "514fpv.io" = cloudflare; + ".514fpv.io" = cloudflare // { domain = "*.514fpv.io"; }; + }; +} diff --git a/spec/eientei/coturn.nix b/spec/eientei/coturn.nix new file mode 100644 index 00000000..6f633083 --- /dev/null +++ b/spec/eientei/coturn.nix @@ -0,0 +1,59 @@ +{ config +, ... }: let + host = "514fpv.io"; +in { + services.coturn = rec { + enable = true; + no-cli = true; + no-tcp-relay = true; + min-port = 49000; + max-port = 50000; + use-auth-secret = true; + static-auth-secret-file = "/nix/persist/service/coturn/secret"; + realm = "edge.${host}"; + cert = "${config.security.acme.certs.".${host}".directory}/full.pem"; + pkey = "${config.security.acme.certs.".${host}".directory}/key.pem"; + extraConfig = '' + # for debugging + #verbose + # ban private IP ranges + no-multicast-peers + denied-peer-ip=0.0.0.0-0.255.255.255 + denied-peer-ip=10.0.0.0-10.255.255.255 + denied-peer-ip=100.64.0.0-100.127.255.255 + denied-peer-ip=127.0.0.0-127.255.255.255 + denied-peer-ip=169.254.0.0-169.254.255.255 + denied-peer-ip=172.16.0.0-172.31.255.255 + denied-peer-ip=192.0.0.0-192.0.0.255 + denied-peer-ip=192.0.2.0-192.0.2.255 + denied-peer-ip=192.88.99.0-192.88.99.255 + denied-peer-ip=192.168.0.0-192.168.255.255 + denied-peer-ip=198.18.0.0-198.19.255.255 + denied-peer-ip=198.51.100.0-198.51.100.255 + denied-peer-ip=203.0.113.0-203.0.113.255 + denied-peer-ip=240.0.0.0-255.255.255.255 + denied-peer-ip=::1 + denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff + denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255 + denied-peer-ip=100::-100::ffff:ffff:ffff:ffff + denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff + ''; + }; + + networking.firewall = let + range = with config.services.coturn; [ { + from = min-port; + to = max-port; + } ]; + in { + allowedUDPPortRanges = range; + allowedUDPPorts = [ 3478 5349 ]; + allowedTCPPortRanges = range; + allowedTCPPorts = [ 3478 5349 ]; + }; + + global.fs.zfs.mountpoints."/nix/persist/service/coturn" = "service/coturn"; +} diff --git a/spec/eientei/default.nix b/spec/eientei/default.nix new file mode 100644 index 00000000..b5a098e7 --- /dev/null +++ b/spec/eientei/default.nix @@ -0,0 +1,46 @@ +{ lib +, pkgs +, ... }: { + global = { + id = "ff96c05be13e44f681292205370eed1a"; + auth.openssh.enable = true; + fs.esp.uuid = "C368-7571"; + fs.type = "zfs"; + fs.zfs.alert.secret = "/nix/persist/secret/telegram"; + fs.zfs.split.enable = true; + fs.zfs.split.store = "d9202e56-a14f-4342-acdb-dbae33d680fc"; + fs.zfs.split.secret = "1404c4f1-b890-4cf0-ab8a-26bd81bd2254"; + fs.zfs.replication.enable = true; + fs.zfs.replication.remote = "eientei@archive:archive/backup/koishi/eientei"; + fs.cryptsetup.enable = true; + fs.cryptsetup.allowDiscards = false; + fs.cryptsetup.uuids.secret = "c33c9b18-a280-42d7-8740-3f8d3f60dc43"; + gpu.enable = true; + gpu.type = "intel"; + gpu.session = false; + boot.lanzaboote = true; + boot.memtest = 4; + acme.enable = true; + oci.enable = true; + }; + + services.udev.extraRules = '' + SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="80:61:5f:07:9e:2f", NAME="ix0" + SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="00:e0:4c:68:bb:30", NAME="ss0" + ''; + boot.kernelParams = [ "zfs.zfs_arc_max=17179869184" ]; + + imports = lib.pipe ./. [ + builtins.readDir + (lib.filterAttrs (n: ty: ty == "regular" && n != "default.nix")) + (lib.mapAttrsToList (n: _: ./${n})) + ]; + + hardware.enableRedistributableFirmware = true; + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "mpt3sas" "nvme" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ "i915" ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + environment.systemPackages = with pkgs; [ python3 ]; +} diff --git a/spec/library/forgejo.nix b/spec/eientei/forgejo.nix similarity index 86% rename from spec/library/forgejo.nix rename to spec/eientei/forgejo.nix index d957c9cf..f207d7c3 100644 --- a/spec/library/forgejo.nix +++ b/spec/eientei/forgejo.nix @@ -1,4 +1,5 @@ { pkgs +, config , ...}: let host = "src.514fpv.io"; in { @@ -10,7 +11,7 @@ in { database.createDatabase = true; settings = { server = { - SSH_PORT = 8087; + SSH_PORT = 8086; COOKIE_SECURE = true; LANDING_PAGE = "explore"; DOMAIN = host; @@ -30,4 +31,6 @@ in { addSSL = true; locations."/".proxyPass = "http://unix:/run/forgejo/forgejo.sock"; }; + + global.fs.zfs.mountpoints."/nix/persist/service/forgejo" = "service/forgejo"; } diff --git a/spec/eientei/frigate.nix b/spec/eientei/frigate.nix new file mode 100644 index 00000000..dcdbb2e5 --- /dev/null +++ b/spec/eientei/frigate.nix @@ -0,0 +1,30 @@ +{ pkgs +, config +, ...}: { + # container image used for edgetpu support + virtualisation.oci-containers.containers.frigate = { + extraOptions = [ + "--privileged" + "--shm-size=128mb" + "--device=/dev/bus/usb:/dev/bus/usb" + ]; + image = "ghcr.io/blakeblackshear/frigate:stable"; + volumes = [ + "/etc/localtime:/etc/localtime:ro" + "/nix/persist/service/frigate/config:/config" + "/nix/persist/service/frigate/data:/media/frigate" + #"/tmp/frigate:/tmp/cache" + ]; + ports = [ + "5000:5000" + "8554:8554" # RTSP feeds + "8555:8555/tcp" # WebRTC over tcp + "8555:8555/udp" # WebRTC over udp + ]; + }; + + networking.firewall.interfaces.ss0.allowedTCPPorts = [ 5000 8554 8555 ]; + networking.firewall.interfaces.ss0.allowedUDPPorts = [ 8555 ]; + + global.fs.zfs.mountpoints."/nix/persist/service/frigate" = "service/frigate"; +} diff --git a/spec/eientei/hass.nix b/spec/eientei/hass.nix new file mode 100644 index 00000000..210d6755 --- /dev/null +++ b/spec/eientei/hass.nix @@ -0,0 +1,37 @@ +{ pkgs +, config +, ...}: { + services.home-assistant = { + enable = true; + config = null; + configDir = "/nix/persist/service/hass"; + + extraComponents = [ + "default_config" + "met" + "esphome" + "mqtt" + "synology_dsm" + ]; + + extraPackages = python3Packages: with python3Packages; [ + aiogithubapi gtts radios + ]; + }; + + services.nginx.virtualHosts."home.514fpv.io" = { + useACMEHost = ".514fpv.io"; + addSSL = true; + extraConfig = '' + proxy_buffering off; + ''; + locations."/" = { + proxyPass = "http://127.0.0.1:8123"; + proxyWebsockets = true; + }; + }; + + global.fs.zfs.mountpoints."/nix/persist/service/hass" = "service/hass"; + + nixpkgs.config.permittedInsecurePackages = [ "openssl-1.1.1w" ]; +} diff --git a/spec/eientei/jellyfin.nix b/spec/eientei/jellyfin.nix new file mode 100644 index 00000000..9f3c3671 --- /dev/null +++ b/spec/eientei/jellyfin.nix @@ -0,0 +1,35 @@ +{ pkgs +, config +, ...}: { + services.jellyfin = { + enable = true; + openFirewall = true; + dataDir = "/nix/persist/service/jellyfin"; + }; + + environment.systemPackages = with pkgs; [ jellyfin jellyfin-web jellyfin-ffmpeg ]; + + users.users.jellyfin.uid = 282; + users.groups.jellyfin.gid = 282; + + services.nginx.virtualHosts."jellyfin.514fpv.io" = { + useACMEHost = ".514fpv.io"; + addSSL = true; + locations = { + "= /".return = "302 https://jellyfin.514fpv.io:2096/web/"; + "/" = { + proxyPass = "http://127.0.0.1:8096"; + extraConfig = '' + proxy_buffering off; + ''; + }; + "= /web/".proxyPass = "http://127.0.0.1:8096/web/index.html"; + "/socket" = { + proxyPass = "http://127.0.0.1:8096"; + proxyWebsockets = true; + }; + }; + }; + + global.fs.zfs.mountpoints."/nix/persist/service/jellyfin" = "service/jellyfin"; +} diff --git a/spec/eientei/matrix.nix b/spec/eientei/matrix.nix new file mode 100644 index 00000000..1af2d400 --- /dev/null +++ b/spec/eientei/matrix.nix @@ -0,0 +1,51 @@ +{ config +, ... }: let + host = "514fpv.io"; +in { + services.matrix-synapse = { + enable = true; + withJemalloc = true; + dataDir = "/nix/persist/service/matrix"; + extraConfigFiles = [ "/nix/persist/service/matrix/secrets.yml" ]; + + settings = { + server_name = host; + public_baseurl = "https://${host}:8448/"; + + listeners = [ { + bind_addresses = [ "127.0.0.1" ]; + port = 8008; + tls = false; + type = "http"; + x_forwarded = true; + resources = [ + { compress = true; names = [ "client" ]; } + { compress = false; names = [ "federation" ]; } + ]; + } ]; + + # turn server configuration + turn_uris = [ + "turn:${config.services.coturn.realm}:3478?transport=udp" + "turn:${config.services.coturn.realm}:3478?transport=tcp" + ]; + turn_user_lifetime = "1h"; + }; + }; + + services.nginx.virtualHosts.${host} = { + listen = [{ addr = "0.0.0.0"; port = 8448; ssl = true; }]; + useACMEHost = host; + addSSL = true; + locations."/".extraConfig = '' + return 404; + ''; + locations."/health".proxyPass = "http://127.0.0.1:8008"; + locations."/_matrix".proxyPass = "http://127.0.0.1:8008"; + locations."/_synapse/client".proxyPass = "http://127.0.0.1:8008"; + }; + + networking.firewall.allowedTCPPorts = [ 8448 ]; + + global.fs.zfs.mountpoints."/nix/persist/service/matrix" = "service/matrix"; +} diff --git a/spec/eientei/minecraft.nix b/spec/eientei/minecraft.nix new file mode 100644 index 00000000..a18a5e6b --- /dev/null +++ b/spec/eientei/minecraft.nix @@ -0,0 +1,106 @@ +{ pkgs, lib, config, ... }: let + inherit (lib) mapAttrs' nameValuePair; + + servers = { + #bungeecord = { + # cmdline = "${pkgs.graalvmCEPackages.graalvm-ce}/bin/java -Xms2G -Xmx4G -XX:+UseG1GC -XX:G1HeapRegionSize=4M -XX:+UnlockExperimentalVMOptions -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch -jar waterfall-1.20-578.jar"; + # stop = "end"; + #}; + + #limbo = { + # cmdline = "${pkgs.graalvmCEPackages.graalvm-ce}/bin/java -Xms1G -Xmx1G -jar NanoLimbo-1.8-all.jar"; + # stop = "stop"; + #}; + + greedycraft = { + cmdline = "${pkgs.jdk8}/bin/java -Xmx10G -Xms10G -Xss4M -Dfile.encoding=GBK -Dsun.rmi.dgc.server.gcInterval=1800000 -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:+AlwaysPreTouch -XX:+UseStringDeduplication -Dfml.ignorePatchDiscrepancies=true -Dfml.ignoreInvalidMinecraftCertificates=true -XX:-OmitStackTraceInFastThrow -XX:+OptimizeStringConcat -XX:+UseAdaptiveGCBoundary -XX:G1HeapRegionSize=32M -jar forge-1.12.2-14.23.5.2855.jar nogui"; + stop = "stop"; + }; + + nfwc = { + cmdline = "${pkgs.jdk}/bin/java @user_jvm_args.txt @libraries/net/minecraftforge/forge/1.19.2-43.3.8/unix_args.txt"; + stop = "stop"; + }; + }; + + prefix = "minecraft-server-"; + data = "/nix/persist/service/minecraft"; +in { + # https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/services/games/minecraft-server.nix + users.users.minecraft = { + description = "Minecraft server service user"; + home = data; + createHome = true; + isSystemUser = true; + uid = 1021; + group = "minecraft"; + }; + users.groups.minecraft.gid = 1021; + + systemd.sockets = mapAttrs' (name: value: with value; (nameValuePair "${prefix}${name}" { + bindsTo = [ "${prefix}${name}.service" ]; + socketConfig = { + ListenFIFO = "/run/minecraft-server/${name}.stdin"; + SocketMode = "0660"; + SocketUser = "minecraft"; + SocketGroup = "minecraft"; + RemoveOnStop = true; + FlushPending = true; + }; + })) servers; + + systemd.services = let + stopScript = { name, stop }: pkgs.writeShellScript "minecraft-server-stop" '' + echo ${stop} > ${config.systemd.sockets."${prefix}${name}".socketConfig.ListenFIFO} + + # Wait for the PID of the minecraft server to disappear before + # returning, so systemd doesn't attempt to SIGKILL it. + while kill -0 "$1" 2> /dev/null; do + sleep 1s + done + ''; + in mapAttrs' (name: value: with value; (nameValuePair "${prefix}${name}" { + description = "Minecraft Server Service for ${name}"; + wantedBy = [ "multi-user.target" ]; + requires = [ "${prefix}${name}.socket" ]; + after = [ "network.target" "${prefix}${name}.socket" ]; + path = [ pkgs.bash ]; + + serviceConfig = { + ExecStart = cmdline; + ExecStop = "${stopScript { inherit name stop; }} $MAINPID"; + Restart = "always"; + User = "minecraft"; + WorkingDirectory = "${data}/${name}"; + + StandardInput = "socket"; + StandardOutput = "journal"; + StandardError = "journal"; + + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + UMask = "0077"; + }; + })) servers; + + global.fs.zfs.mountpoints.${data} = "service/minecraft"; + networking.firewall.allowedTCPPorts = [ 25565 ]; +} diff --git a/spec/eientei/mosquitto.nix b/spec/eientei/mosquitto.nix new file mode 100644 index 00000000..8cbeae02 --- /dev/null +++ b/spec/eientei/mosquitto.nix @@ -0,0 +1,17 @@ +{ + services.mosquitto = { + enable = true; + listeners = [ { + users.frigate = { + acl = [ "readwrite #" ]; + hashedPassword = "$7$101$Zt3Me3AkHbrKBbbI$SsLTGeTwtkFGXS2F1/S3Vg6w7TcgMLU037ttedKaw/Ela0wgqbaAVxecK1j5i5TDw9OjZMkNI9mUzz9l0d7vcg=="; + }; + users.root = { + acl = [ "readwrite #" ]; + hashedPassword = "$7$101$FjpEh+NpG27RltQe$u2dmg9wz+BBnp1s4z6E6x95TyGO5B9RM2z51qxetvAqO4QDvye1J3UCdMvjuz52iMqzZ3eKi+T2xDeLR7CeMgQ=="; + }; + } ]; + }; + + networking.firewall.allowedTCPPorts = [ 1883 ]; +} diff --git a/spec/library/nextcloud.nix b/spec/eientei/nextcloud.nix similarity index 72% rename from spec/library/nextcloud.nix rename to spec/eientei/nextcloud.nix index dfac5afb..23d1e678 100644 --- a/spec/library/nextcloud.nix +++ b/spec/eientei/nextcloud.nix @@ -4,11 +4,12 @@ in { services.nextcloud = { enable = true; - package = pkgs.nextcloud28; + package = pkgs.nextcloud30; extraApps = { - inherit (pkgs.nextcloud28Packages.apps) + inherit (pkgs.nextcloud30Packages.apps) notify_push impersonate spreed - contacts bookmarks deck polls notes forms; + contacts bookmarks deck polls notes forms + twofactor_webauthn; }; home = "/nix/persist/service/nextcloud"; configureRedis = true; @@ -20,8 +21,9 @@ in { config.dbtype = "pgsql"; config.adminuser = "koishi"; config.adminpassFile = builtins.toString (pkgs.writeText "password" "initial_password"); - extraOptions.overwriteprotocol = "https"; - extraOptions.default_phone_region = "US"; + settings.overwriteprotocol = "https"; + settings.default_phone_region = "US"; + settings.maintenance_window_start = 17; caching.redis = true; phpOptions.upload_max_filesize = "128G"; phpOptions.post_max_size = "128G"; @@ -37,4 +39,6 @@ in { useACMEHost = ".514fpv.io"; addSSL = true; }; + + global.fs.zfs.mountpoints."/nix/persist/service/nextcloud" = "service/nextcloud"; } diff --git a/spec/library/nginx.nix b/spec/eientei/nginx.nix similarity index 60% rename from spec/library/nginx.nix rename to spec/eientei/nginx.nix index e6980dcc..a83b542e 100644 --- a/spec/library/nginx.nix +++ b/spec/eientei/nginx.nix @@ -11,25 +11,12 @@ # set a reasonable default clientMaxBodySize = "64m"; - # proxy services running on TrueNAS virtualHosts = { "_" = { addSSL = true; useACMEHost = ".514fpv.io"; extraConfig = "return 444;"; }; - - "home.514fpv.io" = { - useACMEHost = ".514fpv.io"; - addSSL = true; - extraConfig = '' - proxy_buffering off; - ''; - locations."/" = { - proxyPass = "http://192.168.1.250:8123"; - proxyWebsockets = true; - }; - }; }; }; diff --git a/spec/eientei/photoprism.nix b/spec/eientei/photoprism.nix new file mode 100644 index 00000000..66a3874e --- /dev/null +++ b/spec/eientei/photoprism.nix @@ -0,0 +1,36 @@ +{ lib +, ... }: with lib; { + services.photoprism = { + enable = true; + originalsPath = "/run/storage/aerial/raw"; + settings = { + PHOTOPRISM_ADMIN_USER = "koishi"; + PHOTOPRISM_FFMPEG_ENCODER = "vaapi"; + PHOTOPRISM_ORIGINALS_LIMIT = "-1"; + PHOTOPRISM_RESOLUTION_LIMIT = "-1"; + }; + passwordFile = "/var/lib/photoprism/password"; + }; + + services.nginx.virtualHosts."raw.514fpv.io" = { + useACMEHost = ".514fpv.io"; + addSSL = true; + extraConfig = '' + proxy_buffering off; + ''; + locations."/" = { + proxyPass = "http://127.0.0.1:2342"; + proxyWebsockets = true; + }; + }; + + systemd.services.photoprism.serviceConfig = { + PrivateDevices = mkForce false; + DevicePolicy = "closed"; + DeviceAllow = [ "/dev/dri/renderD128 rw" ]; + }; + + environment.persistence."/nix/persist/fhs".directories = [ + "/var/lib/private/photoprism" + ]; +} diff --git a/spec/eientei/photoview.nix b/spec/eientei/photoview.nix new file mode 100644 index 00000000..f7ccb595 --- /dev/null +++ b/spec/eientei/photoview.nix @@ -0,0 +1,20 @@ +{ + services.photoview = { + enable = true; + database.driver = "postgres"; + database.string = "postgresql:///photoview?host=/var/run/postgresql"; + stateDir = "/nix/persist/service/photoview"; + secrets = "/nix/persist/service/photoview/secrets.env" ; + }; + + services.nginx.virtualHosts."dvr.514fpv.io" = { + useACMEHost = ".514fpv.io"; + addSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:8000"; + proxyWebsockets = true; + }; + }; + + global.fs.zfs.mountpoints."/nix/persist/service/photoview" = "service/photoview"; +} diff --git a/spec/library/postgresql.nix b/spec/eientei/postgresql.nix similarity index 70% rename from spec/library/postgresql.nix rename to spec/eientei/postgresql.nix index dc082ee9..22d99ced 100644 --- a/spec/library/postgresql.nix +++ b/spec/eientei/postgresql.nix @@ -7,4 +7,6 @@ package = pkgs.postgresql_15; }; + + global.fs.zfs.mountpoints."/nix/persist/service/postgresql" = "service/postgresql"; } diff --git a/spec/eientei/tubesync.nix b/spec/eientei/tubesync.nix new file mode 100644 index 00000000..202dae12 --- /dev/null +++ b/spec/eientei/tubesync.nix @@ -0,0 +1,8 @@ +{ + services.tubesync = { + enable = false; + stateDir = "/nix/persist/service/tubesync"; + }; + + global.fs.zfs.mountpoints."/nix/persist/service/tubesync" = "service/tubesync"; +} diff --git a/spec/eientei/vaultwarden.nix b/spec/eientei/vaultwarden.nix new file mode 100644 index 00000000..8e3df95f --- /dev/null +++ b/spec/eientei/vaultwarden.nix @@ -0,0 +1,27 @@ +{ + services.vaultwarden = { + enable = true; + environmentFile = "/nix/persist/service/vaultwarden/secret.env"; + config = { + domain = "https://vault.514fpv.io:2096"; + signupsAllowed = false; + rocketAddress = "127.0.0.1"; + rocketPort = 8222; + rocketLog = "critical"; + databaseUrl = "postgresql:///vaultwarden"; + }; + dbBackend = "postgresql"; + }; + + services.nginx.virtualHosts."vault.514fpv.io" = { + useACMEHost = ".514fpv.io"; + addSSL = true; + locations."/".proxyPass = "http://127.0.0.1:8222"; + }; + + environment.persistence."/nix/persist/fhs".directories = [ + "/var/lib/bitwarden_rs" + ]; + + global.fs.zfs.mountpoints."/nix/persist/service/vaultwarden" = "service/vaultwarden"; +} diff --git a/spec/focus/acme.nix b/spec/focus/acme.nix new file mode 100644 index 00000000..47a359d3 --- /dev/null +++ b/spec/focus/acme.nix @@ -0,0 +1,11 @@ +{ + security.acme.certs = let + cloudflare = { + dnsProvider = "cloudflare"; + credentialsFile = "/nix/persist/secret/cloudflare"; + }; + in { + "sf.514fpv.io" = cloudflare; + ".sf.514fpv.io" = cloudflare // { domain = "*.sf.514fpv.io"; }; + }; +} diff --git a/spec/focus/default.nix b/spec/focus/default.nix new file mode 100644 index 00000000..46b14034 --- /dev/null +++ b/spec/focus/default.nix @@ -0,0 +1,48 @@ +{ lib +, ... }: { + global = { + id = "22e9d4e37bd7436ba0cbe6e767fb0912"; + auth.openssh.enable = true; + fs.esp.uuid = "8C36-CBE2"; + fs.type = "zfs"; + fs.zfs.alert.secret = "/nix/persist/secret/telegram"; + fs.zfs.split.enable = true; + fs.zfs.split.store = "59b73292-8098-4774-b8b6-59c23130d405"; + fs.zfs.split.secret = "f8983719-f9e7-42b2-b8f3-0f32f6b328ae"; + fs.zfs.replication.enable = true; + fs.zfs.replication.remote = "focus@edge.514fpv.io:archive/backup/koishi/focus"; + fs.zfs.replication.port = 8087; + fs.cryptsetup.enable = true; + fs.cryptsetup.allowDiscards = false; + fs.cryptsetup.uuids.secret = "c2bc361e-6f9a-48fa-b698-ed3603a9664a"; + boot.lanzaboote = true; + boot.memtest = 4; + acme.enable = true; + netdata.enable = true; + netdata.host = "sf.514fpv.io"; + netdata.addSSL = true; + netdata.useACMEHost = "sf.514fpv.io"; + }; + + services.udev.extraRules = '' + SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="6c:b3:11:3d:80:13", NAME="2g5" + ''; + + networking.proxy = { + allProxy = "http://compat:3128"; + noProxy = "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1,localhost,.localdomain"; + }; + + imports = lib.pipe ./. [ + builtins.readDir + (lib.filterAttrs (n: ty: ty == "regular" && n != "default.nix")) + (lib.mapAttrsToList (n: _: ./${n})) + ]; + + hardware.enableRedistributableFirmware = true; + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ "i915" ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + hardware.cpu.intel.updateMicrocode = true; +} diff --git a/spec/focus/nextcloud.nix b/spec/focus/nextcloud.nix new file mode 100644 index 00000000..eabee803 --- /dev/null +++ b/spec/focus/nextcloud.nix @@ -0,0 +1,47 @@ +{ pkgs +, ...}: let + host = "cloud.sf.514fpv.io"; +in { + services.nextcloud = { + enable = true; + package = pkgs.nextcloud30; + extraApps = { + inherit (pkgs.nextcloud30Packages.apps) + notify_push impersonate spreed + contacts bookmarks deck polls notes forms + twofactor_webauthn; + }; + home = "/nix/persist/service/nextcloud"; + configureRedis = true; + webfinger = true; + maxUploadSize = "128G"; + fastcgiTimeout = 300; + hostName = host; + phpExtraExtensions = all: with all; [ bz2 ]; + database.createLocally = true; + config.dbtype = "pgsql"; + config.adminuser = "koishi"; + config.adminpassFile = builtins.toString (pkgs.writeText "password" "initial_password"); + settings.overwriteprotocol = "https"; + settings.maintenance_window_start = 17; + settings.default_phone_region = "US"; + settings.default_language = "zh"; + settings.default_locale = "zh_Hans_CN"; + caching.redis = true; + phpOptions.upload_max_filesize = "128G"; + phpOptions.post_max_size = "128G"; + phpOptions.output_buffering = "0"; + phpOptions."opcache.interned_strings_buffer" = "32"; + https = true; + }; + + users.users.nextcloud.uid = 1022; + users.groups.nextcloud.gid = 1022; + + services.nginx.virtualHosts.${host} = { + useACMEHost = ".sf.514fpv.io"; + addSSL = true; + }; + + global.fs.zfs.mountpoints."/nix/persist/service/nextcloud" = "service/nextcloud"; +} diff --git a/spec/focus/nginx.nix b/spec/focus/nginx.nix new file mode 100644 index 00000000..7c8bb5e7 --- /dev/null +++ b/spec/focus/nginx.nix @@ -0,0 +1,24 @@ +{ + services.nginx = { + enable = true; + + # enable all recommended settings + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + + # set a reasonable default + clientMaxBodySize = "64m"; + + virtualHosts = { + "_" = { + addSSL = true; + useACMEHost = ".sf.514fpv.io"; + extraConfig = "return 444;"; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; +} diff --git a/spec/focus/postgresql.nix b/spec/focus/postgresql.nix new file mode 100644 index 00000000..22d99ced --- /dev/null +++ b/spec/focus/postgresql.nix @@ -0,0 +1,12 @@ +{ pkgs +, config +, ...}: { + services.postgresql = { + enable = true; + dataDir = "/nix/persist/service/postgresql/${config.services.postgresql.package.psqlSchema}"; + + package = pkgs.postgresql_15; + }; + + global.fs.zfs.mountpoints."/nix/persist/service/postgresql" = "service/postgresql"; +} diff --git a/spec/hakugyokurou/default.nix b/spec/hakugyokurou/default.nix new file mode 100644 index 00000000..f1f9f285 --- /dev/null +++ b/spec/hakugyokurou/default.nix @@ -0,0 +1,61 @@ +{ pkgs, lib, config, ... }: { + global = { + id = "09a2900f15b74f36b023a9ebcd539f6a"; + fs.esp.uuid = "AE73-B83E"; + fs.type = "xfs"; + fs.store = "e4c673cb-03c5-44ac-b2f1-1085a7f7a553"; + fs.cryptsetup.enable = true; + fs.cryptsetup.uuids.persist = "122001b1-7a43-4bab-ae7c-ba8eeb1cc864"; + boot.lanzaboote = true; + gpu.enable = true; + gpu.type = "amdgpu"; + android.enable = true; + }; + + home = { + gnome.enable = true; + jetbrains.enable = true; + vscode.enable = true; + steam.enable = true; + minecraft.enable = true; + minecraft.user = "app"; + }; + + users.homeModules = [ { + wayland.windowManager.sway.config = { + input = { + "9610:30:HID_258a:001e_Mouse".natural_scroll = "disabled"; + "1539:61441:NVTK0603:00_0603:F001_UNKNOWN".map_to_output = "eDP-1"; + "1539:61441:NVTK0603:00_0603:F001".map_to_output = "eDP-1"; + }; + output = { + eDP-1.scale = "2"; + eDP-1.transform = "270"; + }; + }; + } ]; + + powerManagement.enable = true; + powerManagement.cpuFreqGovernor = "performance"; + boot.kernelParams = [ + "video=eDP-1:panel_orientation=left_side_up" + ]; + boot.plymouth.extraConfig = '' + DeviceScale=2 + ''; + + services.greetd.settings.default_session.command = let + cfg = config.programs.regreet; + in lib.mkOverride 999 "${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} ${lib.escapeShellArgs cfg.cageArgs} -- ${pkgs.writeShellScript "cageResolution" '' + ${lib.getExe pkgs.wlr-randr} --output eDP-1 --scale 2 --transform 90 + ${lib.getExe cfg.package} + ''}"; + + hardware.sensor.iio.enable = true; + hardware.enableRedistributableFirmware = true; + boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "thunderbolt" "usbhid" "rtsx_pci_sdmmc" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + hardware.cpu.intel.updateMicrocode = true; +} diff --git a/spec/incinerator/default.nix b/spec/incinerator/default.nix index e89145f9..124e93a6 100644 --- a/spec/incinerator/default.nix +++ b/spec/incinerator/default.nix @@ -1,12 +1,13 @@ -{ modulesPath, ... }: { - faucet = { - id = "bc8036643fc24f04ab150ff6a38dcce7"; +{ lib +, modulesPath +, ... }: { + global = { + id = "62ea62fde6c04fb6b5c791892b6798c8"; auth.openssh.enable = true; libvirt.enable = false; - io.bluetooth = false; - io.audio = false; - fs.type = "btrfs"; - fs.store = "534f985d-0540-49eb-a2f6-b30ea16f41ad"; + fs.type = "ext4"; + fs.store = "7c3459d2-c046-4ef6-8bc6-a8cf97bcade9"; + oci.enable = true; }; imports = lib.pipe ./. [ @@ -20,4 +21,5 @@ boot.kernelModules = [ "kvm-intel" ]; boot.extraModulePackages = [ ]; boot.kernelParams = [ "console=ttyS0,115200n8" ]; + boot.initrd.systemd.emergencyAccess = true; } diff --git a/spec/koumakyou/default.nix b/spec/koumakyou/default.nix new file mode 100644 index 00000000..b1c239d3 --- /dev/null +++ b/spec/koumakyou/default.nix @@ -0,0 +1,71 @@ +{ pkgs, lib, config, jovian, ... }: { + global = { + id = "0a920a834b5f480bab258040096d4c6e"; + fs.esp.uuid = "ACB3-4AFF"; + fs.type = "xfs"; + fs.store = "6b885fa6-5c13-4a46-94da-a287232606b9"; + fs.cryptsetup.enable = true; + fs.cryptsetup.uuids.persist = "b4a5a66d-b491-4ac7-bc71-1ea8b1a503be"; + boot.lanzaboote = true; + gpu.enable = true; + gpu.type = "amdgpu"; + }; + + home = { + jetbrains.enable = true; + vscode.enable = true; + minecraft.enable = true; + minecraft.user = "app"; + }; + + users.homeModules = [ { + wayland.windowManager.sway.config = { + input."1046:911:Goodix_Capacitive_TouchScreen".map_to_output = "eDP-1"; + output = { + eDP-1.scale = "1.75"; + }; + }; + } ]; + + powerManagement.enable = true; + powerManagement.cpuFreqGovernor = "performance"; + powerManagement.resumeCommands = '' + # trackpad driver resume workaround + ${pkgs.kmod}/bin/modprobe -r bmi260_i2c + ${pkgs.kmod}/bin/modprobe -r bmi260_core + ${pkgs.kmod}/bin/modprobe bmi260_i2c + ${pkgs.kmod}/bin/modprobe bmi260_core + ''; + console.packages = [ pkgs.terminus_font ]; + console.font = "ter-v32n"; + console.earlySetup = true; + + specialisation.desktop.configuration = { + global.jovian = false; + jovian.steamos.useSteamOSConfig = false; + services.greetd.settings.default_session.command = let + cfg = config.programs.regreet; + in "${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} ${lib.escapeShellArgs cfg.cageArgs} -- ${pkgs.writeShellScript "cageResolution" '' + ${lib.getExe pkgs.wlr-randr} --output eDP-1 --scale 2 + ${lib.getExe cfg.package} + ''}"; + environment.systemPackages = with pkgs; [ + (writeShellScriptBin "sway-logout" '' + ${systemd}/bin/systemctl --user unset-environment WAYLAND_DISPLAY SWAYSOCK + ${sway}/bin/swaymsg exit + '') + ]; + }; + + hardware.enableRedistributableFirmware = true; + boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "thunderbolt" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + hardware.cpu.intel.updateMicrocode = true; + + imports = [ + jovian.nixosModules.jovian + ./jovian.nix + ]; +} diff --git a/spec/koumakyou/jovian.nix b/spec/koumakyou/jovian.nix new file mode 100644 index 00000000..f06d43f7 --- /dev/null +++ b/spec/koumakyou/jovian.nix @@ -0,0 +1,80 @@ +{ pkgs, lib, config, ... }: with lib; { + options.global.jovian = mkEnableOption "set up Jovian NixOS" // { default = true; }; + + config = mkIf config.global.jovian { + jovian = { + hardware.has.amd.gpu = true; + steamos.useSteamOSConfig = true; + steamos.enableDefaultCmdlineConfig = false; + + steam = { + enable = true; + autoStart = true; + desktopSession = "plasma"; + user = "app"; + }; + }; + + users.home.persistApp.directories = [ + ".steam" ".local/share/Steam" + ".config/gamescope" ".config/hhd" + ".kodi" + ]; + + users.users.app.extraGroups = [ "networkmanager" "gamemode" ]; + programs.gamemode.enable = true; + programs.regreet.enable = false; + home.plasma.enable = true; + home.plasma.extraConfig = { + configFile = { + kwinrc.Xwayland.Scale = 1.75; + kscreenlockerrc.Daemon.Autolock = false; + }; + }; + + home-manager.users.app = { + home.packages = with pkgs; [ + (pkgs.kodi-wayland.passthru.withPackages (kodiPkgs: with kodiPkgs; [ + joystick # keymap steam-controller + #controller-topology-project + libretro libretro-2048 + libretro-fuse libretro-genplus libretro-mgba + libretro-nestopia libretro-snes9x + jellycon + ])) + ]; + + home.pointerCursor = { + package = pkgs.steamdeck-hw-theme; + name = "steam"; + }; + }; + + services.handheld-daemon = { + enable = true; + user = "app"; + }; + + jovian.decky-loader = { + # ~/.steam/steam/.cef-enable-remote-debugging + enable = true; + user = "app"; + extraPackages = with pkgs; [ + curl unzip util-linux gnugrep procps pciutils kmod ryzenadj + ]; + extraPythonPackages = pythonPackages: with pythonPackages; [ + hid pyyaml + ]; + stateDir = "/nix/persist/decky"; + }; + + boot.kernelParams = [ + "iomem=relaxed" + "amd_pstate=passive" + ]; + + services.udev.extraRules = '' + SUBSYSTEM=="power_supply", ATTR{online}=="0", RUN+="${pkgs.ryzenadj}/bin/ryzenadj --max-performance" + ''; + }; +} diff --git a/spec/library/acme.nix b/spec/library/acme.nix deleted file mode 100644 index 82a71ff9..00000000 --- a/spec/library/acme.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ - security.acme = { - acceptTerms = true; - defaults.email = "koishi@514fpv.one"; - defaults.group = "nginx"; - certs = let - cloudflare = { - dnsProvider = "cloudflare"; - credentialsFile = "/nix/persist/secret/cloudflare"; - }; - in { - "514fpv.io" = cloudflare; - ".514fpv.io" = cloudflare // { domain = "*.514fpv.io"; }; - }; - }; - - environment.persistence."/nix/persist/fhs".directories = [ "/var/lib/acme" ]; -} diff --git a/spec/library/default.nix b/spec/library/default.nix deleted file mode 100644 index e69ec41d..00000000 --- a/spec/library/default.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ pkgs -, lib -, modulesPath -, ... }: { - faucet = { - id = "bc8036643fc24f04ab150ff6a38dcce7"; - auth.openssh.enable = true; - libvirt.enable = false; - io.bluetooth = false; - io.audio = false; - fs.esp.uuid = "00A5-929C"; - fs.type = "xfs"; - fs.store = "25b9f83f-7f6d-432f-a169-2985e5930401"; - fs.extPersist.enable = true; - fs.extPersist.device = "/dev/disk/by-uuid/ba3d0989-9f81-4ac0-b5a7-124c86835cfa"; - fs.extPersist.fsType = "ext4"; - }; - - networking.proxy = { - default = "socks5://192.168.1.253:1080"; - noProxy = "127.0.0.1,localhost,.localdomain"; - }; - - imports = lib.pipe ./. [ - builtins.readDir - (lib.filterAttrs (n: ty: ty == "regular" && n != "default.nix")) - (lib.mapAttrsToList (n: _: ./${n})) - ] ++ [ (modulesPath + "/profiles/qemu-guest.nix") ]; - - boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "ahci" "virtio_pci" "sr_mod" "virtio_blk" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; - boot.extraModulePackages = [ ]; - boot.kernelParams = [ "console=ttyS0,115200n8" ]; - environment.systemPackages = with pkgs; [ python3 ]; -} diff --git a/spec/reimaden/ac71/Makefile b/spec/reimaden/ac71/Makefile new file mode 100644 index 00000000..80f68679 --- /dev/null +++ b/spec/reimaden/ac71/Makefile @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0 +PWD := $(shell pwd) +MODNAME = ac71 +MODVER = 0.0 + +obj-m += $(MODNAME).o + +# alphabetically sorted +$(MODNAME)-y += ec.o \ + main.o \ + misc.o \ + pdev.o \ + events.o \ + +$(MODNAME)-$(CONFIG_ACPI_BATTERY) += battery.o +$(MODNAME)-$(CONFIG_HWMON) += hwmon.o hwmon_fan.o hwmon_pwm.o fan.o + +all: + $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules + +install: + $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean diff --git a/spec/reimaden/ac71/battery.c b/spec/reimaden/ac71/battery.c new file mode 100644 index 00000000..67311c22 --- /dev/null +++ b/spec/reimaden/ac71/battery.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ec.h" + +/* ========================================================================== */ + +#if IS_ENABLED(CONFIG_ACPI_BATTERY) + +static bool battery_hook_registered; + +static bool nobattery; +module_param(nobattery, bool, 0444); +MODULE_PARM_DESC(nobattery, "do not expose battery related controls (default=false)"); + +/* ========================================================================== */ + +static ssize_t charge_control_end_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = ec_read_byte(BATT_CHARGE_CTRL_ADDR); + + if (status < 0) + return status; + + status &= BATT_CHARGE_CTRL_VALUE_MASK; + + if (status == 0) + status = 100; + + return sprintf(buf, "%d\n", status); +} + +static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int status, value; + + if (kstrtoint(buf, 10, &value) || !(1 <= value && value <= 100)) + return -EINVAL; + + status = ec_read_byte(BATT_CHARGE_CTRL_ADDR); + if (status < 0) + return status; + + if (value == 100) + value = 0; + + status = (status & ~BATT_CHARGE_CTRL_VALUE_MASK) | value; + + status = ec_write_byte(BATT_CHARGE_CTRL_ADDR, status); + + if (status < 0) + return status; + + return count; +} + +static DEVICE_ATTR_RW(charge_control_end_threshold); +static struct attribute *ac71_batt_attrs[] = { + &dev_attr_charge_control_end_threshold.attr, + NULL +}; +ATTRIBUTE_GROUPS(ac71_batt); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) +static int ac71_batt_add(struct power_supply *battery, struct acpi_battery_hook *hook) +#else +static int ac71_batt_add(struct power_supply *battery) +#endif +{ + if (strcmp(battery->desc->name, "BAT0") != 0) + return 0; + + return device_add_groups(&battery->dev, ac71_batt_groups); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) +static int ac71_batt_remove(struct power_supply *battery, struct acpi_battery_hook *hook) +#else +static int ac71_batt_remove(struct power_supply *battery) +#endif +{ + if (strcmp(battery->desc->name, "BAT0") != 0) + return 0; + + device_remove_groups(&battery->dev, ac71_batt_groups); + return 0; +} + +static struct acpi_battery_hook ac71_batt_hook = { + .add_battery = ac71_batt_add, + .remove_battery = ac71_batt_remove, + .name = "AC71 laptop battery extension", +}; + +int __init ac71_battery_setup(void) +{ + if (nobattery) + return -ENODEV; + + battery_hook_register(&ac71_batt_hook); + battery_hook_registered = true; + + return 0; +} + +void ac71_battery_cleanup(void) +{ + if (battery_hook_registered) + battery_hook_unregister(&ac71_batt_hook); +} + +#endif diff --git a/spec/reimaden/ac71/battery.h b/spec/reimaden/ac71/battery.h new file mode 100644 index 00000000..eeffb19c --- /dev/null +++ b/spec/reimaden/ac71/battery.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_BATTERY_H +#define AC71_BATTERY_H + +#if IS_ENABLED(CONFIG_ACPI_BATTERY) + +#include + +int __init ac71_battery_setup(void); +void ac71_battery_cleanup(void); + +#else + +static inline int ac71_battery_setup(void) +{ + return 0; +} + +static inline void ac71_battery_cleanup(void) +{ + +} + +#endif + +#endif /* AC71_BATTERY_H */ diff --git a/spec/reimaden/ac71/default.nix b/spec/reimaden/ac71/default.nix new file mode 100644 index 00000000..87ccdd59 --- /dev/null +++ b/spec/reimaden/ac71/default.nix @@ -0,0 +1,6 @@ +{ config +, ... }: { + boot.extraModulePackages = [ + (config.boot.kernelPackages.callPackage ./package.nix { }) + ]; +} diff --git a/spec/reimaden/ac71/ec.c b/spec/reimaden/ac71/ec.c new file mode 100644 index 00000000..2ce7a02c --- /dev/null +++ b/spec/reimaden/ac71/ec.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include +#include +#include + +#include "ec.h" +#include "wmi.h" + +/* ========================================================================== */ + +static DECLARE_RWSEM(ec_lock); + +/* ========================================================================== */ + +int __must_check ac71_ec_lock(void) +{ + return down_write_killable(&ec_lock); +} + +void ac71_ec_unlock(void) +{ + up_write(&ec_lock); +} + +int __must_check ac71_ec_transaction(uint16_t addr, uint16_t data, + union ac71_ec_result *result, bool read) +{ + uint8_t buf[] = { + addr & 0xFF, + addr >> 8, + data & 0xFF, + data >> 8, + 0, + read ? 1 : 0, + 0, + 0, + }; + static_assert(ARRAY_SIZE(buf) == 8); + + /* the returned ACPI_TYPE_BUFFER is 40 bytes long for some reason ... */ + uint8_t output_buf[sizeof(union acpi_object) + 40]; + + struct acpi_buffer input = { sizeof(buf), buf }, + output = { sizeof(output_buf), output_buf }; + union acpi_object *obj; + acpi_status status = AE_OK; + int err; + + if (read) err = down_read_killable(&ec_lock); + else err = down_write_killable(&ec_lock); + + if (err) + goto out; + + memset(output_buf, 0, sizeof(output_buf)); + + status = wmi_evaluate_method(AC71_WMI_WMBC_GUID, 0, + AC71_WMBC_GETSETULONG_ID, &input, &output); + + if (read) up_read(&ec_lock); + else up_write(&ec_lock); + + if (ACPI_FAILURE(status)) { + err = -EIO; + goto out; + } + + obj = output.pointer; + + if (result) { + if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= sizeof(*result)) { + memcpy(result, obj->buffer.pointer, sizeof(*result)); + } else { + err = -ENODATA; + goto out; + } + } + +out: + pr_debug( + "%s(addr=%#06x, data=%#06x, result=%c, read=%c)" + ": (%d) [%#010lx] %s" + ": [%*ph]\n", + + __func__, (unsigned int) addr, (unsigned int) data, + result ? 'y' : 'n', read ? 'y' : 'n', + err, (unsigned long) status, acpi_format_exception(status), + (obj && obj->type == ACPI_TYPE_BUFFER) ? + (int) min(sizeof(*result), (size_t) obj->buffer.length) : 0, + (obj && obj->type == ACPI_TYPE_BUFFER) ? + obj->buffer.pointer : NULL + ); + + return err; +} +ALLOW_ERROR_INJECTION(ac71_ec_transaction, ERRNO); diff --git a/spec/reimaden/ac71/ec.h b/spec/reimaden/ac71/ec.h new file mode 100644 index 00000000..739cbb6c --- /dev/null +++ b/spec/reimaden/ac71/ec.h @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_LAPTOP_EC_H +#define AC71_LAPTOP_EC_H + +#include +#include + +/* ========================================================================== */ +/* + * EC register addresses and bitmasks, + * some of them are not used, + * only for documentation + */ + +#define ADDR(page, offset) (((uint16_t)(page) << 8) | ((uint16_t)(offset))) + +/* ========================================================================== */ + +#define AP_BIOS_BYTE_ADDR ADDR(0x07, 0xA4) +#define AP_BIOS_BYTE_FN_LOCK_SWITCH BIT(3) + +/* ========================================================================== */ + +/* battery charger control register */ +#define BATT_CHARGE_CTRL_ADDR ADDR(0x07, 0xB9) +#define BATT_CHARGE_CTRL_VALUE_MASK GENMASK(6, 0) +#define BATT_CHARGE_CTRL_REACHED BIT(7) + +#define BATT_STATUS_ADDR ADDR(0x04, 0x32) +#define BATT_STATUS_DISCHARGING BIT(0) + +/* possibly temp (in C) = value / 10 + X */ +#define BATT_TEMP_ADDR ADDR(0x04, 0xA2) + +#define BATT_ALERT_ADDR ADDR(0x04, 0x94) + +#define BIOS_CTRL_1_ADDR ADDR(0x07, 0x4E) +#define BIOS_CTRL_1_FN_LOCK_STATUS BIT(4) + +#define BIOS_CTRL_2_ADDR ADDR(0x07, 0x82) +#define BIOS_CTRL_2_FAN_V2_NEW BIT(0) +#define BIOS_CTRL_2_FAN_QKEY BIT(1) +#define BIOS_CTRL_2_OFFICE_MODE_FAN_TABLE_TYPE BIT(2) +#define BIOS_CTRL_2_FAN_V3 BIT(3) +#define BIOS_CTRL_2_DEFAULT_MODE BIT(4) + +/* 3rd control register of a different kind */ +#define BIOS_CTRL_3_ADDR ADDR(0x7, 0xA3) +#define BIOS_CTRL_3_FAN_REDUCED_DUTY_CYCLE BIT(5) +#define BIOS_CTRL_3_FAN_ALWAYS_ON BIT(6) + +#define BIOS_INFO_1_ADDR ADDR(0x04, 0x9F) +#define BIOS_INFO_5_ADDR ADDR(0x04, 0x66) + +/* ========================================================================== */ + +#define CTRL_1_ADDR ADDR(0x07, 0x41) +#define CTRL_1_MANUAL_MODE BIT(0) +#define CTRL_1_ITE_KBD_EFFECT_REACTIVE BIT(3) +#define CTRL_1_FAN_ABNORMAL BIT(5) + +#define CTRL_2_ADDR ADDR(0x07, 0x8C) +#define CTRL_2_SINGLE_COLOR_KEYBOARD BIT(0) +#define CTRL_2_SINGLE_COLOR_KBD_BL_OFF BIT(1) +#define CTRL_2_TURBO_LEVEL_MASK GENMASK(3, 2) +#define CTRL_2_TURBO_LEVEL_0 0x00 +#define CTRL_2_TURBO_LEVEL_1 BIT(2) +#define CTRL_2_TURBO_LEVEL_2 BIT(3) +#define CTRL_2_TURBO_LEVEL_3 (BIT(2) | BIT(3)) +// #define CTRL_2_SINGLE_COLOR_KBD_? BIT(4) +#define CTRL_2_SINGLE_COLOR_KBD_BRIGHTNESS GENMASK(7, 5) + +#define CTRL_3_ADDR ADDR(0x07, 0xA5) +#define CTRL_3_PWR_LED_MASK GENMASK(1, 0) +#define CTRL_3_PWR_LED_NONE BIT(1) +#define CTRL_3_PWR_LED_BOTH BIT(0) +#define CTRL_3_PWR_LED_LEFT 0x00 +#define CTRL_3_FAN_QUIET BIT(2) +#define CTRL_3_OVERBOOST BIT(4) +#define CTRL_3_HIGH_PWR BIT(7) + +#define CTRL_4_ADDR ADDR(0x07, 0xA6) +#define CTRL_4_OVERBOOST_DYN_TEMP_OFF BIT(1) +#define CTRL_4_TOUCHPAD_TOGGLE_OFF BIT(6) + +#define CTRL_5_ADDR ADDR(0x07, 0xC5) + +#define CTRL_6_ADDR ADDR(0x07, 0x8E) + +/* ========================================================================== */ + +#define DEVICE_STATUS_ADDR ADDR(0x04, 0x7B) +#define DEVICE_STATUS_WIFI_ON BIT(7) + +/* ========================================================================== */ + +/* fan control register */ +#define FAN_CTRL_ADDR ADDR(0x07, 0x51) +#define FAN_CTRL_LEVEL_MASK GENMASK(2, 0) +#define FAN_CTRL_TURBO BIT(4) +#define FAN_CTRL_AUTO BIT(5) +#define FAN_CTRL_FAN_BOOST BIT(6) + +#define FAN_RPM_1_ADDR ADDR(0x04, 0x64) +#define FAN_RPM_2_ADDR ADDR(0x04, 0x6C) + +#define FAN_PWM_1_ADDR ADDR(0x18, 0x04) +#define FAN_PWM_2_ADDR ADDR(0x18, 0x09) + +#define FAN_TEMP_1_ADDR ADDR(0x04, 0x3e) +#define FAN_TEMP_2_ADDR ADDR(0x04, 0x4f) + +#define FAN_MODE_INDEX_ADDR ADDR(0x07, 0xAB) +#define FAN_MODE_INDEX_LOW_MASK GENMASK(3, 0) +#define FAN_MODE_INDEX_HIGH_MASK GENMASK(7, 4) + +/* ========================================================================== */ + +/* + * the actual keyboard type is seemingly determined from this number, + * the project id, the controller firmware version, + * and the HID usage page of the descriptor of the controller + */ +#define KEYBOARD_TYPE_ADDR ADDR(0x07, 0x3C) +#define KEYBOARD_TYPE_101 25 +#define KEYBOARD_TYPE_101M 41 +#define KEYBOARD_TYPE_102 17 +#define KEYBOARD_TYPE_102M 33 +#define KEYBOARD_TYPE_85 25 +#define KEYBOARD_TYPE_86 17 +#define KEYBOARD_TYPE_87 73 +#define KEYBOARD_TYPE_88 65 +#define KEYBOARD_TYPE_97 57 +#define KEYBOARD_TYPE_98 49 +#define KEYBOARD_TYPE_99 121 +#define KEYBOARD_TYPE_100 113 + +/* ========================================================================== */ + +#define PROJ_ID_ADDR ADDR(0x07, 0x40) +#define PROJ_ID_GIxKN 1 +#define PROJ_ID_GJxKN 2 +#define PROJ_ID_GKxCN 3 +#define PROJ_ID_GIxCN 4 +#define PROJ_ID_GJxCN 5 +#define PROJ_ID_GK5CN_X 6 +#define PROJ_ID_GK7CN_S 7 +#define PROJ_ID_GK7CPCS_GK5CQ7Z 8 +#define PROJ_ID_IDP 11 +#define PROJ_ID_ID6Y 12 +#define PROJ_ID_ID7Y 13 +#define PROJ_ID_PF4MU_PF4MN_PF5MU 14 +#define PROJ_ID_CML_GAMING 15 +#define PROJ_ID_GK7NXXR 16 +#define PROJ_ID_GM5MU1Y 17 + +/* ========================================================================== */ + +#define STATUS_1_ADDR ADDR(0x07, 0x68) +#define STATUS_1_SUPER_KEY_LOCK BIT(0) +#define STATUS_1_LIGHTBAR BIT(1) +#define STATUS_1_FAN_BOOST BIT(2) + +#define SUPPORT_1_ADDR ADDR(0x07, 0x65) +#define SUPPORT_1_AIRPLANE_MODE BIT(0) +#define SUPPORT_1_GPS_SWITCH BIT(1) +#define SUPPORT_1_OVERCLOCK BIT(2) +#define SUPPORT_1_MACRO_KEY BIT(3) +#define SUPPORT_1_SHORTCUT_KEY BIT(4) +#define SUPPORT_1_SUPER_KEY_LOCK BIT(5) +#define SUPPORT_1_LIGHTBAR BIT(6) +#define SUPPORT_1_FAN_BOOST BIT(7) + +#define SUPPORT_2_ADDR ADDR(0x07, 0x66) +#define SUPPORT_2_SILENT_MODE BIT(0) +#define SUPPORT_2_USB_CHARGING BIT(1) +#define SUPPORT_2_SINGLE_ZONE_KBD BIT(2) +#define SUPPORT_2_CHINA_MODE BIT(5) +#define SUPPORT_2_MY_BATTERY BIT(6) + +#define SUPPORT_5_ADDR ADDR(0x07, 0x42) +#define SUPPORT_5_FAN_TURBO BIT(4) +#define SUPPORT_5_FAN BIT(5) + +#define SUPPORT_6 ADDR(0x07, 0x8E) +#define SUPPORT_6_FAN3P5 BIT(1) + +/* ========================================================================== */ + +#define TRIGGER_1_ADDR ADDR(0x07, 0x67) +#define TRIGGER_1_SUPER_KEY_LOCK BIT(0) +#define TRIGGER_1_LIGHTBAR BIT(1) +#define TRIGGER_1_FAN_BOOST BIT(2) +#define TRIGGER_1_SILENT_MODE BIT(3) +#define TRIGGER_1_USB_CHARGING BIT(4) + +#define TRIGGER_2_ADDR ADDR(0x07, 0x5D) + +/* ========================================================================== */ + +#define PLATFORM_ID_ADDR ADDR(0x04, 0x56) +#define POWER_STATUS_ADDR ADDR(0x04, 0x5E) +#define POWER_SOURCE_ADDR ADDR(0x04, 0x90) + +#define PL1_ADDR ADDR(0x07, 0x83) +#define PL2_ADDR ADDR(0x07, 0x84) +#define PL4_ADDR ADDR(0x07, 0x85) + +/* ========================================================================== */ + +union ac71_ec_result { + uint32_t dword; + struct { + uint16_t w1; + uint16_t w2; + } words; + struct { + uint8_t b1; + uint8_t b2; + uint8_t b3; + uint8_t b4; + } bytes; +}; + +int __must_check ac71_ec_transaction(uint16_t addr, uint16_t data, + union ac71_ec_result *result, bool read); + +static inline __must_check int ac71_ec_read(uint16_t addr, union ac71_ec_result *result) +{ + return ac71_ec_transaction(addr, 0, result, true); +} + +static inline __must_check int ac71_ec_write(uint16_t addr, uint16_t data) +{ + return ac71_ec_transaction(addr, data, NULL, false); +} + +static inline __must_check int ec_write_byte(uint16_t addr, uint8_t data) +{ + return ac71_ec_write(addr, data); +} + +static inline __must_check int ec_read_byte(uint16_t addr) +{ + union ac71_ec_result result; + int err; + + err = ac71_ec_read(addr, &result); + + if (err) + return err; + + return result.bytes.b1; +} + +#endif /* AC71_LAPTOP_EC_H */ diff --git a/spec/reimaden/ac71/events.c b/spec/reimaden/ac71/events.c new file mode 100644 index 00000000..5f3212a1 --- /dev/null +++ b/spec/reimaden/ac71/events.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "pdev.h" +#include "wmi.h" + +/* ========================================================================== */ + +#define KBD_BL_LED_SUFFIX ":" LED_FUNCTION_KBD_BACKLIGHT + +/* ========================================================================== */ + +static struct { + const char *guid; + bool handler_installed; +} ac71_wmi_event_guids[] = { + { .guid = AC71_WMI_EVENT0_GUID }, + { .guid = AC71_WMI_EVENT1_GUID }, + { .guid = AC71_WMI_EVENT2_GUID }, +}; + +static const struct key_entry ac71_wmi_hotkeys[] = { + + /* reported via keyboard controller */ + { KE_IGNORE, 0x01, { KEY_CAPSLOCK }}, + { KE_IGNORE, 0x02, { KEY_NUMLOCK }}, + { KE_IGNORE, 0x03, { KEY_SCROLLLOCK }}, + + /* reported via "video bus" */ + { KE_IGNORE, 0x14, { KEY_BRIGHTNESSUP }}, + { KE_IGNORE, 0x15, { KEY_BRIGHTNESSDOWN }}, + + /* reported via keyboard controller */ + { KE_IGNORE, 0x35, { KEY_MUTE }}, + { KE_IGNORE, 0x36, { KEY_VOLUMEDOWN }}, + { KE_IGNORE, 0x37, { KEY_VOLUMEUP }}, + + /* + * not reported by other means when in manual mode, + * handled automatically when it automatic mode + */ + { KE_KEY, 0xb1, { KEY_KBDILLUMDOWN }}, + { KE_KEY, 0xb2, { KEY_KBDILLUMUP }}, + { KE_KEY, 0xb8, { KEY_FN_ESC }}, + + { KE_END } + +}; + +/* ========================================================================== */ + +static struct input_dev *ac71_input_dev; + +/* ========================================================================== */ + +static void toggle_fn_lock_from_event_handler(void) +{ + int status = ac71_fn_lock_get_state(); + + if (status < 0) + return; + + /* seemingly the returned status in the WMI event handler is not the current */ + pr_info("setting Fn lock state from %d to %d\n", !status, status); + ac71_fn_lock_set_state(status); +} + +#if IS_ENABLED(CONFIG_LEDS_BRIGHTNESS_HW_CHANGED) +extern struct rw_semaphore leds_list_lock; +extern struct list_head leds_list; + +static void emit_keyboard_led_hw_changed(void) +{ + struct led_classdev *led; + + if (down_read_killable(&leds_list_lock)) + return; + + list_for_each_entry (led, &leds_list, node) { + size_t name_length; + const char *suffix; + + if (!(led->flags & LED_BRIGHT_HW_CHANGED)) + continue; + + name_length = strlen(led->name); + + if (name_length < strlen(KBD_BL_LED_SUFFIX)) + continue; + + suffix = led->name + name_length - strlen(KBD_BL_LED_SUFFIX); + + if (strcmp(suffix, KBD_BL_LED_SUFFIX) == 0) { + if (mutex_lock_interruptible(&led->led_access)) + break; + + if (led_update_brightness(led) >= 0) + led_classdev_notify_brightness_hw_changed(led, led->brightness); + + mutex_unlock(&led->led_access); + break; + } + } + + up_read(&leds_list_lock); +} +#endif + +static void ac71_wmi_event_d2_handler(union acpi_object *obj) +{ + bool do_report = true; + + if (!obj || obj->type != ACPI_TYPE_INTEGER) + return; + + switch (obj->integer.value) { + /* caps lock */ + case 1: + pr_info("caps lock\n"); + break; + + /* num lock */ + case 2: + pr_info("num lock\n"); + break; + + /* scroll lock */ + case 3: + pr_info("scroll lock\n"); + break; + + /* touchpad on */ + case 4: + pr_info("touchpad on\n"); + break; + + /* touchpad off */ + case 5: + pr_info("touchpad off\n"); + break; + + /* increase screen brightness */ + case 20: + pr_info("increase screen brightness\n"); + /* do_report = !acpi_video_handles_brightness_key_presses() */ + break; + + /* decrease screen brightness */ + case 21: + pr_info("decrease screen brightness\n"); + /* do_report = !acpi_video_handles_brightness_key_presses() */ + break; + + /* mute/unmute */ + case 53: + pr_info("toggle mute\n"); + break; + + /* decrease volume */ + case 54: + pr_info("decrease volume\n"); + break; + + /* increase volume */ + case 55: + pr_info("increase volume\n"); + break; + + /* enable super key (win key) lock */ + case 64: + pr_info("enable super key lock\n"); + break; + + /* decrease volume */ + case 65: + pr_info("disable super key lock\n"); + break; + + /* enable/disable airplane mode */ + case 164: + pr_info("toggle airplane mode\n"); + break; + + /* super key (win key) lock state changed */ + case 165: + pr_info("super key lock state changed\n"); + sysfs_notify(&ac71_platform_dev->dev.kobj, NULL, "super_key_lock"); + break; + + case 166: + pr_info("lightbar state changed\n"); + break; + + /* fan boost state changed */ + case 167: + pr_info("fan boost state changed\n"); + break; + + /* charger unplugged/plugged in */ + case 171: + pr_info("AC plugged/unplugged\n"); + break; + + /* perf mode button pressed */ + case 176: + pr_info("change perf mode\n"); + /* TODO: should it be handled here? */ + break; + + /* increase keyboard backlight */ + case 177: + pr_info("keyboard backlight decrease\n"); + /* TODO: should it be handled here? */ + break; + + /* decrease keyboard backlight */ + case 178: + pr_info("keyboard backlight increase\n"); + /* TODO: should it be handled here? */ + break; + + /* toggle Fn lock (Fn+ESC)*/ + case 184: + pr_info("toggle Fn lock\n"); + toggle_fn_lock_from_event_handler(); + sysfs_notify(&ac71_platform_dev->dev.kobj, NULL, "fn_lock"); + break; + + /* keyboard backlight brightness changed */ + case 240: + pr_info("keyboard backlight changed\n"); + +#if IS_ENABLED(CONFIG_LEDS_BRIGHTNESS_HW_CHANGED) + emit_keyboard_led_hw_changed(); +#endif + break; + + default: + pr_warn("unknown code: %u\n", (unsigned int) obj->integer.value); + break; + } + + if (do_report && ac71_input_dev) + sparse_keymap_report_event(ac71_input_dev, + obj->integer.value, 1, true); + +} + +static void ac71_wmi_event_handler(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + pr_info("%s(value=%#04x)\n", __func__, (unsigned int) value); + status = wmi_get_event_data(value, &response); + + if (ACPI_FAILURE(status)) { + pr_err("bad WMI event status: %#010x\n", (unsigned int) status); + return; + } + + obj = response.pointer; + + if (obj) { + pr_info("obj->type = %d\n", (int) obj->type); + if (obj->type == ACPI_TYPE_INTEGER) { + pr_info("int = %u\n", (unsigned int) obj->integer.value); + } else if (obj->type == ACPI_TYPE_STRING) { + pr_info("string = '%s'\n", obj->string.pointer); + } else if (obj->type == ACPI_TYPE_BUFFER) { + uint32_t i; + + for (i = 0; i < obj->buffer.length; i++) + pr_info("buf[%u] = %#04x\n", + (unsigned int) i, + (unsigned int) obj->buffer.pointer[i]); + } + } + + switch (value) { + case 0xd2: + ac71_wmi_event_d2_handler(obj); + break; + case 0xd1: + case 0xd0: + break; + } + + kfree(obj); +} + +static int __init setup_input_dev(void) +{ + int err = 0; + + ac71_input_dev = input_allocate_device(); + if (!ac71_input_dev) + return -ENOMEM; + + ac71_input_dev->name = "AC71 laptop input device"; + ac71_input_dev->phys = "ac71/input0"; + ac71_input_dev->id.bustype = BUS_HOST; + ac71_input_dev->dev.parent = &ac71_platform_dev->dev; + + err = sparse_keymap_setup(ac71_input_dev, ac71_wmi_hotkeys, NULL); + if (err) + goto err_free_device; + + err = input_register_device(ac71_input_dev); + if (err) + goto err_free_device; + + return err; + +err_free_device: + input_free_device(ac71_input_dev); + ac71_input_dev = NULL; + + return err; +} + +/* ========================================================================== */ + +int __init ac71_wmi_events_setup(void) +{ + int err = 0, i; + + (void) setup_input_dev(); + + for (i = 0; i < ARRAY_SIZE(ac71_wmi_event_guids); i++) { + const char *guid = ac71_wmi_event_guids[i].guid; + acpi_status status = + wmi_install_notify_handler(guid, ac71_wmi_event_handler, NULL); + + if (ACPI_FAILURE(status)) { + pr_warn("could not install WMI notify handler for '%s': [%#010lx] %s\n", + guid, (unsigned long) status, acpi_format_exception(status)); + } else { + ac71_wmi_event_guids[i].handler_installed = true; + } + } + + return err; +} + +void ac71_wmi_events_cleanup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ac71_wmi_event_guids); i++) { + if (ac71_wmi_event_guids[i].handler_installed) { + wmi_remove_notify_handler(ac71_wmi_event_guids[i].guid); + ac71_wmi_event_guids[i].handler_installed = false; + } + } + + if (ac71_input_dev) + input_unregister_device(ac71_input_dev); +} diff --git a/spec/reimaden/ac71/events.h b/spec/reimaden/ac71/events.h new file mode 100644 index 00000000..313b9487 --- /dev/null +++ b/spec/reimaden/ac71/events.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_WMI_EVENTS_H +#define AC71_WMI_EVENTS_H + +#if IS_ENABLED(CONFIG_LEDS_CLASS) + +#include + +int __init ac71_wmi_events_setup(void); +void ac71_wmi_events_cleanup(void); + +#else + +static inline int ac71_wmi_events_setup(void) +{ + return 0; +} + +static inline void ac71_wmi_events_cleanup(void) +{ + +} + +#endif + +#endif /* AC71_WMI_EVENTS_H */ diff --git a/spec/reimaden/ac71/fan.c b/spec/reimaden/ac71/fan.c new file mode 100644 index 00000000..fb6cb29c --- /dev/null +++ b/spec/reimaden/ac71/fan.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) +static inline int fixp_linear_interpolate(int x0, int y0, int x1, int y1, int x) +{ + if (y0 == y1 || x == x0) + return y0; + if (x1 == x0 || x == x1) + return y1; + + return y0 + ((y1 - y0) * (x - x0) / (x1 - x0)); +} +#else +#include /* fixp-arith.h needs it, but doesn't include it */ +#include +#endif + +#include +#include +#include + +#include "ec.h" +#include "fan.h" +#include "util.h" + +/* ========================================================================== */ + +static const uint16_t ac71_fan_rpm_addrs[] = { + FAN_RPM_1_ADDR, + FAN_RPM_2_ADDR, +}; + +static const uint16_t ac71_fan_pwm_addrs[] = { + FAN_PWM_1_ADDR, + FAN_PWM_2_ADDR, +}; + +static const uint16_t ac71_fan_temp_addrs[] = { + FAN_TEMP_1_ADDR, + FAN_TEMP_2_ADDR, +}; + +/* ========================================================================== */ + +static DEFINE_MUTEX(fan_lock); + +/* ========================================================================== */ + +static int ac71_fan_get_status(void) +{ + return ec_read_byte(FAN_CTRL_ADDR); +} + +/* 'fan_lock' must be held */ +static int ac71_fan_get_mode_unlocked(void) +{ + int err; + + lockdep_assert_held(&fan_lock); + + err = ec_read_byte(CTRL_1_ADDR); + if (err < 0) + return err; + + if (err & CTRL_1_MANUAL_MODE) { + err = ac71_fan_get_status(); + if (err < 0) + return err; + + if (err & FAN_CTRL_FAN_BOOST) { + err = ac71_fan_get_pwm(0); + + if (err < 0) + return err; + + if (err == FAN_MAX_PWM) + err = 0; /* disengaged */ + else + err = 1; /* manual */ + + } else if (err & FAN_CTRL_AUTO) { + err = 2; /* automatic fan control */ + } else { + err = 1; /* manual */ + } + } else { + err = 2; /* automatic fan control */ + } + + return err; +} + +/* ========================================================================== */ + +int ac71_fan_get_rpm(uint8_t fan_index) +{ + union ac71_ec_result res; + int err; + + if (fan_index >= ARRAY_SIZE(ac71_fan_rpm_addrs)) + return -EINVAL; + + err = ac71_ec_read(ac71_fan_rpm_addrs[fan_index], &res); + + if (err) + return err; + + return res.bytes.b1 << 8 | res.bytes.b2; +} + +int ac71_fan_query_abnorm(void) +{ + int res = ec_read_byte(CTRL_1_ADDR); + + if (res < 0) + return res; + + return !!(res & CTRL_1_FAN_ABNORMAL); +} + +int ac71_fan_get_pwm(uint8_t fan_index) +{ + int err; + + if (fan_index >= ARRAY_SIZE(ac71_fan_pwm_addrs)) + return -EINVAL; + + err = ec_read_byte(ac71_fan_pwm_addrs[fan_index]); + if (err < 0) + return err; + + return fixp_linear_interpolate(0, 0, FAN_MAX_PWM, U8_MAX, err); +} + +int ac71_fan_set_pwm(uint8_t fan_index, uint8_t pwm) +{ + if (fan_index >= ARRAY_SIZE(ac71_fan_pwm_addrs)) + return -EINVAL; + + return ec_write_byte(ac71_fan_pwm_addrs[fan_index], + fixp_linear_interpolate(0, 0, + U8_MAX, FAN_MAX_PWM, + pwm)); +} + +int ac71_fan_get_temp(uint8_t fan_index) +{ + if (fan_index >= ARRAY_SIZE(ac71_fan_temp_addrs)) + return -EINVAL; + + return ec_read_byte(ac71_fan_temp_addrs[fan_index]); +} + +int ac71_fan_get_mode(void) +{ + int err = mutex_lock_interruptible(&fan_lock); + + if (err) + return err; + + err = ac71_fan_get_mode_unlocked(); + + mutex_unlock(&fan_lock); + return err; +} + +int ac71_fan_set_mode(uint8_t mode) +{ + int err, oldpwm; + + err = mutex_lock_interruptible(&fan_lock); + if (err) + return err; + + switch (mode) { + case 0: + err = ec_write_byte(FAN_CTRL_ADDR, FAN_CTRL_FAN_BOOST); + if (err) + goto out; + + err = ac71_fan_set_pwm(0, FAN_MAX_PWM); + break; + case 1: + oldpwm = err = ac71_fan_get_pwm(0); + if (err < 0) + goto out; + + err = ec_write_byte(FAN_CTRL_ADDR, FAN_CTRL_FAN_BOOST); + if (err < 0) + goto out; + + err = ac71_fan_set_pwm(0, oldpwm); + if (err < 0) + (void) ec_write_byte(FAN_CTRL_ADDR, 0x80 | FAN_CTRL_AUTO); + /* try to restore automatic fan control */ + + break; + case 2: + err = ec_write_byte(FAN_CTRL_ADDR, 0x80 | FAN_CTRL_AUTO); + break; + default: + err = -EINVAL; + break; + } + +out: + mutex_unlock(&fan_lock); + return err; +} diff --git a/spec/reimaden/ac71/fan.h b/spec/reimaden/ac71/fan.h new file mode 100644 index 00000000..deab2b99 --- /dev/null +++ b/spec/reimaden/ac71/fan.h @@ -0,0 +1,22 @@ +#ifndef AC71_LAPTOP_FAN_H +#define AC71_LAPTOP_FAN_H + +#include + +/* ========================================================================== */ + +#define FAN_MAX_PWM 200 +#define FAN_CTRL_MAX_LEVEL 7 +#define FAN_CTRL_LEVEL(level) (128 + (level)) + +/* ========================================================================== */ + +int ac71_fan_get_rpm(uint8_t fan_index); +int ac71_fan_query_abnorm(void); +int ac71_fan_get_pwm(uint8_t fan_index); +int ac71_fan_set_pwm(uint8_t fan_index, uint8_t pwm); +int ac71_fan_get_temp(uint8_t fan_index); +int ac71_fan_get_mode(void); +int ac71_fan_set_mode(uint8_t mode); + +#endif /* AC71_LAPTOP_FAN_H */ diff --git a/spec/reimaden/ac71/hwmon.c b/spec/reimaden/ac71/hwmon.c new file mode 100644 index 00000000..5098dead --- /dev/null +++ b/spec/reimaden/ac71/hwmon.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include + +#include "hwmon_fan.h" +#include "hwmon_pwm.h" + +/* ========================================================================== */ + +static bool nohwmon; +module_param(nohwmon, bool, 0444); +MODULE_PARM_DESC(nohwmon, "do not report to the hardware monitoring subsystem (default=false)"); + +/* ========================================================================== */ + +int __init ac71_hwmon_setup(void) +{ + if (nohwmon) + return -ENODEV; + + (void) ac71_hwmon_fan_setup(); + (void) ac71_hwmon_pwm_setup(); + + return 0; +} + +void ac71_hwmon_cleanup(void) +{ + (void) ac71_hwmon_fan_cleanup(); + (void) ac71_hwmon_pwm_cleanup(); +} diff --git a/spec/reimaden/ac71/hwmon.h b/spec/reimaden/ac71/hwmon.h new file mode 100644 index 00000000..13489234 --- /dev/null +++ b/spec/reimaden/ac71/hwmon.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_HWMON_H +#define AC71_HWMON_H + +#if IS_ENABLED(CONFIG_HWMON) + +#include + +int __init ac71_hwmon_setup(void); +void ac71_hwmon_cleanup(void); + +#else + +static inline int ac71_hwmon_setup(void) +{ + return 0; +} + +static inline void ac71_hwmon_cleanup(void) +{ + +} + +#endif + +#endif /* AC71_HWMON_H */ diff --git a/spec/reimaden/ac71/hwmon_fan.c b/spec/reimaden/ac71/hwmon_fan.c new file mode 100644 index 00000000..48df41a1 --- /dev/null +++ b/spec/reimaden/ac71/hwmon_fan.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include +#include +#include + +#include "ec.h" +#include "fan.h" +#include "pdev.h" + +/* ========================================================================== */ + +static struct device *ac71_hwmon_fan_dev; + +/* ========================================================================== */ + +static umode_t ac71_hwmon_fan_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + return 0444; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; + } + default: + break; + } + + return 0; +} + +static int ac71_hwmon_fan_read(struct device *device, enum hwmon_sensor_types type, + u32 attr, int channel, long *value) +{ + int err; + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + err = ac71_fan_get_rpm(channel); + if (err < 0) + return err; + + *value = err; + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + err = ac71_fan_get_temp(channel); + if (err < 0) + return err; + + *value = err * 1000; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ac71_hwmon_fan_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + static const char * const temp_labels[] = { + "fan1_temp", + "fan2_temp", + }; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_label: + *str = temp_labels[channel]; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* ========================================================================== */ + +static const struct hwmon_channel_info *ac71_hwmon_fan_ch_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT, + HWMON_F_INPUT), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + NULL +}; + +static const struct hwmon_ops ac71_hwmon_fan_ops = { + .is_visible = ac71_hwmon_fan_is_visible, + .read = ac71_hwmon_fan_read, + .read_string = ac71_hwmon_fan_read_string, +}; + +static const struct hwmon_chip_info ac71_hwmon_fan_chip_info = { + .ops = &ac71_hwmon_fan_ops, + .info = ac71_hwmon_fan_ch_info, +}; + +/* ========================================================================== */ + +int __init ac71_hwmon_fan_setup(void) +{ + int err = 0; + + ac71_hwmon_fan_dev = hwmon_device_register_with_info( + &ac71_platform_dev->dev, KBUILD_MODNAME ".hwmon.fan", NULL, + &ac71_hwmon_fan_chip_info, NULL); + + if (IS_ERR(ac71_hwmon_fan_dev)) + err = PTR_ERR(ac71_hwmon_fan_dev); + + return err; +} + +void ac71_hwmon_fan_cleanup(void) +{ + if (!IS_ERR_OR_NULL(ac71_hwmon_fan_dev)) + hwmon_device_unregister(ac71_hwmon_fan_dev); +} diff --git a/spec/reimaden/ac71/hwmon_fan.h b/spec/reimaden/ac71/hwmon_fan.h new file mode 100644 index 00000000..ce3b6ca4 --- /dev/null +++ b/spec/reimaden/ac71/hwmon_fan.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_HWMON_FAN_H +#define AC71_HWMON_FAN_H + +#include + +int __init ac71_hwmon_fan_setup(void); +void ac71_hwmon_fan_cleanup(void); + +#endif /* AC71_HWMON_FAN_H */ diff --git a/spec/reimaden/ac71/hwmon_pwm.c b/spec/reimaden/ac71/hwmon_pwm.c new file mode 100644 index 00000000..b0a39388 --- /dev/null +++ b/spec/reimaden/ac71/hwmon_pwm.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fan.h" +#include "pdev.h" +#include "util.h" + +/* ========================================================================== */ + +static struct device *ac71_hwmon_pwm_dev; + +/* ========================================================================== */ + +static umode_t ac71_hwmon_pwm_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_pwm && attr != hwmon_pwm_enable) + return -EOPNOTSUPP; + + return 0644; +} + +static int ac71_hwmon_pwm_read(struct device *device, enum hwmon_sensor_types type, + u32 attr, int channel, long *value) +{ + int err; + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + err = ac71_fan_get_mode(); + if (err < 0) + return err; + + *value = err; + break; + case hwmon_pwm_input: + err = ac71_fan_get_pwm(channel); + if (err < 0) + return err; + + *value = err; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ac71_hwmon_pwm_write(struct device *device, enum hwmon_sensor_types type, + u32 attr, int channel, long value) +{ + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + return ac71_fan_set_mode(value); + case hwmon_pwm_input: + return ac71_fan_set_pwm(channel, value); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct hwmon_channel_info *ac71_hwmon_pwm_ch_info[] = { + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT, HWMON_PWM_INPUT), + NULL +}; + +static const struct hwmon_ops ac71_hwmon_pwm_ops = { + .is_visible = ac71_hwmon_pwm_is_visible, + .read = ac71_hwmon_pwm_read, + .write = ac71_hwmon_pwm_write, +}; + +static const struct hwmon_chip_info ac71_hwmon_pwm_chip_info = { + .ops = &ac71_hwmon_pwm_ops, + .info = ac71_hwmon_pwm_ch_info, +}; + +/* ========================================================================== */ + +int __init ac71_hwmon_pwm_setup(void) +{ + int err = 0; + + ac71_hwmon_pwm_dev = hwmon_device_register_with_info( + &ac71_platform_dev->dev, KBUILD_MODNAME ".hwmon.pwm", NULL, + &ac71_hwmon_pwm_chip_info, NULL); + + if (IS_ERR(ac71_hwmon_pwm_dev)) + err = PTR_ERR(ac71_hwmon_pwm_dev); + + return err; +} + +void ac71_hwmon_pwm_cleanup(void) +{ + if (!IS_ERR_OR_NULL(ac71_hwmon_pwm_dev)) + hwmon_device_unregister(ac71_hwmon_pwm_dev); +} diff --git a/spec/reimaden/ac71/hwmon_pwm.h b/spec/reimaden/ac71/hwmon_pwm.h new file mode 100644 index 00000000..8c5110a3 --- /dev/null +++ b/spec/reimaden/ac71/hwmon_pwm.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_HWMON_PWM_H +#define AC71_HWMON_PWM_H + +#include + +int __init ac71_hwmon_pwm_setup(void); +void ac71_hwmon_pwm_cleanup(void); + +#endif /* AC71_HWMON_PWM_H */ diff --git a/spec/reimaden/ac71/main.c b/spec/reimaden/ac71/main.c new file mode 100644 index 00000000..fbc5449b --- /dev/null +++ b/spec/reimaden/ac71/main.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "ec.h" +#include "wmi.h" + +/* submodules */ +#include "pdev.h" +#include "events.h" +#include "hwmon.h" +#include "battery.h" + +/* ========================================================================== */ + +#define SUBMODULE_ENTRY(_name, _req) { .name = #_name, .init = ac71_ ## _name ## _setup, .cleanup = ac71_ ## _name ## _cleanup, .required = _req } + +static struct ac71_submodule { + const char *name; + + bool required : 1, + initialized : 1; + + int (*init)(void); + void (*cleanup)(void); +} ac71_submodules[] __refdata = { + SUBMODULE_ENTRY(pdev, true), /* must be first */ + SUBMODULE_ENTRY(wmi_events, false), + SUBMODULE_ENTRY(hwmon, false), + SUBMODULE_ENTRY(battery, false), +}; + +#undef SUBMODULE_ENTRY + +static void do_cleanup(void) +{ + int i; + + for (i = ARRAY_SIZE(ac71_submodules) - 1; i >= 0; i--) { + const struct ac71_submodule *sm = &ac71_submodules[i]; + + if (sm->initialized) + sm->cleanup(); + } +} + +static const struct dmi_system_id ac71_dmi_table[] __initconst = { + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "LAPAC71H"), + { } + } + } +}; + +static int __init ac71_module_init(void) +{ + int err = 0, i; + + if (!wmi_has_guid(AC71_WMI_WMBC_GUID)) { + pr_err("WMI GUID not found\n"); + err = -ENODEV; goto out; + } + + if (!dmi_check_system(ac71_dmi_table)) { + pr_err("no DMI match\n"); + err = -ENODEV; goto out; + } + + err = ec_read_byte(PROJ_ID_ADDR); + if (err < 0) { + pr_err("failed to query project id: %d\n", err); + goto out; + } + + pr_info("project id: %d\n", err); + + err = ec_read_byte(PLATFORM_ID_ADDR); + if (err < 0) { + pr_err("failed to query platform id: %d\n", err); + goto out; + } + + pr_info("platform id: %d\n", err); + + for (i = 0; i < ARRAY_SIZE(ac71_submodules); i++) { + struct ac71_submodule *sm = &ac71_submodules[i]; + + err = sm->init(); + if (err) { + pr_warn("failed to initialize %s submodule: %d\n", sm->name, err); + if (sm->required) + goto out; + } else { + sm->initialized = true; + } + } + + err = 0; + +out: + if (err) + do_cleanup(); + else + pr_info("module loaded\n"); + + return err; +} + +static void __exit ac71_module_cleanup(void) +{ + do_cleanup(); + pr_info("module unloaded\n"); +} + +/* ========================================================================== */ + +module_init(ac71_module_init); +module_exit(ac71_module_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AC71 laptop platform driver"); +MODULE_ALIAS("wmi:" AC71_WMI_WMBC_GUID); diff --git a/spec/reimaden/ac71/misc.c b/spec/reimaden/ac71/misc.c new file mode 100644 index 00000000..8c4c49d0 --- /dev/null +++ b/spec/reimaden/ac71/misc.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include + +#include "ec.h" +#include "misc.h" +#include "util.h" + +/* ========================================================================== */ + +int ac71_fn_lock_get_state(void) +{ + int status = ec_read_byte(BIOS_CTRL_1_ADDR); + + if (status < 0) + return status; + + return !!(status & BIOS_CTRL_1_FN_LOCK_STATUS); +} + +int ac71_fn_lock_set_state(bool state) +{ + int status = ec_read_byte(BIOS_CTRL_1_ADDR); + + if (status < 0) + return status; + + status = SET_BIT(status, BIOS_CTRL_1_FN_LOCK_STATUS, state); + + return ec_write_byte(BIOS_CTRL_1_ADDR, status); +} diff --git a/spec/reimaden/ac71/misc.h b/spec/reimaden/ac71/misc.h new file mode 100644 index 00000000..3b394d8a --- /dev/null +++ b/spec/reimaden/ac71/misc.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_MISC_H +#define AC71_MISC_H + +#include + +/* ========================================================================== */ + +int ac71_fn_lock_get_state(void); +int ac71_fn_lock_set_state(bool state); + +#endif /* AC71_MISC_H */ diff --git a/spec/reimaden/ac71/package.nix b/spec/reimaden/ac71/package.nix new file mode 100644 index 00000000..7d7fba1f --- /dev/null +++ b/spec/reimaden/ac71/package.nix @@ -0,0 +1,19 @@ +{ stdenv +, lib +, kernel +, kmod }: stdenv.mkDerivation rec { + name = "ac71-${version}-${kernel.version}"; + version = "2024.2.13"; + + # adapted from https://github.com/pobrn/qc71_laptop + src = ./.; + + hardeningDisable = [ "pic" "format" ]; + nativeBuildInputs = kernel.moduleBuildDependencies; + + makeFlags = [ + "KERNELRELEASE=${kernel.modDirVersion}" + "KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" + "INSTALL_MOD_PATH=$(out)" + ]; +} diff --git a/spec/reimaden/ac71/pdev.c b/spec/reimaden/ac71/pdev.c new file mode 100644 index 00000000..ec2ceed9 --- /dev/null +++ b/spec/reimaden/ac71/pdev.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "pr.h" + +#include +#include +#include +#include +#include + +#include "util.h" +#include "ec.h" +#include "misc.h" +#include "pdev.h" + +/* ========================================================================== */ + +struct platform_device *ac71_platform_dev; + +/* ========================================================================== */ + +static ssize_t fan_reduced_duty_cycle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = ec_read_byte(BIOS_CTRL_3_ADDR); + + if (status < 0) + return status; + + return sprintf(buf, "%d\n", !!(status & BIOS_CTRL_3_FAN_REDUCED_DUTY_CYCLE)); +} + +static ssize_t fan_reduced_duty_cycle_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + bool value; + + if (kstrtobool(buf, &value)) + return -EINVAL; + + status = ec_read_byte(BIOS_CTRL_3_ADDR); + if (status < 0) + return status; + + status = SET_BIT(status, BIOS_CTRL_3_FAN_REDUCED_DUTY_CYCLE, value); + + status = ec_write_byte(BIOS_CTRL_3_ADDR, status); + + if (status < 0) + return status; + + return count; +} + +static ssize_t fan_always_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = ec_read_byte(BIOS_CTRL_3_ADDR); + + if (status < 0) + return status; + + return sprintf(buf, "%d\n", !!(status & BIOS_CTRL_3_FAN_ALWAYS_ON)); +} + +static ssize_t fan_always_on_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + bool value; + + if (kstrtobool(buf, &value)) + return -EINVAL; + + status = ec_read_byte(BIOS_CTRL_3_ADDR); + if (status < 0) + return status; + + status = SET_BIT(status, BIOS_CTRL_3_FAN_ALWAYS_ON, value); + + status = ec_write_byte(BIOS_CTRL_3_ADDR, status); + + if (status < 0) + return status; + + return count; +} + +static ssize_t fn_lock_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = ac71_fn_lock_get_state(); + + if (status < 0) + return status; + + return sprintf(buf, "%d\n", status); +} + +static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + bool value; + + if (kstrtobool(buf, &value)) + return -EINVAL; + + status = ac71_fn_lock_set_state(value); + if (status < 0) + return status; + + return count; +} + +static ssize_t fn_lock_switch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = ec_read_byte(AP_BIOS_BYTE_ADDR); + + if (status < 0) + return status; + + return sprintf(buf, "%d\n", !!(status & AP_BIOS_BYTE_FN_LOCK_SWITCH)); +} + +static ssize_t fn_lock_switch_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + bool value; + + if (kstrtobool(buf, &value)) + return -EINVAL; + + status = ec_read_byte(AP_BIOS_BYTE_ADDR); + if (status < 0) + return status; + + status = SET_BIT(status, AP_BIOS_BYTE_FN_LOCK_SWITCH, value); + + status = ec_write_byte(AP_BIOS_BYTE_ADDR, status); + + if (status < 0) + return status; + + return count; +} + +static ssize_t manual_control_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = ec_read_byte(CTRL_1_ADDR); + + if (status < 0) + return status; + + return sprintf(buf, "%d\n", !!(status & CTRL_1_MANUAL_MODE)); +} + +static ssize_t manual_control_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + bool value; + + if (kstrtobool(buf, &value)) + return -EINVAL; + + status = ec_read_byte(CTRL_1_ADDR); + if (status < 0) + return status; + + status = SET_BIT(status, CTRL_1_MANUAL_MODE, value); + + status = ec_write_byte(CTRL_1_ADDR, status); + + if (status < 0) + return status; + + return count; +} + +static ssize_t super_key_lock_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = ec_read_byte(STATUS_1_ADDR); + + if (status < 0) + return status; + + return sprintf(buf, "%d\n", !!(status & STATUS_1_SUPER_KEY_LOCK)); +} + +static ssize_t super_key_lock_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int status; + bool value; + + if (kstrtobool(buf, &value)) + return -EINVAL; + + status = ec_read_byte(STATUS_1_ADDR); + if (status < 0) + return status; + + if (value != !!(status & STATUS_1_SUPER_KEY_LOCK)) { + status = ec_write_byte(TRIGGER_1_ADDR, TRIGGER_1_SUPER_KEY_LOCK); + + if (status < 0) + return status; + } + + return count; +} + +/* ========================================================================== */ + +static DEVICE_ATTR_RW(fn_lock); +static DEVICE_ATTR_RW(fn_lock_switch); +static DEVICE_ATTR_RW(fan_always_on); +static DEVICE_ATTR_RW(fan_reduced_duty_cycle); +static DEVICE_ATTR_RW(manual_control); +static DEVICE_ATTR_RW(super_key_lock); + +static struct attribute *ac71_attrs[] = { + &dev_attr_fn_lock.attr, + &dev_attr_fn_lock_switch.attr, + &dev_attr_fan_always_on.attr, + &dev_attr_fan_reduced_duty_cycle.attr, + &dev_attr_manual_control.attr, + &dev_attr_super_key_lock.attr, + NULL +}; + +/* ========================================================================== */ + +static umode_t ac71_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + bool ok = false; + + if (attr == &dev_attr_fn_lock.attr || attr == &dev_attr_fn_lock_switch.attr) + ok = true; + else if (attr == &dev_attr_fan_always_on.attr || attr == &dev_attr_fan_reduced_duty_cycle.attr) + ok = true; + else if (attr == &dev_attr_manual_control.attr) + ok = true; + else if (attr == &dev_attr_super_key_lock.attr) + ok = false; + + return ok ? attr->mode : 0; +} + +/* ========================================================================== */ + +static const struct attribute_group ac71_group = { + .is_visible = ac71_attr_is_visible, + .attrs = ac71_attrs, +}; + +static const struct attribute_group *ac71_groups[] = { + &ac71_group, + NULL +}; + +/* ========================================================================== */ + +int __init ac71_pdev_setup(void) +{ + int err; + + ac71_platform_dev = platform_device_alloc(KBUILD_MODNAME, PLATFORM_DEVID_NONE); + if (!ac71_platform_dev) { + err = -ENOMEM; + goto out; + } + + ac71_platform_dev->dev.groups = ac71_groups; + + err = platform_device_add(ac71_platform_dev); + if (err) { + platform_device_put(ac71_platform_dev); + ac71_platform_dev = NULL; + } + +out: + return err; +} + +void ac71_pdev_cleanup(void) +{ + /* checks for IS_ERR_OR_NULL() */ + platform_device_unregister(ac71_platform_dev); +} diff --git a/spec/reimaden/ac71/pdev.h b/spec/reimaden/ac71/pdev.h new file mode 100644 index 00000000..33e176a5 --- /dev/null +++ b/spec/reimaden/ac71/pdev.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_PDEV_H +#define AC71_PDEV_H + +#include +#include + +/* ========================================================================== */ + +extern struct platform_device *ac71_platform_dev; + +/* ========================================================================== */ + +int __init ac71_pdev_setup(void); +void ac71_pdev_cleanup(void); + +#endif /* AC71_PDEV_H */ diff --git a/spec/reimaden/ac71/pr.h b/spec/reimaden/ac71/pr.h new file mode 100644 index 00000000..853a4e21 --- /dev/null +++ b/spec/reimaden/ac71/pr.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_PR_H +#define AC71_PR_H + +#define pr_fmt(fmt) KBUILD_MODNAME "/" KBUILD_BASENAME ": %s: " fmt, __func__ + +#include + +#endif /* AC71_PR_H */ diff --git a/spec/reimaden/ac71/util.h b/spec/reimaden/ac71/util.h new file mode 100644 index 00000000..7a8b356b --- /dev/null +++ b/spec/reimaden/ac71/util.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_UTIL_H +#define AC71_UTIL_H + +#define SET_BIT(value, bit, on) ((on) ? ((value) | (bit)) : ((value) & ~(bit))) + +#endif /* AC71_UTIL_H */ diff --git a/spec/reimaden/ac71/wmi.h b/spec/reimaden/ac71/wmi.h new file mode 100644 index 00000000..7fd2172d --- /dev/null +++ b/spec/reimaden/ac71/wmi.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef AC71_LAPTOP_WMI_H +#define AC71_LAPTOP_WMI_H + +/* ========================================================================== */ +/* WMI methods */ + +/* AcpiTest_MULong */ +#define AC71_WMI_WMBC_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000" +#define AC71_WMBC_GETSETULONG_ID 4 + +/* ========================================================================== */ +/* WMI events */ + +/* AcpiTest_EventULong */ +#define AC71_WMI_EVENT0_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" + +/* AcpiTest_EventString */ +#define AC71_WMI_EVENT1_GUID "ABBC0F71-8EA1-11D1-00A0-C90629100000" + +/* AcpiTest_EventPackage */ +#define AC71_WMI_EVENT2_GUID "ABBC0F70-8EA1-11D1-00A0-C90629100000" + +#endif /* AC71_LAPTOP_WMI_H */ diff --git a/spec/reimaden/default.nix b/spec/reimaden/default.nix new file mode 100644 index 00000000..10962ef7 --- /dev/null +++ b/spec/reimaden/default.nix @@ -0,0 +1,42 @@ +{ + global = { + id = "6b564d01975a4e93b4a51f34ba24e5b4"; + fs.esp.uuid = "FC6D-75D8"; + fs.type = "zfs"; + boot.lanzaboote = true; + gpu.enable = true; + gpu.type = "intel"; + gpu.arc = "8086:5691"; + android.enable = true; + }; + + home = { + gnome.enable = true; + #headless.enable = "eDP-1"; + gyroflow.enable = true; + jetbrains.enable = true; + vscode.enable = true; + libreoffice.enable = true; + steam.enable = true; + }; + + home.plasma.extraConfig = { + configFile = { + kcminputrc."Libinput/2362/597/UNIW0001:00 093A:0255 Touchpad".NaturalScroll = true; + }; + }; + + powerManagement.enable = true; + powerManagement.cpuFreqGovernor = "powersave"; + boot.kernelParams = [ "zfs.zfs_arc_max=34359738368" ]; + boot.loader.timeout = 2; + + hardware.enableRedistributableFirmware = true; + boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + hardware.cpu.intel.updateMicrocode = true; + + imports = [ ./ac71 ]; +} diff --git a/spec/shatter/default.nix b/spec/shatter/default.nix new file mode 100644 index 00000000..9602114c --- /dev/null +++ b/spec/shatter/default.nix @@ -0,0 +1,34 @@ +{ lib +, pkgs +, ... }: { + global = { + id = "f16d3a87afdd4eb2b1fa4c098d4a9c1a"; + auth.openssh.enable = true; + fs.esp.uuid = "153D-AA49"; + fs.type = "xfs"; + fs.store = "67878f8f-3724-4551-a2e2-f43d674e1d93"; + fs.cryptsetup.enable = true; + fs.cryptsetup.uuids.persist = "3633e292-59e8-497d-9e51-0186ca73c25c"; + gpu.enable = true; + gpu.type = "intel"; + gpu.session = false; + boot.lanzaboote = true; + }; + + services.udev.extraRules = '' + SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="88:c9:b3:b5:15:0e", NAME="i225-v" + SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="8c:a6:82:70:08:ca", NAME="aqc113c" + ''; + boot.kernelParams = [ "zfs.zfs_arc_max=2147483648" ]; + networking.firewall.checkReversePath = false; + boot.swraid.enable = true; + boot.swraid.mdadmConf = '' + PROGRAM /run/current-system/sw/bin/true + ''; + + hardware.enableRedistributableFirmware = true; + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ "i915" ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; +} diff --git a/spec/shinkirou/default.nix b/spec/shinkirou/default.nix new file mode 100644 index 00000000..d2594a80 --- /dev/null +++ b/spec/shinkirou/default.nix @@ -0,0 +1,71 @@ +{ lib +, pkgs +, config +, ... }: { + global = { + id = "23c46cfc573f4f889af9ba5d4678c2ab"; + fs.esp.uuid = "6695-0740"; + fs.type = "zfs"; + boot.lanzaboote = true; + gpu.enable = true; + gpu.type = "prime"; + android.enable = true; + asusd.enable = true; + }; + + home = { + plasma.specialise = true; + gyroflow.enable = true; + vscode.enable = true; + libreoffice.enable = true; + minecraft.enable = true; + steam.enable = true; + }; + + users.homeModules = [ { + wayland.windowManager.sway.config = { + input."1267:16857:ELAN9009:00_04F3:41D9".map_to_output = "DP-3"; + output = { + eDP-1.scale = "1.5"; + eDP-1.pos = "0 0"; + DP-3.scale = "2.25"; + DP-3.pos = "0 1066"; + }; + }; + } ]; + + home.plasma.extraConfig = { + configFile = { + kwinrc.Xwayland.Scale = 2.25; + kcminputrc."Libinput/1267/16857/ELAN9009:00 04F3:41D9".OutputName = "DP-3"; + kcminputrc."Libinput/1267/12962/ASUE140F:00 04F3:32A2 Touchpad".NaturalScroll = true; + kcminputrc."Libinput/2821/6523/ASUSTeK ROG GLADIUS III".NaturalScroll = true; + }; + }; + + powerManagement.enable = true; + powerManagement.cpuFreqGovernor = "performance"; + boot.kernelParams = [ "zfs.zfs_arc_max=34359738368" ]; + console.packages = [ pkgs.terminus_font ]; + console.font = "ter-v32n"; + console.earlySetup = true; + + hardware.nvidia.prime = { + amdgpuBusId = "PCI:9:0:0"; + nvidiaBusId = "PCI:1:0:0"; + }; + + services.greetd.settings.default_session.command = let + cfg = config.programs.regreet; + in "${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} ${lib.escapeShellArgs cfg.cageArgs} -- ${pkgs.writeShellScript "cageResolution" '' + ${lib.getExe pkgs.wlr-randr} --output DP-3 --scale 2.25 + ${lib.getExe cfg.package} + ''}"; + + hardware.enableRedistributableFirmware = true; + boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + hardware.cpu.amd.updateMicrocode = true; +} diff --git a/spec/web/default.nix b/spec/web/default.nix new file mode 100644 index 00000000..f3d240db --- /dev/null +++ b/spec/web/default.nix @@ -0,0 +1,23 @@ +{ lib +, ... }: { + global = { + id = "63795fdf54e048dcbefcbc525ec3779d"; + auth.openssh.enable = true; + libvirt.enable = false; + fs.esp.uuid = "3838-0946"; + fs.type = "xfs"; + fs.store = "8476f738-b83b-4736-abd7-08a1943cf60a"; + lowmem.enable = true; + acme.enable = true; + }; + + home = { + util.minimal = true; + }; + + imports = lib.pipe ./. [ + builtins.readDir + (lib.filterAttrs (n: ty: ty == "regular" && n != "default.nix")) + (lib.mapAttrsToList (n: _: ./${n})) + ]; +} diff --git a/spec/web/google-compute-config.nix b/spec/web/google-compute-config.nix new file mode 100644 index 00000000..8b878f31 --- /dev/null +++ b/spec/web/google-compute-config.nix @@ -0,0 +1,90 @@ +{ pkgs +, lib +, config +, modulesPath +, ... }: + +let + inherit (lib) + boolToString + mkDefault + mkIf + optional + readFile + ; +in + +{ + imports = [ + #(modulesPath + "/profiles/headless.nix") + (modulesPath + "/profiles/qemu-guest.nix") + ]; + + + boot.kernelParams = [ "console=ttyS0,115200n8" "panic=1" "boot.panic_on_fail" ]; + boot.initrd.kernelModules = [ "virtio_scsi" ]; + boot.kernelModules = [ "virtio_pci" "virtio_net" ]; + + # enable OS Login. This also requires setting enable-oslogin=TRUE metadata on + # instance or project level + security.googleOsLogin.enable = true; + + # Use GCE udev rules for dynamic disk volumes + services.udev.packages = [ pkgs.google-guest-configs ]; + services.udev.path = [ pkgs.google-guest-configs ]; + + # Configure default metadata hostnames + networking.extraHosts = '' + 169.254.169.254 metadata.google.internal metadata + ''; + + networking.timeServers = [ "metadata.google.internal" ]; + + networking.usePredictableInterfaceNames = false; + + # GC has 1460 MTU + networking.interfaces.eth0.mtu = 1460; + + systemd.packages = [ pkgs.google-guest-agent ]; + systemd.services.google-guest-agent = { + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ config.environment.etc."default/instance_configs.cfg".source ]; + path = optional config.users.mutableUsers pkgs.shadow; + }; + systemd.services.google-startup-scripts.wantedBy = [ "multi-user.target" ]; + systemd.services.google-shutdown-scripts.wantedBy = [ "multi-user.target" ]; + + security.sudo.extraRules = mkIf config.users.mutableUsers [ + { groups = [ "google-sudoers" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; } + ]; + + security.sudo-rs.extraRules = mkIf config.users.mutableUsers [ + { groups = [ "google-sudoers" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; } + ]; + + users.groups.google-sudoers = mkIf config.users.mutableUsers { }; + + boot.extraModprobeConfig = readFile "${pkgs.google-guest-configs}/etc/modprobe.d/gce-blacklist.conf"; + + environment.etc."sysctl.d/60-gce-network-security.conf".source = "${pkgs.google-guest-configs}/etc/sysctl.d/60-gce-network-security.conf"; + + environment.etc."default/instance_configs.cfg".text = '' + [Accounts] + useradd_cmd = useradd -m -s /run/current-system/sw/bin/bash -p * {user} + + [Daemons] + accounts_daemon = ${boolToString config.users.mutableUsers} + + [InstanceSetup] + # Make sure GCE image does not replace host key that NixOps sets. + set_host_keys = false + + [MetadataScripts] + default_shell = ${pkgs.stdenv.shell} + + [NetworkInterfaces] + dhclient_script = ${pkgs.google-guest-configs}/bin/google-dhclient-script + # We set up network interfaces declaratively. + setup = false + ''; +} diff --git a/spec/web/nginx.nix b/spec/web/nginx.nix new file mode 100644 index 00000000..fa4cb3da --- /dev/null +++ b/spec/web/nginx.nix @@ -0,0 +1,51 @@ +{ pkgs +, ...}: { + services.nginx = { + enable = true; + + # enable all recommended settings + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + + virtualHosts = let + mkRedirect = host: { + locations."/".return = "307 https://${host}$request_uri"; + forceSSL = true; + enableACME = true; + }; + in { + "_" = { + rejectSSL = true; + extraConfig = "return 444;"; + }; + + "514fpv.one" = { + root = pkgs.callPackage ./site { }; + forceSSL = true; + enableACME = true; + }; + + "www.514fpv.one" = { + globalRedirect = "514fpv.one"; + enableACME = true; + }; + + "uptime.514fpv.one" = { + locations."/" = { + proxyPass = "http://127.0.0.1:4000"; + proxyWebsockets = true; + }; + forceSSL = true; + enableACME = true; + }; + + # redirections to home server + "src.514fpv.one" = mkRedirect "src.514fpv.io:2096"; + "cloud.514fpv.one" = mkRedirect "cloud.514fpv.io:2096"; + }; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; +} diff --git a/spec/web/site/Gemfile b/spec/web/site/Gemfile new file mode 100644 index 00000000..b6961f93 --- /dev/null +++ b/spec/web/site/Gemfile @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "jekyll-theme-chirpy", "~> 6.4", ">= 6.4.2" + +group :test do + gem "html-proofer", "~> 4.4" +end + +# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem +# and associated library. +platforms :mingw, :x64_mingw, :mswin, :jruby do + gem "tzinfo", ">= 1", "< 3" + gem "tzinfo-data" +end + +# Performance-booster for watching directories on Windows +gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] + +# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem +# do not have a Java counterpart. +gem "http_parser.rb", "~> 0.6.0"#, :platforms => [:jruby] diff --git a/spec/web/site/Gemfile.lock b/spec/web/site/Gemfile.lock new file mode 100644 index 00000000..fd1b90e6 --- /dev/null +++ b/spec/web/site/Gemfile.lock @@ -0,0 +1,118 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + colorator (1.1.0) + concurrent-ruby (1.2.3) + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + ethon (0.16.0) + ffi (>= 1.15.0) + eventmachine (1.2.7) + ffi (1.16.3) + forwardable-extended (2.6.0) + google-protobuf (3.25.3) + html-proofer (4.4.3) + addressable (~> 2.3) + mercenary (~> 0.3) + nokogiri (~> 1.13) + parallel (~> 1.10) + rainbow (~> 3.0) + typhoeus (~> 1.3) + yell (~> 2.0) + zeitwerk (~> 2.5) + http_parser.rb (0.6.0) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + jekyll (4.3.3) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 1.0) + jekyll-sass-converter (>= 2.0, < 4.0) + jekyll-watch (~> 2.0) + kramdown (~> 2.3, >= 2.3.1) + kramdown-parser-gfm (~> 1.0) + liquid (~> 4.0) + mercenary (>= 0.3.6, < 0.5) + pathutil (~> 0.9) + rouge (>= 3.0, < 5.0) + safe_yaml (~> 1.0) + terminal-table (>= 1.8, < 4.0) + webrick (~> 1.7) + jekyll-archives (2.2.1) + jekyll (>= 3.6, < 5.0) + jekyll-include-cache (0.2.1) + jekyll (>= 3.7, < 5.0) + jekyll-paginate (1.1.0) + jekyll-redirect-from (0.16.0) + jekyll (>= 3.3, < 5.0) + jekyll-sass-converter (3.0.0) + sass-embedded (~> 1.54) + jekyll-seo-tag (2.8.0) + jekyll (>= 3.8, < 5.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-theme-chirpy (6.5.0) + jekyll (~> 4.3) + jekyll-archives (~> 2.2) + jekyll-include-cache (~> 0.2) + jekyll-paginate (~> 1.1) + jekyll-redirect-from (~> 0.16) + jekyll-seo-tag (~> 2.8) + jekyll-sitemap (~> 1.4) + jekyll-watch (2.2.1) + listen (~> 3.0) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.4) + listen (3.8.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.4.0) + mini_portile2 (2.8.5) + nokogiri (1.16.2) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + parallel (1.24.0) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (5.0.4) + racc (1.7.3) + rainbow (3.1.1) + rake (13.1.0) + rb-fsevent (0.11.2) + rb-inotify (0.10.1) + ffi (~> 1.0) + rexml (3.2.6) + rouge (4.2.0) + safe_yaml (1.0.5) + sass-embedded (1.71.1) + google-protobuf (~> 3.25) + rake (>= 13.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + typhoeus (1.4.1) + ethon (>= 0.9.0) + unicode-display_width (2.5.0) + webrick (1.8.1) + yell (2.2.2) + zeitwerk (2.6.13) + +PLATFORMS + ruby + +DEPENDENCIES + html-proofer (~> 4.4) + http_parser.rb (~> 0.6.0) + jekyll-theme-chirpy (~> 6.4, >= 6.4.2) + tzinfo (>= 1, < 3) + tzinfo-data + wdm (~> 0.1.1) + +BUNDLED WITH + 2.5.5 diff --git a/spec/web/site/_config.yml b/spec/web/site/_config.yml new file mode 100644 index 00000000..555fc780 --- /dev/null +++ b/spec/web/site/_config.yml @@ -0,0 +1,136 @@ +theme: "jekyll-theme-chirpy" +lang: "en" +timezone: "Asia/Hong_Kong" + +title: "514fpv" +tagline: "Unconscious requiem in the sky" +description: >- + Unconscious requiem in the sky + +url: "https://514fpv.one" +avatar: "/assets/img/koishi-margin-noalpha.png" +social_preview_image: "/assets/img/koishi0.png" +toc: true +theme_mode: + +github: + username: "514fpv" + +twitter: + username: "514fpv" + +social: + name: "514fpv" + email: "koishi@514fpv.one" + links: + - "https://src.514fpv.io:2096/koishi" + - "https://youtube.com/@514fpv" + +assets: + self_host: + enabled: true + env: production + +pwa: + enabled: true + +paginate: 8 +baseurl: "" + +comments: + active: # The global switch for posts comments, e.g., 'disqus'. Keep it empty means disable + # The active options are as follows: + disqus: + shortname: # fill with the Disqus shortname. › https://help.disqus.com/en/articles/1717111-what-s-a-shortname + # utterances settings › https://utteranc.es/ + utterances: + repo: # / + issue_term: # < url | pathname | title | ...> + # Giscus options › https://giscus.app + giscus: + repo: # / + repo_id: + category: + category_id: + mapping: # optional, default to 'pathname' + input_position: # optional, default to 'bottom' + lang: # optional, default to the value of `site.lang` + reactions_enabled: # optional, default to the value of `1` + +# ------------ The following options are not recommended to be modified ------------------ + +kramdown: + syntax_highlighter: rouge + syntax_highlighter_opts: # Rouge Options › https://github.com/jneen/rouge#full-options + css_class: highlight + # default_lang: console + span: + line_numbers: false + block: + line_numbers: true + start_line: 1 + +collections: + tabs: + output: true + sort_by: order + +defaults: + - scope: + path: "" # An empty string here means all files in the project + type: posts + values: + layout: post + comments: true # Enable comments in posts. + toc: true # Display TOC column in posts. + permalink: /posts/:title/ + - scope: + path: _drafts + values: + comments: false + - scope: + path: "" + type: tabs # see `site.collections` + values: + layout: page + permalink: /:title/ + - scope: + path: assets/img/favicons + values: + swcache: true + - scope: + path: assets/js/dist + values: + swcache: true + +sass: + style: compressed + +compress_html: + clippings: all + comments: all + endings: all + profile: false + blanklines: false + ignore: + envs: [development] + +exclude: + - "*.gem" + - "*.gemspec" + - docs + - tools + - README.md + - LICENSE + - rollup.config.js + - package*.json + - result # nix result symlink + +jekyll-archives: + enabled: [categories, tags] + layouts: + category: category + tag: tag + permalinks: + tag: /tags/:name/ + category: /categories/:name/ diff --git a/spec/web/site/_data/contact.yml b/spec/web/site/_data/contact.yml new file mode 100644 index 00000000..3ca78a76 --- /dev/null +++ b/spec/web/site/_data/contact.yml @@ -0,0 +1,25 @@ +# The contact options. + +- type: git + icon: "fab fa-git-alt" + url: "https://src.514fpv.io:2096/koishi" + +- type: mastodon + icon: "fab fa-mastodon" + url: "https://cathode.church/@koishi" + +#- type: github +# icon: "fab fa-github" +# +#- type: twitter +# icon: "fa-brands fa-x-twitter" + +- type: email + icon: "fas fa-envelope" + noblank: true + +- type: rss + icon: "fas fa-rss" + noblank: true + + diff --git a/spec/web/site/_data/locales/en.yml b/spec/web/site/_data/locales/en.yml new file mode 100644 index 00000000..8322299e --- /dev/null +++ b/spec/web/site/_data/locales/en.yml @@ -0,0 +1 @@ +meta: 'Source code' diff --git a/spec/web/site/_data/share.yml b/spec/web/site/_data/share.yml new file mode 100644 index 00000000..14375ee8 --- /dev/null +++ b/spec/web/site/_data/share.yml @@ -0,0 +1,23 @@ +# Sharing options at the bottom of the post. +# Icons from + +platforms: + - type: Twitter + icon: "fa-brands fa-square-x-twitter" + link: "https://twitter.com/intent/tweet?text=TITLE&url=URL" + + - type: Facebook + icon: "fab fa-facebook-square" + link: "https://www.facebook.com/sharer/sharer.php?title=TITLE&u=URL" + + - type: Telegram + icon: "fab fa-telegram" + link: "https://t.me/share/url?url=URL&text=TITLE" + + - type: Linkedin + icon: "fab fa-linkedin" + link: "https://www.linkedin.com/sharing/share-offsite/?url=URL" + + - type: Weibo + icon: "fab fa-weibo" + link: "http://service.weibo.com/share/share.php?title=TITLE&url=URL" diff --git a/spec/web/site/_plugins/posts-lastmod-hook.rb b/spec/web/site/_plugins/posts-lastmod-hook.rb new file mode 100644 index 00000000..1fd6ecf9 --- /dev/null +++ b/spec/web/site/_plugins/posts-lastmod-hook.rb @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +# +# Check for changed posts + +Jekyll::Hooks.register :posts, :post_init do |post| + + commit_num = `git rev-list --count HEAD "#{ post.path }"` + + if commit_num.to_i > 1 + lastmod_date = `git log -1 --pretty="%ad" --date=iso "#{ post.path }"` + post.data['last_modified_at'] = lastmod_date + end + +end diff --git a/spec/web/site/_posts/2023-04-22-startup-music.md b/spec/web/site/_posts/2023-04-22-startup-music.md new file mode 100644 index 00000000..78d17d23 --- /dev/null +++ b/spec/web/site/_posts/2023-04-22-startup-music.md @@ -0,0 +1,34 @@ +--- +title: "BLHeli32 ESC Startup Music" +date: "2023-04-22 23:05:14 +0800" +categories: [ "Multirotor", "Hardware" ] +tags: [ "music", "propulsion", "blheli32" ] +pin: true +--- +This post details how to replicate my quadcopter ESC startup music. Note that a BLHeli32 ESC or one with compatible music functionality is required. + +Turn on music for all four ESCs, and set `Gen. Length` to 11, `Gen. Interval` to 0. In the `All ESC together` section, make sure the checkboxes for `Show 1/X for length`, `Show Space` and `Play 1 Octave lower` are all set to enable. + +Then, set the music data as follows. + +``` +A5 1/4 G5 1/4 A5 1/4 E5 1/4 C5 1/4 B4 1/4 C5 1/4 A4 1/8 B4 1/8 A4 1/2 A5 1/2 G5 1/2 E5 1/2 A5 1/4 B5 1/4 C6 1/4 E6 1/4 C6 1/4 B5 1/4 G5 1/4 B4 1/4 A4 1/2 B4 1/2 C5 1/2 P1/2 P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 +``` +{: file='ESC#1'} + +``` +P1/1 P1/1 C5 1/2 C6 1/2 B5 1/2 G5 1/2 P1/1 P1/1 C5 1/2 G5 1/2 A5 1/2 P1/2 P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 +``` +{: file='ESC#2'} + +``` +P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 P1/2 E5 1/4 G5 1/4 A5 1/4 G5 1/4 A5 1/4 B5 1/4 C6 1/4 B5 1/4 D6 1/4 B5 1/4 A5 1/4 G5 1/4 E5 1/4 E5 1/4 A4 1/2 G4 1/2 A4 1/4 B4 1/4 C5 1/4 E5 1/4 C5 1/4 E5 1/4 C5 1/4 B4 1/4 E4 1/4 E4 1/4 C4 1/4 E4 1/4 C4 1/2 D4 1/2 +``` +{: file='ESC#3'} + +``` +P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 P1/1 P1/2 P1/2 P1/1 P1/1 C6 1/4 B5 1/4 A5 1/4 G5 1/4 C5 1/2 B4 1/2 P1/1 P1/1 A4 1/4 G4 1/4 E4 1/4 G4 1/4 A4 1/2 B4 1/2 +``` +{: file='ESC#4'} + + diff --git a/spec/web/site/_tabs/about.md b/spec/web/site/_tabs/about.md new file mode 100644 index 00000000..e5c2bea5 --- /dev/null +++ b/spec/web/site/_tabs/about.md @@ -0,0 +1,14 @@ +--- +icon: fas fa-info-circle +order: 4 +--- +I fly multirotor aircraft, with a focus on cinematic and long range FPV cruising. + +I also write software, and I self host [almost everything](https://src.514fpv.io:2096/koishi/nixos/src/branch/master/spec/eientei){:target="_blank"} I use online, including the [website](https://src.514fpv.io:2096/koishi/nixos/src/branch/master/spec/web/site){:target="_blank"} you're currently visiting. + +If you find what I do interesting, feel free to drop by and say hi, I would really appreciate that. + +> I can read and write Simplified Chinese. If you prefer to converse with me in Chinese, feel free to do so without the overhead of translation software. +> +> 我会读写简体中文。如果您喜欢用中文与我交流,请随时与我联系,无需使用翻译软件。 +{: .prompt-tip } diff --git a/spec/web/site/_tabs/archives.md b/spec/web/site/_tabs/archives.md new file mode 100644 index 00000000..c3abc594 --- /dev/null +++ b/spec/web/site/_tabs/archives.md @@ -0,0 +1,5 @@ +--- +layout: archives +icon: fas fa-archive +order: 3 +--- diff --git a/spec/web/site/_tabs/categories.md b/spec/web/site/_tabs/categories.md new file mode 100644 index 00000000..2d241be9 --- /dev/null +++ b/spec/web/site/_tabs/categories.md @@ -0,0 +1,5 @@ +--- +layout: categories +icon: fas fa-stream +order: 1 +--- diff --git a/spec/web/site/_tabs/tags.md b/spec/web/site/_tabs/tags.md new file mode 100644 index 00000000..ded3adc1 --- /dev/null +++ b/spec/web/site/_tabs/tags.md @@ -0,0 +1,5 @@ +--- +layout: tags +icon: fas fa-tags +order: 2 +--- diff --git a/spec/web/site/assets/img/favicons/android-chrome-192x192.png b/spec/web/site/assets/img/favicons/android-chrome-192x192.png new file mode 100644 index 00000000..23e9954e Binary files /dev/null and b/spec/web/site/assets/img/favicons/android-chrome-192x192.png differ diff --git a/spec/web/site/assets/img/favicons/android-chrome-512x512.png b/spec/web/site/assets/img/favicons/android-chrome-512x512.png new file mode 100644 index 00000000..776e38e3 Binary files /dev/null and b/spec/web/site/assets/img/favicons/android-chrome-512x512.png differ diff --git a/spec/web/site/assets/img/favicons/apple-touch-icon.png b/spec/web/site/assets/img/favicons/apple-touch-icon.png new file mode 100644 index 00000000..13d2b96d Binary files /dev/null and b/spec/web/site/assets/img/favicons/apple-touch-icon.png differ diff --git a/spec/web/site/assets/img/favicons/favicon-16x16.png b/spec/web/site/assets/img/favicons/favicon-16x16.png new file mode 100644 index 00000000..13944716 Binary files /dev/null and b/spec/web/site/assets/img/favicons/favicon-16x16.png differ diff --git a/spec/web/site/assets/img/favicons/favicon-32x32.png b/spec/web/site/assets/img/favicons/favicon-32x32.png new file mode 100644 index 00000000..56251a47 Binary files /dev/null and b/spec/web/site/assets/img/favicons/favicon-32x32.png differ diff --git a/spec/web/site/assets/img/favicons/favicon.ico b/spec/web/site/assets/img/favicons/favicon.ico new file mode 100644 index 00000000..00bdd1dd Binary files /dev/null and b/spec/web/site/assets/img/favicons/favicon.ico differ diff --git a/spec/web/site/assets/img/favicons/mstile-150x150.png b/spec/web/site/assets/img/favicons/mstile-150x150.png new file mode 100644 index 00000000..4a21e29c Binary files /dev/null and b/spec/web/site/assets/img/favicons/mstile-150x150.png differ diff --git a/spec/web/site/assets/img/favicons/safari-pinned-tab.svg b/spec/web/site/assets/img/favicons/safari-pinned-tab.svg new file mode 100644 index 00000000..0390ba7b --- /dev/null +++ b/spec/web/site/assets/img/favicons/safari-pinned-tab.svg @@ -0,0 +1,69 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/spec/web/site/assets/img/koishi-margin-noalpha.png b/spec/web/site/assets/img/koishi-margin-noalpha.png new file mode 100644 index 00000000..aa7846ec Binary files /dev/null and b/spec/web/site/assets/img/koishi-margin-noalpha.png differ diff --git a/spec/web/site/assets/img/koishi0.png b/spec/web/site/assets/img/koishi0.png new file mode 100644 index 00000000..3a61f2e6 Binary files /dev/null and b/spec/web/site/assets/img/koishi0.png differ diff --git a/spec/web/site/assets/lib/.placeholder b/spec/web/site/assets/lib/.placeholder new file mode 100644 index 00000000..e69de29b diff --git a/spec/web/site/default.nix b/spec/web/site/default.nix new file mode 100644 index 00000000..63913a06 --- /dev/null +++ b/spec/web/site/default.nix @@ -0,0 +1,41 @@ +{ lib +, stdenvNoCC +, ruby +, bundlerEnv +, writeShellScriptBin +, fetchFromGitHub }: let + name = "website"; + version = "20240114"; + + assets = fetchFromGitHub { + name = "${name}-assets"; + owner = "cotes2020"; + repo = "chirpy-static-assets"; + rev = "c577249"; + hash = "sha256-Uxe7PMNwzKUN2b77QbjWLxEtOOLodPOyeMuUM1eKdlc="; + }; + + gems = bundlerEnv { + name = "${name}-gems"; + inherit ruby; + gemfile = ./Gemfile; + lockfile = ./Gemfile.lock; + gemset = ./gemset.nix; + }; +in stdenvNoCC.mkDerivation { + pname = name; + inherit version; + src = ./.; + buildInputs = [ gems ruby ]; + nativeBuildInputs = [ (writeShellScriptBin "git" "true") ]; + + configurePhase = '' + cp -r ${assets}/. assets/lib/ + ''; + + buildPhase = "JEKYLL_ENV=production bundle exec jekyll build"; + + installPhase = '' + cp -r _site "$out" + ''; +} diff --git a/spec/web/site/gemset.nix b/spec/web/site/gemset.nix new file mode 100644 index 00000000..b526c889 --- /dev/null +++ b/spec/web/site/gemset.nix @@ -0,0 +1,495 @@ +{ + addressable = { + dependencies = ["public_suffix"]; + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0irbdwkkjwzajq1ip6ba46q49sxnrl2cw7ddkdhsfhb6aprnm3vr"; + type = "gem"; + }; + version = "2.8.6"; + }; + colorator = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0f7wvpam948cglrciyqd798gdc6z3cfijciavd0dfixgaypmvy72"; + type = "gem"; + }; + version = "1.1.0"; + }; + concurrent-ruby = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1qh1b14jwbbj242klkyz5fc7npd4j0mvndz62gajhvl1l3wd7zc2"; + type = "gem"; + }; + version = "1.2.3"; + }; + em-websocket = { + dependencies = ["eventmachine" "http_parser.rb"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1a66b0kjk6jx7pai9gc7i27zd0a128gy73nmas98gjz6wjyr4spm"; + type = "gem"; + }; + version = "0.5.3"; + }; + ethon = { + dependencies = ["ffi"]; + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "17ix0mijpsy3y0c6ywrk5ibarmvqzjsirjyprpsy3hwax8fdm85v"; + type = "gem"; + }; + version = "0.16.0"; + }; + eventmachine = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0wh9aqb0skz80fhfn66lbpr4f86ya2z5rx6gm5xlfhd05bj1ch4r"; + type = "gem"; + }; + version = "1.2.7"; + }; + ffi = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1yvii03hcgqj30maavddqamqy50h7y6xcn2wcyq72wn823zl4ckd"; + type = "gem"; + }; + version = "1.16.3"; + }; + forwardable-extended = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "15zcqfxfvsnprwm8agia85x64vjzr2w0xn9vxfnxzgcv8s699v0v"; + type = "gem"; + }; + version = "2.6.0"; + }; + google-protobuf = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1mnxzcq8kmyfb9bkzqnp019d1hx1vprip3yzdkkha6b3qz5rgg9r"; + type = "gem"; + }; + version = "3.25.3"; + }; + html-proofer = { + dependencies = ["addressable" "mercenary" "nokogiri" "parallel" "rainbow" "typhoeus" "yell" "zeitwerk"]; + groups = ["test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "00hhwzl5llvsasbj6gpgd1272xrqz8wlxzq7qm0xa4z3mk24013j"; + type = "gem"; + }; + version = "4.4.3"; + }; + "http_parser.rb" = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "15nidriy0v5yqfjsgsra51wmknxci2n2grliz78sf9pga3n0l7gi"; + type = "gem"; + }; + version = "0.6.0"; + }; + i18n = { + dependencies = ["concurrent-ruby"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0qaamqsh5f3szhcakkak8ikxlzxqnv49n2p7504hcz2l0f4nj0wx"; + type = "gem"; + }; + version = "1.14.1"; + }; + jekyll = { + dependencies = ["addressable" "colorator" "em-websocket" "i18n" "jekyll-sass-converter" "jekyll-watch" "kramdown" "kramdown-parser-gfm" "liquid" "mercenary" "pathutil" "rouge" "safe_yaml" "terminal-table" "webrick"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0638cvpmk3py1w2dxpav6l0c854y6l94b6gyc2aa16i7r897z64a"; + type = "gem"; + }; + version = "4.3.3"; + }; + jekyll-archives = { + dependencies = ["jekyll"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0c2rks7xf6ajp18h4f4wmmbqm5ljprv70bqcz2sabi17zncmz9n0"; + type = "gem"; + }; + version = "2.2.1"; + }; + jekyll-include-cache = { + dependencies = ["jekyll"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "01d2l6qrmjc42664ns83cv36jbvalcxqbkmj5i22fakka7jvkm67"; + type = "gem"; + }; + version = "0.2.1"; + }; + jekyll-paginate = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0r7bcs8fq98zldih4787zk5i9w24nz5wa26m84ssja95n3sas2l8"; + type = "gem"; + }; + version = "1.1.0"; + }; + jekyll-redirect-from = { + dependencies = ["jekyll"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1nz6kd6qsa160lmjmls4zgx7fwcpp8ac07mpzy80z6zgd7jwldb6"; + type = "gem"; + }; + version = "0.16.0"; + }; + jekyll-sass-converter = { + dependencies = ["sass-embedded"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "00n9v19h0qgjijygfdkdh2gwpmdlz49nw1mqk6fnp43f317ngrz2"; + type = "gem"; + }; + version = "3.0.0"; + }; + jekyll-seo-tag = { + dependencies = ["jekyll"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0638mqhqynghnlnaz0xi1kvnv53wkggaq94flfzlxwandn8x2biz"; + type = "gem"; + }; + version = "2.8.0"; + }; + jekyll-sitemap = { + dependencies = ["jekyll"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0622rwsn5i0m5xcyzdn86l68wgydqwji03lqixdfm1f1xdfqrq0d"; + type = "gem"; + }; + version = "1.4.0"; + }; + jekyll-theme-chirpy = { + dependencies = ["jekyll" "jekyll-archives" "jekyll-include-cache" "jekyll-paginate" "jekyll-redirect-from" "jekyll-seo-tag" "jekyll-sitemap"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0xlrwyz68rfr820gm98gidc49vprb80im32s6ydli0q1mv1inmzq"; + type = "gem"; + }; + version = "6.5.0"; + }; + jekyll-watch = { + dependencies = ["listen"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1qd7hy1kl87fl7l0frw5qbn22x7ayfzlv9a5ca1m59g0ym1ysi5w"; + type = "gem"; + }; + version = "2.2.1"; + }; + kramdown = { + dependencies = ["rexml"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1ic14hdcqxn821dvzki99zhmcy130yhv5fqfffkcf87asv5mnbmn"; + type = "gem"; + }; + version = "2.4.0"; + }; + kramdown-parser-gfm = { + dependencies = ["kramdown"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0a8pb3v951f4x7h968rqfsa19c8arz21zw1vaj42jza22rap8fgv"; + type = "gem"; + }; + version = "1.1.0"; + }; + liquid = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1czxv2i1gv3k7hxnrgfjb0z8khz74l4pmfwd70c7kr25l2qypksg"; + type = "gem"; + }; + version = "4.0.4"; + }; + listen = { + dependencies = ["rb-fsevent" "rb-inotify"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "13rgkfar8pp31z1aamxf5y7cfq88wv6rxxcwy7cmm177qq508ycn"; + type = "gem"; + }; + version = "3.8.0"; + }; + mercenary = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0f2i827w4lmsizrxixsrv2ssa3gk1b7lmqh8brk8ijmdb551wnmj"; + type = "gem"; + }; + version = "0.4.0"; + }; + mini_portile2 = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1kl9c3kdchjabrihdqfmcplk3lq4cw1rr9f378y6q22qwy5dndvs"; + type = "gem"; + }; + version = "2.8.5"; + }; + nokogiri = { + dependencies = ["mini_portile2" "racc"]; + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "173zavvxlwyi48lfskk48wcrdbkvjlhjhvy4jpcrfx72rpjjx4k8"; + type = "gem"; + }; + version = "1.16.2"; + }; + parallel = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "15wkxrg1sj3n1h2g8jcrn7gcapwcgxr659ypjf75z1ipkgxqxwsv"; + type = "gem"; + }; + version = "1.24.0"; + }; + pathutil = { + dependencies = ["forwardable-extended"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "12fm93ljw9fbxmv2krki5k5wkvr7560qy8p4spvb9jiiaqv78fz4"; + type = "gem"; + }; + version = "0.16.2"; + }; + public_suffix = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1bni4qjrsh2q49pnmmd6if4iv3ak36bd2cckrs6npl111n769k9m"; + type = "gem"; + }; + version = "5.0.4"; + }; + racc = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "01b9662zd2x9bp4rdjfid07h09zxj7kvn7f5fghbqhzc625ap1dp"; + type = "gem"; + }; + version = "1.7.3"; + }; + rainbow = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0smwg4mii0fm38pyb5fddbmrdpifwv22zv3d3px2xx497am93503"; + type = "gem"; + }; + version = "3.1.1"; + }; + rake = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1ilr853hawi09626axx0mps4rkkmxcs54mapz9jnqvpnlwd3wsmy"; + type = "gem"; + }; + version = "13.1.0"; + }; + rb-fsevent = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1zmf31rnpm8553lqwibvv3kkx0v7majm1f341xbxc0bk5sbhp423"; + type = "gem"; + }; + version = "0.11.2"; + }; + rb-inotify = { + dependencies = ["ffi"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1jm76h8f8hji38z3ggf4bzi8vps6p7sagxn3ab57qc0xyga64005"; + type = "gem"; + }; + version = "0.10.1"; + }; + rexml = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "05i8518ay14kjbma550mv0jm8a6di8yp5phzrd8rj44z9qnrlrp0"; + type = "gem"; + }; + version = "3.2.6"; + }; + rouge = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1fkfa0iq3r9b0zzrxpxha17avmyzci3kidzmfbf6fd1279mndpb0"; + type = "gem"; + }; + version = "4.2.0"; + }; + safe_yaml = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0j7qv63p0vqcd838i2iy2f76c3dgwzkiz1d1xkg7n0pbnxj2vb56"; + type = "gem"; + }; + version = "1.0.5"; + }; + sass-embedded = { + dependencies = ["google-protobuf" "rake"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1ccqqkmicqs2nbawyknb17qfafwqq0k6jxibcm86vqd1jp185pxa"; + type = "gem"; + }; + version = "1.71.1"; + }; + terminal-table = { + dependencies = ["unicode-display_width"]; + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "14dfmfjppmng5hwj7c5ka6qdapawm3h6k9lhn8zj001ybypvclgr"; + type = "gem"; + }; + version = "3.0.2"; + }; + typhoeus = { + dependencies = ["ethon"]; + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0z7gamf6s83wy0yqms3bi4srirn3fc0lc7n65lqanidxcj1xn5qw"; + type = "gem"; + }; + version = "1.4.1"; + }; + unicode-display_width = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1d0azx233nags5jx3fqyr23qa2rhgzbhv8pxp46dgbg1mpf82xky"; + type = "gem"; + }; + version = "2.5.0"; + }; + webrick = { + groups = ["default"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "13qm7s0gr2pmfcl7dxrmq38asaza4w0i2n9my4yzs499j731wh8r"; + type = "gem"; + }; + version = "1.8.1"; + }; + yell = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1g16kcdhdfvczn7x81jiq6afg3bdxmb73skqjyjlkp5nqcy6y5hx"; + type = "gem"; + }; + version = "2.2.2"; + }; + zeitwerk = { + groups = ["default" "test"]; + platforms = []; + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1m67qmsak3x8ixs8rb971azl3l7wapri65pmbf5z886h46q63f1d"; + type = "gem"; + }; + version = "2.6.13"; + }; +} diff --git a/spec/web/site/gemset.sh b/spec/web/site/gemset.sh new file mode 100755 index 00000000..bf061e7a --- /dev/null +++ b/spec/web/site/gemset.sh @@ -0,0 +1,3 @@ +#!/bin/sh +rm -f Gemfile.lock gemset.nix +BUNDLE_FORCE_RUBY_PLATFORM=true nix run nixpkgs#bundix --impure -- --lock diff --git a/spec/web/site/index.html b/spec/web/site/index.html new file mode 100644 index 00000000..e4d427d2 --- /dev/null +++ b/spec/web/site/index.html @@ -0,0 +1,3 @@ +--- +layout: home +--- diff --git a/spec/web/uptime.nix b/spec/web/uptime.nix new file mode 100644 index 00000000..d9b27c68 --- /dev/null +++ b/spec/web/uptime.nix @@ -0,0 +1,11 @@ +{ + services.uptime-kuma = { + enable = true; + settings.HOST = "127.0.0.1"; + settings.PORT = "4000"; + }; + + environment.persistence."/nix/persist/fhs".directories = [ + "/var/lib/private/uptime-kuma" + ]; +} diff --git a/spec/yume/default.nix b/spec/yume/default.nix new file mode 100644 index 00000000..1550ec16 --- /dev/null +++ b/spec/yume/default.nix @@ -0,0 +1,37 @@ +{ pkgs +, lib +, config +, modulesPath +, ... }: { + global = { + id = "d40d05288c1544af94e534eab24768fc"; + fs.esp.uuid = "6CEB-765A"; + fs.type = "xfs"; + fs.store = "b80e8bbe-d1ff-4d2e-ae25-9e9048d67355"; + boot.lanzaboote = true; + gpu.enable = true; + gpu.type = "nvidia"; + android.enable = true; + }; + + home = { + gnome.enable = true; + gyroflow.enable = true; + jetbrains.enable = true; + vscode.enable = true; + libreoffice.enable = true; + minecraft.enable = true; + steam.enable = true; + }; + + imports = lib.pipe ./. [ + builtins.readDir + (lib.filterAttrs (n: ty: ty == "regular" && n != "default.nix")) + (lib.mapAttrsToList (n: _: ./${n})) + ] ++ [ (modulesPath + "/profiles/qemu-guest.nix") ]; + + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "usbhid" "sr_mod" "virtio_blk" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; +}