Compare commits

..

12 commits

Author SHA1 Message Date
Chris Kruining
9aaf0f0a2b
asdffasdfa 2025-08-13 07:41:30 +02:00
Chris Kruining
5d8c897b4d
update the path to system secrets, still need to fix the home secrets 2025-08-11 16:18:53 +02:00
Chris Kruining
3a6672cad9
get going with sops agian, not that hard, just need to set up my keys properly... 2025-08-11 15:22:58 +02:00
Chris Kruining
69c6d85754
resolve merge artifacts 2025-08-11 15:22:17 +02:00
Chris Kruining
de1bc287d5
reorder inputs 2025-08-07 11:59:22 +02:00
Chris Kruining
4bd4327a6d
Merge branch 'feature/nix-anywhere' of https://github.com/chris-kruining/sneeuwvlok into feature/nix-anywhere 2025-08-07 11:48:27 +02:00
Chris Kruining
7e6beb208d
kaas 2025-08-07 11:48:23 +02:00
Chris Kruining
cfb9d086b8
yep yep, justfiles are cooooool 2025-08-07 11:48:23 +02:00
Chris Kruining
a1316fdf0e
update deps 2025-08-07 11:48:23 +02:00
Chris Kruining
98362802d5
kaas 2025-08-07 11:02:45 +02:00
Chris Kruining
3921693f84
yep yep, justfiles are cooooool 2025-08-04 16:21:29 +02:00
Chris Kruining
8228418b7f
update deps 2025-08-04 16:21:17 +02:00
76 changed files with 661 additions and 4354 deletions

2
.envrc
View file

@ -1,2 +0,0 @@
# shellcheck shell=bash
use flake

View file

@ -1,15 +0,0 @@
name: Test action
on:
workflow_dispatch:
push:
branches:
- main
jobs:
kaas:
runs-on: nix
steps:
- name: Echo
run: |
nix --version

4
.gitattributes vendored
View file

@ -1,4 +0,0 @@
* text=auto
core.autocrlf=false
core.eol=lf
core.filemode=false

8
.gitignore vendored
View file

@ -1,8 +1,2 @@
# ---> Nix
# Ignore build outputs from performing a nix-build or `nix build` command
result
result-*
# Ignore automatically generated direnv output
.direnv
*.qcow2

View file

@ -1,34 +0,0 @@
def RESET: "0";
def BOLD: "1";
def DIM: "2";
def ITALIC: "3";
def UNDERLINE: "4";
def BLINKING: "5";
def INVERSE: "7";
def HIDDEN: "8";
def STRIKETHROUGH: "9";
def RESET_FONT: "22";
def BLACK: 0;
def RED: 1;
def GREEN: 2;
def YELLOW: 3;
def BLUE: 4;
def MAGENTA: 5;
def CYAN: 6;
def WHITE: 7;
def DEFAULT: 9;
def foreground(color): 30 + color;
def background(color): 40 + color;
def bright(color): 60 + color;
def escape(options):
(if ((options|type) == "array") then options else [options] end) as $o
| "\u001b[\($o | map(tostring) | join(";"))m";
def style(options): escape(options) + . + escape([RESET]);
def to_title:
(.|ascii_upcase) as $str
| escape([BOLD, foreground(BLACK), background(WHITE)]) + " " + $str + " " + escape([RESET]);

View file

@ -1,59 +0,0 @@
import "format" as _ {search:"./"};
def n_max(limit):
if . > limit then limit else . end;
def n_min(limit):
if . < limit then limit else . end;
def pad_right(width):
(. | tostring) as $s
| ($s | length) as $l
| ((width - $l) | n_min(0)) as $w
| ($s + (" " * $w));
def to_cells(sizes; fn):
to_entries
| map(
(sizes[.key]) as $size
| (" " + .value)
| pad_right($size + 2)
| fn // .
);
def to_cells(sizes): to_cells(sizes; null);
def to_line(left; joiner; right):
[left, .[1], (.[1:] | map([joiner, .]) ), right] | flatten | join("");
def create(data; header_callback; cell_callback):
(data[0] | to_entries | map(.key)) as $keys
| ([$keys]) as $header
| (data | map(to_entries | map(.value))) as $rows
| ($header + $rows) as $cells
| (
$keys # Use keys so that we have an array of the correct size
| to_entries
| map(
(.key) as $i
| $cells
| map(.[$i] | length)
| max
)
) as $column_sizes
| (
[
($column_sizes | map("═" * (. + 2)) | to_line("╔"; "╤"; "╗")),
($keys | to_cells($column_sizes; header_callback) | to_line("║"; "│"; "║")),
($rows | map([
($column_sizes | map("─" * (. + 2)) | to_line("╟"; "┼"; "╢")),
(. | to_cells($column_sizes; cell_callback) | to_line("║"; "│"; "║"))
])),
($column_sizes | map("═" * (. + 2)) | to_line("╚"; "╧"; "╝"))
]
| flatten
| join("\n")
);
def create(data; header_callback): create(data; header_callback; null);
def create(data): create(data; _::style(_::BOLD); null);

View file

@ -1,11 +0,0 @@
@_default: list
[doc('List machines')]
@list:
ls -1 ../systems/x86_64-linux/
[doc('Update the target machine')]
[no-exit-message]
@update machine:
just assert '-d "../systems/x86_64-linux/{{ machine }}"' "Machine {{ machine }} does not exist, must be one of: $(ls ../systems/x86_64-linux/ | sed ':a;N;$!ba;s/\n/, /g')"
nixos-rebuild switch -L --sudo --target-host {{ machine }} --flake ..#{{ machine }}

View file

@ -1,98 +0,0 @@
set unstable := true
set quiet := true
_default:
just --list
[script]
list:
cd .. && just vars get ulmo zitadel/users | jq -r -C '
import ".jq/table" as table;
import ".jq/format" as f;
fromjson
| to_entries
| sort_by(.key)
| map(
(.key|f::to_title) + ":\n"
+ table::create(
.value
| to_entries
| sort_by(.key)
| map({username:.key} + .value)
)
)
| join("\n\n┄┄┄\n\n")
';
[script]
add:
exec 5>&1
pad () { [ "$#" -gt 1 ] && [ -n "$2" ] && printf "%$2.${2#-}s" "$1"; }
input() {
local label=$1
local value=$2
local res=$(gum input --header "$label" --value "$value")
echo -e "\e[2m$(pad "$label" -11)\e[0m$res" >&5
echo $res
}
data=`cd .. && just vars get ulmo zitadel/users | jq 'fromjson'`
# Gather inputs
org=`
jq -r 'to_entries | map(.key)[]' <<< "$data" \
| gum choose --header 'Which organisation to save to?' --select-if-one
`
username=`input 'user name' 'new-user'`
email=`input 'email' 'new.user@example.com'`
first_name=`input 'first name' 'John'`
last_name=`input 'last name' 'Doe'`
user_exists=`jq --arg 'org' "$org" --arg 'username' "$username" '.[$org][$username]? | . != null' <<< "$data"`
if [ "$user_exists" == "true" ]; then
gum confirm 'User already exists, overwrite it?' --padding="1 1" || exit 0
fi
next=`
jq \
--arg 'org' "$org" \
--arg 'username' "$username" \
--arg 'email' "$email" \
--arg 'first_name' "$first_name" \
--arg 'last_name' "$last_name" \
--compact-output \
'.[$org] += { $username: { email: $email, firstName: $first_name, lastName: $last_name } }' \
<<< $data
`
gum spin --title "saving..." -- echo "$(cd .. && just vars set ulmo 'zitadel/users' "$next")"
[script]
remove:
data=`cd .. && just vars get ulmo zitadel/users | jq fromjson`
# Gather inputs
org=`
jq -r 'to_entries | map(.key)[]' <<< "$data" \
| gum choose --header 'Which organisation?' --select-if-one
`
user=`
jq -r --arg org "$org" '.[$org] | to_entries | map(.key)[]' <<< "$data" \
| gum choose --header 'Which user?' --select-if-one
`
next=`
jq \
--arg 'org' "$org" \
--arg 'user' "$user" \
--compact-output \
'del(.[$org][$user])' \
<<< $data
`
gum spin --title "saving..." -- echo "$(cd .. && just vars set ulmo 'zitadel/users' "$next")"

View file

@ -1,33 +0,0 @@
set unstable := true
set quiet := true
base_path := invocation_directory() / "systems/x86_64-linux"
_default:
just --list
[doc('list all vars of the target machine')]
list machine:
sops decrypt {{ base_path }}/{{ machine }}/secrets.yml
edit machine:
sops edit {{ base_path }}/{{ machine }}/secrets.yml
@set machine key value:
sops set {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')" "\"$(echo '{{ value }}' | sed 's/\"/\\\"/g')\""
git add {{ base_path }}/{{ machine }}/secrets.yml
git commit -m 'chore(secrets): set secret "{{ key }}" for machine "{{ machine }}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null
echo "Done"
get machine key:
sops decrypt {{ base_path }}/{{ machine }}/secrets.yml | yq ".$(echo "{{ key }}" | sed -E 's/\//./g')"
remove machine key:
sops unset {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')"
git add {{ base_path }}/{{ machine }}/secrets.yml
git commit -m 'chore(secrets): removed secret "{{ key }}" from machine "{{ machine }}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null
echo "Done"

View file

@ -1,36 +0,0 @@
@_default:
just --list --list-submodules
[doc('Manage vars')]
mod vars '.just/vars.just'
[doc('Manage users')]
mod users '.just/users.just'
[doc('Manage machines')]
mod machine '.just/machine.just'
[doc('Show information about project')]
@show:
echo "show"
[doc('update the flake dependencies')]
@update:
nix flake update
git commit -m 'chore: update dependencies' -- ./flake.lock > /dev/null
echo "Done"
[doc('Introspection on flake output')]
@select key:
nix eval --show-trace --json .#{{ key }} | jq .
#===============================================================================================
# Utils
#===============================================================================================
[no-exit-message]
[no-cd]
[private]
@assert condition message:
[ {{ condition }} ] || { echo -e 1>&2 "\n\x1b[1;41m Error \x1b[0m {{ message }}\n"; exit 1; }

View file

@ -1,11 +0,0 @@
keys:
- &ulmo_1 age19qfpf980tadguqq44zf6xwvjvl428dyrj46ha3n6aeqddwhtnuqqml7etq
- &ulmo_2 age1ewes0f5snqx3sh5ul6fa6qtxzhd25829v6mf5rx2wnheat6fefps5rme2x
creation_rules:
# All Machine secrets
- path_regex: systems/[^/]+/[^/]+/[^/]+\.(yml|yaml)$
key_groups:
- age:
- *ulmo_1
- *ulmo_2

57
.sops.yml Normal file
View file

@ -0,0 +1,57 @@
keys:
- home:
- &chris age1ewes0f5snqx3sh5ul6fa6qtxzhd25829v6mf5rx2wnheat6fefps5rme2x
- system:
- &aule age
- &mandos age
- &manwe age10c5hmykkduvy75yvqfnchm5lcesr5puarhkwp4l7xdwpykdm397q6xdxuy
- &melkor age
- &orome age
- &tulkas age
- &varda age
- &yavanna age1ewes0f5snqx3sh5ul6fa6qtxzhd25829v6mf5rx2wnheat6fefps5rme2x
creation_rules:
#===================================================================
# HOSTS
#===================================================================
- path_regex: systems/x86_64-linux/aule/secrets.yaml$
age: *aule
- path_regex: systems/x86_64-linux/mandos/secrets.yaml$
age: *mandos
- path_regex: systems/x86_64-linux/manwe/secrets.yaml$
key_groups:
- age:
- *manwe
- *yavanna
- path_regex: systems/x86_64-linux/melkor/secrets.yaml$
age: *melkor
- path_regex: systems/x86_64-linux/orome/secrets.yaml$
age: *orome
- path_regex: systems/x86_64-linux/tulkas/secrets.yaml$
age: *tulkas
- path_regex: systems/x86_64-linux/varda/secrets.yaml$
age: *varda
- path_regex: systems/x86_64-linux/yavanna/secrets.yaml$
age: *yavanna
#===================================================================
# USERS
#===================================================================
- path_regex: homes/x86_64-linux/chris@\w+/secrets.yaml$
age: *chris

View file

@ -18,4 +18,5 @@ nix build .#install-isoConfigurations.minimal
- [dafitt/dotfiles](https://github.com/dafitt/dotfiles/)
- [khaneliman/khanelinix](https://github.com/khaneliman/khanelinix)
- [alex007sirois/nix-config](https://github.com/alex007sirois/nix-config) (justfile)
- [hmajid2301/nixicle](https://gitlab.com/hmajid2301/nixicle) (the GOAT, he did what I am aiming for!)

659
flake.lock generated

File diff suppressed because it is too large Load diff

117
flake.nix
View file

@ -9,6 +9,11 @@
inputs.nixpkgs.follows = "nixpkgs";
};
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
@ -25,13 +30,13 @@
inputs.nixpkgs.follows = "nixpkgs";
};
# neovim
nvf.url = "github:notashelf/nvf";
# plymouth theme
nixos-boot.url = "github:Melkor333/nixos-boot";
firefox.url = "github:nix-community/flake-firefox-nightly";
nixos-wsl = {
url = "github:nix-community/nixos-wsl";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-compat.follows = "";
};
};
stylix.url = "github:nix-community/stylix";
@ -41,10 +46,13 @@
inputs.nixpkgs.follows = "nixpkgs";
};
zen-browser = {
url = "github:0xc000022070/zen-browser-flake";
inputs.nixpkgs.follows = "nixpkgs";
};
# neovim
nvf.url = "github:notashelf/nvf";
# plymouth theme
nixos-boot.url = "github:Melkor333/nixos-boot";
zen-browser.url = "github:MarceColl/zen-browser-flake";
nix-minecraft.url = "github:Infinidoge/nix-minecraft";
@ -70,73 +78,38 @@
grub2-themes = {
url = "github:vinceliuice/grub2-themes";
};
nixos-wsl = {
url = "github:nix-community/nixos-wsl";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-compat.follows = "";
};
};
terranix = {
url = "github:terranix/terranix";
inputs.nixpkgs.follows = "nixpkgs";
};
clan-core = {
url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
inputs.nixpkgs.follows = "nixpkgs";
};
mydia = {
url = "github:chris-kruining/mydia";
# url = "github:getmydia/mydia";
};
};
outputs = inputs:
inputs.snowfall-lib.mkFlake {
inherit inputs;
src = ./.;
outputs = inputs: inputs.snowfall-lib.mkFlake {
inherit inputs;
src = ./.;
snowfall = {
namespace = "sneeuwvlok";
snowfall = {
namespace = "sneeuwvlok";
meta = {
name = "sneeuwvlok";
title = "Sneeuwvlok";
};
meta = {
name = "sneeuwvlok";
title = "Sneeuwvlok";
};
};
channels-config = {
allowUnfree = true;
permittedInsecurePackages = [
# Due to *arr stack
"dotnet-sdk-6.0.428"
"aspnetcore-runtime-6.0.36"
# I think this is because of zen
"qtwebengine-5.15.19"
# For Nheko, the matrix client
"olm-3.2.16"
];
};
overlays = with inputs; [
fenix.overlays.default
nix-minecraft.overlay
flux.overlays.default
];
systems.modules = with inputs; [
clan-core.nixosModules.default
];
homes.modules = with inputs; [
stylix.homeModules.stylix
plasma-manager.homeModules.plasma-manager
channels-config = {
allowUnfree = true;
permittedInsecurePackages = [
"dotnet-sdk-6.0.428"
"aspnetcore-runtime-6.0.36"
];
};
overlays = with inputs; [
fenix.overlays.default
nix-minecraft.overlay
flux.overlays.default
];
homes.modules = with inputs; [
stylix.homeModules.stylix
plasma-manager.homeManagerModules.plasma-manager
];
};
}

View file

@ -1,11 +1,10 @@
{osConfig, ...}: {
{ osConfig, ... }:
{
home.stateVersion = osConfig.system.stateVersion;
programs.git = {
settings.user = {
name = "Chris Kruining";
email = "chris@kruining.eu";
};
userName = "Chris Kruining";
userEmail = "chris@kruining.eu";
};
sneeuwvlok = {

View file

@ -1,11 +1,10 @@
{osConfig, ...}: {
{ osConfig, ... }:
{
home.stateVersion = osConfig.system.stateVersion;
programs.git = {
settings.user = {
name = "Chris Kruining";
email = "chris@kruining.eu";
};
userName = "Chris Kruining";
userEmail = "chris@kruining.eu";
};
sneeuwvlok = {
@ -36,7 +35,6 @@
bitwarden.enable = true;
discord.enable = true;
ladybird.enable = true;
matrix.enable = true;
obs.enable = true;
onlyoffice.enable = true;
signal.enable = true;

View file

@ -0,0 +1,21 @@
user_level_secrets: ENC[AES256_GCM,data:TNT+via+r4bpgROz,iv:cVO6/r4Aovr5uJFhU87mE5XwRJ518y4OJdHo4m92ahM=,tag:jYInD+euh7k1zSnMRppI5Q==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1ewes0f5snqx3sh5ul6fa6qtxzhd25829v6mf5rx2wnheat6fefps5rme2x
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTYVRQTEVSMWM3WXY3eTdW
ZkUwSnNidlJwWGVETURpNUJRRUllYXo4WjNvCmxmN21qVzNFV3N4UVR6WEV1am1W
eW1KTk9HVDluek1BUnBmSGI3Y2ZqaDQKLS0tIHlMYldYMTVORVNWbEgrWlBSanRM
bUZiMHlOU3pxYUhQSTREb0l4TmFlOEkKiasV2H481aJzAvEAvyeWqGYDOW+WKRFX
yyocZDo0o1lHz/gNXoC0/ujU+O3rSXdsy6Qdz6Rm+xeFUfe4KoD4bg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-08-11T13:21:38Z"
mac: ENC[AES256_GCM,data:kfMcZuYuQqxxfqtyfH7DltSkq8YNz+vroB+ZQKTIpCNC/W6vJP1o23/xLRzdnEgnnH5GfgZQFAK8Am00/bUD2BgEPyXxXNf1lG70ocFbRM9htii92BFfHgfi25zlEqCO7yrudm1HEJyYrFbZnT63H6u1OgWSC38CzEZTBsCE0kU=,iv:feWGBau48s2GSvZjnKPfP2z46SBuHbh//4zzcLv+MTY=,tag:D86akwawLxobhEu2AvBFKg==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.9.4

View file

@ -1,11 +1,10 @@
{osConfig, ...}: {
{ osConfig, ... }:
{
home.stateVersion = osConfig.system.stateVersion;
programs.git = {
settings.user = {
name = "Chris Kruining";
email = "chris@kruining.eu";
};
userName = "Chris Kruining";
userEmail = "chris@kruining.eu";
};
sneeuwvlok = {

View file

@ -1,11 +1,10 @@
{osConfig, ...}: {
{ osConfig, ... }:
{
home.stateVersion = osConfig.system.stateVersion;
programs.git = {
settings.user = {
name = "Chris Kruining";
email = "chris@kruining.eu";
};
userName = "Chris Kruining";
userEmail = "chris@kruining.eu";
};
sneeuwvlok = {

24
justfile Normal file
View file

@ -0,0 +1,24 @@
[private]
default:
@just -l
[doc('Update flake dependencies')]
update:
nix flake update
[doc('install nixos on a system (uses nix-anywhere)
> profile: Which profile to use
> host: How to reach the target system in the standard format of `user@host`
')]
install profile host:
nix run nixpkgs#nixos-anywhere -- \
--flake .#{{profile}} \
--generate-hardware-config nixos-generate-config ./hardware-configuration.nix \
{{host}}
[doc('builds the configuration for the host')]
build host:
nh os build . -H {{host}}
edit-secrets target:
sops --config "{{justfile_directory()}}/.sops.yml" edit "{{justfile_directory()}}/{{ if target =~ ".+@.+" { "homes" } else { "systems" } }}/x86_64-linux/{{target}}/secrets.yaml"

View file

@ -1,38 +0,0 @@
{ lib, ...}:
let
inherit (builtins) isString typeOf;
inherit (lib) mkOption types throwIfNot concatStringsSep splitStringBy toLower map;
in
{
options = {
mkUrlOptions =
defaults:
{
host = mkOption {
type = types.str;
example = "host.tld";
description = ''
Hostname
'';
} // (defaults.host or {});
port = mkOption {
type = types.port;
default = 1234;
example = "1234";
description = ''
Port
'';
} // (defaults.port or {});
protocol = mkOption {
type = types.str;
default = "https";
example = "https";
description = ''
Which protocol to use when creating a url string
'';
} // (defaults.protocol or {});
};
};
}

View file

@ -1,39 +0,0 @@
{ lib, ...}:
let
inherit (builtins) isString typeOf match toString head;
inherit (lib) throwIfNot concatStringsSep splitStringBy toLower map concatMapAttrsStringSep;
in
{
strings = {
#========================================================================================
# Converts a string to snake case
#
# simply replaces any uppercase letter to its lowercase variant preceeded by an underscore
#========================================================================================
toSnakeCase =
str:
throwIfNot (isString str) "toSnakeCase only accepts string values, but got ${typeOf str}" (
str
|> splitStringBy (prev: curr: builtins.match "[a-z]" prev != null && builtins.match "[A-Z]" curr != null) true
|> map (p: toLower p)
|> concatStringsSep "_"
);
#========================================================================================
# Converts a set of url parts to a string
#========================================================================================
toUrl =
{ protocol ? null, host, port ? null, path ? null, query ? null, hash ? null }:
let
trim_slashes = str: str |> match "^\/*(.+?)\/*$" |> head;
encode_to_str = set: concatMapAttrsStringSep "&" (n: v: "${n}=${v}") set;
_protocol = if protocol != null then "${protocol}://" else "";
_port = if port != null then ":${toString port}" else "";
_path = if path != null then "/${path |> trim_slashes}" else "";
_query = if query != null then "?${query |> encode_to_str}" else "";
_hash = if hash != null then "#${hash |> encode_to_str}" else "";
in
"${_protocol}${host}${_port}${_path}${_query}${_hash}";
};
}

View file

@ -1,19 +0,0 @@
{ config, lib, pkgs, namespace, osConfig ? {}, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.application.matrix;
in
{
options.${namespace}.application.matrix = {
enable = mkEnableOption "enable Matrix client (Fractal)";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [ fractal element-desktop ];
programs.element-desktop = {
enable = true;
};
};
}

View file

@ -1,20 +1,16 @@
{
inputs,
config,
lib,
pkgs,
namespace,
...
}: let
{ inputs, config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.application.onlyoffice;
in {
in
{
options.${namespace}.application.onlyoffice = {
enable = mkEnableOption "enable onlyoffice";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [onlyoffice-desktopeditors];
home.packages = with pkgs; [ onlyoffice-bin ];
# fonts.packages = with pkgs; [ corefonts ];
};
}

View file

@ -10,7 +10,7 @@ in
};
config = mkIf cfg.enable {
home.packages = with pkgs; [ protonup-ng ];
home.packages = with pkgs; [ protonup ];
home.sessionVariables = {
STEAM_EXTRA_COMPAT_TOOLS_PATHS = "\${HOME}/.steam/root/compatibilitytools.d";

View file

@ -10,6 +10,6 @@ in
};
config = mkIf cfg.enable {
home.packages = with pkgs; [ teamspeak3 teamspeak6-client ];
home.packages = with pkgs; [ teamspeak_client ];
};
}

View file

@ -5,61 +5,35 @@ let
cfg = config.${namespace}.application.zen;
in
{
imports = [
inputs.zen-browser.homeModules.default
];
options.${namespace}.application.zen = {
enable = mkEnableOption "enable zen";
};
config = mkIf cfg.enable {
home.packages = [ inputs.zen-browser.packages.${pkgs.system}.specific ];
home.sessionVariables = {
MOZ_ENABLE_WAYLAND = "1";
};
programs.zen-browser = {
enable = true;
policies = {
AutofillAddressEnabled = true;
AutofillCreditCardEnabled = false;
AppAutoUpdate = false;
DisableAppUpdate = true;
ManualAppUpdateOnly = true;
DisableFeedbackCommands = true;
DisableFirefoxStudies = true;
DisablePocket = true;
DisableTelemetry = true;
DontCheckDefaultBrowser = false;
# DontCheckDefaultBrowser = false;
NoDefaultBookmarks = true;
OfferToSaveLogins = false;
# OfferToSaveLogins = false;
EnableTrackingProtection = {
Value = true;
Locked = true;
Cryptomining = true;
Fingerprinting = true;
};
HttpAllowlist = [
"http://ulmo"
];
};
policies.ExtensionSettings = let
mkExtension = id: {
install_url = "https://addons.mozilla.org/firefox/downloads/latest/${builtins.toString id}/latest.xpi";
installation_mode = "force_installed";
};
in
{
ublock_origin = 4531307;
ghostry = 4562168;
bitwarden = 4562769;
sponsorblock = 4541835;
};
};
};

View file

@ -64,7 +64,7 @@ in
};
kwalletrc = {
Wallet.Enabled = true;
Wallet.Enabled = false;
};
plasmarc = {

View file

@ -15,7 +15,7 @@ in {
programs.zed-editor = {
enable = true;
extensions = [ "nix" "toml" "html" "just-ls" ];
extensions = [ "nix" "toml" "html" ];
userSettings = {
assistant.enabled = false;

View file

@ -4,9 +4,7 @@ let
in
{
systemd.user.startServices = "sd-switch";
programs.home-manager = {
enable = true;
};
programs.home-manager.enable = true;
home.stateVersion = mkDefault (osConfig.system.stateVersion or "25.05");
}

View file

@ -17,7 +17,6 @@ in
eza.enable = true;
fzf.enable = true;
git.enable = true;
just.enable = true;
starship.enable = true;
tmux.enable = true;
yazi.enable = true;

View file

@ -1,14 +1,10 @@
{
config,
lib,
pkgs,
namespace,
...
}: let
{ config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkEnableOption mkIf;
cfg = config.${namespace}.shell.toolset.git;
in {
in
{
options.${namespace}.shell.toolset.git = {
enable = mkEnableOption "version-control system";
};
@ -16,7 +12,7 @@ in {
config = mkIf cfg.enable {
home.sessionVariables.GITHUB_TOKEN = "$(cat /run/agenix/tokenGH)";
home.packages = with pkgs; [lazygit lazyjj jujutsu];
home.packages = with pkgs; [ lazygit lazyjj jujutsu ];
programs = {
zsh.initContent = ''
@ -33,6 +29,12 @@ in {
git = {
enable = true;
package = pkgs.gitFull;
difftastic = {
enable = true;
background = "dark";
color = "always";
display = "inline";
};
ignores = [
# General:
@ -65,7 +67,7 @@ in {
"*.elc"
];
settings = {
extraConfig = {
init.defaultBranch = "main";
core = {
editor = "nvim";
@ -102,16 +104,6 @@ in {
};
};
};
difftastic = {
enable = true;
git.enable = true;
options = {
background = "dark";
color = "always";
display = "inline";
};
};
};
};
}

View file

@ -1,15 +0,0 @@
{ config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkEnableOption mkIf;
cfg = config.${namespace}.shell.toolset.just;
in
{
options.${namespace}.shell.toolset.just = {
enable = mkEnableOption "version-control system";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [ just gum ];
};
}

View file

@ -31,9 +31,7 @@ in {
base16Scheme = "${pkgs.base16-schemes}/share/themes/${cfg.theme}.yaml";
image = ./${cfg.theme}.jpg;
polarity = cfg.polarity;
# targets.qt.platform = mkDefault "kde";
targets.zen-browser.profileNames = [ "Chris" ];
targets.qt.platform = mkDefault "kde6";
fonts = {
serif = {
@ -52,7 +50,7 @@ in {
};
emoji = {
package = pkgs.noto-fonts-color-emoji;
package = pkgs.noto-fonts-emoji;
name = "Noto Color Emoji";
};
};

View file

@ -1,15 +1,10 @@
{
inputs,
config,
lib,
pkgs,
namespace,
...
}: let
{ inputs, config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.application.steam;
in {
in
{
options.${namespace}.application.steam = {
enable = mkEnableOption "enable steam";
};
@ -18,7 +13,7 @@ in {
programs = {
steam = {
enable = true;
package = pkgs.steam.override {
package = pkgs.steam-small.override {
extraEnv = {
DXVK_HUD = "compiler";
MANGOHUD = true;

View file

@ -12,18 +12,7 @@ in
};
config = mkIf cfg.enable {
environment.plasma6.excludePackages = with pkgs.kdePackages; [
elisa
kmahjongg
kmines
konversation
kpat
ksudoku
konsole
kate
ghostwriter
oxygen
];
environment.plasma6.excludePackages = with pkgs.kdePackages; [ konsole kate ghostwriter oxygen ];
environment.sessionVariables.NIXOS_OZONE_WL = "1";
services = {

View file

@ -17,6 +17,11 @@ in
};
amdgpu = {
amdvlk = {
enable = true;
support32Bit.enable = true;
};
initrd.enable = true;
};
};

View file

@ -1,6 +0,0 @@
{ ... }:
{
config = {
home-manager.backupFileExtension = "homeManagerBackup";
};
}

View file

@ -1,11 +1,15 @@
{ pkgs, lib, namespace, config, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.nix;
in
{
options.${namespace}.nix = {};
options.${namespace}.nix = {
enable = mkEnableOption "Enable nix command";
};
config = {
config = mkIf cfg.enable {
programs.git.enable = true;
nix = {

View file

@ -130,23 +130,6 @@ in
scopes = [ "offline_access" "openid" "email" "picture" "profile" "groups" ];
redirect_uris = [ "http://localhost:3000/api/auth/oauth2/callback/authelia" ];
}
{
client_id = "forgejo";
client_name = "forgejo";
# ZPuiW2gpVV6MGXIJFk5P3EeSW8V_ICgqduF.hJVCKkrnVmRqIQXRk0o~HSA8ZdCf8joA4m_F
client_secret = "$pbkdf2-sha512$310000$CzZjvJT75bz5z7MjwxsEtg$JtOiIgaY5/HcLLxJgyX4zvsQV9jIoow0e4JdlFsk/LWRDOJ0kc.PzstlYfw7QERTXtJILoWsDqPzmvpneK5Leg";
public = false;
require_pkce = true;
pkce_challenge_method = "S256";
token_endpoint_auth_method = "client_secret_post";
authorization_policy = "one_factor";
userinfo_signed_response_alg = "none";
consent_mode = "implicit";
scopes = [ "offline_access" "openid" "email" "picture" "profile" "groups" ];
response_types = [ "code" ];
grant_types = [ "authorization_code" ];
redirect_uris = [ "http://localhost:5002/user/oauth2/authelia/callback" ];
}
];
};
};

View file

@ -0,0 +1 @@
{ ... }: {}

View file

@ -1,15 +1,10 @@
{
inputs,
lib,
config,
namespace,
...
}: let
{ inputs, lib, config, namespace, ... }: let
inherit (lib) mkEnableOption mkIf;
cfg = config.${namespace}.services.authentication.himmelblau;
in {
imports = [inputs.himmelblau.nixosModules.himmelblau];
in
{
imports = [ inputs.himmelblau.nixosModules.himmelblau ];
options.${namespace}.services.authentication.himmelblau = {
enable = mkEnableOption "enable azure entra ID authentication";
@ -19,7 +14,7 @@ in {
services.himmelblau = {
enable = true;
settings = {
domain = "";
domains = [];
pam_allow_groups = [];
local_groups = [];
};

View file

@ -0,0 +1,86 @@
{ config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.authentication.zitadel;
db_name = "zitadel";
db_user = "zitadel";
in
{
options.${namespace}.services.authentication.zitadel = {
enable = mkEnableOption "Zitadel";
};
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [
zitadel
];
services = {
zitadel = {
enable = true;
openFirewall = true;
masterKeyFile = config.sops.secrets."zitadel/masterKey".path;
tlsMode = "external";
settings = {
Port = 9092;
Database = {
Host = "/run/postgresql";
# Zitadel will report error if port is not set
Port = 5432;
Database = db_name;
User.Username = db_user;
};
};
steps = {
TestInstance = {
InstanceName = "Zitadel test";
Org = {
Name = "Kruining.eu";
Human = {
UserName = "admin";
Password = "kaas";
};
};
};
};
};
postgresql = {
enable = true;
ensureDatabases = [ db_name ];
ensureUsers = [
{
name = db_user;
ensureDBOwnership = true;
}
];
};
caddy = {
enable = true;
virtualHosts = {
"auth-z.kruining.eu".extraConfig = ''
reverse_proxy h2c://127.0.0.1:9092
'';
};
# extraConfig = ''
# (auth) {
# forward_auth h2c://127.0.0.1:9092 {
# uri /api/authz/forward-auth
# copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
# }
# }
# '';
};
};
# Secrets
sops.secrets."zitadel/masterKey" = {
owner = "zitadel";
group = "zitadel";
restartUnits = [ "zitadel.service" ];
};
};
}

View file

@ -1,733 +0,0 @@
{ config, lib, pkgs, namespace, system, inputs, ... }:
let
inherit (lib) mkIf mkEnableOption mkOption types toUpper toSentenceCase nameValuePair mapAttrs mapAttrs' concatMapAttrs concatMapStringsSep filterAttrsRecursive listToAttrs imap0 head drop length literalExpression attrNames;
inherit (lib.${namespace}.strings) toSnakeCase;
cfg = config.${namespace}.services.authentication.zitadel;
database = "zitadel";
in
{
options.${namespace}.services.authentication.zitadel = {
enable = mkEnableOption "Zitadel";
organization = mkOption {
type = types.attrsOf (types.submodule ({ name, ... }: {
options =
let
org = name;
in
{
isDefault = mkOption {
type = types.bool;
default = false;
example = "true";
description = ''
True sets the '${org}' org as default org for the instance. Only one org can be default org.
Nothing happens if you set it to false until you set another org as default org.
'';
};
project = mkOption {
default = {};
type = types.attrsOf (types.submodule {
options = {
hasProjectCheck = mkOption {
type = types.bool;
default = false;
example = "true";
description = ''
ZITADEL checks if the org of the user has permission to this project.
'';
};
privateLabelingSetting = mkOption {
type = types.nullOr (types.enum [ "unspecified" "enforceProjectResourceOwnerPolicy" "allowLoginUserResourceOwnerPolicy" ]);
default = null;
example = "enforceProjectResourceOwnerPolicy";
description = ''
Defines from where the private labeling should be triggered,
supported values:
- unspecified
- enforceProjectResourceOwnerPolicy
- allowLoginUserResourceOwnerPolicy
'';
};
projectRoleAssertion = mkOption {
type = types.bool;
default = false;
example = "true";
description = ''
Describes if roles of user should be added in token.
'';
};
projectRoleCheck = mkOption {
type = types.bool;
default = false;
example = "true";
description = ''
ZITADEL checks if the user has at least one on this project.
'';
};
role = mkOption {
default = {};
type = types.attrsOf (types.submodule ({ name, ... }: {
options =
let
roleName = name;
in
{
displayName = mkOption {
type = types.str;
default = toSentenceCase name;
example = "RoleName";
description = ''
Name used for project role.
'';
};
group = mkOption {
type = types.nullOr types.str;
default = null;
example = "some_group";
description = ''
Group used for project role.
'';
};
};
}));
};
assign = mkOption {
default = {};
type = types.attrsOf (types.listOf types.str);
};
application = mkOption {
default = {};
type = types.attrsOf (types.submodule {
options = {
redirectUris = mkOption {
type = types.nonEmptyListOf types.str;
example = ''
[ "https://example.com/redirect/url" ]
'';
description = ''
.
'';
};
grantTypes = mkOption {
type = types.nonEmptyListOf (types.enum [ "authorizationCode" "implicit" "refreshToken" "deviceCode" "tokenExchange" ]);
example = ''
[ "authorizationCode" ]
'';
description = ''
.
'';
};
responseTypes = mkOption {
type = types.nonEmptyListOf (types.enum [ "code" "idToken" "idTokenToken" ]);
example = ''
[ "code" ]
'';
description = ''
.
'';
};
exportMap =
let
strOpt = mkOption { type = types.nullOr types.str; default = null; };
in
mkOption {
type = types.submodule { options = { client_id = strOpt; client_secret = strOpt; }; };
default = {};
example = literalExpression ''
{
client_id = "SSO_CLIENT_ID";
client_secret = "SSO_CLIENT_SECRET";
}
'';
description = ''
Remap the outputted variables to another key.
'';
};
};
});
};
};
});
};
user = mkOption {
default = {};
type = types.attrsOf (types.submodule ({ name, ... }: {
options =
let
username = name;
in
{
email = mkOption {
type = types.str;
example = "someone@some.domain";
description = ''
Username.
'';
};
userName = mkOption {
type = types.nullOr types.str;
default = username;
example = "some_user_name";
description = ''
Username. Default value is the key of the config object you created, you can overwrite that by setting this option
'';
};
firstName = mkOption {
type = types.str;
example = "John";
description = ''
First name of the user.
'';
};
lastName = mkOption {
type = types.str;
example = "Doe";
description = ''
Last name of the user.
'';
};
roles = mkOption {
type = types.listOf types.str;
default = [];
example = "[ \"ORG_OWNER\" ]";
description = ''
List of roles granted to organisation.
'';
};
instanceRoles = mkOption {
type = types.listOf types.str;
default = [];
example = "[ \"IAM_OWNER\" ]";
description = ''
List of roles granted to instance.
'';
};
};
}));
};
action = mkOption {
default = {};
type = types.attrsOf (types.submodule ({ name, ... }: {
options = {
script = mkOption {
type = types.str;
example = ''
(ctx, api) => {
api.v1.claims.setClaim('some_claim', 'some_value');
};
'';
description = ''
The script to run. This must be a function that receives 2 parameters, and returns void. During the creation of the action's script this module simly does `const {{name}} = {{script}}`.
'';
};
timeout = mkOption {
type = (types.ints.between 0 20);
default = 10;
example = "10";
description = ''
After which time the action will be terminated if not finished.
'';
};
allowedToFail = mkOption {
type = types.bool;
default = true;
example = "true";
description = ''
Allowed to fail.
'';
};
};
}));
};
triggers = mkOption {
default = [];
type = types.listOf (types.submodule {
options = {
flowType = mkOption {
type = types.enum [ "authentication" "customiseToken" "internalAuthentication" "samlResponse" ];
example = "customiseToken";
description = ''
Type of the flow to which the action triggers belong.
'';
};
triggerType = mkOption {
type = types.enum [ "postAuthentication" "preCreation" "postCreation" "preUserinfoCreation" "preAccessTokenCreation" "preSamlResponse" ];
example = "postAuthentication";
description = ''
Trigger type on when the actions get triggered.
'';
};
actions = mkOption {
type = types.nonEmptyListOf types.str;
example = ''[ "action_name" ]'';
description = ''
Names of actions to trigger
'';
};
};
});
};
};
}));
};
};
config = let
_refTypeMap = {
org = { type = "org"; };
project = { type = "project"; };
user = { type = "user"; tfType = "human_user"; };
};
mapRef' = { type, tfType ? type }: name: { "${type}Id" = "\${ resource.zitadel_${tfType}.${toSnakeCase name}.id }"; };
mapRef = type: name: mapRef' (_refTypeMap.${type}) name;
mapEnum = prefix: value: "${prefix}_${value |> toSnakeCase |> toUpper}";
mapValue = type: value: ({
appType = mapEnum "OIDC_APP_TYPE" value;
grantTypes = map (t: mapEnum "OIDC_GRANT_TYPE" t) value;
responseTypes = map (t: mapEnum "OIDC_RESPONSE_TYPE" t) value;
authMethodType = mapEnum "OIDC_AUTH_METHOD_TYPE" value;
flowType = mapEnum "FLOW_TYPE" value;
triggerType = mapEnum "TRIGGER_TYPE" value;
accessTokenType = mapEnum "OIDC_TOKEN_TYPE" value;
}."${type}" or value);
toResource = name: value: nameValuePair
(toSnakeCase name)
(lib.mapAttrs' (k: v: nameValuePair (toSnakeCase k) (mapValue k v)) value);
withRef = type: name: attrs: attrs // (mapRef type name);
select = keys: callback: set:
if (length keys) == 0 then
mapAttrs' callback set
else let key = head keys; in
concatMapAttrs (k: v: select (drop 1 keys) (callback k) (v.${key} or {})) set
;
append = attrList: set: set // (listToAttrs attrList);
config' = config;
# this is a nix package, the generated json file to be exact
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
inherit system;
modules = [
({ config, lib, ... }: {
config =
let
forEach = src: key: set:
let
_key = concatMapStringsSep "_" (k: "\${item.${k}}") key;
in
{
forEach = lib.tfRef ''{
for item in ${src} :
"''${item.org}_''${item.name}" => item
}'';
}
// set;
in
{
terraform.required_providers.zitadel = {
source = "zitadel/zitadel";
version = "2.2.0";
};
provider.zitadel = {
domain = "auth.kruining.eu";
insecure = "false";
jwt_profile_file = "/var/lib/zitadel/machine-key.json";
};
locals = {
extra_users = lib.tfRef "
flatten([ for org, users in jsondecode(file(\"${config'.sops.secrets."zitadel/users".path}\")): [
for name, details in users: {
org = org
name = name
email = details.email
firstName = details.firstName
lastName = details.lastName
}
] ])
";
orgs = cfg.organization |> mapAttrs (org: _: lib.tfRef "resource.zitadel_org.${org}.id");
};
resource = {
# Organizations
zitadel_org = cfg.organization |> select [] (name: { isDefault, ... }:
{ inherit name isDefault; }
|> toResource name
);
# Projects per organization
zitadel_project = cfg.organization |> select [ "project" ] (org: name: { hasProjectCheck, privateLabelingSetting, projectRoleAssertion, projectRoleCheck, ... }:
{
inherit name hasProjectCheck privateLabelingSetting projectRoleAssertion projectRoleCheck;
}
|> withRef "org" org
|> toResource "${org}_${name}"
);
# Each OIDC app per project
zitadel_application_oidc = cfg.organization |> select [ "project" "application" ] (org: project: name: { redirectUris, grantTypes, responseTypes, ...}:
{
inherit name redirectUris grantTypes responseTypes;
accessTokenRoleAssertion = true;
idTokenRoleAssertion = true;
accessTokenType = "JWT";
}
|> withRef "org" org
|> withRef "project" "${org}_${project}"
|> toResource "${org}_${project}_${name}"
);
# Each project role
zitadel_project_role = cfg.organization |> select [ "project" "role" ] (org: project: name: value:
{ inherit (value) displayName group; roleKey = name; }
|> withRef "org" org
|> withRef "project" "${org}_${project}"
|> toResource "${org}_${project}_${name}"
);
# Each project role assignment
zitadel_user_grant = cfg.organization |> select [ "project" "assign" ] (org: project: user: roles:
{ roleKeys = roles; }
|> withRef "org" org
|> withRef "project" "${org}_${project}"
|> withRef "user" "${org}_${user}"
|> toResource "${org}_${project}_${user}"
);
# Users
zitadel_human_user =
cfg.organization
|> select [ "user" ] (org: name: { email, userName, firstName, lastName, ... }:
{
inherit email userName firstName lastName;
isEmailVerified = true;
}
|> withRef "org" org
|> toResource "${org}_${name}"
)
|> append
[
(forEach "local.extra_users" [ "org" "name" ] {
orgId = lib.tfRef "local.orgs[each.value.org]";
userName = lib.tfRef "each.value.name";
email = lib.tfRef "each.value.email";
firstName = lib.tfRef "each.value.firstName";
lastName = lib.tfRef "each.value.lastName";
isEmailVerified = true;
}
|> toResource "extraUsers")
]
;
# Global user roles
zitadel_instance_member =
cfg.organization
|> filterAttrsRecursive (n: v: !(v ? "instanceRoles" && (length v.instanceRoles) == 0))
|> select [ "user" ] (org: name: { instanceRoles, ... }:
{ roles = instanceRoles; }
|> withRef "user" "${org}_${name}"
|> toResource "${org}_${name}"
);
# Organazation specific roles
zitadel_org_member =
cfg.organization
|> filterAttrsRecursive (n: v: !(v ? "roles" && (length v.roles) == 0))
|> select [ "user" ] (org: name: { roles, ... }:
{ inherit roles; }
|> withRef "org" org
|> withRef "user" "${org}_${name}"
|> toResource "${org}_${name}"
);
# Organazation's actions
zitadel_action = cfg.organization |> select [ "action" ] (org: name: { timeout, allowedToFail, script, ...}:
{
inherit allowedToFail name;
timeout = "${toString timeout}s";
script = "const ${name} = ${script}";
}
|> withRef "org" org
|> toResource "${org}_${name}"
);
# Organazation's action assignments
zitadel_trigger_actions =
cfg.organization
|> concatMapAttrs (org: { triggers, ... }:
triggers
|> imap0 (i: { flowType, triggerType, actions, ... }: (let name = "trigger_${toString i}"; in
{
inherit flowType triggerType;
actionIds =
actions
|> map (action: (lib.tfRef "zitadel_action.${org}_${toSnakeCase action}.id"));
}
|> withRef "org" org
|> toResource "${org}_${name}"
))
|> listToAttrs
);
# SMTP config
zitadel_smtp_config.default = {
sender_address = "chris@kruining.eu";
sender_name = "no-reply (Zitadel)";
tls = true;
host = "black-mail.nl:587";
user = "chris@kruining.eu";
password = lib.tfRef "file(\"${config'.sops.secrets."zitadel/email".path}\")";
set_active = true;
};
# Client credentials per app
local_sensitive_file = cfg.organization |> select [ "project" "application" ] (org: project: name: { exportMap, ... }:
nameValuePair "${org}_${project}_${name}" {
content = ''
${if exportMap.client_id != null then exportMap.client_id else "CLIENT_ID"}=${lib.tfRef "resource.zitadel_application_oidc.${org}_${project}_${name}.client_id"}
${if exportMap.client_secret != null then exportMap.client_secret else "CLIENT_SECRET"}=${lib.tfRef "resource.zitadel_application_oidc.${org}_${project}_${name}.client_secret"}
'';
filename = "/var/lib/zitadel/clients/${org}_${project}_${name}";
}
);
};
};
})
];
};
in
mkIf cfg.enable {
${namespace}.services.persistance.postgresql.enable = true;
environment.systemPackages = with pkgs; [
zitadel
];
systemd.tmpfiles.rules = [
"d /tmp/zitadelApplyTerraform 0755 zitadel zitadel -"
"d /var/lib/zitadel/clients 0755 zitadel zitadel -"
];
systemd.services.zitadelApplyTerraform = {
description = "Zitadel terraform apply";
wantedBy = [ "multi-user.target" ];
wants = [ "zitadel.service" ];
script =
let
tofu = lib.getExe pkgs.opentofu;
in
''
if [ "$(systemctl is-active zitadel)" != "active" ]; then
echo "Zitadel is not running"
exit 1
fi
# Print the path to the source for easier debugging
echo "config location: ${terraformConfiguration}"
# Copy infra code into workspace
cp -f ${terraformConfiguration} config.tf.json
# Initialize OpenTofu
${tofu} init
# Run the infrastructure code
${tofu} plan -refresh=false -out=tfplan
${tofu} apply -auto-approve tfplan
'';
serviceConfig = {
Type = "oneshot";
User = "zitadel";
Group = "zitadel";
WorkingDirectory = "/tmp/zitadelApplyTerraform";
};
};
services = {
zitadel = {
enable = true;
openFirewall = true;
masterKeyFile = config.sops.secrets."zitadel/masterKey".path;
tlsMode = "external";
settings = {
Port = 9092;
ExternalDomain = "auth.kruining.eu";
ExternalPort = 443;
ExternalSecure = true;
Metrics.Type = "otel";
Tracing.Type = "otel";
Telemetry.Enabled = true;
SystemDefaults = {
PasswordHasher.Hasher.Algorithm = "argon2id";
SecretHasher.Hasher.Algorithm = "argon2id";
};
Database.postgres = {
Host = "localhost";
# Zitadel will report error if port is not set
Port = 5432;
Database = database;
User = {
Username = database;
SSL.Mode = "disable";
};
Admin = {
Username = "postgres";
SSL.Mode = "disable";
};
};
};
steps = {
FirstInstance = {
# Not sure, this option seems to be mostly irrelevant
InstanceName = "eu";
MachineKeyPath = "/var/lib/zitadel/machine-key.json";
# PatPath = "/var/lib/zitadel/machine-key.pat";
# LoginClientPatPath = "/var/lib/zitadel/machine-key.json";
Org = {
Name = "kruining";
Human = {
UserName = "chris";
FirstName = "Chris";
LastName = "Kruining";
Email = {
Address = "chris@kruining.eu";
Verified = true;
};
Password = "KaasIsAwesome1!";
};
Machine = {
Machine = {
Username = "terraform-service-user";
Name = "Terraform";
};
MachineKey = { ExpirationDate = "2026-01-01T00:00:00Z"; Type = 1; };
# Pat = { ExpirationDate = "2026-01-01T00:00:00Z"; };
};
# LoginClient.Machine = {
# Username = "terraform-service-user";
# Name = "Terraform";
# };
};
};
};
# extraStepsPaths = [
# config.sops.templates."secrets.yaml".path
# ];
};
postgresql = {
enable = true;
ensureDatabases = [ database ];
ensureUsers = [
{
name = database;
ensureDBOwnership = true;
}
];
};
caddy = {
enable = true;
virtualHosts = {
"auth.kruining.eu".extraConfig = ''
reverse_proxy h2c://::1:9092
'';
};
extraConfig = ''
(auth) {
forward_auth h2c://::1:9092 {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
}
'';
};
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
# Secrets
sops = {
secrets = {
"zitadel/masterKey" = {
owner = "zitadel";
group = "zitadel";
restartUnits = [ "zitadel.service" ]; #EMGDB#6O$8qpGoLI1XjhUhnng1san@0
};
"zitadel/email" = {
owner = "zitadel";
group = "zitadel";
key = "email/chris_kruining_eu";
restartUnits = [ "zitadel.service" ];
};
"zitadel/users" = {
owner = "zitadel";
group = "zitadel";
restartUnits = [ "zitadelApplyTerraform.service" ];
};
};
templates = {
"users.yml" = {
};
};
};
};
}

View file

@ -1,35 +0,0 @@
{ config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.backup.borg;
in
{
options.${namespace}.services.backup.borg = {
enable = mkEnableOption "Borg Backup";
};
config = mkIf cfg.enable {
programs.ssh.extraConfig = ''
Host beheer.hazelhof.nl
Port 222
User chris
AddressFamily inet
IdentityFile /home/chris/.ssh/id_ed25519
'';
services = {
borgbackup.jobs = {
media = {
paths = "/var/media/test";
encryption.mode = "none";
# environment.BORG_SSH = "ssh -4 -i /home/chris/.ssh/id_ed25519";
environment.BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
repo = "ssh://beheer.hazelhof.nl//media";
compression = "auto,zstd";
startAt = "daily";
};
};
};
};
}

View file

@ -1,234 +0,0 @@
{
config,
lib,
pkgs,
namespace,
...
}: let
inherit (builtins) toString toJSON;
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.communication.matrix;
domain = "kruining.eu";
fqn = "matrix.${domain}";
port = 4001;
database = "synapse";
in {
options.${namespace}.services.communication.matrix = {
enable = mkEnableOption "Matrix server (Synapse)";
};
config = mkIf cfg.enable {
${namespace}.services = {
persistance.postgresql.enable = true;
# virtualisation.podman.enable = true;
};
networking.firewall.allowedTCPPorts = [4001];
services = {
matrix-synapse = {
enable = true;
extras = ["oidc"];
extraConfigFiles = [
config.sops.templates."synapse-oidc.yaml".path
];
settings = {
server_name = domain;
public_baseurl = "https://${fqn}";
enable_metrics = true;
registration_shared_secret = "tZtBnlhEmLbMwF0lQ112VH1Rl5MkZzYH9suI4pEoPXzk6nWUB8FJF4eEnwLkbstz";
url_preview_enabled = true;
precence.enabled = true;
# Since we'll be using OIDC for auth disable all local options
enable_registration = false;
enable_registration_without_verification = false;
password_config.enabled = false;
backchannel_logout_enabled = true;
sso = {
client_whitelist = ["http://[::1]:9092"];
update_profile_information = true;
};
database = {
# this is postgresql (also the default, but I prefer to be explicit)
name = "psycopg2";
args = {
database = database;
user = database;
};
};
listeners = [
{
bind_addresses = ["::"];
port = port;
type = "http";
tls = false;
x_forwarded = true;
resources = [
{
names = ["client" "federation" "openid" "metrics" "media" "health"];
compress = true;
}
];
}
];
};
};
mautrix-signal = {
enable = true;
registerToSynapse = true;
settings = {
appservice = {
provisioning.enabled = false;
};
homeserver = {
address = "http://[::1]:${toString port}";
domain = domain;
};
bridge = {
permissions = {
"@chris:${domain}" = "admin";
};
};
};
};
mautrix-telegram = {
enable = true;
registerToSynapse = true;
settings = {
telegram = {
api_id = 32770816;
api_hash = "7b63778a976619c9d4ab62adc51cde79";
bot_token = "disabled";
catch_up = true;
sequential_updates = true;
};
appservice = {
port = 40011;
provisioning.enabled = false;
};
homeserver = {
address = "http://[::1]:${toString port}";
domain = domain;
};
bridge = {
permissions = {
"@chris:${domain}" = "admin";
};
};
};
};
mautrix-whatsapp = {
enable = true;
registerToSynapse = true;
settings = {
appservice = {
provisioning.enabled = false;
};
homeserver = {
address = "http://[::1]:${toString port}";
domain = domain;
};
bridge = {
permissions = {
"@chris:${domain}" = "admin";
};
};
};
};
postgresql = {
enable = true;
ensureDatabases = [database];
ensureUsers = [
{
name = database;
ensureDBOwnership = true;
}
];
};
caddy = {
enable = true;
virtualHosts = let
server = {
"m.server" = "${fqn}:443";
};
client = {
"m.homeserver".base_url = "https://${fqn}";
"m.identity_server".base_url = "https://auth.kruining.eu";
};
in {
"${domain}".extraConfig = ''
header /.well-known/matrix/* Content-Type application/json
header /.well-known/matrix/* Access-Control-Allow-Origin *
respond /.well-known/matrix/server `${toJSON server}`
respond /.well-known/matrix/client `${toJSON client}`
'';
"${fqn}".extraConfig = ''
reverse_proxy /_matrix/* http://::1:4001
reverse_proxy /_synapse/client/* http://::1:4001
'';
};
};
};
sops = {
secrets = {
"synapse/oidc_id" = {};
"synapse/oidc_secret" = {};
};
templates = {
"synapse-oidc.yaml" = {
owner = "matrix-synapse";
content = ''
oidc_providers:
- discover: true
idp_id: zitadel
idp_name: Zitadel
issuer: "https://auth.kruining.eu"
scopes:
- openid
- profile
client_id: '${config.sops.placeholder."synapse/oidc_id"}'
client_secret: '${config.sops.placeholder."synapse/oidc_secret"}'
backchannel_logout_enabled: true
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
'';
restartUnits = ["matrix-synapse.service"];
};
};
};
};
}

View file

@ -1,210 +0,0 @@
{
config,
lib,
pkgs,
namespace,
...
}: let
inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption mkOption;
cfg = config.${namespace}.services.development.forgejo;
domain = "git.amarth.cloud";
in {
options.${namespace}.services.development.forgejo = {
enable = mkEnableOption "Forgejo";
port = mkOption {
type = lib.types.port;
default = 5002;
example = "1234";
description = ''
Which port to bind forgejo to
'';
};
};
config = mkIf cfg.enable {
${namespace}.services = {
persistance.postgresql.enable = true;
virtualisation.podman.enable = true;
};
environment.systemPackages = with pkgs; [forgejo];
services = {
forgejo = {
enable = true;
useWizard = false;
database.type = "postgres";
settings = {
DEFAULT = {
APP_NAME = "Tamin Amarth";
APP_SLOGAN = "Where code is forged";
};
server = {
DOMAIN = domain;
ROOT_URL = "https://${domain}/";
HTTP_PORT = cfg.port;
LANDING_PAGE = "explore";
};
cors = {
ENABLED = true;
ALLOW_DOMAIN = "https://*.amarth.cloud";
};
security = {
INSTALL_LOCK = true;
PASSWORD_HASH_ALGO = "argon2";
DISABLE_WEBHOOKS = true;
};
ui = {
EXPLORE_PAGING_NUM = 50;
ISSUE_PAGING_NUM = 50;
MEMBERS_PAGING_NUM = 50;
};
"ui.meta" = {
AUTHOR = "Where code is forged!";
DESCRIPTION = "Self-hosted solution for git, because FOSS is the anvil of the future";
};
admin = {
USER_DISABLED_FEATURES = "manage_gpg_keys";
EXTERNAL_USER_DISABLE_FEATURES = "manage_gpg_keys";
};
service = {
# Auth
ENABLE_BASIC_AUTHENTICATION = false;
DISABLE_REGISTRATION = false;
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
SHOW_REGISTRATION_BUTTON = false;
# Privacy
DEFAULT_KEEP_EMAIL_PRIVATE = true;
DEFAULT_USER_VISIBILITY = "private";
DEFAULT_ORG_VISIBILITY = "private";
# Common sense
VALID_SITE_URL_SCHEMES = "https";
};
openid = {
ENABLE_OPENID_SIGNIN = true;
ENABLE_OPENID_SIGNUP = true;
WHITELISTED_URIS = "https://auth.kruining.eu";
};
oauth2_client = {
ENABLE_AUTO_REGISTRATION = true;
UPDATE_AVATAR = true;
ACCOUNT_LINKING = "auto";
};
actions = {
ENABLED = true;
# DEFAULT_ACTIONS_URL = "https://data.forgejo.org";
};
other = {
SHOW_FOOTER_VERSION = false;
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
};
metrics = {
ENABLED = true;
};
api = {
ENABLE_SWAGGER = false;
};
mirror = {
ENABLED = true;
};
session = {
PROVIDER = "db";
COOKIE_SECURE = true;
};
mailer = {
ENABLED = true;
PROTOCOL = "smtp+starttls";
SMTP_ADDR = "black-mail.nl";
SMTP_PORT = 587;
FROM = "chris@kruining.eu";
USER = "chris@kruining.eu";
PASSWD_URI = "file:${config.sops.secrets."forgejo/email".path}";
};
};
};
openssh.settings.AllowUsers = ["forgejo"];
gitea-actions-runner = {
package = pkgs.forgejo-runner;
instances.default = {
enable = true;
name = "default";
url = "https://git.amarth.cloud";
# Obtaining the path to the runner token file may differ
# tokenFile should be in format TOKEN=<secret>, since it's EnvironmentFile for systemd
tokenFile = config.sops.secrets."forgejo/action_runner_token".path;
# token = "ZBetud1F0IQ9VjVFpZ9bu0FXgx9zcsy1x25yvjhw";
labels = [
"default:docker://nixos/nix:latest"
"ubuntu:docker://ubuntu:24-bookworm"
"nix:docker://git.amarth.cloud/amarth/runners/default:latest"
];
settings = {
log.level = "info";
};
};
};
caddy = {
enable = true;
virtualHosts = {
"${domain}".extraConfig = ''
# import auth
# stupid dumb way to prevent the login page and go to zitadel instead
# be aware that this does not disable local login at all!
# rewrite /user/login /user/oauth2/Zitadel
reverse_proxy http://127.0.0.1:${toString cfg.port}
'';
};
};
};
users = {
users."gitea-runner" = {
isSystemUser = true;
group = "gitea-runner";
};
groups."gitea-runner" = {};
};
sops.secrets = {
"forgejo/action_runner_token" = {
owner = "gitea-runner";
group = "gitea-runner";
restartUnits = ["gitea-runner-default.service"];
};
"forgejo/email" = {
owner = "forgejo";
group = "forgejo";
key = "email/chris_kruining_eu";
restartUnits = ["forgejo.service"];
};
};
};
}

View file

@ -1,15 +1,11 @@
{
pkgs,
lib,
namespace,
config,
...
}: let
{ pkgs, lib, namespace, config, ... }:
let
inherit (lib) mkIf mkEnableOption mkOption;
inherit (lib.types) str;
cfg = config.${namespace}.services.media;
in {
in
{
options.${namespace}.services.media = {
enable = mkEnableOption "Enable media services";
@ -56,55 +52,105 @@ in {
};
systemd.tmpfiles.rules = [
# "d '${cfg.path}/series' 0770 ${cfg.user} ${cfg.group} - -"
# "d '${cfg.path}/movies' 0770 ${cfg.user} ${cfg.group} - -"
# "d '${cfg.path}/music' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/qbittorrent' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/sabnzbd' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/incomplete' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/done' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/series' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/movies' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/music' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/qbittorrent' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/sabnzbd' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/reiverr/config' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/incomplete' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/done' 0700 ${cfg.user} ${cfg.group} - -"
];
#=========================================================================
# Services
#=========================================================================
services = {
bazarr = {
enable = true;
openFirewall = true;
user = cfg.user;
group = cfg.group;
listenPort = 2005;
};
flaresolverr = {
enable = true;
openFirewall = true;
port = 2007;
};
# port is harcoded in nixpkgs module
jellyfin = {
services = let
serviceConf = {
enable = true;
openFirewall = true;
user = cfg.user;
group = cfg.group;
};
in {
jellyfin = serviceConf;
radarr = serviceConf;
sonarr = serviceConf;
bazarr = serviceConf;
lidarr = serviceConf;
postgresql = {
jellyseerr = {
enable = true;
openFirewall = true;
};
prowlarr = {
enable = true;
openFirewall = true;
};
qbittorrent = {
enable = true;
openFirewall = true;
webuiPort = 5000;
serverConfig = {
LegalNotice.Accepted = true;
};
user = cfg.user;
group = cfg.group;
};
sabnzbd = {
enable = true;
openFirewall = true;
configFile = "${cfg.path}/sabnzbd/config.ini";
user = cfg.user;
group = cfg.group;
};
caddy = {
enable = true;
virtualHosts = {
"media.kruining.eu".extraConfig = ''
import auth
reverse_proxy http://127.0.0.1:9494
'';
"jellyfin.kruining.eu".extraConfig = ''
reverse_proxy http://[::1]:8096
reverse_proxy http://127.0.0.1:8096
'';
};
};
};
systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL";
${namespace}.services.virtualisation.podman.enable = true;
virtualisation = {
oci-containers = {
backend = "podman";
containers = {
flaresolverr = {
image = "flaresolverr/flaresolverr";
autoStart = true;
ports = [ "127.0.0.1:8191:8191" ];
};
reiverr = {
image = "ghcr.io/aleksilassila/reiverr:v2.2.0";
autoStart = true;
ports = [ "127.0.0.1:9494:9494" ];
volumes = [ "${cfg.path}/reiverr/config:/config" ];
};
};
};
};
networking.firewall.allowedTCPPorts = [ 80 443 6969 ];
};
}

View file

@ -1,183 +0,0 @@
{
config,
lib,
namespace,
...
}: let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.media.glance;
in {
options.${namespace}.services.media.glance = {
enable = mkEnableOption "Enable Glance";
};
config = mkIf cfg.enable {
services.glance = {
enable = true;
openFirewall = true;
environmentFile = config.sops.templates."glance/secrets.env".path;
settings = {
server = {
host = "0.0.0.0";
port = 2000;
};
theme = {
# Teal city predefined theme (https://github.com/glanceapp/glance/blob/main/docs/themes.md#teal-city)
background-color = "225 14 15";
primary-color = "157 47 65";
contrast-multiplier = 1.1;
};
pages = [
{
name = "Home";
columns = [
{
size = "small";
widgets = [
{
type = "calendar";
first-day-of-the-week = "monday";
}
];
}
{
size = "full";
widgets = [
{
type = "monitor";
cache = "1m";
title = "Services";
sites = [
{
title = "Zitadel";
url = "https://auth.kruining.eu";
icon = "sh:zitadel";
}
{
title = "Forgejo";
url = "https://git.amarth.cloud/chris";
icon = "sh:forgejo";
}
{
title = "Vaultwarden";
url = "https://vault.kruining.eu";
icon = "sh:vaultwarden";
}
];
}
{
type = "monitor";
cache = "1m";
title = "Observability";
sites = [
{
title = "Grafana";
url = "http://${config.networking.hostName}:${builtins.toString config.services.grafana.settings.server.http_port}";
icon = "sh:grafana";
}
{
title = "Prometheus";
url = "http://${config.networking.hostName}:${builtins.toString config.services.prometheus.port}";
icon = "sh:prometheus";
}
];
}
{
type = "monitor";
cache = "1m";
title = "Media";
sites = [
{
title = "Jellyfin";
url = "http://${config.networking.hostName}:8096";
icon = "sh:jellyfin";
}
{
title = "Radarr";
url = "http://${config.networking.hostName}:2001";
icon = "sh:radarr";
}
{
title = "Sonarr";
url = "http://${config.networking.hostName}:2002";
icon = "sh:sonarr";
}
{
title = "Lidarr";
url = "http://${config.networking.hostName}:2003";
icon = "sh:lidarr";
}
{
title = "Prowlarr";
url = "http://${config.networking.hostName}:2004";
icon = "sh:prowlarr";
}
{
title = "qBittorrent";
url = "http://${config.networking.hostName}:${builtins.toString config.services.qbittorrent.webuiPort}";
icon = "sh:qbittorrent";
}
{
title = "SABnzbd";
url = "http://${config.networking.hostName}:8080";
icon = "sh:sabnzbd";
}
];
}
{
type = "videos";
channels = [
"UCXuqSBlHAE6Xw-yeJA0Tunw" # Linus Tech Tips
"UCR-DXc1voovS8nhAvccRZhg" # Jeff Geerling
"UCsBjURrPoezykLs9EqgamOA" # Fireship
"UCBJycsmduvYEL83R_U4JriQ" # Marques Brownlee
"UCHnyfMqiRRG1u-2MsSQLbXA" # Veritasium
];
}
];
}
{
size = "small";
widgets = [
{
type = "weather";
location = "Amsterdam, The Netherlands";
units = "metric";
hour-format = "24h";
}
{
type = "server-stats";
servers = [
{
type = "local";
name = "Ulmo";
}
];
}
];
}
];
}
];
};
};
sops.templates."glance/secrets.env" = {
# owner = config.services.glance.user;
# group = config.services.glance.group;
content = ''
RADARR_KEY="${config.sops.placeholder."radarr/apikey"}"
SONARR_KEY="${config.sops.placeholder."sonarr/apikey"}"
LIDARR_KEY="${config.sops.placeholder."lidarr/apikey"}"
'';
};
};
}

View file

@ -1,95 +0,0 @@
{
config,
lib,
namespace,
inputs,
system,
...
}: let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.media.mydia;
in {
imports = [
inputs.mydia.nixosModules.default
];
options.${namespace}.services.media.mydia = {
enable = mkEnableOption "Enable Mydia";
};
config = mkIf cfg.enable {
services.mydia = {
enable = true;
port = 2010;
listenAddress = "0.0.0.0";
openFirewall = true;
mediaLibraries = [
"/var/mydia/movies"
"/var/mydia/series"
];
database = {
# type = "sqlite";
# uri = "file:///var/lib/mydia/mydia.db";
type = "postgres";
uri = "postgres://mydia@localhost:5432/mydia?sslmode=disable";
passwordFile = config.sops.templates."mydia/database_password".path;
};
secretKeyBaseFile = config.sops.secrets."mydia/secret_key_base".path;
guardianSecretKeyFile = config.sops.secrets."mydia/guardian_secret".path;
oidc = {
enable = true;
issuer = "https://auth.kruining.eu";
clientIdFile = config.sops.secrets."mydia/oidc_id".path;
clientSecretFile = config.sops.secrets."mydia/oidc_secret".path;
scopes = ["openid" "profile" "email"];
};
downloadClients = {
qbittorrent = {
type = "qbittorrent";
host = "localhost";
port = 2008;
username = "admin";
passwordFile = config.sops.secrets."mydia/qbittorrent_password".path;
useSsl = false;
};
};
};
sops.secrets = let
base =
["secret_key_base" "guardian_secret" "oidc_id" "oidc_secret"]
|> lib.map (name:
lib.nameValuePair "mydia/${name}" {
owner = config.services.mydia.user;
group = config.services.mydia.group;
restartUnits = ["mydia.service"];
})
|> lib.listToAttrs;
in
base
// {
"mydia/qbittorrent_password" = {
owner = config.services.mydia.user;
group = config.services.mydia.group;
restartUnits = ["mydia.service"];
key = "qbittorrent/password";
};
};
sops.templates."mydia/database_password" = {
owner = config.services.mydia.user;
group = config.services.mydia.group;
restartUnits = ["mydia.service"];
content = ''
DATABASE_PASSWORD=""
'';
};
};
}

View file

@ -6,7 +6,7 @@ let
cfg = config.${namespace}.services.media.nextcloud;
in
{
options.${namespace}.services.media.nextcloud = {
options.modules.services.nextcloud = {
enable = mkEnableOption "Nextcloud";
user = mkOption {
@ -40,7 +40,7 @@ in
services.nextcloud = {
enable = true;
# webserver = "caddy";
webserver = "caddy";
package = pkgs.nextcloud31;
hostName = "localhost";

View file

@ -2,10 +2,10 @@
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.media.nfs;
cfg = config.${namespace}.media.nfs;
in
{
options.${namespace}.services.media.nfs = {
options.${namespace}.media.nfs = {
enable = mkEnableOption "Enable NFS";
};

View file

@ -1,354 +0,0 @@
{
pkgs,
config,
lib,
namespace,
inputs,
system,
...
}: let
inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption mkOption types;
cfg = config.${namespace}.services.media.servarr;
in {
options.${namespace}.services.media = {
servarr = mkOption {
type = types.attrsOf (types.submodule ({name, ...}: {
options = {
enable = mkEnableOption "Enable ${name}";
debug = mkEnableOption "Use tofu plan instead of tofu apply for ${name} ";
port = mkOption {
type = types.port;
};
rootFolders = mkOption {
type = types.listOf types.str;
default = [];
};
};
}));
default = {};
};
};
config = {
services =
cfg
|> lib.mapAttrsToList (service: {
enable,
port,
...
}: (mkIf enable {
"${service}" =
{
enable = true;
openFirewall = true;
environmentFiles = [
config.sops.templates."${service}/config.env".path
];
settings = {
auth.authenticationMethod = "External";
server = {
bindaddress = "0.0.0.0";
port = port;
};
postgres = {
host = "localhost";
port = "5432";
user = service;
maindb = service;
logdb = service;
};
};
}
// (lib.optionalAttrs (service != "prowlarr") {
user = service;
group = "media";
});
}))
|> lib.concat [
{
qbittorrent = {
enable = true;
openFirewall = true;
webuiPort = 2008;
serverConfig = {
LegalNotice.Accepted = true;
Prefecences.WebUI = {
Username = "admin";
Password_PBKDF2 = "@ByteArray(JpfX3wSUcMolUFD+8AD67w==:fr5kmc6sK9xsCfGW6HkPX2K1lPYHL6g2ncLLwuOVmjphmxkwBJ8pi/XQDsDWzyM/MRh5zPhUld2Xqn8o7BWv3Q==)";
};
};
user = "qbittorrent";
group = "media";
};
# port is harcoded in nixpkgs module
sabnzbd = {
enable = true;
openFirewall = true;
configFile = "/var/media/sabnzbd/config.ini";
# configFile = config.sops.templates."sabnzbd/config.ini".path;
user = "sabnzbd";
group = "media";
};
postgresql = {
ensureDatabases = cfg |> lib.attrNames;
ensureUsers =
cfg
|> lib.attrNames
|> lib.map (service: {
name = service;
ensureDBOwnership = true;
});
};
}
]
|> lib.mkMerge;
systemd.services =
cfg
|> lib.mapAttrsToList (service: {
enable,
debug,
port,
rootFolders,
...
}: (mkIf enable {
"${service}ApplyTerraform" = let
config' = config;
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
inherit system;
modules = [
({
config,
lib,
...
}: {
config = {
variable = {
api_key = {
type = "string";
description = "${service} api key";
};
};
terraform.required_providers.${service} = {
source = "devopsarr/${service}";
version =
{
radarr = "2.3.3";
sonarr = "3.4.0";
prowlarr = "3.1.0";
lidarr = "1.13.0";
readarr = "2.1.0";
whisparr = "1.2.0";
}.${
service
};
};
provider.${service} = {
url = "http://127.0.0.1:${toString port}";
api_key = lib.tfRef "var.api_key";
};
resource = {
"${service}_root_folder" = mkIf (lib.elem service ["radarr" "sonarr" "whisparr"]) (
rootFolders
|> lib.imap (i: f: lib.nameValuePair "local${toString i}" {path = f;})
|> lib.listToAttrs
);
"${service}_download_client_qbittorrent" = mkIf (lib.elem service ["radarr" "sonarr" "lidarr" "whisparr"]) {
"main" = {
name = "qBittorrent";
enable = true;
priority = 1;
host = "localhost";
username = "admin";
password = "poChieN5feeph0igeaCadeJ9Xux0ohmuy6ruH5ieThaPheib3iuzoo0ahw1aiceif1feegioh9Aimau0pai5thoh5ieH0aechohw";
url_base = "/";
port = 2008;
};
};
# "${service}_download_client_sabnzbd" = mkIf (lib.elem service ["radarr" "sonarr" "lidarr" "whisparr"]) {
# "main" = {
# name = "SABnzbd";
# enable = true;
# priority = 1;
# host = "localhost";
# url_base = "/";
# port = 8080;
# };
# };
};
};
})
];
};
in {
description = "${service} terraform apply";
wantedBy = ["multi-user.target"];
wants = ["${service}.service"];
preStart = ''
install -d -m 0770 -o ${service} -g media /var/lib/${service}ApplyTerraform
${
rootFolders
|> lib.map (folder: "install -d -m 0770 -o media -g media ${folder}")
|> lib.join "\n"
}
'';
script = ''
# Sleep for a bit to give the service a chance to start up
sleep 5s
if [ "$(systemctl is-active ${service})" != "active" ]; then
echo "${service} is not running"
exit 1
fi
# Print the path to the source for easier debugging
echo "config location: ${terraformConfiguration}"
# Copy infra code into workspace
cp -f ${terraformConfiguration} config.tf.json
# Initialize OpenTofu
${lib.getExe pkgs.opentofu} init
# Run the infrastructure code
${lib.getExe pkgs.opentofu} \
${
if debug
then "plan"
else "apply -auto-approve"
} \
-var-file='${config.sops.templates."${service}/config.tfvars".path}'
'';
serviceConfig = {
Type = "oneshot";
User = service;
Group = "media";
WorkingDirectory = "/var/lib/${service}ApplyTerraform";
EnvironmentFile = [
config.sops.templates."${service}/config.env".path
];
};
};
}))
|> lib.mkMerge;
users =
cfg
|> lib.mapAttrsToList (service: {enable, ...}: (mkIf enable {
users.${service} = {
isSystemUser = true;
group = lib.mkDefault service;
extraGroups = ["media"];
};
groups.${service} = {};
}))
|> lib.mkMerge;
sops =
cfg
|> lib.mapAttrsToList (service: {enable, ...}: (mkIf enable {
secrets."${service}/apikey" = {
owner = service;
group = "media";
restartUnits = ["${service}.service"];
};
templates = {
"${service}/config.env" = {
owner = service;
group = "media";
restartUnits = ["${service}.service"];
content = ''
${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}"
'';
};
"${service}/config.tfvars" = {
owner = service;
group = "media";
restartUnits = ["${service}.service"];
content = ''
api_key = "${config.sops.placeholder."${service}/apikey"}"
'';
};
};
}))
|> lib.concat [
{
secrets = {
"sabnzbd/apikey" = {};
"sabnzbd/sunnyweb/username" = {};
"sabnzbd/sunnyweb/password" = {};
};
templates = {
"sabnzbd/config.ini" = {
owner = "sabnzbd";
group = "media";
mode = "0660";
content = ''
__version__ = 19
__encoding__ = utf-8
[misc]
download_dir = /var/media/downloads/incomplete
complete_dir = /var/media/downloads/done
api_key = ${config.sops.placeholder."sabnzbd/apikey"}
log_dir = logs
[servers]
[[news.sunnyusenet.com]]
name = news.sunnyusenet.com
displayname = news.sunnyusenet.com
host = news.sunnyusenet.com
port = 563
timeout = 60
username = ${config.sops.placeholder."sabnzbd/sunnyweb/username"}
password = ${config.sops.placeholder."sabnzbd/sunnyweb/password"}
connections = 8
ssl = 1
ssl_verify = 3
ssl_ciphers = ""
enable = 1
required = 0
optional = 0
retention = 0
expire_date = ""
quota = ""
usage_at_start = 0
priority = 1
notes = ""
'';
};
};
}
]
|> lib.mkMerge;
};
}

View file

@ -1,7 +0,0 @@
{
"title": "Default Dash",
"description": "The default dashboard",
"timezone": "browser",
"editable": false,
"panels": []
}

View file

@ -1,147 +0,0 @@
{
pkgs,
config,
lib,
namespace,
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
cfg = config.${namespace}.services.observability.grafana;
db_user = "grafana";
db_name = "grafana";
in {
options.${namespace}.services.observability.grafana = {
enable = mkEnableOption "enable Grafana";
};
config = mkIf cfg.enable {
services = {
grafana = {
enable = true;
openFirewall = true;
settings = {
server = {
http_port = 9001;
http_addr = "0.0.0.0";
domain = "ulmo";
};
auth = {
disable_login_form = false;
oauth_auto_login = true;
};
"auth.basic".enable = false;
"auth.generic_oauth" = {
enable = true;
name = "Zitadel";
client_id = "$__file{${config.sops.secrets."grafana/oidc_id".path}}";
client_secret = "$__file{${config.sops.secrets."grafana/oidc_secret".path}}";
scopes = "openid email profile offline_access urn:zitadel:iam:org:project:roles";
email_attribute_path = "email";
login_attribute_path = "username";
name_attribute_path = "full_name";
role_attribute_path = "contains(urn:zitadel:iam:org:project:roles[*], 'owner') && 'GrafanaAdmin' || contains(urn:zitadel:iam:org:project:roles[*], 'contributer') && 'Editor' || 'Viewer'";
auth_url = "https://auth.kruining.eu/oauth/v2/authorize";
token_url = "https://auth.kruining.eu/oauth/v2/token";
api_url = "https://auth.kruining.eu/oidc/v1/userinfo";
allow_sign_up = true;
auto_login = true;
use_pkce = true;
usr_refresh_token = true;
allow_assign_grafana_admin = true;
};
database = {
type = "postgres";
host = "/var/run/postgresql:5432";
name = db_name;
user = db_user;
ssl_mode = "disable";
};
users = {
allow_sign_up = false;
allow_org_create = false;
viewers_can_edit = false;
default_theme = "system";
};
analytics = {
reporting_enabled = false;
check_for_updates = false;
check_for_plugin_updates = false;
feedback_links_enabled = false;
};
};
provision = {
enable = true;
dashboards.settings = {
apiVersion = 1;
providers = [
{
name = "Default Dashboard";
disableDeletion = true;
allowUiUpdates = false;
options = {
path = "/etc/grafana/dashboards";
foldersFromFilesStructure = true;
};
}
];
};
datasources.settings.datasources = [
{
name = "Prometheus";
type = "prometheus";
url = "http://localhost:9005";
isDefault = true;
editable = false;
}
{
name = "Loki";
type = "loki";
url = "http://localhost:9003";
editable = false;
}
];
};
};
postgresql = {
enable = true;
ensureDatabases = [db_name];
ensureUsers = [
{
name = db_user;
ensureDBOwnership = true;
}
];
};
};
environment.etc."/grafana/dashboards/default.json".source = ./dashboards/default.json;
sops = {
secrets = {
"grafana/oidc_id" = {
owner = "grafana";
group = "grafana";
};
"grafana/oidc_secret" = {
owner = "grafana";
group = "grafana";
};
};
};
};
}

View file

@ -1,49 +0,0 @@
{ pkgs, config, lib, namespace, ... }:
let
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
cfg = config.${namespace}.services.observability.loki;
in
{
options.${namespace}.services.observability.loki = {
enable = mkEnableOption "enable Grafana Loki";
};
config = mkIf cfg.enable {
services.loki = {
enable = true;
configuration = {
auth_enabled = false;
server = {
http_listen_port = 9003;
};
common = {
ring = {
instance_addr = "127.0.0.1";
kvstore.store = "inmemory";
};
replication_factor = 1;
path_prefix = "/tmp/loki";
};
schema_config.configs = [
{
from = "2025-01-01";
store = "tsdb";
object_store = "filesystem";
schema = "v13";
index = {
prefix = "index_";
period = "24h";
};
}
];
};
};
networking.firewall.allowedTCPPorts = [ 9003 ];
};
}

View file

@ -1,48 +0,0 @@
{ pkgs, config, lib, namespace, ... }:
let
inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.observability.prometheus;
in
{
options.${namespace}.services.observability.prometheus = {
enable = mkEnableOption "enable Prometheus";
};
config = mkIf cfg.enable {
services.prometheus = {
enable = true;
port = 9002;
globalConfig.scrape_interval = "15s";
scrapeConfigs = [
{
job_name = "prometheus";
static_configs = [
{ targets = [ "localhost:9002" ]; }
];
}
{
job_name = "node";
static_configs = [
{ targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ]; }
];
}
];
exporters = {
node = {
enable = true;
port = 9005;
enabledCollectors = [ "systemd" ];
openFirewall = true;
};
};
};
networking.firewall.allowedTCPPorts = [ 9002 ];
};
}

View file

@ -1,58 +0,0 @@
{ pkgs, config, lib, namespace, ... }:
let
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
cfg = config.${namespace}.services.observability.promtail;
in
{
options.${namespace}.services.observability.promtail = {
enable = mkEnableOption "enable Grafana Promtail";
};
config = mkIf cfg.enable {
services.promtail = {
enable = true;
# Ensures proper permissions
extraFlags = [
"-config.expand-env=true"
];
configuration = {
server = {
http_listen_port = 9004;
grpc_listen_port = 0;
};
positions = {
filename = "filename";
};
clients = [
{
url = "http://::1:9003/loki/api/v1/push";
}
];
scrape_configs = [
{
job_name = "journal";
journal = {
max_age = "12h";
labels = {
job = "systemd-journal";
host = "ulmo";
};
};
relabel_configs = [
{ source_labels = [ "__journal__systemd_unit" ]; target_label = "unit"; }
];
}
];
};
};
networking.firewall.allowedTCPPorts = [ 9004 ];
};
}

View file

@ -1,25 +0,0 @@
{ pkgs, config, lib, namespace, ... }:
let
inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.observability.uptime-kuma;
in
{
options.${namespace}.services.observability.uptime-kuma = {
enable = mkEnableOption "enable uptime kuma";
};
config = mkIf cfg.enable {
services.uptime-kuma = {
enable = true;
settings = {
PORT = toString 9006;
HOST = "0.0.0.0";
};
};
networking.firewall.allowedTCPPorts = [ 9006 ];
};
}

View file

@ -1,26 +0,0 @@
{ config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.persistance.postgresql;
in
{
options.${namespace}.services.persistance.postgresql = {
enable = mkEnableOption "Postgresql";
};
config = mkIf cfg.enable {
services = {
postgresql = {
enable = true;
authentication = ''
# Generated file, do not edit!
# TYPE DATABASE USER ADDRESS METHOD
local all all trust
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
'';
};
};
};
}

View file

@ -1,219 +1,28 @@
{
pkgs,
config,
lib,
namespace,
...
}: let
inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption mkOption types getAttrs toUpper concatMapAttrsStringSep;
{ pkgs, config, lib, namespace, ... }:
let
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
cfg = config.${namespace}.services.security.vaultwarden;
databaseProviderSqlite = types.submodule ({...}: {
options = {
type = mkOption {
type = types.enum ["sqlite"];
};
file = mkOption {
type = types.path;
description = ''
Path to sqlite database file.
'';
};
};
});
databaseProviderPostgresql = types.submodule ({...}: let
urlOptions = lib.${namespace}.options.mkUrlOptions {
host = {
description = ''
Hostname of the postgresql server
'';
};
port = {
default = 5432;
example = "5432";
description = ''
Port of the postgresql server
'';
};
protocol = mkOption {
default = "postgres";
example = "postgres";
};
};
in {
options =
{
type = mkOption {
type = types.enum ["postgresql"];
};
sslMode = mkOption {
type = types.enum ["verify-ca" "verify-full" "require" "prefer" "allow" "disabled"];
default = "verify-full";
example = "verify-ca";
description = ''
How to verify the server's ssl
| mode | eavesdropping protection | MITM protection | Statement |
|-------------|--------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| disable | No | No | I don't care about security, and I don't want to pay the overhead of encryption. |
| allow | Maybe | No | I don't care about security, but I will pay the overhead of encryption if the server insists on it. |
| prefer | Maybe | No | I don't care about encryption, but I wish to pay the overhead of encryption if the server supports it. |
| require | Yes | No | I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want. |
| verify-ca | Yes | Depends on CA policy | I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server that I trust. |
| verify-full | Yes | Yes | I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server I trust, and that it's the one I specify. |
[Source](https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS)
'';
};
}
// (urlOptions |> getAttrs ["protocol" "host" "port"]);
});
in {
in
{
options.${namespace}.services.security.vaultwarden = {
enable = mkEnableOption "enable vaultwarden";
database = mkOption {
type = types.oneOf [
(types.addCheck databaseProviderSqlite (x: x ? type && x.type == "sqlite"))
(types.addCheck databaseProviderPostgresql (x: x ? type && x.type == "postgresql"))
null
];
default = null;
description = '''';
};
};
config = mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d '/var/lib/vaultwarden' 0700 vaultwarden vaultwarden - -"
environment.systemPackages = with pkgs; [
vaultwarden
vaultwarden-postgresql
];
# systemd.services.vaultwarden.wants = [ "zitadelApplyTerraform.service" ];
services.vaultwarden = {
enable = true;
dbBackend = "postgresql";
services = {
vaultwarden = {
enable = true;
dbBackend = "postgresql";
package = pkgs.${namespace}.vaultwarden;
config = {
SIGNUPS_ALLOWED = false;
DOMAIN = "https://vault.kruining.eu";
DATABASE_URL = "postgres://localhost:5432/vaultwarden?sslmode=disable";
WEB_VAULT_ENABLED = true;
SSO_ENABLED = true;
SSO_ONLY = true;
SSO_PKCE = true;
SSO_AUTH_ONLY_NOT_SESSION = false;
SSO_ROLES_ENABLED = true;
SSO_ORGANIZATIONS_ENABLED = true;
SSO_ORGANIZATIONS_REVOCATION = true;
SSO_AUTHORITY = "https://auth.kruining.eu/";
SSO_SCOPES = "email profile offline_access";
ROCKET_ADDRESS = "::1";
ROCKET_PORT = 8222;
ROCKET_LOG = "critical";
SMTP_HOST = "black-mail.nl";
SMTP_PORT = 587;
SMTP_SECURITY = "starttls";
SMTP_USERNAME = "chris@kruining.eu";
SMTP_FROM = "chris@kruining.eu";
SMTP_FROM_NAME = "Chris' Vaultwarden";
};
environmentFile = [
"/var/lib/zitadel/clients/nix_ulmo_vaultwarden"
config.sops.templates."vaultwarden/config.env".path
];
};
postgresql = {
enable = true;
ensureDatabases = ["vaultwarden"];
ensureUsers = [
{
name = "vaultwarden";
ensureDBOwnership = true;
}
];
};
caddy = {
enable = true;
virtualHosts = {
"vault.kruining.eu".extraConfig = ''
encode zstd gzip
handle_path /admin {
respond 401 {
close
}
}
reverse_proxy http://localhost:${toString config.services.vaultwarden.config.ROCKET_PORT} {
header_up X-Real-IP {remote_host}
}
'';
};
};
};
sops = {
secrets = {
"vaultwarden/email" = {
owner = config.users.users.vaultwarden.name;
group = config.users.users.vaultwarden.name;
key = "email/chris_kruining_eu";
restartUnits = ["vaultwarden.service"];
};
};
templates = {
"vaultwarden/config.env" = {
content = ''
SMTP_PASSWORD='${config.sops.placeholder."vaultwarden/email"}';
'';
owner = config.users.users.vaultwarden.name;
group = config.users.groups.vaultwarden.name;
};
temp-db-output.content = let
config =
cfg.database
|> (
{type, ...} @ db:
if type == "sqlite"
then {inherit (db) type file;}
else if type == "postgresql"
then {
inherit (db) type;
url = lib.${namespace}.strings.toUrl {
inherit (db) protocol host port;
path = "vaultwarden";
query = {
sslmode = db.sslMode;
};
};
}
else {}
)
|> concatMapAttrsStringSep "\n" (n: v: "${toUpper n}=${v}");
in ''
# GENERATED VALUES
${config}
'';
config = {
SIGNUPS_ALLOWED = false;
DOMAIN = "https://passwords.kruining.eu";
};
};
};

View file

@ -12,7 +12,6 @@ in
config = mkIf cfg.enable {
virtualisation = {
containers.enable = true;
oci-containers.backend = "podman";
podman = {
enable = true;

View file

@ -1,2 +1,2 @@
{...}: {
}
{ ... }:
{}

View file

@ -1,4 +1,4 @@
{ pkgs, config, namespace, inputs, system, ... }:
{ pkgs, config, namespace, inputs, ... }:
let
cfg = config.${namespace}.system.security.sops;
in
@ -13,14 +13,10 @@ in
environment.systemPackages = with pkgs; [ sops ];
sops = {
defaultSopsFormat = "yaml";
defaultSopsFile = inputs.self + "/systems/${system}/${config.networking.hostName}/secrets.yml";
age.keyFile = "/home/.sops-key.age";
age = {
# keyFile = "~/.config/sops/age/keys.txt";
# sshKeyPaths = [ "~/.ssh/id_ed25519" ];
# generateKey = true;
};
defaultSopsFile = ../../../../systems/x86_64-linux/${config.networking.hostName}/secrets.yaml;
defaultSopsFormat = "yaml";
};
};
}

View file

@ -14,8 +14,9 @@ in
sudo-rs = {
enable = true;
execWheelOnly = true;
extraConfig = ''Defaults env_keep += "EDITOR PATH DISPLAY"'';
extraConfig = ''
Defaults env_keep += "EDITOR PATH DISPLAY"
'';
};
};
};

View file

@ -1,29 +0,0 @@
{ lib, stdenv, rustPlatform, fetchFromGitHub, openssl, pkg-config, postgresql, dbBackend ? "postgresql", ... }:
rustPlatform.buildRustPackage rec {
pname = "vaultwarden";
version = "1.34.3";
src = fetchFromGitHub {
owner = "Timshel";
repo = "vaultwarden";
rev = "1.34.3";
hash = "sha256-Dj0ySVRvBZ/57+UHas3VI8bi/0JBRqn0IW1Dq+405J0=";
};
cargoHash = "sha256-4sDagd2XGamBz1XvDj4ycRVJ0F+4iwHOPlj/RglNDqE=";
# used for "Server Installed" version in admin panel
env.VW_VERSION = version;
nativeBuildInputs = [ pkg-config ];
buildInputs =
[ openssl ]
++ lib.optional (dbBackend == "postgresql") postgresql;
buildFeatures = dbBackend;
meta = with lib; {
license = licenses.agpl3Only;
mainProgram = "vaultwarden";
};
}

View file

@ -1,395 +0,0 @@
__version__ = 19
__encoding__ = utf-8
[misc]
helpful_warnings = 1
queue_complete = hibernate_pc
queue_complete_pers = 0
bandwidth_perc = 100
refresh_rate = 1
interface_settings = '{"dateFormat":"YYYY-MM-DD HH:mm","extraQueueColumns":[],"extraHistoryColumns":[],"displayCompact":false,"displayFullWidth":false,"confirmDeleteQueue":true,"confirmDeleteHistory":true,"keyboardShortcuts":true}'
queue_limit = 20
config_lock = 0
fixed_ports = 1
notified_new_skin = 2
direct_unpack_tested = 1
sorters_converted = 1
check_new_rel = 1
auto_browser = 0
language = en
enable_https_verification = 0
host = 0.0.0.0
port = 8080
https_port = ""
username = ""
password = ""
bandwidth_max = ""
cache_limit = 1G
web_dir = Glitter
web_color = Auto
https_cert = server.cert
https_key = server.key
https_chain = ""
enable_https = 0
inet_exposure = 0
api_key = 0052eba0db9d4b4f93a8a96f0cb85198
nzb_key = 171ebeb3e0044c379dc7719bef6b3144
socks5_proxy_url = ""
permissions = ""
download_dir = /var/media/downloads/incomplete
download_free = ""
complete_dir = /var/media/downloads/done
complete_free = ""
fulldisk_autoresume = 0
script_dir = ""
nzb_backup_dir = ""
admin_dir = admin
backup_dir = ""
dirscan_dir = ""
dirscan_speed = 5
password_file = ""
log_dir = logs
max_art_tries = 3
top_only = 0
sfv_check = 1
script_can_fail = 0
enable_recursive = 1
flat_unpack = 0
par_option = ""
pre_check = 0
nice = ""
win_process_prio = 3
ionice = ""
fail_hopeless_jobs = 1
fast_fail = 1
auto_disconnect = 1
pre_script = None
end_queue_script = None
no_dupes = 0
no_series_dupes = 0
no_smart_dupes = 0
dupes_propercheck = 1
pause_on_pwrar = 1
ignore_samples = 0
deobfuscate_final_filenames = 1
auto_sort = ""
direct_unpack = 0
propagation_delay = 0
folder_rename = 1
replace_spaces = 0
replace_underscores = 0
replace_dots = 0
safe_postproc = 1
pause_on_post_processing = 0
enable_all_par = 0
sanitize_safe = 0
cleanup_list = ,
unwanted_extensions = ,
action_on_unwanted_extensions = 0
unwanted_extensions_mode = 0
new_nzb_on_failure = 0
history_retention = ""
history_retention_option = all
history_retention_number = 1
quota_size = ""
quota_day = ""
quota_resume = 0
quota_period = m
enable_tv_sorting = 0
tv_sort_string = ""
tv_categories = tv,
enable_movie_sorting = 0
movie_sort_string = ""
movie_sort_extra = -cd%1
movie_categories = movies,
enable_date_sorting = 0
date_sort_string = ""
date_categories = tv,
schedlines = ,
rss_rate = 60
ampm = 0
start_paused = 0
preserve_paused_state = 0
enable_par_cleanup = 1
process_unpacked_par2 = 1
enable_multipar = 1
enable_unrar = 1
enable_7zip = 1
enable_filejoin = 1
enable_tsjoin = 1
overwrite_files = 0
ignore_unrar_dates = 0
backup_for_duplicates = 0
empty_postproc = 0
wait_for_dfolder = 0
rss_filenames = 0
api_logging = 1
html_login = 1
warn_dupl_jobs = 0
keep_awake = 1
tray_icon = 1
allow_incomplete_nzb = 0
enable_broadcast = 1
ipv6_hosting = 0
ipv6_staging = 0
api_warnings = 1
no_penalties = 0
x_frame_options = 1
allow_old_ssl_tls = 0
enable_season_sorting = 1
verify_xff_header = 0
rss_odd_titles = nzbindex.nl/, nzbindex.com/, nzbclub.com/
quick_check_ext_ignore = nfo, sfv, srr
req_completion_rate = 100.2
selftest_host = self-test.sabnzbd.org
movie_rename_limit = 100M
episode_rename_limit = 20M
size_limit = 0
direct_unpack_threads = 3
history_limit = 5
wait_ext_drive = 5
max_foldername_length = 246
nomedia_marker = ""
ipv6_servers = 1
url_base = /sabnzbd
host_whitelist = usenet.kruining.eu, ulmo
local_ranges = ,
max_url_retries = 10
downloader_sleep_time = 10
receive_threads = 2
switchinterval = 0.005
ssdp_broadcast_interval = 15
ext_rename_ignore = ,
email_server = ""
email_to = ,
email_from = ""
email_account = ""
email_pwd = ""
email_endjob = 0
email_full = 0
email_dir = ""
email_rss = 0
email_cats = *,
config_conversion_version = 4
disable_par2cmdline = 0
disable_archive = 0
unrar_parameters = ""
outgoing_nntp_ip = ""
[logging]
log_level = 1
max_log_size = 5242880
log_backups = 5
[ncenter]
ncenter_enable = 0
ncenter_cats = *,
ncenter_prio_startup = 0
ncenter_prio_download = 0
ncenter_prio_pause_resume = 0
ncenter_prio_pp = 0
ncenter_prio_complete = 1
ncenter_prio_failed = 1
ncenter_prio_disk_full = 1
ncenter_prio_new_login = 0
ncenter_prio_warning = 0
ncenter_prio_error = 0
ncenter_prio_queue_done = 0
ncenter_prio_other = 1
ncenter_prio_quota = 1
[acenter]
acenter_enable = 0
acenter_cats = *,
acenter_prio_startup = 0
acenter_prio_download = 0
acenter_prio_pause_resume = 0
acenter_prio_pp = 0
acenter_prio_complete = 1
acenter_prio_failed = 1
acenter_prio_disk_full = 1
acenter_prio_new_login = 0
acenter_prio_warning = 0
acenter_prio_error = 0
acenter_prio_queue_done = 0
acenter_prio_other = 1
acenter_prio_quota = 1
[ntfosd]
ntfosd_enable = 1
ntfosd_cats = *,
ntfosd_prio_startup = 0
ntfosd_prio_download = 0
ntfosd_prio_pause_resume = 0
ntfosd_prio_pp = 0
ntfosd_prio_complete = 1
ntfosd_prio_failed = 1
ntfosd_prio_disk_full = 1
ntfosd_prio_new_login = 0
ntfosd_prio_warning = 0
ntfosd_prio_error = 0
ntfosd_prio_queue_done = 0
ntfosd_prio_other = 1
ntfosd_prio_quota = 1
[prowl]
prowl_enable = 0
prowl_cats = *,
prowl_apikey = ""
prowl_prio_startup = -3
prowl_prio_download = -3
prowl_prio_pause_resume = -3
prowl_prio_pp = -3
prowl_prio_complete = 0
prowl_prio_failed = 1
prowl_prio_disk_full = 1
prowl_prio_new_login = -3
prowl_prio_warning = -3
prowl_prio_error = -3
prowl_prio_queue_done = -3
prowl_prio_other = 0
prowl_prio_quota = 0
[pushover]
pushover_token = ""
pushover_userkey = ""
pushover_device = ""
pushover_emergency_expire = 3600
pushover_emergency_retry = 60
pushover_enable = 0
pushover_cats = *,
pushover_prio_startup = -3
pushover_prio_download = -2
pushover_prio_pause_resume = -2
pushover_prio_pp = -3
pushover_prio_complete = -1
pushover_prio_failed = -1
pushover_prio_disk_full = 1
pushover_prio_new_login = -3
pushover_prio_warning = 1
pushover_prio_error = 1
pushover_prio_queue_done = -3
pushover_prio_other = -1
pushover_prio_quota = -1
[pushbullet]
pushbullet_enable = 0
pushbullet_cats = *,
pushbullet_apikey = ""
pushbullet_device = ""
pushbullet_prio_startup = 0
pushbullet_prio_download = 0
pushbullet_prio_pause_resume = 0
pushbullet_prio_pp = 0
pushbullet_prio_complete = 1
pushbullet_prio_failed = 1
pushbullet_prio_disk_full = 1
pushbullet_prio_new_login = 0
pushbullet_prio_warning = 0
pushbullet_prio_error = 0
pushbullet_prio_queue_done = 0
pushbullet_prio_other = 1
pushbullet_prio_quota = 1
[apprise]
apprise_enable = 0
apprise_cats = *,
apprise_urls = ""
apprise_target_startup = ""
apprise_target_startup_enable = 0
apprise_target_download = ""
apprise_target_download_enable = 0
apprise_target_pause_resume = ""
apprise_target_pause_resume_enable = 0
apprise_target_pp = ""
apprise_target_pp_enable = 0
apprise_target_complete = ""
apprise_target_complete_enable = 1
apprise_target_failed = ""
apprise_target_failed_enable = 1
apprise_target_disk_full = ""
apprise_target_disk_full_enable = 0
apprise_target_new_login = ""
apprise_target_new_login_enable = 1
apprise_target_warning = ""
apprise_target_warning_enable = 0
apprise_target_error = ""
apprise_target_error_enable = 0
apprise_target_queue_done = ""
apprise_target_queue_done_enable = 0
apprise_target_other = ""
apprise_target_other_enable = 1
apprise_target_quota = ""
apprise_target_quota_enable = 1
[nscript]
nscript_enable = 0
nscript_cats = *,
nscript_script = ""
nscript_parameters = ""
nscript_prio_startup = 0
nscript_prio_download = 0
nscript_prio_pause_resume = 0
nscript_prio_pp = 0
nscript_prio_complete = 1
nscript_prio_failed = 1
nscript_prio_disk_full = 1
nscript_prio_new_login = 0
nscript_prio_warning = 0
nscript_prio_error = 0
nscript_prio_queue_done = 0
nscript_prio_other = 1
nscript_prio_quota = 1
[categories]
[[*]]
name = *
order = 0
pp = 3
script = None
dir = ""
newzbin = ""
priority = 0
[[movies]]
name = movies
order = 1
pp = ""
script = Default
dir = ""
newzbin = ""
priority = -100
[[tv]]
name = tv
order = 2
pp = ""
script = Default
dir = ""
newzbin = ""
priority = -100
[[audio]]
name = audio
order = 3
pp = ""
script = Default
dir = ""
newzbin = ""
priority = -100
[[software]]
name = software
order = 4
pp = ""
script = Default
dir = ""
newzbin = ""
priority = -100
[servers]
[[news.sunnyusenet.com]]
name = news.sunnyusenet.com
displayname = news.sunnyusenet.com
host = news.sunnyusenet.com
port = 563
timeout = 60
username = michiel@hazelhof.nl
password = dasusenet
connections = 8
ssl = 1
ssl_verify = 3
ssl_ciphers = ""
enable = 1
required = 0
optional = 0
retention = 0
expire_date = ""
quota = ""
usage_at_start = 0
priority = 1
notes = ""

View file

@ -1,21 +0,0 @@
{
mkShell,
inputs,
pkgs,
stdenv,
...
}:
mkShell {
packages = with pkgs; [
bash
sops
just
yq
pwgen
alejandra
nil
nixd
openssl
inputs.clan-core.packages.${stdenv.hostPlatform.system}.clan-cli
];
}

View file

@ -1,8 +1,3 @@
# Description
<<<<<<< HEAD
My steambox.
=======
My desktop, reasoning for the name being the following chain of thought:
**Manwe -> the king of the valar -> leader -> desktop is main machine**
>>>>>>> 72b0f6f8fad97a4ade1b54dfada26828a170febf

View file

@ -5,8 +5,6 @@
./hardware.nix
];
system.activationScripts.remove-gtkrc.text = "rm -f /home/chris/.gtkrc-2.0";
sneeuwvlok = {
hardware.has = {
gpu.amd = true;

View file

@ -1,34 +1,59 @@
{ config, lib, pkgs, modulesPath, ... }:
{ config, lib, pkgs, modulesPath, inputs, ... }:
let
inherit (lib.modules) mkDefault;
in
{
# TODO :: Implement disko at some point
imports = [
inputs.disko.nixosModules.disko
];
swapDevices = [];
config = {
swapDevices = [];
boot.supportedFilesystems = [ "nfs" ];
boot.supportedFilesystems = [ "nfs" ];
fileSystems = {
"/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
disko.devices = {
disk = {
main = {
device = "/dev/nvme0";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
size = "100M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
"/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
};
fileSystems = {
"/home/chris/media" = {
device = "ulmo:/";
fsType = "nfs";
};
"/home/chris/media" = {
device = "ulmo:/";
fsType = "nfs";
"/home/chris/mandos" = {
device = "mandos:/";
fsType = "nfs";
};
};
# "/home/chris/mandos" = {
# device = "mandos:/";
# fsType = "nfs";
# };
};
}

View file

@ -0,0 +1,31 @@
zitadel:
masterKey: ENC[AES256_GCM,data:iSeZOloWLrdP8S+ac7ubIcv9TF3Sm8Ni,iv:8v3/ratFQ5vq2rbZOUMKfPhVTA9uQY2eFQU4IR8s3VU=,tag:9y90aDQ2PfFT//X2i2YvvA==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age10c5hmykkduvy75yvqfnchm5lcesr5puarhkwp4l7xdwpykdm397q6xdxuy
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4R0UyWmx5L3hCbGhQVXI0
NmpkMThPVlgrRHZZMnFrNTAwbzVTY1F6NEVVCjJaRHdhbHV6R1RJM2JIQzc3dkNu
a01FYlM3b1dXbmxGN2tWU3FMdXMveG8KLS0tIG1SSjNXdXZNN2ZyQ2UyZ0pIZXJJ
NmpMS2oySFE1S1RER3J1RGl4MlRQK00Ks+PcxcHmygYz+a+d0ZrzrdUpTQ50NYkA
aDFbtRtukn9e7i3bGUyD4nisSvs4YjfoQxR/pC8hs4k3f5V2jwDh2w==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ewes0f5snqx3sh5ul6fa6qtxzhd25829v6mf5rx2wnheat6fefps5rme2x
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwaTN4clFoWDNwU2lpaHBn
M2pVeU5oM0JRNmp6NEJjQ3BHeWlzeSs3bTI0CnBocngvbzZQUXBsMG9Oc2J6dlBT
MjdtaFdmOHg5ZmZmSkViWGJFYThQYXcKLS0tIFRNd2JiVlFTREtDMTdzR2V0SlVo
Q0d5ZDVDM05LdFp4UnB4dFRPUm5vU0UKR/MAONEWaT6XXyPB1IrSIKqW5PZNIbuB
n7QX3DJIzlajtmq+82/wPFPTBkLvSSjV5FKL5ErMwTDndcIn+NlOhQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-08-11T13:11:00Z"
mac: ENC[AES256_GCM,data:P34YsR/Rvc3q4Os5n9hxonJLCXwifMRnKOCM59h5MRMT/aqjl+QlBX+oUADsqDSrhUscQb3N/UlpFeOT6qg+FmJbT/mYMH6v1xK16VD0M7VWydXpmjDu5If+O89lgDHsiEOGDgeR04jkiaY0yzT9U8l9CND5fMvF3I9o5Z1SZQk=,iv:NgUD8gB2bQa5vh0nb0Ngqp5dn0yqskHudWo8xoVjM4Q=,tag:5oTcnailDCHeMvMLz63e1w==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.9.4

View file

@ -1,229 +1,14 @@
{...}: {
{ ... }:
{
imports = [
./disks.nix
./hardware.nix
];
networking = {
interfaces.enp2s0 = {
ipv6.addresses = [
{
address = "2a0d:6e00:1dc9:0::dead:beef";
prefixLength = 64;
}
];
useDHCP = true;
};
defaultGateway = {
address = "192.168.1.1";
interface = "enp2s0";
};
defaultGateway6 = {
address = "fe80::1";
interface = "enp2s0";
};
};
# Expose amarht cloud stuff like this until I have a proper solution
services.caddy.virtualHosts = {
"auth.amarth.cloud".extraConfig = ''
reverse_proxy http://192.168.1.223:9092
'';
"amarth.cloud".extraConfig = ''
reverse_proxy http://192.168.1.223:8080
'';
};
sneeuwvlok = {
services = {
backup.borg.enable = true;
authentication.zitadel = {
enable = true;
organization = {
nix = {
user = {
chris = {
email = "chris@kruining.eu";
firstName = "Chris";
lastName = "Kruining";
roles = ["ORG_OWNER"];
instanceRoles = ["IAM_OWNER"];
};
kaas = {
email = "chris+kaas@kruining.eu";
firstName = "Kaas";
lastName = "Kruining";
};
};
project = {
ulmo = {
projectRoleCheck = true;
projectRoleAssertion = true;
hasProjectCheck = true;
role = {
jellyfin = {
group = "jellyfin";
};
jellyfin_admin = {
group = "jellyfin";
};
};
assign = {
chris = ["jellyfin" "jellyfin_admin"];
kaas = ["jellyfin"];
};
application = {
jellyfin = {
redirectUris = ["https://jellyfin.kruining.eu/sso/OID/redirect/zitadel"];
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
};
forgejo = {
redirectUris = ["https://git.amarth.cloud/user/oauth2/zitadel/callback"];
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
};
vaultwarden = {
redirectUris = ["https://vault.kruining.eu/identity/connect/oidc-signin"];
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
exportMap = {
client_id = "SSO_CLIENT_ID";
client_secret = "SSO_CLIENT_SECRET";
};
};
matrix = {
redirectUris = ["https://matrix.kruining.eu/_synapse/client/oidc/callback"];
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
};
mydia = {
redirectUris = ["http://localhost:2010/auth/oidc/callback"];
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
};
grafana = {
redirectUris = ["http://localhost:9001/login/generic_oauth"];
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
};
};
};
};
action = {
flattenRoles = {
script = ''
(ctx, api) => {
if (ctx.v1.user.grants == undefined || ctx.v1.user.grants.count == 0) {
return;
}
const roles = ctx.v1.user.grants.grants.flatMap(({ roles, projectId }) => roles.map(role => projectId + ':' + role));
api.v1.claims.setClaim('nix:zitadel:custom', JSON.stringify({ roles }));
};
'';
};
};
triggers = [
{
flowType = "customiseToken";
triggerType = "preUserinfoCreation";
actions = ["flattenRoles"];
}
{
flowType = "customiseToken";
triggerType = "preAccessTokenCreation";
actions = ["flattenRoles"];
}
];
};
};
};
communication.matrix.enable = true;
development.forgejo.enable = true;
networking.ssh.enable = true;
media.enable = true;
media.glance.enable = true;
media.mydia.enable = true;
media.nfs.enable = true;
media.servarr = {
radarr = {
enable = true;
port = 2001;
rootFolders = [
"/var/media/movies"
];
};
sonarr = {
enable = true;
# debug = true;
port = 2002;
rootFolders = [
"/var/media/series"
];
};
lidarr = {
enable = true;
debug = true;
port = 2003;
rootFolders = [
"/var/media/music"
];
};
prowlarr = {
enable = true;
debug = true;
port = 2004;
};
};
observability = {
grafana.enable = true;
prometheus.enable = true;
loki.enable = true;
promtail.enable = true;
# uptime-kuma.enable = true;
};
security.vaultwarden = {
enable = true;
database = {
# type = "sqlite";
# file = "/var/lib/vaultwarden/state.db";
type = "postgresql";
host = "localhost";
port = 5432;
sslMode = "disabled";
};
};
};
editor = {

View file

@ -5,7 +5,9 @@ in
{
# TODO :: Implement disko at some point
swapDevices = [];
swapDevices = [
{ device = "/dev/disk/by-uuid/0ddf001a-5679-482e-b254-04a1b9094794"; }
];
boot.supportedFilesystems = [ "nfs" ];

View file

@ -1,60 +0,0 @@
email:
chris_kruining_eu: ENC[AES256_GCM,data:/JS+dQ6ABlkdjRZP+sGeUY3js30swS4=,iv:d5CcoY6DD3DJ/e3t0OU/KUULccJpTN0uBQPQzl/3R0s=,tag:aTN7RdzXkIpci9tEBjevSA==,type:str]
info_amarth_cloud: ENC[AES256_GCM,data:/x7aAFAxXYYf79tB08VQmmuTIy2TvdSTFfAzIWdIr+I=,iv:plNxS6oOin+oEql+1xsePOsUfLJkf+ZPBviPRTbIghE=,tag:hjtK3rysd2NNBA2mWdv8cw==,type:str]
zitadel:
masterKey: ENC[AES256_GCM,data:4MPvBo407qrS7NF4oUTf84tZoPkSRmiHdD7qpkYeHME=,iv:H2NIAN0xBUDqnyco9gA3zYAsKtSeA/JpqYrPhc1eqc0=,tag:6OFGDfsucG5gDerImgpuXA==,type:str]
nix: {}
users: ENC[AES256_GCM,data:pMSK3Re/DZeMnFNCEgjTGWWMYYX5eLOoZwGg3oO7WQ0Sx7z7sLRPpqlGVw384G6uYjR19MpnVud6hHPkGY/FoTO0vsJ+a2anFpmLjLsPNehiQ57rnvnWJCeVJyTz0kqKt7vS1kGpdtjH5d98PerNzxR0FvTrjJhQCfHyP/S8/G6vD6cLmeBXaStpKJ6TM0UIPcWSTzrpV3O292xAFooWYv19hkM4C6IJtbej8zTmY8pEsHk5OY3w,iv:r3603xOtSE1CEdMR9epaWclbO3PXjMWpnJT7HEbF57o=,tag:HAPUlIuumY0IjL0P4Q1aFA==,type:str]
forgejo:
action_runner_token: ENC[AES256_GCM,data:yJ6OnRq5kinbuhvH06K5o3l86EafuBoojMwg/qhP+cgeH+BwPeE+Ng==,iv:IeXJahPxgLNIUFmkgp495tLVh8UyQBmJ2SnVEUhlhHs=,tag:XYQi613CxSp8AQeilJMrsg==,type:str]
synapse:
oidc_id: ENC[AES256_GCM,data:XbCpyGq0LeRJWq8dv/5Dipvp,iv:YDhgl26z1NBbIQLoLdGVz0+ze6o1ZcmgVHPfwoRj57I=,tag:y2vUuqnDmtTvVQmZCAlnLg==,type:str]
oidc_secret: ENC[AES256_GCM,data:nVFi5EFbNMZ0mvrDHVYC0NiwJlo2eEw44D+Fcv9SKSb2oO00lGEDkP/oXDj5YgDq6RLQSe3f/SUOn77ntwnZYg==,iv:awe7VNUYOn9ofl1QlQTrEN5d0i5WkVM35qndruL4VXo=,tag:8Yoc9lFF9aWbtAa5fzQGEA==,type:str]
radarr:
apikey: ENC[AES256_GCM,data:G141GW4PyS5pbAV39HcVscMw3s30txOgTZzWaL7o+ccZfnfDLv796O6xKXdqGZ8saLsveghLw9Z6a5luusHyQ3Q5ESL6W7SVeZVTuSqSC3i/4jl75FJxhnsgVsfrnYxzLGpKiw==,iv:sZl/XLh6y3WgSAn6nH3sFB6atBifZdghm+QsCNDbcjY=,tag:Tw+R80nrF0T0yDti0Uf+ig==,type:str]
sonarr:
apikey: ENC[AES256_GCM,data:s8bgDJ+LpIH1Mt3KSiIKB8LnxztOkHdc8J6+50o+HoDUAfIIsZkA2oX/m7UecrTSRi6ay8D9yjhe6ZwSNXhJh6wQqTS7gZWn8f6QfrfI+8DKdc9enh91suQxjkz8Q+wnKK0zBg==,iv:LmAe6v+6ItVnHB6gko6mhiGOuVBksBYP4dXfbxpAIPE=,tag:DZ8kwOwaWwWTGWEGu5S0Kg==,type:str]
lidarr:
apikey: ENC[AES256_GCM,data:I2eKaxidmxem7C7ukmyIfwASNqrkS4vEOiCcU5kSNY6DR0pXsYg0PBdgu8vzK6llbXODLdG5t55BordIWvVRJGAauo0FMvtp59NSNpza7cK68tdKGvNefD6bqhUIR06BY11niQ==,iv:48AD7cd17TlWY5yAagepLOIVwgxhD/d13Pnup6GsWDA=,tag:teOVtW8opE99hqAXQwvlrA==,type:str]
prowlarr:
apikey: ENC[AES256_GCM,data:pyZ2WGEs/PlIdhDsQq2TPGJbplkd5fLF0ZkBjITqIJlnAzYHb+rl+KOM4rHqQcI6yAJM8X1Y3ymGrD7vG7GiRxB7yoEG13SKhZIWOddTnxIhbkz81RfrL2fUJIydOaP6sS//9Q==,iv:Tr6MWoC6nC7rdVTOjT1T2itT+lVL4GnUiAr5/+IHAs0=,tag:keIJNuGeVht8+xSN3FnBGA==,type:str]
mydia:
oidc_id: ENC[AES256_GCM,data:LfYWh9EC0aio3w1Xsj/jtU6z,iv:+dX9KkNtfQMYSX4yr83KyXalWMD/aWby7fC8aL4ZT3I=,tag:CvdbMoMTuC9FohTMIE5pmg==,type:str]
oidc_secret: ENC[AES256_GCM,data:PgI4hmP/3wt9uj+1QvCYcT8Wav0hgCRADouzWM3V695SSfXfbwDgez8tA/tm1/1jymAU2F2sZH8G2hZ1cdHyHQ==,iv:h3o3jsTmnoNE3+mGX12J3ZU0/6PlQNjdndEvaj/czj0=,tag:p3+p4E8fBtR7a8UpM8cUsg==,type:str]
secret_key_base: ENC[AES256_GCM,data:yG7HJ5r74Qtxbeyf8F6dA0uHv2pQ8YAJKlKiKjS+m24JRvJWQaTThJ+c5HbuUa6R3e9XtVHchhlVPkF0Is/b+g==,iv:v65xdRr4JdKZmBtjZ08/J3LLqnphSGt9QfVPNQ2x/xg=,tag:n7tD2dhr4IJn1LWM9WW8UA==,type:str]
guardian_secret: ENC[AES256_GCM,data:OjnNFSHlecL+qXwlhTm++itRM6ga5E5KrSJxbgIUpbMEkIWgu3xhRtnPdipXbedgall0XdO/s+jnWCagZX94BA==,iv:DukdKvm9vey8BWUiml20tgA/Vji1XVX4+sUPge9nTk0=,tag:q3HdvgUYqR0APiaFz0ul5Q==,type:str]
qbittorrent:
password_hash: ENC[AES256_GCM,data:yCfCslj01wtfwzzPOGlwA6wLLf+EUuEweYa3ZxvDtd/VGMxuV38quV+ob1Of+W0UH3+U4Qmgh4BK3I3IJZuKOvNdkZ0i81YBwW6cgvZUmnxwh8wokpNzxCKbYk5nF7y7SaGEdzQLvV7ad3fNMJsQ+s2zCsKWbm+j8Bwgq0E=,iv:IIktPS9pYXaYPzH0r4wrkp31CpunKnr70Ainu6hOeWY=,tag:bYCfhDfIwiQZ1tKAvITewQ==,type:str]
password: ENC[AES256_GCM,data:UepYY6UjJV/jo2aXTOEnKRtsjSqOSYPQlKlrAa7rf9rdnt2UXGjCkvN+A72pICuIBCAmhXZBAUMvmWTV9trk6NREHe0cY1xTC7pNv3x9TM/ZQmH498pbT/95pYAKwouHp9heJQ==,iv:FzjF+xPoaOp+gplxpz940V2dkWSTWe8dWUxexCoxxHc=,tag:TDZsboq9fEmmBrwJN/HTpQ==,type:str]
grafana:
oidc_id: ENC[AES256_GCM,data:NVdIgCQ6nz4BSUDJYCKyILtK,iv:tcljy9PzC/yyd7TSdngyJt+uh60uXi2PKu47czErbaQ=,tag:zE4q3dD4UQaHIpGeZ1L48Q==,type:str]
oidc_secret: ENC[AES256_GCM,data:b7qILK9ZHW2khtM1Hl/KdjCv3Wq6eOo2Ym/cbjcMB8/3Hn2UelpP4K4lFyiV3bn1/GF6Jl5Z7A0EwMybOx0InA==,iv:3HL/7BiyObwT8DmFxzNPI9CdmCH/4j/4oc9x7qBE1k0=,tag:dBhcq1zLKy6N+jp/v42R4A==,type:str]
sabnzbd:
sunnyweb:
password: ENC[AES256_GCM,data:flw8AahqO1Mx,iv:Qhu8iVWMzzqy18y8dj3aHoBnSZatm74/tYvZ456l2sA=,tag:sCYBdw7kD0zJZFFr5EyPIQ==,type:str]
username: ENC[AES256_GCM,data:IboJ8WDWuVNgvrk7c3V8I5S6Xg==,iv:BRohMuQFQz2S+HFasIaok6npT3C5v/SlhAhbLQXfB0s=,tag:M3/u0WBQ3AufHqe4DCtsrA==,type:str]
apikey: ENC[AES256_GCM,data:j5sPXKbBhMdNHOuoTfZ+c8nGu5JameOgK2z428iLdP01Hi6MvHVaN8Zs8YxMoSBtOjdtIEC8MS+3m1S1rU/P4pCRfZpK5ua1DBHq4l0xROUqokFWjDcAmJJv3pYXl0cQxQcGKQ==,iv:v5hu3gmO1Zn1FfXkHLPGN9f7JOcQjzoQahdqJwfM+xY=,tag:uI1LFcTgcyRgAaTJ1kzKow==,type:str]
sops:
age:
- recipient: age19qfpf980tadguqq44zf6xwvjvl428dyrj46ha3n6aeqddwhtnuqqml7etq
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwdDZyZkxvNU4zM3NHb2gx
ZlhLZk5JWUFGMWZGeUVHNkFFU1NtZlBQVVhjCmZGai9NdmdUeU5VcW9ROVZKTW5q
cmZaQ2JlaldaTWduQklocUZLT2FUcGcKLS0tIHlqVU0wdXJ0dTE4dlZSVEczd2Yv
RVFxVHFxbkVNbEZsaVcwYXZCdUc5R1kKQdAN6LEKmGLCSkKhNuEr0YK2zl9Aw1kK
6C25lN532mG55zIRectZda1Fmi1GMZ/2v3b5qz7x+TDMA9m/47OjmA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ewes0f5snqx3sh5ul6fa6qtxzhd25829v6mf5rx2wnheat6fefps5rme2x
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoK3lqRDhEMXEvaUp3OWdV
eFlZSGpJcGs0RTdRbllWdmdZTzl3RTlDNlIwCm92R290NjNyK2NNbWpINTBhazNS
NTJYWEw0SGc1TUtrd0NZSmowakMvSlEKLS0tIG5uUEIrZGVORkRNVnBVOHgyMXZG
TTRWaHhpNWlkVDFmMFN4ZTNHMUxyNVkKV693pzTKRkZboQCMPr9IyMGSgxfuHXcb
Y6BNcp6Qg6PWtX5QI7wRkPNINAK1TEbRBba+b8h6gMmVU4DliQyFiQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-12-11T22:19:26Z"
mac: ENC[AES256_GCM,data:J1RVA3s9qemyLGo4svCofqIA4XNYgDWDc3JRbfynGLtAocOQPtXOLKoEauplDWMQ8hFIGRznIzv5XkCH6hfxQhjNI0UCuR0WhFZtnQU59hS+Qg4AQKVukRdjY136RpNiBMCMNhiXs8NAbuVxrFramgFClFQgVO+b6+Q3w2JspNE=,iv:hPUnwDUg+Wbx/YDugY8TjFIJqUzJE75tv4vzc2NHdrQ=,tag:xwqb7I315SbAlK7MlT9uBA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0