Compare commits

...

33 Commits

Author SHA1 Message Date
Alexander Heldt
7d6785f27e manatee: Remove calibre-web module 2026-03-07 12:10:30 +00:00
Alexander Heldt
f446706a9c manatee: nixfmt 2026-03-07 12:08:01 +00:00
Alexander Heldt
6ea5db6d37 manatee: Add komga-bookmanager service 2026-03-07 12:07:05 +00:00
Alexander Heldt
e46b5689a2 Add komga-bookmanager input 2026-03-07 12:05:41 +00:00
Alexander Heldt
8fa60a1b06 Update komga-comictracker input 2026-03-07 12:05:07 +00:00
Alexander Heldt
4f5de3b4cc manatee: Expose komga publicly via komga.ppp.pm 2026-03-02 21:41:41 +01:00
Alexander Heldt
65569f6b57 manatee: Add services homepage with auto-discovery
Add a homepage module that generates a static landing page served on
port 9999 via nginx. Each service module registers itself via the shared
mod.homepage.services option, so enabling a module automatically adds
it to the page.
2026-02-28 22:03:12 +01:00
Alexander Heldt
f2130be7e4 backwards: Continued attempt to fix audio output 2026-02-17 21:35:51 +01:00
Alexander Heldt
efaa92d4a1 manatee: Add web based reader for komga 2026-02-17 20:25:11 +00:00
Alexander Heldt
5ab63a0880 manatee: Add bluetooth reset handling 2026-02-15 15:33:02 +01:00
Alexander Heldt
d614186ef9 backwards: Use NetworkManager for wifi 2026-02-15 15:10:48 +01:00
Alexander Heldt
aefe89f648 backwards: Try to fix audio output 2026-02-15 15:10:19 +01:00
Alexander Heldt
2a1c8038ba Update komga-comictracker input 2026-02-14 15:36:44 +01:00
Alexander Heldt
9fd7c65b44 manatee: Update komga-comictracker to not use notifications 2026-02-14 11:38:44 +01:00
Alexander Heldt
e78f603717 Update komga-comictracker input 2026-02-14 11:37:56 +01:00
Alexander Heldt
e5c9fcea33 manatee: Fix komga-comicbooktracker credentials file permissions 2026-02-14 11:18:51 +01:00
Alexander Heldt
1dbcb4439a pinwheel: Fix eglot code action indicator in emacs 2026-02-14 11:08:52 +01:00
Alexander Heldt
b26fb44824 pinwheel: Unpin nh from local nixpkgs to use cached builds 2026-02-14 11:08:44 +01:00
Alexander Heldt
48e352f015 pinwheel: Disable use of ssh defaults (as they're inlined) 2026-02-14 11:05:51 +01:00
Alexander Heldt
e575a147cd pinwheel: Fix pants use of system 2026-02-14 11:05:51 +01:00
Alexander Heldt
7653bfce22 manatee: Add komga-comicbooktracker 2026-02-14 10:54:01 +01:00
Alexander Heldt
78f0afe517 pinwheel: Fix window focus in hyprland 2026-02-14 10:38:48 +01:00
Alexander Heldt
c19b59f88e Use pkgs.nixfmt instead of pkgs.nixfmt-rfc-style 2026-02-14 10:38:48 +01:00
Alexander Heldt
53fad59691 pinwheel: Install copilot in intellij 2026-02-14 10:38:48 +01:00
Alexander Heldt
3ba141ce36 pinwheel: Remove trackpad from battery monitor 2026-02-14 10:12:15 +01:00
Alexander Heldt
29094de29e pinwheel: Use flat mouse acceleration profile 2026-02-14 10:12:15 +01:00
Alexander Heldt
44a7bb72ef pinwheel: Add dynamic hyprland monitor hotplug handler 2026-02-14 10:12:13 +01:00
Alexander Heldt
2537692f24 pinwheel: Simplify whib development 2026-02-14 10:10:34 +01:00
Alexander Heldt
f8c060a0cc pinwheel: Fix openvpn config 2026-02-14 10:10:10 +01:00
Alexander Heldt
cc6069e854 Update flake inputs 2026-02-14 10:06:39 +01:00
Alexander Heldt
1d180a623a manatee: Add OpenCL hardware support
To improve jellyfin transcoding
2026-02-08 19:12:51 +00:00
Alexander Heldt
a0bdcc0444 manatee: Add swapfile 2026-02-08 19:12:32 +00:00
Alexander Heldt
0ecef88132 Update flake inputs 2026-02-08 19:12:23 +00:00
32 changed files with 2286 additions and 241 deletions

208
flake.lock generated
View File

@@ -10,11 +10,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1762618334,
"narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=",
"lastModified": 1770165109,
"narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=",
"owner": "ryantm",
"repo": "agenix",
"rev": "fcdea223397448d35d9b31f798479227e80183f6",
"rev": "b027ee29d959fda4b60b57566d64c98a202e0feb",
"type": "github"
},
"original": {
@@ -43,11 +43,11 @@
]
},
"locked": {
"lastModified": 1767024902,
"narHash": "sha256-sMdk6QkMDhIOnvULXKUM8WW8iyi551SWw2i6KQHbrrU=",
"lastModified": 1770895474,
"narHash": "sha256-JBcrq1Y0uw87VZdYsByVbv+GBuT6ECaCNb9txLX9UuU=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "b8a0c5ba5a9fbd2c660be7dd98bdde0ff3798556",
"rev": "a494d50d32b5567956b558437ceaa58a380712f7",
"type": "github"
},
"original": {
@@ -85,11 +85,11 @@
]
},
"locked": {
"lastModified": 1766150702,
"narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=",
"lastModified": 1769524058,
"narHash": "sha256-zygdD6X1PcVNR2PsyK4ptzrVEiAdbMqLos7utrMDEWE=",
"owner": "nix-community",
"repo": "disko",
"rev": "916506443ecd0d0b4a0f4cf9d40a3c22ce39b378",
"rev": "71a3fc97d80881e91710fe721f1158d3b96ae14d",
"type": "github"
},
"original": {
@@ -106,11 +106,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1767777451,
"narHash": "sha256-rWTDh+NZl2hTXmfHPaIpRwSRlhbLHY8UaXTCU5zwzDk=",
"lastModified": 1771057865,
"narHash": "sha256-Suny75DfQS3Mqd8ihiDZNsRs4CfRC3GiQDMmaFPj2qM=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "32dceb02d7b008d127988876a48c5d471179e8e6",
"rev": "56690eb79372e49054b0c2b87780fe3f86be7616",
"type": "github"
},
"original": {
@@ -138,11 +138,11 @@
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1765121682,
"narHash": "sha256-4VBOP18BFeiPkyhy9o4ssBNQEvfvv1kXkasAYd0+rrA=",
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "65f23138d8d09a92e30f1e5c87611b23ef451bf3",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
@@ -169,27 +169,6 @@
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": [
"nix-jetbrains-plugins",
"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"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
@@ -240,11 +219,11 @@
]
},
"locked": {
"lastModified": 1767792169,
"narHash": "sha256-WSAu+ZxF697u/OJDdBLO+YFhtqFsPowrXXOQbjDT/uA=",
"lastModified": 1771037579,
"narHash": "sha256-NX5XuhGcsmk0oEII2PEtMRgvh2KaAv3/WWQsOpxAgR4=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "aea57993a89bfc2a66c0434e0f4383ebf164e2a3",
"rev": "05e6dc0f6ed936f918cb6f0f21f1dad1e4c53150",
"type": "github"
},
"original": {
@@ -298,11 +277,11 @@
]
},
"locked": {
"lastModified": 1766946335,
"narHash": "sha256-MRD+Jr2bY11MzNDfenENhiK6pvN+nHygxdHoHbZ1HtE=",
"lastModified": 1770511807,
"narHash": "sha256-suKmSbSk34uPOJDTg/GbPrKEJutzK08vj0VoTvAFBCA=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "4af02a3925b454deb1c36603843da528b67ded6c",
"rev": "7c75487edd43a71b61adb01cae8326d277aab683",
"type": "github"
},
"original": {
@@ -330,11 +309,11 @@
"xdph": "xdph"
},
"locked": {
"lastModified": 1767907620,
"narHash": "sha256-zpQr4jkAoARBI22dFDnRekUagdRt6Mfc+ThpSSHm90s=",
"lastModified": 1771026735,
"narHash": "sha256-vIzZ1Pb1o9xOXwGTdr09vRwGFgD/II6ZnGpnCfl8zz8=",
"owner": "hyprwm",
"repo": "Hyprland",
"rev": "5b1b79c29c5e0ea974b2a9da5d122dd0f3bedca6",
"rev": "e80f705d76d4dbe836e0f57aadea994a624ac63e",
"type": "github"
},
"original": {
@@ -350,11 +329,11 @@
]
},
"locked": {
"lastModified": 1766066098,
"narHash": "sha256-d3HmUbmfTDIt9mXEHszqyo2byqQMoyJtUJCZ9U1IqHQ=",
"lastModified": 1770889987,
"narHash": "sha256-JPbwE1Yg5t9zrMcOyDQwl1Pd8e0J1dtZf3kGnC7udwQ=",
"owner": "hyprwm",
"repo": "contrib",
"rev": "41dbcac8183bb1b3a4ade0d8276b2f2df6ae4690",
"rev": "8fdd69edefed16c9ab395b433a1c3323d5633f30",
"type": "github"
},
"original": {
@@ -450,11 +429,11 @@
]
},
"locked": {
"lastModified": 1764612430,
"narHash": "sha256-54ltTSbI6W+qYGMchAgCR6QnC1kOdKXN6X6pJhOWxFg=",
"lastModified": 1767983607,
"narHash": "sha256-8C2co8NYfR4oMOUEsPROOJ9JHrv9/ktbJJ6X1WsTbXc=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "0d00dc118981531aa731150b6ea551ef037acddd",
"rev": "d4037379e6057246b408bbcf796cf3e9838af5b2",
"type": "github"
},
"original": {
@@ -527,11 +506,11 @@
]
},
"locked": {
"lastModified": 1766253372,
"narHash": "sha256-1+p4Kw8HdtMoFSmJtfdwjxM4bPxDK9yg27SlvUMpzWA=",
"lastModified": 1770139857,
"narHash": "sha256-bCqxcXjavgz5KBJ/1CBLqnagMMf9JvU1m9HmYVASKoc=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "51a4f93ce8572e7b12b7284eb9e6e8ebf16b4be9",
"rev": "9038eec033843c289b06b83557a381a2648d8fa5",
"type": "github"
},
"original": {
@@ -552,11 +531,11 @@
]
},
"locked": {
"lastModified": 1763640274,
"narHash": "sha256-Uan1Nl9i4TF/kyFoHnTq1bd/rsWh4GAK/9/jDqLbY5A=",
"lastModified": 1770501770,
"narHash": "sha256-NWRM6+YxTRv+bT9yvlhhJ2iLae1B1pNH3mAL5wi2rlQ=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "f6cf414ca0e16a4d30198fd670ec86df3c89f671",
"rev": "0bd8b6cde9ec27d48aad9e5b4deefb3746909d40",
"type": "github"
},
"original": {
@@ -581,11 +560,11 @@
]
},
"locked": {
"lastModified": 1767473322,
"narHash": "sha256-RGOeG+wQHeJ6BKcsSB8r0ZU77g9mDvoQzoTKj2dFHwA=",
"lastModified": 1770203293,
"narHash": "sha256-PR/KER+yiHabFC/h1Wjb+9fR2Uy0lWM3Qld7jPVaWkk=",
"owner": "hyprwm",
"repo": "hyprwire",
"rev": "d5e7d6b49fe780353c1cf9a1cf39fa8970bd9d11",
"rev": "37bc90eed02b0c8b5a77a0b00867baf3005cfb98",
"type": "github"
},
"original": {
@@ -594,6 +573,48 @@
"type": "github"
}
},
"komga-bookmanager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1772572455,
"narHash": "sha256-B4KOdV2GQogU1XL9903qFkOFf/6H7UGa+dprNGctgMY=",
"ref": "main",
"rev": "002732d4ebc5b80c2b8522896427c9f6bee9e1d1",
"revCount": 6,
"type": "git",
"url": "ssh://gitea@git.ppp.pm:1122/alex/komga-bookmanager.git"
},
"original": {
"ref": "main",
"type": "git",
"url": "ssh://gitea@git.ppp.pm:1122/alex/komga-bookmanager.git"
}
},
"komga-comictracker": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1772884418,
"narHash": "sha256-2V/JnGsIhvQoAiH7iIQkOPX5OTZVwGTOEv+nFetd4I8=",
"ref": "main",
"rev": "0b91385e8906ca40cc89381bf8394ed24a116c6b",
"revCount": 49,
"type": "git",
"url": "ssh://gitea@git.ppp.pm:1122/alex/komga-comictracker.git"
},
"original": {
"ref": "main",
"type": "git",
"url": "ssh://gitea@git.ppp.pm:1122/alex/komga-comictracker.git"
}
},
"naviterm": {
"inputs": {
"flake-utils": "flake-utils",
@@ -602,11 +623,11 @@
]
},
"locked": {
"lastModified": 1757496832,
"narHash": "sha256-R5EMcms24G6QGk62iNAMApeZmKsHwCDLj68UUdkhSLw=",
"lastModified": 1770485744,
"narHash": "sha256-ZJGHKtEWhpXyONjy6/popJz0uL2MXmknGr9Vr1B6BAk=",
"owner": "detoxify92",
"repo": "naviterm",
"rev": "3b3bd2bace3676000f530b2f47fa28f431c56761",
"rev": "b311af7ae0ab095657264b5fe4a43b9118207584",
"type": "gitlab"
},
"original": {
@@ -617,16 +638,14 @@
},
"nh": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1767773550,
"narHash": "sha256-8VCfAbKKj+OHD5Mz5TBB7mE/zWe/5MyFTbXEayI0WG8=",
"lastModified": 1770930949,
"narHash": "sha256-ulX/6yOI9fmiiDqNjn59oLsVkABRo0M2Wkr2w8Ric64=",
"owner": "viperML",
"repo": "nh",
"rev": "5f279c597e6e2af1757e0cd0b071aeb29d3e85a5",
"rev": "b37b9a6ca31741282873dd8e335f66aad54f6326",
"type": "github"
},
"original": {
@@ -653,18 +672,17 @@
"nix-jetbrains-plugins": {
"inputs": {
"flake-compat": "flake-compat_2",
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
],
"systems": "systems_4"
},
"locked": {
"lastModified": 1767434185,
"narHash": "sha256-S289tJM4HQQStEF9QMCtS93duiYwfecWy/zhBoEb890=",
"lastModified": 1771059595,
"narHash": "sha256-avvMQpGX/erjzPI10bN5uHDD08er5QC+pBypQisUNHs=",
"owner": "nix-community",
"repo": "nix-jetbrains-plugins",
"rev": "d3b3c5d901ce5980dec75f54f2ca6446f51b3451",
"rev": "fb4b27d169c7fbfc92c9dfec971e9c87506cb8f0",
"type": "github"
},
"original": {
@@ -675,11 +693,11 @@
},
"nixos-hardware": {
"locked": {
"lastModified": 1767185284,
"narHash": "sha256-ljDBUDpD1Cg5n3mJI81Hz5qeZAwCGxon4kQW3Ho3+6Q=",
"lastModified": 1770882871,
"narHash": "sha256-nw5g+xl3veea+maxJ2/81tMEA/rPq9aF1H5XF35X+OE=",
"owner": "nixos",
"repo": "nixos-hardware",
"rev": "40b1a28dce561bea34858287fbb23052c3ee63fe",
"rev": "af04cb78aa85b2a4d1c15fc7270347e0d0eda97b",
"type": "github"
},
"original": {
@@ -691,16 +709,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1767640445,
"narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=",
"owner": "nixos",
"lastModified": 1770770419,
"narHash": "sha256-iKZMkr6Cm9JzWlRYW/VPoL0A9jVKtZYiU4zSrVeetIs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5",
"rev": "6c5e707c6b5339359a9a9e215c5e66d6d802fd7a",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"owner": "NixOS",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
@@ -721,6 +739,22 @@
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1771008912,
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pppdotpm-site": {
"inputs": {
"nixpkgs": [
@@ -752,11 +786,11 @@
]
},
"locked": {
"lastModified": 1767281941,
"narHash": "sha256-6MkqajPICgugsuZ92OMoQcgSHnD6sJHwk8AxvMcIgTE=",
"lastModified": 1770726378,
"narHash": "sha256-kck+vIbGOaM/dHea7aTBxdFYpeUl/jHOy5W3eyRvVx8=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "f0927703b7b1c8d97511c4116eb9b4ec6645a0fa",
"rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae",
"type": "github"
},
"original": {
@@ -773,12 +807,14 @@
"home-manager": "home-manager_2",
"hyprland": "hyprland",
"hyprland-contrib": "hyprland-contrib",
"komga-bookmanager": "komga-bookmanager",
"komga-comictracker": "komga-comictracker",
"naviterm": "naviterm",
"nh": "nh",
"nix-gc-env": "nix-gc-env",
"nix-jetbrains-plugins": "nix-jetbrains-plugins",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs",
"nixpkgs": "nixpkgs_2",
"pppdotpm-site": "pppdotpm-site",
"whib-backend": "whib-backend",
"whib-frontend": "whib-frontend"

View File

@@ -13,7 +13,6 @@
nh = {
url = "github:viperML/nh";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-gc-env.url = "github:Julow/nix-gc-env";
@@ -60,11 +59,25 @@
whib-backend = {
url = "git+ssh://gitea@git.ppp.pm:1122/alex/whib.git?ref=master";
# url = "path:/home/alex/code/own/whib";
inputs.nixpkgs.follows = "nixpkgs";
};
whib-frontend = {
url = "git+ssh://gitea@git.ppp.pm:1122/alex/whib-react.git?ref=master";
# url = "path:/home/alex/code/own/whib-react";
inputs.nixpkgs.follows = "nixpkgs";
};
komga-comictracker = {
url = "git+ssh://gitea@git.ppp.pm:1122/alex/komga-comictracker.git?ref=main";
# url = "path:/home/alex/code/own/komga-comictracker";
inputs.nixpkgs.follows = "nixpkgs";
};
komga-bookmanager = {
url = "git+ssh://gitea@git.ppp.pm:1122/alex/komga-bookmanager.git?ref=main";
# url = "path:/home/alex/code/own/komga-comictracker";
inputs.nixpkgs.follows = "nixpkgs";
};
};
@@ -148,7 +161,7 @@
in
{
${system}.default = pkgs.mkShell {
packages = [ pkgs.nixfmt-rfc-style ];
packages = [ pkgs.nixfmt ];
};
};
};

View File

@@ -22,6 +22,13 @@
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
extraConfig.pipewire."90-hdmi-fix" = {
"context.properties" = {
"default.clock.rate" = 48000;
"default.clock.allowed-rates" = [ 48000 ];
};
};
};
hardware = {

View File

@@ -38,6 +38,11 @@ in
efi.canTouchEfiVariables = true;
};
extraModprobeConfig = ''
options snd-intel-dspcfg dsp_driver=1
options snd_hda_intel power_save=0 power_save_controller=N
'';
};
};
}

View File

@@ -3,33 +3,40 @@
networking = {
hostName = "backwards";
networkmanager.enable = false;
wireless.enable = false;
#wireless.networks are defined in the secret `wpa_supplicant.conf`
wireless = {
networkmanager = {
enable = true;
secretsFile = config.age.secrets.wireless-network-secrets.path;
wifi.backend = "iwd";
networks = {
"w1-f1_5G" = {
pskRaw = "ext:w1-f1_psk";
};
};
};
ensureProfiles = {
environmentFiles = [
config.age.secrets.wireless-network-secrets.path
];
defaultGateway = "192.168.50.1";
nameservers = [ "1.1.1.1" ];
interfaces = {
wlp1s0 = {
useDHCP = false;
ipv4 = {
addresses = [
{
address = "192.168.50.202";
prefixLength = 24;
}
];
profiles = {
w1-f1_5G = {
connection = {
id = "w1-f1_5G";
type = "wifi";
interface-name = "wlp1s0";
};
wifi = {
ssid = "w1-f1_5G";
mode = "infrastructure";
};
wifi-security = {
key-mgmt = "wpa-psk";
psk = "$w1_f1_psk";
};
ipv4 = {
method = "manual";
addresses = "192.168.50.202/24";
gateway = "192.168.50.1";
dns = "1.1.1.1";
};
};
};
};
};

View File

@@ -26,6 +26,13 @@
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
swapDevices = [
{
device = "/swapfile";
size = 32 * 1024; # 32GB
}
];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction

View File

@@ -1,4 +1,4 @@
{ inputs, ... }:
{ inputs, pkgs, ... }:
{
imports = [ inputs.home-manager.nixosModules.home-manager ];
@@ -13,7 +13,9 @@
home.username = "alex";
home.homeDirectory = "/home/alex";
home.packages = [ ];
home.packages = [
pkgs.streamrip
];
home.stateVersion = "24.11";
};

View File

@@ -10,6 +10,14 @@ in
};
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Audiobookshelf";
port = 8000;
description = "Audiobooks & podcasts";
}
];
users.users.audiobookshelf = {
isSystemUser = true;
description = "audiobookshelf";

View File

@@ -1,34 +0,0 @@
{ lib, config, ... }:
let
enabled = config.mod.calibre-web.enable;
in
{
options = {
mod.calibre-web = {
enable = lib.mkEnableOption "add calibre-web module";
};
};
config = lib.mkIf enabled {
services = {
calibre-web = {
enable = true;
user = "storage";
group = "storage";
listen = {
ip = "0.0.0.0";
port = 8083;
};
dataDir = "/mnt/media/public/books";
options = {
calibreLibrary = "/mnt/media/public/books";
enableBookUploading = true;
};
};
};
};
}

View File

@@ -20,6 +20,19 @@
"--http-timeout=60"
];
};
"komga.ppp.pm" = {
dnsProvider = "hetzner";
environmentFile = config.age.secrets.hetzner-dns.path;
group = "nginx";
extraLegoFlags = [
"--dns.resolvers=1.1.1.1:53,8.8.8.8:53"
"--dns.propagation-wait=60s"
"--dns-timeout=60"
"--http-timeout=60"
];
};
};
};

View File

@@ -16,12 +16,12 @@ in
nginx.enable = true;
syncthing.enable = true;
transmission.enable = true;
calibre-web.enable = true;
audiobookshelf.enable = true;
jellyfin.enable = true;
immich.enable = true;
navidrome.enable = true;
komga.enable = true;
homepage.enable = true;
};
};
}

View File

@@ -6,8 +6,70 @@
}:
let
nginxEnabled = config.mod.nginx.enable;
script = pkgs.writeShellScript "bt-reset" ''
set -euo pipefail
export PATH="${
lib.makeBinPath [
pkgs.bluez
pkgs.util-linux
pkgs.kmod
pkgs.gnugrep
pkgs.coreutils
]
}"
logger -t bt-reset "Starting Bluetooth adapter reset..."
# Exit early if the adapter is already present and running
if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
logger -t bt-reset "hci0 is already UP RUNNING nothing to do"
exit 0
fi
# If hci0 exists but isn't UP, try bringing it up
if hciconfig hci0 2>/dev/null; then
logger -t bt-reset "hci0 exists but not running bringing it up"
hciconfig hci0 up || true
sleep 2
if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
logger -t bt-reset "hci0 is UP now"
systemctl restart bluetooth.service
logger -t bt-reset "bluetooth.service restarted done"
exit 0
fi
fi
# Hard reset: reload the btusb kernel module (works for USB adapters)
logger -t bt-reset "hci0 missing reloading btusb module..."
modprobe -r btusb 2>/dev/null || true
sleep 3
modprobe btusb
sleep 3
if hciconfig hci0 2>/dev/null; then
hciconfig hci0 up
logger -t bt-reset "hci0 restored after module reload"
else
logger -t bt-reset "ERROR: hci0 not found after module reload"
exit 1
fi
# Restart the bluetooth systemd service so bluetoothd picks up the adapter
systemctl restart bluetooth.service
logger -t bt-reset "bluetooth.service restarted done"
'';
in
{
mod.homepage.services = [
{
name = "Home Assistant";
port = 8123;
description = "Home automation";
}
];
hardware.bluetooth.enable = true;
virtualisation.oci-containers = {
@@ -57,71 +119,114 @@ in
};
};
};
# Trigger reset via udev when hci0 disappears
udev.extraRules = ''
ACTION=="remove", SUBSYSTEM=="bluetooth", KERNEL=="hci0", \
TAG+="systemd", ENV{SYSTEMD_WANTS}+="bt-reset.service"
'';
};
systemd.user = {
timers = {
"update-hetzner-ha-dns" = {
unitConfig = {
Description = "updates Hetzner DNS for home-assistant";
};
systemd = {
services = {
# Trigger reset on bluetoothd failure
bluetooth = {
unitConfig.OnFailure = [ "bt-reset.service" ];
};
timerConfig = {
Unit = "update-hetzner-ha-dns.service";
OnCalendar = "*-*-* *:00/30:00";
Persistent = true;
};
bt-reset = {
description = "Reset Bluetooth adapter";
after = [ "bluetooth.service" ];
wantedBy = [ "timers.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = script;
Restart = "on-failure";
RestartSec = "10s";
StartLimitIntervalSec = "120";
StartLimitBurst = 3;
};
};
};
services = {
"update-hetzner-ha-dns" = {
unitConfig = {
Description = "updates Hetzner DNS for home-assistant";
timers.bt-reset = {
description = "Periodically reset Bluetooth adapter";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "5min"; # first run 5 min after boot
OnUnitActiveSec = "4h"; # then every 4 hours
RandomizedDelaySec = "5min";
};
};
user = {
timers = {
"update-hetzner-dns" = {
unitConfig = {
Description = "updates Hetzner DNS records";
};
timerConfig = {
Unit = "update-hetzner-dns.service";
OnCalendar = "*-*-* *:00/30:00";
Persistent = true;
};
wantedBy = [ "timers.target" ];
};
};
serviceConfig = {
Type = "exec";
EnvironmentFile = config.age.secrets.hetzner-dns.path;
services = {
"update-hetzner-dns" = {
unitConfig = {
Description = "updates Hetzner DNS records";
};
serviceConfig = {
Type = "exec";
EnvironmentFile = config.age.secrets.hetzner-dns.path;
};
path = [
pkgs.curl
pkgs.coreutils
pkgs.jq
];
script = ''
SUBDOMAINS="ha komga"
INTERFACE="enp3s0"
CURRENT_IP=$(curl -s --fail --interface "$INTERFACE" ifconfig.me)
for SUBDOMAIN in $SUBDOMAINS; do
LAST_IP_FILE="/tmp/hetzner-dns-''${SUBDOMAIN}-ip"
LAST_IP=""
if [[ -f "$LAST_IP_FILE" ]]; then
LAST_IP=$(cat "$LAST_IP_FILE")
fi
if [[ "$CURRENT_IP" == "$LAST_IP" ]]; then
echo "$SUBDOMAIN: IP unchanged, NOOP update."
else
echo "$SUBDOMAIN: Updating IP"
JSON_BODY=$(jq -n --arg ip "$CURRENT_IP" '{records: [{value: $ip}]}')
curl \
--fail \
-X POST \
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$JSON_BODY" \
"https://api.hetzner.cloud/v1/zones/ppp.pm/rrsets/''${SUBDOMAIN}/A/actions/set_records" \
&& echo $CURRENT_IP > $LAST_IP_FILE
fi
done
'';
};
path = [
pkgs.curl
pkgs.coreutils # For `cat`
pkgs.jq
];
script = ''
LAST_IP_FILE="/tmp/hetzner-dns-ha-ip"
INTERFACE="enp3s0"
CURRENT_IP=$(curl -s --fail --interface "$INTERFACE" ifconfig.me)
LAST_IP=""
if [[ -f "$LAST_IP_FILE" ]]; then
LAST_IP=$(cat "$LAST_IP_FILE")
fi
if [[ "$CURRENT_IP" == "$LAST_IP" ]]; then
echo "IP unchanged, NOOP update."
exit 0
else
echo "Updating IP"
JSON_BODY=$(jq -n --arg ip "$CURRENT_IP" '{records: [{value: $ip}]}')
curl \
--fail \
-X POST \
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$JSON_BODY" \
"https://api.hetzner.cloud/v1/zones/ppp.pm/rrsets/ha/A/actions/set_records" \
&& echo $CURRENT_IP > $LAST_IP_FILE
fi
'';
};
};
};

View File

@@ -0,0 +1,111 @@
{
pkgs,
lib,
config,
...
}:
let
enabled = config.mod.homepage.enable;
nginxEnabled = config.mod.nginx.enable;
services = config.mod.homepage.services;
serviceToCard = svc: ''
<a class="card" href="http://manatee:${toString svc.port}">
<div class="name">${svc.name}</div>
<div class="desc">${svc.description}</div>
<div class="port">:${toString svc.port}</div>
</a>
'';
page = pkgs.writeTextDir "index.html" ''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>manatee</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #1a1b26;
color: #c0caf5;
min-height: 100vh;
padding: 3rem 1.5rem;
}
h1 {
text-align: center;
font-size: 1.5rem;
font-weight: 400;
color: #7aa2f7;
margin-bottom: 2rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
max-width: 900px;
margin: 0 auto;
}
.card {
display: block;
background: #24283b;
border: 1px solid #414868;
border-radius: 8px;
padding: 1.25rem;
text-decoration: none;
color: inherit;
transition: border-color 0.15s;
}
.card:hover { border-color: #7aa2f7; }
.name { font-size: 1.1rem; font-weight: 600; color: #c0caf5; }
.desc { font-size: 0.85rem; color: #565f89; margin-top: 0.35rem; }
.port { font-size: 0.8rem; color: #414868; margin-top: 0.5rem; font-family: monospace; }
</style>
</head>
<body>
<h1>manatee</h1>
<div class="grid">
${lib.concatMapStrings serviceToCard services}
</div>
</body>
</html>
'';
in
{
options = {
mod.homepage = {
enable = lib.mkEnableOption "Enable homepage module";
services = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
name = lib.mkOption { type = lib.types.str; };
port = lib.mkOption { type = lib.types.port; };
description = lib.mkOption { type = lib.types.str; };
};
}
);
default = [ ];
description = "Services to display on the homepage";
};
};
};
config = lib.mkIf (enabled && nginxEnabled) {
services.nginx.virtualHosts."homepage" = {
listen = [
{
addr = "0.0.0.0";
port = 9999;
}
];
root = page;
locations."/" = {
index = "index.html";
};
};
networking.firewall.allowedTCPPorts = [ 9999 ];
};
}

View File

@@ -10,6 +10,14 @@ in
};
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Immich";
port = 2283;
description = "Photo library";
}
];
users.users.immich = {
isSystemUser = true;
group = "storage";

View File

@@ -31,6 +31,7 @@ in
extraPackages = [
pkgs.intel-media-driver # Modern Intel VA-API driver (needed for N305)
pkgs.libvdpau-va-gl # VDPAU backend for VA-API GLX interop
pkgs.intel-compute-runtime # OpenCL support
];
};
@@ -46,6 +47,14 @@ in
};
};
mod.homepage.services = [
{
name = "Jellyfin";
port = 8096;
description = "Media streaming";
}
];
networking = {
firewall.allowedTCPPorts = [ 8096 ];
};

View File

@@ -1,6 +1,13 @@
{ lib, config, ... }:
{
inputs,
pkgs,
lib,
config,
...
}:
let
enabled = config.mod.komga.enable;
nginxEnabled = config.mod.nginx.enable;
in
{
options = {
@@ -9,7 +16,30 @@ in
};
};
imports = [
inputs.komga-comictracker.nixosModules.default
inputs.komga-bookmanager.nixosModules.default
];
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Komga";
port = 8002;
description = "Comic library";
}
{
name = "Komga Reader";
port = 8888;
description = "Comic reader";
}
{
name = "Komga Book Manager";
port = 8686;
description = "Book manager";
}
];
users.users.komga = {
isSystemUser = true;
group = "storage";
@@ -21,8 +51,75 @@ in
user = "komga";
group = "storage";
settings.server.port = 8002;
settings = {
server.port = 8002;
komga."cors.allowed-origins" = [
"http://manatee:8888"
"https://komga.ppp.pm"
];
};
openFirewall = true;
};
services.nginx = lib.mkIf nginxEnabled {
virtualHosts."komga-reader" = {
listen = [
{
addr = "0.0.0.0";
port = 8888;
}
];
root = (pkgs.writeTextDir "komga-reader.html" (builtins.readFile ./komga-reader.html));
locations."/" = {
index = "komga-reader.html";
tryFiles = "$uri $uri/ /komga-reader.html";
};
};
virtualHosts."komga.ppp.pm" = {
forceSSL = true;
useACMEHost = "komga.ppp.pm";
locations."/" = {
proxyPass = "http://127.0.0.1:8002";
proxyWebsockets = true;
};
};
};
networking.firewall.allowedTCPPorts = [ 8888 ];
programs.comictracker = {
enable = true;
komgaUrl = "http://127.0.0.1:8002";
komgaLibraryId = "0NVZH5AK3RPE1";
secretsFile = config.age.secrets.komga-comicbooktracker-credentials.path;
};
services.komga-book-manager = {
enable = true;
port = 8686;
group = "storage";
komgaUrl = "http://127.0.0.1:8002";
credentialsFile = config.age.secrets.komga-bookmanager-credentials.path;
libraryRoot = "/mnt/media/public/books";
libraryId = "0PNE1NEPY6995";
};
age.secrets = {
"komga-comicbooktracker-credentials" = {
file = ../../../../secrets/manatee/komga-comicbooktracker-credentials.age;
owner = "alex";
group = "users";
};
"komga-bookmanager-credentials" = {
file = ../../../../secrets/manatee/komga-bookmanager-credentials.age;
owner = "alex";
group = "users";
};
};
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,14 @@ in
};
config = {
mod.homepage.services = lib.mkIf navidromeEnabled [
{
name = "Navidrome";
port = 4533;
description = "Music streaming";
}
];
services = lib.mkIf navidromeEnabled {
navidrome = {
enable = true;

View File

@@ -10,6 +10,14 @@ in
};
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Syncthing";
port = 8384;
description = "File sync";
}
];
services.syncthing = {
enable = true;

View File

@@ -15,6 +15,14 @@ in
};
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Transmission";
port = 9091;
description = "Torrent client";
}
];
services = {
transmission = {
enable = true;

View File

@@ -30,12 +30,6 @@ in
# Low battery notification for bluetooth devices
systemd.user =
let
trackpad = {
id = "battery_hid_a8o91o3doe5ofeo38_battery";
name = "trackpad";
threshold = "20";
};
headphones = {
id = "headset_dev_38_18_4C_18_A4_6E";
name = "headphones";
@@ -65,7 +59,6 @@ in
in
builtins.listToAttrs (
builtins.map mkTimer [
trackpad
headphones
]
);
@@ -119,7 +112,6 @@ in
in
builtins.listToAttrs (
builtins.map mkService [
trackpad
headphones
]
);

View File

@@ -555,6 +555,9 @@ Setup prefix for keybindings.
)
)
:custom
(eglot-code-action-indications nil)
:hook (
(eglot-managed-mode . mp-eglot-eldoc)
(go-mode . eglot-ensure)

View File

@@ -7,6 +7,59 @@
}:
let
enabled = config.mod.hyprland.enable;
monitorScript = pkgs.writeShellScript "hyprland-monitor-handler" ''
INTERNAL="eDP-1"
EXTERNAL_MONITORS="HDMI-A-1 DP-3"
HYPRCTL="${pkgs.hyprland}/bin/hyprctl"
JQ="${pkgs.jq}/bin/jq"
get_active_external() {
# Return the first connected external monitor
for mon in $EXTERNAL_MONITORS; do
if $HYPRCTL monitors -j | $JQ -e ".[] | select(.name == \"$mon\")" > /dev/null 2>&1; then
echo "$mon"
return 0
fi
done
return 1
}
bind_workspaces() {
local external
if external=$(get_active_external); then
# External monitor connected: 1-5 on external, 6-10 on internal
for ws in 1 2 3 4 5; do
$HYPRCTL keyword workspace "$ws, monitor:$external, default:true"
done
for ws in 6 7 8 9 10; do
$HYPRCTL keyword workspace "$ws, monitor:$INTERNAL, default:true"
done
else
# No external monitor: all workspaces on internal
for ws in 1 2 3 4 5 6 7 8 9 10; do
$HYPRCTL keyword workspace "$ws, monitor:$INTERNAL, default:true"
done
fi
}
handle_event() {
case $1 in
monitoradded*|monitorremoved*)
sleep 0.5 # Give monitor time to initialize
bind_workspaces
;;
esac
}
# Bind workspaces on startup
bind_workspaces
${pkgs.socat}/bin/socat -U - UNIX-CONNECT:"$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" | while read -r line; do
handle_event "$line"
done
'';
in
{
options = {
@@ -41,17 +94,7 @@ in
monitor=eDP-1, 1920x1200, auto-center-down, 1
monitor=HDMI-A-1, 2560x1440@100, auto-center-up, 1
workspace = 1, monitor:HDMI-A-1
workspace = 2, monitor:HDMI-A-1
workspace = 3, monitor:HDMI-A-1
workspace = 4, monitor:HDMI-A-1
workspace = 5, monitor:HDMI-A-1
workspace = 6, monitor:eDP-1
workspace = 7, monitor:eDP-1
workspace = 8, monitor:eDP-1
workspace = 9, monitor:eDP-1
workspace = 10, monitor:eDP-1
monitor=DP-3, 2560x1440@60, auto-center-up, 1
workspace = w[tv1], gapsout:0, gapsin:0
workspace = f[1], gapsout:0, gapsin:0
@@ -60,6 +103,9 @@ in
windowrule = border_size 0, match:float 0, match:workspace f[1]
windowrule = rounding 0, match:float 0, match:workspace f[1]
# https://wiki.archlinux.org/title/Hyprland#Jetbrains_apps_focus_issues
windowrule = match:xwayland true, no_initial_focus on
exec-once=dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
'';
@@ -79,6 +125,7 @@ in
follow_mouse = 2;
sensitivity = 0.3;
accel_profile = "flat";
touchpad = {
natural_scroll = false;
tap-and-drag = false;
@@ -171,6 +218,23 @@ in
pkgs.wdisplays
pkgs.bc
];
systemd.user.services.hyprland-monitors = {
Unit = {
Description = "Hyprland monitor hotplug handler";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Service = {
Type = "simple";
ExecStart = "${monitorScript}";
Restart = "on-failure";
RestartSec = 5;
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
# To start electron apps like `chromium` with wayland support

View File

@@ -2,7 +2,7 @@
{
home-manager.users.alex = {
home.packages = [
pkgs.nixfmt-rfc-style
pkgs.nixfmt
pkgs.nix-tree
];
};

View File

@@ -23,15 +23,14 @@ in
services.resolved = {
enable = true;
dnssec = "false";
domains = [ "~." ];
fallbackDns = [
"1.1.1.1#one.one.one.one"
"1.0.0.1#one.one.one.one"
];
extraConfig = ''
DNSOverTLS=yes
'';
settings = {
Resolve = {
Domains = [ "~." ];
DNSSEC = false;
DNSOverTLS = true;
};
};
};
};
}

View File

@@ -3,6 +3,7 @@
home-manager.users.alex = {
programs.ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks = {
"manatee" = {

View File

@@ -21,11 +21,10 @@ in
# (pkgs.callPackage ./pants.nix { inherit (pkgs) stdenv.hostPlatform.system; })
# (pkgs.callPackage ./syb-cli.nix { })
(inputs.nix-jetbrains-plugins.lib."${pkgs.stdenv.hostPlatform.system}".buildIdeWithPlugins
pkgs.jetbrains
"idea"
[ "IdeaVIM" ]
)
(inputs.nix-jetbrains-plugins.lib.buildIdeWithPlugins pkgs "idea" [
"IdeaVIM"
"com.github.copilot"
])
(pkgs.google-cloud-sdk.withExtraComponents [
pkgs.google-cloud-sdk.components.gke-gcloud-auth-plugin
@@ -68,6 +67,13 @@ in
};
};
# Needed for `copilot`
programs.nix-ld.enable = true;
programs.nix-ld.libraries = [
pkgs.stdenv.cc.cc.lib
pkgs.zlib
];
services.openvpn.servers = lib.mkIf openvpnEnabled {
work-staging = {
config = "config ${config.age.secrets.work-staging-ovpn.path}";

View File

@@ -1,5 +1,4 @@
{
system,
pkgs,
lib,
...
@@ -11,7 +10,7 @@ let
match =
v: l: builtins.elemAt (lib.lists.findFirst (x: (if_let v (builtins.elemAt x 0)) != null) null l) 1;
package = match { platform = system; } [
package = match { platform = pkgs.stdenv.hostPlatform.system; } [
[
{ platform = "aarch64-linux"; }
{

View File

@@ -15,7 +15,8 @@ in
domain = "whib-frontend.local";
port = "8081";
backendHost = "https://api.whib.ppp.pm/"; # "whib-backend.local";
# backendHost = "https://api.whib.ppp.pm/";
backendHost = "http://localhost:8080";
};
virtualisation.vmVariant = {

View File

@@ -0,0 +1,7 @@
age-encryption.org/v1
-> ssh-ed25519 wkRvNA GW8mvnaXpspxr78xV0fKhXwHVvReyjvDc0v7uPwhuBI
Rne8JZYVhrTTesSFpRQ/IOZlFIMoX9Wmv5n1Ed7Ehv8
-> ssh-ed25519 +oNaHQ d7utzodGQ7LsD2Uht1rbT8Qq9BZp3PkJS9EDhajCjnk
qd2Vj+1TQrjEKkSVAf0cXcCdkgeN/Jbp4UrBSp3cKYQ
--- JQr5UQlutONqnTeoT/mIVZL8ME7ipUDK8zDfNcN3uhU
ø5-VŸÌ²ÂòÀ-®ªÁÅ'^žô5kú.t(d1‡)É'<u%

Binary file not shown.

View File

@@ -33,6 +33,8 @@ in {
"manatee/syncthing-cert.age".publicKeys = [ manatee alex ];
"manatee/syncthing-key.age".publicKeys = [ manatee alex ];
"manatee/hetzner-dns.age".publicKeys = [ manatee alex ];
"manatee/komga-comicbooktracker-credentials.age".publicKeys = [ manatee alex];
"manatee/komga-bookmanager-credentials.age".publicKeys = [ manatee alex];
"backwards/root.backwards.age".publicKeys = [ backwards alex ];
"backwards/root.backwards.pub.age".publicKeys = [ backwards alex ];