terra: desktop setup, flatpak, unstable packages, key management
- Hyprland workspace rules: start-communications.sh launches telegram + discord into special:communications; qbz/discord/telegram switched to flatpak (nix-flatpak, Flathub) — removes qbz and proton-pass-cli flake inputs - proton-pass-cli and claude-code sourced from nixpkgs-unstable; unstable pkgs set threaded into home-manager via extraSpecialArgs - GTK/libadwaita dark theme fixed: dconf color-scheme = prefer-dark written declaratively instead of a per-session gsettings call - scripts/keys: store/restore SSH host keys and sops age keys via Proton Pass (ssh_host#<config> / age#<config> / age#admin naming)
This commit is contained in:
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
hyprctl dispatch 'hl.dsp.global ("quickshell:launcher7")'
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Start both comms apps; Hyprland window rules move them to special:communications.
|
||||||
|
telegram-desktop &
|
||||||
|
discord &
|
||||||
Generated
+69
@@ -118,6 +118,24 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"home-manager": {
|
"home-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -332,6 +350,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775888245,
|
||||||
|
"narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "13043924aaa7375ce482ebe2494338e058282925",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"proton-pass-cli": {
|
"proton-pass-cli": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -452,6 +486,25 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"qbz": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1784877312,
|
||||||
|
"narHash": "sha256-eQ4xvBKnQp4C5lEVukx/75umFFmWyCjbuO82qyFDw14=",
|
||||||
|
"owner": "vicrodh",
|
||||||
|
"repo": "qbz",
|
||||||
|
"rev": "c4cc755fd02d999084883e864c0697139a8b8648",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "vicrodh",
|
||||||
|
"repo": "qbz",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"authentik-nix": "authentik-nix",
|
"authentik-nix": "authentik-nix",
|
||||||
@@ -463,6 +516,7 @@
|
|||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
"proton-pass-cli": "proton-pass-cli",
|
"proton-pass-cli": "proton-pass-cli",
|
||||||
|
"qbz": "qbz",
|
||||||
"sops-nix": "sops-nix"
|
"sops-nix": "sops-nix"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -501,6 +555,21 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"treefmt-nix": {
|
"treefmt-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
|||||||
@@ -3,10 +3,6 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
|
||||||
# Second nixpkgs, used for ONE package: immich. 26.05 pins 2.7.5, but
|
|
||||||
# jupiter's imported database was written by 3.0.0 and immich never
|
|
||||||
# migrates a schema backwards. NOT `follows` — the point is a different
|
|
||||||
# package set. See services/media/immich.nix.
|
|
||||||
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
disko = {
|
disko = {
|
||||||
url = "github:nix-community/disko";
|
url = "github:nix-community/disko";
|
||||||
@@ -16,18 +12,10 @@
|
|||||||
url = "github:Mic92/sops-nix";
|
url = "github:Mic92/sops-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
# NB: no `inputs.nixpkgs.follows` here — nixos-images has no `nixpkgs`
|
|
||||||
# input (it takes nixos-stable / nixos-unstable), so declaring one only
|
|
||||||
# printed "override for a non-existent input" on every nix command.
|
|
||||||
nixos-images = {
|
nixos-images = {
|
||||||
url = "github:nix-community/nixos-images";
|
url = "github:nix-community/nixos-images";
|
||||||
inputs.nixos-stable.follows = "nixpkgs";
|
inputs.nixos-stable.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
# Pinned as an input rather than `nix run github:...` from scripts/deploy.
|
|
||||||
# Both it and disko run at the exact moment a disk is being wiped, so the
|
|
||||||
# revision has to come from flake.lock — reviewed, reproducible, and
|
|
||||||
# resolvable from the local store — instead of whatever upstream master
|
|
||||||
# happens to be that day (which also fails outright with no network).
|
|
||||||
nixos-anywhere = {
|
nixos-anywhere = {
|
||||||
url = "github:nix-community/nixos-anywhere";
|
url = "github:nix-community/nixos-anywhere";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -43,16 +31,13 @@
|
|||||||
url = "github:strangeglyph/mediamanager-nix";
|
url = "github:strangeglyph/mediamanager-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
# Deliberately NOT `inputs.nixpkgs.follows` — upstream states overriding it
|
|
||||||
# breaks their pinned python dependency set. Costs a second nixpkgs in the
|
|
||||||
# lock; builds come prebuilt from nix-community's Cachix.
|
|
||||||
authentik-nix.url = "github:nix-community/authentik-nix";
|
authentik-nix.url = "github:nix-community/authentik-nix";
|
||||||
# Unofficial packaging of Proton's pass-cli (not in nixpkgs) — used by
|
|
||||||
# ./scripts/deploy to pull sudo/ssh passwords from the "HomeLab" vault.
|
|
||||||
proton-pass-cli = {
|
proton-pass-cli = {
|
||||||
url = "github:tomsch/proton-pass-cli-nix";
|
url = "github:tomsch/proton-pass-cli-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
nix-flatpak.url = "github:gmodena/nix-flatpak";
|
||||||
|
qbz.url = "github:vicrodh/qbz";
|
||||||
# NOTE: the `tome` input (private repo git+ssh://gitea@git.mgaction.town) was
|
# NOTE: the `tome` input (private repo git+ssh://gitea@git.mgaction.town) was
|
||||||
# removed temporarily — the credential-less installer-iso can't fetch it, so
|
# removed temporarily — the credential-less installer-iso can't fetch it, so
|
||||||
# `install terra localhost` failed at nixos-install (post-disko). Re-add it
|
# `install terra localhost` failed at nixos-install (post-disko). Re-add it
|
||||||
@@ -105,6 +90,7 @@
|
|||||||
disko.nixosModules.disko
|
disko.nixosModules.disko
|
||||||
sops-nix.nixosModules.sops
|
sops-nix.nixosModules.sops
|
||||||
home-manager.nixosModules.home-manager
|
home-manager.nixosModules.home-manager
|
||||||
|
inputs.nix-flatpak.nixosModules.nix-flatpak
|
||||||
./hosts/terra/configuration.nix
|
./hosts/terra/configuration.nix
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,16 @@
|
|||||||
|
|
||||||
networking.hostName = "terra";
|
networking.hostName = "terra";
|
||||||
|
|
||||||
|
services.flatpak = {
|
||||||
|
enable = true;
|
||||||
|
remotes = [{ name = "flathub"; location = "https://dl.flathub.org/repo/flathub.flatpakrepo"; }];
|
||||||
|
packages = [
|
||||||
|
{ appId = "com.blitzfc.qbz"; origin = "flathub"; }
|
||||||
|
{ appId = "com.discordapp.Discord"; origin = "flathub"; }
|
||||||
|
{ appId = "org.telegram.desktop"; origin = "flathub"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
# Proton Pass CLI — not in nixpkgs; ./scripts/deploy uses it to autofill
|
# Proton Pass CLI — not in nixpkgs; ./scripts/deploy uses it to autofill
|
||||||
# sudo/ssh passwords from the "HomeLab" vault. Packaged by the
|
# sudo/ssh passwords from the "HomeLab" vault. Packaged by the
|
||||||
# proton-pass-cli flake input (github:tomsch/proton-pass-cli-nix).
|
# proton-pass-cli flake input (github:tomsch/proton-pass-cli-nix).
|
||||||
@@ -34,6 +44,17 @@
|
|||||||
boot.loader.efi.canTouchEfiVariables = true;
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
hardware.cpu.amd.updateMicrocode = true;
|
hardware.cpu.amd.updateMicrocode = true;
|
||||||
|
|
||||||
|
# ---- GPU (Radeon RX 6800 XT / Navi 21) ----
|
||||||
|
# amdgpu needs the redistributable navi21 firmware blobs to bind the card.
|
||||||
|
# Without them the module loads but never initialises the GPU: there's no
|
||||||
|
# DRM card for it, the display falls back to the 1024x768 EFI
|
||||||
|
# simple-framebuffer as "Unknown-1", and the real DP-2/HDMI-A-1 outputs
|
||||||
|
# never appear — so hyprland's monitor rules match nothing.
|
||||||
|
hardware.enableRedistributableFirmware = true;
|
||||||
|
# Early KMS: bind amdgpu in the initrd so it drives the console + greeter
|
||||||
|
# from boot instead of handing over from simple-framebuffer later.
|
||||||
|
boot.initrd.kernelModules = [ "amdgpu" ];
|
||||||
|
|
||||||
# ---- Dev-data disks — NOT in disko, mounted read-write, never wiped ----
|
# ---- Dev-data disks — NOT in disko, mounted read-write, never wiped ----
|
||||||
# UUIDs captured from the running CachyOS box; verify after install
|
# UUIDs captured from the running CachyOS box; verify after install
|
||||||
# (`lsblk -o NAME,UUID,MOUNTPOINT`) in case disko/kernel enumerates differently.
|
# (`lsblk -o NAME,UUID,MOUNTPOINT`) in case disko/kernel enumerates differently.
|
||||||
|
|||||||
+30
-29
@@ -1,28 +1,23 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
# home-manager profile for darman on terra. System-level Hyprland enable
|
# home-manager profile for darman on terra. The Hyprland config lives in its
|
||||||
# (session entry, portals) lives in ../../services/desktop/desktop-hyprland.nix; this
|
# own module (./hyprland.nix); this handles the rest of the user session
|
||||||
# manages the user's own hyprland.conf + session packages.
|
# (theming, terminal, packages).
|
||||||
{
|
{
|
||||||
home.stateVersion = "26.05";
|
imports = [ ./home/hyprland.nix ];
|
||||||
|
|
||||||
wayland.windowManager.hyprland = {
|
home.stateVersion = "26.05";
|
||||||
enable = true;
|
home.keyboard.layout = "de";
|
||||||
# Starter config — replace with your real dotfiles.
|
|
||||||
settings = {
|
|
||||||
monitor = [ ",preferred,auto,1" ];
|
|
||||||
"$mod" = "SUPER";
|
|
||||||
bind = [
|
|
||||||
"$mod, Return, exec, alacritty"
|
|
||||||
"$mod, Q, killactive"
|
|
||||||
"$mod, D, exec, wofi --show drun"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.git.enable = true;
|
programs.git.enable = true;
|
||||||
programs.home-manager.enable = true;
|
programs.home-manager.enable = true;
|
||||||
|
|
||||||
|
# Give darman a managed ~/.zshrc. Without any zsh dotfile, zsh runs its
|
||||||
|
# first-run `zsh-newuser-install` prompt on every new terminal. oh-my-zsh +
|
||||||
|
# powerlevel10k still come from the system /etc/zshrc (common.nix), which
|
||||||
|
# always loads for interactive shells before this ~/.zshrc.
|
||||||
|
programs.zsh.enable = true;
|
||||||
|
|
||||||
# ---- Dracula theming (GTK + Qt) ----
|
# ---- Dracula theming (GTK + Qt) ----
|
||||||
gtk = {
|
gtk = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -58,20 +53,26 @@
|
|||||||
# be a package in home.packages, not a plain xdg.dataFile.
|
# be a package in home.packages, not a plain xdg.dataFile.
|
||||||
xdg.mime.enable = true;
|
xdg.mime.enable = true;
|
||||||
xdg.configFile."quickshell".source = ../../dotfiles/quickshell;
|
xdg.configFile."quickshell".source = ../../dotfiles/quickshell;
|
||||||
|
xdg.configFile."scripts".source = ../../dotfiles/scripts;
|
||||||
home.packages = [
|
home.packages = [
|
||||||
(pkgs.writeTextDir "share/mime/packages/application-x-ms-sln.xml"
|
(pkgs.writeTextDir "share/mime/packages/application-x-ms-sln.xml"
|
||||||
(builtins.readFile ../../dotfiles/mime/application-x-ms-sln.xml))
|
(builtins.readFile ../../dotfiles/mime/application-x-ms-sln.xml))
|
||||||
pkgs.claude-code
|
pkgs.claude-code
|
||||||
pkgs.quickshell
|
pkgs.quickshell
|
||||||
pkgs.opencode
|
pkgs.opencode
|
||||||
|
pkgs.hyprcursor
|
||||||
|
pkgs.bibata-cursors
|
||||||
];
|
];
|
||||||
|
|
||||||
programs.alacritty = {
|
programs.alacritty = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
env.SHELL = "/bin/zsh";
|
# NixOS has no /bin/zsh (only /bin/sh); point at the real store path or
|
||||||
|
# alacritty exits instantly trying to exec a missing shell — which looked
|
||||||
|
# like "the SUPER+Return keybind doesn't work".
|
||||||
|
env.SHELL = "${pkgs.zsh}/bin/zsh";
|
||||||
terminal.shell = {
|
terminal.shell = {
|
||||||
program = "/bin/zsh";
|
program = "${pkgs.zsh}/bin/zsh";
|
||||||
args = [ "-l" ];
|
args = [ "-l" ];
|
||||||
};
|
};
|
||||||
window = {
|
window = {
|
||||||
@@ -86,17 +87,17 @@
|
|||||||
background = "#222831";
|
background = "#222831";
|
||||||
foreground = "#ffd369";
|
foreground = "#ffd369";
|
||||||
};
|
};
|
||||||
hints.enabled = [
|
# hints.enabled = [
|
||||||
{
|
# {
|
||||||
hyperlinks = true;
|
# hyperlinks = true;
|
||||||
regex = "(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file:|git://|ssh:|ftp://)[^\\u0000-\\u001F\\u007F-\\u009F<>\"\\s{-}\\^⟨⟩`]+";
|
# regex = "(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file:|git://|ssh:|ftp://)[^\\u0000-\\u001F\\u007F-\\u009F<>\"\\s{-}\\^⟨⟩`]+";
|
||||||
command = "xdg-open";
|
# command = "xdg-open";
|
||||||
mouse.enabled = true;
|
# mouse.enabled = true;
|
||||||
}
|
# }
|
||||||
];
|
# ];
|
||||||
keyboard.bindings = [
|
keyboard.bindings = [
|
||||||
# ESC + CR: nix strings have no \u escape, so fromJSON (which
|
# ESC + CR: nix has no literal escape for the ESC control char, so
|
||||||
# supports \u001B) is used to get the literal control chars here.
|
# fromJSON decodes it from the JSON unicode escape below.
|
||||||
{ key = "Return"; mods = "Shift"; chars = builtins.fromJSON ''"\u001B\r"''; }
|
{ key = "Return"; mods = "Shift"; chars = builtins.fromJSON ''"\u001B\r"''; }
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,270 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
|
||||||
|
# Hyprland config migrated from github.com/darman96/hyprland-dotfiles (the
|
||||||
|
# hyprlang `hypr/*.conf` files) into the home-manager lua-style `settings`
|
||||||
|
# (configType defaults to "lua" on stateVersion 26.05). Each top-level
|
||||||
|
# `settings` attr becomes an `hl.<name>(...)` call in ~/.config/hypr/hyprland.lua;
|
||||||
|
# `_args` lists become multi-arg calls, `_var` locals become `local x = ...`, and
|
||||||
|
# `lib.generators.mkLuaInline` values render as raw Lua expressions.
|
||||||
|
#
|
||||||
|
# Imported by home.nix. System-level Hyprland enable (session entry, portals)
|
||||||
|
# lives in ../../services/desktop/desktop-hyprland.nix; this manages the user's
|
||||||
|
# own hyprland.lua.
|
||||||
|
#
|
||||||
|
# Deliberately NOT migrated:
|
||||||
|
# - hyprbars.conf / hyprredsquare.conf: config for the third-party plugins
|
||||||
|
# `hyprbevelbars` + `hyprredsquare`, which aren't packaged in nixpkgs. Load
|
||||||
|
# the plugins via `wayland.windowManager.hyprland.plugins` and re-add their
|
||||||
|
# config once they're available.
|
||||||
|
# - hyprqt6engine.conf + `QT_QPA_PLATFORMTHEME=hyprqt6engine`: terra themes Qt
|
||||||
|
# through qtct/Dracula in home.nix, so that env var is left off to avoid a conflict.
|
||||||
|
# - hyprlock.conf: a separate program (use `programs.hyprlock` if wanted).
|
||||||
|
# - the duplicate pamixer/amixer + `.wob` volume binds: kept only the clean
|
||||||
|
# pipewire `wpctl`/`playerctl` set (no wob overlay is configured here).
|
||||||
|
# - `XDG_MENU_PREFIX=arch-` and `VCPKG_ROOT`: Arch-/user-specific.
|
||||||
|
# Many binds reference apps/scripts not packaged on terra yet (vivaldi-stable,
|
||||||
|
# dolphin, vicinae, grimblast, waypaper, discord, gitkraken, qbz,
|
||||||
|
# ~/.config/scripts/start-communications.sh); add them separately.
|
||||||
|
|
||||||
|
let
|
||||||
|
lua = lib.generators.mkLuaInline;
|
||||||
|
|
||||||
|
# Dispatchers → the new hl.dsp.* API (signatures verified against hyprland
|
||||||
|
# 0.55's src/config/lua/bindings/LuaBindingsDispatchers.cpp).
|
||||||
|
dsp = {
|
||||||
|
exec = cmd: lua ''hl.dsp.exec_cmd("${cmd}")'';
|
||||||
|
global = name: lua ''hl.dsp.global("${name}")'';
|
||||||
|
exit = lua "hl.dsp.exit()";
|
||||||
|
killactive = lua "hl.dsp.window.close()";
|
||||||
|
togglefloating = lua ''hl.dsp.window.float({ action = "toggle" })'';
|
||||||
|
fullscreen = lua "hl.dsp.window.fullscreen()"; # mode 0
|
||||||
|
maximize = lua ''hl.dsp.window.fullscreen({ mode = "maximized" })''; # mode 1
|
||||||
|
togglegroup = lua "hl.dsp.group.toggle()";
|
||||||
|
changegroupactive = lua "hl.dsp.group.next()";
|
||||||
|
drag = lua "hl.dsp.window.drag()";
|
||||||
|
resizemouse = lua "hl.dsp.window.resize()";
|
||||||
|
movefocus = dir: lua ''hl.dsp.focus({ direction = "${dir}" })'';
|
||||||
|
movewindow = dir: lua ''hl.dsp.window.move({ direction = "${dir}" })'';
|
||||||
|
resizeactive = x: y:
|
||||||
|
lua ''hl.dsp.window.resize({ x = ${toString x}, y = ${toString y}, relative = true })'';
|
||||||
|
workspace = n: lua ''hl.dsp.focus({ workspace = ${toString n} })'';
|
||||||
|
workspaceRef = s: lua ''hl.dsp.focus({ workspace = "${s}" })'';
|
||||||
|
movetoworkspace = n: lua ''hl.dsp.window.move({ workspace = ${toString n} })'';
|
||||||
|
togglespecial = name: lua ''hl.dsp.workspace.toggle_special("${name}")'';
|
||||||
|
};
|
||||||
|
|
||||||
|
bind = keys: dispatcher: { _args = [ keys dispatcher ]; };
|
||||||
|
bindo = keys: dispatcher: opts: { _args = [ keys dispatcher opts ]; };
|
||||||
|
|
||||||
|
# SUPER + 1..9 → workspaces 1..9, SUPER + 0 → workspace 10.
|
||||||
|
wsKeys = [
|
||||||
|
{ k = "1"; n = 1; } { k = "2"; n = 2; } { k = "3"; n = 3; }
|
||||||
|
{ k = "4"; n = 4; } { k = "5"; n = 5; } { k = "6"; n = 6; }
|
||||||
|
{ k = "7"; n = 7; } { k = "8"; n = 8; } { k = "9"; n = 9; }
|
||||||
|
{ k = "0"; n = 10; }
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
wayland.windowManager.hyprland = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
# ---- colours (from colors.conf) ----
|
||||||
|
fg_color = { _var = "rgba(eeeeeeff)"; };
|
||||||
|
fg_accent = { _var = "rgba(FFD063ff)"; };
|
||||||
|
bg_color = { _var = "rgba(0F1012ff)"; };
|
||||||
|
bg_accent = { _var = "rgba(292C30ff)"; };
|
||||||
|
|
||||||
|
# ---- monitors ----
|
||||||
|
monitor = [
|
||||||
|
{ output = "DP-2"; mode = "2560x1440@144"; position = "1920x0"; scale = 1; }
|
||||||
|
{ output = "HDMI-A-1"; mode = "1920x1080@60"; position = "0x360"; scale = 1; }
|
||||||
|
];
|
||||||
|
|
||||||
|
# ---- variables (general/decoration/input/misc/...) ----
|
||||||
|
config = {
|
||||||
|
input = {
|
||||||
|
kb_layout = "de";
|
||||||
|
numlock_by_default = true;
|
||||||
|
follow_mouse = 1;
|
||||||
|
sensitivity = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
debug.disable_logs = false;
|
||||||
|
|
||||||
|
general = {
|
||||||
|
border_size = 1;
|
||||||
|
col = {
|
||||||
|
inactive_border = lua "bg_accent";
|
||||||
|
active_border = lua "fg_accent";
|
||||||
|
};
|
||||||
|
layout = "dwindle";
|
||||||
|
gaps_in = 4;
|
||||||
|
gaps_out = 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
decoration = {
|
||||||
|
dim_special = 0.3;
|
||||||
|
rounding = 10;
|
||||||
|
blur = {
|
||||||
|
enabled = true;
|
||||||
|
special = true; # blur behind the special workspace
|
||||||
|
size = 6;
|
||||||
|
passes = 2;
|
||||||
|
ignore_opacity = true;
|
||||||
|
};
|
||||||
|
shadow.enabled = false;
|
||||||
|
active_opacity = 0.9;
|
||||||
|
inactive_opacity = 0.9;
|
||||||
|
};
|
||||||
|
|
||||||
|
animations.enabled = true;
|
||||||
|
|
||||||
|
misc = {
|
||||||
|
close_special_on_empty = true;
|
||||||
|
disable_hyprland_logo = true;
|
||||||
|
disable_splash_rendering = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
binds = {
|
||||||
|
hide_special_on_workspace_change = true;
|
||||||
|
workspace_back_and_forth = true;
|
||||||
|
allow_workspace_cycles = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# ---- animations ----
|
||||||
|
# specialWorkspace, enabled, speed 8, default curve, slidefadevert -50%
|
||||||
|
animation = [
|
||||||
|
{ leaf = "specialWorkspace"; enabled = true; speed = 8; bezier = "default"; style = "slidefadevert -50%"; }
|
||||||
|
];
|
||||||
|
|
||||||
|
# ---- environment (environment.conf) ----
|
||||||
|
env = [
|
||||||
|
{ _args = [ "HYPRCURSOR_THEME" "Bibata-Modern-Classic" ]; }
|
||||||
|
{ _args = [ "HYPRCURSOR_SIZE" "24" ]; }
|
||||||
|
{ _args = [ "XCURSOR_THEME" "Bibata-Modern-Classic" ]; }
|
||||||
|
{ _args = [ "XCURSOR_SIZE" "24" ]; }
|
||||||
|
{ _args = [ "GDK_BACKEND" "wayland,x11" ]; }
|
||||||
|
{ _args = [ "SDL_VIDEODRIVER" "wayland" ]; }
|
||||||
|
{ _args = [ "CLUTTER_BACKEND" "wayland" ]; }
|
||||||
|
{ _args = [ "XDG_CURRENT_DESKTOP" "Hyprland" ]; }
|
||||||
|
{ _args = [ "XDG_SESSION_DESKTOP" "Hyprland" ]; }
|
||||||
|
{ _args = [ "XDG_SESSION_TYPE" "wayland" ]; }
|
||||||
|
];
|
||||||
|
|
||||||
|
# ---- keybinds (keybinds.conf) ----
|
||||||
|
bind = [
|
||||||
|
(bindo "SUPER + SUPER_L" (dsp.exec "bash $HOME/.config/quickshell/open_launcher.sh") { release = true; })
|
||||||
|
(bind "SUPER + Return" (dsp.exec "alacritty"))
|
||||||
|
|
||||||
|
# quickshell app-launcher variants (evaluating — pick one)
|
||||||
|
]
|
||||||
|
++ (map (n: bind "SUPER + CTRL + ${toString n}" (dsp.global "quickshell:launcher${toString n}")) (lib.range 1 7))
|
||||||
|
++ [
|
||||||
|
# restart quickshell (also starts it if not running)
|
||||||
|
(bind "SUPER + CTRL + 0" (dsp.exec "qs kill; sleep 0.3; qs"))
|
||||||
|
# toggle the Slant sidebar
|
||||||
|
(bind "SUPER + CTRL + S" (dsp.global "quickshell:sidebar"))
|
||||||
|
|
||||||
|
(bind "SUPER + B" (dsp.exec "vivaldi"))
|
||||||
|
(bind "SUPER + E" (dsp.exec "dolphin"))
|
||||||
|
(bind "SUPER + SHIFT + G" (dsp.workspaceRef "game"))
|
||||||
|
(bind "SUPER + S" (dsp.exec "grim -o DP-2 ~/screenshot.png"))
|
||||||
|
(bind "SUPER + SHIFT + S" (dsp.exec "grimblast copy area --freeze"))
|
||||||
|
(bind "SUPER + L" (dsp.exec "hyprlock"))
|
||||||
|
(bind "SUPER + W" (dsp.exec "waypaper --random"))
|
||||||
|
|
||||||
|
# window management
|
||||||
|
(bind "SUPER + Q" dsp.killactive)
|
||||||
|
(bind "SUPER + SHIFT + Q" dsp.exit)
|
||||||
|
(bind "SUPER + F" dsp.maximize)
|
||||||
|
(bind "SUPER + SHIFT + F" dsp.fullscreen)
|
||||||
|
(bind "SUPER + Space" dsp.togglefloating)
|
||||||
|
|
||||||
|
# focus
|
||||||
|
(bind "SUPER + left" (dsp.movefocus "left"))
|
||||||
|
(bind "SUPER + right" (dsp.movefocus "right"))
|
||||||
|
(bind "SUPER + up" (dsp.movefocus "up"))
|
||||||
|
(bind "SUPER + down" (dsp.movefocus "down"))
|
||||||
|
|
||||||
|
# move
|
||||||
|
(bind "SUPER + SHIFT + left" (dsp.movewindow "left"))
|
||||||
|
(bind "SUPER + SHIFT + right" (dsp.movewindow "right"))
|
||||||
|
(bind "SUPER + SHIFT + up" (dsp.movewindow "up"))
|
||||||
|
(bind "SUPER + SHIFT + down" (dsp.movewindow "down"))
|
||||||
|
|
||||||
|
# resize
|
||||||
|
(bind "SUPER + CTRL + left" (dsp.resizeactive (-20) 0))
|
||||||
|
(bind "SUPER + CTRL + right" (dsp.resizeactive 20 0))
|
||||||
|
(bind "SUPER + CTRL + up" (dsp.resizeactive 0 (-20)))
|
||||||
|
(bind "SUPER + CTRL + down" (dsp.resizeactive 0 20))
|
||||||
|
|
||||||
|
# tabbed / group
|
||||||
|
(bind "SUPER + g" dsp.togglegroup)
|
||||||
|
(bind "SUPER + tab" dsp.changegroupactive)
|
||||||
|
|
||||||
|
# special workspaces
|
||||||
|
(bind "SUPER + T" (dsp.togglespecial "terminal"))
|
||||||
|
(bind "SUPER + C" (dsp.togglespecial "communications"))
|
||||||
|
(bind "SUPER + M" (dsp.togglespecial "music"))
|
||||||
|
(bind "SUPER + V" (dsp.togglespecial "version_control"))
|
||||||
|
|
||||||
|
# cycle workspaces
|
||||||
|
(bind "SUPER + ALT + up" (dsp.workspaceRef "e+1"))
|
||||||
|
(bind "SUPER + ALT + down" (dsp.workspaceRef "e-1"))
|
||||||
|
|
||||||
|
# mouse move / resize
|
||||||
|
(bindo "SUPER + mouse:272" dsp.drag { mouse = true; })
|
||||||
|
(bindo "SUPER + mouse:273" dsp.resizemouse { mouse = true; })
|
||||||
|
|
||||||
|
# multimedia (pipewire)
|
||||||
|
(bindo "XF86AudioRaiseVolume" (dsp.exec "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+") { repeating = true; })
|
||||||
|
(bindo "XF86AudioLowerVolume" (dsp.exec "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-") { repeating = true; })
|
||||||
|
(bind "XF86AudioMute" (dsp.exec "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"))
|
||||||
|
(bind "XF86AudioPlay" (dsp.exec "playerctl play-pause"))
|
||||||
|
(bind "XF86AudioPause" (dsp.exec "playerctl play-pause"))
|
||||||
|
(bind "XF86AudioNext" (dsp.exec "playerctl next"))
|
||||||
|
(bind "XF86AudioPrev" (dsp.exec "playerctl previous"))
|
||||||
|
]
|
||||||
|
++ (map (w: bind "SUPER + ${w.k}" (dsp.workspace w.n)) wsKeys)
|
||||||
|
++ (map (w: bind "SUPER + SHIFT + ${w.k}" (dsp.movetoworkspace w.n)) wsKeys);
|
||||||
|
|
||||||
|
# ---- window rules (windowrules.conf) ----
|
||||||
|
window_rule = [
|
||||||
|
{ name = "games"; match.class = "gamescope"; workspace = "name:game"; opacity = "1 1 override"; }
|
||||||
|
{ name = "vivaldi"; match.title = "(.*)(- YouTube - Vivaldi)"; opacity = "1 1 override"; }
|
||||||
|
{ name = "jetbrains"; match.class = "(jetbrains-)(.*)"; no_initial_focus = true; float = true; center = true; }
|
||||||
|
{ name = "jetbrains-toolbox"; match.class = "jetbrains-toolbox"; move = "1933 21"; }
|
||||||
|
{ name = "comms"; match.class = "discord|org.telegram.desktop"; workspace = "special:communications"; }
|
||||||
|
{ name = "music"; match.class = "qbz"; workspace = "special:music"; }
|
||||||
|
{ name = "vicinae"; match.class = "vicinae"; stay_focused = true; }
|
||||||
|
{ name = "gitkraken"; match.class = "gitkraken"; workspace = "special:version_control"; }
|
||||||
|
];
|
||||||
|
|
||||||
|
# ---- workspace rules (workspacerules.conf) ----
|
||||||
|
workspace_rule = [
|
||||||
|
{ workspace = "name:game"; monitor = "DP-2"; decorate = false; }
|
||||||
|
{ workspace = "special:terminal"; on_created_empty = "alacritty"; gaps_out = 256; }
|
||||||
|
{ workspace = "special:communications"; on_created_empty = "$HOME/.config/scripts/start-communications.sh"; gaps_out = 128; }
|
||||||
|
{ workspace = "special:music"; on_created_empty = "com.blitzfc.qbz"; gaps_out = 128; }
|
||||||
|
{ workspace = "special:version_control"; on_created_empty = "com.axosoft.GitKraken"; gaps_out = 128; }
|
||||||
|
];
|
||||||
|
|
||||||
|
# ---- autostart (autostart.conf) ----
|
||||||
|
on = {
|
||||||
|
_args = [
|
||||||
|
"hyprland.start"
|
||||||
|
(lua ''
|
||||||
|
function()
|
||||||
|
hl.exec_cmd("systemctl --user start hyprpolkitagent")
|
||||||
|
hl.exec_cmd("gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'")
|
||||||
|
hl.exec_cmd("vicinae server")
|
||||||
|
hl.exec_cmd("quickshell")
|
||||||
|
hl.exec_cmd("alacritty", { workspace = "special:terminal silent" })
|
||||||
|
hl.exec_cmd("kbuildsycoca6 --noincremental")
|
||||||
|
end'')
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Executable
+175
@@ -0,0 +1,175 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Manage homelab SSH host keys and sops age keys in the Proton Pass HomeLab vault.
|
||||||
|
#
|
||||||
|
# Vault naming (HomeLab vault; override with HOMELAB_PASS_VAULT):
|
||||||
|
#
|
||||||
|
# ssh_host#<config> ssh-key item ↔ ~/.config/homelab/<config>/ssh_host_ed25519_key{,.pub}
|
||||||
|
# age#<config> note item ↔ ~/.config/homelab/<config>/age.txt
|
||||||
|
# age#admin note item ↔ ~/.config/sops/age/keys.txt
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/keys store [--force] [<config>...]
|
||||||
|
# ./scripts/keys restore [<config>...]
|
||||||
|
#
|
||||||
|
# store: upload local keys to the vault. Skips items that already exist
|
||||||
|
# unless --force is given (deletes the existing item first).
|
||||||
|
# restore: download vault items to local files with correct permissions.
|
||||||
|
#
|
||||||
|
# With no <config> args both subcommands operate on every hosts/ directory.
|
||||||
|
# The admin age key is always included regardless of <config> args.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||||
|
REPO="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || dirname "$SCRIPT_DIR")"
|
||||||
|
cd "$REPO"
|
||||||
|
|
||||||
|
KEYDIR="${HOMELAB_KEY_DIR:-${HOME:-/root}/.config/homelab}"
|
||||||
|
ADMIN_AGE="${HOME:-/root}/.config/sops/age/keys.txt"
|
||||||
|
VAULT="${HOMELAB_PASS_VAULT:-HomeLab}"
|
||||||
|
|
||||||
|
die() { echo "error: $*" >&2; exit 1; }
|
||||||
|
need() { command -v "$1" >/dev/null 2>&1 || die "missing required tool: $1"; }
|
||||||
|
|
||||||
|
need pass-cli
|
||||||
|
need jq
|
||||||
|
|
||||||
|
# Resolve an active vault item by title → item ID, or empty string.
|
||||||
|
# Active-only filter avoids the trashed-item-shadows-active bug (see deploy).
|
||||||
|
resolve_item() {
|
||||||
|
local title="$1"
|
||||||
|
pass-cli item list --vault-name "$VAULT" --filter-state active --output human 2>/dev/null \
|
||||||
|
| awk -v t="$title" '
|
||||||
|
{ i = index($0, "]: "); if (i == 0) next
|
||||||
|
id = substr($0, 4, i - 4)
|
||||||
|
rest = substr($0, i + 3)
|
||||||
|
sub(/ \(state=[^)]*\)$/, "", rest)
|
||||||
|
if (rest == t) { print id; exit } }' || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# All configs: every hosts/<dir> that has a configuration.nix.
|
||||||
|
all_configs() {
|
||||||
|
for d in hosts/*/; do
|
||||||
|
[ -f "${d}configuration.nix" ] && basename "$d"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# store helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Returns 0 (skip) if item exists and --force was not given; 1 (proceed) otherwise.
|
||||||
|
# Deletes the existing item when --force is set.
|
||||||
|
should_store() {
|
||||||
|
local title="$1" id
|
||||||
|
id="$(resolve_item "$title")"
|
||||||
|
if [ -n "$id" ]; then
|
||||||
|
if [ -n "$FORCE" ]; then
|
||||||
|
echo ">> $title: deleting existing item (--force)"
|
||||||
|
pass-cli item delete --vault-name "$VAULT" --item-id "$id"
|
||||||
|
else
|
||||||
|
echo ">> $title: already in vault — skipping (use --force to overwrite)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
store_ssh_key() {
|
||||||
|
local config="$1" keyfile="$KEYDIR/$1/ssh_host_ed25519_key" title="ssh_host#$1"
|
||||||
|
[ -f "$keyfile" ] || { echo ">> $title: $keyfile not found — skipping"; return; }
|
||||||
|
should_store "$title" && return
|
||||||
|
echo ">> $title: uploading"
|
||||||
|
pass-cli item create ssh-key import \
|
||||||
|
--from-private-key "$keyfile" \
|
||||||
|
--title "$title" \
|
||||||
|
--vault-name "$VAULT"
|
||||||
|
}
|
||||||
|
|
||||||
|
store_age_key() {
|
||||||
|
local title="$1" src="$2"
|
||||||
|
[ -f "$src" ] || { echo ">> $title: $src not found — skipping"; return; }
|
||||||
|
should_store "$title" && return
|
||||||
|
echo ">> $title: uploading"
|
||||||
|
pass-cli item create note \
|
||||||
|
--title "$title" \
|
||||||
|
--note "$(cat "$src")" \
|
||||||
|
--vault-name "$VAULT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# restore helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
restore_ssh_key() {
|
||||||
|
local config="$1" title="ssh_host#$1" id json
|
||||||
|
id="$(resolve_item "$title")"
|
||||||
|
if [ -z "$id" ]; then
|
||||||
|
echo ">> $title: not in vault — skipping"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo ">> $title: restoring"
|
||||||
|
json="$(pass-cli item view --vault-name "$VAULT" --item-id "$id" --output json)"
|
||||||
|
private_key="$(jq -r '.item.content.content.SshKey.private_key' <<< "$json")"
|
||||||
|
public_key="$(jq -r '.item.content.content.SshKey.public_key' <<< "$json")"
|
||||||
|
[ -n "$private_key" ] || die "$title: private_key missing from vault item JSON"
|
||||||
|
[ -n "$public_key" ] || die "$title: public_key missing from vault item JSON"
|
||||||
|
mkdir -p "$KEYDIR/$config"
|
||||||
|
printf '%s' "$private_key" > "$KEYDIR/$config/ssh_host_ed25519_key"
|
||||||
|
chmod 600 "$KEYDIR/$config/ssh_host_ed25519_key"
|
||||||
|
printf '%s\n' "$public_key" > "$KEYDIR/$config/ssh_host_ed25519_key.pub"
|
||||||
|
chmod 644 "$KEYDIR/$config/ssh_host_ed25519_key.pub"
|
||||||
|
echo " → $KEYDIR/$config/ssh_host_ed25519_key{,.pub}"
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_age_key() {
|
||||||
|
local title="$1" dest="$2" id content
|
||||||
|
id="$(resolve_item "$title")"
|
||||||
|
if [ -z "$id" ]; then
|
||||||
|
echo ">> $title: not in vault — skipping"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo ">> $title: restoring"
|
||||||
|
content="$(pass-cli item view --vault-name "$VAULT" --item-id "$id" \
|
||||||
|
--output json | jq -r '.item.content.note')"
|
||||||
|
[ -n "$content" ] || die "$title: note field is empty in vault item"
|
||||||
|
mkdir -p "$(dirname "$dest")"
|
||||||
|
printf '%s\n' "$content" > "$dest"
|
||||||
|
chmod 600 "$dest"
|
||||||
|
echo " → $dest"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# main
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
cmd="${1:-}"
|
||||||
|
[ -n "$cmd" ] || die "usage: ./scripts/keys <store|restore> [--force] [<config>...]"
|
||||||
|
shift
|
||||||
|
|
||||||
|
FORCE=""
|
||||||
|
[ "${1:-}" = "--force" ] && { FORCE=1; shift; }
|
||||||
|
|
||||||
|
configs=("$@")
|
||||||
|
[ "${#configs[@]}" -gt 0 ] || mapfile -t configs < <(all_configs)
|
||||||
|
|
||||||
|
case "$cmd" in
|
||||||
|
store)
|
||||||
|
for config in "${configs[@]}"; do
|
||||||
|
store_ssh_key "$config"
|
||||||
|
store_age_key "age#$config" "$KEYDIR/$config/age.txt"
|
||||||
|
done
|
||||||
|
store_age_key "age#admin" "$ADMIN_AGE"
|
||||||
|
;;
|
||||||
|
|
||||||
|
restore)
|
||||||
|
for config in "${configs[@]}"; do
|
||||||
|
restore_ssh_key "$config"
|
||||||
|
restore_age_key "age#$config" "$KEYDIR/$config/age.txt"
|
||||||
|
done
|
||||||
|
restore_age_key "age#admin" "$ADMIN_AGE"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
die "unknown command '$cmd' — usage: ./scripts/keys <store|restore> [--force] [<config>...]"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -35,10 +35,6 @@ in
|
|||||||
kdePackages.dolphin
|
kdePackages.dolphin
|
||||||
vivaldi
|
vivaldi
|
||||||
rishot
|
rishot
|
||||||
# tome removed for now — private git+ssh flake input the installer can't
|
|
||||||
# fetch. The dev SDKs below stay (public, cached) for when it comes back.
|
|
||||||
|
|
||||||
# Tome development (Tome.App targets net10.0; ClientApp is Preact/Vite).
|
|
||||||
dotnetCorePackages.sdk_10_0
|
dotnetCorePackages.sdk_10_0
|
||||||
nodejs
|
nodejs
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -5,10 +5,14 @@
|
|||||||
{
|
{
|
||||||
programs.hyprland.enable = true;
|
programs.hyprland.enable = true;
|
||||||
|
|
||||||
|
services.gnome.gnome-keyring.enable = true;
|
||||||
|
security.pam.services.login.enableGnomeKeyring = true;
|
||||||
|
security.pam.services.greetd.enableGnomeKeyring = true;
|
||||||
|
|
||||||
services.greetd = {
|
services.greetd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings.default_session.command =
|
settings.default_session.command =
|
||||||
"${pkgs.tuigreet}/bin/tuigreet --time --cmd Hyprland";
|
"${pkgs.tuigreet}/bin/tuigreet --time --cmd start-hyprland";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Audio (pipewire replaces pulseaudio/jack).
|
# Audio (pipewire replaces pulseaudio/jack).
|
||||||
|
|||||||
Reference in New Issue
Block a user