Author SHA1 Message Date
moons-14 a2ee175609 fix(noctalia): split taskbar overview hook patch 2026-08-05 04:01:29 +09:00
moons-14 66bf009afd fix(noctalia): restore valid window switcher patch 2026-08-05 04:01:14 +09:00
moons-14 33e09f8e93 normcap-translate 2026-08-05 03:56:16 +09:00
moons-14 eff32fddcd background-opacity 2026-08-05 03:56:16 +09:00
moons-14 8ce3a6082a dns 2026-08-05 03:56:16 +09:00
moons-14 a8246261ad noctalia: add taskbar overview command hook (#61) 2026-08-05 03:54:14 +09:00
moons-14 8b51b5c55f noctalia taskbar 2026-08-05 02:55:46 +09:00
moons-14 e24d85da56 echo cancel 2026-08-05 02:10:48 +09:00
moons-14 01ead9a385 labwc suspend 2026-08-05 02:10:36 +09:00
moons-14 328f11d0ed screencast 2026-08-05 01:40:35 +09:00
moons-14 d877ffc76c nh 2026-08-05 01:40:18 +09:00
moons-14 fe40adaeee activity watch 2026-08-05 00:59:32 +09:00
moons-14 991dde5305 noctalia patch 2026-08-05 00:40:02 +09:00
moons-14 96ce4d768c nani wayland 2026-08-05 00:16:12 +09:00
moons-14 30b1480e50 hazkey 2026-08-04 23:06:25 +09:00
moons-14 71011d7bd1 thunderbird 2026-08-04 23:06:11 +09:00
moons-14 9cced59e58 thunderbird 2026-08-04 23:06:01 +09:00
moons-14 20a601402e rate 2026-08-04 17:03:03 +09:00
moons-14 1b4a5fa5a2 wallpaper engine 2026-08-04 16:58:40 +09:00
moons-14 5fb55f2e6a open ghostty 2026-08-04 16:49:56 +09:00
moons-14 2a3f7ee6ff nani 2026-08-04 16:16:32 +09:00
moons-14 247db71f2d nani 2026-08-04 16:04:44 +09:00
moons-14 a44a83a587 handy 2026-08-04 15:43:12 +09:00
moons-14 1f1d46ea2a find-cursor 2026-08-04 15:26:43 +09:00
moons-14 29c4815b88 screenshot 2026-08-04 14:59:21 +09:00
moons-14 28a46d9990 skill 2026-08-04 14:42:31 +09:00
moons-14 403971eef6 vesktop
CI: NixOS / Check all NixOS configurations (push) Canceled after 0s
2026-08-04 01:22:34 +09:00
moons-14 c62468396d chrome 2026-08-03 23:35:42 +09:00
moons-14 9145b36412 wallpaper 2026-08-03 23:16:21 +09:00
moons-14 f574b1d1e7 obs 2026-08-03 23:16:14 +09:00
moons-14 cf088a6998 labwc 2026-08-03 22:56:44 +09:00
moons-14 e4eb1802d7 screen shot 2026-08-03 22:29:39 +09:00
moons-14 a7c6a1507c ghostty 2026-08-03 22:27:58 +09:00
moons-14 49141e3478 kanshi 2026-08-03 21:45:35 +09:00
moons-14 cc91ad88b0 noctalia doc 2026-08-03 21:44:02 +09:00
moons-14 bb1f81a598 codex bar 2026-08-03 21:42:33 +09:00
moons-14 c0fdd9b4ef vscode 2026-08-03 21:35:44 +09:00
moons-14 aafcc1555d vicinae 2026-08-03 21:31:30 +09:00
moons-14 245f22e094 hash 2026-08-03 20:07:54 +09:00
moons-14 15ca59e43c suspend 2026-08-03 19:59:45 +09:00
moons-14 3627587bc7 niri 2026-08-03 18:17:42 +09:00
moons-14 d90aed6903 vicinae 2026-08-03 17:45:24 +09:00
moons-14 3e57b6c4c1 Keyring 2026-08-03 15:52:28 +09:00
moons-14 bdca6fb2fb lock screen 2026-08-03 15:28:52 +09:00
moons-14 98397cf152 polkit-gnome 2026-08-03 15:11:58 +09:00
moons-14 3162bc0eba neovim 2026-08-03 15:10:03 +09:00
moons-14 c3bb2f4d43 mac dock 2026-08-03 15:08:41 +09:00
moons-14 5edbc6cbb4 zsh 2026-08-03 14:50:42 +09:00
moons-14 bf28275b40 zoom 2026-08-03 14:46:57 +09:00
moons-14 92b4bc7b8d yazi 2026-08-03 14:33:31 +09:00
moons-14 9bbef37010 vscode 2026-08-03 14:33:25 +09:00
moons-14 e708ceee26 nh 2026-08-03 14:32:59 +09:00
moons-14 7604dfb0e3 vlc 2026-08-03 14:04:42 +09:00
moons-14 02f97f73df vicinae 2026-08-03 14:02:41 +09:00
moons-14 cde17e6a68 tealdeer 2026-08-03 13:40:02 +09:00
moons-14 64fe5020d0 ssh 2026-08-03 13:34:02 +09:00
moons-14 843dd0cbf4 slack 2026-08-03 13:32:27 +09:00
moons-14 0060fdae15 gnome app 2026-08-03 13:27:35 +09:00
moons-14 6402cdcf3a nautilus right click 2026-08-03 13:27:23 +09:00
moons-14 118d91f1d5 nautilus 2026-08-03 13:05:38 +09:00
moons-14 b261f4aec7 remote desktop 2026-08-03 13:05:34 +09:00
moons-14 b146081ba8 minecraft 2026-08-03 13:05:24 +09:00
moons-14 6f19ea8f23 prismlauncher 2026-08-03 12:47:45 +09:00
moons-14 aaa4542f25 mangohud 2026-08-03 12:33:25 +09:00
moons-14 d3ce44ff63 loupe 2026-08-03 12:32:10 +09:00
moons-14 fb3254daa1 playerctl 2026-08-03 12:28:50 +09:00
moons-14 e69203ce4c keybind 2026-08-03 12:24:38 +09:00
moons-14 e84fa82710 niri 2026-08-03 11:44:27 +09:00
moons-14 d5e4c90710 fcitx5 on labwc 2026-08-03 11:44:15 +09:00
moons-14 d67d53644e fcitx5 2026-08-03 09:59:01 +09:00
moons-14 f1dd96945a vscode 2026-08-03 09:50:43 +09:00
moons-14 aceb3d4808 labwc 2026-08-02 23:53:38 +09:00
moons-14 ac874a849e grok 2026-08-02 22:06:39 +09:00
moons-14 519f7dd54d gnone apps 2026-08-02 22:04:46 +09:00
moons-14 233ba91309 ghostty 2026-08-02 21:57:19 +09:00
moons-14 e6c80ad5d6 gamemode 2026-08-02 21:48:44 +09:00
moons-14 847d7ee6dd nix-index 2026-08-02 21:48:38 +09:00
moons-14 5e12c1d953 fxitx5 2026-08-02 21:35:35 +09:00
moons-14 dfc4dc79d6 easyeffects 2026-08-02 20:39:24 +09:00
moons-14 5deab38d3c drawio 2026-08-02 20:35:08 +09:00
moons-14 3c674e34c1 docker 2026-08-02 20:33:19 +09:00
moons-14 506602d7e1 vesktop 2026-08-02 20:25:18 +09:00
moons-14 6b4253e1d4 direnv 2026-08-02 20:17:14 +09:00
moons-14 a3660b5733 chatgpt 2026-08-02 20:14:08 +09:00
moons-14 894b18bd10 chrome 2026-08-02 19:57:32 +09:00
moons-14 08effb9e29 celluloid 2026-08-02 19:52:47 +09:00
moons-14 605e63acdb home file 2026-08-02 19:40:54 +09:00
moons-14 12c9d5241b galleria fingerprint 2026-08-02 19:11:42 +09:00
moons-14 ac44bdf22f baobab 2026-08-02 19:07:23 +09:00
moons-14 421ede5d57 atuin 2026-08-02 19:03:25 +09:00
moons-14 7204e3e8f6 1password 2026-08-02 19:00:34 +09:00
moons-14 f696ed6b93 remove pear-desktop 2026-08-02 18:56:37 +09:00
moons-14 d1891382ea 1password 2026-08-02 18:54:40 +09:00
moons-14 4a7516939c galleria disk uuid 2026-08-02 18:28:13 +09:00
moons-14 176cd60d68 feat 2026-08-02 17:54:30 +09:00
moons-14 7c66c15da5 Replace Parsec with Sunshine and Moonlight 2026-07-31 23:56:43 +09:00
moons-14 dddb7e07ab galleria chrome beta 2026-07-31 23:40:25 +09:00
moons-14 9ede34fcfd fix 2026-07-31 23:07:22 +09:00
moons-14 855b8c55cf luncher 2026-07-31 22:49:18 +09:00
moons-14 a51e9584b0 apps 2026-07-31 22:06:53 +09:00
moons-14 3dae1d26ef noctalia 2026-07-31 22:02:52 +09:00
moons-14 06cd9516b3 side folder 2026-07-31 21:24:40 +09:00
moons-14 f3098f2674 feat 2026-07-31 15:29:21 +09:00
moons-14 2f9c8f3d33 text editor 2026-07-31 14:39:43 +09:00
moons-14 82d00fb574 systemd 2026-07-31 14:39:33 +09:00
moons-14 ad6dff2f6a ly 2026-07-31 14:19:55 +09:00
moons-14 54b84c0544 noctalia workspace 2026-07-31 13:58:24 +09:00
moons-14 c3dbcda528 display 2026-07-31 13:58:09 +09:00
moons-14 480a1c3194 display 2026-07-31 13:40:16 +09:00
moons-14 37d4a4a7c5 kmscon 2026-07-31 13:40:08 +09:00
moons-14 e68e2f536f remove app list 2026-07-31 13:26:27 +09:00
moons-14 36f01a084f galleria host 2026-07-31 12:24:17 +09:00
moons-14 e9ab8c2caa sops add host 2026-07-31 12:23:05 +09:00
moons-14 7ef25c5e63 galleria hardware 2026-07-31 12:02:52 +09:00
moons-14 d6026a5bfc update 2026-07-30 18:02:00 +09:00
moons-14 b477446f5c galleria 2026-07-30 16:46:44 +09:00
moons-14 ee9ce66d55 mozc 2026-07-30 16:45:45 +09:00
moons-14 ecabb8b630 nwg-drawer 2026-07-30 16:16:41 +09:00
moons-14 831ae7bbc2 labwc 2026-07-30 15:56:46 +09:00
moons-14 8dc9452d95 labwc 2026-07-30 15:24:55 +09:00
moons-14 c566f426ec zoom 2026-07-30 02:05:35 +09:00
moons-14 e56af06b04 display 2026-07-30 02:05:29 +09:00
moons-14 acb5ed3449 zoom 2026-07-30 01:33:43 +09:00
moons-14 9f40aadab3 vscode 2026-07-30 01:32:32 +09:00
moons-14 55e1e0b5b4 minecraft mac 2026-07-30 01:06:13 +09:00
moons-14 aef1338d79 minecraft 2026-07-29 14:30:22 +09:00
moons-14 5bbf1d42e9 ci 2026-07-29 10:19:59 +09:00
moons-14 f02eeac2a3 parsec 2026-07-29 10:14:22 +09:00
moons-14 e23e0058bd privact 2026-07-29 09:52:32 +09:00
moons-14 e474c38aff parsec 2026-07-29 09:33:54 +09:00
moons-14 04e4bdaeff windows codex update error 2026-07-29 04:57:38 +09:00
moons-14 c02d6936fa windows 2026-07-29 04:54:08 +09:00
moons-14 3f35a54e0a windows 2026-07-29 04:01:25 +09:00
moons-14 601c94a5d8 niri 2026-07-28 01:33:30 +09:00
moons-14 7e8bd33c89 mac 2026-07-28 01:01:21 +09:00
moons-14 6f4f048f4c mac keyboard 2026-07-28 00:45:32 +09:00
moons-14 c2a5e174b9 mac dock 2026-07-28 00:30:46 +09:00
moons-14 b1954ba5be add hosts 2026-07-28 00:19:14 +09:00
moons-14 09a4fd83a7 noctalia 2026-07-28 00:07:48 +09:00
moons-14 1f4ec6b8cd x1g13 2026-07-27 23:45:54 +09:00
moons-14 5132a1af1b vicinae 2026-07-27 23:16:30 +09:00
moons-14 2a02beda38 mac 2026-07-27 22:27:49 +09:00
moons-14 ee0bd37915 mac 2026-07-27 22:04:11 +09:00
moons-14 a8113633db oxker test 2026-07-27 21:29:20 +09:00
moons-14 91bb7e80db feat hosts 2026-07-27 21:18:00 +09:00
moons-14 9bb95535cf fingerprint 2026-07-27 20:57:15 +09:00
moons-14 fcd0d75537 feat 2026-07-27 20:55:05 +09:00
moons-14 a8e9a4dce5 update flake.nix 2026-07-27 20:34:27 +09:00
moons-14 72ff60fb16 x1g9 2026-07-27 19:58:07 +09:00
moons-14 f65318b765 mac 2026-07-27 19:43:42 +09:00
moons-14 0257b2e2fd mac 2026-07-27 19:43:31 +09:00
moons-14 8fec0494ec feat 2026-07-27 19:31:25 +09:00
moons-14 89eeb23d1c add docs 2026-07-27 18:41:42 +09:00
moons-14 bf94d1183f add profiles 2026-07-27 18:31:50 +09:00
moons-14 6bd05887db add systems 2026-07-27 18:02:48 +09:00
moons-14 3e32c9874b add apps 2026-07-27 16:49:48 +09:00
moons-14 684acef46c add apps 2026-07-27 16:10:17 +09:00
moons-14 9061825c63 add apps 2026-07-27 16:10:03 +09:00
moons-14 e703e1b31c add vim, vscode, zed, zellij, zsh 2026-07-27 15:24:37 +09:00
moons-14 79cc69045f add gtk, niri, noctalia, ssh, swaylock 2026-07-27 15:10:46 +09:00
moons-14 5c2ff3cbcf add nix-index, ly 2026-07-27 14:32:59 +09:00
moons-14 c5d4de5a9d add fcitx5, ghostty, git 2026-07-27 14:06:13 +09:00
moons-14 c58bdd30c3 x1g9 2026-07-27 12:34:20 +09:00
moons-14 9771a85e3f x1g9 2026-07-27 11:26:27 +09:00
moons-14 5b1bde7d90 nix registory 2026-07-27 10:58:24 +09:00
moons-14 1d82ab92b6 feat 2026-07-27 10:05:55 +09:00
356 changed files with 12188 additions and 176 deletions
+27
View File
@@ -0,0 +1,27 @@
# Reference: https://github.com/ryoppippi/dotfiles/blob/main/.github/workflows/nix-build.yaml
name: Check NixOS configurations
description: Build every NixOS configuration and the Registry tests
runs:
using: composite
steps:
- name: Build every NixOS configuration
shell: bash
run: |
set -euo pipefail
mapfile -t hosts < <(
nix eval --raw .#nixosConfigurations \
--apply 'configs: builtins.concatStringsSep "\n" (builtins.attrNames configs)'
)
installables=(.#checks.x86_64-linux.registry)
for host in "${hosts[@]}"; do
installables+=(".#nixosConfigurations.${host}.config.system.build.toplevel")
done
nix build \
--keep-going \
--no-link \
--print-build-logs \
--show-trace \
"${installables[@]}"
+22
View File
@@ -0,0 +1,22 @@
# Reference: https://github.com/ryoppippi/dotfiles/blob/main/.github/actions/setup-nix/action.yaml
name: Setup Nix
description: Install Nix and cache the Nix store
runs:
using: composite
steps:
- name: Allow unprivileged user namespaces
if: runner.os == 'Linux'
shell: bash
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 2>/dev/null || true
- name: Install Nix
uses: nixbuild/nix-quick-install-action@9f63be77f412a248c9d9a65a4c82cf066cdf8f0c # v35
with:
nix_conf: |
accept-flake-config = true
max-jobs = auto
- name: Cache Nix store
uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}-
gc-max-store-size-linux: 4G
+51
View File
@@ -0,0 +1,51 @@
# Reference: https://github.com/ryoppippi/dotfiles/blob/main/.github/workflows/nix-build.yaml
name: "CI: NixOS"
on:
push:
branches:
- main
paths:
- flake.nix
- flake.lock
- "flake/**"
- "hosts/**"
- "libs/**"
- "modules/**"
- "overlays/**"
- "shells/**"
- "tests/**"
- ".github/actions/check-nixos/**"
- ".github/actions/setup-nix/**"
- ".github/workflows/nixos.yaml"
pull_request:
paths:
- flake.nix
- flake.lock
- "flake/**"
- "hosts/**"
- "libs/**"
- "modules/**"
- "overlays/**"
- "shells/**"
- "tests/**"
- ".github/actions/check-nixos/**"
- ".github/actions/setup-nix/**"
- ".github/workflows/nixos.yaml"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
check:
name: Check all NixOS configurations
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Setup Nix
uses: ./.github/actions/setup-nix
- name: Check NixOS configurations
uses: ./.github/actions/check-nixos
+48
View File
@@ -0,0 +1,48 @@
# Reference: https://github.com/ryoppippi/dotfiles/blob/main/.github/workflows/update-flake.yaml
name: "Bot: Update flake inputs"
on:
schedule:
- cron: "0 6 * * *"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
permissions:
contents: write
pull-requests: write
jobs:
update:
name: Update and validate flake inputs
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Setup Nix
uses: ./.github/actions/setup-nix
- name: Update flake inputs
id: update
run: |
nix flake update
if git diff --quiet -- flake.lock; then
echo 'changed=false' >> "$GITHUB_OUTPUT"
else
echo 'changed=true' >> "$GITHUB_OUTPUT"
fi
- name: Check updated NixOS configurations
if: steps.update.outputs.changed == 'true'
uses: ./.github/actions/check-nixos
- name: Create update pull request
if: steps.update.outputs.changed == 'true'
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: flake.lock
branch: automation/update-flake-lock
delete-branch: true
commit-message: "flake: update inputs"
title: "flake: update inputs"
body: |
Automated update of `flake.lock`.
The updated inputs passed the Registry tests and a build of every NixOS configuration.
+12 -6
View File
@@ -4,6 +4,7 @@
!README.md
!LICENSE
!AGENTS.md
!/docs/
!.github/
!.gitea/
@@ -19,9 +20,14 @@
!/flake/
!/overlays/
!/modules/applications/
!/modules/hardwares/
!/modules/profiles/
!/modules/services/
!/modules/systems/
!/modules/users/
!/images/
!/secrets/
!/windows/
!/hosts/
!/libs/
!/modules/
!/tests/
!/skills/
+20
View File
@@ -0,0 +1,20 @@
keys:
- &admin_yubikey1 age1yubikey1qvy5y8kxqc63y7fk0tfv43u499a8z8q332ff087lythry9cc6hdxxkznc6t
- &host_x1g13 age12g85cuvg4kjfr79lqf5fx2k0d82tchrgv88xgkt7ukk2cfcsw98s2rjyat
- &host_ops age18rtm2dq2r62zvnhwdq0gkm24hu85r7zyleyk3jqv22zpdtw064eq7ay7dl
- &host_internal-app-01 age1mcp5gma7y0k59equhzqfsnn0ed335ljjtn0k08ua77htlxf0x54qsravch
- &host_galleria age1wh7r9wnvyrgt5efjvg2324khsf4w6e9atpmz2udr4uw7frhnvvwquzcu72
creation_rules:
- path_regex: ^secrets/common/[^/]+\.ya?ml$
key_groups:
- age:
- *admin_yubikey1
- *host_x1g13
- *host_ops
- *host_internal-app-01
- *host_galleria
- path_regex: ^secrets/hosts/x1g13/[^/]+\.ya?ml$
key_groups:
- age:
- *admin_yubikey1
- *host_x1g13
+828 -14
View File
@@ -1,28 +1,842 @@
# Repository Guidelines
## Project Structure & Module Organization
## Project Structure and Ownership
This repository manages NixOS and Home Manager configuration as a flake. `flake.nix` defines inputs and imports the project modules. Keep flake-level plumbing in `flake/`, reusable package overlays in `overlays/`, and development environments in `shells/`. Add configuration under the appropriate `modules/` category: `applications/`, `hardwares/`, `profiles/`, `services/`, `systems/`, or `users/`. Keep host- or user-specific choices near their owning module rather than in the root flake.
This repository manages NixOS, nix-darwin, and Home Manager configurations as a
flake. `flake.nix` defines inputs and delegates flake outputs through
flake-parts. Keep configuration with the component that owns it, rather than in
the root flake or an unrelated host.
This file documents the current repository contract, not a hypothetical future
layout. When a structural, ownership, profile, host-role, or validation change
makes any statement here stale, update `AGENTS.md` in the same change.
| Path | Responsibility |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `modules/applications/` | One software component, including GUI applications, window managers, desktop environments, CLI tools, and editors |
| `modules/systems/` | OS foundations such as Nix, boot, locale, Wayland, and networking |
| `modules/services/` | Daemons, long-running services, and configuration that involves permissions or user groups |
| `modules/hardwares/` | Reusable drivers, hardware families, and VM or WSL guest configuration |
| `modules/users/` | User identity and the user's NixOS-, nix-darwin-, and Home Manager-specific definitions |
| `modules/profiles/` | Purpose- or form-factor-oriented compositions of multiple units |
| `hosts/` | Machine-specific facts and the profiles selected for each machine; direct unit selections are exceptional |
| `libs/` | Registry, unit discovery, and host construction logic |
| `overlays/` | Package replacements and additions |
| `shells/` | Development shells |
| `flake/` | Supporting flake outputs such as formatters, checks, and Git hooks |
| `skills/` | Repository-specific Codex workflows that enforce this contract for recurring changes |
Before adding or materially extending an application or service, read and
follow `skills/add-application-or-service/SKILL.md`. `AGENTS.md` remains the
authoritative contract when the skill and repository ever disagree.
Use **unit** as the generic internal term for a Registry-managed component and
**profile** for a unit that composes multiple units. Do not introduce a
`features/` layer. Window managers and desktop environments such as niri and
labwc belong in `modules/applications/`; do not create a separate `desktop/`
module category.
Before adding configuration, decide whether it is owned by an application,
system foundation, service, hardware family, user, profile, or individual host.
Prefer the following placements:
| Configuration | Placement |
| ---------------------------------------------------- | --------------------------------------------------------- |
| Nix settings shared by every system host | `modules/systems/nix/common.nix` |
| NixOS-only boot configuration | `modules/systems/boot/.../nixos.nix` |
| Disko NixOS module and CLI | `modules/systems/disko/` |
| macOS-wide input, document, and dialog defaults | `modules/systems/macos-defaults/darwin.nix` |
| macOS Dock defaults | `modules/systems/dock/darwin.nix` |
| macOS trackpad defaults | `modules/systems/trackpad/darwin.nix` |
| Finder-specific preferences | `modules/applications/finder/darwin.nix` |
| Ghostty-specific configuration | `modules/applications/ghostty/` |
| niri-specific configuration | `modules/applications/niri/` |
| Desktop applications shared by labwc and niri | `modules/profiles/interface/linux-desktop/meta.nix` |
| Applications and services specific to niri | `modules/profiles/interface/niri/meta.nix` |
| labwc and its session configuration | `modules/applications/labwc/` |
| A Linux package plus its macOS Homebrew cask | `modules/applications/<name>/home.nix` and `darwin.nix` |
| Docker daemon and Docker group membership | `modules/services/docker/nixos.nix` |
| The laptop unit composition | `modules/profiles/platform/laptop/meta.nix` |
| The Intel ThinkPad X1 composition | `modules/profiles/platform/thinkpad-x1/meta.nix` |
| The Intel/NVIDIA desktop composition | `modules/profiles/platform/intel-nvidia-desktop/meta.nix` |
| NVIDIA GPU driver configuration | `modules/hardwares/nvidia/` |
| The development-environment unit composition | `modules/profiles/workload/development/meta.nix` |
| Cross-platform fingerprint selection | `modules/profiles/security/fingerprint/meta.nix` |
| A user's OS- and Home Manager-specific configuration | `modules/users/<name>/` |
| Host-specific monitor layout | `hosts/<name>/home.nix` |
| Generated host disk UUIDs | `hosts/<name>/hardware-configuration.nix` |
| Package replacement or addition | `overlays/` |
| Formatter, checks, or Git hooks | `flake/` |
## Unit Discovery and Identity
A directory below `modules/` is a unit if, and only if, it contains at least one
reserved root file or reserved Home Manager fragment. Directories used only for
classification, such as `modules/applications/` or
`modules/profiles/interface/`, are namespaces rather than units when they have
no reserved fragment of their own.
The Registry recognizes exactly these eight reserved paths relative to a unit:
| File | Target and responsibility |
| ----------------- | -------------------------------------------------------------------------------- |
| `common.nix` | System-side configuration fragment shared by NixOS and nix-darwin |
| `nixos.nix` | NixOS-only system configuration fragment |
| `darwin.nix` | nix-darwin-only system configuration fragment |
| `home.nix` | Home Manager fragment shared by NixOS and nix-darwin |
| `home/common.nix` | Home Manager fragment shared by NixOS and nix-darwin |
| `home/nixos.nix` | Home Manager fragment loaded only on NixOS |
| `home/darwin.nix` | Home Manager fragment loaded only on nix-darwin |
| `meta.nix` | Registry descriptor for dependencies, external modules, and descriptive metadata |
Root `common.nix` is never applied to Home Manager. `home.nix` and
`home/common.nix` have identical dispatch semantics; use either or both when a
useful file split exists. The `home/` directory is a reserved fragment directory
of its parent unit when it contains `common.nix`, `nixos.nix`, or `darwin.nix`;
it is not discovered as a child unit in that case.
The Registry derives a unit ID from the path relative to `modules/`, joining
path components with dots. Category names remain plural. It also derives the
enable option by prefixing the same components with `my` and appending `enable`.
| Unit directory | Unit ID | Enable option |
| ---------------------------------- | ------------------------- | ----------------------------------- |
| `modules/applications/ghostty/` | `applications.ghostty` | `my.applications.ghostty.enable` |
| `modules/applications/niri/` | `applications.niri` | `my.applications.niri.enable` |
| `modules/systems/boot/uefi/` | `systems.boot.uefi` | `my.systems.boot.uefi.enable` |
| `modules/services/docker/` | `services.docker` | `my.services.docker.enable` |
| `modules/hardwares/qemu-guest/` | `hardwares.qemu-guest` | `my.hardwares.qemu-guest.enable` |
| `modules/users/moons/` | `users.moons` | `my.users.moons.enable` |
| `modules/profiles/interface/niri/` | `profiles.interface.niri` | `my.profiles.interface.niri.enable` |
Represent option paths as attribute-path lists, never as Nix source encoded in
strings or evaluated dynamically. Generate and read attributes with helpers such
as `lib.setAttrByPath` and `lib.getAttrFromPath`:
```nix
{
id = "applications.ghostty";
optionPath = [
"my"
"applications"
"ghostty"
"enable"
];
kind = "applications";
name = "ghostty";
relativePath = [
"applications"
"ghostty"
];
}
```
For `modules/profiles/interface/niri/`, path inference additionally gives
`kind = "profiles"`, `group = "interface"`, and `name = "niri"`. The path is
always authoritative for identity. `meta.nix` may provide display metadata such
as `description`, but it must not override or alias the unit ID.
## Unit Files and Fragment Contract
Only reserved files that a unit actually needs should exist. The Registry
registers present fragments and does not require empty or placeholder files. All
of the following are valid units:
```text
# Home Manager only
modules/applications/ghostty/
├── home.nix
├── home/
│ ├── nixos.nix
│ └── darwin.nix
└── settings.nix
# NixOS only
modules/services/docker/
└── nixos.nix
# nix-darwin only
modules/systems/macos-defaults/
└── darwin.nix
# NixOS and Home Manager, with metadata and helpers
modules/applications/niri/
├── nixos.nix
├── home.nix
├── meta.nix
├── settings.nix
└── keybindings.nix
# Metadata only, commonly a composition profile
modules/profiles/interface/niri/
└── meta.nix
# System configuration shared by NixOS and nix-darwin
modules/systems/nix/
└── common.nix
```
Each fragment is optional and registered independently. A unit may therefore
contain only `home/nixos.nix` or `home/darwin.nix`; it does not need a
placeholder `home.nix` or `home/common.nix`.
### Configuration fragments
Every reserved path except `meta.nix` is a configuration fragment to which the
Registry adds the enable condition. These fragments return the configuration
for their class directly and must not define top-level `imports`, `options`, or
`config` attributes:
```nix
# modules/services/docker/nixos.nix
{ primaryUser, ... }:
{
virtualisation.docker = {
enable = true;
autoPrune.enable = true;
};
users.users.${primaryUser}.extraGroups = [
"docker"
];
}
```
Conceptually, the Registry supplies a wrapper like this:
```nix
{ config, lib, ... }@args:
{
config =
lib.mkIf
config.my.services.docker.enable
(import dockerNixosPath args);
}
```
Do not add hand-written `mkEnableOption`, `cfg`, or `mkIf` boilerplate to each
unit. The Registry generates the enable option from the unit path and guards the
fragment.
### Helper files and directories
Every path other than the eight reserved paths is an ordinary helper, regardless
of its extension. The Registry neither discovers nor automatically imports
helper files such as `settings.nix`, `keybindings.nix`, `packages.nix`,
`colors.nix`, `rules.nix`, or `helpers.nix`. Import a helper explicitly from the
reserved fragment that uses it:
```nix
# modules/applications/niri/home.nix
{ lib, ... }:
let
settings = import ./settings.nix;
keybindings = import ./keybindings.nix;
in
{
programs.niri.settings = lib.recursiveUpdate settings {
binds = keybindings;
};
}
```
Do not use a leading underscore to mark a file private; `_settings.nix` has no
special meaning. Give helper files descriptive names instead. When helpers are
configuration functions, pass the module arguments explicitly and combine them
with normal Nix expressions:
```nix
# modules/applications/example/home.nix
{ lib, ... }@args:
lib.mkMerge [
(import ./packages.nix args)
(import ./settings.nix args)
]
```
The same discovery rule applies recursively to helper directories:
```text
modules/applications/niri/
├── home.nix
└── parts/
├── appearance.nix
└── keybindings.nix
```
Here `parts/` is not a unit because it directly contains no reserved file. A
helper directory that directly contains a root reserved file is itself
discovered as a unit, so never use reserved filenames inside a directory that
is intended to contain helpers only. The reserved `home/` fragment directory is
the sole exception to ordinary recursive child-unit discovery.
### Registry metadata
`meta.nix` is a Registry descriptor, not a NixOS, nix-darwin, or Home Manager
module. It may declare `description`, `includes`, and class-specific external
module imports:
```nix
# modules/applications/niri/meta.nix
{ inputs, ... }:
{
description = "Niri Wayland compositor";
includes = [
"systems.wayland"
"services.xdg-portal"
];
imports.nixos = [
inputs.niri-flake.nixosModules.niri
];
imports.home = [
inputs.niri-flake.homeModules.niri
];
}
```
External modules, including modules supplied by flake inputs, define Nix module
options and therefore belong in `meta.nix` under `imports.nixos`,
`imports.darwin`, or `imports.home`. Do not place them in a configuration
fragment's top-level `imports`: the Nix module system resolves imports before a
configuration-level enable condition.
`includes` lists units to enable whenever the declaring unit is enabled. Always
use fully qualified unit IDs:
```nix
# modules/profiles/interface/niri/meta.nix
{
includes = [
"profiles.interface.linux-desktop"
"applications.niri"
"applications.noctalia"
"services.ly"
"services.swayidle"
];
}
```
Never omit a prefix such as `applications.` merely because the including unit is
a profile. Fully qualified IDs make ownership explicit and allow moves, name
collisions, and missing dependencies to be detected. Do not enable another unit
by assigning to its enable option from a class fragment; declare the dependency
in `meta.includes`.
Application metadata should include only dependencies technically required for
the application to work. A profile owns the user's choice to adopt several
otherwise independent applications together. For example, the niri application
includes the Wayland foundation as a technical dependency. The
`profiles.interface.linux-desktop` profile selects Ghostty and Nautilus because
both labwc and niri use them, while the cross-platform `profiles.interface.gui`
profile selects Vicinae for graphical hosts. The labwc and niri profiles select
their compositor, Noctalia, and the session services they require. Ghostty and
Vicinae must not depend on either compositor, and compositor-specific
keybindings remain owned by the corresponding application unit.
## Profiles
Profiles compose units by purpose or form factor; they do not replace clear
application, system, service, or hardware ownership. The current profile
structure is:
```text
modules/profiles/
├── README.md
├── base/
├── interface/
│ ├── cli/
│ ├── gui/
│ ├── macos/
│ ├── linux-desktop/
│ ├── labwc/
│ └── niri/
├── networking/
│ ├── tailscale-client/
│ └── tailscale-subnet-router/
├── platform/
│ ├── nixos/
│ ├── intel-nvidia-desktop/
│ ├── laptop/
│ ├── thinkpad-x1/
│ ├── desktop/
│ └── vm/
├── workload/
│ ├── development/
│ ├── personal/
│ ├── server/
│ └── remote-access/
└── security/
├── fingerprint/
├── secrets/
├── secure-boot/
└── tpm-storage/
```
`modules/profiles/README.md` is the compatibility inventory for this structure.
Whenever a profile is added, removed, renamed, changes host-class support, or
changes meaning, update that README and every affected `hosts/default.nix`
selection in the same change. Remove stale profile directories and references;
do not retain compatibility aliases.
The profile layers have these responsibilities:
- `base` contains only invariants required by every supported host. It includes
`systems.nix` and the universal Atuin, tealdeer, trippy, and xh CLI tools;
optional secrets, interface, hardware, and workloads do not belong there.
- `interface` describes how the host is operated. `interface.cli` is shared by
NixOS and macOS and includes `tio`. `interface.gui` owns cross-platform
graphical interface applications such as Vicinae. `interface.macos` owns the
macOS Finder, Dock, trackpad, and shared default preferences and includes
`interface.gui`.
`interface.linux-desktop` owns the common labwc/niri desktop selection,
including Ghostty and Nautilus, and also includes `interface.gui`. Labwc and
niri remain independently selectable and do not imply CLI or personal
workloads.
- `platform` describes NixOS foundations and physical or virtual form factors.
macOS does not need an empty symmetric platform profile.
- `workload` describes optional host uses. `workload.development` and
`workload.personal` are cross-platform profiles, not `*-linux` variants.
- `networking` describes network roles and topology rather than user workloads.
- `security` describes optional security policies. Select
`security.fingerprint` instead of listing `systems.fingerprint` directly in a
host. The underlying `systems.fingerprint` unit owns NixOS fingerprint
authentication and macOS Touch ID sudo configuration through its class
fragments.
Do not split a semantic profile into `*-linux` and cross-platform variants merely
because an application is installed differently on each OS. Keep the semantic
profile cross-platform when its purpose is shared, and implement OS differences
inside the owning application unit. For example, Chrome, Vesktop, draw.io,
Slack, and Zoom use Linux Home Manager configuration in `home.nix` and macOS
Homebrew casks in `darwin.nix`. Guard a Linux-only Home Manager package with the
host platform when the same unit also has a Darwin implementation.
An explicitly OS-specific profile is appropriate when the composition itself is
OS-specific, such as `interface.macos`, `interface.linux-desktop`,
`platform.nixos`, or a NixOS
subnet-router. Do not create an OS suffix for a thin package difference that the
owning application unit can express.
The `desktop/` name above is a form-factor profile under `profiles/platform/`,
not a top-level module category.
A profile may consist only of `meta.includes`. Small settings that belong only
to the composition and have no useful independent identity may go directly in
the profile's `nixos.nix`, `darwin.nix`, or `home.nix`. Extract configuration to
an appropriate application, system, service, or hardware unit when any of these
conditions holds:
- It should be independently enableable.
- Multiple profiles reuse it.
- It owns separate configuration files.
- Its NixOS, nix-darwin, and Home Manager implementations differ.
- Other units depend on it.
- It involves a daemon, permissions, or user groups.
`security.tpm-storage` intentionally does not own a disk identifier. A host that
selects it must define `boot.initrd.luks.devices.cryptroot.device` in its own
NixOS module.
## Registry Responsibilities
Implement unit discovery with Nix standard functionality such as
`builtins.readDir`. Do not depend on an external indiscriminate auto-import
mechanism, and do not design the repository around `import-tree`. Registry logic
has these responsibilities:
1. Recursively visit directories below `modules/`.
2. Check the five reserved root filenames and the three reserved filenames
directly inside the unit's `home/` fragment directory.
3. Register a directory as a unit when at least one reserved fragment exists
there, including a unit that has only a reserved `home/` fragment.
4. Derive the unit ID from the path relative to `modules/`.
5. Record only class fragments that exist.
6. Evaluate `meta.nix` as a descriptor only when it exists.
7. Exclude non-reserved files from discovery and implicit imports.
8. Generate every unit's `my.<unit path>.enable` option.
9. Enable included units from `meta.includes`.
10. Raise a clear evaluation error for a reference to a missing unit ID.
11. Apply only the fragments appropriate to the current host class.
12. Pass `home.nix`, `home/common.nix`, and the matching OS-specific Home
Manager fragment only for hosts that enable Home Manager.
A unit record may conceptually look like this; the implementation need not use
this exact representation:
```nix
{
id = "applications.ghostty";
directory = ./applications/ghostty;
fragments = {
common = null;
nixos = null;
darwin = null;
home = ./applications/ghostty/home.nix;
homeCommon = null;
homeNixos = ./applications/ghostty/home/nixos.nix;
homeDarwin = ./applications/ghostty/home/darwin.nix;
};
meta = { };
}
```
Keep the custom Registry limited to unit discovery, enable-option generation,
`includes`, and class dispatch. Do not reimplement general Nix imports or Nix
module evaluation. In particular, never infer a unit ID from metadata or
implicitly load a non-reserved file.
## Hosts and Class Dispatch
`hosts/` is outside Registry discovery. A host contains machine-specific facts,
differences, and unit selection, not reusable shared configuration. Appropriate
host-owned data includes:
- Generated `hardware-configuration.nix`.
- Disk UUIDs and disko target devices.
- Monitor identifiers, layout, and scale.
- MAC addresses and static IP addresses.
- Kernel parameters required by one machine only.
- `system.stateVersion`.
- Host-specific secret references.
- The profiles enabled on that host and, only in exceptional cases, direct
application or other unit selections that cannot be expressed by a coherent
reusable profile.
A host registry may use a specification like this:
```nix
# hosts/default.nix
{
nix-example = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./nix-example;
profiles = [
"base"
"interface.cli"
"platform.vm"
"workload.development"
"workload.remote-access"
];
};
ops = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./ops;
profiles = [
"base"
"interface.cli"
"platform.vm"
"workload.remote-access"
];
};
internal-app-01 = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./internal-app-01;
profiles = [
"base"
"interface.cli"
"platform.vm"
"workload.server"
];
};
installer = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./installer;
homeManager = false;
profiles = [ "base" ];
};
x1g9 = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./x1g9;
profiles = [
"base"
"interface.cli"
"interface.labwc"
"interface.niri"
"platform.thinkpad-x1"
"security.fingerprint"
"workload.personal"
];
};
x1g13 = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./x1g13;
profiles = [
"base"
"interface.cli"
"interface.labwc"
"interface.niri"
"networking.tailscale-client"
"platform.thinkpad-x1"
"security.fingerprint"
"security.secrets"
"security.secure-boot"
"security.tpm-storage"
"workload.development"
"workload.personal"
];
};
galleria = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./galleria;
profiles = [
"base"
"interface.cli"
"interface.labwc"
"interface.niri"
"platform.intel-nvidia-desktop"
"security.secrets"
"security.secure-boot"
"security.tpm-storage"
"workload.development"
"workload.game"
"workload.personal"
];
};
m2 = {
system = "aarch64-darwin";
stateVersion = "26.05";
user = "moons";
path = ./m2;
profiles = [
"base"
"interface.cli"
"interface.macos"
"security.fingerprint"
"workload.development"
"workload.personal"
];
};
}
```
The current role assignment is intentional: nix-example is the development VM;
ops is the remote-access VM with host-specific static networking;
internal-app-01 is the container server VM; and installer builds the minimal
installation ISO without Home Manager. x1g9 is a full NixOS desktop with niri,
labwc, ly, the shared Linux desktop applications, and the personal workload.
x1g13 is the secure NixOS development and personal ThinkPad, with the same
desktop sessions plus Tailscale client, SOPS, Secure Boot, and TPM-backed disk
unlock. galleria is the Intel/NVIDIA physical desktop shared with Windows; it
uses dedicated NixOS partitions, LUKS, Secure Boot, and TPM-backed disk unlock.
m2 is the daily-use macOS development and personal machine with the macOS
interface defaults. Keep the desktop sessions independently selectable, and
keep the development and personal profiles usable across NixOS and Darwin.
Treat entries in `profiles` and the exceptional `applications` field as IDs
relative to their respective category roots. Add the category prefixes during
host construction:
```nix
selectedUnits =
[ "users.${spec.user}" ]
++ map (name: "profiles.${name}") spec.profiles
++ map (name: "applications.${name}") spec.applications
++ spec.units or [ ];
```
`applications` and `units` are escape hatches, not normal host composition.
Do not add either field when an existing profile expresses the concern, when an
existing profile can coherently include the unit, or when the concern is
reusable enough to deserve a small profile. For example, add a development tool
to `workload.development` and select `security.fingerprint`; do not write
`applications = [ "ghostty" ];` or `units = [ "systems.fingerprint" ];` in a
host. A direct selection is permitted only for a genuinely exceptional,
machine-specific unit that would make every reasonable profile misleading; add
an adjacent comment explaining that exception. Prefer profiles for host
composition and omit both escape-hatch fields by default.
Host modules use normal Nix module semantics and are not Registry-guarded
configuration fragments. For example:
```text
hosts/
├── installer/
│ └── nixos.nix
├── internal-app-01/
│ ├── nixos.nix
│ └── hardware-configuration.nix
├── nix-example/
│ ├── nixos.nix
│ └── hardware-configuration.nix
├── ops/
│ ├── nixos.nix
│ └── hardware-configuration.nix
├── galleria/
│ ├── disk-identifiers.nix
│ ├── disko.nix
│ ├── hardware-configuration.nix
│ └── nixos.nix
├── x1g9/
│ ├── nixos.nix
│ └── hardware-configuration.nix
├── x1g13/
│ ├── nixos.nix
│ ├── home.nix
│ ├── disko.nix
│ └── hardware-configuration.nix
└── m2/
└── darwin.nix
```
`hosts/x1g9/nixos.nix` explicitly loads `hardware-configuration.nix` with the
normal top-level Nix module `imports`. `hosts/x1g13/nixos.nix` loads its
generated hardware configuration and host-local `disko.nix` the same way. Do
not confuse these host imports with the prohibition on top-level `imports` in
unit configuration fragments. `hosts/galleria/disko.nix` manages only the two
dedicated NixOS partitions by PARTUUID and deliberately excludes the Windows
disk, Windows partitions, and the Windows EFI System Partition.
Derive the system class from the host's `system`:
- A Linux NixOS host receives `common.nix` and `nixos.nix`.
- A nix-darwin host receives `common.nix` and `darwin.nix`.
- A NixOS host with integrated Home Manager additionally receives `home.nix`,
`home/common.nix`, and `home/nixos.nix`.
- A nix-darwin host with integrated Home Manager additionally receives
`home.nix`, `home/common.nix`, and `home/darwin.nix`.
Home Manager is additive, not a system class mutually exclusive with NixOS or
nix-darwin. Normal machine configurations combine NixOS or nix-darwin with Home
Manager; the installer ISO explicitly sets `homeManager = false`. If standalone
Home Manager is supported later, add an explicit host kind because `system`
alone cannot distinguish it from NixOS.
Do not duplicate reusable settings in hosts, but do not force genuinely
machine-specific values into a common unit merely to remove a host-local line.
## Coding Style and Implementation Rules
Use two-space indentation in Nix files and let `nixfmt` decide layout. Prefer
small units, explicit imports, and descriptive kebab-case names, for example
`modules/services/media-server/nixos.nix`. Use camelCase for Nix attributes
unless an upstream option dictates otherwise. Shell snippets must pass `shfmt`
and `shellcheck`; YAML, TOML, and Markdown are formatted by the configured
treefmt tools.
When implementing or modifying modules:
- Do not create `features/` or a top-level `desktop/` module category.
- Do not add per-unit `mkEnableOption`, `cfg`, or `mkIf` boilerplate; the Registry
derives and guards enable options from paths.
- Put unit dependencies in `meta.includes`, not in direct assignments to another
unit's enable option from a class fragment.
- Do not assume any non-reserved file is discovered or loaded automatically.
- Do not require an `_` prefix for helper or private files.
- Do not create unused reserved fragments, including placeholder files under the
reserved `home/` fragment directory.
- Do not override a path-derived unit ID from `meta.nix`.
- Keep technical application dependencies separate from the applications a
personal environment chooses to combine in a profile.
- Keep cross-platform profile names semantic. Put Linux package installation in
an application's `home.nix` and the corresponding macOS Homebrew cask in its
`darwin.nix`; do not create a thin `*-linux` profile for that difference.
- Keep shared labwc/niri selections in `profiles.interface.linux-desktop` and
session-specific applications or services in the respective labwc or niri
profile.
- Keep `modules/profiles/README.md`, the profile directories, and host profile
selections synchronized whenever any of them changes.
- Do not select applications or units directly in `hosts/default.nix` unless
they meet the documented exceptional, machine-specific escape-hatch rule.
Prefer adding the unit to an existing coherent profile or creating a small,
justified reusable profile.
- Do not rely on module-list ordering to override values. Use Nix module
priorities such as `lib.mkDefault`, `lib.mkForce`, `lib.mkBefore`, or
`lib.mkAfter` explicitly when required.
- Keep Registry responsibilities narrow; use normal Nix imports and module
evaluation for everything outside discovery, generated enables, includes, and
class dispatch.
## Build, Test, and Development Commands
- `nix develop .#dotnix` enters the main development shell and installs the repository's pre-commit hooks.
- `nix develop .#android` provides Android platform tools such as `adb` and `fastboot`.
- `nix develop .#dotnix` enters the main development shell and installs the
repository's pre-commit hooks.
- `nix develop .#android` provides Android platform tools such as `adb` and
`fastboot`.
- `nix fmt` formats all supported files through treefmt.
- `nix flake check` evaluates flake outputs and runs configured checks.
- `pre-commit run --all-files` runs formatting, dead-code and static Nix checks, shell linting, and secret scanning.
- `nix flake update` refreshes pinned inputs in `flake.lock`; review lockfile changes before committing.
- `pre-commit run --all-files` runs formatting, dead-code and static Nix checks,
shell linting, and secret scanning.
- `nix flake update` refreshes pinned inputs in `flake.lock`; review lockfile
changes before committing.
If direnv is installed, `direnv allow` activates the `dotnix` shell from `.envrc` automatically.
## Coding Style & Naming Conventions
Use two-space indentation in Nix files and let `nixfmt` decide layout. Prefer small modules with explicit imports and descriptive kebab-case filenames, for example `modules/services/media-server.nix`. Use camelCase for Nix attributes unless an upstream option dictates otherwise. Shell snippets must pass `shfmt` and `shellcheck`; YAML, TOML, and Markdown are formatted by the configured treefmt tools.
If direnv is installed, `direnv allow` activates the `dotnix` shell from `.envrc`
automatically.
## Testing Guidelines
There is no separate unit-test suite. Before submitting changes, run `nix flake check` and `pre-commit run --all-files`. For system-specific changes, also build or evaluate the affected NixOS/Home Manager configuration without switching the live machine. Never commit generated secrets, `.age` plaintext, or local `.direnv/` state.
There is no separate unit-test suite. Before submitting changes, run
`nix flake check` and `pre-commit run --all-files`. For system-specific changes,
also build or evaluate the affected NixOS, nix-darwin, or Home Manager
configuration without switching the live machine. Never commit generated
secrets, `.age` plaintext, or local `.direnv/` state.
## Commit & Pull Request Guidelines
For Registry changes, test discovery of each supported fragment combination,
dependency closure through `meta.includes`, missing-unit errors, and class
dispatch. Verify that helper files are ignored until explicitly imported and
that directories without a directly contained reserved file remain namespaces.
Recent history favors short, lowercase, imperative subjects such as `fix` and `update action`; automated dependency commits use `chore(deps): ...`. Prefer a specific summary that states the affected area, such as `shells: add deployment tools`. Keep commits focused. Pull requests should explain the motivation, list affected hosts or profiles, report validation commands, and note any manual migration or secret-management steps. Include screenshots only for visible desktop or application configuration changes.
For profile changes, additionally:
- Check for stale profile IDs after every add, removal, or rename.
- Evaluate every affected real host without switching it.
- Evaluate a cross-platform profile on both NixOS and nix-darwin, even when only
one current host selects it.
- Confirm `modules/profiles/README.md` accurately states compatibility and any
required host-owned values.
- When adding a Darwin application fragment, verify the resulting
`homebrew.casks` selection as well as module evaluation.
- Preserve the intended host roles: nix-example remains the development VM;
ops remains the statically networked remote-access VM; internal-app-01 remains
the container server VM; installer remains the Home Manager-free installation
ISO; x1g9 provides niri, labwc, ly, and the personal application set; x1g13
additionally provides the development, Tailscale client, secrets, Secure Boot,
and TPM storage roles; galleria remains the Intel/NVIDIA dual-boot desktop
with LUKS, Secure Boot, and TPM storage; m2 remains the daily-use development
and personal machine.
## Commit and Pull Request Guidelines
Recent history favors short, lowercase, imperative subjects such as `fix` and
`update action`; automated dependency commits use `chore(deps): ...`. Prefer a
specific summary that states the affected area, such as
`shells: add deployment tools`. Keep commits focused. Pull requests should
explain the motivation, list affected hosts or profiles, report validation
commands, and note any manual migration or secret-management steps. Include
screenshots only for visible desktop or application configuration changes.
+68
View File
@@ -0,0 +1,68 @@
# Fingerprint Commands
This note covers the basic `fprintd` commands used by the fingerprint module.
## Enroll a new fingerprint
Register a fingerprint for the current user:
```sh
fprintd-enroll $USER
```
To enroll a specific finger, pass the finger name:
```sh
fprintd-enroll -f right-index-finger $USER
```
Common finger names include:
- `left-thumb`
- `left-index-finger`
- `right-thumb`
- `right-index-finger`
Follow the prompts and swipe or touch the sensor until enrollment completes.
## List enrolled fingerprints
Show fingerprints registered for the current user:
```sh
fprintd-list $USER
```
You can also list fingerprints for another user:
```sh
fprintd-list <username>
```
## Delete fingerprints
Delete one enrolled fingerprint for the current user:
```sh
fprintd-delete
```
Delete all enrolled fingerprints for the current user:
```sh
fprintd-delete $USER
```
Delete fingerprints for another user:
```sh
fprintd-delete <username>
```
## Verify authentication
Test fingerprint authentication for the current user:
```sh
fprintd-verify
```
+157
View File
@@ -0,0 +1,157 @@
# NixOSインストール手順
## 事前準備
1. [ISOビルド](iso-build.md)を参照してISOを作成
2. USBに書き込んで対象マシンでブート
## ネットワーク接続
### 有線LAN
DHCPで自動設定される。
### WiFi(有線が使えない場合)
```bash
nmcli device wifi connect <SSID> --ask
```
## SSH接続
コンソールに表示されたIPアドレスに接続:
```bash
ssh root@<ip-address>
```
## インストール手順
### 1. dotfilesのクローン
```bash
git clone [email protected]:moons-14/dotfiles.git ~/dotfiles
```
### 2. SSHホストキーの生成
新しいホスト用のSSHホストキーを生成:
```bash
ssh-keygen -t ed25519 -f /tmp/ssh_host_ed25519_key -N ""
```
### 3. age公開鍵の取得
SSHホストキーからage公開鍵を取得:
```bash
ssh-to-age -i /tmp/ssh_host_ed25519_key.pub
```
出力されたage公開鍵をコピー。
### 4. .sops.yamlの編集
```bash
cd ~/dotfiles
vim .sops.yaml
```
以下を追加:
```yaml
keys:
- &host_<hostname> <age公開鍵>
creation_rules:
- path_regex: ^secrets/hosts/<hostname>/[^/]+\.ya?ml$
key_groups:
- age:
- *admin_yubikey1
- *host_<hostname>
```
### 5. シークレットの再暗号化
```bash
sops updatekeys secrets/common/system.yaml
sops updatekeys secrets/hosts/<hostname>/*.yaml
```
### 6. disko設定の作成
新しいホスト用の`hosts/<hostname>/disko.nix`を作成。
#### シンプル構成(暗号化なし)
```nix
_:
{
disko.enableConfig = true;
disko.devices.disk.main = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
}
```
#### LUKS暗号化 + btrfs
`hosts/x1g13/disko.nix`を参照。
### 7. ディスクのパーティション
```bash
cd ~/dotfiles
nix run github:nix-community/disko -- --mode disko hosts/<hostname>/disko.nix
```
### 8. ホストキーのコピー
```bash
mkdir -p /mnt/etc/ssh
cp /tmp/ssh_host_ed25519_key* /mnt/etc/ssh/
chmod 600 /mnt/etc/ssh/ssh_host_ed25519_key
```
### 9. NixOSインストール
```bash
nixos-install --flake ~/dotfiles#<hostname>
```
### 10. 再起動
```bash
reboot
```
## インストール後の確認
- SSHでログインできるか
- sopsシークレットが復号できるか
- diskoでパーティションが正しく設定されているか
+26
View File
@@ -0,0 +1,26 @@
# カスタムISOビルド
## ビルド
```bash
nix build .#nixosConfigurations.installer.config.system.build.isoImage
```
## ISO書き込み
```bash
# USBデバイスの確認
lsblk
# 書き込み(/dev/sdXは実際のデバイスに置き換える)
sudo dd if=./result/nixos-minimal-*.iso of=/dev/sdX bs=4M status=progress
sync
```
## ISOの特徴
- SSH鍵認証でrootログイン可能
- 有線LANはDHCPで自動設定
- WiFiは`nmcli`で手動設定可能
- disko/sops/ageなどのツールを内蔵
- ブート時にIPアドレスとヘルプを表示
+17
View File
@@ -0,0 +1,17 @@
# Generate Sops key file
```bash
mkdir -p ~/.config/sops/age
chmod 700 ~/.config/sops/age
age-plugin-yubikey --identity --slot 1 \
> ~/.config/sops/age/yubikey-identity.txt
chmod 600 ~/.config/sops/age/yubikey-identity.txt
```
## Edit sops file
```bash
sops secrets/common/system.yaml
```
Generated
+451 -146
View File
File diff suppressed because it is too large Load Diff
+27 -6
View File
@@ -11,6 +11,11 @@
inputs.nixpkgs.follows = "nixpkgs";
};
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-26.05";
inputs.nixpkgs.follows = "nixpkgs";
};
# Hardware / Platform
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
nixos-wsl.url = "github:nix-community/NixOS-WSL";
@@ -18,6 +23,11 @@
# Desktop
niri-flake.url = "github:sodiboo/niri-flake";
nix-hazkey = {
url = "github:aster-void/nix-hazkey";
inputs.nixpkgs.follows = "nixpkgs";
};
stylix = {
url = "github:nix-community/stylix";
inputs.nixpkgs.follows = "nixpkgs";
@@ -25,7 +35,13 @@
# Terminal
ghostty = {
url = "github:moons-14/ghostty";
url = "github:ghostty-org/ghostty";
};
# Speech to text
handy = {
url = "github:cjpais/Handy";
inputs.nixpkgs.follows = "nixpkgs";
};
# Shell / Launcher
@@ -36,10 +52,7 @@
inputs.nixpkgs.follows = "nixpkgs";
};
noctalia = {
url = "github:noctalia-dev/noctalia";
inputs.nixpkgs.follows = "nixpkgs";
};
noctalia.url = "github:noctalia-dev/noctalia/cachix";
# Editor
nixvim = {
@@ -91,7 +104,15 @@
};
# Systems
systems.url = "github:nix-systems/default-linux";
systems.url = "github:nix-systems/default";
browser-previews = {
url = "github:nix-community/browser-previews";
inputs.nixpkgs.follows = "nixpkgs";
};
nani-translate-linux.url = "git+https://github.com/zunoser/nani-translate-linux.git";
};
outputs =
+1
View File
@@ -2,5 +2,6 @@
imports = [
./formatter.nix
./git-hooks.nix
./registry.nix
];
}
+54
View File
@@ -0,0 +1,54 @@
{
inputs,
lib,
...
}:
let
dotfilesLib = import ../libs {
inherit inputs lib;
root = ../.;
};
hostSpecsPath = ../hosts/default.nix;
hostSpecs =
if builtins.pathExists hostSpecsPath then
let
value = import hostSpecsPath;
in
if builtins.isFunction value then
value (
builtins.intersectAttrs (builtins.functionArgs value) {
inherit inputs lib;
}
)
else
value
else
{ };
configurations = dotfilesLib.hosts.mkConfigurations hostSpecs;
in
{
flake = {
inherit (configurations) darwinConfigurations nixosConfigurations;
lib = dotfilesLib;
};
perSystem =
{ pkgs, system, ... }:
let
nixosChecks =
lib.mapAttrs' (name: nixos: lib.nameValuePair "nixos-${name}" nixos.config.system.build.toplevel)
(
lib.filterAttrs (
_: nixos: nixos.pkgs.stdenv.hostPlatform.system == system
) configurations.nixosConfigurations
);
in
{
checks = {
registry = import ../tests/registry.nix {
inherit inputs lib pkgs;
};
}
// nixosChecks;
};
}
+135
View File
@@ -0,0 +1,135 @@
{
nix-example = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./nix-example;
profiles = [
"base"
"interface.cli"
"platform.vm"
"workload.development"
"workload.remote-access"
];
};
ops = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./ops;
profiles = [
"base"
"interface.cli"
"platform.vm"
"workload.remote-access"
];
};
internal-app-01 = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./internal-app-01;
profiles = [
"base"
"interface.cli"
"platform.vm"
"workload.server"
];
};
installer = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./installer;
homeManager = false;
profiles = [ "base" ];
};
x1g9 = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./x1g9;
profiles = [
"base"
"interface.cli"
"interface.labwc"
"interface.niri"
"platform.thinkpad-x1"
"security.fingerprint"
# "security.secrets"
"workload.personal"
];
};
x1g13 = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./x1g13;
profiles = [
"base"
"interface.cli"
"interface.labwc"
"interface.niri"
"networking.tailscale-client"
"platform.thinkpad-x1"
"security.fingerprint"
"security.secrets"
"security.secure-boot"
"security.tpm-storage"
"workload.development"
"workload.game"
"workload.personal"
];
};
galleria = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./galleria;
profiles = [
"base"
"interface.cli"
"interface.labwc"
"interface.niri"
"platform.intel-nvidia-desktop"
"security.secrets"
"security.secure-boot"
"security.tpm-storage"
"security.fingerprint"
"workload.development"
"workload.game"
"workload.personal"
];
};
m2 = {
system = "aarch64-darwin";
stateVersion = "26.05";
user = "moons";
path = ./m2;
profiles = [
"base"
"interface.cli"
"interface.macos"
"security.fingerprint"
# "security.secrets"
"workload.development"
"workload.game"
"workload.personal"
];
};
}
+91
View File
@@ -0,0 +1,91 @@
_:
let
espPart = "/dev/disk/by-partuuid/008b04ef-9c06-4049-bffb-3906f5c3a9c1";
nixosPart = "/dev/disk/by-partuuid/04295552-cbb8-4511-ac1e-1171ec20f8d1";
btrfsMountOptions = [
"compress=zstd"
"noatime"
"ssd"
"space_cache=v2"
];
in
{
disko.enableConfig = true;
# These are deliberately partition paths, not the whole Windows disk. Disko
# must never own or destroy the disk's GPT or any Windows partition.
disko.devices.disk = {
esp = {
type = "disk";
device = espPart;
destroy = false;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
nixos = {
type = "disk";
device = nixosPart;
destroy = false;
content = {
type = "luks";
name = "cryptroot";
askPassword = true;
settings.allowDiscards = true;
extraFormatArgs = [
"--type"
"luks2"
"--pbkdf"
"argon2id"
"--label"
"NixOS-LUKS"
];
content = {
type = "btrfs";
extraArgs = [
"-f"
"-L"
"NixOS"
];
subvolumes = {
"@root" = {
mountpoint = "/";
mountOptions = btrfsMountOptions;
};
"@home" = {
mountpoint = "/home";
mountOptions = btrfsMountOptions;
};
"@nix" = {
mountpoint = "/nix";
mountOptions = btrfsMountOptions;
};
"@log" = {
mountpoint = "/var/log";
mountOptions = btrfsMountOptions;
};
"@swap" = {
mountpoint = "/.swapvol";
mountOptions = [ "noatime" ];
swap.swapfile.size = "32G";
};
};
};
};
};
};
}
+30
View File
@@ -0,0 +1,30 @@
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
"usbhid"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
+46
View File
@@ -0,0 +1,46 @@
{
lib,
config,
...
}:
{
services.kanshi = {
enable = true;
settings = [
{
profile = {
name = "galleria";
outputs = [
{
criteria = "HDMI-A-1";
status = "enable";
position = "0,0";
scale = 1.5;
}
{
criteria = "DP-1";
status = "enable";
position = "2560,0";
}
{
criteria = "DP-2";
status = "enable";
position = "5120,0";
scale = 1.5;
}
];
};
}
];
};
home.file.".wallpapers" = {
source = lib.mkForce (
config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/Pictures/wallpapers"
);
recursive = lib.mkForce false;
};
}
+14
View File
@@ -0,0 +1,14 @@
{ inputs, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
./disko.nix
];
boot.initrd.luks.devices.cryptroot.device =
"/dev/disk/by-partuuid/04295552-cbb8-4511-ac1e-1171ec20f8d1";
environment.systemPackages = with inputs.browser-previews.packages.${pkgs.system}; [
google-chrome-beta
];
}
+194
View File
@@ -0,0 +1,194 @@
{
pkgs,
lib,
modulesPath,
...
}:
{
imports = [ "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" ];
boot.zfs.forceImportRoot = false;
networking = {
hostName = "nixos-installer";
networkmanager = {
enable = true;
wifi.powersave = false;
};
};
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PubkeyAuthentication = "yes";
};
};
users.users.root.openssh.authorizedKeys.keys = [
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIKhxDkucmeCor6CKoXAua7DgDSzuXrZOtpdkyzQxz5+aAAAABHNzaDo= moons@moons14.com"
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIN6hZJyng/5LgFKPjR6uZAd/00UkO0vN0uQOoIvfSELdAAAABHNzaDo= moons@moons14.com"
];
environment.systemPackages = with pkgs; [
git
disko
sops
age
ssh-to-age
age-plugin-yubikey
yubikey-manager
pcsc-tools
mkpasswd
rsync
vim
wget
curl
jq
parted
cryptsetup
btrfs-progs
efibootmgr
pciutils
sbctl
tpm2-tools
util-linux
];
services.pcscd.enable = true;
environment.etc."installer-help.txt".text = ''
╔══════════════════════════════════════════════════════════════╗
║ NixOS Installer ISO ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ SSH Access: ║
║ ssh root@<ip-address> ║
║ ║
║ Network Setup: ║
║ Wired: Auto-configured via DHCP ║
║ WiFi: nmcli device wifi connect <SSID> --ask ║
║ ║
║ Installation Workflow: ║
║ ║
║ 1. Clone dotfiles: ║
║ git clone git@github.com:moons-14/dotfiles.git ~/dotfiles║
║ ║
║ 2. Generate SSH host key for new host: ║
║ ssh-keygen -t ed25519 -f /tmp/ssh_host_ed25519_key -N "" ║
║ ║
║ 3. Get age public key from SSH host key: ║
║ ssh-to-age -i /tmp/ssh_host_ed25519_key.pub ║
║ ║
║ 4. Add age key to .sops.yaml: ║
║ cd ~/dotfiles ║
║ # Edit .sops.yaml and add the age key ║
║ # Add new host entry to creation_rules ║
║ ║
║ 5. Re-encrypt secrets: ║
║ sops updatekeys secrets/common/system.yaml ║
║ sops updatekeys secrets/hosts/<host>/*.yaml ║
║ ║
║ 6. Create disko.nix for new host: ║
║ # Check disk devices ║
║ lsblk -f ║
║ ║
║ # Create hosts/<host>/disko.nix ║
║ # Example: LUKS + btrfs ║
║ # See hosts/x1g13/disko.nix for reference ║
║ ║
║ 7. Partition disk with disko: ║
║ nix run github:nix-community/disko -- \ ║
║ --mode disko hosts/<host>/disko.nix ║
║ ║
║ 8. Copy host key to installed system: ║
║ mkdir -p /mnt/etc/ssh ║
║ cp /tmp/ssh_host_ed25519_key* /mnt/etc/ssh/ ║
║ chmod 600 /mnt/etc/ssh/ssh_host_ed25519_key ║
║ ║
║ 9. Install NixOS: ║
║ nixos-install --flake ~/dotfiles#<host> ║
║ ║
║ Disko Configuration Examples: ║
║ ║
║ Simple (no encryption): ║
║ disko.devices.disk.main = { ║
║ type = "disk"; ║
║ device = "/dev/sda"; ║
║ content = { ║
║ type = "gpt"; ║
║ partitions = { ║
║ ESP = { size = "512M"; type = "EF00"; ║
║ content = { type = "filesystem"; ║
║ format = "vfat"; mountpoint = "/boot"; }; }; ║
║ root = { size = "100%"; ║
║ content = { type = "filesystem"; ║
║ format = "ext4"; mountpoint = "/"; }; }; ║
║ }; ║
║ }; ║
║ }; ║
║ ║
║ LUKS + btrfs (see hosts/x1g13/disko.nix): ║
║ - Use partuuid for device path ║
║ - Set askPassword = true for LUKS ║
║ - Configure btrfs subvolumes ║
║ ║
╚══════════════════════════════════════════════════════════════╝
'';
systemd.services.installer-banner = {
description = "Display installer help on console";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.coreutils}/bin/cat /etc/installer-help.txt";
StandardOutput = "tty";
TTYPath = "/dev/tty1";
};
};
systemd.services.display-ip = {
description = "Display IP address on console";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = pkgs.writeShellScript "display-ip" ''
sleep 2
echo ""
echo "=== Network Interfaces ==="
${pkgs.iproute2}/bin/ip -4 addr show | ${pkgs.gnugrep}/bin/grep inet
echo ""
echo "=== SSH Access ==="
for ip in $(${pkgs.iproute2}/bin/ip -4 addr show | ${pkgs.gnugrep}/bin/grep -oP 'inet \K[\d.]+' | ${pkgs.gnugrep}/bin/grep -v '127.0.0.1'); do
echo " ssh root@$ip"
done
echo ""
'';
StandardOutput = "tty";
TTYPath = "/dev/tty1";
};
};
nix = {
settings = {
experimental-features = [
"nix-command"
"flakes"
];
trusted-users = [ "root" ];
};
extraOptions = ''
experimental-features = nix-command flakes
'';
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
@@ -0,0 +1,36 @@
# Do not modify this file! It was generated by `nixos-generate-config` and may
# be overwritten by future invocations.
{ lib, modulesPath, ... }:
{
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules = [
"ata_piix"
"uhci_hcd"
"virtio_pci"
"virtio_scsi"
"sd_mod"
"sr_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/1b12ab98-2537-4207-a3f4-bb8ba7b53b00";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/8365-C778";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
+3
View File
@@ -0,0 +1,3 @@
{
imports = [ ./hardware-configuration.nix ];
}
+10
View File
@@ -0,0 +1,10 @@
{ hostName, ... }:
{
# networking.hostName and networking.localHostName are derived from the
# registry name; computerName controls the user-visible macOS name.
networking.computerName = hostName;
# Keep this value stable after the first activation. It is independent of
# the Home Manager stateVersion in hosts/default.nix.
system.stateVersion = 7;
}
@@ -0,0 +1,36 @@
# Do not modify this file! It was generated by `nixos-generate-config` and may
# be overwritten by future invocations.
{ lib, modulesPath, ... }:
{
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules = [
"ata_piix"
"uhci_hcd"
"virtio_pci"
"virtio_scsi"
"sd_mod"
"sr_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/8f0eaec6-5dc9-4821-aa8d-fb6809b5a5bf";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/201C-961B";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
+3
View File
@@ -0,0 +1,3 @@
{
imports = [ ./hardware-configuration.nix ];
}
+36
View File
@@ -0,0 +1,36 @@
# Do not modify this file! It was generated by `nixos-generate-config` and may
# be overwritten by future invocations.
{ lib, modulesPath, ... }:
{
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules = [
"ata_piix"
"uhci_hcd"
"virtio_pci"
"virtio_scsi"
"sd_mod"
"sr_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/69fa2193-1e4f-438a-8898-5de8a3f36e5b";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/D09B-4277";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
+51
View File
@@ -0,0 +1,51 @@
{
imports = [ ./hardware-configuration.nix ];
networking = {
useDHCP = false;
interfaces = {
ens18 = {
useDHCP = false;
ipv4.addresses = [
{
address = "10.50.128.20";
prefixLength = 24;
}
];
};
ens19 = {
useDHCP = false;
ipv4.addresses = [
{
address = "10.50.7.101";
prefixLength = 24;
}
];
};
ens20 = {
useDHCP = false;
ipv4.routes = [
{
address = "10.50.64.0";
prefixLength = 24;
via = "10.50.82.1";
}
];
ipv4.addresses = [
{
address = "10.50.82.10";
prefixLength = 24;
}
];
};
};
defaultGateway = {
address = "10.50.128.1";
interface = "ens18";
};
};
}
+89
View File
@@ -0,0 +1,89 @@
_:
let
espPart = "/dev/disk/by-partuuid/a53e3b19-67de-40de-9ded-3eac3117689a";
nixosPart = "/dev/disk/by-partuuid/311d0f9c-f35f-42e6-b6fc-a4d67dd21b2e";
btrfsMountOptions = [
"compress=zstd"
"noatime"
"ssd"
"space_cache=v2"
];
in
{
disko.enableConfig = true;
disko.devices.disk = {
esp = {
type = "disk";
device = espPart;
destroy = false;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
nixos = {
type = "disk";
device = nixosPart;
destroy = false;
content = {
type = "luks";
name = "cryptroot";
askPassword = true;
settings.allowDiscards = true;
extraFormatArgs = [
"--type"
"luks2"
"--pbkdf"
"argon2id"
"--label"
"NixOS-LUKS"
];
content = {
type = "btrfs";
extraArgs = [
"-f"
"-L"
"NixOS"
];
subvolumes = {
"@root" = {
mountpoint = "/";
mountOptions = btrfsMountOptions;
};
"@home" = {
mountpoint = "/home";
mountOptions = btrfsMountOptions;
};
"@nix" = {
mountpoint = "/nix";
mountOptions = btrfsMountOptions;
};
"@log" = {
mountpoint = "/var/log";
mountOptions = btrfsMountOptions;
};
"@swap" = {
mountpoint = "/.swapvol";
mountOptions = [ "noatime" ];
swap.swapfile.size = "32G";
};
};
};
};
};
};
}
+32
View File
@@ -0,0 +1,32 @@
# Do not modify this file! It was generated by `nixos-generate-config`
# and may be overwritten by future invocations. Make changes in nixos.nix.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"thunderbolt"
"nvme"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
swapDevices = [ ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
+22
View File
@@ -0,0 +1,22 @@
{
services.kanshi = {
enable = true;
settings = [
{
profile = {
name = "x1g13";
outputs = [
{
criteria = "eDP-1";
status = "enable";
position = "0,0";
scale = 1.5;
}
];
};
}
];
};
}
+9
View File
@@ -0,0 +1,9 @@
{
imports = [
./hardware-configuration.nix
./disko.nix
];
boot.initrd.luks.devices.cryptroot.device =
"/dev/disk/by-partuuid/311d0f9c-f35f-42e6-b6fc-a4d67dd21b2e";
}
+52
View File
@@ -0,0 +1,52 @@
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"thunderbolt"
"nvme"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/16b29578-6836-414b-a5e1-863bc21c5fc3";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/209A-C8C9";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.wlp0s20f3.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
+5
View File
@@ -0,0 +1,5 @@
{
imports = [
./hardware-configuration.nix
];
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+17
View File
@@ -0,0 +1,17 @@
{
inputs,
lib ? inputs.nixpkgs.lib,
root,
}:
let
registry = import ./registry.nix {
inherit inputs lib;
modulesRoot = root + "/modules";
};
hosts = import ./hosts.nix {
inherit inputs lib registry;
};
in
{
inherit hosts registry;
}
+191
View File
@@ -0,0 +1,191 @@
{
inputs,
lib,
registry,
}:
let
ensure =
condition: message: value:
if condition then value else throw "host registry: ${message}";
isLinux = system: lib.hasSuffix "-linux" system;
isDarwin = system: lib.hasSuffix "-darwin" system;
hostFile =
spec: name:
let
path = spec.path + "/${name}";
in
if builtins.pathExists path then path else null;
selectedUnits =
spec:
[ "users.${spec.user}" ]
++ map (name: "profiles.${name}") (spec.profiles or [ ])
++ map (name: "applications.${name}") (spec.applications or [ ])
++ (spec.units or [ ]);
validateSpec =
name: spec:
ensure (builtins.isAttrs spec) "${name}: host specification must be an attribute set" (
ensure (spec ? system && builtins.isString spec.system) "${name}: system is required" (
ensure (isLinux spec.system || isDarwin spec.system)
"${name}: unsupported system '${spec.system}'; expected a Linux NixOS or Darwin system"
(
ensure (spec ? user && builtins.isString spec.user && spec.user != "") "${name}: user is required" (
ensure (spec ? path && builtins.pathExists spec.path)
"${name}: path must name an existing host directory"
(
ensure
(
spec ? stateVersion
&& builtins.isString spec.stateVersion
&& builtins.match "[0-9][0-9]\\.[0-9][0-9]" spec.stateVersion != null
)
"${name}: stateVersion is required and must have the form YY.MM"
(
ensure
(lib.all
(field: builtins.isList (spec.${field} or [ ]) && lib.all builtins.isString (spec.${field} or [ ]))
[
"profiles"
"applications"
"units"
]
)
"${name}: profiles, applications, and units must be lists of strings"
(ensure (builtins.isBool (spec.homeManager or true)) "${name}: homeManager must be a boolean" spec)
)
)
)
)
)
);
mkSpecialArgs = name: spec: {
inherit inputs registry;
inherit (spec) system;
hostName = name;
primaryUser = spec.user;
};
mkHomeManagerModule =
name: spec: selected:
let
homePath = hostFile spec "home.nix";
homeModules = [
(registry.mkModule {
class = "home";
systemClass = "nixos";
})
(registry.mkSelectionModule selected)
{ home.stateVersion = spec.stateVersion; }
]
++ lib.optional (homePath != null) homePath;
in
{
imports = [ inputs.home-manager.nixosModules.home-manager ];
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = mkSpecialArgs name spec;
users.${spec.user}.imports = homeModules;
};
};
mkDarwinHomeManagerModule =
name: spec: selected:
let
homePath = hostFile spec "home.nix";
homeModules = [
(registry.mkModule {
class = "home";
systemClass = "darwin";
})
(registry.mkSelectionModule selected)
{ home.stateVersion = spec.stateVersion; }
]
++ lib.optional (homePath != null) homePath;
in
{
imports = [ inputs.home-manager.darwinModules.home-manager ];
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = mkSpecialArgs name spec;
users.${spec.user}.imports = homeModules;
};
};
mkNixos =
name: rawSpec:
let
spec = validateSpec name rawSpec;
selected = registry.validateUnitIds (selectedUnits spec);
nixosPath = hostFile spec "nixos.nix";
modules = [
(registry.mkModule { class = "nixos"; })
(registry.mkSelectionModule selected)
{
networking.hostName = lib.mkDefault name;
system.stateVersion = spec.stateVersion;
}
]
++ lib.optional (spec.homeManager or true) (mkHomeManagerModule name spec selected)
++ lib.optional (nixosPath != null) nixosPath;
in
inputs.nixpkgs.lib.nixosSystem {
inherit (spec) system;
specialArgs = mkSpecialArgs name spec;
inherit modules;
};
mkDarwin =
name: rawSpec:
let
spec = validateSpec name rawSpec;
selected = registry.validateUnitIds (selectedUnits spec);
darwinPath = hostFile spec "darwin.nix";
modules = [
(registry.mkModule { class = "darwin"; })
(registry.mkSelectionModule selected)
{
networking.hostName = lib.mkDefault name;
system.primaryUser = lib.mkDefault spec.user;
}
]
++ lib.optional (spec.homeManager or true) (mkDarwinHomeManagerModule name spec selected)
++ lib.optional (darwinPath != null) darwinPath;
in
ensure (inputs ? nix-darwin) "${name}: the nix-darwin input is required" (
inputs.nix-darwin.lib.darwinSystem {
inherit (spec) system;
specialArgs = mkSpecialArgs name spec;
inherit modules;
}
);
mkConfigurations =
hostSpecs:
let
validated = lib.mapAttrs validateSpec hostSpecs;
in
{
nixosConfigurations = lib.mapAttrs mkNixos (
lib.filterAttrs (_: spec: isLinux spec.system) validated
);
darwinConfigurations = lib.mapAttrs mkDarwin (
lib.filterAttrs (_: spec: isDarwin spec.system) validated
);
};
in
{
inherit
mkConfigurations
mkDarwin
mkNixos
selectedUnits
;
}
+386
View File
@@ -0,0 +1,386 @@
{
inputs,
lib,
modulesRoot,
}:
let
rootFragmentFiles = {
common = "common.nix";
nixos = "nixos.nix";
darwin = "darwin.nix";
home = "home.nix";
meta = "meta.nix";
};
homeFragmentFiles = {
homeCommon = "common.nix";
homeNixos = "nixos.nix";
homeDarwin = "darwin.nix";
};
fragmentFileNames = rootFragmentFiles // lib.mapAttrs (_: name: "home/${name}") homeFragmentFiles;
isFile = kind: kind == "regular" || kind == "symlink";
ensure =
condition: message: value:
if condition then value else throw "unit registry: ${message}";
callWithAvailableArgs =
value: availableArgs:
if builtins.isFunction value then
value (builtins.intersectAttrs (builtins.functionArgs value) availableArgs)
else
value;
pathFor =
relativePath:
if relativePath == [ ] then
modulesRoot
else
modulesRoot + "/${lib.concatStringsSep "/" relativePath}";
entryIsFile = entries: name: builtins.hasAttr name entries && isFile entries.${name};
normalizeMeta =
unit:
let
metaPath = unit.fragments.meta;
importedValue =
if metaPath == null then
{ }
else
callWithAvailableArgs (import metaPath) {
inherit inputs lib unit;
};
imported =
ensure (builtins.isAttrs importedValue) "${unit.id}: meta.nix must return an attribute set"
importedValue;
allowedKeys = [
"description"
"includes"
"imports"
];
unknownKeys = lib.filter (name: !(builtins.elem name allowedKeys)) (builtins.attrNames imported);
description = imported.description or null;
includes = imported.includes or [ ];
imports = imported.imports or { };
allowedImportKeys = [
"nixos"
"darwin"
"home"
];
unknownImportKeys =
if builtins.isAttrs imports then
lib.filter (name: !(builtins.elem name allowedImportKeys)) (builtins.attrNames imports)
else
[ ];
normalized = {
inherit description includes;
imports = {
nixos = imports.nixos or [ ];
darwin = imports.darwin or [ ];
home = imports.home or [ ];
};
};
in
ensure (unknownKeys == [ ])
"${unit.id}: meta.nix has unsupported keys: ${lib.concatStringsSep ", " unknownKeys}"
(
ensure (description == null || builtins.isString description)
"${unit.id}: meta.description must be a string"
(
ensure (builtins.isList includes && lib.all builtins.isString includes)
"${unit.id}: meta.includes must be a list of fully qualified unit IDs"
(
ensure (lib.unique includes == includes) "${unit.id}: meta.includes contains duplicate unit IDs" (
ensure (builtins.isAttrs imports) "${unit.id}: meta.imports must be an attribute set" (
ensure (unknownImportKeys == [ ])
"${unit.id}: meta.imports has unsupported classes: ${lib.concatStringsSep ", " unknownImportKeys}"
(
ensure (lib.all builtins.isList [
normalized.imports.nixos
normalized.imports.darwin
normalized.imports.home
]) "${unit.id}: every meta.imports.<class> value must be a list" normalized
)
)
)
)
)
);
makeUnit =
relativePath: entries: homeEntries:
let
directory = pathFor relativePath;
id = lib.concatStringsSep "." relativePath;
rootFragments = lib.mapAttrs (
_class: fileName: if entryIsFile entries fileName then directory + "/${fileName}" else null
) rootFragmentFiles;
homeFragments = lib.mapAttrs (
_class: fileName: if entryIsFile homeEntries fileName then directory + "/home/${fileName}" else null
) homeFragmentFiles;
fragments = rootFragments // homeFragments;
baseUnit = {
inherit
id
directory
fragments
relativePath
;
optionPath = [ "my" ] ++ relativePath ++ [ "enable" ];
kind = builtins.head relativePath;
name = lib.last relativePath;
}
//
lib.optionalAttrs (builtins.length relativePath > 2 && builtins.head relativePath == "profiles")
{
group = builtins.elemAt relativePath 1;
};
in
ensure (relativePath != [ ]) "the modules root cannot itself be a unit" (
ensure (lib.all (component: component != "" && !(lib.hasInfix "." component)) relativePath)
"${id}: path components must be non-empty and must not contain dots"
(baseUnit // { meta = normalizeMeta baseUnit; })
);
walk =
relativePath:
let
directory = pathFor relativePath;
entries = builtins.readDir directory;
homeEntries =
if relativePath != [ ] && (entries.home or null) == "directory" then
builtins.readDir (directory + "/home")
else
{ };
hasRootFragment = lib.any (fileName: entryIsFile entries fileName) (
builtins.attrValues rootFragmentFiles
);
hasHomeFragment = lib.any (fileName: entryIsFile homeEntries fileName) (
builtins.attrValues homeFragmentFiles
);
hasFragment = hasRootFragment || hasHomeFragment;
childDirectories = lib.filter (
name: entries.${name} == "directory" && !(name == "home" && hasHomeFragment)
) (builtins.attrNames entries);
current = lib.optional hasFragment (makeUnit relativePath entries homeEntries);
children = lib.concatMap (name: walk (relativePath ++ [ name ])) childDirectories;
in
current ++ children;
discoveredUnits =
ensure (builtins.pathExists modulesRoot) "modules root does not exist: ${toString modulesRoot}"
(walk [ ]);
unitsById = builtins.listToAttrs (map (unit: lib.nameValuePair unit.id unit) discoveredUnits);
dependencyValidation = lib.foldl' (
valid: unit:
lib.foldl' (
inner: includedId:
if builtins.hasAttr includedId unitsById then
inner
else
throw "unit registry: ${unit.id} includes missing unit '${includedId}'"
) valid unit.meta.includes
) true discoveredUnits;
units = builtins.seq dependencyValidation unitsById;
unitIds = builtins.attrNames units;
getUnit =
id: if builtins.hasAttr id units then units.${id} else throw "unit registry: unknown unit '${id}'";
validateUnitIds =
ids:
ensure (
builtins.isList ids && lib.all builtins.isString ids
) "selected units must be a list of strings" (map (id: builtins.seq (getUnit id) id) ids);
optionDefinitions = lib.foldl' lib.recursiveUpdate { } (
map (
unit:
lib.setAttrByPath unit.optionPath (
lib.mkOption {
type = lib.types.bool;
default = false;
description =
if unit.meta.description == null then
"Whether to enable the ${unit.id} unit."
else
"Whether to enable ${unit.meta.description}.";
}
)
) discoveredUnits
);
enabled = config: unit: lib.getAttrFromPath unit.optionPath config;
enableUnit = id: lib.setAttrByPath (getUnit id).optionPath true;
includeConfig =
config: unit: lib.mkIf (enabled config unit) (lib.mkMerge (map enableUnit unit.meta.includes));
fragmentClasses = {
nixos = [
"common"
"nixos"
];
darwin = [
"common"
"darwin"
];
home = {
nixos = [
"home"
"homeCommon"
"homeNixos"
];
darwin = [
"home"
"homeCommon"
"homeDarwin"
];
};
};
fragmentClassesFor =
{
class,
systemClass,
}:
ensure (builtins.hasAttr class fragmentClasses) "unsupported module class '${class}'" (
if class == "home" then
ensure
(builtins.elem systemClass [
"nixos"
"darwin"
])
"the home module class requires systemClass to be 'nixos' or 'darwin'"
fragmentClasses.home.${systemClass}
else
ensure (
systemClass == null
) "systemClass is only supported for the home module class" fragmentClasses.${class}
);
applyFragment =
{
config,
fragmentName,
fragmentPath,
options,
specialArgs,
unit,
}:
let
fragment = import fragmentPath;
directArgs = specialArgs // {
inherit
config
lib
options
specialArgs
unit
;
};
fragmentArgSpec = builtins.functionArgs fragment;
fragmentArgs = builtins.listToAttrs (
lib.concatMap (
name:
if builtins.hasAttr name directArgs then
[ (lib.nameValuePair name directArgs.${name}) ]
else if fragmentArgSpec.${name} then
[ ]
else
[ (lib.nameValuePair name config._module.args.${name}) ]
) (builtins.attrNames fragmentArgSpec)
);
resultValue = if builtins.isFunction fragment then fragment fragmentArgs else fragment;
result =
ensure (builtins.isAttrs resultValue) "${unit.id}: ${fragmentName} must return an attribute set"
resultValue;
forbiddenKeys = lib.filter (name: builtins.hasAttr name result) [
"imports"
"options"
"config"
];
in
ensure (forbiddenKeys == [ ])
"${unit.id}: ${fragmentName} is a configuration fragment and cannot define top-level ${lib.concatStringsSep ", " forbiddenKeys}"
result;
externalImports = class: lib.concatMap (unit: unit.meta.imports.${class}) discoveredUnits;
mkModule =
{
class,
systemClass ? null,
}:
let
selectedFragmentClasses = fragmentClassesFor { inherit class systemClass; };
in
builtins.seq selectedFragmentClasses (
builtins.seq dependencyValidation (
{
config,
lib,
options,
specialArgs,
...
}:
let
fragmentConfigs = lib.concatMap (
unit:
lib.filter (value: value != null) (
map (
fragmentClass:
let
fragmentName = fragmentFileNames.${fragmentClass};
fragmentPath = unit.fragments.${fragmentClass};
in
if fragmentPath == null then
null
else
lib.mkIf (enabled config unit) (applyFragment {
inherit
config
fragmentName
fragmentPath
options
specialArgs
unit
;
})
) selectedFragmentClasses
)
) discoveredUnits;
in
{
imports = externalImports class;
options = optionDefinitions;
config = lib.mkMerge ((map (includeConfig config) discoveredUnits) ++ fragmentConfigs);
}
)
);
mkSelectionModule =
selectedIds:
let
checkedIds = validateUnitIds (lib.unique selectedIds);
in
{
config = lib.mkMerge (map enableUnit checkedIds);
};
in
{
inherit
getUnit
mkModule
mkSelectionModule
unitIds
units
validateUnitIds
;
}
@@ -0,0 +1,4 @@
_: {
programs._1password-gui.enable = true;
programs._1password.enable = true;
}
+9
View File
@@ -0,0 +1,9 @@
{ primaryUser, lib, ... }:
{
programs._1password-gui.polkitPolicyOwners = [ primaryUser ];
# 1Password SSH Agentと競合するagentを無効化
programs.ssh.startAgent = lib.mkForce false;
programs.gnupg.agent.enableSSHSupport = lib.mkForce false;
services.gnome.gcr-ssh-agent.enable = lib.mkForce false;
}
@@ -0,0 +1,8 @@
{
homebrew.casks = [ "activitywatch" ];
launchd.agents.activitywatch = {
command = "/usr/bin/open -gja ActivityWatch";
serviceConfig.RunAtLoad = true;
};
}
@@ -0,0 +1,11 @@
{ pkgs, ... }:
{
services.activitywatch = {
enable = true;
watchers.aw-awatcher = {
package = pkgs.awatcher;
executable = "awatcher";
};
};
}
@@ -0,0 +1,3 @@
{
description = "ActivityWatch automated time tracker";
}
+4
View File
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.arduino-cli ];
}
+14
View File
@@ -0,0 +1,14 @@
{ pkgs, ... }:
let
arduinoIdeX11 = pkgs.arduino-ide.overrideAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.makeWrapper ];
postFixup = (old.postFixup or "") + ''
wrapProgram $out/bin/arduino-ide \
--add-flags "--ozone-platform=x11"
'';
});
in
{
environment.systemPackages = [ arduinoIdeX11 ];
}
+9
View File
@@ -0,0 +1,9 @@
{
programs.atuin = {
enable = true;
enableZshIntegration = true;
enableBashIntegration = true;
enableFishIntegration = true;
};
}
+4
View File
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.baobab ];
}
+89
View File
@@ -0,0 +1,89 @@
# Bashtop theme with Nord palette (https://www.nordtheme.com)
# by Justin Zobel <[email protected]>
# Colors should be in 6 or 2 character hexadecimal or single spaced rgb decimal: "#RRGGBB", "#BW" or "0-255 0-255 0-255"
# example for white: "#ffffff", "#ff" or "255 255 255".
# All graphs and meters can be gradients
# For single color graphs leave "mid" and "end" variable empty.
# Use "start" and "end" variables for two color gradient
# Use "start", "mid" and "end" for three color gradient
# Main background, empty for terminal default, need to be empty if you want transparent background
theme[main_bg]="#282A36"
# Main text color
theme[main_fg]="#BD93F9"
# Title color for boxes
theme[title]="#f8f8f2"
# Highlight color for keyboard shortcuts
theme[hi_fg]="#ff79c6"
# Background color of selected item in processes box
theme[selected_bg]="#44475A"
# Foreground color of selected item in processes box
theme[selected_fg]="#ECEFF4"
# Color of inactive/disabled text
theme[inactive_fg]="#6272a4"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#BD93F9"
# Cpu box outline color
theme[cpu_box]="#ff79c6"
# Memory/disks box outline color
theme[mem_box]="#ff79c6"
# Net up/down box outline color
theme[net_box]="#ff79c6"
# Processes box outline color
theme[proc_box]="#ff79c6"
# Box divider line and small boxes line color
theme[div_line]="#ff79c6"
# Temperature graph colors
theme[temp_start]="#bd93f9"
theme[temp_mid]="#bd93f9"
theme[temp_end]="#bd93f9"
# CPU graph colors
theme[cpu_start]="#bd93f9"
theme[cpu_mid]="#bd93f9"
theme[cpu_end]="#bd93f9"
# Mem/Disk free meter
theme[free_start]="#bd93f9"
theme[free_mid]="#bd93f9"
theme[free_end]="#bd93f9"
# Mem/Disk cached meter
theme[cached_start]="#bd93f9"
theme[cached_mid]="#bd93f9"
theme[cached_end]="#bd93f9"
# Mem/Disk available meter
theme[available_start]="#bd93f9"
theme[available_mid]="#bd93f9"
theme[available_end]="#bd93f9"
# Mem/Disk used meter
theme[used_start]="#bd93f9"
theme[used_mid]="#bd93f9"
theme[used_end]="#bd93f9"
# Download graph colors
theme[download_start]="#bd93f9"
theme[download_mid]="#bd93f9"
theme[download_end]="#bd93f9"
# Upload graph colors
theme[upload_start]="#bd93f9"
theme[upload_mid]="#bd93f9"
theme[upload_end]="#bd93f9"
+8
View File
@@ -0,0 +1,8 @@
{
programs.btop = {
enable = true;
settings.color_theme = "dracula";
themes.dracula = builtins.readFile ./dracula.theme;
};
}
@@ -0,0 +1,31 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.celluloid ];
xdg.mimeApps = {
enable = true;
defaultApplicationPackages = [ pkgs.celluloid ];
defaultApplications = builtins.listToAttrs (
map
(mime: {
name = mime;
value = [ "io.github.celluloid_player.Celluloid.desktop" ];
})
[
"video/3gpp"
"video/3gpp2"
"video/mp2t"
"video/mp4"
"video/mpeg"
"video/ogg"
"video/quicktime"
"video/webm"
"video/x-flv"
"video/x-m4v"
"video/x-matroska"
"video/x-msvideo"
"video/x-ms-wmv"
]
);
};
}
+6
View File
@@ -0,0 +1,6 @@
{
homebrew = {
enable = true;
casks = [ "google-chrome" ];
};
}
@@ -0,0 +1,19 @@
_: {
programs.google-chrome = {
enable = true;
commandLineArgs = [
"--enable-features=MiddleClickAutoscroll"
];
};
xdg.mimeApps = {
enable = true;
defaultApplications = {
"text/html" = "google-chrome.desktop";
"x-scheme-handler/http" = "google-chrome.desktop";
"x-scheme-handler/https" = "google-chrome.desktop";
};
};
}
+18
View File
@@ -0,0 +1,18 @@
{
inputs,
pkgs,
...
}:
{
home.packages = [
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.claude-code
];
home.file.".claude/settings.json".text = builtins.toJSON {
statusLine = {
type = "command";
command = "bun x ccusage statusline --no-offline";
padding = 0;
};
};
}
@@ -0,0 +1,8 @@
{
# The former Codex app cask is deprecated in favor of ChatGPT, whose desktop
# application includes the current Codex experience on macOS.
homebrew = {
enable = true;
casks = [ "chatgpt" ];
};
}
@@ -0,0 +1,10 @@
{ inputs, ... }:
{
description = "Codex Desktop for Linux";
includes = [ "applications.codex" ];
imports.nixos = [
inputs.codex-desktop-linux.nixosModules.default
];
}
@@ -0,0 +1,18 @@
{
inputs,
pkgs,
...
}:
{
programs.codexDesktopLinux = {
enable = true;
cliPackage = inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex;
remoteControl.enable = true;
remoteMobileControl.enable = true;
computerUseUi.enable = false;
linuxFeatures = [
"appshots"
"open-target-discovery"
];
};
}
+6
View File
@@ -0,0 +1,6 @@
{ inputs, pkgs, ... }:
{
home.packages = [
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex
];
}
+10
View File
@@ -0,0 +1,10 @@
{
programs.direnv = {
enable = true;
nix-direnv.enable = true;
config.global = {
warn_timeout = "10s";
};
};
}
+95
View File
@@ -0,0 +1,95 @@
_: {
programs.vesktop = {
enable = true;
settings = {
discordBranch = "stable";
hardwareAcceleration = true;
hardwareVideoAcceleration = true;
tray = true;
minimizeToTray = true;
# Discord Rich Presence
arRPC = true;
openLinksWithElectron = false;
spellCheckLanguages = [
"ja-JP"
"en-US"
];
};
vencord = {
useSystem = false;
settings = {
autoUpdate = true;
autoUpdateNotification = false;
useQuickCss = false;
cloud.settingsSync = false;
notifications = {
position = "bottom-right";
useNative = "not-focused";
timeout = 5000;
logLimit = 50;
};
plugins = {
# プライバシー・安全性
NoTrack.enabled = true;
ClearURLs.enabled = true;
# 設定・セッション
BetterSettings.enabled = true;
BetterSessions.enabled = true;
# 画像・添付ファイル
FixImagesQuality.enabled = true;
ImageZoom.enabled = true;
ViewIcons.enabled = true;
CopyFileContents.enabled = true;
# メッセージ操作
QuickReply.enabled = true;
SendTimestamps.enabled = true;
FullSearchContext.enabled = true;
MessageLinkEmbeds.enabled = true;
Unindent.enabled = true;
ValidReply.enabled = true;
# 通知・誤操作対策
ReadAllNotificationsButton.enabled = true;
NoReplyMention.enabled = true;
NotificationVolume.enabled = true;
# UI・パフォーマンス
NoTypingAnimation.enabled = true;
FavoriteEmojiFirst.enabled = true;
KeepCurrentChannel.enabled = true;
# サーバー・権限確認
PermissionsViewer.enabled = true;
MemberCount.enabled = true;
# ボイス・アクティビティ
CallTimer.enabled = true;
GameActivityToggle.enabled = true;
# Vesktop向け
WebKeybinds.enabled = true;
WebScreenShareFixes.enabled = true;
# Message history
MessageLogger.enabled = true;
ShowHiddenChannels.enabled = true;
};
};
};
};
}
+6
View File
@@ -0,0 +1,6 @@
{
homebrew = {
enable = true;
casks = [ "orbstack" ];
};
}
@@ -0,0 +1,6 @@
{ pkgs, ... }:
{
home.packages = [
pkgs.docker-client
];
}
@@ -0,0 +1,6 @@
{ pkgs, ... }:
{
home.packages = [
pkgs.oxker
];
}
+5
View File
@@ -0,0 +1,5 @@
{
description = "Docker command-line client and NixOS daemon";
includes = [ "services.docker" ];
}
+4
View File
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.drawio ];
}
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.easyeffects ];
}
@@ -0,0 +1,82 @@
{ inputs, pkgs, ... }:
{
services.hazkey = {
enable = true;
server.package =
inputs.nix-hazkey.packages.${pkgs.stdenv.hostPlatform.system}.hazkey-server.override
{ enableVulkan = true; };
};
i18n.inputMethod = {
enable = true;
type = "fcitx5";
fcitx5 = {
waylandFrontend = true;
addons = with pkgs; [
fcitx5-gtk
kdePackages.fcitx5-qt
qt6Packages.fcitx5-configtool
];
settings = {
inputMethod = {
GroupOrder."0" = "Default";
"Groups/0" = {
Name = "Default";
"Default Layout" = "jp";
DefaultIM = "hazkey";
};
"Groups/0/Items/0" = {
Name = "keyboard-jp";
};
"Groups/0/Items/1" = {
Name = "hazkey";
};
};
globalOptions = {
Hotkey = {
EnumerateWithTriggerKeys = false;
EnumerateSkipFirst = false;
ModifierOnlyKeyTimeout = 250;
};
"Hotkey/TriggerKeys"."1" = "Zenkaku_Hankaku";
"Hotkey/ActivateKeys"."0" = "Henkan";
"Hotkey/DeactivateKeys"."0" = "Muhenkan";
"Hotkey/PrevPage"."0" = "Up";
"Hotkey/NextPage"."0" = "Down";
"Hotkey/PrevCandidate"."0" = "Shift+Tab";
"Hotkey/NextCandidate"."0" = "Tab";
"Hotkey/TogglePreedit"."0" = "Control+Alt+P";
Behavior = {
ActiveByDefault = false;
resetStateWhenFocusIn = "No";
ShareInputState = "No";
PreeditEnabledByDefault = true;
ShowInputMethodInformation = true;
showInputMethodInformationWhenFocusIn = false;
CompactInputMethodInformation = true;
ShowFirstInputMethodInformation = true;
DefaultPageSize = 5;
OverrideXkbOption = false;
CustomXkbOption = "";
EnabledAddons = "";
DisabledAddons = "";
PreloadInputMethod = true;
AllowInputMethodForPassword = false;
ShowPreeditForPassword = false;
AutoSavePeriod = 30;
};
};
};
};
};
}
+6
View File
@@ -0,0 +1,6 @@
{ inputs, ... }:
{
description = "Fcitx 5 input method framework with Hazkey Japanese input";
imports.home = [ inputs.nix-hazkey.homeModules.hazkey ];
}
+15
View File
@@ -0,0 +1,15 @@
{
system.defaults.finder = {
AppleShowAllExtensions = true;
AppleShowAllFiles = false;
ShowPathbar = true;
ShowStatusBar = true;
_FXShowPosixPathInTitle = true;
_FXSortFoldersFirst = true;
FXDefaultSearchScope = "SCcf";
FXPreferredViewStyle = "Nlsv";
NewWindowTarget = "Home";
FXEnableExtensionChangeWarning = true;
FXRemoveOldTrashItems = true;
};
}
+17
View File
@@ -0,0 +1,17 @@
{
programs.gamemode = {
enable = true;
enableRenice = true;
settings = {
general = {
softrealtime = "auto";
renice = 10;
};
custom = {
start = "notify-send -a 'Gamemode' 'Optimizations activated'";
end = "notify-send -a 'Gamemode' 'Optimizations deactivated'";
};
};
};
}
+6
View File
@@ -0,0 +1,6 @@
{
programs.gamescope = {
enable = true;
capSysNice = true;
};
}
+6
View File
@@ -0,0 +1,6 @@
{
homebrew = {
enable = true;
casks = [ "ghostty" ];
};
}
@@ -0,0 +1,39 @@
_: {
programs.ghostty = {
enable = true;
settings = {
theme = "dracula";
background-blur-radius = 20;
background-opacity = 0.9;
background-opacity-cells = true;
font-family = "BlexMono Nerd Font Mono";
mouse-hide-while-typing = true;
window-decoration = "auto";
keybind = [
"performable:ctrl+shift+c=copy_to_clipboard"
"ctrl+shift+v=paste_from_clipboard"
"ctrl+shift+t=new_tab"
"ctrl+alt+left_bracket=previous_tab"
"ctrl+alt+right_bracket=next_tab"
"alt+q=close_window"
"global:alt+space=toggle_quick_terminal"
"global:alt+t=new_window"
"ctrl+shift+semicolon=increase_font_size:1"
"ctrl+shift+minus=decrease_font_size:1"
];
quick-terminal-screen = "mouse";
quick-terminal-position = "top";
quick-terminal-size = "98%,100%";
quick-terminal-autohide = false;
quick-terminal-keyboard-interactivity = "on-demand";
gtk-quick-terminal-layer = "top";
quit-after-last-window-closed = false;
shell-integration-features = "no-ssh-env,no-ssh-terminfo";
};
};
xdg.configFile."ghostty/themes/dracula".source = ./dracula.theme;
}
@@ -0,0 +1,17 @@
_: {
# Global Ghostty keybindings are handled by the running app. Start it hidden
# at login so Option+T and Option+Space work before opening a terminal.
launchd.agents.ghostty-global-keybindings = {
enable = true;
config = {
ProgramArguments = [
"/usr/bin/open"
"-gja"
"Ghostty"
];
RunAtLoad = true;
};
};
programs.ghostty.package = null;
}
@@ -0,0 +1,44 @@
# MIT License
#
# Copyright (c) 2023 Dracula Theme
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
palette = 0=#21222c
palette = 1=#ff5555
palette = 2=#50fa7b
palette = 3=#f1fa8c
palette = 4=#bd93f9
palette = 5=#ff79c6
palette = 6=#8be9fd
palette = 7=#f8f8f2
palette = 8=#6272a4
palette = 9=#ff6e6e
palette = 10=#69ff94
palette = 11=#ffffa5
palette = 12=#d6acff
palette = 13=#ff92df
palette = 14=#a4ffff
palette = 15=#ffffff
background = #282a36
foreground = #f8f8f2
cursor-color = #f8f8f2
cursor-text = #282a36
selection-foreground = #f8f8f2
selection-background = #44475a
@@ -0,0 +1,6 @@
{ inputs, system, ... }: {
programs.ghostty = {
systemd.enable = true;
package = inputs.ghostty.packages.${system}.default;
};
}
+78
View File
@@ -0,0 +1,78 @@
{ pkgs, ... }:
let
signingKeyPath = ".ssh/1password-git-signing.pub";
signingKeyFile = "~/${signingKeyPath}";
gitSshSign = pkgs.writeShellScript "git-ssh-sign" ''
one_password_sock="$HOME/.1password/agent.sock"
if { [ -n "''${SSH_CONNECTION:-}" ] || [ -n "''${SSH_CLIENT:-}" ]; } \
&& [ -n "''${SSH_AUTH_SOCK:-}" ] \
&& [ -S "$SSH_AUTH_SOCK" ]; then
exec ${pkgs.openssh}/bin/ssh-keygen "$@"
fi
if [ -S "$one_password_sock" ]; then
export SSH_AUTH_SOCK="$one_password_sock"
exec ${pkgs.openssh}/bin/ssh-keygen "$@"
fi
if [ -n "''${SSH_AUTH_SOCK:-}" ] && [ -S "$SSH_AUTH_SOCK" ]; then
exec ${pkgs.openssh}/bin/ssh-keygen "$@"
fi
echo "git ssh signing failed: no forwarded SSH agent or 1Password agent socket found" >&2
echo "expected: forwarded SSH_AUTH_SOCK or $one_password_sock" >&2
exit 1
'';
in
{
home.packages = [ pkgs.gh ];
home.file.${signingKeyPath}.text = ''
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLwReAiwhXoO34S2+MrvqUhi8IWp4IzUq4OSp3niJdq 1password-git-signing
'';
programs.git = {
enable = true;
ignores = [
".direnv/"
".envrc"
"!.envrc.example"
];
signing = {
key = signingKeyFile;
format = "ssh";
signByDefault = true;
};
settings = {
user = {
name = "moons";
email = "moons@moons14.com";
};
push.default = "simple";
credential.helper = "cache --timeout=7200";
init.defaultBranch = "main";
log.decorate = "full";
log.date = "iso";
merge.conflictStyle = "diff3";
gpg.ssh.program = "${gitSshSign}";
alias = {
br = "branch --sort=-committerdate";
co = "checkout";
df = "diff";
com = "commit -a";
gs = "stash";
gp = "pull";
lg = "log --graph --pretty=format:'%Cred%h%Creset - %C(yellow)%d%Creset %s %C(green)(%cr)%C(bold blue) <%an>%Creset' --abbrev-commit";
st = "status";
};
};
};
}
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.gnome-disk-utility ];
}
@@ -0,0 +1,24 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.gnome-text-editor ];
dconf.settings."org/gnome/TextEditor" = {
style-variant = "follow";
wrap-text = true;
spellcheck = true;
restore-session = true;
show-line-numbers = false;
show-right-margin = false;
show-map = false;
highlight-current-line = false;
auto-indent = false;
discover-settings = false;
enable-snippets = false;
keybindings = "default";
};
xdg.mimeApps = {
enable = true;
defaultApplicationPackages = [ pkgs.gnome-text-editor ];
};
}
+9
View File
@@ -0,0 +1,9 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.gnupg ];
services.gpg-agent = {
enable = false;
enableSshSupport = false;
};
}
+6
View File
@@ -0,0 +1,6 @@
{
programs.gnupg.agent = {
enable = true;
enableSSHSupport = false;
};
}
+6
View File
@@ -0,0 +1,6 @@
{ inputs, pkgs, ... }:
{
home.packages = [
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.grok
];
}
+22
View File
@@ -0,0 +1,22 @@
{ pkgs, ... }:
{
gtk = {
enable = true;
theme = {
name = "Dracula";
package = pkgs.dracula-theme;
};
cursorTheme = {
package = pkgs.adwaita-icon-theme;
name = "Adwaita";
};
iconTheme = {
package = pkgs.papirus-icon-theme;
name = "Papirus-Dark";
};
};
dconf.settings."org/gnome/desktop/wm/preferences" = {
button-layout = ":minimize,maximize,close";
};
}
+6
View File
@@ -0,0 +1,6 @@
{
programs.dconf.enable = true;
programs.seahorse.enable = true;
services.gnome.gnome-keyring.enable = true;
security.pam.services.ly.enableGnomeKeyring = true;
}
+8
View File
@@ -0,0 +1,8 @@
{
homebrew.casks = [ "handy" ];
launchd.agents.handy = {
command = "/usr/bin/open -gja Handy --args --start-hidden";
serviceConfig.RunAtLoad = true;
};
}
+74
View File
@@ -0,0 +1,74 @@
{
config,
inputs,
lib,
pkgs,
...
}:
let
handy = inputs.handy.packages.${pkgs.stdenv.hostPlatform.system}.handy;
settingsFile = "${config.xdg.configHome}/com.pais.handy/settings_store.json";
in
{
home.packages = [
handy
pkgs.wtype
];
# Handy has no disabled-shortcut setting. Empty bindings are rejected before
# registration, leaving niri and labwc as the sole owners of Ctrl+Space.
home.activation.disableHandyGlobalShortcuts = {
after = [ "writeBoundary" ];
before = [ ];
data = ''
settingsFile=${lib.escapeShellArg settingsFile}
mkdir -p "$(dirname "$settingsFile")"
if [ -f "$settingsFile" ]; then
${lib.getExe pkgs.jq} \
'.settings.bindings.transcribe.current_binding = ""
| .settings.bindings.transcribe_with_post_process.current_binding = ""' \
"$settingsFile" > "$settingsFile.tmp"
else
${lib.getExe pkgs.jq} -n '
{
settings: {
bindings: {
transcribe: {
id: "transcribe",
name: "Transcribe",
description: "Converts your speech into text.",
default_binding: "ctrl+space",
current_binding: ""
},
transcribe_with_post_process: {
id: "transcribe_with_post_process",
name: "Transcribe with Post-Processing",
description: "Converts your speech into text and applies AI post-processing.",
default_binding: "ctrl+shift+space",
current_binding: ""
}
}
}
}
' > "$settingsFile.tmp"
fi
mv "$settingsFile.tmp" "$settingsFile"
'';
};
systemd.user.services.handy = {
Unit = {
Description = "Handy speech-to-text";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${lib.getExe handy} --start-hidden";
Restart = "on-failure";
RestartSec = 5;
};
Install.WantedBy = [ "graphical-session.target" ];
};
}
+3
View File
@@ -0,0 +1,3 @@
{
description = "Handy offline speech-to-text";
}
+12
View File
@@ -0,0 +1,12 @@
{ pkgs, ... }:
{
programs.java = {
enable = true;
package = pkgs.jdk25;
};
home.packages = with pkgs; [
maven
gradle
];
}
+6
View File
@@ -0,0 +1,6 @@
{
homebrew = {
enable = true;
casks = [ "kde-connect" ];
};
}
+3
View File
@@ -0,0 +1,3 @@
{
programs.kdeconnect.enable = true;
}
+228
View File
@@ -0,0 +1,228 @@
{ lib, pkgs, ... }:
let
systemctl = lib.getExe' pkgs.systemd "systemctl";
keybind = key: actionAttrs: {
"@key" = key;
action = actionAttrs;
};
action =
key: name:
keybind key {
"@name" = name;
};
execute =
key: command:
keybind key {
"@name" = "Execute";
"@command" = command;
};
snapToEdge =
key: direction:
keybind key {
"@name" = "SnapToEdge";
"@direction" = direction;
"@combine" = "yes";
};
confirmAction = key: message: name: {
"@key" = key;
action = {
"@name" = "If";
prompt."@message" = message;
"then".action."@name" = name;
};
};
menuExecute = label: command: {
inherit label;
action = {
name = "Execute";
inherit command;
};
};
showMenu = button: menu: {
"@button" = button;
"@action" = "Press";
action = {
"@name" = "ShowMenu";
"@menu" = menu;
};
};
menuAction = label: name: {
inherit label;
action.name = name;
};
in
{
wayland.windowManager.labwc = {
enable = true;
# NixOS owns the package so Home Manager only generates the user config.
package = null;
systemd = {
enable = true;
variables = [
"DISPLAY"
"WAYLAND_DISPLAY"
"XDG_CURRENT_DESKTOP"
"XDG_SESSION_TYPE"
];
};
menu = [
{
menuId = "root-menu";
items = [
(menuExecute "ターミナル" "ghostty")
(menuExecute "Vicinae" "vicinae toggle")
(menuExecute "ファイル" "nautilus --new-window")
{ separator = true; }
(menuAction "Reconfigure" "Reconfigure")
(menuAction "Exit" "Exit")
];
}
];
rc = {
keyboard = {
default = true;
numlock = "on";
keybind = [
# labwc window management
(action "W-q" "Close")
(action "W-f" "ToggleMaximize")
(action "W-c" "Iconify")
(action "W-Tab" "NextWindow")
(action "W-S-Tab" "PreviousWindow")
(action "W-Up" "Lower")
(action "W-Down" "Raise")
(action "W-Left" "NextWindow")
(action "W-Right" "PreviousWindow")
(snapToEdge "W-C-Left" "left")
(snapToEdge "W-C-Right" "right")
(snapToEdge "W-C-Up" "up")
(snapToEdge "W-C-Down" "down")
(confirmAction "W-S-e" "Exit labwc?" "Exit")
(execute "Print" "screenshot region")
(execute "C-Print" "screenshot output")
(execute "A-Print" "screenshot all")
# spawn applications (sync with niri modules/applications/niri/home.nix)
(execute "W-t" "ghostty")
(execute "W-d" "vicinae toggle")
(execute "W-s" "noctalia msg panel-toggle launcher")
(execute "W-e" "nautilus --new-window")
(execute "W-l" "loginctl lock-session")
(execute "W-v" "vicinae vicinae://launch/clipboard/history?toggle=true")
(execute "W-j" "nani-translate-primary")
(execute "W-S-j" "normcap-translate")
(execute "W-C-j" "${lib.getExe' pkgs.xdg-utils "xdg-open"} naniapp://translate")
(execute "W-space" "ghostty +toggle-quick-terminal")
(execute "W-p" "wdisplays")
(execute "W-z" "wl-find-cursor -c 0xCCFF453A -s 160 -d 1200")
(execute "C-space" "handy --toggle-transcription")
# Function keys (sync with niri modules/applications/niri/home.nix)
(execute "XF86AudioRaiseVolume" "noctalia msg volume-up")
(execute "XF86AudioLowerVolume" "noctalia msg volume-down")
(execute "XF86AudioMute" "noctalia msg volume-mute")
(execute "XF86AudioMicMute" "noctalia msg mic-mute")
(execute "XF86MonBrightnessUp" "noctalia msg brightness-up")
(execute "XF86MonBrightnessDown" "noctalia msg brightness-down")
(execute "XF86Favorites" "noctalia msg caffeine-toggle")
(execute "XF86AudioPlay" "playerctl play-pause")
(execute "XF86AudioPause" "playerctl play-pause")
(execute "XF86AudioStop" "playerctl stop")
(execute "XF86AudioPrev" "playerctl previous")
(execute "XF86AudioNext" "playerctl next")
(execute "XF86Display" "wdisplays")
];
};
core = {
decoration = "client";
gap = 10;
autoEnableOutputs = "yes";
reuseOutputMode = "yes";
};
theme = {
name = "Dracula";
cornerRadius = 8;
dropShadows = "yes";
dropShadowsOnTiled = "yes";
};
windowSwitcher = {
"@preview" = "yes";
"@outlines" = "yes";
osd = {
"@style" = "thumbnail";
};
};
focus = {
followMouse = "yes";
followMouseRequiresMovement = "yes";
raiseOnFocus = "no";
};
desktops = {
"@number" = 1;
"@popupTime" = 500;
"@prefix" = "Workspace";
};
mouse = {
default = true;
context = {
"@name" = "Desktop";
mousebind = [
(showMenu "Right" "root-menu")
(showMenu "Middle" "client-list-combined-menu")
];
};
};
libinput.device = [
{
"@category" = "touchpad";
naturalScroll = "yes";
tap = "yes";
tapAndDrag = "yes";
dragLock = "yes";
clickMethod = "clickfinger";
scrollMethod = "twoFinger";
scrollFactor = "4.0";
disableWhileTyping = "yes";
}
{
"@category" = "non-touch";
pointerSpeed = "-0.1";
accelProfile = "flat";
}
];
};
environment = [
"XKB_DEFAULT_LAYOUT=jp"
"XKB_DEFAULT_OPTIONS=ctrl:nocaps"
];
};
xdg.configFile."labwc/shutdown".text = lib.mkAfter ''
${systemctl} --user stop graphical-session.target
'';
}
+9
View File
@@ -0,0 +1,9 @@
{
description = "labwc Wayland stacking compositor";
includes = [
"systems.wayland"
"applications.screenshot"
"applications.wl-find-cursor"
];
}
+62
View File
@@ -0,0 +1,62 @@
{
inputs,
lib,
pkgs,
...
}:
let
unstable = inputs.nixpkgs-unstable.legacyPackages.${pkgs.stdenv.hostPlatform.system};
labwc = unstable.labwc.overrideAttrs (oldAttrs: {
patches = (oldAttrs.patches or [ ]) ++ [
./patches/remove-ext-workspace-output-on-destroy.patch
];
});
in
{
programs.labwc = {
enable = true;
package = labwc;
};
xdg.portal.config.labwc.default = [
"wlr"
"gtk"
];
# xdg-desktop-portal-wlr implements screencasting for wlroots compositors.
# Keep it with Labwc: Niri uses xdg-desktop-portal-gnome instead.
xdg.portal.wlr = {
enable = true;
settings.screencast = {
chooser_type = "dmenu";
chooser_cmd = "${pkgs.fuzzel}/bin/fuzzel --dmenu";
};
};
# NixOS decides whether a graphical session manages graphical-session.target
# itself by checking XDG_CURRENT_DESKTOP against a hard-coded list. Labwc is
# currently absent from that list, so NixOS starts
# nixos-fake-graphical-session.target before Labwc is ready.
#
# This configuration uses Home Manager's Labwc systemd integration, which
# imports the Wayland environment and starts labwc-session.target correctly.
# Mark the Labwc session as systemd-aware before the display-manager wrapper
# performs its hard-coded check. Otherwise services such as Fcitx5 can start
# before WAYLAND_DISPLAY is available.
#
# Despite the services.xserver namespace, this session wrapper is also used
# for Wayland sessions by display managers such as Ly.
services.xserver.displayManager.sessionCommands = lib.mkAfter ''
case ":''${XDG_CURRENT_DESKTOP:-}:" in
*:labwc:*)
case ":$XDG_CURRENT_DESKTOP:" in
*:X-NIXOS-SYSTEMD-AWARE:*)
;;
*)
export XDG_CURRENT_DESKTOP="$XDG_CURRENT_DESKTOP:X-NIXOS-SYSTEMD-AWARE"
;;
esac
;;
esac
'';
}
@@ -0,0 +1,28 @@
From: Codex <[email protected]>
Subject: [PATCH] output: remove destroyed output from ext-workspace group
The DRM backend destroys and recreates outputs when a session is paused and
resumed. Remove the output from the ext-workspace group before wlroots
finishes it, otherwise the group's output_bind listener remains attached and
wlr_output_finish() aborts.
---
src/output.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/output.c b/src/output.c
index 2eab8ec..f19c3ff 100644
--- a/src/output.c
+++ b/src/output.c
@@ -277,6 +277,9 @@ handle_output_destroy(struct wl_listener *listener, void *data)
struct seat *seat = &server.seat;
regions_evacuate_output(output);
regions_destroy(seat, &output->regions);
+ wlr_ext_workspace_group_handle_v1_output_leave(
+ server.workspaces.ext_group, output->wlr_output);
+
if (seat->overlay.active.output == output) {
overlay_finish(seat);
}
--
2.51.0
@@ -0,0 +1,167 @@
# shellcheck shell=bash
set -o errexit -o nounset -o pipefail
playlist_file="$1"
default_id="$2"
configuration_id="$3"
shift 3
state_dir="${XDG_RUNTIME_DIR:?}/linux-wallpaperengine"
state_file="$state_dir/current"
usage() {
printf '%s\n' \
'Usage: wallpaperengine-wallpaper COMMAND [ID]' \
'' \
'Commands:' \
' select ID Select a declaratively configured ID for this session' \
' next Select the next configured ID' \
' previous Select the previous configured ID' \
' list List configured IDs and mark the selected one' \
' current Print the selected ID' \
' restart Restart the selected wallpaper' \
' stop Stop Wallpaper Engine for this session'
}
load_playlist() {
mapfile -t wallpaper_ids < <(awk '/^[0-9]+$/ && !seen[$0]++' "$playlist_file")
if [ "${#wallpaper_ids[@]}" -eq 0 ]; then
echo "wallpaperengine-wallpaper: no wallpaper IDs are configured in settings.nix" >&2
exit 1
fi
}
contains_id() {
local candidate="$1"
local id
for id in "${wallpaper_ids[@]}"; do
[ "$id" = "$candidate" ] && return 0
done
return 1
}
current_id() {
local saved_configuration=""
local saved_id=""
local -a saved_state=()
if [ -s "$state_file" ]; then
mapfile -t saved_state <"$state_file"
saved_configuration="${saved_state[0]:-}"
saved_id="${saved_state[1]:-}"
fi
if [ "$saved_configuration" = "$configuration_id" ] && contains_id "$saved_id"; then
printf '%s\n' "$saved_id"
else
printf '%s\n' "$default_id"
fi
}
select_id() {
local id="$1"
if ! contains_id "$id"; then
echo "wallpaperengine-wallpaper: ID is not declared in settings.nix: $id" >&2
exit 2
fi
mkdir -p "$state_dir"
printf '%s\n%s\n' "$configuration_id" "$id" >"$state_file"
systemctl --user restart linux-wallpaperengine.service
}
cycle() {
local step="$1"
local current
local index=0
local next_index
current=$(current_id)
for i in "${!wallpaper_ids[@]}"; do
if [ "${wallpaper_ids[$i]}" = "$current" ]; then
index="$i"
break
fi
done
next_index=$(((index + step + ${#wallpaper_ids[@]}) % ${#wallpaper_ids[@]}))
select_id "${wallpaper_ids[$next_index]}"
}
load_playlist
command="${1:-}"
if [ -z "$command" ]; then
usage
exit 2
fi
shift
case "$command" in
select)
[ "$#" -eq 1 ] || {
usage >&2
exit 2
}
select_id "$1"
;;
next)
[ "$#" -eq 0 ] || {
usage >&2
exit 2
}
cycle 1
;;
previous)
[ "$#" -eq 0 ] || {
usage >&2
exit 2
}
cycle -1
;;
list)
[ "$#" -eq 0 ] || {
usage >&2
exit 2
}
current=$(current_id)
for id in "${wallpaper_ids[@]}"; do
if [ "$id" = "$current" ]; then
printf '* %s\n' "$id"
else
printf ' %s\n' "$id"
fi
done
;;
current)
[ "$#" -eq 0 ] || {
usage >&2
exit 2
}
current_id
;;
restart)
[ "$#" -eq 0 ] || {
usage >&2
exit 2
}
systemctl --user restart linux-wallpaperengine.service
;;
stop)
[ "$#" -eq 0 ] || {
usage >&2
exit 2
}
systemctl --user stop linux-wallpaperengine.service
;;
help | -h | --help)
usage
;;
*)
usage >&2
exit 2
;;
esac
@@ -0,0 +1,57 @@
# shellcheck shell=bash
set -o errexit -o nounset -o pipefail
playlist_file="$1"
default_id="$2"
configuration_id="$3"
assets_dir="$4"
workshop_dir="$5"
scaling="$6"
shift 6
state_file="${XDG_RUNTIME_DIR:?}/linux-wallpaperengine/current"
wallpaper_id="$default_id"
saved_configuration=""
saved_id=""
saved_state=()
outputs=()
if [ -s "$state_file" ]; then
mapfile -t saved_state <"$state_file"
saved_configuration="${saved_state[0]:-}"
saved_id="${saved_state[1]:-}"
if [ "$saved_configuration" = "$configuration_id" ] &&
awk -v id="$saved_id" '$0 == id { found = 1 } END { exit !found }' "$playlist_file"; then
wallpaper_id="$saved_id"
fi
fi
wallpaper_path="$workshop_dir/$wallpaper_id"
if [ ! -d "$wallpaper_path" ]; then
echo "linux-wallpaperengine: missing declared wallpaper: $wallpaper_path" >&2
exit 1
fi
if [ ! -d "$assets_dir" ]; then
echo "linux-wallpaperengine: missing Wallpaper Engine assets: $assets_dir" >&2
exit 1
fi
mapfile -t outputs < <(wlr-randr --json | jq -r '.[] | select(.enabled == true) | .name')
if [ "${#outputs[@]}" -eq 0 ]; then
echo "linux-wallpaperengine: no enabled Wayland outputs were detected" >&2
exit 1
fi
engine_args=("$@" --assets-dir "$assets_dir")
for output in "${outputs[@]}"; do
engine_args+=(
--screen-root "$output"
--bg "$wallpaper_path"
--scaling "$scaling"
)
done
exec linux-wallpaperengine "${engine_args[@]}"
@@ -0,0 +1,158 @@
{
config,
lib,
pkgs,
...
}:
let
settings = import ../settings.nix;
inherit (settings)
assetsDirectory
fps
mute
scaling
selectedWallpaperId
switchIntervalSeconds
wallpaperIds
workshopDirectory
;
resolveHomePath =
path:
if lib.hasPrefix "~/" path then
"${config.home.homeDirectory}/${lib.removePrefix "~/" path}"
else if lib.hasPrefix "/" path then
path
else
"${config.home.homeDirectory}/${path}";
hasWallpapers = wallpaperIds != [ ];
defaultWallpaperId =
if selectedWallpaperId != null then
selectedWallpaperId
else if hasWallpapers then
lib.head wallpaperIds
else
"";
configurationId = builtins.hashString "sha256" (builtins.toJSON settings);
resolvedAssetsDirectory = resolveHomePath assetsDirectory;
resolvedWorkshopDirectory = resolveHomePath workshopDirectory;
playlist = pkgs.writeText "linux-wallpaperengine-playlist" (
lib.concatMapStringsSep "\n" (id: id) wallpaperIds + "\n"
);
engineArguments = lib.optional mute "--silent" ++ [
"--fps"
(toString fps)
];
controller = pkgs.writeShellApplication {
name = "wallpaperengine-wallpaper";
runtimeInputs = [
pkgs.coreutils
pkgs.gawk
pkgs.systemd
];
text = ''
exec ${lib.getExe pkgs.bash} ${./controller.sh} \
${lib.escapeShellArg playlist} \
${lib.escapeShellArg defaultWallpaperId} \
${lib.escapeShellArg configurationId} \
"$@"
'';
};
launcher = pkgs.writeShellApplication {
name = "linux-wallpaperengine-launcher";
runtimeInputs = [
pkgs.coreutils
pkgs.gawk
pkgs.jq
pkgs.linux-wallpaperengine
pkgs.wlr-randr
];
text = ''
exec ${lib.getExe pkgs.bash} ${./launcher.sh} \
${lib.escapeShellArg playlist} \
${lib.escapeShellArg defaultWallpaperId} \
${lib.escapeShellArg configurationId} \
${lib.escapeShellArg resolvedAssetsDirectory} \
${lib.escapeShellArg resolvedWorkshopDirectory} \
${lib.escapeShellArg scaling} \
${lib.escapeShellArgs engineArguments}
'';
};
in
assert lib.assertMsg (lib.all (
id: builtins.isString id && builtins.match "[0-9]+" id != null
) wallpaperIds) "linux-wallpaperengine: wallpaperIds must contain only numeric strings";
assert lib.assertMsg (
lib.unique wallpaperIds == wallpaperIds
) "linux-wallpaperengine: wallpaperIds must not contain duplicates";
assert lib.assertMsg (
selectedWallpaperId == null || builtins.elem selectedWallpaperId wallpaperIds
) "linux-wallpaperengine: selectedWallpaperId must be null or a member of wallpaperIds";
assert lib.assertMsg (
switchIntervalSeconds == null || (builtins.isInt switchIntervalSeconds && switchIntervalSeconds > 0)
) "linux-wallpaperengine: switchIntervalSeconds must be null or a positive integer";
assert lib.assertMsg (
builtins.isInt fps && fps > 0
) "linux-wallpaperengine: fps must be a positive integer";
assert lib.assertMsg (
builtins.isString assetsDirectory && assetsDirectory != ""
) "linux-wallpaperengine: assetsDirectory must be a non-empty string";
assert lib.assertMsg (
builtins.isString workshopDirectory && workshopDirectory != ""
) "linux-wallpaperengine: workshopDirectory must be a non-empty string";
assert lib.assertMsg (builtins.elem scaling [
"default"
"fill"
"fit"
"stretch"
]) "linux-wallpaperengine: scaling must be default, fill, fit, or stretch";
{
home.packages = [
controller
pkgs.linux-wallpaperengine
];
systemd.user.services = lib.optionalAttrs hasWallpapers {
linux-wallpaperengine = {
Unit = {
Description = "Linux Wallpaper Engine";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = lib.getExe launcher;
Restart = "on-abnormal";
};
Install.WantedBy = [ "graphical-session.target" ];
};
linux-wallpaperengine-switch = {
Unit.Description = "Switch Linux Wallpaper Engine wallpaper";
Service = {
Type = "oneshot";
ExecStart = "${lib.getExe controller} next";
};
};
};
systemd.user.timers =
lib.optionalAttrs
(hasWallpapers && builtins.length wallpaperIds > 1 && switchIntervalSeconds != null)
{
linux-wallpaperengine-switch = {
Unit = {
Description = "Periodically switch Linux Wallpaper Engine wallpaper";
PartOf = [ "graphical-session.target" ];
};
Timer = {
OnActiveSec = "${toString switchIntervalSeconds}s";
OnUnitActiveSec = "${toString switchIntervalSeconds}s";
Unit = "linux-wallpaperengine-switch.service";
};
Install.WantedBy = [ "graphical-session.target" ];
};
};
}
@@ -0,0 +1,3 @@
{
description = "Wallpaper Engine backgrounds for Linux";
}
@@ -0,0 +1,23 @@
{
assetsDirectory = "~/.local/share/Steam/steamapps/common/wallpaper_engine/assets";
workshopDirectory = "~/.local/share/Steam/steamapps/workshop/content/431960";
wallpaperIds = [
"2833810156"
"2834355367"
"2836628501"
"2845095254"
"2859848175"
"2861661119"
"2982896307"
];
# null selects the first ID above.
selectedWallpaperId = null;
# Set a number such as 300 to rotate through multiple IDs.
switchIntervalSeconds = 300;
fps = 30;
mute = true;
scaling = "fill";
}
+22
View File
@@ -0,0 +1,22 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.loupe ];
xdg.mimeApps = {
enable = true;
defaultApplicationPackages = [ pkgs.loupe ];
defaultApplications = {
"image/avif" = [ "org.gnome.Loupe.desktop" ];
"image/bmp" = [ "org.gnome.Loupe.desktop" ];
"image/gif" = [ "org.gnome.Loupe.desktop" ];
"image/heic" = [ "org.gnome.Loupe.desktop" ];
"image/heif" = [ "org.gnome.Loupe.desktop" ];
"image/jpeg" = [ "org.gnome.Loupe.desktop" ];
"image/jxl" = [ "org.gnome.Loupe.desktop" ];
"image/png" = [ "org.gnome.Loupe.desktop" ];
"image/svg+xml" = [ "org.gnome.Loupe.desktop" ];
"image/webp" = [ "org.gnome.Loupe.desktop" ];
};
};
}
@@ -0,0 +1,3 @@
_: {
programs.mangohud.enable = true;
}

Some files were not shown because too many files have changed in this diff Show More