Compare commits

..

3 commits

Author SHA1 Message Date
Chris Kruining
e849826de6
chore: update dependencies 2025-12-08 16:32:45 +01:00
Chris Kruining
3730ab856b
feat: improve justfiles 2025-12-08 16:31:52 +01:00
Chris Kruining
eab9e8b58d trying some stuff 2025-12-08 15:30:25 +00:00
45 changed files with 1017 additions and 2119 deletions

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,58 +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, .[0], (.[1:] | map([joiner, .]) ), right] | flatten | join("");
def create(data; header_callback; cell_callback):
(data[0] | keys_unsorted) as $keys
| (data | map(to_entries | map(.value))) as $rows
| ([$keys] + $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,14 +1,14 @@
@_default: list
set unstable := true
set quiet := true
_default: list
[doc('List machines')]
@list:
list:
ls -1 ../systems/x86_64-linux/
[doc('Update target machine')]
[doc('Update the target machine')]
[no-exit-message]
@update machine:
echo "Checking vars"
cd .. && just vars _check {{ machine }}
echo ""
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 }} --build-host {{ machine }} --flake ..#{{ machine }} --log-format internal-json -v |& nom --json
update machine:
just assert '-d "../systems/x86_64-linux/{{ machine }}"' "Machine {{ machine }} does not exist, must be one of: $(ls ../systems/x86_64-linux/ | tr '\n' ' ')"
nixos-rebuild switch --use-remote-sudo --target-host {{ machine }} --flake ..#{{ machine }}

View file

@ -1,101 +0,0 @@
set unstable := true
set quiet := true
_default:
just --list users
[doc('List available users')]
[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")
';
[doc('Add a new user')]
[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' ''`
email=`input 'email' ''`
first_name=`input 'first name' ''`
last_name=`input 'last name' ''`
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")"
[doc('Remove a new user')]
[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,20 +1,18 @@
set unstable := true
set quiet := true
base_path := justfile_directory() + "/systems/x86_64-linux"
base_path := invocation_directory() / "systems/x86_64-linux"
_default:
just --list vars
just --list
[doc('List all vars of {machine}')]
[doc('list all vars of the target machine')]
list machine:
sops decrypt {{ base_path }}/{{ machine }}/secrets.yml
[doc('Edit all vars of {machine} in your editor')]
edit machine:
sops edit {{ base_path }}/{{ machine }}/secrets.yml
[doc('Set var {value} by {key} for {machine}')]
@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')\""
@ -23,11 +21,9 @@ edit machine:
echo "Done"
[doc('Get var by {key} from {machine}')]
get machine key:
sops decrypt {{ base_path }}/{{ machine }}/secrets.yml | yq ".$(echo "{{ key }}" | sed -E 's/\//./g') // \"\""
sops decrypt {{ base_path }}/{{ machine }}/secrets.yml | yq ".$(echo "{{ key }}" | sed -E 's/\//./g')"
[doc('Remove var by {key} for {machine}')]
remove machine key:
sops unset {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')"
@ -35,69 +31,3 @@ remove machine key:
git commit -m 'chore(secrets): removed secret "{{ key }}" from machine "{{ machine }}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null
echo "Done"
[doc('Remove var by {key} for {machine}')]
[script]
generate machine:
for key in $(nix eval --apply 'builtins.attrNames' --json ..#nixosConfigurations.{{ machine }}.config.sops.secrets | jq -r '.[]'); do
# Skip if there's no script
[ -f "{{ justfile_directory() }}/script/$key" ] || continue
# Skip if we already have a value
[ $(just vars get {{ machine }} "$key" | jq -r) ] && continue
echo "Executing script for $key"
just vars set {{ machine }} "$key" "$(cd -- "$(dirname "{{ justfile_directory() }}/script/$key")" && source "./$(basename $key)")"
done
[script]
check:
cd ..
for machine in $(ls {{ base_path }}); do
just vars _check "$machine"
done
[no-exit-message]
[script]
_check machine:
# If the default nix file is missing,
# we can skip this folder as we are
# missing the files used to compare
# the defined vs the configured secrets
if [ ! -f "{{ base_path }}/{{ machine }}/default.nix" ]; then
printf "\r• %-8sskipped\n" "{{ machine }}"
exit 0
fi
exec 3< <(jq -nr \
--rawfile defined <(nix eval --json ..#nixosConfigurations.{{ machine }}.config.sops.secrets 2>/dev/null) \
--rawfile configured <([ -f "{{ base_path }}/{{ machine }}/secrets.yml" ] && sops decrypt {{ base_path }}/{{ machine }}/secrets.yml | yq '.' || echo "{}") \
'
[ $configured | fromjson | paths(scalars) | join("/") ] as $conf
| $defined
| fromjson
| map(.key | select(. | IN($conf[]) | not))
| unique
| .[]
')
pid=$! # Process Id of the previous running command
spin='⠇⠋⠙⠸⢰⣠⣄⡆'
i=0
while kill -0 $pid 2>/dev/null
do
i=$(( (i+1) %${#spin} ))
printf "\r${spin:$i:1} %s" "{{ machine }}"
sleep .1
done
mapfile -t missing <&3
if (( ${#missing[@]} > 0 )); then
printf '\r✗ %-8smissing %d secret(s):\n%s\n' "{{ machine }}" "${#missing[@]}" "$(printf -- ' %s\n' "${missing[@]}")"
exit 1
else
printf "\r✓ %-8sup to date\n" "{{ machine }}"
fi

View file

@ -1,36 +1,40 @@
@_default:
_default:
just --list --list-submodules
[doc('Manage vars')]
set unstable
set quiet
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:
show:
echo "show"
[doc('update the flake dependencies')]
@update:
update:
nix flake update
git commit -m 'chore: update dependencies' -- ./flake.lock > /dev/null
echo "Done"
[doc('Rebase branch on main')]
rebase:
git stash -q \
&& git fetch \
&& git rebase origin/main \
&& git stash pop -q
echo "Done"
[doc('Introspection on flake output')]
@select key:
nix eval --show-trace --json .#{{ key }} | jq .
select key:
nix eval --json .#{{ key }} | jq .
#===============================================================================================
# Utils
#===============================================================================================
[no-exit-message]
# ===============================================================================================
[no-cd]
[no-exit-message]
[private]
@assert condition message:
assert condition message:
[ {{ condition }} ] || { echo -e 1>&2 "\n\x1b[1;41m Error \x1b[0m {{ message }}\n"; exit 1; }

View file

@ -1,7 +1,6 @@
keys:
- &ulmo_1 age19qfpf980tadguqq44zf6xwvjvl428dyrj46ha3n6aeqddwhtnuqqml7etq
- &ulmo_2 age1ewes0f5snqx3sh5ul6fa6qtxzhd25829v6mf5rx2wnheat6fefps5rme2x
- &manwe_1 age1jmrmdw4kmjeu9d6z74r2unqt7wpgsx24vqejmdjretsnsn8g4drsl3m98w
creation_rules:
# All Machine secrets
@ -10,4 +9,3 @@ creation_rules:
- age:
- *ulmo_1
- *ulmo_2
- *manwe_1

599
flake.lock generated

File diff suppressed because it is too large Load diff

View file

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

View file

@ -32,7 +32,7 @@ in {
image = ./${cfg.theme}.jpg;
polarity = cfg.polarity;
targets.qt.platform = mkDefault "kde";
# targets.qt.platform = mkDefault "kde";
targets.zen-browser.profileNames = [ "Chris" ];
fonts = {

View file

@ -15,33 +15,28 @@ in {
};
config = mkIf cfg.enable {
# environment.systemPackages = with pkgs; [ steam ];
programs = {
steam = {
enable = true;
remotePlay.openFirewall = true;
dedicatedServer.openFirewall = true;
localNetworkGameTransfers.openFirewall = true;
# package = pkgs.steam.override {
# extraEnv = {
# DXVK_HUD = "compiler";
# MANGOHUD = true;
# };
# };
package = pkgs.steam.override {
extraEnv = {
DXVK_HUD = "compiler";
MANGOHUD = true;
};
};
# gamescopeSession = {
# enable = true;
# args = ["--immediate-flips"];
# };
gamescopeSession = {
enable = true;
args = ["--immediate-flips"];
};
};
# https://github.com/FeralInteractive/gamemode
# gamemode = {
# enable = true;
# enableRenice = true;
# settings = {};
# };
gamemode = {
enable = true;
enableRenice = true;
settings = {};
};
# gamescope = {
# enable = true;

View file

@ -1,26 +0,0 @@
{
lib,
config,
namespace,
inputs,
...
}: let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.desktop.cosmic;
in {
options.${namespace}.desktop.cosmic = {
enable =
mkEnableOption "Enable Cosmic desktop"
// {
default = config.${namespace}.desktop.use == "cosmic";
};
};
config = mkIf cfg.enable {
services = {
displayManager.cosmic-greeter.enable = true;
desktopManager.cosmic.enable = true;
};
};
}

View file

@ -1,22 +1,18 @@
{
lib,
config,
namespace,
inputs,
...
}: let
{ lib, config, namespace, inputs, ... }:
let
inherit (lib) mkIf mkOption mkEnableOption mkMerge;
inherit (lib.types) nullOr enum;
cfg = config.${namespace}.desktop;
in {
in
{
imports = [
inputs.grub2-themes.nixosModules.default
];
options.${namespace}.desktop = {
use = mkOption {
type = nullOr (enum ["plasma" "gamescope" "gnome" "cosmic"]);
type = nullOr (enum [ "plasma" "gamescope" "gnome" ]);
default = null;
example = "plasma";
description = "Which desktop to enable";
@ -24,11 +20,11 @@ in {
};
config = mkMerge [
{
({
services.displayManager = {
enable = true;
};
}
})
# (mkIf (cfg.use != null) {
# ${namespace}.desktop.${cfg.use}.enable = true;

View file

@ -22,7 +22,7 @@ in
konsole
kate
ghostwriter
# oxygen
oxygen
];
environment.sessionVariables.NIXOS_OZONE_WL = "1";

View file

@ -1,36 +1,16 @@
{
config,
lib,
pkgs,
namespace,
...
}: let
{ config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
user = "authelia-testing";
cfg = config.${namespace}.services.authentication.authelia;
in {
in
{
options.${namespace}.services.authentication.authelia = {
enable = mkEnableOption "Authelia";
};
config = mkIf cfg.enable {
${namespace}.services.networking.caddy = {
hosts = {
"auth.kruining.eu".extraConfig = ''
reverse_proxy http://127.0.0.1:9091
'';
};
extraConfig = ''
(auth) {
forward_auth http://127.0.0.1:9091 {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
}
'';
};
environment.systemPackages = with pkgs; [
authelia
];
@ -132,8 +112,8 @@ in {
authorization_policy = "one_factor";
userinfo_signed_response_alg = "none";
consent_mode = "implicit";
scopes = ["openid" "profile" "groups"];
redirect_uris = ["https://jellyfin.kruining.eu/sso/OID/redirect/authelia"];
scopes = [ "openid" "profile" "groups" ];
redirect_uris = [ "https://jellyfin.kruining.eu/sso/OID/redirect/authelia" ];
}
{
client_id = "streamarr";
@ -147,8 +127,8 @@ in {
authorization_policy = "one_factor";
userinfo_signed_response_alg = "none";
consent_mode = "implicit";
scopes = ["offline_access" "openid" "email" "picture" "profile" "groups"];
redirect_uris = ["http://localhost:3000/api/auth/oauth2/callback/authelia"];
scopes = [ "offline_access" "openid" "email" "picture" "profile" "groups" ];
redirect_uris = [ "http://localhost:3000/api/auth/oauth2/callback/authelia" ];
}
{
client_id = "forgejo";
@ -162,10 +142,10 @@ in {
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"];
scopes = [ "offline_access" "openid" "email" "picture" "profile" "groups" ];
response_types = [ "code" ];
grant_types = [ "authorization_code" ];
redirect_uris = [ "http://localhost:5002/user/oauth2/authelia/callback" ];
}
];
};
@ -215,8 +195,48 @@ in {
- jellyfin-users
- admin
- dev
jacqueline:
disabled: false
displayname: Jacqueline Bevers
password: $argon2id$v=19$m=65536,t=3,p=4$XgN8yEJV+syAE5yeos3HsA$SlN+j/lJfxJ5VxLu2CdrwowlCiWQNNGhIrSyDpohq18
groups:
- jellyfin-users
martijn:
disabled: false
displayname: Martijn Kruining
password: $argon2id$v=19$m=65536,t=3,p=4$XgN8yEJV+syAE5yeos3HsA$SlN+j/lJfxJ5VxLu2CdrwowlCiWQNNGhIrSyDpohq18
groups:
- jellyfin-users
andrea:
disabled: false
displayname: Andrea Kruining
password: $argon2id$v=19$m=65536,t=3,p=4$XgN8yEJV+syAE5yeos3HsA$SlN+j/lJfxJ5VxLu2CdrwowlCiWQNNGhIrSyDpohq18
groups:
- jellyfin-users
'';
};
};
services.caddy = {
enable = true;
virtualHosts = {
"auth.kruining.eu".extraConfig = ''
reverse_proxy http://127.0.0.1:9091
'';
};
extraConfig = ''
(auth) {
forward_auth http://127.0.0.1:9091 {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
}
'';
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
};
}

View file

@ -444,7 +444,8 @@ in
|> withRef "org" org
|> toResource "${org}_${name}"
)
|> append [
|> append
[
(forEach "local.extra_users" [ "org" "name" ] {
orgId = lib.tfRef "local.orgs[each.value.org]";
userName = lib.tfRef "each.value.name";
@ -537,25 +538,7 @@ in
};
in
mkIf cfg.enable {
${namespace}.services = {
persistance.postgresql.enable = true;
networking.caddy = {
hosts = {
"auth.kruining.eu" = ''
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
}
}
'';
};
};
${namespace}.services.persistance.postgresql.enable = true;
environment.systemPackages = with pkgs; [
zitadel
@ -696,6 +679,23 @@ in
}
];
};
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 ];

View file

@ -15,7 +15,6 @@
port = 4001;
database = "synapse";
keyFile = "/var/lib/element-call/key";
in {
options.${namespace}.services.communication.matrix = {
enable = mkEnableOption "Matrix server (Synapse)";
@ -25,76 +24,9 @@ in {
${namespace}.services = {
persistance.postgresql.enable = true;
# virtualisation.podman.enable = true;
};
networking.caddy = {
# globalConfig = ''
# layer4 {
# 127.0.0.1:4004
# route {
# proxy {
# upstream synapse:4004
# }
# }
# }
# 127.0.0.1:4005
# route {
# proxy {
# upstream synapse:4005
# }
# }
# }
# }
# '';
hosts = let
server = {
"m.server" = "${fqn}:443";
};
client = {
"m.homeserver".base_url = "https://${fqn}";
"m.identity_server".base_url = "https://auth.${domain}";
"org.matrix.msc3575.proxy".url = "https://${domain}";
"org.matrix.msc4143.rtc_foci" = [
{
type = "livekit";
livekit_service_url = "https://${domain}/livekit/jwt";
}
];
};
in {
"${domain}, darkch.at" = ''
# Route for lk-jwt-service
handle /livekit/jwt* {
uri strip_prefix /livekit/jwt
reverse_proxy http://[::1]:${toString config.services.lk-jwt-service.port} {
header_up Host {host}
header_up X-Forwarded-Server {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
}
}
handle_path /livekit/sfu* {
reverse_proxy http://[::1]:${toString config.services.livekit.settings.port} {
header_up Host {host}
header_up X-Forwarded-Server {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
}
}
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}" = ''
reverse_proxy /_matrix/* http://::1:${toString port}
reverse_proxy /_synapse/client/* http://::1:${toString port}
'';
};
};
};
networking.firewall.allowedTCPPorts = [4001];
services = {
matrix-synapse = {
@ -120,34 +52,11 @@ in {
# Since we'll be using OIDC for auth disable all local options
enable_registration = false;
enable_registration_without_verification = false;
password_config.enabled = true;
password_config.enabled = false;
backchannel_logout_enabled = true;
# Element Call options
max_event_delay_duration = "24h";
rc_message = {
per_second = 0.5;
burst_count = 30;
};
rc_delayed_event_mgmt = {
per_second = 1;
burst_count = 20;
};
turn_uris = ["turn:turn.${domain}:4004?transport=udp" "turn:turn.${domain}:4004?transport=tcp"];
experimental_features = {
# MSC2965: OAuth 2.0 Authorization Server Metadata discovery
msc2965_enabled = true;
# MSC3266: Room summary API. Used for knocking over federation
msc3266_enabled = true;
# MSC4222 needed for syncv2 state_after. This allow clients to
# correctly track the state of the room.
msc4222_enabled = true;
};
sso = {
client_whitelist = ["http://[::1]:9092/" "https://auth.kruining.eu/"];
client_whitelist = ["http://[::1]:9092"];
update_profile_information = true;
};
@ -186,38 +95,7 @@ in {
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;
# port = 40011;
};
homeserver = {
@ -240,6 +118,7 @@ in {
settings = {
appservice = {
provisioning.enabled = false;
# port = 40012;
};
homeserver = {
@ -266,121 +145,35 @@ in {
];
};
livekit = {
caddy = {
enable = true;
openFirewall = true;
inherit keyFile;
settings = {
port = 4002;
room.auto_create = false;
virtualHosts = let
server = {
"m.server" = "${fqn}:443";
};
client = {
"m.homeserver".base_url = "https://${fqn}";
"m.identity_server".base_url = "https://auth.kruining.eu";
};
lk-jwt-service = {
enable = true;
port = 4003;
# can be on the same virtualHost as synapse
livekitUrl = "wss://${domain}/livekit/sfu";
inherit keyFile;
};
coturn = rec {
enable = true;
listening-port = 4004;
tls-listening-port = 40004;
no-cli = true;
no-tcp-relay = true;
min-port = 50000;
max-port = 50100;
use-auth-secret = true;
static-auth-secret-file = config.sops.secrets."coturn/secret".path;
realm = "turn.${domain}";
# cert = "${config.security.acme.certs.${realm}.directory}/full.pem";
# pkey = "${config.security.acme.certs.${realm}.directory}/key.pem";
extraConfig = ''
# for debugging
verbose
# ban private IP ranges
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
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
'';
};
};
networking.firewall = {
allowedTCPPortRanges = [];
allowedTCPPorts = [
# Synapse
port
# coTURN ports
config.services.coturn.listening-port
config.services.coturn.alt-listening-port
config.services.coturn.tls-listening-port
config.services.coturn.alt-tls-listening-port
];
allowedUDPPortRanges = with config.services.coturn;
lib.singleton {
from = min-port;
to = max-port;
};
allowedUDPPorts = [
# coTURN ports
config.services.coturn.listening-port
config.services.coturn.alt-listening-port
];
};
systemd = {
services.livekit-key = {
before = ["lk-jwt-service.service" "livekit.service"];
wantedBy = ["multi-user.target"];
path = with pkgs; [livekit coreutils gawk];
script = ''
echo "Key missing, generating key"
echo "lk-jwt-service: $(livekit-server generate-keys | tail -1 | awk '{print $3}')" > "${keyFile}"
'';
serviceConfig.Type = "oneshot";
unitConfig.ConditionPathExists = "!${keyFile}";
};
services.lk-jwt-service.environment.LIVEKIT_FULL_ACCESS_HOMESERVERS = "${domain}";
};
sops = {
secrets = {
"synapse/oidc_id" = {
restartUnits = ["synapse-matrix.service"];
};
"synapse/oidc_secret" = {
restartUnits = ["synapse-matrix.service"];
};
"coturn/secret" = {
owner = config.systemd.services.coturn.serviceConfig.User;
group = config.systemd.services.coturn.serviceConfig.Group;
restartUnits = ["coturn.service"];
};
"synapse/oidc_id" = {};
"synapse/oidc_secret" = {};
};
templates = {
@ -395,19 +188,13 @@ in {
scopes:
- openid
- profile
- email
- offline_access
client_id: '${config.sops.placeholder."synapse/oidc_id"}'
client_secret: '${config.sops.placeholder."synapse/oidc_secret"}'
backchannel_logout_enabled: true
user_profile_method: userinfo_endpoint
allow_existing_users: true
enable_registration: true
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
email_template: "{{ user.email }}"
'';
restartUnits = ["matrix-synapse.service"];
};

View file

@ -28,20 +28,6 @@ in {
${namespace}.services = {
persistance.postgresql.enable = true;
virtualisation.podman.enable = true;
networking.caddy = {
hosts = {
"${domain}" = ''
# 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}
'';
};
};
};
environment.systemPackages = with pkgs; [forgejo];
@ -49,7 +35,6 @@ in {
services = {
forgejo = {
enable = true;
lfs.enable = true;
useWizard = false;
database.type = "postgres";
@ -182,6 +167,21 @@ in {
};
};
};
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 = {

View file

@ -1,16 +1,11 @@
{
inputs,
config,
lib,
pkgs,
namespace,
...
}: let
{ inputs, config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption mkOption;
inherit (lib.types) str;
cfg = config.${namespace}.services.games.minecraft;
in {
in
{
imports = [
inputs.nix-minecraft.nixosModules.minecraft-servers
];
@ -30,7 +25,7 @@ in {
};
config = mkIf cfg.enable {
users.users.${cfg.user} = {
user.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
};
@ -82,7 +77,7 @@ in {
inherit whitelist;
inherit jvmOpts;
package = pkgs.fabricServers.fabric-1_21_4.override {loaderVersion = "0.16.10";};
package = pkgs.fabricServers.fabric-1_21_4.override { loaderVersion = "0.16.10"; };
serverProperties = {
gamemode = "survival";
@ -108,14 +103,8 @@ in {
inherit (pkgs) linkFarmFromDrvs fetchurl;
in {
mods = linkFarmFromDrvs "mods" (attrValues {
FabricApi = fetchurl {
url = "https://cdn.modrinth.com/data/P7dR8mSH/versions/ZNwYCTsk/fabric-api-0.118.0%2B1.21.4.jar";
sha512 = "1e0d31b6663dc2c7be648f3a5a9cf7b698b9a0fd0f7ae16d1d3f32d943d7c5205ff63a4f81b0c4e94a8997482cce026b7ca486e99d9ce35ac069aeb29b02a30d";
};
Terralith = fetchurl {
url = "https://cdn.modrinth.com/data/8oi3bsk5/versions/MuJMtPGQ/Terralith_1.21.x_v2.5.8.jar";
sha512 = "f862ed5435ce4c11a97d2ea5c40eee9f817c908f3223b5fd3e3fff0562a55111d7429dc73a2f1ca0b1af7b1ff6fa0470ed6efebb5de13336c40bb70fb357dd60";
};
FabricApi = fetchurl { url = "https://cdn.modrinth.com/data/P7dR8mSH/versions/ZNwYCTsk/fabric-api-0.118.0%2B1.21.4.jar"; sha512 = "1e0d31b6663dc2c7be648f3a5a9cf7b698b9a0fd0f7ae16d1d3f32d943d7c5205ff63a4f81b0c4e94a8997482cce026b7ca486e99d9ce35ac069aeb29b02a30d"; };
Terralith = fetchurl { url = "https://cdn.modrinth.com/data/8oi3bsk5/versions/MuJMtPGQ/Terralith_1.21.x_v2.5.8.jar"; sha512 = "f862ed5435ce4c11a97d2ea5c40eee9f817c908f3223b5fd3e3fff0562a55111d7429dc73a2f1ca0b1af7b1ff6fa0470ed6efebb5de13336c40bb70fb357dd60"; };
# DistantHorizons = fetchurl { url = "https://cdn.modrinth.com/data/uCdwusMi/versions/jptcCdp2/DistantHorizons-2.2.1-a-1.20.4-forge-fabric.jar"; sha512 = "47368d91099d0b5f364339a69f4e425f8fb1e3a7c3250a8b649da76135e68a22f1a76b191c87e15a5cdc0a1d36bc57f2fa825490d96711d09d96807be97d575d"; };
});
};
@ -136,7 +125,7 @@ in {
inherit whitelist;
inherit jvmOpts;
package = pkgs.fabricServers.fabric-1_19_2.override {loaderVersion = "0.16.9";};
package = pkgs.fabricServers.fabric-1_19_2.override { loaderVersion = "0.16.9"; };
serverProperties = {
gamemode = "survival";
@ -158,16 +147,13 @@ in {
inherit (lib) concatMapAttrs;
readDirRec = src: dir: fn:
concatMapAttrs (
name: type:
if type == "directory"
concatMapAttrs (name: type: if type == "directory"
then (readDirRec src "${dir}/${name}" fn)
else {"${dir}/${name}" = fn "${dir}/${name}";}
else { "${dir}/${name}" = (fn "${dir}/${name}"); }
) (readDir "${src}/${dir}");
copyDir = dir: readDirRec src dir (x: "${src}/${x}");
in
{
in {
"ops.json" = {
value = ops;
};
@ -178,11 +164,7 @@ in {
inherit (builtins) attrNames readDir map;
inherit (pkgs) linkFarm;
linkFarmFromDir = name: dir:
linkFarm name (map (x: {
name = x;
path = "${src}/${dir}/${x}";
}) (attrNames (readDir "${src}/${dir}")));
linkFarmFromDir = name: dir: linkFarm name (map (x: { name = x; path = "${src}/${dir}/${x}"; }) (attrNames (readDir "${src}/${dir}")));
in {
Deftu = linkFarmFromDir "tekxit-deftu" "Deftu";
TKXAddons = linkFarmFromDir "tekxit-TKXAddons" "TKXAddons";

View file

@ -0,0 +1,25 @@
{ config, lib, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.games.palworld;
in
{
options.${namespace}.services.games.palworld = {
enable = mkEnableOption "Palworld";
};
config = mkIf cfg.enable {
# kaas = (pkgs.mkSteamServer rec {
# name = "Palworld";
# src = pkgs.fetchSteam {
# inherit name;
# appId = "2394010";
# hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
# };
#
# sartCmd = "PalServer.sh";
# hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
# });
};
}

View file

@ -1,30 +0,0 @@
{
config,
lib,
namespace,
...
}: let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.games.palworld;
in {
options.${namespace}.services.games.palworld = {
enable = mkEnableOption "Palworld";
};
config = mkIf cfg.enable {
# kaas = (pkgs.mkSteamServer rec {
# name = "Palworld";
# src = pkgs.fetchSteam {
# inherit name;
# appId = "2394010";
# hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
# };
#
# sartCmd = "PalServer.sh";
# hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
# });
sops.secrets."palworld/password" = {};
};
}

View file

@ -35,6 +35,13 @@ in {
#=========================================================================
environment.systemPackages = with pkgs; [
podman-tui
jellyfin
jellyfin-web
jellyfin-ffmpeg
jellyseerr
mediainfo
id3v2
yt-dlp
];
#=========================================================================
@ -49,6 +56,9 @@ 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} - -"
@ -67,9 +77,54 @@ in {
listenPort = 2005;
};
flaresolverr = {
enable = true;
openFirewall = true;
port = 2007;
};
# port is harcoded in nixpkgs module
jellyfin = {
enable = true;
openFirewall = true;
user = cfg.user;
group = cfg.group;
};
postgresql = {
enable = true;
};
caddy = {
enable = true;
virtualHosts = {
"jellyfin.kruining.eu".extraConfig = ''
reverse_proxy http://[::1]:8096
'';
};
};
};
systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL";
sops = {
secrets = {
# "qbittorrent/password" = {};
"qbittorrent/password_hash" = {};
};
templates = {
"qbittorrent/password.conf" = {
owner = cfg.user;
group = cfg.group;
restartUnits = ["qbittorrent.service"];
path = "${config.services.qbittorrent.profileDir}/qBittorrent/config/password.conf";
content = ''
[Preferences]
WebUI\Password_PBKDF2="${config.sops.placeholder."qbittorrent/password_hash"}"
'';
};
};
};
};
}

View file

@ -130,6 +130,16 @@ in {
}
];
}
{
type = "videos";
channels = [
"UCXuqSBlHAE6Xw-yeJA0Tunw" # Linus Tech Tips
"UCR-DXc1voovS8nhAvccRZhg" # Jeff Geerling
"UCsBjURrPoezykLs9EqgamOA" # Fireship
"UCBJycsmduvYEL83R_U4JriQ" # Marques Brownlee
"UCHnyfMqiRRG1u-2MsSQLbXA" # Veritasium
];
}
];
}

View file

@ -1,49 +0,0 @@
{
pkgs,
config,
lib,
namespace,
inputs,
system,
...
}: let
inherit (builtins) toString;
inherit (lib) mkIf mkEnableOption mkOption types;
cfg = config.${namespace}.services.media.jellyfin;
in {
options.${namespace}.services.media.jellyfin = {
enable = mkEnableOption "Enable jellyfin server";
};
config = mkIf cfg.enable {
${namespace}.services.networking.caddy = {
hosts = {
"jellyfin.kruining.eu" = ''
reverse_proxy http://[::1]:8096
'';
};
};
environment.systemPackages = with pkgs; [
jellyfin
jellyfin-web
jellyfin-ffmpeg
mediainfo
id3v2
yt-dlp
];
services = {
# port is harcoded in nixpkgs module
jellyfin = {
enable = true;
openFirewall = true;
user = "media";
group = "media";
};
};
systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL";
};
}

View file

@ -36,7 +36,7 @@ in {
# 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;
passwordFile = config.sops.secrets."mydia/qbittorrent_password".path;
};
secretKeyBaseFile = config.sops.secrets."mydia/secret_key_base".path;
@ -82,14 +82,5 @@ in {
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

@ -1,15 +1,11 @@
{
config,
lib,
pkgs,
namespace,
...
}: let
{ config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption mkOption;
inherit (lib.types) str;
cfg = config.${namespace}.services.media.nextcloud;
in {
in
{
options.${namespace}.services.media.nextcloud = {
enable = mkEnableOption "Nextcloud";
@ -25,14 +21,6 @@ in {
};
config = mkIf cfg.enable {
${namespace}.services.networking.caddy = {
hosts."cloud.kruining.eu" = ''
php_fastcgi unix//run/phpfpm/nextcloud.sock {
env front_controller_active true
}
'';
};
users = {
users.${cfg.user} = {
isSystemUser = true;
@ -87,5 +75,14 @@ in {
# startServices = true;
# };
services.caddy = {
enable = true;
virtualHosts."cloud.kruining.eu".extraConfig = ''
php_fastcgi unix//run/phpfpm/nextcloud.sock {
env front_controller_active true
}
'';
};
};
}

View file

@ -11,8 +11,6 @@
inherit (lib) mkIf mkEnableOption mkOption types;
cfg = config.${namespace}.services.media.servarr;
servarr = import ./lib.nix {inherit lib;};
anyEnabled = cfg |> lib.attrNames |> lib.length |> (l: l > 0);
in {
options.${namespace}.services.media = {
servarr = mkOption {
@ -35,7 +33,7 @@ in {
};
};
config = mkIf anyEnabled {
config = {
services =
cfg
|> lib.mapAttrsToList (service: {
@ -69,13 +67,15 @@ in {
};
};
}
// (lib.optionalAttrs (lib.elem service ["radarr" "sonarr" "lidarr" "whisparr"]) {
// (lib.optionalAttrs (service != "prowlarr") {
user = service;
group = "media";
});
}))
|> lib.concat [
{
|> lib.mkMerge
|> (set:
set
// {
qbittorrent = {
enable = true;
openFirewall = true;
@ -86,7 +86,6 @@ in {
Prefecences.WebUI = {
Username = "admin";
Password_PBKDF2 = config.sops.secrets."qbittorrent/password_hash".path;
};
};
@ -94,46 +93,16 @@ in {
group = "media";
};
# port is harcoded in nixpkgs module
sabnzbd = {
enable = true;
openFirewall = true;
allowConfigWrite = false;
configFile = lib.mkForce null;
secretFiles = [
config.sops.templates."sabnzbd/config.ini".path
];
settings = {
misc = {
port = 2009;
download_dir = "/var/media/downloads/incomplete";
complete_dir = "/var/media/downloads/done";
};
servers = {
"news.sunnyusenet.com" = {
name = "news.sunnyusenet.com";
displayname = "news.sunnyusenet.com";
host = "news.sunnyusenet.com";
port = 563;
timeout = 60;
};
};
};
configFile = "${cfg.path}/sabnzbd/config.ini";
user = "sabnzbd";
group = "media";
};
flaresolverr = {
enable = true;
openFirewall = true;
port = 2007;
};
postgresql = {
ensureDatabases = cfg |> lib.attrNames;
ensureUsers =
@ -144,9 +113,7 @@ in {
ensureDBOwnership = true;
});
};
}
]
|> lib.mkMerge;
});
systemd.services =
cfg
@ -158,9 +125,6 @@ in {
...
}: (mkIf enable {
"${service}ApplyTerraform" = let
config' = config;
lib' = lib;
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
inherit system;
@ -171,29 +135,13 @@ in {
...
}: {
config = {
variable =
cfg
|> lib'.mapAttrsToList (s: _: {
"${s}_api_key" = {
variable = {
api_key = {
type = "string";
description = "${s} API key";
description = "${service} api key";
};
})
|> lib'.concat [
{
qbittorrent_api_key = {
type = "string";
description = "qbittorrent api key";
};
sabnzbd_api_key = {
type = "string";
description = "sabnzbd api key";
};
}
]
|> lib'.mkMerge;
terraform.required_providers.${service} = {
source = "devopsarr/${service}";
version =
@ -211,195 +159,17 @@ in {
provider.${service} = {
url = "http://127.0.0.1:${toString port}";
api_key = lib.tfRef "var.${service}_api_key";
api_key = lib.tfRef "var.api_key";
};
resource =
{
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 = lib.tfRef "var.qbittorrent_api_key";
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";
api_key = lib.tfRef "var.sabnzbd_api_key";
url_base = "/";
port = 8080;
};
};
}
// (lib.optionalAttrs (service == "prowlarr") (
cfg
|> lib'.filterAttrs (s: _: lib'.elem s ["radarr" "sonarr" "lidarr" "whisparr"])
|> lib'.mapAttrsToList (s: {port, ...}: {
"prowlarr_application_${s}"."main" = let
p = cfg.prowlarr.port or config'.services.prowlarr.settings.server.port or 9696;
in {
name = s;
sync_level = "addOnly";
base_url = "http://localhost:${toString port}";
prowlarr_url = "http://localhost:${toString p}";
api_key = lib.tfRef "var.${s}_api_key";
# sync_categories = [3000 3010 3030];
};
})
|> lib'.concat [
{
"prowlarr_indexer" = {
"nyaa" = {
enable = true;
app_profile_id = 1;
priority = 1;
name = "Nyaa";
implementation = "Cardigann";
config_contract = "CardigannSettings";
protocol = "torrent";
fields = [
{
name = "definitionFile";
text_value = "nyaasi";
}
{
name = "baseSettings.limitsUnit";
number_value = 0;
}
{
name = "torrentBaseSettings.preferMagnetUrl";
bool_value = false;
}
{
name = "prefer_magnet_links";
bool_value = true;
}
{
name = "sonarr_compatibility";
bool_value = false;
}
{
name = "strip_s01";
bool_value = false;
}
{
name = "radarr_compatibility";
bool_value = false;
}
{
name = "filter-id";
number_value = 0;
}
{
name = "cat-id";
number_value = 0;
}
{
name = "sort";
number_value = 0;
}
{
name = "type";
number_value = 1;
}
];
};
# "_1337x" = {
# enable = true;
# app_profile_id = 1;
# priority = 1;
# name = "1337x";
# implementation = "Cardigann";
# config_contract = "CardigannSettings";
# protocol = "torrent";
# tags = [1];
# fields = [
# {
# name = "definitionFile";
# text_value = "1337x";
# }
# {
# name = "baseSettings.limitsUnit";
# number_value = 0;
# }
# {
# name = "torrentBaseSettings.preferMagnetUrl";
# bool_value = false;
# }
# {
# name = "disablesort";
# bool_value = false;
# }
# {
# name = "sort";
# number_value = 2;
# }
# {
# name = "type";
# number_value = 1;
# }
# ];
# };
# "nzbgeek" = {
# enable = true;
# app_profile_id = 2;
# priority = 1;
# name = "NZBgeek";
# implementation = "Newznab";
# config_contract = "NewznabSettings";
# protocol = "usenet";
# fields = [
# {
# name = "baseUrl";
# text_value = "https://api.nzbgeek.info";
# }
# {
# name = "apiPath";
# text_value = "/api";
# }
# {
# name = "apiKey";
# text_value = "__TODO_API_KEY_SECRET__";
# }
# {
# name = "baseSettings.limitsUnit";
# number_value = 5;
# }
# ];
# };
};
}
]
|> lib'.mkMerge
));
};
})
];
};
@ -434,7 +204,7 @@ in {
cp -f ${terraformConfiguration} config.tf.json
# Initialize OpenTofu
${lib.getExe pkgs.opentofu} init
${lib.getExe pkgs.opentofu} init -upgrade
# Run the infrastructure code
${lib.getExe pkgs.opentofu} \
@ -443,7 +213,7 @@ in {
then "plan"
else "apply -auto-approve"
} \
-var-file='${config.sops.templates."servarr/config.tfvars".path}'
-var-file='${config.sops.templates."${service}/config.tfvars".path}'
'';
serviceConfig = {
@ -471,11 +241,6 @@ in {
};
groups.${service} = {};
}))
|> lib.concat [
{
groups.media = {};
}
]
|> lib.mkMerge;
sops =
@ -496,57 +261,17 @@ in {
${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 = {
"qbittorrent/password" = {};
"qbittorrent/password_hash" = {
owner = "qbittorrent";
group = "media";
};
"sabnzbd/apikey" = {};
"sabnzbd/nzbkey" = {};
"sabnzbd/sunnyweb/username" = {};
"sabnzbd/sunnyweb/password" = {};
};
templates = {
"servarr/config.tfvars" = {
owner = "media";
group = "media";
mode = "0440";
restartUnits = cfg |> lib.attrNames |> lib.map (s: "${s}.service");
content = ''
${
cfg
|> lib.attrNames
|> lib.map (s: "${s}_api_key = \"${config.sops.placeholder."${s}/apikey"}\"")
|> lib.join "\n"
}
qbittorrent_api_key = "${config.sops.placeholder."qbittorrent/password"}"
sabnzbd_api_key = "${config.sops.placeholder."sabnzbd/apikey"}"
'';
};
"sabnzbd/config.ini" = {
owner = "sabnzbd";
group = "media";
mode = "0660";
content = ''
[misc]
api_key = ${config.sops.placeholder."sabnzbd/apikey"}
nzb_key = ${config.sops.placeholder."sabnzbd/nzbkey"}
[servers]
[[news.sunnyusenet.com]]
username = ${config.sops.placeholder."sabnzbd/sunnyweb/username"}
password = ${config.sops.placeholder."sabnzbd/sunnyweb/password"}
'';
};
};
}
]
|> lib.mkMerge;
};
}

View file

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

View file

@ -1,40 +0,0 @@
{
config,
pkgs,
lib,
namespace,
...
}: let
inherit (builtins) length;
inherit (lib) mkIf mkEnableOption mkOption types attrNames mapAttrs;
cfg = config.${namespace}.services.networking.caddy;
hasHosts = (cfg.hosts |> attrNames |> length) > 0;
in {
options.${namespace}.services.networking.caddy = {
enable = mkEnableOption "enable caddy" // {default = true;};
hosts = mkOption {
type = types.attrsOf types.str;
};
extraConfig = mkOption {
type = types.str;
};
};
config = mkIf hasHosts {
services.caddy = {
enable = cfg.enable;
package = pkgs.caddy.withPlugins {
plugins = ["github.com/corazawaf/coraza-caddy/v2@v2.1.0"];
hash = "sha256-AdL/LFKXbWmCsJ/xZWZmYBnw57c7sS6s1miR3sSx1Ow=";
};
virtualHosts =
cfg.hosts
|> mapAttrs (host: extraConfig: {inherit extraConfig;});
};
};
}

View file

@ -1,10 +1,5 @@
{
pkgs,
config,
lib,
namespace,
...
}: let
{ pkgs, config, lib, namespace, ... }:
let
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
@ -12,7 +7,8 @@
db_user = "grafana";
db_name = "grafana";
in {
in
{
options.${namespace}.services.observability.grafana = {
enable = mkEnableOption "enable Grafana";
};
@ -30,10 +26,6 @@ in {
domain = "ulmo";
};
security = {
secret_key = "$__file{${config.sops.secrets."grafana/secret_key".path}}";
};
auth = {
disable_login_form = false;
oauth_auto_login = true;
@ -43,8 +35,8 @@ in {
"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}}";
client_id = "334170712283611395";
client_secret = "AFjypmURdladmQn1gz2Ke0Ta5LQXapnuKkALVZ43riCL4qWicgV2Z6RlwpoWBZg1";
scopes = "openid email profile offline_access urn:zitadel:iam:org:project:roles";
email_attribute_path = "email";
login_attribute_path = "username";
@ -123,7 +115,7 @@ in {
postgresql = {
enable = true;
ensureDatabases = [db_name];
ensureDatabases = [ db_name ];
ensureUsers = [
{
name = db_user;
@ -134,22 +126,5 @@ in {
};
environment.etc."/grafana/dashboards/default.json".source = ./dashboards/default.json;
sops = {
secrets = {
"grafana/secret_key" = {
owner = "grafana";
group = "grafana";
};
"grafana/oidc_id" = {
owner = "grafana";
group = "grafana";
};
"grafana/oidc_secret" = {
owner = "grafana";
group = "grafana";
};
};
};
};
}

View file

@ -0,0 +1,21 @@
{ config, pkgs, lib, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.persistance.convex;
in
{
imports = [ ./source.nix ];
options.${namespace}.services.persistance.convex = {
enable = mkEnableOption "enable Convex";
};
config = mkIf cfg.enable {
services.convex = {
enable = true;
package = pkgs.${namespace}.convex;
secret = "ThisIsMyAwesomeSecret";
};
};
}

View file

@ -0,0 +1,149 @@
{ config, pkgs, lib, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption mkPackageOption mkOption optional types;
cfg = config.services.convex;
default_user = "convex";
default_group = "convex";
in
{
options.services.convex = {
enable = mkEnableOption "enable Convex (backend only for now)";
package = mkPackageOption pkgs "convex" {};
name = lib.mkOption {
type = types.str;
default = "convex";
description = ''
Name for the instance.
'';
};
secret = lib.mkOption {
type = types.str;
default = "";
description = ''
Secret for the instance.
'';
};
apiPort = mkOption {
type = types.port;
default = 3210;
description = ''
The TCP port to use for the API.
'';
};
actionsPort = mkOption {
type = types.port;
default = 3211;
description = ''
The TCP port to use for the HTTP actions.
'';
};
dashboardPort = mkOption {
type = types.port;
default = 6791;
description = ''
The TCP port to use for the Dashboard.
'';
};
openFirewall = lib.mkOption {
type = types.bool;
default = false;
description = ''
Whether to open ports in the firewall for the server.
'';
};
user = lib.mkOption {
type = types.str;
default = default_user;
description = ''
As which user to run the service.
'';
};
group = lib.mkOption {
type = types.str;
default = default_group;
description = ''
As which group to run the service.
'';
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.secret != "";
message = ''
No secret provided for convex
'';
}
];
users = {
users.${cfg.user} = {
description = "System user for convex service";
isSystemUser = true;
group = cfg.group;
};
groups.${cfg.group} = {};
};
networking.firewall.allowedTCPPorts = optional cfg.openFirewall [ cfg.apiPort cfg.actionsPort cfg.dashboardPort ];
environment.systemPackages = [ cfg.package ];
systemd.services.convex = {
description = "Convex Backend server";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin --instance-name ${cfg.name} --instance-secret ${cfg.secret}";
Type = "notify";
User = cfg.user;
Group = cfg.group;
RuntimeDirectory = "convex";
RuntimeDirectoryMode = "0775";
StateDirectory = "convex";
StateDirectoryMode = "0775";
Umask = "0077";
CapabilityBoundingSet = "";
NoNewPrivileges = true;
# Sandboxing
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateUsers = true;
ProtectClock = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
LockPersonality = true;
};
};
};
}

View file

@ -1,19 +1,14 @@
{
config,
lib,
pkgs,
namespace,
...
}: let
{ config, lib, pkgs, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.persistance.postgresql;
in {
in
{
options.${namespace}.services.persistance.postgresql = {
enable = mkEnableOption "Postgresql";
};
# Access db with `psql -U postgres`
config = mkIf cfg.enable {
services = {
postgresql = {

View file

@ -91,22 +91,6 @@ in {
};
config = mkIf cfg.enable {
${namespace}.services.networking.caddy.hosts = {
"vault.kruining.eu" = ''
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}
}
'';
};
systemd.tmpfiles.rules = [
"d '/var/lib/vaultwarden' 0700 vaultwarden vaultwarden - -"
];
@ -166,6 +150,25 @@ in {
}
];
};
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 = {

View file

@ -0,0 +1,59 @@
{
lib,
stdenv,
rustPlatform,
fetchFromGitHub,
# dependencies
openssl,
pkg-config,
cmake,
llvmPackages,
postgresql,
sqlite,
#options
dbBackend ? "postgresql",
...
}:
rustPlatform.buildRustPackage rec {
pname = "convex";
version = "2025-08-20-c9b561e";
src = fetchFromGitHub {
owner = "get-convex";
repo = "convex-backend";
rev = "c9b561e1b365c85ef28af35d742cb7dd174b5555";
hash = "sha256-4h4AQt+rQ+nTw6eTbbB5vqFt9MFjKYw3Z7bGXdXijJ0=";
};
cargoHash = "sha256-pcDNWGrk9D0qcF479QAglPLFDZp27f8RueP5/lq9jho=";
cargoBuildFlags = [
"-p" "local_backend"
"--bin" "convex-local-backend"
];
env = {
LIBCLANG_PATH = "${llvmPackages.libclang}/lib";
};
strictDeps = true;
# Build-time dependencies
nativeBuildInputs = [ pkg-config cmake rustPlatform.bindgenHook ];
# Run-time dependencies
buildInputs =
[ openssl ]
++ lib.optional (dbBackend == "sqlite") sqlite
++ lib.optional (dbBackend == "postgresql") postgresql;
buildFeatures = "";
meta = with lib; {
license = licenses.fsl11Asl20;
mainProgram = "convex";
};
}

View file

@ -1,22 +1,18 @@
{
pkgs,
inputs,
}: let
{ pkgs, inputs }: let
inherit (builtins) fetchurl;
inherit (pkgs) makeDesktopItem copyDesktopItems wineWow64Packages;
inherit (pkgs) makeDesktopItem copyDesktopItems wineWowPackages;
inherit (inputs.erosanix.lib.x86_64-linux) mkWindowsAppNoCC makeDesktopIcon copyDesktopIcons;
wine = wineWow64Packages.base;
in
mkWindowsAppNoCC rec {
wine = wineWowPackages.base;
in mkWindowsAppNoCC rec {
inherit wine;
pname = "studio";
version = "2.25.4";
src = fetchurl {
url = "https://studio.download.bricklink.info/Studio2.0+EarlyAccess/Archive/2.25.12_1/Studio+2.0+EarlyAccess.exe";
sha256 = "sha256:1xl3zvzkzr64zphk7rnpfx3whhbaykzw06m3nd5dc12r2p4sdh3v";
url = "https://studio.download.bricklink.info/Studio2.0+EarlyAccess/Archive/2.25.4_1/Studio+2.0+EarlyAccess.exe";
sha256 = "sha256:1gw6pyvfr7zr42g21hqgiwkjs88nvhq2c2v40y21frvwv17hja92";
};
enableMonoBootPrompt = false;
@ -50,11 +46,11 @@ in
enableHUD = false;
enabledWineSymlinks = {};
enabledWineSymlinks = { };
graphicsDriver = "auto";
inhibitIdle = false;
nativeBuildInputs = [copyDesktopIcons copyDesktopItems];
nativeBuildInputs = [ copyDesktopIcons copyDesktopItems ];
winAppInstall = ''
wine64 ${src}
@ -63,10 +59,7 @@ in
wine64 reg add 'HKEY_CURRENT_USER\Software\Wine\X11 Driver' /t REG_SZ /v UseTakeFocus /d N /f
'';
winAppPreRun = ''
wineserver -W
wine64 reg add 'HKEY_CURRENT_USER\Software\Wine\X11 Driver' /t REG_SZ /v UseTakeFocus /d N /f
'';
winAppPreRun = '''';
winAppRun = ''
wine64 "$WINEPREFIX/drive_c/Program Files/Studio 2.0/Studio.exe" "$ARGS"
@ -104,6 +97,6 @@ in
homepage = "https://www.bricklink.com/v3/studio/main.page";
license = "";
maintainers = [];
platforms = ["x86_64-linux"];
platforms = [ "x86_64-linux" ];
};
}
}

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,19 +0,0 @@
#!/usr/bin/bash
import base64
import hashlib
import sys
import uuid
password = sys.argv[1]
salt = uuid.uuid4()
salt_bytes = salt.bytes
password = str.encode(password)
hashed_password = hashlib.pbkdf2_hmac("sha512", password, salt_bytes, 100000, dklen=64)
b64_salt = base64.b64encode(salt_bytes).decode("utf-8")
b64_password = base64.b64encode(hashed_password).decode("utf-8")
password_string = "@ByteArray({salt}:{password})".format(
salt=b64_salt, password=b64_password
)
print(password_string)

View file

@ -1,3 +0,0 @@
#!/bin/bash
pwgen -s 128 1

View file

@ -1,3 +0,0 @@
#!/bin/bash
python ./hash.py "$(just vars get ulmo qbittorrent/password | jq -r)"

View file

@ -17,6 +17,5 @@ mkShell {
nixd
openssl
inputs.clan-core.packages.${stdenv.hostPlatform.system}.clan-cli
nix-output-monitor
];
}

View file

@ -1,33 +0,0 @@
sops:
age:
- recipient: age19qfpf980tadguqq44zf6xwvjvl428dyrj46ha3n6aeqddwhtnuqqml7etq
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZMC9nRjVFWnZlMHJJK0Nl
dWFTR0FCUGNBYXIrUHlIUUphZll2QU9IOEZrCitFS3JvK3hYYmpEZ05aRStpdUd1
L3JjNDl1Z2hQQ3FuNUZNM1hCRUtQUG8KLS0tIEg4VVEvVjZYN3JHSXljQW1xS3E4
eVpyM1lSWExndlZhMkw2Vis4dVhjSVUKbk+z1h3Hb1A6SEbZ3g5vYui/FfkMyfxx
Zm67JenYittHvQggTIErAgJatTocfVB6Zy4FqJtPCOevTVrRTRkwAg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ewes0f5snqx3sh5ul6fa6qtxzhd25829v6mf5rx2wnheat6fefps5rme2x
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtOGJXWi9vUzdFbkx2NmVa
YnhITlNMc1RRRXoyOFNPN1B4VWQ5ZDUwNDFBCnVmdDFyUnptekxhOUlwdVcyRjFI
cHRSRkoyWnFVUDJMcXpVcmM5bjRKMkkKLS0tIDROWXR1UFFUa0NxcUtkdEwxQ2Vl
OW50OE9RMWpyT1AvS0QzZ3JVNDViYlkK77H0Uq3eRy0CHgH4bhdo7FVEJpKeR/DB
KZonll74qqsyW4n+hIbIybjaqtF3RBN4kj5ARuIGFmH8sAl6jSyHXA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1jmrmdw4kmjeu9d6z74r2unqt7wpgsx24vqejmdjretsnsn8g4drsl3m98w
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZOURoRmk4QldEZExTRDYx
cXluYkg4OUFUNDNrQUNiNWRwKzhEQkdaemxzCnM3b25GYm5TM3NuNnBsVWRmQzNL
bTRabmx2UzBkN1dadlhwajN5RDIxVW8KLS0tIDhSQ1o4RGZBdlVHaHRKQWFyazU0
N0lnMjMvREpmNWZvTUdiT0tjMk4vTk0KmIN1a3gjmFzaEwJBu41sw5Z61UgiO5fc
/pkS22BeVonuB12SmJX+77A1CxFz1EwM8HSShFKlpN2hPCJFJL7Nng==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-02-25T07:59:24Z"
mac: ENC[AES256_GCM,data:64AkqWb97nUciWtOOHP/SZhUeo/5ahxa0cN14ILw/jmToFkn8uDrSfY8/ibqBB0mmfhwGzcnI/5QpCLVzCSgG1J68bdPeSsYTZPwy2/0S0ven+GeqYHMfJ2Q1eJE7TONyOEvSdYdUWG+ff5t0qhSet9F2BgFnMSKcNeAaxIY6KU=,iv:aMQXbKk8oKSLBHIZyJLJahu5HHEMysmhcgfpDdZG+Ak=,tag:hqBVXis8MdqRorxttYeQaw==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

View file

@ -1,4 +1,5 @@
{ pkgs, ...}: {
{ ... }:
{
imports = [
./disks.nix
./hardware.nix
@ -6,10 +7,6 @@
system.activationScripts.remove-gtkrc.text = "rm -f /home/chris/.gtkrc-2.0";
services.logrotate.checkConfig = false;
environment.systemPackages = with pkgs; [ beyond-all-reason ];
sneeuwvlok = {
hardware.has = {
gpu.amd = true;
@ -33,6 +30,7 @@
};
};
services.displayManager.autoLogin = {
enable = true;
user = "chris";

View file

@ -27,30 +27,16 @@
};
};
# virtualisation = {
# containers.enable = true;
# podman = {
# enable = true;
# dockerCompat = true;
# };
# 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
'';
# oci-containers = {
# backend = "podman";
# containers = {
# homey = {
# image = "ghcr.io/athombv/homey-shs:latest";
# autoStart = true;
# privileged = true;
# volumes = [
# "/home/chris/.homey-shs:/homey/user"
# ];
# ports = [
# "4859:4859"
# ];
# };
# };
# };
# };
"amarth.cloud".extraConfig = ''
reverse_proxy http://192.168.1.223:8080
'';
};
sneeuwvlok = {
services = {
@ -132,26 +118,6 @@
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
};
grafana = {
redirectUris = ["http://localhost:9001/login/generic_oauth"];
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
};
};
};
convex = {
projectRoleCheck = true;
projectRoleAssertion = true;
hasProjectCheck = true;
application = {
scry = {
redirectUris = ["https://nautical-salamander-320.eu-west-1.convex.cloud/api/auth/callback/zitadel"];
grantTypes = ["authorizationCode"];
responseTypes = ["code"];
};
};
};
};
@ -193,22 +159,11 @@
development.forgejo.enable = true;
networking.ssh.enable = true;
networking.caddy.hosts = {
# Expose amarht cloud stuff like this until I have a proper solution
"auth.amarth.cloud" = ''
reverse_proxy http://192.168.1.223:9092
'';
"amarth.cloud" = ''
reverse_proxy http://192.168.1.223:8080
'';
};
media.enable = true;
media.glance.enable = true;
media.mydia.enable = true;
media.nfs.enable = true;
media.jellyfin.enable = true;
media.servarr = {
radarr = {
enable = true;
@ -238,7 +193,7 @@
prowlarr = {
enable = true;
# debug = true;
debug = true;
port = 2004;
};
};
@ -251,6 +206,8 @@
# uptime-kuma.enable = true;
};
persistance.convex.enable = true;
security.vaultwarden = {
enable = true;
database = {

View file

@ -4,12 +4,13 @@ email:
zitadel:
masterKey: ENC[AES256_GCM,data:4MPvBo407qrS7NF4oUTf84tZoPkSRmiHdD7qpkYeHME=,iv:H2NIAN0xBUDqnyco9gA3zYAsKtSeA/JpqYrPhc1eqc0=,tag:6OFGDfsucG5gDerImgpuXA==,type:str]
nix: {}
users: ENC[AES256_GCM,data:w/2Vdq0EHXaJ5u/aA/reSCtwRHreWm1U1WoJT927xV81zoN0ytoYOwush610caZu8vVXkL4b0hysK77dyWJkdkYpwLY8xG9pLkYlU3lN5E/2tgEjB7Dd7oY7TFTCNuypmIzYh6V74KiHMeA0vlyWUp9lLNt40Ro3MZLT42DyTYjF6YBoUHUp0fS0rKypILJGobJBrwz2YWagXj80IqaaUmmsIcYAaM2u3dQviLlRkIyUxPd1wjFoMc/OMp5Y8A4ZHroCN0wJitGeEEP33GD+MUy58u05pA430AD5Mo4H2V7b3t0qIkOQ8a0BgSVA8UqmrcY/TfikuIZ1kTyCxvD7kmjPq5tG+bhtHt85wgk1XffVO3NDTK7UrltO8R6KolQ5bBgcKgl7YnFTN5qSAT+xrYg8oZaPrGQBTx6eEVETKHKe4oSDkGlAle86lenhF+jm3k2ALmH9X3P/TpAtfRhuU+sUKqhrqQ2Nf4M7LfBtd7lyt2ESqilKokcl51gWCY+1B75dCEIdb/BPmpwzJBGFOI2nZqhxFnVa8TyMpT7C2TxK7rCBPDt5NnNvWYc4+8sRXHBz7s2R5NTk4gaJODlo3HvyL0MV,iv:XlO48HKJWRgwsozmgXstfirwb5CUY+ywelbgLlcx/n4=,tag:GuQMkL2mpNkTJIep79x0zw==,type:str]
users: ENC[AES256_GCM,data:xkjm0+PBt6gmZyfi3n3OIEe5b+d4OtN0Y3UfmdcbcJHbJZuiz+60oUjlAN0vjtsi0muufoAqtGJTIpm9nDZzzN7b7LK43TAhcuSlIm5LpbZFp1U3H4laRbTwauAT6wA0aDCfAkwTozxAuEUk1jAu+65ktJNJb7b0PR7s/I/wf7IgW2+K4Jv3LIOZIipUwfuvXuTzsxCElYRvGZXmIuXrYq1EaymksHHggemrKeMWLAae7mzz5v3aBbwxiVjQNkQkS4ApsO/5nZUat0oqXA==,iv:fptZn4NmX3iYKSEPLJAOFpt+KQ6TR1w9KaY9IF4p/Wk=,tag:UKvMOSIT5/mhfZA3usbLhQ==,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]
kaas: ENC[AES256_GCM,data:3yI6lH0rw+f2OFJ94Z7zb0pYwy4FDFs9rJi2wpd9VVWghmey5g4O788ypXa34XqKCQDDHDgTxwyDs6KpvCQQaLV1PDhXd4Po0SSlIOkUtCWhOf6Tp3PM2ASoE+AAAzJLJUc6AZdBJRyYU9V+UvO9jW+WmlpZpsg5crnVMzZo7f2AF0ep9A/A5BL1Y2UhYQE4LDVkLC9AL3hl8IhF5xSdZdO0ugrP0x7CKVUxA7fJyOjx7/IKVwvgKD4xlhIgv9lYPTvE2vUs+w==,iv:e6b98ZnBqf7hh3SSKGdTl63OpQm1oK95lHXdwTiLft8=,tag:IS/lDgvJvSd7OmDLP+uG1g==,type:str]
radarr:
apikey: ENC[AES256_GCM,data:G141GW4PyS5pbAV39HcVscMw3s30txOgTZzWaL7o+ccZfnfDLv796O6xKXdqGZ8saLsveghLw9Z6a5luusHyQ3Q5ESL6W7SVeZVTuSqSC3i/4jl75FJxhnsgVsfrnYxzLGpKiw==,iv:sZl/XLh6y3WgSAn6nH3sFB6atBifZdghm+QsCNDbcjY=,tag:Tw+R80nrF0T0yDti0Uf+ig==,type:str]
sonarr:
@ -23,23 +24,9 @@ mydia:
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]
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]
secret_key: ENC[AES256_GCM,data:u6IRFV1D/4g+eqQIUPW0QHlkoa+MliymThp34k+QCHqQ247er4bCdgftuWsXgPAPY7DtwFVLG7Do5eBqIiii7g==,iv:FY7LIW0O5/Cp2JvYu17ctInt0rgkzjaPHfxZBs0GTac=,tag:Gtu+ZGAgsi5vzILOKDac1g==,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]
nzbkey: ENC[AES256_GCM,data:tGFnZ24XNI7U8pVYq45ENSVTeVkkcWfT5/NewqSJ3sm7Bexxml/PFTMBIl+97mWzNMMFklBurX/115P06NHCj1mxEvIjIc1bF4yuYhZFdSTlqRVWaESE/Ei7gke758FCt37N43wADgaKj4i5jizDHJMIbaw8ncP3qBSCy1F4BAU=,iv:RA+3oYGhVLBG+ikHMwBG3t2iN15lGsncdmlkfF6vJhY=,tag:6FNM18KCSzzpIXYDpQfHSg==,type:str]
whisparr:
apikey: ENC[AES256_GCM,data:kIGCsd4mszm90PoQMzlSEBKw9Ow0GvP1qdLtwXYKkAb6b65l89v8lMWJ2X1MyD2gJX+P+Bv1F/2BSjUFXErq/UYnp4dAjwKi/ezGCbhjMutDM1FvwFWEHRnR3gjd9uXPWJ8Xhg==,iv:98aPQlcZHJovpnzACDs6RtKblLnHg6wyi+Er5DAowj8=,tag:Tl8jz/pWYWAtBCfoztKdyw==,type:str]
coturn:
secret: ENC[AES256_GCM,data:5RmLZ7vQIAvIzvax8oNJkImQ6vXR+MZ2eqxaBJCBlccnFC1rP16/6UtausXVf0eWysw+fpMW5yEmUtAdyxQoPiBCK8lziAZBdkekQnAvFouBaWy8WIZt6XRa71P4xDCDGudpMiGwGGNt+R9yylez+azaLrLyJM3481RPohDMoOM=,iv:2P83lgxGtHwYr+ApAdHopVfRWagxWlC+nt53API/SiQ=,tag:Qv+A03BE1QvEqJMtORiQVA==,type:str]
qbittorrent:
password: ENC[AES256_GCM,data:LIDxh0Ni0JgQGWFix/Ihw7IlUPgzMhrMlWNP5LKkAnEM6EoqA9kFwiPeizB0CZ20+vSqRiL9fikBf8qGLA17L7AKh8I4OTFDlpKpMRtRlMq9S5UBEyOqtOMcvkCSf6/qGoORd1KJSlaitZk47SYRuccOpy/2vAvbMRdLm0SYEqc=,iv:tQdN1N9kXoq7OZbR2eYyy50FltsMAAUI4Lr7U4/SpJE=,tag:3ZOLvjHXD7i7WFy1/Ggqtg==,type:str]
password_hash: ENC[AES256_GCM,data:urufJbSErLqPdU6jLLZk+27fe4k+cKLXcGRGSqroUDdGMzDnhSF+ZWuPxwDlJQR3ws2GnuiEASncwNO/SALKXFDk2V2gsKJ4hsjyiIbsqCwSEFB/XMY0nY/x0xrcIfMVE0HdrNYeQ3zT01Z5jQpSd7wo2M63LaULL/Av498=,iv:tnUVhOgrImKa6iii2hJZn5LKrySM5v47B2zDZMgmUow=,tag:g3xa/4Z+t1Q9Wnd4XzefLg==,type:str]
password_hash: ENC[AES256_GCM,data:QWuQYmfBn9eLDYztH7TmQvw74MvmzCQ98OlBtyjm1Icr2c63epRuHWzQbm+Q+1jrCSiQreOB3ZyjLzkeV6SlLonryUSD71uBWVwctgPXO0XDrxE1Vi6dkiwC3TF65JTMDhyjDLEj1YkiMP25Fz5NidJTP/r9GlXTfM7gjWo=,iv:bpgL5IoAv+1PUtgNIjLcbzN8C9z55ndypz4LEELAhLc=,tag:VB+XTCwLeIEYKnOr/0f7zA==,type:str]
password: ENC[AES256_GCM,data:UepYY6UjJV/jo2aXTOEnKRtsjSqOSYPQlKlrAa7rf9rdnt2UXGjCkvN+A72pICuIBCAmhXZBAUMvmWTV9trk6NREHe0cY1xTC7pNv3x9TM/ZQmH498pbT/95pYAKwouHp9heJQ==,iv:FzjF+xPoaOp+gplxpz940V2dkWSTWe8dWUxexCoxxHc=,tag:TDZsboq9fEmmBrwJN/HTpQ==,type:str]
sops:
age:
- recipient: age19qfpf980tadguqq44zf6xwvjvl428dyrj46ha3n6aeqddwhtnuqqml7etq
@ -60,7 +47,7 @@ sops:
TTRWaHhpNWlkVDFmMFN4ZTNHMUxyNVkKV693pzTKRkZboQCMPr9IyMGSgxfuHXcb
Y6BNcp6Qg6PWtX5QI7wRkPNINAK1TEbRBba+b8h6gMmVU4DliQyFiQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-03-05T10:38:19Z"
mac: ENC[AES256_GCM,data:gS6YTRTl6UdOC7Afrj1LrkgA7MWRLF0HNWytfzhkvThLW+JJrHPEhvWiYrsPW1Bm6o2JkKqVP5HfzcuGNIHJySkEQ4HV02BbibtMNiUKqk+voATsWOpo6957bwRJaTbvDvxmzIQ38TSUoj/pt8Z8WTl0hSPAlqNlWYffXX0y8K4=,iv:53R2bKYKiHJi9DTecg7hiuGNb3Kj9rA2U/oPJ+AFO5I=,tag:5uqvmEJCaCS/yNqyt/FPZg==,type:str]
lastmodified: "2025-12-04T11:24:52Z"
mac: ENC[AES256_GCM,data:jIgkl1lcVDSlKqJs9fjaHUAZsGL+22T86/qqKyDziHl0+VU763Ezwm8P+la+55jIIT2zLhFcUjhn2BabBi90OeEPztAC4rGpZj6+ZZ0GDCj/JhjPAAo3LgAKOCG0Xgf8MZWr/rXd6bLhW7Qj36PMJnap26rjEiUZeSvpWS2dz8g=,iv:CDx8fBI9Dl1uwrbMD1fa7/h3C7haK3xZxJI59mtL1LA=,tag:2UDRFJoevGEBKZA/9eUiOw==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0