This commit is contained in:
mjallen18
2025-09-30 18:29:34 -05:00
parent ec23a7fe14
commit 751b4f9f69
37 changed files with 814 additions and 971 deletions

View File

@@ -44,7 +44,7 @@ in
# Example of using these functions together # Example of using these functions together
nixosConfigurations = lib.mapAttrs' ( nixosConfigurations = lib.mapAttrs' (
name: _name:
{ system, hostname, ... }: { system, hostname, ... }:
{ {
name = hostname; name = hostname;

View File

@@ -1,5 +1,5 @@
# Example usage of the reverse proxy utilities # Example usage of the reverse proxy utilities
{ inputs, lib, ... }: { lib, ... }:
let let
inherit (lib.mjallen-lib.reverseproxy) inherit (lib.mjallen-lib.reverseproxy)
mkReverseProxy mkReverseProxy
@@ -89,11 +89,15 @@ in
domain = "example.com"; domain = "example.com";
priority = 20; priority = 20;
rule = "Host(`custom.example.com`) && PathPrefix(`/api`)"; rule = "Host(`custom.example.com`) && PathPrefix(`/api`)";
middlewares = [ "crowdsec" "whitelist-geoblock" "rate-limit" ]; middlewares = [
"crowdsec"
"whitelist-geoblock"
"rate-limit"
];
}; };
# Example usage in a Traefik configuration: # Example usage in a Traefik configuration:
# #
# mjallen.services.traefik = { # mjallen.services.traefik = {
# enable = true; # enable = true;
# extraServices = multipleProxies.extraServices; # extraServices = multipleProxies.extraServices;

View File

@@ -19,7 +19,7 @@ in
nixosSystems = inputs.self.mjallen-lib.file.filterNixOSSystems allSystems; nixosSystems = inputs.self.mjallen-lib.file.filterNixOSSystems allSystems;
in in
inputs.nixpkgs.lib.mapAttrs' ( inputs.nixpkgs.lib.mapAttrs' (
name: _name:
{ system, hostname, ... }: { system, hostname, ... }:
let let
# Create extended lib with mjallen-lib # Create extended lib with mjallen-lib
@@ -86,7 +86,7 @@ in
allHomes = inputs.self.mjallen-lib.file.scanHomes ../homes; allHomes = inputs.self.mjallen-lib.file.scanHomes ../homes;
in in
inputs.nixpkgs.lib.mapAttrs' ( inputs.nixpkgs.lib.mapAttrs' (
name: _name:
{ {
system, system,
username, username,

View File

@@ -1,77 +1,112 @@
{ inputs }: { inputs }:
let let
inherit (inputs.nixpkgs.lib) inherit (inputs.nixpkgs.lib)
mkOption
types
listToAttrs listToAttrs
nameValuePair nameValuePair
; ;
in in
rec { rec {
# Create a service configuration for Traefik # Create a service configuration for Traefik
mkService = { mkService =
name, {
url, name,
loadBalancer ? { }, url,
}: { loadBalancer ? { },
inherit name url; }:
config = { {
loadBalancer = { inherit name url;
servers = [{ inherit url; }]; config = {
} // loadBalancer; loadBalancer = {
servers = [ { inherit url; } ];
}
// loadBalancer;
};
}; };
};
# Create a router configuration for Traefik # Create a router configuration for Traefik
mkRouter = { mkRouter =
subdomain, {
domain ? "mjallen.dev", subdomain,
service, domain ? "mjallen.dev",
entryPoints ? [ "websecure" ], service,
middlewares ? [ "crowdsec" "whitelist-geoblock" ], entryPoints ? [ "websecure" ],
priority ? null, middlewares ? [
rule ? null, "crowdsec"
tls ? { certResolver = "letsencrypt"; }, "whitelist-geoblock"
}: { ],
inherit subdomain service entryPoints middlewares; priority ? null,
config = { rule ? null,
inherit entryPoints service middlewares tls; tls ? {
rule = if rule != null then rule else "Host(`${subdomain}.${domain}`)"; certResolver = "letsencrypt";
} // (if priority != null then { inherit priority; } else { }); },
}; }:
{
inherit
subdomain
service
entryPoints
middlewares
;
config = {
inherit
entryPoints
service
middlewares
tls
;
rule = if rule != null then rule else "Host(`${subdomain}.${domain}`)";
}
// (if priority != null then { inherit priority; } else { });
};
# Create both service and router for a simple reverse proxy setup # Create both service and router for a simple reverse proxy setup
mkReverseProxy = { mkReverseProxy =
name, {
subdomain, name,
url, subdomain,
domain ? "mjallen.dev", url,
entryPoints ? [ "websecure" ], domain ? "mjallen.dev",
middlewares ? [ "crowdsec" "whitelist-geoblock" ], entryPoints ? [ "websecure" ],
priority ? null, middlewares ? [
rule ? null, "crowdsec"
tls ? { certResolver = "letsencrypt"; }, "whitelist-geoblock"
loadBalancer ? { }, ],
}: { priority ? null,
service = mkService { rule ? null,
inherit name url loadBalancer; tls ? {
certResolver = "letsencrypt";
},
loadBalancer ? { },
}:
{
service = mkService {
inherit name url loadBalancer;
};
router = mkRouter {
inherit
subdomain
domain
entryPoints
middlewares
priority
rule
tls
;
service = name;
};
}; };
router = mkRouter {
inherit subdomain domain entryPoints middlewares priority rule tls;
service = name;
};
};
# Convert a list of services to the format expected by Traefik module # Convert a list of services to the format expected by Traefik module
servicesToConfig = services: servicesToConfig =
listToAttrs (map (service: nameValuePair service.name service.config) services); services: listToAttrs (map (service: nameValuePair service.name service.config) services);
# Convert a list of routers to the format expected by Traefik module # Convert a list of routers to the format expected by Traefik module
routersToConfig = routers: routersToConfig =
listToAttrs (map (router: nameValuePair router.subdomain router.config) routers); routers: listToAttrs (map (router: nameValuePair router.subdomain router.config) routers);
# Helper to create multiple reverse proxies at once # Helper to create multiple reverse proxies at once
mkReverseProxies = proxies: mkReverseProxies =
proxies:
let let
results = map mkReverseProxy proxies; results = map mkReverseProxy proxies;
services = map (result: result.service) results; services = map (result: result.service) results;
@@ -91,31 +126,47 @@ rec {
middlewares = { middlewares = {
# Authentication middleware # Authentication middleware
auth = [ "authentik" ]; auth = [ "authentik" ];
# Basic security (default) # Basic security (default)
basic = [ "crowdsec" "whitelist-geoblock" ]; basic = [
"crowdsec"
"whitelist-geoblock"
];
# Internal only access # Internal only access
internal = [ "crowdsec" "whitelist-geoblock" "internal-ipallowlist" ]; internal = [
"crowdsec"
"whitelist-geoblock"
"internal-ipallowlist"
];
# WebSocket support # WebSocket support
websocket = [ "crowdsec" "whitelist-geoblock" "onlyoffice-websocket" ]; websocket = [
"crowdsec"
"whitelist-geoblock"
"onlyoffice-websocket"
];
# Authenticated with basic security # Authenticated with basic security
authBasic = [ "crowdsec" "whitelist-geoblock" "authentik" ]; authBasic = [
"crowdsec"
"whitelist-geoblock"
"authentik"
];
}; };
# Common service URL builders # Common service URL builders
urls = { urls = {
# Local container service # Local container service
container = containerName: port: "http://\${config.containers.${containerName}.localAddress}:${toString port}"; container =
containerName: port: "http://\${config.containers.${containerName}.localAddress}:${toString port}";
# Local host service # Local host service
localhost = port: "http://127.0.0.1:${toString port}"; localhost = port: "http://127.0.0.1:${toString port}";
# Network service # Network service
network = ip: port: "http://${ip}:${toString port}"; network = ip: port: "http://${ip}:${toString port}";
# Server IP service (using your server IP pattern) # Server IP service (using your server IP pattern)
server = port: "http://\${serverIp}:${toString port}"; server = port: "http://\${serverIp}:${toString port}";
}; };
@@ -123,31 +174,47 @@ rec {
# Pre-configured reverse proxy templates # Pre-configured reverse proxy templates
templates = { templates = {
# Standard web application # Standard web application
webapp = { name, subdomain, port, ... }@args: webapp =
mkReverseProxy ({ { port, ... }@args:
url = urls.localhost port; mkReverseProxy (
middlewares = middlewares.basic; {
} // args); url = urls.localhost port;
middlewares = middlewares.basic;
}
// args
);
# Authenticated web application # Authenticated web application
authWebapp = { name, subdomain, port, ... }@args: authWebapp =
mkReverseProxy ({ { port, ... }@args:
url = urls.localhost port; mkReverseProxy (
middlewares = middlewares.authBasic; {
} // args); url = urls.localhost port;
middlewares = middlewares.authBasic;
}
// args
);
# Container-based service # Container-based service
containerService = { name, subdomain, containerName, port, ... }@args: containerService =
mkReverseProxy ({ { containerName, port, ... }@args:
url = urls.container containerName port; mkReverseProxy (
middlewares = middlewares.basic; {
} // args); url = urls.container containerName port;
middlewares = middlewares.basic;
}
// args
);
# Internal-only service # Internal-only service
internalService = { name, subdomain, port, ... }@args: internalService =
mkReverseProxy ({ { port, ... }@args:
url = urls.localhost port; mkReverseProxy (
middlewares = middlewares.internal; {
} // args); url = urls.localhost port;
middlewares = middlewares.internal;
}
// args
);
}; };
} }

View File

@@ -498,30 +498,30 @@ in
# send_cancel = "0"; # send_cancel = "0";
# }; # };
# hyprgrass-bind = [ # hyprgrass-bind = [
# # swipe left from right edge # # swipe left from right edge
# ", edge:r:l, workspace, +1" # ", edge:r:l, workspace, +1"
# # swipe up from bottom edge # # swipe up from bottom edge
# ", edge:d:u, exec, ${cfg.defaultApps.browser.pname}" # ", edge:d:u, exec, ${cfg.defaultApps.browser.pname}"
# # swipe down from left edge # # swipe down from left edge
# ", edge:l:d, exec, pactl set-sink-volume @DEFAULT_SINK@ -4%" # ", edge:l:d, exec, pactl set-sink-volume @DEFAULT_SINK@ -4%"
# # swipe down with 4 fingers # # swipe down with 4 fingers
# ", swipe:4:d, killactive" # ", swipe:4:d, killactive"
# # swipe diagonally left and down with 3 fingers # # swipe diagonally left and down with 3 fingers
# # l (or r) must come before d and u # # l (or r) must come before d and u
# ", swipe:3:ld, exec, foot" # ", swipe:3:ld, exec, foot"
# # tap with 3 fingers # # tap with 3 fingers
# ", tap:3, exec, foot" # ", tap:3, exec, foot"
# # longpress can trigger mouse binds: # # longpress can trigger mouse binds:
# ", longpress:2, movewindow" # ", longpress:2, movewindow"
# ", longpress:3, resizewindow" # ", longpress:3, resizewindow"
# ]; # ];
# }; # };
}; };

View File

@@ -0,0 +1,160 @@
{
config,
lib,
namespace,
...
}:
with lib;
let
cfg = config.${namespace}.hardware.disko;
defaultBtrfsMountOptions = [
"compress=${cfg.compression}"
"noatime"
];
defaultBcachefsMountOptions = [
"noatime"
];
subvolumes =
let
make =
name: subvolume:
nameValuePair "${name}" {
mountOptions =
if subvolume.mountOptions == null then
if cfg.filesystem == "btrfs" then defaultBtrfsMountOptions else defaultBcachefsMountOptions
else
subvolume.mountOptions;
mountpoint = if subvolume.mountPoint == null then "/${name}" else subvolume.mountPoint;
};
in
mapAttrs' make cfg.subvolumes;
# BTRFS root partition configuration
root = {
name = "${cfg.filesystem}-root";
size = "100%";
content = {
type = cfg.filesystem;
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = subvolumes;
}
// (
if cfg.filesystem == "btrfs" then
{
extraArgs = [ "-f" ]; # Override existing partition
}
else
{
# This refers to a filesystem in the `bcachefs_filesystems` attrset below.
filesystem = "mounted_subvolumes_in_multi";
label = "ssd.ssd1";
extraFormatArgs = [
"--discard"
];
}
);
};
# Luks root partition configuration
luksRoot = {
name = "cryptroot";
size = "100%";
content = {
type = "luks";
name = "cryptroot";
extraOpenArgs = [
"--allow-discards"
"--perf-no_read_workqueue"
"--perf-no_write_workqueue"
];
settings = {
crypttabExtraOpts = [
"fido2-device=auto"
"token-timeout=10"
];
};
content = {
type = cfg.filesystem;
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = subvolumes;
}
// (
if cfg.filesystem == "btrfs" then
{
extraArgs = [ "-f" ]; # Override existing partition
}
else
{
# This refers to a filesystem in the `bcachefs_filesystems` attrset below.
filesystem = "mounted_subvolumes_in_multi";
label = "ssd.ssd1";
extraFormatArgs = [
"--discard"
];
}
);
};
};
in
{
imports = [ ./options.nix ];
config = lib.mkIf cfg.enable {
disko.devices = lib.mkMerge [
{
nodev."/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"defaults"
"size=25%"
];
};
disk = {
main = {
device = cfg.rootDisk;
type = "disk";
imageSize = "32G";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
swap = lib.mkIf cfg.enableSwap {
type = "8200";
size = cfg.swapSize;
};
root = if cfg.enableLuks then luksRoot else root;
};
};
};
};
# configure Bcachefs
bcachefs_filesystems = lib.mkIf (cfg.filesystem == "bcachefs") {
mounted_subvolumes_in_multi = {
type = "bcachefs_filesystem";
# passwordFile = "/etc/nixos/pool.jwe";
extraFormatArgs = [
"--compression=${cfg.compression}"
];
subvolumes = subvolumes;
};
};
}
];
};
}

View File

@@ -20,5 +20,30 @@ in
enableLuks = mkBoolOpt false "Enable Luks"; enableLuks = mkBoolOpt false "Enable Luks";
swapSize = mkOpt types.str "16G" "size of swap part"; swapSize = mkOpt types.str "16G" "size of swap part";
rootDisk = mkOpt types.str "/dev/nvme0n1" "Root disk";
compression = mkOpt types.str "zstd" "Type of compression to enable";
subvolumes =
mkOpt
(types.attrsOf (
types.submodule {
options = {
mountPoint = mkOpt (types.nullOr types.path) null "Mountpoint of the subvolume";
mountOptions = mkOpt (types.nullOr (types.listOf types.str)) null "Extra mount options";
};
}
))
{
"home" = { };
"etc" = { };
"nix" = { };
"root" = { };
"log" = {
mountPoint = "/var/log";
};
}
"Subvolumes on root disk";
}; };
} }

View File

@@ -264,102 +264,102 @@ in
ps: with ps; [ ps: with ps; [
# pkgs.${namespace}.python-roborock # pkgs.${namespace}.python-roborock
# pkgs.${namespace}.pyvesync # pkgs.${namespace}.pyvesync
# # Core functionality # # Core functionality
# aiohttp # aiohttp
# aiodns # aiodns
# paho-mqtt # paho-mqtt
# pillow # pillow
# pytz # pytz
# pyyaml # pyyaml
# sqlalchemy # sqlalchemy
# # Discovery & networking # # Discovery & networking
# zeroconf # zeroconf
# netdisco # netdisco
# ifaddr # ifaddr
# ssdp # ssdp
# # Device protocols # # Device protocols
# pyserial # Serial communications # pyserial # Serial communications
# bluepy # Bluetooth LE # bluepy # Bluetooth LE
# # Smart home ecosystems # # Smart home ecosystems
# mutagen # Media file metadata # mutagen # Media file metadata
# pysonos # Sonos # pysonos # Sonos
# pywemo # Belkin WeMo # pywemo # Belkin WeMo
# python-miio # Xiaomi devices # python-miio # Xiaomi devices
# python-kasa # TP-Link # python-kasa # TP-Link
# # Sensors & monitoring # # Sensors & monitoring
# meteocalc # Weather calculations # meteocalc # Weather calculations
# speedtest-cli # Internet speed # speedtest-cli # Internet speed
# # Visualization & UI # # Visualization & UI
# matplotlib # Graphing # matplotlib # Graphing
# # Security # # Security
# bcrypt # bcrypt
# cryptography # cryptography
# pyjwt # pyjwt
# # Media # # Media
# ha-ffmpeg # Camera streams # ha-ffmpeg # Camera streams
# # Specialized integrations # # Specialized integrations
# python-matter-server # Matter protocol # python-matter-server # Matter protocol
# # System integrations # # System integrations
# psutil # System monitoring # psutil # System monitoring
psycopg2 psycopg2
# numpy # numpy
# hassil # hassil
# pyturbojpeg # pyturbojpeg
# paho-mqtt # paho-mqtt
# pychromecast # pychromecast
# pyatv # pyatv
# python-otbr-api # python-otbr-api
# brother # brother
# pyipp # pyipp
# govee-ble # govee-ble
# adguardhome # adguardhome
# nextcord # nextcord
# aiogithubapi # aiogithubapi
# jellyfin-apiclient-python # jellyfin-apiclient-python
# pylitterbot # pylitterbot
# dateparser # dateparser
# aionut # aionut
# nextcloudmonitor # nextcloudmonitor
# ollama # ollama
# pynecil # pynecil
# aiopyarr # aiopyarr
# pysabnzbd # pysabnzbd
# getmac # getmac
# zigpy # zigpy
# bellows # For Zigbee EmberZNet-based adapters # bellows # For Zigbee EmberZNet-based adapters
# zigpy-xbee # For XBee adapters # zigpy-xbee # For XBee adapters
# zigpy-deconz # For ConBee/RaspBee adapters # zigpy-deconz # For ConBee/RaspBee adapters
# pyicloud # iCloud # pyicloud # iCloud
# pyatv # Apple TV # pyatv # Apple TV
# opencv-python # opencv-python
# face-recognition # face-recognition
# ibeacon-ble # ibeacon-ble
# gehomesdk # gehomesdk
# onedrive-personal-sdk # onedrive-personal-sdk
# pkgs.${namespace}.python-steam # pkgs.${namespace}.python-steam
# apple-weatherkit # apple-weatherkit
# samsungctl # samsungctl
# samsungtvws # samsungtvws
# aiohomekit # aiohomekit
# icmplib # icmplib
# aioelectricitymaps # aioelectricitymaps
# wyoming # wyoming
# pysmartthings # pysmartthings
# wakeonlan # wakeonlan
# ephem # ephem
]; ];
config = { config = {

View File

@@ -12,7 +12,7 @@ let
hostAddress = "10.0.1.3"; hostAddress = "10.0.1.3";
actualUserId = config.users.users.nix-apps.uid; actualUserId = config.users.users.nix-apps.uid;
actualGroupId = config.users.groups.jallen-nas.gid; actualGroupId = config.users.groups.jallen-nas.gid;
actualConfig = actualConfig =
{ lib, ... }: { lib, ... }:
{ {
@@ -80,12 +80,12 @@ let
system.stateVersion = "23.11"; system.stateVersion = "23.11";
}; };
bindMounts = { bindMounts = {
${dataDir} = { ${dataDir} = {
hostPath = cfg.dataDir; hostPath = cfg.dataDir;
isReadOnly = false; isReadOnly = false;
};
}; };
};
# Create reverse proxy configuration using mkReverseProxy # Create reverse proxy configuration using mkReverseProxy
reverseProxyConfig = lib.${namespace}.mkReverseProxy { reverseProxyConfig = lib.${namespace}.mkReverseProxy {
@@ -95,23 +95,25 @@ let
middlewares = cfg.reverseProxy.middlewares; middlewares = cfg.reverseProxy.middlewares;
}; };
actualContainer = (lib.${namespace}.mkContainer { actualContainer =
name = "actual"; (lib.${namespace}.mkContainer {
localAddress = cfg.localAddress; name = "actual";
port = cfg.port; localAddress = cfg.localAddress;
bindMounts = bindMounts; port = cfg.port;
config = actualConfig; bindMounts = bindMounts;
}) { inherit lib; }; config = actualConfig;
})
{ inherit lib; };
fullConfig = { fullConfig = {
${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable { ${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable {
reverseProxies = [ reverseProxyConfig ]; reverseProxies = [ reverseProxyConfig ];
}; };
} // actualContainer; }
// actualContainer;
in in
{ {
imports = [ ./options.nix ]; imports = [ ./options.nix ];
config = mkIf cfg.enable fullConfig; config = mkIf cfg.enable fullConfig;
} }

View File

@@ -1,11 +1,9 @@
{ {
config, config,
lib, lib,
pkgs,
namespace, namespace,
... ...
}: }:
with lib;
let let
cfg = config.${namespace}.services.crowdsec; cfg = config.${namespace}.services.crowdsec;
in in
@@ -71,6 +69,7 @@ in
]; ];
}; };
settings = { settings = {
# general.api.server.enable = true;
capi.credentialsFile = cfg.apiKey; capi.credentialsFile = cfg.apiKey;
}; };
}; };

View File

@@ -13,7 +13,7 @@ let
mailerPasswordFile = config.sops.secrets."jallen-nas/gitea/mail-key".path; mailerPasswordFile = config.sops.secrets."jallen-nas/gitea/mail-key".path;
metricsTokenFile = config.sops.secrets."jallen-nas/gitea/metrics-key".path; metricsTokenFile = config.sops.secrets."jallen-nas/gitea/metrics-key".path;
serviceConfig = serviceConfig =
{ lib, ... }: { lib, ... }:
{ {
services.gitea = { services.gitea = {
@@ -74,7 +74,7 @@ let
services.resolved.enable = true; services.resolved.enable = true;
system.stateVersion = "23.11"; system.stateVersion = "23.11";
}; };
bindMounts = { bindMounts = {
${dataDir} = { ${dataDir} = {
hostPath = cfg.dataDir; hostPath = cfg.dataDir;
@@ -95,19 +95,22 @@ let
middlewares = cfg.reverseProxy.middlewares; middlewares = cfg.reverseProxy.middlewares;
}; };
containerConfig = (lib.${namespace}.mkContainer { containerConfig =
name = "gitea"; (lib.${namespace}.mkContainer {
localAddress = cfg.localAddress; name = "gitea";
port = cfg.httpPort; localAddress = cfg.localAddress;
bindMounts = bindMounts; port = cfg.httpPort;
config = serviceConfig; bindMounts = bindMounts;
}) { inherit lib; }; config = serviceConfig;
})
{ inherit lib; };
giteaConfig = { giteaConfig = {
${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable { ${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable {
reverseProxies = [ reverseProxyConfig ]; reverseProxies = [ reverseProxyConfig ];
}; };
} // containerConfig; }
// containerConfig;
in in
{ {
imports = [ ./options.nix ]; imports = [ ./options.nix ];

View File

@@ -1,4 +1,9 @@
{ config, lib, namespace, ... }: {
config,
lib,
namespace,
...
}:
let let
inherit (lib.${namespace}) mkOpt mkReverseProxyOpt; inherit (lib.${namespace}) mkOpt mkReverseProxyOpt;
cfg = config.${namespace}.services.glance; cfg = config.${namespace}.services.glance;
@@ -16,7 +21,7 @@ with lib;
reverseProxy = mkReverseProxyOpt; reverseProxy = mkReverseProxyOpt;
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.glance = { services.glance = {
enable = true; enable = true;

View File

@@ -1,4 +1,9 @@
{ config, lib, namespace, ... }: {
config,
lib,
namespace,
...
}:
let let
inherit (lib.${namespace}) mkOpt mkReverseProxyOpt; inherit (lib.${namespace}) mkOpt mkReverseProxyOpt;
cfg = config.${namespace}.services.matrix; cfg = config.${namespace}.services.matrix;
@@ -19,10 +24,16 @@ let
port = cfg.port; port = cfg.port;
tls = false; tls = false;
x_forwarded = true; x_forwarded = true;
bind_addresses = [ "::1" "0.0.0.0" ]; bind_addresses = [
"::1"
"0.0.0.0"
];
resources = [ resources = [
{ {
names = [ "client" "federation" ]; names = [
"client"
"federation"
];
compress = false; compress = false;
} }
]; ];
@@ -37,7 +48,7 @@ let
issuer = "https://authentik.mjallen.dev/application/o/matrix/"; issuer = "https://authentik.mjallen.dev/application/o/matrix/";
client_id = "KiChwyQn2kMtXU6LU0x3dlCb0jO6VB6e9xsN9NPs"; # TO BE FILLED client_id = "KiChwyQn2kMtXU6LU0x3dlCb0jO6VB6e9xsN9NPs"; # TO BE FILLED
client_secret = "6XRfNCUayZqnyaMv0QSEeFz98x2y8BkXnDyylmvAbg71YkQVtpEybP6jmPzncpJsx4k5evtziicgu8p9dOa2oADHL6Ao13643VMTsI4BSel1sbIICA2TH755BpB9J39A"; # TO BE FILLED client_secret = "6XRfNCUayZqnyaMv0QSEeFz98x2y8BkXnDyylmvAbg71YkQVtpEybP6jmPzncpJsx4k5evtziicgu8p9dOa2oADHL6Ao13643VMTsI4BSel1sbIICA2TH755BpB9J39A"; # TO BE FILLED
scopes =[ scopes = [
"openid" "openid"
"profile" "profile"
"email" "email"
@@ -64,31 +75,31 @@ let
cp_max = 10; cp_max = 10;
}; };
}; };
# Registration settings # Registration settings
enable_registration = false; # Set to true initially to create admin user enable_registration = false; # Set to true initially to create admin user
enable_registration_without_verification = false; enable_registration_without_verification = false;
# registration_shared_secret = "BogieDudie1"; # registration_shared_secret = "BogieDudie1";
# Media settings # Media settings
max_upload_size = "50M"; max_upload_size = "50M";
media_store_path = "/var/lib/matrix-synapse/media"; media_store_path = "/var/lib/matrix-synapse/media";
# Logging # Logging
# log_config = "/var/lib/matrix-synapse/log_config.yaml"; # log_config = "/var/lib/matrix-synapse/log_config.yaml";
trusted_key_servers = [ trusted_key_servers = [
{ {
server_name = "matrix.org"; server_name = "matrix.org";
} }
]; ];
}; };
}; };
users.users.matrix-synapse = { users.users.matrix-synapse = {
isSystemUser = true; isSystemUser = true;
group = "matrix-synapse"; group = "matrix-synapse";
}; };
users.groups.matrix-synapse = {}; users.groups.matrix-synapse = { };
services.postgresql = { services.postgresql = {
enable = lib.mkDefault true; enable = lib.mkDefault true;
@@ -135,19 +146,22 @@ let
middlewares = cfg.reverseProxy.middlewares; middlewares = cfg.reverseProxy.middlewares;
}; };
matrixContainer = (lib.${namespace}.mkContainer { matrixContainer =
name = "matrix-synapse"; (lib.${namespace}.mkContainer {
localAddress = cfg.localAddress; name = "matrix-synapse";
port = cfg.port; localAddress = cfg.localAddress;
bindMounts = bindMounts; port = cfg.port;
config = matrixConfig; bindMounts = bindMounts;
}) { inherit lib; }; config = matrixConfig;
})
{ inherit lib; };
fullConfig = { fullConfig = {
${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable { ${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable {
reverseProxies = [ reverseProxyConfig ]; reverseProxies = [ reverseProxyConfig ];
}; };
} // matrixContainer; }
// matrixContainer;
in in
with lib; with lib;
{ {
@@ -164,4 +178,4 @@ with lib;
}; };
config = lib.mkIf cfg.enable fullConfig; config = lib.mkIf cfg.enable fullConfig;
} }

View File

@@ -96,24 +96,28 @@ in
secretFile = secretsFile; secretFile = secretsFile;
extraApps = { extraApps = {
inherit (pkgs.nextcloud31Packages.apps) app_api inherit (pkgs.nextcloud31Packages.apps)
bookmarks app_api
mail bookmarks
calendar mail
contacts calendar
integration_openai contacts
integration_paperless integration_openai
maps integration_paperless
oidc_login maps
onlyoffice oidc_login
previewgenerator onlyoffice
recognize previewgenerator
richdocuments recognize
user_oidc; richdocuments
user_oidc
;
inherit nextcloudPhotos inherit
nextcloudPdfViewer nextcloudPhotos
nextcloudAssist; nextcloudPdfViewer
nextcloudAssist
;
}; };
config = { config = {

View File

@@ -1,4 +1,9 @@
{ config, lib, namespace, ... }: {
config,
lib,
namespace,
...
}:
let let
inherit (lib.${namespace}) mkOpt mkReverseProxyOpt; inherit (lib.${namespace}) mkOpt mkReverseProxyOpt;
cfg = config.${namespace}.services.ntfy; cfg = config.${namespace}.services.ntfy;
@@ -66,19 +71,22 @@ let
middlewares = cfg.reverseProxy.middlewares; middlewares = cfg.reverseProxy.middlewares;
}; };
ntfyContainer = (lib.${namespace}.mkContainer { ntfyContainer =
name = "ntfy"; (lib.${namespace}.mkContainer {
localAddress = cfg.localAddress; name = "ntfy";
port = cfg.port; localAddress = cfg.localAddress;
bindMounts = bindMounts; port = cfg.port;
config = ntfyConfig; bindMounts = bindMounts;
}) { inherit lib; }; config = ntfyConfig;
})
{ inherit lib; };
fullConfig = { fullConfig = {
${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable { ${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable {
reverseProxies = [ reverseProxyConfig ]; reverseProxies = [ reverseProxyConfig ];
}; };
} // ntfyContainer; }
// ntfyContainer;
in in
with lib; with lib;
{ {

View File

@@ -1,196 +0,0 @@
# Tabby Web Service Module
This module provides a NixOS service for running the Tabby Web terminal application server.
## Features
- Systemd service with automatic startup
- User and group management
- Database migration on startup
- Configurable environment variables
- Security hardening
- Firewall integration
- Support for PostgreSQL and SQLite databases
- Social authentication configuration
## Basic Usage
```nix
{
mjallen.services.tabby-web = {
enable = true;
port = 9000;
openFirewall = true;
};
}
```
## Advanced Configuration
```nix
{
mjallen.services.tabby-web = {
enable = true;
port = 8080;
openFirewall = true;
# Use PostgreSQL instead of SQLite
databaseUrl = "postgresql://tabby:password@localhost:5432/tabby";
# Use S3 for app distribution storage
appDistStorage = "s3://my-bucket/tabby-dist";
# Configure social authentication
socialAuth = {
github = {
key = "your-github-oauth-key";
secret = "your-github-oauth-secret";
};
gitlab = {
key = "your-gitlab-oauth-key";
secret = "your-gitlab-oauth-secret";
};
};
# Performance tuning
workers = 8;
timeout = 300;
# Additional environment variables
extraEnvironment = {
DEBUG = "0";
LOG_LEVEL = "info";
};
};
}
```
## Configuration Options
### Basic Options
- `enable`: Enable the tabby-web service
- `port`: Port to run the server on (default: 9000)
- `openFirewall`: Whether to open the firewall port (default: false)
- `user`: User to run the service as (default: "tabby-web")
- `group`: Group to run the service as (default: "tabby-web")
- `dataDir`: Data directory (default: "/var/lib/tabby-web")
### Database Configuration
- `databaseUrl`: Database connection URL
- SQLite: `"sqlite:///var/lib/tabby-web/tabby.db"` (default)
- PostgreSQL: `"postgresql://user:password@host:port/database"`
### Storage Configuration
- `appDistStorage`: Storage URL for app distributions
- Local: `"file:///var/lib/tabby-web/dist"` (default)
- S3: `"s3://bucket-name/path"`
- GCS: `"gcs://bucket-name/path"`
### Social Authentication
Configure OAuth providers:
```nix
socialAuth = {
github = {
key = "oauth-key";
secret = "oauth-secret";
};
gitlab = {
key = "oauth-key";
secret = "oauth-secret";
};
microsoftGraph = {
key = "oauth-key";
secret = "oauth-secret";
};
googleOauth2 = {
key = "oauth-key";
secret = "oauth-secret";
};
};
```
### Performance Options
- `workers`: Number of gunicorn worker processes (default: 4)
- `timeout`: Worker timeout in seconds (default: 120)
### Additional Configuration
- `extraEnvironment`: Additional environment variables as an attribute set
## Service Management
```bash
# Start the service
sudo systemctl start tabby-web
# Enable automatic startup
sudo systemctl enable tabby-web
# Check service status
sudo systemctl status tabby-web
# View logs
sudo journalctl -u tabby-web -f
# Run management commands
sudo -u tabby-web tabby-web-manage migrate
sudo -u tabby-web tabby-web-manage add_version 1.0.156-nightly.2
```
## Security
The service runs with extensive security hardening:
- Dedicated user and group
- Restricted filesystem access
- No new privileges
- Protected system directories
- Private temporary directory
- Memory execution protection
- Namespace restrictions
## Database Setup
### PostgreSQL
If using PostgreSQL, ensure the database and user exist:
```sql
CREATE USER tabby WITH PASSWORD 'your-password';
CREATE DATABASE tabby OWNER tabby;
```
### SQLite
SQLite databases are created automatically in the data directory.
## Troubleshooting
1. **Service fails to start**: Check logs with `journalctl -u tabby-web`
2. **Database connection issues**: Verify database URL and credentials
3. **Permission errors**: Ensure data directory has correct ownership
4. **Port conflicts**: Check if another service is using the configured port
## Integration with Reverse Proxy
Example Nginx configuration:
```nginx
server {
listen 80;
server_name tabby.example.com;
location / {
proxy_pass http://localhost:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View File

@@ -1,121 +0,0 @@
{
config,
lib,
pkgs,
namespace,
...
}:
with lib;
let
cfg = config.${namespace}.services.tabby-web;
# Build environment variables from configuration
environmentVars = {
DATABASE_URL = cfg.databaseUrl;
APP_DIST_STORAGE = cfg.appDistStorage;
PORT = toString cfg.port;
}
// optionalAttrs (cfg.socialAuth.github.key != null) {
SOCIAL_AUTH_GITHUB_KEY = cfg.socialAuth.github.key;
}
// optionalAttrs (cfg.socialAuth.github.secret != null) {
SOCIAL_AUTH_GITHUB_SECRET = cfg.socialAuth.github.secret;
}
// optionalAttrs (cfg.socialAuth.gitlab.key != null) {
SOCIAL_AUTH_GITLAB_KEY = cfg.socialAuth.gitlab.key;
}
// optionalAttrs (cfg.socialAuth.gitlab.secret != null) {
SOCIAL_AUTH_GITLAB_SECRET = cfg.socialAuth.gitlab.secret;
}
// optionalAttrs (cfg.socialAuth.microsoftGraph.key != null) {
SOCIAL_AUTH_MICROSOFT_GRAPH_KEY = cfg.socialAuth.microsoftGraph.key;
}
// optionalAttrs (cfg.socialAuth.microsoftGraph.secret != null) {
SOCIAL_AUTH_MICROSOFT_GRAPH_SECRET = cfg.socialAuth.microsoftGraph.secret;
}
// optionalAttrs (cfg.socialAuth.googleOauth2.key != null) {
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = cfg.socialAuth.googleOauth2.key;
}
// optionalAttrs (cfg.socialAuth.googleOauth2.secret != null) {
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = cfg.socialAuth.googleOauth2.secret;
}
// cfg.extraEnvironment;
in
{
imports = [ ./options.nix ];
config = mkIf cfg.enable {
# Create user and group
users.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
home = cfg.dataDir;
createHome = true;
description = "Tabby Web service user";
};
users.groups.${cfg.group} = { };
# Ensure data directory exists with correct permissions
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.dataDir}/dist' 0750 ${cfg.user} ${cfg.group} - -"
];
# Create the systemd service
systemd.services.tabby-web = {
description = "Tabby Web Terminal Application Server";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ] ++ optional (hasPrefix "postgresql://" cfg.databaseUrl) "postgresql.service";
environment = environmentVars;
serviceConfig = {
Type = "exec";
User = cfg.user;
Group = cfg.group;
WorkingDirectory = cfg.dataDir;
# Use the tabby-web package from our custom packages
ExecStart = "${pkgs.${namespace}.tabby-web}/bin/tabby-web --workers ${toString cfg.workers} --timeout ${toString cfg.timeout}";
# Run database migrations before starting the service
ExecStartPre = "${pkgs.${namespace}.tabby-web}/bin/tabby-web-manage migrate";
# Security settings
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [ cfg.dataDir ];
PrivateTmp = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictSUIDSGID = true;
RestrictRealtime = true;
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
# Restart policy
Restart = "always";
RestartSec = "10s";
# Resource limits
LimitNOFILE = "65536";
};
# Ensure the service starts after database if using PostgreSQL
requisite = mkIf (hasPrefix "postgresql://" cfg.databaseUrl) [ "postgresql.service" ];
};
# Open firewall if requested
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};
# Add the tabby-web package to system packages
environment.systemPackages = [ pkgs.${namespace}.tabby-web ];
};
}

View File

@@ -1,45 +0,0 @@
# Example configuration for Tabby Web service
# Add this to your NixOS configuration to enable tabby-web
{
# Basic configuration - SQLite database, local storage
mjallen.services.tabby-web = {
enable = true;
port = 9000;
openFirewall = true;
};
# Advanced configuration example (commented out)
/*
mjallen.services.tabby-web = {
enable = true;
port = 8080;
openFirewall = true;
# Use PostgreSQL database
databaseUrl = "postgresql://tabby:password@localhost:5432/tabby";
# Use S3 for app distribution storage
appDistStorage = "s3://my-bucket/tabby-dist";
# Configure GitHub OAuth
socialAuth.github = {
key = "your-github-oauth-key";
secret = "your-github-oauth-secret";
};
# Performance tuning
workers = 8;
timeout = 300;
# Custom data directory
dataDir = "/srv/tabby-web";
# Additional environment variables
extraEnvironment = {
DEBUG = "0";
LOG_LEVEL = "info";
};
};
*/
}

View File

@@ -1,127 +0,0 @@
{ lib, namespace, ... }:
with lib;
{
options.${namespace}.services.tabby-web = {
enable = mkEnableOption "Tabby Web terminal application server";
port = mkOption {
type = types.port;
default = 9000;
description = "Port for tabby-web server";
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = "Whether to open firewall for tabby-web";
};
user = mkOption {
type = types.str;
default = "tabby-web";
description = "User to run tabby-web as";
};
group = mkOption {
type = types.str;
default = "tabby-web";
description = "Group to run tabby-web as";
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/tabby-web";
description = "Directory to store tabby-web data";
};
databaseUrl = mkOption {
type = types.str;
default = "sqlite:///var/lib/tabby-web/tabby.db";
description = "Database connection URL";
example = "postgresql://user:password@localhost:5432/tabby";
};
appDistStorage = mkOption {
type = types.str;
default = "file:///var/lib/tabby-web/dist";
description = "Storage URL for app distributions";
example = "s3://my-bucket/tabby-dist";
};
socialAuth = {
github = {
key = mkOption {
type = types.nullOr types.str;
default = null;
description = "GitHub OAuth key";
};
secret = mkOption {
type = types.nullOr types.str;
default = null;
description = "GitHub OAuth secret";
};
};
gitlab = {
key = mkOption {
type = types.nullOr types.str;
default = null;
description = "GitLab OAuth key";
};
secret = mkOption {
type = types.nullOr types.str;
default = null;
description = "GitLab OAuth secret";
};
};
microsoftGraph = {
key = mkOption {
type = types.nullOr types.str;
default = null;
description = "Microsoft Graph OAuth key";
};
secret = mkOption {
type = types.nullOr types.str;
default = null;
description = "Microsoft Graph OAuth secret";
};
};
googleOauth2 = {
key = mkOption {
type = types.nullOr types.str;
default = null;
description = "Google OAuth2 key";
};
secret = mkOption {
type = types.nullOr types.str;
default = null;
description = "Google OAuth2 secret";
};
};
};
extraEnvironment = mkOption {
type = types.attrsOf types.str;
default = { };
description = "Extra environment variables for tabby-web";
example = {
DEBUG = "1";
LOG_LEVEL = "info";
};
};
workers = mkOption {
type = types.ints.positive;
default = 4;
description = "Number of gunicorn worker processes";
};
timeout = mkOption {
type = types.ints.positive;
default = 120;
description = "Worker timeout in seconds";
};
};
}

View File

@@ -9,42 +9,46 @@ let
cfg = config.${namespace}.services.traefik; cfg = config.${namespace}.services.traefik;
# Process extraServices into service configurations # Process extraServices into service configurations
extraServiceConfigs = extraServiceConfigs =
let let
makeService = service: nameValuePair service.name { makeService =
loadBalancer.servers = [ service:
{ nameValuePair service.name {
url = service.url; loadBalancer.servers = [
} {
]; url = service.url;
}; }
];
};
in in
listToAttrs (map makeService cfg.extraServices); listToAttrs (map makeService cfg.extraServices);
# Process extraRouters into router configurations # Process extraRouters into router configurations
extraRouterConfigs = extraRouterConfigs =
let let
makeRouter = router: nameValuePair router.subdomain { makeRouter =
entryPoints = router.entryPoints; router:
rule = "Host(`${router.subdomain}.${domain}`)"; nameValuePair router.subdomain {
service = router.service; entryPoints = router.entryPoints;
middlewares = router.middlewares ++ [ rule = "Host(`${router.subdomain}.${domain}`)";
"crowdsec" service = router.service;
"whitelist-geoblock" middlewares = router.middlewares ++ [
]; "crowdsec"
tls.certResolver = "letsencrypt"; "whitelist-geoblock"
}; ];
tls.certResolver = "letsencrypt";
};
in in
listToAttrs (map makeRouter cfg.extraRouters); listToAttrs (map makeRouter cfg.extraRouters);
# Process reverseProxies into service and router configurations # Process reverseProxies into service and router configurations
reverseProxyServiceConfigs = reverseProxyServiceConfigs =
let let
makeService = reverseProxy: nameValuePair reverseProxy.service.name reverseProxy.service.config; makeService = reverseProxy: nameValuePair reverseProxy.service.name reverseProxy.service.config;
in in
listToAttrs (map makeService cfg.reverseProxies); listToAttrs (map makeService cfg.reverseProxies);
reverseProxyRouterConfigs = reverseProxyRouterConfigs =
let let
makeRouter = reverseProxy: nameValuePair reverseProxy.router.subdomain reverseProxy.router.config; makeRouter = reverseProxy: nameValuePair reverseProxy.router.subdomain reverseProxy.router.config;
in in
@@ -292,7 +296,7 @@ in
url = authUrl; url = authUrl;
} }
]; ];
authentik.loadBalancer.servers = [ authentik.loadBalancer.servers = [
{ {
url = authentikUrl; url = authentikUrl;
@@ -348,7 +352,9 @@ in
url = paperlessUrl; url = paperlessUrl;
} }
]; ];
} // extraServiceConfigs // reverseProxyServiceConfigs; }
// extraServiceConfigs
// reverseProxyServiceConfigs;
routers = { routers = {
auth = { auth = {
@@ -457,7 +463,9 @@ in
]; ];
tls.certResolver = "letsencrypt"; tls.certResolver = "letsencrypt";
}; };
} // extraRouterConfigs // reverseProxyRouterConfigs; }
// extraRouterConfigs
// reverseProxyRouterConfigs;
}; };
}; };
}; };

View File

@@ -1,34 +1,42 @@
{ lib, namespace, ... }: { lib, namespace, ... }:
with lib; with lib;
let let
inherit (lib.${namespace}) mkOpt mkBoolOpt; inherit (lib.${namespace}) mkOpt;
in in
{ {
options.${namespace}.services.traefik = { options.${namespace}.services.traefik = {
enable = mkEnableOption "enable traefik"; enable = mkEnableOption "enable traefik";
extraServices = mkOpt (types.listOf (types.submodule { extraServices = mkOpt (types.listOf (
options = { types.submodule {
name = mkOpt types.str "" "Name of the service"; options = {
url = mkOpt types.str "http://localhost:8080" "Url of the service"; name = mkOpt types.str "" "Name of the service";
}; url = mkOpt types.str "http://localhost:8080" "Url of the service";
})) [ ] "List of extra services to forward"; };
}
)) [ ] "List of extra services to forward";
extraRouters = mkOpt (types.listOf (types.submodule { extraRouters = mkOpt (types.listOf (
options = { types.submodule {
entryPoints = mkOpt (types.listOf types.str) [ "websecure" ] "Entrypoint"; options = {
subdomain = mkOpt types.str "" "subdomain of the service"; entryPoints = mkOpt (types.listOf types.str) [ "websecure" ] "Entrypoint";
service = mkOpt types.str "" "name of the service"; subdomain = mkOpt types.str "" "subdomain of the service";
middlewares = mkOpt (types.listOf (types.enum [ service = mkOpt types.str "" "name of the service";
"authentik" middlewares = mkOpt (types.listOf (
"onlyoffice-websocket" types.enum [
"crowdsec" "authentik"
"whitelist-geoblock" "onlyoffice-websocket"
"internal-ipallowlist" "crowdsec"
])) [ ] "List of middlewares to enable"; "whitelist-geoblock"
}; "internal-ipallowlist"
})) [ ] "List of extra services to forward"; ]
)) [ ] "List of middlewares to enable";
};
}
)) [ ] "List of extra services to forward";
reverseProxies = mkOpt (types.listOf types.attrs) [ ] "List of reverse proxy configurations from mkReverseProxy"; reverseProxies =
mkOpt (types.listOf types.attrs) [ ]
"List of reverse proxy configurations from mkReverseProxy";
}; };
} }

View File

@@ -23,14 +23,14 @@ in
uri = "tcp://0.0.0.0:10300"; uri = "tcp://0.0.0.0:10300";
}; };
piper = { # piper = {
package = pkgs.stable.wyoming-piper; # package = pkgs.stable.wyoming-piper;
servers.hass-piper = { # servers.hass-piper = {
enable = true; # enable = true;
voice = "en-us-ryan-high"; # voice = "en-us-ryan-high";
uri = "tcp://0.0.0.0:10200"; # uri = "tcp://0.0.0.0:10200";
}; # };
}; # };
}; };
}; };
} }

View File

@@ -1,44 +1,45 @@
{ lib {
, stdenv lib,
, fetchzip stdenv,
, autoPatchelfHook fetchzip,
, makeWrapper autoPatchelfHook,
# Core dependencies makeWrapper,
, alsa-lib # Core dependencies
, at-spi2-core alsa-lib,
, cairo at-spi2-core,
, cups cairo,
, dbus cups,
, expat dbus,
, fontconfig expat,
, freetype fontconfig,
, gdk-pixbuf freetype,
, glib gdk-pixbuf,
, gtk3 glib,
, libarchive gtk3,
, libdrm libarchive,
, libGL libdrm,
, libx11 libGL,
, libxcb libx11,
, libxext libxcb,
, libxkbcommon libxext,
, mesa libxkbcommon,
, nspr mesa,
, nss nspr,
, pango nss,
, systemd pango,
, xorg systemd,
, zlib xorg,
# Additional CEF/Chromium dependencies zlib,
, libnotify # Additional CEF/Chromium dependencies
, libpulseaudio libnotify,
, libuuid libpulseaudio,
, libva libuuid,
, pipewire libva,
, udev pipewire,
, wayland udev,
, jdk17 # for RuneLite/HDOS wayland,
, gtk2 ? null # for RS3 jdk17 # for RuneLite/HDOS
, # for RS3
}: }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
@@ -79,7 +80,7 @@ stdenv.mkDerivation rec {
nspr nspr
nss nss
pango pango
stdenv.cc.cc.lib # for libstdc++.so.6 stdenv.cc.cc.lib # for libstdc++.so.6
systemd systemd
zlib zlib
# Additional CEF/Chromium deps # Additional CEF/Chromium deps
@@ -90,7 +91,8 @@ stdenv.mkDerivation rec {
pipewire pipewire
udev udev
wayland wayland
] ++ (with xorg; [ ]
++ (with xorg; [
libXcomposite libXcomposite
libXcursor libXcursor
libXdamage libXdamage
@@ -119,10 +121,10 @@ stdenv.mkDerivation rec {
# Copy all files to lib directory # Copy all files to lib directory
cp -r ./bolt-launcher/* $out/lib/bolt-launcher/ cp -r ./bolt-launcher/* $out/lib/bolt-launcher/
# The main executable is 'bolt' (lowercase) # The main executable is 'bolt' (lowercase)
chmod +x $out/lib/bolt-launcher/bolt chmod +x $out/lib/bolt-launcher/bolt
# Also make chrome-sandbox executable (needed for CEF) # Also make chrome-sandbox executable (needed for CEF)
if [ -f $out/lib/bolt-launcher/chrome-sandbox ]; then if [ -f $out/lib/bolt-launcher/chrome-sandbox ]; then
chmod +x $out/lib/bolt-launcher/chrome-sandbox chmod +x $out/lib/bolt-launcher/chrome-sandbox
@@ -155,7 +157,7 @@ stdenv.mkDerivation rec {
# Make sure they can find each other # Make sure they can find each other
patchelf --set-rpath "$out/lib/bolt-launcher:${lib.makeLibraryPath buildInputs}" \ patchelf --set-rpath "$out/lib/bolt-launcher:${lib.makeLibraryPath buildInputs}" \
$out/lib/bolt-launcher/bolt || true $out/lib/bolt-launcher/bolt || true
# Patch the CEF libraries # Patch the CEF libraries
for lib in $out/lib/bolt-launcher/*.so*; do for lib in $out/lib/bolt-launcher/*.so*; do
if [ -f "$lib" ]; then if [ -f "$lib" ]; then
@@ -168,8 +170,11 @@ stdenv.mkDerivation rec {
description = "Free open-source third-party implementation of the Jagex Launcher"; description = "Free open-source third-party implementation of the Jagex Launcher";
homepage = "https://bolt.adamcake.com/"; homepage = "https://bolt.adamcake.com/";
license = licenses.agpl3Only; license = licenses.agpl3Only;
platforms = [ "x86_64-linux" "aarch64-linux" ]; platforms = [
"x86_64-linux"
"aarch64-linux"
];
maintainers = with maintainers; [ ]; maintainers = with maintainers; [ ];
sourceProvenance = with sourceTypes; [ binaryNativeCode ]; sourceProvenance = with sourceTypes; [ binaryNativeCode ];
}; };
} }

View File

@@ -20,7 +20,7 @@ buildHomeAssistantComponent rec {
beautifulsoup4 beautifulsoup4
]; ];
meta = { meta = {
changelog = "https://github.com/natekspencer/ha-bedjet/releases/tag/${version}"; changelog = "https://github.com/natekspencer/ha-bedjet/releases/tag/${version}";
description = "This project provides various entities to allow control of a BedJet device."; description = "This project provides various entities to allow control of a BedJet device.";
homepage = "https://github.com/natekspencer/ha-bedjet"; homepage = "https://github.com/natekspencer/ha-bedjet";

View File

@@ -7,4 +7,4 @@ fetchNextcloudApp {
sha256 = "sha256-F2hh/0RlLG2zcEatfd4fejRV0i2hMkwONM4P7nhdh18="; sha256 = "sha256-F2hh/0RlLG2zcEatfd4fejRV0i2hMkwONM4P7nhdh18=";
url = "https://github.com/nextcloud/photos/archive/refs/tags/v31.0.8.tar.gz"; url = "https://github.com/nextcloud/photos/archive/refs/tags/v31.0.8.tar.gz";
license = "agpl3Only"; license = "agpl3Only";
} }

View File

@@ -29,20 +29,22 @@ python3Packages.buildPythonPackage rec {
build-system = with python3Packages; [ poetry-core ]; build-system = with python3Packages; [ poetry-core ];
dependencies = with python3Packages; [ dependencies =
aiohttp with python3Packages;
aiomqtt [
async-timeout aiohttp
click aiomqtt
construct async-timeout
dacite click
paho-mqtt construct
pycryptodome dacite
pyrate-limiter paho-mqtt
vacuum-map-parser-roborock pycryptodome
pyshark pyrate-limiter
] vacuum-map-parser-roborock
++ lib.optionals stdenv.hostPlatform.isDarwin [ pycryptodomex ]; pyshark
]
++ lib.optionals stdenv.hostPlatform.isDarwin [ pycryptodomex ];
nativeCheckInputs = with python3Packages; [ nativeCheckInputs = with python3Packages; [
aioresponses aioresponses
@@ -61,4 +63,4 @@ python3Packages.buildPythonPackage rec {
maintainers = with maintainers; [ fab ]; maintainers = with maintainers; [ fab ];
mainProgram = "roborock"; mainProgram = "roborock";
}; };
} }

View File

@@ -35,4 +35,4 @@ python3Packages.buildPythonPackage rec {
license = with licenses; [ mit ]; license = with licenses; [ mit ];
maintainers = with maintainers; [ fab ]; maintainers = with maintainers; [ fab ];
}; };
} }

View File

@@ -53,7 +53,7 @@ stdenv.mkDerivation rec {
# Set up yarn # Set up yarn
export HOME=$TMPDIR export HOME=$TMPDIR
cd frontend cd frontend
# Fix up yarn.lock and set up offline cache # Fix up yarn.lock and set up offline cache
fixup-yarn-lock yarn.lock fixup-yarn-lock yarn.lock
yarn config --offline set yarn-offline-mirror ${yarnDeps} yarn config --offline set yarn-offline-mirror ${yarnDeps}
@@ -82,98 +82,107 @@ stdenv.mkDerivation rec {
''; '';
installPhase = '' installPhase = ''
runHook preInstall runHook preInstall
# Create output directories # Create output directories
mkdir -p $out/lib/tabby-web mkdir -p $out/lib/tabby-web
mkdir -p $out/bin mkdir -p $out/bin
mkdir -p $out/share/tabby-web mkdir -p $out/share/tabby-web
# Install backend # Install backend
cp -r backend/* $out/lib/tabby-web/ cp -r backend/* $out/lib/tabby-web/
# Install frontend build output # Install frontend build output
if [ -d frontend/dist ]; then if [ -d frontend/dist ]; then
cp -r frontend/dist/* $out/share/tabby-web/ cp -r frontend/dist/* $out/share/tabby-web/
elif [ -d frontend/build ]; then elif [ -d frontend/build ]; then
cp -r frontend/build/* $out/share/tabby-web/ cp -r frontend/build/* $out/share/tabby-web/
fi fi
# Create main executable wrapper # Create main executable wrapper
makeWrapper ${python3.withPackages (ps: with ps; [ gunicorn django ])}/bin/python $out/bin/tabby-web \ makeWrapper ${
--add-flags "-m gunicorn tabby_web.wsgi:application" \ python3.withPackages (
--set PYTHONPATH "$out/lib/tabby-web" \ ps: with ps; [
--set DJANGO_SETTINGS_MODULE "tabby_web.settings" \ gunicorn
--set STATIC_ROOT "$out/share/tabby-web" \ django
--run "cd $out/lib/tabby-web" \ ]
--run 'export DATABASE_URL="''${DATABASE_URL:-sqlite:///tmp/tabby-web.db}"' \ )
--run 'export APP_DIST_STORAGE="''${APP_DIST_STORAGE:-file:///tmp/tabby-web-dist}"' \ }/bin/python $out/bin/tabby-web \
--run 'export PORT="''${PORT:-9000}"' \ --add-flags "-m gunicorn tabby_web.wsgi:application" \
--add-flags '--bind "0.0.0.0:$PORT"' \ --set PYTHONPATH "$out/lib/tabby-web" \
--add-flags "--workers 4" \ --set DJANGO_SETTINGS_MODULE "tabby_web.settings" \
--add-flags "--timeout 120" --set STATIC_ROOT "$out/share/tabby-web" \
--run "cd $out/lib/tabby-web" \
--run 'export DATABASE_URL="''${DATABASE_URL:-sqlite:///tmp/tabby-web.db}"' \
--run 'export APP_DIST_STORAGE="''${APP_DIST_STORAGE:-file:///tmp/tabby-web-dist}"' \
--run 'export PORT="''${PORT:-9000}"' \
--add-flags '--bind "0.0.0.0:$PORT"' \
--add-flags "--workers 4" \
--add-flags "--timeout 120"
# Create Django management wrapper # Create Django management wrapper
makeWrapper ${python3.withPackages (ps: with ps; [ django ])}/bin/python $out/bin/tabby-web-manage \ makeWrapper ${
--add-flags "manage.py" \ python3.withPackages (ps: with ps; [ django ])
--set PYTHONPATH "$out/lib/tabby-web" \ }/bin/python $out/bin/tabby-web-manage \
--set DJANGO_SETTINGS_MODULE "tabby_web.settings" \ --add-flags "manage.py" \
--set STATIC_ROOT "$out/share/tabby-web" \ --set PYTHONPATH "$out/lib/tabby-web" \
--run "cd $out/lib/tabby-web" \ --set DJANGO_SETTINGS_MODULE "tabby_web.settings" \
--run 'export DATABASE_URL="''${DATABASE_URL:-sqlite:///tmp/tabby-web.db}"' \ --set STATIC_ROOT "$out/share/tabby-web" \
--run 'export APP_DIST_STORAGE="''${APP_DIST_STORAGE:-file:///tmp/tabby-web-dist}"' --run "cd $out/lib/tabby-web" \
--run 'export DATABASE_URL="''${DATABASE_URL:-sqlite:///tmp/tabby-web.db}"' \
--run 'export APP_DIST_STORAGE="''${APP_DIST_STORAGE:-file:///tmp/tabby-web-dist}"'
# Create a help script # Create a help script
cat > $out/bin/tabby-web-help << 'HELP_EOF' cat > $out/bin/tabby-web-help << 'HELP_EOF'
#!/bin/bash #!/bin/bash
cat << 'HELP' cat << 'HELP'
Tabby Web - Terminal application server Tabby Web - Terminal application server
Usage: Usage:
tabby-web Start the server tabby-web Start the server
tabby-web-manage <command> Run Django management commands tabby-web-manage <command> Run Django management commands
tabby-web-help Show this help tabby-web-help Show this help
Environment Variables: Environment Variables:
DATABASE_URL Database connection URL DATABASE_URL Database connection URL
Examples: sqlite:///path/to/db.sqlite Examples: sqlite:///path/to/db.sqlite
postgresql://user:pass@host:5432/dbname postgresql://user:pass@host:5432/dbname
APP_DIST_STORAGE Storage URL for app distributions APP_DIST_STORAGE Storage URL for app distributions
Examples: file:///path/to/storage Examples: file:///path/to/storage
s3://bucket-name/path s3://bucket-name/path
gcs://bucket-name/path gcs://bucket-name/path
PORT Server port (default: 9000) PORT Server port (default: 9000)
Social Authentication (optional): Social Authentication (optional):
SOCIAL_AUTH_GITHUB_KEY GitHub OAuth key SOCIAL_AUTH_GITHUB_KEY GitHub OAuth key
SOCIAL_AUTH_GITHUB_SECRET GitHub OAuth secret SOCIAL_AUTH_GITHUB_SECRET GitHub OAuth secret
SOCIAL_AUTH_GITLAB_KEY GitLab OAuth key SOCIAL_AUTH_GITLAB_KEY GitLab OAuth key
SOCIAL_AUTH_GITLAB_SECRET GitLab OAuth secret SOCIAL_AUTH_GITLAB_SECRET GitLab OAuth secret
SOCIAL_AUTH_MICROSOFT_GRAPH_KEY Microsoft Graph OAuth key SOCIAL_AUTH_MICROSOFT_GRAPH_KEY Microsoft Graph OAuth key
SOCIAL_AUTH_MICROSOFT_GRAPH_SECRET Microsoft Graph OAuth secret SOCIAL_AUTH_MICROSOFT_GRAPH_SECRET Microsoft Graph OAuth secret
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY Google OAuth2 key SOCIAL_AUTH_GOOGLE_OAUTH2_KEY Google OAuth2 key
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET Google OAuth2 secret SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET Google OAuth2 secret
Examples: Examples:
# Development with defaults # Development with defaults
tabby-web tabby-web
# Production with PostgreSQL # Production with PostgreSQL
DATABASE_URL="postgresql://user:pass@localhost:5432/tabby" tabby-web DATABASE_URL="postgresql://user:pass@localhost:5432/tabby" tabby-web
# Run migrations # Run migrations
tabby-web-manage migrate tabby-web-manage migrate
# Add app version # Add app version
tabby-web-manage add_version 1.0.156-nightly.2 tabby-web-manage add_version 1.0.156-nightly.2
HELP HELP
HELP_EOF HELP_EOF
chmod +x $out/bin/tabby-web-help chmod +x $out/bin/tabby-web-help
runHook postInstall runHook postInstall
''; '';
meta = with lib; { meta = with lib; {

View File

@@ -9,6 +9,7 @@ jallen-nas:
cloudflare-zone-api-token: ENC[AES256_GCM,data:N02jcaPLYVzOmo5omGvOKUw2MZg8/cVolRcw/pu+sFnV8IsrUFOjmA==,iv:NZ+OaNR5lmsXicYQ7QL9CBMhlm397VbqmIcmr6GGBWw=,tag:FOT0EzDDuJ/kKOArn8e/rA==,type:str] cloudflare-zone-api-token: ENC[AES256_GCM,data:N02jcaPLYVzOmo5omGvOKUw2MZg8/cVolRcw/pu+sFnV8IsrUFOjmA==,iv:NZ+OaNR5lmsXicYQ7QL9CBMhlm397VbqmIcmr6GGBWw=,tag:FOT0EzDDuJ/kKOArn8e/rA==,type:str]
cloudflare-api-key: ENC[AES256_GCM,data:SWCsa1YzUpl5aQmeVBzKjfkZdAfduX8pl5RKd+EP6pgyMCCc6Q==,iv:ccIzA1OzGyRnq8gxXAg4B3HHtKcvXhXKMWVuTs/PHLI=,tag:R9KrYDrAluTAyuv7DfYVWQ==,type:str] cloudflare-api-key: ENC[AES256_GCM,data:SWCsa1YzUpl5aQmeVBzKjfkZdAfduX8pl5RKd+EP6pgyMCCc6Q==,iv:ccIzA1OzGyRnq8gxXAg4B3HHtKcvXhXKMWVuTs/PHLI=,tag:R9KrYDrAluTAyuv7DfYVWQ==,type:str]
cloudflare-email: ENC[AES256_GCM,data:WCe6JlTQnv2PXYcySZNbZ5Lv,iv:qc+o+GEqdRm3U5qBqvH23HOah3Sa63QzqZyDXWozcqo=,tag:v8YY3jCoVC8h12wHTFjkIg==,type:str] cloudflare-email: ENC[AES256_GCM,data:WCe6JlTQnv2PXYcySZNbZ5Lv,iv:qc+o+GEqdRm3U5qBqvH23HOah3Sa63QzqZyDXWozcqo=,tag:v8YY3jCoVC8h12wHTFjkIg==,type:str]
crowdsec-capi: ENC[AES256_GCM,data:9T3e6CzJZOT1KAXlpG323oPmk9xsoVVWI/WYnhdmzyymj61LgNJKvA==,iv:NywJk/tkmIGR5jIgxpvheRBCrK64QytXAkr+40nn62M=,tag:XFeafjL/84r0fLa8UpjyjQ==,type:str]
collabora: ENC[AES256_GCM,data:tFbbm16DFMsxT0I1ogXTRwyTgkE=,iv:yzembXvJ9+DroplBUDiMPa/jn9pjpAI7f5oHSTaZedA=,tag:yprIBtaRIjaHW6nplLYYzQ==,type:str] collabora: ENC[AES256_GCM,data:tFbbm16DFMsxT0I1ogXTRwyTgkE=,iv:yzembXvJ9+DroplBUDiMPa/jn9pjpAI7f5oHSTaZedA=,tag:yprIBtaRIjaHW6nplLYYzQ==,type:str]
mariadb: mariadb:
root_pass: ENC[AES256_GCM,data:AmZ3lU/GM9lMAjchF4kvjkIZlYX0KZV7ov0dxtnDmg==,iv:9JQuHWcb3/lCR3gw4PFtzMKxk85GXzFV35NguJydUkk=,tag:MEvmiYhAYa1LUGTN9wm/3w==,type:str] root_pass: ENC[AES256_GCM,data:AmZ3lU/GM9lMAjchF4kvjkIZlYX0KZV7ov0dxtnDmg==,iv:9JQuHWcb3/lCR3gw4PFtzMKxk85GXzFV35NguJydUkk=,tag:MEvmiYhAYa1LUGTN9wm/3w==,type:str]
@@ -173,8 +174,8 @@ sops:
NXZkbVZyV0VtTzArOE1uU1JwMXZZN0EKLDU1x+rIWecDD9x//huoM2BM9NRSa4g1 NXZkbVZyV0VtTzArOE1uU1JwMXZZN0EKLDU1x+rIWecDD9x//huoM2BM9NRSa4g1
L5nodU/J0XsfB9z3kr7eY5LYSwsqGkAxI1cXJYZGHF+bozJjweyXTQ== L5nodU/J0XsfB9z3kr7eY5LYSwsqGkAxI1cXJYZGHF+bozJjweyXTQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-09-17T02:00:58Z" lastmodified: "2025-09-30T23:04:02Z"
mac: ENC[AES256_GCM,data:l1J7omMlj1orONfmsgvGG+DZvdv5GmsdvQmUqPml9EfE7gF0ZoioEaGfcWDLBfutH9VU/9cWLclNbDinqL63m7/tAvv/8LGVvJ0s1T8xbAbu7d95+hXH7rMVU6C15h+maXLV4wNlFJFlNp1cFmZzmJmG3scXPZQ1aJM33ol/hV0=,iv:67MnPLlTh1WO6c15hB4LkeKc91r8epS5MZWT6LCOVRk=,tag:msZ3iBU+poU3BRDtV6G5zg==,type:str] mac: ENC[AES256_GCM,data:rDWyDZSXNGs2q4epxCQBI5Mj8E5Dpen6F6cUU7NxTVlOI933Gi12bdpuFghrjEf2S1Lk0u/duOM07q2NJrsMOgVPws2f/jzcCzcpPeaUsrD1vkQUpCr2hUKNjSIEbrrtwanm2vbr0LMV0noxFluf68fpeph+/ZMe8eqJjxXWK+A=,iv:DvmxVM7m76trz5aXx/Llsrqmk53uTipo4SHaOdc2YUM=,tag:cIC5iF7+iaIjwLiYR22exg==,type:str]
pgp: pgp:
- created_at: "2025-08-24T02:21:34Z" - created_at: "2025-08-24T02:21:34Z"
enc: |- enc: |-

View File

@@ -1,7 +1,6 @@
# Hook home-manager to make a trampoline for each app we install # Hook home-manager to make a trampoline for each app we install
# from: https://github.com/nix-community/home-manager/issues/1341#issuecomment-1870352014 # from: https://github.com/nix-community/home-manager/issues/1341#issuecomment-1870352014
{ {
config,
lib, lib,
pkgs, pkgs,
... ...

View File

@@ -77,7 +77,7 @@
} }
]; ];
}; };
virtualisation = { virtualisation = {
docker.enable = false; docker.enable = false;
podman.enable = false; podman.enable = false;

View File

@@ -8,7 +8,7 @@
}: }:
{ {
imports = [ imports = [
# ./adguard.nix # ./adguard.nix
./boot.nix ./boot.nix
./sops.nix ./sops.nix
]; ];
@@ -93,7 +93,7 @@
device = "/dev/disk/bu-uuid/a85420cc-a026-4100-80d2-a999081d792a"; device = "/dev/disk/bu-uuid/a85420cc-a026-4100-80d2-a999081d792a";
fsType = "bcachefs"; fsType = "bcachefs";
options = [ options = [
"nofail" "nofail"
]; ];
}; };
} }

View File

@@ -1,4 +1,4 @@
{ namespace, ... }: { config, namespace, ... }:
{ {
${namespace} = { ${namespace} = {
services = { services = {
@@ -67,7 +67,7 @@
enable = true; enable = true;
port = 9898; port = 9898;
apiAddress = "10.0.1.3"; apiAddress = "10.0.1.3";
apiKey = "1daH89qmJ41r2Lpd9hvDw4sxtOAtBzaj3aKFOFqE"; apiKey = config.sops.secrets."jallen-nas/crowdsec-capi".path;
dataDir = "/media/nas/main/nix-app-data/crowdsec"; dataDir = "/media/nas/main/nix-app-data/crowdsec";
}; };

View File

@@ -249,9 +249,9 @@ in
mountPoint = "/media/nas/main"; mountPoint = "/media/nas/main";
options = [ options = [
"noauto" "noauto"
"nofail" "nofail"
# "x-systemd.mount-timeout=0" # "x-systemd.mount-timeout=0"
# "x-systemd.device-timeout=0" # "x-systemd.device-timeout=0"
]; ];
}; };

View File

@@ -94,6 +94,15 @@ in
restartUnits = [ "podman-collabora.service" ]; restartUnits = [ "podman-collabora.service" ];
}; };
# ------------------------------
# crowdsec
# ------------------------------
"jallen-nas/crowdsec-capi" = {
sopsFile = defaultSops;
restartUnits = [ "crowdsec.service" ];
};
# ------------------------------ # ------------------------------
# mariadb # TODO # mariadb # TODO
# ------------------------------ # ------------------------------