Compare commits
1 Commits
main
..
a6fac14f57
| Author | SHA1 | Date | |
|---|---|---|---|
| a6fac14f57 |
@@ -9,7 +9,7 @@ let
|
|||||||
flakePath = config.config-manager.flakePath;
|
flakePath = config.config-manager.flakePath;
|
||||||
nixosConfiguration = config.config-manager.nixosConfiguration;
|
nixosConfiguration = config.config-manager.nixosConfiguration;
|
||||||
|
|
||||||
nh = inputs.nh.packages."${pkgs.stdenv.hostPlatform.system}".default;
|
nh = inputs.nh.packages."${pkgs.system}".default;
|
||||||
|
|
||||||
config-manager =
|
config-manager =
|
||||||
if flakePath == "" then
|
if flakePath == "" then
|
||||||
|
|||||||
Generated
+40
-863
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
nh = {
|
nh = {
|
||||||
url = "github:viperML/nh/b00a24b39944efd4ec7944f02e0bd9113d991767";
|
url = "github:viperML/nh";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
nix-gc-env.url = "github:Julow/nix-gc-env";
|
nix-gc-env.url = "github:Julow/nix-gc-env";
|
||||||
@@ -32,26 +33,11 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
hyprland = {
|
|
||||||
url = "github:hyprwm/Hyprland";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
hyprland-contrib = {
|
hyprland-contrib = {
|
||||||
url = "github:hyprwm/contrib";
|
url = "github:hyprwm/contrib";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
nix-jetbrains-plugins = {
|
|
||||||
url = "github:nix-community/nix-jetbrains-plugins";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
naviterm = {
|
|
||||||
url = "gitlab:detoxify92/naviterm";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
pppdotpm-site = {
|
pppdotpm-site = {
|
||||||
url = "git+ssh://gitea@git.ppp.pm:1122/alex/ppp.pm-site.git?ref=main";
|
url = "git+ssh://gitea@git.ppp.pm:1122/alex/ppp.pm-site.git?ref=main";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -59,43 +45,6 @@
|
|||||||
|
|
||||||
whib-backend = {
|
whib-backend = {
|
||||||
url = "git+ssh://gitea@git.ppp.pm:1122/alex/whib.git?ref=master";
|
url = "git+ssh://gitea@git.ppp.pm:1122/alex/whib.git?ref=master";
|
||||||
# url = "path:/home/alex/code/own/whib";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
whib-frontend = {
|
|
||||||
url = "git+ssh://gitea@git.ppp.pm:1122/alex/whib-react.git?ref=master";
|
|
||||||
# url = "path:/home/alex/code/own/whib-react";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
komga-comictracker = {
|
|
||||||
url = "git+ssh://gitea@git.ppp.pm:1122/alex/komga-comictracker.git?ref=main";
|
|
||||||
# url = "path:/home/alex/code/own/komga-comictracker";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
komga-bookmanager = {
|
|
||||||
url = "git+ssh://gitea@git.ppp.pm:1122/alex/komga-bookmanager.git?ref=main";
|
|
||||||
# url = "path:/home/alex/code/own/komga-bookmanager";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
komga-reading-stats = {
|
|
||||||
url = "git+ssh://gitea@git.ppp.pm:1122/alex/komga-reading-stats.git?ref=main";
|
|
||||||
# url = "path:/home/alex/code/own/komga-reading-stats";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
puppy-tracker = {
|
|
||||||
url = "git+ssh://gitea@git.ppp.pm:1122/alex/puppy-tracker.git?ref=main";
|
|
||||||
# url = "path:/home/alex/code/puppy-tracker";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
solo-referee = {
|
|
||||||
url = "git+ssh://gitea@git.ppp.pm:1122/alex/solo-referee.git?ref=main";
|
|
||||||
# url = "path:/home/alex/code/solo-referee";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -151,7 +100,6 @@
|
|||||||
./hosts/tadpole/configuration.nix
|
./hosts/tadpole/configuration.nix
|
||||||
./hosts/tadpole/home.nix
|
./hosts/tadpole/home.nix
|
||||||
inputs.whib-backend.nixosModules.${system}.default
|
inputs.whib-backend.nixosModules.${system}.default
|
||||||
inputs.whib-frontend.nixosModules.${system}.default
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -167,7 +115,6 @@
|
|||||||
modules = [
|
modules = [
|
||||||
./hosts/test-vm/configuration.nix
|
./hosts/test-vm/configuration.nix
|
||||||
inputs.whib-backend.nixosModules.${system}.default
|
inputs.whib-backend.nixosModules.${system}.default
|
||||||
inputs.whib-frontend.nixosModules.${system}.default
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -179,7 +126,7 @@
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
${system}.default = pkgs.mkShell {
|
${system}.default = pkgs.mkShell {
|
||||||
packages = [ pkgs.nixfmt ];
|
packages = [ pkgs.nixfmt-rfc-style ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,13 +22,6 @@
|
|||||||
alsa.enable = true;
|
alsa.enable = true;
|
||||||
alsa.support32Bit = true;
|
alsa.support32Bit = true;
|
||||||
pulse.enable = true;
|
pulse.enable = true;
|
||||||
|
|
||||||
extraConfig.pipewire."90-hdmi-fix" = {
|
|
||||||
"context.properties" = {
|
|
||||||
"default.clock.rate" = 48000;
|
|
||||||
"default.clock.allowed-rates" = [ 48000 ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
hardware = {
|
hardware = {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
inputs.agenix.packages."${pkgs.stdenv.hostPlatform.system}".default
|
inputs.agenix.packages."${pkgs.system}".default
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,6 @@ in
|
|||||||
|
|
||||||
efi.canTouchEfiVariables = true;
|
efi.canTouchEfiVariables = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
extraModprobeConfig = ''
|
|
||||||
options snd-intel-dspcfg dsp_driver=1
|
|
||||||
options snd_hda_intel power_save=0 power_save_controller=N
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
{
|
{
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs.nethack
|
|
||||||
|
|
||||||
pkgs.moonlight-qt
|
pkgs.moonlight-qt
|
||||||
|
|
||||||
pkgs.pcsx2
|
pkgs.pcsx2
|
||||||
|
|||||||
@@ -4,3 +4,6 @@
|
|||||||
|
|
||||||
[url "git@github.com:"]
|
[url "git@github.com:"]
|
||||||
insteadOf = https://github.com/
|
insteadOf = https://github.com/
|
||||||
|
|
||||||
|
[url "git@codeberg.org:"]
|
||||||
|
insteadOf = https://codeberg.org/
|
||||||
|
|||||||
@@ -8,14 +8,6 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
desktopManager = {
|
|
||||||
gnome.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
displayManager = {
|
|
||||||
gdm.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
xserver = {
|
xserver = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
@@ -23,6 +15,14 @@
|
|||||||
layout = "se";
|
layout = "se";
|
||||||
variant = "";
|
variant = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
desktopManager = {
|
||||||
|
gnome.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
displayManager = {
|
||||||
|
gdm.enable = true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,40 +3,33 @@
|
|||||||
networking = {
|
networking = {
|
||||||
hostName = "backwards";
|
hostName = "backwards";
|
||||||
|
|
||||||
wireless.enable = false;
|
networkmanager.enable = false;
|
||||||
|
|
||||||
networkmanager = {
|
#wireless.networks are defined in the secret `wpa_supplicant.conf`
|
||||||
|
wireless = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
wifi.backend = "iwd";
|
secretsFile = config.age.secrets.wireless-network-secrets.path;
|
||||||
|
|
||||||
ensureProfiles = {
|
networks = {
|
||||||
environmentFiles = [
|
"w1-f1_5G" = {
|
||||||
config.age.secrets.wireless-network-secrets.path
|
pskRaw = "ext:w1-f1_psk";
|
||||||
];
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
profiles = {
|
defaultGateway = "192.168.50.1";
|
||||||
w1-f1_5G = {
|
nameservers = [ "1.1.1.1" ];
|
||||||
connection = {
|
interfaces = {
|
||||||
id = "w1-f1_5G";
|
wlp1s0 = {
|
||||||
type = "wifi";
|
useDHCP = false;
|
||||||
interface-name = "wlp1s0";
|
|
||||||
};
|
|
||||||
wifi = {
|
|
||||||
ssid = "w1-f1_5G";
|
|
||||||
mode = "infrastructure";
|
|
||||||
};
|
|
||||||
wifi-security = {
|
|
||||||
key-mgmt = "wpa-psk";
|
|
||||||
psk = "$w1_f1_psk";
|
|
||||||
};
|
|
||||||
ipv4 = {
|
ipv4 = {
|
||||||
method = "manual";
|
addresses = [
|
||||||
addresses = "192.168.50.202/24";
|
{
|
||||||
gateway = "192.168.50.1";
|
address = "192.168.50.202";
|
||||||
dns = "1.1.1.1";
|
prefixLength = 24;
|
||||||
};
|
}
|
||||||
};
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,22 +35,12 @@ in
|
|||||||
identityFile = "/home/alex/.ssh/alex.backwards-git.ppp.pm";
|
identityFile = "/home/alex/.ssh/alex.backwards-git.ppp.pm";
|
||||||
};
|
};
|
||||||
|
|
||||||
"*" = {
|
"codeberg.org" = {
|
||||||
forwardAgent = false;
|
hostname = "codeberg.org";
|
||||||
addKeysToAgent = "no";
|
identityFile = "/home/alex/.ssh/alex.backwards-codeberg.org";
|
||||||
compression = false;
|
|
||||||
serverAliveInterval = 0;
|
|
||||||
serverAliveCountMax = 3;
|
|
||||||
hashKnownHosts = false;
|
|
||||||
userKnownHostsFile = "~/.ssh/known_hosts";
|
|
||||||
controlMaster = "no";
|
|
||||||
controlPath = "~/.ssh/master-%r@%n:%p";
|
|
||||||
controlPersist = "no";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
home.packages = [ pkgs.sshfs ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.etc."ssh/authorized_keys_command" = {
|
environment.etc."ssh/authorized_keys_command" = {
|
||||||
@@ -131,6 +121,19 @@ in
|
|||||||
owner = "alex";
|
owner = "alex";
|
||||||
group = "users";
|
group = "users";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"alex.backwards-codeberg.org" = {
|
||||||
|
file = ../../../../secrets/backwards/alex.backwards-codeberg.org.age;
|
||||||
|
path = "/home/alex/.ssh/alex.backwards-codeberg.org";
|
||||||
|
owner = "alex";
|
||||||
|
group = "users";
|
||||||
|
};
|
||||||
|
"alex.backwards-codeberg.org.pub" = {
|
||||||
|
file = ../../../../secrets/backwards/alex.backwards-codeberg.org.pub.age;
|
||||||
|
path = "/home/alex/.ssh/alex.backwards-codeberg.org.pub";
|
||||||
|
owner = "alex";
|
||||||
|
group = "users";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,21 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ttrpg = {
|
||||||
|
path = "/home/alex/sync/reading-material/ttrpg";
|
||||||
|
devices = [
|
||||||
|
"phone"
|
||||||
|
"tablet"
|
||||||
|
"pinwheel"
|
||||||
|
];
|
||||||
|
versioning = {
|
||||||
|
type = "staggered";
|
||||||
|
params = {
|
||||||
|
maxAge = "2592000"; # 30 days
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
"phone-gps" = {
|
"phone-gps" = {
|
||||||
path = "/home/alex/sync/phone-gps";
|
path = "/home/alex/sync/phone-gps";
|
||||||
devices = [ "phone" ];
|
devices = [ "phone" ];
|
||||||
|
|||||||
@@ -23,8 +23,6 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.variables.EDITOR = "vim";
|
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
vim
|
vim
|
||||||
git
|
git
|
||||||
|
|||||||
@@ -79,16 +79,11 @@
|
|||||||
{ device = config.disko.devices.disk.root.device; }
|
{ device = config.disko.devices.disk.root.device; }
|
||||||
{ device = config.disko.devices.disk.disk1.device; }
|
{ device = config.disko.devices.disk.disk1.device; }
|
||||||
{ device = config.disko.devices.disk.disk2.device; }
|
{ device = config.disko.devices.disk.disk2.device; }
|
||||||
{ device = config.disko.devices.disk.disk3.device; }
|
|
||||||
{ device = config.disko.devices.disk.disk4.device; }
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.zfs.autoScrub.enable = true;
|
services.zfs.autoScrub.enable = true;
|
||||||
|
|
||||||
# Don't force-import the pool if it appears in use elsewhere; safer default in 26.11+.
|
|
||||||
boot.zfs.forceImportRoot = false;
|
|
||||||
|
|
||||||
networking.hostId = "0a9474e7"; # Required by ZFS
|
networking.hostId = "0a9474e7"; # Required by ZFS
|
||||||
disko.devices = {
|
disko.devices = {
|
||||||
disk = {
|
disk = {
|
||||||
@@ -154,39 +149,6 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
disk3 = {
|
|
||||||
type = "disk";
|
|
||||||
device = "/dev/disk/by-id/ata-TOSHIBA_MG10ACA20TE_85K2A0UCF4MJ";
|
|
||||||
content = {
|
|
||||||
type = "gpt";
|
|
||||||
partitions = {
|
|
||||||
zfs = {
|
|
||||||
size = "100%";
|
|
||||||
content = {
|
|
||||||
type = "zfs";
|
|
||||||
pool = "storage";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
disk4 = {
|
|
||||||
type = "disk";
|
|
||||||
device = "/dev/disk/by-id/ata-TOSHIBA_MG10ACA20TE_85K2A0V6F4MJ";
|
|
||||||
content = {
|
|
||||||
type = "gpt";
|
|
||||||
partitions = {
|
|
||||||
zfs = {
|
|
||||||
size = "100%";
|
|
||||||
content = {
|
|
||||||
type = "zfs";
|
|
||||||
pool = "storage";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
zpool = {
|
zpool = {
|
||||||
@@ -204,13 +166,6 @@
|
|||||||
"disk2"
|
"disk2"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
{
|
|
||||||
mode = "mirror";
|
|
||||||
members = [
|
|
||||||
"disk3"
|
|
||||||
"disk4"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,13 +26,6 @@
|
|||||||
boot.kernelModules = [ "kvm-intel" ];
|
boot.kernelModules = [ "kvm-intel" ];
|
||||||
boot.extraModulePackages = [ ];
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
swapDevices = [
|
|
||||||
{
|
|
||||||
device = "/swapfile";
|
|
||||||
size = 32 * 1024; # 32GB
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||||
# (the default) this is the recommended approach. When using systemd-networkd it's
|
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||||
# still possible to use this option, but it's recommended to use it in conjunction
|
# still possible to use this option, but it's recommended to use it in conjunction
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{ inputs, pkgs, ... }:
|
{ inputs, ... }:
|
||||||
{
|
{
|
||||||
imports = [ inputs.home-manager.nixosModules.home-manager ];
|
imports = [ inputs.home-manager.nixosModules.home-manager ];
|
||||||
|
|
||||||
@@ -13,11 +13,7 @@
|
|||||||
home.username = "alex";
|
home.username = "alex";
|
||||||
home.homeDirectory = "/home/alex";
|
home.homeDirectory = "/home/alex";
|
||||||
|
|
||||||
home.packages = [
|
home.packages = [ ];
|
||||||
pkgs.streamrip
|
|
||||||
pkgs.claude-code
|
|
||||||
pkgs.wl-clipboard
|
|
||||||
];
|
|
||||||
|
|
||||||
home.stateVersion = "24.11";
|
home.stateVersion = "24.11";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
inputs.agenix.packages."${pkgs.stdenv.hostPlatform.system}".default
|
inputs.agenix.packages."${pkgs.system}".default
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,6 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
config = lib.mkIf enabled {
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Audiobookshelf";
|
|
||||||
port = 8000;
|
|
||||||
description = "Audiobooks & podcasts";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
users.users.audiobookshelf = {
|
users.users.audiobookshelf = {
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
description = "audiobookshelf";
|
description = "audiobookshelf";
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{ lib, config, ... }:
|
||||||
|
let
|
||||||
|
enabled = config.mod.calibre-web.enable;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
mod.calibre-web = {
|
||||||
|
enable = lib.mkEnableOption "add calibre-web module";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf enabled {
|
||||||
|
services = {
|
||||||
|
calibre-web = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
user = "storage";
|
||||||
|
group = "storage";
|
||||||
|
|
||||||
|
listen = {
|
||||||
|
ip = "0.0.0.0";
|
||||||
|
port = 8083;
|
||||||
|
};
|
||||||
|
|
||||||
|
options = {
|
||||||
|
calibreLibrary = "/mnt/media/public/books";
|
||||||
|
enableBookUploading = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
{ config, ... }:
|
|
||||||
{
|
|
||||||
security.acme = {
|
|
||||||
acceptTerms = true;
|
|
||||||
|
|
||||||
defaults = {
|
|
||||||
email = "acme@ppp.pm";
|
|
||||||
};
|
|
||||||
|
|
||||||
certs = {
|
|
||||||
"ha.ppp.pm" = {
|
|
||||||
dnsProvider = "hetzner";
|
|
||||||
environmentFile = config.age.secrets.hetzner-dns.path;
|
|
||||||
group = "nginx";
|
|
||||||
|
|
||||||
extraLegoFlags = [
|
|
||||||
"--dns.resolvers=1.1.1.1:53,8.8.8.8:53"
|
|
||||||
"--dns.propagation-wait=60s" # Wait for 60 seconds for DNS propagation
|
|
||||||
"--dns-timeout=60"
|
|
||||||
"--http-timeout=60"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
"komga.ppp.pm" = {
|
|
||||||
dnsProvider = "hetzner";
|
|
||||||
environmentFile = config.age.secrets.hetzner-dns.path;
|
|
||||||
group = "nginx";
|
|
||||||
|
|
||||||
extraLegoFlags = [
|
|
||||||
"--dns.resolvers=1.1.1.1:53,8.8.8.8:53"
|
|
||||||
"--dns.propagation-wait=60s"
|
|
||||||
"--dns-timeout=60"
|
|
||||||
"--http-timeout=60"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
"romm.ppp.pm" = {
|
|
||||||
dnsProvider = "hetzner";
|
|
||||||
environmentFile = config.age.secrets.hetzner-dns.path;
|
|
||||||
group = "nginx";
|
|
||||||
|
|
||||||
extraLegoFlags = [
|
|
||||||
"--dns.resolvers=1.1.1.1:53,8.8.8.8:53"
|
|
||||||
"--dns.propagation-wait=60s"
|
|
||||||
"--dns-timeout=60"
|
|
||||||
"--http-timeout=60"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
"puppy.ppp.pm" = {
|
|
||||||
dnsProvider = "hetzner";
|
|
||||||
environmentFile = config.age.secrets.hetzner-dns.path;
|
|
||||||
group = "nginx";
|
|
||||||
|
|
||||||
extraLegoFlags = [
|
|
||||||
"--dns.resolvers=1.1.1.1:53,8.8.8.8:53"
|
|
||||||
"--dns.propagation-wait=60s"
|
|
||||||
"--dns-timeout=60"
|
|
||||||
"--http-timeout=60"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
age = {
|
|
||||||
secrets = {
|
|
||||||
"hetzner-dns".file = ../../../../secrets/manatee/hetzner-dns.age;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -12,21 +12,14 @@ in
|
|||||||
|
|
||||||
ssh.enable = true;
|
ssh.enable = true;
|
||||||
git.enable = true;
|
git.enable = true;
|
||||||
tmux.enable = true;
|
|
||||||
|
|
||||||
nginx.enable = true;
|
nginx.enable = true;
|
||||||
syncthing.enable = true;
|
syncthing.enable = true;
|
||||||
transmission.enable = true;
|
transmission.enable = true;
|
||||||
|
calibre-web.enable = true;
|
||||||
audiobookshelf.enable = true;
|
audiobookshelf.enable = true;
|
||||||
jellyfin.enable = true;
|
jellyfin.enable = true;
|
||||||
immich.enable = false;
|
immich.enable = true;
|
||||||
navidrome.enable = true;
|
|
||||||
komga.enable = true;
|
|
||||||
romm.enable = true;
|
|
||||||
homepage.enable = true;
|
|
||||||
disk-smart.enable = true;
|
|
||||||
puppy-tracker.enable = true;
|
|
||||||
solo-referee.enable = true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.disk-smart.enable;
|
|
||||||
|
|
||||||
disks = [
|
|
||||||
{ path = "/dev/disk/by-id/ata-ST8000VN004-3CP101_WWZ8QCG4"; name = "seagate_8tb_1"; label = "Seagate 8TB #1"; }
|
|
||||||
{ path = "/dev/disk/by-id/ata-ST8000VN004-3CP101_WWZ8QDJ5"; name = "seagate_8tb_2"; label = "Seagate 8TB #2"; }
|
|
||||||
{ path = "/dev/disk/by-id/ata-TOSHIBA_MG10ACA20TE_85K2A0UCF4MJ"; name = "toshiba_20tb_1"; label = "Toshiba 20TB #1"; }
|
|
||||||
{ path = "/dev/disk/by-id/ata-TOSHIBA_MG10ACA20TE_85K2A0V6F4MJ"; name = "toshiba_20tb_2"; label = "Toshiba 20TB #2"; }
|
|
||||||
];
|
|
||||||
|
|
||||||
outputDir = "/var/lib/disk-smart";
|
|
||||||
|
|
||||||
collectScript = pkgs.writeShellScript "disk-smart-collect" ''
|
|
||||||
set -euo pipefail
|
|
||||||
export PATH="${lib.makeBinPath [ pkgs.smartmontools pkgs.jq pkgs.coreutils ]}"
|
|
||||||
|
|
||||||
mkdir -p ${outputDir}
|
|
||||||
|
|
||||||
result="{"
|
|
||||||
|
|
||||||
${lib.concatMapStringsSep "\n" (disk: ''
|
|
||||||
raw=$(smartctl -j -A -H ${disk.path} 2>/dev/null || true)
|
|
||||||
|
|
||||||
temp=$(echo "$raw" | jq -r '.temperature.current // empty')
|
|
||||||
power_on=$(echo "$raw" | jq -r '.power_on_time.hours // empty')
|
|
||||||
smart_status=$(echo "$raw" | jq -r '.smart_status.passed // empty')
|
|
||||||
reallocated=$(echo "$raw" | jq -r '[.ata_smart_attributes.table[] | select(.name == "Reallocated_Sector_Ct")][0].raw.value // empty')
|
|
||||||
pending=$(echo "$raw" | jq -r '[.ata_smart_attributes.table[] | select(.name == "Current_Pending_Sector")][0].raw.value // empty')
|
|
||||||
|
|
||||||
result="$result\"${disk.name}\":{\"temperature\":$temp,\"power_on_hours\":$power_on,\"smart_passed\":$smart_status,\"reallocated_sectors\":$reallocated,\"pending_sectors\":$pending},"
|
|
||||||
'') disks}
|
|
||||||
|
|
||||||
# Remove trailing comma, close object
|
|
||||||
result="''${result%,}}"
|
|
||||||
|
|
||||||
echo "$result" | jq . > ${outputDir}/smart.json.tmp
|
|
||||||
mv ${outputDir}/smart.json.tmp ${outputDir}/smart.json
|
|
||||||
'';
|
|
||||||
|
|
||||||
indent = prefix: s:
|
|
||||||
lib.concatMapStringsSep "\n"
|
|
||||||
(line: if line == "" then line else prefix + line)
|
|
||||||
(lib.splitString "\n" s);
|
|
||||||
|
|
||||||
mkSensor = disk: ''
|
|
||||||
- name: "${disk.label} Temperature"
|
|
||||||
value_template: "{{ value_json.${disk.name}.temperature }}"
|
|
||||||
unit_of_measurement: "°C"
|
|
||||||
device_class: temperature
|
|
||||||
state_class: measurement
|
|
||||||
- name: "${disk.label} Power On Hours"
|
|
||||||
value_template: "{{ value_json.${disk.name}.power_on_hours }}"
|
|
||||||
unit_of_measurement: "h"
|
|
||||||
state_class: total_increasing
|
|
||||||
- name: "${disk.label} SMART Passed"
|
|
||||||
value_template: "{{ value_json.${disk.name}.smart_passed }}"
|
|
||||||
- name: "${disk.label} Reallocated Sectors"
|
|
||||||
value_template: "{{ value_json.${disk.name}.reallocated_sectors }}"
|
|
||||||
state_class: measurement
|
|
||||||
- name: "${disk.label} Pending Sectors"
|
|
||||||
value_template: "{{ value_json.${disk.name}.pending_sectors }}"
|
|
||||||
state_class: measurement
|
|
||||||
'';
|
|
||||||
|
|
||||||
sensorYaml = indent " " (lib.concatMapStrings mkSensor disks);
|
|
||||||
|
|
||||||
sectorEntities = lib.concatMap (disk: [
|
|
||||||
"sensor.${disk.name}_reallocated_sectors"
|
|
||||||
"sensor.${disk.name}_pending_sectors"
|
|
||||||
]) disks;
|
|
||||||
|
|
||||||
sectorEntitiesYaml = lib.concatMapStringsSep "\n"
|
|
||||||
(id: " - ${id}") sectorEntities;
|
|
||||||
|
|
||||||
smartPassedEntities = map (disk: "sensor.${disk.name}_smart_passed") disks;
|
|
||||||
|
|
||||||
smartPassedEntitiesYaml = lib.concatMapStringsSep "\n"
|
|
||||||
(id: " - ${id}") smartPassedEntities;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.disk-smart = {
|
|
||||||
enable = lib.mkEnableOption "Enable disk SMART monitoring module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
mod.home-assistant.extraConfig = ''
|
|
||||||
rest:
|
|
||||||
- resource: http://127.0.0.1:9633/smart.json
|
|
||||||
scan_interval: 60
|
|
||||||
sensor:
|
|
||||||
${sensorYaml}
|
|
||||||
automation disk_smart:
|
|
||||||
- alias: "Disk sector count increased"
|
|
||||||
trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id:
|
|
||||||
${sectorEntitiesYaml}
|
|
||||||
condition:
|
|
||||||
- condition: template
|
|
||||||
value_template: "{{ trigger.from_state.state | int(-1) >= 0 and trigger.to_state.state | int(0) > trigger.from_state.state | int(0) }}"
|
|
||||||
action:
|
|
||||||
- service: notify.mobile_app_pixel_9_pro
|
|
||||||
data:
|
|
||||||
title: "Disk SMART warning"
|
|
||||||
message: "{{ trigger.to_state.attributes.friendly_name }} increased from {{ trigger.from_state.state }} to {{ trigger.to_state.state }}"
|
|
||||||
- alias: "Disk SMART check failed"
|
|
||||||
trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id:
|
|
||||||
${smartPassedEntitiesYaml}
|
|
||||||
condition:
|
|
||||||
- condition: template
|
|
||||||
value_template: "{{ trigger.to_state.state | lower == 'false' }}"
|
|
||||||
action:
|
|
||||||
- service: notify.mobile_app_pixel_9_pro
|
|
||||||
data:
|
|
||||||
title: "Disk SMART FAILURE"
|
|
||||||
message: "{{ trigger.to_state.attributes.friendly_name }} reports SMART failure — drive is likely failing"
|
|
||||||
'';
|
|
||||||
|
|
||||||
systemd.services.disk-smart-collect = {
|
|
||||||
description = "Collect disk SMART data";
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
ExecStart = collectScript;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.timers.disk-smart-collect = {
|
|
||||||
description = "Periodically collect disk SMART data";
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
timerConfig = {
|
|
||||||
OnBootSec = "1min";
|
|
||||||
OnUnitActiveSec = "1min";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."127.0.0.1" = {
|
|
||||||
listen = [
|
|
||||||
{ addr = "127.0.0.1"; port = 9633; }
|
|
||||||
];
|
|
||||||
|
|
||||||
locations."= /smart.json" = {
|
|
||||||
alias = "${outputDir}/smart.json";
|
|
||||||
extraConfig = ''
|
|
||||||
default_type application/json;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,7 @@ in
|
|||||||
{ path = ./gitconfig; }
|
{ path = ./gitconfig; }
|
||||||
];
|
];
|
||||||
|
|
||||||
settings = {
|
extraConfig = {
|
||||||
rerere.enable = true;
|
rerere.enable = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,300 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
nginxEnabled = config.mod.nginx.enable;
|
|
||||||
cfg = config.mod.home-assistant;
|
|
||||||
|
|
||||||
configFile = pkgs.writeText "ha-configuration.yaml" ''
|
|
||||||
# Loads default set of integrations. Do not remove.
|
|
||||||
default_config:
|
|
||||||
|
|
||||||
http:
|
|
||||||
use_x_forwarded_for: true
|
|
||||||
trusted_proxies:
|
|
||||||
- 127.0.0.1
|
|
||||||
|
|
||||||
# Load frontend themes from the themes folder
|
|
||||||
frontend:
|
|
||||||
themes: !include_dir_merge_named themes
|
|
||||||
|
|
||||||
automation: !include automations.yaml
|
|
||||||
script: !include scripts.yaml
|
|
||||||
scene: !include scenes.yaml
|
|
||||||
|
|
||||||
recorder:
|
|
||||||
purge_keep_days: 365
|
|
||||||
|
|
||||||
alert:
|
|
||||||
fridge_door:
|
|
||||||
name: Fridge is open
|
|
||||||
done_message: Fride is closed
|
|
||||||
entity_id: binary_sensor.kyldorr
|
|
||||||
state: "on"
|
|
||||||
repeat: 2
|
|
||||||
skip_first: true
|
|
||||||
notifiers:
|
|
||||||
- mobile_app_pixel_9_pro
|
|
||||||
|
|
||||||
${cfg.extraConfig}'';
|
|
||||||
|
|
||||||
btResetScript = pkgs.writeShellScript "bt-reset" ''
|
|
||||||
set -euo pipefail
|
|
||||||
export PATH="${
|
|
||||||
lib.makeBinPath [
|
|
||||||
pkgs.bluez
|
|
||||||
pkgs.util-linux
|
|
||||||
pkgs.kmod
|
|
||||||
pkgs.gnugrep
|
|
||||||
pkgs.coreutils
|
|
||||||
]
|
|
||||||
}"
|
|
||||||
|
|
||||||
logger -t bt-reset "Starting Bluetooth adapter reset..."
|
|
||||||
|
|
||||||
# Exit early if the adapter is already present and running
|
|
||||||
if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
|
|
||||||
logger -t bt-reset "hci0 is already UP RUNNING — nothing to do"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If hci0 exists but isn't UP, try bringing it up
|
|
||||||
if hciconfig hci0 2>/dev/null; then
|
|
||||||
logger -t bt-reset "hci0 exists but not running — bringing it up"
|
|
||||||
hciconfig hci0 up || true
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
|
|
||||||
logger -t bt-reset "hci0 is UP now"
|
|
||||||
systemctl restart bluetooth.service
|
|
||||||
logger -t bt-reset "bluetooth.service restarted — done"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Hard reset: reload the btusb kernel module (works for USB adapters)
|
|
||||||
logger -t bt-reset "hci0 missing — reloading btusb module..."
|
|
||||||
modprobe -r btusb 2>/dev/null || true
|
|
||||||
sleep 3
|
|
||||||
modprobe btusb
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
if hciconfig hci0 2>/dev/null; then
|
|
||||||
hciconfig hci0 up
|
|
||||||
logger -t bt-reset "hci0 restored after module reload"
|
|
||||||
else
|
|
||||||
logger -t bt-reset "ERROR: hci0 not found after module reload"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Restart the bluetooth systemd service so bluetoothd picks up the adapter
|
|
||||||
systemctl restart bluetooth.service
|
|
||||||
logger -t bt-reset "bluetooth.service restarted — done"
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.home-assistant = {
|
|
||||||
extraConfig = lib.mkOption {
|
|
||||||
type = lib.types.lines;
|
|
||||||
default = "";
|
|
||||||
description = "Extra YAML to append to Home Assistant's configuration.yaml";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Home Assistant";
|
|
||||||
port = 8123;
|
|
||||||
description = "Home automation";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
hardware.bluetooth.enable = true;
|
|
||||||
|
|
||||||
# Give up and enter a `failed` state (visible in `systemctl --failed`) if the
|
|
||||||
# container restarts more than 5 times in 5 minutes, instead of crash-looping
|
|
||||||
# forever. The window is deliberately long: a slow crash-loop (~5s per attempt)
|
|
||||||
# stays under systemd's default 5-starts-per-10s limit and would otherwise
|
|
||||||
# retry indefinitely.
|
|
||||||
systemd.services.podman-homeassistant = {
|
|
||||||
startLimitIntervalSec = 300;
|
|
||||||
startLimitBurst = 5;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.oci-containers = {
|
|
||||||
backend = "podman";
|
|
||||||
|
|
||||||
containers.homeassistant = {
|
|
||||||
image = "ghcr.io/home-assistant/home-assistant:stable";
|
|
||||||
|
|
||||||
volumes = [
|
|
||||||
"/home/alex/.config/home-assistant:/config"
|
|
||||||
"${configFile}:/config/configuration.yaml:ro"
|
|
||||||
# Pass in bluetooth
|
|
||||||
"/run/dbus:/run/dbus:ro"
|
|
||||||
];
|
|
||||||
|
|
||||||
environment.TZ = "Europe/Stockholm";
|
|
||||||
|
|
||||||
extraOptions = [
|
|
||||||
"--network=host"
|
|
||||||
|
|
||||||
# Allows HA to perform low-level network operations (scan/reset adapter)
|
|
||||||
"--cap-add=NET_ADMIN"
|
|
||||||
"--cap-add=NET_RAW"
|
|
||||||
|
|
||||||
# Pass in Zigbee antenna
|
|
||||||
"--device=/dev/serial/by-id/usb-Nabu_Casa_ZBT-2_9C139EAAD464-if00:/dev/ttyACM0"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services = {
|
|
||||||
blueman.enable = true;
|
|
||||||
|
|
||||||
nginx = lib.mkIf nginxEnabled {
|
|
||||||
recommendedProxySettings = true;
|
|
||||||
|
|
||||||
virtualHosts."ha.ppp.pm" = {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEHost = "ha.ppp.pm";
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
proxy_buffering off;
|
|
||||||
'';
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8123";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Trigger reset via udev when hci0 disappears
|
|
||||||
udev.extraRules = ''
|
|
||||||
ACTION=="remove", SUBSYSTEM=="bluetooth", KERNEL=="hci0", \
|
|
||||||
TAG+="systemd", ENV{SYSTEMD_WANTS}+="bt-reset.service"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd = {
|
|
||||||
services = {
|
|
||||||
# Trigger reset on bluetoothd failure
|
|
||||||
bluetooth = {
|
|
||||||
unitConfig.OnFailure = [ "bt-reset.service" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
bt-reset = {
|
|
||||||
description = "Reset Bluetooth adapter";
|
|
||||||
after = [ "bluetooth.service" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
ExecStart = btResetScript;
|
|
||||||
|
|
||||||
Restart = "on-failure";
|
|
||||||
RestartSec = "10s";
|
|
||||||
StartLimitIntervalSec = "120";
|
|
||||||
StartLimitBurst = 3;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
timers.bt-reset = {
|
|
||||||
description = "Periodically reset Bluetooth adapter";
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
timerConfig = {
|
|
||||||
OnBootSec = "5min"; # first run 5 min after boot
|
|
||||||
OnUnitActiveSec = "4h"; # then every 4 hours
|
|
||||||
RandomizedDelaySec = "5min";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
user = {
|
|
||||||
timers = {
|
|
||||||
"update-hetzner-dns" = {
|
|
||||||
unitConfig = {
|
|
||||||
Description = "updates Hetzner DNS records";
|
|
||||||
};
|
|
||||||
|
|
||||||
timerConfig = {
|
|
||||||
Unit = "update-hetzner-dns.service";
|
|
||||||
OnCalendar = "*-*-* *:00/30:00";
|
|
||||||
Persistent = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services = {
|
|
||||||
"update-hetzner-dns" = {
|
|
||||||
unitConfig = {
|
|
||||||
Description = "updates Hetzner DNS records";
|
|
||||||
};
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "exec";
|
|
||||||
EnvironmentFile = config.age.secrets.hetzner-dns.path;
|
|
||||||
};
|
|
||||||
|
|
||||||
path = [
|
|
||||||
pkgs.curl
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.jq
|
|
||||||
];
|
|
||||||
|
|
||||||
script = ''
|
|
||||||
SUBDOMAINS="ha komga romm puppy"
|
|
||||||
INTERFACE="enp3s0"
|
|
||||||
|
|
||||||
CURRENT_IP=$(curl -s --fail --interface "$INTERFACE" ifconfig.me)
|
|
||||||
|
|
||||||
for SUBDOMAIN in $SUBDOMAINS; do
|
|
||||||
LAST_IP_FILE="/tmp/hetzner-dns-''${SUBDOMAIN}-ip"
|
|
||||||
|
|
||||||
LAST_IP=""
|
|
||||||
if [[ -f "$LAST_IP_FILE" ]]; then
|
|
||||||
LAST_IP=$(cat "$LAST_IP_FILE")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$CURRENT_IP" == "$LAST_IP" ]]; then
|
|
||||||
echo "$SUBDOMAIN: IP unchanged, NOOP update."
|
|
||||||
else
|
|
||||||
echo "$SUBDOMAIN: Updating IP"
|
|
||||||
|
|
||||||
JSON_BODY=$(jq -n --arg ip "$CURRENT_IP" '{records: [{value: $ip}]}')
|
|
||||||
|
|
||||||
curl \
|
|
||||||
--fail \
|
|
||||||
-X POST \
|
|
||||||
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "$JSON_BODY" \
|
|
||||||
"https://api.hetzner.cloud/v1/zones/ppp.pm/rrsets/''${SUBDOMAIN}/A/actions/set_records" \
|
|
||||||
&& echo $CURRENT_IP > $LAST_IP_FILE
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
age = {
|
|
||||||
secrets = {
|
|
||||||
"hetzner-dns" = {
|
|
||||||
file = ../../../../secrets/manatee/hetzner-dns.age;
|
|
||||||
owner = "alex";
|
|
||||||
group = "users";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.homepage.enable;
|
|
||||||
nginxEnabled = config.mod.nginx.enable;
|
|
||||||
services = config.mod.homepage.services;
|
|
||||||
|
|
||||||
serviceToCard =
|
|
||||||
svc:
|
|
||||||
let
|
|
||||||
href = if svc.url != null then svc.url else "http://manatee:${toString svc.port}";
|
|
||||||
in
|
|
||||||
''
|
|
||||||
<a class="card" href="${href}">
|
|
||||||
<div class="name">${svc.name}</div>
|
|
||||||
<div class="desc">${svc.description}</div>
|
|
||||||
<div class="port">:${toString svc.port}</div>
|
|
||||||
</a>
|
|
||||||
'';
|
|
||||||
|
|
||||||
page = pkgs.writeTextDir "index.html" ''
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>manatee</title>
|
|
||||||
<style>
|
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
||||||
background: #1a1b26;
|
|
||||||
color: #c0caf5;
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 3rem 1.5rem;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #7aa2f7;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
||||||
gap: 1rem;
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
display: block;
|
|
||||||
background: #24283b;
|
|
||||||
border: 1px solid #414868;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.25rem;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
transition: border-color 0.15s;
|
|
||||||
}
|
|
||||||
.card:hover { border-color: #7aa2f7; }
|
|
||||||
.name { font-size: 1.1rem; font-weight: 600; color: #c0caf5; }
|
|
||||||
.desc { font-size: 0.85rem; color: #565f89; margin-top: 0.35rem; }
|
|
||||||
.port { font-size: 0.8rem; color: #414868; margin-top: 0.5rem; font-family: monospace; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>manatee</h1>
|
|
||||||
<div class="grid">
|
|
||||||
${lib.concatMapStrings serviceToCard services}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.homepage = {
|
|
||||||
enable = lib.mkEnableOption "Enable homepage module";
|
|
||||||
services = lib.mkOption {
|
|
||||||
type = lib.types.listOf (
|
|
||||||
lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
name = lib.mkOption { type = lib.types.str; };
|
|
||||||
port = lib.mkOption { type = lib.types.port; };
|
|
||||||
description = lib.mkOption { type = lib.types.str; };
|
|
||||||
url = lib.mkOption {
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
description = "Link target for the card; defaults to http://manatee:<port>.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
default = [ ];
|
|
||||||
description = "Services to display on the homepage";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf (enabled && nginxEnabled) {
|
|
||||||
services.nginx.virtualHosts."homepage" = {
|
|
||||||
listen = [
|
|
||||||
{
|
|
||||||
addr = "0.0.0.0";
|
|
||||||
port = 9999;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
root = page;
|
|
||||||
locations."/" = {
|
|
||||||
index = "index.html";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 9999 ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -10,14 +10,6 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
config = lib.mkIf enabled {
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Immich";
|
|
||||||
port = 2283;
|
|
||||||
description = "Photo library";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
users.users.immich = {
|
users.users.immich = {
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
group = "storage";
|
group = "storage";
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ in
|
|||||||
extraPackages = [
|
extraPackages = [
|
||||||
pkgs.intel-media-driver # Modern Intel VA-API driver (needed for N305)
|
pkgs.intel-media-driver # Modern Intel VA-API driver (needed for N305)
|
||||||
pkgs.libvdpau-va-gl # VDPAU backend for VA-API GLX interop
|
pkgs.libvdpau-va-gl # VDPAU backend for VA-API GLX interop
|
||||||
pkgs.intel-compute-runtime # OpenCL support
|
|
||||||
];
|
];
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -47,18 +46,6 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Jellyfin";
|
|
||||||
port = 8096;
|
|
||||||
description = "Media streaming";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
networking = {
|
|
||||||
firewall.allowedTCPPorts = [ 8096 ];
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
pkgs.jellyfin
|
pkgs.jellyfin
|
||||||
pkgs.jellyfin-web
|
pkgs.jellyfin-web
|
||||||
|
|||||||
@@ -1,153 +0,0 @@
|
|||||||
{
|
|
||||||
inputs,
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.komga.enable;
|
|
||||||
nginxEnabled = config.mod.nginx.enable;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.komga = {
|
|
||||||
enable = lib.mkEnableOption "Enable komga module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
imports = [
|
|
||||||
inputs.komga-comictracker.nixosModules.default
|
|
||||||
inputs.komga-bookmanager.nixosModules.default
|
|
||||||
inputs.komga-reading-stats.nixosModules.default
|
|
||||||
];
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Komga";
|
|
||||||
port = 8002;
|
|
||||||
description = "Comic library";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "Komga Reader";
|
|
||||||
port = 8888;
|
|
||||||
description = "Comic reader";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "Komga Book Manager";
|
|
||||||
port = 8686;
|
|
||||||
description = "Book manager";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "Komga Reading Stats";
|
|
||||||
port = 8787;
|
|
||||||
description = "Reading stats";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
users.users.komga = {
|
|
||||||
isSystemUser = true;
|
|
||||||
group = "storage";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.komga = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
user = "komga";
|
|
||||||
group = "storage";
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
server.port = 8002;
|
|
||||||
komga."cors.allowed-origins" = [
|
|
||||||
"http://manatee:8888"
|
|
||||||
"https://komga.ppp.pm"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
openFirewall = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx = lib.mkIf nginxEnabled {
|
|
||||||
virtualHosts."komga-reader" = {
|
|
||||||
listen = [
|
|
||||||
{
|
|
||||||
addr = "0.0.0.0";
|
|
||||||
port = 8888;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
root = (pkgs.writeTextDir "komga-reader.html" (builtins.readFile ./komga-reader.html));
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
index = "komga-reader.html";
|
|
||||||
tryFiles = "$uri $uri/ /komga-reader.html";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualHosts."komga.ppp.pm" = {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEHost = "komga.ppp.pm";
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8002";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 8888 ];
|
|
||||||
|
|
||||||
programs.comictracker = {
|
|
||||||
enable = true;
|
|
||||||
komgaUrl = "http://127.0.0.1:8002";
|
|
||||||
komgaLibraryId = "0NVZH5AK3RPE1";
|
|
||||||
secretsFile = config.age.secrets.komga-comicbooktracker-credentials.path;
|
|
||||||
};
|
|
||||||
|
|
||||||
services.komga-book-manager = {
|
|
||||||
enable = true;
|
|
||||||
port = 8686;
|
|
||||||
group = "storage";
|
|
||||||
komgaUrl = "http://127.0.0.1:8002";
|
|
||||||
credentialsFile = config.age.secrets.komga-bookmanager-credentials.path;
|
|
||||||
libraryRoot = "/mnt/media/public/books";
|
|
||||||
libraryId = "0PNE1NEPY6995";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.komga-reading-stats = {
|
|
||||||
enable = true;
|
|
||||||
host = "0.0.0.0";
|
|
||||||
port = 8787;
|
|
||||||
origin = "http://manatee:8787";
|
|
||||||
|
|
||||||
anthropicApiKeyFile = config.age.secrets.komga-reading-stats-claude-api-key.path;
|
|
||||||
claudeModel = "claude-sonnet-4-6";
|
|
||||||
komga = {
|
|
||||||
url = "http://127.0.0.1:8002";
|
|
||||||
apiKeyFile = config.age.secrets.komga-reading-stats-komga-api-key.path;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
age.secrets = {
|
|
||||||
"komga-comicbooktracker-credentials" = {
|
|
||||||
file = ../../../../secrets/manatee/komga-comicbooktracker-credentials.age;
|
|
||||||
owner = "alex";
|
|
||||||
group = "users";
|
|
||||||
};
|
|
||||||
|
|
||||||
"komga-bookmanager-credentials" = {
|
|
||||||
file = ../../../../secrets/manatee/komga-bookmanager-credentials.age;
|
|
||||||
owner = "alex";
|
|
||||||
group = "users";
|
|
||||||
};
|
|
||||||
|
|
||||||
"komga-reading-stats-claude-api-key" = {
|
|
||||||
file = ../../../../secrets/manatee/komga-reading-stats-claude-api-key.age;
|
|
||||||
};
|
|
||||||
|
|
||||||
"komga-reading-stats-komga-api-key" = {
|
|
||||||
file = ../../../../secrets/manatee/komga-reading-stats-komga-api-key.age;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
navidromeEnabled = config.mod.navidrome.enable;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.navidrome = {
|
|
||||||
enable = lib.mkEnableOption "Enable navidrome module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
mod.homepage.services = lib.mkIf navidromeEnabled [
|
|
||||||
{
|
|
||||||
name = "Navidrome";
|
|
||||||
port = 4533;
|
|
||||||
description = "Music streaming";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
services = lib.mkIf navidromeEnabled {
|
|
||||||
navidrome = {
|
|
||||||
enable = true;
|
|
||||||
openFirewall = true;
|
|
||||||
|
|
||||||
user = "navidrome";
|
|
||||||
group = "storage";
|
|
||||||
settings = {
|
|
||||||
Port = 4533;
|
|
||||||
Address = "0.0.0.0";
|
|
||||||
MusicFolder = "/mnt/media/public/music";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
let
|
|
||||||
hostAddress = "192.168.50.203";
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
networking = {
|
networking = {
|
||||||
hostName = "manatee";
|
hostName = "manatee";
|
||||||
|
|
||||||
# Required for asymmetric routing (sending replies out a different interface
|
|
||||||
# than the default route). Without this, the kernel drops the return traffic.
|
|
||||||
firewall.checkReversePath = "loose";
|
|
||||||
|
|
||||||
defaultGateway = "192.168.50.1";
|
defaultGateway = "192.168.50.1";
|
||||||
nameservers = [ "1.1.1.1" ];
|
nameservers = [ "1.1.1.1" ];
|
||||||
interfaces = {
|
interfaces = {
|
||||||
@@ -18,33 +11,12 @@ in
|
|||||||
ipv4 = {
|
ipv4 = {
|
||||||
addresses = [
|
addresses = [
|
||||||
{
|
{
|
||||||
address = hostAddress;
|
address = "192.168.50.203";
|
||||||
prefixLength = 24;
|
prefixLength = 24;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
ipv4.routes = [
|
|
||||||
{
|
|
||||||
address = "0.0.0.0";
|
|
||||||
prefixLength = 0;
|
|
||||||
via = "192.168.50.1"; # Router
|
|
||||||
options = {
|
|
||||||
table = "100";
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
localCommands = ''
|
|
||||||
# Ensure local LAN traffic uses the main table, e.g. responds to the local machine
|
|
||||||
ip rule list | grep -q "192.168.50.0/24 lookup main" || \
|
|
||||||
ip rule add to 192.168.50.0/24 lookup main priority 4999
|
|
||||||
|
|
||||||
# All other traffic from this IP uses Table 100 (e.g. responds to router and back out)
|
|
||||||
ip rule list | grep -q "from ${hostAddress} lookup 100" || \
|
|
||||||
ip rule add from ${hostAddress} lookup 100 priority 5000
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,5 @@ in
|
|||||||
recommendedTlsSettings = true;
|
recommendedTlsSettings = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
networking = {
|
|
||||||
firewall = {
|
|
||||||
allowedTCPPorts = [ 443 ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
{
|
|
||||||
inputs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.puppy-tracker.enable;
|
|
||||||
nginxEnabled = config.mod.nginx.enable;
|
|
||||||
port = 8089;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.puppy-tracker = {
|
|
||||||
enable = lib.mkEnableOption "Enable puppy-tracker module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
imports = [
|
|
||||||
inputs.puppy-tracker.nixosModules.default
|
|
||||||
];
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Puppy Tracker";
|
|
||||||
port = port;
|
|
||||||
description = "Sleep, meals, pees, poos";
|
|
||||||
# Login needs HTTPS (Secure cookies), so link to the public vhost.
|
|
||||||
url = "https://puppy.ppp.pm";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
services.puppy-tracker = {
|
|
||||||
enable = true;
|
|
||||||
inherit port;
|
|
||||||
openFirewall = true;
|
|
||||||
# Served publicly over HTTPS via the nginx vhost below.
|
|
||||||
secureCookies = true;
|
|
||||||
# Shared registration secret; the file holds `PUPPY_INVITE_CODE=...`.
|
|
||||||
inviteCodeFile = config.age.secrets."puppy-tracker-invite-code".path;
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx = lib.mkIf nginxEnabled {
|
|
||||||
virtualHosts."puppy.ppp.pm" = {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEHost = "puppy.ppp.pm";
|
|
||||||
|
|
||||||
# Photo uploads are up to 15 MB; give nginx headroom over its 1 MB default.
|
|
||||||
extraConfig = ''
|
|
||||||
client_max_body_size 20m;
|
|
||||||
'';
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:${toString port}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
age.secrets."puppy-tracker-invite-code".file =
|
|
||||||
../../../../secrets/manatee/puppy-tracker-invite-code.age;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.romm.enable;
|
|
||||||
nginxEnabled = config.mod.nginx.enable;
|
|
||||||
|
|
||||||
configFile = pkgs.writeText "romm-config.yml" ''
|
|
||||||
filesystem:
|
|
||||||
skip_hash_calculation: false
|
|
||||||
exclude:
|
|
||||||
roms:
|
|
||||||
single_file:
|
|
||||||
extensions:
|
|
||||||
- xml
|
|
||||||
- txt
|
|
||||||
- nfo
|
|
||||||
- dat
|
|
||||||
- jpg
|
|
||||||
- png
|
|
||||||
names:
|
|
||||||
- '._*'
|
|
||||||
- 'Thumbs.db'
|
|
||||||
- '.DS_Store'
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Give up and enter a `failed` state (visible in `systemctl --failed`) if a
|
|
||||||
# container restarts more than 5 times in 5 minutes, instead of crash-looping
|
|
||||||
# forever. The window is deliberately long: a slow crash-loop (~5s per attempt)
|
|
||||||
# stays under systemd's default 5-starts-per-10s limit and would otherwise
|
|
||||||
# retry indefinitely.
|
|
||||||
crashLoopGuard = {
|
|
||||||
startLimitIntervalSec = 300;
|
|
||||||
startLimitBurst = 5;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.romm = {
|
|
||||||
enable = lib.mkEnableOption "Enable romm module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "RomM";
|
|
||||||
port = 8085;
|
|
||||||
description = "ROM library manager";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d /var/lib/romm 0755 root root -"
|
|
||||||
"d /var/lib/romm/db 0755 root root -"
|
|
||||||
"d /var/lib/romm/redis 0755 999 1000 -"
|
|
||||||
"d /var/lib/romm/resources 0755 root root -"
|
|
||||||
"d /var/lib/romm/assets 0755 root root -"
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.services.podman-romm = crashLoopGuard;
|
|
||||||
systemd.services.podman-romm-db = crashLoopGuard;
|
|
||||||
systemd.services.podman-romm-redis = crashLoopGuard;
|
|
||||||
|
|
||||||
systemd.services.romm-net = {
|
|
||||||
description = "Create Podman network for RomM";
|
|
||||||
after = [ "podman.service" ];
|
|
||||||
requires = [ "podman.service" ];
|
|
||||||
before = [
|
|
||||||
"podman-romm.service"
|
|
||||||
"podman-romm-db.service"
|
|
||||||
"podman-romm-redis.service"
|
|
||||||
];
|
|
||||||
requiredBy = [
|
|
||||||
"podman-romm.service"
|
|
||||||
"podman-romm-db.service"
|
|
||||||
"podman-romm-redis.service"
|
|
||||||
];
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
ExecStart = pkgs.writeShellScript "romm-net-create" ''
|
|
||||||
${pkgs.podman}/bin/podman network exists romm-net \
|
|
||||||
|| ${pkgs.podman}/bin/podman network create romm-net
|
|
||||||
'';
|
|
||||||
ExecStop = "${pkgs.podman}/bin/podman network rm -f romm-net";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.oci-containers = {
|
|
||||||
backend = "podman";
|
|
||||||
|
|
||||||
containers.romm-db = {
|
|
||||||
image = "mariadb:latest";
|
|
||||||
|
|
||||||
environment = {
|
|
||||||
MARIADB_DATABASE = "romm";
|
|
||||||
MARIADB_USER = "romm";
|
|
||||||
};
|
|
||||||
|
|
||||||
environmentFiles = [
|
|
||||||
config.age.secrets.romm-db-password.path
|
|
||||||
];
|
|
||||||
|
|
||||||
volumes = [
|
|
||||||
"/var/lib/romm/db:/var/lib/mysql"
|
|
||||||
];
|
|
||||||
|
|
||||||
extraOptions = [
|
|
||||||
"--network=romm-net"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
containers.romm-redis = {
|
|
||||||
image = "redis:alpine";
|
|
||||||
|
|
||||||
volumes = [
|
|
||||||
"/var/lib/romm/redis:/data"
|
|
||||||
];
|
|
||||||
|
|
||||||
extraOptions = [
|
|
||||||
"--network=romm-net"
|
|
||||||
"--user=root"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
containers.romm = {
|
|
||||||
image = "rommapp/romm:latest";
|
|
||||||
|
|
||||||
dependsOn = [
|
|
||||||
"romm-db"
|
|
||||||
"romm-redis"
|
|
||||||
];
|
|
||||||
|
|
||||||
environment = {
|
|
||||||
DB_HOST = "romm-db";
|
|
||||||
DB_PORT = "3306";
|
|
||||||
DB_NAME = "romm";
|
|
||||||
DB_USER = "romm";
|
|
||||||
REDIS_HOST = "romm-redis";
|
|
||||||
REDIS_PORT = "6379";
|
|
||||||
ROMM_AUTH_ENABLED = "true";
|
|
||||||
};
|
|
||||||
|
|
||||||
environmentFiles = [
|
|
||||||
config.age.secrets.romm-auth-secret-key.path
|
|
||||||
config.age.secrets.romm-db-password.path
|
|
||||||
config.age.secrets.romm-metadata-api-keys.path
|
|
||||||
];
|
|
||||||
|
|
||||||
ports = [
|
|
||||||
"127.0.0.1:8086:8080"
|
|
||||||
];
|
|
||||||
|
|
||||||
volumes = [
|
|
||||||
"${configFile}:/romm/config/config.yml:ro"
|
|
||||||
"/mnt/media/public/games:/romm/library"
|
|
||||||
"/var/lib/romm/resources:/romm/resources"
|
|
||||||
"/var/lib/romm/assets:/romm/assets"
|
|
||||||
];
|
|
||||||
|
|
||||||
extraOptions = [
|
|
||||||
"--network=romm-net"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx = lib.mkIf nginxEnabled {
|
|
||||||
virtualHosts."romm-local" = {
|
|
||||||
listen = [
|
|
||||||
{
|
|
||||||
addr = "0.0.0.0";
|
|
||||||
port = 8085;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
client_max_body_size 0;
|
|
||||||
'';
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8086";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualHosts."romm.ppp.pm" = {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEHost = "romm.ppp.pm";
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
client_max_body_size 0;
|
|
||||||
'';
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8086";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 8085 ];
|
|
||||||
|
|
||||||
age.secrets = {
|
|
||||||
"romm-auth-secret-key" = {
|
|
||||||
file = ../../../../secrets/manatee/romm-auth-secret-key.age;
|
|
||||||
owner = "root";
|
|
||||||
group = "root";
|
|
||||||
};
|
|
||||||
|
|
||||||
"romm-db-password" = {
|
|
||||||
file = ../../../../secrets/manatee/romm-db-password.age;
|
|
||||||
owner = "root";
|
|
||||||
group = "root";
|
|
||||||
};
|
|
||||||
|
|
||||||
"romm-metadata-api-keys" = {
|
|
||||||
file = ../../../../secrets/manatee/romm-metadata-api-keys.age;
|
|
||||||
owner = "root";
|
|
||||||
group = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
inputs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.solo-referee.enable;
|
|
||||||
port = 8090;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.solo-referee = {
|
|
||||||
enable = lib.mkEnableOption "Enable solo-referee module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
imports = [
|
|
||||||
inputs.solo-referee.nixosModules.default
|
|
||||||
];
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Solo Referee";
|
|
||||||
port = port;
|
|
||||||
description = "Solo-RPG tool with Claude as GM";
|
|
||||||
# url omitted → homepage links to http://manatee:${port}.
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
services.solo-referee = {
|
|
||||||
enable = true;
|
|
||||||
inherit port;
|
|
||||||
# Reachable on the LAN / Tailscale at manatee:${port}; no public vhost.
|
|
||||||
address = "0.0.0.0";
|
|
||||||
openFirewall = true;
|
|
||||||
# Anthropic API key kept out of the store; the file holds
|
|
||||||
# `ANTHROPIC_API_KEY=...`. Without it the offline stub GM runs.
|
|
||||||
apiKeyFile = config.age.secrets."solo-referee-api-key".path;
|
|
||||||
};
|
|
||||||
|
|
||||||
age.secrets."solo-referee-api-key".file =
|
|
||||||
../../../../secrets/manatee/solo-referee-api-key.age;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -21,26 +21,12 @@ in
|
|||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
programs.ssh = {
|
programs.ssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableDefaultConfig = false;
|
|
||||||
|
|
||||||
settings = {
|
matchBlocks = {
|
||||||
"git.ppp.pm" = {
|
"git.ppp.pm" = {
|
||||||
hostname = "git.ppp.pm";
|
hostname = "git.ppp.pm";
|
||||||
identityFile = "/home/alex/.ssh/alex.manatee-git.ppp.pm";
|
identityFile = "/home/alex/.ssh/alex.manatee-git.ppp.pm";
|
||||||
};
|
};
|
||||||
|
|
||||||
"*" = {
|
|
||||||
forwardAgent = false;
|
|
||||||
addKeysToAgent = "no";
|
|
||||||
compression = false;
|
|
||||||
serverAliveInterval = 0;
|
|
||||||
serverAliveCountMax = 3;
|
|
||||||
hashKnownHosts = false;
|
|
||||||
userKnownHostsFile = "~/.ssh/known_hosts";
|
|
||||||
controlMaster = "no";
|
|
||||||
controlPath = "~/.ssh/master-%r@%n:%p";
|
|
||||||
controlPersist = "no";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,14 +10,6 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
config = lib.mkIf enabled {
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Syncthing";
|
|
||||||
port = 8384;
|
|
||||||
description = "File sync";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
services.syncthing = {
|
services.syncthing = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.tmux.enable;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.tmux = {
|
|
||||||
enable = lib.mkEnableOption "enable tmux module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
home-manager.users.alex = {
|
|
||||||
programs.tmux = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
baseIndex = 1;
|
|
||||||
keyMode = "vi";
|
|
||||||
|
|
||||||
# Allow vi mode to be enabled instantly
|
|
||||||
escapeTime = 0;
|
|
||||||
|
|
||||||
plugins = [ pkgs.tmuxPlugins.sensible ];
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
set -g renumber-windows on
|
|
||||||
|
|
||||||
# https://old.reddit.com/r/tmux/comments/mesrci/tmux_2_doesnt_seem_to_use_256_colors/
|
|
||||||
set -g default-terminal "xterm-256color"
|
|
||||||
set -ga terminal-overrides ",*256col*:Tc"
|
|
||||||
set -ga terminal-overrides ',*:Ss=\E[%p1%d q:Se=\E[ q'
|
|
||||||
set-environment -g COLORTERM "truecolor"
|
|
||||||
|
|
||||||
set-option -g allow-rename off
|
|
||||||
|
|
||||||
# Remove date/time etc. on the right side
|
|
||||||
set -g status-right ""
|
|
||||||
|
|
||||||
bind r source-file ~/.config/tmux/tmux.conf \; display "Config reloaded"
|
|
||||||
|
|
||||||
# Remove accidental `suspend-client` triggers
|
|
||||||
unbind C-z
|
|
||||||
|
|
||||||
bind | split-window -h -c "#{pane_current_path}"
|
|
||||||
bind - split-window -v -c "#{pane_current_path}"
|
|
||||||
|
|
||||||
# Move panes shortcuts
|
|
||||||
bind h select-pane -L
|
|
||||||
bind j select-pane -D
|
|
||||||
bind k select-pane -U
|
|
||||||
bind l select-pane -R
|
|
||||||
|
|
||||||
# Resize panes
|
|
||||||
bind -r H resize-pane -L 5
|
|
||||||
bind -r J resize-pane -D 5
|
|
||||||
bind -r K resize-pane -U 5
|
|
||||||
bind -r L resize-pane -R 5
|
|
||||||
|
|
||||||
# Move windows
|
|
||||||
bind -r Left swap-window -t -1 \; select-window -t -1
|
|
||||||
bind -r Right swap-window -t +1 \; select-window -t +1
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -15,14 +15,6 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
config = lib.mkIf enabled {
|
||||||
mod.homepage.services = [
|
|
||||||
{
|
|
||||||
name = "Transmission";
|
|
||||||
port = 9091;
|
|
||||||
description = "Torrent client";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
transmission = {
|
transmission = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|||||||
@@ -17,10 +17,7 @@
|
|||||||
users.users.alex = {
|
users.users.alex = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
description = "alex";
|
description = "alex";
|
||||||
extraGroups = [
|
extraGroups = [ "wheel" ];
|
||||||
"wheel"
|
|
||||||
"networkmanager"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
|
|||||||
@@ -14,19 +14,17 @@
|
|||||||
home.homeDirectory = "/home/alex";
|
home.homeDirectory = "/home/alex";
|
||||||
|
|
||||||
home.packages = [
|
home.packages = [
|
||||||
inputs.whib-backend.packages.${pkgs.stdenv.hostPlatform.system}.whib-import
|
inputs.whib-backend.packages.${pkgs.system}.whib-import
|
||||||
# pkgs.beekeeper-studio
|
pkgs.beekeeper-studio
|
||||||
|
pkgs.bitwarden-desktop
|
||||||
pkgs.gimp
|
pkgs.gimp
|
||||||
pkgs.zip
|
pkgs.zip
|
||||||
pkgs.unzip
|
|
||||||
pkgs.unar
|
pkgs.unar
|
||||||
pkgs.jq
|
pkgs.jq
|
||||||
pkgs.dbeaver-bin
|
pkgs.dbeaver-bin
|
||||||
pkgs.htop
|
pkgs.htop
|
||||||
pkgs.onlyoffice-desktopeditors
|
pkgs.onlyoffice-bin
|
||||||
pkgs.wdisplays
|
pkgs.wdisplays
|
||||||
pkgs.vlc
|
|
||||||
pkgs.opencode
|
|
||||||
];
|
];
|
||||||
|
|
||||||
home.stateVersion = "23.05";
|
home.stateVersion = "23.05";
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
inputs.agenix.packages."${pkgs.stdenv.hostPlatform.system}".default
|
inputs.agenix.packages."${pkgs.system}".default
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.ai.enable;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.ai = {
|
|
||||||
enable = lib.mkEnableOption "enable ai module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
home-manager.users.alex = {
|
|
||||||
home.packages = [
|
|
||||||
pkgs.claude-code
|
|
||||||
];
|
|
||||||
|
|
||||||
programs.zsh.shellAliases = {
|
|
||||||
wclaude = "CLAUDE_CONFIG_DIR=$HOME/.claude-personal claude";
|
|
||||||
pclaude = "CLAUDE_CONFIG_DIR=$HOME/.claude-work claude";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -30,6 +30,12 @@ in
|
|||||||
# Low battery notification for bluetooth devices
|
# Low battery notification for bluetooth devices
|
||||||
systemd.user =
|
systemd.user =
|
||||||
let
|
let
|
||||||
|
trackpad = {
|
||||||
|
id = "battery_hid_a8o91o3doe5ofeo38_battery";
|
||||||
|
name = "trackpad";
|
||||||
|
threshold = "20";
|
||||||
|
};
|
||||||
|
|
||||||
headphones = {
|
headphones = {
|
||||||
id = "headset_dev_38_18_4C_18_A4_6E";
|
id = "headset_dev_38_18_4C_18_A4_6E";
|
||||||
name = "headphones";
|
name = "headphones";
|
||||||
@@ -59,6 +65,7 @@ in
|
|||||||
in
|
in
|
||||||
builtins.listToAttrs (
|
builtins.listToAttrs (
|
||||||
builtins.map mkTimer [
|
builtins.map mkTimer [
|
||||||
|
trackpad
|
||||||
headphones
|
headphones
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -112,6 +119,7 @@ in
|
|||||||
in
|
in
|
||||||
builtins.listToAttrs (
|
builtins.listToAttrs (
|
||||||
builtins.map mkService [
|
builtins.map mkService [
|
||||||
|
trackpad
|
||||||
headphones
|
headphones
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ in
|
|||||||
nix-index.enable = false;
|
nix-index.enable = false;
|
||||||
greetd.enable = true;
|
greetd.enable = true;
|
||||||
hyprland.enable = true;
|
hyprland.enable = true;
|
||||||
hyprlock.enable = true;
|
swaylock.enable = true;
|
||||||
physlock.enable = false;
|
physlock.enable = false;
|
||||||
|
|
||||||
power.enable = true;
|
power.enable = true;
|
||||||
@@ -33,7 +33,6 @@ in
|
|||||||
rust.enable = true;
|
rust.enable = true;
|
||||||
scala.enable = true;
|
scala.enable = true;
|
||||||
python.enable = true;
|
python.enable = true;
|
||||||
gleam.enable = true;
|
|
||||||
|
|
||||||
keyboard.enable = true;
|
keyboard.enable = true;
|
||||||
containers = {
|
containers = {
|
||||||
@@ -42,8 +41,6 @@ in
|
|||||||
};
|
};
|
||||||
vm.enable = true;
|
vm.enable = true;
|
||||||
scripts.enable = true;
|
scripts.enable = true;
|
||||||
|
|
||||||
ai.enable = true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
#+END_SRC
|
#+END_SRC
|
||||||
*** This makes emacsclient startup faster in TUI-mode
|
*** This makes emacsclient startup faster in TUI-mode
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(setq-default xterm-query-timeout 0.1)
|
(setq-default xterm-query-timeout nil)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
*** Disable startup messages
|
*** Disable startup messages
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -479,34 +479,7 @@ Setup prefix for keybindings.
|
|||||||
|
|
||||||
* Flycheck
|
* Flycheck
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(use-package flycheck
|
(use-package flycheck)
|
||||||
:preface
|
|
||||||
(defun mp-flycheck-eldoc (callback &rest _ignored)
|
|
||||||
"Print flycheck messages at point by calling CALLBACK."
|
|
||||||
(when-let ((flycheck-errors (and flycheck-mode (flycheck-overlay-errors-at (point)))))
|
|
||||||
(mapc
|
|
||||||
(lambda (err)
|
|
||||||
(funcall callback
|
|
||||||
(format "%s: %s"
|
|
||||||
(let ((level (flycheck-error-level err)))
|
|
||||||
(pcase level
|
|
||||||
('info (propertize "I" 'face 'flycheck-error-list-info))
|
|
||||||
('error (propertize "E" 'face 'flycheck-error-list-error))
|
|
||||||
('warning (propertize "W" 'face 'flycheck-error-list-warning))
|
|
||||||
(_ level)))
|
|
||||||
(flycheck-error-message err))
|
|
||||||
:thing (or (flycheck-error-id err)
|
|
||||||
(flycheck-error-group err))
|
|
||||||
:face 'font-lock-doc-face))
|
|
||||||
flycheck-errors)))
|
|
||||||
|
|
||||||
(defun mp-flycheck-prefer-eldoc ()
|
|
||||||
(add-hook 'eldoc-documentation-functions #'mp-flycheck-eldoc nil t)
|
|
||||||
(setq eldoc-documentation-strategy 'eldoc-documentation-compose-eagerly)
|
|
||||||
(setq flycheck-display-errors-function nil)
|
|
||||||
(setq flycheck-help-echo-function nil))
|
|
||||||
|
|
||||||
:hook ((flycheck-mode . mp-flycheck-prefer-eldoc)))
|
|
||||||
|
|
||||||
(use-package flycheck-eglot
|
(use-package flycheck-eglot
|
||||||
:after (flycheck eglot)
|
:after (flycheck eglot)
|
||||||
@@ -530,12 +503,6 @@ Setup prefix for keybindings.
|
|||||||
(add-hook 'before-save-hook #'eglot-format-buffer -10 t))))
|
(add-hook 'before-save-hook #'eglot-format-buffer -10 t))))
|
||||||
|
|
||||||
(use-package eglot
|
(use-package eglot
|
||||||
:preface
|
|
||||||
(defun mp-eglot-eldoc ()
|
|
||||||
(setq eldoc-echo-area-use-multiline-p nil)
|
|
||||||
(setq eldoc-documentation-strategy
|
|
||||||
'eldoc-documentation-compose-eagerly))
|
|
||||||
|
|
||||||
:config
|
:config
|
||||||
(add-to-list 'eglot-server-programs
|
(add-to-list 'eglot-server-programs
|
||||||
'(scala-mode .
|
'(scala-mode .
|
||||||
@@ -544,9 +511,6 @@ Setup prefix for keybindings.
|
|||||||
(add-to-list 'eglot-server-programs
|
(add-to-list 'eglot-server-programs
|
||||||
'(nix-mode . ("nixd")))
|
'(nix-mode . ("nixd")))
|
||||||
|
|
||||||
(add-to-list 'eglot-server-programs
|
|
||||||
'(gleam-ts-mode . ("gleam" "lsp")))
|
|
||||||
|
|
||||||
(setq-default eglot-workspace-configuration
|
(setq-default eglot-workspace-configuration
|
||||||
'(
|
'(
|
||||||
:metals (
|
:metals (
|
||||||
@@ -555,11 +519,7 @@ Setup prefix for keybindings.
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
:custom
|
|
||||||
(eglot-code-action-indications nil)
|
|
||||||
|
|
||||||
:hook (
|
:hook (
|
||||||
(eglot-managed-mode . mp-eglot-eldoc)
|
|
||||||
(go-mode . eglot-ensure)
|
(go-mode . eglot-ensure)
|
||||||
(go-mode . alex/organize-imports-on-save)
|
(go-mode . alex/organize-imports-on-save)
|
||||||
(go-mode . alex/format-on-save)
|
(go-mode . alex/format-on-save)
|
||||||
@@ -569,9 +529,6 @@ Setup prefix for keybindings.
|
|||||||
(nix-mode . eglot-ensure)
|
(nix-mode . eglot-ensure)
|
||||||
(nix-mode . alex/format-on-save)
|
(nix-mode . alex/format-on-save)
|
||||||
|
|
||||||
(gleam-ts-mode . eglot-ensure)
|
|
||||||
(gleam-ts-mode . alex/format-on-save)
|
|
||||||
|
|
||||||
(python-mode . eglot-ensure)
|
(python-mode . eglot-ensure)
|
||||||
(javascript-mode . eglot-ensure)
|
(javascript-mode . eglot-ensure)
|
||||||
(js-mode . eglot-ensure)
|
(js-mode . eglot-ensure)
|
||||||
@@ -588,6 +545,16 @@ Setup prefix for keybindings.
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(use-package eglot-booster
|
||||||
|
:after eglot
|
||||||
|
:config (eglot-booster-mode))
|
||||||
|
#+END_SRC
|
||||||
|
** Eldoc-box
|
||||||
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
(use-package eldoc-box
|
||||||
|
:after eglot
|
||||||
|
:bind (:map eglot-mode-map
|
||||||
|
("M-h" . eldoc-box-help-at-point)))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** Go
|
** Go
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -606,12 +573,6 @@ Setup prefix for keybindings.
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** Gleam
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
|
||||||
(use-package gleam-ts-mode
|
|
||||||
:mode "\\.gleam\\'"
|
|
||||||
)
|
|
||||||
#+END_SRC
|
|
||||||
** YAML
|
** YAML
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(use-package yaml-mode
|
(use-package yaml-mode
|
||||||
|
|||||||
@@ -12,6 +12,18 @@ let
|
|||||||
epkgs.flymake-go-staticcheck
|
epkgs.flymake-go-staticcheck
|
||||||
epkgs.tree-sitter-langs
|
epkgs.tree-sitter-langs
|
||||||
epkgs.treesit-grammars.with-all-grammars
|
epkgs.treesit-grammars.with-all-grammars
|
||||||
|
|
||||||
|
(epkgs.trivialBuild {
|
||||||
|
pname = "eglot-booster";
|
||||||
|
version = "main-2024-04-11";
|
||||||
|
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "jdtsmith";
|
||||||
|
repo = "eglot-booster";
|
||||||
|
rev = "e19dd7ea81bada84c66e8bdd121408d9c0761fe6";
|
||||||
|
hash = "sha256-vF34ZoUUj8RENyH9OeKGSPk34G6KXZhEZozQKEcRNhs=";
|
||||||
|
};
|
||||||
|
})
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,6 +50,7 @@ in
|
|||||||
ec
|
ec
|
||||||
emacs
|
emacs
|
||||||
pkgs.wl-clipboard
|
pkgs.wl-clipboard
|
||||||
|
pkgs.emacs-lsp-booster
|
||||||
pkgs.nixd
|
pkgs.nixd
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
fonts.packages = [
|
fonts.packages = [
|
||||||
pkgs.noto-fonts
|
pkgs.noto-fonts
|
||||||
pkgs.noto-fonts-cjk-sans
|
pkgs.noto-fonts-cjk-sans
|
||||||
pkgs.noto-fonts-color-emoji
|
pkgs.noto-fonts-emoji
|
||||||
pkgs.nerd-fonts.jetbrains-mono
|
pkgs.nerd-fonts.jetbrains-mono
|
||||||
pkgs.liberation_ttf
|
pkgs.liberation_ttf
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{ ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
programs.fzf = {
|
programs.fzf = {
|
||||||
|
|||||||
@@ -6,38 +6,6 @@
|
|||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
enabled = config.mod.git.enable;
|
enabled = config.mod.git.enable;
|
||||||
|
|
||||||
# Wrapper around `ssh-keygen` used as git's SSH signing program. Before a
|
|
||||||
# signing operation it ensures the passphrase-protected signing key is loaded
|
|
||||||
# into the agent — `ssh-keygen -Y sign` reads the key from disk and re-prompts
|
|
||||||
# every commit otherwise, since `AddKeysToAgent` only ever caches auth keys.
|
|
||||||
# Loading it once (through the GUI askpass) lets later commits reuse the
|
|
||||||
# cached key from the agent. Verification and every other op pass straight
|
|
||||||
# through to the real ssh-keygen untouched.
|
|
||||||
sshSignWrapper = pkgs.writeShellApplication {
|
|
||||||
name = "git-ssh-sign";
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.openssh
|
|
||||||
pkgs.gawk
|
|
||||||
pkgs.gnugrep
|
|
||||||
];
|
|
||||||
text = ''
|
|
||||||
key="${config.age.secrets."alex.pinwheel-github.com-signing".path}"
|
|
||||||
|
|
||||||
case " $* " in
|
|
||||||
*" -Y sign "*)
|
|
||||||
fp=""
|
|
||||||
fp="$(ssh-keygen -lf "$key.pub" 2>/dev/null | awk '{print $2}')" || true
|
|
||||||
if [ -n "$fp" ] && ! ssh-add -l 2>/dev/null | grep -qF "$fp"; then
|
|
||||||
# </dev/null detaches stdin so ssh-add uses SSH_ASKPASS (the GUI).
|
|
||||||
ssh-add "$key" </dev/null || true
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
exec ssh-keygen "$@"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
@@ -47,7 +15,7 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
config = lib.mkIf enabled {
|
||||||
home-manager.users.alex = { lib, ... }: {
|
home-manager.users.alex = {
|
||||||
programs.git = {
|
programs.git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
@@ -55,31 +23,11 @@ in
|
|||||||
{ path = ./gitconfig; }
|
{ path = ./gitconfig; }
|
||||||
];
|
];
|
||||||
|
|
||||||
signing = {
|
extraConfig = {
|
||||||
key = config.age.secrets."alex.pinwheel-github.com-signing.pub".path;
|
|
||||||
signByDefault = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
rerere.enable = true;
|
rerere.enable = true;
|
||||||
|
|
||||||
# Tells Git to use SSH instead of the default GPG
|
|
||||||
gpg.format = "ssh";
|
|
||||||
|
|
||||||
# Sign via a wrapper that loads the signing key into the agent on
|
|
||||||
# first use, so subsequent signed commits reuse the cached key
|
|
||||||
# instead of re-prompting for the passphrase every time.
|
|
||||||
gpg.ssh.program = "${sshSignWrapper}/bin/git-ssh-sign";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
home.file.".ssh/config".target = ".ssh/config_source";
|
|
||||||
|
|
||||||
home.activation.sshConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
|
||||||
run cat ~/.ssh/config_source > ~/.ssh/config
|
|
||||||
run chmod 600 ~/.ssh/config
|
|
||||||
'';
|
|
||||||
|
|
||||||
home.packages = [ pkgs.tig ];
|
home.packages = [ pkgs.tig ];
|
||||||
|
|
||||||
home.file.".tigrc".text = ''
|
home.file.".tigrc".text = ''
|
||||||
@@ -87,20 +35,5 @@ in
|
|||||||
set main-view-line-number-interval = 1
|
set main-view-line-number-interval = 1
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
age.secrets = {
|
|
||||||
"alex.pinwheel-github.com-signing" = {
|
|
||||||
file = ../../../../secrets/pinwheel/alex.pinwheel-github.com-signing.age;
|
|
||||||
path = "/home/alex/.ssh/alex.pinwheel-github.com-signing";
|
|
||||||
owner = "alex";
|
|
||||||
group = "users";
|
|
||||||
};
|
|
||||||
"alex.pinwheel-github.com-signing.pub" = {
|
|
||||||
file = ../../../../secrets/pinwheel/alex.pinwheel-github.com-signing.pub.age;
|
|
||||||
path = "/home/alex/.ssh/alex.pinwheel-github.com-signing.pub";
|
|
||||||
owner = "alex";
|
|
||||||
group = "users";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,8 @@
|
|||||||
[url "git@github.com:"]
|
[url "git@github.com:"]
|
||||||
insteadOf = https://github.com/
|
insteadOf = https://github.com/
|
||||||
|
|
||||||
|
[url "git@codeberg.org:"]
|
||||||
|
insteadOf = https://codeberg.org/
|
||||||
|
|
||||||
[url "gitea@git.ppp.pm:"]
|
[url "gitea@git.ppp.pm:"]
|
||||||
insteadOf = https://git.ppp.pm/
|
insteadOf = https://git.ppp.pm/
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
enabled = config.mod.gleam.enable;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.gleam = {
|
|
||||||
enable = lib.mkEnableOption "enable gleam module";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
home-manager.users.alex = {
|
|
||||||
home.packages = [
|
|
||||||
pkgs.gleam
|
|
||||||
pkgs.erlang
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -20,9 +20,7 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
package = pkgs.go;
|
package = pkgs.go;
|
||||||
env = {
|
goPath = "code/go";
|
||||||
GOPATH = "/home/alex/code/go";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
home.packages = [
|
home.packages = [
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ in
|
|||||||
let
|
let
|
||||||
session = {
|
session = {
|
||||||
user = "alex";
|
user = "alex";
|
||||||
command = "uwsm start hyprland-uwsm.desktop";
|
command = "${pkgs.hyprland}/bin/Hyprland";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
inputs,
|
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
@@ -7,71 +6,6 @@
|
|||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
enabled = config.mod.hyprland.enable;
|
enabled = config.mod.hyprland.enable;
|
||||||
|
|
||||||
monitorScript = pkgs.writeShellScript "hyprland-monitor-handler" ''
|
|
||||||
INTERNAL="eDP-1"
|
|
||||||
EXTERNAL_MONITORS="HDMI-A-1 DP-3"
|
|
||||||
HYPRCTL="${pkgs.hyprland}/bin/hyprctl"
|
|
||||||
JQ="${pkgs.jq}/bin/jq"
|
|
||||||
|
|
||||||
get_active_external() {
|
|
||||||
# Return the first connected external monitor
|
|
||||||
for mon in $EXTERNAL_MONITORS; do
|
|
||||||
if $HYPRCTL monitors -j | $JQ -e ".[] | select(.name == \"$mon\")" > /dev/null 2>&1; then
|
|
||||||
echo "$mon"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
bind_workspaces() {
|
|
||||||
local external batch=""
|
|
||||||
|
|
||||||
if external=$(get_active_external); then
|
|
||||||
# External monitor connected: move workspaces 1-5 to external, 6-10 to internal
|
|
||||||
for ws in 1 2 3 4 5; do
|
|
||||||
batch="$batch dispatch moveworkspacetomonitor $ws $external;"
|
|
||||||
done
|
|
||||||
for ws in 6 7 8 9 10; do
|
|
||||||
batch="$batch dispatch moveworkspacetomonitor $ws $INTERNAL;"
|
|
||||||
done
|
|
||||||
else
|
|
||||||
# No external monitor: move all workspaces to internal
|
|
||||||
for ws in 1 2 3 4 5 6 7 8 9 10; do
|
|
||||||
batch="$batch dispatch moveworkspacetomonitor $ws $INTERNAL;"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
$HYPRCTL --batch "$batch"
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_event() {
|
|
||||||
case $1 in
|
|
||||||
monitoradded*|monitorremoved*)
|
|
||||||
sleep 0.5
|
|
||||||
bind_workspaces
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Start the event listener first so monitoradded events emitted during
|
|
||||||
# session startup are not lost in the gap before we begin reading them.
|
|
||||||
${pkgs.socat}/bin/socat -U - UNIX-CONNECT:"$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" | while read -r line; do
|
|
||||||
handle_event "$line"
|
|
||||||
done &
|
|
||||||
LISTENER_PID=$!
|
|
||||||
|
|
||||||
# Give socat a moment to actually connect before the initial bind.
|
|
||||||
sleep 0.2
|
|
||||||
bind_workspaces
|
|
||||||
|
|
||||||
# Re-bind once more after the DRM subsystem has had time to enumerate
|
|
||||||
# external connectors, in case they were not yet present at session start.
|
|
||||||
(sleep 3; bind_workspaces) &
|
|
||||||
|
|
||||||
wait $LISTENER_PID
|
|
||||||
'';
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
@@ -81,50 +15,28 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
config = lib.mkIf enabled {
|
||||||
programs.hyprland = {
|
home-manager.users.alex = {
|
||||||
|
wayland.windowManager.hyprland = {
|
||||||
enable = true;
|
enable = true;
|
||||||
withUWSM = true;
|
|
||||||
package = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.hyprland;
|
|
||||||
portalPackage =
|
|
||||||
inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.xdg-desktop-portal-hyprland;
|
|
||||||
|
|
||||||
xwayland = {
|
xwayland = {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
home-manager.users.alex = {
|
|
||||||
wayland.windowManager.hyprland = {
|
|
||||||
enable = true;
|
|
||||||
systemd.enable = false;
|
|
||||||
configType = "hyprlang";
|
|
||||||
|
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
exec-once = uwsm app -- waybar
|
exec-once=waybar
|
||||||
exec-once = uwsm app -- hyprctl setcursor Adwaita 24
|
|
||||||
|
|
||||||
env = GDK_DPI_SCALE,1.5
|
env = GDK_DPI_SCALE,1.5
|
||||||
env = HYPRCURSOR_THEME,Adwaita
|
env = XCURSOR_SIZE,64
|
||||||
env = HYPRCURSOR_SIZE,24
|
|
||||||
|
|
||||||
monitor=eDP-1, 1920x1200, auto-center-down, 1
|
monitor=eDP-1, 1920x1200, 0x0, 1
|
||||||
monitor=HDMI-A-1, 2560x1440@100, auto-center-up, 1
|
|
||||||
monitor=DP-3, 2560x1440@60, auto-center-up, 1
|
|
||||||
|
|
||||||
# Workspaces 1-5 on external monitors (HDMI-A-1 or DP-3)
|
workspace = 1, monitor:HDMI-A-1
|
||||||
workspace = 1, monitor:HDMI-A-1, default:true
|
|
||||||
workspace = 2, monitor:HDMI-A-1
|
workspace = 2, monitor:HDMI-A-1
|
||||||
workspace = 3, monitor:HDMI-A-1
|
workspace = 3, monitor:HDMI-A-1
|
||||||
workspace = 4, monitor:HDMI-A-1
|
workspace = 4, monitor:HDMI-A-1
|
||||||
workspace = 5, monitor:HDMI-A-1
|
workspace = 5, monitor:HDMI-A-1
|
||||||
workspace = 1, monitor:DP-3, default:true
|
workspace = 6, monitor:eDP-1
|
||||||
workspace = 2, monitor:DP-3
|
|
||||||
workspace = 3, monitor:DP-3
|
|
||||||
workspace = 4, monitor:DP-3
|
|
||||||
workspace = 5, monitor:DP-3
|
|
||||||
|
|
||||||
# Workspaces 6-10 on internal monitor
|
|
||||||
workspace = 6, monitor:eDP-1, default:true
|
|
||||||
workspace = 7, monitor:eDP-1
|
workspace = 7, monitor:eDP-1
|
||||||
workspace = 8, monitor:eDP-1
|
workspace = 8, monitor:eDP-1
|
||||||
workspace = 9, monitor:eDP-1
|
workspace = 9, monitor:eDP-1
|
||||||
@@ -132,14 +44,12 @@ in
|
|||||||
|
|
||||||
workspace = w[tv1], gapsout:0, gapsin:0
|
workspace = w[tv1], gapsout:0, gapsin:0
|
||||||
workspace = f[1], gapsout:0, gapsin:0
|
workspace = f[1], gapsout:0, gapsin:0
|
||||||
windowrule = border_size 0, match:float 0, match:workspace w[tv1]
|
windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||||
windowrule = rounding 0, match:float 0, match:workspace w[tv1]
|
windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
|
||||||
windowrule = border_size 0, match:float 0, match:workspace f[1]
|
windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
|
||||||
windowrule = rounding 0, match:float 0, match:workspace f[1]
|
windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
|
||||||
|
|
||||||
# https://wiki.archlinux.org/title/Hyprland#Jetbrains_apps_focus_issues
|
|
||||||
windowrule = match:xwayland true, no_initial_focus on
|
|
||||||
|
|
||||||
|
exec-once=dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
|
||||||
'';
|
'';
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
@@ -158,7 +68,6 @@ in
|
|||||||
follow_mouse = 2;
|
follow_mouse = 2;
|
||||||
|
|
||||||
sensitivity = 0.3;
|
sensitivity = 0.3;
|
||||||
accel_profile = "flat";
|
|
||||||
touchpad = {
|
touchpad = {
|
||||||
natural_scroll = false;
|
natural_scroll = false;
|
||||||
tap-and-drag = false;
|
tap-and-drag = false;
|
||||||
@@ -251,23 +160,6 @@ in
|
|||||||
pkgs.wdisplays
|
pkgs.wdisplays
|
||||||
pkgs.bc
|
pkgs.bc
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.user.services.hyprland-monitors = {
|
|
||||||
Unit = {
|
|
||||||
Description = "Hyprland monitor hotplug handler";
|
|
||||||
PartOf = [ "graphical-session.target" ];
|
|
||||||
After = [ "graphical-session.target" ];
|
|
||||||
};
|
|
||||||
Service = {
|
|
||||||
Type = "simple";
|
|
||||||
ExecStart = "${monitorScript}";
|
|
||||||
Restart = "on-failure";
|
|
||||||
RestartSec = 5;
|
|
||||||
};
|
|
||||||
Install = {
|
|
||||||
WantedBy = [ "graphical-session.target" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# To start electron apps like `chromium` with wayland support
|
# To start electron apps like `chromium` with wayland support
|
||||||
@@ -286,6 +178,87 @@ in
|
|||||||
# openGL is needed for wayland/hyprland
|
# openGL is needed for wayland/hyprland
|
||||||
hardware.graphics.enable = true;
|
hardware.graphics.enable = true;
|
||||||
|
|
||||||
boot.kernelParams = [ "i915.enable_psr=0" ];
|
systemd.user.services.hyprland-monitors = {
|
||||||
|
# systemctl --user restart hyprland-monitors.service
|
||||||
|
# journalctl --user -u hyprland-monitors.service -e -f
|
||||||
|
unitConfig = {
|
||||||
|
Description = "handles hyprland monitor connect/disconnect";
|
||||||
|
};
|
||||||
|
|
||||||
|
wantedBy = [ "graphical-session.target" ];
|
||||||
|
requires = [ "graphical-session.target" ];
|
||||||
|
after = [ "graphical-session.target" ];
|
||||||
|
|
||||||
|
path = [
|
||||||
|
pkgs.coreutils # to include `cat`
|
||||||
|
pkgs.waybar
|
||||||
|
pkgs.hyprland
|
||||||
|
pkgs.socat
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.bc
|
||||||
|
pkgs.libnotify
|
||||||
|
];
|
||||||
|
|
||||||
|
script =
|
||||||
|
let
|
||||||
|
moveWSToMonitor =
|
||||||
|
monitor: first: last:
|
||||||
|
if last < first then
|
||||||
|
throw "'first' has to be less than or equal to 'last'"
|
||||||
|
else
|
||||||
|
builtins.genList (
|
||||||
|
n: "dispatch moveworkspacetomonitor ${builtins.toString (first + n)} ${monitor}"
|
||||||
|
) (last - first + 1);
|
||||||
|
|
||||||
|
external = moveWSToMonitor "HDMI-A-1" 1 5;
|
||||||
|
internal = moveWSToMonitor "eDPI-1" 6 10;
|
||||||
|
onlyInternal = moveWSToMonitor "eDPI-1" 1 10;
|
||||||
|
in
|
||||||
|
''
|
||||||
|
update() {
|
||||||
|
HDMI_STATUS=$(cat /sys/class/drm/card1-HDMI-A-1/status)
|
||||||
|
|
||||||
|
INTERNAL_WIDTH=1920
|
||||||
|
INTERNAL_HEIGHT=1200
|
||||||
|
|
||||||
|
if [ $HDMI_STATUS = "connected" ]; then
|
||||||
|
notify-send "Using external and laptop monitor"
|
||||||
|
|
||||||
|
hyprctl keyword monitor HDMI-A-1,preferred,0x0,1
|
||||||
|
|
||||||
|
HDMI=$(hyprctl monitors -j | jq '.[] | select(.name=="HDMI-A-1")')
|
||||||
|
HDMI_WIDTH=$(echo $HDMI | jq .width)
|
||||||
|
HDMI_HEIGHT=$(echo $HDMI | jq .height)
|
||||||
|
|
||||||
|
INTERNAL_POS_X=$(echo "($HDMI_WIDTH - $INTERNAL_WIDTH) / 2" | bc)
|
||||||
|
if (( $(echo "$INTERNAL_POS_X < 0" | bc) )); then INTERNAL_POS_X=0; fi
|
||||||
|
INTERNAL_POS_Y=$HDMI_HEIGHT
|
||||||
|
|
||||||
|
hyprctl keyword monitor eDP-1,$INTERNAL_WIDTH"x"$INTERNAL_HEIGHT,$INTERNAL_POS_X"x"$INTERNAL_POS_Y,1
|
||||||
|
hyprctl --batch "${lib.strings.concatStringsSep ";" (external ++ internal)}"
|
||||||
|
else
|
||||||
|
notify-send "Using only laptop monitor"
|
||||||
|
|
||||||
|
hyprctl --batch "keyword monitor HDMI-A,disable; keyword monitor eDP-1,$INTERNAL_WIDTH"x"$INTERNAL_HEIGHT,0x0,1"
|
||||||
|
hyprctl --batch "${lib.strings.concatStringsSep ";" onlyInternal}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
handle() {
|
||||||
|
case $1 in
|
||||||
|
monitoradded\>\>*|monitorremoved\>\>*)
|
||||||
|
echo "handling event: \"$1\""
|
||||||
|
update ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Starting service with instance \"$HYPRLAND_INSTANCE_SIGNATURE\""
|
||||||
|
|
||||||
|
# Do initial configuration
|
||||||
|
update
|
||||||
|
|
||||||
|
socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do handle "$line"; done
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
home.packages = [ pkgs.typescript-language-server ];
|
home.packages = [ pkgs.nodePackages.typescript-language-server ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
users.users.alex.extraGroups = [ "video" ];
|
users.users.alex.extraGroups = [ "video" ];
|
||||||
|
programs.light.enable = true;
|
||||||
|
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
wayland.windowManager.hyprland = lib.mkIf hyprlandEnabled {
|
wayland.windowManager.hyprland = lib.mkIf hyprlandEnabled {
|
||||||
settings = {
|
settings = {
|
||||||
bind = [
|
bind = [
|
||||||
", XF86MonBrightnessUp, exec, ${pkgs.brightnessctl}/bin/brightnessctl set +5%"
|
", XF86MonBrightnessUp, exec, ${pkgs.light}/bin/light -A 5"
|
||||||
", XF86MonBrightnessDown, exec, ${pkgs.brightnessctl}/bin/brightnessctl set 5%-"
|
", XF86MonBrightnessDown, exec, ${pkgs.light}/bin/light -U 5"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
{ pkgs, ... }:
|
|
||||||
{
|
{
|
||||||
home-manager = {
|
services.connman = {
|
||||||
users.alex = {
|
|
||||||
home.packages = [ pkgs.networkmanager ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networking = {
|
|
||||||
wireless.enable = false; # Wireless is managed by networkmanager
|
|
||||||
|
|
||||||
networkmanager = {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
wifi = {
|
wifi = {
|
||||||
backend = "iwd";
|
backend = "iwd";
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
networkInterfaceBlacklist = [
|
||||||
|
"vmnet"
|
||||||
|
"vboxnet"
|
||||||
|
"virbr"
|
||||||
|
"ifb"
|
||||||
|
"ve"
|
||||||
|
"docker"
|
||||||
|
"br-"
|
||||||
|
"wg-"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs.nixfmt
|
pkgs.nixfmt-rfc-style
|
||||||
pkgs.nix-tree
|
pkgs.nix-tree
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,19 +18,21 @@ in
|
|||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs.openvpn
|
pkgs.openvpn
|
||||||
|
pkgs.update-systemd-resolved
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.resolved = {
|
services.resolved = {
|
||||||
enable = true;
|
enable = false;
|
||||||
settings = {
|
dnssec = "true";
|
||||||
Resolve = {
|
domains = [ "~." ];
|
||||||
Domains = [ "~." ];
|
fallbackDns = [
|
||||||
DNSSEC = false;
|
"1.1.1.1#one.one.one.one"
|
||||||
DNSOverTLS = true;
|
"1.0.0.1#one.one.one.one"
|
||||||
};
|
];
|
||||||
};
|
extraConfig = ''
|
||||||
|
DNSOverTLS=yes
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,12 +80,12 @@ in
|
|||||||
path = [
|
path = [
|
||||||
pkgs.coreutils # For `cat`
|
pkgs.coreutils # For `cat`
|
||||||
pkgs.libnotify
|
pkgs.libnotify
|
||||||
pkgs.hyprlock
|
pkgs.swaylock
|
||||||
];
|
];
|
||||||
|
|
||||||
script =
|
script =
|
||||||
let
|
let
|
||||||
pause-music = "${pkgs.playerctl}/bin/playerctl -a pause";
|
pause-music = "${pkgs.playerctl}/bin/playerctl -p spotify pause";
|
||||||
in
|
in
|
||||||
''
|
''
|
||||||
BATTERY_CAPACITY=$(cat /sys/class/power_supply/${lowbat.battery}/capacity)
|
BATTERY_CAPACITY=$(cat /sys/class/power_supply/${lowbat.battery}/capacity)
|
||||||
@@ -103,7 +103,7 @@ in
|
|||||||
|
|
||||||
BATTERY_STATUS=$(cat /sys/class/power_supply/${lowbat.battery}/status)
|
BATTERY_STATUS=$(cat /sys/class/power_supply/${lowbat.battery}/status)
|
||||||
if [[ $BATTERY_STATUS = "Discharging" ]]; then
|
if [[ $BATTERY_STATUS = "Discharging" ]]; then
|
||||||
${pause-music}; ${pkgs.hyprlock}/bin/hyprlock & sleep 0.5; systemctl suspend
|
${pause-music}; ${pkgs.swaylock}/bin/swaylock -f; systemctl suspend
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
let
|
let
|
||||||
hyprlandEnabled = config.mod.hyprland.enable;
|
hyprlandEnabled = config.mod.hyprland.enable;
|
||||||
|
|
||||||
grimblast = inputs.hyprland-contrib.packages.${pkgs.stdenv.hostPlatform.system}.grimblast;
|
grimblast = inputs.hyprland-contrib.packages.${pkgs.system}.grimblast;
|
||||||
area = "${pkgs.libnotify}/bin/notify-send 'ps: selected area' && ${grimblast}/bin/grimblast copy area";
|
area = "${pkgs.libnotify}/bin/notify-send 'ps: selected area' && ${grimblast}/bin/grimblast copy area";
|
||||||
screen = "${pkgs.libnotify}/bin/notify-send 'ps: selected screen' &&${grimblast}/bin/grimblast copy output";
|
screen = "${pkgs.libnotify}/bin/notify-send 'ps: selected screen' &&${grimblast}/bin/grimblast copy output";
|
||||||
in
|
in
|
||||||
|
|||||||
+5
-7
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
inputs,
|
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
@@ -14,18 +13,18 @@ in
|
|||||||
settings = {
|
settings = {
|
||||||
bind =
|
bind =
|
||||||
let
|
let
|
||||||
prev = "${pkgs.playerctl}/bin/playerctl -p naviterm,spotify previous";
|
prev = "${pkgs.playerctl}/bin/playerctl -p spotify previous";
|
||||||
next = "${pkgs.playerctl}/bin/playerctl -p naviterm,spotify next";
|
next = "${pkgs.playerctl}/bin/playerctl -p spotify next";
|
||||||
in
|
in
|
||||||
[
|
[
|
||||||
", XF86AudioPrev, exec, ${prev}"
|
", XF86AudioPrev, exec, ${prev}"
|
||||||
", XF86AudioNext, exec, ${next}"
|
", XF86AudioNext, exec, ${next}"
|
||||||
", XF86AudioPlay, exec, ${pkgs.playerctl}/bin/playerctl -p naviterm,spotify play-pause"
|
", XF86AudioPlay, exec, ${pkgs.playerctl}/bin/playerctl -p spotify play-pause"
|
||||||
", XF86AudioPause, exec, ${pkgs.playerctl}/bin/playerctl -p naviterm,spoitfy play-pause"
|
", XF86AudioPause, exec, ${pkgs.playerctl}/bin/playerctl -p spoitfy play-pause"
|
||||||
|
|
||||||
"$mod ALT, LEFT, exec, ${prev}"
|
"$mod ALT, LEFT, exec, ${prev}"
|
||||||
"$mod ALT, RIGHT, exec, ${next}"
|
"$mod ALT, RIGHT, exec, ${next}"
|
||||||
"$mod ALT, DOWN, exec, ${pkgs.playerctl}/bin/playerctl -p naviterm,spotify play-pause"
|
"$mod ALT, DOWN, exec, ${pkgs.playerctl}/bin/playerctl -p spotify play-pause"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -33,7 +32,6 @@ in
|
|||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs.playerctl
|
pkgs.playerctl
|
||||||
pkgs.spotify
|
pkgs.spotify
|
||||||
inputs.naviterm.packages.${pkgs.stdenv.hostPlatform.system}.default
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1,35 +1,8 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
# Enable gnome-keyring at system level for PAM integration
|
|
||||||
services.gnome.gnome-keyring.enable = true;
|
|
||||||
|
|
||||||
# Use openssh's own ssh-agent — gcr's ssh-agent stalls signing RSA keys.
|
|
||||||
services.gnome.gcr-ssh-agent.enable = false;
|
|
||||||
programs.ssh.startAgent = true;
|
|
||||||
|
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
services.gnome-keyring = {
|
|
||||||
enable = true;
|
|
||||||
components = [ "secrets" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
home.sessionVariables = {
|
|
||||||
# gnome-keyring's PAM hooks export SSH_AUTH_SOCK pointing at a dead gcr
|
|
||||||
# socket (gcr-ssh-agent is disabled above), which shadows openssh's own
|
|
||||||
# agent and silently breaks passphrase caching. Force it back to the
|
|
||||||
# openssh agent started by `programs.ssh.startAgent`.
|
|
||||||
SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/ssh-agent";
|
|
||||||
|
|
||||||
# Route passphrase prompts through seahorse's GUI askpass instead of the
|
|
||||||
# terminal. `prefer` uses the GUI even when a tty is attached (ssh only
|
|
||||||
# falls back to askpass with no controlling terminal otherwise).
|
|
||||||
SSH_ASKPASS = "${pkgs.seahorse}/libexec/seahorse/ssh-askpass";
|
|
||||||
SSH_ASKPASS_REQUIRE = "prefer";
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.ssh = {
|
programs.ssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableDefaultConfig = false;
|
|
||||||
|
|
||||||
matchBlocks = {
|
matchBlocks = {
|
||||||
"manatee" = {
|
"manatee" = {
|
||||||
@@ -46,6 +19,12 @@
|
|||||||
port = 1122;
|
port = 1122;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"andromeda" = {
|
||||||
|
hostname = "andromeda.a2x.se";
|
||||||
|
user = "alex";
|
||||||
|
identityFile = "/home/alex/.ssh/alex.pinwheel-andromeda";
|
||||||
|
};
|
||||||
|
|
||||||
"tadpole" = {
|
"tadpole" = {
|
||||||
hostname = "65.21.106.222";
|
hostname = "65.21.106.222";
|
||||||
user = "alex";
|
user = "alex";
|
||||||
@@ -58,30 +37,19 @@
|
|||||||
identityFile = "/home/alex/.ssh/alex.pinwheel-github.com";
|
identityFile = "/home/alex/.ssh/alex.pinwheel-github.com";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"codeberg.org" = {
|
||||||
|
hostname = "codeberg.org";
|
||||||
|
identityFile = "/home/alex/.ssh/alex.pinwheel-codeberg.org";
|
||||||
|
};
|
||||||
|
|
||||||
"git.ppp.pm" = {
|
"git.ppp.pm" = {
|
||||||
hostname = "git.ppp.pm";
|
hostname = "git.ppp.pm";
|
||||||
identityFile = "/home/alex/.ssh/alex.pinwheel-git.ppp.pm";
|
identityFile = "/home/alex/.ssh/alex.pinwheel-git.ppp.pm";
|
||||||
};
|
};
|
||||||
|
|
||||||
"*" = {
|
|
||||||
forwardAgent = false;
|
|
||||||
addKeysToAgent = "yes";
|
|
||||||
compression = false;
|
|
||||||
serverAliveInterval = 0;
|
|
||||||
serverAliveCountMax = 3;
|
|
||||||
hashKnownHosts = false;
|
|
||||||
userKnownHostsFile = "~/.ssh/known_hosts";
|
|
||||||
controlMaster = "no";
|
|
||||||
controlPath = "~/.ssh/master-%r@%n:%p";
|
|
||||||
controlPersist = "no";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
home.packages = [
|
home.packages = [ pkgs.sshfs ];
|
||||||
pkgs.sshfs
|
|
||||||
pkgs.seahorse # GUI for managing gnome-keyring
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
age.secrets = {
|
age.secrets = {
|
||||||
@@ -124,6 +92,19 @@
|
|||||||
group = "users";
|
group = "users";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"alex.pinwheel-codeberg.org" = {
|
||||||
|
file = ../../../../secrets/pinwheel/alex.pinwheel-codeberg.org.age;
|
||||||
|
path = "/home/alex/.ssh/alex.pinwheel-codeberg.org";
|
||||||
|
owner = "alex";
|
||||||
|
group = "users";
|
||||||
|
};
|
||||||
|
"alex.pinwheel-codeberg.org.pub" = {
|
||||||
|
file = ../../../../secrets/pinwheel/alex.pinwheel-codeberg.org.pub.age;
|
||||||
|
path = "/home/alex/.ssh/alex.pinwheel-codeberg.org.pub";
|
||||||
|
owner = "alex";
|
||||||
|
group = "users";
|
||||||
|
};
|
||||||
|
|
||||||
"alex.pinwheel-git.ppp.pm" = {
|
"alex.pinwheel-git.ppp.pm" = {
|
||||||
file = ../../../../secrets/pinwheel/alex.pinwheel-git.ppp.pm.age;
|
file = ../../../../secrets/pinwheel/alex.pinwheel-git.ppp.pm.age;
|
||||||
path = "/home/alex/.ssh/alex.pinwheel-git.ppp.pm";
|
path = "/home/alex/.ssh/alex.pinwheel-git.ppp.pm";
|
||||||
@@ -137,6 +118,19 @@
|
|||||||
group = "users";
|
group = "users";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"alex.pinwheel-andromeda" = {
|
||||||
|
file = ../../../../secrets/pinwheel/alex.pinwheel-andromeda.age;
|
||||||
|
path = "/home/alex/.ssh/alex.pinwheel-andromeda";
|
||||||
|
owner = "alex";
|
||||||
|
group = "users";
|
||||||
|
};
|
||||||
|
"alex.pinwheel-andromeda.pub" = {
|
||||||
|
file = ../../../../secrets/pinwheel/alex.pinwheel-andromeda.pub.age;
|
||||||
|
path = "/home/alex/.ssh/alex.pinwheel-andromeda.pub";
|
||||||
|
owner = "alex";
|
||||||
|
group = "users";
|
||||||
|
};
|
||||||
|
|
||||||
"alex.pinwheel-tadpole" = {
|
"alex.pinwheel-tadpole" = {
|
||||||
file = ../../../../secrets/pinwheel/alex.pinwheel-tadpole.age;
|
file = ../../../../secrets/pinwheel/alex.pinwheel-tadpole.age;
|
||||||
path = "/home/alex/.ssh/alex.pinwheel-tadpole";
|
path = "/home/alex/.ssh/alex.pinwheel-tadpole";
|
||||||
|
|||||||
+16
-31
@@ -5,13 +5,13 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
enabled = config.mod.hyprlock.enable;
|
enabled = config.mod.swaylock.enable;
|
||||||
hyprlandEnabled = config.mod.hyprland.enable;
|
hyprlandEnabled = config.mod.hyprland.enable;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
mod.hyprlock = {
|
mod.swaylock = {
|
||||||
enable = lib.mkEnableOption "enable hyprlock module";
|
enable = lib.mkEnableOption "enable swaylock module";
|
||||||
|
|
||||||
dpmsTimeout = lib.mkOption {
|
dpmsTimeout = lib.mkOption {
|
||||||
description = "timeout in seconds before DPMS is turned on";
|
description = "timeout in seconds before DPMS is turned on";
|
||||||
@@ -23,32 +23,13 @@ in
|
|||||||
|
|
||||||
config = lib.mkIf enabled {
|
config = lib.mkIf enabled {
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
programs.hyprlock = {
|
programs.swaylock = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
general = {
|
color = "000000";
|
||||||
hide_cursor = true;
|
indicator-idle-visible = false;
|
||||||
};
|
show-failed-attempts = true;
|
||||||
|
|
||||||
background = [
|
|
||||||
{
|
|
||||||
color = "rgb(000000)";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
input-field = [
|
|
||||||
{
|
|
||||||
size = "250, 50";
|
|
||||||
position = "0, 0";
|
|
||||||
halign = "center";
|
|
||||||
valign = "center";
|
|
||||||
outline_thickness = 2;
|
|
||||||
dots_center = true;
|
|
||||||
fade_on_empty = false;
|
|
||||||
placeholder_text = "";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -56,20 +37,20 @@ in
|
|||||||
settings = {
|
settings = {
|
||||||
bind =
|
bind =
|
||||||
let
|
let
|
||||||
pause-music = "${pkgs.playerctl}/bin/playerctl -a pause";
|
pause-music = "${pkgs.playerctl}/bin/playerctl -p spotify pause";
|
||||||
|
|
||||||
dpmsTimeout = config.mod.hyprlock.dpmsTimeout;
|
dpmsTimeout = config.mod.swaylock.dpmsTimeout;
|
||||||
dpms-lock = pkgs.writeShellScript "dpms-lock" ''
|
dpms-lock = pkgs.writeShellScript "dpms-lock" ''
|
||||||
${pkgs.swayidle}/bin/swayidle \
|
${pkgs.swayidle}/bin/swayidle \
|
||||||
timeout ${dpmsTimeout} "${pkgs.hyprland}/bin/hyprctl dispatch dpms off" \
|
timeout ${dpmsTimeout} "${pkgs.hyprland}/bin/hyprctl dispatch dpms off" \
|
||||||
resume "${pkgs.hyprland}/bin/hyprctl dispatch dpms on" &
|
resume "${pkgs.hyprland}/bin/hyprctl dispatch dpms on" &
|
||||||
|
|
||||||
${pkgs.hyprlock}/bin/hyprlock; ${pkgs.procps}/bin/pkill swayidle
|
${pkgs.swaylock}/bin/swaylock && ${pkgs.procps}/bin/pkill swayidle
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
[
|
[
|
||||||
"$mod, x, exec, ${pause-music}; ${dpms-lock}"
|
"$mod, x, exec, ${pause-music}; ${dpms-lock}"
|
||||||
"$mod SHIFT, x, exec, ${pause-music}; ${pkgs.hyprlock}/bin/hyprlock & sleep 0.5; systemctl suspend"
|
"$mod SHIFT, x, exec, ${pause-music}; ${pkgs.swaylock}/bin/swaylock -f; systemctl suspend"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -78,7 +59,11 @@ in
|
|||||||
security = {
|
security = {
|
||||||
polkit.enable = true;
|
polkit.enable = true;
|
||||||
|
|
||||||
pam.services.hyprlock = {};
|
pam.services.swaylock.text = ''
|
||||||
|
# PAM configuration file for the swaylock screen locker. By default, it includes
|
||||||
|
# the 'login' configuration file (see /etc/pam.d/login)
|
||||||
|
auth include login
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -68,6 +68,17 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ttrpg = {
|
||||||
|
path = "/home/alex/sync/reading-material/ttrpg";
|
||||||
|
devices = [ "backwards" ];
|
||||||
|
versioning = {
|
||||||
|
type = "staggered";
|
||||||
|
params = {
|
||||||
|
maxAge = "2592000"; # 30 days
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
"phone-gps" = {
|
"phone-gps" = {
|
||||||
path = "/home/alex/sync/phone-gps";
|
path = "/home/alex/sync/phone-gps";
|
||||||
devices = [ "phone" ];
|
devices = [ "phone" ];
|
||||||
|
|||||||
@@ -1,23 +1,7 @@
|
|||||||
{ pkgs, ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
services.tailscale.enable = true;
|
services.tailscale.enable = true;
|
||||||
|
|
||||||
# Pinned to 1.96.5. 1.98.0 regressed split-DNS handling under work-vpn: the
|
|
||||||
# netmap's "resolve <tailnet>.ts.net locally via MagicDNS" hint is dropped
|
|
||||||
# when translated into systemd-resolved config, so *.ts.net queries get sent
|
|
||||||
# to a public resolver (199.247.155.53) that the corporate VPN's port-53
|
|
||||||
# egress filter blocks.
|
|
||||||
services.tailscale.package = pkgs.tailscale.overrideAttrs (_: rec {
|
|
||||||
version = "1.96.5";
|
|
||||||
src = pkgs.fetchFromGitHub {
|
|
||||||
owner = "tailscale";
|
|
||||||
repo = "tailscale";
|
|
||||||
tag = "v${version}";
|
|
||||||
hash = "sha256-vYYb+2OtuXftjGGG0zWJesHccrClB8YZpclv9KzNN/c=";
|
|
||||||
};
|
|
||||||
vendorHash = "sha256-rhuWEEN+CtumVxOw6Dy/IRxWIrZ2x6RJb6ULYwXCQc4=";
|
|
||||||
});
|
|
||||||
|
|
||||||
networking.firewall = {
|
networking.firewall = {
|
||||||
checkReversePath = "loose";
|
checkReversePath = "loose";
|
||||||
allowedUDPPorts = [ 41641 ];
|
allowedUDPPorts = [ 41641 ];
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
# https://old.reddit.com/r/tmux/comments/mesrci/tmux_2_doesnt_seem_to_use_256_colors/
|
# https://old.reddit.com/r/tmux/comments/mesrci/tmux_2_doesnt_seem_to_use_256_colors/
|
||||||
set -g default-terminal "xterm-256color"
|
set -g default-terminal "xterm-256color"
|
||||||
set -ga terminal-overrides ",*256col*:Tc"
|
set -ga terminal-overrides ",*256col*:Tc"
|
||||||
set -ga terminal-overrides ',*:Ss=\E[%p1%d q:Se=\E[ q'
|
set -ga terminal-overrides "*:Ss=\E[%p1%d q:Se=\E[ q"
|
||||||
set-environment -g COLORTERM "truecolor"
|
set-environment -g COLORTERM "truecolor"
|
||||||
|
|
||||||
set-option -g allow-rename off
|
set-option -g allow-rename off
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
programs.git = lib.mkIf gitEnabled {
|
programs.git = lib.mkIf gitEnabled {
|
||||||
settings = {
|
extraConfig = {
|
||||||
core = {
|
core = {
|
||||||
editor = "vim";
|
editor = "vim";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,14 +7,14 @@
|
|||||||
let
|
let
|
||||||
hyprlandEnabled = config.mod.hyprland.enable;
|
hyprlandEnabled = config.mod.hyprland.enable;
|
||||||
|
|
||||||
music-status = pkgs.writeShellScript "music-status" ''
|
spotify-status = pkgs.writeShellScript "spotify-status" ''
|
||||||
STATUS=$(${pkgs.playerctl}/bin/playerctl -p naviterm,spotify status 2>&1)
|
STATUS=$(${pkgs.playerctl}/bin/playerctl -p spotify status 2>&1)
|
||||||
|
|
||||||
if [ "$STATUS" = "No players found" ]; then
|
if [ "$STATUS" = "No players found" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
else
|
else
|
||||||
FORMAT="{{markup_escape(xesam:title)}} - {{markup_escape(xesam:artist)}}"
|
FORMAT="{{markup_escape(xesam:title)}} - {{markup_escape(xesam:artist)}}"
|
||||||
OUTPUT=$(${pkgs.playerctl}/bin/playerctl -p naviterm,spotify metadata --format "$FORMAT")
|
OUTPUT=$(${pkgs.playerctl}/bin/playerctl -p spotify metadata --format "$FORMAT")
|
||||||
case "$STATUS" in
|
case "$STATUS" in
|
||||||
"Playing")
|
"Playing")
|
||||||
echo "<span font='14' rise='-3000'></span> $OUTPUT"
|
echo "<span font='14' rise='-3000'></span> $OUTPUT"
|
||||||
@@ -85,25 +85,11 @@ let
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
work-vpn-status = pkgs.writeShellScript "work-vpn-status" ''
|
work-vpn-status = pkgs.writeShellScript "work-vpn-status" ''
|
||||||
ACTIVE_ENVS=""
|
STAGING=$(systemctl is-active openvpn-work-staging.service)
|
||||||
|
[ "$STAGING" == "active" ] && echo "WORK-VPN STAGING ON" && exit 0
|
||||||
|
|
||||||
STAGING_STATUS=$(systemctl is-active openvpn-work-staging.service)
|
PRODUCTION=$(systemctl is-active openvpn-work-production.service)
|
||||||
if [ "$STAGING_STATUS" == "active" ]; then
|
[ "$PRODUCTION" == "active" ] && echo "WORK-VPN PRODUCTION ON" && exit 0
|
||||||
ACTIVE_ENVS="S"
|
|
||||||
fi
|
|
||||||
|
|
||||||
PRODUCTION_STATUS=$(systemctl is-active openvpn-work-production.service)
|
|
||||||
if [ "$PRODUCTION_STATUS" == "active" ]; then
|
|
||||||
if [ -n "$ACTIVE_ENVS" ]; then
|
|
||||||
ACTIVE_ENVS="$ACTIVE_ENVS&P"
|
|
||||||
else
|
|
||||||
ACTIVE_ENVS="P"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$ACTIVE_ENVS" ]; then
|
|
||||||
echo "WORK-VPN $ACTIVE_ENVS ON"
|
|
||||||
fi
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
toggle-bt-power = pkgs.writeShellScript "toggle-bt-power" ''
|
toggle-bt-power = pkgs.writeShellScript "toggle-bt-power" ''
|
||||||
@@ -140,7 +126,7 @@ in
|
|||||||
modules-left = lib.mkIf hyprlandEnabled [ "hyprland/workspaces" ];
|
modules-left = lib.mkIf hyprlandEnabled [ "hyprland/workspaces" ];
|
||||||
modules-right = [
|
modules-right = [
|
||||||
"custom/work-vpn-status"
|
"custom/work-vpn-status"
|
||||||
"custom/music"
|
"custom/spotify"
|
||||||
"custom/container-status"
|
"custom/container-status"
|
||||||
"custom/dunst"
|
"custom/dunst"
|
||||||
"bluetooth"
|
"bluetooth"
|
||||||
@@ -156,8 +142,8 @@ in
|
|||||||
interval = 2;
|
interval = 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
"custom/music" = {
|
"custom/spotify" = {
|
||||||
exec = music-status;
|
exec = spotify-status;
|
||||||
interval = 2;
|
interval = 2;
|
||||||
max-length = 70;
|
max-length = 70;
|
||||||
tooltip = false;
|
tooltip = false;
|
||||||
@@ -236,10 +222,7 @@ in
|
|||||||
height = 30;
|
height = 30;
|
||||||
spacing = 20;
|
spacing = 20;
|
||||||
fixed-center = false;
|
fixed-center = false;
|
||||||
output = [
|
output = [ "HDMI-A-1" ];
|
||||||
"HDMI-A-1"
|
|
||||||
"DP-3"
|
|
||||||
];
|
|
||||||
|
|
||||||
modules-left = lib.mkIf hyprlandEnabled [ "hyprland/workspaces" ];
|
modules-left = lib.mkIf hyprlandEnabled [ "hyprland/workspaces" ];
|
||||||
modules-right = [
|
modules-right = [
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
inputs,
|
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
@@ -12,66 +11,30 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
# Ensure bashInteractive is first in PATH inside nix devshells.
|
|
||||||
# stdenv provides a non-interactive bash that breaks Copilot shell commands.
|
|
||||||
# Adding bashInteractive to home.packages alone isn't enough because devshell
|
|
||||||
# packages are prepended to PATH. This precmd hook runs after direnv's hook
|
|
||||||
# and re-prepends bashInteractive so it takes priority.
|
|
||||||
programs.zsh.initContent = ''
|
|
||||||
_ensure_bash_interactive() {
|
|
||||||
[[ "$PATH" == "${pkgs.bashInteractive}/bin:"* ]] || export PATH="${pkgs.bashInteractive}/bin:$PATH"
|
|
||||||
}
|
|
||||||
precmd_functions+=(_ensure_bash_interactive)
|
|
||||||
|
|
||||||
# Source the zsh-specific rc file that nix-direnv emits ($DIRENV_ZSH_RC)
|
|
||||||
# so devshell completions and zsh setup are picked up. direnv itself only
|
|
||||||
# exports env vars, so without this hook the zsh side of the devshell is
|
|
||||||
# never loaded. Guarded by LAST_LOADED_DIRENV_ZSH_RC so we don't re-source
|
|
||||||
# it on every precmd.
|
|
||||||
_nix_direnv_bridge_hook() {
|
|
||||||
if [[ -n "$DIRENV_ZSH_RC" && "$LAST_LOADED_DIRENV_ZSH_RC" != "$DIRENV_ZSH_RC" ]]; then
|
|
||||||
if [[ -f "$DIRENV_ZSH_RC" ]]; then
|
|
||||||
source "$DIRENV_ZSH_RC"
|
|
||||||
export LAST_LOADED_DIRENV_ZSH_RC="$DIRENV_ZSH_RC"
|
|
||||||
echo "❄️ direnv zsh loaded..."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
autoload -Uz add-zsh-hook
|
|
||||||
add-zsh-hook precmd _nix_direnv_bridge_hook
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Configure IntelliJ to exclude .direnv from indexing
|
|
||||||
home.activation.intellijIgnoreDirenv = ''
|
|
||||||
for idea_config in $HOME/.config/JetBrains/IntelliJIdea*; do
|
|
||||||
if [ -d "$idea_config" ]; then
|
|
||||||
$DRY_RUN_CMD mkdir -p "$idea_config/options"
|
|
||||||
$DRY_RUN_CMD tee "$idea_config/options/filetypes.xml" > /dev/null <<'EOF'
|
|
||||||
<application>
|
|
||||||
<component name="FileTypeManager" version="18">
|
|
||||||
<ignoreFiles list="*.pyc;*.pyo;*.rbc;*.yarb;*~;.DS_Store;.git;.hg;.svn;CVS;__pycache__;_svn;vssver.scc;vssver2.scc;.direnv" />
|
|
||||||
</component>
|
|
||||||
</application>
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
'';
|
|
||||||
|
|
||||||
home.sessionVariables = {
|
home.sessionVariables = {
|
||||||
GITHUB_ACTOR = "Alexander Heldt";
|
GITHUB_ACTOR = "Alexander Heldt";
|
||||||
GITHUB_TOKEN = "$(${pkgs.coreutils}/bin/cat ${config.age.secrets.work-github-token.path})";
|
GITHUB_TOKEN = "$(${pkgs.coreutils}/bin/cat ${config.age.secrets.work-github-token.path})";
|
||||||
};
|
};
|
||||||
|
|
||||||
home.packages = [
|
home.packages =
|
||||||
# (pkgs.callPackage ./pants.nix { inherit (pkgs) stdenv.hostPlatform.system; })
|
let
|
||||||
|
intellij = (
|
||||||
|
pkgs.jetbrains.idea-ultimate.overrideAttrs (
|
||||||
|
final: prev: {
|
||||||
|
version = "2024.2.4";
|
||||||
|
src = pkgs.fetchurl {
|
||||||
|
url = "https://download.jetbrains.com/idea/ideaIU-${final.version}.tar.gz";
|
||||||
|
sha256 = "8411fda793a20356a4982e4f18f6691839d8a471e2081ab6d8cc78b3f8b02532";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
in
|
||||||
|
[
|
||||||
|
# (pkgs.callPackage ./pants.nix { inherit (pkgs) system; })
|
||||||
# (pkgs.callPackage ./syb-cli.nix { })
|
# (pkgs.callPackage ./syb-cli.nix { })
|
||||||
|
|
||||||
(inputs.nix-jetbrains-plugins.lib.buildIdeWithPlugins pkgs "idea" [
|
(pkgs.jetbrains.plugins.addPlugins intellij [ "ideavim" ])
|
||||||
"IdeaVIM"
|
|
||||||
"com.github.copilot"
|
|
||||||
])
|
|
||||||
pkgs.bashInteractive
|
|
||||||
|
|
||||||
(pkgs.google-cloud-sdk.withExtraComponents [
|
(pkgs.google-cloud-sdk.withExtraComponents [
|
||||||
pkgs.google-cloud-sdk.components.gke-gcloud-auth-plugin
|
pkgs.google-cloud-sdk.components.gke-gcloud-auth-plugin
|
||||||
@@ -82,28 +45,13 @@ EOF
|
|||||||
pkgs.postman
|
pkgs.postman
|
||||||
pkgs.grpcurl
|
pkgs.grpcurl
|
||||||
|
|
||||||
pkgs.slack
|
|
||||||
|
|
||||||
# for `radio`
|
# for `radio`
|
||||||
pkgs.go-mockery
|
pkgs.go-mockery
|
||||||
pkgs.golangci-lint
|
pkgs.golangci-lint
|
||||||
|
|
||||||
(pkgs.writeShellScriptBin "work-vpn" ''
|
|
||||||
case $1 in
|
|
||||||
up)
|
|
||||||
sudo sh -c "systemctl start openvpn-work-staging.service; systemctl start openvpn-work-production.service"
|
|
||||||
;;
|
|
||||||
down)
|
|
||||||
sudo sh -c "systemctl stop openvpn-work-staging.service; systemctl stop openvpn-work-production.service"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
'')
|
|
||||||
];
|
];
|
||||||
|
|
||||||
programs.go = lib.mkIf goEnabled {
|
programs.go = lib.mkIf goEnabled {
|
||||||
env = {
|
goPrivate = [ "$(${pkgs.coreutils}/bin/cat ${config.age.secrets.work-go-private.path})" ];
|
||||||
GOPRIVATE = [ "$(${pkgs.coreutils}/bin/cat ${config.age.secrets.work-go-private.path})" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.git = lib.mkIf gitEnabled {
|
programs.git = lib.mkIf gitEnabled {
|
||||||
@@ -116,13 +64,6 @@ EOF
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Needed for `copilot`
|
|
||||||
programs.nix-ld.enable = true;
|
|
||||||
programs.nix-ld.libraries = [
|
|
||||||
pkgs.stdenv.cc.cc.lib
|
|
||||||
pkgs.zlib
|
|
||||||
];
|
|
||||||
|
|
||||||
services.openvpn.servers = lib.mkIf openvpnEnabled {
|
services.openvpn.servers = lib.mkIf openvpnEnabled {
|
||||||
work-staging = {
|
work-staging = {
|
||||||
config = "config ${config.age.secrets.work-staging-ovpn.path}";
|
config = "config ${config.age.secrets.work-staging-ovpn.path}";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
system,
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
@@ -10,7 +11,7 @@ let
|
|||||||
match =
|
match =
|
||||||
v: l: builtins.elemAt (lib.lists.findFirst (x: (if_let v (builtins.elemAt x 0)) != null) null l) 1;
|
v: l: builtins.elemAt (lib.lists.findFirst (x: (if_let v (builtins.elemAt x 0)) != null) null l) 1;
|
||||||
|
|
||||||
package = match { platform = pkgs.stdenv.hostPlatform.system; } [
|
package = match { platform = system; } [
|
||||||
[
|
[
|
||||||
{ platform = "aarch64-linux"; }
|
{ platform = "aarch64-linux"; }
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -56,14 +56,6 @@ in
|
|||||||
|
|
||||||
initContent = lib.strings.concatStringsSep "\n" [
|
initContent = lib.strings.concatStringsSep "\n" [
|
||||||
"export KEYTIMEOUT=1"
|
"export KEYTIMEOUT=1"
|
||||||
|
|
||||||
# Point every interactive shell at openssh's ssh-agent. home-manager's
|
|
||||||
# session vars set this too, but hm-session-vars runs once and is then
|
|
||||||
# inherited — so a tmux server that outlives this change (or started
|
|
||||||
# with the stale gcr socket) hands new panes a dead SSH_AUTH_SOCK.
|
|
||||||
# Re-exporting the fixed path per-shell keeps every pane on the same
|
|
||||||
# agent, so each key is only ever prompted for once per session.
|
|
||||||
''export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent"''
|
|
||||||
"bindkey -v '^?' backward-delete-char"
|
"bindkey -v '^?' backward-delete-char"
|
||||||
"bindkey '^a' beginning-of-line"
|
"bindkey '^a' beginning-of-line"
|
||||||
"bindkey '^e' end-of-line"
|
"bindkey '^e' end-of-line"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
inputs.agenix.packages."${pkgs.stdenv.hostPlatform.system}".default
|
inputs.agenix.packages."${pkgs.system}".default
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,6 @@
|
|||||||
group = "nginx";
|
group = "nginx";
|
||||||
};
|
};
|
||||||
|
|
||||||
"whib.ppp.pm" = {
|
|
||||||
webroot = "/var/lib/acme/acme-challenge/";
|
|
||||||
group = "nginx";
|
|
||||||
};
|
|
||||||
|
|
||||||
"api.whib.ppp.pm" = {
|
"api.whib.ppp.pm" = {
|
||||||
webroot = "/var/lib/acme/acme-challenge/";
|
webroot = "/var/lib/acme/acme-challenge/";
|
||||||
group = "nginx";
|
group = "nginx";
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ in
|
|||||||
|
|
||||||
pppdotpm-site.enable = true;
|
pppdotpm-site.enable = true;
|
||||||
whib-backend.enable = true;
|
whib-backend.enable = true;
|
||||||
whib-frontend.enable = true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,10 +67,6 @@ in
|
|||||||
HTTP_PORT = 3001;
|
HTTP_PORT = 3001;
|
||||||
};
|
};
|
||||||
|
|
||||||
oauth2 = {
|
|
||||||
JWT_CLAIM_ISSUER = "https://${gitDomain}/";
|
|
||||||
};
|
|
||||||
|
|
||||||
database = {
|
database = {
|
||||||
type = "sqlite3";
|
type = "sqlite3";
|
||||||
passwordFile = config.age.secrets.gitea-dbpassword.path;
|
passwordFile = config.age.secrets.gitea-dbpassword.path;
|
||||||
|
|||||||
@@ -28,17 +28,9 @@ in
|
|||||||
identityFile = "/home/alex/.ssh/alex.tadpole-git.ppp.pm";
|
identityFile = "/home/alex/.ssh/alex.tadpole-git.ppp.pm";
|
||||||
};
|
};
|
||||||
|
|
||||||
"*" = {
|
"codeberg.org" = {
|
||||||
forwardAgent = false;
|
hostname = "codeberg.org";
|
||||||
addKeysToAgent = "no";
|
identityFile = "/home/alex/.ssh/alex.tadpole-codeberg.org";
|
||||||
compression = false;
|
|
||||||
serverAliveInterval = 0;
|
|
||||||
serverAliveCountMax = 3;
|
|
||||||
hashKnownHosts = false;
|
|
||||||
userKnownHostsFile = "~/.ssh/known_hosts";
|
|
||||||
controlMaster = "no";
|
|
||||||
controlPath = "~/.ssh/master-%r@%n:%p";
|
|
||||||
controlPersist = "no";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -48,7 +40,6 @@ in
|
|||||||
mode = "0755";
|
mode = "0755";
|
||||||
text = ''
|
text = ''
|
||||||
#!${pkgs.bash}/bin/bash
|
#!${pkgs.bash}/bin/bash
|
||||||
[ "$1" = "alex" ] || exit 0
|
|
||||||
for file in ${authorizedKeysPath}/*; do
|
for file in ${authorizedKeysPath}/*; do
|
||||||
${pkgs.coreutils}/bin/cat "$file"
|
${pkgs.coreutils}/bin/cat "$file"
|
||||||
done
|
done
|
||||||
@@ -72,7 +63,7 @@ in
|
|||||||
KbdInteractiveAuthentication = false;
|
KbdInteractiveAuthentication = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
authorizedKeysCommand = "/etc/ssh/authorized_keys_command %u";
|
authorizedKeysCommand = "/etc/ssh/authorized_keys_command";
|
||||||
authorizedKeysCommandUser = "root";
|
authorizedKeysCommandUser = "root";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -110,6 +101,19 @@ in
|
|||||||
owner = "alex";
|
owner = "alex";
|
||||||
group = "users";
|
group = "users";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"alex.tadpole-codeberg.org" = {
|
||||||
|
file = ../../../../secrets/tadpole/alex.tadpole-codeberg.org.age;
|
||||||
|
path = "/home/alex/.ssh/alex.tadpole-codeberg.org";
|
||||||
|
owner = "alex";
|
||||||
|
group = "users";
|
||||||
|
};
|
||||||
|
"alex.tadpole-codeberg.org.pub" = {
|
||||||
|
file = ../../../../secrets/tadpole/alex.tadpole-codeberg.org.pub.age;
|
||||||
|
path = "/home/alex/.ssh/alex.tadpole-codeberg.org.pub";
|
||||||
|
owner = "alex";
|
||||||
|
group = "users";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,30 +4,24 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
backendEnabled = config.mod.whib-backend.enable;
|
enabled = config.mod.whib-backend.enable;
|
||||||
frontendEnabled = config.mod.whib-frontend.enable;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
mod.whib-backend = {
|
mod.whib-backend = {
|
||||||
enable = lib.mkEnableOption "enable WHIB backend";
|
enable = lib.mkEnableOption "enable WHIB backend";
|
||||||
};
|
};
|
||||||
|
|
||||||
mod.whib-frontend = {
|
|
||||||
enable = lib.mkEnableOption "enable WHIB frontend";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = lib.mkIf enabled {
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
assertion = backendEnabled && config.services.nginx.enable;
|
assertion = config.services.nginx.enable;
|
||||||
message = "Option 'config.services.nginx' must be enabled";
|
message = "Option 'config.services.nginx' must be enabled";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
services = {
|
services.whib-backend = {
|
||||||
whib-backend = lib.mkIf backendEnabled {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
backend = {
|
backend = {
|
||||||
@@ -56,15 +50,6 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
whib-frontend = lib.mkIf frontendEnabled {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
domain = "whib.ppp.pm";
|
|
||||||
useACMEHost = "whib.ppp.pm";
|
|
||||||
backendHost = "https://api.whib.ppp.pm";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
age.secrets = {
|
age.secrets = {
|
||||||
"whib-backend-env-vars".file = ../../../../secrets/tadpole/whib-backend-env-vars.age;
|
"whib-backend-env-vars".file = ../../../../secrets/tadpole/whib-backend-env-vars.age;
|
||||||
"whib-postgres-env-vars".file = ../../../../secrets/tadpole/whib-postgres-env-vars.age;
|
"whib-postgres-env-vars".file = ../../../../secrets/tadpole/whib-postgres-env-vars.age;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
imports = [
|
imports = [
|
||||||
./ppp.pm-site.nix
|
./ppp.pm-site.nix
|
||||||
./whib-backend.nix
|
./whib-backend.nix
|
||||||
./whib-frontend.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
@@ -14,7 +13,6 @@
|
|||||||
mod = {
|
mod = {
|
||||||
pppdotpm-site.enable = false;
|
pppdotpm-site.enable = false;
|
||||||
whib-backend.enable = true;
|
whib-backend.enable = true;
|
||||||
whib-frontend.enable = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.a = {
|
users.users.a = {
|
||||||
@@ -26,9 +24,9 @@
|
|||||||
security.sudo.wheelNeedsPassword = false;
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
virtualisation.vmVariant = {
|
virtualisation.vmVariant = {
|
||||||
# following configuration is added only when building VM the *first* time with `build-vm`
|
# following configuration is added only when building VM with build-vm
|
||||||
virtualisation = {
|
virtualisation = {
|
||||||
diskSize = 8192;
|
diskSize = 4096;
|
||||||
memorySize = 2048;
|
memorySize = 2048;
|
||||||
cores = 3;
|
cores = 3;
|
||||||
graphics = false;
|
graphics = false;
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
{ lib, config, ... }:
|
|
||||||
let
|
|
||||||
enabled = config.mod.whib-frontend.enable;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
mod.whib-frontend = {
|
|
||||||
enable = lib.mkEnableOption "enable WHIB backend";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf enabled {
|
|
||||||
services.whib-frontend = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
domain = "whib-frontend.local";
|
|
||||||
port = "8081";
|
|
||||||
# backendHost = "https://api.whib.ppp.pm/";
|
|
||||||
backendHost = "http://localhost:8080";
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.vmVariant = {
|
|
||||||
virtualisation = {
|
|
||||||
forwardPorts = [
|
|
||||||
{
|
|
||||||
# Service API
|
|
||||||
from = "host";
|
|
||||||
host.port = 8081;
|
|
||||||
guest.port = 8081;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 Pu0HWg eK/pdhmsF334C7rSuYsRnXCtenmlT2hOXpfW5CQEARY
|
||||||
|
odooTLu8ZQUZjCeVPZYOA6Vgb470cosE1Q1iBkE9Kc0
|
||||||
|
-> ssh-ed25519 +oNaHQ nJU52SSZ9v3+8NuXR6coSHosEYrs7T8GeZYzV/quOU4
|
||||||
|
IV5YduRGdJLy93gVwfYmwvldRXoXXX3QvAsH3ljBadw
|
||||||
|
--- 3gJg9NFmqHCrgcvgnYOeSY1H4klPEyzI+07IlKCOItc
|
||||||
|
¦Ì\5çܤ‰}õyñÐáAý_J§
õ“{ÂÉp±+ꜞ+¹JØeìñLìW¯å¯éµ7šÉ=S*³¥É�}fPܽқÒÉ!X©ê7úÎ3¡øœ³\`S•è‰iºözW¸À©<�4Pb™ˆ—€ZI’©hH˜îÃî¦61õ'™é…ybmCmE
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 wkRvNA mr8aDxrNmdcxT5BEXJ32Q4DckYKidh3zblrSm8GV3hg
|
|
||||||
JngH+sfId8Z6SuXnQo9EQR0nw+y7LfdgYgI7SYisPL4
|
|
||||||
-> ssh-ed25519 +oNaHQ QI1+VLIa1sN8HSzBXoAGio7TcfxpGERw30uNlMCmejw
|
|
||||||
m3+nrTwsAb/Fg1p9JCYnc7jS9uteMO3AbUtDbKP60Dk
|
|
||||||
--- etDNlalBL2SdgfFxIhDCAWXpXcSZr+BlCoTt6yIUiBQ
|
|
||||||
èZw œ»ØÁëkáÂÎó4=UÉÜ:J
|
|
||||||
ã–›½p#"ˆà^pŠî!KÚÔ
|
|
||||||
¯’cŽC÷ô´cd¾·"g.óÒ¦ó¥_(ÄA% ¶Ýp�ÇÏGA2`CqÖPš¢�P¾Ç$jåMººŒþ¾ƒ™_â
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 wkRvNA GW8mvnaXpspxr78xV0fKhXwHVvReyjvDc0v7uPwhuBI
|
|
||||||
Rne8JZYVhrTTesSFpRQ/IOZlFIMoX9Wmv5n1Ed7Ehv8
|
|
||||||
-> ssh-ed25519 +oNaHQ d7utzodGQ7LsD2Uht1rbT8Qq9BZp3PkJS9EDhajCjnk
|
|
||||||
qd2Vj+1TQrjEKkSVAf0cXcCdkgeN/Jbp4UrBSp3cKYQ
|
|
||||||
--- JQr5UQlutONqnTeoT/mIVZL8ME7ipUDK8zDfNcN3uhU
|
|
||||||
ø5-VŸÌ²ÂòÀ-®ªÁÅ'^žô5kú.t(d1‡)É'<u%
|
|
||||||
Binary file not shown.
@@ -1,7 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 wkRvNA 5bEWAlzS34w3tMqMCTRqFoni4MKyQCcaBdEQ96BccVk
|
|
||||||
85rIfjpzL/cNx5SlpKeOUzsMl0rwvCdPgrZ0jlXE1nQ
|
|
||||||
-> ssh-ed25519 +oNaHQ d2ZZ/XQ5fgT8FMwhyfWHB5FESXM0Y9tlE1KcWwQV7HY
|
|
||||||
bCj5fLqN2zfPBcnXaJAYJYA+PXTPAdLZVy2FinZvUTM
|
|
||||||
--- vxopGi8/YrgI/VceX807yk7edWrdYgjmGYG9Zp/cuQA
|
|
||||||
žFc˜ÖÂ|ª67Ñ~uðáúÉ€Wê–ÿ’eïó‰ÍVMj5‹ÜG?³JâË_pîcf57^ƒŒ¥r@g&�?„¬4ŠþÎðˆÉ‘@ƒN…œsjZ¡Šé^-âyB{\ºÔñ3«ˆýT)³ž¯uÝ\Wa\àìB)@d ™vrì8q.kÚ™˜
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 wkRvNA ykXbxHlehL+AucBCiS7NKNOWjHm51ZTbfgM7fPU+QDo
|
|
||||||
FtPBWg91yWq90n4PkzdwpIq+DnwL+nwUNzH3mQP4lgg
|
|
||||||
-> ssh-ed25519 +oNaHQ fCNig/NdBmbZqicNhSnYKz7Rmc7S2JpN+sQ50o+QFg0
|
|
||||||
AQiKSjZMsjuJGsnZWjSNePrbXBQ/f1zhNCXLznbZk1w
|
|
||||||
--- zjW49MAgjerHuHqMrSSDpgkWLD40eMSr+ZkKZ0Y9pJo
|
|
||||||
•؇¯O¹Þ°�}Œ/¨ïd°°
ü‹ªŠÒ°ø@²ÆuJð�Íz§bíïü�:Yñ‚fýúë›�
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 wkRvNA 1U7rIOHezvIpeb7QBkwz+NOgagrPDltkFtWY2N/hdE4
|
|
||||||
9rLqhxhc4c7cGB9hDPy9r0y5QbDp0CcYFwIWczB0mlU
|
|
||||||
-> ssh-ed25519 +oNaHQ w9QggF6DOTSMUC5n+7OKpFOM8iDSQakgx/10pIhM9ws
|
|
||||||
GOk7OQpLh5OjyZTiJQxd7hhN93EcSMnMxueNh6AwCOk
|
|
||||||
--- mzQrbeaXCVPu0MqNC0iAMLCiZbopyfYanwhsgDjFbZE
|
|
||||||
�¡&ÈEÆÝ¼�žÊÆ öfh(tJn´Ð†ç$^åkì[Cå
)ÉŸN¾UÚW¿üt/ræ7'NU
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,8 +0,0 @@
|
|||||||
age-encryption.org/v1
|
|
||||||
-> ssh-ed25519 wkRvNA 5d22LU+2Mn6fq8SHOCwDht/ebnI2uOk6WKf+t1kwwCM
|
|
||||||
hCGnLoCy1PX5PJx2IjQnyESmtKM6wVQmyS6aHNhkb1g
|
|
||||||
-> ssh-ed25519 +oNaHQ gPUMsavbGVPOuvTtNgoDuzrT+q0I7Wbkd6QK5z4oUGc
|
|
||||||
M3HhrugFlNQkL7WxF1qrW+ocGRqOXid32AVVYLSSxPI
|
|
||||||
--- TGURCDEIuSFCfXBHxzFHA2svHES7Ubagy1uYjbWCO7g
|
|
||||||
gá¿Ó†Ò™±£Qâö
|
|
||||||
oF[H:t aÆr3úZ0ßx�@:˜0Ó´¡µÂI[Õ\í=º@eâPí�W€Ã†§rX,¶âÈÇ*sš$c:�FlÎÙ±z|B#
päZ4ns[Ó×%\ìP±;ÞR㣧ô$9�¤7íÏÔAŠœÖ0©.�xç°��?»¾9©,ýt+ ¥Â šf±AÉ)mV
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
|
|||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 vxPbZg IKvbUY2e3wCrphoiFOKJYxyWvj4DjMlW1yv1VrZ/kBk
|
||||||
|
8sS9cNUsb8NtPHD5iKx8dfcgKxLObcZDfWJohduWEl4
|
||||||
|
-> ssh-ed25519 +oNaHQ nIY6nLvP+HIUlCumO/MxGoBGfutwfwv4zlvsLCuu2C8
|
||||||
|
eFqY9TaVsYoKulu5e++WI1zYzoBinEVUJt/lDan+ttQ
|
||||||
|
--- 67CbghCaK4yhjqH6vHEUzfeFth9PmX8h+qTKHocKGzk
|
||||||
|
iFÞ0ƒV•5œ1²·‹7‰¡~w&Õ´>¨Žƒ1¶bƒ…Å化ÆgŽ«cì6Fb�snÁ5àhk6¥W?0Ô@
ûÞó³|CѼÄm�ð›GÍ1‹.½
|
||||||
|
0Á¨#wŠ¢A°]‚½!é¡s%�´â Ðp
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user