Compare commits

..

No commits in common. "f6907766f08e5c04f7dbd5e66d7267747450472d" and "0a89116be6ec27f640ad5108c3bf21f833c414dd" have entirely different histories.

60 changed files with 1238 additions and 1820 deletions

2
.gitignore vendored
View File

@ -1,5 +1,3 @@
result
result/
.aider*
.env

447
flake.lock generated
View File

@ -8,11 +8,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1747575206,
"narHash": "sha256-NwmAFuDUO/PFcgaGGr4j3ozG9Pe5hZ/ogitWhY+D81k=",
"lastModified": 1736955230,
"narHash": "sha256-uenf8fv2eG5bKM8C/UvFaiJMZ4IpUFaQxk9OH5t/1gA=",
"owner": "ryantm",
"repo": "agenix",
"rev": "4835b1dc898959d8547a871ef484930675cb47f1",
"rev": "e600439ec4c273cf11e06fe4d9d906fb98fa097c",
"type": "github"
},
"original": {
@ -21,6 +21,37 @@
"type": "github"
}
},
"cachix": {
"inputs": {
"devenv": [
"teslamate-flake",
"devenv"
],
"flake-compat": [
"teslamate-flake",
"devenv"
],
"git-hooks": [
"teslamate-flake",
"devenv"
],
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1728672398,
"narHash": "sha256-KxuGSoVUFnQLB2ZcYODW7AVPAh9JqRlD5BrfsC/Q4qs=",
"owner": "cachix",
"repo": "cachix",
"rev": "aac51f698309fd0f381149214b7eee213c66ef0a",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "latest",
"repo": "cachix",
"type": "github"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
@ -29,11 +60,11 @@
]
},
"locked": {
"lastModified": 1744478979,
"narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
"lastModified": 1700795494,
"narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "43975d782b418ebf4969e9ccba82466728c2851b",
"rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
"type": "github"
},
"original": {
@ -43,6 +74,154 @@
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nix": "nix",
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1732298876,
"narHash": "sha256-WXlcDNMaMJeI4JO4VfQM2ZZCBJBds7j7N04tS9UjiYU=",
"owner": "cachix",
"repo": "devenv",
"rev": "741e23a22f3dc9e53075be3eaa795ea9ed6f5129",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv-root": {
"flake": false,
"locked": {
"narHash": "sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY=",
"type": "file",
"url": "file:///dev/null"
},
"original": {
"type": "file",
"url": "file:///dev/null"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"teslamate-flake",
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"teslamate-flake",
"nixpkgs"
]
},
"locked": {
"lastModified": 1730504689,
"narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "506278e768c2a08bec68eb62932193e341f55c90",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"teslamate-flake",
"devenv"
],
"gitignore": "gitignore",
"nixpkgs": [
"teslamate-flake",
"devenv",
"nixpkgs"
],
"nixpkgs-stable": [
"teslamate-flake",
"devenv"
]
},
"locked": {
"lastModified": 1730302582,
"narHash": "sha256-W1MIJpADXQCgosJZT8qBYLRuZls2KSiKdpnTVdKBuvU=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"teslamate-flake",
"devenv",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@ -51,11 +230,11 @@
]
},
"locked": {
"lastModified": 1745494811,
"narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=",
"lastModified": 1703113217,
"narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be",
"rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1",
"type": "github"
},
"original": {
@ -71,24 +250,40 @@
]
},
"locked": {
"lastModified": 1748665073,
"narHash": "sha256-RMhjnPKWtCoIIHiuR9QKD7xfsKb3agxzMfJY8V9MOew=",
"lastModified": 1743387206,
"narHash": "sha256-24N3NAuZZbYqZ39NgToZgHUw6M7xHrtrAm18kv0+2Wo=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "282e1e029cb6ab4811114fc85110613d72771dea",
"rev": "15c5f9d04fabd176f30286c8f52bbdb2c853a146",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.05",
"ref": "release-24.11",
"repo": "home-manager",
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"local-unstable": {
"locked": {
"lastModified": 0,
"narHash": "sha256-eCA4jXsPHiBkrf1sNOfQPYS2g9DoCsICVzk4ec0cEdo=",
"narHash": "sha256-uewgkTWbDOpOP+wEA3f03XEKsPHsJi0iDqBGQnxWQo0=",
"path": "/home/giulio/dev/nixpkgs",
"type": "path"
},
@ -97,45 +292,50 @@
"type": "path"
}
},
"nixos-unstable": {
"nix": {
"inputs": {
"flake-compat": [
"teslamate-flake",
"devenv"
],
"flake-parts": "flake-parts",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs_5",
"nixpkgs-23-11": [
"teslamate-flake",
"devenv"
],
"nixpkgs-regression": [
"teslamate-flake",
"devenv"
],
"pre-commit-hooks": [
"teslamate-flake",
"devenv"
]
},
"locked": {
"lastModified": 1748929857,
"narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4",
"lastModified": 1727438425,
"narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=",
"owner": "domenkozar",
"repo": "nix",
"rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4",
"owner": "domenkozar",
"ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"nixos-master": {
"locked": {
"lastModified": 1745391562,
"narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=",
"lastModified": 1743784178,
"narHash": "sha256-rxchbu1Zcthv7fV+KWiF79SjUaeMe3tiK5qYhg+u+pA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-master": {
"locked": {
"lastModified": 1749040375,
"narHash": "sha256-zwVvfxgrXcInI2E/dDg9v80OrvKUT7HtPECu53Khcq0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "57afa2783caf7d6713f63c8e29fba6c52a3a5300",
"rev": "d142f5ee8a1bc234868d9ea5bcc7c0db708651e1",
"type": "github"
},
"original": {
@ -145,19 +345,51 @@
"type": "github"
}
},
"nixpkgs_2": {
"nixos-unstable": {
"locked": {
"lastModified": 1748889542,
"narHash": "sha256-Hb4iMhIbjX45GcrgOp3b8xnyli+ysRPqAgZ/LZgyT5k=",
"lastModified": 1743689281,
"narHash": "sha256-y7Hg5lwWhEOgflEHRfzSH96BOt26LaYfrYWzZ+VoVdg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "10d7f8d34e5eb9c0f9a0485186c1ca691d2c5922",
"rev": "2bfc080955153be0be56724be6fa5477b4eefabb",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1703013332,
"narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1744440957,
"narHash": "sha256-FHlSkNqFmPxPJvy+6fNLaNeWnF1lZSgqVCl/eWaJRc4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "26d499fc9f1d567283d5d56fcf367edd815dba1d",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "10d7f8d34e5eb9c0f9a0485186c1ca691d2c5922",
"rev": "26d499fc9f1d567283d5d56fcf367edd815dba1d",
"type": "github"
}
},
@ -177,6 +409,70 @@
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1730531603,
"narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1717432640,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1716977621,
"narHash": "sha256-Q1UQzYcMJH4RscmpTkjlgqQDX5yi1tZL0O345Ri6vXQ=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs_7": {
"locked": {
"lastModified": 1732014248,
"narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "23e89b7da85c3640bbc2173fe04f4bd114342367",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nvidia-patch": {
"inputs": {
"nixpkgs": [
@ -185,11 +481,11 @@
"utils": "utils"
},
"locked": {
"lastModified": 1748931931,
"narHash": "sha256-0NUhiMITsYlXDjgcVbLayI0rgrEdf5NIbpW3oLueYUs=",
"lastModified": 1743670877,
"narHash": "sha256-OHuOhVCfx10VpWwl9T5Q+QB3To0N95flBR1rSwOiUHA=",
"owner": "icewind1991",
"repo": "nvidia-patch-nixos",
"rev": "fa8f006a236349790c94801ac85e43f103b35baf",
"rev": "e80a4919e88a8cb496f649234fb3fc7e992ece10",
"type": "github"
},
"original": {
@ -221,11 +517,12 @@
"agenix-flake": "agenix-flake",
"home-manager": "home-manager_2",
"local-unstable": "local-unstable",
"nixos-master": "nixos-master",
"nixos-unstable": "nixos-unstable",
"nixpkgs": "nixpkgs_2",
"nixpkgs-master": "nixpkgs-master",
"nvidia-patch": "nvidia-patch",
"pepeflake": "pepeflake"
"pepeflake": "pepeflake",
"teslamate-flake": "teslamate-flake"
}
},
"systems": {
@ -258,6 +555,50 @@
"type": "github"
}
},
"teslamate-flake": {
"inputs": {
"devenv": "devenv",
"devenv-root": "devenv-root",
"flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_7",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1732358620,
"narHash": "sha256-diQRtJYfzGIVLxrdBad3XKWCtR97rj9Q1ZJ9MmvJGRk=",
"owner": "teslamate-org",
"repo": "teslamate",
"rev": "0ec408c8e182fe64e9568b6f137cbfb528717e8e",
"type": "github"
},
"original": {
"owner": "teslamate-org",
"ref": "v1.32.0",
"repo": "teslamate",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"teslamate-flake",
"nixpkgs"
]
},
"locked": {
"lastModified": 1732292307,
"narHash": "sha256-5WSng844vXt8uytT5djmqBCkopyle6ciFgteuA9bJpw=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "705df92694af7093dfbb27109ce16d828a79155f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems_2"

View File

@ -1,13 +1,14 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/10d7f8d34e5eb9c0f9a0485186c1ca691d2c5922";
nixos-unstable.url = "github:NixOS/nixpkgs/c2a03962b8e24e669fb37b7df10e7c79531ff1a4";
nixpkgs-master.url = "github:NixOS/nixpkgs/master";
nixpkgs.url = "github:NixOS/nixpkgs/26d499fc9f1d567283d5d56fcf367edd815dba1d";
nixos-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nixos-master.url = "github:NixOS/nixpkgs/master";
local-unstable.url = "path:///home/giulio/dev/nixpkgs";
pepeflake.url = "git+https://git.giugl.io/peperunas/pepeflake";
teslamate-flake.url = "github:teslamate-org/teslamate/v1.32.0";
agenix-flake.url = "github:ryantm/agenix";
home-manager = {
url = "github:nix-community/home-manager/release-25.05";
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
nvidia-patch = {
@ -20,9 +21,10 @@
{ self
, nixpkgs
, nixos-unstable
, nixpkgs-master
, nixos-master
, local-unstable
, home-manager
, teslamate-flake
, nvidia-patch
, agenix-flake
, pepeflake
@ -60,14 +62,16 @@
};
unstablePkgs = importNixpkgs { flake = nixos-unstable; };
masterPkgs = importNixpkgs { flake = nixpkgs-master; };
masterPkgs = importNixpkgs { flake = nixos-master; };
localPkgs = importNixpkgs { flake = local-unstable; };
teslamatePkgs = importNixpkgs { flake = teslamate-flake; };
agenixPkgs = importNixpkgs { flake = agenix-flake; };
pepePkgs = pepeflake.packages.${system} // pepeflake.legacyPackages.${system} or { };
additionalOverlays = [
(final: prev: { inherit unstablePkgs; })
(final: prev: { inherit localPkgs; })
(final: prev: { inherit teslamatePkgs; })
(final: prev: { inherit agenixPkgs; })
(final: prev: { inherit masterPkgs; })
(final: prev: { inherit pepePkgs; })
@ -100,7 +104,7 @@
});
pkgsLinuxX64Cuda = wrapPkgsSystem { system = sysLinuxX64; cudaSupport = true; };
pkgsLinuxX64Cuda = wrapPkgsSystem { system = sysLinuxX64; };
utilsLinuxX64Cuda = wrapUtils { pkgs = pkgsLinuxX64Cuda; };
pkgsLinuxAarch = wrapPkgsSystem { system = sysLinuxAarch; };
@ -118,6 +122,7 @@
roles = [ ];
}];
imports = [
teslamate-flake.nixosModules.default
agenix-flake.nixosModules.default
];
};

View File

@ -17,9 +17,6 @@
environmentFile = config.age.secrets.restic-environment.path;
repository = "b2:architect:/";
paths = [ "/var/lib" "/services" ];
exclude = [
"/var/lib/ollama"
];
pruneOpts = [
"--keep-daily 45"
"--keep-weekly 12"

View File

@ -0,0 +1,25 @@
{ config, lib, ... }:
let
domain = "htbaz.giugl.io";
in
{
services.bazarr = {
enable = true;
group = "media";
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" ];
locations."/" = {
allowLan = true;
port = 6767;
allow = [
tailscale.net
];
};
};
users.groups.media.members = [ "bazarr" ];
}

View File

@ -1,9 +1,12 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, ... }:
let
macbookPubkey = (import ../pubkeys.nix).macbook;
pubkeys = [ macbookPubkey ];
domain = "devs.giugl.io";
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) generateDeviceStrings;
in
{
imports = [
@ -12,20 +15,46 @@ in
./hardware.nix
./firewall.nix
./nginx.nix
./gitea.nix
./sonarr.nix
./radarr.nix
./bazarr.nix
./nzbget.nix
./nextcloud.nix
./minio.nix
./matrix.nix
./fail2ban.nix
./dns.nix
# ./minecraft.nix
./prowlarr.nix
./redlib.nix
# ./invidious.nix
./jellyfin.nix
# ./docker.nix
./tailscale.nix
./headscale.nix
./llm.nix
# ./photoprism.nix
./sunshine.nix
./jellyseer.nix
./postgres.nix
./netdata.nix
./homeassistant.nix
./searx.nix
];
age.identityPaths = [ "/root/.ssh/id_ed25519" ];
architect = {
networks.lan = {
interface = "enp6s0";
net = "10.0.0.0/24";
devices = {
architect = { address = "10.0.0.250"; hostname = "architect.${domain}"; };
router = { address = "10.0.0.1"; hostname = "router.${domain}"; };
dvr = { address = "10.0.0.3"; hostname = "dvr.${domain}"; };
};
};
firewall = {
openTCP = [ 22 ];
@ -48,8 +77,8 @@ in
};
};
kernelParams = with config.pepe.core.network.interfaces.lan; [
"ip=${devices.architect.address}::${devices.brigettine.address}:255.255.255.0::${interface}:off"
kernelParams = with config.architect.networks.lan; [
"ip=${devices.architect.address}::${devices.router.address}:255.255.255.0::${interface}:off"
];
kernel.sysctl = { "net.ipv4.ip_forward" = 1; };
@ -67,17 +96,42 @@ in
tmp.tmpfsSize = "50%";
};
networking = with config.pepe.core.network.interfaces.lan; {
networking = with config.architect.networks.lan; {
hostName = "architect";
hostId = "49350853";
useDHCP = false;
defaultGateway = devices.brigettine.address;
defaultGateway = devices.router.address;
interfaces = {
${interface}.ipv4.addresses = [{
address = devices.architect.address;
prefixLength = 24;
}];
};
extraHosts = (generateDeviceStrings config.architect.networks.lan.devices) + ''
# Blacklist
0.0.0.0 metrics.plex.tv
0.0.0.0 analytics.plex.tv
0.0.0.0 cdn.luckyorange.com
0.0.0.0 w1.luckyorange.com
0.0.0.0 browser.sentry-cdn.com
0.0.0.0 analytics.facebook.com
0.0.0.0 ads.facebook.com
0.0.0.0 extmaps-api.yandex.net
0.0.0.0 logservice.hicloud.com
0.0.0.0 logbak.hicloud.com
0.0.0.0 logservice1.hicloud.com
0.0.0.0 samsung-com.112.2o7.net
0.0.0.0 supportmetrics.apple.com
0.0.0.0 analytics.oneplus.cn
0.0.0.0 click.oneplus.cn
0.0.0.0 analytics-api.samsunghealthcn.com
'';
};
hardware.opengl = {
enable = true;
extraPackages = with pkgs; [ vaapiVdpau ];
};
services = {
@ -99,177 +153,5 @@ in
};
smartd.enable = true;
};
pepe = {
core = {
media = {
enable = true;
path = "/media";
};
network.interfaces = {
lan = {
interface = "enp6s0";
type = "lan";
net = "10.0.0.0/24";
devices = {
architect = { address = "10.0.0.250"; hostname = "architect.${domain}"; isEndpoint = true; };
brigettine = { address = "10.0.0.1"; hostname = "router.${domain}"; };
dreamel10 = { address = "10.0.0.199"; hostname = "dreamel10.${domain}"; };
reolinkcamera = { address = "10.0.0.200"; hostname = "reolinkcamera.${domain}"; };
lgtv = { address = "10.0.0.202"; hostname = "lgtv.${domain}"; };
};
};
};
};
services = {
gitea = {
enable = true;
domain = "git.giugl.io";
};
immich = {
enable = true;
domain = "photos.giugl.io";
package = pkgs.unstablePkgs.immich;
};
radarr = {
enable = true;
domain = "htrad.giugl.io";
package = pkgs.unstablePkgs.radarr;
};
sonarr = {
enable = true;
domain = "htson.giugl.io";
package = pkgs.unstablePkgs.sonarr;
};
bazarr = {
enable = true;
domain = "htbaz.giugl.io";
package = pkgs.unstablePkgs.bazarr;
};
nzbget = {
enable = true;
domain = "htnzb.giugl.io";
package = pkgs.unstablePkgs.nzbget;
};
jellyfin = {
enable = true;
domain = "media.giugl.io";
package = pkgs.unstablePkgs.jellyfin;
};
jellyseer = {
enable = true;
domain = "aumm-aumm.giugl.io";
};
prowlarr = {
enable = true;
domain = "htpro.giugl.io";
};
redlib = {
enable = true;
domain = "reddit.giugl.io";
package = pkgs.unstablePkgs.redlib;
settings = {
REDLIB_ROBOTS_DISABLE_INDEXING = "on";
REDLIB_DEFAULT_THEME = "dracula";
REDLIB_DEFAULT_SHOW_NSFW = "on";
REDLIB_DEFAULT_BLUR_NSFW = "off";
REDLIB_DEFAULT_USE_HLS = "on";
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION = "on";
};
};
llm = {
enable = true;
package = pkgs.unstablePkgs.ollama-cuda;
backendDomain = "ollama.giugl.io";
acceleration = "cuda";
};
homeassistant = {
enable = true;
package = pkgs.unstablePkgs.home-assistant;
domain = "home.giugl.io";
extraComponents = [
"otbr"
"litterrobot"
"apple_tv"
"homekit"
"homekit_controller"
"spotify"
"hue"
"sonos"
"tplink"
"ollama"
"wyoming"
"whisper"
"piper"
"isal"
"radarr"
"sonarr"
"mqtt"
"mqtt_eventstream"
"mqtt_json"
"mqtt_room"
"mqtt_statestream"
"github"
"webostv"
"reolink"
"onvif"
"xiaomi_miio"
"ring"
];
extraPackages = python3Packages: with pkgs.unstablePkgs.python3Packages; [
pyporscheconnectapi
];
config = {
http = {
server_host = "127.0.0.1";
server_port = 8123;
use_x_forwarded_for = true;
trusted_proxies = [ "127.0.0.1" ];
};
homeassistant = {
name = "Brigettine Square";
latitude = 52.1958;
longitude = 0.180746;
unit_system = "metric";
};
default_config = { };
automation = "!include automations.yaml";
frontend.themes = "!include_dir_merge_named themes";
};
};
headscale = {
enable = true;
domain = "vipienne.giugl.io";
settings = with config.pepe.core.network.interfaces.tailscale; {
server_url = "https://${domain}";
prefixes.v4 = net;
dns = {
magic_dns = false;
override_local_dns = true;
global = [ devices.architect.address ];
nameservers.global = [ devices.architect.address ];
};
log.level = "debug";
};
};
};
};
}

View File

@ -1,13 +1,70 @@
{ ... }:
{ config, pkgs, ... }:
with pkgs.lib;
let
generateCoreDNSConfig = domains:
let
generateForDomain = domain: conf:
concatMapStrings
(iface:
let
architectIP = config.architect.networks.${iface}.devices.architect.address;
interfaceNet = config.architect.networks.${iface}.net;
in
''
${domain} {
view ${iface} {
expr incidr(client_ip(), '${interfaceNet}')
}
template IN A ${domain} {
answer "${domain}. 60 IN A ${architectIP}"
}
template IN HTTPS ${domain} {
answer "${domain}. 60 IN HTTPS 1 . ipv4hint=\"${architectIP}\""
}
cache
log
}
''
)
conf.dnsInterfaces;
in
concatStrings (mapAttrsToList generateForDomain domains);
# Combine vhosts and the single domain
allDomains = config.architect.vhost // {
"architect.devs.giugl.io" = { dnsInterfaces = [ "lan" "tailscale" ]; };
};
domain = "adguard.giugl.io";
in
{
pepe.core.dns = {
enable = true;
nextDNSId = "d65174";
extraDomains = {
"architect.devs.giugl.io" = {
dnsInterfaces = [ "lan" "tailscale" ];
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = config.services.adguardhome.port;
allowLan = true;
allow = [
tailscale.net
];
};
};
services = {
coredns = {
enable = true;
config = ''
${generateCoreDNSConfig allDomains}
. {
cache
forward . 45.90.28.77 45.90.30.77
}
'';
};
};
}

View File

@ -7,8 +7,8 @@
packageFirewall = pkgs.nftables;
bantime-increment.enable = true;
ignoreIP = [
config.pepe.core.network.interfaces.tailscale.net
config.pepe.core.network.interfaces.lan.net
config.architect.networks.lan.net
config.architect.networks.tailscale.net
];
};
}

View File

@ -1,18 +1,20 @@
{ config, lib, ... }:
let
firewallRules = config.pepe.core.firewall;
openTCP = lib.concatMapStringsSep "," (x: toString x) firewallRules.openTCP;
openUDP = lib.concatMapStringsSep "," (x: toString x) firewallRules.openUDP;
ifaces = config.pepe.core.network.interfaces;
deviceAddress = interface: device:
ifaces.${interface}.devices.${device}.address;
openTCP = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openTCP;
openUDP = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openUDP;
openTCPVPN = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openTCPVPN;
openUDPVPN = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openUDPVPN;
deviceAddress = interface: device:
config.architect.networks.${interface}.devices.${device}.address;
gdevices = [
(deviceAddress "tailscale" "architect")
(deviceAddress "tailscale" "dodino")
(deviceAddress "tailscale" "manduria")
(deviceAddress "tailscale" "kmerr")
(deviceAddress "tailscale" "chuck")
];
in
{
@ -23,7 +25,7 @@ in
nftables = {
enable = true;
ruleset = with config.pepe.core.network.interfaces; ''
ruleset = with config.architect.networks; ''
table ip raw {
chain PREROUTING {
type filter hook prerouting priority raw; policy accept;

34
hosts/architect/gitea.nix Normal file
View File

@ -0,0 +1,34 @@
{ config, lib, ... }:
let
domain = "git.giugl.io";
in
{
architect = {
firewall.openTCP = [ config.services.gitea.settings.server.SSH_PORT ];
vhost.${domain} = {
dnsInterfaces = [ "lan" "tailscale" ];
locations."/" = {
port = config.services.gitea.settings.server.HTTP_PORT;
allowWAN = true;
};
};
};
services.gitea = {
enable = true;
database.type = "sqlite3";
appName = "Gitea";
# https://github.com/NixOS/nixpkgs/issues/235442#issuecomment-1574329453
lfs.enable = true;
settings = {
server = {
DOMAIN = domain;
ROOT_URL = "https://${domain}";
SSH_PORT = 22;
HTTP_PORT = 3001;
};
openid.enable_openid_signin = true;
};
};
}

View File

@ -56,7 +56,7 @@
options = [ "bind" ];
};
"${config.pepe.core.media.path}" = lib.mkIf config.pepe.core.media.enable {
"/media" = {
device = "nvmedata/media";
fsType = "zfs";
};

View File

@ -0,0 +1,49 @@
{ config, pkgs, ... }:
let
domain = "vipienne.giugl.io";
headscalePkg = pkgs.headscale;
in
{
environment.systemPackages = [ headscalePkg ];
architect = {
firewall = {
openUDP = [ config.services.tailscale.port ];
};
vhost.${domain} = {
dnsInterfaces = [ "lan" "tailscale" ];
locations."/" = {
port = config.services.headscale.port;
allowWAN = true;
proxyWebsockets = true;
};
};
};
services.headscale = {
enable = true;
package = headscalePkg;
port = 1194;
settings = {
server_url = "https://${domain}";
# log.level = "debug";
dns = {
magic_dns = false;
# base_domain = domain;
override_local_dns = true;
global = [
config.architect.networks.tailscale.devices.architect.address
];
nameservers.global = [
config.architect.networks.tailscale.devices.architect.address
];
};
logtail.enabled = false;
prefixes.v4 = config.architect.networks.tailscale.net;
noise.private_key_path = "/var/lib/headscale/noise_private.key";
};
};
}

View File

@ -0,0 +1,115 @@
{ config, pkgs, ... }:
let
domain = "home.giugl.io";
piperPort = 10200;
whisperPort = 10300;
in
{
services.go2rtc = {
enable = true;
settings.api.listen = "127.0.0.1:1984";
};
# services.wyoming = {
# faster-whisper = {
# servers."home" = {
# enable = true;
# uri = "tcp://127.0.0.1:${toString whisperPort}";
# model = "large-v3";
# device = "cuda";
# language = "en";
# };
# };
# piper = {
# servers."home" = {
# enable = true;
# uri = "tcp://127.0.0.1:${toString piperPort}";
# voice = "en-us-ryan-medium";
# lengthScale = 0.63;
# };
# };
# };
services.home-assistant = {
enable = true;
package = pkgs.unstablePkgs.home-assistant;
config = {
http = {
server_host = "127.0.0.1";
server_port = 8123;
use_x_forwarded_for = true;
trusted_proxies = [ "127.0.0.1" ];
};
homeassistant = {
name = "Brigettine Square";
latitude = 52.1958;
longitude = 0.180746;
unit_system = "metric";
};
default_config = { };
automation = "!include automations.yaml";
frontend.themes = "!include_dir_merge_named themes";
};
extraComponents = [
"otbr"
"litterrobot"
"apple_tv"
"homekit"
"homekit_controller"
"spotify"
"hue"
"sonos"
"tplink"
"ollama"
"wyoming"
"whisper"
"piper"
"isal"
"radarr"
"sonarr"
"mqtt"
"mqtt_eventstream"
"mqtt_json"
"mqtt_room"
"mqtt_statestream"
"github"
"webostv"
"reolink"
"onvif"
"xiaomi_miio"
"ring"
];
extraPackages = python3Packages: with pkgs.unstablePkgs.python3Packages; [
pyporscheconnectapi
];
};
services.mosquitto = {
enable = true;
listeners = [
{
address = "127.0.0.1";
acl = [ "pattern readwrite #" ];
omitPasswordAuth = true;
settings.allow_anonymous = true;
}
];
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = config.services.home-assistant.config.http.server_port;
allowWAN = true;
allowLan = true;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
};
}

View File

@ -0,0 +1,51 @@
{ config, pkgs, lib, ... }:
let
domain = "media.giugl.io";
port = 8096;
allowLan = true;
in
{
# needed since StateDirectory does not accept symlinks
systemd.services.jellyfin.serviceConfig.StateDirectory = lib.mkForce "";
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "lan" "tailscale" ];
locations = {
"/" = {
inherit port allowLan;
allow = [
tailscale.net
];
};
"/socket" = {
inherit port allowLan;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
};
};
services.jellyfin = {
enable = true;
group = "media";
package = pkgs.unstablePkgs.jellyfin;
};
users.groups = {
media.members = [ "jellyfin" ];
video.members = [ "jellyfin" ];
render.members = [ "jellyfin" ];
};
fileSystems."/tmp/jellyfin" = {
device = "none";
fsType = "tmpfs";
options = [ "defaults" "size=20G" "uid=jellyfin" ];
};
}

View File

@ -0,0 +1,23 @@
{ config, pkgs, ... }:
let
domain = "aumm-aumm.giugl.io";
in
{
services.jellyseerr = {
enable = true;
# package = pkgs.unstablePkgs.jellyseerr;
};
architect.vhost.${domain} = {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = config.services.jellyseerr.port;
allowLan = true;
allow = [
config.architect.networks.tailscale.net
];
};
};
}

67
hosts/architect/llm.nix Normal file
View File

@ -0,0 +1,67 @@
{ config, pkgs, ... }:
let
backendDomain = "ollama.giugl.io";
frontendDomain = "llm.giugl.io";
ollamaPkg = pkgs.unstablePkgs.ollama-cuda;
uiPkg = pkgs.unstablePkgs.open-webui;
tikaPkg = pkgs.unstablePkgs.tika;
in
{
environment = {
systemPackages = [ ollamaPkg ];
};
services = {
ollama = {
enable = true;
package = ollamaPkg;
acceleration = "cuda";
environmentVariables = {
OLLAMA_FLASH_ATTENTION = "1";
OLLAMA_NUM_PARALLEL = "2";
OLLAMA_KV_CACHE_TYPE = "q8_0";
};
};
open-webui = {
enable = true;
package = uiPkg;
};
tika = {
enable = true;
package = tikaPkg;
};
};
architect.vhost.${backendDomain} = {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
host = config.services.ollama.host;
port = config.services.ollama.port;
allowLan = true;
allowWAN = true;
recommendedProxySettings = false;
extraConfig = ''
proxy_buffering off;
proxy_read_timeout 600s;
proxy_set_header Host localhost:${toString config.services.ollama.host};
'';
};
};
architect.vhost.${frontendDomain} = {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
host = config.services.open-webui.host;
port = config.services.open-webui.port;
allowLan = true;
allowWAN = true;
proxyWebsockets = true;
};
};
}

35
hosts/architect/minio.nix Normal file
View File

@ -0,0 +1,35 @@
{ config, lib, pkgs, ... }:
let
domain = "s3.giugl.io";
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) architectInterfaceAddress;
in
{
services = {
minio = {
enable = true;
package = pkgs.minio_legacy_fs;
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:9000";
extraConfig = ''
client_max_body_size 500M;
allow ${config.architect.networks.lan.net};
allow ${config.architect.networks.tailscale.net};
deny all;
'';
};
};
};
networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain}
${architectInterfaceAddress "tailscale"} ${domain}
'';
}

View File

@ -22,6 +22,8 @@ in
};
environment.systemPackages = with pkgs; [
nodejs-18_x
libtensorflow
ffmpeg
];
@ -58,7 +60,7 @@ in
enable = true;
hostName = domain;
https = true;
package = pkgs.nextcloud31;
package = pkgs.nextcloud30;
datadir = "/services/nextcloud";
configureRedis = true;
caching = {

View File

@ -1,25 +1,148 @@
{ config, lib, ... }:
with lib;
{
options.architect = {
firewall = lib.mkOption {
internal = true;
default = config.pepe.core.firewall;
firewall = {
openTCP = mkOption {
type = types.listOf types.int;
default = [ ];
};
openUDP = mkOption {
type = types.listOf types.int;
default = [ ];
};
};
networks = lib.mkOption {
internal = true;
default = config.pepe.core.network.interfaces;
networks = mkOption {
type = types.attrsOf (types.submodule {
options = {
interface = mkOption {
type = types.str;
description = "The network interface name.";
};
net = mkOption {
type = types.str;
description = "The network address in CIDR format.";
};
devices = mkOption {
type = types.attrsOf (types.submodule {
options = {
address = mkOption {
type = types.str;
description = "The IP address of the device.";
};
hostname = mkOption {
type = types.str;
description = "The hostname of the device.";
};
};
});
default = { };
description = "An attribute set of devices with their configurations.";
};
};
});
default = { };
description = "An attribute set of networks with their configurations.";
};
vhost = lib.mkOption {
internal = true;
default = config.pepe.core.vhost.hosts;
vhost = mkOption {
type = types.attrsOf (types.submodule {
options = {
dnsInterfaces = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of interfaces to add extra DNS hosts for this vhost.";
};
locations = mkOption {
type = types.attrsOf (types.submodule {
options = {
extraConfig = mkOption {
type = types.str;
description = "Extra configuration for the location.";
default = "";
};
allowLan = mkOption {
type = types.bool;
default = false;
};
proxyWebsockets = mkOption {
type = types.bool;
default = false;
};
host = mkOption {
type = types.str;
description = "The host for the location.";
default = "127.0.0.1";
};
port = mkOption {
type = types.int;
description = "The port number for the location.";
};
allow = mkOption {
type = types.listOf types.str;
default = [ ];
description = "IP address or CIDR block to allow.";
};
path = mkOption {
type = types.str;
default = "";
};
recommendedProxySettings = mkOption {
type = types.bool;
default = true;
description = "Force the use of recommended proxy configuration.";
};
allowWAN = mkOption {
type = types.bool;
default = false;
description = "If set to false, deny all WAN traffic.";
};
};
});
default = { };
description = "An attribute set of location configurations.";
};
};
});
default = { };
description = "An attribute set of domain configurations.";
};
};
config.architect.networks.docker = {
interface = "docker0";
net = "172.17.0.0/16";
# TODO: move to nginx
config = {
services.nginx.virtualHosts = mapAttrs
(domain: conf: {
forceSSL = true;
useACMEHost= "giugl.io";
locations = mapAttrs
(path: location: {
proxyPass = "http://${location.host}:${toString location.port}${location.path}";
proxyWebsockets = location.proxyWebsockets;
recommendedProxySettings = location.recommendedProxySettings;
extraConfig = ''
${concatMapStringsSep "\n" (allowCIDR: "allow ${allowCIDR};") location.allow}
${optionalString location.allowLan ''allow ${config.architect.networks."lan".net};''}
${optionalString (!location.allowWAN) "deny all;"}
'' + location.extraConfig;
})
conf.locations;
})
config.architect.vhost;
};
}

View File

@ -3,6 +3,6 @@
{
services.postgresql = {
enable = true;
package = lib.mkForce pkgs.postgresql_16;
package = lib.mkForce pkgs.postgresql;
};
}

View File

@ -0,0 +1,28 @@
{ config, pkgs, ... }:
let
domain = "htpro.giugl.io";
in
{
services.prowlarr = {
enable = true;
package = pkgs.unstablePkgs.prowlarr;
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = 9696;
allowLan = true;
proxyWebsockets=true;
allow = [
tailscale.net
];
};
};
users.groups.media.members = [ "prowlarr" ];
}

View File

@ -0,0 +1,26 @@
{ config, pkgs, ... }:
let
domain = "htrad.giugl.io";
in
{
services.radarr = {
enable = true;
package = pkgs.unstablePkgs.radarr;
group = "media";
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = 7878;
allowLan = true;
allow = [
tailscale.net
];
};
};
users.groups.media.members = [ "radarr" ];
}

View File

@ -0,0 +1,26 @@
{ config, pkgs, ... }:
let
domain = "htson.giugl.io";
in
{
services.sonarr = {
enable = true;
group = "media";
package = pkgs.unstablePkgs.sonarr;
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = 8989;
allowLan = true;
allow = [
tailscale.net
];
};
};
users.groups.media.members = [ "sonarr" ];
}

View File

@ -38,19 +38,22 @@ let
sunshinePkg = (pkgs.unstablePkgs.sunshine.override { cudaSupport = true; });
in
{
pepe.core.graphics = {
enable = true;
nvidia = true;
};
boot.kernelModules = [ "uinput" ];
environment.systemPackages = with pkgs.unstablePkgs; [ gamemode heroic ];
hardware = {
pulseaudio.enable = false;
};
nvidia = {
modesetting.enable = true;
powerManagement.enable = false;
powerManagement.finegrained = false;
open = false;
nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.latest;
};
};
systemd.services.NetworkManager-wait-online.enable = pkgs.lib.mkForce false;
programs.steam = {
enable = true;
@ -156,6 +159,7 @@ in
xserver = {
enable = true;
videoDrivers = [ "nvidia" ];
desktopManager.xfce.enable = true;
monitorSection = ''

View File

@ -7,26 +7,24 @@ let
inherit (utilities) generateDeviceStrings;
in
{
pepe.core = {
firewall.openUDP = [ 41641 ];
network.interfaces.tailscale = {
architect = {
networks.tailscale = {
interface = "ts0";
net = "100.64.0.0/10";
type = "vpn";
devices = {
architect = { address = "100.64.0.1"; hostname = "architect.${domain}"; isEndpoint = true; };
architect = { address = "100.64.0.1"; hostname = "architect.${domain}"; };
kmerr = { address = "100.64.0.2"; hostname = "kmerr.${domain}"; };
work_laptop = { address = "100.64.0.4"; hostname = "work_laptop.${domain}"; };
work_desktop = { address = "100.64.0.5"; hostname = "work_desktop.${domain}"; };
parallels = { address = "100.64.0.3"; hostname = "parallels.${domain}"; };
chuck = { address = "100.64.0.4"; hostname = "chuck.${domain}"; };
dodino = { address = "100.64.0.5"; hostname = "dodino.${domain}"; };
manduria = { address = "100.64.0.6"; hostname = "manduria.${domain}"; };
tommy = { address = "100.64.0.7"; hostname = "tommy.${domain}"; };
ucsb-workstation = { address = "100.64.0.8"; hostname = "ucsb-workstation.${domain}"; };
alfredo = { address = "100.64.0.9"; hostname = "alfredo.${domain}"; };
appletv = { address = "100.64.0.13"; hostname = "appletv.${domain}"; };
watkinshouse = { address = "100.64.0.14"; hostname = "watkinshouse.${domain}"; };
afsun = { address = "100.64.0.15"; hostname = "afsun.${domain}"; };
jacopo-desktop = { address = "100.64.0.21"; hostname = "jacopo-desktop.${domain}"; };
jacopo-tv = { address = "100.64.0.22"; hostname = "jacopo-tv.${domain}"; };
jacopo-phone = { address = "100.64.0.28"; hostname = "jacopo-phone.${domain}"; };
};
};
};
@ -34,10 +32,10 @@ in
services = {
tailscale = {
enable = true;
interfaceName = config.pepe.core.network.interfaces.tailscale.interface;
interfaceName = config.architect.networks.tailscale.interface;
package = pkgs.unstablePkgs.tailscale;
};
};
networking.extraHosts = generateDeviceStrings config.pepe.core.network.interfaces.tailscale.devices;
networking.extraHosts = generateDeviceStrings config.architect.networks.tailscale.devices;
}

View File

@ -1,16 +1,13 @@
{ config, lib, ... }:
let
ifaces = config.pepe.core.network.interfaces;
in
{
# device.address device.hostname
generateDeviceStrings = devices: lib.concatStringsSep "\n"
(lib.mapAttrsToList (name: device: "${device.address} ${device.hostname}") devices);
getDeviceAddress = interface: device:
ifaces.${interface}.devices.${device}.address;
config.architect.networks.${interface}.devices.${device}.address;
architectInterfaceAddress = interface:
ifaces.${interface}.devices.architect.address;
config.architect.networks.${interface}.devices.architect.address;
}

View File

@ -31,14 +31,13 @@
(mkSysRole "common")
(mkSysRole "acme")
(mkUser { name = "root"; roles = [ ]; })
../modules
];
home-manager = {
useGlobalPkgs = true;
};
system.stateVersion = "25.05";
system.stateVersion = "24.11";
}
home-manager.nixosModules.home-manager

View File

@ -1,11 +0,0 @@
{...}: {
imports = [
./media.nix
./graphics.nix
./network.nix
./vhost.nix
./firewall.nix
./dns.nix
./docker.nix
];
}

View File

@ -1,169 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkOption types mkIf concatMapStrings concatStrings mapAttrsToList attrsets;
cfg = config.pepe.core.dns;
in
{
options.pepe.core.dns = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable the DNS server.";
};
nextDNSId = mkOption {
type = types.str;
default = "d65174";
description = "NextDNS ID for DNS over TLS.";
};
extraDomains = mkOption {
type = types.attrsOf (types.submodule {
options = {
dnsInterfaces = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of interfaces to add DNS entries for this domain.";
};
};
});
default = { };
description = "Additional domains to add to DNS configuration.";
};
};
config = mkIf cfg.enable {
services.coredns = {
enable = true;
config =
let
# Function to generate domain-specific configurations
generateDomainConfig = domain: conf: ifaceName:
let
iface = config.pepe.core.network.interfaces.${ifaceName};
ifaceEndpoint = lib.head (lib.attrNames (lib.filterAttrs (_: device: device.isEndpoint) iface.devices));
serverIP = iface.devices.${ifaceEndpoint}.address;
interfaceNet = iface.net;
in
''
${domain} {
view ${ifaceName} {
expr incidr(client_ip(), '${interfaceNet}')
}
template IN A ${domain} {
answer "${domain}. 60 IN A ${serverIP}"
}
template IN HTTPS ${domain} {
answer "${domain}. 60 IN HTTPS 1 . ipv4hint=\"${serverIP}\""
}
cache
log
}
'';
# Function to generate device views for an interface
generateDeviceViews = ifaceName:
let
iface = config.pepe.core.network.interfaces.${ifaceName};
in
concatMapStrings
({ name, device }:
let
deviceIP = device.address;
serverName = "${name}-${cfg.nextDNSId}.dns.nextdns.io";
in
''
. {
view ${name} {
expr client_ip() == '${deviceIP}'
}
forward . tls://45.90.28.77 tls://45.90.30.77 {
tls_servername ${serverName}
health_check 5s
}
}
''
)
(attrsets.mapAttrsToList
(name: device: { inherit name device; })
iface.devices
);
# Collect all interfaces used across all domains
allInterfaces = lib.unique (lib.flatten
(lib.mapAttrsToList
(_: conf: conf.dnsInterfaces)
(config.pepe.core.vhost.hosts // cfg.extraDomains)
));
# Generate all device views once
allDeviceViews = concatMapStrings generateDeviceViews allInterfaces;
# Function to generate configurations for all domains
generateCoreDNSConfig = domains:
let
generateForDomain = domain: conf:
concatMapStrings
(ifaceName: generateDomainConfig domain conf ifaceName)
conf.dnsInterfaces;
in
concatStrings (mapAttrsToList generateForDomain domains);
allDomains = config.pepe.core.vhost.hosts // cfg.extraDomains;
in
let
# Function to generate DNS records for individual devices based on dnsResolvableName
generateDeviceHostRecords =
let
generateRecordsForInterface = ifaceName: ifaceConfig:
lib.concatStringsSep "\n" (lib.mapAttrsToList
(deviceName: deviceConfig:
if deviceConfig.hostname!= null then
let
deviceAddress = deviceConfig.address;
deviceIface = ifaceConfig.net;
deviceHost = deviceConfig.hostname;
in
''
${deviceHost} {
view ${ifaceName} {
expr incidr(client_ip(), '${deviceIface}')
}
template IN A ${deviceHost} {
answer "${deviceHost}. 60 IN A ${deviceAddress}"
}
cache
log
}
''
else ""
)
ifaceConfig.devices);
in
lib.concatStringsSep "\n" (lib.mapAttrsToList generateRecordsForInterface config.pepe.core.network.interfaces);
in
''
${generateCoreDNSConfig allDomains}
${generateDeviceHostRecords}
${allDeviceViews}
. {
forward . tls://45.90.28.77 tls://45.90.30.77 {
tls_servername "lan-${cfg.nextDNSId}.dns.nextdns.io"
health_check 5s
}
}
'';
};
};
}

View File

@ -1,60 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.core.docker;
in
{
options.pepe.core.docker = with lib; {
enable = mkEnableOption "Enable Docker";
nvidia = mkEnableOption "Enable NVIDIA Container Toolkit";
dataRoot = mkOption {
type = types.str;
default = "/var/lib/docker";
description = "Docker data root directory";
};
extraOptions = mkOption {
type = types.str;
default = "";
description = "Extra options for Docker daemon";
};
enableOnBoot = mkOption {
type = types.bool;
default = false;
description = "Start Docker on boot";
};
iptables = mkOption {
type = types.bool;
default = false;
description = "Whether Docker should manipulate iptables";
};
users = mkOption {
type = types.listOf types.str;
default = [];
description = "Users to add to the docker group";
};
};
config = mkIf cfg.enable {
hardware.nvidia-container-toolkit.enable = cfg.nvidia;
virtualisation = {
oci-containers.backend = "docker";
docker = {
enable = true;
extraOptions = cfg.extraOptions;
enableOnBoot = cfg.enableOnBoot;
daemon.settings = {
iptables = cfg.iptables;
data-root = cfg.dataRoot;
};
};
};
users.users = lib.genAttrs cfg.users (user: {
extraGroups = [ "docker" ];
});
};
}

View File

@ -1,27 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types;
cfg = config.pepe.core.firewall;
in
{
options.pepe.core.firewall = {
openTCP = mkOption {
type = types.listOf types.int;
default = [ ];
description = "TCP ports to open in the firewall";
};
openUDP = mkOption {
type = types.listOf types.int;
default = [ ];
description = "UDP ports to open in the firewall";
};
};
config = {
networking.firewall = {
allowedTCPPorts = cfg.openTCP;
allowedUDPPorts = cfg.openUDP;
};
};
}

View File

@ -1,39 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.core.graphics;
in
{
options.pepe.core.graphics = with lib; {
enable = mkEnableOption "Enable graphics";
nvidia = mkEnableOption "Enable nvidia graphics";
};
config = mkIf cfg.enable {
hardware = {
graphics = {
enable = true;
extraPackages = with pkgs; mkIf cfg.nvidia [ vaapiVdpau ];
};
nvidia = mkIf cfg.nvidia {
modesetting.enable = true;
powerManagement.enable = false;
powerManagement.finegrained = false;
open = false;
nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.latest;
};
};
services = {
xserver = {
enable = true;
videoDrivers = mkIf cfg.nvidia [ "nvidia" ];
};
};
};
}

View File

@ -1,27 +0,0 @@
{ config, lib, ... }:
let
cfg = config.pepe.core.media;
in
{
options.pepe.core.media = with lib; {
enable = mkEnableOption "Enable media library";
group = mkOption {
type = types.str;
default = "media";
};
path = mkOption {
type = types.str;
default = null;
};
groupMembers = mkOption {
type = types.listOf types.str;
default = [];
};
};
config = lib.mkIf cfg.enable {
users.groups.${cfg.group}.members = [ cfg.group ] ++ cfg.groupMembers;
};
}

View File

@ -1,126 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types;
cfg = config.pepe.core.network;
in
{
options.pepe.core.network = {
interfaces = mkOption {
type = types.attrsOf (types.submodule {
options = {
interface = mkOption {
type = types.str;
description = "The network interface name.";
};
type = mkOption {
type = types.enum [ "lan" "wan" "vpn" ];
description = "The type of interface (lan, wan, or vpn).";
};
net = mkOption {
type = types.str;
description = "The network address in CIDR format.";
};
devices = mkOption {
type = types.attrsOf (types.submodule {
options = {
address = mkOption {
type = types.str;
description = "The IP address of the device.";
};
hostname = mkOption {
type = types.str;
description = "The hostname of the device.";
};
isEndpoint = mkOption {
type = types.bool;
default = false;
description = "Whether this device serves as a DNS endpoint for this interface.";
};
};
});
default = { };
description = "An attribute set of devices with their configurations.";
};
};
});
default = { };
description = "An attribute set of networks with their configurations.";
};
interfacesByType = mkOption {
type = types.attrsOf (types.listOf types.str);
default = { };
description = "Interfaces grouped by type (lan, wan, vpn) for easy access.";
internal = true;
};
dnsEndpoints = mkOption {
type = types.attrsOf (types.submodule {
options = {
interface = mkOption {
type = types.str;
description = "The interface this DNS endpoint belongs to.";
};
device = mkOption {
type = types.str;
description = "The device name that serves as the DNS endpoint.";
};
address = mkOption {
type = types.str;
description = "The IP address of the DNS endpoint.";
};
serverName = mkOption {
type = types.str;
default = "";
description = "DNS server name for TLS connections.";
};
};
});
default = { };
description = "DNS endpoints for each interface.";
internal = true;
};
};
config = {
# Create lists of interfaces by type for easy access elsewhere
pepe.core.network.interfacesByType = {
lan = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "lan") cfg.interfaces);
wan = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "wan") cfg.interfaces);
vpn = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "vpn") cfg.interfaces);
};
# Collect DNS endpoints from all interfaces
pepe.core.network.dnsEndpoints =
let
collectEndpoints = ifaceName: iface:
lib.mapAttrs'
(deviceName: device:
lib.nameValuePair
"${ifaceName}-${deviceName}"
{
interface = ifaceName;
device = deviceName;
address = device.address;
serverName = device.dnsServerName;
}
)
(lib.filterAttrs (_: device: device.isDnsEndpoint) iface.devices);
in
lib.foldl
(acc: ifaceName:
acc // (collectEndpoints ifaceName cfg.interfaces.${ifaceName})
)
{ }
(lib.attrNames cfg.interfaces);
};
}

View File

@ -1,131 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types mapAttrs concatMapStringsSep optionalString mkIf;
cfg = config.pepe.core.vhost;
in
{
options.pepe.core.vhost = {
hosts = mkOption {
type = types.attrsOf (types.submodule {
options = {
locations = mkOption {
type = types.attrsOf (types.submodule {
options = {
extraConfig = mkOption {
type = types.str;
description = "Extra configuration for the location.";
default = "";
};
proxyWebsockets = mkOption {
type = types.bool;
default = false;
};
host = mkOption {
type = types.str;
description = "The host for the location.";
default = "127.0.0.1";
};
port = mkOption {
type = types.int;
description = "The port number for the location.";
};
allow = mkOption {
type = types.listOf types.str;
default = [ ];
description = "IP address or CIDR block to allow.";
};
allowVPN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow VPN traffic.";
};
allowLAN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow LAN traffic.";
};
allowWAN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow WAN traffic. If false, deny all WAN traffic.";
};
path = mkOption {
type = types.str;
default = "";
};
recommendedProxySettings = mkOption {
type = types.bool;
default = true;
description = "Force the use of recommended proxy configuration.";
};
};
});
default = { };
description = "An attribute set of location configurations.";
};
resolveLocally = mkOption {
type = types.bool;
default = true;
description = "If true, ensure the vhost is resolvable to 127.0.0.1 locally on this server.";
};
};
});
default = { };
description = "An attribute set of domain configurations.";
};
};
config = {
pepe.core.firewall.openTCP = [ 80 443 ];
# Configure DNS entries for vhosts when DNS is enabled
pepe.core.dns = mkIf config.pepe.core.dns.enable {
extraDomains = mapAttrs
(domain: conf: {
dnsInterfaces =
(lib.optionals (lib.any (loc: loc.allowLAN) (lib.attrValues conf.locations))
config.pepe.core.network.interfacesByType.lan) ++
(lib.optionals (lib.any (loc: loc.allowVPN) (lib.attrValues conf.locations))
config.pepe.core.network.interfacesByType.vpn);
})
cfg.hosts;
};
services.nginx.virtualHosts = mapAttrs
(domain: conf: {
forceSSL = true;
useACMEHost = "giugl.io";
locations = mapAttrs
(path: location: {
proxyPass = "http://${location.host}:${toString location.port}${location.path}";
proxyWebsockets = location.proxyWebsockets;
recommendedProxySettings = location.recommendedProxySettings;
extraConfig = ''
${concatMapStringsSep "\n" (allowCIDR: "allow ${allowCIDR};") location.allow}
${optionalString location.allowLAN (concatMapStringsSep "\n" (ifaceName: "allow ${config.pepe.core.network.interfaces.${ifaceName}.net};") config.pepe.core.network.interfacesByType.lan)}
${optionalString location.allowVPN (concatMapStringsSep "\n" (ifaceName: "allow ${config.pepe.core.network.interfaces.${ifaceName}.net};") config.pepe.core.network.interfacesByType.vpn)}
${optionalString (!location.allowWAN) "deny all;"}
'' + location.extraConfig;
})
conf.locations;
})
cfg.hosts;
networking.extraHosts = lib.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList
(domain: hostConf:
lib.optionals hostConf.resolveLocally "127.0.0.1 ${domain}"
)
cfg.hosts
));
};
}

View File

@ -1,7 +0,0 @@
{ ... }: {
imports = [
./services
./core
];
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.bazarr;
in
{
options.pepe.services.bazarr = with lib; {
enable = mkEnableOption "Enable bazarr";
package = mkPackageOption pkgs "bazarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.bazarr = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 6767;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "bazarr" ];
};
}

View File

@ -1,20 +0,0 @@
{ ... }: {
imports = [
./bazarr
./gitea
./homeassistant
./immich
./jellyfin
./jellyseer
./lidarr
./llm
./minio
./navidrome
./nzbget
./prowlarr
./radarr
./redlib
./sonarr
./headscale
];
}

View File

@ -1,53 +0,0 @@
{ config, pkgs, lib, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.gitea;
in
{
options.pepe.services.gitea = with lib; {
enable = mkEnableOption "Enable gitea";
package = mkPackageOption pkgs "gitea" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
pepe.core = {
firewall.openTCP = [ config.services.gitea.settings.server.SSH_PORT ];
vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.gitea.settings.server.HTTP_PORT;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
};
services.gitea = {
enable = true;
package = cfg.package;
database.type = "sqlite3";
appName = "Gitea";
# https://github.com/NixOS/nixpkgs/issues/235442#issuecomment-1574329453
lfs.enable = true;
settings = {
server = {
DOMAIN = cfg.domain;
ROOT_URL = "https://${cfg.domain}";
SSH_PORT = 22;
HTTP_PORT = 3001;
};
service = {
DISABLE_REGISTRATION = true;
};
};
};
};
}

View File

@ -1,59 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.headscale;
in
{
options.pepe.services.headscale = with lib; {
enable = mkEnableOption "Enable Headscale";
package = mkPackageOption pkgs "headscale" { };
domain = mkOption {
type = types.str;
default = null;
description = "Domain for the Headscale service.";
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Host for the Headscale service.";
};
port = mkOption {
type = types.int;
default = 1194;
description = "Port for the Headscale service.";
};
settings = mkOption {
type = types.attrsOf types.anything;
default = { };
description = "Arbitrary configuration settings for Headscale.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
services.headscale = {
enable = true;
package = cfg.package;
port = cfg.port;
settings = cfg.settings;
};
pepe.core = {
firewall.openUDP = [ cfg.port ];
vhost.hosts.${cfg.domain} = {
locations."/" = {
host = cfg.host;
port = cfg.port;
allowWAN = true;
proxyWebsockets = true;
extraConfig = ''
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
proxy_buffering off;
'';
};
};
};
};
}

View File

@ -1,69 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.homeassistant;
in
{
options.pepe.services.homeassistant = with lib; {
enable = mkEnableOption "Enable Home Assistant";
package = mkPackageOption pkgs "home-assistant" { };
domain = mkOption {
type = types.str;
default = null;
};
extraComponents = mkOption {
type = types.listOf types.str;
default = [];
description = "Additional components to enable in Home Assistant";
};
extraPackages = mkOption {
type = types.functionTo (types.listOf types.package);
default = _: [];
description = "Additional Python packages for Home Assistant";
};
config = mkOption {
type = types.attrs;
default = {};
description = "Home Assistant configuration";
};
};
config = mkIf cfg.enable {
services.home-assistant = {
enable = true;
package = cfg.package;
extraComponents = cfg.extraComponents;
extraPackages = cfg.extraPackages;
config = cfg.config;
};
services.mosquitto = {
enable = true;
listeners = [
{
address = "127.0.0.1";
acl = [ "pattern readwrite #" ];
omitPasswordAuth = true;
settings.allow_anonymous = true;
}
];
};
services.go2rtc = {
enable = true;
settings.api.listen = "127.0.0.1:1984";
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.home-assistant.config.http.server_port or 8123;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
};
};
};
}

View File

@ -1,42 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.pepe.services.immich;
in
{
options.pepe.services.immich = with lib; {
enable = mkEnableOption "Enable immich";
package = mkPackageOption pkgs "immich" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = lib.mkIf cfg.enable {
services = {
immich = {
enable = true;
package = cfg.package;
# accelerationDevices = null;
};
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
host = "[::1]";
port = config.services.immich.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
extraConfig = ''
# allow large file uploads
client_max_body_size 50000M;
'';
};
};
users.users.immich.extraGroups = [ "video" "render" "media" "nextcloud" ];
};
}

View File

@ -1,63 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf mkForce;
cfg = config.pepe.services.jellyfin;
in
{
options.pepe.services.jellyfin = with lib; {
enable = mkEnableOption "Enable jellyfin";
package = mkPackageOption pkgs "jellyfin" { };
domain = mkOption {
type = types.str;
default = null;
};
tmpfsSize = mkOption {
type = types.str;
default = "20G";
description = "Size of the tmpfs mount for Jellyfin";
};
};
config = mkIf cfg.enable {
# needed since StateDirectory does not accept symlinks
systemd.services.jellyfin.serviceConfig.StateDirectory = mkForce "";
pepe.core.vhost.hosts.${cfg.domain} = {
locations = {
"/" = {
port = 8096;
allowLAN = true;
allowVPN = true;
};
"/socket" = {
port = 8096;
allowLAN = true;
allowVPN = true;
proxyWebsockets = true;
};
};
};
services.jellyfin = {
enable = true;
group = "media";
package = cfg.package;
};
users.groups = {
video.members = [ "jellyfin" ];
render.members = [ "jellyfin" ];
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "jellyfin" ];
fileSystems."/tmp/jellyfin" = {
device = "none";
fsType = "tmpfs";
options = [ "defaults" "size=${cfg.tmpfsSize}" "uid=jellyfin" ];
};
};
}

View File

@ -1,32 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.jellyseer;
in
{
options.pepe.services.jellyseer = with lib; {
enable = mkEnableOption "Enable jellyseer";
package = mkPackageOption pkgs "jellyseerr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.jellyseerr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.jellyseerr.port;
allowLAN = true;
allowVPN = true;
};
};
};
}

View File

@ -1,36 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.lidarr;
in
{
options.pepe.services.lidarr = with lib; {
enable = mkEnableOption "Enable lidarr";
package = mkPackageOption pkgs "lidarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.lidarr = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = with config.pepe.core.network; {
dnsInterfaces = [ interfaceTypes.lan interfaceTypes.vpn ];
locations."/" = {
port = 8686;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "lidarr" ];
};
}

View File

@ -1,110 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf mkEnableOption mkPackageOption mkOption types optionalAttrs mkMerge;
cfg = config.pepe.services.llm;
in
{
options.pepe.services.llm = {
enable = mkEnableOption "Enable LLM backend service (Ollama)";
package = mkPackageOption pkgs "ollama" { };
backendDomain = mkOption {
type = types.nullOr types.str;
default = null;
description = "Domain for Ollama backend. If null, no vhost is created for the backend.";
};
acceleration = mkOption {
type = types.enum [ "none" "cuda" "rocm" ];
default = "none";
description = "Acceleration type for Ollama";
};
environmentVariables = mkOption {
type = types.attrsOf types.str;
default = {
OLLAMA_FLASH_ATTENTION = "1";
OLLAMA_NUM_PARALLEL = "2";
OLLAMA_KV_CACHE_TYPE = "q8_0";
};
description = "Environment variables for Ollama";
};
frontend = {
enable = mkEnableOption "Enable LLM frontend service (Open WebUI)"; # Defaults to false
uiPackage = mkPackageOption pkgs "open-webui" { };
tikaPackage = mkPackageOption pkgs "tika" { }; # Tika for document processing with Open WebUI
domain = mkOption {
type = types.nullOr types.str;
default = null;
description = "Domain for Open WebUI frontend. If null, no vhost is created for the frontend.";
};
};
};
config = mkMerge [
# Combined environment packages
{
environment.systemPackages =
(if cfg.enable then [ cfg.package ] else [ ]) ++
(if cfg.enable && cfg.frontend.enable then [ cfg.frontend.uiPackage cfg.frontend.tikaPackage ] else [ ]);
}
# Backend Ollama Service Configuration
(mkIf cfg.enable {
services.ollama = {
enable = true;
package = cfg.package;
acceleration = cfg.acceleration;
environmentVariables = cfg.environmentVariables;
};
pepe.core.vhost.hosts = optionalAttrs (cfg.backendDomain != null) {
"${cfg.backendDomain}" = {
locations."/" = {
host = config.services.ollama.host;
port = config.services.ollama.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
recommendedProxySettings = false;
extraConfig = ''
proxy_buffering off;
proxy_read_timeout 600s; # Ollama can take time to respond
proxy_set_header Host localhost:${toString config.services.ollama.port};
'';
};
};
};
})
# Frontend Open WebUI and Tika Service Configuration
(mkIf (cfg.enable && cfg.frontend.enable) {
services.open-webui = {
enable = true;
package = cfg.frontend.uiPackage;
};
services.tika = {
enable = true;
package = cfg.frontend.tikaPackage;
};
pepe.core.vhost.hosts = optionalAttrs (cfg.frontend.domain != null) {
"${cfg.frontend.domain}" = {
locations."/" = {
host = config.services.open-webui.host;
port = config.services.open-webui.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
};
};
};
})
];
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.minio;
in
{
options.pepe.services.minio = with lib; {
enable = mkEnableOption "Enable MinIO S3-compatible object storage";
package = mkPackageOption pkgs "minio" {};
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.minio = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 9000;
allowLAN = true;
allowVPN = true;
extraConfig = ''
client_max_body_size 500M;
'';
};
};
};
}

View File

@ -1,84 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.navidrome;
in
{
options.pepe.services.navidrome = with lib; {
enable = mkEnableOption "Enable navidrome";
package = mkPackageOption pkgs "navidrome" { };
domain = mkOption {
type = types.str;
default = null;
};
musicFolder = mkOption {
type = types.str;
default = "/media/Music";
description = "Path to the music library";
};
beetsConfig = mkOption {
type = types.str;
default = "/media/beets.conf";
description = "Path to the beets configuration file";
};
settings = mkOption {
type = types.attrs;
default = {};
description = "Additional settings for Navidrome";
};
};
config = mkIf cfg.enable {
services.navidrome = {
enable = true;
package = cfg.package;
settings = {
MusicFolder = cfg.musicFolder;
LastFM.enable = true;
LastFM.ApiKey = "5cef5cb5f9d31326b97d0f929ca9cf20";
LastFM.Secret = "d1296896126f4caae47407aecf080b25";
Spotify.ID = "3900c029b4f34f3fb61d554dda64794d";
Spotify.Secret = "d931ce5575a9401aa5ff8d37558cca0a";
EnableGravatar = true;
LogLevel = "WARN";
} // cfg.settings;
};
pepe.core.vhost.hosts.${cfg.domain} = with config.pepe.core.network; {
dnsInterfaces = [ interfaceTypes.lan interfaceTypes.vpn ];
locations."/" = {
port = 4533;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
systemd.services = {
"beets-update" = {
enable = true;
before = [ "beets-import.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.beets}/bin/beet -c ${cfg.beetsConfig} update";
};
};
"beets-import" = {
enable = true;
path = [ pkgs.imagemagick ];
requires = [ "beets-update.service" ];
after = [ "beets-update.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.beets}/bin/beet -c ${cfg.beetsConfig} import --flat -q ${cfg.musicFolder}";
};
startAt = "weekly";
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "navidrome" ];
};
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.nzbget;
in
{
options.pepe.services.nzbget = with lib; {
enable = mkEnableOption "Enable nzbget";
package = mkPackageOption pkgs "nzbget" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.nzbget = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 6789;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "nzbget" ];
};
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.prowlarr;
in
{
options.pepe.services.prowlarr = with lib; {
enable = mkEnableOption "Enable prowlarr";
package = mkPackageOption pkgs "prowlarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.prowlarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 9696;
allowLAN = true;
allowVPN = true;
proxyWebsockets = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "prowlarr" ];
};
}

View File

@ -1,34 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.radarr;
in
{
options.pepe.services.radarr = with lib; {
enable = mkEnableOption "Enable radarr";
package = mkPackageOption pkgs "radarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.radarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 7878;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ config.services.radarr.group ];
};
}

View File

@ -1,48 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.redlib;
in
{
options.pepe.services.redlib = with lib; {
enable = mkEnableOption "Enable Redlib";
package = mkPackageOption pkgs "redlib" { };
domain = mkOption {
type = types.str;
default = null;
};
settings = mkOption {
type = types.attrsOf types.str;
default = {
REDLIB_ROBOTS_DISABLE_INDEXING = "on";
REDLIB_DEFAULT_THEME = "dracula";
REDLIB_DEFAULT_SHOW_NSFW = "on";
REDLIB_DEFAULT_BLUR_NSFW = "off";
REDLIB_DEFAULT_USE_HLS = "on";
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION = "on";
};
description = "Environment variables for Redlib configuration";
};
};
config = mkIf cfg.enable {
services.redlib = {
enable = true;
port = 9090;
package = cfg.package;
};
systemd.services.redlib.environment = cfg.settings;
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.redlib.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
};
}

View File

@ -1,34 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.sonarr;
in
{
options.pepe.services.sonarr = with lib; {
enable = mkEnableOption "Enable sonarr";
package = mkPackageOption pkgs "sonarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.sonarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 8989;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ config.services.sonarr.group ];
};
}

View File

@ -4,22 +4,22 @@ let
giuglioDomain = "giugl.io";
in
{
age.secrets.cloudflare = {
file = ../secrets/cloudflare.age;
age.secrets.ovh = {
file = ../secrets/ovh.age;
owner = "acme";
};
security.acme = {
acceptTerms = true;
certs.${giuglioDomain} =
{
dnsProvider = "cloudflare";
environmentFile = config.age.secrets.cloudflare.path;
dnsProvider = "ovh";
environmentFile = config.age.secrets.ovh.path;
extraDomainNames = [ "*.${giuglioDomain}" ];
};
defaults = {
email = "letsencrypt@depasquale.giugl.io";
dnsProvider = "cloudflare";
environmentFile = config.age.secrets.cloudflare.path;
dnsProvider = "ovh";
environmentFile = config.age.secrets.ovh.path;
};
};
}

View File

@ -8,7 +8,7 @@ in
sessionVariables = {
AICHAT_CONFIG_DIR = configDir;
};
packages = [ pkgs.aichat ];
packages = [ pkgs.unstablePkgs.aichat ];
file.".config/aichat/config.yaml".text = lib.readFile ./aichat/config.yaml;
file.".config/aichat/roles/commitmessage.md".text = lib.readFile ./aichat/roles/commitmessage.md;

View File

@ -15,7 +15,9 @@ let
tree
] ++ lib.optional (!pkgs.stdenv.isDarwin) pastebinit;
unstablePkgs = with pkgs.unstablePkgs; [ aider-chat ];
unstablePkgs = with pkgs.unstablePkgs; [
aider-chat-full
];
in
{
imports = [
@ -26,7 +28,7 @@ in
home = {
packages = stablePkgs ++ unstablePkgs;
stateVersion = "25.05";
stateVersion = "24.11";
};
}

View File

@ -1,28 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QXZdow
pLJHkOUl7t8yhuq+vytj+LHENNT71NJH/gS4cEMNhEkzZjc8ScSrhVgLl/TMGImh
cb6yfRIccymVLL8gdXitIAk1srZjuwiZrFK4Ril/x8RKfV50GrHDMf+8Os3QNE05
VprnYCbyKj05+/fHSSUDnQmAAe7wvi4iA2Aa5E4DWE7t+QcMB5epPMBdJoWX6ELS
cYI7nDu32AIkYCODht9B5SChbwHACLqqG8CV5N2TfRBi6v7R2FaLhbbRvdAIQWQc
x2TGI1utRURtxgs/WhQ6Sco1QNrIEO89a/78OGxrIT/PMAukvx2J2WG6iH/2LgX0
H39680n3/YTTmL4OMWW+hKlzv4tdBsbgjR0sKRI/OzDrn/s0ZzA1JACwUn2ndOS/
0EcvW5VLLd2PHvwABm2Biw7mhilC4D6d9YZu/9uFbmPCzlXO3zmeeSnw630gDWxX
CRTEQMNE7+cdQy8j/upDBQOBnSSgmBMriuklsgcPZGnTkxWM7b9NOAeXavTucXhD
9PART4hQomlvGBzKM6Ien9kgeDG9OA9WkM+2rvLWQLDy3T9Qo37d/4qLg3sPNr4M
NUUSKRQKnA7i126ZrX/mUxcp3kJkCAsMX4GF6VMVobXshCSHqKhEWKwHuyPWI4rn
gHUqvSMKDujkA6v+9UaZESQt8jKl+N44MDUMdfYwPyE
-> ssh-ed25519 7eGqHw WIUvcH8xsrI0AcutXKxwhbrZifrb8vntIWCGDUW3qiI
3IuBdZRAGA/nfEuakP2/E+QA07jBJjBlNtotGLZlkZQ
-> ssh-rsa tO3rGg
DOBhMzioILjlrOAdycMzSwDxgww5p8QAlV/L7ECBrBFscoWageW4GOyZVjtE/7Ot
beG59oP8ra4lczXfPSOtY2qz8pN59vqw3WqG3TPMvR8xZDslVXE8nubfkoUohsG8
1ZibaP/ygQSYHUfnLzebYBhk1N4u6jeIcxJXgTLhWLlvkv9+AKAcoHhSWVbKMbz+
i8WLECJHuOeR43hJxuxvkPTWsu6HUB+yXT1J+dPcIe9W20CqX+CN0pjwSUJu0T0A
tZf2NfkYmFnho2IEdxdKnGRDE2lvQmZjf1oQn7GWFqywlVB3eCOR6K/s1Tb4I2W1
wybLuEndQBnUsUDQVs34OeN/5L3Pw40zksFSSVe3Fu5XxmWl1WemGWp1/jB7j6u7
7bnOlAMUzpqQ9yom0oxXYwEC9pUtqPwYGKeAV9o5U3vVXMDWtEXXMCe0oDjJY8js
e5xSOuaMi9CcVkU+HyjrEgw0uS4ymDWVghT4IVl8zNWeare5ALMcR/290xffaQyg
--- DIW17nFtjI4Oo+Qoi0YzeLH8A6IVAQwhtELkXWWhtwo
D*;ôCx<08>Auçd rß¾ö0ð£ú1Ldæoòr´&<26>JL4WíÌ!²=ê7:pÀØí*ûpVÙK³,0®rλâãu¼¡ã­¶óWEâ{£q½7,Ôßÿk“¾²<C2BE>ºÎàäxÔ+^xDâº0g“K,ª(zØwáC˘þ è{d}-FÜ
õ<EFBFBD>Ëþ¦TÌÍA„

29
secrets/ovh.age Normal file
View File

@ -0,0 +1,29 @@
age-encryption.org/v1
-> ssh-rsa QXZdow
aYgowxTfdGOqTYOZBbkg/dH7f+m6nvVF/8qZX0DE4hazln/QS9maWbkOwD7FLldm
HRNV/YwZZEhbujHbDqgxnXk7Q11KOA72864B6mF2VZUruyo0cnACqo7OyzwApqv/
+LPjGb9h/gCJpQ3a5Jdh202FfaNGAh358fZVDyd37XPSOykiIAAxgMlDyn+96OiM
P2vsyduWXDsqzCqtiNQrKVjryI5CIGOTAcYTgQ35S3uXFD8Gu27KfagUwZp2hdyp
3WmGl+ZTrPNdOwzLWGj/RXaeTslABn1Owmq1naASRvJpp97ToynRzkDA50rBqUyR
vGVB9IJxSjkSm3BJ4UAI6rpoz/6t2jkfNNE1cPix4AYjPAMyU+uiUSaZ/UBkwlXw
08rM1eGcBaErB1ExcDV5+jUCdJBfi6Q9vIG7Ty4wbN1PfztAhzEyzT0L1bTn1AKC
4S9n5lqFa1CdraK9eh2A+o9CNlkta+Z24ctPTVqBYtImBTKHOTofhr0omQdFV6M2
bhxsOoAAoNhwn/lWC2fAcgfPQrUOW524+eHyPjsvf4rNNv0bk5EP1J4vMrWr9rqJ
v5GEQ77YVXYQthiyg74XYc3Eo8sbtE+ncDoOquzdT385POd870qi1ht+JMY6OEmj
q8lxVau2SFTKPkkmZKmtoNrYdKp5+DsB3nOUKcIXofs
-> ssh-ed25519 7eGqHw cCrhq1kfav4TYAUOpP4O6fQ958O37Uad2jX9SUrnxn4
TSiMyrYsdblB5SFwZpw7HhmicWX1vNomhBP4HtlvHJo
-> ssh-rsa tO3rGg
J6oPMt6hiry6ks3hlAjUAY1AzEYU+7voto5XC+I6Fmyfabz9zaJ3TtbCPVF5BRNR
DOYLiD24EbcVoqECn2A2MRK1xH4owBD5YaE3Il2NwSJHhC+ZhROaMTu5mHxbzK/u
BF2MLRZ0Bwwq4szaHoFf12TFwNtIRZXS9m6l4jHdsxWj6x0iui18p3JLxij1cVwE
03rSWz+9c8bpZ6LHuPJAhatBZHSZwkKwH8Dn8NOxCLmVNRM4PyvJsj9lRn7fMwRY
64QI2z6bRAry6oINbVAAOsPlM0Ix+7hbFs/UstnENFqfcDvPzrrhALDhuDLIJpGu
WgAaMStZGjydy0oqHJceuduxVreqTlfiki7yruRFqRBgjMopwOsw5i9UPWR6SZ+E
cUCFeEynUMrmFSp5qvDX0WtkU2G/GRFEPaB+k+UN+JduIRb2RBCLt2uG0249TwO8
T4sq098XTM8wARgOv6n51lHFCPpM3iSbP5KMCYH9FhsJV0Qu9Q7157McNZuVL9Ie
--- KYLAPCcTkg/tF2c2ni4UaBTV5AhUleg8GgJH0oRQSK0
½;¬jŒ<6A>a羄ïÓÄ<C393>5`hÂŒø»æy;JúãÈå³C¢µ‡£ÏwX:eßøw³ù»ÜH
L<EFBFBD>he­jCÓ2¨ì"#˵„=Î/Dzˆ1ÒÅÿ¼™^Nû$ÃéM·úqN…v1µØÁÇ”ç¸T¦ÌñÙ—Ç0FsÕ(WeõöË…¡˜Ý8|^iYFQæ3œ ¡Õ­
A¤1­ïEÜÂÚM_=;•¸×jFÜVý[Ýät°¬{© w×…Ê<E280A6>Ö)

View File

@ -11,5 +11,5 @@ in
"nextcloud-database.age".publicKeys = pubkeys;
"restic-environment.age".publicKeys = pubkeys;
"restic-passwords.age".publicKeys = pubkeys;
"cloudflare.age".publicKeys = pubkeys;
"ovh.age".publicKeys = pubkeys;
}

View File

@ -1,62 +1,14 @@
#!/usr/bin/env bash
RELEASE=25.05
# this guy updates the hash of the last
# successful build of nixpkgs on hydra
# (so we don't have to rebuild EVERYTHING)
RELEASE=24.11
update_channel() {
local channel_name="$1" # Hydra channel name (e.g., nixos-24.11)
local flake_input_name="$2" # Flake input name (e.g., nixpkgs)
local new old
new=$(curl -sL "https://monitoring.nixos.org/prometheus/api/v1/query?query=channel_revision" | jq -r ".data.result[] | select(.metric.channel==\"nixos-${RELEASE}\") | .metric.revision")
old=$(jq -r ".nodes.nixpkgs.locked.rev" flake.lock)
# Get latest revision from Hydra
new=$(curl -sL "https://monitoring.nixos.org/prometheus/api/v1/query?query=channel_revision" |
jq -r ".data.result[] | select(.metric.channel==\"${channel_name}\") | .metric.revision")
echo "Old hash: ${old}"
echo "New hash: ${new}"
# Get current revision from flake.lock using input name
old=$(jq -r ".nodes as \$nodes |
.nodes.root.inputs.[\"${flake_input_name}\"] as \$target_input |
\$nodes | to_entries[] |
select(.key == \$target_input) |
.value.locked.rev" flake.lock)
if [ "${old}" == "${new}" ]; then
return
fi
replace_hash "${flake_input_name}" "${new}"
echo "${new}"
}
replace_hash() {
local name="$1" hash="$2"
sed -i "s|\(${name}\.url = \"github:NixOS/nixpkgs/\)[^\"]*|\1${hash}|" flake.nix
# sed -i "s|${name}/${old}|${name}/${new}|" flake.nix
}
update_stable() {
echo "Checking NixOS ${RELEASE}..."
new_hash=$(update_channel "nixos-${RELEASE}" "nixpkgs")
if [ -z "$new_hash" ]; then
return
fi
echo "Updated stable to: ${new_hash}"
}
update_unstable() {
echo "Checking unstable..."
new_hash=$(update_channel "nixos-unstable" "nixos-unstable")
if [ -z "$new_hash" ]; then
return
fi
echo "Updated unstable to: ${new_hash}"
}
update_stable
update_unstable
sed -i s/"${old}"/"${new}"/ flake.nix