Compare commits
23 Commits
32df2c3573
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 380b428d9a | |||
| 2a911b057b | |||
| 403cf2fde5 | |||
| 65064d971c | |||
| 875fa633c7 | |||
| 1cbca207b6 | |||
| f701177197 | |||
| 778cde8c40 | |||
| f9d5c70fb1 | |||
| 1fd25998fb | |||
| 7994f174f6 | |||
| d48e4f8a08 | |||
| dcdd2c2d90 | |||
| 9a4ed1b04b | |||
| 6978396646 | |||
| fba5a7a2aa | |||
| f02606902c | |||
| 34b89af77f | |||
| f98606dcce | |||
| 1015cf4577 | |||
| d51f41566c | |||
| 064ba9655a | |||
| 2c576715de |
+26
@@ -0,0 +1,26 @@
|
||||
keys:
|
||||
- &primary age1yyzgmazjxkvwtfcv9re3lqmt2ru5dcrfu3sauysm0wzfwzvyap8qkjkq32
|
||||
- &host_14900k age1elk6zwmcylwfk7gd4pjda7g29upftjvxys8py42s8d42jklnyv7s7dm9z2
|
||||
- &host_uConsole age193gw802ytal7h5p5q37kpd9079k2vsflzmnvupcwfxh2kjdrwqtsk3g6rm
|
||||
- &host_t2mbp age1yr7vurfxc3w8ewfw9djfm54atw6ayze69qglamecuft5q0n9gu2sadsa2m
|
||||
- &host_ideapad age1hya7pgpe8zal52w3pjf036tpapmehedatfm4r84h30t4wuh079ssedfd37
|
||||
- &host_nix-server age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9
|
||||
- &host_r5500 age1pewusvlcgzcnk0kpskgc9qr6jlq8s2yzwnqrnh84p7v5z0kj3qhsya8h2x
|
||||
creation_rules:
|
||||
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
|
||||
key_groups:
|
||||
- age:
|
||||
- *primary
|
||||
- *host_14900k
|
||||
- *host_uConsole
|
||||
- *host_t2mbp
|
||||
- *host_ideapad
|
||||
- *host_nix-server
|
||||
- *host_r5500
|
||||
# Host secrets at modules/hosts/nix-server/secrets.yaml (see configuration imports),
|
||||
# or optional extra files under _secrets/.
|
||||
- path_regex: modules/hosts/nix-server/(secrets\.(yaml|json|env|ini)|_secrets/.*\.(yaml|json|env|ini))$
|
||||
key_groups:
|
||||
- age:
|
||||
- *primary
|
||||
- *host_nix-server
|
||||
Generated
+453
-90
@@ -16,6 +16,21 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1770169865,
|
||||
"narHash": "sha256-iPiy13xzDQ9GjpOez+NNIjh/qjl7i4RDf9dF2x5mF9I=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "8254ccf3b5b5131890ee073776f2e61c6d1e55d4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"cursor": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -23,11 +38,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776686554,
|
||||
"narHash": "sha256-TaJnAbwSnfJ9My8Df8o6MKswuc3dq6qGF+zTZF906Eg=",
|
||||
"lastModified": 1780452866,
|
||||
"narHash": "sha256-Fq5cR/qGYcGwOOZf6sShtM+kljwecgntgcKXnIBrQDE=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "c1a35069ee41595f8549fb529d29647565a30e75",
|
||||
"revCount": 99,
|
||||
"rev": "344f4065e69455adbcdfb4db1cd8d07a73ae7140",
|
||||
"revCount": 114,
|
||||
"type": "git",
|
||||
"url": "https://git.chiasson.cloud/Olivier/cursor-nixos-flake"
|
||||
},
|
||||
@@ -36,6 +51,27 @@
|
||||
"url": "https://git.chiasson.cloud/Olivier/cursor-nixos-flake"
|
||||
}
|
||||
},
|
||||
"ddrm": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777930144,
|
||||
"narHash": "sha256-P7FfGTREtztsCTf4wSlqHf9EYlLt/AdrpmTfUezF6Uw=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "c3d014f96855e45b53f7391ce19729493887cb96",
|
||||
"revCount": 19,
|
||||
"type": "git",
|
||||
"url": "https://git.chiasson.cloud/Olivier/DDRM"
|
||||
},
|
||||
"original": {
|
||||
"rev": "c3d014f96855e45b53f7391ce19729493887cb96",
|
||||
"type": "git",
|
||||
"url": "https://git.chiasson.cloud/Olivier/DDRM"
|
||||
}
|
||||
},
|
||||
"dgop": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -43,11 +79,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776181116,
|
||||
"narHash": "sha256-aUNKF+jzGY+jRlR7Bp82v/zNHdI9bFELLuYYWbaM6fo=",
|
||||
"lastModified": 1778679554,
|
||||
"narHash": "sha256-zoPgnxIlDja91/4TmnCui+Fzc/xU/1jdJFu9bovtOk8=",
|
||||
"owner": "AvengeMedia",
|
||||
"repo": "dgop",
|
||||
"rev": "e2078a7c5620be2e4897e7dabc08ade6dac9a454",
|
||||
"rev": "06574b54fa4878a93d8605962d50b13e9528a4ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -56,6 +92,29 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"disko": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"navi",
|
||||
"nixos-anywhere",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769524058,
|
||||
"narHash": "sha256-zygdD6X1PcVNR2PsyK4ptzrVEiAdbMqLos7utrMDEWE=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "71a3fc97d80881e91710fe721f1158d3b96ae14d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "master",
|
||||
"repo": "disko",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"dms": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -64,11 +123,11 @@
|
||||
"quickshell": "quickshell"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775588644,
|
||||
"narHash": "sha256-iYBdSBvcW7bJtc84G6k5TFJEbPHQrif9KzZyE9Lbq8M=",
|
||||
"lastModified": 1777431599,
|
||||
"narHash": "sha256-g6r/Gx8PTDzO3jCNzzySA+Ff1lmLF9nDlMCNyyoQjoE=",
|
||||
"owner": "AvengeMedia",
|
||||
"repo": "DankMaterialShell",
|
||||
"rev": "9798d78300d402178896f6ee1c370baed490158a",
|
||||
"rev": "eb5afcdc40ea5446c27e18552ff4a19f9daf9484",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -112,11 +171,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775087534,
|
||||
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||
"lastModified": 1778716662,
|
||||
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -130,11 +189,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775087534,
|
||||
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||
"lastModified": 1769996383,
|
||||
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -144,6 +203,46 @@
|
||||
}
|
||||
},
|
||||
"flake-parts_3": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"navi",
|
||||
"nixos-anywhere",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768135262,
|
||||
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_4": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1778716662,
|
||||
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_5": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nur",
|
||||
@@ -166,7 +265,25 @@
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
@@ -189,11 +306,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776721614,
|
||||
"narHash": "sha256-zGuW7C4tsScib2560yE5VV6lY/MdRs30aU9cbg3RP+U=",
|
||||
"lastModified": 1779507042,
|
||||
"narHash": "sha256-7wOwi8B6D0BYsieZCnHZZj2sNUzgJhLoIVSfkwB7lxQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "c555a4a34a260493be5adb795c54e013c58f2d34",
|
||||
"rev": "509ed3c603349a9d43de9e2ae6613baea6bd5b34",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -210,11 +327,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776184304,
|
||||
"narHash": "sha256-No6QGBmIv5ChiwKCcbkxjdEQ/RO2ZS1gD7SFy6EZ7rc=",
|
||||
"lastModified": 1778805320,
|
||||
"narHash": "sha256-nGFJ01m2CTBKD4ABtcY4vLhHrRN91LKr/pn41PcU78A=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "3c7524c68348ef79ce48308e0978611a050089b2",
|
||||
"rev": "9846abe15e7d0d36b8acbd4d05f2b87461744c92",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -225,11 +342,11 @@
|
||||
},
|
||||
"import-tree": {
|
||||
"locked": {
|
||||
"lastModified": 1773693634,
|
||||
"narHash": "sha256-BtZ2dtkBdSUnFPPFc+n0kcMbgaTxzFNPv2iaO326Ffg=",
|
||||
"lastModified": 1778781969,
|
||||
"narHash": "sha256-Jjuz5CmSkur8KvLDoGa+vylEp+RkQtv4mt/qcMznpH0=",
|
||||
"owner": "vic",
|
||||
"repo": "import-tree",
|
||||
"rev": "c41e7d58045f9057880b0d85e1152d6a4430dbf1",
|
||||
"rev": "d321337efd0f23a9eb14a42adb7b2c29313ab274",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -254,6 +371,50 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"navi": {
|
||||
"inputs": {
|
||||
"crane": "crane",
|
||||
"flake-parts": "flake-parts_2",
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
"nixos-anywhere": "nixos-anywhere",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"stable": "stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777283737,
|
||||
"narHash": "sha256-sVwTOYwxzUUqAEr5kjOZooJ/v8H2hcuZ42f17MgCqX8=",
|
||||
"owner": "cafkafk",
|
||||
"repo": "navi",
|
||||
"rev": "b060683e4ca2bd37a1c3bcb4e04abd2296190e9d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cafkafk",
|
||||
"repo": "navi",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-github-actions": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"navi",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1729742964,
|
||||
"narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"rev": "e04df33f62cdcf93d73e9a04142464753a16db67",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-monitor": {
|
||||
"locked": {
|
||||
"lastModified": 1771568669,
|
||||
@@ -269,21 +430,44 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-vm-test": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"navi",
|
||||
"nixos-anywhere",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769079217,
|
||||
"narHash": "sha256-R6qzhu+YJolxE2vUsPQWWwUKMbAG5nXX3pBtg8BNX38=",
|
||||
"owner": "Enzime",
|
||||
"repo": "nix-vm-test",
|
||||
"rev": "58c15f78947b431d6c206e0966500c7e9139bd2f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "Enzime",
|
||||
"ref": "pr-105-latest",
|
||||
"repo": "nix-vm-test",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixcord": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts_2",
|
||||
"flake-parts": "flake-parts_4",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-nixcord": "nixpkgs-nixcord"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777125640,
|
||||
"narHash": "sha256-jKmRu5PknoI0pk3WEqMhVReosUubUCq3M/izEQWzb+4=",
|
||||
"lastModified": 1779498537,
|
||||
"narHash": "sha256-6LQjFDS69JufrN4sVsMNsXxeSF6BbDzMSbN7sVApsaA=",
|
||||
"owner": "KaylorBen",
|
||||
"repo": "nixcord",
|
||||
"rev": "0e738683dd7551a9cbfa343397b1592dfd785b7e",
|
||||
"rev": "45a98c17b0d9e695bdee92ab00c76657eddf47e7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -292,7 +476,62 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-anywhere": {
|
||||
"inputs": {
|
||||
"disko": "disko",
|
||||
"flake-parts": "flake-parts_3",
|
||||
"nix-vm-test": "nix-vm-test",
|
||||
"nixos-images": "nixos-images",
|
||||
"nixos-stable": "nixos-stable",
|
||||
"nixpkgs": [
|
||||
"navi",
|
||||
"nixpkgs"
|
||||
],
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772874867,
|
||||
"narHash": "sha256-d79nCSys2aaV6E1tL/H4xEeKJvLFwEeP6yj5q0glbuQ=",
|
||||
"owner": "cafkafk",
|
||||
"repo": "nixos-anywhere",
|
||||
"rev": "b88c2ee9734ce4ec5aa63cf74c2ea58f6f9970ec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cafkafk",
|
||||
"ref": "kexec-ssh-args-bug",
|
||||
"repo": "nixos-anywhere",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-images": {
|
||||
"inputs": {
|
||||
"nixos-stable": [
|
||||
"navi",
|
||||
"nixos-anywhere",
|
||||
"nixos-stable"
|
||||
],
|
||||
"nixos-unstable": [
|
||||
"navi",
|
||||
"nixos-anywhere",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766770015,
|
||||
"narHash": "sha256-kUmVBU+uBUPl/v3biPiWrk680b8N9rRMhtY97wsxiJc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixos-images",
|
||||
"rev": "e4dba54ddb6b2ad9c6550e5baaed2fa27938a5d2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixos-images",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixos-images_2": {
|
||||
"inputs": {
|
||||
"nixos-stable": [
|
||||
"nixos-raspberrypi",
|
||||
@@ -322,17 +561,17 @@
|
||||
"inputs": {
|
||||
"argononed": "argononed",
|
||||
"flake-compat": "flake-compat_2",
|
||||
"nixos-images": "nixos-images",
|
||||
"nixos-images": "nixos-images_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775857096,
|
||||
"narHash": "sha256-+eSij7C0oMqz76rGnB99RuWptBuEkJBm9vgb5fIwRrg=",
|
||||
"lastModified": 1779023229,
|
||||
"narHash": "sha256-MInilg7B/06c34SwOuGSBho4l0H1EZcmvxTkSWCs5pE=",
|
||||
"owner": "nvmd",
|
||||
"repo": "nixos-raspberrypi",
|
||||
"rev": "1dc4ca5f93587932383c0b61e1753f5eed1c3bba",
|
||||
"rev": "06c6e3513e1ee64b651913193fc6ac38aa4963f5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -342,17 +581,33 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"nixos-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1776548001,
|
||||
"narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=",
|
||||
"owner": "nixos",
|
||||
"lastModified": 1769598131,
|
||||
"narHash": "sha256-e7VO/kGLgRMbWtpBqdWl0uFg8Y2XWFMdz0uUJvlML8o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc",
|
||||
"rev": "fa83fd837f3098e3e678e6cf017b2b36102c7211",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-25.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1750134718,
|
||||
"narHash": "sha256-v263g4GbxXv87hMXMCpjkIxd/viIF7p3JpJrwgKdNiI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9e83b64f727c88a7711a2c463a7b16eedb69a84c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
@@ -360,11 +615,11 @@
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1774748309,
|
||||
"narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=",
|
||||
"lastModified": 1777168982,
|
||||
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "333c4e0545a6da976206c74db8773a1645b5870a",
|
||||
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -375,11 +630,26 @@
|
||||
},
|
||||
"nixpkgs-lib_2": {
|
||||
"locked": {
|
||||
"lastModified": 1774748309,
|
||||
"narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=",
|
||||
"lastModified": 1769909678,
|
||||
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "333c4e0545a6da976206c74db8773a1645b5870a",
|
||||
"rev": "72716169fe93074c333e8d0173151350670b824c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib_3": {
|
||||
"locked": {
|
||||
"lastModified": 1777168982,
|
||||
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -390,11 +660,11 @@
|
||||
},
|
||||
"nixpkgs-nixcord": {
|
||||
"locked": {
|
||||
"lastModified": 1776734388,
|
||||
"narHash": "sha256-vl3dkhlE5gzsItuHoEMVe+DlonsK+0836LIRDnm6MXQ=",
|
||||
"lastModified": 1779102034,
|
||||
"narHash": "sha256-vZJZjLo513IeI8hjzHFc6TDezUd4uCE2Eq4SNO3DNNg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "10e7ad5bbcb421fe07e3a4ad53a634b0cd57ffac",
|
||||
"rev": "687f05a9184cad4eaf905c48b63649e3a86f5433",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -406,11 +676,27 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1775579569,
|
||||
"narHash": "sha256-/m3yyS/EnXqoPGBJYVy4jTOsirdgsEZ3JdN2gGkBr14=",
|
||||
"lastModified": 1779357205,
|
||||
"narHash": "sha256-cCO8aTqss5x9Ky8GWkpY0Hy5fyTZEbtifSUV8QjSzic=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f83fc3c307e74bc5fd5adb7eb6b8b13ffd2a36e1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1778869304,
|
||||
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "dfd9566f82a6e1d55c30f861879186440614696e",
|
||||
"rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -422,17 +708,17 @@
|
||||
},
|
||||
"nur": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_3",
|
||||
"flake-parts": "flake-parts_5",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776720544,
|
||||
"narHash": "sha256-SjaFRV8Oqu3LtEGxr1q5K+bMPbxPPjc7z1adadC8yE8=",
|
||||
"lastModified": 1779493406,
|
||||
"narHash": "sha256-70dCjL6KdsNG+hPHqUsrTF/gQtnucRMo2B/oGvf8aOw=",
|
||||
"owner": "nix-community",
|
||||
"repo": "NUR",
|
||||
"rev": "fe8c1a700dbbfb474f7e80f6ca6223d0bd61d79d",
|
||||
"rev": "e27d8a76f2167da18bd37ab38f463c13daf2bc21",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -451,11 +737,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773135655,
|
||||
"narHash": "sha256-eb4/TZEU1cMpUPtUuxcr2sfiCciHtesBtPHzS1zh2Uo=",
|
||||
"lastModified": 1778402771,
|
||||
"narHash": "sha256-WS8hQ8Yk4M1rfkp2aUCaUkGVBU0ppCYAhklBk5kBdFU=",
|
||||
"owner": "robertjakub",
|
||||
"repo": "oom-hardware",
|
||||
"rev": "ad592fd988ee7a7c1bd68ff8b819973e1ae900ef",
|
||||
"rev": "9f338e9250b7c01ac97750851867fc8158e8f54c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -465,6 +751,27 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"personal-website": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1779906441,
|
||||
"narHash": "sha256-py8KJJMi4awjZHi5FWBPYfbRPvk3Rg9SeFkPJydsG2E=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "339a9ba1ef79dc9976af77f1fea0302de8a696d0",
|
||||
"revCount": 16,
|
||||
"type": "git",
|
||||
"url": "https://git.chiasson.cloud/Olivier/Personal-Website"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.chiasson.cloud/Olivier/Personal-Website"
|
||||
}
|
||||
},
|
||||
"quickshell": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -473,16 +780,16 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766725085,
|
||||
"narHash": "sha256-O2aMFdDUYJazFrlwL7aSIHbUSEm3ADVZjmf41uBJfHs=",
|
||||
"lastModified": 1776854048,
|
||||
"narHash": "sha256-lLbV66V3RMNp1l8/UelmR4YzoJ5ONtgvEtiUMJATH/o=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "41828c4180fb921df7992a5405f5ff05d2ac2fff",
|
||||
"revCount": 715,
|
||||
"rev": "783c953987dc56ff0601abe6845ed96f1d00495a",
|
||||
"revCount": 806,
|
||||
"type": "git",
|
||||
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
||||
},
|
||||
"original": {
|
||||
"rev": "41828c4180fb921df7992a5405f5ff05d2ac2fff",
|
||||
"rev": "783c953987dc56ff0601abe6845ed96f1d00495a",
|
||||
"type": "git",
|
||||
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
||||
}
|
||||
@@ -490,18 +797,21 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"cursor": "cursor",
|
||||
"ddrm": "ddrm",
|
||||
"dgop": "dgop",
|
||||
"dms": "dms",
|
||||
"flake-parts": "flake-parts",
|
||||
"home-manager": "home-manager",
|
||||
"import-tree": "import-tree",
|
||||
"mobile-nixos": "mobile-nixos",
|
||||
"navi": "navi",
|
||||
"nix-monitor": "nix-monitor",
|
||||
"nixcord": "nixcord",
|
||||
"nixos-raspberrypi": "nixos-raspberrypi",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nur": "nur",
|
||||
"oom-hardware": "oom-hardware",
|
||||
"personal-website": "personal-website",
|
||||
"sops-nix": "sops-nix",
|
||||
"spicetify-nix": "spicetify-nix",
|
||||
"swiftshare": "swiftshare",
|
||||
@@ -519,11 +829,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776119890,
|
||||
"narHash": "sha256-Zm6bxLNnEOYuS/SzrAGsYuXSwk3cbkRQZY0fJnk8a5M=",
|
||||
"lastModified": 1777944972,
|
||||
"narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "d4971dd58c6627bfee52a1ad4237637c0a2fb0cd",
|
||||
"rev": "c591bf665727040c6cc5cb409079acb22dcce33c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -537,14 +847,14 @@
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems"
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776578704,
|
||||
"narHash": "sha256-4+JHYCweZ/SSrMcu2nJ5gc7gop2scBk0JIIfaUKuTaQ=",
|
||||
"lastModified": 1779000518,
|
||||
"narHash": "sha256-wdtytSnzMe85J/qeXJALMzSLRFTZ1gBHwn81l1PtT8k=",
|
||||
"owner": "Gerg-L",
|
||||
"repo": "spicetify-nix",
|
||||
"rev": "73f6d24b4f5bdacc3b41ddcf9965bef2781f97dd",
|
||||
"rev": "5dde76b38418892ccb3d99e99bed7f8a43ac294c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -553,19 +863,35 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"stable": {
|
||||
"locked": {
|
||||
"lastModified": 1750133334,
|
||||
"narHash": "sha256-urV51uWH7fVnhIvsZIELIYalMYsyr2FCalvlRTzqWRw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "36ab78dab7da2e4e27911007033713bab534187b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-25.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"swiftshare": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777652580,
|
||||
"narHash": "sha256-CO4RXrd0eQ2INc8/S2CTWCIHUdvVkqwKZ/9o7a/pcFg=",
|
||||
"lastModified": 1779059662,
|
||||
"narHash": "sha256-PkBItyS1oZ4MJ+eEgF5iLKxx28rmSyk/bHp63tjW/5g=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "42575990389388d8d07da6fb4110d77ea7493159",
|
||||
"revCount": 75,
|
||||
"rev": "030da2f52d3cbe3c577ce12b5abbd35e90e1f093",
|
||||
"revCount": 79,
|
||||
"type": "git",
|
||||
"url": "https://git.chiasson.cloud/Olivier/SwiftShare"
|
||||
},
|
||||
@@ -604,6 +930,21 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"t2fanrd": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -611,11 +952,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775302822,
|
||||
"narHash": "sha256-QoK8SYoIc0d/PoRdIUo+fkDNAHZIP2+AJ6PDM9ehGiY=",
|
||||
"lastModified": 1777412856,
|
||||
"narHash": "sha256-WrcIo3y9uFCuzgzbxc465FBS3zAZMQlfYszefkOUCWc=",
|
||||
"owner": "GnomedDev",
|
||||
"repo": "T2FanRD",
|
||||
"rev": "5b1c0c10785b8e8dfe124a4d6aaa7c2becdac65c",
|
||||
"rev": "48baf962697ec3d4d969c74cf601ee8e15b7aeaa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -627,11 +968,11 @@
|
||||
"t2linux-patches": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1776111571,
|
||||
"narHash": "sha256-1neTptNNPtwbBYSQOE48GM8CYx780eI5JQTFYmwN0og=",
|
||||
"lastModified": 1779914035,
|
||||
"narHash": "sha256-VsHuI2CbQ8gFplW+51gUJvCqo1Ts10Ueks9aTtkAOiw=",
|
||||
"owner": "t2linux",
|
||||
"repo": "linux-t2-patches",
|
||||
"rev": "76589a89790c33c137d173f2d98b6096cd16b132",
|
||||
"rev": "7ee7d19c38e5df31a386b2a0c35ca8f064003960",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -640,6 +981,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"navi",
|
||||
"nixos-anywhere",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769691507,
|
||||
"narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"wallpapers": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -658,14 +1021,14 @@
|
||||
},
|
||||
"wrapper-modules": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776464146,
|
||||
"narHash": "sha256-XwLFfJDz71vIF7BAhnbLhrzQjmDC2uXdo7N0oHUrYzA=",
|
||||
"lastModified": 1779297405,
|
||||
"narHash": "sha256-VFoBwH7ZjVxCnvZTb5ODRXt70sLtWMxstive0N+RS50=",
|
||||
"owner": "BirdeeHub",
|
||||
"repo": "nix-wrapper-modules",
|
||||
"rev": "75febede14a0845f4ef429da692a0698bf433600",
|
||||
"rev": "e7ed7a1205945befdf2e0d73ba7df91d935e5af1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -682,11 +1045,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776663782,
|
||||
"narHash": "sha256-qzBBuxZbn7vPD9ZDl3xmCBGa6qEc8Q//76Cbx4W0tE4=",
|
||||
"lastModified": 1779455631,
|
||||
"narHash": "sha256-svU6Ro4xiMxMA1KJGwQ/nfKwz3yXE/SONCw2Z1qTXHA=",
|
||||
"owner": "0xc000022070",
|
||||
"repo": "zen-browser-flake",
|
||||
"rev": "b93be06dc91630bf0ced69c54d0e1e05e56ae460",
|
||||
"rev": "5bcdfcef664bf62831dcb4b947004d9c5fbf7201",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -91,6 +91,20 @@
|
||||
url = "git+https://git.chiasson.cloud/Olivier/SwiftShare";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
# After pushing Personal-Website, `nix flake update personal-website` refreshes the lock pin.
|
||||
personal-website = {
|
||||
url = "git+https://git.chiasson.cloud/Olivier/Personal-Website";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
# DDRM browser-extension backend
|
||||
ddrm = {
|
||||
url = "git+https://git.chiasson.cloud/Olivier/DDRM?rev=c3d014f96855e45b53f7391ce19729493887cb96";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
navi.url = "github:cafkafk/navi";
|
||||
};
|
||||
|
||||
outputs = inputs:
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
{
|
||||
inputs,
|
||||
self,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
ssh = self.lib.sshInventory;
|
||||
|
||||
# Remote deploy identity (see `nixosModules.systemDeployBuilder`).
|
||||
defaultTargetUser = _: "builder";
|
||||
|
||||
hostSpecs = {
|
||||
"14900k" = {
|
||||
configuration = self.nixosModules."14900kConfiguration";
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "14900k";
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
};
|
||||
ideapad = {
|
||||
configuration = self.nixosModules.ideapadConfiguration;
|
||||
system = "aarch64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "ideapad";
|
||||
system = "aarch64-linux";
|
||||
};
|
||||
};
|
||||
t2mbp = {
|
||||
configuration = self.nixosModules.t2mbpConfiguration;
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "t2mbp";
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
};
|
||||
uConsole = {
|
||||
modules = [
|
||||
inputs.nixos-raspberrypi.nixosModules.raspberry-pi-5.base
|
||||
inputs.oom-hardware.nixosModules.uc.kernel
|
||||
inputs.oom-hardware.nixosModules.uc.configtxt
|
||||
inputs.oom-hardware.nixosModules.uc.base-cm5
|
||||
self.nixosModules.uConsoleConfiguration
|
||||
];
|
||||
system = "aarch64-linux";
|
||||
specialArgs = inputs // {
|
||||
inherit self;
|
||||
inputs = inputs;
|
||||
host = "uConsole";
|
||||
system = "aarch64-linux";
|
||||
};
|
||||
};
|
||||
nix-server = {
|
||||
configuration = self.nixosModules.nix-serverConfiguration;
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "nix-server";
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
};
|
||||
r5500 = {
|
||||
configuration = self.nixosModules.r5500Configuration;
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "r5500";
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
deployments = lib.mapAttrs (
|
||||
name: entry:
|
||||
{
|
||||
targetHost = entry.hostName;
|
||||
targetUser = defaultTargetUser name;
|
||||
tags =
|
||||
[ name ]
|
||||
++ lib.optionals (name == "nix-server") [ "server" ]
|
||||
++ lib.optionals (lib.elem name [
|
||||
"ideapad"
|
||||
"uConsole"
|
||||
]) [ "aarch64" ];
|
||||
}
|
||||
// lib.optionalAttrs (name == "14900k") {
|
||||
allowLocalDeployment = true;
|
||||
}
|
||||
// lib.optionalAttrs (name == "nix-server") {
|
||||
targetPort = 22;
|
||||
}
|
||||
) ssh.activeHosts;
|
||||
|
||||
metaNixpkgs = import inputs.nixpkgs {
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
in
|
||||
{
|
||||
flake.navi = self.lib.mkNaviHiveConfig {
|
||||
inherit metaNixpkgs hostSpecs deployments;
|
||||
};
|
||||
|
||||
flake.naviHive = inputs.navi.lib.makeHive self.outputs.navi;
|
||||
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
system,
|
||||
...
|
||||
}:
|
||||
lib.optionalAttrs (lib.elem system [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
]) {
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = [ inputs.navi.packages.${system}.default ];
|
||||
shellHook = ''
|
||||
echo "Navi fleet deploy (from repo root):"
|
||||
echo " navi apply --on <host> # build + switch one host"
|
||||
echo " navi apply-local --node 14900k --sudo # switch this machine locally (needs root), --node if hostname differs"
|
||||
echo " navi tui # interactive fleet dashboard"
|
||||
'';
|
||||
};
|
||||
|
||||
apps = {
|
||||
navi = {
|
||||
type = "app";
|
||||
program = lib.getExe inputs.navi.packages.${system}.default;
|
||||
};
|
||||
navi-tui = {
|
||||
type = "app";
|
||||
program =
|
||||
toString (
|
||||
pkgs.writeShellApplication {
|
||||
name = "navi-tui";
|
||||
runtimeInputs = [ inputs.navi.packages.${system}.default ];
|
||||
text = ''
|
||||
exec navi tui "$@"
|
||||
'';
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.nixosModules.desktop = {
|
||||
imports = [
|
||||
self.nixosModules.desktopShellDmsOptions
|
||||
self.nixosModules.desktopHyprland
|
||||
self.nixosModules.desktopNiri
|
||||
self.nixosModules.desktopPlasma
|
||||
self.nixosModules.desktopOptions
|
||||
self.nixosModules.desktopGui
|
||||
self.nixosModules.desktopWallpapers
|
||||
self.nixosModules.desktopWaydroid
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
{ ... }: {
|
||||
flake.nixosModules.desktopGui =
|
||||
{ config, lib, pkgs, self, inputs, options, ... }:
|
||||
let
|
||||
pi5Greeter = self.lib.pi5NiriKdl;
|
||||
cfg = config.chiasson.desktop;
|
||||
guiEnabled = cfg.hyprland.enable || cfg.niri.enable || cfg.plasma.enable;
|
||||
hmAvailable = lib.hasAttrByPath [ "home-manager" "sharedModules" ] options;
|
||||
dm = cfg.displayManager;
|
||||
variant = dm.variant or "sddm";
|
||||
useGreeter = variant == "dankgreeter";
|
||||
sddmTheme =
|
||||
if dm.sddm.theme.package == null then
|
||||
"breeze"
|
||||
else
|
||||
"${dm.sddm.theme.package}/share/sddm/themes/${dm.sddm.theme.id}";
|
||||
effectiveDefaultSession =
|
||||
if cfg.defaultSession != null then
|
||||
cfg.defaultSession
|
||||
else if cfg.hyprland.enable then
|
||||
"hyprland"
|
||||
else if cfg.niri.enable then
|
||||
"niri"
|
||||
else
|
||||
"plasma";
|
||||
greeterCompositor =
|
||||
if effectiveDefaultSession == "niri" then
|
||||
"niri"
|
||||
else if effectiveDefaultSession == "hyprland" then
|
||||
"hyprland"
|
||||
else
|
||||
"niri";
|
||||
enabledUsers = config.chiasson.users.enabled or [ ];
|
||||
greeterConfigHome =
|
||||
if dm.greeter.configHome != null then
|
||||
dm.greeter.configHome
|
||||
else if enabledUsers != [ ] then
|
||||
"/home/${builtins.head enabledUsers}"
|
||||
else
|
||||
null;
|
||||
in
|
||||
{
|
||||
# Unconditional imports only — conditional imports that peek at `config` recurse during merge.
|
||||
imports = [
|
||||
inputs.dms.nixosModules.greeter
|
||||
];
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf guiEnabled {
|
||||
services.xserver.enable = true;
|
||||
|
||||
# Chromium/Electron (Edge, Vesktop, etc.) only add native Wayland + IME flags when this is
|
||||
# set; nixpkgs wrappers gate on NIXOS_OZONE_WL and WAYLAND_DISPLAY. Helps PipeWire/desktop
|
||||
# portal screen capture on wlroots-like sessions. Harmless on X11 (flags stay off).
|
||||
environment.sessionVariables.NIXOS_OZONE_WL = lib.mkDefault "1";
|
||||
|
||||
xdg.portal.enable = true;
|
||||
xdg.portal.extraPortals =
|
||||
[ pkgs.xdg-desktop-portal-gtk ]
|
||||
++ lib.optionals cfg.plasma.enable [ pkgs.kdePackages.xdg-desktop-portal-kde ];
|
||||
|
||||
# Backlight control: `brightnessctl` + its udev rule (auto-loaded from the package's
|
||||
# `lib/udev/rules.d`). The rule grants the `video` group write access to
|
||||
# `/sys/class/backlight/*/brightness`; the user catalog adds desktop users to `video`.
|
||||
# The Hyprland defaults already bind XF86MonBrightness* to brightnessctl, and the Niri
|
||||
# base config does the same — this guarantees the binary is actually present.
|
||||
environment.systemPackages = [ pkgs.brightnessctl ];
|
||||
services.udev.packages = [ pkgs.brightnessctl ];
|
||||
})
|
||||
(lib.mkIf (guiEnabled && !useGreeter) {
|
||||
services.displayManager.sddm = {
|
||||
enable = true;
|
||||
wayland.enable = dm.sddm.wayland.enable;
|
||||
theme = sddmTheme;
|
||||
inherit (dm.sddm) enableHidpi settings;
|
||||
extraPackages = lib.optionals (dm.sddm.theme.package != null) (
|
||||
with pkgs.kdePackages; [
|
||||
qtdeclarative
|
||||
qtsvg
|
||||
]
|
||||
);
|
||||
};
|
||||
services.displayManager.defaultSession = effectiveDefaultSession;
|
||||
})
|
||||
(lib.mkIf (guiEnabled && useGreeter) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = greeterConfigHome != null;
|
||||
message = "DankGreeter needs chiasson.desktop.displayManager.greeter.configHome or a non-empty chiasson.users.enabled list.";
|
||||
}
|
||||
];
|
||||
|
||||
services.displayManager.defaultSession = effectiveDefaultSession;
|
||||
|
||||
programs.dank-material-shell.greeter = {
|
||||
enable = true;
|
||||
compositor = {
|
||||
name = greeterCompositor;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.niri.raspberryPi5DrmWorkaround && greeterCompositor == "niri") {
|
||||
customConfig = lib.mkDefault pi5Greeter.dankGreeterCompositorConfig;
|
||||
};
|
||||
configHome = greeterConfigHome;
|
||||
};
|
||||
})
|
||||
(lib.mkIf (cfg.defaultPackages.enabled && guiEnabled) {
|
||||
environment.systemPackages = cfg.defaultPackages.packages;
|
||||
})
|
||||
(lib.mkIf (cfg.extraPackages != [ ] && guiEnabled) {
|
||||
environment.systemPackages = cfg.extraPackages;
|
||||
})
|
||||
(lib.mkIf (guiEnabled && cfg.keyring.enable) {
|
||||
services.gnome.gnome-keyring.enable = true;
|
||||
security.pam.services.login.enableGnomeKeyring = true;
|
||||
services.xserver.updateDbusEnvironment = lib.mkDefault true;
|
||||
# Electron apps (Element, Slack, etc.) use libsecret via keytar; without it they report
|
||||
# "unsupported keyring" even if gnome-keyring is enabled.
|
||||
environment.systemPackages = [ pkgs.libsecret ];
|
||||
})
|
||||
(lib.mkIf (guiEnabled && cfg.keyring.enable && !useGreeter) {
|
||||
security.pam.services.sddm.enableGnomeKeyring = true;
|
||||
})
|
||||
(lib.mkIf (guiEnabled && cfg.keyring.enable && useGreeter) {
|
||||
security.pam.services.greetd.enableGnomeKeyring = true;
|
||||
})
|
||||
(lib.mkIf (guiEnabled && cfg.keyring.enable && hmAvailable) {
|
||||
"home-manager".sharedModules = [
|
||||
({ lib, pkgs, ... }: {
|
||||
services.gnome-keyring = {
|
||||
enable = lib.mkDefault true;
|
||||
components = [ "secrets" ];
|
||||
};
|
||||
home.packages = [
|
||||
pkgs.gcr
|
||||
pkgs.libsecret
|
||||
];
|
||||
})
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
{ self, ... }: {
|
||||
flake.nixosModules.desktopHyprland =
|
||||
{ config, options, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.chiasson.desktop;
|
||||
hmAvailable = lib.hasAttrByPath [ "home-manager" "sharedModules" ] options;
|
||||
in
|
||||
{
|
||||
options.chiasson.desktop.hyprland = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Hyprland session + HM wiring.";
|
||||
};
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
default = { };
|
||||
description = "Extra `wayland.windowManager.hyprland.settings` merged with defaults.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf cfg.hyprland.enable {
|
||||
programs.hyprland.enable = true;
|
||||
})
|
||||
(lib.mkIf (cfg.hyprland.enable && hmAvailable) {
|
||||
"home-manager".sharedModules = [ self.homeManagerModules.desktopHyprland ];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
flake.homeManagerModules.desktopHyprland = {
|
||||
config,
|
||||
lib,
|
||||
osConfig ? { },
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
hyprlandEnabled = osConfig.chiasson.desktop.hyprland.enable or false;
|
||||
keyringEnabled = osConfig.chiasson.desktop.keyring.enable or false;
|
||||
# nixpkgs hyprland-plugins pin is stale for current Hyprland — override to a known-good rev.
|
||||
hyprbarsPatched =
|
||||
let
|
||||
hyprlandPluginsSrc = pkgs.fetchFromGitHub {
|
||||
owner = "hyprwm";
|
||||
repo = "hyprland-plugins";
|
||||
rev = "b85a56b9531013c79f2f3846fd6ee2ff014b8960";
|
||||
hash = "sha256-xwNa+1D8WPsDnJtUofDrtyDCZKZotbUymzV/R5s+M0I=";
|
||||
};
|
||||
in
|
||||
pkgs.hyprlandPlugins.hyprbars.overrideAttrs (_: {
|
||||
version = "unstable-2026-02-23";
|
||||
src = "${hyprlandPluginsSrc}/hyprbars";
|
||||
});
|
||||
in
|
||||
{
|
||||
config = lib.mkIf hyprlandEnabled {
|
||||
wayland.windowManager.hyprland = {
|
||||
enable = true;
|
||||
# null = use NixOS Hyprland/xdg-desktop-portal-hyprland (same versions as the session).
|
||||
package = null;
|
||||
portalPackage = null;
|
||||
plugins = [ hyprbarsPatched ];
|
||||
extraConfig = ''
|
||||
source = ~/.config/hypr/colors.conf
|
||||
'';
|
||||
|
||||
settings = lib.mkMerge [
|
||||
(lib.mkIf keyringEnabled {
|
||||
exec-once = lib.mkBefore [
|
||||
"dbus-update-activation-environment --systemd --all"
|
||||
];
|
||||
})
|
||||
{
|
||||
monitor = [ ",preferred,auto,auto" ];
|
||||
general = {
|
||||
gaps_in = 8;
|
||||
gaps_out = 4;
|
||||
border_size = 2;
|
||||
allow_tearing = false;
|
||||
};
|
||||
cursor.no_hardware_cursors = true;
|
||||
env = [
|
||||
"XCURSOR_THEME,phinger-cursors-dark"
|
||||
"XCURSOR_SIZE,32"
|
||||
];
|
||||
input = {
|
||||
kb_layout = "ca";
|
||||
kb_variant = "";
|
||||
numlock_by_default = true;
|
||||
};
|
||||
binds.scroll_event_delay = 50;
|
||||
exec-once = [
|
||||
"nm-applet --indicator &"
|
||||
"sleep 1 && hyprctl reload"
|
||||
];
|
||||
|
||||
# Default keybinds
|
||||
bind =
|
||||
[
|
||||
"SUPER,T,exec,kitty -e fish"
|
||||
"ControlSuper,T,exec,konsole"
|
||||
"SUPER,D,exec,rofi -show drun"
|
||||
"ControlSuper,D,exec,rofi -show window"
|
||||
"SUPER,E,exec,dolphin"
|
||||
"Super, Q, killactive"
|
||||
"ControlSuper, Q, exec, hyprctl kill"
|
||||
"Super, F, fullscreen, 0"
|
||||
"Super, G, fullscreen, 1"
|
||||
"ShiftSuper, F, fullscreenstate, 0 3"
|
||||
"Super, Minus, splitratio, -0.1"
|
||||
"Super, Equal, splitratio, 0.1"
|
||||
"AltSuper, Space, togglefloating"
|
||||
"Super, P, pin"
|
||||
"Super, Space, exec, dms ipc call spotlight toggle"
|
||||
"Super, I, exec, dms ipc call settings focusOrToggle"
|
||||
"Super, N, exec, dms ipc call notepad toggle"
|
||||
"ShiftSuper, N, exec, dms ipc call notifications toggle"
|
||||
"Super, M, exec, dms ipc call processlist focusOrToggle"
|
||||
"Super, L, exec, dms ipc call lock lock"
|
||||
"ShiftSuper, V, exec, dms ipc call clipboard toggle"
|
||||
"Super, Tab, cyclenext"
|
||||
"Super, Tab, bringactivetotop"
|
||||
"Super, left, movefocus, l"
|
||||
"Super, right, movefocus, r"
|
||||
"Super, up, movefocus, u"
|
||||
"Super, down, movefocus, d"
|
||||
"ShiftSuper, left, movewindow, l"
|
||||
"ShiftSuper, right, movewindow, r"
|
||||
"ShiftSuper, up, movewindow, u"
|
||||
"ShiftSuper, down, movewindow, d"
|
||||
]
|
||||
++ (builtins.map (i: "Super, ${toString i}, workspace, ${toString i}") (builtins.genList (n: n + 1) 9))
|
||||
++ (builtins.map (i: "ControlSuper, ${toString i}, focusworkspaceoncurrentmonitor, ${toString i}") (builtins.genList (n: n + 1) 9))
|
||||
++ (builtins.map (i: "ShiftSuper, ${toString i}, movetoworkspacesilent, ${toString i}") (builtins.genList (n: n + 1) 9));
|
||||
|
||||
bindm = [
|
||||
" , mouse:282, movewindow"
|
||||
"Super, mouse:272, movewindow"
|
||||
"Super, mouse:273, resizewindow"
|
||||
];
|
||||
|
||||
bindl = [
|
||||
"Super, mouse_up, splitratio, -0.1"
|
||||
"Super, mouse_down, splitratio, 0.1"
|
||||
" , XF86AudioPlay, exec, playerctl play-pause"
|
||||
" , XF86AudioPrev, exec, playerctl previous"
|
||||
" , XF86AudioNext, exec, playerctl next"
|
||||
" , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
|
||||
" , XF86MonBrightnessDown, exec, brightnessctl s 10%-"
|
||||
" , XF86MonBrightnessUp, exec, brightnessctl s +10%"
|
||||
];
|
||||
|
||||
bindel = [
|
||||
" , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"
|
||||
" , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
|
||||
];
|
||||
|
||||
# Decoration / blur
|
||||
decoration = {
|
||||
rounding = 10;
|
||||
active_opacity = 0.95;
|
||||
inactive_opacity = 0.85;
|
||||
shadow = {
|
||||
enabled = true;
|
||||
range = 5;
|
||||
render_power = 8;
|
||||
};
|
||||
blur = {
|
||||
enabled = true;
|
||||
new_optimizations = true;
|
||||
xray = false;
|
||||
size = 2;
|
||||
passes = 4;
|
||||
vibrancy = 10;
|
||||
};
|
||||
};
|
||||
misc = {
|
||||
disable_hyprland_logo = true;
|
||||
disable_splash_rendering = true;
|
||||
};
|
||||
plugin.hyprbars = {
|
||||
enabled = true;
|
||||
bar_height = 30;
|
||||
bar_blur = true;
|
||||
bar_padding = 10;
|
||||
bar_button_padding = 7;
|
||||
bar_precedence_over_border = true;
|
||||
bar_part_of_window = true;
|
||||
bar_title_enabled = true;
|
||||
bar_text_size = 10;
|
||||
bar_text_font = "Sans";
|
||||
bar_text_align = "center";
|
||||
bar_buttons_alignment = "left";
|
||||
icon_on_hover = true;
|
||||
"hyprbars-button" = [
|
||||
"rgb(ed6a5f), 12, , hyprctl dispatch killactive, rgb(460804)"
|
||||
"rgb(f6be50), 12, , hyprctl dispatch movetoworkspacesilent special:minimized, rgb(90591d)"
|
||||
"rgb(61c555), 12, , hyprctl dispatch fullscreen 1, rgb(2a6218)"
|
||||
];
|
||||
};
|
||||
|
||||
windowrule = [
|
||||
"sync_fullscreen 0, match:class ^(?i)microsoft-edge|Spotify|org.kde.gwenview|zen-beta$"
|
||||
"opacity 1.0 override 0.95 override, match:class ^(?i)microsoft-edge$"
|
||||
"opacity 1.0 override 1.00 override, match:class ^(?i)com.stremio.stremio$"
|
||||
"opacity 1.0 override 0.85 override, match:class ^(?i)zen-beta$"
|
||||
"no_screen_share on, match:class ^(?i)(microsoft-edge|zen-beta)$, match:title ^(?i).*(scotiabank|paypal).*"
|
||||
"no_screen_share on, match:class ^(?i)microsoft-edge$, match:initial_title ^(?i).*(?i)personal 2.*edge.*$"
|
||||
"hyprbars:no_bar on, match:class ^(?i)(microsoft-edge|Cursor|Flow|looking-glass-client|localsend_app)$"
|
||||
];
|
||||
}
|
||||
(osConfig.chiasson.desktop.hyprland.settings or { })
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
{ self, inputs, ... }:
|
||||
let
|
||||
# Keep defaults in this let — a bare niri-settings.nix next to this file would get picked up by import-tree.
|
||||
niriBaseSettings =
|
||||
pkgs:
|
||||
{
|
||||
input.keyboard = {
|
||||
xkb.layout = "ca";
|
||||
xkb.variant = "";
|
||||
};
|
||||
input."focus-follows-mouse" = _: {
|
||||
props."max-scroll-amount" = "45%";
|
||||
content = { };
|
||||
};
|
||||
input."warp-mouse-to-focus" = _: { };
|
||||
layout.gaps = 5;
|
||||
|
||||
window-rules = [
|
||||
{
|
||||
matches = [
|
||||
{
|
||||
app-id = "^$";
|
||||
title = "^$";
|
||||
}
|
||||
];
|
||||
open-floating = true;
|
||||
open-focused = false;
|
||||
}
|
||||
];
|
||||
#TODO[epic=Binds] Go over binds again
|
||||
binds = {
|
||||
"Mod+T"."spawn-sh" = "${pkgs.lib.getExe pkgs.kitty} -e fish"; #TODO[epic=Binds] This should only be set if having kitty
|
||||
"Mod+Control+T"."spawn-sh" = "konsole"; #TODO[epic=Binds] This should only be set if having konsole
|
||||
"Mod+D"."spawn-sh" = "rofi -show drun"; #TODO[epic=Binds] This should only be set if having rofi
|
||||
"Mod+Space"."spawn-sh" = "dms ipc call spotlight toggle"; #TODO[epic=Binds] This should only be set if having dms
|
||||
"Mod+E"."spawn-sh" = "dolphin"; #TODO[epic=Binds] This should only be set if having dolphin
|
||||
"Mod+Control+O"."spawn-sh" = "rofi -show window"; #TODO[epic=Binds] This should only be set if having rofi
|
||||
"Mod+V"."spawn-sh" = "dms ipc call clipboard toggle"; #TODO[epic=Binds] This should only be set if having dms
|
||||
"Mod+N"."spawn-sh" = "dms ipc call notepad toggle"; #TODO[epic=Binds] This should only be set if having dms
|
||||
"Mod+Shift+N"."spawn-sh" = "dms ipc call notifications toggle"; #TODO[epic=Binds] This should only be set if having dms
|
||||
"Mod+M"."spawn-sh" = "dms ipc call processlist toggle"; #TODO[epic=Binds] This should only be set if having dms
|
||||
"Mod+L"."spawn-sh" = "dms ipc call lock lock"; #TODO[epic=Binds] This should only be set if having dms
|
||||
|
||||
"Mod+Q"."close-window" = _: { };
|
||||
"Mod+F"."maximize-column" = _: { };
|
||||
"Mod+Shift+F"."fullscreen-window" = _: { };
|
||||
"Mod+O"."toggle-overview" = _: { };
|
||||
"Mod+Shift+NumberSign"."show-hotkey-overlay" = _: { };
|
||||
"Mod+Shift+E".quit = _: { };
|
||||
|
||||
"Mod+Left"."focus-column-or-monitor-left" = _: { };
|
||||
"Mod+Down"."focus-window-or-monitor-down" = _: { };
|
||||
"Mod+Up"."focus-window-or-monitor-up" = _: { };
|
||||
"Mod+Right"."focus-column-or-monitor-right" = _: { };
|
||||
|
||||
"Mod+Shift+WheelScrollDown"."focus-workspace-down" = _: { };
|
||||
"Mod+Shift+WheelScrollUp"."focus-workspace-up" = _: { };
|
||||
|
||||
"Mod+WheelScrollDown"."focus-column-or-monitor-right" = _: { };
|
||||
"Mod+WheelScrollUp"."focus-column-or-monitor-left" = _: { };
|
||||
|
||||
"Mod+Shift+Left"."move-column-left-or-to-monitor-left" = _: { };
|
||||
"Mod+Shift+Down"."move-window-down" = _: { };
|
||||
"Mod+Shift+Up"."move-window-up" = _: { };
|
||||
"Mod+Shift+Right"."move-column-right-or-to-monitor-right" = _: { };
|
||||
"Mod+Page_Up"."focus-workspace-up" = _: { };
|
||||
"Mod+Page_Down"."focus-workspace-down" = _: { };
|
||||
"Mod+Shift+Page_Up"."move-column-to-workspace-up" = _: { };
|
||||
"Mod+Shift+Page_Down"."move-column-to-workspace-down" = _: { };
|
||||
"Mod+R"."switch-preset-column-width" = _: { };
|
||||
"Mod+BracketLeft"."consume-or-expel-window-left" = _: { };
|
||||
"Mod+BracketRight"."consume-or-expel-window-right" = _: { };
|
||||
"Mod+Comma"."consume-window-into-column" = _: { };
|
||||
"Mod+Period"."expel-window-from-column" = _: { };
|
||||
"Mod+Alt+Space"."toggle-window-floating" = _: { };
|
||||
#"Mod+Shift+V"."switch-focus-between-floating-and-tiling" = _: { }; #TODO[epic=Binds] Find free bind that is not in the way and not overcomplicated to remember
|
||||
|
||||
"Mod+1"."focus-workspace" = 1;
|
||||
"Mod+2"."focus-workspace" = 2;
|
||||
"Mod+3"."focus-workspace" = 3;
|
||||
"Mod+4"."focus-workspace" = 4;
|
||||
"Mod+5"."focus-workspace" = 5;
|
||||
"Mod+6"."focus-workspace" = 6;
|
||||
"Mod+7"."focus-workspace" = 7;
|
||||
"Mod+8"."focus-workspace" = 8;
|
||||
"Mod+9"."focus-workspace" = 9;
|
||||
"Mod+Ctrl+1"."move-column-to-workspace" = 1;
|
||||
"Mod+Ctrl+2"."move-column-to-workspace" = 2;
|
||||
"Mod+Ctrl+3"."move-column-to-workspace" = 3;
|
||||
"Mod+Ctrl+4"."move-column-to-workspace" = 4;
|
||||
"Mod+Ctrl+5"."move-column-to-workspace" = 5;
|
||||
"Mod+Ctrl+6"."move-column-to-workspace" = 6;
|
||||
"Mod+Ctrl+7"."move-column-to-workspace" = 7;
|
||||
"Mod+Ctrl+8"."move-column-to-workspace" = 8;
|
||||
"Mod+Ctrl+9"."move-column-to-workspace" = 9;
|
||||
|
||||
"XF86AudioRaiseVolume".spawn = [
|
||||
"wpctl"
|
||||
"set-volume"
|
||||
"@DEFAULT_AUDIO_SINK@"
|
||||
"0.05+"
|
||||
];
|
||||
"XF86AudioLowerVolume".spawn = [
|
||||
"wpctl"
|
||||
"set-volume"
|
||||
"@DEFAULT_AUDIO_SINK@"
|
||||
"0.05-"
|
||||
];
|
||||
"XF86AudioMute".spawn = [
|
||||
"wpctl"
|
||||
"set-mute"
|
||||
"@DEFAULT_AUDIO_SINK@"
|
||||
"toggle"
|
||||
];
|
||||
|
||||
# Backlight: relies on `pkgs.brightnessctl` being on PATH (provided by `desktopGui` when
|
||||
# any GUI session is enabled) and the user being in the `video` group (catalog default).
|
||||
"XF86MonBrightnessUp".spawn = [ "brightnessctl" "set" "+5%" ];
|
||||
"XF86MonBrightnessDown".spawn = [ "brightnessctl" "set" "5%-" ];
|
||||
|
||||
Print.screenshot = _: { };
|
||||
"Ctrl+Print"."screenshot-screen" = _: { };
|
||||
"Alt+Print"."screenshot-window" = _: { };
|
||||
};
|
||||
};
|
||||
|
||||
keyringNiriStartupKdl = ''
|
||||
spawn-at-startup "dbus-update-activation-environment" "--systemd" "--all"
|
||||
'';
|
||||
|
||||
mergeNiriSettings =
|
||||
pkgs: niriCfg: keyringEnable:
|
||||
let
|
||||
lib = pkgs.lib;
|
||||
pi5 = self.lib.pi5NiriKdl;
|
||||
rpi5Extra = lib.optionalString (niriCfg.raspberryPi5DrmWorkaround or false) pi5.drmExtraConfig;
|
||||
base = niriBaseSettings pkgs;
|
||||
userExtra = niriCfg.extraSettings or { };
|
||||
keyringExtra = lib.optionalString keyringEnable keyringNiriStartupKdl;
|
||||
extraConfigMerged = keyringExtra + rpi5Extra + (userExtra.extraConfig or "");
|
||||
windowRules = (base.window-rules or [ ]) ++ (userExtra.window-rules or [ ]);
|
||||
in
|
||||
lib.recursiveUpdate base (
|
||||
lib.removeAttrs userExtra [ "window-rules" "extraConfig" ]
|
||||
// lib.optionalAttrs (windowRules != [ ]) {
|
||||
window-rules = windowRules;
|
||||
}
|
||||
// lib.optionalAttrs (rpi5Extra != "" || (userExtra.extraConfig or "") != "") {
|
||||
extraConfig = extraConfigMerged;
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
flake.homeManagerModules.desktopNiri =
|
||||
{ lib, pkgs, osConfig ? { }, ... }:
|
||||
let
|
||||
niriOs = osConfig.chiasson.desktop.niri or { };
|
||||
niriEnabled = osConfig.chiasson.desktop.niri.enable or false;
|
||||
keyringEnabled = osConfig.chiasson.desktop.keyring.enable or false;
|
||||
mergedSettings = mergeNiriSettings pkgs niriOs keyringEnabled;
|
||||
niriConfigPkg = inputs.wrapper-modules.wrappers.niri.wrap {
|
||||
inherit pkgs;
|
||||
settings = mergedSettings;
|
||||
};
|
||||
in
|
||||
{
|
||||
config = lib.mkIf niriEnabled {
|
||||
xdg.configFile."niri/config.kdl".source = "${niriConfigPkg}/niri-config.kdl";
|
||||
};
|
||||
};
|
||||
|
||||
flake.nixosModules.desktopNiri =
|
||||
{ config, options, lib, pkgs, self, ... }:
|
||||
let
|
||||
cfg = config.chiasson.desktop;
|
||||
hmAvailable = lib.hasAttrByPath [ "home-manager" "sharedModules" ] options;
|
||||
in
|
||||
{
|
||||
options.chiasson.desktop.niri = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Niri compositor session + NixOS packages.";
|
||||
};
|
||||
|
||||
raspberryPi5DrmWorkaround = lib.mkEnableOption ''
|
||||
Pi 5 + RP1 DSI: Niri DRM debug rules (renderD128, ignore card1/2) + matching DankGreeter niri config.
|
||||
Opt-in only — HDMI-only / other Pi setups do not need this.
|
||||
'';
|
||||
|
||||
extraSettings = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
default = { };
|
||||
description = ''
|
||||
Merged into shared defaults → `config.kdl` via wrapper-modules. Put raw KDL in `extraConfig`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = !cfg.niri.raspberryPi5DrmWorkaround || cfg.niri.enable;
|
||||
message = "chiasson.desktop.niri.raspberryPi5DrmWorkaround requires chiasson.desktop.niri.enable.";
|
||||
}
|
||||
];
|
||||
}
|
||||
(lib.mkIf cfg.niri.enable {
|
||||
programs.niri.enable = true;
|
||||
programs.niri.package = pkgs.niri;
|
||||
programs.xwayland.enable = true;
|
||||
xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gnome ];
|
||||
# Niri resolves `xwayland-satellite` from PATH to provide XWayland + `$DISPLAY` for X11
|
||||
# clients (Steam, etc.). See https://github.com/YaLTeR/niri/issues/452
|
||||
environment.systemPackages = [ pkgs.xwayland-satellite ];
|
||||
})
|
||||
(lib.mkIf (cfg.niri.enable && hmAvailable) {
|
||||
"home-manager".sharedModules = [ self.homeManagerModules.desktopNiri ];
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
{ ... }: {
|
||||
flake.nixosModules.desktopOptions =
|
||||
{ config, options, lib, pkgs, self, inputs, ... }:
|
||||
let
|
||||
cfg = config.chiasson.desktop;
|
||||
hmAvailable = lib.hasAttrByPath [ "home-manager" "sharedModules" ] options;
|
||||
guiEnabled = cfg.hyprland.enable || cfg.niri.enable || cfg.plasma.enable;
|
||||
dmsEnabled = cfg.shell == "dms";
|
||||
sddmIni = pkgs.formats.ini { };
|
||||
# Pixie SDDM theme — Qt6 main; upstream has a qt5 branch if you need it.
|
||||
pixieSddm = pkgs.stdenvNoCC.mkDerivation {
|
||||
pname = "pixie-sddm";
|
||||
version = "0-unstable-2026-03-29";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "xCaptaiN09";
|
||||
repo = "pixie-sddm";
|
||||
rev = "12a5f459ebd6d699be42c188c10976c8bb7076d7";
|
||||
hash = "sha256-lmE/49ySuAZDh5xLochWqfSw9qWrIV+fYaK5T2Ckck8=";
|
||||
};
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
installPhase = ''
|
||||
mkdir -p "$out/share/sddm/themes/pixie"
|
||||
cp -r "$src"/. "$out/share/sddm/themes/pixie/"
|
||||
'';
|
||||
meta = {
|
||||
description = "Pixel / Material 3 inspired SDDM theme (Qt6)";
|
||||
homepage = "https://github.com/xCaptaiN09/pixie-sddm";
|
||||
license = lib.licenses.mit;
|
||||
platforms = lib.platforms.linux;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options.chiasson.desktop = {
|
||||
defaultSession = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum [
|
||||
"hyprland"
|
||||
"niri"
|
||||
"plasma"
|
||||
]);
|
||||
default = null;
|
||||
example = "niri";
|
||||
description = ''
|
||||
DM session preference; `null` picks from which compositor flags are enabled. Turn on only
|
||||
the compositor you use so SDDM does not drag in extras.
|
||||
'';
|
||||
};
|
||||
|
||||
shell = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum [ "dms" ]);
|
||||
default = null;
|
||||
example = "dms";
|
||||
description = "Desktop shell overlay (e.g. DMS). Extend the enum when you add another.";
|
||||
};
|
||||
|
||||
displayManager = {
|
||||
variant = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum [
|
||||
"sddm"
|
||||
"dankgreeter"
|
||||
]);
|
||||
default = null;
|
||||
description = ''
|
||||
SDDM vs DankGreeter (greetd + DMS — [docs](https://danklinux.com/docs/dankgreeter/nixos-flake)).
|
||||
`null`: Plasma-only → SDDM; Hyprland/Niri + `desktop.shell = "dms"` → DankGreeter; else SDDM.
|
||||
'';
|
||||
};
|
||||
|
||||
greeter = {
|
||||
configHome = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "/home/olivier";
|
||||
description = ''
|
||||
Whose DMS files to mirror into the greeter cache. `null` → first entry in `chiasson.users.enabled`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
displayManager.sddm = {
|
||||
wayland.enable = lib.mkEnableOption ''
|
||||
SDDM greeter on Wayland (nicer on HiDPI; turn off if the greeter glitches on your GPU).
|
||||
'' // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
enableHidpi = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Passed through to `services.displayManager.sddm.enableHidpi`.";
|
||||
};
|
||||
|
||||
theme = {
|
||||
package = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.package;
|
||||
default = pixieSddm;
|
||||
description = ''
|
||||
Package providing `share/sddm/themes/<id>`. Default: bundled [Pixie](https://github.com/xCaptaiN09/pixie-sddm).
|
||||
`null` → Breeze. Other nixpkgs examples: `catppuccin-sddm`, `sddm-sugar-dark`.
|
||||
'';
|
||||
};
|
||||
|
||||
id = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "pixie";
|
||||
description = ''
|
||||
Subdir under `share/sddm/themes/` (default `pixie`). Match your theme package (e.g. catppuccin ids).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = sddmIni.type;
|
||||
default = { };
|
||||
description = "Extra `services.displayManager.sddm.settings` (INI).";
|
||||
};
|
||||
};
|
||||
|
||||
plasma.enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Plasma 6 session bits for this flake.";
|
||||
};
|
||||
|
||||
defaultPackages = {
|
||||
enabled = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Shared desktop utility packages (ntfs3g, cifs-utils, …).";
|
||||
};
|
||||
packages = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = with pkgs; [
|
||||
ntfs3g
|
||||
cifs-utils
|
||||
usbutils
|
||||
xhost
|
||||
];
|
||||
description = "Packages merged when `defaultPackages.enabled` is true.";
|
||||
};
|
||||
};
|
||||
|
||||
extraPackages = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = [ ];
|
||||
description = "Extra packages on GUI hosts.";
|
||||
};
|
||||
|
||||
homeManager = {
|
||||
bundleWisdom = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Add `wisdom` (baseline + bash) to HM `sharedModules`. Other slices still go in per-user `extraModules`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
keyring = {
|
||||
enable = lib.mkEnableOption ''
|
||||
gnome-keyring + pam (login + sddm or greetd) + HM user service + `gcr` + `services.xserver.updateDbusEnvironment`.
|
||||
niri/hyprland: `dbus-update-activation-environment` at compositor start so libsecret/Electron see `WAYLAND_DISPLAY`.
|
||||
'' // {
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.defaultSession != "hyprland" || cfg.hyprland.enable;
|
||||
message = "chiasson.desktop.defaultSession = \"hyprland\" requires chiasson.desktop.hyprland.enable = true.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.defaultSession != "niri" || cfg.niri.enable;
|
||||
message = "chiasson.desktop.defaultSession = \"niri\" requires chiasson.desktop.niri.enable = true.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.defaultSession != "plasma" || cfg.plasma.enable;
|
||||
message = "chiasson.desktop.defaultSession = \"plasma\" requires chiasson.desktop.plasma.enable = true.";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
cfg.displayManager.variant != "dankgreeter" || cfg.hyprland.enable || cfg.niri.enable;
|
||||
message = "chiasson.desktop.displayManager.variant = \"dankgreeter\" requires chiasson.desktop.hyprland or chiasson.desktop.niri.";
|
||||
}
|
||||
{
|
||||
assertion = cfg.displayManager.variant != "dankgreeter" || cfg.shell == "dms";
|
||||
message = "DankGreeter expects chiasson.desktop.shell = \"dms\" for DMS/matugen sync; use SDDM or enable DMS.";
|
||||
}
|
||||
];
|
||||
}
|
||||
(lib.mkIf guiEnabled {
|
||||
chiasson.desktop.displayManager.variant = lib.mkDefault (
|
||||
if cfg.plasma.enable && !(cfg.hyprland.enable || cfg.niri.enable) then
|
||||
"sddm"
|
||||
else if (cfg.hyprland.enable || cfg.niri.enable) && cfg.shell == "dms" then
|
||||
"dankgreeter"
|
||||
else
|
||||
"sddm"
|
||||
);
|
||||
})
|
||||
(lib.mkIf (guiEnabled && hmAvailable && cfg.homeManager.bundleWisdom) {
|
||||
"home-manager".sharedModules = [ self.homeManagerModules.wisdom ];
|
||||
})
|
||||
(lib.mkIf (dmsEnabled && hmAvailable) {
|
||||
"home-manager".sharedModules = [ self.homeManagerModules.desktopShellDms ];
|
||||
})
|
||||
(lib.mkIf (hmAvailable && (dmsEnabled || cfg.niri.enable)) {
|
||||
"home-manager".extraSpecialArgs = { inherit inputs; };
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{ ... }: {
|
||||
flake.nixosModules.desktopPlasma =
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.chiasson.desktop;
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.plasma.enable {
|
||||
services.desktopManager.plasma6.enable = true;
|
||||
environment.etc."xdg/menus/applications.menu".text =
|
||||
builtins.readFile "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
{ inputs, ... }: {
|
||||
flake.nixosModules.desktopShellDmsOptions = { lib, ... }: {
|
||||
options.chiasson.desktop.shells.dms = {
|
||||
enableGpuTemp = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "GPU temp in DMS bar.";
|
||||
};
|
||||
obsidianSnippetsDir = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Legacy single Obsidian snippets dir for matugen.";
|
||||
};
|
||||
obsidianConfigDirs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "Vault `.obsidian/` paths for matugen.";
|
||||
};
|
||||
obsidianSnippetsDirs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "Explicit `.obsidian/snippets` paths.";
|
||||
};
|
||||
extraRightBarWidgets = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.attrs;
|
||||
default = [ ];
|
||||
description = "Extra right-bar widgets (prepended).";
|
||||
};
|
||||
enableWvkbdToggle = lib.mkEnableOption ''
|
||||
wvkbd DMS plugin + bar toggle (touch / uConsole).
|
||||
'';
|
||||
enableRbwLockToggle = lib.mkEnableOption ''
|
||||
rbw vault lock/unlock button in the DMS bar (Bitwarden CLI via rbw).
|
||||
'';
|
||||
rebuildCommand = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
||||
default = null;
|
||||
example = [ "sudo" "nixos-rebuild" "switch" "--flake" ".#14900k" ];
|
||||
description = "Command used by DMS nix-monitor widget for rebuild actions.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
flake.homeManagerModules.desktopShellDms = {
|
||||
lib,
|
||||
osConfig ? { },
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = lib.attrByPath [ "chiasson" "desktop" "shells" "dms" ] { } osConfig;
|
||||
selectedShell = lib.attrByPath [ "chiasson" "desktop" "shell" ] null osConfig;
|
||||
dmsEnabled = selectedShell == "dms";
|
||||
hostName = lib.attrByPath [ "networking" "hostName" ] "nixos" osConfig;
|
||||
rebuildCommand =
|
||||
if (cfg.rebuildCommand or null) != null then
|
||||
cfg.rebuildCommand
|
||||
else
|
||||
[ "sudo" "nixos-rebuild" "switch" "--flake" ".#${hostName}" ];
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./home-manager/default.nix
|
||||
];
|
||||
|
||||
config = lib.mkIf dmsEnabled {
|
||||
dms.enable = true;
|
||||
dms.enableGpuTemp = cfg.enableGpuTemp or true;
|
||||
dms.obsidianSnippetsDir = cfg.obsidianSnippetsDir or null;
|
||||
dms.obsidianConfigDirs = cfg.obsidianConfigDirs or [ ];
|
||||
dms.obsidianSnippetsDirs = cfg.obsidianSnippetsDirs or [ ];
|
||||
dms.enableWvkbdToggle = cfg.enableWvkbdToggle or false;
|
||||
dms.enableRbwLockToggle = cfg.enableRbwLockToggle or false;
|
||||
dms.extraRightBarWidgets =
|
||||
(lib.optionals (cfg.enableWvkbdToggle or false) [
|
||||
{
|
||||
id = "wvkbdToggle";
|
||||
enabled = true;
|
||||
}
|
||||
])
|
||||
++ (lib.optionals (cfg.enableRbwLockToggle or false) [
|
||||
{
|
||||
id = "rbwLockToggle";
|
||||
enabled = true;
|
||||
}
|
||||
])
|
||||
++ (cfg.extraRightBarWidgets or [ ]);
|
||||
programs.nix-monitor.rebuildCommand = rebuildCommand;
|
||||
};
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Plugins
|
||||
|
||||
PluginComponent {
|
||||
id: root
|
||||
|
||||
property bool vaultUnlocked: false
|
||||
|
||||
function refreshLockState() {
|
||||
if (!statusProcess.running)
|
||||
statusProcess.exec(["rbw", "unlocked"]);
|
||||
}
|
||||
|
||||
function toggleLock() {
|
||||
if (root.vaultUnlocked) {
|
||||
Quickshell.execDetached(["rbw", "lock"]);
|
||||
} else {
|
||||
Quickshell.execDetached(["rbw", "unlock"]);
|
||||
}
|
||||
}
|
||||
|
||||
pillClickAction: () => root.toggleLock()
|
||||
|
||||
Component.onCompleted: refreshLockState()
|
||||
|
||||
Timer {
|
||||
interval: 2500
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: root.refreshLockState()
|
||||
}
|
||||
|
||||
Process {
|
||||
id: statusProcess
|
||||
command: ["rbw", "unlocked"]
|
||||
onExited: (exitCode, _exitStatus) => {
|
||||
root.vaultUnlocked = exitCode === 0;
|
||||
}
|
||||
}
|
||||
|
||||
horizontalBarPill: Component {
|
||||
MouseArea {
|
||||
implicitWidth: iconH.implicitWidth
|
||||
implicitHeight: iconH.implicitHeight
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.toggleLock()
|
||||
|
||||
DankIcon {
|
||||
id: iconH
|
||||
name: root.vaultUnlocked ? "lock_open_right" : "lock"
|
||||
size: root.iconSize
|
||||
color: parent.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verticalBarPill: Component {
|
||||
MouseArea {
|
||||
implicitWidth: iconV.implicitWidth
|
||||
implicitHeight: iconV.implicitHeight
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.toggleLock()
|
||||
|
||||
DankIcon {
|
||||
id: iconV
|
||||
name: root.vaultUnlocked ? "lock_open_right" : "lock"
|
||||
size: root.iconSize
|
||||
color: parent.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modules.Plugins
|
||||
|
||||
PluginSettings {
|
||||
id: root
|
||||
pluginId: "rbwLockToggle"
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: "Shows rbw vault state with a closed lock (locked) or open lock (unlocked). Click to run `rbw unlock` (pinentry) or `rbw lock`. Requires `rbw` on PATH in the DMS session and a working pinentry."
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "rbwLockToggle",
|
||||
"name": "Bitwarden (rbw) lock",
|
||||
"description": "Bar control for rbw vault: locked/unlocked padlock; click to unlock or lock",
|
||||
"version": "1.0.0",
|
||||
"author": "NixOS-V2",
|
||||
"type": "widget",
|
||||
"capabilities": ["dankbar-widget"],
|
||||
"component": "./RbwLockToggle.qml",
|
||||
"settings": "./RbwLockToggleSettings.qml",
|
||||
"icon": "lock_open",
|
||||
"permissions": ["settings_read"]
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Plugins
|
||||
|
||||
PluginComponent {
|
||||
id: root
|
||||
|
||||
function toggleKeyboard() {
|
||||
Quickshell.execDetached(["sh", "-c", "pkill -SIGRTMIN -x wvkbd-mobintl"]);
|
||||
}
|
||||
|
||||
pillClickAction: () => root.toggleKeyboard()
|
||||
|
||||
horizontalBarPill: Component {
|
||||
MouseArea {
|
||||
implicitWidth: icon.implicitWidth
|
||||
implicitHeight: icon.implicitHeight
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.toggleKeyboard()
|
||||
|
||||
DankIcon {
|
||||
id: icon
|
||||
name: "keyboard"
|
||||
size: Theme.iconSize
|
||||
color: parent.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verticalBarPill: Component {
|
||||
MouseArea {
|
||||
implicitWidth: iconV.implicitWidth
|
||||
implicitHeight: iconV.implicitHeight
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.toggleKeyboard()
|
||||
|
||||
DankIcon {
|
||||
id: iconV
|
||||
name: "keyboard"
|
||||
size: Theme.iconSize
|
||||
color: parent.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modules.Plugins
|
||||
|
||||
PluginSettings {
|
||||
id: root
|
||||
pluginId: "wvkbdToggle"
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: "Click the keyboard icon in the bar to show or hide the on-screen keyboard (wvkbd)."
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "wvkbdToggle",
|
||||
"name": "Virtual Keyboard Toggle",
|
||||
"description": "Bar button to show/hide the wvkbd on-screen keyboard (for touch/tablet)",
|
||||
"version": "1.0.0",
|
||||
"author": "NixOS-V2",
|
||||
"type": "widget",
|
||||
"capabilities": ["dankbar-widget"],
|
||||
"component": "./WvkbdToggle.qml",
|
||||
"settings": "./WvkbdToggleSettings.qml",
|
||||
"icon": "keyboard",
|
||||
"permissions": ["settings_read"]
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Vesktop / Vencord theme — matugen + DMS wallpaper colors.
|
||||
* Derived from pywal-vencord-style mapping (NixOS-New templates/colors-discord.css).
|
||||
*/
|
||||
|
||||
* {
|
||||
/* Surfaces */
|
||||
--background-primary: {{colors.background.default.hex}};
|
||||
--background-secondary: {{colors.surface.default.hex}};
|
||||
--background-tertiary: {{colors.surface_dim.default.hex}};
|
||||
|
||||
--background-primary-alt: {{colors.background.default.hex}};
|
||||
--background-secondary-alt: {{colors.surface.default.hex}};
|
||||
--background-tertiary-alt: {{colors.surface_dim.default.hex}};
|
||||
|
||||
--channeltextarea-background: {{colors.surface.default.hex}};
|
||||
--custom-channel-members-bg: {{colors.surface.default.hex}};
|
||||
|
||||
--profile-gradient-primary-color: {{colors.surface.default.hex}};
|
||||
--profile-gradient-secondary-color: {{colors.surface_variant.default.hex}};
|
||||
|
||||
--__header-bar-background: {{colors.surface.default.hex}} !important;
|
||||
|
||||
--bg-base-tertiary: {{colors.background.default.hex}};
|
||||
|
||||
--card-primary-bg: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
--input-background: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
--autocomplete-bg: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
--background-nested-floating: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
--background-floating: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
--scrollbar-auto-track: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
--scrollbar-thin-track: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
|
||||
--border-subtle: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
--background-base-lowest: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
--background-surface-high: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||
|
||||
--button-secondary-background: color-mix(in srgb, {{colors.surface.default.hex}}, black 30%);
|
||||
--background-surface-higher: color-mix(in srgb, {{colors.surface.default.hex}}, black 30%);
|
||||
--background-base-lower: color-mix(in srgb, {{colors.surface.default.hex}}, black 35%);
|
||||
|
||||
--background-message-hover: color-mix(in srgb, {{colors.surface.default.hex}}, black 40%);
|
||||
--button-secondary-background-hover: color-mix(in srgb, {{colors.surface.default.hex}}, black 40%);
|
||||
--background-base-low: color-mix(in srgb, {{colors.surface.default.hex}}, black 40%);
|
||||
--background-surface-highest: color-mix(in srgb, {{colors.surface.default.hex}}, black 40%);
|
||||
--chat-background-default: color-mix(in srgb, {{colors.surface.default.hex}}, black 45%);
|
||||
|
||||
--button-secondary-background-active: color-mix(in srgb, {{colors.surface.default.hex}}, black 60%);
|
||||
|
||||
--primary-630: {{colors.surface_variant.default.hex}};
|
||||
|
||||
/* Muted / secondary chrome */
|
||||
--scrollbar-auto-thumb: {{colors.on_surface_variant.default.hex}};
|
||||
--scrollbar-thin-thumb: {{colors.on_surface_variant.default.hex}};
|
||||
--interactive-muted: {{colors.on_surface_variant.default.hex}};
|
||||
--text-muted: {{colors.on_surface_variant.default.hex}};
|
||||
--background-modifier-hover: color-mix(in srgb, {{colors.on_surface_variant.default.hex}}, black 40%);
|
||||
--background-modifier-active: color-mix(in srgb, {{colors.on_surface_variant.default.hex}}, black 20%);
|
||||
--background-modifier-accent: {{colors.secondary_container.default.hex}};
|
||||
--background-accent: {{colors.secondary.default.hex}};
|
||||
|
||||
--input-border: {{colors.outline.default.hex}};
|
||||
--border-normal: {{colors.outline.default.hex}};
|
||||
--icon-secondary: {{colors.on_surface_variant.default.hex}};
|
||||
--icon-tertiary: {{colors.on_surface_variant.default.hex}};
|
||||
--channel-icon: {{colors.on_surface_variant.default.hex}};
|
||||
--channels-default: {{colors.on_surface_variant.default.hex}};
|
||||
--header-primary: {{colors.on_surface.default.hex}};
|
||||
--__lottieIconColor: {{colors.on_surface_variant.default.hex}};
|
||||
--interactive-normal: {{colors.on_surface_variant.default.hex}};
|
||||
|
||||
/* Selection / highlights */
|
||||
--red-400: {{colors.error.default.hex}};
|
||||
--background-modifier-selected: {{colors.secondary_container.default.hex}};
|
||||
|
||||
--notice-background-positive: color-mix(in srgb, {{colors.tertiary.default.hex}}, black 75%);
|
||||
--notice-text-positive: {{colors.tertiary.default.hex}};
|
||||
|
||||
/* Danger */
|
||||
--status-danger: {{colors.error.default.hex}};
|
||||
--button-outline-danger-border: {{colors.error.default.hex}};
|
||||
--button-outline-danger-text: {{colors.error.default.hex}};
|
||||
--button-danger-background: {{colors.error.default.hex}};
|
||||
|
||||
--yellow-300: {{colors.error.default.hex}};
|
||||
|
||||
/* Brand / accents */
|
||||
--brand-experiment: {{colors.primary.default.hex}};
|
||||
--brand-experiment-360: {{colors.primary.default.hex}};
|
||||
--brand-experiment-500: {{colors.primary.default.hex}};
|
||||
--profile-gradient-button-color: {{colors.primary.default.hex}};
|
||||
|
||||
--green-360: {{colors.primary.default.hex}};
|
||||
|
||||
/* Foreground */
|
||||
--text-normal: {{colors.on_surface.default.hex}};
|
||||
--interactive-active: {{colors.on_surface.default.hex}};
|
||||
}
|
||||
|
||||
/* Status indicators (legacy Discord hex fills) */
|
||||
rect[fill="#d83a41"] {
|
||||
fill: {{colors.error.default.hex}} !important;
|
||||
}
|
||||
|
||||
rect[fill="#cc954c"] {
|
||||
fill: {{colors.secondary.default.hex}} !important;
|
||||
}
|
||||
|
||||
rect[fill="#40a258"] {
|
||||
fill: {{colors.primary.default.hex}} !important;
|
||||
}
|
||||
|
||||
.wrapper_ef3116 {
|
||||
background: {{colors.background.default.hex}} !important;
|
||||
}
|
||||
|
||||
.bar_c38106 {
|
||||
background: {{colors.background.default.hex}} !important;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{ inputs, ... }: {
|
||||
flake.nixosModules.desktopWallpapers =
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.chiasson.desktop.wallpapers;
|
||||
d = config.chiasson.desktop;
|
||||
guiEnabled = d.hyprland.enable || d.niri.enable || d.plasma.enable;
|
||||
|
||||
wallpaperDir = pkgs.stdenvNoCC.mkDerivation {
|
||||
pname = "nixos-v2-wallpapers";
|
||||
version = "0.1";
|
||||
src = builtins.path {
|
||||
path = cfg.source;
|
||||
name = "wallpapers-src";
|
||||
};
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
installPhase = ''
|
||||
mkdir -p "$out/share/wallpapers"
|
||||
find "$src" -mindepth 1 -maxdepth 1 ! -name .git -exec cp -a {} "$out/share/wallpapers/" \;
|
||||
'';
|
||||
};
|
||||
|
||||
installPath = "${wallpaperDir}/share/wallpapers";
|
||||
in
|
||||
{
|
||||
options.chiasson.desktop.wallpapers = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Copy `source` into the store; sets `NIXOS_V2_WALLPAPERS` and `/etc/wallpapers`.
|
||||
'' // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
source = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = inputs.wallpapers;
|
||||
description = ''
|
||||
Directory copied into the store. Default: `inputs.wallpapers`
|
||||
(`git+https://git.chiasson.cloud/Olivier/wallpapers`, pinned in `flake.lock`).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (guiEnabled && cfg.enable) {
|
||||
environment = {
|
||||
variables.NIXOS_V2_WALLPAPERS = installPath;
|
||||
sessionVariables.NIXOS_V2_WALLPAPERS = installPath;
|
||||
etc."wallpapers".source = installPath;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
{ ... }: {
|
||||
flake.nixosModules.desktopWaydroid =
|
||||
{ config, lib, pkgs, ... }:
|
||||
# `virtualisation.waydroid.package` defaults to `waydroid-nftables` when `networking.nftables.enable`
|
||||
# is true — required on recent kernels where legacy `ip_tables` / `waydroid-net.sh` (iptables) fails
|
||||
# with `waydroid session start` (nixpkgs#459520).
|
||||
let
|
||||
cfg = config.chiasson.desktop.waydroid;
|
||||
in
|
||||
{
|
||||
options.chiasson.desktop.waydroid = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Waydroid + synced base props / nav mode. Needs Wayland; desktop hosts only. This module also
|
||||
sets `networking.nftables.enable` (default) so NixOS uses `waydroid-nftables` — needed on newer
|
||||
kernels where `waydroid-net.sh` / iptables fails. For **Google Play** apps (e.g. Hot Wheels
|
||||
Showcase), run `sudo waydroid init -s GAPPS -f` once after the first `nixos-rebuild` (or reset
|
||||
the container if you already initialized without GAPPS); then sign in and install from the Play
|
||||
Store. If `dnsmasq` reports port 53 in use, free that port (Waydroid needs it).
|
||||
'';
|
||||
|
||||
width = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 1920;
|
||||
description = "Waydroid rendering width (`persist.waydroid.width`).";
|
||||
};
|
||||
|
||||
height = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 1080;
|
||||
description = "Waydroid rendering height (`persist.waydroid.height`).";
|
||||
};
|
||||
|
||||
multiWindows = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Enable Waydroid multi-window integration.";
|
||||
};
|
||||
|
||||
navigationMode = lib.mkOption {
|
||||
type = lib.types.enum [ "3button" "gestures" ];
|
||||
default = "gestures";
|
||||
description = "Maps to Waydroid `navigation_mode` secure prop (3button=0, gestures=2).";
|
||||
};
|
||||
|
||||
extraBaseProperties = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = { };
|
||||
description = ''
|
||||
Extra `key = value` pairs written into `/var/lib/waydroid/waydroid_base.prop` on activation
|
||||
(same mechanism as width/height). Use for upstream tweaks — e.g. NVIDIA hosts often need
|
||||
`ro.hardware.gralloc=default` and `ro.hardware.egl=swiftshader`; Linux 5.18+ may need
|
||||
`sys.use_memfd=true` ([NixOS Waydroid wiki](https://wiki.nixos.org/wiki/Waydroid)).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
networking.nftables.enable = lib.mkDefault true;
|
||||
|
||||
virtualisation.waydroid.enable = true;
|
||||
|
||||
system.activationScripts.waydroidProps = {
|
||||
text = ''
|
||||
PROP_FILE="/var/lib/waydroid/waydroid_base.prop"
|
||||
|
||||
if [ ! -f "$PROP_FILE" ]; then
|
||||
echo "waydroid: $PROP_FILE not found yet, skipping prop sync."
|
||||
echo "waydroid: run 'sudo waydroid init' once, then rebuild."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
set_prop() {
|
||||
key="$1"
|
||||
value="$2"
|
||||
|
||||
if ${lib.getExe pkgs.gnugrep} -q "^''${key}=" "$PROP_FILE"; then
|
||||
${pkgs.gnused}/bin/sed -i "s|^''${key}=.*|''${key}=''${value}|" "$PROP_FILE"
|
||||
else
|
||||
printf "%s=%s\n" "$key" "$value" >> "$PROP_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
set_prop "persist.waydroid.multi_windows" "${if cfg.multiWindows then "true" else "false"}"
|
||||
set_prop "persist.waydroid.width" "${toString cfg.width}"
|
||||
set_prop "persist.waydroid.height" "${toString cfg.height}"
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (k: v: ''
|
||||
set_prop ${lib.escapeShellArg k} ${lib.escapeShellArg v}'') cfg.extraBaseProperties
|
||||
)}
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.services.waydroid-navigation-mode = {
|
||||
description = "Set Waydroid Android navigation mode";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "waydroid-container.service" ];
|
||||
wants = [ "waydroid-container.service" ];
|
||||
path = [ config.virtualisation.waydroid.package ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
|
||||
script = ''
|
||||
set -eu
|
||||
|
||||
nav_mode="${if cfg.navigationMode == "gestures" then "2" else "0"}"
|
||||
|
||||
if ! systemctl -q is-active waydroid-container.service; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for _ in $(seq 1 30); do
|
||||
if waydroid shell settings put secure navigation_mode "$nav_mode" >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "waydroid: warning: could not set navigation_mode=$nav_mode (container not ready?)" >&2
|
||||
exit 0
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
# Monitor layout for 14900k (ported from NixOS-New `hosts/clients/14900k/home.nix`).
|
||||
# Niri: `chiasson.desktop.niri.extraSettings` (`extraConfig` KDL + `binds` merged with defaults).
|
||||
# Hyprland: `chiasson.desktop.hyprland.settings` (merged in HM when `chiasson.desktop.hyprland.enable`).
|
||||
|
||||
#TODO[epic=Moderate] Clean this up, move to host's configuration.nix.
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
chiasson.desktop.niri.extraSettings = {
|
||||
extraConfig = ''
|
||||
output "DP-2" {
|
||||
mode "2560x1080@144"
|
||||
scale 1.0
|
||||
position x=0 y=0
|
||||
focus-at-startup
|
||||
}
|
||||
output "HDMI-A-3" {
|
||||
mode "1920x1080@60"
|
||||
scale 1.0
|
||||
position x=-1920 y=0
|
||||
}
|
||||
output "DP-4" {
|
||||
mode "1920x1080@144"
|
||||
scale 1.0
|
||||
position x=0 y=-1080
|
||||
}
|
||||
|
||||
'';
|
||||
|
||||
binds."XF86Tools".spawn = [
|
||||
"wpctl"
|
||||
"set-mute"
|
||||
"@DEFAULT_AUDIO_SOURCE@"
|
||||
"toggle"
|
||||
];
|
||||
};
|
||||
|
||||
chiasson.desktop.hyprland.settings = lib.mkIf config.chiasson.desktop.hyprland.enable (
|
||||
let
|
||||
monitorList = [
|
||||
"DP-2, 2560x1080@144, 0x0, 1"
|
||||
"DP-4, 1920x1080@144, 0x-1080, 1"
|
||||
"HDMI-A-3, 1920x1080@60, -1920x0, 1"
|
||||
];
|
||||
workspaceList = [
|
||||
"1, monitor:DP-3, default:true"
|
||||
"2, monitor:DP-3"
|
||||
"3, monitor:DP-3"
|
||||
"4, monitor:Unknown-2, default:true"
|
||||
"5, monitor:Unknown-2"
|
||||
"6, monitor:Unknown-2"
|
||||
"7, monitor:DP-4"
|
||||
"8, monitor:DP-4"
|
||||
"9, monitor:DP-4"
|
||||
];
|
||||
in
|
||||
{
|
||||
monitor = lib.mkBefore monitorList;
|
||||
workspace = workspaceList;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
# NFS exports from nixdesk (14900k) to r5500 (192.168.2.100), formerly nix-server (192.168.2.238):
|
||||
# - /mnt/deep/jellyfin → nix-server /mnt/nixdesk-jellyfin (Jellyfin bulk libraries)
|
||||
#
|
||||
# Jellyfin root on nixdesk uses owner olivier + group nfsmedia (990); dirs here are 2775 so
|
||||
# local writes and NFS all_squash (anonuid=olivier, anongid=990) get rwx via owner or group.
|
||||
#
|
||||
# Legacy trees may still need a one-time `chgrp -R nfsmedia` / `chmod -R g+rwX` on deep folders.
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
olivierUid = config.users.users.olivier.uid or 1000;
|
||||
in
|
||||
{
|
||||
# Avoid UID/GID mismatches across machines: map all NFS writes from nix-server to a single
|
||||
# local system user/group on this server.
|
||||
users.groups.nfsmedia = { gid = 990; };
|
||||
users.users.nfsmedia = {
|
||||
isSystemUser = true;
|
||||
uid = 990;
|
||||
group = "nfsmedia";
|
||||
};
|
||||
|
||||
# olivier: owner for local use; nfsmedia: group used by NFS all_squash (990).
|
||||
systemd.tmpfiles.settings."14900k-nfs-export-paths" = {
|
||||
"/mnt/deep/jellyfin"."d" = { mode = "2775"; user = "olivier"; group = "nfsmedia"; };
|
||||
};
|
||||
|
||||
# After exports are up, ensure group nfsmedia can write throughout library roots (idempotent;
|
||||
# scoped to library folders only — not whole disks). Runs on each `nixos-rebuild switch`.
|
||||
system.activationScripts.nfs-export-group-write = {
|
||||
deps = [ "specialfs" ];
|
||||
text = ''
|
||||
for d in /mnt/deep/jellyfin
|
||||
do
|
||||
[ -d "$d" ] || continue
|
||||
${pkgs.acl}/bin/setfacl -R -m g:nfsmedia:rwx "$d" 2>/dev/null || true
|
||||
${pkgs.acl}/bin/setfacl -R -d -m g:nfsmedia:rwx "$d" 2>/dev/null || true
|
||||
done
|
||||
'';
|
||||
};
|
||||
|
||||
# Fixed ports so the firewall can allow NFS v3 helpers (see networking.firewall below).
|
||||
services.nfs.server = {
|
||||
enable = true;
|
||||
mountdPort = 4000;
|
||||
lockdPort = 4001;
|
||||
statdPort = 4002;
|
||||
# fsid= unique per export tree (avoids client ESTALE when multiple paths are exported).
|
||||
# Squash nix-server clients to olivier:nfsmedia so Jellyfin can write .nfo/posters into
|
||||
# existing olivier-owned library folders (990-only squash was "other" r-x on typical 755 trees).
|
||||
exports = ''
|
||||
/mnt/deep/jellyfin 192.168.2.100(rw,sync,no_subtree_check,crossmnt,root_squash,all_squash,anonuid=${toString olivierUid},anongid=990,fsid=1)
|
||||
/mnt/deep/jellyfin 192.168.2.238(rw,sync,no_subtree_check,crossmnt,root_squash,all_squash,anonuid=${toString olivierUid},anongid=990,fsid=2)
|
||||
'';
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
111 # portmapper
|
||||
2049
|
||||
4000
|
||||
4001
|
||||
4002
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
111
|
||||
2049
|
||||
4000
|
||||
4001
|
||||
4002
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# Extra local disks. Declared here, not in hardware.nix (hardware.nix is generated).
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
# Stable UID so NTFS `uid=` matches `users.users.olivier` (override if your account is not 1000).
|
||||
olivierUid = config.users.users.olivier.uid or 1000;
|
||||
in
|
||||
{
|
||||
users.users.olivier.uid = lib.mkDefault 1000;
|
||||
# LABEL="MediaLibrary" (btrfs on sda1 by UUID). No subvol=@ — this disk has no @ subvolume.
|
||||
fileSystems."/mnt/2nd" = {
|
||||
device = "/dev/disk/by-uuid/17d8a981-db3b-415e-a0f7-7dbc519e04ab";
|
||||
fsType = "btrfs";
|
||||
options = [
|
||||
"compress=zstd"
|
||||
"noatime"
|
||||
"nofail"
|
||||
"x-systemd.device-timeout=30"
|
||||
];
|
||||
};
|
||||
|
||||
#new deep storage unit
|
||||
fileSystems."/mnt/deep" = {
|
||||
device = "/dev/disk/by-uuid/64fb08fe-da5d-4405-afa3-1603a411e9e5";
|
||||
fsType = "btrfs";
|
||||
options = [
|
||||
"compress=zstd"
|
||||
"noatime"
|
||||
"nofail"
|
||||
"x-systemd.device-timeout=30"
|
||||
];
|
||||
};
|
||||
|
||||
# LABEL="Deep Storage Unit". Owner olivier, group nfsmedia (990) so:
|
||||
# - local logins write as user 1000 (owner rwx);
|
||||
# - NFS (all_squash → uid/gid 990) matches group 990 → rwx (see jellyfin-nfs-export).
|
||||
#fileSystems."/mnt/test" = {
|
||||
# device = "/dev/disk/by-uuid/BC12E55E12E51DE0";
|
||||
# fsType = "ntfs-3g";
|
||||
# options = [
|
||||
# "rw"
|
||||
# "force"
|
||||
# "uid=${toString olivierUid}"
|
||||
# "gid=990"
|
||||
# "umask=0002"
|
||||
# ];
|
||||
#};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Moonfin 2.0.0 Jellyfin client — upstream Flatpak bundle (not on Flathub yet).
|
||||
# https://github.com/Moonfin-Client/Moonfin-Core/releases/tag/2.0.0
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
chiasson.system.flatpak.bundles = [
|
||||
{
|
||||
appId = "org.moonfin.linux";
|
||||
bundle = pkgs.fetchurl {
|
||||
url = "https://github.com/Moonfin-Client/Moonfin-Core/releases/download/2.0.0/Moonfin_Linux_v2.0.0.flatpak";
|
||||
hash = "sha256-sLtrsqBaJ1wriTkIdLylqMc9ygNkHrNm4YS/816nIFQ=";
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# NVIDIA for host desktop.
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
boot.kernelParams = [ "snd_hda_core.gpu_bind=0" ];
|
||||
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
|
||||
|
||||
services.xserver.videoDrivers = [ "nvidia" ];
|
||||
|
||||
hardware.nvidia = {
|
||||
modesetting.enable = true;
|
||||
powerManagement.enable = false;
|
||||
powerManagement.finegrained = false;
|
||||
open = true;
|
||||
nvidiaSettings = true;
|
||||
package = config.boot.kernelPackages.nvidiaPackages.latest;
|
||||
};
|
||||
|
||||
hardware.nvidia-container-toolkit.enable = true;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Logitech Unifying / Bolt receivers; Keychron VIA/VIAL on hidraw.
|
||||
{ ... }:
|
||||
{
|
||||
hardware.logitech.wireless.enable = true;
|
||||
|
||||
services.udev.extraRules = ''
|
||||
# Keychron VIA/VIAL on hidraw
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="3434", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl"
|
||||
# PS5 DualSense & DualSense Edge controllers over USB hidraw
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0ce6|0df2", MODE="0660", TAG+="uaccess"
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{ ... }: {
|
||||
#TODO[epic=Moderate] Clean this up, move to host's configuration.nix.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
powerManagement.cpuFreqGovernor = "performance";
|
||||
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
hardware.enableAllFirmware = true;
|
||||
hardware.cpu.intel.updateMicrocode = true;
|
||||
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Epson ET-2760 on Wi‑Fi via IPP/mDNS.
|
||||
# Import from configuration.nix when you need printing (see commented import there).
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
services.printing = {
|
||||
enable = true;
|
||||
webInterface = true;
|
||||
drivers = with pkgs; [
|
||||
epson-escpr2
|
||||
epson-escpr
|
||||
];
|
||||
};
|
||||
|
||||
services.avahi = {
|
||||
enable = true;
|
||||
nssmdns4 = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 631 ];
|
||||
}
|
||||
@@ -15,7 +15,11 @@
|
||||
./_private/platform.nix
|
||||
./_private/nvidia.nix
|
||||
./_private/peripherals.nix
|
||||
# ./_private/printing-epson.nix
|
||||
./_private/displays.nix
|
||||
./_private/media-disk.nix
|
||||
./_private/jellyfin-nfs-export.nix
|
||||
./_private/moonfin-flatpak.nix
|
||||
];
|
||||
|
||||
sops = {
|
||||
@@ -36,7 +40,25 @@
|
||||
group = "users";
|
||||
mode = "0400";
|
||||
};
|
||||
services.cloudflare-warp.enable = true;
|
||||
|
||||
# Intel iGPU video acceleration (VA-API / QSV via oneVPL).
|
||||
# This fixes common NixOS issues like `vaInitialize failed` and missing QSV encoders in apps.
|
||||
hardware.graphics = {
|
||||
enable = true;
|
||||
enable32Bit = true; # Required by Wine/DXVK for 32-bit Vulkan userspace.
|
||||
extraPackages = with pkgs; [
|
||||
intel-media-driver # iHD (Gen8+)
|
||||
vpl-gpu-rt # oneVPL runtime (QSV)
|
||||
libvdpau-va-gl
|
||||
];
|
||||
};
|
||||
|
||||
environment.sessionVariables = {
|
||||
LIBVA_DRIVER_NAME = "iHD";
|
||||
# Force VA-API to use the Intel iGPU render node (otherwise libva may pick NVIDIA and iHD fails).
|
||||
LIBVA_DRM_DEVICE = "/dev/dri/renderD128";
|
||||
};
|
||||
|
||||
chiasson.system.caching.attic = {
|
||||
enable = true;
|
||||
@@ -64,33 +86,52 @@
|
||||
};
|
||||
};
|
||||
|
||||
chiasson.system.chromiumHevc.enable = true;
|
||||
|
||||
chiasson.system = {
|
||||
# libvirt/QEMU + VFIO; host uses Intel iGPU for Niri while NVIDIA is passed through (see
|
||||
# `_private/nvidia.nix`, `_private/displays.nix`). If your GPU is not RTX 2070-class IDs, set
|
||||
# `chiasson.system.vm.gpuPassthrough.vfioIds` from `lspci -nn` (GPU + HDA functions in the same group).
|
||||
vm = {
|
||||
enable = true;
|
||||
gpuPassthrough.enable = false;
|
||||
};
|
||||
ytDlpTelequebecPatch.enable = true;
|
||||
|
||||
remoteDesktop = {
|
||||
enable = false;
|
||||
moonlight.enable = false;
|
||||
sunshine.enable = false;
|
||||
};
|
||||
audio.enable = true;
|
||||
docker.enable = true;
|
||||
gaming.enable = true;
|
||||
gaming.launchers.enableBottles = false;
|
||||
gaming.gamescope.enable = true;
|
||||
gaming.steam.steamTinkerLaunch.enable = true;
|
||||
gaming.sunshine.enable = true;
|
||||
gaming.sunshine.cudaSupport = true;
|
||||
|
||||
monitorInput.enable = true;
|
||||
|
||||
flatpak.enable = true;
|
||||
flatpak.flathub.appIds = [ "com.usebottles.bottles" ];
|
||||
|
||||
palera1n.enable = true;
|
||||
uconsoleKernelBuilder.enable = true;
|
||||
|
||||
extraPackages = [ pkgs.sops pkgs.nodejs_22 ];
|
||||
extraPackages = with pkgs; [
|
||||
sops
|
||||
nodejs_22
|
||||
ffmpeg
|
||||
bento4
|
||||
yt-dlp
|
||||
|
||||
# Native install (avoid flatpak sandbox issues for QSV/VAAPI).
|
||||
handbrake
|
||||
|
||||
qbittorrent
|
||||
|
||||
# Diagnostics
|
||||
libva-utils # vainfo
|
||||
vlc
|
||||
element-desktop
|
||||
thunderbird
|
||||
|
||||
prismlauncher
|
||||
dualsensectl
|
||||
|
||||
devenv
|
||||
];
|
||||
|
||||
|
||||
networking = {
|
||||
hostName = "nixdesk";
|
||||
@@ -106,6 +147,9 @@
|
||||
self.homeManagerModules.wisdomTerminalsKitty
|
||||
self.homeManagerModules.wisdomBrowsersEdge
|
||||
self.homeManagerModules.wisdomBrowsersFlow
|
||||
self.homeManagerModules.wisdomBrowsersOrion
|
||||
self.homeManagerModules.wisdomBrowsersZen
|
||||
self.homeManagerModules.wisdomBrowsersChromiumHevc
|
||||
self.homeManagerModules.wisdomEditorsCursor
|
||||
self.homeManagerModules.wisdomEditorsObsidian
|
||||
self.homeManagerModules.wisdomShellYazi
|
||||
@@ -120,8 +164,10 @@
|
||||
{
|
||||
programs.git = {
|
||||
enable = true;
|
||||
userName = "OlivierChiasson";
|
||||
userEmail = "olivierchiasson@hotmail.fr";
|
||||
settings.user = {
|
||||
name = "OlivierChiasson";
|
||||
email = "olivierchiasson@hotmail.fr";
|
||||
};
|
||||
};
|
||||
|
||||
chiasson.home = {
|
||||
@@ -139,6 +185,13 @@
|
||||
|
||||
browsers.edge.enable = true;
|
||||
browsers.flow.enable = false;
|
||||
browsers.orion.enable = true;
|
||||
browsers.zen.enable = true;
|
||||
browsers.chromiumHevc = {
|
||||
enable = true;
|
||||
packages = [ "google-chrome" ];
|
||||
vaapi.gpu = "intel"; # Chromium + NVIDIA VA-API → frame pool errors in Jellyfin cuz chrome is proprietary rats nests, gecko engine might support NVIDIA VA-API
|
||||
};
|
||||
|
||||
editors.cursor.enable = true;
|
||||
editors.obsidian.enable = true;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{ self, inputs, ... }: {
|
||||
|
||||
flake.nixosConfigurations."14900k" = inputs.nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "14900k";
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
modules = [
|
||||
self.nixosModules."14900kConfiguration"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{ self, ... }: {
|
||||
#TODO[epic=Moderate] Move this somewhere else, would prefer not relying on this module
|
||||
flake.nixosModules."client-services" = { ... }: {
|
||||
imports = [
|
||||
self.nixosModules.systemBluetooth
|
||||
self.nixosModules.systemDeployBuilder
|
||||
];
|
||||
|
||||
chiasson.system.deploy.builder.enable = true;
|
||||
|
||||
# Lab-ish SSH defaults on clients — tighten for anything exposed.
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
KbdInteractiveAuthentication = false;
|
||||
PasswordAuthentication = true;
|
||||
PermitRootLogin = "yes"; # consider tightening later
|
||||
UseDns = false;
|
||||
};
|
||||
};
|
||||
|
||||
# Printing, polkit, udisks.
|
||||
services.printing.enable = true;
|
||||
security.polkit.enable = true;
|
||||
services.udisks2.enable = true;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
# Cameras on the Lenovo Duet 3 (`lenovo-wormdingler`) — TODO
|
||||
|
||||
The front and rear cameras do **not** work on the current Mobile NixOS image.
|
||||
This file is a starting point for picking the work back up later, so we don't
|
||||
have to re-diagnose from scratch.
|
||||
|
||||
## Current state (May 2026)
|
||||
|
||||
Empirical, taken on the running device:
|
||||
|
||||
```
|
||||
$ uname -a
|
||||
Linux ideapad 6.5.0 #1-mobile-nixos SMP Tue Jan 1 00:00:00 UTC 1980 aarch64 GNU/Linux
|
||||
|
||||
$ ls /dev/video*
|
||||
/dev/video0 ← Qualcomm Venus video decoder (h.264/h.265 dec)
|
||||
/dev/video1 ← Qualcomm Venus video encoder (h.264/h.265 enc)
|
||||
|
||||
$ ls /dev/media* # nothing — no media controller graph at all
|
||||
$ lsmod | grep -i camss # nothing
|
||||
$ wpctl status | grep -A1 Sources # nothing — PipeWire has no camera sources
|
||||
```
|
||||
|
||||
Relevant kernel config bits in `/proc/config.gz` on the running image:
|
||||
|
||||
```
|
||||
CONFIG_MEDIA_CAMERA_SUPPORT=y ← media framework is on
|
||||
# CONFIG_VIDEO_QCOM_CAMSS is not set ← Qualcomm Camera Subsystem driver is OFF
|
||||
# CONFIG_VIDEO_OV5675 is not set ← every relevant sensor driver is OFF
|
||||
# CONFIG_VIDEO_OV13858 is not set
|
||||
# CONFIG_VIDEO_HI556 is not set
|
||||
# (full grep of `VIDEO_OV*` / `VIDEO_HI*` is all `not set`)
|
||||
```
|
||||
|
||||
So the *media framework* is enabled, but the *ISP driver* (CAMSS) and every
|
||||
plausible sensor driver are off, and no out-of-tree modules are shipped. The
|
||||
two `/dev/video*` nodes are the Venus codecs, not cameras — that's why
|
||||
`snapshot` reports "no camera found."
|
||||
|
||||
## Why this is non-trivial
|
||||
|
||||
1. **Kernel rebuild required.** `mobile-nixos` builds its own kernel for this
|
||||
device (see `mobile.kernel.structuredConfig` in `hardware.nix`, where we
|
||||
already enable `CIFS` and `EXFAT_FS`). Adding camera support means adding:
|
||||
- `VIDEO_QCOM_CAMSS = module;`
|
||||
- The right sensor driver(s) — and we don't currently know which ones the
|
||||
Duet 3 actually uses. The `wormdingler` Chromium-OS DT references a pair
|
||||
of OV-series sensors but the exact part numbers can vary by SKU and
|
||||
production batch.
|
||||
- Their dependencies (`I2C`, `V4L2`, `MEDIA_CONTROLLER`, etc. — most are
|
||||
already pulled in by `MEDIA_CAMERA_SUPPORT`).
|
||||
|
||||
2. **Device-tree wiring.** Even with the drivers compiled in, the device tree
|
||||
has to describe the CCI bus, the sensor I²C addresses, the regulators, the
|
||||
reset/enable GPIOs, and the CSI port mapping. Mobile NixOS' DT for
|
||||
wormdingler may or may not include these nodes — needs verification by
|
||||
reading the upstream device file at
|
||||
`${inputs.mobile-nixos}/devices/lenovo-wormdingler/`.
|
||||
|
||||
3. **libcamera is the user-space side.** CAMSS is not a "single v4l2 device"
|
||||
driver — it exposes a media-controller graph that has to be configured by
|
||||
libcamera (per-sensor IPA, format negotiation, etc.). Apps then talk to
|
||||
libcamera via the `libcamerasrc` PipeWire module or directly. So the
|
||||
user-space stack is:
|
||||
- `pkgs.libcamera` (system-wide)
|
||||
- `pkgs.pipewire` already running, but needs the libcamera module enabled
|
||||
(`services.pipewire.libcamera = …` or equivalent — check current option
|
||||
name)
|
||||
- GUI: `snapshot`, `gnome-camera`, or anything that talks to PipeWire
|
||||
video sources.
|
||||
|
||||
## Investigation checklist
|
||||
|
||||
When picking this up again, do these in order:
|
||||
|
||||
1. **Identify the actual sensors.** The cleanest way:
|
||||
- Read the upstream Mobile NixOS device file for wormdingler
|
||||
(`devices/lenovo-wormdingler/default.nix` and any `.dts` overlays).
|
||||
- Cross-check with the Chromium OS overlay at
|
||||
<https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/refs/heads/main/overlay-trogdor/>
|
||||
and the upstream Linux DTS at
|
||||
`arch/arm64/boot/dts/qcom/sc7180-trogdor-wormdingler-*.dts`.
|
||||
- As a runtime cross-check, when CAMSS is eventually loaded, `dmesg | grep -i
|
||||
-E 'cci|sensor|isp'` will print the I²C probe attempts.
|
||||
|
||||
2. **Enable kernel options** in `modules/hosts/ideapad/hardware.nix` under
|
||||
`mobile.kernel.structuredConfig`:
|
||||
|
||||
```nix
|
||||
(helpers: with helpers; {
|
||||
VIDEO_QCOM_CAMSS = module;
|
||||
# plus whatever sensors step 1 identified, e.g.:
|
||||
# VIDEO_OV5675 = module;
|
||||
# VIDEO_OV13858 = module;
|
||||
})
|
||||
```
|
||||
|
||||
Cross-build on the 14900k via the existing flow (binfmt aarch64 + push back).
|
||||
Reboot, then check:
|
||||
|
||||
```
|
||||
ls /dev/media* # expect at least /dev/media0
|
||||
sudo dmesg | grep -i -E 'camss|sensor|isp|cci' # probe history
|
||||
sudo modprobe -v qcom-camss # if not auto-loaded
|
||||
```
|
||||
|
||||
3. **Install diagnostic tools** for this round of work (do **not** keep these
|
||||
in the long-term config unless cameras actually work):
|
||||
|
||||
```nix
|
||||
environment.systemPackages = with pkgs; [
|
||||
v4l-utils # provides v4l2-ctl, media-ctl
|
||||
libcamera # provides `cam`, `qcam`
|
||||
];
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```
|
||||
v4l2-ctl --list-devices
|
||||
media-ctl -p # dumps the full media-controller graph
|
||||
cam -l # libcamera's view of available cameras
|
||||
```
|
||||
|
||||
4. **Wire libcamera into PipeWire.** Once `cam -l` shows at least one camera,
|
||||
enable PipeWire's libcamera module (option name may have shifted; current
|
||||
nixpkgs typically has `services.pipewire.wireplumber.extraConfig` or
|
||||
similar). Then `wpctl status` should show new Sources under "Video", and
|
||||
`snapshot` will see them.
|
||||
|
||||
5. **Re-add a camera GUI** to `configuration.nix`. `snapshot` is the simplest
|
||||
touch-first option; `gnome-camera` and `cheese` are alternatives.
|
||||
|
||||
## Why nothing else is being touched right now
|
||||
|
||||
Steps 1–4 above are speculative — there's no guarantee the Duet 3 cameras have
|
||||
working mainline-Linux sensor drivers at all. The conservative move is to
|
||||
leave the config tablet-usable without them, document the dead end, and revisit
|
||||
when there's time for a real spike.
|
||||
@@ -0,0 +1,48 @@
|
||||
{ pkgs, ... }: {
|
||||
# ─────────────────────── Power & thermal ───────────────────────
|
||||
# Snapdragon 7c (sc7180) on a tablet form factor: aim for battery life. `schedutil` is the
|
||||
# right modern cpufreq governor on ARM (responsive + power-aware); use it instead of
|
||||
# `powersave` to avoid pinning the CPU at minimum frequency under interactive load.
|
||||
powerManagement.cpuFreqGovernor = "schedutil";
|
||||
powerManagement.enable = true;
|
||||
|
||||
# ─────────────────────── logind: lid & power button ───────────────────────
|
||||
# Tablet form factor: lid close = suspend (even on AC), short power-press = suspend, long
|
||||
# power-press = poweroff. Niri's own power-key handler must stay disabled — see the
|
||||
# `input.disable-power-key-handling` flag in `_private/touch-tablet.nix` — otherwise niri's
|
||||
# `block` inhibitor on `handle-power-key` pre-empts logind and turns the wake-from-suspend
|
||||
# press (which the EC re-delivers as KEY_POWER) into an immediate re-suspend loop
|
||||
# (https://github.com/niri-wm/niri/issues/2233).
|
||||
services.logind.settings.Login = {
|
||||
HandleLidSwitch = "suspend";
|
||||
HandleLidSwitchExternalPower = "suspend";
|
||||
HandleLidSwitchDocked = "ignore";
|
||||
HandlePowerKey = "suspend";
|
||||
HandlePowerKeyLongPress = "poweroff";
|
||||
};
|
||||
|
||||
# ─────────────────────── Idle / suspend tuning ───────────────────────
|
||||
# Allow suspend-to-RAM but disable hibernate (ARM swap-resume is unreliable, and we don't
|
||||
# have a swap device by default anyway).
|
||||
systemd.sleep.settings.Sleep = {
|
||||
AllowHibernation = "no";
|
||||
AllowHybridSleep = "no";
|
||||
AllowSuspendThenHibernate = "no";
|
||||
};
|
||||
|
||||
# upower picks the right battery percentages for low/critical out of the box; just make
|
||||
# sure the action on critical is hibernate-then-poweroff fallback (we disabled hibernate
|
||||
# so it'll go straight to poweroff). DMS reads upower for the bar widget.
|
||||
services.upower = {
|
||||
enable = true;
|
||||
criticalPowerAction = "PowerOff";
|
||||
percentageLow = 15;
|
||||
percentageCritical = 7;
|
||||
percentageAction = 3;
|
||||
};
|
||||
|
||||
# ─────────────────────── Bluetooth audio quality of life ───────────────────────
|
||||
# Duet has limited mic/speaker hw — keep wpa_supplicant power-save off so audio doesn't crackle
|
||||
# over Bluetooth when CPU is idle. (Wi-Fi + BT share the chip; aggressive power-save = stutter.)
|
||||
networking.networkmanager.wifi.powersave = false;
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
# Host-only: ideapad tablet ergonomics — touchscreen calibration, IIO sensors, virtual keyboard,
|
||||
# touch-controller resume fix, and per-session helper daemons (tablet-mode toggle + auto-rotation
|
||||
# via iio-sensor-proxy) for both Niri and Hyprland. Lives at the NixOS layer because the hardware
|
||||
# bits are system-wide; the per-compositor autostart hooks are gated on `chiasson.desktop.<wm>.enable`
|
||||
# so they stay dormant if you pick the other session at the greeter.
|
||||
#
|
||||
# Hyprland uses CW transforms via `hyprctl`; Niri uses CCW transforms via `niri msg output`.
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
# ─────────────────────── Hyprland helpers ───────────────────────
|
||||
ideapadTabletModeDaemon = pkgs.writeShellScriptBin "ideapad-tablet-mode-daemon" ''
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HYPRCTL="${pkgs.hyprland}/bin/hyprctl"
|
||||
JQ="${pkgs.jq}/bin/jq"
|
||||
PKILL="${pkgs.procps}/bin/pkill"
|
||||
PGREP="${pkgs.procps}/bin/pgrep"
|
||||
WVKBD="${pkgs.wvkbd}/bin/wvkbd-mobintl"
|
||||
|
||||
monitor_name="DSI-1"
|
||||
keyboard_scale="1.25"
|
||||
tablet_scale="1.6"
|
||||
state_file="''${XDG_RUNTIME_DIR:-/run/user/$(${pkgs.coreutils}/bin/id -u)}/ideapad-input-mode.state"
|
||||
|
||||
current_transform() {
|
||||
"$HYPRCTL" -j monitors 2>/dev/null | "$JQ" -r --arg mon "$monitor_name" '
|
||||
([.[] | select(.name == $mon)][0].transform // 1 | tostring)
|
||||
' 2>/dev/null || echo "1"
|
||||
}
|
||||
|
||||
has_attached_pogo_dock() {
|
||||
"$HYPRCTL" -j devices 2>/dev/null | "$JQ" -e '
|
||||
any((([.keyboards[]?.name] + [.mice[]?.name])[]?); (ascii_downcase | test("google-inc\\.-hammer")))
|
||||
' >/dev/null
|
||||
}
|
||||
|
||||
# wvkbd is no longer auto-spawned at session start — this daemon owns its lifecycle so we
|
||||
# only have a virtual keyboard surface in memory when the pogo cover is detached.
|
||||
start_wvkbd() {
|
||||
"$PGREP" -x wvkbd-mobintl >/dev/null 2>&1 && return 0
|
||||
"$WVKBD" --non-exclusive -H 520 -L 360 --fn 'DejaVu Sans 18' >/dev/null 2>&1 &
|
||||
}
|
||||
stop_wvkbd() {
|
||||
"$PKILL" -x wvkbd-mobintl >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
apply_mode() {
|
||||
mode="$1"
|
||||
transform="$(current_transform)"
|
||||
if [ "$mode" = "tablet" ]; then
|
||||
"$HYPRCTL" keyword monitor "$monitor_name,1200x2000@60.0,0x0,$tablet_scale, transform, $transform" >/dev/null 2>&1 || true
|
||||
start_wvkbd
|
||||
else
|
||||
"$HYPRCTL" keyword monitor "$monitor_name,1200x2000@60.0,0x0,$keyboard_scale, transform, $transform" >/dev/null 2>&1 || true
|
||||
stop_wvkbd
|
||||
fi
|
||||
"$HYPRCTL" keyword input:touchdevice:output "$monitor_name" >/dev/null 2>&1 || true
|
||||
"$HYPRCTL" keyword input:touchdevice:transform "$transform" >/dev/null 2>&1 || true
|
||||
printf "%s\n" "$mode" > "$state_file"
|
||||
}
|
||||
|
||||
# Always reapply on startup — the cached state file may lie (e.g., session restart while
|
||||
# pogo state changed) and wvkbd needs to be (re)spawned since nothing else launches it now.
|
||||
previous_mode=""
|
||||
|
||||
while true; do
|
||||
if has_attached_pogo_dock; then
|
||||
mode="keyboard"
|
||||
else
|
||||
mode="tablet"
|
||||
fi
|
||||
|
||||
if [ "$mode" != "$previous_mode" ]; then
|
||||
apply_mode "$mode"
|
||||
previous_mode="$mode"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
'';
|
||||
|
||||
ideapadAutoRotateDaemon = pkgs.writeShellScriptBin "ideapad-autorotate-daemon" ''
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HYPRCTL="${pkgs.hyprland}/bin/hyprctl"
|
||||
MONITOR_SENSOR="${pkgs.iio-sensor-proxy}/bin/monitor-sensor"
|
||||
|
||||
monitor_name="DSI-1"
|
||||
keyboard_scale="1.25"
|
||||
tablet_scale="1.6"
|
||||
state_file="''${XDG_RUNTIME_DIR:-/run/user/$(${pkgs.coreutils}/bin/id -u)}/ideapad-input-mode.state"
|
||||
|
||||
scale_for_mode() {
|
||||
mode="$(cat "$state_file" 2>/dev/null || echo keyboard)"
|
||||
if [ "$mode" = "tablet" ]; then
|
||||
printf "%s\n" "$tablet_scale"
|
||||
else
|
||||
printf "%s\n" "$keyboard_scale"
|
||||
fi
|
||||
}
|
||||
|
||||
transform_for_orientation() {
|
||||
orientation="$1"
|
||||
case "$orientation" in
|
||||
normal) printf "0\n" ;;
|
||||
bottom-up) printf "2\n" ;;
|
||||
left-up) printf "1\n" ;;
|
||||
right-up) printf "3\n" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
apply_orientation() {
|
||||
orientation="$1"
|
||||
transform="$(transform_for_orientation "$orientation")" || return 0
|
||||
scale="$(scale_for_mode)"
|
||||
"$HYPRCTL" keyword monitor "$monitor_name,1200x2000@60.0,0x0,$scale, transform, $transform" >/dev/null 2>&1 || true
|
||||
"$HYPRCTL" keyword input:touchdevice:output "$monitor_name" >/dev/null 2>&1 || true
|
||||
"$HYPRCTL" keyword input:touchdevice:transform "$transform" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
while true; do
|
||||
"$MONITOR_SENSOR" --accel 2>/dev/null | while IFS= read -r line; do
|
||||
case "$line" in
|
||||
*normal*) apply_orientation "normal" ;;
|
||||
*bottom-up*) apply_orientation "bottom-up" ;;
|
||||
*left-up*) apply_orientation "left-up" ;;
|
||||
*right-up*) apply_orientation "right-up" ;;
|
||||
esac
|
||||
done
|
||||
sleep 2
|
||||
done
|
||||
'';
|
||||
|
||||
# ─────────────────────── Niri helpers ───────────────────────
|
||||
# `niri msg output DSI-1 transform <T>` accepts: normal | 90 | 180 | 270.
|
||||
#
|
||||
# Empirical mapping for this exact device + ACCEL_MOUNT_MATRIX (lenovo-wormdingler family):
|
||||
# physical position iio orientation correct niri transform
|
||||
# ────────────────── ──────────────── ──────────────────────
|
||||
# keyboard down (LS) left-up normal
|
||||
# keyboard up (LS) right-up 180
|
||||
# keyboard right (P) bottom-up 90
|
||||
# keyboard left (P) normal 270
|
||||
# iio's "normal" is *not* the natural landscape pose here — the panel-mount matrix in the
|
||||
# mobile-nixos device file biases it toward portrait-with-keyboard-left. Don't trust the
|
||||
# textbook iio→Wayland convention; trust the table above.
|
||||
ideapadNiriTabletModeDaemon = pkgs.writeShellScriptBin "ideapad-niri-tablet-mode-daemon" ''
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
NIRI="${pkgs.niri}/bin/niri"
|
||||
JQ="${pkgs.jq}/bin/jq"
|
||||
PKILL="${pkgs.procps}/bin/pkill"
|
||||
PGREP="${pkgs.procps}/bin/pgrep"
|
||||
WVKBD="${pkgs.wvkbd}/bin/wvkbd-mobintl"
|
||||
|
||||
output_name="DSI-1"
|
||||
keyboard_scale="1.25"
|
||||
tablet_scale="1.6"
|
||||
state_file="''${XDG_RUNTIME_DIR:-/run/user/$(${pkgs.coreutils}/bin/id -u)}/ideapad-input-mode.state"
|
||||
|
||||
has_attached_pogo_dock() {
|
||||
# Niri doesn't expose input devices via IPC, so we look in `/dev/input/by-id` for the
|
||||
# USB-class enumeration of the Google Hammer / Whiskers pogo keyboard. The directory
|
||||
# contains symlinks; presence of either string means the cover is attached.
|
||||
# NOTE: `find` ships in findutils, not coreutils — using the wrong package here makes
|
||||
# the pipeline fail silently (stderr is dropped) and pogo always reads as detached.
|
||||
${pkgs.findutils}/bin/find /dev/input/by-id -maxdepth 1 -type l 2>/dev/null \
|
||||
| ${pkgs.gnugrep}/bin/grep -i -E 'google.*hammer|google.*whiskers' >/dev/null
|
||||
}
|
||||
|
||||
# wvkbd is owned by this daemon: spawn on pogo detach, kill on pogo attach. The DMS bar
|
||||
# `pkill -SIGRTMIN -x wvkbd-mobintl` toggle still works while wvkbd is running (i.e., in
|
||||
# tablet mode). When the pogo cover is on, the pill is a no-op — that's intentional, the
|
||||
# physical keyboard is the input.
|
||||
start_wvkbd() {
|
||||
"$PGREP" -x wvkbd-mobintl >/dev/null 2>&1 && return 0
|
||||
"$WVKBD" --non-exclusive -H 520 -L 360 --fn 'DejaVu Sans 18' >/dev/null 2>&1 &
|
||||
}
|
||||
stop_wvkbd() {
|
||||
"$PKILL" -x wvkbd-mobintl >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
apply_mode() {
|
||||
mode="$1"
|
||||
if [ "$mode" = "tablet" ]; then
|
||||
"$NIRI" msg output "$output_name" scale "$tablet_scale" >/dev/null 2>&1 || true
|
||||
start_wvkbd
|
||||
else
|
||||
"$NIRI" msg output "$output_name" scale "$keyboard_scale" >/dev/null 2>&1 || true
|
||||
stop_wvkbd
|
||||
fi
|
||||
printf "%s\n" "$mode" > "$state_file"
|
||||
}
|
||||
|
||||
# Always reapply on startup — see Hyprland daemon's identical comment.
|
||||
previous_mode=""
|
||||
|
||||
while true; do
|
||||
if has_attached_pogo_dock; then
|
||||
mode="keyboard"
|
||||
else
|
||||
mode="tablet"
|
||||
fi
|
||||
|
||||
if [ "$mode" != "$previous_mode" ]; then
|
||||
apply_mode "$mode"
|
||||
previous_mode="$mode"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
'';
|
||||
|
||||
ideapadNiriAutoRotateDaemon = pkgs.writeShellScriptBin "ideapad-niri-autorotate-daemon" ''
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
NIRI="${pkgs.niri}/bin/niri"
|
||||
MONITOR_SENSOR="${pkgs.iio-sensor-proxy}/bin/monitor-sensor"
|
||||
|
||||
output_name="DSI-1"
|
||||
|
||||
transform_for_orientation() {
|
||||
orientation="$1"
|
||||
case "$orientation" in
|
||||
normal) printf "270\n" ;;
|
||||
bottom-up) printf "90\n" ;;
|
||||
left-up) printf "normal\n" ;;
|
||||
right-up) printf "180\n" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
apply_orientation() {
|
||||
orientation="$1"
|
||||
transform="$(transform_for_orientation "$orientation")" || return 0
|
||||
"$NIRI" msg output "$output_name" transform "$transform" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
while true; do
|
||||
"$MONITOR_SENSOR" --accel 2>/dev/null | while IFS= read -r line; do
|
||||
case "$line" in
|
||||
*normal*) apply_orientation "normal" ;;
|
||||
*bottom-up*) apply_orientation "bottom-up" ;;
|
||||
*left-up*) apply_orientation "left-up" ;;
|
||||
*right-up*) apply_orientation "right-up" ;;
|
||||
esac
|
||||
done
|
||||
sleep 2
|
||||
done
|
||||
'';
|
||||
in
|
||||
{
|
||||
# ─────────────────────── Hardware ───────────────────────
|
||||
hardware.sensor.iio.enable = true;
|
||||
|
||||
# Touchscreen calibration — identity matrix is correct: hardware coordinates are already aligned
|
||||
# with the panel-native frame, and per-orientation rotation is handled by `niri msg output`,
|
||||
# not by re-tuning this matrix. Rotate the *output*, never this matrix.
|
||||
services.udev.extraRules = ''
|
||||
SUBSYSTEM=="input", ENV{ID_INPUT_TOUCHSCREEN}=="1", ENV{LIBINPUT_CALIBRATION_MATRIX}="1 0 0 0 1 0"
|
||||
'';
|
||||
|
||||
# ─────────────────────── Touch controller resume fix ───────────────────────
|
||||
# The hid-over-i2c touch controller at i2c bus 4-0001 wedges across S3 suspend: after resume it
|
||||
# re-enumerates with the correct capabilities but reports zero events on touch. Cycling the
|
||||
# `i2c_hid_of` driver (unbind + bind) un-wedges it. systemd-sleep runs every executable in
|
||||
# `/etc/systemd/system-sleep/` with `$1 = pre|post`; we only act on `post`. Driver name is
|
||||
# discovered at runtime so a future kernel rename to `i2c_hid` doesn't break this.
|
||||
environment.etc."systemd/system-sleep/ideapad-touch-rebind".source =
|
||||
pkgs.writeShellScript "ideapad-touch-rebind" ''
|
||||
set -eu
|
||||
[ "$1" = post ] || exit 0
|
||||
dev=4-0001
|
||||
dev_dir=/sys/bus/i2c/devices/$dev
|
||||
[ -L "$dev_dir/driver" ] || exit 0
|
||||
drv=$(${pkgs.coreutils}/bin/basename "$(${pkgs.coreutils}/bin/readlink "$dev_dir/driver")")
|
||||
echo "ideapad-touch-rebind: cycling $drv for $dev" >&2
|
||||
echo "$dev" > "/sys/bus/i2c/drivers/$drv/unbind" || true
|
||||
${pkgs.coreutils}/bin/sleep 0.3
|
||||
echo "$dev" > "/sys/bus/i2c/drivers/$drv/bind"
|
||||
'';
|
||||
|
||||
# ─────────────────────── User-facing tools ───────────────────────
|
||||
# System-wide so any user session (Niri or Hyprland) can launch wvkbd / hyprctl / niri-msg helpers.
|
||||
environment.systemPackages = [
|
||||
pkgs.wvkbd
|
||||
pkgs.iio-sensor-proxy
|
||||
ideapadTabletModeDaemon
|
||||
ideapadAutoRotateDaemon
|
||||
ideapadNiriTabletModeDaemon
|
||||
ideapadNiriAutoRotateDaemon
|
||||
];
|
||||
|
||||
# ─────────────────────── Niri session autostart ───────────────────────
|
||||
# Set on the NixOS layer; the `desktopNiri` HM module merges this into per-user `niri/config.kdl`.
|
||||
# Touch input is glued to DSI-1 so it follows whatever transform the autorotate daemon sets.
|
||||
# wvkbd is intentionally NOT spawned here — `ideapad-niri-tablet-mode-daemon` starts/kills it
|
||||
# in response to pogo-cover attach/detach so we don't keep a virtual-keyboard surface alive
|
||||
# while a physical keyboard is plugged in.
|
||||
chiasson.desktop.niri.extraSettings = lib.mkIf config.chiasson.desktop.niri.enable {
|
||||
input.touch.map-to-output = "DSI-1";
|
||||
|
||||
# Required for logind's `HandlePowerKey` in `_private/platform.nix` to take effect: otherwise
|
||||
# niri grabs a `block` inhibitor on `handle-power-key` and suspends via D-Bus, including on
|
||||
# the EC's wake-from-suspend KEY_POWER event → instant re-suspend loop.
|
||||
# https://github.com/niri-wm/niri/issues/2233
|
||||
input."disable-power-key-handling" = _: { };
|
||||
|
||||
# wrapper-modules schema: each entry is a `command argv` list of strings (or a single string).
|
||||
spawn-at-startup = [
|
||||
[ "ideapad-niri-autorotate-daemon" ]
|
||||
[ "ideapad-niri-tablet-mode-daemon" ]
|
||||
];
|
||||
};
|
||||
|
||||
# ─────────────────────── Hyprland session autostart ───────────────────────
|
||||
# Same lifecycle policy as Niri: the tablet-mode daemon owns wvkbd, no exec-once for it.
|
||||
chiasson.desktop.hyprland.settings = lib.mkIf config.chiasson.desktop.hyprland.enable {
|
||||
exec-once = lib.mkAfter [
|
||||
"${pkgs.procps}/bin/pkill -x ideapad-tablet-mode-daemon >/dev/null 2>&1 || true; ideapad-tablet-mode-daemon &"
|
||||
"${pkgs.procps}/bin/pkill -x ideapad-autorotate-daemon >/dev/null 2>&1 || true; ideapad-autorotate-daemon &"
|
||||
];
|
||||
bind = lib.mkAfter [
|
||||
"Super, K, exec, pkill -SIGRTMIN -x wvkbd-mobintl"
|
||||
];
|
||||
monitor = lib.mkAfter [
|
||||
"DSI-1,1200x2000@60.0,0x0,1.25, transform, 1"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
{ self, inputs, ... }: {
|
||||
|
||||
# Lenovo Chromebook Duet 3 (`lenovo-wormdingler`) on Mobile NixOS.
|
||||
# Full V2 stack: mobile-nixos device + Niri/Hyprland/DMS, DankGreeter, wvkbd, IIO sensors,
|
||||
# touchscreen calibration + resume-rebind, attic cache, sops, and the standard user catalog.
|
||||
# Host-only quirks live in `_private/touch-tablet.nix` and `_private/platform.nix`.
|
||||
flake.nixosModules.ideapadConfiguration =
|
||||
{
|
||||
self,
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
# Mobile NixOS device + family + depthcharge system-type.
|
||||
(import "${inputs.mobile-nixos}/lib/configuration.nix" {
|
||||
device = "lenovo-wormdingler";
|
||||
})
|
||||
|
||||
self.nixosModules.ideapadHardware
|
||||
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
|
||||
self.nixosModules.system
|
||||
self.nixosModules.desktop
|
||||
self.nixosModules.users
|
||||
|
||||
self.nixosModules."client-services"
|
||||
|
||||
# Host-only: IIO + touchscreen calibration + per-compositor tablet/autorotate helpers.
|
||||
./_private/touch-tablet.nix
|
||||
|
||||
# Host-only: cpufreq, lid/power-button policy, upower thresholds.
|
||||
./_private/platform.nix
|
||||
];
|
||||
|
||||
# ─────────────────────── Sops ───────────────────────
|
||||
# `host_ideapad` recipient in `.sops.yaml` derives from the new ed25519 host key (post-reflash).
|
||||
sops = {
|
||||
defaultSopsFile = ../../../secrets/secrets.yaml;
|
||||
defaultSopsFormat = "yaml";
|
||||
age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
||||
};
|
||||
|
||||
sops.secrets."users/olivier/hashedPassword".neededForUsers = true;
|
||||
sops.secrets."caching/attic/token" = {
|
||||
owner = "olivier";
|
||||
group = "users";
|
||||
mode = "0400";
|
||||
};
|
||||
sops.secrets."swiftshare/API_KEY" = {
|
||||
owner = "olivier";
|
||||
group = "users";
|
||||
mode = "0400";
|
||||
};
|
||||
|
||||
# ─────────────────────── Mobile NixOS / firmware ───────────────────────
|
||||
# mruby's test-suite breaks on aarch64 in the Nix sandbox; the overlay strips checks and
|
||||
# rebuilds Mobile NixOS' script-loader against the patched mruby.
|
||||
chiasson.system.ideapadMrubyOverlay.enable = true;
|
||||
|
||||
# Wi-Fi modem (qcom-wcn3990) + Bluetooth (QCA crnv32) need binary blobs.
|
||||
nixpkgs.config.allowUnfreePredicate =
|
||||
pkg: builtins.elem (lib.getName pkg) [
|
||||
"chromeos-sc7180-unredistributable-firmware"
|
||||
"chromeos-sc7180-unredistributable-firmware-zstd"
|
||||
];
|
||||
hardware.firmware = [ pkgs.chromeos-sc7180-unredistributable-firmware ];
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
|
||||
# ─────────────────────── Attic (substitution + push + CLI token) ───────
|
||||
chiasson.system.caching.attic = {
|
||||
enable = true;
|
||||
cacheName = "nixos-new";
|
||||
endpoint = "http://192.168.2.238:8080/";
|
||||
publicKey = "nixos-new:8NySIcT0HP7KvGQKgBRWoWESxxRA8BVYo8S85UNpNX0=";
|
||||
tokenFile = config.sops.secrets."caching/attic/token".path;
|
||||
push.enable = true;
|
||||
userCli.enable = true;
|
||||
};
|
||||
|
||||
# ─────────────────────── System bits ───────────────────────
|
||||
chiasson.system = {
|
||||
audio.enable = true;
|
||||
networking = {
|
||||
hostName = "ideapad";
|
||||
networkManager = {
|
||||
enable = true;
|
||||
unmanaged = [ ];
|
||||
};
|
||||
wifi.tools.enabled = true;
|
||||
};
|
||||
extraPackages = with pkgs; [
|
||||
gitMinimal
|
||||
sops
|
||||
ssh-to-age
|
||||
];
|
||||
};
|
||||
|
||||
# ─────────────────────── Desktop ───────────────────────
|
||||
# Both compositors are enabled — DankGreeter picks at login, V2 default is Niri.
|
||||
# Per-session tablet-mode / autorotate daemons live in `_private/touch-tablet.nix`.
|
||||
chiasson.desktop = {
|
||||
niri.enable = true;
|
||||
hyprland.enable = false;
|
||||
|
||||
defaultSession = "niri";
|
||||
shell = "dms";
|
||||
shells.dms = {
|
||||
enableWvkbdToggle = true;
|
||||
enableRbwLockToggle = true;
|
||||
# Cross-build on the 14900k via binfmt and push back over LAN — much faster than
|
||||
# rebuilding aarch64 closure on the Snapdragon. Mirrors the old NixOS-New flow:
|
||||
# ssh out to nixdesk, run nixos-rebuild --target-host pointing back at us.
|
||||
rebuildCommand = [
|
||||
"bash"
|
||||
"-lc"
|
||||
''
|
||||
ssh -t olivier@nixdesk \
|
||||
"nixos-rebuild switch --flake path:/home/olivier/NixOS-V2#ideapad --target-host olivier@ideapad --sudo --ask-sudo-password 2>&1"
|
||||
''
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
# ─────────────────────── Users / HM ───────────────────────
|
||||
chiasson.users.enabled = [ "olivier" ];
|
||||
|
||||
# Touch-friendly application set, mirroring uConsole's selection (no heavy IDEs / gaming).
|
||||
chiasson.users.extraModules.olivier = [
|
||||
self.homeManagerModules.wisdomFilebrowsersDolphin
|
||||
self.homeManagerModules.wisdomTerminalsKitty
|
||||
self.homeManagerModules.wisdomBrowsersZen
|
||||
self.homeManagerModules.wisdomEditorsKate
|
||||
self.homeManagerModules.wisdomEditorsCursor
|
||||
self.homeManagerModules.wisdomShellFish
|
||||
self.homeManagerModules.wisdomShellOhMyPosh
|
||||
self.homeManagerModules.wisdomAppsSpotify
|
||||
self.homeManagerModules.wisdomAppsLocalsend
|
||||
self.homeManagerModules.wisdomDesktopScreenshot
|
||||
{
|
||||
chiasson.home = {
|
||||
shell = {
|
||||
fish.enable = true;
|
||||
ohMyPosh.enable = true;
|
||||
};
|
||||
terminals.kitty.enable = true;
|
||||
filebrowsers.dolphin.enable = true;
|
||||
browsers.zen.enable = true;
|
||||
editors.kate.enable = true;
|
||||
editors.cursor.enable = true;
|
||||
apps.spotify.enable = true;
|
||||
apps.localsend.enable = true;
|
||||
desktop = {
|
||||
screenshot = {
|
||||
enable = true;
|
||||
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
# Tablet-class apps: kept inline rather than promoting to wisdom modules — these aren't
|
||||
# part of the broader catalog (no use on uConsole / 14900k / servers) and adding a wisdom
|
||||
# module per single-host package would just be ceremony. If a second tablet host ever
|
||||
# appears, factor them out then.
|
||||
#
|
||||
# NOTE on cameras: no v4l2/libcamera GUI is installed. The Mobile NixOS kernel for
|
||||
# `lenovo-wormdingler` ships with `CONFIG_VIDEO_QCOM_CAMSS` disabled and no
|
||||
# `VIDEO_OV*`/`VIDEO_HI*` sensor drivers, so `/dev/video0`-`/dev/video1` only expose
|
||||
# the Qualcomm Venus codecs (h.264/h.265 enc/dec) and there is no camera source for
|
||||
# PipeWire / libcamera to pick up. See `_private/CAMERA-TODO.md` for the steps that
|
||||
# would (potentially) bring the front/rear cameras online — it's a kernel-rebuild +
|
||||
# device-tree + libcamera project, not a config tweak.
|
||||
(
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
home.packages = with pkgs; [
|
||||
# PDF viewer — fits the existing KDE app set (Dolphin + Kate).
|
||||
kdePackages.okular
|
||||
# ePub reader, GTK4, large touch targets.
|
||||
foliate
|
||||
];
|
||||
}
|
||||
)
|
||||
];
|
||||
|
||||
system.stateVersion = "26.05";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{ self, inputs, ... }: {
|
||||
|
||||
flake.nixosConfigurations.ideapad = inputs.nixpkgs.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "ideapad";
|
||||
system = "aarch64-linux";
|
||||
};
|
||||
modules = [
|
||||
self.nixosModules.ideapadConfiguration
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{ ... }: {
|
||||
flake.nixosModules.ideapadHardware =
|
||||
# Mobile NixOS' depthcharge system-type wires up the disk image, kernel partitions and
|
||||
# rootfs entirely; we don't need a generated `hardware-configuration.nix`. This module is a
|
||||
# placeholder so the host follows the standard `<host>Configuration` / `<host>Hardware` shape
|
||||
# and gives us a place to drop kernel-config knobs that aren't covered by the device family.
|
||||
{ ... }:
|
||||
{
|
||||
# Useful on a portable: mounting USB sticks (often exFAT) and SMB shares.
|
||||
boot.supportedFilesystems = [ "exfat" "cifs" ];
|
||||
|
||||
# Mobile NixOS builds its own kernel — the regular `boot.kernelModules` won't help if the
|
||||
# module isn't compiled in. `mobile.kernel.structuredConfig` lives upstream in
|
||||
# `modules/system-types/depthcharge/kernel/` and is the right layer to add features.
|
||||
mobile.kernel.structuredConfig = [
|
||||
(helpers: with helpers; {
|
||||
CIFS = module;
|
||||
EXFAT_FS = module;
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# personal-website sops secrets
|
||||
|
||||
Add these keys to `secrets.yaml` (on a host with your age key):
|
||||
|
||||
```yaml
|
||||
personal-website:
|
||||
ghcr-token: <same PAT as swiftshare/ghcr-token, or new>
|
||||
database-password: <strong password>
|
||||
auth-secret: <openssl rand -base64 32>
|
||||
oauth-discord-client-secret: <Discord OAuth secret>
|
||||
```
|
||||
|
||||
```bash
|
||||
cd modules/hosts/nix-server
|
||||
sops secrets.yaml
|
||||
```
|
||||
|
||||
After editing, verify with `sops -d secrets.yaml | yq '.personal-website'`.
|
||||
@@ -0,0 +1,65 @@
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
sops = {
|
||||
templates."atticd.env" = {
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
content = ''
|
||||
ATTIC_SERVER_TOKEN_RS256_SECRET_BASE64=${config.sops.placeholder."attic/server-token-rs256-secret-base64"}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
sops.secrets."attic/server-token-rs256-secret-base64" = {
|
||||
sopsFile = ../../../../secrets/attic-secrets.yaml;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
};
|
||||
|
||||
# SQLite on disk was the main source of random multi-minute stalls (see attic#113).
|
||||
# NAR blobs stay in /var/lib/atticd/storage; only metadata moves to Postgres.
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [ "atticd" ];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "atticd";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
services.atticd = {
|
||||
enable = true;
|
||||
environmentFile = config.sops.templates."atticd.env".path;
|
||||
settings = {
|
||||
listen = "0.0.0.0:8080";
|
||||
jwt = { };
|
||||
# Use a libpq socket URI format accepted by Attic's parser.
|
||||
database.url = "postgresql:///atticd?host=/run/postgresql&user=atticd";
|
||||
chunking = {
|
||||
nar-size-threshold = 65536;
|
||||
min-size = 16384;
|
||||
avg-size = 65536;
|
||||
max-size = 262144;
|
||||
};
|
||||
storage = {
|
||||
type = "local";
|
||||
path = "/var/lib/atticd/storage";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.atticd = {
|
||||
serviceConfig = {
|
||||
Restart = lib.mkForce "always";
|
||||
RestartSec = lib.mkForce 5;
|
||||
# Large closures; default limits can wedge uploads under load.
|
||||
LimitNOFILE = 1048576;
|
||||
};
|
||||
};
|
||||
|
||||
chiasson.system.networking.firewall.allowedTCPPorts = [ 8080 ];
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# Cloudflare dynamic DNS via NixOS (kissgyorgy/cloudflare-dyndns).
|
||||
{ config, ... }:
|
||||
let
|
||||
secretFilePath = ../secrets.yaml;
|
||||
in
|
||||
{
|
||||
sops.secrets."cloudflare-ddns/api-token".sopsFile = secretFilePath;
|
||||
|
||||
services.cloudflare-dyndns = {
|
||||
enable = true;
|
||||
apiTokenFile = config.sops.secrets."cloudflare-ddns/api-token".path;
|
||||
domains = [
|
||||
"chiasson.cloud"
|
||||
"chiassoncloud.services"
|
||||
"swiftshare.cloud"
|
||||
"blackfry.day"
|
||||
"yestur.day"
|
||||
"rp-own.life"
|
||||
"xn--1iu.cc"
|
||||
];
|
||||
proxied = true;
|
||||
ipv4 = true;
|
||||
ipv6 = false;
|
||||
# Default: *:0/5 (every 5 minutes).
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# DDRM Flask backend (Widevine / PlayReady decrypt). Extension URL: http://<host>:58239
|
||||
{ pkgs, inputs, ... }:
|
||||
{
|
||||
services.ddrm-media-server = {
|
||||
enable = true;
|
||||
port = 58239;
|
||||
listenAddress = "0.0.0.0";
|
||||
openFirewall = true;
|
||||
package = inputs.ddrm.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||
# State: /var/lib/ddrm-media (venv + configs + CDMs on first run — needs network for pip).
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Dispatcharr — IPTV / M3U / EPG / HDHomeRun-style proxy (Docker, AIO image).
|
||||
# Docs: https://dispatcharr.github.io/Dispatcharr-Docs/
|
||||
# Compose reference: https://github.com/Dispatcharr/Dispatcharr/blob/main/docker/docker-compose.aio.yml
|
||||
#
|
||||
# Web UI: http://<host>:9191
|
||||
# After deploy: create an admin user, add playlists / EPG; point Jellyfin at the tuner/M3U URLs
|
||||
# Dispatcharr shows in its UI.
|
||||
{ lib, pkgs, ... }:
|
||||
{
|
||||
systemd.tmpfiles.settings."nix-server-dispatcharr-data" = {
|
||||
"/var/lib/dispatcharr"."d" = {
|
||||
# Dispatcharr runs as a non-root user inside the container; keep /data writable.
|
||||
# (Plugins like EPG Janitor write to `/data/*.json`.)
|
||||
mode = "0777";
|
||||
user = "root";
|
||||
group = "root";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.docker-dispatcharr.preStart = lib.mkBefore ''
|
||||
${pkgs.coreutils}/bin/mkdir -p /var/lib/dispatcharr
|
||||
'';
|
||||
|
||||
virtualisation.oci-containers.containers.dispatcharr = {
|
||||
image = "ghcr.io/dispatcharr/dispatcharr:latest";
|
||||
ports = [ "9191:9191" ];
|
||||
volumes = [
|
||||
"/var/lib/dispatcharr:/data"
|
||||
];
|
||||
environment = {
|
||||
DISPATCHARR_ENV = "aio";
|
||||
REDIS_HOST = "localhost";
|
||||
CELERY_BROKER_URL = "redis://localhost:6379/0";
|
||||
DISPATCHARR_LOG_LEVEL = "info";
|
||||
TZ = "America/Moncton";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 9191 ];
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{ ... }:
|
||||
{
|
||||
# FlareSolverr (Cloudflare / JS challenge solver for some indexers).
|
||||
# Typically used by Prowlarr as an HTTP proxy.
|
||||
#
|
||||
# UI/endpoint: http://<host>:8191
|
||||
services.flaresolverr.enable = true;
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8191 ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
services.gitea = {
|
||||
enable = true;
|
||||
|
||||
# Migrated sqlite DB and repos; do not provision a fresh database.
|
||||
database = {
|
||||
type = "sqlite3";
|
||||
createDatabase = false;
|
||||
};
|
||||
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = "git.chiasson.cloud";
|
||||
HTTP_PORT = 3002;
|
||||
ROOT_URL = "https://git.chiasson.cloud/";
|
||||
# Clone URLs and LAN git@… -p 222 (was Docker host 222 → container 22).
|
||||
# Port 222 is <1024 (privileged); systemd must grant CAP_NET_BIND_SERVICE below.
|
||||
SSH_PORT = 222;
|
||||
START_SSH_SERVER = true;
|
||||
SSH_LISTEN_HOST = "0.0.0.0";
|
||||
SSH_LISTEN_PORT = 222;
|
||||
};
|
||||
|
||||
service.DISABLE_REGISTRATION = false;
|
||||
};
|
||||
};
|
||||
|
||||
# First boot after migration runs DB migrate + hook regen; default WatchdogSec=30 kills
|
||||
# gitea while storage/actions init is still running. Type=notify also fails if startup
|
||||
# is slow; PrivateUsers breaks access to migrated files owned by the real gitea uid.
|
||||
# Port 222 is privileged (<1024); Docker mapped host 222→container 22 as root.
|
||||
systemd.services.gitea.serviceConfig = {
|
||||
Type = lib.mkForce "simple";
|
||||
PrivateUsers = lib.mkForce false;
|
||||
NoNewPrivileges = lib.mkForce false;
|
||||
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
|
||||
CapabilityBoundingSet = lib.mkForce [ "CAP_NET_BIND_SERVICE" ];
|
||||
TimeoutStartSec = lib.mkForce "20min";
|
||||
WatchdogSec = lib.mkForce 0;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
3002
|
||||
222
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
secretFilePath = ../secrets.yaml;
|
||||
in
|
||||
{
|
||||
sops.secrets."immich/database-password".sopsFile = secretFilePath;
|
||||
|
||||
# Placeholders are expanded only inside template `content` (not in arbitrary Nix strings).
|
||||
sops.templates."immich-db.env" = {
|
||||
content = ''
|
||||
POSTGRES_PASSWORD=${config.sops.placeholder."immich/database-password"}
|
||||
DB_PASSWORD=${config.sops.placeholder."immich/database-password"}
|
||||
'';
|
||||
};
|
||||
|
||||
chiasson.system.services.immich = {
|
||||
enable = true;
|
||||
host = "0.0.0.0";
|
||||
port = 2283;
|
||||
timezone = "America/Moncton";
|
||||
uploadLocation = "/var/lib/immich/library";
|
||||
environmentFiles = [ config.sops.templates."immich-db.env".path ];
|
||||
postgres = {
|
||||
user = "postgres";
|
||||
#password = ""; # Defined in sops.templates."immich-db.env"
|
||||
database = "immich";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
# Jellyfin (native NixOS service). Local media: /var/lib/media (group `media`; jellyfin + server).
|
||||
# Dashboard: Movies → /var/lib/media/movies, Shows → /var/lib/media/tv (see nixdesk-nfs-client.nix
|
||||
# for bulk libraries on nixdesk at /mnt/nixdesk-jellyfin/{movies,tv}).
|
||||
# Do not use "Mixed Movies and Shows" (deprecated): https://jellyfin.org/docs/general/server/media/mixed-movies-and-shows
|
||||
# Dedicated disk: fileSystems."/var/lib/media" in hardware.nix, then fix ownership.
|
||||
{ lib, ... }:
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
jellyfin-web = prev.jellyfin-web.overrideAttrs (oldAttrs: {
|
||||
postInstall =
|
||||
(oldAttrs.postInstall or "")
|
||||
+ ''
|
||||
# Blank default Jellyfin banner assets (read-only store otherwise). Wildcards
|
||||
# track hashed filenames across jellyfin-web releases; bump if layout changes.
|
||||
find "$out" -type f \( -name 'banner-light.*.png' -o -name 'banner-dark.*.png' \) \
|
||||
-exec truncate -s 0 {} \;
|
||||
'';
|
||||
});
|
||||
})
|
||||
];
|
||||
|
||||
users.groups.media = { };
|
||||
|
||||
users.users.jellyfin.extraGroups = [ "media" ];
|
||||
users.users.server.extraGroups = [ "media" ];
|
||||
|
||||
systemd.tmpfiles.settings."nix-server-var-lib-media" = {
|
||||
"/var/lib/media"."d" = {
|
||||
mode = "0775";
|
||||
user = "root";
|
||||
group = "media";
|
||||
};
|
||||
"/var/lib/media/movies"."d" = {
|
||||
mode = "0775";
|
||||
user = "root";
|
||||
group = "media";
|
||||
};
|
||||
"/var/lib/media/tv"."d" = {
|
||||
mode = "0775";
|
||||
user = "root";
|
||||
group = "media";
|
||||
};
|
||||
};
|
||||
|
||||
services.jellyfin = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
|
||||
# `users.users.jellyfin.extraGroups` does not affect systemd; the service must list
|
||||
# supplementary groups explicitly. Without `media`, directories mode 775 root:media are
|
||||
# not writable by uid jellyfin (it only had group `jellyfin`), so deletes fail.
|
||||
systemd.services.jellyfin.serviceConfig = {
|
||||
SupplementaryGroups = [ "media" ];
|
||||
# Jellyfin libraries on NFS (e.g. /mnt/nixdesk-jellyfin). PrivateUsers breaks
|
||||
# uid mapping for NFS auth in practice; disable so metadata writes use the real jellyfin uid
|
||||
# (squashed to olivier:nfsmedia on nixdesk exports).
|
||||
PrivateUsers = lib.mkForce false;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# NFS mounts of nixdesk (14900k) bulk storage for nix-server. Exports live in
|
||||
# modules/hosts/14900k/_private/jellyfin-nfs-export.nix
|
||||
#
|
||||
# Jellyfin library paths (see also services/jellyfin.nix):
|
||||
# Movies → /mnt/nixdesk-jellyfin/movies
|
||||
# Shows → /mnt/nixdesk-jellyfin/tv
|
||||
#
|
||||
# If you see "Stale file handle" under /mnt after changing exports or fsid on nixdesk, drop the
|
||||
# old client mount and let automount reattach, e.g.:
|
||||
# sudo umount -l /mnt/nixdesk-jellyfin
|
||||
# ls /mnt/nixdesk-jellyfin
|
||||
# (or reboot nix-server.)
|
||||
{ ... }:
|
||||
let
|
||||
nfsExportHost = "192.168.2.25";
|
||||
# nfsvers+tcp: predictable Linux↔Linux; lookupcache=none: fewer stale dentries after export changes.
|
||||
nfsClientOpts = [
|
||||
"rw"
|
||||
"noatime"
|
||||
"nofail"
|
||||
"_netdev"
|
||||
"nfsvers=3"
|
||||
"tcp"
|
||||
"lookupcache=none"
|
||||
"x-systemd.automount"
|
||||
"x-systemd.idle-timeout=3600"
|
||||
];
|
||||
in
|
||||
{
|
||||
fileSystems."/mnt/nixdesk-jellyfin" = {
|
||||
device = "${nfsExportHost}:/mnt/deep/jellyfin";
|
||||
fsType = "nfs";
|
||||
options = nfsClientOpts;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
# Organizr — homelab dashboard (Docker). UI: http://<host>:8888
|
||||
# Official image: https://github.com/organizr/docker-organizr
|
||||
#
|
||||
# Wizard errors like "API … /default/ not writable" are almost always host permissions on
|
||||
# `/var/lib/organizr`: the first container run may leave root-owned files under `/config`.
|
||||
{ lib, pkgs, ... }:
|
||||
{
|
||||
users.groups.organizr = { gid = 950; };
|
||||
users.users.organizr = {
|
||||
isSystemUser = true;
|
||||
uid = 950;
|
||||
group = "organizr";
|
||||
};
|
||||
|
||||
systemd.tmpfiles.settings."nix-server-organizr-config" = {
|
||||
"/var/lib/organizr"."d" = {
|
||||
mode = "0755";
|
||||
user = "organizr";
|
||||
group = "organizr";
|
||||
};
|
||||
};
|
||||
|
||||
# Recursively reset ownership (handles root-owned files from an earlier container run).
|
||||
systemd.tmpfiles.settings."nix-server-organizr-config-perms" = {
|
||||
"/var/lib/organizr"."Z" = {
|
||||
mode = "0755";
|
||||
user = "organizr";
|
||||
group = "organizr";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.docker-organizr.preStart = lib.mkBefore ''
|
||||
${pkgs.coreutils}/bin/mkdir -p /var/lib/organizr
|
||||
${pkgs.coreutils}/bin/chown -R organizr:organizr /var/lib/organizr
|
||||
'';
|
||||
|
||||
virtualisation.oci-containers.containers.organizr = {
|
||||
image = "ghcr.io/organizr/organizr:latest";
|
||||
ports = [ "8888:80" ];
|
||||
volumes = [
|
||||
"/var/lib/organizr:/config"
|
||||
];
|
||||
environment = {
|
||||
PUID = "950";
|
||||
PGID = "950";
|
||||
TZ = "America/Moncton";
|
||||
# v2-master / master are stable v2; optional override:
|
||||
# branch = "v2-master";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8888 ];
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
secretFilePath = ../secrets.yaml;
|
||||
in
|
||||
{
|
||||
sops.secrets."personal-website/database-password".sopsFile = secretFilePath;
|
||||
sops.secrets."personal-website/auth-secret".sopsFile = secretFilePath;
|
||||
sops.secrets."personal-website/oauth-discord-client-secret".sopsFile = secretFilePath;
|
||||
|
||||
sops.templates."personal-website-postgres.env" = {
|
||||
content = ''
|
||||
POSTGRES_PASSWORD=${config.sops.placeholder."personal-website/database-password"}
|
||||
POSTGRES_USER=chiassoncloud
|
||||
POSTGRES_DB=chiassoncloud
|
||||
'';
|
||||
};
|
||||
|
||||
sops.templates."personal-website.env" = {
|
||||
content = ''
|
||||
DATABASE_URL=postgresql://chiassoncloud:${config.sops.placeholder."personal-website/database-password"}@personal-website-db:5432/chiassoncloud
|
||||
AUTH_SECRET=${config.sops.placeholder."personal-website/auth-secret"}
|
||||
AUTH_DISCORD_SECRET=${config.sops.placeholder."personal-website/oauth-discord-client-secret"}
|
||||
'';
|
||||
};
|
||||
|
||||
services.personalWebsite = {
|
||||
enable = true;
|
||||
|
||||
app = {
|
||||
image = "ghcr.io/olivierchiasson/personal-website:main";
|
||||
ghcr = {
|
||||
username = "olivierchiasson";
|
||||
passwordFile = config.sops.secrets."swiftshare/ghcr-token".path;
|
||||
};
|
||||
|
||||
port = 3001;
|
||||
authUrl = "https://chiasson.cloud";
|
||||
publicUrl = "https://chiasson.cloud";
|
||||
disableTelemetry = true;
|
||||
environmentFiles = [ config.sops.templates."personal-website.env".path ];
|
||||
};
|
||||
|
||||
database = {
|
||||
user = "chiassoncloud";
|
||||
name = "chiassoncloud";
|
||||
environmentFiles = [ config.sops.templates."personal-website-postgres.env".path ];
|
||||
};
|
||||
|
||||
auth.discord.clientId = "1400660345068191855";
|
||||
|
||||
umami = {
|
||||
websiteId = "3b2f29d3-11b8-4a3b-bc76-bda3f27926d1";
|
||||
scriptUrl = "https://analytics.chiasson.cloud/script.js";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{config, ...}: {
|
||||
virtualisation = {
|
||||
docker.enable = true;
|
||||
oci-containers = {
|
||||
backend = "docker";
|
||||
containers = {
|
||||
portainer = {
|
||||
image = "portainer/portainer-ce:latest";
|
||||
ports = [ "9443:9443" ];
|
||||
volumes = [
|
||||
"/var/run/docker.sock:/var/run/docker.sock"
|
||||
"/var/lib/portainer:/data"
|
||||
];
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [ 9443 ];
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
# Prowlarr (indexer manager). UI: http://<host>:9696
|
||||
# Data dir is /var/lib/prowlarr (see systemd unit ExecStart -data=…), not ~/.config/Prowlarr.
|
||||
services.prowlarr.enable = true;
|
||||
|
||||
# Useful when Prowlarr/Sonarr/Radarr need to write into shared areas (downloads, etc.).
|
||||
users.groups.prowlarr = { };
|
||||
users.users.prowlarr = {
|
||||
isSystemUser = true;
|
||||
group = "prowlarr";
|
||||
extraGroups = [ "media" ];
|
||||
};
|
||||
|
||||
systemd.services.prowlarr.preStart = lib.mkBefore ''
|
||||
mkdir -p /var/lib/prowlarr/Definitions/Custom
|
||||
ln -sf ${./prowlarr/torrent9-custom.yml} /var/lib/prowlarr/Definitions/Custom/torrent9-custom.yml
|
||||
'';
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 9696 ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
---
|
||||
id: torrent9-custom
|
||||
name: Torrent9 (Custom URL)
|
||||
description: "Torrent9 is a FRENCH Public site for MOVIES / TV / GENERAL"
|
||||
language: fr-FR
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
testlinktorrent: false
|
||||
links:
|
||||
- https://www.torrent9.club/
|
||||
- https://www6.torrent9.to/
|
||||
legacylinks:
|
||||
- https://www.torrent9.pl/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.black-mirror.xyz/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.unblocked.casa/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.proxyportal.fun/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.uk-unblock.xyz/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.ind-unblock.xyz/ # this is a proxy for torrent9clone
|
||||
- https://ww1.torrent9.to/
|
||||
- https://www.torrent9.is/
|
||||
- https://torrent9.li/ # not a proxy for torrent9 or torrent9clone
|
||||
- https://www.oxtorrent.me/
|
||||
- https://www.torrent9.gg/
|
||||
- https://www.torrent9.fi/ # this is the torrent9clone domain
|
||||
- https://www.torrent9.fm/
|
||||
- https://torrent9.se/ # redirect to www.
|
||||
- https://torrent9.ninjaproxy1.com/ # no response data
|
||||
- https://torrent9.proxyninja.org/ # Error 1007
|
||||
- https://www.torrent9.se/
|
||||
- https://torrent9.unblockninja.com/ # 403 forbidden
|
||||
- https://ww1.torrent9.fm/
|
||||
- https://www.torrent9.zone/ # clone? details links are broken
|
||||
- https://torrent9.to/
|
||||
- https://ww2.torrent9.to/
|
||||
- https://www5.torrent9.to/
|
||||
|
||||
caps:
|
||||
# dont forget to update the search fields category case block
|
||||
categorymappings:
|
||||
- {id: films, cat: Movies, desc: "Movies"}
|
||||
- {id: series, cat: TV, desc: "TV"}
|
||||
- {id: musique, cat: Audio, desc: "Music"}
|
||||
- {id: ebook, cat: Books, desc: "Books"}
|
||||
- {id: logiciels, cat: PC, desc: "Software"}
|
||||
- {id: jeux-pc, cat: PC/Games, desc: "PC Games"}
|
||||
- {id: other, cat: Other, desc: "Other"} # dummy cat for results missing icon
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
allowrawsearch: true
|
||||
|
||||
settings:
|
||||
- name: info_flaresolverr
|
||||
type: info_flaresolverr
|
||||
- name: multilang
|
||||
type: checkbox
|
||||
label: Replace MULTi by another language in release name
|
||||
default: false
|
||||
- name: multilanguage
|
||||
type: select
|
||||
label: Replace MULTi by this language
|
||||
default: FRENCH
|
||||
options:
|
||||
FRENCH: FRENCH
|
||||
MULTi FRENCH: MULTi FRENCH
|
||||
ENGLISH: ENGLISH
|
||||
MULTi ENGLISH: MULTi ENGLISH
|
||||
VOSTFR: VOSTFR
|
||||
MULTi VOSTFR: MULTi VOSTFR
|
||||
- name: vostfr
|
||||
type: checkbox
|
||||
label: Replace VOSTFR and SUBFRENCH with ENGLISH
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site (Only works for searches with Keywords)
|
||||
default: ".html"
|
||||
options:
|
||||
".html": best
|
||||
".html,trie-date-d": created desc
|
||||
".html,trie-date-a": created asc
|
||||
".html,trie-seeds-d": seeders desc
|
||||
".html,trie-seeds-a": seeders asc
|
||||
".html,trie-poid-d": size desc
|
||||
".html,trie-poid-a": size asc
|
||||
".html,trie-nom-d": title desc
|
||||
".html,trie-nom-a": title asc
|
||||
|
||||
download:
|
||||
selectors:
|
||||
- selector: a[href^="magnet:?"]
|
||||
attribute: href
|
||||
- selector: script:contains("magnet:?")
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "\\s'(magnet:\\?.+?)';"
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "{{ if .Keywords }}search_torrent/{{ .Keywords }}{{ .Config.sort }}{{ else }}home/{{ end }}"
|
||||
keywordsfilters:
|
||||
# if searching for season packs with S01 to saison 1 #9712
|
||||
- name: re_replace
|
||||
args: ["(?i)(S0)(\\d{1,2})$", "saison $2"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(S)(\\d{1,3})$", "saison $2"]
|
||||
- name: replace
|
||||
args: [" ", "-"]
|
||||
|
||||
headers:
|
||||
# site blocks all Linux User-Agents, so use a slightly altered Windows Jackett UA here (e.g. Safari/537.36 > Safari/537.35)
|
||||
User-Agent: ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.35"]
|
||||
|
||||
rows:
|
||||
selector: table.table-striped > tbody > tr
|
||||
filters:
|
||||
- name: andmatch
|
||||
|
||||
fields:
|
||||
category_optional:
|
||||
selector: td:nth-child(1) i
|
||||
optional: true
|
||||
case:
|
||||
i[class="fa fa-video-camera"]: films
|
||||
i[class="fa fa-tv"]: series # search by name
|
||||
i[class="fa fa-desktop"]: series # keywordless search
|
||||
i[class="fa fa-music"]: musique
|
||||
i[class="fa fa-gamepad"]: jeux-pc
|
||||
i[class="fa fa-laptop"]: logiciels
|
||||
i[class="fa fa-book"]: ebook
|
||||
category:
|
||||
text: "{{ if .Result.category_optional }}{{ .Result.category_optional }}{{ else }}other{{ end }}"
|
||||
title_default:
|
||||
selector: td:nth-child(1) a
|
||||
title_optional:
|
||||
selector: td:nth-child(1) a[title]
|
||||
attribute: title
|
||||
optional: true
|
||||
title_phase1:
|
||||
text: "{{ if .Result.title_optional }}{{ .Result.title_optional }}{{ else }}{{ .Result.title_default }}{{ end }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)\\b(FRENCH|MULTI|TRUEFRENCH|VOSTFR|SUBFRENCH)\\b(.+?)(\\b((19|20)\\d{2})\\b)$", "$3 $1$2"]
|
||||
title_vostfr:
|
||||
text: "{{ .Result.title_phase1 }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)\\b(vostfr|subfrench)\\b", "ENGLISH"]
|
||||
title_phase2:
|
||||
text: "{{ if .Config.vostfr }}{{ .Result.title_vostfr }}{{ else }}{{ .Result.title_phase1 }}{{ end }}"
|
||||
title_multilang:
|
||||
text: "{{ .Result.title_phase2 }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)\\b(MULTI(?!.*(?:FRENCH|ENGLISH|VOSTFR)))\\b", "{{ .Config.multilanguage }}"]
|
||||
title:
|
||||
text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase2 }}{{ end }}"
|
||||
details:
|
||||
selector: td:nth-child(1) a
|
||||
attribute: href
|
||||
download:
|
||||
selector: td:nth-child(1) a
|
||||
attribute: href
|
||||
date:
|
||||
selector: td:nth-child(2):contains("/")
|
||||
optional: true
|
||||
default: now
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "dd/MM/yyyy"
|
||||
size:
|
||||
selector: "{{ if .Keywords }}td:nth-child(3){{ else }}td:nth-child(2){{ end }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(\\w)o", "$1B"]
|
||||
seeders:
|
||||
selector: "{{ if .Keywords }}td:nth-child(4){{ else }}td:nth-child(3){{ end }}"
|
||||
optional: true
|
||||
default: 0
|
||||
leechers:
|
||||
selector: "{{ if .Keywords }}td:nth-child(5){{ else }}td:nth-child(4){{ end }}"
|
||||
optional: true
|
||||
default: 0
|
||||
downloadvolumefactor:
|
||||
text: 0
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
# engine n/a
|
||||
@@ -0,0 +1,36 @@
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
webPort = 8081;
|
||||
btPort = 51413;
|
||||
downloadsDir = "/var/lib/downloads";
|
||||
in
|
||||
{
|
||||
# qBittorrent (headless). Web UI: http://<host>:8081
|
||||
services.qbittorrent = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
webuiPort = webPort;
|
||||
# Prefer a stable port for NAT/firewall and for easier debugging.
|
||||
torrentingPort = btPort;
|
||||
};
|
||||
|
||||
users.groups.qbittorrent = { };
|
||||
users.users.qbittorrent = {
|
||||
isSystemUser = true;
|
||||
group = "qbittorrent";
|
||||
extraGroups = [ "media" ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.settings."nix-server-downloads-dir" = {
|
||||
"${downloadsDir}"."d" = {
|
||||
mode = "2775";
|
||||
user = "root";
|
||||
group = "media";
|
||||
};
|
||||
};
|
||||
|
||||
# Some NixOS versions don't open UDP for torrenting even when openFirewall=true.
|
||||
networking.firewall.allowedTCPPorts = [ webPort btPort ];
|
||||
networking.firewall.allowedUDPPorts = [ btPort ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{ ... }:
|
||||
{
|
||||
# Radarr (movie automation). UI: http://<host>:7878
|
||||
services.radarr.enable = true;
|
||||
|
||||
# Keep permissions aligned with Jellyfin (/var/lib/media via group `media`).
|
||||
users.groups.radarr = { };
|
||||
users.users.radarr = {
|
||||
isSystemUser = true;
|
||||
group = "radarr";
|
||||
extraGroups = [ "media" ];
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 7878 ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{ ... }:
|
||||
{
|
||||
# Blank default Seerr branding assets (read-only store otherwise).
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
seerr = prev.seerr.overrideAttrs (oldAttrs: {
|
||||
postInstall =
|
||||
(oldAttrs.postInstall or "")
|
||||
+ ''
|
||||
find "$out/share/public" -maxdepth 1 -type f \( -name 'logo_full.svg' -o -name 'logo_stacked.svg' \) \
|
||||
-exec truncate -s 0 {} \;
|
||||
'';
|
||||
});
|
||||
})
|
||||
];
|
||||
|
||||
# "Seerr" request management. For Jellyfin, Jellyseerr is the right choice.
|
||||
# UI: http://<host>:5055
|
||||
services.seerr.enable = true;
|
||||
|
||||
users.groups.jellyseerr = { };
|
||||
users.users.jellyseerr = {
|
||||
isSystemUser = true;
|
||||
group = "jellyseerr";
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 5055 ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{ ... }:
|
||||
{
|
||||
# Sonarr (TV automation). UI: http://<host>:8989
|
||||
services.sonarr.enable = true;
|
||||
|
||||
# Ensure Sonarr can manage the same libraries as Jellyfin.
|
||||
users.groups.sonarr = { };
|
||||
users.users.sonarr = {
|
||||
isSystemUser = true;
|
||||
group = "sonarr";
|
||||
extraGroups = [ "media" ];
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8989 ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
secretFilePath = ../secrets.yaml;
|
||||
in
|
||||
{
|
||||
sops.secrets."swiftshare/ghcr-token".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/database-password".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/oauth-discord-client-secret".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/oauth-github-client-secret".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/auth-secret".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/oauth-google-client-id".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/oauth-google-client-secret".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/smtp-pass".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/minio-access-key".sopsFile = secretFilePath;
|
||||
sops.secrets."swiftshare/minio-secret-key".sopsFile = secretFilePath;
|
||||
|
||||
# Docker `--env-file` expects `KEY=value`. Separate snippets for DB/MinIO so only `swiftshare.env` hits the app container.
|
||||
sops.templates."swiftshare-postgres.env" = {
|
||||
content = ''
|
||||
POSTGRES_PASSWORD=${config.sops.placeholder."swiftshare/database-password"}
|
||||
'';
|
||||
};
|
||||
|
||||
sops.templates."swiftshare-minio.env" = {
|
||||
content = ''
|
||||
MINIO_ROOT_USER=${config.sops.placeholder."swiftshare/minio-access-key"}
|
||||
MINIO_ROOT_PASSWORD=${config.sops.placeholder."swiftshare/minio-secret-key"}
|
||||
'';
|
||||
};
|
||||
|
||||
sops.templates."swiftshare.env" = {
|
||||
content = ''
|
||||
DATABASE_URL=postgresql://swiftshare:${config.sops.placeholder."swiftshare/database-password"}@swiftshare-db:5432/swiftshare
|
||||
AUTH_SECRET=${config.sops.placeholder."swiftshare/auth-secret"}
|
||||
AUTH_DISCORD_SECRET=${config.sops.placeholder."swiftshare/oauth-discord-client-secret"}
|
||||
AUTH_GITHUB_SECRET=${config.sops.placeholder."swiftshare/oauth-github-client-secret"}
|
||||
AUTH_GOOGLE_SECRET=${config.sops.placeholder."swiftshare/oauth-google-client-secret"}
|
||||
AUTH_GOOGLE_ID=${config.sops.placeholder."swiftshare/oauth-google-client-id"}
|
||||
SMTP_PASS=${config.sops.placeholder."swiftshare/smtp-pass"}
|
||||
STORAGE_ACCESS_KEY=${config.sops.placeholder."swiftshare/minio-access-key"}
|
||||
STORAGE_SECRET_KEY=${config.sops.placeholder."swiftshare/minio-secret-key"}
|
||||
'';
|
||||
};
|
||||
|
||||
services.swiftshare = {
|
||||
enable = true;
|
||||
|
||||
app = {
|
||||
image = "ghcr.io/olivierchiasson/swiftshare:main";
|
||||
ghcr = {
|
||||
username = "olivierchiasson";
|
||||
passwordFile = config.sops.secrets."swiftshare/ghcr-token".path;
|
||||
};
|
||||
|
||||
origin = "https://swiftshare.cloud";
|
||||
port = 3000;
|
||||
uploadBodySizeLimit = "100mb";
|
||||
disableTelemetry = true;
|
||||
environmentFiles = [ config.sops.templates."swiftshare.env".path ];
|
||||
};
|
||||
|
||||
database = {
|
||||
user = "swiftshare";
|
||||
#password = ""; # Defined in sops.templates."swiftshare-postgres.env"
|
||||
name = "swiftshare";
|
||||
environmentFiles = [ config.sops.templates."swiftshare-postgres.env".path ];
|
||||
#exposePort.enable = true;
|
||||
};
|
||||
|
||||
auth = {
|
||||
#secret = "";
|
||||
|
||||
discord = {
|
||||
clientId = "1400660345068191855";
|
||||
#clientSecret = ""; # Defined in sops.templates."swiftshare.env"
|
||||
};
|
||||
|
||||
# GitHub OAuth App (https://github.com/settings/developers) — replace placeholders.
|
||||
github = {
|
||||
clientId = "Ov23lifcVKR6B1iYDicU";
|
||||
#clientSecret = ""; # Defined in sops.templates."swiftshare.env"
|
||||
};
|
||||
|
||||
# Google Cloud OAuth 2.0 client — replace placeholders.
|
||||
#google = {
|
||||
# clientId = ""; # Defined in sops.templates."swiftshare.env"
|
||||
# clientSecret = ""; # Defined in sops.templates."swiftshare.env"
|
||||
#};
|
||||
|
||||
# SMTP for Better Auth email verification / password reset.
|
||||
smtp = {
|
||||
host = "smtp.purelymail.com";
|
||||
port = 465;
|
||||
secure = true;
|
||||
user = "noreply@swiftshare.cloud";
|
||||
#pass = ""; # Defined in sops.templates."swiftshare.env"
|
||||
from = "noreply@swiftshare.cloud";
|
||||
};
|
||||
};
|
||||
|
||||
minio = {
|
||||
#accessKey = ""; # Defined in sops.templates."swiftshare-minio.env"
|
||||
#secretKey = ""; # Defined in sops.templates."swiftshare-minio.env"
|
||||
bucketName = "swiftshare-assets";
|
||||
environmentFiles = [ config.sops.templates."swiftshare-minio.env".path ];
|
||||
};
|
||||
|
||||
umami = {
|
||||
websiteId = "b4e1240d-a9d8-4075-b64d-0d3e0329cac8";
|
||||
scriptUrl = "https://analytics.chiasson.cloud/script.js";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
inputs.ddrm.nixosModules.default
|
||||
self.nixosModules.systemDeployBuilder
|
||||
self.nixosModules.nix-serverHardware
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
self.nixosModules.system
|
||||
@@ -16,7 +18,11 @@
|
||||
./_services/attic-cache-server.nix
|
||||
./_services/portainer.nix
|
||||
./_services/swiftshare.nix
|
||||
./_services/personal-website.nix
|
||||
./_services/immich.nix
|
||||
./_services/ddrm-media-server.nix
|
||||
./_services/gitea.nix
|
||||
./_services/cloudflare-dyndns.nix
|
||||
];
|
||||
|
||||
boot.loader.grub = {
|
||||
@@ -68,6 +74,8 @@
|
||||
extraPackages = with pkgs; [ btop ];
|
||||
};
|
||||
|
||||
chiasson.system.deploy.builder.enable = true;
|
||||
|
||||
chiasson.users = {
|
||||
enabled = [ "server" ];
|
||||
hostOverrides.server = {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.nixosConfigurations.nix-server = inputs.nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "nix-server";
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
modules = [
|
||||
self.nixosModules.nix-serverConfiguration
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
swiftshare:
|
||||
ghcr-token: ENC[AES256_GCM,data:wNzBA8Ib5WjxoKkGiWkfeGspKzy/vzbwwAp/+cjRF9Vsmlyx67OovQ==,iv:MCrkALYCHiPDb1tNQaWRrxuYSRXD6JtzJzEOr1aqhBk=,tag:okQfIP5IJUUIFfwAlZM1ow==,type:str]
|
||||
ghcr-token: ENC[AES256_GCM,data:V/5dLVLv4BbAxdMiBxXgmNbK17HAQkqzHJA2NWzOFfFlcy3dq8SnZQ==,iv:YTB3Bef+kZXunXVUCkFj/YZo1POdx2K+bNvzarSJ1Iw=,tag:HEBT4ZKMXTIy+ZEkNx3rHw==,type:str]
|
||||
database-password: ENC[AES256_GCM,data:r9GSaoQ7bS644ipb3kU=,iv:KYDTzYtjfz5meDb0nemY1lhSFEorKHL0hSRIcQaHg5c=,tag:RVjAfb8XGsybAgIc2/hH+g==,type:str]
|
||||
auth-secret: ENC[AES256_GCM,data:tTXLMWASBfF49gBFrf+CZ3R4oTt7hEGUhAqEdvoQtm0zbb2VUhTq7y4tH/c=,iv:Halfu9hBex4SEUMHLAicqApTxZP0NV9pJZTr+bBSek4=,tag:1WqN75zT+zoka9sIXOJGfQ==,type:str]
|
||||
oauth-discord-client-secret: ENC[AES256_GCM,data:a9Iarcpl1HOFXdsDMh3H662T8yqVvGtfguVICwWVrAg=,iv:LsUserWQcEDV0TiRWj1sHh5/ZiFQzyc1gRWg+Ewwjik=,tag:33Ml08oHVXl0ZMmiwQ2mig==,type:str]
|
||||
@@ -11,10 +11,15 @@ swiftshare:
|
||||
minio-secret-key: ENC[AES256_GCM,data:szkx+MTbMWmfbQ==,iv:+1zlHJRKMR4XDv1rrkOeilz06YA1W/1o+egylm/ZjPs=,tag:70QO3dPp9WRd71Puzl47QA==,type:str]
|
||||
immich:
|
||||
database-password: ENC[AES256_GCM,data:YWLt2pty/yVrrF7K,iv:uqrQGfST/A6LzRZ4+O0puXA1bd/7CL5A/T7jU+/++X8=,tag:/gNGK3z4RembX+tBET4M5g==,type:str]
|
||||
personal-website:
|
||||
database-password: ENC[AES256_GCM,data:PR6nNKOqB/SE956hXA4=,iv:/1usgEXfY+ef9bOAaCdjduqBqoonAm5saFBSjdGhm1Q=,tag:mDThIsYVUKyN6vQlh2YYbQ==,type:str]
|
||||
auth-secret: ENC[AES256_GCM,data:NHY+0tOA6FRmvkKZ/KgpogwOf+DuF42aoxspPUdVSi/iY5dF2yY8hwHaehI=,iv:9Gv/1YDcU+rMuX3PrwwT97qdsywIn+/wWEk17evyloo=,tag:R6RZbRhyXkwfZ0p/fZ+YcA==,type:str]
|
||||
oauth-discord-client-secret: ENC[AES256_GCM,data:YegPgoSRKNcDaID9LPWxHDz4T7VnhFfuWMyALfFhpg8=,iv:VSLWA1HG1+Y70tKnRoFulBZSKdoJTYmIDzCXIZeFYCc=,tag:yNR8rrm/7Mrj/RIVNLFfsg==,type:str]
|
||||
cloudflare-ddns:
|
||||
api-token: ENC[AES256_GCM,data:wFKbclETO0YQTcfNUdKyr6mxQODeiaYn3gLeC1mWeRda97rOvlum+Q==,iv:IuT4exNhh0z+9DbY3WNnVqEy4398DTm7aluhOv9XFss=,tag:GGPoSJLBScTmXyQ7Vab6EA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1yyzgmazjxkvwtfcv9re3lqmt2ru5dcrfu3sauysm0wzfwzvyap8qkjkq32
|
||||
enc: |
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlVWpFK2RRSHRxVFVSdEdI
|
||||
S01BSEZQUTZCV0tvM1lpSFNYc3g3ek5QNjJrClNyVUtKYnRtWVRYRkE2SStWRVRR
|
||||
@@ -22,8 +27,8 @@ sops:
|
||||
ZUpnemRBSmlSZVpmRW0wNFhIK3BibVkKdD14ki8dJbYMjsBkC1Nm5TOM6M33eLJ6
|
||||
IUrKDWeZXEVe2sMhBb31Zv+tinwtHSsvpxDIsjstpxtH+5wTyoQVdA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1elk6zwmcylwfk7gd4pjda7g29upftjvxys8py42s8d42jklnyv7s7dm9z2
|
||||
enc: |
|
||||
recipient: age1yyzgmazjxkvwtfcv9re3lqmt2ru5dcrfu3sauysm0wzfwzvyap8qkjkq32
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1bFJDNDdsWGIzMDl3TmRr
|
||||
MStsZVFRa1dIVmJGU3krWWlpc2FZMU9EREF3CkdDZFc0Y3ZIMVZxNHorWFRHaWwy
|
||||
@@ -31,8 +36,8 @@ sops:
|
||||
NUZIYnZIMDRWTXpwTURMc2tzelp3VjAKHHBkHhz+t03W0ojsOBB2i3K4ZMUXvrwF
|
||||
4mjNqNBcAJ1uHgJP7qvpNjxEW1LcsdQKmXavoqizX+XfLaA3zEwB0Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9
|
||||
enc: |
|
||||
recipient: age1elk6zwmcylwfk7gd4pjda7g29upftjvxys8py42s8d42jklnyv7s7dm9z2
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwTWR0N3UwdTB0UDZxRmV5
|
||||
R3dkYUhZaElMbkxxSllTNWkrb05VSkJrMUNRCjZTUTlvVTU2MHY5ZS9oU2pCSlFu
|
||||
@@ -40,7 +45,8 @@ sops:
|
||||
WWZwbkR4dTFjK2NZcW9pTTNHd252N3cKiz8l9AWciFOBU+wcT9T1WA4bToPYfq8G
|
||||
Nf0uOoSWPTJ/2SRNkSu7FMumATH4ldQ6TFSwKda3mBfBwhnFzLq10Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-04-30T18:45:45Z"
|
||||
mac: ENC[AES256_GCM,data:DD9NZcYQVSByaQvGAB7b/Wpk7SWBBsWtzAM9MkIHMmyxNomiPPUFQR6+18QDUCHQXk1xXMUi79bnTRz8SdoBXVjbHG8Qhy3n6D1sFeEgXC42pgem7hBPfmJlgcIPNYEguXPISLsp/Zx9ISEnH5Zul0v8/G2ACN7Y/U3jtaHx4U8=,iv:g1k16EhTR+t9jCpvhmiXYZV99aMk1DrS4frpl5q93lM=,tag:FigaXNw+IbpZ7E0a+ySb3g==,type:str]
|
||||
recipient: age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9
|
||||
lastmodified: "2026-05-28T21:04:07Z"
|
||||
mac: ENC[AES256_GCM,data:L53UYFh5xtuMx19GKAg3jW7U0/DlwJ2usy/pup+4t1HQN3KHxMwbc4BzLYkLnRBTwKMJdfKXiYmmYiYvfbbWzsPtfXLxPnF/5ROiCJ2NlxAe86SmRy2nI++eTHAXRgexIhYyL7SchsroGRvW2B3aL1jV+Eu11fD9trA9Ex1EfuI=,iv:XrRJCFSgwW2+N+4FnWrFFZz8UEVzhuhpRtHGtf8dyqc=,tag:LcO4+ilwKdU+JPyjyKaGNw==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.12.2
|
||||
version: 3.13.1
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Media stack storage on r5500: btrfs subvolume @media-stack on the OS disk (sda4).
|
||||
{ config, pkgs, lib, ... }:
|
||||
let
|
||||
btrfsUuid = "934a5ec3-4bab-49c3-96c9-c857c50076ba";
|
||||
btrfsDevice = "/dev/disk/by-uuid/${btrfsUuid}";
|
||||
# Created under subvol=@ → full path is @/@media-stack (not a top-level @media-stack).
|
||||
mediaSubvol = "@/@media-stack";
|
||||
in
|
||||
{
|
||||
# Create @media-stack before /mnt/media-stack is mounted.
|
||||
systemd.services.r5500-media-stack-subvolume = {
|
||||
description = "Create btrfs subvolume @media-stack on sda4 if missing";
|
||||
before = [ "mnt-media\\x2dmedia\\x2dstack.mount" ];
|
||||
wantedBy = [ "local-fs-pre.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
rootMount="/mnt/.r5500-btrfs-root"
|
||||
mkdir -p "$rootMount"
|
||||
if ! mountpoint -q "$rootMount"; then
|
||||
mount -o subvol=@ "${btrfsDevice}" "$rootMount"
|
||||
umountAfter=1
|
||||
else
|
||||
umountAfter=0
|
||||
fi
|
||||
if ! ${pkgs.btrfs-progs}/bin/btrfs subvolume show "$rootMount/@media-stack" >/dev/null 2>&1; then
|
||||
${pkgs.btrfs-progs}/bin/btrfs subvolume create "$rootMount/@media-stack"
|
||||
fi
|
||||
if [ "$umountAfter" -eq 1 ]; then
|
||||
umount "$rootMount"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
# Optional 1 TiB cap (run once after first boot if desired):
|
||||
# sudo btrfs quota enable /mnt/media-stack && sudo btrfs qgroup limit 1T /mnt/media-stack
|
||||
|
||||
fileSystems."/mnt/media-stack" = {
|
||||
device = btrfsDevice;
|
||||
fsType = "btrfs";
|
||||
neededForBoot = false;
|
||||
options = [
|
||||
"subvol=${mediaSubvol}"
|
||||
"compress=zstd"
|
||||
"noatime"
|
||||
"nofail"
|
||||
"x-systemd.device-timeout=30"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
# Shared media group, directory layout, and Jellyfin config bind-mount on /mnt/media-stack.
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
paths = import ./media-stack-paths.nix;
|
||||
prowlarrCustomIndexer = "${./../_services/prowlarr/torrent9-custom.yml}";
|
||||
inherit (paths)
|
||||
mediaRoot
|
||||
downloadsDir
|
||||
downloadsIncompleteDir
|
||||
sonarrDataDir
|
||||
radarrDataDir
|
||||
prowlarrDataDir
|
||||
qbittorrentDataDir
|
||||
seerrConfigDir
|
||||
dispatcharrDataDir
|
||||
organizrDataDir
|
||||
jellyfinConfigDir
|
||||
jellyfinMoviesDir
|
||||
jellyfinTvDir
|
||||
;
|
||||
in
|
||||
{
|
||||
_module.args.mediaStackPaths = paths;
|
||||
|
||||
users.groups.media = { };
|
||||
|
||||
users.users.server.extraGroups = [ "media" ];
|
||||
|
||||
# Layout dirs only after /mnt/media-stack is mounted (tmpfiles at early boot would
|
||||
# otherwise create paths on the root fs and break bind mounts / boot).
|
||||
systemd.services.r5500-media-stack-dirs = {
|
||||
description = "Create media-stack directory layout";
|
||||
after = [ "mnt-media\\x2dmedia\\x2dstack.mount" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
set -e
|
||||
install -d -m 0775 -o root -g media ${mediaRoot}
|
||||
install -d -m 2775 -o root -g media ${jellyfinMoviesDir} ${jellyfinTvDir} ${downloadsDir}
|
||||
install -d -m 2775 -o root -g media ${downloadsIncompleteDir}
|
||||
install -d -m 0755 -o jellyfin -g jellyfin ${jellyfinConfigDir}
|
||||
install -d -m 0700 -o sonarr -g sonarr ${sonarrDataDir}
|
||||
install -d -m 0700 -o radarr -g radarr ${radarrDataDir}
|
||||
install -d -m 0700 -o prowlarr -g prowlarr ${prowlarrDataDir}
|
||||
install -d -m 0750 -o qbittorrent -g qbittorrent ${qbittorrentDataDir}
|
||||
install -d -m 0755 -o jellyseerr -g jellyseerr ${seerrConfigDir} 2>/dev/null \
|
||||
|| install -d -m 0755 -o root -g root ${seerrConfigDir}
|
||||
install -d -m 0777 -o root -g root ${dispatcharrDataDir}
|
||||
install -d -m 0755 -o organizr -g organizr ${organizrDataDir} 2>/dev/null \
|
||||
|| install -d -m 0755 -o root -g root ${organizrDataDir}
|
||||
install -d -m 0700 -o prowlarr -g prowlarr ${prowlarrDataDir}/Definitions/Custom
|
||||
ln -sfn ${prowlarrCustomIndexer} ${prowlarrDataDir}/Definitions/Custom/torrent9-custom.yml
|
||||
'';
|
||||
};
|
||||
|
||||
# Jellyfin metadata on the media subvolume; nofail so a missing subvol never bricks boot.
|
||||
fileSystems."/var/lib/jellyfin" = {
|
||||
device = jellyfinConfigDir;
|
||||
fsType = "none";
|
||||
neededForBoot = false;
|
||||
options = [
|
||||
"bind"
|
||||
"nofail"
|
||||
"x-systemd.after=mnt-media\\x2dmedia\\x2dstack.mount"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# Path constants for the r5500 media stack (imported by _services/* and media-paths.nix).
|
||||
{
|
||||
mediaRoot = "/mnt/media-stack";
|
||||
downloadsDir = "/mnt/media-stack/downloads";
|
||||
downloadsIncompleteDir = "/mnt/media-stack/downloads/incomplete";
|
||||
sonarrDataDir = "/mnt/media-stack/sonarr";
|
||||
radarrDataDir = "/mnt/media-stack/radarr";
|
||||
prowlarrDataDir = "/mnt/media-stack/prowlarr";
|
||||
qbittorrentDataDir = "/mnt/media-stack/qbittorrent";
|
||||
seerrConfigDir = "/mnt/media-stack/seerr";
|
||||
dispatcharrDataDir = "/mnt/media-stack/dispatcharr";
|
||||
organizrDataDir = "/mnt/media-stack/organizr";
|
||||
jellyfinConfigDir = "/mnt/media-stack/jellyfin/config";
|
||||
jellyfinMoviesDir = "/mnt/media-stack/jellyfin/movies";
|
||||
jellyfinTvDir = "/mnt/media-stack/jellyfin/tv";
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{ lib, pkgs, mediaStackPaths, ... }:
|
||||
let
|
||||
dataDir = mediaStackPaths.dispatcharrDataDir;
|
||||
in
|
||||
{
|
||||
systemd.tmpfiles.settings."r5500-dispatcharr-data" = {
|
||||
"${dataDir}"."d" = {
|
||||
mode = "0777";
|
||||
user = "root";
|
||||
group = "root";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.docker-dispatcharr.preStart = lib.mkBefore ''
|
||||
${pkgs.coreutils}/bin/mkdir -p ${dataDir}
|
||||
'';
|
||||
|
||||
virtualisation.oci-containers.containers.dispatcharr = {
|
||||
image = "ghcr.io/dispatcharr/dispatcharr:latest";
|
||||
ports = [ "9191:9191" ];
|
||||
volumes = [
|
||||
"${dataDir}:/data"
|
||||
];
|
||||
environment = {
|
||||
DISPATCHARR_ENV = "aio";
|
||||
REDIS_HOST = "localhost";
|
||||
CELERY_BROKER_URL = "redis://localhost:6379/0";
|
||||
DISPATCHARR_LOG_LEVEL = "info";
|
||||
TZ = "America/Moncton";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 9191 ];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Docker backend for Dispatcharr and Organizr on r5500.
|
||||
{ ... }:
|
||||
{
|
||||
virtualisation.docker.enable = true;
|
||||
virtualisation.oci-containers.backend = "docker";
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{ ... }:
|
||||
{
|
||||
services.flaresolverr.enable = true;
|
||||
networking.firewall.allowedTCPPorts = [ 8191 ];
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Jellyfin on r5500. Local libraries: /mnt/media-stack/jellyfin/{movies,tv}.
|
||||
# Bulk libraries: /mnt/nixdesk-jellyfin/{movies,tv} (NFS from 14900k).
|
||||
{ lib, ... }:
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
jellyfin-web = prev.jellyfin-web.overrideAttrs (oldAttrs: {
|
||||
postInstall =
|
||||
(oldAttrs.postInstall or "")
|
||||
+ ''
|
||||
find "$out" -type f \( -name 'banner-light.*.png' -o -name 'banner-dark.*.png' \) \
|
||||
-exec truncate -s 0 {} \;
|
||||
'';
|
||||
});
|
||||
})
|
||||
];
|
||||
|
||||
users.users.jellyfin.extraGroups = [ "media" ];
|
||||
|
||||
services.jellyfin = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
|
||||
systemd.services.jellyfin.serviceConfig = {
|
||||
SupplementaryGroups = [ "media" ];
|
||||
PrivateUsers = lib.mkForce false;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# NFS mounts of nixdesk (14900k) bulk storage for r5500. Exports live in
|
||||
# modules/hosts/14900k/_private/jellyfin-nfs-export.nix
|
||||
#
|
||||
# Jellyfin library paths:
|
||||
# Movies → /mnt/nixdesk-jellyfin/movies
|
||||
# Shows → /mnt/nixdesk-jellyfin/tv
|
||||
{ ... }:
|
||||
let
|
||||
nfsExportHost = "192.168.2.25";
|
||||
nfsClientOpts = [
|
||||
"rw"
|
||||
"noatime"
|
||||
"nofail"
|
||||
"_netdev"
|
||||
"nfsvers=3"
|
||||
"tcp"
|
||||
"lookupcache=none"
|
||||
"x-systemd.automount"
|
||||
"x-systemd.idle-timeout=3600"
|
||||
];
|
||||
in
|
||||
{
|
||||
fileSystems."/mnt/nixdesk-jellyfin" = {
|
||||
device = "${nfsExportHost}:/mnt/deep/jellyfin";
|
||||
fsType = "nfs";
|
||||
options = nfsClientOpts;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{ lib, pkgs, mediaStackPaths, ... }:
|
||||
let
|
||||
configDir = mediaStackPaths.organizrDataDir;
|
||||
in
|
||||
{
|
||||
users.groups.organizr = { gid = 950; };
|
||||
users.users.organizr = {
|
||||
isSystemUser = true;
|
||||
uid = 950;
|
||||
group = "organizr";
|
||||
};
|
||||
|
||||
systemd.tmpfiles.settings."r5500-organizr-config" = {
|
||||
"${configDir}"."d" = {
|
||||
mode = "0755";
|
||||
user = "organizr";
|
||||
group = "organizr";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.tmpfiles.settings."r5500-organizr-config-perms" = {
|
||||
"${configDir}"."Z" = {
|
||||
mode = "0755";
|
||||
user = "organizr";
|
||||
group = "organizr";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.docker-organizr.preStart = lib.mkBefore ''
|
||||
${pkgs.coreutils}/bin/mkdir -p ${configDir}
|
||||
${pkgs.coreutils}/bin/chown -R organizr:organizr ${configDir}
|
||||
'';
|
||||
|
||||
virtualisation.oci-containers.containers.organizr = {
|
||||
image = "ghcr.io/organizr/organizr:latest";
|
||||
ports = [ "8888:80" ];
|
||||
volumes = [
|
||||
"${configDir}:/config"
|
||||
];
|
||||
environment = {
|
||||
PUID = "950";
|
||||
PGID = "950";
|
||||
TZ = "America/Moncton";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8888 ];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{ lib, mediaStackPaths, ... }:
|
||||
{
|
||||
services.prowlarr = {
|
||||
enable = true;
|
||||
dataDir = mediaStackPaths.prowlarrDataDir;
|
||||
};
|
||||
|
||||
users.groups.prowlarr = { };
|
||||
users.users.prowlarr = {
|
||||
isSystemUser = true;
|
||||
group = "prowlarr";
|
||||
extraGroups = [ "media" ];
|
||||
};
|
||||
|
||||
systemd.services.prowlarr.serviceConfig.ReadWritePaths = lib.mkAfter [
|
||||
mediaStackPaths.prowlarrDataDir
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 9696 ];
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
---
|
||||
id: torrent9-custom
|
||||
name: Torrent9 (Custom URL)
|
||||
description: "Torrent9 is a FRENCH Public site for MOVIES / TV / GENERAL"
|
||||
language: fr-FR
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
testlinktorrent: false
|
||||
links:
|
||||
- https://www.torrent9.club/
|
||||
- https://www6.torrent9.to/
|
||||
legacylinks:
|
||||
- https://www.torrent9.pl/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.black-mirror.xyz/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.unblocked.casa/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.proxyportal.fun/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.uk-unblock.xyz/ # this is a proxy for torrent9clone
|
||||
- https://torrent9.ind-unblock.xyz/ # this is a proxy for torrent9clone
|
||||
- https://ww1.torrent9.to/
|
||||
- https://www.torrent9.is/
|
||||
- https://torrent9.li/ # not a proxy for torrent9 or torrent9clone
|
||||
- https://www.oxtorrent.me/
|
||||
- https://www.torrent9.gg/
|
||||
- https://www.torrent9.fi/ # this is the torrent9clone domain
|
||||
- https://www.torrent9.fm/
|
||||
- https://torrent9.se/ # redirect to www.
|
||||
- https://torrent9.ninjaproxy1.com/ # no response data
|
||||
- https://torrent9.proxyninja.org/ # Error 1007
|
||||
- https://www.torrent9.se/
|
||||
- https://torrent9.unblockninja.com/ # 403 forbidden
|
||||
- https://ww1.torrent9.fm/
|
||||
- https://www.torrent9.zone/ # clone? details links are broken
|
||||
- https://torrent9.to/
|
||||
- https://ww2.torrent9.to/
|
||||
- https://www5.torrent9.to/
|
||||
|
||||
caps:
|
||||
# dont forget to update the search fields category case block
|
||||
categorymappings:
|
||||
- {id: films, cat: Movies, desc: "Movies"}
|
||||
- {id: series, cat: TV, desc: "TV"}
|
||||
- {id: musique, cat: Audio, desc: "Music"}
|
||||
- {id: ebook, cat: Books, desc: "Books"}
|
||||
- {id: logiciels, cat: PC, desc: "Software"}
|
||||
- {id: jeux-pc, cat: PC/Games, desc: "PC Games"}
|
||||
- {id: other, cat: Other, desc: "Other"} # dummy cat for results missing icon
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
allowrawsearch: true
|
||||
|
||||
settings:
|
||||
- name: info_flaresolverr
|
||||
type: info_flaresolverr
|
||||
- name: multilang
|
||||
type: checkbox
|
||||
label: Replace MULTi by another language in release name
|
||||
default: false
|
||||
- name: multilanguage
|
||||
type: select
|
||||
label: Replace MULTi by this language
|
||||
default: FRENCH
|
||||
options:
|
||||
FRENCH: FRENCH
|
||||
MULTi FRENCH: MULTi FRENCH
|
||||
ENGLISH: ENGLISH
|
||||
MULTi ENGLISH: MULTi ENGLISH
|
||||
VOSTFR: VOSTFR
|
||||
MULTi VOSTFR: MULTi VOSTFR
|
||||
- name: vostfr
|
||||
type: checkbox
|
||||
label: Replace VOSTFR and SUBFRENCH with ENGLISH
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site (Only works for searches with Keywords)
|
||||
default: ".html"
|
||||
options:
|
||||
".html": best
|
||||
".html,trie-date-d": created desc
|
||||
".html,trie-date-a": created asc
|
||||
".html,trie-seeds-d": seeders desc
|
||||
".html,trie-seeds-a": seeders asc
|
||||
".html,trie-poid-d": size desc
|
||||
".html,trie-poid-a": size asc
|
||||
".html,trie-nom-d": title desc
|
||||
".html,trie-nom-a": title asc
|
||||
|
||||
download:
|
||||
selectors:
|
||||
- selector: a[href^="magnet:?"]
|
||||
attribute: href
|
||||
- selector: script:contains("magnet:?")
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "\\s'(magnet:\\?.+?)';"
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "{{ if .Keywords }}search_torrent/{{ .Keywords }}{{ .Config.sort }}{{ else }}home/{{ end }}"
|
||||
keywordsfilters:
|
||||
# if searching for season packs with S01 to saison 1 #9712
|
||||
- name: re_replace
|
||||
args: ["(?i)(S0)(\\d{1,2})$", "saison $2"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(S)(\\d{1,3})$", "saison $2"]
|
||||
- name: replace
|
||||
args: [" ", "-"]
|
||||
|
||||
headers:
|
||||
# site blocks all Linux User-Agents, so use a slightly altered Windows Jackett UA here (e.g. Safari/537.36 > Safari/537.35)
|
||||
User-Agent: ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.35"]
|
||||
|
||||
rows:
|
||||
selector: table.table-striped > tbody > tr
|
||||
filters:
|
||||
- name: andmatch
|
||||
|
||||
fields:
|
||||
category_optional:
|
||||
selector: td:nth-child(1) i
|
||||
optional: true
|
||||
case:
|
||||
i[class="fa fa-video-camera"]: films
|
||||
i[class="fa fa-tv"]: series # search by name
|
||||
i[class="fa fa-desktop"]: series # keywordless search
|
||||
i[class="fa fa-music"]: musique
|
||||
i[class="fa fa-gamepad"]: jeux-pc
|
||||
i[class="fa fa-laptop"]: logiciels
|
||||
i[class="fa fa-book"]: ebook
|
||||
category:
|
||||
text: "{{ if .Result.category_optional }}{{ .Result.category_optional }}{{ else }}other{{ end }}"
|
||||
title_default:
|
||||
selector: td:nth-child(1) a
|
||||
title_optional:
|
||||
selector: td:nth-child(1) a[title]
|
||||
attribute: title
|
||||
optional: true
|
||||
title_phase1:
|
||||
text: "{{ if .Result.title_optional }}{{ .Result.title_optional }}{{ else }}{{ .Result.title_default }}{{ end }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)\\b(FRENCH|MULTI|TRUEFRENCH|VOSTFR|SUBFRENCH)\\b(.+?)(\\b((19|20)\\d{2})\\b)$", "$3 $1$2"]
|
||||
title_vostfr:
|
||||
text: "{{ .Result.title_phase1 }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)\\b(vostfr|subfrench)\\b", "ENGLISH"]
|
||||
title_phase2:
|
||||
text: "{{ if .Config.vostfr }}{{ .Result.title_vostfr }}{{ else }}{{ .Result.title_phase1 }}{{ end }}"
|
||||
title_multilang:
|
||||
text: "{{ .Result.title_phase2 }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)\\b(MULTI(?!.*(?:FRENCH|ENGLISH|VOSTFR)))\\b", "{{ .Config.multilanguage }}"]
|
||||
title:
|
||||
text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase2 }}{{ end }}"
|
||||
details:
|
||||
selector: td:nth-child(1) a
|
||||
attribute: href
|
||||
download:
|
||||
selector: td:nth-child(1) a
|
||||
attribute: href
|
||||
date:
|
||||
selector: td:nth-child(2):contains("/")
|
||||
optional: true
|
||||
default: now
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "dd/MM/yyyy"
|
||||
size:
|
||||
selector: "{{ if .Keywords }}td:nth-child(3){{ else }}td:nth-child(2){{ end }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(\\w)o", "$1B"]
|
||||
seeders:
|
||||
selector: "{{ if .Keywords }}td:nth-child(4){{ else }}td:nth-child(3){{ end }}"
|
||||
optional: true
|
||||
default: 0
|
||||
leechers:
|
||||
selector: "{{ if .Keywords }}td:nth-child(5){{ else }}td:nth-child(4){{ end }}"
|
||||
optional: true
|
||||
default: 0
|
||||
downloadvolumefactor:
|
||||
text: 0
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
# engine n/a
|
||||
@@ -0,0 +1,43 @@
|
||||
{ lib, mediaStackPaths, ... }:
|
||||
let
|
||||
webPort = 8081;
|
||||
btPort = 51413;
|
||||
inherit (mediaStackPaths) downloadsDir downloadsIncompleteDir qbittorrentDataDir;
|
||||
in
|
||||
{
|
||||
services.qbittorrent = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
webuiPort = webPort;
|
||||
torrentingPort = btPort;
|
||||
};
|
||||
|
||||
users.groups.qbittorrent = { };
|
||||
users.users.qbittorrent = {
|
||||
isSystemUser = true;
|
||||
group = "qbittorrent";
|
||||
extraGroups = [ "media" ];
|
||||
home = qbittorrentDataDir;
|
||||
};
|
||||
|
||||
fileSystems."/var/lib/qbittorrent" = {
|
||||
device = qbittorrentDataDir;
|
||||
fsType = "none";
|
||||
neededForBoot = false;
|
||||
options = [
|
||||
"bind"
|
||||
"nofail"
|
||||
"x-systemd.after=mnt-media\\x2dmedia\\x2dstack.mount"
|
||||
];
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ webPort btPort ];
|
||||
networking.firewall.allowedUDPPorts = [ btPort ];
|
||||
|
||||
# Default save path for new torrents (existing qBittorrent.conf may override after migration).
|
||||
systemd.services.qbittorrent.preStart = lib.mkAfter ''
|
||||
mkdir -p ${downloadsDir} ${downloadsIncompleteDir}
|
||||
chmod 2775 ${downloadsDir} ${downloadsIncompleteDir}
|
||||
chgrp media ${downloadsDir} ${downloadsIncompleteDir}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{ lib, mediaStackPaths, ... }:
|
||||
{
|
||||
services.radarr = {
|
||||
enable = true;
|
||||
dataDir = mediaStackPaths.radarrDataDir;
|
||||
};
|
||||
|
||||
users.groups.radarr = { };
|
||||
users.users.radarr = {
|
||||
isSystemUser = true;
|
||||
group = "radarr";
|
||||
extraGroups = [ "media" ];
|
||||
};
|
||||
|
||||
systemd.services.radarr.serviceConfig.ReadWritePaths = lib.mkAfter [
|
||||
mediaStackPaths.radarrDataDir
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 7878 ];
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{ lib, mediaStackPaths, ... }:
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
seerr = prev.seerr.overrideAttrs (oldAttrs: {
|
||||
postInstall =
|
||||
(oldAttrs.postInstall or "")
|
||||
+ ''
|
||||
find "$out/share/public" -maxdepth 1 -type f \( -name 'logo_full.svg' -o -name 'logo_stacked.svg' \) \
|
||||
-exec truncate -s 0 {} \;
|
||||
'';
|
||||
});
|
||||
})
|
||||
];
|
||||
|
||||
services.seerr = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
configDir = mediaStackPaths.seerrConfigDir;
|
||||
};
|
||||
|
||||
users.groups.jellyseerr = { };
|
||||
users.users.jellyseerr = {
|
||||
isSystemUser = true;
|
||||
group = "jellyseerr";
|
||||
};
|
||||
|
||||
systemd.services.seerr.serviceConfig = {
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = "jellyseerr";
|
||||
Group = "jellyseerr";
|
||||
ReadWritePaths = [ mediaStackPaths.seerrConfigDir ];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{ lib, mediaStackPaths, ... }:
|
||||
{
|
||||
services.sonarr = {
|
||||
enable = true;
|
||||
dataDir = mediaStackPaths.sonarrDataDir;
|
||||
};
|
||||
|
||||
users.groups.sonarr = { };
|
||||
users.users.sonarr = {
|
||||
isSystemUser = true;
|
||||
group = "sonarr";
|
||||
extraGroups = [ "media" ];
|
||||
};
|
||||
|
||||
systemd.services.sonarr.serviceConfig.ReadWritePaths = lib.mkAfter [
|
||||
mediaStackPaths.sonarrDataDir
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8989 ];
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.nixosModules.r5500Configuration =
|
||||
{
|
||||
self,
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
self.nixosModules.systemDeployBuilder
|
||||
self.nixosModules.r5500Hardware
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
self.nixosModules.system
|
||||
self.nixosModules.users
|
||||
./_private/media-disk.nix
|
||||
./_private/media-paths.nix
|
||||
./_services/docker-media.nix
|
||||
./_services/nixdesk-nfs-client.nix
|
||||
./_services/jellyfin.nix
|
||||
./_services/sonarr.nix
|
||||
./_services/radarr.nix
|
||||
./_services/prowlarr.nix
|
||||
./_services/flaresolverr.nix
|
||||
./_services/seerr.nix
|
||||
./_services/qbittorrent.nix
|
||||
./_services/dispatcharr.nix
|
||||
./_services/organizr.nix
|
||||
];
|
||||
|
||||
boot.loader.grub = {
|
||||
enable = true;
|
||||
efiSupport = false;
|
||||
device = "/dev/sda";
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PasswordAuthentication = true;
|
||||
KbdInteractiveAuthentication = false;
|
||||
PermitRootLogin = "no";
|
||||
UseDns = false;
|
||||
};
|
||||
};
|
||||
|
||||
sops = {
|
||||
defaultSopsFile = ../../../secrets/secrets.yaml;
|
||||
defaultSopsFormat = "yaml";
|
||||
age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
||||
};
|
||||
|
||||
sops.secrets."users/server/hashedPassword".neededForUsers = true;
|
||||
|
||||
security.sudo.wheelNeedsPassword = true;
|
||||
|
||||
nix.settings = {
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
trusted-users = [ "root" "@wheel" ];
|
||||
allowed-users = [ "root" "@wheel" ];
|
||||
};
|
||||
|
||||
chiasson.system = {
|
||||
networking = {
|
||||
hostName = "r5500";
|
||||
networkManager.enable = true;
|
||||
};
|
||||
extraPackages = with pkgs; [ btop git ];
|
||||
};
|
||||
|
||||
chiasson.system.deploy.builder.enable = true;
|
||||
|
||||
chiasson.users = {
|
||||
enabled = [ "server" ];
|
||||
hostOverrides.server = {
|
||||
hashedPasswordFile = config.sops.secrets."users/server/hashedPassword".path;
|
||||
};
|
||||
};
|
||||
|
||||
services.xserver.enable = lib.mkDefault false;
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.nixosConfigurations.r5500 = inputs.nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "r5500";
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
modules = [
|
||||
self.nixosModules.r5500Configuration
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{ ... }: {
|
||||
flake.nixosModules.r5500Hardware =
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
modulesPath,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [
|
||||
"uhci_hcd"
|
||||
"ehci_pci"
|
||||
"ahci"
|
||||
"usb_storage"
|
||||
"usbhid"
|
||||
"sd_mod"
|
||||
];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-intel" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems."/" = {
|
||||
device = "/dev/disk/by-uuid/934a5ec3-4bab-49c3-96c9-c857c50076ba";
|
||||
fsType = "btrfs";
|
||||
options = [ "subvol=@" ];
|
||||
};
|
||||
|
||||
fileSystems."/home" = {
|
||||
device = "/dev/disk/by-uuid/934a5ec3-4bab-49c3-96c9-c857c50076ba";
|
||||
fsType = "btrfs";
|
||||
options = [ "subvol=@home" ];
|
||||
};
|
||||
|
||||
fileSystems."/boot" = {
|
||||
device = "/dev/disk/by-uuid/6399d086-687b-4ca9-ad34-da1dd85203d5";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
swapDevices = [
|
||||
{ device = "/dev/disk/by-uuid/ddb9fea1-7c44-44bc-bc74-79a3adb6cc35"; }
|
||||
];
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{ pkgs, ... }: {
|
||||
# Apple T2 machines often need additional Broadcom firmware not shipped in
|
||||
# linux-firmware. Firmware is stored in this host directory.
|
||||
hardware.firmware = [
|
||||
(pkgs.stdenvNoCC.mkDerivation (final: {
|
||||
name = "t2mbp-brcm-firmware";
|
||||
src = ./firmware/brcm;
|
||||
installPhase = ''
|
||||
mkdir -p "$out/lib/firmware/brcm"
|
||||
cp -v ${final.src}/* "$out/lib/firmware/brcm"
|
||||
'';
|
||||
}))
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{ ... }: {
|
||||
# Bootloader and EFI
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
boot.loader.efi.efiSysMountPoint = "/boot";
|
||||
boot.loader.systemd-boot.configurationLimit = 5;
|
||||
|
||||
# Hibernate support (resume from swap partition).
|
||||
boot.resumeDevice = "/dev/disk/by-uuid/403c9698-b501-4198-96cf-f27f82f1eb1a";
|
||||
|
||||
# Suspend is allowed again: kernel cmdline + out-of-tree apple-bce are in
|
||||
# `t2linux/_private/boot-tuning.nix` and `t2linux/_private/kernel.nix`. If you need
|
||||
# to block sleep while debugging: `systemd.sleep.settings.Sleep.AllowSuspend = "no";`
|
||||
|
||||
# Power and thermal management.
|
||||
powerManagement.cpuFreqGovernor = "ondemand";
|
||||
services.thermald.enable = true;
|
||||
hardware.sensor.iio.enable = true;
|
||||
|
||||
|
||||
|
||||
# Pull in linux-firmware (and friends) from nixpkgs.
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
hardware.enableAllFirmware = true;
|
||||
|
||||
# Enable flakes and the newer CLI.
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
imports = [
|
||||
self.nixosModules.t2mbpHardware
|
||||
self.nixosModules.t2linux
|
||||
inputs.t2fanrd.nixosModules.t2fanrd
|
||||
inputs.t2fanrd.nixosModules.t2fanrd #TODO[epic=Moderate] Convert to flake parts module
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
|
||||
@@ -95,14 +95,12 @@
|
||||
};
|
||||
defaultSession = "niri";
|
||||
shell = "dms";
|
||||
shells.dms = {
|
||||
enableRbwLockToggle = true;
|
||||
};
|
||||
};
|
||||
|
||||
chiasson.system = {
|
||||
remoteDesktop = {
|
||||
enable = false;
|
||||
moonlight.enable = false;
|
||||
sunshine.enable = false;
|
||||
};
|
||||
audio.enable = true;
|
||||
extraPackages = [ pkgs.sops ];
|
||||
networking = {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{ self, inputs, ... }: {
|
||||
|
||||
flake.nixosConfigurations.t2mbp = inputs.nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs = {
|
||||
inherit self inputs;
|
||||
host = "t2mbp";
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
modules = [
|
||||
self.nixosModules.t2mbpConfiguration
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{ ... }: {
|
||||
# T2: align with https://github.com/deqrocks/apple-bce#required-kernel-parameters (suspend stack).
|
||||
# (Older t2linux guidance used s2idle + pcie_ports=compat; deqrocks fork expects deep + auto + pm_async=off.)
|
||||
boot.kernelParams = [
|
||||
"mem_sleep_default=deep"
|
||||
"intel_iommu=on"
|
||||
"iommu=pt"
|
||||
"pcie_ports=auto"
|
||||
"pm_async=off"
|
||||
# https://wiki.t2linux.org/guides/hybrid-graphics/#enabling-the-igpu — helps some post-suspend black screens.
|
||||
"i915.enable_guc=3"
|
||||
];
|
||||
|
||||
# Hybrid T2 Macs: prefer Intel for display/GL (saves power; avoids broken AMDGPU after resume).
|
||||
# https://wiki.t2linux.org/guides/hybrid-graphics/
|
||||
boot.extraModprobeConfig = ''
|
||||
options apple-gmux force_igd=y
|
||||
'';
|
||||
|
||||
# If the AMD dGPU misbehaves after S3 (Electron apps won’t start, suspend breaks), keep it unloaded.
|
||||
# See https://github.com/deqrocks/apple-bce#notes-for-dgpu-models — drop this list if you need DRI_PRIME
|
||||
# or external displays wired through the dGPU.
|
||||
boot.blacklistedKernelModules = [ "amdgpu" ];
|
||||
|
||||
boot.kernelModules = [ "apple-bce" ];
|
||||
boot.initrd.availableKernelModules = [ "apple-bce" ];
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
{ lib, pkgs, inputs, config, ... }:
|
||||
let
|
||||
# Two-stage callPackage matches nixos-hardware apple/t2 generic.nix.
|
||||
linuxT2GenericFromDir =
|
||||
{ lib, ... }@args:
|
||||
{ kernel, t2linuxPatchesSrc }:
|
||||
let
|
||||
patchNames = lib.sort lib.lessThan (
|
||||
lib.filter (n: builtins.match "^[0-9]{4}-.*\\.patch$" n != null) (
|
||||
lib.attrNames (builtins.readDir t2linuxPatchesSrc)
|
||||
)
|
||||
);
|
||||
t2Patches = map
|
||||
(name: {
|
||||
inherit name;
|
||||
patch = "${t2linuxPatchesSrc}/${name}";
|
||||
})
|
||||
patchNames;
|
||||
in
|
||||
kernel.override (
|
||||
args
|
||||
// {
|
||||
pname = "linux-t2";
|
||||
structuredExtraConfig = with lib.kernel; {
|
||||
# In-tree staging driver disabled; we ship deqrocks/apple-bce via boot.extraModulePackages.
|
||||
APPLE_BCE = no;
|
||||
APPLE_GMUX = module;
|
||||
APFS_FS = module;
|
||||
BRCMFMAC = module;
|
||||
BT_BCM = module;
|
||||
BT_HCIBCM4377 = module;
|
||||
BT_HCIUART_BCM = yes;
|
||||
BT_HCIUART = module;
|
||||
HID_APPLETB_BL = module;
|
||||
HID_APPLETB_KBD = module;
|
||||
HID_APPLE = module;
|
||||
HID_MAGICMOUSE = module;
|
||||
DRM_APPLETBDRM = module;
|
||||
HID_SENSOR_ALS = module;
|
||||
SND_PCM = module;
|
||||
STAGING = yes;
|
||||
};
|
||||
kernelPatches = t2Patches ++ (args.kernelPatches or [ ]);
|
||||
argsOverride.extraMeta = {
|
||||
description = "The Linux kernel (with patches from the T2 Linux project)";
|
||||
maintainers = with lib.maintainers; [ soopyc ];
|
||||
};
|
||||
}
|
||||
// (args.argsOverride or { })
|
||||
);
|
||||
|
||||
# linux_6_19 was dropped from nixpkgs; t2linux/linux-t2-patches HEAD targets 7.x (see 76589a8).
|
||||
linuxT2Kernel = (pkgs.callPackage linuxT2GenericFromDir { }) {
|
||||
kernel = pkgs.linux_latest;
|
||||
t2linuxPatchesSrc = inputs.t2linux-patches;
|
||||
};
|
||||
in
|
||||
{
|
||||
boot.kernelPackages = lib.mkForce (pkgs.linuxPackagesFor linuxT2Kernel);
|
||||
|
||||
# https://github.com/deqrocks/apple-bce (branch no-state-suspend) — BCE + suspend-oriented fixes.
|
||||
# README also mentions companion userspace: t2-upower, t2-kbd-tb (not packaged here yet).
|
||||
boot.extraModulePackages = [
|
||||
(config.boot.kernelPackages.callPackage ./apple-bce.nix { })
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{ ... }: {
|
||||
flake.nixosModules.t2linux = {
|
||||
imports = [
|
||||
./_private/kernel.nix
|
||||
./_private/boot-tuning.nix
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{ lib, pkgs, ... }:
|
||||
let
|
||||
uconsole4gRuntime = with pkgs; [
|
||||
bash
|
||||
coreutils
|
||||
gnugrep
|
||||
gnused
|
||||
gawk
|
||||
util-linux
|
||||
usbutils
|
||||
pciutils
|
||||
lsb-release
|
||||
libgpiod
|
||||
modemmanager
|
||||
iproute2
|
||||
iputils
|
||||
busybox
|
||||
socat
|
||||
systemd
|
||||
];
|
||||
|
||||
# Use a lightweight wrapper to avoid ShellCheck gating the build.
|
||||
uconsole4gCm5 = pkgs.writeShellScriptBin "uconsole-4g-cm5" ''
|
||||
export PATH=${lib.makeBinPath uconsole4gRuntime}:$PATH
|
||||
exec ${pkgs.bash}/bin/bash ${./uconsole-4g-cm5.sh} "$@"
|
||||
'';
|
||||
in
|
||||
{
|
||||
networking.modemmanager.enable = true;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
libgpiod
|
||||
uconsole4gCm5
|
||||
socat
|
||||
ripgrep
|
||||
];
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
{ pkgs, ... }: {
|
||||
# Keep Raspberry Pi kernel cmdline in sync with current system profile.
|
||||
system.activationScripts.updateRpiCmdline.text = ''
|
||||
export PATH="${pkgs.gnused}/bin:''${PATH}"
|
||||
for cmdline in /boot/firmware/nixos/*/cmdline.txt; do
|
||||
[ -e "$cmdline" ] || continue
|
||||
current_init="$(readlink -f /nix/var/nix/profiles/system)/init"
|
||||
if grep -q 'init=/nix/store/' "$cmdline"; then
|
||||
sed -i "s#init=/nix/store/[^ ]*/init#init=$current_init#" "$cmdline"
|
||||
fi
|
||||
if ! grep -q 'root=' "$cmdline"; then
|
||||
sed -i 's/ init=/ root=PARTUUID=4d44c78a-ee3c-4e3e-9eee-0f2eb10347b6 rootfstype=ext4 init=/' "$cmdline"
|
||||
fi
|
||||
done
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
# cockpit-file-sharing expects a live Samba stack: /etc/samba/smb.conf, smbd, and
|
||||
# `include = registry` in [global] for net registry share management.
|
||||
services.samba = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
winbindd.enable = false;
|
||||
settings.global = {
|
||||
workgroup = "WORKGROUP";
|
||||
"server string" = config.networking.hostName;
|
||||
include = "registry";
|
||||
};
|
||||
};
|
||||
|
||||
services.nfs.server = {
|
||||
enable = true;
|
||||
mountdPort = 4000;
|
||||
lockdPort = 4001;
|
||||
statdPort = 4002;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
111
|
||||
2049
|
||||
4000
|
||||
4001
|
||||
4002
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
111
|
||||
2049
|
||||
4000
|
||||
4001
|
||||
4002
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
acl,
|
||||
bash,
|
||||
coreutils,
|
||||
dpkg,
|
||||
fetchurl,
|
||||
findutils,
|
||||
hostname,
|
||||
iproute2,
|
||||
jq,
|
||||
lib,
|
||||
nfs-utils,
|
||||
python3Packages,
|
||||
samba,
|
||||
stdenv,
|
||||
systemd,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "cockpit-file-sharing";
|
||||
version = "4.5.6-1";
|
||||
|
||||
src = fetchurl {
|
||||
url = "https://github.com/45Drives/cockpit-file-sharing/releases/download/v4.5.6-1/cockpit-file-sharing_4.5.6-1jammy_all.deb";
|
||||
hash = "sha256-ViTdhiCmqwuBvAfzT8hr2kqZqyWkV9OZ9FEPD10ajF8=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ dpkg ];
|
||||
|
||||
unpackPhase = "dpkg-deb -x $src source";
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out/share/cockpit
|
||||
cp -r source/usr/share/cockpit/file-sharing $out/share/cockpit/
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
passthru.cockpitPath = [
|
||||
acl
|
||||
bash
|
||||
coreutils
|
||||
findutils
|
||||
hostname
|
||||
iproute2
|
||||
jq
|
||||
nfs-utils
|
||||
python3Packages.botocore
|
||||
samba
|
||||
systemd
|
||||
];
|
||||
|
||||
meta = {
|
||||
description = "Cockpit plugin to manage Samba and NFS file sharing (45Drives)";
|
||||
homepage = "https://github.com/45Drives/cockpit-file-sharing";
|
||||
license = lib.licenses.gpl3Plus;
|
||||
platforms = lib.platforms.linux;
|
||||
sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
cockpitFileSharing = pkgs.callPackage ./cockpit-file-sharing/package.nix { };
|
||||
in
|
||||
{
|
||||
imports = [ ./cockpit-file-sharing-services.nix ];
|
||||
|
||||
services.cockpit = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
allowed-origins = [
|
||||
"https://${config.networking.hostName}:${toString config.services.cockpit.port}"
|
||||
"https://192.168.2.99:${toString config.services.cockpit.port}"
|
||||
];
|
||||
plugins = with pkgs; [
|
||||
cockpit-files
|
||||
cockpit-machines
|
||||
cockpit-podman
|
||||
cockpitFileSharing
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{ ... }: {
|
||||
#TODO[epic=Moderate] Clean this up, move to host's configuration.nix.
|
||||
# Native Raspberry Pi boot flow.
|
||||
boot.loader.raspberry-pi.bootloader = "kernel";
|
||||
boot.loader.grub.enable = false;
|
||||
boot.consoleLogLevel = 7;
|
||||
boot.kernelModules = [ "nvme" ];
|
||||
|
||||
# Root device for kernel cmdline (NVMe boot).
|
||||
boot.kernelParams = [
|
||||
"root=PARTUUID=4d44c78a-ee3c-4e3e-9eee-0f2eb10347b6"
|
||||
"rootfstype=ext4"
|
||||
];
|
||||
|
||||
console = {
|
||||
earlySetup = true;
|
||||
font = "ter-v32n";
|
||||
};
|
||||
|
||||
# Enable PCIe x1 for CM5 NVMe adapter.
|
||||
hardware.raspberry-pi.config.cm5.base-dt-params.pciex1 = {
|
||||
enable = true;
|
||||
value = "on";
|
||||
};
|
||||
|
||||
powerManagement.cpuFreqGovernor = "powersave";
|
||||
|
||||
# Root + firmware as boot-critical mounts.
|
||||
fileSystems."/".neededForBoot = true;
|
||||
fileSystems."/boot/firmware".neededForBoot = true;
|
||||
|
||||
# Pi generational boot expects boot-firmware.mount to exist.
|
||||
systemd.mounts = [
|
||||
{
|
||||
what = "/dev/disk/by-label/FIRMWARE";
|
||||
where = "/boot/firmware";
|
||||
type = "vfat";
|
||||
options = "noatime,fmask=0022,dmask=0022";
|
||||
wantedBy = [ "local-fs.target" ];
|
||||
after = [ "local-fs-pre.target" ];
|
||||
}
|
||||
];
|
||||
|
||||
# There is no serial console on uConsole CM5 by default.
|
||||
systemd.services."serial-getty@ttyS0".enable = false;
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# nixpkgs unstable dropped several Raspberry Pi attrs; nixos-raspberrypi modules still
|
||||
# reference them but ship derivations under pkgs/raspberrypi/.
|
||||
{ inputs, ... }: {
|
||||
nixpkgs.overlays = [
|
||||
(final: _prev:
|
||||
let
|
||||
rp = "${inputs.nixos-raspberrypi}/pkgs/raspberrypi";
|
||||
in
|
||||
{
|
||||
raspberrypi-utils = final.callPackage "${rp}/raspberrypi-utils.nix" { };
|
||||
raspberrypi-udev-rules = final.callPackage "${rp}/udev-rules.nix" { };
|
||||
})
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{ ... }: {
|
||||
#TODO[epic=Moderate] Clean this up, move to host's configuration.nix.
|
||||
# Enable uinput for gamepad remap.
|
||||
hardware.uinput.enable = true;
|
||||
programs.mosh.enable = true;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# Pi 5 / CM5 onboard Wi-Fi (brcmfmac). Without a regulatory domain the driver scans
|
||||
# disallowed channels and spams: brcmf_set_channel: set chanspec … fail, reason -52
|
||||
# (open raspberrypi/linux#6049). Wi-Fi often still works; logs and scans are noisy.
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
wifiCountry = "CA"; # change if you are not in Canada
|
||||
in
|
||||
{
|
||||
boot.kernelParams = [
|
||||
"cfg80211.ieee80211_regdom=${wifiCountry}"
|
||||
];
|
||||
|
||||
# Firmware country code (config.txt) — picked up before userspace regdom.
|
||||
hardware.raspberry-pi.extra-config = ''
|
||||
country=${wifiCountry}
|
||||
'';
|
||||
|
||||
# Apply regdom before NetworkManager starts scanning.
|
||||
systemd.services.wifi-regulatory-domain = {
|
||||
description = "Set Wi-Fi regulatory domain for brcmfmac";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "NetworkManager.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
${pkgs.iw}/bin/iw reg set ${wifiCountry}
|
||||
'';
|
||||
};
|
||||
|
||||
networking.networkmanager.wifi.powersave = false;
|
||||
networking.networkmanager.wifi.scanRandMacAddress = false;
|
||||
networking.networkmanager.connectionConfig."wifi.bgscan" = "0";
|
||||
}
|
||||
@@ -29,7 +29,10 @@
|
||||
|
||||
self.nixosModules."client-services"
|
||||
./_private/platform.nix
|
||||
./_private/raspberrypi-utils-overlay.nix
|
||||
./_private/wifi-brcmfmac.nix
|
||||
./_private/services.nix
|
||||
./_private/cockpit.nix
|
||||
./_private/activation.nix
|
||||
./_private/4g/default.nix
|
||||
];
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.nixosConfigurations.uConsole = inputs.nixos-raspberrypi.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
specialArgs = inputs // {
|
||||
inherit self;
|
||||
inputs = inputs;
|
||||
host = "uConsole";
|
||||
system = "aarch64-linux";
|
||||
};
|
||||
trustCaches = false;
|
||||
modules = [
|
||||
inputs.nixos-raspberrypi.nixosModules.raspberry-pi-5.base
|
||||
inputs.oom-hardware.nixosModules.uc.kernel
|
||||
inputs.oom-hardware.nixosModules.uc.configtxt
|
||||
inputs.oom-hardware.nixosModules.uc.base-cm5
|
||||
self.nixosModules.uConsoleConfiguration
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# Build a raw Navi hive attrset from host specs + deployment targets.
|
||||
# Call `inputs.navi.lib.makeHive` on the result to produce `flake.naviHive`.
|
||||
{ lib, inputs, ... }: {
|
||||
flake.lib.mkNaviHiveConfig =
|
||||
{
|
||||
metaNixpkgs,
|
||||
hostSpecs,
|
||||
deployments,
|
||||
}:
|
||||
let
|
||||
deployNodes = lib.filterAttrs (name: _: deployments ? ${name}) hostSpecs;
|
||||
in
|
||||
{
|
||||
meta = {
|
||||
nixpkgs = metaNixpkgs;
|
||||
nodeNixpkgs = lib.mapAttrs (
|
||||
name: spec:
|
||||
import inputs.nixpkgs {
|
||||
system = spec.system;
|
||||
}
|
||||
) deployNodes;
|
||||
nodeSpecialArgs = lib.mapAttrs (_: spec: spec.specialArgs) deployNodes;
|
||||
allowApplyAll = false;
|
||||
};
|
||||
}
|
||||
// lib.mapAttrs (
|
||||
name: spec:
|
||||
{
|
||||
imports = spec.modules or [ spec.configuration ];
|
||||
deployment = deployments.${name};
|
||||
}
|
||||
) deployNodes;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# Pi5 + DSI DRM KDL snippets (`desktop.niri.raspberryPi5DrmWorkaround`) — lives in `flake.lib`.
|
||||
{ ... }:
|
||||
let
|
||||
drmExtraConfig = ''
|
||||
debug {
|
||||
render-drm-device "/dev/dri/renderD128"
|
||||
ignore-drm-device "/dev/dri/card1"
|
||||
ignore-drm-device "/dev/dri/card2"
|
||||
}
|
||||
'';
|
||||
in
|
||||
{
|
||||
flake.lib.pi5NiriKdl = {
|
||||
inherit drmExtraConfig;
|
||||
|
||||
# Keep in sync with DMS greeter niri template when upstream edits it.
|
||||
dankGreeterCompositorConfig = ''
|
||||
hotkey-overlay {
|
||||
skip-at-startup
|
||||
}
|
||||
|
||||
environment {
|
||||
DMS_RUN_GREETER "1"
|
||||
}
|
||||
|
||||
${drmExtraConfig}
|
||||
|
||||
gestures {
|
||||
hot-corners {
|
||||
off
|
||||
}
|
||||
}
|
||||
|
||||
layout {
|
||||
background-color "#000000"
|
||||
}
|
||||
'';
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user