Compare commits
18 Commits
873bec97fc
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| acd16ebc14 | |||
| b9db20d45c | |||
| d8f2392f80 | |||
| be9f5bccf6 | |||
| 27c8db6b29 | |||
| a966a47714 | |||
| c2d2c62364 | |||
| 9eed9f81d4 | |||
| b471966e8a | |||
| 37d6977223 | |||
| 20963732a5 | |||
| e9ac72d162 | |||
| e68498cd42 | |||
| 2b9e446876 | |||
| 7313f718df | |||
| 233e463f87 | |||
| 727d6c3da9 | |||
| 1934cd036e |
+231
@@ -61,6 +61,7 @@ This guide documents methods for installing NixOS on a Proxmox virtual machine a
|
||||
- [[#rebuildsh---enhanced-nixos-rebuild-wrapper][rebuild.sh - Enhanced NixOS Rebuild Wrapper]]
|
||||
- [[#backupsh---automated-backup-script][backup.sh - Automated Backup Script]]
|
||||
- [[#hash-utilsh---file-hash-verification][hash-util.sh - File Hash Verification]]
|
||||
- [[#pve-buildsh---disposable-lxc-builder-on-proxmox-ve][pve-build.sh - Disposable LXC Builder on Proxmox VE]]
|
||||
- [[#optional-nixos-modules][Optional NixOS Modules]]
|
||||
- [[#reverse-proxies][Reverse Proxies]]
|
||||
- [[#file-servers][File Servers]]
|
||||
@@ -335,6 +336,155 @@ Verifies file integrity using SHA256 checksums.
|
||||
./bin/hash-util.sh --path configuration.nix --hash $(sha256sum configuration.nix | cut -d' ' -f1)
|
||||
#+end_src
|
||||
|
||||
** pve-build.sh - Disposable LXC Builder on Proxmox VE
|
||||
Builds a NixOS configuration on a throwaway LXC container running on the Proxmox VE node, then activates the result on the requester (= the machine invoking the script, by default). Offloads the heavy compilation work from the requester to a disposable builder; the closure is shipped back over =nix copy= and activated locally.
|
||||
|
||||
The disposable is cloned from a NixOS LXC ostemplate (=nixos-lxc= flake output, =proxmox-lxc= format). The template is built locally and uploaded to PVE on first use, then reused.
|
||||
|
||||
*** Prerequisites
|
||||
- An SSH alias =pve= in =~/.ssh/config= pointing at the PVE node (the script defers all connection details — HostName, User, Port, IdentityFile — to SSH config). Override with =PVE_HOST= or =--pve-host= if your alias differs.
|
||||
- The operator's pubkey must be in the =adminKeys= list in =machines/builder/default.nix=. The disposable's =builder= user trusts whatever keys are listed there.
|
||||
- The caller must resolve disposable hostnames (=nixos-builder-<VMID>=) — typically via dnsmasq or split-DNS that reads from PVE. The script never uses IPs for SSH.
|
||||
- =jq= on the caller (to parse =pvesh= JSON output).
|
||||
|
||||
*** Flow
|
||||
1. Ensure a NixOS LXC ostemplate exists on PVE; build =.#nixos-lxc= locally and =scp= it if missing.
|
||||
2. Clone a fresh container under a free VMID (scanning downward from 9999), start it, wait for SSH on its hostname.
|
||||
3. Build phase (runs as the calling user, no =sudo=):
|
||||
#+begin_example
|
||||
nixos-rebuild build --flake .#<requester> --build-host root@<ct-ip>
|
||||
#+end_example
|
||||
builds the closure on the disposable and copies it back to the local Nix store.
|
||||
4. Activate phase (only for =test= / =switch= / =boot=; runs locally with =sudo=, no =--build-host=):
|
||||
#+begin_example
|
||||
sudo nixos-rebuild <cmd> --flake .#<requester>
|
||||
#+end_example
|
||||
the closure is already local, so this only activates.
|
||||
5. Destroy the disposable (=pct destroy --purge --force=).
|
||||
|
||||
The build/activate split avoids needing the requester's root user to SSH to the disposable — only the calling user does.
|
||||
|
||||
*** Composite vs. step commands
|
||||
Each stage can be invoked on its own, or chained via the composite commands:
|
||||
|
||||
| Composite | Stages chained | Disposable on success |
|
||||
|-----------+-----------------------------------------------------------------+-----------------------|
|
||||
| =build= | deploy-image + start-builder + build-on | left running |
|
||||
| =test= | deploy-image + start-builder + build-on + activate(test) | left running |
|
||||
| =switch= | deploy-image + start-builder + build-on + activate(switch) + destroy-builder | destroyed |
|
||||
| =boot= | deploy-image + start-builder + build-on + activate(boot) + destroy-builder | destroyed |
|
||||
|
||||
| Step command | Stage | Effect |
|
||||
|---------------------------+-------+-----------------------------------------------------------------------|
|
||||
| =check-image= | 1 | Read-only: exit 0 if a NixOS LXC ostemplate is on PVE, 1 if not. |
|
||||
| =deploy-image= | 1 | Idempotent: build =.#nixos-lxc= locally + scp to PVE if missing. |
|
||||
| =update-image= | 1 | Force: rebuild =.#nixos-lxc= and replace the ostemplate on PVE. |
|
||||
| =start-builder= | 2 | deploy-image + clone a fresh CT + start. |
|
||||
| =build-on [VMID]= | 3 | Build closure on an existing disposable. |
|
||||
| =activate <test\|switch\|boot>= | 4 | Activate LOCALLY (closure must already be in the local store). |
|
||||
| =destroy-builder [VMID]= | 5 | =pct destroy --purge --force=. |
|
||||
| =info [VMID]= | - | List disposables, or show detail for one. |
|
||||
|
||||
*** Stateless design
|
||||
The script writes no state files. Each step discovers prior steps' artifacts dynamically from PVE:
|
||||
|
||||
- Disposables are identified by hostname pattern =nixos-builder-<VMID>= (override via =CT_HOSTNAME_PREFIX=). When a step needs a target VMID and none is passed explicitly, it queries =pvesh get /cluster/resources= for LXC containers matching the prefix.
|
||||
- Exactly one match :: used automatically.
|
||||
- Zero matches :: the step errors out and points at =create=.
|
||||
- Multiple matches :: the step errors out, lists the matches, and asks for an explicit VMID.
|
||||
- VMID allocation starts at =9999= and scans downward (configurable via =--vmid-start= / =--vmid-floor=) so disposables sit clearly above regular VM IDs.
|
||||
- The built closure lives in the caller's local Nix store; =nixos-rebuild= finds it naturally during =activate=, so no IPC between =build-on= and =activate= is needed.
|
||||
|
||||
*** Hostname-based SSH
|
||||
All SSH to disposables targets their hostname (=nixos-builder-<VMID>=), never their IP. The caller's resolver must be able to look up PVE container hostnames — typically via dnsmasq or split-DNS that reads from PVE. The script never resolves IPs for SSH; =get_ct_ip= is used only for =info= display and diagnostic logging.
|
||||
|
||||
This makes the script safe to re-run, interrupt, or split across shell sessions — there is no =latest-vmid= file to drift out of sync with reality.
|
||||
|
||||
*** Basic Usage
|
||||
#+begin_src sh
|
||||
# Full happy path — build + activate susano permanently, auto-destroy CT.
|
||||
pve-build switch --machine susano
|
||||
|
||||
# Step-by-step (each step discovers the prior step's artifacts from PVE):
|
||||
pve-build deploy-image # build + upload the LXC ostemplate (no-op if present)
|
||||
pve-build start-builder # clones a fresh disposable CT
|
||||
pve-build build-on # discovers the CT on PVE, builds on it
|
||||
pve-build activate switch # activates locally (no SSH)
|
||||
pve-build destroy-builder # discovers the CT on PVE, destroys it
|
||||
|
||||
# Rebuild the LXC image after editing machines/builder/default.nix.
|
||||
pve-build update-image
|
||||
|
||||
# Read-only check (exit 0 if image is already on PVE, 1 otherwise).
|
||||
pve-build check-image && echo ready || echo missing
|
||||
|
||||
# Operate on a specific CT (skips discovery — needed when multiple exist).
|
||||
pve-build build-on 305
|
||||
#+end_src
|
||||
|
||||
*** Advanced Examples
|
||||
#+begin_src sh
|
||||
# Build + activate temporarily without persisting to the bootloader.
|
||||
pve-build test --machine fujin
|
||||
|
||||
# Use a non-default PVE node and a higher starting VMID.
|
||||
pve-build switch --pve-host root@10.0.0.5 --vmid-start 9999
|
||||
|
||||
# Keep the disposable even after a successful switch (for inspection).
|
||||
pve-build switch --keep
|
||||
|
||||
# List every disposable currently on PVE.
|
||||
pve-build info
|
||||
|
||||
# Inspect a specific disposable in detail.
|
||||
pve-build info 307
|
||||
|
||||
# Destroy a specific disposable by VMID.
|
||||
pve-build destroy-builder 307
|
||||
#+end_src
|
||||
|
||||
*** Command Reference
|
||||
**** Composite commands
|
||||
- =build= - deploy-image + start-builder + build-on.
|
||||
- =test= - build + activate temporarily (reverts on reboot).
|
||||
- =switch= - build + activate permanently. Disposable destroyed on success.
|
||||
- =boot= - build + set as boot default. Disposable destroyed on success.
|
||||
|
||||
**** Step commands
|
||||
- =check-image= - Read-only: exit 0 if ostemplate is on PVE, 1 if missing.
|
||||
- =deploy-image= - Idempotent: build =.#nixos-lxc= locally + scp to PVE if missing.
|
||||
- =update-image= - Force rebuild + replace the ostemplate on PVE.
|
||||
- =start-builder= - deploy-image + clone a fresh CT + start.
|
||||
- =build-on [VMID]= - Build closure on an existing disposable (default: discovered).
|
||||
- =activate <test|switch|boot>= - Activate LOCALLY (closure must already be built).
|
||||
- =destroy-builder [VMID]= - =pct destroy --purge --force= (default: discovered).
|
||||
- =info [VMID]= - List disposables, or show detail for one.
|
||||
|
||||
**** Aliases
|
||||
The old command names still work as aliases:
|
||||
- =ensure-template= → =deploy-image=
|
||||
- =create= → =start-builder=
|
||||
|
||||
**** Options
|
||||
- =--machine NAME= - Requester machine name (default: current hostname).
|
||||
- =--pve-host HOST= - PVE SSH alias or target (default: =pve=).
|
||||
- =--pve-storage NAME= - Ostemplate storage (default: =local=).
|
||||
- =--rootfs-storage NAME= - Rootfs storage (default: =local-lvm=).
|
||||
- =--bridge NAME= - Network bridge (default: =vmbr0=).
|
||||
- =--vmid-start N= - Highest VMID to consider (default: =9999=); scanned downward.
|
||||
- =--vmid-floor N= - Lowest VMID to consider (default: =100=).
|
||||
- =--rootfs-gib N= - Rootfs size in GiB (default: =20=).
|
||||
- =--keep= - Keep the disposable even on successful =switch= / =boot=.
|
||||
- =--show-trace= , =--verbose= - Passed through to =nixos-rebuild=.
|
||||
|
||||
**** Environment
|
||||
= PVE_HOST= , = PVE_STORAGE= , = PVE_ROOTFS_STORAGE= , = PVE_BRIDGE= , = VMID_START= , = VMID_FLOOR= , = BUILD_SSH_USER= , = CT_BOOT_TIMEOUT= , = CT_ROOTFS_GIB= .
|
||||
|
||||
*** Notes
|
||||
- Disposables are matched by hostname prefix (=nixos-builder-= by default). If you want a parallel run with multiple builders, pass explicit VMIDs to =build-on= / =destroy-builder= / =info=.
|
||||
- =destroy-builder= calls =pct destroy <vmid> --purge --force=, so it stops and removes a running container in one step.
|
||||
- To rebuild the LXC template after editing =machines/builder/default.nix=, remove the old tarball from =root@<pve>:/var/lib/vz/template/cache/= — the script always picks the alphabetically last =nixos-lxc-*.tar.xz= it finds.
|
||||
|
||||
* Optional NixOS Modules
|
||||
** Reverse Proxies
|
||||
The following modules can be enabled to provide a reverse proxy.
|
||||
@@ -405,6 +555,87 @@ dov = {
|
||||
};
|
||||
#+end_src
|
||||
|
||||
** YubiKey
|
||||
Provides PAM U2F authentication (touch YubiKey for login/sudo) and a seamless SSH agent backed by the YubiKey PIV applet.
|
||||
|
||||
#+begin_src nix
|
||||
dov = {
|
||||
yubikey.enable = true;
|
||||
};
|
||||
#+end_src
|
||||
|
||||
*** PAM U2F (login & sudo)
|
||||
After enabling the module and rebuilding, enroll your YubiKey:
|
||||
|
||||
1. Create the YubiKey config directory:
|
||||
#+begin_src sh
|
||||
mkdir -p ~/.config/Yubico
|
||||
#+end_src
|
||||
|
||||
2. Generate the U2F key mapping (touch YubiKey when prompted):
|
||||
#+begin_src bash
|
||||
nix-shell -p pam_u2f --run 'pamu2fcfg -u fujin' > ~/.config/Yubico/u2f_keys
|
||||
#+end_src
|
||||
|
||||
*Nushell users:* use ~out>~ instead of ~>~:
|
||||
#+begin_src
|
||||
nix-shell -p pam_u2f --run 'pamu2fcfg -u fujin' out> ~/.config/Yubico/u2f_keys
|
||||
#+end_src
|
||||
|
||||
3. Add a backup YubiKey (optional):
|
||||
#+begin_src bash
|
||||
nix-shell -p pam_u2f --run 'pamu2fcfg -u fujin -n' >> ~/.config/Yubico/u2f_keys
|
||||
#+end_src
|
||||
|
||||
4. Rebuild and switch:
|
||||
#+begin_src sh
|
||||
sudo nixos-rebuild switch --flake .#fujin
|
||||
#+end_src
|
||||
|
||||
5. Test:
|
||||
#+begin_src sh
|
||||
sudo true # should prompt to touch YubiKey
|
||||
#+end_src
|
||||
|
||||
The default mode is =sufficient= (touch key *or* type password). If the key is absent or not enrolled, the normal password prompt follows — you cannot lock yourself out.
|
||||
|
||||
*** SSH with FIDO2-backed keys
|
||||
The module enables the standard OpenSSH agent (not GPG agent's SSH emulation, which doesn't reliably support =-sk= keys). The YubiKey uses FIDO2 — the same interface that PAM U2F uses — so no PIV/pcscd setup is needed for SSH.
|
||||
|
||||
1. Generate a FIDO2-backed SSH key (one-time, touch YubiKey when prompted):
|
||||
#+begin_src sh
|
||||
ssh-keygen -t ed25519-sk -C "yubikey@fujin"
|
||||
#+end_src
|
||||
|
||||
*Resident key* (recoverable on a new machine without the stub file):
|
||||
#+begin_src sh
|
||||
ssh-keygen -t ed25519-sk -O resident -C "yubikey@fujin"
|
||||
#+end_src
|
||||
|
||||
2. Load the key into the agent (once per login session, enter passphrase):
|
||||
#+begin_src sh
|
||||
ssh-add ~/.ssh/id_ed25519_sk
|
||||
#+end_src
|
||||
|
||||
*After a rebuild*, log out and back in so the shell picks up the new =SSH_AUTH_SOCK=. In an existing session:
|
||||
#+begin_src sh
|
||||
export SSH_AUTH_SOCK=/run/user/$(id -u)/ssh-agent
|
||||
#+end_src
|
||||
|
||||
3. Copy the public key to remote hosts:
|
||||
#+begin_src sh
|
||||
ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub susano
|
||||
#+end_src
|
||||
|
||||
4. Test:
|
||||
#+begin_src sh
|
||||
ssh susano # touch YubiKey, no password
|
||||
#+end_src
|
||||
|
||||
Each SSH authentication requires a physical YubiKey touch. The passphrase is only asked when loading the key into the agent (step 2), not per-connection.
|
||||
|
||||
*Note:* The key stub (~/.ssh/id_ed25519_sk) is tied to this specific generation — regenerating produces a different key. Back it up or use =-O resident=.
|
||||
|
||||
* Notes and Configuration Details
|
||||
** Remote Build Configuration
|
||||
To leverage remote builds (e.g., building fujin configurations on izanagi), you need to set up SSH keys for the root user:
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# Backlog
|
||||
|
||||
<!-- SECTION: ENTRIES -->
|
||||
|
||||
<!-- SECTION: Nixos -->
|
||||
|
||||
- [ ] [P2] Add nushell completion for rebuild script *(priority: P2)*
|
||||
|
||||
<!-- SECTION: HISTORY -->
|
||||
|
||||
<!-- SECTION: Nixos -->
|
||||
|
||||
| Timestamp | Item ID | Action | Details |
|
||||
|-----------|---------|--------|---------|
|
||||
| 2026-07-26T11:58:28.175Z | n-dk597m | item_created | Add nushell completion for rebuild script |
|
||||
|
||||
<!-- SECTION: INTEGRITY -->
|
||||
|
||||
<!-- saved: 2026-07-26T11:58:28Z | checksum: sha256:1805e1b08cf2b01e73cdd1dd28269bd5ccf5ccdeaf584ff43d5a68edd3d17cfc | entries: 1 | history: 1 -->
|
||||
Generated
+246
-273
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
{
|
||||
description = "Susano NixOS Homelab";
|
||||
description = "Alex's NixOS";
|
||||
|
||||
inputs = {
|
||||
# Nixpkgs
|
||||
@@ -41,7 +41,11 @@
|
||||
emacs-overlay.url = "github:nix-community/emacs-overlay/master";
|
||||
|
||||
stylix = {
|
||||
url = "github:nix-community/stylix";
|
||||
# TODO: revert to github:nix-community/stylix once
|
||||
# https://github.com/nix-community/stylix/pull/2501 is merged;
|
||||
# stylix master still sets `programs.rofi.font`, which
|
||||
# home-manager renamed to `programs.rofi.settings.font`.
|
||||
url = "github:Azd325/stylix/rofi-settings-font";
|
||||
inputs.nixpkgs.follows = "unstable";
|
||||
};
|
||||
|
||||
@@ -70,7 +74,12 @@
|
||||
|
||||
thefuck.url = "github:LichHunter/thefuck";
|
||||
|
||||
backlog.url = "github:LichHunter/backlog";
|
||||
backlog.url = "git+https://gitea.susano-homelab.duckdns.org/fujin/backlog.git";
|
||||
|
||||
noctalia = {
|
||||
url = "github:noctalia-dev/noctalia";
|
||||
inputs.nixpkgs.follows = "unstable";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
@@ -208,7 +217,36 @@
|
||||
"fujin";
|
||||
};
|
||||
|
||||
packages.x86_64-linux = {
|
||||
packages.x86_64-linux =
|
||||
let
|
||||
builderSystem = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
./machines/builder
|
||||
({ modulesPath, ... }: {
|
||||
imports = [ (modulesPath + "/virtualisation/proxmox-lxc.nix") ];
|
||||
})
|
||||
];
|
||||
specialArgs = {
|
||||
inherit inputs;
|
||||
};
|
||||
};
|
||||
in {
|
||||
# pve-build keys template identity on this output's outPath: the
|
||||
# store-path hash (covers nixpkgs pin + builder config) becomes the
|
||||
# template filename on PVE. overrideAttrs only names the derivation
|
||||
# so the filename reads <hash>-nixos-lxc-builder.tar.xz instead of
|
||||
# <hash>-tarball.tar.xz.
|
||||
nixos-lxc = builderSystem.config.system.build.image.overrideAttrs (_: {
|
||||
name = "nixos-lxc-builder";
|
||||
});
|
||||
|
||||
# Toplevel the builder image boots. pve-build compares it against
|
||||
# /run/current-system inside a running disposable to detect clones
|
||||
# of stale templates (pct config drops the ostemplate field after
|
||||
# creation, so it cannot be used for this).
|
||||
nixos-lxc-toplevel = builderSystem.config.system.build.toplevel;
|
||||
|
||||
izanami-proxmox = nixos-generators.nixosGenerate {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
{
|
||||
imports = [
|
||||
./waybar
|
||||
./noctalia
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.dov.bar.noctalia;
|
||||
c = config.lib.stylix.colors.withHashtag;
|
||||
|
||||
# Build a noctalia palette from the active stylix base16 scheme.
|
||||
# Both dark/light keys carry the same values so the palette works
|
||||
# regardless of which theme.mode noctalia picks.
|
||||
mkPalette = variant: {
|
||||
mPrimary = c.base0D;
|
||||
mOnPrimary = c.base00;
|
||||
mSecondary = c.base0E;
|
||||
mOnSecondary = c.base00;
|
||||
mTertiary = c.base0C;
|
||||
mOnTertiary = c.base00;
|
||||
mError = c.base08;
|
||||
mOnError = c.base00;
|
||||
mSurface = if variant == "dark" then c.base00 else c.base07;
|
||||
mOnSurface = if variant == "dark" then c.base05 else c.base01;
|
||||
mSurfaceVariant = if variant == "dark" then c.base01 else c.base06;
|
||||
mOnSurfaceVariant = if variant == "dark" then c.base04 else c.base02;
|
||||
mHover = if variant == "dark" then mkForce c.base02 else mkForce c.base06;
|
||||
mOnHover = if variant == "dark" then mkForce c.base06 else mkForce c.base01;
|
||||
mOutline = c.base03;
|
||||
mShadow = c.base00;
|
||||
terminal = {
|
||||
background = if variant == "dark" then c.base00 else c.base07;
|
||||
foreground = if variant == "dark" then c.base05 else c.base01;
|
||||
cursor = if variant == "dark" then c.base05 else c.base01;
|
||||
cursorText = if variant == "dark" then c.base00 else c.base07;
|
||||
selectionBg = c.base02;
|
||||
selectionFg = if variant == "dark" then c.base05 else c.base01;
|
||||
normal = {
|
||||
black = c.base00;
|
||||
red = c.base08;
|
||||
green = c.base0B;
|
||||
yellow = c.base0A;
|
||||
blue = c.base0D;
|
||||
magenta = c.base0E;
|
||||
cyan = c.base0C;
|
||||
white = c.base05;
|
||||
};
|
||||
bright = {
|
||||
black = c.base03;
|
||||
red = c.base08;
|
||||
green = c.base0B;
|
||||
yellow = c.base0A;
|
||||
blue = c.base0D;
|
||||
magenta = c.base0E;
|
||||
cyan = c.base0C;
|
||||
white = c.base07;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
themeMode = if config.stylix.polarity == "dark" then "dark" else "light";
|
||||
in
|
||||
{
|
||||
options.dov.bar.noctalia.enable = mkEnableOption "noctalia bar";
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
programs.noctalia = {
|
||||
enable = true;
|
||||
package = inputs.noctalia.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||
systemd.enable = true;
|
||||
|
||||
customPalettes.stylix = {
|
||||
dark = mkPalette "dark";
|
||||
light = mkPalette "light";
|
||||
};
|
||||
|
||||
settings = {
|
||||
theme = {
|
||||
mode = themeMode;
|
||||
source = "custom";
|
||||
custom_palette = "stylix";
|
||||
};
|
||||
|
||||
location.address = "Marseille, France";
|
||||
|
||||
bar.main = {
|
||||
position = "top";
|
||||
thickness = 34;
|
||||
radius = 12;
|
||||
margin_h = 0;
|
||||
margin_v = 6;
|
||||
padding = 14;
|
||||
widget_spacing = 6;
|
||||
capsule = true;
|
||||
capsule_fill = "surface_variant";
|
||||
capsule_radius = 20.0;
|
||||
reserve_space = true;
|
||||
|
||||
start = [ "workspaces" "media" "active_window" ];
|
||||
center = [ "clock" ];
|
||||
end = [ "tray" "caffeine" "keyboard_layout" "cpu" "ram" "brightness" "volume" "mic" "network" "battery" "session" ];
|
||||
};
|
||||
|
||||
widget = {
|
||||
clock = {
|
||||
format = "{:%a %d %b, %H:%M}";
|
||||
tooltip_format = "{:%A, %B %d, %Y}";
|
||||
};
|
||||
|
||||
workspaces.display = "id";
|
||||
|
||||
# Named sysmon instances — type field overrides the widget id
|
||||
cpu = {
|
||||
type = "sysmon";
|
||||
stat = "cpu_usage";
|
||||
};
|
||||
|
||||
ram = {
|
||||
type = "sysmon";
|
||||
stat = "ram_pct";
|
||||
};
|
||||
|
||||
# Microphone — named volume widget with input device
|
||||
mic = {
|
||||
type = "volume";
|
||||
device = "input";
|
||||
};
|
||||
|
||||
battery.warning_threshold = 30;
|
||||
|
||||
keyboard_layout = {
|
||||
display = "short";
|
||||
hide_when_single_layout = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -8,8 +8,8 @@ in {
|
||||
options.dov.browser.zen.enable = mkEnableOption "zen config";
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
home.packages = with pkgs; [
|
||||
inputs.zen-browser.packages."${system}".default
|
||||
home.packages = [
|
||||
inputs.zen-browser.packages."${pkgs.stdenv.hostPlatform.system}".default
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ let
|
||||
cfg = config.dov.dynamic-theme;
|
||||
schemes = "${pkgs.base16-schemes}/share/themes";
|
||||
|
||||
# Single source of truth for builtin themes
|
||||
# Curated defaults — covers the popular schemes without blowing up
|
||||
# build memory. Users can add more via `dov.dynamic-theme.themes`.
|
||||
builtinThemes = {
|
||||
gruvbox = {
|
||||
dark = "${schemes}/gruvbox-dark-hard.yaml";
|
||||
@@ -16,9 +17,42 @@ let
|
||||
dark = "${schemes}/catppuccin-mocha.yaml";
|
||||
light = "${schemes}/catppuccin-latte.yaml";
|
||||
};
|
||||
nord = {
|
||||
dark = "${schemes}/nord.yaml";
|
||||
light = "${schemes}/nord-light.yaml";
|
||||
};
|
||||
solarized = {
|
||||
dark = "${schemes}/solarized-dark.yaml";
|
||||
light = "${schemes}/solarized-light.yaml";
|
||||
};
|
||||
tokyo-night = {
|
||||
dark = "${schemes}/tokyo-night-dark.yaml";
|
||||
light = "${schemes}/tokyo-night-light.yaml";
|
||||
};
|
||||
rose-pine = {
|
||||
dark = "${schemes}/rose-pine.yaml";
|
||||
light = "${schemes}/rose-pine-dawn.yaml";
|
||||
};
|
||||
one = {
|
||||
dark = "${schemes}/onedark.yaml";
|
||||
light = "${schemes}/one-light.yaml";
|
||||
};
|
||||
material = {
|
||||
dark = "${schemes}/material-darker.yaml";
|
||||
light = "${schemes}/material-lighter.yaml";
|
||||
};
|
||||
google = {
|
||||
dark = "${schemes}/google-dark.yaml";
|
||||
light = "${schemes}/google-light.yaml";
|
||||
};
|
||||
github = {
|
||||
dark = "${schemes}/github-dark.yaml";
|
||||
light = "${schemes}/github.yaml";
|
||||
};
|
||||
};
|
||||
|
||||
# Generate all theme-variant combinations
|
||||
# Generate all theme-variant combinations from the configured themes.
|
||||
# Each theme produces a "-dark" and "-light" variant.
|
||||
themeVariants = concatMapAttrs (name: theme: {
|
||||
"${name}-dark" = { scheme = theme.dark; polarity = "dark"; };
|
||||
"${name}-light" = { scheme = theme.light; polarity = "light"; };
|
||||
@@ -112,28 +146,42 @@ in {
|
||||
};
|
||||
});
|
||||
default = builtinThemes;
|
||||
description = "Available themes with dark and light variants";
|
||||
description = ''
|
||||
Available themes with dark and light variants. Each entry
|
||||
generates two specialisations (`<name>-dark` and
|
||||
`<name>-light`). Add more here to extend the defaults.
|
||||
'';
|
||||
};
|
||||
|
||||
doomThemes = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {
|
||||
gruvbox-dark = "doom-gruvbox";
|
||||
gruvbox-light = "doom-one-light";
|
||||
catppuccin-dark = "doom-one";
|
||||
catppuccin-light = "doom-one-light";
|
||||
# Auto-generate sane defaults from the configured themes, then
|
||||
# overlay specific overrides for schemes that ship a dedicated
|
||||
# doom-theme.
|
||||
default =
|
||||
let
|
||||
generated = concatMapAttrs (name: _: {
|
||||
"${name}-dark" = "doom-one";
|
||||
"${name}-light" = "doom-one-light";
|
||||
}) cfg.themes;
|
||||
in generated // {
|
||||
"gruvbox-dark" = "doom-gruvbox";
|
||||
"solarized-dark" = "doom-solarized-dark";
|
||||
"solarized-light" = "doom-solarized-light";
|
||||
"tokyo-night-dark" = "doom-tokyo-night";
|
||||
"nord-dark" = "doom-nordic";
|
||||
"material-dark" = "doom-material";
|
||||
"rose-pine-dark" = "doom-rose-pine";
|
||||
"catppuccin-dark" = "doom-catppuccin-mocha";
|
||||
"catppuccin-light" = "doom-catppuccin-latte";
|
||||
};
|
||||
description = ''
|
||||
Mapping from theme variant name (e.g. "gruvbox-dark") to the Doom
|
||||
Emacs theme symbol (e.g. "doom-gruvbox") that theme-switch will
|
||||
live-load via emacsclient when switching to that variant. Variants
|
||||
absent from this map are left unchanged in Emacs.
|
||||
|
||||
Only themes actually installed in Doom will load; the defaults use
|
||||
themes bundled with doom-themes (so they work out of the box, though
|
||||
catppuccin variants fall back to generic dark/light). For accurate
|
||||
catppuccin colours, install the `catppuccin-theme` Emacs package and
|
||||
map the variants to `catppuccin-mocha` / `catppuccin-latte`.
|
||||
Mapping from variant name (e.g. "gruvbox-dark") to the Doom
|
||||
Emacs theme symbol. Defaults are auto-generated from the
|
||||
configured themes (dark → doom-one, light → doom-one-light)
|
||||
with overrides for schemes that have a dedicated doom-theme.
|
||||
Only themes installed in Doom will load; others are silently
|
||||
skipped.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -167,6 +215,7 @@ in {
|
||||
stylix = {
|
||||
enable = true;
|
||||
autoEnable = true;
|
||||
overlays.enable = false;
|
||||
# Base scheme/polarity; overridable per specialisation below.
|
||||
base16Scheme = mkDefault themeVariants.${baseVariant}.scheme;
|
||||
polarity = mkDefault themeVariants.${baseVariant}.polarity;
|
||||
@@ -213,5 +262,21 @@ in {
|
||||
Install.WantedBy = [ "graphical-session.target" ];
|
||||
};
|
||||
}
|
||||
|
||||
# Nushell tab-completion: declares `theme-switch`'s argument signature
|
||||
# so nushell completes variant names natively (takes priority over
|
||||
# carapace's generic fallback). `extraConfig` is `types.lines`, so it
|
||||
# merges cleanly with the carapace completer from hm-modules/shell/nu.
|
||||
(mkIf config.programs.nushell.enable {
|
||||
programs.nushell.extraConfig = ''
|
||||
def "nu-complete theme-variants" [] {
|
||||
"${availableVariants}" | split row " "
|
||||
}
|
||||
|
||||
extern theme-switch [
|
||||
variant: string@"nu-complete theme-variants"
|
||||
]
|
||||
'';
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ in {
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
# Workaround for https://github.com/NixOS/nix/issues/9574
|
||||
|
||||
@@ -19,7 +19,7 @@ in {
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
# Workaround for https://github.com/NixOS/nix/issues/9574
|
||||
|
||||
@@ -26,7 +26,7 @@ in {
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
# Workaround for https://github.com/NixOS/nix/issues/9574
|
||||
|
||||
@@ -33,7 +33,7 @@ in
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
# Workaround for https://github.com/NixOS/nix/issues/9574
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# Minimal NixOS configuration for a disposable LXC builder on Proxmox VE.
|
||||
#
|
||||
# Built via the `nixos-lxc` flake output using nixos-generators with
|
||||
# format = "proxmox-lxc". The resulting tarball is uploaded to PVE's
|
||||
# /var/lib/vz/template/cache/ and cloned by pve-build each time a
|
||||
# disposable builder is needed.
|
||||
#
|
||||
# Responsibilities of this image:
|
||||
# * Run nix with flakes as the `builder` user so nixos-rebuild
|
||||
# --build-host builder@<hostname> works against it.
|
||||
# * Be a competent builder: all cores, hardlink dedup, keep-derivations
|
||||
# and keep-outputs, keep-going on failure, idle CPU scheduling.
|
||||
# * Accept SSH from the operator (`builder` user + admin keys shared
|
||||
# across the rest of the homelab). Root SSH is disabled.
|
||||
# * Stay tiny — no bootloader, no kernel, no home-manager. The proxmox-lxc
|
||||
# format module from nixpkgs already sets boot.isContainer = true and
|
||||
# handles the LXC-specific bits.
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
inputs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
flakeInputs = lib.filterAttrs (_: lib.isType "flake") inputs;
|
||||
|
||||
# Operator keys — same ones baked into every other machine in this flake.
|
||||
# Whoever runs pve-build must hold a matching private key. Add more
|
||||
# keys here as needed; the authorized_keys list mirrors this exactly.
|
||||
adminKeys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBcGhVpjmWEw1GEw0y/ysJPa2v3+u/Rt/iES/Se2huH2 alexander0derevianko@gmail.com"
|
||||
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIGXvmStLAC4f+D9/b3/eKl6lb8xLQOfDqwu3Piocrr7ZAAAABHNzaDo= yubikey@fujin"
|
||||
];
|
||||
in {
|
||||
nixpkgs = {
|
||||
hostPlatform = "x86_64-linux";
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
|
||||
nix = {
|
||||
settings = {
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
flake-registry = "";
|
||||
nix-path = config.nix.nixPath;
|
||||
|
||||
# Parallelism — use every core the container can see.
|
||||
cores = 0; # 0 = use all visible cores per build job
|
||||
max-jobs = "auto"; # auto = one local build job per core
|
||||
|
||||
# Store hygiene — hardlink identical files so the store stays compact
|
||||
# across many sequential builds.
|
||||
auto-optimise-store = true;
|
||||
|
||||
# Cache reuse — keep derivation files and their outputs around even
|
||||
# when no current generation references them. Massively speeds up
|
||||
# repeated builds of the same flakes and lets you inspect what a
|
||||
# prior build actually pulled in.
|
||||
gc-keep-derivations = true;
|
||||
gc-keep-outputs = true;
|
||||
|
||||
# Don't abort the whole build on the first failing derivation — let
|
||||
# nix continue so the caller sees every failure in one pass.
|
||||
keep-going = true;
|
||||
|
||||
# `builder` drives builds via nixos-rebuild --build-host. Trust it so
|
||||
# `nix copy --to ssh://builder@<ct>` from the caller works without
|
||||
# extra configuration.
|
||||
trusted-users = [ "builder" ];
|
||||
allowed-users = [ "builder" ];
|
||||
|
||||
# Keep the default cache so flake inputs and build outputs resolve
|
||||
# without having to build them from source.
|
||||
substituters = lib.mkForce [ "https://cache.nixos.org/" ];
|
||||
trusted-public-keys = lib.mkForce [
|
||||
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
||||
];
|
||||
};
|
||||
channel.enable = false;
|
||||
|
||||
# Be polite to other tenants on the PVE node when no builds are running.
|
||||
daemonCPUSchedPolicy = "idle";
|
||||
|
||||
registry = lib.mapAttrs (_: flake: { inherit flake; }) flakeInputs;
|
||||
nixPath = lib.mapAttrsToList (n: _: "${n}=flake:${n}") flakeInputs;
|
||||
};
|
||||
|
||||
# Networking: PVE creates the veth pair and attaches it to the bridge, but
|
||||
# because NixOS is not a recognised PVE ostype (no setup plugin exists),
|
||||
# PVE cannot write the guest-side network config. We configure eth0 DHCP
|
||||
# ourselves via systemd-networkd. The proxmox-lxc module already enables
|
||||
# networking.useNetworkd; this block provides the matching .network file.
|
||||
systemd.network = {
|
||||
enable = true;
|
||||
networks."10-eth0" = {
|
||||
matchConfig.Name = "eth0";
|
||||
networkConfig.DHCP = "yes";
|
||||
};
|
||||
};
|
||||
|
||||
time.timeZone = "Europe/Warsaw";
|
||||
i18n.defaultLocale = "en_US.UTF-8";
|
||||
|
||||
# `builder` is the only SSH-reachable account on this disposable. It is a
|
||||
# normal user (no root login via SSH — see services.openssh.settings below)
|
||||
# but has passwordless sudo for the rare case a build needs it. Build
|
||||
# traffic itself goes through nix-daemon, which trusts `builder`.
|
||||
users.users.builder = {
|
||||
isNormalUser = true;
|
||||
description = "Disposable LXC builder";
|
||||
extraGroups = [ "wheel" ];
|
||||
openssh.authorizedKeys.keys = adminKeys;
|
||||
};
|
||||
|
||||
security.sudo = {
|
||||
enable = true;
|
||||
extraRules = [{
|
||||
users = [ "builder" ];
|
||||
commands = [{
|
||||
command = "ALL";
|
||||
options = [ "NOPASSWD" ];
|
||||
}];
|
||||
}];
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
vim
|
||||
wget
|
||||
curl
|
||||
ripgrep
|
||||
jq
|
||||
git
|
||||
tmux
|
||||
htop
|
||||
ncdu
|
||||
file
|
||||
iproute2
|
||||
nix-output-monitor # `nom build` for friendlier nix build output
|
||||
];
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PermitRootLogin = "no"; # only `builder` may log in
|
||||
PasswordAuthentication = false;
|
||||
};
|
||||
};
|
||||
|
||||
# This is a disposable image; do NOT change.
|
||||
system.stateVersion = "25.05";
|
||||
}
|
||||
@@ -66,9 +66,11 @@
|
||||
};
|
||||
|
||||
networking = {
|
||||
# OPNsense (192.168.100.1) runs Unbound with the homelab.lan overrides;
|
||||
# the old 192.168.1.x Pi-hole/router are gone and returned NXDOMAIN for
|
||||
# the local zone. 1.1.1.1 stays as a fallback if the firewall is down.
|
||||
nameservers = [
|
||||
"192.168.1.2" # PyHole
|
||||
"192.168.1.1" # Router
|
||||
"192.168.100.1" # OPNsense Unbound
|
||||
"1.1.1.1"
|
||||
];
|
||||
dhcpcd.extraConfig = ''
|
||||
@@ -112,6 +114,12 @@
|
||||
|
||||
dov = {
|
||||
development.emacs.enable = true;
|
||||
development.qoder = {
|
||||
enable = false;
|
||||
version = "1.19.2";
|
||||
hash = "sha256-/P3UStsRHKKWC1xET9SL23E0CUPxQ5DkmWFco+dH37A="; # nix store prefetch-file --json https://download.qoder.com/release/latest/qoder_amd64.deb | jq -r .hash
|
||||
};
|
||||
development.android.enable = true;
|
||||
|
||||
virtualisation.docker.enable = true;
|
||||
virtualisation.incus.enable = true;
|
||||
@@ -121,6 +129,8 @@
|
||||
display-manager.ly.enable = true;
|
||||
|
||||
gaming.enable = true;
|
||||
|
||||
yubikey.enable = true;
|
||||
};
|
||||
|
||||
###
|
||||
@@ -134,7 +144,9 @@
|
||||
# Stylix is handled entirely at the Home Manager level (see
|
||||
# hm-modules/theme). Load the stylix HM module here so the `stylix`
|
||||
# option exists for fujin's home configuration.
|
||||
sharedModules = [ inputs.stylix.homeModules.stylix ];
|
||||
sharedModules = [
|
||||
inputs.stylix.homeModules.stylix
|
||||
];
|
||||
|
||||
users."${username}" = { imports = [ ./home.nix ] ++ extraHomeModules; };
|
||||
};
|
||||
|
||||
@@ -42,6 +42,12 @@
|
||||
;; change `org-directory'. It must be set before org loads!
|
||||
(setq org-directory "~/org/")
|
||||
|
||||
;; Use bash, not nu, for non-interactive subprocesses (compile, shell-command,
|
||||
;; lsp-mode installers). Nu doesn't unescape Emacs's POSIX-style backslash
|
||||
;; escaping, which corrupts args like -Djdt.download.url=...?...=... .
|
||||
;; vterm keeps using nu (see vterm-shell below).
|
||||
(setq shell-file-name "/run/current-system/sw/bin/bash")
|
||||
|
||||
|
||||
;; Whenever you reconfigure a package, make sure to wrap your config in an
|
||||
;; `after!' block, otherwise Doom's defaults may override your settings. E.g.
|
||||
|
||||
@@ -47,9 +47,10 @@
|
||||
|
||||
browser.zen.enable = true;
|
||||
|
||||
#window-manager.hypr.enable = true;
|
||||
|
||||
bar.waybar.enable = true;
|
||||
bar = {
|
||||
waybar.enable = false;
|
||||
noctalia.enable = true;
|
||||
};
|
||||
|
||||
launcher.wofi.enable = true;
|
||||
|
||||
@@ -151,13 +152,14 @@
|
||||
wl-clipboard
|
||||
#cloudflared
|
||||
kdePackages.okular #pdf tool
|
||||
kdePackages.gwenview # image viewer
|
||||
#nextcloud-client
|
||||
|
||||
#music
|
||||
#mpd
|
||||
#mpv
|
||||
#mpc-cli
|
||||
] ++ [ inputs.thefuck.packages.${pkgs.system}.default ];
|
||||
] ++ [ inputs.thefuck.packages.${pkgs.stdenv.hostPlatform.system}.default ];
|
||||
|
||||
# Stylix theming is driven by `dov.dynamic-theme` (hm-modules/theme),
|
||||
# which sets `enable`, `base16Scheme` and `polarity` + specialisations.
|
||||
|
||||
@@ -26,7 +26,7 @@ in {
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
# Workaround for https://github.com/NixOS/nix/issues/9574
|
||||
|
||||
@@ -33,7 +33,7 @@ in {
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
# Workaround for https://github.com/NixOS/nix/issues/9574
|
||||
|
||||
@@ -51,7 +51,7 @@ in {
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ in
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
# Workaround for https://github.com/NixOS/nix/issues/9574
|
||||
|
||||
@@ -42,7 +42,7 @@ in
|
||||
nix = {
|
||||
settings = {
|
||||
# Enable flakes and new 'nix' command
|
||||
experimental-features = "nix-command flakes";
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
# Opinionated: disable global registry
|
||||
flake-registry = "";
|
||||
# Workaround for https://github.com/NixOS/nix/issues/9574
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
@@ -15,5 +15,7 @@
|
||||
./gitlab
|
||||
./jenkins
|
||||
./gaming
|
||||
./yubikey
|
||||
./scripts
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
username,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.dov.development.android;
|
||||
|
||||
mtk-udev-rules = pkgs.writeTextFile {
|
||||
name = "mtk-udev-rules";
|
||||
destination = "/etc/udev/rules.d/60-mtk.rules";
|
||||
text = ''
|
||||
# MediaTek BROM (0003), Preloader VCOM (2000/2001), META (6000)
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="0e8d", MODE="0666", TAG+="uaccess"
|
||||
# Oppo/Realme fastboot + adb
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="22d9", MODE="0666", TAG+="uaccess"
|
||||
|
||||
# keep ModemManager's hands off the preloader port
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="0e8d", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0e8d", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
options.dov.development.android = {
|
||||
enable = lib.mkEnableOption "Android and MediaTek flashing tools";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
android-tools
|
||||
mtkclient
|
||||
usbutils
|
||||
];
|
||||
|
||||
users.users.${username}.extraGroups = [ "dialout" ];
|
||||
|
||||
services.udev.packages = [ mtk-udev-rules ];
|
||||
};
|
||||
}
|
||||
@@ -4,5 +4,7 @@
|
||||
imports = [
|
||||
./nix-vscode-server
|
||||
./emacs
|
||||
./qoder
|
||||
./android
|
||||
];
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ in {
|
||||
## Emacs itself
|
||||
binutils # native-comp needs 'as', provided by this
|
||||
# 28.2 + native-comp
|
||||
((emacsPackagesFor emacs30).emacsWithPackages (epkgs: [
|
||||
((emacsPackagesFor emacs).emacsWithPackages (epkgs: [
|
||||
epkgs.vterm
|
||||
epkgs.treesit-grammars.with-all-grammars
|
||||
epkgs.mu4e
|
||||
@@ -39,13 +39,13 @@ in {
|
||||
|
||||
## Module dependencies
|
||||
# :checkers spell
|
||||
(aspellWithDicts (ds: with ds; [ en en-computers en-science ]))
|
||||
(aspellWithDicts (ds: with ds; [ en ]))
|
||||
# :tools editorconfig
|
||||
editorconfig-core-c # per-project style config
|
||||
# :tools lookup & :lang org +roam
|
||||
sqlite
|
||||
# :lang latex & :lang org (latex previews)
|
||||
texlive.combined.scheme-medium
|
||||
texliveMedium
|
||||
# :lang nix
|
||||
nixfmt
|
||||
#nixd
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.dov.development.qoder;
|
||||
qoder = pkgs.callPackage ./package.nix {
|
||||
inherit (cfg)
|
||||
version
|
||||
url
|
||||
hash
|
||||
extraFlags
|
||||
;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.dov.development.qoder = {
|
||||
enable = mkEnableOption "Qoder IDE (unfree, repackaged from the upstream .deb)";
|
||||
|
||||
version = mkOption {
|
||||
type = types.str;
|
||||
default = "1.19.2";
|
||||
description = ''
|
||||
Label only — upstream publishes a rolling "latest" URL, so this is
|
||||
just what the store path is called. Bump it together with `hash`.
|
||||
'';
|
||||
};
|
||||
|
||||
url = mkOption {
|
||||
type = types.str;
|
||||
default = "https://download.qoder.com/release/latest/qoder_amd64.deb";
|
||||
description = "Upstream .deb to repackage.";
|
||||
};
|
||||
|
||||
hash = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
example = "sha256-0000000000000000000000000000000000000000000=";
|
||||
description = ''
|
||||
SRI hash of the .deb. Refresh whenever upstream ships a new build:
|
||||
nix store prefetch-file --json <url> | jq -r .hash
|
||||
'';
|
||||
};
|
||||
|
||||
extraFlags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "--disable-gpu-sandbox" ];
|
||||
description = "Extra Electron/Chromium flags appended to the wrapper.";
|
||||
};
|
||||
|
||||
keyring = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Enable gnome-keyring. Qoder is wrapped with
|
||||
--password-store=gnome-libsecret; without a running secret service
|
||||
the sign-in token is lost on every restart.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.hash != "";
|
||||
message = ''
|
||||
dov.development.qoder.hash is empty. Prefetch the .deb first:
|
||||
nix store prefetch-file --json ${cfg.url} | jq -r .hash
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ qoder ];
|
||||
}
|
||||
|
||||
(mkIf cfg.keyring {
|
||||
services.gnome.gnome-keyring.enable = true;
|
||||
|
||||
# gnome-keyring enables gcr-ssh-agent by default, which asserts against
|
||||
# programs.ssh.startAgent from dov.yubikey. We only want the secret
|
||||
# service here — SSH keys stay with the OpenSSH agent.
|
||||
services.gnome.gcr-ssh-agent.enable = false;
|
||||
})
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
fetchurl,
|
||||
autoPatchelfHook,
|
||||
makeWrapper,
|
||||
wrapGAppsHook3,
|
||||
zstd,
|
||||
|
||||
alsa-lib,
|
||||
at-spi2-atk,
|
||||
at-spi2-core,
|
||||
atk,
|
||||
cairo,
|
||||
cups,
|
||||
dbus,
|
||||
expat,
|
||||
fontconfig,
|
||||
freetype,
|
||||
gdk-pixbuf,
|
||||
glib,
|
||||
gtk3,
|
||||
krb5,
|
||||
libdrm,
|
||||
libgbm,
|
||||
libglvnd,
|
||||
libnotify,
|
||||
libsecret,
|
||||
libuuid,
|
||||
libxkbcommon,
|
||||
nspr,
|
||||
nss,
|
||||
pango,
|
||||
systemd,
|
||||
zlib,
|
||||
|
||||
libx11,
|
||||
libxcb,
|
||||
libxcomposite,
|
||||
libxcursor,
|
||||
libxdamage,
|
||||
libxext,
|
||||
libxfixes,
|
||||
libxi,
|
||||
libxkbfile,
|
||||
libxrandr,
|
||||
libxrender,
|
||||
libxscrnsaver,
|
||||
libxshmfence,
|
||||
libxtst,
|
||||
|
||||
version,
|
||||
url,
|
||||
hash,
|
||||
passwordStore ? "gnome-libsecret",
|
||||
extraFlags ? [ ],
|
||||
}:
|
||||
|
||||
let
|
||||
runtimeLibs = [
|
||||
alsa-lib
|
||||
at-spi2-atk
|
||||
at-spi2-core
|
||||
atk
|
||||
cairo
|
||||
cups
|
||||
dbus
|
||||
expat
|
||||
fontconfig
|
||||
freetype
|
||||
gdk-pixbuf
|
||||
glib
|
||||
gtk3
|
||||
krb5
|
||||
libdrm
|
||||
libgbm
|
||||
libglvnd
|
||||
libnotify
|
||||
libsecret
|
||||
libuuid
|
||||
libxkbcommon
|
||||
nspr
|
||||
nss
|
||||
pango
|
||||
zlib
|
||||
(lib.getLib systemd)
|
||||
|
||||
libx11
|
||||
libxcb
|
||||
libxcomposite
|
||||
libxcursor
|
||||
libxdamage
|
||||
libxext
|
||||
libxfixes
|
||||
libxi
|
||||
libxkbfile
|
||||
libxrandr
|
||||
libxrender
|
||||
libxscrnsaver
|
||||
libxshmfence
|
||||
libxtst
|
||||
];
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "qoder";
|
||||
inherit version;
|
||||
|
||||
src = fetchurl {
|
||||
inherit url hash;
|
||||
name = "qoder-${version}_amd64.deb";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
autoPatchelfHook
|
||||
makeWrapper
|
||||
wrapGAppsHook3
|
||||
zstd
|
||||
];
|
||||
|
||||
buildInputs = runtimeLibs;
|
||||
|
||||
# dlopen'ed at runtime, so autoPatchelf can't see them in the ELF headers
|
||||
runtimeDependencies = [
|
||||
(lib.getLib systemd)
|
||||
libglvnd
|
||||
libgbm
|
||||
];
|
||||
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
dontWrapGApps = true; # wrapped by hand in preFixup
|
||||
|
||||
# `ar` comes from stdenv. --no-same-permissions keeps the setuid bit on
|
||||
# chrome-sandbox from blowing up the build inside the nix sandbox.
|
||||
unpackPhase = ''
|
||||
runHook preUnpack
|
||||
ar x $src
|
||||
tar -xf data.tar.* --no-same-permissions --no-same-owner
|
||||
runHook postUnpack
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out/share/qoder $out/bin
|
||||
|
||||
if [ -d usr/share/qoder ]; then
|
||||
cp -r usr/share/qoder/. $out/share/qoder/
|
||||
elif [ -d opt/Qoder ]; then
|
||||
cp -r opt/Qoder/. $out/share/qoder/
|
||||
elif [ -d opt/qoder ]; then
|
||||
cp -r opt/qoder/. $out/share/qoder/
|
||||
else
|
||||
echo "unexpected .deb layout:" >&2
|
||||
find . -maxdepth 3 -type d >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for d in applications icons pixmaps; do
|
||||
if [ -d "usr/share/$d" ]; then
|
||||
mkdir -p "$out/share/$d"
|
||||
cp -r "usr/share/$d/." "$out/share/$d/"
|
||||
fi
|
||||
done
|
||||
|
||||
# Store paths can never be setuid — drop the helper, use --no-sandbox.
|
||||
rm -f $out/share/qoder/chrome-sandbox
|
||||
|
||||
if [ ! -x $out/share/qoder/qoder ]; then
|
||||
echo "main binary not where expected:" >&2
|
||||
ls $out/share/qoder >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for f in $out/share/applications/*.desktop; do
|
||||
[ -e "$f" ] || continue
|
||||
substituteInPlace "$f" \
|
||||
--replace-quiet "/usr/share/qoder/qoder" "$out/bin/qoder" \
|
||||
--replace-quiet "/opt/Qoder/qoder" "$out/bin/qoder" \
|
||||
--replace-quiet "/opt/qoder/qoder" "$out/bin/qoder"
|
||||
done
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
# preFixup, not installPhase: gappsWrapperArgs is only populated by then.
|
||||
preFixup = ''
|
||||
makeWrapper $out/share/qoder/qoder $out/bin/qoder \
|
||||
"''${gappsWrapperArgs[@]}" \
|
||||
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath runtimeLibs}" \
|
||||
--set-default NIXOS_OZONE_WL 1 \
|
||||
--add-flags "--no-sandbox" \
|
||||
--add-flags "--password-store=${passwordStore}" \
|
||||
${lib.optionalString (extraFlags != [ ]) ''--add-flags "${lib.escapeShellArgs extraFlags}"''}
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Qoder — agentic coding IDE, repackaged from the upstream .deb";
|
||||
homepage = "https://qoder.com";
|
||||
license = lib.licenses.unfree;
|
||||
sourceProvenance = [ lib.sourceTypes.binaryNativeCode ];
|
||||
platforms = [ "x86_64-linux" ];
|
||||
mainProgram = "qoder";
|
||||
};
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
username,
|
||||
...
|
||||
}:
|
||||
{
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "pve-build" ''
|
||||
export PATH="${
|
||||
lib.makeBinPath [
|
||||
pkgs.jq
|
||||
pkgs.openssh
|
||||
]
|
||||
}:$PATH"
|
||||
${builtins.readFile ./pve-build.sh}
|
||||
'')
|
||||
];
|
||||
|
||||
home-manager.users.${username} = {
|
||||
programs.nushell.extraConfig =
|
||||
lib.mkIf config.home-manager.users.${username}.programs.nushell.enable
|
||||
''
|
||||
def "nu-complete pve-build-cmds" [] {
|
||||
[build test switch boot check-image deploy-image update-image start-builder build-on activate destroy-builder info]
|
||||
}
|
||||
|
||||
def "nu-complete pve-build-activate" [] {
|
||||
[test switch boot]
|
||||
}
|
||||
|
||||
extern pve-build [
|
||||
command?: string@"nu-complete pve-build-cmds"
|
||||
--machine: string
|
||||
--pve-host: string
|
||||
--pve-storage: string
|
||||
--rootfs-storage: string
|
||||
--bridge: string
|
||||
--vmid-start: int
|
||||
--vmid-floor: int
|
||||
--rootfs-gib: int
|
||||
--keep
|
||||
--local-build
|
||||
--show-trace
|
||||
--verbose
|
||||
--debug
|
||||
--help(-h)
|
||||
]
|
||||
'';
|
||||
};
|
||||
}
|
||||
Executable
+1143
File diff suppressed because it is too large
Load Diff
@@ -243,6 +243,9 @@ in {
|
||||
hl.exec_cmd("waybar")
|
||||
hl.exec_cmd("mako")
|
||||
''
|
||||
+ lib.optionalString hm-cfg.bar.noctalia.enable ''
|
||||
hl.exec_cmd("noctalia")
|
||||
''
|
||||
+ ''
|
||||
end)
|
||||
'';
|
||||
|
||||
@@ -18,7 +18,7 @@ listener {
|
||||
|
||||
listener {
|
||||
timeout = 300 # 5min
|
||||
on-timeout = loginctl lock-session # lock screen when timeout has passed
|
||||
on-timeout = hyprlock # lock screen when timeout has passed
|
||||
}
|
||||
|
||||
# listener {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.dov.yubikey;
|
||||
in {
|
||||
options.dov.yubikey = {
|
||||
enable = mkEnableOption "YubiKey integration (PAM U2F + SSH agent)";
|
||||
|
||||
pamControl = mkOption {
|
||||
type = types.enum [ "sufficient" "required" ];
|
||||
default = "sufficient";
|
||||
description = ''
|
||||
PAM control flag for U2F authentication.
|
||||
- "sufficient": touch YubiKey OR type password (default).
|
||||
Can't lock yourself out — if the key is missing or not
|
||||
enrolled, the normal password prompt follows.
|
||||
- "required": touch YubiKey AND type password (true 2FA).
|
||||
'';
|
||||
};
|
||||
|
||||
pamServices = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "login" "sudo" ];
|
||||
description = ''
|
||||
PAM services to enable U2F for. Scoped per-service rather
|
||||
than globally so that e.g. sshd is not affected.
|
||||
'';
|
||||
};
|
||||
|
||||
authFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = ''
|
||||
Central U2F authfile path. When null, uses the default
|
||||
per-user location (~/.config/Yubico/u2f_keys). Set to a
|
||||
nix-store path for a central, non-user-writable mapping.
|
||||
|
||||
Generate mappings with:
|
||||
pamu2fcfg -u <username>
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
# --- Shared infrastructure: smart card daemon + device access ---
|
||||
# hardware.gpgSmartcards installs the CCID udev rules that give
|
||||
# pcscd permission to open the YubiKey's smart-card USB interface.
|
||||
hardware.gpgSmartcards.enable = true;
|
||||
services.pcscd.enable = true;
|
||||
services.udev.packages = with pkgs; [
|
||||
yubikey-personalization
|
||||
libfido2
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
yubikey-manager # `ykman` CLI
|
||||
pam_u2f # `pamu2fcfg` for key enrollment
|
||||
libfido2 # `fido2-token` management
|
||||
];
|
||||
}
|
||||
|
||||
# --- PAM U2F: touch YubiKey for login / sudo ---
|
||||
{
|
||||
security.pam.u2f = {
|
||||
enable = true;
|
||||
control = cfg.pamControl;
|
||||
settings = {
|
||||
cue = true; # "Please touch the device."
|
||||
interactive = true; # "Insert your U2F device, then press ENTER."
|
||||
nouserok = true; # fall through to password if key not enrolled yet
|
||||
} // optionalAttrs (cfg.authFile != null) {
|
||||
inherit (cfg) authFile;
|
||||
};
|
||||
};
|
||||
|
||||
# Enable U2F per-service, not globally (avoids enabling for sshd).
|
||||
security.pam.services = genAttrs cfg.pamServices (_: {
|
||||
u2fAuth = true;
|
||||
});
|
||||
}
|
||||
|
||||
# --- SSH agent for FIDO2 (-sk) keys ---
|
||||
# yubikey-agent is incompatible with YubiKey firmware 5.7.x (PIV
|
||||
# management key auth changed). GPG agent's SSH emulation doesn't
|
||||
# reliably handle -sk keys. Use the standard OpenSSH agent instead,
|
||||
# which is the same software stack that created the key.
|
||||
{
|
||||
programs.gnupg.agent.enableSSHSupport = mkForce false;
|
||||
programs.ssh.startAgent = true;
|
||||
}
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user