Author SHA1 Message Date
moons-14 ec3770502d Add central Nix builder and binary cache 2026-08-31 08:07:48 +09:00
moons-14 fda29cd08d update 2026-08-30 06:48:50 +09:00
moons-14 db0a440da4 update Python to 3.14 2026-08-30 05:52:30 +09:00
moons-14 76f5dce9c0 update CodexBar to 0.56.0 2026-08-30 05:52:23 +09:00
moons-14 3b61457718 remove obsolete Codex skill alias 2026-08-30 05:52:16 +09:00
moons-14 c9f1584797 replace Codex Desktop with ChatGPT 2026-08-30 05:52:06 +09:00
moons-14 d51948c307 hugging face 2026-08-29 16:29:42 +09:00
moons-14 9a38e9f614 gpu settings 2026-08-29 16:14:32 +09:00
moons-14 2cb7e76ca1 nix s9 camera 2026-08-27 12:09:55 +09:00
moons-14 749410e032 fix 2026-08-26 21:35:17 +09:00
moons-14 d43f19c20a nix builder 2026-08-26 17:28:09 +09:00
moons-14 32a3067cb0 hardware configuration 2026-08-26 15:53:44 +09:00
moons-14 6e60bd8886 add nix-builder 2026-08-26 13:21:37 +09:00
moons-14 bcf7ef56cf update 2026-08-25 09:24:58 +09:00
moons-14 63e2008423 chrome video play 2026-08-24 02:14:30 +09:00
moons-14 3b8147ff46 codex setting 2026-08-23 14:06:05 +09:00
moons-14 c84f257149 ffmpeg and yt-dlp 2026-08-23 14:04:36 +09:00
moons-14 6347292c1d projects 2026-08-21 05:44:16 +09:00
moons-14 2a82433754 fix(vesktop): restrict WebRTC to public default interface (#67) 2026-08-21 05:31:13 +09:00
moons-14 847ee17b29 linuxPackages 2026-08-21 05:08:30 +09:00
moons-14 95f74aabcd feat 2026-08-21 05:08:30 +09:00
moons-14 717572ae24 feat 2026-08-21 05:08:30 +09:00
moons-14 8086c9a7f3 fix(vesktop): avoid DTLS stalls with multiple interfaces (#66)
* fix(vesktop): add WebRTC IP handling support

* fix(vesktop): constrain WebRTC interface selection
2026-08-21 04:31:24 +09:00
moons-14 eba23455d4 noctalia 2026-08-21 02:32:23 +09:00
moons-14 b3b1031364 fix 2026-08-20 06:18:23 +09:00
moons-14 366ff54403 allow suspend 2026-08-18 10:19:12 +09:00
moons-14 456e9946f4 flake update 2026-08-18 06:56:34 +09:00
moons-14 c104404e5b remove handy 2026-08-18 06:56:28 +09:00
moons-14 cb41932fec add installer keys 2026-08-18 06:29:01 +09:00
moons-14 fa4b7ca404 codex mutable config 2026-08-18 06:25:46 +09:00
moons-14 847d33e83b simplify codex home configuration 2026-08-18 06:12:20 +09:00
moons-14 b2c700e1e4 remove niri window overview bindings 2026-08-18 06:10:56 +09:00
moons-14 c1540d8764 remove noctalia patches 2026-08-18 06:10:43 +09:00
moons-14 5b8c3b1415 remove labwc patch 2026-08-18 06:10:29 +09:00
moons-14 6743569789 remove window-overview 2026-08-18 06:09:11 +09:00
moons-14 70253fc451 evremap 2026-08-18 05:16:18 +09:00
moons-14 5107b80173 feat(ssh): use available SSH agent socket 2026-08-18 05:00:21 +09:00
moons-14 2bdea522cb feat(niri): toggle floating windows with middle click 2026-08-18 05:00:13 +09:00
moons-14 c113d0d46d feat(x1g9): remap VXE mouse buttons 2026-08-18 05:00:05 +09:00
moons-14 fa50be8feb vicinae chrome integuration 2026-08-18 04:41:31 +09:00
moons-14 33ebef4a1e niri keybind 2026-08-18 04:21:16 +09:00
moons-14 94048ef8d5 wallpaper 2026-08-18 04:20:59 +09:00
moons-14 c7627fa1b0 darwin: add stable Xcode and iOS simulator shell (#65)
* darwin: add xcode ios simulator shell

* docs: add iOS simulator setup
2026-08-14 20:35:13 +09:00
moons-14 d68da620ac oh my openagent 2026-08-10 03:25:01 +09:00
moons-14 5f0df47775 codex 2026-08-09 19:54:35 +09:00
moons-14 119342e564 codex 2026-08-09 19:12:02 +09:00
moons-14 1f066e8937 codex 2026-08-09 18:51:09 +09:00
moons-14 0a440e3da8 update codex session usage 2026-08-09 18:13:03 +09:00
moons-14 bdf93ac6bb codex agents prompt 2026-08-09 15:58:40 +09:00
moons-14 9ae5da4d30 zed docker file extension 2026-08-09 15:40:11 +09:00
moons-14 872436b170 skill 2026-08-08 13:14:29 +09:00
moons-14 c14325e29c Update flake.lock 2026-08-08 06:46:59 +09:00
moons-14 45bc66e25f codex prompt 2026-08-08 06:45:22 +09:00
moons-14 78ee2d41ec codex usage vicinae 2026-08-08 05:51:40 +09:00
moons-14 0b1ae13048 window-overview 2026-08-08 04:15:35 +09:00
moons-14 458b0a2cdb galleria tailscale 2026-08-08 00:16:33 +09:00
moons-14 01179f3dc6 desktop: disable system sleep 2026-08-07 22:00:03 +09:00
moons-14 42949fc06c zed: enable Copilot edit predictions 2026-08-07 21:59:57 +09:00
moons-14 a7f514c0f9 opencode: install package through Home Manager 2026-08-07 21:59:47 +09:00
moons-14 9bed1c3e25 zed: add vim keymaps 2026-08-07 21:58:25 +09:00
moons-14 791b6baa83 mod + v toggle 2026-08-07 20:33:44 +09:00
moons-14 349f49e496 fix 2026-08-07 20:33:33 +09:00
moons-14 3166c12448 codex 2026-08-07 18:34:49 +09:00
moons-14 4c459e4aa7 codex back vesion 2026-08-07 18:34:17 +09:00
moons-14 9b9141dd84 lock codex version 2026-08-07 17:30:50 +09:00
moons-14 16e3fda275 ghostty single instance 2026-08-07 14:44:48 +09:00
moons-14 ca36b07839 Display OFF 2026-08-07 06:28:27 +09:00
moons-14 5ebcbb4abf labwc 2026-08-06 17:22:00 +09:00
moons-14 f8fd8a3d99 wallpaper engine 2026-08-06 17:13:02 +09:00
moons-14 16742d2fd7 window overlay 2026-08-05 06:21:26 +09:00
moons-14 15da7affaa window-overview 2026-08-05 05:53:23 +09:00
moons-14 548647fec7 window switch 2026-08-05 05:27:40 +09:00
moons-14 bcbd08c225 wallpaper vicinae 2026-08-05 05:04:22 +09:00
moons-14 a0ae83d24e feat 2026-08-05 04:37:32 +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
97 changed files with 2479 additions and 551 deletions
+1
View File
@@ -30,3 +30,4 @@
!/modules/
!/tests/
!/skills/
+12
View File
@@ -24,6 +24,11 @@ makes any statement here stale, update `AGENTS.md` in the same change.
| `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
@@ -352,6 +357,7 @@ modules/profiles/
│ ├── labwc/
│ └── niri/
├── networking/
│ ├── homelab-cache-client/
│ ├── tailscale-client/
│ └── tailscale-subnet-router/
├── platform/
@@ -362,7 +368,11 @@ modules/profiles/
│ ├── desktop/
│ └── vm/
├── workload/
│ ├── camera/
│ ├── development/
│ ├── game/
│ ├── machine-learning/
│ ├── nix-builder/
│ ├── personal/
│ ├── server/
│ └── remote-access/
@@ -597,6 +607,7 @@ A host registry may use a specification like this:
"security.secrets"
"security.secure-boot"
"security.tpm-storage"
"workload.camera"
"workload.development"
"workload.personal"
];
@@ -619,6 +630,7 @@ A host registry may use a specification like this:
"security.tpm-storage"
"workload.development"
"workload.game"
"workload.machine-learning"
"workload.personal"
];
};
+119
View File
@@ -0,0 +1,119 @@
# iOS Simulator 初期セットアップ
この手順は `m2` の macOS 環境で、stable Xcode と最新の stable iOS Simulator Runtime を使える状態にするためのもの。
## 前提
- `m2` が `workload.development` profile を有効にしていること
- Mac App Store に Apple Account でサインイン済みであること
- dotfiles を最新化していること
Xcode 本体は `applications.xcode` が Mac App Store 版を管理する。Simulator Runtime は Apple が管理する mutable state のため、Nix store には入れず専用 dev shell から導入する。
## 1. macOS 設定を反映する
リポジトリ直下で nix-darwin の設定を反映する。
```bash
sudo darwin-rebuild switch --flake .#m2
```
これにより `/Applications/Xcode.app` に stable Xcode がインストールされる。
Xcode のインストールで Mac App Store の認証エラーになる場合は、App Store を一度開いてサインイン状態を確認してから再実行する。
## 2. iOS Simulator Runtime を導入する
初回セットアップは次の1コマンドで行う。
```bash
nix develop .#ios -c ios-simulator-install
```
`ios-simulator-install` は次を順に実行する。
1. `/Applications/Xcode.app` が存在することを確認
2. `xcode-select` の Developer Directory を stable Xcode に切り替え
3. Xcode の first-launch components を導入
4. 利用可能な新しい hardware support components を確認
5. 選択中の Xcode に対応する最新の iOS Simulator Runtime をダウンロードしてインストール
6. Xcode のバージョンとインストール済み Simulator Runtime を表示
途中で `sudo` の認証を求められる場合がある。
## 3. インストールを確認する
```bash
xcodebuild -version
xcode-select -p
xcrun simctl list runtimes
xcrun simctl list devices available
```
`xcode-select -p` は次を指していること。
```text
/Applications/Xcode.app/Contents/Developer
```
`xcrun simctl list runtimes` に iOS runtime が表示されればセットアップ完了。
## 4. Simulator を起動する
```bash
open -a Simulator
```
Simulator の Device メニューから、インストール済み runtime で利用可能な iPhone を選択する。
## Runtime の更新
Xcode を stable の新しいバージョンへ更新した後は、同じコマンドを再実行する。
```bash
nix develop .#ios -c ios-simulator-install
```
Xcode の選択、first-launch components、hardware support、iOS Simulator Runtime の状態をまとめて更新できる。
## トラブルシューティング
### Xcode が見つからない
次のエラーが出る場合、先に nix-darwin の設定を反映する。
```text
Xcode is not installed at /Applications/Xcode.app.
```
```bash
sudo darwin-rebuild switch --flake .#m2
```
### Simulator Runtime が見えない
まず runtime 一覧を確認する。
```bash
xcrun simctl list runtimes
```
iOS runtime がない場合は再度インストーラーを実行する。
```bash
nix develop .#ios -c ios-simulator-install
```
### Command Line Tools 側を参照している
```bash
xcode-select -p
```
が `/Library/Developer/CommandLineTools` を指している場合でも、`ios-simulator-install` が `/Applications/Xcode.app/Contents/Developer` へ切り替える。
手動で直す場合は次を実行する。
```bash
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
```
Generated
+820 -295
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -60,6 +60,11 @@
inputs.nixpkgs.follows = "nixpkgs";
};
deploy-rs = {
url = "github:serokell/deploy-rs";
inputs.nixpkgs.follows = "nixpkgs";
};
# Disk management
disko = {
url = "github:nix-community/disko";
@@ -91,6 +96,13 @@
url = "github:ilysenko/codex-desktop-linux";
};
codex-session-usage.url = "github:moons-14/codex-session-usage";
skills = {
url = "github:mattpocock/skills";
flake = false;
};
# Index / Search
nix-index-database = {
url = "github:nix-community/nix-index-database";
@@ -104,6 +116,9 @@
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
@@ -1,5 +1,6 @@
{
imports = [
./deploy.nix
./formatter.nix
./git-hooks.nix
./registry.nix
+28
View File
@@ -0,0 +1,28 @@
{
inputs,
self,
...
}:
{
flake.deploy = {
nodes.nix-builder = {
hostname = "nix-builder";
sshUser = "moons";
user = "root";
interactiveSudo = true;
remoteBuild = true;
autoRollback = true;
magicRollback = true;
profiles.system.path = inputs.deploy-rs.lib.x86_64-linux.activate.nixos self.nixosConfigurations.nix-builder;
};
};
perSystem =
{ system, ... }:
{
apps.deploy = inputs.deploy-rs.apps.${system}.default;
checks = inputs.deploy-rs.lib.${system}.deployChecks self.deploy;
};
}
+8 -31
View File
@@ -1,47 +1,20 @@
{
nix-example = {
nix-builder = {
system = "x86_64-linux";
stateVersion = "26.05";
user = "moons";
path = ./nix-example;
path = ./nix-builder;
profiles = [
"base"
"interface.cli"
"networking.tailscale-client"
"platform.vm"
"workload.development"
"workload.nix-builder"
"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";
@@ -91,6 +64,7 @@
"workload.game"
"workload.personal"
];
};
galleria = {
@@ -104,6 +78,7 @@
"interface.cli"
"interface.labwc"
"interface.niri"
"networking.tailscale-client"
"platform.intel-nvidia-desktop"
"security.secrets"
"security.secure-boot"
@@ -111,7 +86,9 @@
"security.fingerprint"
"workload.development"
"workload.game"
"workload.machine-learning"
"workload.personal"
"workload.camera"
];
};
+5
View File
@@ -4,6 +4,11 @@
...
}:
{
# This desktop is permanently connected to AC power.
systemd.user.services.swayidle.Service.Environment = [
"SWAYIDLE_ASSUME_AC=1"
];
services.kanshi = {
enable = true;
+7
View File
@@ -8,6 +8,13 @@
boot.initrd.luks.devices.cryptroot.device =
"/dev/disk/by-partuuid/04295552-cbb8-4511-ac1e-1171ec20f8d1";
# Keep Windows data and recovery partitions out of UDisks-based file
# managers. The shared EFI System Partition stays available as /boot.
services.udev.extraRules = ''
ENV{ID_PART_ENTRY_UUID}=="0480f887-d1f9-489d-b8fe-78549ced1938", ENV{UDISKS_IGNORE}="1"
ENV{ID_PART_ENTRY_UUID}=="6c70041b-3f65-4eb1-8b08-18ed20001877", ENV{UDISKS_IGNORE}="1"
'';
environment.systemPackages = with inputs.browser-previews.packages.${pkgs.system}; [
google-chrome-beta
];
+1
View File
@@ -31,6 +31,7 @@
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"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLwReAiwhXoO34S2+MrvqUhi8IWp4IzUq4OSp3niJdq"
];
environment.systemPackages = with pkgs; [
-3
View File
@@ -1,3 +0,0 @@
{
imports = [ ./hardware-configuration.nix ];
}
+101
View File
@@ -0,0 +1,101 @@
# nix-builder bootstrap
The host configuration can be built before its cache signing secret exists.
Harmonia's socket remains stopped until SOPS installs the signing key at
`/run/secrets/harmonia/signing-key`.
## Proxmox storage layout
The host configuration expects three filesystems. Keep the build scratch space
separate from the store so a large build cannot fill the root filesystem.
| Mount point | Suggested size | Contents |
| -------------------- | -------------- | ------------------------------- |
| `/` | 48 GiB | NixOS and mutable system state |
| `/var/lib/nix-build` | 192 GiB | Disposable build scratch space |
| `/nix/store` | 1 TiB | Fleet closures and binary cache |
The build-server policy starts emergency store GC below 64 GiB free and aims
for 128 GiB free. Persistent roots under `/var/lib/nix-fleet/roots` protect the
latest fleet builds from that GC. It also limits Nix to two concurrent
derivations while allowing each derivation to use every vCPU assigned to the
VM.
For the two dedicated ext4 data filesystems, remove the default root-reserved
blocks once after formatting; keep the root filesystem's reserve intact:
```bash
sudo tune2fs -m 0 /dev/disk/by-label/nix-build
sudo tune2fs -m 0 /dev/disk/by-label/nix-store
```
## Initial deployment
Once the VM is reachable as `moons@nix-builder`, deploy it from the repository:
```bash
nix run .#deploy -- .#nix-builder
```
deploy-rs uses the target's `ssh-ng` store, so the system closure is built on
the builder rather than copied from the laptop. Automatic and magic rollback
remain enabled.
## Add the host SOPS recipient
After the VM has a stable SSH host key, derive its age recipient:
```bash
ssh-keyscan -t ed25519 nix-builder 2>/dev/null | ssh-to-age
```
Add the recipient to `.sops.yaml` and add a creation rule for
`secrets/hosts/nix-builder/*.yaml`. The admin YubiKey recipient should remain in
the same key group for recovery.
## Generate the cache signing key
Run this on a trusted Nix machine, preferably with the temporary files on a
tmpfs:
```bash
nix-store --generate-binary-cache-key \
cache.app.homelabs.run-1 \
harmonia.private \
harmonia.public
```
Create `secrets/hosts/nix-builder/system.yaml` with SOPS and store the complete
contents of `harmonia.private` at `harmonia.signing-key`:
```yaml
harmonia:
signing-key: cache.app.homelabs.run-1:REDACTED
```
Copy the complete contents of `harmonia.public` to
`modules/systems/nix/homelab-cache/public-key`. The private plaintext file must
not be committed or retained.
After committing both encrypted/public files, select
`networking.homelab-cache-client` on each client host.
Redeploy the builder and verify the cache after installing the secret:
```bash
nix run .#deploy -- .#nix-builder
curl --fail http://nix-builder:5000/nix-cache-info
```
## Normal operation
Run `fleet-build` on the builder to build and root every NixOS host, or pass a
list of host names to build only those hosts. Run `fleet-deploy` with the normal
deploy-rs target syntax when additional fleet nodes have been added to
`flake/deploy.nix`:
```bash
fleet-build
fleet-build x1g13 galleria
fleet-deploy .#nix-builder
```
+25
View File
@@ -0,0 +1,25 @@
{
fileSystems."/nix/store" = {
device = "/dev/disk/by-label/nix-store";
fsType = "ext4";
options = [
"noatime"
];
neededForBoot = true;
};
fileSystems."/var/lib/nix-build" = {
device = "/dev/disk/by-label/nix-build";
fsType = "ext4";
options = [
"noatime"
];
};
nix.settings.build-dir = "/var/lib/nix-build";
services.fstrim.enable = true;
}
@@ -1,8 +1,12 @@
# Do not modify this file! It was generated by `nixos-generate-config` and may
# be overwritten by future invocations.
# 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.
{ lib, modulesPath, ... }:
{
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [
"ata_piix"
@@ -17,12 +21,12 @@
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/1b12ab98-2537-4207-a3f4-bb8ba7b53b00";
device = "/dev/disk/by-uuid/49fc2e1c-7909-41cc-ac78-55b4d9a01e62";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/8365-C778";
device = "/dev/disk/by-uuid/40D4-ABBE";
fsType = "vfat";
options = [
"fmask=0077"
+19
View File
@@ -0,0 +1,19 @@
{ lib, ... }:
let
hostSecrets = ../../secrets/hosts/nix-builder/system.yaml;
in
{
imports = [
./filesystem.nix
./hardware-configuration.nix
];
sops.secrets = lib.mkIf (builtins.pathExists hostSecrets) {
"harmonia/signing-key" = {
sopsFile = hostSecrets;
restartUnits = [ "harmonia.service" ];
};
};
networking.firewall.interfaces."tailscale0".allowedTCPPorts = [ 5000 ];
}
-36
View File
@@ -1,36 +0,0 @@
# 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
@@ -1,51 +0,0 @@
{
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";
};
};
}
@@ -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";
}
+30 -2
View File
@@ -1,9 +1,37 @@
_: {
{
config,
lib,
pkgs,
...
}:
let
chrome = pkgs.google-chrome.overrideAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.makeWrapper ];
postFixup = (old.postFixup or "") + ''
wrapProgram $out/bin/google-chrome-stable \
--set LIBVA_DRIVER_NAME nvidia \
--set NVD_BACKEND direct
'';
});
features = [
"MiddleClickAutoscroll"
"AcceleratedVideoDecoder"
"AcceleratedVideoDecodeLinuxGL"
"PlatformHEVCDecoderSupport"
]
++ lib.optional config.my.hardwares.nvidia.enable "VaapiOnNvidiaGPUs";
in
{
programs.google-chrome = {
enable = true;
package = if config.my.hardwares.nvidia.enable then chrome else pkgs.google-chrome;
commandLineArgs = [
"--enable-features=MiddleClickAutoscroll"
"--enable-features=${lib.concatStringsSep "," features}"
"--use-gl=angle"
"--use-angle=gl"
];
};
+1 -6
View File
@@ -1,10 +1,5 @@
{ inputs, ... }:
{
_: {
description = "Codex Desktop for Linux";
includes = [ "applications.codex" ];
imports.nixos = [
inputs.codex-desktop-linux.nixosModules.default
];
}
+3 -11
View File
@@ -4,15 +4,7 @@
...
}:
{
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"
];
};
environment.systemPackages = [
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.chatgpt
];
}
@@ -0,0 +1,52 @@
{
lib,
pkgs,
inputs,
...
}:
let
codexSessionUsage = inputs.codex-session-usage.packages.${pkgs.stdenv.hostPlatform.system}.default;
in
{
home.packages = [ codexSessionUsage ];
xdg.dataFile = {
"vicinae/scripts/codex-session-usage/start" = {
executable = true;
text = ''
#!${lib.getExe pkgs.bash}
# @vicinae.schemaVersion 1
# @vicinae.title Start Codex Session Usage
# @vicinae.description Start the local Codex session usage dashboard
# @vicinae.mode compact
# @vicinae.icon 📊
# @vicinae.argument1 { "type": "text", "placeholder": "Port (optional)", "optional": true }
if [[ -z "$1" ]]; then
exec ${lib.getExe codexSessionUsage} start
fi
if [[ "$1" =~ ^[0-9]+$ ]] && (( 10#$1 >= 1 && 10#$1 <= 65535 )); then
exec ${lib.getExe codexSessionUsage} start --port "$1"
fi
printf '%s\n' 'Port must be an integer between 1 and 65535.' >&2
exit 2
'';
};
"vicinae/scripts/codex-session-usage/stop" = {
executable = true;
text = ''
#!${lib.getExe pkgs.bash}
# @vicinae.schemaVersion 1
# @vicinae.title Stop Codex Session Usage
# @vicinae.description Stop the local Codex session usage dashboard
# @vicinae.mode compact
# @vicinae.icon 📊
exec ${lib.getExe codexSessionUsage} stop
'';
};
};
}
+13
View File
@@ -0,0 +1,13 @@
model = "gpt-5.6-sol"
model_reasoning_effort = "medium"
approval_policy = "on-request"
approvals_reviewer = "auto_review"
sandbox_mode = "workspace-write"
web_search = "cached"
[sandbox_workspace_write]
network_access = false
[projects."/home/moons/dotfiles"]
trust_level = "trusted"
+34 -4
View File
@@ -1,6 +1,36 @@
{ inputs, pkgs, ... }:
{
home.packages = [
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex
];
config,
inputs,
lib,
pkgs,
...
}:
let
configDirectory =
if config.home.preferXdgDirectories then
"${config.xdg.configHome}/codex"
else
"${config.home.homeDirectory}/.codex";
configFile = "${configDirectory}/config.toml";
in
{
programs.codex = {
enable = true;
package = inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex;
skills = {
grilling = inputs.skills + "/skills/productivity/grilling";
};
};
# Keep the repository copy as an initial value. Codex may mutate the live
# file between activations; each Home Manager switch resets it from here.
home.activation.resetCodexConfig = {
after = [ "writeBoundary" ];
before = [ ];
data = ''
${pkgs.coreutils}/bin/mkdir -p ${lib.escapeShellArg configDirectory}
${pkgs.coreutils}/bin/install -m 0600 ${./config.toml} ${lib.escapeShellArg configFile}
'';
};
}
+14 -1
View File
@@ -1,6 +1,15 @@
_: {
{
inputs,
pkgs,
...
}:
let
unstable = inputs.nixpkgs-unstable.legacyPackages.${pkgs.stdenv.hostPlatform.system};
in
{
programs.vesktop = {
enable = true;
package = unstable.vesktop;
settings = {
discordBranch = "stable";
@@ -16,6 +25,10 @@ _: {
openLinksWithElectron = false;
# Keep WebRTC on the public interface selected by the default route.
# Secondary private interfaces can otherwise stall voice at DTLS.
webRTCIPHandlingPolicy = "default_public_interface_only";
spellCheckLanguages = [
"ja-JP"
"en-US"
+7 -2
View File
@@ -1,7 +1,12 @@
{ pkgs, ... }:
{ inputs, pkgs, ... }:
{
services.hazkey.enable = true;
services.hazkey = {
enable = true;
server.package =
inputs.nix-hazkey.packages.${pkgs.stdenv.hostPlatform.system}.hazkey-server.override
{ enableVulkan = true; };
};
i18n.inputMethod = {
enable = true;
+4
View File
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.ffmpeg ];
}
@@ -6,6 +6,7 @@ _: {
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";
+7 -1
View File
@@ -1,6 +1,12 @@
{ inputs, system, ... }: {
programs.ghostty = {
systemd.enable = true;
# Labwc's Close action correctly targets one xdg-toplevel, but Ghostty's
# systemd service runs every window in one GTK single-instance process.
# If that process exits while handling the request, every Ghostty window
# disappears together. Keep each launcher invocation independent instead.
systemd.enable = false;
package = inputs.ghostty.packages.${system}.default;
settings.gtk-single-instance = false;
};
}
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.python314Packages.huggingface-hub ];
}
+8 -10
View File
@@ -1,7 +1,5 @@
{ lib, pkgs, ... }:
let
screenshot = import ./screenshot.nix { inherit pkgs; };
systemctl = lib.getExe' pkgs.systemd "systemctl";
keybind = key: actionAttrs: {
@@ -62,8 +60,6 @@ let
};
in
{
home.packages = [ screenshot ];
wayland.windowManager.labwc = {
enable = true;
# NixOS owns the package so Home Manager only generates the user config.
@@ -106,8 +102,6 @@ in
(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")
@@ -117,9 +111,9 @@ in
(snapToEdge "W-C-Up" "up")
(snapToEdge "W-C-Down" "down")
(confirmAction "W-S-e" "Exit labwc?" "Exit")
(execute "Print" "labwc-screenshot region")
(execute "C-Print" "labwc-screenshot output")
(execute "A-Print" "labwc-screenshot all")
(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")
@@ -127,9 +121,13 @@ in
(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-v" "vicinae vicinae://launch/clipboard/history")
(execute "W-j" "nani-translate-primary")
(execute "W-S-j" "nani-translate-ocr")
(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")
# Function keys (sync with niri modules/applications/niri/home.nix)
(execute "XF86AudioRaiseVolume" "noctalia msg volume-up")
+5 -1
View File
@@ -1,5 +1,9 @@
{
description = "labwc Wayland stacking compositor";
includes = [ "systems.wayland" ];
includes = [
"systems.wayland"
"applications.screenshot"
"applications.wl-find-cursor"
];
}
+21
View File
@@ -1,10 +1,31 @@
{
inputs,
lib,
pkgs,
...
}:
let
unstable = inputs.nixpkgs-unstable.legacyPackages.${pkgs.stdenv.hostPlatform.system};
in
{
programs.labwc = {
enable = true;
package = unstable.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
-50
View File
@@ -1,50 +0,0 @@
{ pkgs }:
pkgs.writeShellApplication {
name = "labwc-screenshot";
runtimeInputs = with pkgs; [
coreutils
grim
slurp
wl-clipboard
xdg-user-dirs
];
text = ''
mode="''${1:-region}"
pictures_dir="$(xdg-user-dir PICTURES)"
if [[ -z "$pictures_dir" ]]; then
pictures_dir="$HOME/Pictures"
fi
output_dir="$pictures_dir/Screenshots"
output_file="$output_dir/Screenshot from $(date '+%Y-%m-%d %H-%M-%S').png"
mkdir -p "$output_dir"
case "$mode" in
region)
geometry="$(slurp)" || exit 0
grim -g "$geometry" "$output_file"
;;
output)
geometry="$(slurp -o)" || exit 0
grim -g "$geometry" "$output_file"
;;
all)
grim "$output_file"
;;
*)
echo "Unknown screenshot mode: $mode" >&2
exit 2
;;
esac
wl-copy --type image/png < "$output_file"
'';
}
+6
View File
@@ -0,0 +1,6 @@
{
homebrew = {
enable = true;
casks = [ "nani" ];
};
}
+78
View File
@@ -0,0 +1,78 @@
{ pkgs, ... }:
let
tessdataBest = pkgs.runCommand "tessdata-best-jpn-eng-chi-sim" { } ''
mkdir -p "$out"
ln -s ${
pkgs.fetchurl {
url = "https://github.com/tesseract-ocr/tessdata_best/raw/main/jpn.traineddata";
hash = "sha256-Nr35rII/WRHmJMMNBVPokLirx8MaZbPvFNqUNljEC3k=";
}
} "$out/jpn.traineddata"
ln -s ${
pkgs.fetchurl {
url = "https://github.com/tesseract-ocr/tessdata_best/raw/main/eng.traineddata";
hash = "sha256-goCu0Hgv4nJXpo6hD+fvMkyg+Nhb0v0UXRwrVgvLZro=";
}
} "$out/eng.traineddata"
ln -s ${
pkgs.fetchurl {
url = "https://github.com/tesseract-ocr/tessdata_best/raw/main/chi_sim.traineddata";
hash = "sha256-T+8tEwbI6HYW1NPkxsZ/r11EvjNCKQz48vD246p+c1s=";
}
} "$out/chi_sim.traineddata"
'';
tesseract = pkgs.tesseract5.override {
tessdata = tessdataBest;
};
naniTranslatePrimary = pkgs.writeShellApplication {
name = "nani-translate-primary";
runtimeInputs = with pkgs; [
jq
wl-clipboard
xdg-utils
];
text = ''
selected_text="$(wl-paste --primary --no-newline)" || exit 0
[ -n "$selected_text" ] || exit 0
encoded_text="$(printf '%s' "$selected_text" | jq -sRr @uri)"
exec xdg-open "naniapp://translate?source=$encoded_text"
'';
};
naniTranslateOcr = pkgs.writeShellApplication {
name = "nani-translate-ocr";
runtimeInputs = with pkgs; [
grim
jq
slurp
tesseract
xdg-utils
];
text = ''
geometry="$(slurp)" || exit 0
captured_text="$(
grim -g "$geometry" - \
| tesseract stdin stdout -l jpn+eng+chi_sim --oem 1 --psm 6
)"
[ -n "$captured_text" ] || exit 0
encoded_text="$(printf '%s' "$captured_text" | jq -sRr @uri)"
exec xdg-open "naniapp://translate?source=$encoded_text"
'';
};
in
{
programs.naniTranslateLinux.enable = true;
xdg.mimeApps.defaultApplications."x-scheme-handler/naniapp" = "nani.desktop";
home.packages = [
naniTranslatePrimary
naniTranslateOcr
];
}
+8
View File
@@ -0,0 +1,8 @@
{ inputs, ... }:
{
description = "Nani Translate application";
imports.home = [
inputs.nani-translate-linux.homeManagerModules.default
];
}
+24 -9
View File
@@ -25,6 +25,7 @@ in
systemd.user.sessionVariables.NAUTILUS_4_EXTENSION_DIR = nautilusExtensionDir;
xdg.dataFile."nautilus-python/extensions/open-in-editor.py".text = ''
import os
import subprocess
from gi.repository import GObject, Nautilus
@@ -75,6 +76,12 @@ in
return paths
@staticmethod
def get_working_directory(paths):
path = paths[0]
return path if os.path.isdir(path) else os.path.dirname(path)
@staticmethod
def launch(_item, command):
subprocess.Popen(
@@ -101,6 +108,22 @@ in
items.append(item)
item = Nautilus.MenuItem(
name=f"OpenInEditor::{context}::ghostty",
label="Ghostty で開く",
)
item.connect(
"activate",
self.launch,
[
"${lib.getExe pkgs.ghostty}",
f"--working-directory={self.get_working_directory(paths)}",
],
)
items.append(item)
return items
def get_file_items(self, files):
@@ -139,14 +162,6 @@ in
"file://${config.home.homeDirectory}/Desktop Desktop"
"file://${config.home.homeDirectory}/Pictures Pictures"
"file://${config.home.homeDirectory}/Downloads Downloads"
"file://${config.home.homeDirectory}/projects projects"
"file://${config.home.homeDirectory}/Projects Projects"
];
home.activation.createProjectsDirectory = {
after = [ "writeBoundary" ];
before = [ ];
data = ''
$DRY_RUN_CMD mkdir -p "$HOME/projects"
'';
};
}
+34
View File
@@ -5,4 +5,38 @@
NH_FLAKE = "${config.home.homeDirectory}/dotfiles";
NH_SHOW_ACTIVATION_LOGS = "1";
};
programs.zsh.initContent = ''
export SUDO_PROMPT=$'\a[sudo] authenticate for %u: '
nh() {
local notify=false
local argument
case "$1:$2" in
os:switch | os:build) notify=true ;;
esac
for argument in "$@"; do
if [[ "$argument" == "--update" ]]; then
notify=true
break
fi
done
command nh "$@"
local status=$?
if [[ "$notify" == true ]]; then
printf '\a'
if ((status == 0)); then
print -P "%F{green}nh completed%f"
else
print -P "%F{red}nh failed (exit $status)%f"
fi
fi
return "$status"
}
'';
}
@@ -1,3 +1,4 @@
# niri のデフォルトキーバインド。編集しないこと。
{
"Mod+Shift+Slash".action.show-hotkey-overlay = { };
+58 -1
View File
@@ -7,6 +7,8 @@ in
binds = keybindings // {
# niri window or focus move
"Mod+MouseMiddle".action.toggle-window-floating = [ ];
"Mod+Shift+Left" = {
action.focus-monitor-left = [ ];
hotkey-overlay.title = "Focus Monitor Left";
@@ -24,6 +26,18 @@ in
hotkey-overlay.title = "Focus Monitor Down";
};
# Use the modifier combinations in the opposite direction from Niri's defaults.
"Mod+WheelScrollDown".action.focus-column-right = [ ];
"Mod+WheelScrollUp".action.focus-column-left = [ ];
"Mod+Shift+WheelScrollDown" = {
cooldown-ms = 150;
action.focus-workspace-down = [ ];
};
"Mod+Shift+WheelScrollUp" = {
cooldown-ms = 150;
action.focus-workspace-up = [ ];
};
"Mod+Shift+Ctrl+Left" = {
action.move-window-to-monitor-left = [ ];
hotkey-overlay.title = "Move Window to Monitor Left";
@@ -41,6 +55,19 @@ in
hotkey-overlay.title = "Move Window to Monitor Down";
};
"Print".action.spawn = [
"screenshot"
"region"
];
"Ctrl+Print".action.spawn = [
"screenshot"
"output"
];
"Alt+Print".action.spawn = [
"screenshot"
"all"
];
# spawn applications (sync with labwc modules/applications/labwc/home.nix)
"Mod+T" = {
action.spawn = "ghostty";
@@ -79,10 +106,28 @@ in
"Mod+V" = {
action.spawn = [
"vicinae"
"vicinae://launch/clipboard/history?toggle=true"
"vicinae://launch/clipboard/history"
];
hotkey-overlay.title = "Clipboard History";
};
"Mod+J" = {
repeat = false;
action.spawn = [ "nani-translate-primary" ];
hotkey-overlay.title = "Translate Primary Selection";
};
"Mod+Shift+J" = {
repeat = false;
action.spawn = [ "nani-translate-ocr" ];
hotkey-overlay.title = "OCR and Translate with Nani";
};
"Mod+Ctrl+J" = {
repeat = false;
action.spawn = [
(lib.getExe' pkgs.xdg-utils "xdg-open")
"naniapp://translate"
];
hotkey-overlay.title = "Open Nani Translate";
};
"Mod+Space" = {
action.spawn = [
"ghostty"
@@ -94,6 +139,18 @@ in
action.spawn = "wdisplays";
hotkey-overlay.title = "Display Settings: wdisplays";
};
"Mod+Z" = {
action.spawn = [
"wl-find-cursor"
"-c"
"0xCCFF453A"
"-s"
"160"
"-d"
"1200"
];
hotkey-overlay.title = "Find Cursor";
};
# Function keys (sync with labwc modules/applications/labwc/home.nix)
"XF86AudioRaiseVolume".action.spawn = [
+5 -1
View File
@@ -2,7 +2,11 @@
{
description = "niri Wayland compositor";
includes = [ "systems.wayland" ];
includes = [
"systems.wayland"
"applications.screenshot"
"applications.wl-find-cursor"
];
imports = {
nixos = [
+3
View File
@@ -0,0 +1,3 @@
{
description = "Fleet build and deploy command-line tools";
}
+63
View File
@@ -0,0 +1,63 @@
{
inputs,
pkgs,
primaryUser,
...
}:
let
system = pkgs.stdenv.hostPlatform.system;
deployRs = inputs.deploy-rs.packages.${system}.default;
fleetBuild = pkgs.writeShellApplication {
name = "fleet-build";
runtimeInputs = [
pkgs.jq
pkgs.nix
];
text = ''
flake_ref="''${FLAKE:-/home/${primaryUser}/dotfiles}"
if (( $# == 0 )); then
# Keep this pipeline inside command substitution so pipefail and
# writeShellApplication's errexit propagate evaluation failures.
host_lines="$(
nix eval --json "$flake_ref#nixosConfigurations" \
--apply 'configs: builtins.attrNames configs' |
jq -r '.[] | select(. != "installer")'
)"
if [[ -z "$host_lines" ]]; then
echo "No deployable NixOS hosts found in $flake_ref" >&2
exit 1
fi
mapfile -t hosts <<< "$host_lines"
else
hosts=("$@")
fi
for host in "''${hosts[@]}"; do
nix build \
--out-link "/var/lib/nix-fleet/roots/build/$host" \
"$flake_ref#nixosConfigurations.$host.config.system.build.toplevel"
done
'';
};
fleetDeploy = pkgs.writeShellApplication {
name = "fleet-deploy";
runtimeInputs = [ deployRs ];
text = ''
cd "''${FLAKE:-/home/${primaryUser}/dotfiles}" || exit 1
exec deploy \
--keep-result \
--result-path /var/lib/nix-fleet/roots/deploy \
"$@"
'';
};
in
{
environment.systemPackages = [
fleetBuild
fleetDeploy
];
}
+3 -3
View File
@@ -1,6 +1,6 @@
{ lib, pkgs }:
let
version = "0.46.0";
version = "0.56.0";
architecture =
if pkgs.stdenv.hostPlatform.isx86_64 then
"x86_64"
@@ -10,8 +10,8 @@ let
throw "CodexBar CLI is only packaged for x86_64-linux and aarch64-linux";
hash =
{
x86_64 = "sha256-yMrOpu1WIv2sGVgvY9qf2XCoX2AXMSqR2b8bQDRU6os=";
aarch64 = "sha256-pCp3NOlxFN6zeH67W04WGSPbUae+ktzR5vEunWqu00g=";
x86_64 = "sha256-hzCnAyiizm8ZDfE1ONEP6S2Pdnskclm+XdDBBhEYVqE=";
aarch64 = "sha256-vfcgDEFpORPymcRYmlqvsjhV4pU7lNC8Vd9FDPI9UjM=";
}
.${architecture};
in
+7 -3
View File
@@ -1,6 +1,7 @@
{
lib,
pkgs,
inputs,
...
}:
let
@@ -41,6 +42,8 @@ in
programs.noctalia = {
enable = true;
package = inputs.noctalia.packages.${pkgs.stdenv.hostPlatform.system}.default;
systemd.enable = true;
settings = {
@@ -154,7 +157,7 @@ in
network-connection = {
type = "custom_button";
glyph = "access-point";
actions.left = "nm-connection-editor";
actions.left = "exec nm-connection-editor";
};
tray = {
type = "tray";
@@ -171,10 +174,11 @@ in
"org.gnome.Nautilus"
"org.gnome.TextEditor"
"com.mitchellh.ghostty"
"thunderbird"
"google-chrome"
"code"
"dev.zed.Zed"
"codex-desktop.desktop"
"chatgpt"
"vesktop"
];
show_all_outputs = true;
@@ -193,7 +197,7 @@ in
};
wallpaper = {
enabled = true;
enabled = lib.mkDefault true;
directory = "~/.wallpapers";
fill_mode = "crop";
automation = {
+14
View File
@@ -2,5 +2,19 @@
{
home.packages = [
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.opencode
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.oh-my-opencode
];
home.file.".omo/omo.jsonc".text = builtins.toJSON {
agents = {
sisyphus.model = "openai/gpt-5.6-sol";
hephaestus.model = "openai/gpt-5.6-terra";
prometheus.model = "openai/gpt-5.6-terra";
oracle.model = "openai/gpt-5.6-terra";
momus.model = "openai/gpt-5.6-terra";
librarian.model = "openai/gpt-5.6-luna";
explore.model = "openai/gpt-5.6-luna";
atlas.model = "openai/gpt-5.6-luna";
};
};
}
+53
View File
@@ -0,0 +1,53 @@
{ pkgs, ... }:
{
home.packages = [
(pkgs.writeShellApplication {
name = "screenshot";
runtimeInputs = with pkgs; [
coreutils
grim
slurp
wl-clipboard
xdg-user-dirs
];
text = ''
mode="''${1:-region}"
pictures_dir="$(xdg-user-dir PICTURES)"
if [[ -z "$pictures_dir" ]]; then
pictures_dir="$HOME/Pictures"
fi
output_dir="$pictures_dir/Screenshots"
output_file="$output_dir/Screenshot from $(date '+%Y-%m-%d %H-%M-%S').png"
mkdir -p "$output_dir"
case "$mode" in
region)
geometry="$(slurp)" || exit 0
grim -g "$geometry" "$output_file"
;;
output)
geometry="$(slurp -o)" || exit 0
grim -g "$geometry" "$output_file"
;;
all)
grim "$output_file"
;;
*)
echo "Unknown screenshot mode: $mode" >&2
exit 2
;;
esac
wl-copy --type image/png < "$output_file"
'';
})
];
}
+3
View File
@@ -0,0 +1,3 @@
{
description = "Wayland screenshot command";
}
+3
View File
@@ -24,6 +24,9 @@ lib.mkMerge [
};
extraConfig = ''
Match exec "test -n \"$SSH_AUTH_SOCK\" && test -S \"$SSH_AUTH_SOCK\""
IdentityAgent $SSH_AUTH_SOCK
Match exec "test -S %d/.1password/agent.sock"
IdentityAgent %d/.1password/agent.sock
'';
@@ -0,0 +1,24 @@
{ pkgs, ... }:
{
programs.thunderbird = {
enable = true;
package = pkgs.thunderbird;
languagePacks = [ "ja" ];
policies = {
DisableTelemetry = true;
DisableAppUpdate = true;
InAppNotification_Disabled = true;
};
settings = {
"mailnews.start_page.enabled" = false;
"mail.shell.checkDefaultClient" = false;
};
profiles.default = {
isDefault = true;
};
};
}
@@ -0,0 +1,10 @@
_: {
xdg.mimeApps = {
enable = true;
defaultApplications = {
"x-scheme-handler/mailto" = "thunderbird.desktop";
"message/rfc822" = "thunderbird.desktop";
};
};
}
@@ -10,6 +10,8 @@ in
programs.vicinae = {
enable = true;
enableChromeIntegration = false;
settings = {
font.size = 11;
close_on_focus_loss = true;
@@ -8,9 +8,6 @@ let
in
{
programs.vicinae = {
package = pkgs.vicinae;
systemd = {
enable = true;
autoStart = true;
@@ -0,0 +1,7 @@
{ inputs, pkgs, ... }:
let
unstable = inputs.nixpkgs-unstable.legacyPackages.${pkgs.stdenv.hostPlatform.system};
in
{
home.packages = [ unstable.wl-find-cursor ];
}
@@ -0,0 +1,3 @@
{
description = "Wayland cursor locator";
}
+8
View File
@@ -0,0 +1,8 @@
{
homebrew = {
enable = true;
masApps = {
Xcode = 497799835;
};
};
}
+4
View File
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.yt-dlp ];
}
+6
View File
@@ -4,8 +4,11 @@
extensions = [
"catppuccin"
"nix"
"dockerfile"
];
mutableUserSettings = false;
mutableUserKeymaps = false;
userKeymaps = import ./keymaps.nix;
userSettings = {
agent = {
@@ -36,6 +39,9 @@
light = "Catppuccin Latte";
dark = "Catppuccin Mocha";
};
edit_predictions = {
provider = "copilot";
};
};
};
}
+38
View File
@@ -0,0 +1,38 @@
[
{
context = "Editor && vim_mode == normal";
bindings = {
# This selected native-equivalent subset preserves Zed's Vim defaults for
# K, gd, grr, gri, gra, gO, ]d, [d, zM, zR, [b, and ]b.
"g r t" = "editor::GoToTypeDefinition";
"space space" = "pane::AlternateFile";
"space r n" = "editor::Rename";
"space c a" = "editor::ToggleCodeActions";
"space c f" = "editor::Format";
"space d l" = "editor::Hover";
"space e" = "project_panel::Toggle";
"space t t" = "terminal_panel::Toggle";
"space b c" = "pane::CloseActiveItem";
"space f f" = "file_finder::Toggle";
"space f g" = "pane::DeploySearch";
"space f b" = "tab_switcher::ToggleAll";
"space f s" = "outline::Toggle";
"space f shift-s" = "project_symbols::Toggle";
"space x x" = "diagnostics::Deploy";
};
}
{
# Keep normal-mode editor navigation out of terminals and other panels.
context = "Editor && vim_mode == normal && !menu";
bindings = {
"ctrl-h" = "workspace::ActivatePaneLeft";
"ctrl-j" = "workspace::ActivatePaneDown";
"ctrl-k" = "workspace::ActivatePaneUp";
"ctrl-l" = "workspace::ActivatePaneRight";
"ctrl-left" = "workspace::ActivatePaneLeft";
"ctrl-down" = "workspace::ActivatePaneDown";
"ctrl-up" = "workspace::ActivatePaneUp";
"ctrl-right" = "workspace::ActivatePaneRight";
};
}
]
+1 -4
View File
@@ -5,10 +5,7 @@
modesetting.enable = true;
open = true;
powerManagement = {
enable = true;
kernelSuspendNotifier = true;
};
powerManagement.enable = true;
moduleParams.nvidia.NVreg_TemporaryFilePath = "/var/tmp";
+19
View File
@@ -38,9 +38,13 @@ required on every supported host.
| `platform.vm` | UEFI QEMU NixOS guest with NFS client support |
| `workload.development` | NixOS, macOS with Home Manager |
| `workload.game` | NixOS, macOS |
| `workload.machine-learning` | NixOS with Home Manager |
| `workload.nix-builder` | NixOS central build and binary-cache VM |
| `workload.personal` | NixOS, macOS with Home Manager |
| `workload.remote-access` | NixOS, macOS |
| `workload.camera` | NixOS |
| `workload.server` | NixOS, macOS with Home Manager |
| `networking.homelab-cache-client` | NixOS, macOS with access to nix-builder |
| `networking.tailscale-client` | NixOS, macOS |
| `networking.tailscale-subnet-router` | NixOS |
| `security.fingerprint` | NixOS, macOS |
@@ -58,11 +62,26 @@ selects labwc. A daily-use macOS development machine can combine
`interface.macos`, `workload.development`, and `workload.personal`. Hardware
support does not implicitly select an interface or workload.
`workload.machine-learning` provides the Hugging Face Hub CLI for hosts used
to download and publish machine learning models and datasets.
`workload.nix-builder` provides the central build policy, persistent fleet GC
roots, deploy-rs tooling, SOPS integration, and Harmonia binary cache. Network
reachability and remote shell access remain independent host selections.
`networking.homelab-cache-client` adds the internal Harmonia substituter and
its trusted public key. It requires the public key generated during
`hosts/nix-builder/README.md` bootstrap.
`workload.personal` provides Pear Desktop on both NixOS and macOS. Home Manager
enables performance improvements, synced lyrics, tracker blocking, the album
color theme, and custom output-device selection while preserving user-owned
settings such as the selected device.
`workload.personal` provides ActivityWatch for local activity tracking. On
NixOS, its server and Wayland-compatible watcher run as Home Manager user
services. On macOS, the Homebrew cask starts at login.
On NixOS, `workload.game` provides Steam with Valve Proton and Proton-GE,
Protontricks, Wayland-compatible Steam Input, GameMode, Gamescope, and MangoHud.
Enabling Steam also activates the 32-bit graphics and PipeWire support required
@@ -2,8 +2,6 @@
{
environment.systemPackages = with pkgs; [
alacritty
grim
slurp
wf-recorder
];
}
@@ -1,7 +1,9 @@
{ pkgs, ... }: {
{ pkgs, ... }:
{
home.packages = [
pkgs.playerctl
];
xdg.userDirs = {
enable = true;
createDirectories = true;
@@ -13,5 +15,6 @@
publicShare = "$HOME/Public";
templates = "$HOME/Templates";
videos = "$HOME/Videos";
projects = "$HOME/Projects";
};
}
@@ -12,12 +12,14 @@
"applications.gtk"
"applications.loupe"
"applications.nautilus"
"applications.nani"
"applications.papers"
"applications.qalculate-gtk"
"applications.resources"
"applications.vlc"
"hardwares.graphics"
"services.gvfs"
"services.evremap"
"services.polkit-gnome"
"systems.audio"
"systems.fonts"
@@ -2,8 +2,6 @@
{
environment.systemPackages = with pkgs; [
alacritty
grim
slurp
wf-recorder
];
}
@@ -0,0 +1,5 @@
{
description = "Use the homelab Harmonia binary cache";
includes = [ "systems.nix.homelab-cache" ];
}
@@ -3,6 +3,7 @@
# every system sleep path; screen blanking while locked is handled by
# services.swayidle in the graphical session.
systemd.sleep.settings.Sleep = {
AllowSuspend = true;
AllowHibernation = false;
AllowHybridSleep = false;
AllowSuspendThenHibernate = false;
@@ -0,0 +1,6 @@
{
sops.secrets."users/moons/hashedPassword" = {
sopsFile = ../../../secrets/common/system.yaml;
neededForUsers = true;
};
}
@@ -0,0 +1,5 @@
{
description = "Camera capture and virtual camera tools";
includes = [ "systems.boot.v4l2loopback" ];
}
@@ -0,0 +1,8 @@
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
gphoto2
ffmpeg
v4l-utils
];
}
@@ -4,7 +4,7 @@
bind
bun
nil
python312
python314
uv
];
}
@@ -6,6 +6,7 @@
"applications.claude"
"applications.codex"
"applications.codex-desktop"
"applications.codex-session-usage"
"applications.docker"
"applications.drawio"
"applications.ghostty"
@@ -13,6 +14,7 @@
"applications.java"
"applications.opencode"
"applications.vscode"
"applications.xcode"
"applications.zed"
];
}
@@ -0,0 +1,5 @@
{
description = "Machine learning tools";
includes = [ "applications.huggingface-cli" ];
}
@@ -0,0 +1,10 @@
{
description = "Central Nix builder, deploy controller, and binary cache";
includes = [
"applications.nix-fleet"
"services.harmonia"
"systems.nix.build-server"
"systems.sops"
];
}
@@ -3,13 +3,18 @@
includes = [
"applications.1password"
"applications.activitywatch"
"applications.chrome"
"applications.discord"
"applications.ffmpeg"
"applications.gnome-text-editor"
"applications.kde"
"applications.moonlight"
"applications.slack"
"applications.zoom"
"applications.obs"
"applications.nani"
"applications.thunderbird"
"applications.yt-dlp"
];
}
+20
View File
@@ -0,0 +1,20 @@
{
services.evremap = {
enable = true;
settings = {
device_name = "VXE VXE Mouse 1K Dongle Mouse";
remap = [
{
input = [ "BTN_SIDE" ];
output = [ "KEY_LEFTMETA" ];
}
{
input = [ "BTN_EXTRA" ];
output = [ "BTN_SIDE" ];
}
];
};
};
}
+3
View File
@@ -0,0 +1,3 @@
{
description = "Harmonia binary cache backed by the local Nix store";
}
+19
View File
@@ -0,0 +1,19 @@
let
signingKeyPath = "/run/secrets/harmonia/signing-key";
in
{
services.harmonia.cache = {
enable = true;
signKeyPaths = [ signingKeyPath ];
settings = {
bind = "0.0.0.0:5000";
priority = 30;
};
};
# Keep activation usable while the host-specific SOPS secret is bootstrapped.
# Once the secret exists, starting the socket also starts Harmonia on demand.
systemd.sockets.harmonia.unitConfig.ConditionPathExists = signingKeyPath;
systemd.services.harmonia.unitConfig.ConditionPathExists = signingKeyPath;
}
+2
View File
@@ -10,6 +10,8 @@ let
brightnessState = "$XDG_RUNTIME_DIR/swayidle-brightness";
onBattery = pkgs.writeShellScript "swayidle-on-battery" ''
[ "''${SWAYIDLE_ASSUME_AC:-0}" = 1 ] && exit 1
for supply in /sys/class/power_supply/*; do
[ -f "$supply/type" ] || continue
[ "$(< "$supply/type")" = "Battery" ] || continue
+1 -1
View File
@@ -1,6 +1,6 @@
{ pkgs, lib, ... }:
{
boot.loader.systemd-boot.configurationLimit = lib.mkDefault 8;
boot.kernelPackages = pkgs.linuxPackages_latest;
boot.kernelPackages = pkgs.linuxPackages;
programs.nix-ld.enable = true;
}
@@ -0,0 +1,10 @@
{ config, ... }:
{
boot = {
extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ];
kernelModules = [ "v4l2loopback" ];
extraModprobeConfig = ''
options v4l2loopback devices=1 video_nr=42 card_label="LUMIX S9" exclusive_caps=1
'';
};
}
+92 -6
View File
@@ -1,12 +1,98 @@
{ lib, ... }:
{
networking.nameservers = lib.mkDefault [
"1.1.1.1"
"1.0.0.1"
let
dohPort = 5300;
dohLocalUpstream = "127.0.0.1#${toString dohPort}";
internalDns = [
"10.50.80.53"
"10.50.80.54"
# "fd00:50:80::53"
# "fd00:50:80::54"
];
services.resolved.enable = lib.mkDefault false;
security.pki.certificateFiles = [ ./root_ca.crt ];
internalZones = [
"app.homelabs.run"
];
internalDnsServers = lib.concatMap (zone: map (dns: "/${zone}/${dns}") internalDns) internalZones;
in
{
services.resolved.enable = false;
networking.networkmanager.dns = "none";
services.dnscrypt-proxy = {
enable = true;
upstreamDefaults = true;
settings = {
listen_addresses = [
"127.0.0.1:${toString dohPort}"
];
server_names = [
"cloudflare"
"cloudflare-ipv6"
];
ipv4_servers = true;
ipv6_servers = true;
dnscrypt_servers = false;
doh_servers = true;
odoh_servers = false;
cache = false;
block_ipv6 = false;
ignore_system_dns = true;
bootstrap_resolvers = [
"9.9.9.11:53"
"149.112.112.11:53"
"[2620:fe::11]:53"
"[2620:fe::fe:11]:53"
];
netprobe_address = "1.1.1.1:443";
timeout = 5000;
keepalive = 30;
};
};
services.dnsmasq = {
enable = true;
resolveLocalQueries = true;
settings = {
no-resolv = true;
local-service = "host";
server = [ dohLocalUpstream ] ++ internalDnsServers;
all-servers = true;
cache-size = 10000;
dns-loop-detect = true;
domain-needed = true;
bogus-priv = true;
};
};
systemd.services.dnsmasq = {
wants = [ "dnscrypt-proxy.service" ];
after = [ "dnscrypt-proxy.service" ];
};
security.pki.certificateFiles = [
./root_ca.crt
];
}
@@ -0,0 +1,3 @@
{
description = "Central Nix build server policy and persistent fleet roots";
}
@@ -0,0 +1,33 @@
{ primaryUser, ... }:
let
GiB = 1024 * 1024 * 1024;
in
{
nix = {
nrBuildUsers = 64;
settings = {
# Limit concurrent derivations so build scratch and memory usage remain
# bounded. Each derivation may still use every vCPU exposed to the VM.
max-jobs = 2;
cores = 0;
# Keep enough room for large desktop, browser, and CUDA closures.
min-free = 64 * GiB;
max-free = 128 * GiB;
};
};
systemd.services.nix-daemon.serviceConfig = {
MemoryAccounting = true;
MemoryMax = "90%";
OOMScoreAdjust = 500;
};
systemd.tmpfiles.rules = [
"d /var/lib/nix-fleet 0750 ${primaryUser} users - -"
"d /var/lib/nix-fleet/roots 0750 ${primaryUser} users - -"
"d /var/lib/nix-fleet/roots/build 0750 ${primaryUser} users - -"
"d /var/lib/nix-fleet/roots/deploy 0750 ${primaryUser} users - -"
];
}
+6
View File
@@ -7,14 +7,18 @@
"flakes"
];
connect-timeout = 10;
extra-substituters = [
"https://cache.nixos.org"
"https://nix-community.cachix.org"
"https://moons-dotfiles.cachix.org"
"https://noctalia.cachix.org"
"https://vicinae.cachix.org"
"https://ghostty.cachix.org"
"https://cache.numtide.com"
"https://codex-desktop-linux.cachix.org"
"https://cuda-maintainers.cachix.org"
];
extra-trusted-public-keys = [
@@ -23,8 +27,10 @@
"moons-dotfiles.cachix.org-1:WHoroKiNScG2/dpxHHL1I0qVmvuQhJbEAP+DS2j9Rr0="
"noctalia.cachix.org-1:pCOR47nnMEo5thcxNDtzWpOxNFQsBRglJzxWPp3dkU4="
"vicinae.cachix.org-1:1kDrfienkGHPYbkpNj1mWTr7Fm1+zcenzgTizIcI3oc="
"ghostty.cachix.org-1:QB389yTa6gTyneehvqG58y0WnHjQOqgnA+wBnpWWxns="
"niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g="
"codex-desktop-linux.cachix.org-1:nX/xy6AdK9hQE24A8ALGjkCKj2ObFmcnemiL5Cid4nk="
"cuda-maintainers.cachix.org-1:0dq3bujKpuEPMCX6U4WylrUDZ9JyUG0VpVZa7CNfq5E="
];
};
}
@@ -0,0 +1,22 @@
{ lib, ... }:
let
publicKeyFile = ./public-key;
hasPublicKey = builtins.pathExists publicKeyFile;
publicKey = if hasPublicKey then lib.removeSuffix "\n" (builtins.readFile publicKeyFile) else "";
in
{
assertions = [
{
assertion = hasPublicKey;
message = ''
systems.nix.homelab-cache requires
modules/systems/nix/homelab-cache/public-key
'';
}
];
nix.settings = lib.mkIf hasPublicKey {
extra-substituters = [ "http://nix-builder:5000" ];
extra-trusted-public-keys = [ publicKey ];
};
}
@@ -0,0 +1,3 @@
{
description = "Homelab Harmonia binary-cache client settings";
}
-5
View File
@@ -1,8 +1,3 @@
{
services.pcscd.enable = true;
sops.secrets."users/moons/hashedPassword" = {
sopsFile = ../../../secrets/common/system.yaml;
neededForUsers = true;
};
}
+1
View File
@@ -2,5 +2,6 @@
imports = [
./dotnix.nix
./android.nix
./ios.nix
];
}
+48
View File
@@ -0,0 +1,48 @@
_: {
perSystem =
{ pkgs, ... }:
let
installIosSimulator = pkgs.writeShellApplication {
name = "ios-simulator-install";
text = ''
if [[ "$(/usr/bin/uname -s)" != "Darwin" ]]; then
echo "ios-simulator-install is only supported on macOS." >&2
exit 1
fi
xcode_app="/Applications/Xcode.app"
developer_dir="$xcode_app/Contents/Developer"
if [[ ! -d "$developer_dir" ]]; then
echo "Xcode is not installed at $xcode_app." >&2
echo "Apply the m2 nix-darwin configuration first." >&2
exit 1
fi
current_developer_dir="$(/usr/bin/xcode-select -p 2>/dev/null || true)"
if [[ "$current_developer_dir" != "$developer_dir" ]]; then
echo "Selecting stable Xcode..."
/usr/bin/sudo /usr/bin/xcode-select --switch "$developer_dir"
fi
echo "Installing Xcode first-launch components..."
/usr/bin/sudo /usr/bin/xcodebuild -runFirstLaunch
echo "Checking for newer hardware support..."
/usr/bin/xcodebuild -runFirstLaunch -checkForNewerComponents
echo "Downloading and installing the latest iOS Simulator runtime..."
/usr/bin/xcodebuild -downloadPlatform iOS
echo
/usr/bin/xcodebuild -version
/usr/bin/xcrun simctl list runtimes
'';
};
in
{
devShells.ios = pkgs.mkShell {
packages = [ installIosSimulator ];
};
};
}
+137
View File
@@ -0,0 +1,137 @@
---
name: add-application-or-service
description: Add, install, configure, or enable an application or long-running service in this NixOS, nix-darwin, and Home Manager flake while preserving its Registry architecture and quality bar. Use for new GUI or CLI applications, packages, daemons, background services, application-service pairs, cross-platform installations, profile adoption, or substantial extensions to an existing application or service unit.
---
# Add Application or Service
Add the smallest complete Registry unit change that has a clear owner, an
explicit dependency path, and evidence that every affected host class
evaluates. Treat `AGENTS.md` as the authoritative repository contract; never
replace it with generic Nix conventions.
## Follow the workflow
### 1. Establish the baseline
1. Read `AGENTS.md` completely before editing.
2. Run `git status --short`. Preserve all pre-existing user changes and identify
which later diffs belong to this task.
3. Translate the request into observable outcomes: package or program, desired
configuration, supported host classes, required daemon or permissions, and
the profile or user intent that should select it.
4. Inspect the nearest existing units, relevant profiles, `hosts/default.nix`,
and Registry implementation. Prefer repository evidence over memory.
5. Verify current package names, module options, external module exports, and
Homebrew cask names from the locked inputs or authoritative upstream
documentation. Do not guess an option path.
6. Read [references/review-checklist.md](references/review-checklist.md) before
choosing files or dependencies.
### 2. Choose ownership before code
Classify each concern independently:
- Put the user-facing program and its settings in
`modules/applications/<name>/`.
- Put a daemon, long-running process, firewall rule, permission, or user/group
membership in `modules/services/<name>/`.
- Split an application and independently meaningful daemon into two units.
Let the application include the service only when the service is a technical
requirement of that application.
- Put adoption of otherwise independent units in the narrowest coherent
`modules/profiles/` composition.
- Use another documented owner when the request is actually a system,
hardware, user, overlay, or host concern. Do not force it into an application
or service directory merely because this skill was invoked.
Choose only the reserved fragments that contain real configuration. Use
`common.nix`, `nixos.nix`, and `darwin.nix` for system-side configuration; use
`home.nix` or `home/{common,nixos,darwin}.nix` for Home Manager. Use `meta.nix`
only for description, fully qualified `includes`, and external module imports.
Before editing, formulate a short implementation contract containing:
- the unit ID and owner;
- each file to create or change and why;
- technical dependencies versus profile-level choices;
- supported and affected host classes;
- the evaluations or builds that will prove the change.
Rework the design if an ordinary addition appears to require Registry changes,
new global `specialArgs`, `_module.args`, direct host selection, or an overlay.
Use those mechanisms only with concrete evidence that the documented extension
points cannot express the requirement.
### 3. Implement the minimum complete change
1. Return configuration directly from every reserved fragment. Do not add
top-level `imports`, `options`, or `config`, and do not reproduce Registry
`mkEnableOption`, `cfg`, or `mkIf` boilerplate.
2. Put upstream NixOS, nix-darwin, or Home Manager modules in
`meta.imports.<class>`. Import ordinary helper files explicitly from the
fragment that uses them.
3. Declare unit-to-unit technical dependencies only through fully qualified
`meta.includes`. Never enable another unit by assigning its
`my.<path>.enable` option inside a fragment.
4. Add an independent application or service to an existing coherent profile,
or create a justified profile when no existing one expresses the user
intent. Do not use `hosts/default.nix` application or unit escape hatches for
normal composition.
5. Keep cross-platform purpose shared and installation differences in the
owning unit. Do not create thin `*-linux` profiles.
6. Use existing module arguments and standard options. Do not inject a
dependency through global arguments, Registry internals, import ordering, or
`lib.mkForce`. Use explicit module priorities only when a real ownership
boundary requires them and make that reason visible in the code or handoff.
7. Avoid speculative abstraction. Create a helper only when it separates
meaningful configuration or prevents real duplication. Do not add empty
fragments, compatibility aliases, unused options, redundant comments, or
copied boilerplate.
8. Update `modules/profiles/README.md`, `AGENTS.md`, profile selections, or
other contract documentation whenever the change makes an existing
statement stale. Do not edit them performatively when their meaning remains
accurate.
### 4. Prove the change
Run the validation matrix in
[references/review-checklist.md](references/review-checklist.md). At minimum:
1. Format the task-owned files with the repository formatter and run
`git diff --check`.
2. Inspect the complete task diff for accidental files, duplication, leaked
secrets, forced values, direct enable assignments, and unrelated rewrites.
3. Run `nix flake check`.
4. Run `pre-commit run --all-files`.
5. Evaluate every affected real host without switching it. For a
cross-platform unit or profile, evaluate both NixOS and nix-darwin even if
only one class changed. Build an affected configuration with `--no-link`
when the current platform can build it.
6. Verify selection as well as syntax: confirm that the expected package,
program, service, group, cask, or external module appears in the resulting
configuration.
If a command is unavailable, blocked by the environment, or fails for a
pre-existing reason, diagnose it and report the exact gap. Never silently skip
a required check or weaken the implementation to make a check pass.
### 5. Audit before completion
Reject the change until all of the following are true:
- Every line has one clear owner and is required by the requested behavior.
- Every dependency is either technical and declared in `meta.includes`, or a
user choice owned by a profile.
- The unit is reachable from the intended profile or has an explicit reason to
remain independently selectable.
- No host, Registry, flake root, global argument, or overlay was changed as a
shortcut.
- Reserved fragments, metadata, and profile documentation satisfy the current
repository contract.
- Validation covers every affected host class and all failures are resolved or
explicitly reported.
Conclude with the owner and selection rationale, affected hosts or profiles,
validation commands and results, and any manual activation or runtime check
that remains. Do not claim runtime behavior that was only evaluated.
@@ -0,0 +1,4 @@
interface:
display_name: "Add Application or Service"
short_description: "Add clean, validated Registry units"
default_prompt: "Use $add-application-or-service to add an application or service cleanly and verify every affected host class."
@@ -0,0 +1,139 @@
# Application and Service Review Checklist
Use this reference during design and again during final review.
## Ownership and fragment matrix
| Concern | Owner | Typical fragment |
| -------------------------------------------------------- | ----------------------------------- | ----------------------------------------- |
| User-facing GUI, CLI, editor, or compositor | `modules/applications/<name>/` | `home*.nix`, `nixos.nix`, or `darwin.nix` |
| User-scoped package and program settings | Application unit | `home.nix` or `home/<class>.nix` |
| macOS Homebrew package or cask | Owning application or service | `darwin.nix` |
| Daemon or long-running service | `modules/services/<name>/` | `nixos.nix` or `darwin.nix` |
| Firewall, group, permission, or service account | Owning service | `nixos.nix` or `darwin.nix` |
| Upstream module defining options | Owning unit metadata | `meta.nix` under `imports.<class>` |
| Technical prerequisite unit | Owning unit metadata | Fully qualified `meta.includes` |
| A set of independent tools chosen for one purpose | Narrowest coherent profile | Profile `meta.includes` |
| Machine fact such as UUID, monitor ID, or static address | `hosts/<name>/` | Normal host module |
| Missing or replaced package | `overlays/` only after proving need | Overlay definition |
Use `home/common.nix` when Home Manager configuration is truly shared between
NixOS and Darwin. Use `home/nixos.nix` or `home/darwin.nix` for class-specific
Home Manager behavior. Root `common.nix` is system-side and never Home Manager.
Do not create an unused counterpart for symmetry.
## Dependency review
For every edge from unit A to unit B, answer these questions:
1. Does A fail to work without B? If yes, put the fully qualified ID of B in
A's `meta.includes`.
2. Are A and B merely useful together for a particular workflow? If yes, let a
profile include both.
3. Does the dependency exist only on one host? Keep the machine fact in the
host, but keep reusable behavior in its unit or profile.
4. Is a new `specialArgs`, `_module.args`, Registry field, or direct
`my.*.enable` assignment being proposed? Reject it unless the repository's
normal module and `meta.includes` mechanisms provably cannot model the
requirement.
5. Would adding the dependency make the depended-on application select a
compositor, desktop, personal workload, or unrelated tool? Reverse or remove
the edge; application metadata contains technical requirements, not taste.
Accept no circular dependency, shortened unit ID, duplicate include, stale
unit ID, or ordering-dependent override.
## Minimality and code-quality review
Reject any of these patterns:
- Empty or placeholder reserved fragments.
- Hand-written enable options or guards already generated by the Registry.
- Top-level `imports`, `options`, or `config` in a configuration fragment.
- External module imports hidden in a guarded configuration fragment.
- Helper files assumed to be auto-imported.
- A helper abstraction used once without reducing meaningful complexity.
- Configuration duplicated across fragments when a shared fragment can express
it cleanly.
- Direct host application or unit selection where a profile expresses the
concern.
- A new flake input or overlay when the locked package set already provides the
package and required module.
- Global argument injection for a value owned by one unit.
- `lib.mkForce` used to win an ordering fight instead of resolving ownership.
- Secret material, generated state, machine IDs, or mutable user preferences
committed as reusable configuration.
- Comments that repeat the code, compatibility aliases, dead options, or
opportunistic unrelated cleanup.
Prefer standard upstream module options over hand-written service definitions.
Prefer existing repository arguments and helpers over new plumbing. Preserve
user-owned mutable state unless the requested policy explicitly owns it.
## Validation matrix
Run checks from the repository root and keep the exact results for the handoff.
Do not switch or activate a live system merely to validate a change.
### Always
1. Format task-owned files. If the worktree contains unrelated user changes,
pass only task-owned paths to the configured formatter when supported.
2. Run `git diff --check`.
3. Review `git status --short`, `git diff --stat`, and the complete `git diff`.
4. Run `nix flake check`.
5. Run `pre-commit run --all-files`.
### NixOS or Home Manager on NixOS
- Evaluate each affected host's system toplevel derivation.
- Build at least one affected NixOS configuration with `--no-link` when the
current system supports it.
- Inspect the resulting option that proves selection: for example
`environment.systemPackages`, the user's `home.packages`,
`systemd.services`, `users.users.<name>.extraGroups`, or the upstream
`programs`/`services` option.
Typical build shape:
```sh
nix build .#nixosConfigurations.<host>.config.system.build.toplevel --no-link
```
### nix-darwin or Home Manager on Darwin
- Evaluate every affected Darwin host even when running on Linux.
- Inspect `homebrew.casks` or `homebrew.brews` for Homebrew-backed additions.
- Evaluate the relevant Home Manager program or package option.
- Build a Darwin configuration only on a compatible Darwin builder; otherwise
report that build as an explicit runtime-validation gap.
Typical evaluation shapes:
```sh
nix eval --raw .#darwinConfigurations.<host>.system.drvPath
nix eval --json .#darwinConfigurations.<host>.config.homebrew.casks
```
Confirm the exact output attribute against the current flake before using a
command; do not paste these shapes blindly.
### Profiles and cross-platform changes
- Determine transitive selection through `meta.includes`, not only direct
mentions.
- Evaluate every real host selecting the changed profile.
- Evaluate both host classes for a cross-platform profile, even if only one
current fragment changed.
- Re-read `modules/profiles/README.md` and `hosts/default.nix` for stale meaning,
compatibility, or role statements.
- If no real host selects the new unit, construct a non-persistent evaluation
that enables it or explain why the unit is intentionally dormant. Do not add
a fake host or permanent direct selection as a test harness.
### Runtime-dependent behavior
Evaluation and builds cannot prove GUI appearance, credentials, network access,
hardware behavior, or successful daemon interaction. State the precise manual
post-activation check needed for those behaviors. Never describe evaluation as
a runtime test.