Compare commits

..
18 Commits
Author SHA1 Message Date
Alexander Heldt babec6336f waybar: Pin battery module to BAT0
Bluetooth HID devices (e.g. a mouse) appearing as power_supply
entries in sysfs make waybar's Battery::refreshBatteries() throw
an uncaught exception and abort. Restricting to BAT0 avoids
enumerating those devices.
2026-09-01 10:07:07 +02:00
Alexander Heldt 5ec6f7d636 hyprland: Fix mouse binds, hl.bindm does not exist
Hyprland's lua API only registers hl.bind; there is no separate
hl.bindm. The settings.bindm key rendered as a literal hl.bindm(...)
call, which errored with "attempt to call a nil value" at runtime.
Fold the mouse binds into settings.bind with { mouse = true }, per
the wiki's own example config.
2026-08-26 14:14:07 +02:00
Alexander Heldt acebeaf180 hyprland: Migrate config to Lua, bump back to current
Hyprland 0.56.x dropped the hyprlang (.conf) config backend in favor
of Lua-only config. Switch configType to "lua" and rewrite monitors,
workspace/window rules, binds, env vars, and autostart using the new
hl.* dispatcher API. Un-pin the hyprland flake input (back to current,
following root nixpkgs) now that the config no longer depends on the
old hyprlang backend.

Every module that injects Hyprland keybinds (foot, wezterm, bemenu,
music, light, sound, screenshot, physlock, hyprlock) is converted to
the same Lua bind syntax, since the config is all-or-nothing per
configType. hyprlock's swayidle dpms toggle and the hyprland-monitors
hotplug script's hyprctl dispatch calls are updated to the new
hl.dsp.* expression syntax the same way.

One behavior change: hl.workspace_rule keeps only the last monitor set
per workspace (unlike hyprlang's rule list), so workspaces that used
to declare two candidate monitors now declare one static default;
the hyprland-monitors service still re-dispatches the correct monitor
on startup and hotplug.
2026-08-26 14:08:28 +02:00
Alexander Heldt 548507d60b flake: Pin hyprland to pre-0.56.0 rev, keep own nixpkgs
Hyprland 0.56.0 dropped the hyprlang (.conf) config backend entirely
in favor of a Lua-only config, breaking the deployed hyprland.conf on
pinwheel. Pin back to 79bc0ca (last hyprlang-supporting build) until
the config is migrated to Lua.

Also drop hyprland's nixpkgs.follows override: building the old
Hyprland rev against the current (newer) root nixpkgs caused a
libstdc++ ABI mismatch between the cached hyprtoolkit binary and a
freshly-compiled hyprland-guiutils.
2026-08-26 12:54:43 +02:00
Alexander Heldt 9d15eb0e9f Update flake inputs 2026-08-18 09:39:02 +00:00
Alexander Heldt 4dbd2ccded Update todo flake input 2026-08-18 09:32:39 +00:00
Alexander Heldt b5c2c8d187 flake: Flip todo input to git.ppp.pm gitea host 2026-08-16 14:34:57 +00:00
Alexander Heldt 34990eaaa4 manatee: Back up service state (databases etc.) via restic 2026-08-16 14:23:51 +00:00
Alexander Heldt f5d62b276d backwards: Remove syncthing, repoint pinwheel folders to manatee 2026-08-16 13:48:10 +00:00
Alexander Heldt 7a170ff59c manatee: Sync personal, work and phone-gps, peer phone to org 2026-08-16 13:41:52 +00:00
Alexander Heldt 0a5da7a293 backwards: Pin firefox legacy configPath 2026-08-16 14:19:59 +02:00
Alexander Heldt fbf8db1558 pinwheel: Manage claude settings through nix 2026-08-16 14:19:58 +02:00
Alexander Heldt 8eefa99ca8 tadpole: Use programs.ssh.settings 2026-08-16 14:19:58 +02:00
Alexander Heldt 0d2496c600 backwards: Use programs.ssh.settings 2026-08-16 14:19:58 +02:00
Alexander Heldt 81a1b9714f pinwheel: Use programs.ssh.settings 2026-08-16 14:19:58 +02:00
Alexander Heldt 77628fd6bb pinwheel: Pin firefox legacy configPath 2026-08-16 14:19:58 +02:00
Alexander Heldt 5aaf0fc7f8 pinwheel: Use beamPackages.erlang 2026-08-16 14:19:58 +02:00
Alexander Heldt f33c26a5e4 pinwheel: Enable programs.fuse for sshfs 2026-08-16 14:19:58 +02:00
30 changed files with 750 additions and 390 deletions
Generated
+63 -63
View File
@@ -43,11 +43,11 @@
]
},
"locked": {
"lastModified": 1782073106,
"narHash": "sha256-dnS5SaZlPqR1E0dPXaPc+lFkBwLUbAgbwsVMk7uA6dY=",
"lastModified": 1787400831,
"narHash": "sha256-H3MEkFDZf+UH+QrVgW8TdKrssPFltF8fBg/rh0J1zIc=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "6d6e2384f381def4ea4ea81543cba4bbdac72457",
"rev": "7ce889cb78b97979b83a4648509fc3ef405c3286",
"type": "github"
},
"original": {
@@ -106,11 +106,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1784109865,
"narHash": "sha256-UCJ0O811UeGSe3GlLj8QxVa4ykHRfjl9rZvqFpiECME=",
"lastModified": 1787018211,
"narHash": "sha256-6l9VDWVP2ePuI/8zSXLY4+50BKJpd1IF1REYKsUoxls=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "422352494e740d44d7551549916655b122a9fd01",
"rev": "a65bcf36c4d8aa4c5ff51d571f2a2c2555681dad",
"type": "github"
},
"original": {
@@ -251,11 +251,11 @@
]
},
"locked": {
"lastModified": 1783963347,
"narHash": "sha256-r376E2XpakiXwModDHIxlvB6qLq4iFVEq730vxOO4JY=",
"lastModified": 1786999651,
"narHash": "sha256-MTGMFlLDTklsXhCp4r5GXB4VAVadPdalXLvUjd/K7h0=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "a45a7c451455a51ae740ec3bce4024b312809c29",
"rev": "353742587cbaf079b3caee743115d037bc51fea6",
"type": "github"
},
"original": {
@@ -280,11 +280,11 @@
]
},
"locked": {
"lastModified": 1776511930,
"narHash": "sha256-fCpwFiTW0rT7oKJqr3cqHMnkwypSwQKpbtUEtxdkgrM=",
"lastModified": 1786464181,
"narHash": "sha256-2alOMkLjXANh7unkZnYnCF2K2rApZaOLMoQ3o+VX2CY=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "39435900785d0c560c6ae8777d29f28617d031ef",
"rev": "e4ed7c08123df5af460a0a70961380cbfb872f76",
"type": "github"
},
"original": {
@@ -309,11 +309,11 @@
]
},
"locked": {
"lastModified": 1782566056,
"narHash": "sha256-haEZcHzYrePnjFOYSWTbxm/Nrla0aPslJfmvdCvqtVc=",
"lastModified": 1786464367,
"narHash": "sha256-k58p4wbzIXWyRWrW84pP8tD+iaZSSYiiM+fr0Auk4oU=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "c6e7b9f673f4360bc813d3dc75028f75ee88d3f8",
"rev": "7c895c44e3ca6d28ed68ddd80ec02b02b925e7fc",
"type": "github"
},
"original": {
@@ -341,11 +341,11 @@
"xdph": "xdph"
},
"locked": {
"lastModified": 1784043142,
"narHash": "sha256-vd4VSlddmDAToJv/twyr0O8Siq4U/sOIXeo6DFZIolc=",
"lastModified": 1787612295,
"narHash": "sha256-K7sUeS1tKTSDu+aQK0ZwCxJCls+JzDj1qeTJRGk+CV8=",
"owner": "hyprwm",
"repo": "Hyprland",
"rev": "79bc0ca16e7cca40c247a9200634d150fa8193d1",
"rev": "0bd11c7a04a63d2785abd53363f09d552175d67d",
"type": "github"
},
"original": {
@@ -407,11 +407,11 @@
]
},
"locked": {
"lastModified": 1782563850,
"narHash": "sha256-rs/EzgrgPHbCtJjFZN4aR1HYldH/0NtGAempWVpWQTs=",
"lastModified": 1786464504,
"narHash": "sha256-7sHwM86KILQyHDHDuE2SDBlQ2jvZ0EW3hY7sW009/cg=",
"owner": "hyprwm",
"repo": "hyprland-guiutils",
"rev": "5ba080ee036c30cb2485f2647ff8a61f7aa08178",
"rev": "4c30cf3097ea963c0e250749ee0c59f8b08816d6",
"type": "github"
},
"original": {
@@ -461,11 +461,11 @@
]
},
"locked": {
"lastModified": 1777320127,
"narHash": "sha256-Qu+Wf2Bp5qUjyn2YpZNq8a7JyzTGowhT1knrwE38a9U=",
"lastModified": 1786464129,
"narHash": "sha256-339AkTlpMYSIvFuG0rnR+8Yg4/AZKeJalshJavlnKfg=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "090117506ddc3d7f26e650ff344d378c2ec329cc",
"rev": "9508458be316a0d70d37ebed1ab725ccd10411ff",
"type": "github"
},
"original": {
@@ -513,11 +513,11 @@
]
},
"locked": {
"lastModified": 1782554491,
"narHash": "sha256-+p3MlyN/nqRefcf2IckPlGRUn9+hielqpS9XClbLleM=",
"lastModified": 1785930473,
"narHash": "sha256-DitTu625BhEYpZjtjxtGpjrEJwPwW+X/+jJvhSZNSJM=",
"owner": "hyprwm",
"repo": "hyprtoolkit",
"rev": "bdba25ced39ea39ab004a8f31593ba0b0ff1ca35",
"rev": "af515b69dfbe366dc7873aa1475cb2f4db3ebad7",
"type": "github"
},
"original": {
@@ -538,11 +538,11 @@
]
},
"locked": {
"lastModified": 1783002634,
"narHash": "sha256-xGqHIUK0wIZoW7SiMalwvO6uGOO/VrlQwoRobpE7dDI=",
"lastModified": 1786903207,
"narHash": "sha256-QTwMqLLONRhv9iz6CVeuX6BqQNQCIqI8hL/cPYlR/24=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "41fb809557abd29a57151b6e1aaeabd05f9437e1",
"rev": "6cf50415e06dc6bd9f1252f1b745eac6b4a1cc39",
"type": "github"
},
"original": {
@@ -563,11 +563,11 @@
]
},
"locked": {
"lastModified": 1777159683,
"narHash": "sha256-Jxixw6wZphUp+nHYxOKUYSckL17QMBx2d5Zp0rJHr1g=",
"lastModified": 1786464033,
"narHash": "sha256-QM8Qe4/L8lpdVN4bgwahmi+jyyc4fisseDMe4afcDxA=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "b8632713a6beaf28b56f2a7b0ab2fb7088dbb404",
"rev": "62e62c1ca23da17612c6890d4ad2064f575643db",
"type": "github"
},
"original": {
@@ -592,11 +592,11 @@
]
},
"locked": {
"lastModified": 1778410714,
"narHash": "sha256-o6RzFj4nJXaPRY7EM01siuCQeT41RfwwmcmFQqwFJJg=",
"lastModified": 1786464294,
"narHash": "sha256-ZQsZ2WvBdkboCIyh8LStDPdAIARmxzn0XMNxxoOhjPE=",
"owner": "hyprwm",
"repo": "hyprwire",
"rev": "85148a8e612808cf5ddb25d0b3c5840f3498a7dc",
"rev": "4ce7cd6b6128c1ac41caf23c58a30a26b327f9dd",
"type": "github"
},
"original": {
@@ -677,11 +677,11 @@
]
},
"locked": {
"lastModified": 1773476909,
"narHash": "sha256-hSg/7xFO+3G3wWFq1480OecREqTY+fu06L06rM2UBmQ=",
"lastModified": 1785952180,
"narHash": "sha256-iDuEHW+ll+wdKEfbhFaQQ0vsE0/Nl+TS1osI947cv5w=",
"owner": "detoxify92",
"repo": "naviterm",
"rev": "f89dbde00222fb1e4f611419d05583d8edee4c25",
"rev": "82ec8adc0a9dbb781ddae409bb19269936559f7f",
"type": "gitlab"
},
"original": {
@@ -733,11 +733,11 @@
"systems": "systems_5"
},
"locked": {
"lastModified": 1783761716,
"narHash": "sha256-1I1HEeTEBHcBy/jIO2l5NiJJZSqrQSMjwKo9A9ONZr4=",
"lastModified": 1786782246,
"narHash": "sha256-D8uy48k++htZCZF13wZJ9jcxFmI6G7uId1pSj0wD7rw=",
"owner": "nix-community",
"repo": "nix-jetbrains-plugins",
"rev": "dc3c00516880f4fd82671bea4b3500451b29958d",
"rev": "35359b4f515f165bb2a11c5de793a282daa78c03",
"type": "github"
},
"original": {
@@ -751,11 +751,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1784100666,
"narHash": "sha256-HF/mrw5NYQfKFZgkfV2h1UL5/E3ccfsE2XJ1AbbLLJ0=",
"lastModified": 1786867632,
"narHash": "sha256-ez+ubZlA1RtdjCB18a6zJ9M4u8qoPDy08EcnsW5M3Xw=",
"owner": "nixos",
"repo": "nixos-hardware",
"rev": "fccfa9031a85b78a437f2f153c1f6449f3bc3185",
"rev": "ff17823245ab9ff7bcae6acf950bd89cba82c38c",
"type": "github"
},
"original": {
@@ -783,11 +783,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1784011430,
"narHash": "sha256-lDebytrYdd47IBLwvNOD+6AGeoqZ78CIKlp70hzW280=",
"lastModified": 1786943417,
"narHash": "sha256-b4qgjdFtlz5TAZ1Hi7DFJeqX3sjaDUnrwr9OO+O1rM0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8eeec934ae0dbeca3d7868c059568a65c08b2fc3",
"rev": "0dd31db7e6dbf9ce05697c4545f6fe01accec994",
"type": "github"
},
"original": {
@@ -812,11 +812,11 @@
},
"nixpkgs_3": {
"locked": {
"lastModified": 1784007870,
"narHash": "sha256-djcLt/JJphyNt4eDY9XTly+/WbCK5lqWq9lSgCmJkkQ=",
"lastModified": 1786862985,
"narHash": "sha256-FBJRXmbGXiSUDvYEbfLYRkckayyZ6SK1UEqhCrIZ2Cs=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "18b9261cb3294b6d2a06d03f96872827b8fe2698",
"rev": "e5bdc4a41d4c072fe1e3787eaa0320a384741d44",
"type": "github"
},
"original": {
@@ -856,11 +856,11 @@
]
},
"locked": {
"lastModified": 1783008725,
"narHash": "sha256-jGiy6+sxjNWXSjp25uoJuNfyH9zBK1PEDY0lVoL4ibQ=",
"lastModified": 1784288435,
"narHash": "sha256-ReRHaLgr/uVqdD8afFSn+myXIfpHeOhP0yYe0TJqAA8=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "bca82caa46d5ec0f5d422c61fb1e30bc51313cbe",
"rev": "43b3c1ab9d40fb1dbb008f451988a91e375825e9",
"type": "github"
},
"original": {
@@ -1050,18 +1050,18 @@
]
},
"locked": {
"lastModified": 1786622639,
"narHash": "sha256-q28DCpRkhuFcysdWNio4mIsR5VlonXbM8G+DyxitRPo=",
"lastModified": 1787045443,
"narHash": "sha256-zgpME9KjeRyp2teHkuJUrOUIsWUd1QrCRxUy7GuS41w=",
"ref": "main",
"rev": "a43e443720c3cc3fc38e9367bdac32e6f046c238",
"revCount": 17,
"rev": "3bef06edde9fed897ce7f4c0c3c1b93945fd1e03",
"revCount": 28,
"type": "git",
"url": "file:///home/alex/code/todo"
"url": "ssh://gitea@git.ppp.pm:1122/alex/todo.git"
},
"original": {
"ref": "main",
"type": "git",
"url": "file:///home/alex/code/todo"
"url": "ssh://gitea@git.ppp.pm:1122/alex/todo.git"
}
},
"whib-backend": {
@@ -1134,11 +1134,11 @@
]
},
"locked": {
"lastModified": 1782644412,
"narHash": "sha256-/iSa/bL1QQFLv+uJ9gI0N87J8gOeZXvca7EjoPGKE6w=",
"lastModified": 1786988229,
"narHash": "sha256-frEFLVRj8xXvBBDs44IRiqHo6R2PxsRpluygL7abjjI=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "c01c99fc278ec68c82e9865923088f043c7c1621",
"rev": "59d429bf45aed4e2209043c0c36565ad8e2859a5",
"type": "github"
},
"original": {
+2 -5
View File
@@ -100,11 +100,8 @@
};
todo = {
# Not yet pushed to gitea; using the local git checkout for now (git+file
# respects .gitignore, so web/node_modules isn't copied into the store).
# Flip these once the repo is published, matching the other apps above.
# url = "git+ssh://gitea@git.ppp.pm:1122/alex/todo.git?ref=main";
url = "git+file:///home/alex/code/todo?ref=main";
url = "git+ssh://gitea@git.ppp.pm:1122/alex/todo.git?ref=main";
# url = "git+file:///home/alex/code/todo?ref=main";
inputs.nixpkgs.follows = "nixpkgs";
};
};
-1
View File
@@ -2,7 +2,6 @@
{
imports = [
../../config-manager/default.nix
../../shared-modules/syncthing.nix
./hardware-configuration.nix
./modules
];
-1
View File
@@ -13,7 +13,6 @@ in
ssh.enable = true;
git.enable = true;
nginx.enable = true;
syncthing.enable = true;
};
};
}
@@ -46,6 +46,12 @@ in
package = wrapped;
# Pin the legacy profile location. The default changed to the XDG path
# (`$XDG_CONFIG_HOME/mozilla/firefox`) for stateVersion >= "26.05";
# keep the old `~/.mozilla/firefox` so the existing profile isn't
# stranded (migrating would require moving the profile dir by hand).
configPath = ".mozilla/firefox";
profiles = {
alex = {
id = 0;
+18 -17
View File
@@ -21,31 +21,32 @@ in
home-manager.users.alex = {
programs.ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks = {
settings = {
"manatee" = {
hostname = "manatee";
user = "alex";
identityFile = "/home/alex/.ssh/alex.backwards-manatee";
port = 1122;
HostName = "manatee";
User = "alex";
IdentityFile = "/home/alex/.ssh/alex.backwards-manatee";
Port = 1122;
};
"git.ppp.pm" = {
hostname = "git.ppp.pm";
identityFile = "/home/alex/.ssh/alex.backwards-git.ppp.pm";
HostName = "git.ppp.pm";
IdentityFile = "/home/alex/.ssh/alex.backwards-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";
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";
};
};
};
@@ -1,109 +0,0 @@
{ lib, config, ... }:
let
enabled = config.mod.syncthing.enable;
in
{
options = {
mod.syncthing = {
enable = lib.mkEnableOption "Enable syncthing module";
};
};
config = lib.mkIf enabled {
services.syncthing = {
enable = true;
openDefaultPorts = true;
cert = config.age.secrets.syncthing-cert.path;
key = config.age.secrets.syncthing-key.path;
user = "alex";
group = "users";
dataDir = "/home/alex/sync";
guiAddress = "0.0.0.0:8384";
settings = {
gui = {
user = "syncthing";
password = "$2a$12$J/h/JOUiW24ZXsLYLEl2kOZUS1LftxANi0OlZxLy8Dst3/jpBd0v2";
insecureSkipHostcheck = false;
};
devices = {
phone.id = config.lib.syncthing.phone;
pinwheel.id = config.lib.syncthing.pinwheel;
tablet.id = config.lib.syncthing.tablet;
};
folders = {
org = {
path = "/home/alex/sync/org";
devices = [
"phone"
"pinwheel"
];
versioning = {
type = "staggered";
params = {
maxage = "2592000"; # 30 days
};
};
};
personal = {
path = "/home/alex/sync/personal";
devices = [ "pinwheel" ];
versioning = {
type = "staggered";
params = {
maxAge = "2592000"; # 30 days
};
};
};
work = {
path = "/home/alex/sync/work";
devices = [ "pinwheel" ];
versioning = {
type = "staggered";
params = {
maxAge = "2592000"; # 30 days
};
};
};
books = {
path = "/home/alex/sync/reading-material/books";
devices = [ "pinwheel" ];
versioning = {
type = "staggered";
params = {
maxAge = "2592000"; # 30 days
};
};
};
"phone-gps" = {
path = "/home/alex/sync/phone-gps";
devices = [ "phone" ];
versioning = {
type = "staggered";
params = {
maxage = "2592000"; # 30 days
};
};
};
};
};
};
age = {
secrets = {
"syncthing-cert".file = ../../../../secrets/backwards/syncthing-cert.age;
"syncthing-key".file = ../../../../secrets/backwards/syncthing-key.age;
};
};
};
}
+164 -10
View File
@@ -1,6 +1,121 @@
{ lib, config, ... }:
{
pkgs,
lib,
config,
...
}:
let
enabled = config.mod.restic.enable;
# Where the consistent, restic-ready copies of service state are staged.
# Lives on the external backup disk. root-only (0700): these dumps contain
# credentials (HA .storage, secrets.yaml, session tokens, the romm database).
stateDir = "/mnt/backup/public/state";
# Produce consistent, restore-ready copies of every service's state under
# ${stateDir}. Runs as root before the state backups. SQLite DBs are copied
# via the online-backup API (safe on a live, WAL-mode DB); the romm MariaDB is
# dumped logically with mariadb-dump --single-transaction. Rebuildable caches
# and indexes are skipped to keep the offsite copy lean.
stateDumpScript = pkgs.writeShellScript "state-backup-dump" ''
export PATH=${
lib.makeBinPath [
pkgs.sqlite
pkgs.rsync
pkgs.podman
pkgs.coreutils
pkgs.findutils
]
}
set -euo pipefail
umask 077
STATE=${lib.escapeShellArg stateDir}
install -d -m700 "$STATE"
# snapshot SRC DEST [extra rsync excludes...]
# Copies plain files with rsync (excluding live DBs, their WAL/SHM sidecars
# and logs), then makes a consistent online copy of each *.db / *.sqlite.
snapshot() {
local src="$1" dst="$2"
shift 2
if [ ! -e "$src" ]; then
echo "state-backup: skip (missing) $src"
return 0
fi
install -d -m700 "$dst"
rsync -a --delete \
--exclude='*.sqlite' --exclude='*.sqlite-shm' --exclude='*.sqlite-wal' \
--exclude='*.db' --exclude='*.db-shm' --exclude='*.db-wal' \
--exclude='*.log' --exclude='*.log.*' \
--exclude='__pycache__/' \
"$@" \
"$src/" "$dst/"
while IFS= read -r db; do
local rel="''${db#"$src"/}"
install -d -m700 "$dst/$(dirname "$rel")"
sqlite3 "$db" ".backup '$dst/$rel'"
done < <(find "$src" -maxdepth 4 -type f \( -name '*.sqlite' -o -name '*.db' \))
}
# --- romm: logical MariaDB dump + user assets (saves/states/screenshots) ---
# Cover art under resources/ is intentionally skipped (refetched from
# metadata providers); redis is a rebuildable cache.
if podman exec romm-db true 2>/dev/null; then
install -d -m700 "$STATE/romm"
podman exec romm-db sh -c \
'exec mariadb-dump --user="$MARIADB_USER" --password="$MARIADB_PASSWORD" --single-transaction --no-tablespaces "$MARIADB_DATABASE"' \
> "$STATE/romm/romm.sql"
rsync -a --delete /var/lib/romm/assets/ "$STATE/romm/assets/"
else
echo "state-backup: skip (romm-db not running)"
fi
# --- komga: only database.sqlite matters (read progress, users, collections).
# tasks.sqlite is transient and the lucene/ index is rebuilt on demand. ---
if [ -e /var/lib/komga/database.sqlite ]; then
install -d -m700 "$STATE/komga"
sqlite3 /var/lib/komga/database.sqlite ".backup '$STATE/komga/database.sqlite'"
fi
# --- home-assistant: config, credentials and integrations, plus the two live
# SQLite DBs. Recorder history (home-assistant_v2.db) is included per choice.
# deps/ and tts/ are reinstalled/regenerated; logs are dropped. ---
HA=/home/alex/.config/home-assistant
if [ -e "$HA" ]; then
install -d -m700 "$STATE/home-assistant"
rsync -a --delete \
--exclude='home-assistant_v2.db' --exclude='home-assistant_v2.db-shm' --exclude='home-assistant_v2.db-wal' \
--exclude='zigbee.db' --exclude='zigbee.db-shm' --exclude='zigbee.db-wal' \
--exclude='*.log' --exclude='*.log.*' --exclude='*.log.fault' \
--exclude='deps/' --exclude='tts/' --exclude='__pycache__/' \
"$HA/" "$STATE/home-assistant/"
for db in home-assistant_v2.db zigbee.db; do
if [ -e "$HA/$db" ]; then
sqlite3 "$HA/$db" ".backup '$STATE/home-assistant/$db'"
fi
done
fi
# --- remaining NixOS services: generic snapshot, skipping rebuildable caches ---
snapshot /var/lib/navidrome "$STATE/navidrome" --exclude='cache/'
snapshot /var/lib/jellyfin "$STATE/jellyfin" --exclude='transcodes/' --exclude='cache/' --exclude='metadata/' --exclude='log/' --exclude='data/subtitles/'
snapshot /var/lib/audiobookshelf "$STATE/audiobookshelf" --exclude='metadata/'
snapshot /var/lib/puppy-tracker "$STATE/puppy-tracker"
snapshot /var/lib/solo-referee "$STATE/solo-referee"
snapshot /var/lib/todo "$STATE/todo"
snapshot /var/lib/komga-book-manager "$STATE/komga-book-manager"
snapshot /var/lib/komga-reading-stats "$STATE/komga-reading-stats"
echo "state-backup: dump complete"
'';
# Retention shared by all jobs.
pruneOpts = [
"--keep-daily 1"
"--keep-weekly 7"
"--keep-yearly 12"
];
in
{
options.mod.restic.enable = lib.mkEnableOption "Enable restic";
@@ -22,11 +137,7 @@ in
OnCalendar = "*-*-* 0/12:00:00";
Persistent = true;
};
pruneOpts = [
"--keep-daily 1"
"--keep-weekly 7"
"--keep-yearly 12"
];
inherit pruneOpts;
};
"sync-to-cloud" = {
@@ -39,14 +150,57 @@ in
OnCalendar = "*-*-* 0/12:00:00";
Persistent = true;
};
pruneOpts = [
"--keep-daily 1"
"--keep-weekly 7"
"--keep-yearly 12"
inherit pruneOpts;
};
# Service state (databases etc.). No timer of their own: the state-backup
# orchestrator below runs the dump once and then triggers these in order,
# so both back up the same consistent staging dir without racing it.
"state-to-external" = {
initialize = true;
passwordFile = config.age.secrets.restic-password.path;
paths = [ stateDir ];
repository = "/mnt/backup/restic";
timerConfig = null;
inherit pruneOpts;
};
"state-to-cloud" = {
initialize = true;
passwordFile = config.age.secrets.restic-password.path;
environmentFile = config.age.secrets.restic-cloud-sync-key.path;
repositoryFile = config.age.secrets.restic-cloud-sync-repository.path;
paths = [ stateDir ];
timerConfig = null;
inherit pruneOpts;
};
};
# Dump service state, then push it to the external disk and the cloud, in
# that order. A "-" prefix means a failure of one restic target does not
# block the other; a failed dump (no prefix) aborts before either runs.
systemd.services.state-backup = {
description = "Dump service state and back it up (external + cloud)";
after = [ "podman.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = [
"${stateDumpScript}"
"-${pkgs.systemd}/bin/systemctl start --wait restic-backups-state-to-external.service"
"-${pkgs.systemd}/bin/systemctl start --wait restic-backups-state-to-cloud.service"
];
};
};
systemd.timers.state-backup = {
description = "Timer for service-state backups";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*-*-* 0/12:00:00";
Persistent = true;
};
};
age.secrets = {
"restic-password".file = ../../../../secrets/manatee/restic-password.age;
"restic-cloud-sync-key".file = ../../../../secrets/manatee/restic-cloud-sync-key.age;
@@ -39,6 +39,7 @@ in
};
devices = {
phone.id = config.lib.syncthing.phone;
pinwheel.id = config.lib.syncthing.pinwheel;
};
@@ -46,6 +47,7 @@ in
org = {
path = "/mnt/sync/public/org";
devices = [
"phone"
"pinwheel"
];
versioning = {
@@ -55,6 +57,39 @@ in
};
};
};
personal = {
path = "/mnt/sync/public/personal";
devices = [ "pinwheel" ];
versioning = {
type = "staggered";
params = {
maxage = "2592000"; # 30 days
};
};
};
work = {
path = "/mnt/sync/public/work";
devices = [ "pinwheel" ];
versioning = {
type = "staggered";
params = {
maxage = "2592000"; # 30 days
};
};
};
"phone-gps" = {
path = "/mnt/sync/public/phone-gps";
devices = [ "phone" ];
versioning = {
type = "staggered";
params = {
maxage = "2592000"; # 30 days
};
};
};
};
};
};
+38
View File
@@ -6,6 +6,41 @@
}:
let
enabled = config.mod.ai.enable;
# Shared statusline: show the logged-in account email.
statusLine = {
type = "command";
command = "jq -r '.oauthAccount.emailAddress' \"$CLAUDE_CONFIG_DIR/.claude.json\"";
};
# PostToolUse hook: echoes a JSON payload that injects a comment-policy
# reminder as additional context after every Write/Edit.
commentPolicyPayload = builtins.toJSON {
suppressOutput = true;
hookSpecificOutput = {
hookEventName = "PostToolUse";
additionalContext = "Comment policy: default to ZERO code comments. Review what you just wrote and delete any comment that narrates the change, restates the code, states the obvious, is tangential, or explains design/why rationale -- put that reasoning in your chat reply instead, never in the code. Never use em dashes or en dashes anywhere in file content; use a plain ASCII hyphen or reword.";
};
};
settings = builtins.toJSON {
inherit statusLine;
theme = "dark";
editorMode = "vim";
hooks = {
PostToolUse = [
{
matcher = "Write|Edit";
hooks = [
{
type = "command";
command = "echo ${lib.escapeShellArg commentPolicyPayload}";
}
];
}
];
};
};
in
{
options = {
@@ -24,6 +59,9 @@ in
wclaude = "CLAUDE_CONFIG_DIR=$HOME/.claude-personal claude";
pclaude = "CLAUDE_CONFIG_DIR=$HOME/.claude-work claude";
};
home.file.".claude-work/settings.json".text = settings;
home.file.".claude-personal/settings.json".text = settings;
};
};
}
+8 -1
View File
@@ -28,7 +28,14 @@ in
wayland.windowManager.hyprland = lib.mkIf hyprlandEnabled {
settings = {
bind = [ "$mod, SPACE, exec, ${bmr}" ];
bind = [
{
_args = [
"SUPER + SPACE"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${bmr}")'')
];
}
];
};
};
};
@@ -53,6 +53,12 @@ in
package = wrapped;
# Pin the legacy profile location. The default changed to the XDG path
# (`$XDG_CONFIG_HOME/mozilla/firefox`) for stateVersion >= "26.05";
# keep the old `~/.mozilla/firefox` so the existing profile isn't
# stranded (migrating would require moving the profile dir by hand).
configPath = ".mozilla/firefox";
profiles = {
alex = {
id = 0;
+6 -1
View File
@@ -33,7 +33,12 @@ in
wayland.windowManager.hyprland = lib.mkIf hyprlandEnabled {
settings = {
bind = [
"$mod, RETURN, exec, ${pkgs.foot}/bin/foot"
{
_args = [
"SUPER + RETURN"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${pkgs.foot}/bin/foot")'')
];
}
];
};
};
+1 -1
View File
@@ -18,7 +18,7 @@ in
home-manager.users.alex = {
home.packages = [
pkgs.gleam
pkgs.erlang
pkgs.beamPackages.erlang
];
};
};
+244 -84
View File
@@ -8,6 +8,8 @@
let
enabled = config.mod.hyprland.enable;
mod = "SUPER";
monitorScript = pkgs.writeShellScript "hyprland-monitor-handler" ''
INTERNAL="eDP-1"
EXTERNAL_MONITORS="HDMI-A-1 DP-3"
@@ -31,15 +33,15 @@ let
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;"
batch="$batch dispatch hl.dsp.workspace.move({ monitor = \"$external\", workspace = $ws });"
done
for ws in 6 7 8 9 10; do
batch="$batch dispatch moveworkspacetomonitor $ws $INTERNAL;"
batch="$batch dispatch hl.dsp.workspace.move({ monitor = \"$INTERNAL\", workspace = $ws });"
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;"
batch="$batch dispatch hl.dsp.workspace.move({ monitor = \"$INTERNAL\", workspace = $ws });"
done
fi
@@ -97,59 +99,11 @@ in
wayland.windowManager.hyprland = {
enable = true;
systemd.enable = false;
configType = "hyprlang";
extraConfig = ''
exec-once = uwsm app -- waybar
exec-once = uwsm app -- hyprctl setcursor Adwaita 24
env = GDK_DPI_SCALE,1.5
env = HYPRCURSOR_THEME,Adwaita
env = HYPRCURSOR_SIZE,24
monitor=eDP-1, 1920x1200, auto-center-down, 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, default:true
workspace = 2, monitor:HDMI-A-1
workspace = 3, monitor:HDMI-A-1
workspace = 4, monitor:HDMI-A-1
workspace = 5, monitor:HDMI-A-1
workspace = 1, monitor:DP-3, default:true
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 = 8, monitor:eDP-1
workspace = 9, monitor:eDP-1
workspace = 10, monitor:eDP-1
workspace = w[tv1], gapsout:0, gapsin:0
workspace = f[1], gapsout:0, gapsin:0
windowrule = border_size 0, match:float 0, match:workspace w[tv1]
windowrule = rounding 0, match:float 0, match:workspace w[tv1]
windowrule = border_size 0, match:float 0, match:workspace f[1]
windowrule = rounding 0, match:float 0, match:workspace f[1]
# https://wiki.archlinux.org/title/Hyprland#Jetbrains_apps_focus_issues
windowrule = match:xwayland true, no_initial_focus on
'';
configType = "lua";
settings = {
"$mod" = "SUPER";
animations.enabled = false;
xwayland = {
force_zero_scaling = true;
};
config = {
xwayland.force_zero_scaling = true;
input = {
kb_layout = "se";
@@ -161,7 +115,7 @@ in
accel_profile = "flat";
touchpad = {
natural_scroll = false;
tap-and-drag = false;
tap_and_drag = false;
};
};
@@ -176,12 +130,149 @@ in
gaps_in = 0; # gaps between windows
gaps_out = 0; # gaps between windows and monitor edges
"col.active_border" = "rgba(${config.lib.colors.foreground}ff)";
"col.inactive_border" = "rgba(${config.lib.colors.background}ff)";
col = {
active_border = "rgba(${config.lib.colors.foreground}ff)";
inactive_border = "rgba(${config.lib.colors.background}ff)";
};
};
dwindle = {
force_split = 2;
dwindle.force_split = 2;
misc = {
disable_hyprland_logo = true;
disable_splash_rendering = true;
};
animations.enabled = false;
};
monitor = [
{
output = "eDP-1";
mode = "1920x1200";
position = "auto-center-down";
scale = "1";
}
{
output = "HDMI-A-1";
mode = "2560x1440@100";
position = "auto-center-up";
scale = "1";
}
{
output = "DP-3";
mode = "2560x1440@60";
position = "auto-center-up";
scale = "1";
}
];
# Static defaults only: a workspace can only have one `monitor` in
# hl.workspace_rule (later calls overwrite it, unlike hyprlang's
# rule list). The hyprland-monitors service re-dispatches these to
# whichever external monitor is actually connected at startup and
# on hotplug, so this only matters for the brief window before it
# runs.
workspace_rule = [
{
workspace = "1";
monitor = "HDMI-A-1";
default = true;
}
{
workspace = "2";
monitor = "HDMI-A-1";
}
{
workspace = "3";
monitor = "HDMI-A-1";
}
{
workspace = "4";
monitor = "HDMI-A-1";
}
{
workspace = "5";
monitor = "HDMI-A-1";
}
{
workspace = "6";
monitor = "eDP-1";
default = true;
}
{
workspace = "7";
monitor = "eDP-1";
}
{
workspace = "8";
monitor = "eDP-1";
}
{
workspace = "9";
monitor = "eDP-1";
}
{
workspace = "10";
monitor = "eDP-1";
}
{
workspace = "w[tv1]";
gaps_out = 0;
gaps_in = 0;
}
{
workspace = "f[1]";
gaps_out = 0;
gaps_in = 0;
}
];
window_rule = [
{
name = "no-gaps-wtv1";
match = {
float = false;
workspace = "w[tv1]";
};
border_size = 0;
rounding = 0;
}
{
name = "no-gaps-f1";
match = {
float = false;
workspace = "f[1]";
};
border_size = 0;
rounding = 0;
}
{
# https://wiki.archlinux.org/title/Hyprland#Jetbrains_apps_focus_issues
name = "xwayland-no-initial-focus";
match.xwayland = true;
no_initial_focus = true;
}
];
env = [
{ _args = [ "GDK_DPI_SCALE" "1.5" ]; }
{ _args = [ "HYPRCURSOR_THEME" "Adwaita" ]; }
{ _args = [ "HYPRCURSOR_SIZE" "24" ]; }
];
on = {
_args = [
"hyprland.start"
(lib.generators.mkLuaInline ''
function()
hl.exec_cmd("uwsm app -- waybar")
hl.exec_cmd("uwsm app -- hyprctl setcursor Adwaita 24")
end
'')
];
};
bind =
@@ -193,10 +284,23 @@ in
in
builtins.toString n;
select = builtins.genList (x: "$mod, ${ws x}, workspace, ${builtins.toString (x + 1)}") 10;
move = builtins.genList (
x: "$mod SHIFT, ${ws x}, movetoworkspacesilent, ${builtins.toString (x + 1)}"
) 10;
focusWs = x: {
_args = [
"${mod} + ${ws x}"
(lib.generators.mkLuaInline "hl.dsp.focus({ workspace = ${builtins.toString (x + 1)} })")
];
};
moveToWs = x: {
_args = [
"${mod} + SHIFT + ${ws x}"
(lib.generators.mkLuaInline
"hl.dsp.window.move({ workspace = ${builtins.toString (x + 1)}, follow = false })"
)
];
};
select = builtins.genList focusWs 10;
move = builtins.genList moveToWs 10;
magnifier = pkgs.writeShellScript "magnifier" ''
CURRENT=$(${pkgs.hyprland}/bin/hyprctl getoption cursor:zoom_factor -j | ${pkgs.jq}/bin/jq .float)
@@ -219,31 +323,87 @@ in
select
++ move
++ [
"$mod, ESCAPE, killactive"
"$mod, f, fullscreen, 1"
"$mod SHIFT, f, togglefloating, active"
"$mod, h, movefocus, l"
"$mod, j, movefocus, d"
"$mod, k, movefocus, u"
"$mod, l, movefocus, r"
"$mod CONTROL, 1, exec, ${magnifier} --increase"
"$mod CONTROL, 2, exec, ${magnifier} --decrease"
"$mod CONTROL, 3, exec, ${magnifier} --reset"
{
_args = [
"${mod} + ESCAPE"
(lib.generators.mkLuaInline "hl.dsp.window.close()")
];
}
bindm = [
# mouse movements
"$mod, mouse:272, movewindow" # left click
"$mod, mouse:273, resizewindow" # right click
{
_args = [
"${mod} + f"
(lib.generators.mkLuaInline ''hl.dsp.window.fullscreen({ mode = "maximized" })'')
];
}
{
_args = [
"${mod} + SHIFT + f"
(lib.generators.mkLuaInline ''hl.dsp.window.float({ action = "toggle" })'')
];
}
misc = {
disable_hyprland_logo = true;
disable_splash_rendering = true;
};
{
_args = [
"${mod} + h"
(lib.generators.mkLuaInline ''hl.dsp.focus({ direction = "left" })'')
];
}
{
_args = [
"${mod} + j"
(lib.generators.mkLuaInline ''hl.dsp.focus({ direction = "down" })'')
];
}
{
_args = [
"${mod} + k"
(lib.generators.mkLuaInline ''hl.dsp.focus({ direction = "up" })'')
];
}
{
_args = [
"${mod} + l"
(lib.generators.mkLuaInline ''hl.dsp.focus({ direction = "right" })'')
];
}
{
_args = [
"${mod} + CONTROL + 1"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${magnifier} --increase")'')
];
}
{
_args = [
"${mod} + CONTROL + 2"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${magnifier} --decrease")'')
];
}
{
_args = [
"${mod} + CONTROL + 3"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${magnifier} --reset")'')
];
}
# mouse movements: hyprland's lua API has no separate `bindm`,
# mouse binds are plain `hl.bind` with `{ mouse = true }`.
{
_args = [
"${mod} + mouse:272" # left click
(lib.generators.mkLuaInline "hl.dsp.window.drag()")
{ mouse = true; }
];
}
{
_args = [
"${mod} + mouse:273" # right click
(lib.generators.mkLuaInline "hl.dsp.window.resize()")
{ mouse = true; }
];
}
];
};
};
+24 -4
View File
@@ -59,17 +59,37 @@ in
pause-music = "${pkgs.playerctl}/bin/playerctl -a pause";
dpmsTimeout = config.mod.hyprlock.dpmsTimeout;
dpmsOff = pkgs.writeShellScript "dpms-off" ''
${pkgs.hyprland}/bin/hyprctl dispatch 'hl.dsp.dpms({ action = "off" })'
'';
dpmsOn = pkgs.writeShellScript "dpms-on" ''
${pkgs.hyprland}/bin/hyprctl dispatch 'hl.dsp.dpms({ action = "on" })'
'';
dpms-lock = pkgs.writeShellScript "dpms-lock" ''
${pkgs.swayidle}/bin/swayidle \
timeout ${dpmsTimeout} "${pkgs.hyprland}/bin/hyprctl dispatch dpms off" \
resume "${pkgs.hyprland}/bin/hyprctl dispatch dpms on" &
timeout ${dpmsTimeout} ${dpmsOff} \
resume ${dpmsOn} &
${pkgs.hyprlock}/bin/hyprlock; ${pkgs.procps}/bin/pkill swayidle
'';
in
[
"$mod, x, exec, ${pause-music}; ${dpms-lock}"
"$mod SHIFT, x, exec, ${pause-music}; ${pkgs.hyprlock}/bin/hyprlock & sleep 0.5; systemctl suspend"
{
_args = [
"SUPER + x"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${pause-music}; ${dpms-lock}")'')
];
}
{
_args = [
"SUPER + SHIFT + x"
(lib.generators.mkLuaInline
''hl.dsp.exec_cmd("${pause-music}; ${pkgs.hyprlock}/bin/hyprlock & sleep 0.5; systemctl suspend")''
)
];
}
];
};
};
+12 -2
View File
@@ -14,8 +14,18 @@ in
wayland.windowManager.hyprland = lib.mkIf hyprlandEnabled {
settings = {
bind = [
", XF86MonBrightnessUp, exec, ${pkgs.brightnessctl}/bin/brightnessctl set +5%"
", XF86MonBrightnessDown, exec, ${pkgs.brightnessctl}/bin/brightnessctl set 5%-"
{
_args = [
"XF86MonBrightnessUp"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${pkgs.brightnessctl}/bin/brightnessctl set +5%")'')
];
}
{
_args = [
"XF86MonBrightnessDown"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${pkgs.brightnessctl}/bin/brightnessctl set 5%-")'')
];
}
];
};
};
+14 -7
View File
@@ -16,16 +16,23 @@ in
let
prev = "${pkgs.playerctl}/bin/playerctl -p naviterm,spotify previous";
next = "${pkgs.playerctl}/bin/playerctl -p naviterm,spotify next";
mkExec = key: cmd: {
_args = [
key
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${cmd}")'')
];
};
in
[
", XF86AudioPrev, exec, ${prev}"
", XF86AudioNext, exec, ${next}"
", XF86AudioPlay, exec, ${pkgs.playerctl}/bin/playerctl -p naviterm,spotify play-pause"
", XF86AudioPause, exec, ${pkgs.playerctl}/bin/playerctl -p naviterm,spoitfy play-pause"
(mkExec "XF86AudioPrev" prev)
(mkExec "XF86AudioNext" next)
(mkExec "XF86AudioPlay" "${pkgs.playerctl}/bin/playerctl -p naviterm,spotify play-pause")
(mkExec "XF86AudioPause" "${pkgs.playerctl}/bin/playerctl -p naviterm,spoitfy play-pause")
"$mod ALT, LEFT, exec, ${prev}"
"$mod ALT, RIGHT, exec, ${next}"
"$mod ALT, DOWN, exec, ${pkgs.playerctl}/bin/playerctl -p naviterm,spotify play-pause"
(mkExec "SUPER + ALT + LEFT" prev)
(mkExec "SUPER + ALT + RIGHT" next)
(mkExec "SUPER + ALT + DOWN" "${pkgs.playerctl}/bin/playerctl -p naviterm,spotify play-pause")
];
};
};
+14 -2
View File
@@ -38,8 +38,20 @@ in
in
[
# will lock the screen with `physlock`, see `lockOn.suspend
"$mod SHIFT, x, exec, ${pause-music}; systemctl suspend"
"$mod, x, exec, ${pause-music}; ${config.security.wrapperDir}/physlock -d -s -m"
{
_args = [
"SUPER + SHIFT + x"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${pause-music}; systemctl suspend")'')
];
}
{
_args = [
"SUPER + x"
(lib.generators.mkLuaInline
''hl.dsp.exec_cmd("${pause-music}; ${config.security.wrapperDir}/physlock -d -s -m")''
)
];
}
];
};
};
+12 -2
View File
@@ -19,8 +19,18 @@ in
wayland.windowManager.hyprland = lib.mkIf hyprlandEnabled {
settings = {
bind = [
"$mod, Print, exec, ${area}"
"$mod CTRL, Print, exec, ${screen}"
{
_args = [
"SUPER + Print"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${area}")'')
];
}
{
_args = [
"SUPER + CTRL + Print"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${screen}")'')
];
}
];
};
};
+11 -4
View File
@@ -38,12 +38,19 @@ in
MUTED=$(${pkgs.wireplumber}/bin/wpctl get-volume @DEFAULT_AUDIO_SOURCE@ | grep MUTED | wc -l)
echo $MUTED > /sys/class/leds/platform::micmute/brightness
'';
mkExec = key: cmd: {
_args = [
key
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${cmd}")'')
];
};
in
[
", XF86AudioRaiseVolume, exec, ${pkgs.wireplumber}/bin/wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 2%+"
", XF86AudioLowerVolume, exec, ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%-"
", XF86AudioMute, exec, ${toggle-output-mute}"
", XF86AudioMicMute, exec, ${toggle-input-mute}"
(mkExec "XF86AudioRaiseVolume" "${pkgs.wireplumber}/bin/wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 2%+")
(mkExec "XF86AudioLowerVolume" "${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%-")
(mkExec "XF86AudioMute" "${toggle-output-mute}")
(mkExec "XF86AudioMicMute" "${toggle-input-mute}")
];
};
};
+32 -27
View File
@@ -7,6 +7,11 @@
services.gnome.gcr-ssh-agent.enable = false;
programs.ssh.startAgent = true;
# Create the setuid `fusermount3` wrapper that `sshfs` execs to mount. The
# `programs.fuse` module used to install this unconditionally; newer nixpkgs
# gates it behind this option (default off), so opt in explicitly.
programs.fuse.enable = true;
home-manager.users.alex = {
services.gnome-keyring = {
enable = true;
@@ -31,49 +36,49 @@
enable = true;
enableDefaultConfig = false;
matchBlocks = {
settings = {
"manatee" = {
hostname = "manatee";
user = "alex";
identityFile = "/home/alex/.ssh/alex.pinwheel-manatee";
port = 1122;
HostName = "manatee";
User = "alex";
IdentityFile = "/home/alex/.ssh/alex.pinwheel-manatee";
Port = 1122;
};
"backwards" = {
hostname = "backwards";
user = "alex";
identityFile = "/home/alex/.ssh/alex.pinwheel-backwards";
port = 1122;
HostName = "backwards";
User = "alex";
IdentityFile = "/home/alex/.ssh/alex.pinwheel-backwards";
Port = 1122;
};
"tadpole" = {
hostname = "65.21.106.222";
user = "alex";
identityFile = "/home/alex/.ssh/alex.pinwheel-tadpole";
port = 1122;
HostName = "65.21.106.222";
User = "alex";
IdentityFile = "/home/alex/.ssh/alex.pinwheel-tadpole";
Port = 1122;
};
"github.com" = {
hostname = "github.com";
identityFile = "/home/alex/.ssh/alex.pinwheel-github.com";
HostName = "github.com";
IdentityFile = "/home/alex/.ssh/alex.pinwheel-github.com";
};
"git.ppp.pm" = {
hostname = "git.ppp.pm";
identityFile = "/home/alex/.ssh/alex.pinwheel-git.ppp.pm";
HostName = "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";
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";
};
};
};
+2 -15
View File
@@ -15,7 +15,6 @@
settings = {
devices = {
phone.id = config.lib.syncthing.phone;
backwards.id = config.lib.syncthing.backwards;
manatee.id = config.lib.syncthing.manatee;
};
@@ -24,7 +23,6 @@
path = "/home/alex/sync/org";
devices = [
"phone"
"backwards"
"manatee"
];
versioning = {
@@ -37,7 +35,7 @@
personal = {
path = "/home/alex/sync/personal";
devices = [ "backwards" ];
devices = [ "manatee" ];
versioning = {
type = "staggered";
params = {
@@ -48,18 +46,7 @@
work = {
path = "/home/alex/sync/work";
devices = [ "backwards" ];
versioning = {
type = "staggered";
params = {
maxAge = "2592000"; # 30 days
};
};
};
books = {
path = "/home/alex/sync/reading-material/books";
devices = [ "backwards" ];
devices = [ "manatee" ];
versioning = {
type = "staggered";
params = {
@@ -208,6 +208,7 @@ in
};
battery = {
"bat" = "BAT0";
"interval" = 60;
"format" = "<span font='10' rise='1000'>{icon}</span> {capacity}%";
"format-time" = "{H}h {M}min";
+6 -1
View File
@@ -78,7 +78,12 @@ in
wayland.windowManager.hyprland = lib.mkIf hyprlandEnabled {
settings = {
bind = [
"$mod, RETURN, exec, ${pkgs.wezterm}/bin/wezterm"
{
_args = [
"SUPER + RETURN"
(lib.generators.mkLuaInline ''hl.dsp.exec_cmd("${pkgs.wezterm}/bin/wezterm")'')
];
}
];
};
};
+14 -13
View File
@@ -21,24 +21,25 @@ in
home-manager.users.alex = {
programs.ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks = {
settings = {
"git.ppp.pm" = {
hostname = "git.ppp.pm";
identityFile = "/home/alex/.ssh/alex.tadpole-git.ppp.pm";
HostName = "git.ppp.pm";
IdentityFile = "/home/alex/.ssh/alex.tadpole-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";
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";
};
};
};
Binary file not shown.
Binary file not shown.
-2
View File
@@ -53,8 +53,6 @@ in {
"backwards/root.backwards.pub.age".publicKeys = [ backwards alex ];
"backwards/alex.backwards-manatee.age".publicKeys = [ backwards alex ];
"backwards/alex.backwards-manatee.pub.age".publicKeys = [ backwards manatee alex ];
"backwards/syncthing-cert.age".publicKeys = [ backwards alex ];
"backwards/syncthing-key.age".publicKeys = [ backwards alex ];
"backwards/alex.backwards-git.ppp.pm.age".publicKeys = [ backwards alex ];
"backwards/alex.backwards-git.ppp.pm.pub.age".publicKeys = [ backwards alex ];
"backwards/wireless-network-secrets.age".publicKeys = [ backwards alex ];
-1
View File
@@ -4,7 +4,6 @@
phone = "WLDQC7C-EFOW5HM-R3PULLO-ZMADECF-6FK73FD-FRK5NF4-J6UB7DY-7B4DFQR";
pinwheel = "AKS5L2A-NFCG5GV-3U5SSSZ-PLOX6BQ-ZL5ALXI-D7OK4KE-R2JPWRJ-B6AQJQ7";
manatee = "6YDVLXR-NZV6XKD-ASWPZQS-WKBRHAD-52JV5HU-JEPQ32G-6RGY7KJ-OVBO7AM";
backwards = "XRSQ4NZ-LHCZS6H-R3A75S5-W4FH7F4-3DGA5X2-SOPYWOP-A2WRKGC-IPXH4AM";
tablet = "5BEPSWB-BN4MDZM-7W3ITMP-KJ53J6M-WJMLWEF-GTDJTWI-C4C5SPQ-SFS3DAY";
};
};