Compare commits

..

407 Commits

Author SHA1 Message Date
Ash
acf5242ca6 ci: add plasma-bigscreen to cached packages
All checks were successful
CI / check (pull_request) Successful in 1m30s
CI / build-and-cache (pull_request) Has been skipped
Add plasma-bigscreen to CI cache and expose it in flake packages.
The package is built from upstream master (not yet in nixpkgs).
2026-04-10 16:52:31 -07:00
8ac79169d9 feat: add gym-box
Some checks failed
CI / check (push) Successful in 2m0s
CI / build-and-cache (push) Has been cancelled
2026-04-10 16:43:33 -07:00
962fd35a83 Fix nix flake check warnings and errors
Some checks failed
CI / check (push) Successful in 3m38s
CI / build-and-cache (push) Failing after 1h26m28s
- Move nextcloud-talk-desktop to x86_64-linux-only block (fixes aarch64 eval error)
- Remove duplicate deps from plasma-bigscreen (auto-injected by mkKdeDerivation)
- Replace nixfmt-rfc-style with nixfmt (deprecated alias)
- Set programs.git.signing.format = null (adopt new default)
- Add meta.description to all flake apps
2026-04-08 13:09:41 -07:00
a823caf369 Remove beads/gastown/perles/dolt
Some checks failed
CI / check (push) Failing after 12m14s
CI / build-and-cache (push) Has been cancelled
2026-04-08 11:47:11 -07:00
785561367e Add opencode package and install via base role
Some checks failed
CI / check (push) Successful in 2m50s
CI / build-and-cache (push) Failing after 8m7s
- Add packages/opencode with pre-built binaries from GitHub releases (v1.4.0)
- Support all 4 platforms (aarch64-darwin, x86_64-darwin, x86_64-linux, aarch64-linux)
- Add update-opencode flake app for automated version bumps
- Install opencode via home.roles.base so it's available on all machines
- Reformat flake.nix and packages with nixfmt
2026-04-08 11:33:40 -07:00
ddd21454b7 Merge pull request 'chore(deps): lock file maintenance' (#50) from renovate/lock-file-maintenance into main
Some checks failed
CI / check (push) Successful in 5m31s
CI / build-and-cache (push) Failing after 6h14m41s
Reviewed-on: #50
Reviewed-by: johno <john@ogle.fyi>
2026-04-05 10:00:39 -07:00
790b3f1543 Merge pull request 'Add lnav to base CLI tools' (#51) from ash/add-lnav into main
All checks were successful
CI / check (push) Successful in 3m5s
CI / build-and-cache (push) Successful in 2m29s
Reviewed-on: #51
Reviewed-by: johno <john@ogle.fyi>
2026-03-24 13:32:29 -07:00
Ash
079a3d7a11 Add lnav to base CLI tools
All checks were successful
CI / check (pull_request) Successful in 3m19s
CI / build-and-cache (pull_request) Has been skipped
2026-03-24 13:12:56 -07:00
6bd5df6bf0 chore(deps): lock file maintenance
All checks were successful
CI / check (pull_request) Successful in 5m22s
CI / build-and-cache (pull_request) Has been skipped
2026-03-23 19:11:16 +00:00
718d81a88a Merge pull request 'ci: add patched nextcloud-talk-desktop to binary cache' (#49) from ash/cache-talk-desktop into main
Some checks failed
CI / check (push) Failing after 14m52s
CI / build-and-cache (push) Has been cancelled
2026-03-16 09:01:09 -07:00
Ash
827da51214 ci: add patched nextcloud-talk-desktop to binary cache
All checks were successful
CI / check (pull_request) Successful in 2m41s
CI / build-and-cache (pull_request) Has been skipped
Includes the Wayland screen sharing patch (from ash/talk-desktop-wayland-screenshare)
and adds custom-nextcloud-talk-desktop to CI build-and-cache pipeline.
2026-03-16 08:56:40 -07:00
Ash
068f912dc3 Merge branch 'ash/talk-desktop-wayland-screenshare' into ash/cache-talk-desktop 2026-03-16 08:56:14 -07:00
45db8efaa9 docs: add upstream tracking comments to plasma-bigscreen files
All checks were successful
CI / check (push) Successful in 2m42s
CI / build-and-cache (push) Successful in 1m39s
Flag the custom derivation and workarounds as temporary with TODO
markers and links to the relevant nixpkgs issue/PR. Makes it clear
what to remove once plasma-bigscreen lands in nixpkgs.
2026-03-16 08:48:39 -07:00
c57a4d3769 fix: plasma-bigscreen build — correct source hash and add Qt6QmlPrivate dep
- Set correct source hash for fetchFromGitLab (was placeholder)
- Add qtdeclarative to build inputs for Qt6::QmlPrivate target
- Patch CMakeLists.txt to find_package(Qt6 QmlPrivate) before QCoro6,
  working around QCoro not importing its own transitive dependency

Verified: boxy, zix790prors, and nix-book all build successfully.
2026-03-16 08:48:38 -07:00
Ash
2ae03eb7bf fix: handle missing xwaylandvideobridge attr conditionally 2026-03-16 08:48:38 -07:00
Ash
9b7ce04a41 fix: merge services.displayManager into single block
NixOS doesn't allow duplicate attribute set definitions in the
same module. Combine sessionPackages and autologin config.
2026-03-16 08:48:38 -07:00
Ash
d2ebe7083c boxy: replace Kodi with Plasma Bigscreen
Add plasma-bigscreen role with package built from upstream master
(not yet in nixpkgs, tracking NixOS/nixpkgs#428077).

Changes:
- New role: roles/plasma-bigscreen/ (module + package derivation)
- boxy configuration: swap roles.kodi for roles.plasma-bigscreen
- Keeps all existing functionality: Jellyfin, Stremio, Firefox,
  KDE Connect, app-launcher-server, AVR volume control (kodi user)
- Autologins to plasma-bigscreen-wayland session instead of plasma

NOTE: First build will fail with a hash mismatch on the source
fetch — copy the correct sha256 from the error into package.nix.
Some dep attribute names may also need adjustment on first build.
2026-03-16 08:48:38 -07:00
Ash
8f292893a3 feat: patch nextcloud-talk-desktop for Wayland screen sharing
All checks were successful
CI / check (pull_request) Successful in 2m46s
CI / build-and-cache (pull_request) Has been skipped
Patches the Electron asar bundle to inject setDisplayMediaRequestHandler
with useSystemPicker: true, which routes screen capture through the
native PipeWire/xdg-desktop-portal pipeline on Wayland.

Based on upstream draft PR nextcloud/talk-desktop#1022.
Uses the patched version in communication role.
2026-03-16 08:46:16 -07:00
Ash
c413e27bf8 fix: shift renovate schedules to 5-7 PM Saturday to align with cron
All checks were successful
CI / check (push) Successful in 2m47s
CI / build-and-cache (push) Successful in 1m34s
CronJob runs at 0/6/12/18h. Old 2-4 PM window never had a run
inside it, so Renovate never created PRs. Shift to 5-7 PM so the
18:00 run lands in the window. nixpkgs-qt staggered to 7-9 PM.
2026-03-16 08:12:23 -07:00
Ash
091c570249 fix: remove custom sway portal config, let NixOS sway module handle it
All checks were successful
CI / check (pull_request) Successful in 2m45s
CI / build-and-cache (pull_request) Has been skipped
CI / check (push) Successful in 2m42s
CI / build-and-cache (push) Successful in 1m37s
Only wlr.enable is needed — the sway module already sets
xdg.portal.config.sway.default and adding our own conflicts.
2026-03-16 07:45:05 -07:00
Ash
23cde06c35 fix: mkForce sway portal default to resolve conflict with sway module
All checks were successful
CI / check (pull_request) Successful in 2m52s
CI / build-and-cache (pull_request) Has been skipped
2026-03-16 07:42:24 -07:00
Ash
579c5b135e fix: add xdg-desktop-portal-wlr for Sway screen sharing
Some checks failed
CI / check (pull_request) Failing after 1m47s
CI / build-and-cache (pull_request) Has been skipped
Enable wlr portal conditionally when wayland is active.
Add sway-specific portal config routing ScreenCast and
Screenshot interfaces through the wlr backend.
2026-03-16 07:21:45 -07:00
2e38217e13 claude-code: Update to version 2.1.75
All checks were successful
CI / check (push) Successful in 3m43s
CI / build-and-cache (push) Successful in 1m42s
2026-03-13 16:30:52 -07:00
a77352fd81 feat: add perles to CI binary cache
All checks were successful
CI / check (push) Successful in 2m32s
CI / build-and-cache (push) Successful in 2m31s
Extract shared package definition, add to flake packages output
and CI workflow so perles gets built and cached by Harmonia.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:32:13 -07:00
72b78ab25f feat(wireguard): add WireGuard VPN role
All checks were successful
CI / check (push) Successful in 2m35s
CI / build-and-cache (push) Successful in 1m25s
Replace inline wg-quick config in nix-book with a reusable role that
uses inline config instead of configFile, fixing the world-readable
/tmp key leak. Adds network-online.target dependency to prevent boot
failures from DNS not being ready.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:07:49 -07:00
60888a4901 fix: block humanlayer:create_handoff from being installed
All checks were successful
CI / check (push) Successful in 2m32s
CI / build-and-cache (push) Successful in 2h54m10s
Skip the create_handoff command during humanlayer plugin installation
and explicitly remove it if previously installed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-09 16:14:29 -07:00
433b41590b Update flake inputs, deduplicate beads/gastown package definitions
Some checks failed
CI / check (push) Successful in 3m39s
CI / build-and-cache (push) Has been cancelled
- Update beads and gastown flake inputs and vendorHashes
- Remove merged-upstream patches for gastown and beads
- Deduplicate beads/gastown build definitions in home role by using
  callPackage to reference shared packages/ definitions
- Pin dolt to v1.82.4 (gastown requires >= 1.82.4)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-09 15:30:08 -07:00
a3d039613f fix: replace removed programs.light with brightnessctl
Some checks failed
CI / check (push) Successful in 4m16s
CI / build-and-cache (push) Failing after 6h12m6s
programs.light was removed from nixpkgs (upstream unmaintained).
brightnessctl is the recommended replacement for Sway/Wayland.
2026-03-05 14:48:43 -08:00
0a85616d58 Update flake inputs, claude-code 2.1.69, doom emacs, rotate wallpaper
Some checks failed
CI / build-and-cache (push) Has been cancelled
CI / check (push) Has been cancelled
2026-03-05 14:48:14 -08:00
c85f047ee2 fix: update gastown vendorHash for Go module changes
Some checks failed
CI / check (push) Successful in 2m10s
CI / build-and-cache (push) Failing after 3m57s
The upstream gastown Go dependencies changed, causing a hash mismatch
in the fixed-output derivation. Updated vendorHash in both:
- packages/gastown/default.nix (flake packages output)
- home/roles/development/default.nix (home-manager inline build)
2026-03-05 09:04:28 -08:00
f4c1de5fb3 fix: add execute permission to rotate-wallpaper.sh
Some checks failed
CI / check (push) Successful in 2m13s
CI / build-and-cache (push) Failing after 3m43s
2026-03-05 07:33:15 -08:00
e2770617c7 emacs: add pi-coding-agent package
Some checks failed
CI / check (push) Successful in 6m50s
CI / build-and-cache (push) Failing after 4m35s
Add dnouri/pi-coding-agent as an Emacs frontend for the pi coding agent.
Pinned to commit 8d8158b.

- Package installed from GitHub with all .el files (includes md-ts-mode)
- Suppress grammar auto-install prompt since tree-sitter grammars are
  already managed by Nix (treesit-grammars.with-all-grammars)
- Keybinding: SPC o p to launch pi-coding-agent
- Alias: M-x pi as shortcut for M-x pi-coding-agent
2026-03-04 11:35:00 -08:00
8fd702cad0 Add pi-coding-agent package, simplify Go builds with unstable.buildGoModule
Some checks failed
CI / build-and-cache (push) Has been cancelled
CI / check (push) Has been cancelled
- Add pi-coding-agent package (v0.55.4) with multi-platform support
- Replace custom Go 1.25.6 override with nixpkgs-unstable buildGoModule
  for beads and perles builds
- Update flake inputs: beads, nixpkgs-unstable, perles
- Update vendor hashes for beads and perles
- Add pi-coding-agent to development role
2026-03-04 11:32:50 -08:00
eb4700226f feat(virtualisation): add waydroid
All checks were successful
CI / check (push) Successful in 4m48s
CI / build-and-cache (push) Successful in 2m37s
2026-02-19 21:31:51 -08:00
d4d7769e51 fix(zix790prors): set Sway output to 164.9Hz for AW3423DWF
Some checks failed
CI / check (push) Failing after 38m14s
CI / build-and-cache (push) Has been cancelled
The monitor refresh rate was only configured via xrandr session commands,
which have no effect on Wayland/Sway sessions, leaving it at 60Hz.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 18:02:44 -08:00
c39f711662 chore: update beads 0.49.6→0.52.0 and gastown, make gastown a proper flake
All checks were successful
CI / check (push) Successful in 6m4s
CI / build-and-cache (push) Successful in 8m20s
- beads: update to 0.52.0 (16af00d7), refresh vendorHash
- gastown: update to 35157d02, remove flake=false now that upstream has flake.nix
- gastown: add inputs.nixpkgs.follows and inputs.beads.follows to deduplicate deps
- Clean up stale comments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-18 09:44:11 -08:00
4dd53a0dd0 feat(dev): add Claude Code statusline config via activation hook
All checks were successful
CI / check (push) Successful in 3m48s
CI / build-and-cache (push) Successful in 2m27s
2026-02-17 10:32:33 -08:00
4945a9e339 chore(config): migrate config renovate.json
All checks were successful
CI / check (pull_request) Successful in 3m33s
CI / build-and-cache (pull_request) Has been skipped
CI / check (push) Successful in 3m39s
CI / build-and-cache (push) Successful in 2h59m16s
2026-02-15 08:02:23 +00:00
c50c90ab13 Merge pull request 'Skip already-cached packages in CI build-and-cache' (#43) from fix/ci-skip-cached-packages into main
Some checks failed
CI / check (push) Successful in 4m52s
CI / build-and-cache (push) Has been cancelled
2026-02-14 18:26:31 -08:00
mayor
cd7439b132 Use nix eval for cache path detection instead of --dry-run
All checks were successful
CI / check (pull_request) Successful in 5m15s
CI / build-and-cache (pull_request) Has been skipped
2026-02-14 18:25:03 -08:00
mayor
acd9d8d70f Skip already-cached packages in CI build-and-cache job
All checks were successful
CI / check (pull_request) Successful in 5m26s
CI / build-and-cache (pull_request) Has been skipped
2026-02-14 18:15:36 -08:00
mayor
3ece063a6a Limit CI builds to 2 cores to reduce RAM pressure
Some checks failed
CI / check (push) Successful in 6m29s
CI / build-and-cache (push) Failing after 3h3m47s
Kernel and qt5webengine builds are memory-intensive. Limiting to
2 parallel jobs prevents swapping on john-endesktop.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 14:51:15 -08:00
mayor
1a57eb737c Allow insecure qtwebengine for stremio in CI
Some checks failed
CI / build-and-cache (push) Has been cancelled
CI / check (push) Has been cancelled
qtwebengine-5.15.19 is marked insecure but stremio requires it.
Add to permittedInsecurePackages for the pkgsQt import.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 14:49:09 -08:00
mayor
b6ae5e92b3 Add qt-pinned-stremio to CI cache
Some checks failed
CI / check (push) Failing after 13m32s
CI / build-and-cache (push) Has been skipped
Stremio uses qt5webengine, so it benefits from the separate
nixpkgs-qt input to avoid rebuild churn.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 14:27:28 -08:00
mayor
3588fe97c6 Add nix-deck kernel to CI cache
Some checks failed
CI / build-and-cache (push) Has been cancelled
CI / check (push) Has been cancelled
The Steam Deck (nix-deck) kernel from Jovian-NixOS is expensive to
build. Pre-building it in CI saves significant time on updates.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 14:26:03 -08:00
0d063557c4 fix(doom): pin org-msg to fix build after upstream force-push
Some checks failed
CI / check (push) Failing after 14m15s
CI / build-and-cache (push) Has been skipped
The org-msg commit pinned by doom-emacs was force-pushed away.
Pin it locally to a valid commit on main branch.

Also updates nix-doom-emacs-unstraightened input.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 22:35:44 -08:00
da18500660 fix(kodi): use qt-pinned for stremio to avoid qtwebengine rebuilds
- Add permittedInsecurePackages to qt-pinned config in flake.nix
- Use qt-pinned.stremio instead of pkgs.stremio
- Update warning message to reference nixpkgs-qt

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 22:35:44 -08:00
d286924eb5 Add stremio to the kodi role 2026-02-12 22:35:44 -08:00
mayor
246b05568c Restore separate check job for better CI UX
Some checks failed
CI / build-and-cache (push) Has been cancelled
CI / check (push) Has been cancelled
- PRs: Fast flake check with clear status
- Main: check → build-and-cache (build only starts if check passes)
- Clearer failure attribution and status badges

Tradeoff: 2x nix-setup on main pushes, but better job structure.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 22:34:51 -08:00
mayor
ae096e7589 Fix CI: extract store path from nix build output
All checks were successful
CI / ci (push) Successful in 14m6s
nix build outputs progress info to stdout along with the store path.
Filter to only the /nix/store/ line to get the actual path.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 22:02:12 -08:00
mayor
de1a903f1f Consolidate CI into single job
Some checks failed
CI / ci (push) Failing after 4m7s
Merge check and build-and-cache into one job to eliminate redundant
nix-setup step. Saves ~1m43s per run.

- PRs: checkout → nix-setup → flake check
- Main pushes: same + build/sign/cache all packages

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 21:47:39 -08:00
mayor
4b5fec04fe Add beads and gastown to CI cache, consolidate CI workflow
Some checks failed
CI / check (push) Successful in 3m55s
CI / build-and-cache (push) Failing after 2m29s
- Add packages/beads and packages/gastown with shared definitions
- Expose custom-beads and custom-gastown in flake packages output
- Consolidate CI from matrix (8 parallel jobs) to single job with loop
- Saves ~12 minutes of redundant nix-setup time per run
- Uses ::group:: for collapsible log sections per package

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 21:40:21 -08:00
mayor
f3fa5fcf13 ci: add GC roots to prevent cached packages from being collected
All checks were successful
CI / check (push) Successful in 4m0s
CI / build-and-cache (custom-app-launcher-server) (push) Successful in 2m28s
CI / build-and-cache (custom-claude-code) (push) Successful in 2m27s
CI / build-and-cache (custom-mcrcon-rbw) (push) Successful in 2m18s
CI / build-and-cache (custom-rclone-torbox-setup) (push) Successful in 2m23s
CI / build-and-cache (custom-tea-rbw) (push) Successful in 3m32s
CI / build-and-cache (qt-pinned-jellyfin-media-player) (push) Successful in 3m42s
Creates symlinks in /nix/var/nix/gcroots/ci-cache/ for each pushed package
2026-02-12 21:14:20 -08:00
mayor
d799bd2d32 fix: expose custom packages as flake outputs for CI caching
Some checks failed
CI / build-and-cache (custom-app-launcher-server) (push) Has been cancelled
CI / build-and-cache (custom-claude-code) (push) Has been cancelled
CI / build-and-cache (custom-mcrcon-rbw) (push) Has been cancelled
CI / build-and-cache (custom-rclone-torbox-setup) (push) Has been cancelled
CI / build-and-cache (custom-tea-rbw) (push) Has been cancelled
CI / build-and-cache (qt-pinned-jellyfin-media-player) (push) Has been cancelled
CI / check (push) Has been cancelled
Add packages output with:
- custom-* packages from packages/
- qt-pinned-jellyfin-media-player

Update CI to use hyphenated names (flake attrs can't have dots)
2026-02-12 21:11:48 -08:00
mayor
3b640bf81a ci: build custom packages instead of full system configs
Some checks failed
CI / check (push) Successful in 3m59s
CI / build-and-cache (custom.app-launcher-server) (push) Failing after 2m18s
CI / build-and-cache (custom.claude-code) (push) Failing after 2m22s
CI / build-and-cache (custom.mcrcon-rbw) (push) Failing after 2m20s
CI / build-and-cache (custom.rclone-torbox-setup) (push) Failing after 2m20s
CI / build-and-cache (qt-pinned.jellyfin-media-player) (push) Has been cancelled
CI / build-and-cache (custom.tea-rbw) (push) Has been cancelled
Focus on packages that actually need caching:
- custom.* packages from packages/
- qt-pinned.jellyfin-media-player (qt5webengine)

Avoids unrelated build failures (steam) and is much faster.
2026-02-12 20:56:18 -08:00
mayor
72ec102e00 fix: use signKeyPaths instead of deprecated signKeyPath
Some checks failed
CI / check (push) Successful in 10m31s
CI / build-and-cache (boxy) (push) Failing after 6m13s
CI / build-and-cache (live-usb) (push) Failing after 16m28s
CI / build-and-cache (nix-book) (push) Failing after 14m11s
CI / build-and-cache (zix790prors) (push) Has been cancelled
CI / build-and-cache (nix-deck) (push) Has been cancelled
CI / build-and-cache (john-endesktop) (push) Successful in 13m23s
2026-02-12 19:39:59 -08:00
mayor
028361ce2b fix: update harmonia signing key name to harmonia.john-endesktop
Some checks failed
CI / build-and-cache (boxy) (push) Has been cancelled
CI / check (push) Has been cancelled
CI / build-and-cache (john-endesktop) (push) Has been cancelled
CI / build-and-cache (live-usb) (push) Has been cancelled
CI / build-and-cache (nix-book) (push) Has been cancelled
CI / build-and-cache (nix-deck) (push) Has been cancelled
CI / build-and-cache (zix790prors) (push) Has been cancelled
2026-02-12 19:37:27 -08:00
mayor
bee56c32e5 fix: use internal hostname for harmonia cache
Some checks failed
CI / build-and-cache (boxy) (push) Has been cancelled
CI / build-and-cache (john-endesktop) (push) Has been cancelled
CI / build-and-cache (live-usb) (push) Has been cancelled
CI / build-and-cache (nix-book) (push) Has been cancelled
CI / check (push) Has been cancelled
CI / build-and-cache (nix-deck) (push) Has been cancelled
CI / build-and-cache (zix790prors) (push) Has been cancelled
2026-02-12 19:34:12 -08:00
9b1424b097 fix(gastown): drop town-root-detection patch merged upstream
Some checks failed
CI / check (push) Successful in 6m14s
CI / build-and-cache (boxy) (push) Failing after 9m53s
CI / build-and-cache (john-endesktop) (push) Failing after 12m32s
CI / build-and-cache (live-usb) (push) Failing after 16m14s
CI / build-and-cache (nix-book) (push) Failing after 8m48s
CI / build-and-cache (nix-deck) (push) Failing after 46m52s
CI / build-and-cache (zix790prors) (push) Failing after 23m45s
2026-02-11 08:24:29 -08:00
945864edbe chore: update flake inputs and claude-code to 2.1.32 2026-02-11 08:24:23 -08:00
f323048675 fix(beads): build with Go 1.25.6 for dolt server support
Some checks failed
CI / check (push) Successful in 5m21s
CI / build-and-cache (boxy) (push) Failing after 10m30s
CI / build-and-cache (john-endesktop) (push) Failing after 8m34s
CI / build-and-cache (live-usb) (push) Failing after 27m4s
CI / build-and-cache (nix-book) (push) Failing after 8m22s
CI / build-and-cache (nix-deck) (push) Failing after 25m32s
CI / build-and-cache (zix790prors) (push) Failing after 14m38s
nixpkgs ships Go 1.25.5, but beads' dolt dependencies (dolthub/driver,
dolthub/go-icu-regex) require Go >= 1.25.6. Override Go toolchain to
1.25.6 and add ICU build dependency for dolt's regex library.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 10:41:36 -08:00
e2a81e7290 Merge branch 'polecat/dust/x-fqaob@mlfbyrhb': add harmonia binary cache service
Some checks failed
CI / check (push) Successful in 5m34s
CI / build-and-cache (boxy) (push) Failing after 10m3s
CI / build-and-cache (john-endesktop) (push) Failing after 8m49s
CI / build-and-cache (live-usb) (push) Failing after 1m54s
CI / build-and-cache (nix-book) (push) Failing after 1m52s
CI / build-and-cache (nix-deck) (push) Failing after 2m20s
CI / build-and-cache (zix790prors) (push) Failing after 1m48s
2026-02-09 08:14:09 -08:00
7610a9c0e1 feat(john-endesktop): add harmonia binary cache service
Replace broken k8s harmonia deployment with native NixOS service.

Configuration:
- services.harmonia.enable = true
- Bind to [::]:5000 (IPv4 and IPv6)
- Sign key at /etc/harmonia/signing-key.private
- Open firewall port 5000

The signing key must be placed manually on john-endesktop at
/etc/harmonia/signing-key.private using the key generated earlier.

Closes: x-fqaob
2026-02-09 08:10:17 -08:00
mayor
ff57d3c043 fix: update harmonia signing public key
Some checks failed
CI / check (push) Successful in 6m48s
CI / build-and-cache (boxy) (push) Failing after 27m9s
CI / build-and-cache (john-endesktop) (push) Failing after 9m35s
CI / build-and-cache (live-usb) (push) Failing after 18m27s
CI / build-and-cache (nix-book) (push) Failing after 29m17s
CI / build-and-cache (nix-deck) (push) Failing after 1h12m5s
CI / build-and-cache (zix790prors) (push) Failing after 20m0s
2026-02-08 20:26:48 -08:00
3a36594dc9 Merge branch 'polecat/fury/x-iyz0w@mlecbczk': add build-and-cache job for all nixosConfigurations
Some checks failed
CI / build-and-cache (boxy) (push) Has been cancelled
CI / build-and-cache (john-endesktop) (push) Has been cancelled
CI / check (push) Has been cancelled
CI / build-and-cache (live-usb) (push) Has been cancelled
CI / build-and-cache (nix-book) (push) Has been cancelled
CI / build-and-cache (nix-deck) (push) Has been cancelled
CI / build-and-cache (zix790prors) (push) Has been cancelled
2026-02-08 20:17:44 -08:00
5a7064d07b feat(ci): add build-and-cache job for all nixosConfigurations
- Build all 6 machines (nix-book, boxy, zix790prors, nix-deck, john-endesktop, live-usb) in parallel matrix
- Only runs on push to main after check passes
- Signs closures with NIX_SIGNING_KEY secret
- Pushes to cache via SSH using CACHE_SSH_KEY, CACHE_HOST, CACHE_USER secrets
- Skips Darwin as no builder available

Required Gitea secrets:
- NIX_SIGNING_KEY: Cache signing private key
- CACHE_SSH_KEY: SSH key for cache server access
- CACHE_HOST: Cache server hostname
- CACHE_USER: SSH user for cache server

Closes: x-iyz0w

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 20:17:40 -08:00
8afdf287ee Merge branch 'polecat/shiny/x-qdkuu@mlec8nfv': add harmonia cache to nix.settings
Some checks failed
CI / check (push) Has been cancelled
2026-02-08 20:17:15 -08:00
bb3cdd8046 feat(nix): add harmonia cache to nix.settings
Configure all NixOS machines to use the internal harmonia binary cache:
- Add nix-cache.johnogle.info as substituter
- Add harmonia signing public key to trusted-public-keys
- Enable fallback for local builds when cache unreachable
- Set 5s connect-timeout for faster fallback

Refs: x-qdkuu

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 20:17:08 -08:00
1380fb307a Merge branch 'polecat/nitro/x-xiiep@mlebx809': source jellyfin-media-player from qt-pinned namespace
Some checks failed
CI / check (push) Has been cancelled
2026-02-08 20:16:42 -08:00
6ccfb5097c feat(roles/kodi): source jellyfin-media-player from qt-pinned namespace
Update kodi role to use pkgs.qt-pinned.jellyfin-media-player instead of
pkgs.jellyfin-media-player. This decouples jellyfin from the main nixpkgs
update cycle, avoiding massive qt5webengine rebuilds when updating other
packages.

The qt-pinned namespace was added in commit 03f1692.

Closes: x-xiiep

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 16:49:12 -08:00
1b585847ab Merge branch 'polecat/chrome/x-ymkgu@mlebby8e': update renovate schedules to Saturday afternoon
All checks were successful
CI / check (push) Successful in 16m9s
2026-02-08 14:58:17 -08:00
e7906331dc feat(renovate): update schedules to Saturday afternoon
- lockFileMaintenance: Saturday 2-4pm (was Monday 5am)
- nix-stable-ecosystem: Saturday 2-4pm
- nix-unstable-ecosystem: Saturday 2-4pm
- Add nixpkgs-qt rule: Saturday 4-6pm (staggered)

This allows CI builds to run overnight Saturday→Sunday, with human
review Saturday evening and builds complete by Sunday morning.

Closes: x-ymkgu
2026-02-08 14:58:05 -08:00
dc722843a9 Merge branch 'polecat/rust/x-lnr8g@mlebamik': add nixpkgs-qt input for qt5webengine
Some checks failed
CI / check (push) Has been cancelled
2026-02-08 14:57:19 -08:00
03f169284d feat(flake): add nixpkgs-qt input for qt5webengine packages
Add separate nixpkgs input for qt5webengine-dependent packages like
jellyfin-media-player. This input updates on a separate Renovate
schedule from main nixpkgs to avoid massive qt5webengine rebuilds
when updating other packages.

- Add nixpkgs-qt input pinned to nixos-25.11
- Create pkgs.qt-pinned overlay namespace

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 14:35:35 -08:00
8908500073 feat(home-kodi): enable kdeconnect for kodi user on boxy
All checks were successful
CI / check (push) Successful in 3m26s
Allows KDE Connect discovery and pairing to work when logged in as
the kodi user on the media center.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 13:43:17 -08:00
87f6d5c759 feat(deps): update beads to 0.49.1 with dolt server mode, claude-code to 2.1.30
All checks were successful
CI / check (push) Successful in 5m17s
beads:
- Pin to commit 93965b4a (last before Go 1.25.6 requirement)
- Build locally with corrected vendorHash (upstream default.nix is stale)
- Enables dolt server mode support (gt-1mf.3)

claude-code: 2.1.19 → 2.1.30

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 14:56:24 -08:00
a851c2551c fix(deps): update gastown patch and pin beads to Go 1.24 compatible version
All checks were successful
CI / check (push) Successful in 5m25s
- Update gastown-fix-agent-bead-address-title.patch line numbers (326→315)
  for current upstream gastown source
- Remove obsolete gastown patches (rig-prefix, copydir-symlinks) that are
  now handled upstream
- Pin beads to 55e733c (v0.47.2) which uses Go 1.24.0 - newer versions
  require Go 1.25.6 which isn't in nixpkgs-unstable yet
- Remove beads-search-query-optimization.patch as it targets newer code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 13:18:46 -08:00
mayor
6cf63e86c1 Merge branch 'polecat/rust/x-0cf@ml2ye219': fix doom-intermediates.drv CI failure
All checks were successful
CI / check (push) Successful in 7m37s
Updated nix-doom-emacs-unstraightened flake input to fix stale IFD derivation.

Closes: x-0cf, x-qwd7, hq-cv-mnzq4
2026-01-31 15:50:43 -08:00
c3ed6c0a26 fix(deps): update nix-doom-emacs-unstraightened to fix live-usb flake check
Updates nix-doom-emacs-unstraightened from Jan 25 to Jan 31 release,
which fixes the stale doom-intermediates.drv reference that was causing
nixosConfigurations.live-usb to fail flake check.

Closes: x-0cf

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 15:48:34 -08:00
mayor
53fa89b2e9 Merge branch 'polecat/rust/hq-0h1p9m@ml2ugjq1': fix gastown statusline patch
All checks were successful
CI / check (push) Successful in 5m19s
Regenerated patch with correct hunk headers against locked rev 177094a2.
Root cause was malformed patch format, not a flake.lock issue.

Closes: hq-0h1p9m, x-bwld
2026-01-31 14:01:06 -08:00
3acf9d2796 fix(gastown): regenerate statusline optimization patch with correct line numbers
The patch file had malformed hunk headers with incorrect line numbers
and counts, causing it to fail to apply against the locked gastown rev
(177094a2). This was NOT a flake.lock issue - gastown source was properly
locked.

Changes:
- Regenerated patch from scratch against locked gastown revision
- Re-enabled the patch in default.nix (was commented out with TODO)
- Updated comment to accurately describe the optimization

The optimization skips expensive beads queries for detached tmux sessions
and caches status line output with a 10-second TTL, reducing Dolt CPU
usage from ~70% to ~20%.

Closes: hq-0h1p9m

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 13:59:11 -08:00
123e7d3b3a fix(gastown): repair malformed patch files for nixos-rebuild
Some checks failed
CI / check (push) Failing after 17m48s
- Remove 'index 0000000..1111111' lines that made patches appear as new files
- Fix hunk line counts in several patches
- Add missing leading spaces to blank context lines
- Temporarily disable statusline optimization patch (needs regenerating)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 13:20:12 -08:00
nixos_configs/crew/harry
56097aefa4 refactor(development): move gastown patches to separate files
All checks were successful
CI / check (push) Successful in 3m37s
Replace inline postPatch substituteInPlace calls with proper unified
diff patch files, following the pattern established by beads.

This improves maintainability:
- Each patch is in its own file with clear naming
- Patches use proper unified diff format
- Easier to review, update, and track individual fixes
- Default.nix is cleaner (237 lines of substituteInPlace -> 15 lines)

Patches included:
- gastown-fix-validate-recipient.patch
- gastown-fix-agent-bead-address-title.patch
- gastown-fix-agent-bead-rig-prefix.patch
- gastown-fix-role-home-paths.patch
- gastown-fix-town-root-detection.patch
- gastown-fix-copydir-symlinks.patch
- gastown-statusline-optimization.patch

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 09:06:05 -08:00
21a8b5c5d9 Fix bd SearchIssues inefficient WHERE IN query pattern for Dolt
All checks were successful
CI / check (push) Successful in 3m25s
The Dolt backend's SearchIssues was using a two-phase query:
1. SELECT id FROM issues WHERE ... -> collect all IDs
2. SELECT * FROM issues WHERE id IN (id1, id2, ... id8000+)

With 8000+ issues, this second query with 8000+ placeholders hammers
Dolt CPU at 100%+. The fix changes SearchIssues to select all columns
directly in the first query and scan results inline.

See: hq-ihwsj

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:29:46 -08:00
8f8582b0f3 feat(gastown): add statusline cache writes for CPU optimization
All checks were successful
CI / check (push) Successful in 3m24s
Complete the statusline optimization by adding cache writes to all
output functions. The existing patch added cache functions and cache
reads, but never wrote to the cache.

Changes:
- Add early-return for detached sessions (return static "○ |")
- Add cache read check for attached sessions
- Add setStatusLineCache() calls in all 5 output functions:
  - runWorkerStatusLine
  - runMayorStatusLine
  - runDeaconStatusLine
  - runWitnessStatusLine
  - runRefineryStatusLine

This should reduce Dolt CPU from ~70% to ~20% when agents are idle,
as tmux status lines will use cached results instead of spawning
beads queries every 5 seconds.

Testing: Run `nix switch` then monitor Dolt CPU with `top`

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 12:20:39 -08:00
94fb5a3e64 Fix gastown mail routing for rig-specific agent beads
All checks were successful
CI / check (push) Successful in 3m18s
- Add title-based lookup for hq- prefixed beads (uses title as address if contains "/")
- Add rig-specific prefix handling to parse IDs like j-java-crew-americano → java/crew/americano
- Handles crew, polecat, witness, refinery role patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 17:20:03 -08:00
7df68ba8c8 Add gastown postPatch bug fixes from jt flake
All checks were successful
CI / check (push) Successful in 5m17s
- Fix mail router normalization in validateRecipient
- Fix agentBeadToAddress to use title field for hq- prefixed beads
- Fix crew/polecat home paths (remove incorrect /rig suffix)
- Fix town root detection (RoleUnknown instead of RoleMayor)
- Fix copyDir symlink handling
- Pin to gastown commit 177094a matching jt flake

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:14:52 -08:00
2799632308 Add gastown postPatch bug fixes from jt flake
- Fix mail router normalization in validateRecipient
- Fix agentBeadToAddress to use title field for hq- prefixed beads
- Fix crew/polecat home paths (remove incorrect /rig suffix)
- Fix town root detection (RoleUnknown instead of RoleMayor)
- Fix copyDir symlink handling
- Pin to gastown commit 177094a matching jt flake

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:14:06 -08:00
346c031278 Match darwin configuration name to actual hostname
All checks were successful
CI / check (push) Successful in 3m33s
Use uppercase BLKFV4YF49KT7 so darwin-rebuild --flake ./ works without
explicitly specifying the configuration name.
2026-01-27 11:32:53 -08:00
188d2befb0 Fix sketchybar disk usage showing incorrect percentage
Monitor /System/Volumes/Data instead of / since root is a read-only
APFS snapshot with minimal usage. Also fix inverted formula that was
calculating 100-used instead of just using the capacity value directly.
2026-01-27 11:32:48 -08:00
8e8b5f4304 chore(machines): remove tart-agent-sandbox config
All checks were successful
CI / check (push) Successful in 5m44s
Pivoted to Docker container approach for agent sandboxing instead of
Tart VMs due to networking issues with Cloudflare WARP.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 10:54:33 -08:00
4098ee3987 revert tart agent sandbox sway idea
All checks were successful
CI / check (push) Successful in 3m34s
2026-01-27 09:58:50 -08:00
e1e37da7c2 feat(tart-agent-sandbox): add sway desktop with auto-login
All checks were successful
CI / check (push) Successful in 3m41s
- Enable desktop role with wayland/sway
- Use greetd for passwordless auto-login to sway
- Add video/input groups to agent user

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 09:48:03 -08:00
a46d11a770 feat(machines): add tart-agent-sandbox VM config
All checks were successful
CI / check (push) Successful in 4m26s
NixOS configuration for running LLM agents in isolated Tart VMs on
Apple Silicon. Includes:
- Headless server setup with SSH access
- Agent user with passwordless sudo
- Docker support
- Dev tools for cloning large repos
- Git config optimized for large repositories

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 09:24:47 -08:00
harry
8553b9826e feat(roles): add rclone-mount role for WebDAV mounts
Some checks failed
CI / check (push) Failing after 12m14s
Add a new system-level role for mounting WebDAV filesystems via rclone.
Includes rclone-torbox-setup helper script that uses rbw to bootstrap
credentials from Bitwarden.

Key features:
- Configurable WebDAV URL, username, mount point
- VFS cache mode and buffer size tuning for media streaming
- RequiresMountsFor option for ZFS pool dependencies
- Obscured password storage via environment file

Enable on john-endesktop for TorBox WebDAV access by rdt-client and
Jellyfin. Mount waits for /media ZFS pool before starting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 19:45:00 -08:00
a0c081e12e fix(aerospace): disable ctrl shortcuts
All checks were successful
CI / check (push) Successful in 5m43s
2026-01-26 17:22:33 -08:00
d92e4b3ddf feat(development): add perles TUI for beads 2026-01-26 17:22:28 -08:00
70b40966be chore(claude-code): update to 2.1.19 2026-01-26 17:10:45 -08:00
475a633ab7 feat(base): add watch to base role packages 2026-01-26 17:10:42 -08:00
a39416c9db chore: switch beads and gastown to upstream GitHub repos 2026-01-26 17:10:37 -08:00
63c3f4e84d fix(sketchybar): show disk used% instead of free%
All checks were successful
CI / check (push) Successful in 3m27s
Inverts the df output to show percentage used, matching the other
resource monitors (CPU, memory).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 16:20:31 -08:00
baf64f7f4a fix(emacs): make rbw password helper graceful when rbw unavailable
Add optional no-error parameter to my/get-rbw-password that returns nil
instead of signaling an error when rbw isn't installed or the entry is
missing. Use this for gptel API key so config loads without errors in
environments without rbw configured.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 14:20:33 -08:00
mayor
f0b6ede7ed add dolt to development role
All checks were successful
CI / check (push) Successful in 5m50s
Required for beads dolt backend migration in Gas Town.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 11:55:33 -08:00
d0cb16391f update gt and/or beads 2026-01-26 11:51:06 -08:00
d872293f19 fix(gastown): add ldflags for BuiltProperly check
All checks were successful
CI / check (push) Successful in 3m57s
The gastown build now requires BuiltProperly=1 to be set via ldflags,
otherwise gt errors with "This binary was built with 'go build' directly".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 08:20:20 -08:00
07182cfdcf chore(deps): lock file maintenance
Some checks failed
CI / check (push) Successful in 3m48s
CI / check (pull_request) Failing after 3m59s
2026-01-26 08:04:22 +00:00
hermione
65e91c20f7 fix(emacs): set org-caldav delete to never to prevent mass deletion
All checks were successful
CI / check (push) Successful in 4m5s
Sync state confusion was causing org-caldav to want to delete all
calendar entries. Setting to 'never' prevents accidental data loss.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:35:24 -08:00
hermione
01e376eac4 fix(emacs): correct org-caldav-todo-percent-states format
All checks were successful
CI / check (push) Successful in 3m35s
Format is (PERCENT "KEYWORD") not ("KEYWORD" . PERCENT).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:27:17 -08:00
hermione
9c5be2e27a fix(emacs): add KILL state to org-caldav-todo-percent-states
Some checks failed
CI / check (push) Has been cancelled
org-caldav needs percent mappings for all todo states. Added mappings
for TODO, IN-PROGRESS, WAIT, DONE, and KILL to prevent sync errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:26:08 -08:00
hermione
d9ffb14db5 refactor(emacs): remove DEADLINE logic, keep only CALDAV_UNTIL property
Some checks failed
CI / check (push) Has been cancelled
DEADLINE doesn't limit recurring event display - agenda skip function
handles that now. Simplified advice to only store CALDAV_UNTIL property.

Also made debug logging unconditional to diagnose why some events
don't get the property.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:23:55 -08:00
hermione
07ea05afab feat(emacs): filter recurring events past CALDAV_UNTIL in agenda
Some checks failed
CI / check (push) Has been cancelled
DEADLINE doesn't actually limit recurring event display in org-agenda.
Instead, use org-agenda-skip-function-global to filter entries where
today's date is past the CALDAV_UNTIL property.

The skip function:
- Checks for CALDAV_UNTIL property (set by caldav sync advice)
- Parses YYYYMMDD format
- Skips entry if today > UNTIL date

This properly hides expired recurring events from the agenda.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:20:44 -08:00
hermione
4f5108c9d9 fix(emacs): allow org-caldav export with broken links
All checks were successful
CI / check (push) Successful in 3m55s
mu4e message links in todo.org can't be resolved during iCalendar export,
causing sync to abort. Setting org-export-with-broken-links to 'mark'
allows export to continue (broken links get marked in output).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 13:16:53 -08:00
9243341ed7 fix comment
All checks were successful
CI / check (push) Successful in 4m2s
2026-01-25 12:58:10 -08:00
hermione
b729ee8c7a fix(emacs): use assoc instead of assq for UNTIL lookup in org-caldav advice
All checks were successful
CI / check (push) Successful in 3m48s
assq uses eq for key comparison, which can fail if the key symbols aren't
identical objects. assoc uses equal, which is what org-caldav itself uses
for rrule-props lookups (e.g., INTERVAL, FREQ).

This fixes DEADLINEs not being added to recurring events with UNTIL dates.

Also adds debug logging to help diagnose any remaining issues - will be
removed once verified working.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 12:46:11 -08:00
nixos_configs/crew/hermione
ebc28cebd4 fix(emacs): improve rbw password error handling
Show clear error message when rbw entry not found instead of
embedding error text in URLs/credentials.
2026-01-25 12:46:11 -08:00
c82358d586 gastown and beads: switch to git+https
All checks were successful
CI / check (push) Successful in 6m19s
2026-01-25 12:07:59 -08:00
74388e8c24 [remote-build] use full dns names 2026-01-25 12:07:39 -08:00
nixos_configs/crew/hermione
a98ccddab1 feat(emacs): add org-caldav UNTIL advice for recurring event end dates
Some checks failed
CI / check (push) Failing after 2m34s
Implements advice around org-caldav-insert-org-event-or-todo that:
- Extracts UNTIL from rrule-props
- Adds DEADLINE without repeater (Org 9.7+ treats as recurrence end)
- Stores :CALDAV_UNTIL: property for reference

Also fixes sync command to work before org is opened by requiring
org explicitly in the sync wrapper.

Closes: x-uv5f.1
2026-01-25 09:51:48 -08:00
18570628a5 update beads and gastown
Some checks failed
CI / check (push) Failing after 4m43s
2026-01-24 18:06:20 -08:00
hermione
0c484b6601 fix(emacs): embed credentials in URL for org-caldav auth
Some checks failed
CI / check (push) Failing after 4m14s
url-http-basic-auth-storage approach wasn't working.
Now dynamically sets org-caldav-url with user:pass embedded.
2026-01-24 17:52:02 -08:00
hermione
4853a18474 fix(emacs): correct url-http-basic-auth-storage format
Some checks failed
CI / check (push) Has been cancelled
Auth storage needs base64-encoded 'user:pass' string, not raw password.
2026-01-24 17:47:28 -08:00
hermione
8b8453a37a fix(emacs): move org-caldav sync function before use-package
Some checks failed
CI / check (push) Has been cancelled
Function must be defined before keybinding to avoid commandp error.
Added (require 'org-caldav) inside function for autoloading.
2026-01-24 17:46:02 -08:00
hermione
2b6e289b9a fix(emacs): limit org-caldav to 30 days of past events
Some checks failed
CI / check (push) Has been cancelled
Prevents downloading years of historical calendar entries.
2026-01-24 17:39:48 -08:00
hermione
70d364544f fix(emacs): change org-caldav keybinding to avoid conflict
Some checks failed
CI / check (push) Has been cancelled
Changed from SPC o C to SPC o a s (open -> agenda/calendar -> sync)
to avoid conflict with Claude Code IDE (SPC o c)
2026-01-24 17:34:07 -08:00
hermione
1ffa8524f0 fix(emacs): use rbw for org-caldav auth instead of GPG
Some checks failed
CI / check (push) Has been cancelled
GPG isn't installed, so .authinfo.gpg approach doesn't work.
Added wrapper function my/org-caldav-sync-with-rbw that fetches
credentials from rbw before calling org-caldav-sync.

Setup: rbw add nextcloud-caldav (app password as secret)
2026-01-24 17:27:02 -08:00
hermione
be3c27e868 update gastown
Some checks failed
CI / check (push) Has been cancelled
Executed-By: nixos_configs/crew/hermione
Rig: nixos_configs
Role: crew
2026-01-24 17:25:05 -08:00
c2d286087f fix(home-manager): ensure claude/beads plugin files are writable
Files copied from the nix store inherit read-only permissions, causing
subsequent home-manager activations to fail with "Permission denied".

Add rm -f before copy and chmod u+w after copy for all plugin files:
- humanlayer commands and agents
- local commands and skills
- micro-skills
- beads formulas

Executed-By: nixos_configs/crew/hermione
Rig: nixos_configs
Role: crew
2026-01-24 17:25:05 -08:00
hermione
1172818062 feat(emacs): add org-caldav integration for Nextcloud calendar sync
Some checks failed
CI / check (push) Failing after 1m59s
- Enable org-caldav package in packages.el
- Configure base org-caldav settings (URL, timezone, sync behavior)
- Add Personal calendar two-way sync (~/org/personal-calendar.org)
- Add Tasks calendar one-way sync from todo.org
- Add keybinding SPC o C for manual sync
- Document setup requirements in config comments

Note: Conflict resolution is 'Org always wins' (org-caldav limitation).
User needs to create Nextcloud app password and ~/.authinfo.gpg.

Refs: x-5tb, x-5tb.1, x-5tb.2, x-5tb.3
2026-01-24 17:18:45 -08:00
mayor
9f63e1430c fix(beads): set issue prefix to x-
Some checks failed
CI / check (push) Failing after 2m5s
Ensures beads created in this repo use x- prefix to match routes.jsonl
2026-01-24 16:37:04 -08:00
b14ef1f62a update gastown
Some checks failed
CI / check (push) Failing after 4m2s
2026-01-23 17:10:29 -08:00
87719fa9e6 update gastown
Some checks failed
CI / check (push) Has been cancelled
2026-01-23 17:10:06 -08:00
933612da4c update beads and gastown 2026-01-23 17:10:06 -08:00
shiny
d2c7599267 fix(beads): set routing mode to explicit instead of auto
Some checks failed
CI / check (push) Failing after 2m34s
The routing.mode was defaulting to 'auto', which uses git remote URL
to detect user role. Non-SSH URLs can cause mail and issues to be
routed to ~/.beads-planning instead of the local .beads directory.

Setting routing.mode to 'explicit' disables auto-routing entirely,
keeping all issues in the expected local directory.

Fixes: x-kho

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:15:21 -08:00
chrome
3d16824eac chore: add Gas Town directories to .gitignore
Some checks failed
CI / check (push) Has been cancelled
Added by gt polecat setup.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:56:37 -08:00
2cdc15163c fix(flake): apply claude-code overlay to all platforms
Some checks failed
CI / check (push) Failing after 2m17s
The custom claude-code overlay (for GCS-based builds) was only being
applied to darwinModules. Extract to shared customUnstableOverlays and
apply to nixosModules and nixosModulesUnstable as well.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:35:14 -08:00
a77b1230fe update beads
Some checks failed
CI / check (push) Failing after 4m50s
2026-01-21 15:15:39 -08:00
623a387127 flake: update gastown to a069ff34 (rebased on upstream)
Some checks failed
CI / check (push) Failing after 4m9s
Rebased local patches onto 45 new upstream commits.

Executed-By: mayor
Role: mayor
2026-01-20 22:19:45 -08:00
737f2b09e4 chore(flake): update gastown to gitea fork with timeout fix
Some checks failed
CI / check (push) Failing after 4m16s
Point gastown input to local Gitea fork which includes:
- Increased ClaudeStartTimeout from 60s to 120s
- Fixes intermittent refinery/polecat startup timeouts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 10:34:01 -08:00
cddc9de14a chore(deps): lock file maintenance
All checks were successful
CI / check (push) Successful in 6m18s
2026-01-19 20:15:34 -08:00
53e3bbe78f fix(claude-code): preserve bun appended bundle during NixOS build
All checks were successful
CI / check (push) Successful in 3m32s
Bun standalone executables store their JavaScript code by appending it
after the ELF sections, marked with "---- Bun! ----". The standard Nix
build process was corrupting this:

- autoPatchelfHook rewrites the entire ELF, losing appended data
- strip removes data after ELF sections
- patchelf shrink-rpath also rewrites the ELF

Fix by:
- Using dontStrip and dontPatchELF to skip automatic fixup
- Manually running patchelf --set-interpreter which modifies in-place
  without rewriting the entire file structure

This restores the binary from 99MB (bare bun runtime) to 220MB (full
claude-code application).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Executed-By: mayor
Role: mayor
2026-01-19 10:39:58 -08:00
c258eafe34 fix: apply claude-code overlay to NixOS modules
All checks were successful
CI / check (push) Successful in 4m11s
The custom claude-code package (from GCS) was only being applied to
darwinModules, causing NixOS systems to use the older version from
nixpkgs-unstable instead of the updated version from packages/claude-code.

Extract claudeCodeOverlay as a shared definition and apply it to all
module sets: nixosModules, nixosModulesUnstable, and darwinModules.

Executed-By: mayor
Role: mayor
2026-01-19 10:28:11 -08:00
03d0b76f97 feat: switch gastown input to local Gitea fork
All checks were successful
CI / check (push) Successful in 5m9s
Fork includes mayor startup protocol fix for escalation checking.
2026-01-19 08:58:23 -08:00
b5f7233214 ci: Use semver tag for gitea-actions/nix-setup
All checks were successful
CI / check (push) Successful in 3m21s
Switch from @main to @v1 for stability. Major version tag auto-updates
to latest v1.x.x while avoiding breaking changes.

Closes: nix-7zf
2026-01-18 17:54:24 -08:00
1203662237 feat(home/development): add sqlite3 package
Some checks failed
CI / check (push) Has been cancelled
2026-01-18 08:13:16 -08:00
6ad714b57c chore(beads): remove redirect-mode remnant files
All checks were successful
CI / check (push) Successful in 4m29s
These files should not exist in repos using redirect mode.
The beads data lives in the redirect target instead.
2026-01-17 14:55:19 -08:00
5440214295 chore(claude-code): update to v2.1.12
All checks were successful
CI / check (push) Successful in 4m33s
2026-01-17 13:14:18 -08:00
cc305af899 feat(dev): add gastown multi-agent workspace manager
- Add gastown flake input (non-flake, source only)
- Build gastown package using buildGoModule in development role
- Configure renovate for daily updates of gastown and beads
- Binary: gt (Gas Town CLI by Steve Yegge)
2026-01-17 13:14:12 -08:00
c06adec7d8 feat(dev): add systemd timer for beads gate check
All checks were successful
CI / check (push) Successful in 3m8s
Add periodic timer (every 5 min) that runs `bd gate check --type=timer`
across all beads workspaces with running daemons.

This enables self-scheduling molecules - workflows that can set timer
gates to schedule their own future execution (e.g., watchers that scan
Slack every 8 hours).

The timer:
- Only runs on Linux (uses systemd user services)
- Discovers workspaces via `bd daemon list`
- Silently skips if no beads daemons are running

Related: nixos-configs-1rk.2 (Time beads scheduling mechanism)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 09:23:25 -08:00
7903b2dfd0 refactor(dev): restructure Claude commands/skills directories
All checks were successful
CI / check (push) Successful in 3m16s
Correct terminology mismatch:
- Rename skills/ to commands/ (these are user-invokable commands)
- Create new skills/ for reference materials
- Move bd_workflow.md to skills/ (it's reference material)
- Add micro-skills and formulas directories
- Update default.nix to install both commands and skills

Commands → ~/.claude/commands/ (invokable as /command-name)
Skills → ~/.claude/commands/skills/ (reference materials)
Formulas → ~/.beads/formulas/ (workflow templates)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 17:16:55 -08:00
f472aa9b3d feat(skills): add bd-workflow CLI reference skill
All checks were successful
CI / check (push) Successful in 3m23s
Comprehensive bd CLI reference covering:
- Core commands (create, update, close, show, list)
- Dependency management (bd dep add, bd blocked)
- Formula/molecule workflow (pour, wisp, cook, gates)
- When to use bd vs TodoWrite

Complements existing beads_workflow.md which covers
philosophy and humanlayer integration patterns.

Closes: nixos-configs-6pk

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 17:01:07 -08:00
2e07454ffa fix(dev): update beads flake input with vendorHash override
All checks were successful
CI / check (push) Successful in 4m36s
Temporary workaround for upstream beads vendorHash mismatch.
2026-01-15 16:49:02 -08:00
daf963b290 chore(beads): gitignore sync state files 2026-01-15 16:48:56 -08:00
c3c8688f31 fix(common): make ghostty.terminfo Linux-only
All checks were successful
CI / check (push) Successful in 3m13s
2026-01-15 15:36:03 -08:00
1cee1cd365 Enable mcrcon wrapper in gaming home env
All checks were successful
CI / check (push) Successful in 3m29s
2026-01-14 15:58:27 -08:00
66c27da142 chore(deps): update actions/checkout action to v6
All checks were successful
CI / check (push) Successful in 3m24s
2026-01-14 15:43:36 -08:00
7d6f71f4e4 chore(config): migrate config renovate.json
Some checks failed
CI / check (push) Has been cancelled
CI / check (pull_request) Successful in 3m11s
2026-01-14 23:42:20 +00:00
7091ee3ad5 renovate: add git author
Some checks failed
CI / check (push) Has been cancelled
2026-01-14 15:40:01 -08:00
d78e089695 fix(renovate): Add Gitea workflow detection and prevent dashboard autoclose
All checks were successful
CI / check (push) Successful in 3m3s
2026-01-14 15:26:22 -08:00
28b7a0fda9 feat: Add Renovate configuration for dependency tracking
Some checks failed
CI / check (push) Has been cancelled
2026-01-14 15:12:32 -08:00
b7bccb0b40 feat(parallel_beads): Enhanced validation reporting in PR descriptions
Some checks failed
CI / check (push) Has been cancelled
CI / check (pull_request) Successful in 3m14s
- Added ERROR status for execution failures (command not found, permission error)
- Added status definitions: PASS, FAIL, SKIP, ERROR with clear criteria
- Restructured PR template with three validation sections:
  - Automated Checks: table of executed checks with status
  - Manual Verification Required: unchecked boxes from plan
  - CONTRIBUTING.md Compliance: extracted requirements with verification status
- Added instructions to extract manual verification items from plans
- Enhanced CONTRIBUTING.md extraction to track automated vs manual requirements
- Updated validation summary format to include error count
2026-01-14 15:04:16 -08:00
2d03714934 feat(skills): Add contribution guidelines check to beads skills
All checks were successful
CI / check (push) Successful in 2m57s
2026-01-14 14:26:17 -08:00
3f0e381de2 fix(ci): Add access token for private flake inputs
Some checks failed
CI / check (push) Has been cancelled
2026-01-14 14:25:47 -08:00
1d9fd0aee9 feat(skills): Add batch research+plan skill for multiple beads
Some checks failed
CI / check (push) Has been cancelled
2026-01-14 14:24:53 -08:00
16f6dfcec7 feat(skills): Enforce worktree/branch workflow in parallel_beads
Some checks failed
CI / check (push) Has been cancelled
2026-01-14 14:24:16 -08:00
90ef70eb2e Add mcrcon-rbw wrapper for Minecraft RCON
Some checks failed
CI / check (push) Has been cancelled
Wrapper that auto-authenticates via rbw (Bitwarden) for RCON access.
- Uses minecraft-rcon entry from Bitwarden
- Defaults to 10.0.0.165:25575 (LoadBalancer IP)
- Supports MCRCON_HOST/PORT overrides
- Interactive terminal mode when no args provided

Part of k3s-cluster-config bead k3s-cluster-config-byg
2026-01-14 14:09:45 -08:00
667f5b28dc feat(skills): Close Gitea issues when beads are reconciled
Some checks failed
CI / check (push) Has been cancelled
2026-01-14 13:59:30 -08:00
4bb71d0b7e Remove wixos (WSL) configuration
All checks were successful
CI / check (push) Successful in 3m0s
WSL is no longer used. This removes:
- machines/wixos/ directory and configuration.nix
- nixos-wsl input from flake.nix
- nixosConfigurations.wixos output
- References to wixos in AGENTS.md and .goosehints

Implements bead: nixos-configs-2mk
2026-01-13 18:02:36 -08:00
0bc134f557 fix(mu4e): Configure msmtp to preserve email body content
All checks were successful
CI / check (push) Successful in 6m0s
The mu4e msmtp configuration was causing email bodies to be stripped,
especially for multipart messages from org-msg. This was due to missing
critical msmtp settings.

Changes:
- Add message-sendmail-f-is-evil to prevent -f flag issues
- Add --read-envelope-from to msmtp arguments
- Set both send-mail-function and message-send-mail-function

Fixes: nixos-configs-9l8
2026-01-13 17:48:36 -08:00
1b9df3926e Fix conflicting audio role config: remove pulseaudio, keep pipewire
Some checks failed
CI / check (push) Has been cancelled
Remove services.pulseaudio configuration that conflicted with
services.pipewire. PipeWire replaces PulseAudio and provides
compatibility through pulse.enable.

Also added alsa.enable and alsa.support32Bit for better ALSA support.
2026-01-13 17:48:00 -08:00
bd98793528 feat(roles): Parameterize hardcoded values in printing, nfs-mounts, and virtualisation roles
Some checks failed
CI / check (push) Has been cancelled
- printing role: Add configurable printerName, printerUri, and printerModel options
  to replace hardcoded Brother printer values
- nfs-mounts role: Add configurable server, remotePath, and mountPoint options
  to replace hardcoded NFS server IP (10.0.0.43)
- virtualisation role: Add configurable dockerUsers option as list type
  to replace hardcoded 'johno' docker group membership

All options have sensible defaults matching the original hardcoded values,
ensuring backward compatibility while allowing per-host customization.

Implements bead: nixos-configs-fkt
2026-01-13 17:20:59 -08:00
d78637cf13 feat(home-manager): Add platform compatibility guards to cross-platform roles
Some checks failed
CI / check (push) Has been cancelled
Add lib.optionals pkgs.stdenv.isLinux guards to roles that contain
Linux-only packages or services to prevent build failures on Darwin:

- communication: Guard Electron apps (element-desktop, fluffychat,
  nextcloud-talk-desktop) that don't build on Darwin due to electron
  build-from-source limitations
- kdeconnect: Guard entire config block since services.kdeconnect
  requires D-Bus and systemd (Linux-only)
- sync: Guard syncthingtray package (requires Linux system tray)
- email: Guard systemd.user.services/timers (Darwin uses launchd)
- desktop: Guard Linux-only packages, services, and KDE-specific
  configurations including gnome-keyring, systemd services, and
  XDG mime associations

Implements bead: nixos-configs-tcu
2026-01-13 17:20:01 -08:00
08d16bd2c9 feat(scripts): Add --help flags to all flake apps
Some checks failed
CI / check (push) Has been cancelled
Add consistent --help/-h argument handling to update-doomemacs.sh,
rotate-wallpaper.sh, and upgrade.sh scripts. Each script now displays
usage information and a description of what it does.

update-claude-code already had --help support.
2026-01-13 17:18:46 -08:00
a14ff9be4d fix(flake): Remove duplicate home-manager imports from wixos and zix790prors
Some checks failed
CI / check (pull_request) Successful in 5m36s
CI / check (push) Has been cancelled
The nixosModules list already includes inputs.home-manager.nixosModules.home-manager,
so these individual configuration imports were redundant.
2026-01-13 16:37:41 -08:00
90217ec85a fix(ci): Use full Gitea URL for composite action
All checks were successful
CI / check (push) Successful in 7m29s
Gitea Actions defaults to GitHub for short-form action references.
Use full URL to reference actions from the same Gitea instance.
2026-01-13 15:58:20 -08:00
f99f4069f0 feat(ci): Add Gitea Actions workflow with Nix caching
Some checks failed
CI / check (push) Failing after 1s
Uses johno/gitea-actions/nix-setup composite action for:
- Nix installation via DeterminateSystems/nix-installer-action
- Nix store caching via actions/cache@v4
- Per-repo cache isolation based on flake.lock hash
2026-01-13 15:50:54 -08:00
320a2d3738 refactor: Move import_gitea_issues to user-level skill
Moves from project-level (.claude/commands/) to user-level
(home/roles/development/skills/) so it's available across all projects
via Home Manager activation.

Bead: nixos-configs-g72
2026-01-13 15:37:59 -08:00
92b6cfb710 fix(common): Add ghostty terminfo for SSH compatibility
Installs ghostty.terminfo on all NixOS machines so tmux works
when SSH'ing from a Ghostty terminal.
2026-01-13 14:09:57 -08:00
996fb86ed8 [nixos-configs-vru] Add skill for responding to Gitea PR review comments (#26)
## Summary
- Rewrote gitea_pr_review.md as a comprehensive interactive skill
- Accepts PR number as argument or auto-detects from current branch
- Reads Gitea config from tea CLI config file
- Fetches and displays review comments via REST API
- Interactive comment selection via AskUserQuestion
- Posts replies via `tea comment` with file:line context

## Bead Reference
Implements bead: nixos-configs-vru

## Changes
- Rewritten `home/roles/development/skills/gitea_pr_review.md` (+259/-155 lines)

## Testing
Please leave a review comment on this PR so we can test the skill!

## Limitations
- Thread replies are posted as top-level comments (Gitea API limitation)
- Uses first login from tea config

Reviewed-on: #26
Co-authored-by: John Ogle <john@ogle.fyi>
Co-committed-by: John Ogle <john@ogle.fyi>
2026-01-13 09:08:17 -08:00
47e2392a56 feat(john-endesktop): Enable virtualisation role 2026-01-12 21:57:46 -08:00
c26a11a9a8 fix(boxy): Update appLauncherServer to use module option syntax 2026-01-12 21:42:29 -08:00
7ba1b52ec7 feat(kodi): Add base-linux role to home-kodi imports 2026-01-12 21:42:25 -08:00
056c1a1e62 feat(remote-build): Add john-endesktop as builder machine
- Enable enableBuilder role on john-endesktop
- Add john-endesktop to nix-book's builder list (maxJobs=1, speedFactor=1)
- Document SSH setup process for new clients in remote-build role

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:27:01 -08:00
c92a82b21a fix(audio): Remove conflicting pulseaudio config
The audio role had both pipewire (with pulse.enable = true) and
pulseaudio configured, which are mutually exclusive. PipeWire's
PulseAudio compatibility layer handles pulse clients, so the
services.pulseaudio block is unnecessary and causes conflicts.

Closes: nixos-configs-0vf

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:48:41 -08:00
b6e9de0f61 feat(skills): Add plan-awareness to bead workflow skills
- parallel_beads: Filter beads by plan readiness before selection
  - Include beads with plans or type=bug
  - Warn about skipped beads that need plans first
- beads_implement: Check for plan based on bead type
  - Bugs can proceed without plans
  - Features/tasks warn and ask user preference
- beads_workflow: Document design decisions
  - Artifacts vs statuses for phase tracking
  - One bead per feature as default
  - Discovered-work pattern for splitting work

Closes: nixos-configs-45r, nixos-configs-8gr, nixos-configs-oog, nixos-configs-505

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 18:39:51 -08:00
ba4922981b feat(skills): Add validation status to parallel_beads PR descriptions
Enhances the parallel_beads workflow to capture and report validation status:

- Add step 3 to extract validation criteria from plans or use best-effort
  fallbacks (make test, nix flake check, npm test)
- Update step 4 to run validation and track PASS/FAIL/SKIP results
- Add Validation section with table to PR body templates (gh and tea)
- Enhance result reporting to include validation summary
- Add Validation column and Validation Failures section to summary table

Implements bead: nixos-configs-39m

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 18:29:25 -08:00
47aaad2eb5 docs: Add beads issue tracking documentation to AGENTS.md 2026-01-12 18:02:30 -08:00
8eca8204ff fix(home): Move plasma-manager-kodi to Linux-specific roles 2026-01-12 18:02:26 -08:00
082b0918af feat(skills): Add beads-aware workflow skills
New skills for integrated beads + humanlayer workflow:
- beads_research.md: Research with per-bead artifact storage
- beads_plan.md: Planning with bead linking
- beads_implement.md: Implementation with per-plan checkpoints
- beads_iterate.md: Plan iteration with version history
- beads_workflow.md: Comprehensive workflow documentation

Skills output to thoughts/beads-{id}/ for artifact storage
and automatically update bead notes with artifact links.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:58:41 -08:00
7a5f167a8c Remove perles 2026-01-11 16:30:35 -08:00
9e1003d4fc Add kodi role to zix790prors 2026-01-11 16:28:54 -08:00
bf600987e9 feat(john-endesktop): Add k3s node labels for workload scheduling
Add fast-cpu and fast-storage labels since this node has a faster CPU
than other cluster nodes and is the NFS host with fast local storage.
Also add k3s-upgrade=disabled to exclude from system-upgrade-controller.
2026-01-10 20:14:54 -08:00
346ad3665d feat(k3s-node): Add k3s-node role and enable on john-endesktop
Add reusable k3s-node role with configurable options for server/agent
modes. Configure john-endesktop as a k3s agent joining the cluster at
10.0.0.222.

Role supports:
- Server or agent role selection
- Configurable server address and token file
- Graceful node shutdown
- Optional firewall port opening
- Cluster initialization for first server

Note: NixOS nodes must be labeled with `k3s-upgrade=disabled` to exclude
them from the system-upgrade-controller, since NixOS manages k3s upgrades
through Nix rather than in-place binary replacement.
2026-01-10 20:08:57 -08:00
565acb1632 Add kubectl to home-server 2026-01-10 19:16:29 -08:00
b05c6d8c30 fix(nix-book): Remove suspend-then-hibernate lid behavior 2026-01-10 19:05:05 -08:00
0f555fdd57 feat(emacs): Add beads package configuration with keybindings 2026-01-10 19:02:09 -08:00
9973273b5e Extend nvidia role to include driver configuration
The nvidia role now handles full driver configuration instead of just
packages. Added options for open driver, modesetting, power management,
graphics settings, and driver package selection.

Updated zix790prors and wixos machine configs to use the new role
options, removing duplicated hardware.nvidia configuration blocks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 14:39:41 -08:00
f281384b69 feat(skills): Add import_gitea_issues skill for bead creation
Add a Claude Code skill that imports open Gitea issues as beads:
- Uses 'tea issues' to list open issues
- Checks existing beads to avoid duplicates
- Detects issue type (bug/feature/task) from content
- Creates beads with P2 priority and Gitea issue URL in notes
- Reports summary of imported vs skipped issues

Implements bead: nixos-configs-tdf
2026-01-10 13:24:20 -08:00
4eec701729 feat(skills): Add gitea_pr_review skill for managing PR review comments
Adds a new Claude Code skill that enables reading PR review comments and
posting replies on Gitea/Forgejo instances. Documents both the REST API
approach for reading reviews and the web endpoint approach for thread
replies, with fallback to top-level comments when thread replies aren't
possible due to authentication limitations.

Implements bead: nixos-configs-vru
2026-01-10 13:22:36 -08:00
bbcb13881f refactor(flake): Consolidate overlay configurations into shared functions
Extract duplicated overlay and home-manager configuration code into two
reusable factory functions:

- mkBaseOverlay: Creates the base overlay with unstable pkgs, custom
  packages, and bitwarden-desktop compatibility. Accepts optional
  unstableOverlays parameter for darwin-specific customizations.

- mkHomeManagerConfig: Creates home-manager configuration with shared
  settings (useGlobalPkgs, useUserPackages, doom-emacs module). Accepts
  sharedModules parameter for platform-specific modules like plasma-manager.

This reduces code duplication across nixosModules, nixosModulesUnstable,
and darwinModules, making the flake easier to maintain and extend.

Implements bead: nixos-configs-ek5
2026-01-10 13:15:57 -08:00
c28d6a7896 chore(packages): Remove unused vulkan-hdr-layer package
The vulkan-hdr-layer package was not used anywhere in the configuration.
Removing it to reduce maintenance burden.
2026-01-10 13:14:19 -08:00
79ff0b8aa4 feat: Move bootstrap/build-liveusb scripts to flake apps
- Move bootstrap.sh to scripts/ and add as flake app
- Move build-liveusb.sh to scripts/ and add as flake app
- Update usage comments to show nix run commands
- Improve build-liveusb.sh with better error handling (set -euo pipefail)
- Remove emojis from output messages for cleaner log output

Scripts can now be run consistently via:
  nix run .#bootstrap -- <hostname>
  nix run .#build-liveusb

Implements bead: nixos-configs-bli
2026-01-10 13:06:52 -08:00
1d9249ea83 Ignore .beads in main 2026-01-10 12:45:53 -08:00
2fdd2d5345 fix(skills): Correct reconcile_beads instructions for bd and tea CLI
- Fix jq syntax: bd show --json returns array, use .[0].notes
- Add grep command to extract PR number from URL
- Correct Gitea workflow: tea pr view lists all PRs, use tea pr list --state=closed instead
2026-01-10 12:45:25 -08:00
722cb315dc Stop tracking sync_base.jsonl in 2026-01-10 12:42:26 -08:00
e042acff16 feat(skills): Improve parallel beads workflow with in_review status
- Add step to mark beads as 'in_review' after PR creation
- Add PR URL to bead notes for traceability
- Create reconcile_beads skill to close beads when PRs are merged
- Update summary table to show bead status instead of generic status

Implements bead: nixos-configs-85h
2026-01-10 12:41:04 -08:00
4fe531f87f feat(emacs): Add prebuilt Doom option using nix-doom-emacs-unstraightened
Implement pre-built Doom Emacs packages for the live USB image, eliminating
the need to run `doom sync` after first boot.

Changes:
- Add nix-doom-emacs-unstraightened flake input
- Add homeModule to all three module sets (nixos, unstable, darwin)
- Add `prebuiltDoom` option to emacs role (default: false)
- Enable prebuiltDoom for live-usb configuration
- Pin custom packages in packages.el for deterministic builds:
  - claude-code-ide, gptel-tool-library, beads

When prebuiltDoom=true, all Doom packages are compiled at nix build time
using emacs-overlay. The doom configuration is stored in the nix store
(read-only), and no `doom sync` is required at runtime.

This is ideal for:
- Live USB images
- Immutable/reproducible systems
- Offline deployments

Closes: nixos-configs-1wd
2026-01-10 12:33:40 -08:00
266dee9f8f feat(home-server): Add starship prompt and alphabetize roles 2026-01-10 12:24:52 -08:00
38395c238f Fix race condition in Claude Code skill installation 2026-01-10 12:24:06 -08:00
e4a1771f48 sync beads 2026-01-10 12:15:40 -08:00
ff1fb245ac Add home-manager integration for john-endesktop server
Create home-server.nix with minimal development-focused configuration
enabling base, emacs, development, and tmux roles. Update flake.nix
to wire up home-manager for the johno user on the server.
2026-01-10 12:15:12 -08:00
82fb1738c1 feat(home): Add starship cross-shell prompt role
Add a new home-manager role for starship.rs, a fast and customizable
cross-shell prompt written in Rust.

Configuration includes:
- Bash and Zsh integration enabled
- Clean character symbols (> for success, x for error)
- Vi mode indicator support
- Smart directory truncation (4 levels, truncate to repo root)
- Git branch and status display
- Nix shell indicator with snowflake symbol
- Command duration for long-running commands (2s+)
- Disabled noisy modules (language runtimes, cloud providers)

Enabled in: home-desktop, home-laptop-compact, home-live-usb,
home-media-center configurations.

Closes: nixos-configs-uji
2026-01-10 11:46:43 -08:00
425e4f4cee Extract shared NixOS/Darwin base config into roles/common.nix
Create roles/common.nix containing shared configuration between NixOS and
Darwin: timezone, base packages (git, glances, pciutils, tree, usbutils, vim),
nix settings (experimental-features, max-jobs, trusted-users), gc config,
and allowUnfree setting.

Both roles/default.nix and roles/darwin.nix now import common.nix and only
contain platform-specific configuration.
2026-01-10 11:44:58 -08:00
0e5b11e55d Remove humanlayer prefix from local skills installation
Local skills don't need the humanlayer: prefix since they're not
from the humanlayer/claude-plugins repo.
2026-01-10 11:21:31 -08:00
1ba1a8fc9d [nixos-configs-7hd] Add parallel_beads skill for orchestrating bead processing
- Add skills/ directory for local Claude skills
- Create parallel_beads.md skill that orchestrates:
  - Phase 1: Multi-select bead selection from bd ready
  - Phase 2: Parallel subagents for implementation (worktree, implement, commit, PR)
  - Phase 3: Parallel review subagents
  - Phase 4: Cleanup and summary
- Update default.nix to install local skills alongside humanlayer plugins
- Support both gh (GitHub) and tea (Gitea/Forgejo) based on origin URL
2026-01-10 11:14:43 -08:00
009b84656f [john-endesktop] Update migration plan with completed pre-migration items 2026-01-10 10:49:04 -08:00
ef4e4509d3 [john-endesktop] Remove swap 2026-01-10 09:43:22 -08:00
cd6b528692 [john-endesktop] Update with actual disk ids 2026-01-10 09:34:28 -08:00
3914b54c73 actually actually finish for real? 2026-01-09 11:24:52 -08:00
9aa74258f9 actually finish beads-sync migration 2026-01-09 11:23:46 -08:00
64dda20aa4 finish migration to beads-sync 2026-01-09 11:22:58 -08:00
ac01548e89 chore(beads): commit untracked JSONL files
Auto-committed by bd doctor --fix
2026-01-09 11:22:13 -08:00
bb7f79843b bd sync: 2026-01-09 11:22:07 2026-01-09 11:22:07 -08:00
c1d6663a36 bd init 2026-01-09 11:21:49 -08:00
3cf4403ffa Add perles TUI package for Beads issue tracking
Adds a custom Nix package for perles, a terminal user interface for the
Beads issue tracking system with BQL query language support.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 11:20:02 -08:00
4e6123de9a Simplify beads integration to use bd setup claude
Replace complex marketplace plugin installation with simple 'bd setup claude'
which installs hooks into ~/.claude/settings.json. This is the recommended
approach for Claude Code (CLI + hooks) vs the MCP server approach.
2026-01-08 19:28:17 -08:00
19ee298b71 Add beads Claude plugin installation via home-manager
- Add beadsRepo reference from flake input
- Add activation script to install beads as marketplace plugin
- Updates known_marketplaces.json and config.json declaratively
2026-01-08 19:21:58 -08:00
537f7831a7 Fix claude-code-ide keybinds and consolidate settings
Move keybinding to :init block so SPC o c works before first invocation.
Add :commands for proper autoloading. Migrate customizations from
custom.el to config.el.
2026-01-05 21:11:58 -08:00
bf0d16fe1a Add Mod+Shift+Return keybinding to open browser window in i3/sway 2026-01-05 21:07:11 -08:00
858b6009ae Style blocked org-agenda tasks in purple instead of grey 2026-01-05 19:48:47 -08:00
40b323dcfd Revert "Use direct hibernate on nix-book lid close"
This reverts commit 2c70504c43.
2026-01-05 09:00:48 -08:00
60bd89b02c Make Sent folder pull-only in mbsync config
Split mbsync channel into two: proton-main for bidirectional sync
(excluding Sent) and proton-sent for pull-only sync. This avoids
"far side refuses to store" errors from Proton Bridge rejecting
uploaded sent messages.
2026-01-05 09:00:08 -08:00
5a5de7353b Ensure Mail directory exists before mbsync runs 2026-01-04 17:23:32 -08:00
e25aa7acab Update flake inputs, doom emacs, and claude-code 2026-01-04 17:12:35 -08:00
4a284de8a1 Add wallpaper rotation system with upgrade workflow
- Add home/wallpapers/default.nix with per-wallpaper scaling options
- Add 5 new Metroid-themed wallpapers to the rotation
- Update i3+sway and plasma-manager to use wallpaper module
- Add scripts/rotate-wallpaper.sh to cycle through wallpapers
- Add scripts/upgrade.sh to chain: flake update, doom, claude-code, wallpaper
- Add flake apps: rotate-wallpaper, update-claude-code, upgrade
- Fix claude-code update.sh to use REPO_ROOT for flake app compatibility

Run `nix run .#upgrade` for full system update with wallpaper rotation.
2026-01-04 17:12:27 -08:00
7be694ef66 [doom] Increase default font size 2026-01-04 13:38:56 -08:00
0ccfc30c73 Add personal address to mu init for mu4e integration 2026-01-04 13:36:56 -08:00
dab96a1c50 Replace notmuch with mu4e for email
mu4e's refile moves messages between IMAP folders, enabling
cross-device sync via mbsync. notmuch tags are local-only.

- Switch from notmuch to mu/mu4e packages
- Auto-initialize mu database on first sync
- Configure mu4e with folder shortcuts and refile to Archive
- Add NixOS load-path for mu4e elisp files
2026-01-04 12:59:35 -08:00
7898def044 Fix notmuch inbox tag to match IMAP INBOX folder
Previously, the notmuch config applied the "inbox" tag to all new
messages regardless of which IMAP folder they were in. This caused
tag:inbox to return all 22k+ messages instead of just those in INBOX.

Changes:
- Use temporary "new" tag instead of "inbox" for newly indexed messages
- Add post-new hook that applies tags based on maildir folder location
- inbox tag now only applies to messages in INBOX folder
- Also adds sent, draft, spam, deleted, and archive tags based on folder
2026-01-04 12:23:39 -08:00
4ce48313f6 Update nixpkgs-unstable to fix tree-sitter-razor
tree-sitter-razor was marked as broken in the previous lock, causing
nix flake check to fail on nix-deck. The grammar has been fixed upstream.
2026-01-04 12:03:19 -08:00
2c70504c43 Use direct hibernate on nix-book lid close
S3 deep sleep is broken on 12th Gen Intel Alder Lake. Switch from
suspend-then-hibernate to direct hibernate on all lid close events.
2026-01-04 12:03:10 -08:00
a22c7fec28 Add Tea CLI documentation for Gitea issue tracking 2026-01-04 11:08:32 -08:00
ef3e9b8c82 Implement volume control for boxy via Home Assistant
Add remote volume control for the living room media center (boxy) that
controls the Denon AVR through Home Assistant instead of local audio.

- Create plasma-manager-kodi role with custom KDE hotkeys for volume
  up/down/mute that execute shell scripts
- Scripts retrieve HA token from KDE Wallet and send API requests to
  control media_player.denon_avr_s970h_2
- Add retry logic (3 attempts) and error notifications
- Enable KDE Wallet PAM integration for automatic wallet unlock
- Configure KDE settings: Breeze Dark theme, disable screen locking,
  empty session restore
- Add kconfig package for debugging tools
2026-01-03 17:11:43 -08:00
0590dad71e [gaming] Add yarg 2026-01-02 13:43:00 -08:00
c81594af01 Add suspend-then-hibernate power management for nix-book 2025-12-30 00:29:24 -08:00
6f999882d3 Reorder migration steps to export ZFS pools before shutdown 2025-12-30 00:29:21 -08:00
8bb20bf05e Add NixOS configuration for john-endesktop ZFS/NFS server
Create configuration to migrate john-endesktop from Arch Linux to NixOS while
maintaining existing ZFS pools (media JBOD and swarmvols mirror) and NFS
exports for k3s cluster.

Configuration includes:
- ZFS support with automatic pool import
- NFS server exporting both pools to 10.0.0.0/24
- Correct ZFS hostid (007f0101) to resolve hostid warnings
- Btrfs root filesystem on nvme0n1p5 (810GB)
- Comprehensive migration plan with rollback procedures

The migration is designed to be safe with Arch Linux remaining bootable
as a fallback until NixOS is verified stable.
2025-12-29 23:50:36 -08:00
c480bcdd1d Disable virtual surround
This was breaking microphone access when I was headed into a voice chat
2025-12-29 23:46:20 -08:00
05fed3ede1 Add virtual 4.1 surround sound configuration for zix790prors
Create a PipeWire virtual surround sink that routes audio to multiple
physical outputs:
- FL/FR channels → AmazonBasics USB speaker
- RL/RR channels → Fosi BT20A PRO Bluetooth speaker
- LFE channel → AmazonBasics (duplicated to both channels)

Uses loopback modules with systemd services to maintain correct routing,
as PipeWire's target.object parameter doesn't auto-connect properly.
A timer checks every 10 seconds and fixes incorrect connections.

Configuration is machine-specific and isolated in virtual-surround.nix.
2025-12-29 12:01:29 -08:00
0a9de8d159 Fix rbw-agent launching from systemd services
The rbw unlock systemd services were failing to launch the rbw-agent
daemon due to two issues:

1. Missing RBW_AGENT environment variable - rbw looks for this variable
   to locate the agent binary, falling back to PATH lookup. Systemd
   user services have minimal environments without the necessary PATH.

2. Default KillMode=control-group - when the oneshot service completed,
   systemd was killing all processes in the cgroup including the
   daemonized agent.

Fixed by:
- Setting RBW_AGENT environment variable to explicit agent binary path
- Using KillMode=process to only kill the main process, allowing the
  spawned agent daemon to persist after service completion
2025-12-29 10:21:58 -08:00
055d6ab421 Add systemd services to unlock rbw vault on login and resume
Adds two systemd user services to automatically unlock the rbw vault:
- rbw-unlock-on-login: Runs at graphical session start
- rbw-unlock-on-resume: Runs after resuming from suspend

This solves the issue of mbsync prompting for password every 5 minutes.
Once unlocked, the vault stays unlocked as long as mbsync syncs every
5 minutes (which resets the 1-hour lock timeout). Only prompts at login
or after long suspend periods.
2025-12-26 13:20:18 -08:00
d5c6342b84 [home-desktop] Add email role 2025-12-25 09:54:20 -08:00
e04dacdf65 Add email support with notmuch, mbsync, and msmtp
Set up complete email workflow in Emacs using:
- notmuch for email indexing and UI
- mbsync for IMAP synchronization from proton.johnogle.info
- msmtp for SMTP sending via port 25 with PLAIN auth
- systemd timer for automatic sync every 5 minutes

Configuration includes:
- New email role at home/roles/email with all mail tools
- Doom Emacs notmuch module enabled with saved searches
- Secure credential retrieval via rbw from Bitwarden
- Fixed systemd service PATH to access rbw-agent
- TLS/STARTTLS for secure connections

Email role enabled on nix-book (laptop-compact profile).
2025-12-22 23:00:23 -08:00
7d74917bdc [doom] update 2025-12-22 14:36:14 -08:00
5a4ab71849 Add script to update doomemacs to latest commit
Creates update-doomemacs.sh script that:
- Fetches latest commit SHA from doomemacs/doomemacs repo
- Automatically detects the default branch
- Updates both rev and sha256 in home/roles/emacs/default.nix
- Works from anywhere in the repo using git rev-parse

Also adds a flake app so it can be run with:
  nix run .#update-doomemacs
2025-12-22 14:34:56 -08:00
bcebf9b376 claude-code: Update to version 2.0.75 2025-12-22 14:27:10 -08:00
0f76939983 Increase jellyfinScaleFactor to 2.5 for boxy 2025-12-22 12:30:36 -08:00
a1da2f5cc1 Fix jellyfinScaleFactor for .desktop entry launches 2025-12-22 12:30:35 -08:00
175da48170 Fix jellyfinScaleFactor implementation in kodi role
The original implementation had several issues that prevented it from
building:
- Used buildInputs instead of nativeBuildInputs for makeWrapper
- Referenced wrong executable name (jellyfinmediaplayer vs jellyfin-desktop)
- Used wrapProgram which doesn't work with symlinks from symlinkJoin

Fixed by using makeWrapper directly with the correct executable path
after removing the symlink.

Also enabled jellyfinScaleFactor = 1.5 on boxy for UI scaling.
2025-12-22 12:17:54 -08:00
ac956ef48c [flake] update 2025-12-22 12:03:31 -08:00
0c1190f39c Add jellyfinScaleFactor option to kodi role for UI scaling 2025-12-22 12:02:15 -08:00
00f05d1bb2 [i3+sway] Setup brightness controll for ddc monitors 2025-12-20 11:52:27 -08:00
4e6c6ab81d Make nix gc options overridable with mkDefault 2025-12-08 14:34:53 -08:00
04e1a8563c Fix sketchybar memory indicator to show actual memory pressure
The previous implementation included inactive/cached pages and used
"Pages stored in compressor" (uncompressed size), resulting in inflated
percentages (~88%) that didn't reflect actual memory pressure.

Now uses:
- Anonymous pages (matches Activity Monitor's "App Memory")
- Pages wired down (system memory)
- Pages occupied by compressor (actual RAM used, not uncompressed size)

Also switches to awk for arithmetic to avoid bash integer overflow on
systems with >4GB RAM.
2025-12-08 14:34:22 -08:00
7278dc8306 Fix deprecated NixOS options to resolve flake check warnings
- Update system attribute to stdenv.hostPlatform.system in overlays
- Migrate git config to new settings structure (user.name, user.email)
- Move ssh.addKeysToAgent to matchBlocks configuration
- Disable ssh default config to prevent future deprecation warnings
2025-12-07 22:17:54 -08:00
066eea2999 [kodi] Fix lib.warn syntax for insecure package warning
Fix syntax error by wrapping permittedInsecurePackages list with
lib.warn function call. The warning now properly displays during
evaluation about qtwebengine-5.15.19 being required for
jellyfin-media-player until it migrates to qt6.
2025-12-07 21:11:25 -08:00
80633142fb [kodi] Fix jellyfin 2025-12-07 20:55:34 -08:00
3029e3d9a8 [kodi] jellyfin > jellyfin-media-player 2025-12-07 20:53:59 -08:00
3483e26bce Add 3D printing home role with orca-slicer and openscad
Create a new home-manager role for 3D printing applications including
orca-slicer for slicing and openscad-unstable for 3D modeling. Enable
the role in the desktop configuration for full-featured experience.
2025-12-06 19:22:37 -08:00
b3add6ddf8 Add automatic garbage collection for home-manager user profiles
Configure nix.gc to automatically clean up old home-manager generations
weekly, which complements the existing system-level gc. Uses
--delete-older-than 10d on Linux to maintain a rollback window, but
overrides to -d on Darwin to work around a launchd bug where multi-part
options aren't properly split into separate arguments.
2025-12-06 13:58:26 -08:00
89994e3fc8 Update SketchyBar styling to match Waybar appearance
- Change font from SF Mono to Fira Code for consistency with Waybar
- Reduce bar height from 32px to 30px
- Switch to solid dark gray background (#333333) instead of semi-transparent black
- Remove rounded corners on items (corner_radius 0 instead of 5)
- Make item backgrounds full height (30px) to match bar height
- Remove all separator items between modules for cleaner appearance
- Add topmost and sticky properties to prevent window shadows from darkening bar
- Adjust aerospace bottom gap from 40px to 38px to account for new bar height
- Set all workspace text to white with bold font for active workspace
2025-12-05 14:58:11 -08:00
0e9671a45f Fix SketchyBar workspace indicators not appearing after 25.11 upgrade
Add explicit `drawing=on` to all workspace item states (initial creation,
focused, non-empty, and empty) to prevent items from getting stuck with
`drawing=off`. With `updates=when_shown`, items with `drawing=off` never
run their update scripts, causing workspaces that start empty to never
appear even when focused or given windows.

Changes:
- Add drawing=on to initial workspace creation (line 393)
- Add drawing=on to focused workspace state (line 568)
- Add drawing=on to empty workspace state (line 582)
- Add drawing=on to non-empty workspace state (line 591)
- Update comment for empty workspace to explain drawing=on usage

This ensures workspace indicators automatically appear within 2 seconds
when focused or given windows, without needing manual `sketchybar --update`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 14:44:23 -08:00
f4078970b2 Merge branch '25.11'
* 25.11:
  Simplify aerospace launchd config and remove menu bar hiding
  Remove mbedtls_2 workaround for dolphin packages
  Re-enable packages after NixOS 25.11 upgrade
  Add --unsupported-gpu flag to Sway for zix790prors compatibility
  Fix deprecated package names for NixOS 25.11
  Upgrade NixOS to 25.11 and fix Jovian module organization
2025-12-05 14:20:49 -08:00
0ae4d84ca2 Simplify aerospace launchd config and remove menu bar hiding
- Remove menu bar hiding code (no longer needed for SketchyBar)
- Use built-in programs.aerospace.launchd.enable instead of custom agent
- Remove redundant launchd.agents.aerospace configuration block
2025-12-05 14:13:05 -08:00
7c877fde84 Remove mbedtls_2 workaround for dolphin packages
Dolphin-emu and dolphin-emu-primehack now use maintained mbedtls version
3.6.5 instead of unmaintained mbedtls_2. No longer need to permit insecure
packages.

Verified both packages build successfully without the workaround.
2025-12-04 21:22:49 -08:00
d53286e04c Re-enable packages after NixOS 25.11 upgrade
Re-enable dolphin-emu-primehack now that binary build is fixed in 25.11.
Re-enable fluffychat as security issues have been resolved in nixpkgs 25.11.

Both packages verified to build and function correctly.
2025-12-04 21:22:41 -08:00
bc42c4dc77 Fix SketchyBar workspace indicators not reappearing
The aerospace workspace plugin had two issues preventing workspace
indicators from properly showing/hiding:

1. The script expected workspace number as $1 but update_freq routine
   calls only provide $NAME environment variable. Now extracts from
   either source.

2. Using drawing=off to hide workspaces was unreliable - items wouldn't
   consistently reappear. Now uses width=0 with cleared icon/label
   content to collapse items instead.

Workspaces now properly appear within 2 seconds when windows are
created or moved, without requiring manual sketchybar --update.
2025-12-04 16:31:44 -08:00
445b0cd558 Add --unsupported-gpu flag to Sway for zix790prors compatibility 2025-12-04 16:30:55 -08:00
6d9686f14b Fix deprecated package names for NixOS 25.11
- Remove amdvlk from boxy config (replaced by RADV, enabled by default)
- Rename vaapiVdpau to libva-vdpau-driver in wixos config
- Fixes nix flake check errors
2025-12-04 16:21:43 -08:00
4164832eea Upgrade NixOS to 25.11 and fix Jovian module organization
- Update flake inputs from 25.05 to 25.11 (nixpkgs, home-manager, nix-darwin)
- Remove Jovian compatibility shim that's no longer needed
- Move SteamOS configuration to only import in nix-deck machine
- Fixes jovian module not found error during nixos-rebuild
2025-12-04 16:12:58 -08:00
585f9ef5c7 Remove macOS menu bar hiding from aerospace SketchyBar config 2025-12-04 10:51:12 -08:00
ade60ba5ec Add macOS Ctrl keyboard shortcuts with terminal-aware overrides
Implement Linux-style Ctrl shortcuts (Ctrl+C/V/X/Z for clipboard, Ctrl+N/T/W
for navigation, etc.) while preserving terminal behavior where Ctrl+C sends
SIGINT. Uses per-app NSUserKeyEquivalents to remap Ghostty back to Cmd for
clipboard operations.

Also consolidate aerospace configuration by moving spans-displays preference
from system-level module to home-manager role, allowing full aerospace
configuration to live in home-manager for better modularity.
2025-12-03 10:30:39 -08:00
48fb7cdada Add SketchyBar integration to aerospace with bottom bar positioning
Integrate SketchyBar status bar with aerospace window manager, providing
a native macOS status bar replacement with workspace indicators and system
monitoring. Key features:

- Add sketchybar.enable option to aerospace module
- Install sketchybar package and fonts conditionally
- Create main sketchybarrc with i3/sway color theme
- Position bar at bottom with 40px bottom gap
- Implement workspace indicators with dynamic visibility:
  - Hide empty workspaces
  - Show focused workspace with blue highlight
  - Show non-empty workspaces with inactive styling
  - Use centered icons with fixed 32px width
- Add system monitoring plugins: CPU, memory, disk, battery, volume, clock
- Integrate menu bar extras (Bluetooth, WiFi) as aliases
- Configure aerospace to trigger workspace change events
- Hide native macOS menu bar when SketchyBar enabled
- Set up launchd agent for auto-start
- Use SF Mono Regular 13.0 font matching waybar aesthetic
2025-12-03 09:51:22 -08:00
2d8cfe75a0 Align aerospace keybindings with i3+sway and add resize mode
- Enable programs.aerospace to ensure config generation
- Disable normalizations for pure i3-style tree management
- Update layout keybindings to match i3+sway:
  - cmd-w: accordion-horizontal (tabbed)
  - cmd-s: accordion-vertical (stacking)
  - cmd-e: tiles layout with orientation toggle
- Replace direct resize bindings with cmd-r resize mode
- Add resize mode with hjkl directional controls
2025-12-02 16:07:12 -08:00
385fd798de Fix aerospace namespace conflict and claude-code override
Rename custom aerospace module from services.aerospace to roles.aerospace
to avoid conflicting with nix-darwin's built-in aerospace service support.

Move claude-code package override to flake-level overlay to ensure the
GCS-distributed version is used instead of the npm registry version in
unstable. This is necessary for corporate environments where npm registry
access may be blocked.
2025-12-02 15:16:55 -08:00
fe6558e0c1 Refactor: Extract platform-specific roles to base-linux and base-darwin
Create base-linux and base-darwin modules to cleanly separate platform-
specific role imports from shared roles. This prevents importing modules
that require platform-specific home-manager modules (like plasma-manager
on NixOS) in environments where they don't exist (like nix-darwin).

- base-linux includes: plasma-manager, i3+sway
- base-darwin includes: aerospace
- roles/default.nix now only contains truly cross-platform roles

This architecture makes it immediately clear which roles are shared
versus platform-specific and makes it easy to add new platform-specific
roles in the future.
2025-12-02 15:16:42 -08:00
b9c48f9dd1 Complete migration of home-manager modules to roles
Migrate all remaining home-manager modules from home/modules/ to home/roles/
to establish a unified role-based configuration pattern. This completes the
migration started in Phase 1.

Changes:
- Phase 1-3: Migrated tmux, plasma-manager, kubectl, and emacs to roles
- Phase 4: Migrated aerospace with custom options under home.roles.aerospace.*
- Phase 5: Migrated i3+sway with shared config and override options
- Phase 6: Removed empty home/modules/ directory

All home configs now import only ./roles with role-based enable options.
Updated flake.nix machine-specific overrides to use new namespaces.

Verified with nix flake check - all configurations build successfully.
2025-12-02 14:34:11 -08:00
34351403d1 Extract aerospace configuration into reusable modules
Create both home-manager and nix-darwin modules for aerospace window
manager configuration, removing 110+ lines of duplicated config from
machine-specific files.

Changes:
- Add home/modules/aerospace module with configurable leader key
- Add modules/aerospace.nix for system-level macOS settings
- Include autoraise configuration in home module
- Update home-darwin-work.nix to use new modules
- Update johno-macbookpro configuration to use system module
- Remove inline aerospace/autoraise config and launchd agents
2025-12-01 19:02:00 -08:00
12820ce9ff [aerospace] Disable macOS Spaces mutli-display
This option is known to cause all sorts of issues with aerospace
apparently
2025-12-01 18:18:56 -08:00
0f5eb2e572 [i3+sway] Add focus child binding 2025-12-01 17:39:33 -08:00
f356c91fdb [gaming] Remove heavy build packages
dolphin-emu-primehack will be available as a pre-build in 25.11

retroarch-full -- in general I'm wondering of using steam>retroarch is a
better experience
2025-11-29 11:30:10 -08:00
6b42612135 [gaming] Use recommended steam setup
Updated to use programs.steam recommended from the NixOS wiki
2025-11-29 11:29:43 -08:00
50a8c44d10 [launchers] Add launcher wrappers for compact env
Adds a module to launch specific nixpkgs dynamically, such that they
won't be always included in the nix store
2025-11-29 11:28:04 -08:00
7011fb27a5 Update workspace button background color in waybar 2025-11-26 17:20:55 -08:00
1ff8b81f44 Add environment-specific claude-code package distribution system
Development environments now use standard nixpkgs claude-code by default.
Work environments override with custom binary distribution to bypass
corporate npm registry restrictions via Google Cloud Storage.
2025-11-26 17:20:31 -08:00
55f13dfb08 Rename claude-cli package to claude-code for consistency with nixpkgs 2025-11-26 17:20:17 -08:00
63bf19b85f [claude-code] update 2025-11-25 13:39:02 -08:00
1f9e9138ab Add automated update script for claude-cli package
Create update.sh script that fetches the latest claude-code version
and SHA256 hashes from Homebrew cask and automatically updates the
Nix package definition.

Features:
- Fetches version and all 4 platform SHA256 hashes from Homebrew
- Updates default.nix in-place using awk for reliable parsing
- Validates extracted data before updating
- Supports --dry-run mode to preview changes
- Supports --help flag for usage information
- Idempotent (safe to run multiple times)

Update README to document the automated update method as the
recommended approach, with manual update as a fallback option.
2025-11-25 13:39:02 -08:00
e218822566 Add configurable Claude Code model selection
Add allowArbitraryClaudeCodeModelSelection option to development role
to control whether model specifications are preserved in humanlayer
commands and agents. Defaults to false (strip models) for backward
compatibility. Enable on home-darwin-work to preserve model specs
for opus/sonnet selection.
2025-11-25 13:39:02 -08:00
e88f3580e9 Add thoughts directory to gitignore 2025-11-25 13:39:01 -08:00
5451e75480 Add custom claude-cli package to bypass npm registry restrictions
Create custom Nix package for Claude Code CLI that fetches directly from
Anthropic's Google Cloud Storage distribution instead of npm registry,
working around Block's Cloudflare Teams dependency confusion protection.

- Add claude-cli package with platform-specific binaries
- Include comprehensive README with update instructions
- Enable development role on work machine
- Switch from unstable.claude-code to custom.claude-cli
- Add google-cloud-sdk to work machine packages
2025-11-25 13:38:53 -08:00
fc9474a7c9 [home] Create explicit kubectl role
This allows work machines to enable development, while not including
non-work-related kubectl management stuffs.
2025-11-25 13:37:40 -08:00
20daebbd61 Add ccr and unstable codex 2025-11-24 22:16:12 -08:00
3be23304c4 [development] Fix humanlayer plugins
Removes model specification to better support Claude Code Pro plan.
Because of this, I also re-removed this role from home-darwin-work as we
can take advantage of more powerful models in that context. We will just
need to install the plugins external from nix. Eventually I can turn
this into a config option of course.

Also made sure we are installing the agents from the plugin in addition
to the commands.
2025-11-22 09:47:55 -08:00
9059a739a0 [development] Add claude code plugins 2025-11-22 09:32:54 -08:00
977125645b Remove transition documentation 2025-11-19 19:43:59 -08:00
a9772259f0 [i3] Setup brightness control with ddcutil 2025-11-19 19:41:50 -08:00
4f6d65316a Fixes for stable systems 2025-11-19 19:41:34 -08:00
0b8e3bf527 [steamos] Add virtual malitt-keyboard 2025-11-18 08:56:05 -08:00
d3c906134b [nix-deck] setup stuff 2025-11-18 08:55:49 -08:00
30b616dd93 [gaming] Always include emulators 2025-11-18 08:55:13 -08:00
c9252c42c2 [i3+sway] Only launch waybar in sway 2025-11-18 08:54:46 -08:00
fa7cb55c78 [nix-deck] Use NixOS unstable for better Jovian compatibility
- Switch nix-deck to use nixpkgs-unstable and unstable home-manager/plasma-manager
- Remove jovian-compat.nix shim (not needed on unstable)
- Add bitwarden-desktop compatibility overlay for stable/unstable coexistence
- Update hardware-configuration.nix with actual Steam Deck hardware detection
- Add 8GB swap file configuration
- Configure AMD CPU microcode updates

This allows nix-deck to work with the latest Jovian-NixOS while keeping other
machines on stable 25.05.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 16:40:11 -08:00
2283b0a6df Add Steam Deck (nix-deck) configuration with Jovian-NixOS and remote building
- Add Jovian-NixOS integration for Steam Deck hardware support
- Create nix-deck machine configuration with SteamOS role
- Add jovian-compat.nix for NixOS 25.05 compatibility (remove in 25.11+)
- Create remote-build role for distributed builds
- Configure zix790prors as build host
- Configure nix-book and nix-deck to use remote builder with fallback
- Add comprehensive setup documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 16:06:20 -08:00
4ea9437bb0 [zix790prors] Clean up some comments 2025-11-17 16:05:39 -08:00
d0760a22bd [i3+sway] Replace i3status with i3blocks to replicate waybar
Created i3blocks configuration with modules matching waybar setup including disk, CPU, memory, pulseaudio, backlight, network, battery, and clock. Applied matching color scheme and workspace button styling. i3blocks works with i3wm unlike waybar which only supports sway.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 16:05:29 -08:00
f67a12c29a [gaming] Add additional emulators 2025-11-17 15:36:20 -08:00
fc8a43504d [media] Add ncspot 2025-11-15 14:11:12 -08:00
4a73b3a5ae [i3+sway] Customize waybar 2025-11-15 12:40:53 -08:00
be68202523 [plasma-manager] Fix warning 2025-11-15 12:40:37 -08:00
608fed35ab [i3+sway] Some sway fixes 2025-11-15 12:15:42 -08:00
c2e2dd8675 [i3+sway] Use cmd+shift+f for fullscreen
This matches the aerospace configuration
2025-11-15 11:20:17 -08:00
5750f737f1 [darwin] Update screengrab keys
These conflict with aerospace space-management actions
2025-11-14 11:55:36 -08:00
c27518e0dc [darwin-work] Fix compost compatibility 2025-11-14 11:22:52 -08:00
7f318edc4d Switch from kitty to ghostty 2025-11-10 08:49:24 -08:00
f995240153 Fix flake.lock 2025-11-10 08:48:55 -08:00
d62bae0ddb Rename CLAUDE.md to AGENTS.md 2025-11-10 08:12:19 -08:00
79ae42f41d [nix-darwin] Pin to 25.05 2025-11-10 08:12:05 -08:00
0c15aad5c0 [development] Use claude unstable version 2025-11-08 13:12:37 -08:00
d87793d39b Fix live-usb networking configuration conflict
Disable networking.wireless to prevent conflict with NetworkManager.
The installation-cd-minimal base enables both, causing a build failure.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 13:10:07 -08:00
fad6e61aac Replace customPkgs with pkgs.custom and add pkgs.unstable and flake update 2025-11-08 13:07:19 -08:00
1bc65ceb51 [nix-book] Remove linuxPackages_latest to fix boot I/O timeouts
After downgrade from nixos-unstable to 25.05, nix-book experienced
I/O timeouts during early boot due to incompatible kernel drivers
for Thunderbolt/VMD hardware. Using stable kernel resolves the issue.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 09:06:47 -08:00
bda76c6abc [home] Add rofi configuration for i3+sway
- Add rofi package to desktop role with solarized theme
- Configure rofi with drun, run, and window modes
- Set up proper keybindings: Super+d for rofi (i3) and wofi (sway)
- Fix shared config structure to avoid undefined menu reference

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 19:12:15 -07:00
c42e09e972 [i3] Add compositor 2025-10-27 19:03:54 -07:00
4d986c0b48 [home] Add wallpaper 2025-10-27 19:03:46 -07:00
4e3fdd78d2 Fix KDE systemsettings default applications menu issue
KDE applications in i3 were showing only "Other..." in default
applications due to missing applications.menu file. Plasma 6 renamed
applications.menu to plasma-applications.menu but KDE components
still look for the old name.

This creates a symlink from the new location to the expected location
using XDG config file management.

Fixes kmail and systemsettings not detecting installed applications.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 19:02:26 -07:00
d3703fc5a9 Add XDG desktop portal configuration for i3 + KDE integration
Enables proper desktop integration services when using KDE applications
in i3 window manager, including file dialogs, theme integration, and
screen sharing capabilities.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 19:01:57 -07:00
35c2ebb592 [zix790prors] Set correct refresh rate for x11 2025-10-27 16:51:19 -07:00
569ac528a5 [zix790prors] Add i3 + fixes 2025-10-26 12:00:13 -07:00
6cc8fa4f5d [printing] Ensure network is live 2025-10-25 15:05:19 -07:00
67a82f14fd [nixos] Downgrade from unstable to 25.05 2025-10-25 15:05:08 -07:00
4b68e3f051 [darwin] Configure AutoRaise
Add delays. This works way better with accordion views where the cursor
is often hovering right around window boundaries
2025-10-16 15:29:41 -07:00
81a3657759 [darwin] Add AutoRaise
provides focus-follows-mouse
2025-10-16 10:00:12 -07:00
32e1b81034 [aerospace] Fix fullscreen chord
Cmd-F is too ingrained in muscle memory for Find
2025-10-15 11:14:50 -07:00
6f00c72540 [app-launcher-server] process detection fixes 2025-10-14 18:09:36 -07:00
d26007aa61 [aerospace] More tweaking 2025-10-14 13:43:44 -07:00
1caa8bba3e [aerospace] Further tweaks 2025-10-14 08:26:51 -07:00
d3cb09040a [kodi] Fix autologin for boxy 2025-10-13 14:32:28 -07:00
4bfacffa17 [development] Remove goose 2025-10-13 14:26:08 -07:00
a6961f05ca [app-launcher] Add app-launcher to boxy 2025-10-13 14:25:51 -07:00
b75c43257b [zix790prors] Increase boot partition size 2025-10-04 09:06:18 -07:00
63d741e55e [media] Use delfin instead of jellyfin
This is potentially temporary. But jellyfin-media-player has been
flagged as having a security problem by using an old version of
qtwebengine. So we'll maybe find time to try out delfin or maybe just
wait until the security issue gets fixed or maybe just use the web
browser. We have options
2025-10-01 21:35:18 -07:00
9ed3ad8fb8 Fix linux builds after nix-darwin updates 2025-10-01 21:35:18 -07:00
330cd6f92b Add /boot partition expansion plan and temporary configurationLimit fix
- Add comprehensive BOOT_EXPANSION_PLAN.md with step-by-step instructions for safely expanding /boot from 100MB to 1GB
- Temporarily reduce configurationLimit to 1 to resolve immediate /boot space crisis
- Plan supports 20 generations (configurationLimit = 20) after expansion
- Uses safe approach: shrink /games partition, create new EFI partition
- Includes full recovery context and troubleshooting information

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-01 21:35:18 -07:00
3b9d9ed60b [org-agenda] Add shortcuts for day/week view 2025-10-01 15:22:02 -07:00
2a017f584b [home-work] Fix shell integration 2025-09-30 09:01:57 -07:00
47c4e0097e [home-work] Fix remaining conflicts
claude and other ai tools should follow standard install process on work machines
2025-09-30 08:36:36 -07:00
2b7cf66e69 [home-work] Fix bash/zsh init conflicts 2025-09-30 08:33:38 -07:00
eed88a9ac0 [flake] update 2025-09-30 08:33:28 -07:00
c02b74cce4 [work] Fix conflict with salt
Configured nix-darwin to write shell configuration to .local
variants (/etc/bash.local, /etc/zshrc.local, /etc/zshenv.local) instead
of managing the main shell files. This allows Salt (corporate laptop
management) to manage /etc/bashrc, /etc/zshrc, and /etc/zshenv while
nix-darwin provides the Nix environment setup through the .local files
that Salt already sources.
2025-09-30 08:27:32 -07:00
1132dda8a0 [machines] Setup new work laptop 2025-09-29 17:11:13 -07:00
2696262ed3 [emacs] Add TODO comment 2025-09-29 17:06:13 -07:00
40ac30c78f Update CLAUDE.md 2025-09-23 07:53:01 -07:00
ac986e37e7 [doom] Org agenda use log mode by default 2025-09-22 18:11:52 -07:00
d3664fcf9d [darwin] Add nix-darwin for work laptop
Adds nix-darwin
Simplifies emacs tree-sitter
Probably breaks vterm on linux :(
2025-09-22 17:21:45 -07:00
5591087be1 [desktop] Add more KDE apps 2025-09-22 08:27:08 -07:00
09a701989c [plasma-manager] Fix view_actual_size shortcut 2025-09-22 08:26:53 -07:00
62dbf84b4b [emacs] Prebuild tree-sitter grammars 2025-09-22 08:26:31 -07:00
81799cd6d2 [doom] Switch theme to Tokyo Night 2025-09-20 16:19:02 -07:00
35d965e432 [doom] Disable emoji
This is deprecated and not useful for recent versions of emacs
2025-09-20 16:18:46 -07:00
2c4e6cc060 [doom] Auto install nerd-icons fonts 2025-09-20 16:18:30 -07:00
5c6dba77f0 [doom] Fix doom sync behavior 2025-09-20 16:18:13 -07:00
a6effa3944 [plasma-manager] Add focus-follows-mouse 2025-09-20 15:30:43 -07:00
0d53b86fcb [desktop] Add KDE PIM and Email apps 2025-09-20 15:16:56 -07:00
6bf5c502d8 [plasma-manager] Add krohnkite
Adds korhnkite for automatic tiling WM behavior plus each keyboard
access to windows all within plasma
2025-09-20 15:02:19 -07:00
05592a9ec2 [home-laptop-compact] Enable media 2025-09-20 14:14:35 -07:00
923aaf9e95 [nix-book] Replace OpenVPN with WireGuard wg-quick
Remove services.openvpn.servers and add networking.wg-quick.interfaces.
Configure ogleNet to use /root/Oglehome-VPN-johno-nixbook.conf as the
WireGuard config file.
2025-09-17 22:04:42 -07:00
23b1c450a2 [claude] Add Important Notes about sudo access
Add an "Important Notes" section to CLAUDE.md clarifying that Claude Code does not have sudo access and that users should run elevated commands themselves (e.g., `sudo nixos-rebuild switch`).
2025-09-17 22:03:27 -07:00
a65a8e9af7 [nix-book] Add OpenVPN configuration 2025-09-13 07:24:24 -07:00
56b1111f54 [zix790prors] More local LLM updates
Using qwen3:30b explicitly. The default "qwen3" from ollama was pulling
a very outdated model apparently (qwen3:8b). qwen3:4b and qwen3:30b are the newest.

Also sets up some defaults for gptel that have been useful
2025-09-11 08:39:36 -07:00
6bf0a37533 [doom] Add gptel-tool-library package
Integrate gptel-tool-library by:
- Adding package definition to packages.el with custom recipe
- Configuring library settings and module loading in config.el
- Enabling module-specific functionality for bbdb, buffer, etc.
2025-09-10 07:39:33 -07:00
396c8e0318 [doom] Configure gptel for tool calling
Adds some basic tools for experimenting
2025-09-10 06:57:25 -07:00
b359acfcf0 [doom] Add multiple-cursors 2025-09-10 06:57:06 -07:00
455181365a [nvidia] Add role 2025-09-10 06:56:56 -07:00
06dd292524 [zix790prors] Migrate README to org-mode 2025-09-08 19:31:11 -07:00
8924fdbc6d [zix790prors] Add README
Include detailed specifications and BIOS settings for the NixOS install. Document the dual-boot setup with Windows 11 and shared partitions.
2025-09-08 15:45:34 -07:00
671dc229de [doom] Add Ollama local configuration to gptel
Configure gptel to use Ollama local with specified host and models.
2025-09-08 08:39:34 -07:00
14cdee1468 [zix790prors] Add ollama 2025-09-08 08:02:37 -07:00
c6276c9758 [doom] update 2025-09-08 08:02:30 -07:00
31880e21e8 [emacs] Enable extended unicode support 2025-09-08 07:11:35 -07:00
3d95995ebc [emacs] Use llm doom module for gptel 2025-09-08 07:10:51 -07:00
fb9dd66cf4 [home] Refactor into roles 2025-09-07 11:24:23 -07:00
a90b30eb1c [tea-rbw] Bug fixes 2025-09-06 19:02:25 -07:00
b22a4952e5 [audio] Add easyeffects
Audio EQ package
2025-09-06 19:01:59 -07:00
32f70d46b2 [doom] Add claude-code-ide
The package requires vterm so this is included
2025-09-01 10:46:25 -07:00
d76e9e73f5 [doom] Attempt config with symlink
I'm not 100% this works yet. But the idea here is to symlink from the
config directory into where we are locally storing the doom config. The
intention here is to enable `doom sync` to work without requiring a full
NixOS rebuild
2025-09-01 10:45:18 -07:00
53504ffde3 [printing] Switch from bonjour 2025-08-27 06:58:02 -07:00
e5be1b5675 [home] Update syncthingtray config
Change from explicitly launching syncthingtray to just having it
accessible. I attempted to get plasma-manager to automatically add the
plasmoid but was unsuccessful.
2025-08-25 07:57:17 -07:00
878962ad41 [zix790prors] Add printing 2025-08-25 07:56:59 -07:00
6d5eadcf6a [zix790prors] Update boot configuration 2025-08-25 07:56:43 -07:00
c323d1301b [gaming] ryujink>ryubing
required from prior flake update
2025-08-25 07:56:22 -07:00
6cdbd2e300 [flake] update 2025-08-25 07:56:14 -07:00
f0bf2f2d8c [gaming] Add an emulation suboption 2025-08-20 19:27:00 -07:00
9d6abce8cc [zix790prors] Fix clock timezone thing 2025-08-18 19:19:34 -07:00
68f63db930 [zix790prors] Fix beesd hash table size configs 2025-08-17 19:44:17 -07:00
2e39984d14 [zix790prors] Add new machine 2025-08-17 15:24:05 -07:00
9fed36e6ee [kubectl] Add home module 2025-08-02 10:41:35 -07:00
38a8997448 [btrfs] Add btrfs-progs and compsize 2025-07-30 08:07:31 -07:00
120 changed files with 8781 additions and 1146 deletions

44
.beads/.gitignore vendored Normal file
View File

@@ -0,0 +1,44 @@
# SQLite databases
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm
# Daemon runtime files
daemon.lock
daemon.log
daemon.pid
bd.sock
sync-state.json
last-touched
# Local version tracking (prevents upgrade notification spam after git ops)
.local_version
# Legacy database files
db.sqlite
bd.db
# Worktree redirect file (contains relative path to main repo's .beads/)
# Must not be committed as paths would be wrong in other clones
redirect
# Merge artifacts (temporary files from 3-way merge)
beads.base.jsonl
beads.base.meta.json
beads.left.jsonl
beads.left.meta.json
beads.right.jsonl
beads.right.meta.json
# Sync state (local-only, per-machine)
# These files are machine-specific and should not be shared across clones
.sync.lock
sync_base.jsonl
# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here.
# They would override fork protection in .git/info/exclude, allowing
# contributors to accidentally commit upstream issue databases.
# The JSONL files (issues.jsonl, interactions.jsonl) and config files
# are tracked by git by default since no pattern above ignores them.

0
.beads/.sync.lock Normal file
View File

81
.beads/README.md Normal file
View File

@@ -0,0 +1,81 @@
# Beads - AI-Native Issue Tracking
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
## What is Beads?
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
## Quick Start
### Essential Commands
```bash
# Create new issues
bd create "Add user authentication"
# View all issues
bd list
# View issue details
bd show <issue-id>
# Update issue status
bd update <issue-id> --status in_progress
bd update <issue-id> --status done
# Sync with git remote
bd sync
```
### Working with Issues
Issues in Beads are:
- **Git-native**: Stored in `.beads/issues.jsonl` and synced like code
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
- **Branch-aware**: Issues can follow your branch workflow
- **Always in sync**: Auto-syncs with your commits
## Why Beads?
**AI-Native Design**
- Built specifically for AI-assisted development workflows
- CLI-first interface works seamlessly with AI coding agents
- No context switching to web UIs
🚀 **Developer Focused**
- Issues live in your repo, right next to your code
- Works offline, syncs when you push
- Fast, lightweight, and stays out of your way
🔧 **Git Integration**
- Automatic sync with git commits
- Branch-aware issue tracking
- Intelligent JSONL merge resolution
## Get Started with Beads
Try Beads in your own projects:
```bash
# Install Beads
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
# Initialize in your repo
bd init
# Create your first issue
bd create "Try out Beads"
```
## Learn More
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
- **Quick Start Guide**: Run `bd quickstart`
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
---
*Beads: Issue tracking that moves at the speed of thought*

64
.beads/config.yaml Normal file
View File

@@ -0,0 +1,64 @@
# Beads Configuration File
# This file configures default behavior for all bd commands in this repository
# All settings can also be set via environment variables (BD_* prefix)
# or overridden with command-line flags
# Issue prefix for this repository (used by bd init)
# If not set, bd init will auto-detect from directory name
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
issue-prefix: "x"
# Use no-db mode: load from JSONL, no SQLite, write back after each command
# When true, bd will use .beads/issues.jsonl as the source of truth
# instead of SQLite database
# no-db: false
# Disable daemon for RPC communication (forces direct database access)
# no-daemon: false
# Disable auto-flush of database to JSONL after mutations
# no-auto-flush: false
# Disable auto-import from JSONL when it's newer than database
# no-auto-import: false
# Enable JSON output by default
# json: false
# Default actor for audit trails (overridden by BD_ACTOR or --actor)
# actor: ""
# Path to database (overridden by BEADS_DB or --db)
# db: ""
# Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON)
# auto-start-daemon: true
# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE)
# flush-debounce: "5s"
# Git branch for beads commits (bd sync will commit to this branch)
# IMPORTANT: Set this for team projects so all clones use the same sync branch.
# This setting persists across clones (unlike database config which is gitignored).
# Can also use BEADS_SYNC_BRANCH env var for local override.
# If not set, bd sync will require you to run 'bd config set sync.branch <branch>'.
sync-branch: "beads-sync"
# Multi-repo configuration (experimental - bd-307)
# Allows hydrating from multiple repositories and routing writes to the correct JSONL
# repos:
# primary: "." # Primary repo (where this database lives)
# additional: # Additional repos to hydrate from (read-only)
# - ~/beads-planning # Personal planning repo
# - ~/work-planning # Work planning repo
# Integration settings (access with 'bd config get/set')
# These are stored in the database, not in this file:
# - jira.url
# - jira.project
# - linear.url
# - linear.api-key
# - github.org
# - github.repo
routing.mode: "explicit"

0
.gitattributes vendored Normal file
View File

107
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,107 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: https://git.johnogle.info/johno/gitea-actions/nix-setup@v1
- name: Check flake
run: nix flake check
env:
NIX_CONFIG: "access-tokens = git.johnogle.info=${{ secrets.GITEA_ACCESS_TOKEN }}"
build-and-cache:
runs-on: ubuntu-latest
needs: check
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v6
- uses: https://git.johnogle.info/johno/gitea-actions/nix-setup@v1
- name: Setup SSH for cache
run: |
mkdir -p ~/.ssh
echo "${{ secrets.CACHE_SSH_KEY }}" > ~/.ssh/cache_key
chmod 600 ~/.ssh/cache_key
ssh-keyscan -H ${{ secrets.CACHE_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
- name: Setup signing key
run: |
echo "${{ secrets.NIX_SIGNING_KEY }}" > /tmp/signing-key
chmod 600 /tmp/signing-key
- name: Build, sign, and cache all packages
run: |
PACKAGES=(
custom-claude-code
custom-app-launcher-server
custom-mcrcon-rbw
custom-tea-rbw
custom-rclone-torbox-setup
custom-nextcloud-talk-desktop
qt-pinned-jellyfin-media-player
qt-pinned-stremio
nix-deck-kernel
plasma-bigscreen
)
FAILED=()
SKIPPED=()
for pkg in "${PACKAGES[@]}"; do
echo "::group::Building $pkg"
# Check if package is already cached by evaluating its store path and checking the remote
OUT_PATH=$(nix eval ".#$pkg.outPath" --raw 2>/dev/null)
if [ -n "$OUT_PATH" ] && ssh -i ~/.ssh/cache_key ${{ secrets.CACHE_USER }}@${{ secrets.CACHE_HOST }} \
"nix path-info '$OUT_PATH' >/dev/null 2>&1"; then
echo "⏭ $pkg already cached ($OUT_PATH), skipping"
SKIPPED+=("$pkg")
echo "::endgroup::"
continue
fi
# --cores 2 limits parallel jobs to reduce RAM pressure on john-endesktop
if BUILD_OUTPUT=$(nix build ".#$pkg" --no-link --print-out-paths --cores 2 2>&1); then
OUT_PATH=$(echo "$BUILD_OUTPUT" | grep '^/nix/store/' | tail -1)
echo "$BUILD_OUTPUT"
echo "Store path: $OUT_PATH"
# Sign the closure
nix store sign --key-file /tmp/signing-key -r "$OUT_PATH"
# Push to cache
nix copy --to "ssh-ng://${{ secrets.CACHE_USER }}@${{ secrets.CACHE_HOST }}?ssh-key=$HOME/.ssh/cache_key" "$OUT_PATH"
# Create GC root to prevent garbage collection
OUT_HASH=$(basename "$OUT_PATH" | cut -d'-' -f1)
ssh -i ~/.ssh/cache_key ${{ secrets.CACHE_USER }}@${{ secrets.CACHE_HOST }} \
"mkdir -p /nix/var/nix/gcroots/ci-cache && ln -sfn $OUT_PATH /nix/var/nix/gcroots/ci-cache/${OUT_HASH}"
echo "✓ $pkg cached successfully"
else
echo "✗ $pkg failed to build"
FAILED+=("$pkg")
fi
echo "::endgroup::"
done
if [ ${#SKIPPED[@]} -gt 0 ]; then
echo "Skipped (already cached): ${SKIPPED[*]}"
fi
if [ ${#FAILED[@]} -gt 0 ]; then
echo "::error::Failed packages: ${FAILED[*]}"
exit 1
fi
env:
NIX_CONFIG: "access-tokens = git.johnogle.info=${{ secrets.GITEA_ACCESS_TOKEN }}"

7
.gitignore vendored
View File

@@ -1 +1,8 @@
result
thoughts
.beads
# Gas Town (added by gt)
.runtime/
.claude/
.logs/

View File

@@ -9,7 +9,7 @@ Directory Structure:
----------------------
• packages/ - Custom Nix packages leveraged across various configurations.
• roles/ - Role-based configurations (e.g., kodi, bluetooth) each with its own module (default.nix) for inclusion in machine setups.
• machines/ - Machine-specific configurations (e.g., nix-book, z790prors, boxy, wixos) including configuration.nix and hardware-configuration.nix tailored for each hardware.
• machines/ - Machine-specific configurations (e.g., nix-book, zix790prors, boxy) including configuration.nix and hardware-configuration.nix tailored for each hardware.
• home/ - Home-manager configurations for personal environments and application settings (e.g., home-nix-book.nix, home-z790prors.nix).
Design Principles:

227
AGENTS.md Normal file
View File

@@ -0,0 +1,227 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repository Overview
This is a NixOS configuration repository using flakes, managing multiple machines and home-manager configurations. The repository follows a modular architecture with reusable "roles" that can be composed for different machines.
## Architecture
### Flake Structure
- **flake.nix**: Main entry point defining inputs (nixpkgs, home-manager, plasma-manager, etc.) and outputs for multiple NixOS configurations
- **Machines**: `nix-book`, `boxy`, `zix790prors`, `live-usb`, `johno-macbookpro` (Darwin/macOS)
- **Home configurations**: Standalone home-manager configuration for user `johno`
### Directory Structure
- `machines/`: Machine-specific configurations with hardware-configuration.nix
- `roles/`: Modular system configurations (audio, bluetooth, desktop, users, etc.)
- `home/`: Home Manager configurations and user-specific modules
- `home/modules/`: User environment modules (emacs, i3+sway, plasma-manager, tmux)
- `packages/`: Custom package definitions
### Role-Based Configuration System
The repository uses a custom "roles" system where each role is a NixOS module with enable options:
- `roles.desktop`: Desktop environment with sub-options for X11, Wayland, KDE, gaming, SDDM
- `roles.audio`: Audio configuration
- `roles.bluetooth`: Bluetooth support
- `roles.users`: User account management
- `roles.virtualisation`: Virtualization setup
- `roles.kodi`: Kodi media center
- `roles.nvidia`: NVIDIA GPU configuration
- `roles.printing`: Printing support (CUPS)
- `roles.spotifyd`: Spotify daemon
- `roles.btrfs`: Btrfs filesystem configuration
- `roles.nfs-mounts`: NFS mount configuration
- `roles.darwin`: macOS-specific configurations
Example role usage in machine configuration:
```nix
roles = {
audio.enable = true;
desktop = {
enable = true;
gaming = true;
kde = true;
wayland = true;
};
users.enable = true;
};
```
### Home-Manager Role System
The repository also uses a modular home-manager role system for user-space configuration:
**Available Home Roles:**
- `home.roles.base`: Core CLI tools, git, ssh, bash, rbw (enabled everywhere)
- `home.roles.desktop`: GUI applications, Firefox, KDE services
- `home.roles.office`: LibreOffice, OpenSCAD (heavy packages)
- `home.roles.media`: VLC, Jellyfin, Moonlight (media consumption)
- `home.roles.development`: Custom packages, kubectl, development tools
- `home.roles.communication`: Element, Nextcloud Talk, Google cookie tools
- `home.roles.sync`: Syncthing service and tray (for file synchronization)
- `home.roles.kdeconnect`: KDE Connect for device integration
- `home.roles.gaming`: Gaming applications (future expansion)
**Role-Based Home Configurations:**
- `home-desktop.nix`: Full-featured desktop for development workstations
- `home-media-center.nix`: Living room media consumption and gaming setup (boxy)
- `home-laptop-compact.nix`: Essential tools only, excludes office/media for storage constraints (nix-book)
- `home-live-usb.nix`: Minimal setup for live environments, no persistent services
- `home-darwin-work.nix`: macOS work laptop configuration
**Machine-Specific Role Usage:**
- **nix-book**: Compact laptop → excludes office/media roles due to SSD space constraints
- **boxy**: Living room media center → optimized for media consumption, excludes sync/office (shared machine)
- **zix790prors**: All-purpose workstation → full desktop experience with all roles enabled
- **live-usb**: Temporary environment → only base + desktop roles, no persistent services
- **johno-macbookpro**: macOS work laptop → Darwin-specific configuration with development tools
## Common Commands
### Building and Switching Configurations
**NixOS (Linux):**
```bash
# Build and switch to a specific machine configuration
sudo nixos-rebuild switch --flake .#<hostname>
# Build without switching
nixos-rebuild build --flake .#<hostname>
# Build home-manager configuration only
home-manager switch --flake .#johno
```
**Darwin (macOS):**
```bash
# Build and switch to Darwin configuration
darwin-rebuild switch --flake .#johno-macbookpro
# Build without switching
darwin-rebuild build --flake .#johno-macbookpro
```
### Available Machine Configurations
- `nix-book`: Compact laptop with storage constraints, uses `home/home-laptop-compact.nix`
- `boxy`: Shared living room media center/gaming desktop with AMD GPU, uses `home/home-media-center.nix`
- `zix790prors`: Powerful all-purpose workstation (gaming, 3D modeling, development), dual-boots Windows 11 with shared btrfs /games partition, uses `home/home-desktop.nix`
- `live-usb`: Bootable ISO configuration, uses `home/home-live-usb.nix`
- `johno-macbookpro`: macOS work laptop, uses `home/home-darwin-work.nix`
### Flake Operations
```bash
# Update flake inputs
nix flake update
# Check flake
nix flake check
# Show flake info
nix flake show
```
### Bootstrap New Machine
Use the provided bootstrap script:
```bash
sudo ./bootstrap.sh <hostname>
```
This script pulls from the remote git repository and applies the configuration.
### Build Live USB ISO
Use the provided script to build a bootable ISO:
```bash
./build-liveusb.sh
```
Creates an ISO suitable for Ventoy and other USB boot tools in `./result/iso/`.
## Development Workflow
### Adding New Machines
**NixOS:**
1. Create new directory in `machines/<hostname>/`
2. Add `configuration.nix` with role assignments
3. Include hardware-configuration.nix (generated by nixos-generate-config)
4. Add nixosConfiguration to flake.nix outputs
**Darwin (macOS):**
1. Create new directory in `machines/<hostname>/`
2. Add `configuration.nix` with Darwin role assignments
3. Add darwinConfiguration to flake.nix outputs
### Adding New Roles
1. Create directory in `roles/<role-name>/`
2. Create `default.nix` with module definition using mkEnableOption
3. Add role import to `roles/default.nix`
4. Configure role options in machine configurations
### Home Manager Modules
- Located in `home/modules/`
- Each module has its own `default.nix`
- Imported in main home configuration files
## Key Configuration Details
- **Experimental features**: nix-command and flakes are enabled
- **User**: Primary user is `johno` with trusted-user privileges
- **Locale**: en_US.UTF-8, America/Los_Angeles timezone
- **SSH**: OpenSSH enabled on all configurations
- **Garbage collection**: Automatic, deletes older than 10 days
- **Unfree packages**: Allowed globally
## Issue Tracking (Gitea)
**Tea CLI for Gitea:**
```bash
# Note: When using tea CLI, you must specify --repo johno/nixos-configs
# The CLI doesn't automatically detect the repo from git remote
# List all issues (open by default)
tea issues --repo johno/nixos-configs
# List closed issues
tea issues --repo johno/nixos-configs --state closed
# View specific issue
tea issue --repo johno/nixos-configs 2
# Create new issue
tea issues create --repo johno/nixos-configs --title "Issue title" --body "Description"
# Add comment to issue
tea comment --repo johno/nixos-configs 2 "Comment text"
# Close issue (note: 'issues' is plural, issue number comes last)
tea issues close --repo johno/nixos-configs 2
```
## Important Notes
- **Sudo access**: Claude Code does not have sudo access. Ask the user to run elevated commands like `sudo nixos-rebuild switch`
## Landing the Plane (Session Completion)
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
**MANDATORY WORKFLOW:**
1. **File issues for remaining work** - Create issues for anything that needs follow-up
2. **Run quality gates** (if code changed) - Tests, linters, builds
3. **Update issue status** - Close finished work, update in-progress items
4. **PUSH TO REMOTE** - This is MANDATORY:
```bash
git pull --rebase
bd sync
git push
git status # MUST show "up to date with origin"
```
5. **Clean up** - Clear stashes, prune remote branches
6. **Verify** - All changes committed AND pushed
7. **Hand off** - Provide context for next session
**CRITICAL RULES:**
- Work is NOT complete until `git push` succeeds
- NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds

110
CLAUDE.md
View File

@@ -1,110 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repository Overview
This is a NixOS configuration repository using flakes, managing multiple machines and home-manager configurations. The repository follows a modular architecture with reusable "roles" that can be composed for different machines.
## Architecture
### Flake Structure
- **flake.nix**: Main entry point defining inputs (nixpkgs, home-manager, plasma-manager, etc.) and outputs for multiple NixOS configurations
- **Machines**: `nix-book`, `boxy`, `wixos` (WSL configuration)
- **Home configurations**: Standalone home-manager configuration for user `johno`
### Directory Structure
- `machines/`: Machine-specific configurations with hardware-configuration.nix
- `roles/`: Modular system configurations (audio, bluetooth, desktop, users, etc.)
- `home/`: Home Manager configurations and user-specific modules
- `home/modules/`: User environment modules (emacs, i3+sway, plasma-manager, tmux)
- `packages/`: Custom package definitions
### Role-Based Configuration System
The repository uses a custom "roles" system where each role is a NixOS module with enable options:
- `roles.desktop`: Desktop environment with sub-options for X11, Wayland, KDE, gaming, SDDM
- `roles.audio`: Audio configuration
- `roles.bluetooth`: Bluetooth support
- `roles.users`: User account management
- `roles.virtualisation`: Virtualization setup
- `roles.kodi`: Kodi media center
Example role usage in machine configuration:
```nix
roles = {
audio.enable = true;
desktop = {
enable = true;
gaming = true;
kde = true;
wayland = true;
};
users.enable = true;
};
```
## Common Commands
### Building and Switching Configurations
```bash
# Build and switch to a specific machine configuration
sudo nixos-rebuild switch --flake .#<hostname>
# Build without switching
nixos-rebuild build --flake .#<hostname>
# Build home-manager configuration only
home-manager switch --flake .#johno
```
### Available Machine Configurations
- `nix-book`: Uses `home/home-nix-book.nix`
- `boxy`: Gaming desktop with AMD GPU, uses `home/home.nix`
- `wixos`: WSL configuration, uses `home/home.nix`
### Flake Operations
```bash
# Update flake inputs
nix flake update
# Check flake
nix flake check
# Show flake info
nix flake show
```
### Bootstrap New Machine
Use the provided bootstrap script:
```bash
sudo ./bootstrap.sh <hostname>
```
This script pulls from the remote git repository and applies the configuration.
## Development Workflow
### Adding New Machines
1. Create new directory in `machines/<hostname>/`
2. Add `configuration.nix` with role assignments
3. Include hardware-configuration.nix (generated by nixos-generate-config)
4. Add nixosConfiguration to flake.nix outputs
### Adding New Roles
1. Create directory in `roles/<role-name>/`
2. Create `default.nix` with module definition using mkEnableOption
3. Add role import to `roles/default.nix`
4. Configure role options in machine configurations
### Home Manager Modules
- Located in `home/modules/`
- Each module has its own `default.nix`
- Imported in main home configuration files
## Key Configuration Details
- **Experimental features**: nix-command and flakes are enabled
- **User**: Primary user is `johno` with trusted-user privileges
- **Locale**: en_US.UTF-8, America/Los_Angeles timezone
- **SSH**: OpenSSH enabled on all configurations
- **Garbage collection**: Automatic, deletes older than 10 days
- **Unfree packages**: Allowed globally

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env bash
# Build Live USB ISO from flake configuration
# Creates an uncompressed ISO suitable for Ventoy and other USB boot tools
set -e
echo "Building Live USB ISO..."
nix build .#nixosConfigurations.live-usb.config.system.build.isoImage --show-trace
if [ -f "./result/iso/"*.iso ]; then
iso_file=$(ls ./result/iso/*.iso)
echo "✅ Build complete!"
echo "📁 ISO location: $iso_file"
echo "💾 Ready for Ventoy or dd to USB"
else
echo "❌ Build failed - no ISO file found"
exit 1
fi

250
flake.lock generated
View File

@@ -1,18 +1,41 @@
{
"nodes": {
"flake-compat": {
"doomemacs": {
"flake": false,
"locked": {
"lastModified": 1747046372,
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"lastModified": 1774080407,
"narHash": "sha256-FYbalilgDFjIVwK+D6DjDos1IMmMGA20lRf8k6Ykm1Y=",
"owner": "doomemacs",
"repo": "doomemacs",
"rev": "d8d75443d39d95f3c5256504eb838e0acc62ef44",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"owner": "doomemacs",
"repo": "doomemacs",
"type": "github"
}
},
"emacs-overlay": {
"inputs": {
"nixpkgs": [
"nix-doom-emacs-unstraightened"
],
"nixpkgs-stable": [
"nix-doom-emacs-unstraightened"
]
},
"locked": {
"lastModified": 1774256052,
"narHash": "sha256-7OLaUBQCOCt4XXbjHq9xqBopOJJpbV6Cl8mWdMLzazc=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "c4b7915a9467aa611c7346d2322514cdf8c1ba45",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "emacs-overlay",
"type": "github"
}
},
@@ -23,11 +46,11 @@
]
},
"locked": {
"lastModified": 1752428473,
"narHash": "sha256-IsE7fdAYbRlZuc0H5FtPfhhuHvlxnDGoAxdlnjpVNCU=",
"lastModified": 1768846578,
"narHash": "sha256-82f/+e8HAwmBukiLlr7I3HYvM/2GCd5SOc+BC+qzsOQ=",
"ref": "refs/heads/main",
"rev": "1fad66b55144ab6beaecd900172a21ac3c34dc52",
"revCount": 10,
"rev": "c11ff9d3c67372a843a0fa6bf23132e986bd6955",
"revCount": 14,
"type": "git",
"url": "https://git.johnogle.info/johno/google-cookie-retrieval.git"
},
@@ -43,62 +66,165 @@
]
},
"locked": {
"lastModified": 1752402455,
"narHash": "sha256-mCHfZhQKdTj2JhCFcqfOfa3uKZbwUkPQbd0/zPnhOE8=",
"lastModified": 1774274588,
"narHash": "sha256-dnHvv5EMUgTzGZmA+3diYjQU2O6BEpGLEOgJ1Qe9LaY=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "bf893ad4cbf46610dd1b620c974f824e266cd1df",
"rev": "cf9686ba26f5ef788226843bc31fda4cf72e373b",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.11",
"repo": "home-manager",
"type": "github"
}
},
"nixos-wsl": {
"home-manager-unstable": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs"
"nixpkgs": [
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1752199438,
"narHash": "sha256-xSBMmGtq8K4Qv80TMqREmESCAsRLJRHAbFH2T/2Bf1Y=",
"lastModified": 1774292006,
"narHash": "sha256-RI5sjkDEwIiD2eZHd7iM6ZqPoPWZvn3KdBiMumA3IYI=",
"owner": "nix-community",
"repo": "NixOS-WSL",
"rev": "d34d9412556d3a896e294534ccd25f53b6822e80",
"repo": "home-manager",
"rev": "3cea83bf84abeb72581bdee380fa526d7fcd7e5b",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "main",
"repo": "NixOS-WSL",
"ref": "master",
"repo": "home-manager",
"type": "github"
}
},
"jovian": {
"inputs": {
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1774168156,
"narHash": "sha256-+pwZSARdlM2RQQ6V0q76+WMKW9aNIcxkSOIThcz/f0A=",
"owner": "Jovian-Experiments",
"repo": "Jovian-NixOS",
"rev": "939caad56508542d0f19cab963e2bc693f5f2831",
"type": "github"
},
"original": {
"owner": "Jovian-Experiments",
"repo": "Jovian-NixOS",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1772129556,
"narHash": "sha256-Utk0zd8STPsUJPyjabhzPc5BpPodLTXrwkpXBHYnpeg=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "ebec37af18215214173c98cf6356d0aca24a2585",
"type": "github"
},
"original": {
"owner": "nix-darwin",
"ref": "nix-darwin-25.11",
"repo": "nix-darwin",
"type": "github"
}
},
"nix-doom-emacs-unstraightened": {
"inputs": {
"doomemacs": "doomemacs",
"emacs-overlay": "emacs-overlay",
"nixpkgs": [],
"systems": "systems"
},
"locked": {
"lastModified": 1774265710,
"narHash": "sha256-ar8pFUSAxXhV7DpVRjNvgviWuqOqWPAImb4MM7lSh5Y=",
"owner": "marienz",
"repo": "nix-doom-emacs-unstraightened",
"rev": "f6022b9192e034a817373692ede18a9319cf9730",
"type": "github"
},
"original": {
"owner": "marienz",
"repo": "nix-doom-emacs-unstraightened",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"jovian",
"nixpkgs"
]
},
"locked": {
"lastModified": 1729697500,
"narHash": "sha256-VFTWrbzDlZyFHHb1AlKRiD/qqCJIripXKiCSFS8fAOY=",
"owner": "zhaofengli",
"repo": "nix-github-actions",
"rev": "e418aeb728b6aa5ca8c5c71974e7159c2df1d8cf",
"type": "github"
},
"original": {
"owner": "zhaofengli",
"ref": "matrix-name",
"repo": "nix-github-actions",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1751792365,
"narHash": "sha256-J1kI6oAj25IG4EdVlg2hQz8NZTBNYvIS0l4wpr9KcUo=",
"owner": "NixOS",
"lastModified": 1774244481,
"narHash": "sha256-4XfMXU0DjN83o6HWZoKG9PegCvKvIhNUnRUI19vzTcQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "1fd8bada0b6117e6c7eb54aad5813023eed37ccb",
"rev": "4590696c8693fea477850fe379a01544293ca4e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"owner": "nixos",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"nixpkgs-qt": {
"locked": {
"lastModified": 1751984180,
"narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=",
"lastModified": 1774244481,
"narHash": "sha256-4XfMXU0DjN83o6HWZoKG9PegCvKvIhNUnRUI19vzTcQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0",
"rev": "4590696c8693fea477850fe379a01544293ca4e2",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1774106199,
"narHash": "sha256-US5Tda2sKmjrg2lNHQL3jRQ6p96cgfWh3J1QBliQ8Ws=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "6c9a78c09ff4d6c21d0319114873508a6ec01655",
"type": "github"
},
"original": {
@@ -118,11 +244,34 @@
]
},
"locked": {
"lastModified": 1748196248,
"narHash": "sha256-1iHjsH6/5UOerJEoZKE+Gx1BgAoge/YcnUsOA4wQ/BU=",
"lastModified": 1772361940,
"narHash": "sha256-B1Cz+ydL1iaOnGlwOFld/C8lBECPtzhiy/pP93/CuyY=",
"owner": "nix-community",
"repo": "plasma-manager",
"rev": "b7697abe89967839b273a863a3805345ea54ab56",
"rev": "a4b33606111c9c5dcd10009042bb710307174f51",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "plasma-manager",
"type": "github"
}
},
"plasma-manager-unstable": {
"inputs": {
"home-manager": [
"home-manager-unstable"
],
"nixpkgs": [
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1772361940,
"narHash": "sha256-B1Cz+ydL1iaOnGlwOFld/C8lBECPtzhiy/pP93/CuyY=",
"owner": "nix-community",
"repo": "plasma-manager",
"rev": "a4b33606111c9c5dcd10009042bb710307174f51",
"type": "github"
},
"original": {
@@ -135,9 +284,30 @@
"inputs": {
"google-cookie-retrieval": "google-cookie-retrieval",
"home-manager": "home-manager",
"nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs_2",
"plasma-manager": "plasma-manager"
"home-manager-unstable": "home-manager-unstable",
"jovian": "jovian",
"nix-darwin": "nix-darwin",
"nix-doom-emacs-unstraightened": "nix-doom-emacs-unstraightened",
"nixpkgs": "nixpkgs",
"nixpkgs-qt": "nixpkgs-qt",
"nixpkgs-unstable": "nixpkgs-unstable",
"plasma-manager": "plasma-manager",
"plasma-manager-unstable": "plasma-manager-unstable"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},

440
flake.nix
View File

@@ -2,104 +2,392 @@
description = "A very basic flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixos-wsl.url = "github:nix-community/NixOS-WSL/main";
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
# Separate nixpkgs for qt5webengine-dependent packages (jellyfin-media-player, etc.)
# Updates on separate Renovate schedule to avoid massive qt rebuilds
nixpkgs-qt.url = "github:nixos/nixpkgs/nixos-25.11";
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager";
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager-unstable = {
url = "github:nix-community/home-manager/master";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
plasma-manager = {
url = "github:nix-community/plasma-manager";
inputs.nixpkgs.follows = "nixpkgs";
inputs.home-manager.follows = "home-manager";
};
plasma-manager-unstable = {
url = "github:nix-community/plasma-manager";
inputs.nixpkgs.follows = "nixpkgs-unstable";
inputs.home-manager.follows = "home-manager-unstable";
};
google-cookie-retrieval = {
url = "git+https://git.johnogle.info/johno/google-cookie-retrieval.git";
inputs.nixpkgs.follows = "nixpkgs";
};
jovian = {
url = "github:Jovian-Experiments/Jovian-NixOS";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
nix-doom-emacs-unstraightened = {
url = "github:marienz/nix-doom-emacs-unstraightened";
# Don't follow nixpkgs to avoid rebuild issues with emacs-overlay
inputs.nixpkgs.follows = "";
};
};
outputs = { self, nixpkgs, nixos-wsl, ... } @ inputs: let
baseModules = [
./roles
inputs.home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.sharedModules = [
inputs.plasma-manager.homeManagerModules.plasma-manager
];
home-manager.extraSpecialArgs = {
globalInputs = inputs;
};
}
];
in {
nixosConfigurations.nix-book = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = baseModules ++ [
./machines/nix-book/configuration.nix
outputs =
{
self,
nixpkgs,
nixpkgs-unstable,
...
}@inputs:
let
# Shared overlay function to reduce duplication across module sets
# Parameters:
# unstableOverlays: Additional overlays to apply when importing nixpkgs-unstable
mkBaseOverlay =
{
home-manager.users.johno = import ./home/home-nix-book.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
nixosConfigurations.boxy = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = baseModules ++ [
./machines/boxy/configuration.nix
inputs.home-manager.nixosModules.home-manager
{
home-manager.users.johno = import ./home/home.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
nixosConfigurations.wixos = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = baseModules ++ [
nixos-wsl.nixosModules.default
./machines/wixos/configuration.nix
inputs.home-manager.nixosModules.home-manager
{
home-manager.users.johno = import ./home/home.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
# Live USB ISO configuration
nixosConfigurations.live-usb = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = baseModules ++ [
./machines/live-usb/configuration.nix
{
home-manager.users.nixos = { ... }: {
imports = [ ./home/home.nix ];
home.username = nixpkgs.lib.mkForce "nixos";
home.homeDirectory = nixpkgs.lib.mkForce "/home/nixos";
unstableOverlays ? [ ],
}:
(final: prev: {
unstable = import nixpkgs-unstable {
system = prev.stdenv.hostPlatform.system;
config.allowUnfree = true;
overlays = unstableOverlays;
};
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
# Separate nixpkgs for qt5webengine-heavy packages to avoid rebuild churn
qt-pinned = import inputs.nixpkgs-qt {
system = prev.stdenv.hostPlatform.system;
config = {
allowUnfree = true;
permittedInsecurePackages = [ "qtwebengine-5.15.19" ];
};
};
custom = prev.callPackage ./packages { };
# Compatibility: bitwarden renamed to bitwarden-desktop in unstable
bitwarden-desktop = prev.bitwarden-desktop or prev.bitwarden;
});
homeConfigurations."johno" = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = inputs.nixpkgs.legacyPackages."x86_64-linux";
modules = [
inputs.plasma-manager.homeManagerModules.plasma-manager
./home/home.nix
# Shared home-manager configuration factory
# Parameters:
# sharedModules: Additional modules to include in home-manager.sharedModules
mkHomeManagerConfig =
{
sharedModules ? [ ],
}:
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.sharedModules = sharedModules ++ [
inputs.nix-doom-emacs-unstraightened.homeModule
];
home-manager.extraSpecialArgs = {
globalInputs = inputs;
};
};
# Shared unstable overlays for custom package builds
customUnstableOverlays = [
# Override claude-code in unstable to use our custom GCS-based build
# (needed for corporate networks that block npm registry)
(ufinal: uprev: {
claude-code = uprev.callPackage ./packages/claude-code { };
})
];
extraSpecialArgs = {
nixosModules = [
./roles
inputs.home-manager.nixosModules.home-manager
{
nixpkgs.overlays = [ (mkBaseOverlay { unstableOverlays = customUnstableOverlays; }) ];
}
(mkHomeManagerConfig {
sharedModules = [ inputs.plasma-manager.homeModules.plasma-manager ];
})
];
# Modules for unstable-based systems (like nix-deck)
nixosModulesUnstable = [
./roles
inputs.home-manager-unstable.nixosModules.home-manager
inputs.jovian.nixosModules.jovian
{
nixpkgs.overlays = [ (mkBaseOverlay { unstableOverlays = customUnstableOverlays; }) ];
}
(mkHomeManagerConfig {
sharedModules = [ inputs.plasma-manager-unstable.homeModules.plasma-manager ];
})
];
darwinModules = [
./roles/darwin.nix
inputs.home-manager.darwinModules.home-manager
{
nixpkgs.overlays = [ (mkBaseOverlay { unstableOverlays = customUnstableOverlays; }) ];
}
(mkHomeManagerConfig { sharedModules = [ ]; })
];
in
{
nixosConfigurations.nix-book = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
globalInputs = inputs;
modules = nixosModules ++ [
./machines/nix-book/configuration.nix
{
home-manager.users.johno = {
imports = [ ./home/home-laptop-compact.nix ];
# Machine-specific overrides
home.roles.i3_sway.extraSwayConfig = {
output.eDP-1.scale = "1.75";
};
};
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
nixosConfigurations.boxy = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = nixosModules ++ [
./machines/boxy/configuration.nix
{
home-manager.users.johno = import ./home/home-media-center.nix;
# kodi user: AVR volume control + minimal Plasma config for Bigscreen session
home-manager.users.kodi = import ./home/home-kodi.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
nixosConfigurations.gym-box = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = nixosModules ++ [
./machines/gym-box/configuration.nix
{
home-manager.users.johno = import ./home/home-media-center.nix;
home-manager.users.kodi = import ./home/home-kodi.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
nixosConfigurations.zix790prors = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = nixosModules ++ [
./machines/zix790prors/configuration.nix
{
home-manager.users.johno = {
imports = [ ./home/home-desktop.nix ];
home.roles.i3_sway.extraSwayConfig = {
output = {
"DP-1" = {
mode = "3440x1440@164.900Hz";
};
};
};
};
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
# Live USB ISO configuration
nixosConfigurations.live-usb = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = nixosModules ++ [
./machines/live-usb/configuration.nix
{
home-manager.users.nixos = import ./home/home-live-usb.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
# Steam Deck configuration (using unstable for better Jovian compatibility)
nixosConfigurations.nix-deck = nixpkgs-unstable.lib.nixosSystem rec {
system = "x86_64-linux";
modules = nixosModulesUnstable ++ [
./machines/nix-deck/configuration.nix
{
home-manager.users.johno = import ./home/home-desktop.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
# ZFS/NFS server configuration
nixosConfigurations.john-endesktop = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = nixosModules ++ [
./machines/john-endesktop/configuration.nix
inputs.home-manager.nixosModules.home-manager
{
home-manager.users.johno = import ./home/home-server.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
# Darwin/macOS configurations
darwinConfigurations."BLKFV4YF49KT7" = inputs.nix-darwin.lib.darwinSystem rec {
system = "aarch64-darwin";
modules = darwinModules ++ [
./machines/johno-macbookpro/configuration.nix
{
home-manager.users.johno = import ./home/home-darwin-work.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
# Packages for CI caching (custom packages, flake inputs, and qt-pinned)
packages = nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" ] (
system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
overlays = [ (mkBaseOverlay { }) ];
};
pkgsQt = import inputs.nixpkgs-qt {
inherit system;
config = {
allowUnfree = true;
permittedInsecurePackages = [ "qtwebengine-5.15.19" ];
};
};
in
{
"custom-claude-code" = pkgs.custom.claude-code;
"custom-app-launcher-server" = pkgs.custom.app-launcher-server;
"custom-mcrcon-rbw" = pkgs.custom.mcrcon-rbw;
"custom-tea-rbw" = pkgs.custom.tea-rbw;
"custom-rclone-torbox-setup" = pkgs.custom.rclone-torbox-setup;
"custom-opencode" = pkgs.custom.opencode;
"qt-pinned-jellyfin-media-player" = pkgsQt.jellyfin-media-player;
"qt-pinned-stremio" = pkgsQt.stremio;
# Plasma Bigscreen — not yet in nixpkgs, built from upstream
"plasma-bigscreen" = pkgs.kdePackages.callPackage ./roles/plasma-bigscreen/package.nix { };
}
// (
if system == "x86_64-linux" then
{
"custom-nextcloud-talk-desktop" = pkgs.custom.nextcloud-talk-desktop;
# nix-deck kernel from Jovian-NixOS (Steam Deck) - expensive to build
"nix-deck-kernel" = self.nixosConfigurations.nix-deck.config.boot.kernelPackages.kernel;
}
else
{ }
)
);
# Flake apps
apps = nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ] (
system:
let
pkgs = import nixpkgs { inherit system; };
commonDeps = [
pkgs.curl
pkgs.jq
pkgs.nix
pkgs.git
pkgs.gnused
pkgs.gnugrep
pkgs.coreutils
pkgs.gawk
];
update-doomemacs = pkgs.writeShellScriptBin "update-doomemacs" ''
export PATH="${pkgs.lib.makeBinPath commonDeps}:$PATH"
${builtins.readFile ./scripts/update-doomemacs.sh}
'';
update-claude-code = pkgs.writeShellScriptBin "update-claude-code" ''
export PATH="${pkgs.lib.makeBinPath commonDeps}:$PATH"
${builtins.readFile ./packages/claude-code/update.sh}
'';
update-opencode = pkgs.writeShellScriptBin "update-opencode" ''
export PATH="${pkgs.lib.makeBinPath commonDeps}:$PATH"
${builtins.readFile ./packages/opencode/update.sh}
'';
rotate-wallpaper = pkgs.writeShellScriptBin "rotate-wallpaper" ''
export PATH="${pkgs.lib.makeBinPath commonDeps}:$PATH"
${builtins.readFile ./scripts/rotate-wallpaper.sh}
'';
upgrade = pkgs.writeShellScriptBin "upgrade" ''
export PATH="${pkgs.lib.makeBinPath commonDeps}:$PATH"
${builtins.readFile ./scripts/upgrade.sh}
'';
bootstrap = pkgs.writeShellScriptBin "bootstrap" ''
export PATH="${pkgs.lib.makeBinPath commonDeps}:$PATH"
${builtins.readFile ./scripts/bootstrap.sh}
'';
build-liveusb = pkgs.writeShellScriptBin "build-liveusb" ''
export PATH="${pkgs.lib.makeBinPath commonDeps}:$PATH"
${builtins.readFile ./scripts/build-liveusb.sh}
'';
in
{
update-doomemacs = {
type = "app";
program = "${update-doomemacs}/bin/update-doomemacs";
meta.description = "Update Doom Emacs configuration";
};
update-claude-code = {
type = "app";
program = "${update-claude-code}/bin/update-claude-code";
meta.description = "Update Claude Code package version";
};
update-opencode = {
type = "app";
program = "${update-opencode}/bin/update-opencode";
meta.description = "Update OpenCode package version";
};
rotate-wallpaper = {
type = "app";
program = "${rotate-wallpaper}/bin/rotate-wallpaper";
meta.description = "Rotate desktop wallpaper";
};
upgrade = {
type = "app";
program = "${upgrade}/bin/upgrade";
meta.description = "Upgrade NixOS configuration";
};
bootstrap = {
type = "app";
program = "${bootstrap}/bin/bootstrap";
meta.description = "Bootstrap a new NixOS machine";
};
build-liveusb = {
type = "app";
program = "${build-liveusb}/bin/build-liveusb";
meta.description = "Build a bootable Live USB ISO";
};
}
);
};
};
}

123
home/home-darwin-work.nix Normal file
View File

@@ -0,0 +1,123 @@
{ config, lib, pkgs, globalInputs, system, ... }:
{
# Home Manager configuration for Darwin work laptop
# Corporate-friendly setup with essential development tools
home.username = lib.mkForce "johno";
home.homeDirectory = lib.mkForce "/Users/johno";
home.stateVersion = "24.05";
# System packages
home.packages = with pkgs; [
google-cloud-sdk
];
# Note: ghostty installed via Homebrew (managed outside of nix)
# Override Darwin-incompatible settings from base role
programs.rbw.settings.pinentry = lib.mkForce pkgs.pinentry_mac;
# Disable Home Manager from managing shell RC files
# topsoil/compost will manage these files instead
programs.bash.enable = lib.mkForce false;
programs.zsh.enable = lib.mkForce false;
# Create a local nix integration file that topsoil-managed configs can source
home.file.".nix-integration.sh" = {
text = ''
# Source Home Manager session variables (nix paths, environment, etc.)
if [ -e /etc/profiles/per-user/johno/etc/profile.d/hm-session-vars.sh ]; then
. /etc/profiles/per-user/johno/etc/profile.d/hm-session-vars.sh
fi
# Setup bash completions from nix profiles
if [[ ! -v BASH_COMPLETION_VERSINFO ]] && [ -n "$NIX_PROFILES" ]; then
for profile in $NIX_PROFILES; do
if [ -f "$profile/etc/profile.d/bash_completion.sh" ]; then
. "$profile/etc/profile.d/bash_completion.sh"
break
fi
done
fi
# command-not-found handler
command_not_found_handle() {
local p=/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite
if [ -n "$NIX_PROFILES" ]; then
for profile in $NIX_PROFILES; do
if [ -x "$profile/bin/command-not-found" ] && [ -f "$p" ]; then
"$profile/bin/command-not-found" "$@"
return $?
fi
done
fi
echo "$1: command not found" >&2
return 127
}
'';
};
home.file.".nix-integration.zsh" = {
text = ''
# Source Home Manager session variables (nix paths, environment, etc.)
if [ -e /etc/profiles/per-user/johno/etc/profile.d/hm-session-vars.sh ]; then
. /etc/profiles/per-user/johno/etc/profile.d/hm-session-vars.sh
fi
# Setup zsh completions from nix profiles
typeset -U path cdpath fpath manpath
for profile in ''${(z)NIX_PROFILES}; do
fpath+=($profile/share/zsh/site-functions $profile/share/zsh/$ZSH_VERSION/functions $profile/share/zsh/vendor-completions)
done
autoload -U compinit && compinit
# command-not-found handler
command_not_found_handler() {
local p=/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite
if [ -n "$NIX_PROFILES" ]; then
for profile in ''${(z)NIX_PROFILES}; do
if [ -x "$profile/bin/command-not-found" ] && [ -f "$p" ]; then
"$profile/bin/command-not-found" "$@"
return $?
fi
done
fi
echo "$1: command not found" >&2
return 127
}
'';
};
# Keep SSH and Git disabled to avoid conflicts with work environment
programs.ssh.enable = lib.mkForce false;
programs.git.enable = lib.mkForce false;
programs.rbw.enable = lib.mkForce false;
home.shell.enableShellIntegration = true;
home.roles = {
base.enable = true;
development = {
enable = true;
allowArbitraryClaudeCodeModelSelection = true;
};
tmux.enable = true;
emacs.enable = true;
aerospace = {
enable = true;
leader = "cmd";
ctrlShortcuts.enable = false;
sketchybar.enable = true;
# Optional: Add per-machine userSettings overrides
# userSettings = {
# mode.main.binding."${leader}-custom" = "custom-command";
# };
};
};
imports = [
./roles
./roles/base-darwin
];
}

38
home/home-desktop.nix Normal file
View File

@@ -0,0 +1,38 @@
{ pkgs, globalInputs, system, ... }:
{
# Home Manager configuration for full desktop experience
home.username = "johno";
home.homeDirectory = "/home/johno";
home.stateVersion = "24.05";
# Enable all desktop roles for full-featured experience
home.roles = {
"3d-printing".enable = true;
base.enable = true;
gaming.enable = true;
desktop.enable = true;
emacs.enable = true;
email.enable = true;
i3_sway.enable = true;
office.enable = true;
media.enable = true;
development.enable = true;
communication.enable = true;
sync.enable = true;
kdeconnect.enable = true;
kubectl.enable = true;
tmux.enable = true;
plasma-manager.enable = true;
starship.enable = true;
};
targets.genericLinux.enable = true;
home.sessionVariables = {};
home.sessionPath = [];
imports = [
./roles
./roles/base-linux
];
}

30
home/home-kodi.nix Normal file
View File

@@ -0,0 +1,30 @@
{ pkgs, globalInputs, system, ... }:
{
# Home Manager configuration for kodi user on boxy
# Focused on media center volume control via Home Assistant
home.username = "kodi";
home.homeDirectory = "/home/kodi";
home.stateVersion = "24.05";
# Enable minimal roles for kodi user
home.roles = {
base.enable = true;
plasma-manager-kodi.enable = true;
kdeconnect.enable = true;
};
home.packages = with pkgs; [
kdePackages.kconfig
];
targets.genericLinux.enable = true;
home.sessionVariables = {};
home.sessionPath = [];
imports = [
./roles
./roles/base-linux
];
}

View File

@@ -0,0 +1,46 @@
{ config, lib, pkgs, globalInputs, system, ... }:
{
# Home Manager configuration for compact laptop setups
# Optimized for space-constrained environments
home.username = "johno";
home.homeDirectory = "/home/johno";
home.stateVersion = "24.05";
# Enable essential roles only (exclude heavy office/media packages)
home.roles = {
base.enable = true;
desktop.enable = true;
gaming.enable = true;
development.enable = true;
communication.enable = true;
email.enable = true;
kdeconnect.enable = true;
media.enable = true;
sync.enable = true;
kubectl.enable = true;
tmux.enable = true;
plasma-manager.enable = true;
emacs.enable = true;
i3_sway.enable = true;
starship.enable = true;
# Launcher wrappers for excluded/optional packages
launchers = {
enable = true;
packages = [
"libreoffice"
];
};
};
targets.genericLinux.enable = true;
home.sessionVariables = {};
home.sessionPath = [];
imports = [
./roles
./roles/base-linux
];
}

43
home/home-live-usb.nix Normal file
View File

@@ -0,0 +1,43 @@
{ pkgs, globalInputs, system, ... }:
{
# Home Manager configuration for live USB environments
# Minimal setup without persistent services
home.username = "nixos";
home.homeDirectory = "/home/nixos";
home.stateVersion = "24.05";
# Enable minimal roles only (no sync or kdeconnect for live environment)
home.roles = {
base.enable = true;
desktop.enable = true;
tmux.enable = true;
plasma-manager.enable = true;
emacs = {
enable = true;
# Use pre-built Doom Emacs - all packages built at nix build time
# This means no doom sync is needed after booting the live USB
prebuiltDoom = true;
};
i3_sway.enable = true;
starship.enable = true;
# development.enable = false; # Not needed for live USB
# communication.enable = false; # Not needed for live USB
# office.enable = false; # Not needed for live USB
# media.enable = false; # Not needed for live USB
# sync.enable = false; # No persistent sync on live USB
# kdeconnect.enable = false; # No device integration on live USB
};
targets.genericLinux.enable = true;
home.sessionVariables = {};
home.sessionPath = [];
imports = [
./roles
./roles/base-linux
];
# Live USB specific overrides can go here if needed
}

View File

@@ -0,0 +1,39 @@
{ pkgs, globalInputs, system, ... }:
{
# Home Manager configuration for media center setups
# Optimized for living room media consumption and gaming
home.username = "johno";
home.homeDirectory = "/home/johno";
home.stateVersion = "24.05";
# Enable media center focused roles
home.roles = {
base.enable = true;
desktop.enable = true;
gaming.enable = true;
media.enable = true;
communication.enable = true;
kdeconnect.enable = true;
development.enable = true;
tmux.enable = true;
plasma-manager.enable = true;
emacs.enable = true;
i3_sway.enable = true;
starship.enable = true;
# office.enable = false; # Not needed for media center
# sync.enable = false; # Shared machine, no personal file sync
};
targets.genericLinux.enable = true;
home.sessionVariables = {};
home.sessionPath = [];
imports = [
./roles
./roles/base-linux
];
# Media center specific overrides can go here if needed
}

View File

@@ -1,11 +0,0 @@
{ config, lib, pkgs, ... }:
{
imports = [
./home.nix
];
home.i3_sway.extraSwayConfig = {
output.eDP-1.scale = "1.75";
};
}

27
home/home-server.nix Normal file
View File

@@ -0,0 +1,27 @@
{ pkgs, globalInputs, system, ... }:
{
# Home Manager configuration for servers (minimal with development tools)
home.username = "johno";
home.homeDirectory = "/home/johno";
home.stateVersion = "24.05";
# Minimal roles for server with development capability
home.roles = {
base.enable = true;
development.enable = true;
emacs.enable = true;
kubectl.enable = true;
starship.enable = true;
tmux.enable = true;
};
targets.genericLinux.enable = true;
home.sessionVariables = {};
home.sessionPath = [];
imports = [
./roles
./roles/base-linux
];
}

View File

@@ -1,204 +0,0 @@
{ pkgs, customPkgs, globalInputs, system, ... }:
let
customPkgs = pkgs.callPackage ../packages {};
in
{
# Home Manager needs a bit of information about you and the paths it should
# manage.
home.username = "johno";
home.homeDirectory = "/home/johno";
# This value determines the Home Manager release that your configuration is
# compatible with. This helps avoid breakage when a new Home Manager release
# introduces backwards incompatible changes.
#
# You should not change this value, even if you update Home Manager. If you do
# want to update the value, then make sure to first check the Home Manager
# release notes.
home.stateVersion = "24.05"; # Please read the comment before changing.
# The home.packages option allows you to install Nix packages into your
# environment.
home.packages = [
# # Adds the 'hello' command to your environment. It prints a friendly
# # "Hello, world!" when run.
# pkgs.hello
# # It is sometimes useful to fine-tune packages, for example, by applying
# # overrides. You can do that directly here, just don't forget the
# # parentheses. Maybe you want to install Nerd Fonts with a limited number of
# # fonts?
# (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; })
# # You can also create simple shell scripts directly inside your
# # configuration. For example, this adds a command 'my-hello' to your
# # environment:
# (pkgs.writeShellScriptBin "my-hello" ''
# echo "Hello, ${config.home.username}!"
# '')
pkgs.bitwarden
pkgs.claude-code
pkgs.codex
pkgs.dunst
pkgs.element-desktop
pkgs.fd
#pkgs.fluffychat # security vulnerability in current version
pkgs.goose-cli
pkgs.gzip
pkgs.htop
pkgs.jellyfin-media-player
pkgs.keepassxc
pkgs.killall
pkgs.kitty
pkgs.less
pkgs.moonlight-qt
pkgs.ncdu
pkgs.nextcloud-talk-desktop
pkgs.openscad-unstable
pkgs.pandoc
#pkgs.pinentry-qt
#pkgs.pytest
pkgs.shellcheck
pkgs.solaar # Logitech management software
(pkgs.snapcast.override { pulseaudioSupport = true; })
pkgs.tmux
pkgs.waybar
pkgs.wofi
pkgs.vlc
## Kubernetes cluster management
pkgs.kubectl
pkgs.kubernetes-helm
globalInputs.google-cookie-retrieval.packages.${system}.default
];
# Home Manager is pretty good at managing dotfiles. The primary way to manage
# plain files is through 'home.file'.
home.file = {
# # Building this configuration will create a copy of 'dotfiles/screenrc' in
# # the Nix store. Activating the configuration will then make '~/.screenrc' a
# # symlink to the Nix store copy.
# ".screenrc".source = dotfiles/screenrc;
# # You can also set the file content immediately.
# ".gradle/gradle.properties".text = ''
# org.gradle.console=verbose
# org.gradle.daemon.idletimeout=3600000
# '';
};
targets.genericLinux.enable = true;
# Home Manager can also manage your environment variables through
# 'home.sessionVariables'. These will be explicitly sourced when using a
# shell provided by Home Manager. If you don't want to manage your shell
# through Home Manager then you have to manually source 'hm-session-vars.sh'
# located at either
#
# ~/.nix-profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# /etc/profiles/per-user/johno/etc/profile.d/hm-session-vars.sh
#
home.sessionVariables = {
};
home.sessionPath = [
];
imports = [
./modules/emacs
./modules/i3+sway
./modules/plasma-manager
./modules/tmux
];
programs.bash = {
enable = true;
initExtra = ''
codex() {
local key
key="$(rbw get openai-api-key-codex)"
OPENAI_API_KEY="$key" command codex "$@"
}
'';
};
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
programs.command-not-found.enable = true;
programs.firefox = {
enable = true;
};
programs.git = {
enable = true;
userName = "John Ogle";
userEmail = "john@ogle.fyi";
extraConfig = {
safe.directory = "/etc/nixos";
};
};
programs.jq.enable = true;
programs.k9s.enable = true;
programs.neovim = {
enable = true;
viAlias = true;
vimAlias = true;
};
programs.rbw = {
enable = true;
settings = {
email = "john@johnogle.info";
base_url = "https://bitwarden.johnogle.info";
pinentry = pkgs.pinentry-qt;
};
};
programs.spotify-player.enable = true;
programs.ssh = {
enable = true;
addKeysToAgent = "yes";
matchBlocks = {
"nucdeb1" = {
hostname = "nucdeb1.oglehome";
user = "root";
};
};
};
services.kdeconnect = {
enable = true;
indicator = true;
package = pkgs.kdePackages.kdeconnect-kde;
};
services.gnome-keyring = {
enable = true;
};
services.syncthing = {
enable = true;
tray = {
enable = true;
command = "syncthingtray --wait";
};
};
xdg.enable = true;
}

View File

@@ -1,47 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
doomEmacs = pkgs.fetchFromGitHub {
owner = "doomemacs";
repo = "doomemacs";
rev = "8406c1ff22b95bd0f816de4a0223fa3ce3c82568";
sha256 = "sha256-rOkgOdmLESVAbOeEM9nJTzxyI+akdk48Ed2VlktOy3Q=";
};
in
{
config = {
home.packages = [
pkgs.emacs
pkgs.emacs-all-the-icons-fonts
pkgs.fira-code
pkgs.fontconfig
pkgs.graphviz
pkgs.isort
pkgs.nerd-fonts.fira-code
pkgs.nerd-fonts.droid-sans-mono
pkgs.nil # nix lsp language server
pkgs.nixfmt-rfc-style
(pkgs.ripgrep.override {withPCRE2 = true;})
pkgs.pipenv
pkgs.poetry
pkgs.python3
];
fonts.fontconfig.enable = true;
home.file."${config.xdg.configHome}/emacs".source = doomEmacs;
home.sessionPath = [
"${config.xdg.configHome}/emacs/bin"
];
home.sessionVariables = {
DOOMDIR = "${config.xdg.configHome}/doom";
DOOMLOCALDIR = "${config.xdg.dataHome}/doom";
};
home.file."${config.xdg.configHome}/doom".source = ./doom;
};
}

View File

@@ -1,116 +0,0 @@
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
;; Place your private configuration here! Remember, you do not need to run 'doom
;; sync' after modifying this file!
;; Some functionality uses this to identify you, e.g. GPG configuration, email
;; clients, file templates and snippets. It is optional.
;; (setq user-full-name "John Doe"
;; user-mail-address "john@doe.com")
;; Doom exposes five (optional) variables for controlling fonts in Doom:
;;
;; - `doom-font' -- the primary font to use
;; - `doom-variable-pitch-font' -- a non-monospace font (where applicable)
;; - `doom-big-font' -- used for `doom-big-font-mode'; use this for
;; presentations or streaming.
;; - `doom-symbol-font' -- for symbols
;; - `doom-serif-font' -- for the `fixed-pitch-serif' face
;;
;; See 'C-h v doom-font' for documentation and more examples of what they
;; accept. For example:
;;
;;(setq doom-font (font-spec :family "Fira Code" :size 12 :weight 'semi-light)
;; doom-variable-pitch-font (font-spec :family "Fira Sans" :size 13))
;;
;; If you or Emacs can't find your font, use 'M-x describe-font' to look them
;; up, `M-x eval-region' to execute elisp code, and 'M-x doom/reload-font' to
;; refresh your font settings. If Emacs still can't find your font, it likely
;; wasn't installed correctly. Font issues are rarely Doom issues!
(setq doom-font (font-spec :family "Fira Code"))
;; There are two ways to load a theme. Both assume the theme is installed and
;; available. You can either set `doom-theme' or manually load a theme with the
;; `load-theme' function. This is the default:
(setq doom-theme 'doom-one)
;; This determines the style of line numbers in effect. If set to `nil', line
;; numbers are disabled. For relative line numbers, set this to `relative'.
(setq display-line-numbers-type t)
;; If you use `org' and don't want your org files in the default location below,
;; change `org-directory'. It must be set before org loads!
(setq org-directory "~/org/")
(after! org
(setq org-agenda-span 'week
my-agenda-dirs '("projects" "roam")
org-agenda-files (cons org-directory (mapcan (lambda (x) (directory-files-recursively
(expand-file-name x org-directory)
"\.org$"))
my-agenda-dirs))
org-log-done 'time
org-agenda-custom-commands '(("n" "Agenda"
((agenda "")
(tags-todo "-someday-recurring")))
("s" "Someday Items"
((tags-todo "+someday"))))
org-todo-keywords '((sequence "TODO(t)" "IN-PROGRESS(p)" "WAIT(w)" "|" "DONE(d)" "KILL(k)"))
org-journal-file-type 'weekly
org-journal-file-format "%Y-%m-%d.org"
org-capture-templates
'(("t" "Todo" entry (file+headline "~/org/todo.org" "Inbox")
"* TODO %? \n %i \n%a" :prepend t))))
;; (use-package! org-caldav
;; :defer t
;; :config
;; (setq org-caldav-url "https://nextcloud.johnogle.info/remote.php/dav/calendars/johno"
;; org-caldav-calendar-id "personal"
;; org-icalendar-timezone "America/Los_Angeles"
;; org-caldav-inbox "~/org/calendar.org"
;; org-caldav-files nil
;; org-caldav-sync-direction 'cal->org))
(defun my/get-rbw-password (alias)
"Return the password for ALIAS via rbw, unlocking the vault only if needed."
(let* ((cmd (format "rbw get %s 2>&1" alias))
(output (shell-command-to-string cmd)))
(string-trim output)))
(use-package! gptel
:defer t
:config
(setq! gptel-api-key (my/get-rbw-password "openai-api-key-chatgpt-el")))
;; Whenever you reconfigure a package, make sure to wrap your config in an
;; `after!' block, otherwise Doom's defaults may override your settings. E.g.
;;
;; (after! PACKAGE
;; (setq x y))
;;
;; The exceptions to this rule:
;;
;; - Setting file/directory variables (like `org-directory')
;; - Setting variables which explicitly tell you to set them before their
;; package is loaded (see 'C-h v VARIABLE' to look up their documentation).
;; - Setting doom variables (which start with 'doom-' or '+').
;;
;; Here are some additional functions/macros that will help you configure Doom.
;;
;; - `load!' for loading external *.el files relative to this one
;; - `use-package!' for configuring packages
;; - `after!' for running code after a package has loaded
;; - `add-load-path!' for adding directories to the `load-path', relative to
;; this file. Emacs searches the `load-path' when you load packages with
;; `require' or `use-package'.
;; - `map!' for binding new keys
;;
;; To get information about any of these functions/macros, move the cursor over
;; the highlighted symbol at press 'K' (non-evil users must press 'C-c c k').
;; This will open documentation for it, including demos of how they are used.
;; Alternatively, use `C-h o' to look up a symbol (functions, variables, faces,
;; etc).
;;
;; You can also try 'gd' (or 'C-c c d') to jump to their definition and see how
;; they are implemented.

View File

@@ -1,146 +0,0 @@
{ config, lib, ... }:
with lib;
let
cfg = config.home.i3_sway;
i3_cfg = config.xsession.windowManager.i3.config;
shared_config = recursiveUpdate rec {
modifier = "Mod4";
terminal = "kitty";
defaultWorkspace = "workspace number 1";
keybindings = {
"${shared_config.modifier}+Return" = "exec ${terminal}";
"${shared_config.modifier}+Shift+q" = "kill";
"${shared_config.modifier}+d" = "exec ${i3_cfg.menu}";
"${shared_config.modifier}+h" = "focus left";
"${shared_config.modifier}+j" = "focus down";
"${shared_config.modifier}+k" = "focus up";
"${shared_config.modifier}+l" = "focus right";
"${shared_config.modifier}+Shift+h" = "move left";
"${shared_config.modifier}+Shift+j" = "move down";
"${shared_config.modifier}+Shift+k" = "move up";
"${shared_config.modifier}+Shift+l" = "move right";
"${shared_config.modifier}+Left" = "focus left";
"${shared_config.modifier}+Down" = "focus down";
"${shared_config.modifier}+Up" = "focus up";
"${shared_config.modifier}+Right" = "focus right";
"${shared_config.modifier}+Shift+Left" = "move left";
"${shared_config.modifier}+Shift+Down" = "move down";
"${shared_config.modifier}+Shift+Up" = "move up";
"${shared_config.modifier}+Shift+Right" = "move right";
#"${shared_config.modifier}+h" = "split h";
"${shared_config.modifier}+v" = "split v";
"${shared_config.modifier}+f" = "fullscreen toggle";
"${shared_config.modifier}+s" = "layout stacking";
"${shared_config.modifier}+w" = "layout tabbed";
"${shared_config.modifier}+e" = "layout toggle split";
"${shared_config.modifier}+Shift+space" = "floating toggle";
"${shared_config.modifier}+space" = "focus mode_toggle";
"${shared_config.modifier}+a" = "focus parent";
"${shared_config.modifier}+Shift+minus" = "move scratchpad";
"${shared_config.modifier}+minus" = "scratchpad show";
"${shared_config.modifier}+1" = "workspace number 1";
"${shared_config.modifier}+2" = "workspace number 2";
"${shared_config.modifier}+3" = "workspace number 3";
"${shared_config.modifier}+4" = "workspace number 4";
"${shared_config.modifier}+5" = "workspace number 5";
"${shared_config.modifier}+6" = "workspace number 6";
"${shared_config.modifier}+7" = "workspace number 7";
"${shared_config.modifier}+8" = "workspace number 8";
"${shared_config.modifier}+9" = "workspace number 9";
"${shared_config.modifier}+0" = "workspace number 10";
"${shared_config.modifier}+Shift+1" =
"move container to workspace number 1";
"${shared_config.modifier}+Shift+2" =
"move container to workspace number 2";
"${shared_config.modifier}+Shift+3" =
"move container to workspace number 3";
"${shared_config.modifier}+Shift+4" =
"move container to workspace number 4";
"${shared_config.modifier}+Shift+5" =
"move container to workspace number 5";
"${shared_config.modifier}+Shift+6" =
"move container to workspace number 6";
"${shared_config.modifier}+Shift+7" =
"move container to workspace number 7";
"${shared_config.modifier}+Shift+8" =
"move container to workspace number 8";
"${shared_config.modifier}+Shift+9" =
"move container to workspace number 9";
"${shared_config.modifier}+Shift+0" =
"move container to workspace number 10";
"${shared_config.modifier}+Shift+c" = "reload";
"${shared_config.modifier}+Shift+r" = "restart";
"${shared_config.modifier}+r" = "mode resize";
"XF86MonBrightnessUp" = "exec brightnessctl s +5%";
"XF86MonBrightnessDown" = "exec brightnessctl s 5%-";
};
} cfg.extraSharedConfig;
in {
options.home.i3_sway = {
extraSharedConfig = mkOption {
default = {};
};
extraI3Config = mkOption {
default = {};
};
extraSwayConfig = mkOption {
default = {};
};
};
config = {
xsession.windowManager.i3 = let
base_i3_config = recursiveUpdate shared_config {
keybindings = {
"${shared_config.modifier}+Shift+e" =
"exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
};
};
in {
enable = true;
config = recursiveUpdate base_i3_config cfg.extraI3Config;
};
wayland.windowManager.sway = let
base_sway_config = recursiveUpdate shared_config {
keybindings = {
"${shared_config.modifier}+Shift+e" =
"exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
};
input = {
"type:keyboard" = {
xkb_options = "caps:escape";
};
"type:touchpad" = {
tap = "enabled";
tap_button_map = "lrm";
drag = "enabled";
natural_scroll = "disabled";
dwt = "enabled";
};
};
};
in {
enable = true;
config = recursiveUpdate base_sway_config cfg.extraSwayConfig;
};
};
}

View File

@@ -1,127 +0,0 @@
{ config, lib, pkgs, ... }:
# The current KDE config can be output with the command:
# nix run github:nix-community/plasma-manager
#
# Plasma-manager options documentation
# https://nix-community.github.io/plasma-manager/options.xhtml
#
# TODO: (ambitious) Add Kmail support to plasma-manager
{
programs.plasma = {
enable = true;
overrideConfig = true;
hotkeys.commands."launch-konsole" = {
name = "Launch Konsole";
key = "Meta+Return";
command = "konsole";
};
shortcuts = {
kmix = {
"decrease_microphone_volume" = "Microphone Volume Down";
"decrease_volume" = "Volume Down";
"decrease_volume_small" = "Shift+Volume Down";
"increase_microphone_volume" = "Microphone Volume Up";
"increase_volume" = "Volume Up";
"increase_volume_small" = "Shift+Volume Up";
"mic_mute" = ["Microphone Mute" "Meta+Volume Mute,Microphone Mute" "Meta+Volume Mute,Mute Microphone"];
"mute" = "Volume Mute";
};
mediacontrol = {
"mediavolumedown" = "none,,Media volume down";
"mediavolumeup" = "none,,Media volume up";
"nextmedia" = "Media Next";
"pausemedia" = "Media Pause";
"playmedia" = "none,,Play media playback";
"playpausemedia" = "Media Play";
"previousmedia" = "Media Previous";
"stopmedia" = "Media Stop";
};
ksmserver = {
"Lock Session" = ["Meta+Ctrl+Q" "Screensaver" "Screensaver,Lock Session"];
};
kwin = {
"Window Close" = "Meta+Shift+Q";
"Kill Window" = "Meta+Ctrl+Esc";
"Window Operations Menu" = "Alt+F3";
"Window Resize" = "Meta+R,,Resize Window";
"Overview" = "Meta+W";
"Grid View" = "Meta+G";
"Edit Tiles" = "Meta+T";
"Activate Window Demanding Attention" = "Meta+Ctrl+A";
"Show Desktop" = "Meta+D";
"Walk Through Windows" = "Alt+Tab";
"Walk Through Windows (Reverse)" = "Alt+Shift+Tab";
"Walk Through Windows of Current Application" = "Alt+`";
"Walk Through Windows of Current Application (Reverse)" = "Alt+~";
"Window Fullscreen" = "Meta+Shift+F,,Make Window Fullscreen";
"Window Quick Tile Bottom" = "Meta+Down";
"Window Quick Tile Left" = "Meta+Left";
"Window Quick Tile Right" = "Meta+Right";
"Window Quick Tile Top" = "Meta+Up";
"view_actual_size" = "Meta+0";
"view_zoom_in" = ["Meta++" "Meta+=,Meta++" "Meta+=,Zoom In"];
"view_zoom_out" = "Meta+-";
};
"org_kde_powerdevil"."Decrease Keyboard Brightness" = "Keyboard Brightness Down";
"org_kde_powerdevil"."Decrease Screen Brightness" = "Monitor Brightness Down";
"org_kde_powerdevil"."Decrease Screen Brightness Small" = "Shift+Monitor Brightness Down";
"org_kde_powerdevil"."Hibernate" = "Hibernate";
"org_kde_powerdevil"."Increase Keyboard Brightness" = "Keyboard Brightness Up";
"org_kde_powerdevil"."Increase Screen Brightness" = "Monitor Brightness Up";
"org_kde_powerdevil"."Increase Screen Brightness Small" = "Shift+Monitor Brightness Up";
"org_kde_powerdevil"."PowerDown" = "Power Down";
"org_kde_powerdevil"."PowerOff" = "Power Off";
"org_kde_powerdevil"."Sleep" = "Sleep";
"org_kde_powerdevil"."Toggle Keyboard Backlight" = "Keyboard Light On/Off";
"org_kde_powerdevil"."Turn Off Screen" = [ ];
"org_kde_powerdevil"."powerProfile" = ["Battery" "Meta+B,Battery" "Meta+B,Switch Power Profile"];
plasmashell = {
"activate application launcher" = ["Meta" "Alt+F1,Meta" "Alt+F1,Activate Application Launcher"];
"activate task manager entry 1" = "none,,";
"activate task manager entry 2" = "none,,";
"activate task manager entry 3" = "none,,";
"activate task manager entry 4" = "none,,";
"activate task manager entry 5" = "none,,";
"activate task manager entry 6" = "none,,";
"activate task manager entry 7" = "none,,";
"activate task manager entry 8" = "none,,";
"activate task manager entry 9" = "none,,";
"activate task manager entry 10" = "none,,";
"show activity switcher" = "none,,";
};
};
configFile = {
kwinrc.Desktops.Number = {
value = 10;
immutable = true;
};
kcminputrc.Libinput = {
AccelerationProfile = "adaptive";
PointerAcceleration = 0.5;
};
kcminputrc.Mouse = {
X11LibInputXAccelProfileFlat = false;
XLbInptAccelProfileFlat = false;
};
kdeglobals.KDE.LookAndFeelPackage = "org.kde.breezedark.desktop";
};
};
}

View File

@@ -1,52 +0,0 @@
{ config, lib, pkgs, ... }:
let
tokyo-night = pkgs.tmuxPlugins.mkTmuxPlugin {
pluginName = "tokyo-night";
rtpFilePath = "tokyo-night.tmux";
version = "1.6.1";
src = pkgs.fetchFromGitHub {
owner = "janoamaral";
repo = "tokyo-night-tmux";
rev = "d610ced20d5f602a7995854931440e4a1e0ab780";
sha256 = "sha256-17vEgkL7C51p/l5gpT9dkOy0bY9n8l0/LV51mR1k+V8=";
};
};
in
{
programs.tmux.enable = true;
programs.tmux.terminal = "tmux-direct";
programs.tmux.keyMode = "vi";
programs.tmux.escapeTime = 0;
programs.tmux.mouse = true;
programs.tmux.newSession = true;
programs.tmux.historyLimit = 50000;
programs.tmux.clock24 = true;
programs.tmux.baseIndex = 1;
programs.tmux.prefix = "M-\\\\";
programs.tmux.plugins = with pkgs; [
tmuxPlugins.cpu
tmuxPlugins.battery
tmuxPlugins.better-mouse-mode
tmuxPlugins.net-speed
tmuxPlugins.online-status
tmuxPlugins.pain-control
tmuxPlugins.tilish
tmuxPlugins.yank
{
plugin = tmuxPlugins.resurrect;
extraConfig = "set -g @resurrect-strategy-nvim 'session'";
}
{
plugin = tmuxPlugins.continuum;
extraConfig = ''
set -g @continuum-restore 'on'
set -g @continuum-save-interval '15' # minutes
'';
}
tokyo-night
];
}

View File

@@ -0,0 +1,22 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles."3d-printing";
in
{
options.home.roles."3d-printing" = {
enable = mkEnableOption "Enable 3D printing applications and tools";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
# 3D Slicing Software
orca-slicer # G-code generator for 3D printers (Bambu, Prusa, Voron, etc.)
# 3D Modeling Software
openscad-unstable # 3D parametric model compiler (nightly build)
];
};
}

View File

@@ -0,0 +1,729 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.aerospace;
in
{
options.home.roles.aerospace = {
enable = mkEnableOption "AeroSpace tiling window manager for macOS";
leader = mkOption {
type = types.str;
default = "cmd";
description = "Leader key for aerospace shortcuts (e.g., 'cmd', 'ctrl', 'alt')";
example = "ctrl";
};
launchd.enable = mkOption {
type = types.bool;
default = true;
description = "Whether to enable launchd agent for auto-starting aerospace";
};
userSettings = mkOption {
type = types.attrs;
default = {};
description = ''
Additional aerospace configuration settings to merge with defaults.
Use this to override or extend the default configuration on a per-machine basis.
'';
example = literalExpression ''
{
mode.main.binding."''${leader}-custom" = "custom-command";
}
'';
};
autoraise = {
enable = mkOption {
type = types.bool;
default = true;
description = "Whether to enable autoraise (auto-focus window on hover)";
};
pollMillis = mkOption {
type = types.int;
default = 50;
description = "Polling interval in milliseconds";
};
delay = mkOption {
type = types.int;
default = 2;
description = "Delay before raising window";
};
focusDelay = mkOption {
type = types.int;
default = 2;
description = "Delay before focusing window";
};
};
enableSpansDisplays = mkOption {
type = types.bool;
default = true;
description = ''
Configure macOS Spaces to span displays (required for aerospace multi-monitor support).
Sets com.apple.spaces.spans-displays to true.
NOTE: This was previously set at the system level in modules/aerospace.nix,
but has been moved to home-manager for better modularity.
'';
};
ctrlShortcuts = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Remap common macOS Cmd shortcuts to Ctrl equivalents for all operations.
This makes macOS behave more like Linux.
Shortcuts remapped globally:
- Ctrl+N: New Window
- Ctrl+T: New Tab
- Ctrl+W: Close Tab
- Ctrl+S: Save / Save As
- Ctrl+O: Open
- Ctrl+F: Find
- Ctrl+H: Find and Replace
- Ctrl+P: Print
- Ctrl+C/V/X: Copy/Paste/Cut
- Ctrl+Z: Undo
NOTE: Terminal emulators like Ghostty require per-app overrides (configured separately)
to preserve Ctrl+C as SIGINT instead of Copy.
'';
};
};
sketchybar = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable SketchyBar status bar";
};
};
};
config = mkIf cfg.enable {
# Only apply on Darwin systems
assertions = [
{
assertion = pkgs.stdenv.isDarwin;
message = "Aerospace role is only supported on macOS (Darwin) systems";
}
];
# Configure macOS preferences via targets.darwin.defaults
targets.darwin.defaults = mkMerge [
# Spaces span displays (required for multi-monitor aerospace)
(mkIf cfg.enableSpansDisplays {
"com.apple.spaces" = {
spans-displays = true;
};
})
# Ctrl shortcuts to make macOS behave more like Linux
(mkIf cfg.ctrlShortcuts.enable {
NSGlobalDomain.NSUserKeyEquivalents = {
# Window/Tab operations
"New Window" = "^n";
"New Tab" = "^t";
"Close Tab" = "^w";
# File operations
"Save" = "^s";
"Save As" = "^$s"; # Ctrl+Shift+S
"Open" = "^o";
"Open" = "^o";
# Find operations
"Find" = "^f";
"Find" = "^f";
"Find and Replace" = "^h";
"Find and Replace" = "^h";
# Print
"Print" = "^p";
"Print" = "^p";
# Clipboard operations
"Copy" = "^c";
"Paste" = "^v";
"Cut" = "^x";
# Undo/Redo
"Undo" = "^z";
"Redo" = "^$z"; # Ctrl+Shift+Z
};
})
# Ghostty-specific overrides to preserve terminal behavior
# Remap clipboard operations back to Cmd (macOS default) so Ctrl+C remains SIGINT
(mkIf cfg.ctrlShortcuts.enable {
"com.mitchellh.ghostty".NSUserKeyEquivalents = {
# Remap back to Cmd for clipboard operations
"Copy" = "@c"; # Cmd+C
"Paste" = "@v"; # Cmd+V
"Cut" = "@x"; # Cmd+X
"Undo" = "@z"; # Cmd+Z
"Redo" = "@$z"; # Cmd+Shift+Z
};
})
];
# Install aerospace package and optional tools if enabled
home.packages = [ pkgs.aerospace ]
++ optionals cfg.autoraise.enable [ pkgs.autoraise ]
++ optionals cfg.sketchybar.enable [ pkgs.sketchybar pkgs.sketchybar-app-font ];
# Enable and configure aerospace
programs.aerospace.enable = true;
programs.aerospace.launchd.enable = cfg.launchd.enable;
programs.aerospace.userSettings = mkMerge [
# Default configuration with leader key substitution
{
# Disable normalizations for i3-like behavior
enable-normalization-flatten-containers = false;
enable-normalization-opposite-orientation-for-nested-containers = false;
mode.main.binding = {
"${cfg.leader}-w" = "layout accordion horizontal"; # tabbed
"${cfg.leader}-s" = "layout accordion vertical"; # stacking
"${cfg.leader}-e" = "layout tiles horizontal vertical"; # tiles, toggles orientation
"${cfg.leader}-shift-q" = "close";
"${cfg.leader}-shift-f" = "fullscreen";
"${cfg.leader}-h" = "focus left";
"${cfg.leader}-j" = "focus down";
"${cfg.leader}-k" = "focus up";
"${cfg.leader}-l" = "focus right";
"${cfg.leader}-shift-h" = "move left";
"${cfg.leader}-shift-j" = "move down";
"${cfg.leader}-shift-k" = "move up";
"${cfg.leader}-shift-l" = "move right";
"${cfg.leader}-r" = "mode resize";
"${cfg.leader}-1" = "workspace 1";
"${cfg.leader}-2" = "workspace 2";
"${cfg.leader}-3" = "workspace 3";
"${cfg.leader}-4" = "workspace 4";
"${cfg.leader}-5" = "workspace 5";
"${cfg.leader}-6" = "workspace 6";
"${cfg.leader}-7" = "workspace 7";
"${cfg.leader}-8" = "workspace 8";
"${cfg.leader}-9" = "workspace 9";
"${cfg.leader}-0" = "workspace 10";
"${cfg.leader}-shift-1" = "move-node-to-workspace 1";
"${cfg.leader}-shift-2" = "move-node-to-workspace 2";
"${cfg.leader}-shift-3" = "move-node-to-workspace 3";
"${cfg.leader}-shift-4" = "move-node-to-workspace 4";
"${cfg.leader}-shift-5" = "move-node-to-workspace 5";
"${cfg.leader}-shift-6" = "move-node-to-workspace 6";
"${cfg.leader}-shift-7" = "move-node-to-workspace 7";
"${cfg.leader}-shift-8" = "move-node-to-workspace 8";
"${cfg.leader}-shift-9" = "move-node-to-workspace 9";
"${cfg.leader}-shift-0" = "move-node-to-workspace 10";
"${cfg.leader}-tab" = "workspace-back-and-forth";
"${cfg.leader}-shift-tab" = "move-workspace-to-monitor --wrap-around next";
"${cfg.leader}-enter" = ''
exec-and-forget osascript <<'APPLESCRIPT'
tell application "Ghostty"
activate
tell application "System Events"
keystroke "n" using {command down}
end tell
end tell
APPLESCRIPT
'';
"${cfg.leader}-shift-enter" = ''
exec-and-forget osascript <<'APPLESCRIPT'
tell application "Google Chrome"
set newWindow to make new window
activate
tell newWindow to set index to 1
end tell
APPLESCRIPT
'';
"${cfg.leader}-shift-e" = "exec-and-forget zsh --login -c \"emacsclient -c -n\"";
# Service mode: Deliberate aerospace window management
"${cfg.leader}-i" = "mode service";
# Passthrough mode: Temporarily disable aerospace to use macOS shortcuts
"${cfg.leader}-p" = "mode passthrough";
};
# Resize mode: For window resizing operations
mode.resize.binding = {
h = "resize width -50";
j = "resize height +50";
k = "resize height -50";
l = "resize width +50";
minus = "resize smart -50";
equal = "resize smart +50";
esc = "mode main";
enter = "mode main";
};
# Service mode: For deliberate aerospace window management operations
mode.service.binding = {
esc = ["reload-config" "mode main"];
r = ["flatten-workspace-tree" "mode main"]; # reset layout
f = ["layout floating tiling" "mode main"]; # Toggle between floating and tiling layout
backspace = ["close-all-windows-but-current" "mode main"];
"${cfg.leader}-shift-h" = ["join-with left" "mode main"];
"${cfg.leader}-shift-j" = ["join-with down" "mode main"];
"${cfg.leader}-shift-k" = ["join-with up" "mode main"];
"${cfg.leader}-shift-l" = ["join-with right" "mode main"];
};
# Passthrough mode: All shortcuts pass through to macOS
mode.passthrough.binding = {
esc = "mode main";
"${cfg.leader}-p" = "mode main";
};
# SketchyBar integration - notify bar of workspace changes
exec-on-workspace-change = mkIf cfg.sketchybar.enable [
"/bin/bash" "-c"
"${pkgs.sketchybar}/bin/sketchybar --trigger aerospace_workspace_change FOCUSED=$AEROSPACE_FOCUSED_WORKSPACE PREV=$AEROSPACE_PREV_WORKSPACE"
];
}
# Gaps configuration - prevent windows from overlapping SketchyBar
(mkIf cfg.sketchybar.enable {
gaps = {
outer = {
top = 0;
bottom = 38;
left = 0;
right = 0;
};
};
})
cfg.userSettings
];
# Launchd agent for autoraise
launchd.agents.autoraise = mkIf cfg.autoraise.enable {
enable = true;
config = {
ProgramArguments = [
"${pkgs.autoraise}/bin/AutoRaise"
"-pollMillis" (toString cfg.autoraise.pollMillis)
"-delay" (toString cfg.autoraise.delay)
"-focusDelay" (toString cfg.autoraise.focusDelay)
];
RunAtLoad = true;
KeepAlive = true;
};
};
# SketchyBar configuration
home.file.".config/sketchybar/sketchybarrc" = mkIf cfg.sketchybar.enable {
executable = true;
onChange = "${pkgs.sketchybar}/bin/sketchybar --reload";
text = ''
#!/bin/bash
# Plugin directory
PLUGIN_DIR="$HOME/.config/sketchybar/plugins"
# Colors - i3/sway theme with exact color matching
# Focused window/workspace color from i3/sway
FOCUSED=0xff285577
# Background colors matching i3blocks bar
BAR_BG=0xff333333 # Dark gray
ITEM_BG=0xff333333 # Dark gray matching bar
# Text colors
TEXT=0xffffffff # White text
GRAY=0xff888888 # Muted text for inactive items
# Accent colors for warnings
WARNING=0xffff9900
CRITICAL=0xff900000
# Configure the bar appearance
${pkgs.sketchybar}/bin/sketchybar --bar \
position=bottom \
height=30 \
color=$BAR_BG \
border_width=0 \
corner_radius=0 \
padding_left=10 \
padding_right=10 \
shadow=off \
topmost=on \
sticky=on
# Set default properties for all items
# Using monospace font to match waybar's Fira Code styling
${pkgs.sketchybar}/bin/sketchybar --default \
updates=when_shown \
icon.font="Fira Code:Regular:13.0" \
icon.color=$TEXT \
icon.padding_left=4 \
icon.padding_right=4 \
label.font="Fira Code:Regular:13.0" \
label.color=$TEXT \
label.padding_left=4 \
label.padding_right=4 \
padding_left=4 \
padding_right=4 \
background.corner_radius=0 \
background.height=30
# Register aerospace workspace change event
${pkgs.sketchybar}/bin/sketchybar --add event aerospace_workspace_change
# Create workspace indicators for workspaces 1-10
for sid in 1 2 3 4 5 6 7 8 9 10; do
# Display "0" for workspace 10
if [ "$sid" = "10" ]; then
display="0"
else
display="$sid"
fi
${pkgs.sketchybar}/bin/sketchybar --add item space.$sid left \
--subscribe space.$sid aerospace_workspace_change \
--set space.$sid \
drawing=on \
update_freq=2 \
width=32 \
background.color=$ITEM_BG \
background.corner_radius=0 \
background.height=30 \
background.drawing=on \
icon="$display" \
icon.padding_left=13 \
icon.padding_right=11 \
icon.align=center \
label.drawing=off \
click_script="${pkgs.aerospace}/bin/aerospace workspace $sid" \
script="$PLUGIN_DIR/aerospace.sh $sid"
done
# System monitoring modules (right side)
# Note: Items added to 'right' appear in reverse order (last added = leftmost)
# Adding in reverse to get: disk | cpu | memory | battery | volume | calendar
${pkgs.sketchybar}/bin/sketchybar --add item calendar right \
--set calendar \
icon="📅" \
update_freq=30 \
background.color=$ITEM_BG \
background.drawing=on \
script="$PLUGIN_DIR/calendar.sh"
${pkgs.sketchybar}/bin/sketchybar --add item volume right \
--set volume \
background.color=$ITEM_BG \
background.drawing=on \
script="$PLUGIN_DIR/volume.sh" \
--subscribe volume volume_change
${pkgs.sketchybar}/bin/sketchybar --add item battery right \
--set battery \
update_freq=120 \
background.color=$ITEM_BG \
background.drawing=on \
script="$PLUGIN_DIR/battery.sh" \
--subscribe battery system_woke power_source_change
${pkgs.sketchybar}/bin/sketchybar --add item memory right \
--set memory \
update_freq=5 \
icon="🐏" \
background.color=$ITEM_BG \
background.drawing=on \
script="$PLUGIN_DIR/memory.sh"
${pkgs.sketchybar}/bin/sketchybar --add item cpu right \
--set cpu \
update_freq=2 \
icon="🧠" \
background.color=$ITEM_BG \
background.drawing=on \
script="$PLUGIN_DIR/cpu.sh"
${pkgs.sketchybar}/bin/sketchybar --add item disk right \
--set disk \
update_freq=60 \
icon="💾" \
background.color=$ITEM_BG \
background.drawing=on \
script="$PLUGIN_DIR/disk.sh"
# Menu bar extras / system tray items (rightmost)
# Note: Requires Screen Recording permission for SketchyBar in System Settings
# Use 'sketchybar --query default_menu_items' to discover available items
# Bluetooth
${pkgs.sketchybar}/bin/sketchybar --add alias "Control Center,Bluetooth" right \
--set "Control Center,Bluetooth" \
alias.update_freq=1 \
padding_left=0 \
padding_right=0
# WiFi
${pkgs.sketchybar}/bin/sketchybar --add alias "Control Center,WiFi" right \
--set "Control Center,WiFi" \
alias.update_freq=1 \
padding_left=0 \
padding_right=0
# Add other menu bar apps as discovered
# Common examples:
# - Cloudflare WARP: --add alias "Cloudflare WARP,Item-0" right
# - Notion Calendar: --add alias "Notion Calendar,Item-0" right
# Run 'sketchybar --query default_menu_items' to find exact names
# Update the bar
${pkgs.sketchybar}/bin/sketchybar --update
'';
};
# SketchyBar aerospace workspace plugin
home.file.".config/sketchybar/plugins/aerospace.sh" = mkIf cfg.sketchybar.enable {
executable = true;
text = ''
#!/bin/bash
# Colors
FOCUSED_COLOR=0xff285577
ITEM_BG=0xff333333
TEXT=0xffffffff
GRAY=0xff555555
# Get the currently focused workspace directly from aerospace
# Trim whitespace to ensure clean comparison
FOCUSED=$(${pkgs.aerospace}/bin/aerospace list-workspaces --focused | tr -d ' \n\r')
# Get list of empty workspaces
EMPTY_WORKSPACES=$(${pkgs.aerospace}/bin/aerospace list-workspaces --monitor all --empty)
# Get workspace number - from $1 if provided (event-triggered), otherwise extract from $NAME (routine update)
# $NAME is always available (e.g., "space.1", "space.2", etc.)
# $1 is only available when called via event trigger with positional argument
if [ -n "$1" ]; then
WORKSPACE_NUM=$(echo "$1" | tr -d ' \n\r')
else
# Extract number from item name: "space.1" -> "1", "space.10" -> "10"
WORKSPACE_NUM=$(echo "$NAME" | sed 's/space\.//')
fi
# Check if workspace has windows (is NOT empty)
IS_EMPTY=false
if echo "$EMPTY_WORKSPACES" | grep -q "^$WORKSPACE_NUM$"; then
IS_EMPTY=true
fi
# Check if this workspace is focused
IS_FOCUSED=false
if [ "$WORKSPACE_NUM" = "$FOCUSED" ]; then
IS_FOCUSED=true
fi
# Determine display value (workspace 10 displays as "0")
if [ "$WORKSPACE_NUM" = "10" ]; then
DISPLAY="0"
else
DISPLAY="$WORKSPACE_NUM"
fi
# Determine visibility and styling
# Always show focused workspace (even if empty) with fixed width
# Hide non-focused empty workspaces by setting width to 0 (collapsed)
# Show non-focused non-empty workspaces with fixed width and inactive styling
if [ "$IS_FOCUSED" = "true" ]; then
# Focused workspace - always show with focused styling and bold font
${pkgs.sketchybar}/bin/sketchybar --set space.$WORKSPACE_NUM \
drawing=on \
icon="$DISPLAY" \
width=32 \
icon.padding_left=13 \
icon.padding_right=11 \
icon.align=center \
background.color=$FOCUSED_COLOR \
background.drawing=on \
icon.color=$TEXT \
icon.font="Fira Code:Bold:13.0"
elif [ "$IS_EMPTY" = "true" ]; then
# Empty workspace (not focused) - hide by collapsing width and clearing content
# Using width=0 with drawing=on so updates=when_shown continues to run the script
${pkgs.sketchybar}/bin/sketchybar --set space.$WORKSPACE_NUM \
drawing=on \
icon="" \
label="" \
width=0 \
icon.padding_left=0 \
icon.padding_right=0 \
background.drawing=off
else
# Non-empty workspace (not focused) - show with inactive styling and white text
${pkgs.sketchybar}/bin/sketchybar --set space.$WORKSPACE_NUM \
drawing=on \
icon="$DISPLAY" \
width=32 \
icon.padding_left=13 \
icon.padding_right=11 \
icon.align=center \
background.color=$ITEM_BG \
background.drawing=on \
icon.color=$TEXT \
icon.font="Fira Code:Regular:13.0"
fi
'';
};
# SketchyBar CPU monitoring plugin
home.file.".config/sketchybar/plugins/cpu.sh" = mkIf cfg.sketchybar.enable {
executable = true;
text = ''
#!/bin/bash
CORE_COUNT=$(sysctl -n machdep.cpu.thread_count)
CPU_INFO=$(ps -eo pcpu,user)
CPU_SYS=$(echo "$CPU_INFO" | grep -v $(whoami) | sed "s/[^ 0-9\.]//g" | awk "{sum+=\$1} END {print sum/(100.0 * $CORE_COUNT)}")
CPU_USER=$(echo "$CPU_INFO" | grep $(whoami) | sed "s/[^ 0-9\.]//g" | awk "{sum+=\$1} END {print sum/(100.0 * $CORE_COUNT)}")
CPU_PERCENT="$(echo "$CPU_SYS $CPU_USER" | awk '{printf "%.0f\n", ($1 + $2)*100}')"
${pkgs.sketchybar}/bin/sketchybar --set $NAME label="$CPU_PERCENT%"
'';
};
# SketchyBar memory monitoring plugin
# Shows actual memory pressure (excludes file cache/inactive pages)
home.file.".config/sketchybar/plugins/memory.sh" = mkIf cfg.sketchybar.enable {
executable = true;
text = ''
#!/bin/bash
# Use awk for all arithmetic to avoid bash integer overflow on large RAM systems
# Memory pressure = Anonymous (app memory) + Wired + Compressor RAM
# - Anonymous pages: app-allocated memory (heap, stack) - matches Activity Monitor's "App Memory"
# - Wired: kernel/system memory that can't be paged out
# - Pages occupied by compressor: actual RAM used by compressor (NOT "stored in compressor")
TOTAL_RAM=$(sysctl -n hw.memsize)
MEMORY_PERCENT=$(vm_stat | awk -v total_ram="$TOTAL_RAM" '
/page size of/ { page_size = $8 }
/Anonymous pages/ { anon = $3 + 0 }
/Pages wired/ { wired = $4 + 0 }
/Pages occupied by compressor/ { compressor = $5 + 0 }
END {
used = (anon + wired + compressor) * page_size
printf "%.0f", used / total_ram * 100
}
')
${pkgs.sketchybar}/bin/sketchybar --set $NAME label="$MEMORY_PERCENT%"
'';
};
# SketchyBar disk monitoring plugin
home.file.".config/sketchybar/plugins/disk.sh" = mkIf cfg.sketchybar.enable {
executable = true;
text = ''
#!/bin/bash
# Monitor /System/Volumes/Data which contains user data on APFS
# The root / is a read-only snapshot with minimal usage
DISK_USAGE=$(df -H /System/Volumes/Data | grep -v Filesystem | awk '{print $5}')
${pkgs.sketchybar}/bin/sketchybar --set $NAME label="$DISK_USAGE"
'';
};
# SketchyBar battery monitoring plugin
home.file.".config/sketchybar/plugins/battery.sh" = mkIf cfg.sketchybar.enable {
executable = true;
text = ''
#!/bin/bash
PERCENTAGE=$(pmset -g batt | grep -Eo "\d+%" | cut -d% -f1)
CHARGING=$(pmset -g batt | grep 'AC Power')
if [ "$PERCENTAGE" = "" ]; then
exit 0
fi
# Select icon based on battery level
case ''${PERCENTAGE} in
9[0-9]|100) ICON="🔋"
;;
[6-8][0-9]) ICON="🔋"
;;
[3-5][0-9]) ICON="🔋"
;;
[1-2][0-9]) ICON="🔋"
;;
*) ICON="🪫"
esac
# Show charging icon if connected to power
if [[ $CHARGING != "" ]]; then
ICON=""
fi
${pkgs.sketchybar}/bin/sketchybar --set $NAME icon="$ICON" label="''${PERCENTAGE}%"
'';
};
# SketchyBar volume monitoring plugin
home.file.".config/sketchybar/plugins/volume.sh" = mkIf cfg.sketchybar.enable {
executable = true;
text = ''
#!/bin/bash
if [ "$SENDER" = "volume_change" ]; then
VOLUME=$(osascript -e "output volume of (get volume settings)")
MUTED=$(osascript -e "output muted of (get volume settings)")
if [ "$MUTED" = "true" ]; then
ICON="🔇"
LABEL=""
else
case $VOLUME in
[6-9][0-9]|100) ICON="🔊"
;;
[3-5][0-9]) ICON="🔉"
;;
*) ICON="🔈"
esac
LABEL="$VOLUME%"
fi
${pkgs.sketchybar}/bin/sketchybar --set $NAME icon="$ICON" label="$LABEL"
fi
'';
};
# SketchyBar calendar/clock plugin
home.file.".config/sketchybar/plugins/calendar.sh" = mkIf cfg.sketchybar.enable {
executable = true;
text = ''
#!/bin/bash
${pkgs.sketchybar}/bin/sketchybar --set $NAME label="$(date '+%Y-%m-%d %H:%M')"
'';
};
# Launchd agent for auto-starting sketchybar
launchd.agents.sketchybar = mkIf cfg.sketchybar.enable {
enable = true;
config = {
ProgramArguments = [ "${pkgs.sketchybar}/bin/sketchybar" ];
RunAtLoad = true;
KeepAlive = true;
StandardOutPath = "/tmp/sketchybar.log";
StandardErrorPath = "/tmp/sketchybar.err.log";
};
};
};
}

View File

@@ -0,0 +1,11 @@
{
# Base imports for Darwin home configurations
# Includes Darwin-specific roles that only work on macOS
imports = [
../aerospace
];
# Override to use -d instead of --delete-older-than on Darwin due to launchd bug
# https://github.com/nix-community/home-manager/issues/7211
nix.gc.options = "-d";
}

View File

@@ -0,0 +1,9 @@
{
# Base imports for Linux home configurations
# Includes Linux-specific roles that require Linux-only home-manager modules
imports = [
../plasma-manager
../plasma-manager-kodi
../i3+sway
];
}

104
home/roles/base/default.nix Normal file
View File

@@ -0,0 +1,104 @@
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.home.roles.base;
in
{
options.home.roles.base = {
enable = mkEnableOption "Enable base CLI tools and essential programs";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
fd
glances
gzip
htop
killall
less
lnav
ncdu
shellcheck
tmux
tree
watch
custom.opencode
];
# Automatic garbage collection for user profile (home-manager generations).
# This complements system-level gc which only cleans system generations.
# - Linux: Uses --delete-older-than to keep 10-day rollback window
# - Darwin: Overridden to use -d in base-darwin role to avoid launchd bug
# (https://github.com/nix-community/home-manager/issues/7211)
nix.gc = {
automatic = true;
randomizedDelaySec = mkIf pkgs.stdenv.isLinux "14m";
options = lib.mkDefault "--delete-older-than 10d";
};
# Essential programs everyone needs
programs.bash = {
enable = true;
initExtra = ''
codex() {
local key
key="$(rbw get openai-api-key-codex)"
OPENAI_API_KEY="$key" command codex "$@"
}
'';
};
programs.home-manager.enable = true;
programs.command-not-found.enable = true;
programs.git = {
enable = true;
signing.format = null;
settings = {
user.name = "John Ogle";
user.email = "john@ogle.fyi";
safe.directory = "/etc/nixos";
};
};
programs.jq.enable = true;
programs.neovim = {
enable = true;
viAlias = true;
vimAlias = true;
};
programs.ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks = {
"*" = {
addKeysToAgent = "yes";
};
"nucdeb1" = {
hostname = "nucdeb1.oglehome";
user = "root";
};
};
};
programs.rbw = {
enable = true;
settings = {
email = "john@johnogle.info";
base_url = "https://bitwarden.johnogle.info";
pinentry = pkgs.pinentry-qt;
};
};
# Note: modules must be imported at top-level home config
};
}

View File

@@ -0,0 +1,26 @@
{ config, lib, pkgs, globalInputs, system, ... }:
with lib;
let
cfg = config.home.roles.communication;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.communication = {
enable = mkEnableOption "Enable communication and messaging applications";
};
config = mkIf cfg.enable {
home.packages = [
# For logging back into google chat (cross-platform)
globalInputs.google-cookie-retrieval.packages.${system}.default
] ++ optionals isLinux [
# Linux-only communication apps (Electron apps don't build on Darwin)
pkgs.element-desktop
# Re-enabled in 25.11 after security issues were resolved
pkgs.fluffychat
pkgs.custom.nextcloud-talk-desktop
];
};
}

23
home/roles/default.nix Normal file
View File

@@ -0,0 +1,23 @@
{
# Shared roles that work across all platforms (Linux, Darwin, etc.)
# Platform-specific roles are imported via base-linux or base-darwin
# in each home configuration file
imports = [
./3d-printing
./base
./communication
./desktop
./development
./email
./gaming
./kdeconnect
./kubectl
./launchers
./media
./office
./sync
./tmux
./emacs
./starship
];
}

View File

@@ -0,0 +1,210 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.desktop;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.desktop = {
enable = mkEnableOption "Enable desktop GUI applications and utilities";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Cross-platform desktop applications
bitwarden-desktop
keepassxc
xdg-utils # XDG utilities for opening files/URLs with default applications
] ++ optionals isLinux [
# Linux-only desktop applications
dunst
unstable.ghostty
# Linux-only desktop utilities
feh # Image viewer and wallpaper setter for X11
rofi # Application launcher for X11
solaar # Logitech management software
waybar
wofi # Application launcher for Wayland
# Linux-only system utilities with GUI components
(snapcast.override { pulseaudioSupport = true; })
# KDE tiling window management (Linux-only)
kdePackages.krohnkite # Dynamic tiling extension for KWin 6
# KDE PIM applications for email, calendar, and contacts (Linux-only)
kdePackages.kmail
kdePackages.kmail-account-wizard
kdePackages.kmailtransport
kdePackages.korganizer
kdePackages.kaddressbook
kdePackages.kontact
# KDE System components needed for proper integration (Linux-only)
kdePackages.kded
kdePackages.systemsettings
kdePackages.kmenuedit
# Desktop menu support (Linux-only)
kdePackages.plasma-desktop # Contains applications.menu
# KDE Online Accounts support (Linux-only)
kdePackages.kaccounts-integration
kdePackages.kaccounts-providers
kdePackages.signond
# KDE Mapping (Linux-only)
kdePackages.marble # Virtual globe and world atlas
# KDE Productivity (Linux-only)
kdePackages.kate # Advanced text editor with syntax highlighting
kdePackages.okular # Universal document viewer (PDF, ePub, etc.)
kdePackages.spectacle # Screenshot capture utility
kdePackages.filelight # Visual disk usage analyzer
# KDE Multimedia (Linux-only)
kdePackages.gwenview # Image viewer and basic editor
kdePackages.elisa # Music player
# KDE System Utilities (Linux-only)
kdePackages.ark # Archive manager (zip, tar, 7z, etc.)
kdePackages.yakuake # Drop-down terminal emulator
];
programs.firefox = {
enable = true;
};
programs.spotify-player.enable = true;
# Linux-only: GNOME keyring service
services.gnome-keyring = mkIf isLinux {
enable = true;
};
# Linux-only: systemd user services for rbw vault unlock
systemd.user.services = mkIf isLinux {
# rbw vault unlock on login
rbw-unlock-on-login = {
Unit = {
Description = "Unlock rbw vault at login";
After = [ "graphical-session.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.rbw}/bin/rbw unlock";
Environment = "RBW_AGENT=${pkgs.rbw}/bin/rbw-agent";
# KillMode = "process" prevents systemd from killing the rbw-agent daemon
# when this oneshot service completes. The agent is spawned by rbw unlock
# and needs to persist after the service exits.
KillMode = "process";
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
# rbw vault unlock on resume from suspend
rbw-unlock-on-resume = {
Unit = {
Description = "Unlock rbw vault after resume from suspend";
After = [ "suspend.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.rbw}/bin/rbw unlock";
Environment = "RBW_AGENT=${pkgs.rbw}/bin/rbw-agent";
# KillMode = "process" prevents systemd from killing the rbw-agent daemon
# when this oneshot service completes. The agent is spawned by rbw unlock
# and needs to persist after the service exits.
KillMode = "process";
};
Install = {
WantedBy = [ "suspend.target" ];
};
};
};
# Linux-only: KDE environment variables for proper integration
home.sessionVariables = mkIf isLinux {
QT_QPA_PLATFORMTHEME = "kde";
KDE_SESSION_VERSION = "6";
};
xdg = {
enable = true;
# Ensure desktop files are made available for discovery
desktopEntries = {}; # This creates the desktop files directory structure
mimeApps = {
enable = true;
associations.added = {
# Ensure associations are properly registered
"text/html" = "firefox.desktop";
"x-scheme-handler/http" = "firefox.desktop";
"x-scheme-handler/https" = "firefox.desktop";
};
defaultApplications = {
# Web browsers (cross-platform)
"text/html" = "firefox.desktop";
"x-scheme-handler/http" = "firefox.desktop";
"x-scheme-handler/https" = "firefox.desktop";
"x-scheme-handler/about" = "firefox.desktop";
"x-scheme-handler/unknown" = "firefox.desktop";
} // optionalAttrs isLinux {
# Linux-only: KDE application associations
# Documents
"application/pdf" = "okular.desktop";
"text/plain" = "kate.desktop";
"text/x-tex" = "kate.desktop";
"text/x-c" = "kate.desktop";
"text/x-python" = "kate.desktop";
"application/x-shellscript" = "kate.desktop";
# Images
"image/png" = "gwenview.desktop";
"image/jpeg" = "gwenview.desktop";
"image/jpg" = "gwenview.desktop";
"image/gif" = "gwenview.desktop";
"image/bmp" = "gwenview.desktop";
"image/tiff" = "gwenview.desktop";
"image/webp" = "gwenview.desktop";
# Archives
"application/zip" = "ark.desktop";
"application/x-tar" = "ark.desktop";
"application/x-compressed-tar" = "ark.desktop";
"application/x-7z-compressed" = "ark.desktop";
"application/x-rar" = "ark.desktop";
# Audio
"audio/mpeg" = "elisa.desktop";
"audio/mp4" = "elisa.desktop";
"audio/flac" = "elisa.desktop";
"audio/ogg" = "elisa.desktop";
"audio/wav" = "elisa.desktop";
# Email
"message/rfc822" = "kmail.desktop";
"x-scheme-handler/mailto" = "kmail.desktop";
# Calendar
"text/calendar" = "korganizer.desktop";
"application/x-vnd.akonadi.calendar.event" = "korganizer.desktop";
};
};
};
# Linux-only: Fix for KDE applications.menu file issue on Plasma 6
# KDE still looks for applications.menu but Plasma 6 renamed it to plasma-applications.menu
xdg.configFile."menus/applications.menu" = mkIf isLinux {
source = "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
};
# Note: modules must be imported at top-level home config
};
}

View File

@@ -0,0 +1,123 @@
{ config, lib, pkgs, globalInputs, system, ... }:
with lib;
let
cfg = config.home.roles.development;
# Fetch the claude-plugins repository (for humanlayer commands/agents)
# Update the rev to get newer versions of the commands
claudePluginsRepo = builtins.fetchGit {
url = "https://github.com/jeffh/claude-plugins.git";
# To update: change this to the latest commit hash
# You can find the latest commit at: https://github.com/jeffh/claude-plugins/commits/main
rev = "5e3e4d937162185b6d78c62022cbfd1c8ad42c4c";
ref = "main";
};
# Claude Code statusline: shows model, cwd, git branch, and context usage %
claudeCodeStatusLineConfig = pkgs.writeText "claude-statusline.json" (builtins.toJSON {
type = "command";
command = ''input=$(cat); model=$(echo "$input" | jq -r '.model.display_name'); cwd=$(echo "$input" | jq -r '.workspace.current_dir'); if git -C "$cwd" rev-parse --git-dir > /dev/null 2>&1; then branch=$(git -C "$cwd" --no-optional-locks rev-parse --abbrev-ref HEAD 2>/dev/null || echo ""); if [ -n "$branch" ]; then git_info=" on $branch"; else git_info=""; fi; else git_info=""; fi; usage=$(echo "$input" | jq '.context_window.current_usage'); if [ "$usage" != "null" ]; then current=$(echo "$usage" | jq '.input_tokens + .cache_creation_input_tokens + .cache_read_input_tokens'); size=$(echo "$input" | jq '.context_window.context_window_size'); pct=$((current * 100 / size)); context_info=" | ''${pct}% context"; else context_info=""; fi; printf "%s in %s%s%s" "$model" "$cwd" "$git_info" "$context_info"'';
});
in
{
options.home.roles.development = {
enable = mkEnableOption "Enable development tools and utilities";
allowArbitraryClaudeCodeModelSelection = mkOption {
type = types.bool;
default = false;
description = ''
Whether to preserve model specifications in Claude Code humanlayer commands and agents.
When false (default), the model: line is stripped from frontmatter, allowing Claude Code
to use its default model selection.
When true, the model: specifications from the source files are preserved, allowing
commands to specify opus/sonnet/haiku explicitly.
'';
};
};
config = mkIf cfg.enable {
home.packages = [
pkgs.unstable.claude-code
pkgs.unstable.codex
pkgs.sqlite
# Custom packages
pkgs.custom.tea-rbw
pkgs.custom.pi-coding-agent
];
# Install Claude Code humanlayer command and agent plugins
home.activation.claudeCodeCommands = lib.hm.dag.entryAfter ["writeBoundary"] ''
# Clean up old plugin-installed commands and agents to avoid duplicates
rm -f ~/.claude/commands/humanlayer:* 2>/dev/null || true
rm -f ~/.claude/agents/humanlayer:* 2>/dev/null || true
# Remove explicitly blocked commands that may have been installed previously
rm -f ~/.claude/commands/humanlayer:create_handoff.md 2>/dev/null || true
# Create directories if they don't exist
mkdir -p ~/.claude/commands
mkdir -p ~/.claude/agents
# Copy all humanlayer command files and remove model specifications
for file in ${claudePluginsRepo}/humanlayer/commands/*.md; do
if [ -f "$file" ]; then
filename=$(basename "$file" .md)
# Skip blocked commands
case "$filename" in
create_handoff) continue ;;
esac
dest="$HOME/.claude/commands/humanlayer:''${filename}.md"
rm -f "$dest" 2>/dev/null || true
# Copy file and conditionally remove the "model:" line from frontmatter
${if cfg.allowArbitraryClaudeCodeModelSelection
then "cp \"$file\" \"$dest\""
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
}
chmod u+w "$dest" 2>/dev/null || true
fi
done
# Copy all humanlayer agent files and remove model specifications
for file in ${claudePluginsRepo}/humanlayer/agents/*.md; do
if [ -f "$file" ]; then
filename=$(basename "$file" .md)
dest="$HOME/.claude/agents/humanlayer:''${filename}.md"
rm -f "$dest" 2>/dev/null || true
# Copy file and conditionally remove the "model:" line from frontmatter
${if cfg.allowArbitraryClaudeCodeModelSelection
then "cp \"$file\" \"$dest\""
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
}
chmod u+w "$dest" 2>/dev/null || true
fi
done
$DRY_RUN_CMD echo "Claude Code plugins installed: humanlayer commands/agents"
'';
# Configure Claude Code statusline (merge into existing settings.json)
home.activation.claudeCodeStatusLine = lib.hm.dag.entryAfter ["writeBoundary"] ''
SETTINGS="$HOME/.claude/settings.json"
mkdir -p "$HOME/.claude"
if [ -f "$SETTINGS" ]; then
${pkgs.jq}/bin/jq --slurpfile sl ${claudeCodeStatusLineConfig} '.statusLine = $sl[0]' "$SETTINGS" > "''${SETTINGS}.tmp" && mv "''${SETTINGS}.tmp" "$SETTINGS"
else
${pkgs.jq}/bin/jq -n --slurpfile sl ${claudeCodeStatusLineConfig} '{statusLine: $sl[0]}' > "$SETTINGS"
fi
$DRY_RUN_CMD echo "Claude Code statusline configured"
'';
# Note: modules must be imported at top-level home config
};
}

View File

@@ -0,0 +1,118 @@
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.home.roles.emacs;
doomEmacs = pkgs.fetchFromGitHub {
owner = "doomemacs";
repo = "doomemacs";
rev = "d23bbe87721c61f4d5a605f2914b32780bb89949";
sha256 = "sha256-z+3c0AGkrMf1xZ+pq57aVp4Zo4KsqFMIjEVzSZinghc=";
};
# Shared emacs packages
emacsPackages = epkgs: [
epkgs.vterm
epkgs.treesit-grammars.with-all-grammars
];
# Default emacs configuration with vterm support
defaultEmacsPackage =
if pkgs.stdenv.isDarwin then
pkgs.emacs-macport.pkgs.withPackages emacsPackages
else
pkgs.emacs.pkgs.withPackages emacsPackages;
# Path to doom config directory (relative to this file)
doomConfigDir = ./doom;
in
{
options.home.roles.emacs = {
enable = mkEnableOption "Doom Emacs with vterm and tree-sitter support";
prebuiltDoom = mkOption {
type = types.bool;
default = false;
description = ''
Use nix-doom-emacs-unstraightened to pre-build all Doom packages at
nix build time. This eliminates the need to run `doom sync` after
first boot, making it ideal for live USB images or immutable systems.
When enabled, the doom configuration is read-only (stored in nix store).
'';
};
};
config = mkIf cfg.enable (mkMerge [
# Common configuration for both modes
{
home.packages = [
pkgs.emacs-all-the-icons-fonts
pkgs.fira-code
pkgs.fontconfig
pkgs.graphviz
pkgs.isort
pkgs.nerd-fonts.fira-code
pkgs.nerd-fonts.droid-sans-mono
pkgs.nil # nix lsp language server
pkgs.nixfmt
(pkgs.ripgrep.override { withPCRE2 = true; })
pkgs.pipenv
pkgs.poetry
pkgs.python3
];
fonts.fontconfig.enable = true;
}
# Standard Doom Emacs mode (requires doom sync at runtime)
(mkIf (!cfg.prebuiltDoom) {
programs.emacs = {
enable = true;
package = defaultEmacsPackage;
};
# Mount emacs and tree-sitter grammars from nix store
home.file = {
"${config.xdg.configHome}/emacs".source = doomEmacs;
};
home.sessionPath = [
"${config.xdg.configHome}/emacs/bin"
];
home.sessionVariables = {
DOOMDIR = "${config.xdg.configHome}/doom";
DOOMLOCALDIR = "${config.xdg.dataHome}/doom";
};
# TODO: Use mkOutOfStoreSymlink instead?
home.activation.doomConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
# Always remove and recreate the symlink to ensure it points to the source directory
rm -rf "${config.xdg.configHome}/doom"
ln -sf "${config.home.homeDirectory}/nixos-configs/home/roles/emacs/doom" "${config.xdg.configHome}/doom"
'';
})
# Pre-built Doom Emacs mode (no doom sync needed - ideal for live USB)
(mkIf cfg.prebuiltDoom {
programs.doom-emacs = {
enable = true;
doomDir = doomConfigDir;
doomLocalDir = "${config.xdg.dataHome}/doom";
# Add extra packages that aren't part of Doom but needed for our config
extraPackages = epkgs: [
epkgs.vterm
epkgs.treesit-grammars.with-all-grammars
];
};
})
]);
}

View File

@@ -0,0 +1,394 @@
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
;; Place your private configuration here! Remember, you do not need to run 'doom
;; sync' after modifying this file!
;; Some functionality uses this to identify you, e.g. GPG configuration, email
;; clients, file templates and snippets. It is optional.
;; (setq user-full-name "John Doe"
;; user-mail-address "john@doe.com")
;; Doom exposes five (optional) variables for controlling fonts in Doom:
;;
;; - `doom-font' -- the primary font to use
;; - `doom-variable-pitch-font' -- a non-monospace font (where applicable)
;; - `doom-big-font' -- used for `doom-big-font-mode'; use this for
;; presentations or streaming.
;; - `doom-symbol-font' -- for symbols
;; - `doom-serif-font' -- for the `fixed-pitch-serif' face
;;
;; See 'C-h v doom-font' for documentation and more examples of what they
;; accept. For example:
;;
;;(setq doom-font (font-spec :family "Fira Code" :size 12 :weight 'semi-light)
;; doom-variable-pitch-font (font-spec :family "Fira Sans" :size 13))
;;
;; If you or Emacs can't find your font, use 'M-x describe-font' to look them
;; up, `M-x eval-region' to execute elisp code, and 'M-x doom/reload-font' to
;; refresh your font settings. If Emacs still can't find your font, it likely
;; wasn't installed correctly. Font issues are rarely Doom issues!
(setq doom-font (font-spec :family "Fira Code" :size 16))
;; Auto-install nerd-icons fonts if they're missing
(defun my/ensure-nerd-icons-fonts ()
"Check if nerd-icons fonts are installed and install them if missing."
(when (display-graphic-p)
(unless (find-font (font-spec :name "Symbols Nerd Font Mono"))
(when (fboundp 'nerd-icons-install-fonts)
(nerd-icons-install-fonts t)))))
(add-hook 'doom-init-ui-hook #'my/ensure-nerd-icons-fonts)
;; There are two ways to load a theme. Both assume the theme is installed and
;; available. You can either set `doom-theme' or manually load a theme with the
;; `load-theme' function. This is the default:
(setq doom-theme 'doom-tokyo-night)
;; This determines the style of line numbers in effect. If set to `nil', line
;; numbers are disabled. For relative line numbers, set this to `relative'.
(setq display-line-numbers-type t)
;; If you use `org' and don't want your org files in the default location below,
;; change `org-directory'. It must be set before org loads!
(setq org-directory "~/org/")
(after! org
;; Skip recurring events past their CALDAV_UNTIL date
;; org-caldav ignores UNTIL from RRULE, so we store it as a property
;; and filter here in the agenda
(defun my/skip-if-past-until ()
"Return non-nil if entry has CALDAV_UNTIL and current date is past it."
(let ((until-str (org-entry-get nil "CALDAV_UNTIL")))
(when (and until-str
(string-match "^\\([0-9]\\{4\\}\\)\\([0-9]\\{2\\}\\)\\([0-9]\\{2\\}\\)" until-str))
(let* ((until-year (string-to-number (match-string 1 until-str)))
(until-month (string-to-number (match-string 2 until-str)))
(until-day (string-to-number (match-string 3 until-str)))
(until-time (encode-time 0 0 0 until-day until-month until-year))
(today (current-time)))
(when (time-less-p until-time today)
(org-end-of-subtree t))))))
(setq org-agenda-span 'week
org-agenda-start-with-log-mode t
my-agenda-dirs '("projects" "roam")
org-agenda-files (cons org-directory (mapcan (lambda (x) (directory-files-recursively
(expand-file-name x org-directory)
"\.org$"))
my-agenda-dirs))
org-log-done 'time
org-agenda-skip-function-global #'my/skip-if-past-until
org-agenda-custom-commands '(("n" "Agenda"
((agenda "")
(tags-todo "-someday-recurring")))
("s" "Someday Items"
((tags-todo "+someday"))))
org-todo-keywords '((sequence "TODO(t)" "IN-PROGRESS(p)" "WAIT(w)" "|" "DONE(d)" "KILL(k)"))
org-journal-file-type 'weekly
org-journal-file-format "%Y-%m-%d.org"
org-capture-templates
'(("t" "Todo" entry (file+headline "~/org/todo.org" "Inbox")
"* TODO %? \n %i \n%a" :prepend t)))
;; Make blocked tasks more visible in agenda (they have subtasks to do!)
(custom-set-faces!
'(org-agenda-dimmed-todo-face :foreground "#bb9af7" :weight normal)))
(map! :after org-agenda
:map org-agenda-mode-map
:localleader
(:prefix ("v" . "view")
"d" #'org-agenda-day-view
"w" #'org-agenda-week-view))
;; org-caldav: Sync Org entries with Nextcloud CalDAV
;; Setup requirements:
;; 1. Create Nextcloud app password: Settings -> Security -> Devices & sessions
;; 2. Store in rbw: rbw add nextcloud-caldav (put app password as the secret)
;; 3. Run: doom sync
;; 4. Test: M-x my/org-caldav-sync-with-rbw (or SPC o a s)
;;
;; Note: Conflict resolution is "Org always wins" - treat Org as source of truth
;; for entries that originated in Org.
;; Define sync wrapper before use-package (so keybinding works)
(defun my/org-caldav-sync-with-rbw ()
"Run org-caldav-sync with credentials from rbw embedded in URL."
(interactive)
(require 'org)
(require 'org-caldav)
(let* ((password (my/get-rbw-password "nextcloud-caldav"))
;; Embed credentials in URL (url-encode password in case of special chars)
(encoded-pass (url-hexify-string password)))
(setq org-caldav-url
(format "https://johno:%s@nextcloud.johnogle.info/remote.php/dav/calendars/johno"
encoded-pass))
(org-caldav-sync)))
(use-package! org-caldav
:after org
:commands (org-caldav-sync my/org-caldav-sync-with-rbw)
:init
(map! :leader
(:prefix ("o" . "open")
(:prefix ("a" . "agenda/calendar")
:desc "Sync CalDAV" "s" #'my/org-caldav-sync-with-rbw)))
:config
;; Nextcloud CalDAV base URL (credentials added dynamically by sync wrapper)
(setq org-caldav-url "https://nextcloud.johnogle.info/remote.php/dav/calendars/johno")
;; Timezone for iCalendar export
(setq org-icalendar-timezone "America/Los_Angeles")
;; Sync state storage (in org directory for multi-machine sync)
(setq org-caldav-save-directory (expand-file-name ".org-caldav/" org-directory))
;; Backup file for entries before modification
(setq org-caldav-backup-file (expand-file-name ".org-caldav/backup.org" org-directory))
;; Limit past events to 30 days (avoids uploading years of scheduled tasks)
(setq org-caldav-days-in-past 30)
;; Sync behavior: bidirectional by default
(setq org-caldav-sync-direction 'twoway)
;; What changes from calendar sync back to Org (conservative: title and timestamp only)
(setq org-caldav-sync-changes-to-org 'title-and-timestamp)
;; Deletion handling: never auto-delete to prevent accidental mass deletion
(setq org-caldav-delete-calendar-entries 'never)
(setq org-caldav-delete-org-entries 'never)
;; Enable TODO/VTODO sync
(setq org-icalendar-include-todo 'all)
(setq org-caldav-sync-todo t)
;; Map VTODO percent-complete to org-todo-keywords
;; Format: (PERCENT "KEYWORD") - percent thresholds map to states
(setq org-caldav-todo-percent-states
'((0 "TODO")
(25 "WAIT")
(50 "IN-PROGRESS")
(100 "DONE")
(100 "KILL")))
;; Allow export with broken links (mu4e links can't be resolved during export)
(setq org-export-with-broken-links 'mark)
;; Calendar-specific configuration
(setq org-caldav-calendars
'(;; Personal calendar: two-way sync with family-shared Nextcloud calendar
(:calendar-id "personal"
:inbox "~/org/personal-calendar.org"
:files ("~/org/personal-calendar.org"))
;; Tasks calendar: one-way sync (org → calendar only)
;; SCHEDULED/DEADLINE items from todo.org push to private Tasks calendar.
;; No inbox = no download from calendar (effectively one-way).
;; Note: Create 'tasks' calendar in Nextcloud first, keep it private.
(:calendar-id "tasks"
:files ("~/org/todo.org"))))
;; Handle UNTIL in recurring events
;; org-caldav ignores UNTIL from RRULE - events repeat forever.
;; This advice extracts UNTIL and stores it as a property for agenda filtering.
(defun my/org-caldav-add-until-property (orig-fun eventdata-alist)
"Advice to store CALDAV_UNTIL property for recurring events."
(let ((result (funcall orig-fun eventdata-alist)))
(let* ((rrule-props (alist-get 'rrule-props eventdata-alist))
(until-str (cadr (assoc 'UNTIL rrule-props)))
(summary (alist-get 'summary eventdata-alist)))
;; Debug: log what we're seeing
(message "CALDAV-DEBUG: %s | rrule-props: %S | until: %s"
(or summary "?") rrule-props until-str)
(when until-str
(save-excursion
(org-back-to-heading t)
(org-entry-put nil "CALDAV_UNTIL" until-str))))
result))
(advice-add 'org-caldav-insert-org-event-or-todo
:around #'my/org-caldav-add-until-property)
)
(defun my/get-rbw-password (alias &optional no-error)
"Return the password for ALIAS via rbw, unlocking the vault only if needed.
If NO-ERROR is non-nil, return nil instead of signaling an error when
rbw is unavailable or the entry is not found."
(if (not (executable-find "rbw"))
(if no-error
nil
(user-error "rbw: not installed or not in PATH"))
(let* ((cmd (format "rbw get %s 2>/dev/null" (shell-quote-argument alias)))
(output (string-trim (shell-command-to-string cmd))))
(if (string-empty-p output)
(if no-error
nil
(user-error "rbw: no entry found for '%s' - run: rbw add %s" alias alias))
output))))
(after! gptel
:config
(setq! gptel-api-key (my/get-rbw-password "openai-api-key-chatgpt-el" t)
gptel-default-mode 'org-mode
gptel-use-tools t
gptel-confirm-tool-calls 'always
gptel-include-reasoning 'ignore
gptel-model "qwen3:30b")
;; Set default backend to be Ollama-Local
(setq! gptel-backend
(gptel-make-ollama "Ollama-Local"
:host "localhost:11434"
:stream t
:models '(deepseek-r1 deepseek-r1-fullctx qwen3:30b qwen3:4b llama3.1 qwen2.5-coder mistral-nemo gpt-oss)))
;; Define custom tools
(gptel-make-tool
:name "run_shell_command"
:description "Execute shell commands and return output. Use this to run system commands, check file contents, or perform system operations."
:function (lambda (command)
(condition-case err
(shell-command-to-string command)
(error (format "Error running command: %s" (error-message-string err)))))
:args (list '(:name "command" :type "string" :description "Shell command to execute")))
(gptel-make-tool
:name "read_file"
:description "Read the contents of a file and return as text"
:function (lambda (filepath)
(condition-case err
(with-temp-buffer
(insert-file-contents (expand-file-name filepath))
(buffer-string))
(error (format "Error reading file %s: %s" filepath (error-message-string err)))))
:args (list '(:name "filepath" :type "string" :description "Path to the file to read")))
(gptel-make-tool
:name "list_directory"
:description "List contents of a directory"
:function (lambda (dirpath)
(condition-case err
(mapconcat 'identity
(directory-files (expand-file-name dirpath) nil "^[^.]")
"\n")
(error (format "Error listing directory %s: %s" dirpath (error-message-string err)))))
:args (list '(:name "dirpath" :type "string" :description "Directory path to list"))))
(use-package! pi-coding-agent
:commands (pi-coding-agent pi-coding-agent-toggle)
:init
(defalias 'pi 'pi-coding-agent)
(map! :leader
(:prefix ("o" . "open")
:desc "Pi Coding Agent" "p" #'pi-coding-agent))
:config
;; Tree-sitter grammars are managed by Nix (treesit-grammars.with-all-grammars),
;; so suppress the auto-install prompt
(setq pi-coding-agent-essential-grammar-action 'warn))
(use-package! claude-code-ide
:commands (claude-code-ide-menu claude-code-ide-open-here)
:init
(map! :leader
(:prefix ("o" . "open")
:desc "Claude Code IDE" "c" #'claude-code-ide-menu))
:config
(claude-code-ide-emacs-tools-setup)
(setq claude-code-ide-cli-path "claude"
claude-code-ide-cli-extra-flags "--dangerously-skip-permissions"
claude-code-ide-focus-claude-after-ediff t
claude-code-ide-focus-on-open t
claude-code-ide-show-claude-window-in-ediff t
claude-code-ide-switch-tab-on-ediff t
claude-code-ide-use-ide-diff t
claude-code-ide-use-side-window t
claude-code-ide-window-height 20
claude-code-ide-window-side 'right
claude-code-ide-window-width 90))
(after! gptel
(require 'gptel-tool-library)
(setq gptel-tool-library-use-maybe-safe t
gptel-tool-library-use-unsafe t)
(dolist (module '("bbdb" "buffer" "elisp" "emacs" "gnus" "os" "search-and-replace" "url"))
(gptel-tool-library-load-module module)))
;; mu4e email configuration
;; Add NixOS mu4e to load-path (installed via mu.mu4e package)
(when-let ((mu-path (executable-find "mu")))
(add-to-list 'load-path
(expand-file-name "../share/emacs/site-lisp/mu4e"
(file-name-directory mu-path))))
(after! mu4e
;; User identity
(setq user-mail-address "john@ogle.fyi"
user-full-name "John Ogle")
;; Maildir location (no account prefix - single account)
(setq mu4e-maildir "~/Mail"
mu4e-attachment-dir "~/Downloads")
;; Folder config (matches ~/Mail/INBOX, ~/Mail/Sent, etc.)
(setq mu4e-sent-folder "/Sent"
mu4e-drafts-folder "/Drafts"
mu4e-trash-folder "/Trash"
mu4e-refile-folder "/Archive")
;; Shortcuts for common folders
(setq mu4e-maildir-shortcuts
'((:maildir "/INBOX" :key ?i)
(:maildir "/Archive" :key ?a)
(:maildir "/Sent" :key ?s)
(:maildir "/Trash" :key ?t)))
;; Behavior settings
(setq mu4e-get-mail-command "mbsync -a"
mu4e-update-interval 300 ; 5 minutes (matches systemd timer)
mu4e-change-filenames-when-moving t ; required for mbsync
mu4e-headers-date-format "%Y-%m-%d"
mu4e-headers-time-format "%H:%M")
;; Sending mail via msmtp
;; NOTE: message-sendmail-f-is-evil and --read-envelope-from are required
;; to prevent msmtp from stripping the email body when processing headers.
;; Without these, multipart messages (especially from org-msg) may arrive
;; with empty bodies.
(setq sendmail-program (executable-find "msmtp")
send-mail-function #'message-send-mail-with-sendmail
message-send-mail-function #'message-send-mail-with-sendmail
message-sendmail-f-is-evil t
message-sendmail-extra-arguments '("--read-envelope-from")
message-sendmail-envelope-from 'header))
;; Whenever you reconfigure a package, make sure to wrap your config in an
;; `after!' block, otherwise Doom's defaults may override your settings. E.g.
;;
;; (after! PACKAGE
;; (setq x y))
;;
;; The exceptions to this rule:
;;
;; - Setting file/directory variables (like `org-directory')
;; - Setting variables which explicitly tell you to set them before their
;; package is loaded (see 'C-h v VARIABLE' to look up their documentation).
;; - Setting doom variables (which start with 'doom-' or '+').
;;
;; Here are some additional functions/macros that will help you configure Doom.
;;
;; - `load!' for loading external *.el files relative to this one
;; - `use-package!' for configuring packages
;; - `after!' for running code after a package has loaded
;; - `add-load-path!' for adding directories to the `load-path', relative to
;; this file. Emacs searches the `load-path' when you load packages with
;; `require' or `use-package'.
;; - `map!' for binding new keys
;;
;; To get information about any of these functions/macros, move the cursor over
;; the highlighted symbol at press 'K' (non-evil users must press 'C-c c k').
;; This will open documentation for it, including demos of how they are used.
;; Alternatively, use `C-h o' to look up a symbol (functions, variables, faces,
;; etc).
;;
;; You can also try 'gd' (or 'C-c c d') to jump to their definition and see how
;; they are implemented.

View File

@@ -33,7 +33,7 @@
doom ; what makes DOOM look the way it does
doom-dashboard ; a nifty splash screen for Emacs
;;doom-quit ; DOOM quit-message prompts when you quit Emacs
(emoji +unicode) ; 🙂
;;(emoji +unicode) ; 🙂
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
;;indent-guides ; highlighted indent columns
;;ligatures ; ligatures and symbols to make your code pretty again
@@ -45,7 +45,7 @@
(popup +defaults) ; tame sudden yet inevitable temporary windows
;;tabs ; a tab bar for Emacs
;;treemacs ; a project drawer, like neotree but cooler
;;unicode ; extended unicode support for various languages
unicode ; extended unicode support for various languages
(vc-gutter +pretty) ; vcs diff in the fringe
vi-tilde-fringe ; fringe tildes to mark beyond EOB
;;window-select ; visually switch windows
@@ -59,7 +59,7 @@
;;(format +onsave) ; automated prettiness
;;god ; run Emacs commands without modifier keys
;;lispy ; vim for lisp, for people who don't like vim
;;multiple-cursors ; editing in many places at once
multiple-cursors ; editing in many places at once
;;objed ; text object editing for the innocent
;;parinfer ; turn lisp into python, sort of
;;rotate-text ; cycle region at point between text candidates
@@ -77,7 +77,7 @@
;;eshell ; the elisp shell that works everywhere
;;shell ; simple shell REPL for Emacs
;;term ; basic terminal emulator for Emacs
;;vterm ; the best terminal emulation in Emacs
vterm ; the best terminal emulation in Emacs
:checkers
syntax ; tasing you for every semicolon you forget
@@ -94,6 +94,7 @@
;;editorconfig ; let someone else argue about tabs vs spaces
;;ein ; tame Jupyter notebooks with emacs
(eval +overlay) ; run code, run (also, repls)
llm ; When I said you needed friends, I didn't mean...
lookup ; navigate your code and its documentation
lsp ; M-x vscode
magit ; a git porcelain for Emacs
@@ -175,7 +176,7 @@
;;zig ; C, but simpler
:email
;;(mu4e +org +gmail)
(mu4e +org)
;;notmuch
;;(wanderlust +gmail)

View File

@@ -49,6 +49,26 @@
;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
;; (unpin! t)
;; (package! org-caldav)
(package! org-caldav)
;; Pin org-msg - upstream doom pin references a force-pushed commit
(package! org-msg :pin "aa608b399586fb771ad37045a837f8286a0b6124")
;; Note: Packages with custom recipes must be pinned for nix-doom-emacs-unstraightened
;; to build deterministically. Update pins when upgrading packages.
(package! gptel :recipe (:nonrecursive t))
(package! claude-code-ide
:recipe (:host github :repo "manzaltu/claude-code-ide.el")
:pin "760240d7f03ff16f90ede9d4f4243cd94f3fed73")
(package! gptel-tool-library
:recipe (:host github :repo "aard-fi/gptel-tool-library"
:files ("*.el"))
:pin "baffc3b0d74a2b7cbda0d5cd6dd7726d6ccaca83")
(package! pi-coding-agent
:recipe (:host github :repo "dnouri/pi-coding-agent"
:files ("*.el"))
:pin "8d8158b0a6150ce13d91e561a1223790670acaa7")

View File

@@ -0,0 +1,128 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.email;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.email = {
enable = mkEnableOption "Enable email with mu4e, mbsync, and msmtp";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
isync # provides mbsync for IMAP sync
msmtp # for SMTP sending
mu # email indexer for mu4e
mu.mu4e # mu4e elisp files for Emacs
openssl # for certificate management
];
# Ensure Mail directory exists
home.file."Mail/.keep".text = "";
# mbsync configuration
home.file.".mbsyncrc".text = ''
# IMAP Account Configuration
IMAPAccount proton
Host proton.johnogle.info
Port 143
User john@ogle.fyi
PassCmd "${pkgs.rbw}/bin/rbw get proton.johnogle.info"
TLSType STARTTLS
AuthMechs PLAIN
# Remote Storage
IMAPStore proton-remote
Account proton
# Local Storage
MaildirStore proton-local
Path ~/Mail/
Inbox ~/Mail/INBOX
SubFolders Verbatim
# Channel Configuration - Main (excludes Sent)
Channel proton-main
Far :proton-remote:
Near :proton-local:
Patterns * !Sent
Create Both
Expunge Both
SyncState *
# Channel Configuration - Sent (pull only)
Channel proton-sent
Far :proton-remote:Sent
Near :proton-local:Sent
Create Near
Expunge Near
Sync Pull
SyncState *
# Group both channels
Group proton
Channel proton-main
Channel proton-sent
'';
# msmtp configuration
home.file.".msmtprc".text = ''
# Default settings
defaults
auth plain
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ${config.home.homeDirectory}/.msmtp.log
# Proton mail account
account proton
host proton.johnogle.info
port 25
from john@ogle.fyi
user john@ogle.fyi
passwordeval rbw get proton.johnogle.info
# Set default account
account default : proton
'';
# Linux-only: Systemd service for mail sync (Darwin uses launchd instead)
systemd.user.services = mkIf isLinux {
mbsync = {
Unit = {
Description = "Mailbox synchronization service";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.bash}/bin/bash -c 'mkdir -p ~/Mail && ${pkgs.isync}/bin/mbsync -a && (${pkgs.mu}/bin/mu info >/dev/null 2>&1 || ${pkgs.mu}/bin/mu init --maildir ~/Mail --personal-address=john@ogle.fyi) && ${pkgs.mu}/bin/mu index'";
Environment = "PATH=${pkgs.rbw}/bin:${pkgs.coreutils}/bin";
StandardOutput = "journal";
StandardError = "journal";
};
};
};
# Linux-only: Systemd timer for automatic sync
systemd.user.timers = mkIf isLinux {
mbsync = {
Unit = {
Description = "Mailbox synchronization timer";
};
Timer = {
OnBootSec = "2min";
OnUnitActiveSec = "5min";
Unit = "mbsync.service";
};
Install = {
WantedBy = [ "timers.target" ];
};
};
};
};
}

View File

@@ -0,0 +1,18 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.gaming;
in
{
options.home.roles.gaming = {
enable = mkEnableOption "Enable gaming applications and tools";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
custom.mcrcon-rbw
];
};
}

View File

@@ -0,0 +1,496 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.i3_sway;
wallpaperConfig = import ../../wallpapers;
currentWallpaper = builtins.elemAt wallpaperConfig.wallpapers wallpaperConfig.currentIndex;
shared_config = recursiveUpdate rec {
modifier = "Mod4";
terminal = "ghostty";
defaultWorkspace = "workspace number 1";
keybindings = {
"${shared_config.modifier}+Return" = "exec ${terminal}";
"${shared_config.modifier}+Shift+Return" = "exec ${cfg.browser}";
"${shared_config.modifier}+Shift+q" = "kill";
"${shared_config.modifier}+a" = "focus parent";
"${shared_config.modifier}+Shift+a" = "focus child";
"${shared_config.modifier}+h" = "focus left";
"${shared_config.modifier}+j" = "focus down";
"${shared_config.modifier}+k" = "focus up";
"${shared_config.modifier}+l" = "focus right";
"${shared_config.modifier}+Shift+h" = "move left";
"${shared_config.modifier}+Shift+j" = "move down";
"${shared_config.modifier}+Shift+k" = "move up";
"${shared_config.modifier}+Shift+l" = "move right";
"${shared_config.modifier}+Left" = "focus left";
"${shared_config.modifier}+Down" = "focus down";
"${shared_config.modifier}+Up" = "focus up";
"${shared_config.modifier}+Right" = "focus right";
"${shared_config.modifier}+Shift+Left" = "move left";
"${shared_config.modifier}+Shift+Down" = "move down";
"${shared_config.modifier}+Shift+Up" = "move up";
"${shared_config.modifier}+Shift+Right" = "move right";
#"${shared_config.modifier}+h" = "split h";
"${shared_config.modifier}+v" = "split v";
"${shared_config.modifier}+Shift+f" = "fullscreen toggle";
"${shared_config.modifier}+s" = "layout stacking";
"${shared_config.modifier}+w" = "layout tabbed";
"${shared_config.modifier}+e" = "layout toggle split";
"${shared_config.modifier}+Shift+space" = "floating toggle";
"${shared_config.modifier}+space" = "focus mode_toggle";
"${shared_config.modifier}+Shift+minus" = "move scratchpad";
"${shared_config.modifier}+minus" = "scratchpad show";
"${shared_config.modifier}+1" = "workspace number 1";
"${shared_config.modifier}+2" = "workspace number 2";
"${shared_config.modifier}+3" = "workspace number 3";
"${shared_config.modifier}+4" = "workspace number 4";
"${shared_config.modifier}+5" = "workspace number 5";
"${shared_config.modifier}+6" = "workspace number 6";
"${shared_config.modifier}+7" = "workspace number 7";
"${shared_config.modifier}+8" = "workspace number 8";
"${shared_config.modifier}+9" = "workspace number 9";
"${shared_config.modifier}+0" = "workspace number 10";
"${shared_config.modifier}+Shift+1" =
"move container to workspace number 1";
"${shared_config.modifier}+Shift+2" =
"move container to workspace number 2";
"${shared_config.modifier}+Shift+3" =
"move container to workspace number 3";
"${shared_config.modifier}+Shift+4" =
"move container to workspace number 4";
"${shared_config.modifier}+Shift+5" =
"move container to workspace number 5";
"${shared_config.modifier}+Shift+6" =
"move container to workspace number 6";
"${shared_config.modifier}+Shift+7" =
"move container to workspace number 7";
"${shared_config.modifier}+Shift+8" =
"move container to workspace number 8";
"${shared_config.modifier}+Shift+9" =
"move container to workspace number 9";
"${shared_config.modifier}+Shift+0" =
"move container to workspace number 10";
"${shared_config.modifier}+Shift+c" = "reload";
"${shared_config.modifier}+Shift+r" = "restart";
"${shared_config.modifier}+r" = "mode resize";
"XF86MonBrightnessUp" = "exec ddcutil setvcp 10 + 5";
"XF86MonBrightnessDown" = "exec ddcutil setvcp 10 - 5";
};
} cfg.extraSharedConfig;
in {
options.home.roles.i3_sway = {
enable = mkEnableOption "i3 and Sway tiling window managers with waybar and rofi";
browser = mkOption {
type = types.str;
default = "firefox --new-window";
description = "Browser to use for new window keybinding";
};
extraSharedConfig = mkOption {
type = types.attrs;
default = {};
description = "Extra configuration shared between i3 and sway";
};
extraI3Config = mkOption {
type = types.attrs;
default = {};
description = "Extra i3-specific configuration";
};
extraSwayConfig = mkOption {
type = types.attrs;
default = {};
description = "Extra sway-specific configuration";
};
};
config = mkIf cfg.enable {
# i3blocks configuration file
home.file.".config/i3blocks/config".text = ''
# i3blocks config - replicating waybar setup
separator_block_width=15
markup=pango
[disk]
command=df -h / | awk 'NR==2 {print "💾 " $5}'
interval=30
separator=true
[cpu]
command=top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print "🧠 " int(100 - $1) "%"}'
interval=2
separator=true
[memory]
command=free | awk 'NR==2 {printf "🐏 %.0f%%\n", $3*100/$2}'
interval=5
separator=true
[pulseaudio]
command=${pkgs.writeShellScript "i3blocks-pulseaudio" ''
volume=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -Po '\d+%' | head -1)
muted=$(pactl get-sink-mute @DEFAULT_SINK@ | grep -o 'yes')
if [ "$muted" = "yes" ]; then
echo "🔇"
else
vol_num=''${volume%\%}
if [ $vol_num -le 33 ]; then
echo "🔈 $volume"
elif [ $vol_num -le 66 ]; then
echo "🔉 $volume"
else
echo "🔊 $volume"
fi
fi
''}
interval=1
signal=10
separator=true
[backlight]
command=${pkgs.writeShellScript "i3blocks-backlight" ''
if command -v ddcutil &>/dev/null; then
# Handle mouse scroll events
case $BLOCK_BUTTON in
4) ddcutil setvcp 10 + 5 ;; # Scroll up - increase brightness
5) ddcutil setvcp 10 - 5 ;; # Scroll down - decrease brightness
esac
# Display current brightness
brightness=$(ddcutil getvcp 10 2>/dev/null | grep -oP 'current value =\s*\K\d+')
if [ -n "$brightness" ]; then
echo " $brightness%"
fi
fi
''}
interval=5
separator=true
[network]
command=${pkgs.writeShellScript "i3blocks-network" ''
if iwgetid -r &>/dev/null; then
ssid=$(iwgetid -r)
signal=$(grep "^\s*w" /proc/net/wireless | awk '{print int($3 * 100 / 70)}')
echo "📶 $ssid ($signal%)"
else
ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '127.0.0.1' | head -1)
if [ -n "$ip" ]; then
echo "🔌 $ip"
else
echo ""
fi
fi
''}
interval=5
separator=true
[battery]
command=${pkgs.writeShellScript "i3blocks-battery" ''
if [ -d /sys/class/power_supply/BAT0 ]; then
capacity=$(cat /sys/class/power_supply/BAT0/capacity)
status=$(cat /sys/class/power_supply/BAT0/status)
if [ "$status" = "Charging" ]; then
echo " $capacity%"
else
echo "🔋 $capacity%"
fi
fi
''}
interval=10
separator=true
[time]
command=date '+%Y-%m-%d %H:%M'
interval=1
separator=false
'';
xsession.windowManager.i3 = let
base_i3_config = recursiveUpdate shared_config {
bars = [{
position = "bottom";
statusCommand = "${pkgs.i3blocks}/bin/i3blocks";
trayOutput = "primary"; # Enable system tray on primary output
fonts = {
names = [ "Fira Code" "monospace" ];
size = 11.0;
};
colors = {
background = "#000000";
statusline = "#ffffff";
separator = "#666666";
# Workspace button colors (matching waybar)
focusedWorkspace = {
border = "#285577";
background = "#285577";
text = "#ffffff";
};
activeWorkspace = {
border = "#5f676a";
background = "#5f676a";
text = "#ffffff";
};
inactiveWorkspace = {
border = "#222222";
background = "#222222";
text = "#888888";
};
urgentWorkspace = {
border = "#900000";
background = "#900000";
text = "#ffffff";
};
};
}];
keybindings = shared_config.keybindings // {
"${shared_config.modifier}+d" = "exec rofi -show drun";
"${shared_config.modifier}+Shift+e" =
"exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
};
startup = [
# GNOME polkit authentication agent
{
command = "/run/current-system/sw/libexec/polkit-gnome-authentication-agent-1";
always = false;
notification = false;
}
# Picom compositor for smooth rendering and no tearing (important for Nvidia)
{
command = "picom --backend glx -b";
always = false;
notification = false;
}
# NetworkManager system tray applet
{
command = "nm-applet";
always = false;
notification = false;
}
# Set wallpaper with feh
{
command = "feh ${currentWallpaper.feh} ${currentWallpaper.file}";
always = false;
notification = false;
}
];
};
in {
enable = true;
config = recursiveUpdate base_i3_config cfg.extraI3Config;
};
wayland.windowManager.sway = let
base_sway_config = recursiveUpdate shared_config {
bars = []; # Disable default bar, use waybar instead
keybindings = shared_config.keybindings // {
"${shared_config.modifier}+d" = "exec wofi --show drun";
"${shared_config.modifier}+Shift+e" =
"exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
};
input = {
"type:keyboard" = {
xkb_options = "caps:escape";
};
"type:touchpad" = {
tap = "enabled";
tap_button_map = "lrm";
drag = "enabled";
natural_scroll = "disabled";
dwt = "enabled";
};
};
output = {
"*" = {
bg = "${currentWallpaper.file} ${currentWallpaper.sway}";
};
};
startup = [
# Launch waybar status bar
{
command = "waybar";
always = false;
}
];
};
in {
enable = true;
extraOptions = [ "--unsupported-gpu" ];
config = recursiveUpdate base_sway_config cfg.extraSwayConfig;
};
programs.waybar = {
enable = true;
systemd.enable = false; # Don't auto-start via systemd - only launch in sway
settings = {
mainBar = {
layer = "top";
position = "bottom";
height = 30;
spacing = 4;
modules-left = [ "sway/workspaces" "sway/mode" ];
modules-center = [ ];
modules-right = [ "disk" "cpu" "memory" "pulseaudio" "custom/backlight-ddc" "backlight" "network" "battery" "tray" "clock" ];
"sway/workspaces" = {
disable-scroll = true;
all-outputs = true;
};
"clock" = {
format = "{:%Y-%m-%d %H:%M}";
tooltip-format = "<tt><small>{calendar}</small></tt>";
calendar = {
mode = "year";
mode-mon-col = 3;
weeks-pos = "right";
on-scroll = 1;
format = {
months = "<span color='#ffead3'><b>{}</b></span>";
days = "<span color='#ecc6d9'><b>{}</b></span>";
weeks = "<span color='#99ffdd'><b>W{}</b></span>";
weekdays = "<span color='#ffcc66'><b>{}</b></span>";
today = "<span color='#ff6699'><b><u>{}</u></b></span>";
};
};
};
"disk" = {
interval = 30;
format = "💾 {percentage_used}%";
path = "/";
tooltip-format = "Used: {used} / {total} ({percentage_used}%)\nFree: {free} ({percentage_free}%)";
};
"cpu" = {
format = "🧠 {usage}%";
tooltip = false;
};
"memory" = {
format = "🐏 {percentage}%";
tooltip-format = "RAM: {used:0.1f}G / {total:0.1f}G";
};
"pulseaudio" = {
format = "{icon} {volume}%";
format-muted = "🔇";
format-icons = {
headphone = "🎧";
default = [ "🔈" "🔉" "🔊" ];
};
on-click = "pavucontrol";
};
"backlight" = {
format = " {percent}%";
tooltip = false;
};
"custom/backlight-ddc" = {
exec = pkgs.writeShellScript "waybar-backlight-ddc" ''
if command -v ddcutil &>/dev/null; then
# Display current brightness
brightness=$(ddcutil getvcp 10 --brief 2>/dev/null | awk '{print $4}')
if [ -n "$brightness" ]; then
echo " $brightness%"
fi
fi
'';
interval = 5;
format = "{}";
on-scroll-up = "ddcutil setvcp 10 + 5 2>/dev/null &";
on-scroll-down = "ddcutil setvcp 10 - 5 2>/dev/null &";
tooltip = false;
};
"network" = {
format-wifi = "📶 {essid} ({signalStrength}%)";
format-ethernet = "🔌 {ipaddr}";
format-disconnected = "";
tooltip-format = "{ifname}: {ipaddr}/{cidr}";
};
"battery" = {
states = {
warning = 30;
critical = 15;
};
format = "{icon} {capacity}%";
format-charging = " {capacity}%";
format-icons = [ "🪫" "🔋" "🔋" "🔋" "🔋" ];
};
"tray" = {
spacing = 10;
};
};
};
style = ''
* {
padding: 0 4px;
font-family: "Fira Code", monospace;
font-size: 13px;
}
#workspaces button {
padding: 0 8px;
background-color: #333333;
color: #ffffff;
border: none;
}
#workspaces button.focused {
background-color: #285577;
font-weight: bold;
}
#workspaces button.visible {
background-color: #5f676a;
}
#workspaces button.urgent {
background-color: #900000;
}
'';
};
programs.rofi = {
enable = true;
theme = "solarized";
extraConfig = {
modi = "drun,run,window";
show-icons = true;
drun-display-format = "{name}";
disable-history = false;
hide-scrollbar = true;
display-drun = " Apps";
display-run = " Run";
display-window = " Windows";
sidebar-mode = true;
};
};
};
}

View File

@@ -0,0 +1,22 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.kdeconnect;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.kdeconnect = {
enable = mkEnableOption "Enable KDE Connect for device integration";
};
# KDE Connect services are Linux-only (requires D-Bus and systemd)
config = mkIf (cfg.enable && isLinux) {
services.kdeconnect = {
enable = true;
indicator = true;
package = pkgs.kdePackages.kdeconnect-kde;
};
};
}

View File

@@ -0,0 +1,243 @@
{ config, lib, pkgs, globalInputs, system, ... }:
with lib;
let
cfg = config.home.roles.kubectl;
in
{
options.home.roles.kubectl = {
enable = mkEnableOption "management tools for the homelab k3s oglenet cluster with secure Bitwarden integration";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
kubectl
kubernetes-helm
];
programs.k9s.enable = true;
programs.bash.initExtra = mkAfter ''
# Kubectl secure session management
export KUBECTL_SESSION_DIR="/dev/shm/kubectl-$$"
kube-select() {
if [[ $# -ne 1 ]]; then
echo "Usage: kube-select <context-name>"
echo "Available contexts: $(kube-list)"
return 1
fi
local context="$1"
# Clean up any existing session first
kube-clear 2>/dev/null
# Create new session directory
mkdir -p "$KUBECTL_SESSION_DIR"
chmod 700 "$KUBECTL_SESSION_DIR"
# Set cleanup trap for this shell session
trap "rm -rf '$KUBECTL_SESSION_DIR' 2>/dev/null" EXIT
# Set KUBECONFIG for this session
export KUBECONFIG="$KUBECTL_SESSION_DIR/config"
# Load config from Bitwarden secure notes
if ! rbw get "kubectl-$context" > "$KUBECONFIG" 2>/dev/null; then
echo "Error: Could not retrieve kubectl-$context from Bitwarden"
echo "Make sure the entry exists with name: kubectl-$context"
kube-clear
return 1
fi
# Verify the kubeconfig is valid
if ! kubectl config view >/dev/null 2>&1; then
echo "Error: Invalid kubeconfig retrieved from Bitwarden"
kube-clear
return 1
fi
echo " Loaded kubectl context: $context (session: $$)"
echo " Config location: $KUBECONFIG"
}
kube-list() {
echo "Available kubectl contexts in Bitwarden:"
rbw search kubectl- 2>/dev/null | grep "^kubectl-" | sed 's/^kubectl-/ - /' || echo " (none found or rbw not accessible)"
}
kube-clear() {
if [[ -n "$KUBECTL_TIMEOUT_PID" ]]; then
kill "$KUBECTL_TIMEOUT_PID" 2>/dev/null
unset KUBECTL_TIMEOUT_PID
fi
if [[ -d "$KUBECTL_SESSION_DIR" ]]; then
rm -rf "$KUBECTL_SESSION_DIR"
echo "Cleared kubectl session ($$)"
fi
unset KUBECONFIG
}
kube-status() {
if [[ -f "$KUBECONFIG" ]]; then
local current_context
current_context=$(kubectl config current-context 2>/dev/null)
if [[ -n "$current_context" ]]; then
echo "Active kubectl context: $current_context"
echo "Session: $$ | Config: $KUBECONFIG"
# Show cluster info
local cluster_server
cluster_server=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}' 2>/dev/null)
if [[ -n "$cluster_server" ]]; then
echo "Cluster: $cluster_server"
fi
else
echo "No active context in current session"
fi
else
echo "No kubectl session active in this shell"
echo "Use 'kube-select <context>' to start a session"
fi
}
# Helper function to show available commands
kube-help() {
echo "Secure kubectl session management commands:"
echo ""
echo "Session management:"
echo " kube-select <context> - Load kubeconfig from Bitwarden"
echo " kube-status - Show current session status"
echo " kube-clear - Clear current session"
echo ""
echo "Configuration management:"
echo " kube-list - List available contexts in Bitwarden"
echo ""
echo "Help:"
echo " kube-help - Show this help"
echo ""
echo "Examples:"
echo " kube-select prod # Loads from secure note"
echo " kubectl get pods"
echo " kube-clear"
echo ""
echo "Note: Kubeconfigs are stored as secure notes in Bitwarden"
}
'';
programs.zsh.initExtra = mkAfter ''
# Kubectl secure session management (zsh)
export KUBECTL_SESSION_DIR="/dev/shm/kubectl-$$"
kube-select() {
if [[ $# -ne 1 ]]; then
echo "Usage: kube-select <context-name>"
echo "Available contexts: $(kube-list)"
return 1
fi
local context="$1"
# Clean up any existing session first
kube-clear 2>/dev/null
# Create new session directory
mkdir -p "$KUBECTL_SESSION_DIR"
chmod 700 "$KUBECTL_SESSION_DIR"
# Set cleanup trap for this shell session
trap "rm -rf '$KUBECTL_SESSION_DIR' 2>/dev/null" EXIT
# Set KUBECONFIG for this session
export KUBECONFIG="$KUBECTL_SESSION_DIR/config"
# Load config from Bitwarden secure notes
if ! rbw get "kubectl-$context" > "$KUBECONFIG" 2>/dev/null; then
echo "Error: Could not retrieve kubectl-$context from Bitwarden"
echo "Make sure the entry exists with name: kubectl-$context"
kube-clear
return 1
fi
# Verify the kubeconfig is valid
if ! kubectl config view >/dev/null 2>&1; then
echo "Error: Invalid kubeconfig retrieved from Bitwarden"
kube-clear
return 1
fi
echo " Loaded kubectl context: $context (session: $$)"
echo " Config location: $KUBECONFIG"
}
kube-list() {
echo "Available kubectl contexts in Bitwarden:"
rbw search kubectl- 2>/dev/null | grep "^kubectl-" | sed 's/^kubectl-/ - /' || echo " (none found or rbw not accessible)"
}
kube-clear() {
if [[ -n "$KUBECTL_TIMEOUT_PID" ]]; then
kill "$KUBECTL_TIMEOUT_PID" 2>/dev/null
unset KUBECTL_TIMEOUT_PID
fi
if [[ -d "$KUBECTL_SESSION_DIR" ]]; then
rm -rf "$KUBECTL_SESSION_DIR"
echo "Cleared kubectl session ($$)"
fi
unset KUBECONFIG
}
kube-status() {
if [[ -f "$KUBECONFIG" ]]; then
local current_context
current_context=$(kubectl config current-context 2>/dev/null)
if [[ -n "$current_context" ]]; then
echo "Active kubectl context: $current_context"
echo "Session: $$ | Config: $KUBECONFIG"
# Show cluster info
local cluster_server
cluster_server=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}' 2>/dev/null)
if [[ -n "$cluster_server" ]]; then
echo "Cluster: $cluster_server"
fi
else
echo "No active context in current session"
fi
else
echo "No kubectl session active in this shell"
echo "Use 'kube-select <context>' to start a session"
fi
}
# Helper function to show available commands
kube-help() {
echo "Secure kubectl session management commands:"
echo ""
echo "Session management:"
echo " kube-select <context> - Load kubeconfig from Bitwarden"
echo " kube-status - Show current session status"
echo " kube-clear - Clear current session"
echo ""
echo "Configuration management:"
echo " kube-list - List available contexts in Bitwarden"
echo ""
echo "Help:"
echo " kube-help - Show this help"
echo ""
echo "Examples:"
echo " kube-select prod # Loads from secure note"
echo " kubectl get pods"
echo " kube-clear"
echo ""
echo "Note: Kubeconfigs are stored as secure notes in Bitwarden"
}
'';
};
}

View File

@@ -0,0 +1,36 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.launchers;
# Generate a wrapper script for a package
makeLauncher = packageName: pkgs.writeShellScriptBin packageName ''
exec env NIXPKGS_ALLOW_UNFREE=1 ${pkgs.nix}/bin/nix run --impure nixpkgs#${packageName} -- "$@"
'';
# Generate all launcher scripts from the package list
launcherPackages = map makeLauncher cfg.packages;
in
{
options.home.roles.launchers = {
enable = mkEnableOption "wrapper launchers for excluded packages";
packages = mkOption {
type = types.listOf types.str;
default = [];
example = [ "steam" "libreoffice" "lutris" ];
description = ''
List of package names to create launcher wrappers for.
Each wrapper will run: NIXPKGS_ALLOW_UNFREE=1 nix run --impure nixpkgs#<package>
This is useful for occasionally running packages without permanently installing them.
'';
};
};
config = mkIf cfg.enable {
home.packages = launcherPackages;
};
}

View File

@@ -0,0 +1,27 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.media;
in
{
options.home.roles.media = {
enable = mkEnableOption "Enable media and multimedia applications";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Media players and streaming
# Using delfin instead of jellyfin-media-player to avoid qtwebengine security issues
# For full Jellyfin features, use web interface at http://jellyfin-server:8096
delfin
moonlight-qt
vlc
# Spotify client
# Using unstable version for better authentication support
unstable.ncspot
];
};
}

View File

@@ -0,0 +1,22 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.office;
in
{
options.home.roles.office = {
enable = mkEnableOption "Enable office applications and document processing tools";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Office suite
libreoffice
# CAD/Design tools
openscad-unstable
];
};
}

View File

@@ -0,0 +1,199 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.plasma-manager-kodi;
# Define the volume control scripts as derivations
volumeUpScript = pkgs.writeShellScript "avr-volume-up" ''
#!/usr/bin/env bash
# Configuration
HA_URL="https://home-assistant.johnogle.info"
ENTITY_ID="media_player.denon_avr_s970h_2"
MAX_RETRIES=3
# Read token from KDE Wallet and strip whitespace
TOKEN=$(${pkgs.kdePackages.kwallet}/bin/kwallet-query -r ha_avr_token kdewallet -f Passwords 2>/dev/null | tr -d '[:space:]')
if [ -z "$TOKEN" ]; then
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to retrieve Home Assistant token from KDE Wallet"
exit 1
fi
# Send volume up command with retry logic
for i in $(seq 1 $MAX_RETRIES); do
RESPONSE=$(${pkgs.curl}/bin/curl -s -w "\n%{http_code}" -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"entity_id\": \"$ENTITY_ID\"}" \
"$HA_URL/api/services/media_player/volume_up" 2>&1)
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
if [ "$HTTP_CODE" = "200" ]; then
exit 0
fi
# Wait before retry (except on last attempt)
if [ $i -lt $MAX_RETRIES ]; then
sleep 0.5
fi
done
# All retries failed
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to increase volume after $MAX_RETRIES attempts"
exit 1
'';
volumeDownScript = pkgs.writeShellScript "avr-volume-down" ''
#!/usr/bin/env bash
# Configuration
HA_URL="https://home-assistant.johnogle.info"
ENTITY_ID="media_player.denon_avr_s970h_2"
MAX_RETRIES=3
# Read token from KDE Wallet and strip whitespace
TOKEN=$(${pkgs.kdePackages.kwallet}/bin/kwallet-query -r ha_avr_token kdewallet -f Passwords 2>/dev/null | tr -d '[:space:]')
if [ -z "$TOKEN" ]; then
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to retrieve Home Assistant token from KDE Wallet"
exit 1
fi
# Send volume down command with retry logic
for i in $(seq 1 $MAX_RETRIES); do
RESPONSE=$(${pkgs.curl}/bin/curl -s -w "\n%{http_code}" -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"entity_id\": \"$ENTITY_ID\"}" \
"$HA_URL/api/services/media_player/volume_down" 2>&1)
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
if [ "$HTTP_CODE" = "200" ]; then
exit 0
fi
# Wait before retry (except on last attempt)
if [ $i -lt $MAX_RETRIES ]; then
sleep 0.5
fi
done
# All retries failed
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to decrease volume after $MAX_RETRIES attempts"
exit 1
'';
volumeMuteScript = pkgs.writeShellScript "avr-volume-mute" ''
#!/usr/bin/env bash
# Configuration
HA_URL="https://home-assistant.johnogle.info"
ENTITY_ID="media_player.denon_avr_s970h_2"
MAX_RETRIES=3
# Read token from KDE Wallet and strip whitespace
TOKEN=$(${pkgs.kdePackages.kwallet}/bin/kwallet-query -r ha_avr_token kdewallet -f Passwords 2>/dev/null | tr -d '[:space:]')
if [ -z "$TOKEN" ]; then
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to retrieve Home Assistant token from KDE Wallet"
exit 1
fi
# Get current mute state
STATE_RESPONSE=$(${pkgs.curl}/bin/curl -s -H "Authorization: Bearer $TOKEN" \
"$HA_URL/api/states/$ENTITY_ID")
CURRENT_MUTE=$(echo "$STATE_RESPONSE" | ${pkgs.jq}/bin/jq -r '.attributes.is_volume_muted // false')
# Toggle: if currently muted (true), unmute (false), and vice versa
if [ "$CURRENT_MUTE" = "true" ]; then
NEW_MUTE="false"
NOTIFY_MSG="Unmuted"
else
NEW_MUTE="true"
NOTIFY_MSG="Muted"
fi
# Send mute toggle command with retry logic
for i in $(seq 1 $MAX_RETRIES); do
RESPONSE=$(${pkgs.curl}/bin/curl -s -w "\n%{http_code}" -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"entity_id\": \"$ENTITY_ID\", \"is_volume_muted\": $NEW_MUTE}" \
"$HA_URL/api/services/media_player/volume_mute" 2>&1)
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
if [ "$HTTP_CODE" = "200" ]; then
exit 0
fi
# Wait before retry (except on last attempt)
if [ $i -lt $MAX_RETRIES ]; then
sleep 0.5
fi
done
# All retries failed
${pkgs.libnotify}/bin/notify-send -u critical "Volume Control Error" "Failed to toggle mute after $MAX_RETRIES attempts"
exit 1
'';
in
{
options.home.roles.plasma-manager-kodi = {
enable = mkEnableOption "KDE Plasma volume control for kodi user via Home Assistant";
};
config = mkIf cfg.enable {
programs.plasma = {
enable = true;
overrideConfig = true;
# Disable default kmix volume shortcuts to prevent conflicts
shortcuts.kmix = {
"increase_volume" = "none";
"decrease_volume" = "none";
"mute" = "none";
};
# Define custom volume control commands with key bindings
hotkeys.commands = {
"volume-up-avr" = {
name = "Volume Up AVR";
key = "Volume Up";
command = toString volumeUpScript;
};
"volume-down-avr" = {
name = "Volume Down AVR";
key = "Volume Down";
command = toString volumeDownScript;
};
"volume-mute-avr" = {
name = "Mute Toggle AVR";
key = "Volume Mute";
command = toString volumeMuteScript;
};
};
# KDE Settings customization
configFile = {
# Session restore settings
"ksmserverrc"."General"."loginMode" = "emptySession";
# Screen locking settings
"kscreenlockerrc"."Daemon"."Autolock" = false;
"kscreenlockerrc"."Daemon"."LockOnResume" = false;
# Theme settings
"kdeglobals"."KDE"."LookAndFeelPackage" = "org.kde.breezedark.desktop";
};
};
};
}

View File

@@ -0,0 +1,190 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.plasma-manager;
wallpaperConfig = import ../../wallpapers;
currentWallpaper = builtins.elemAt wallpaperConfig.wallpapers wallpaperConfig.currentIndex;
in
{
options.home.roles.plasma-manager = {
enable = mkEnableOption "KDE Plasma desktop environment configuration";
};
config = mkIf cfg.enable {
# The current KDE config can be output with the command:
# nix run github:nix-community/plasma-manager
#
# Plasma-manager options documentation
# https://nix-community.github.io/plasma-manager/options.xhtml
#
# TODO: (ambitious) Add Kmail support to plasma-manager
programs.plasma = {
enable = true;
overrideConfig = true;
hotkeys.commands."launch-ghostty" = {
name = "Launch Ghostty";
key = "Meta+Return";
command = "ghostty";
};
shortcuts = {
kmix = {
"decrease_microphone_volume" = "Microphone Volume Down";
"decrease_volume" = "Volume Down";
"decrease_volume_small" = "Shift+Volume Down";
"increase_microphone_volume" = "Microphone Volume Up";
"increase_volume" = "Volume Up";
"increase_volume_small" = "Shift+Volume Up";
"mic_mute" = ["Microphone Mute" "Meta+Volume Mute,Microphone Mute" "Meta+Volume Mute,Mute Microphone"];
"mute" = "Volume Mute";
};
mediacontrol = {
"mediavolumedown" = "none,,Media volume down";
"mediavolumeup" = "none,,Media volume up";
"nextmedia" = "Media Next";
"pausemedia" = "Media Pause";
"playmedia" = "none,,Play media playback";
"playpausemedia" = "Media Play";
"previousmedia" = "Media Previous";
"stopmedia" = "Media Stop";
};
ksmserver = {
"Lock Session" = ["Meta+Ctrl+Q" "Screensaver" "Screensaver,Lock Session"];
};
kwin = {
"Window Close" = "Meta+Shift+Q";
"Kill Window" = "Meta+Ctrl+Esc";
"Window Operations Menu" = "Alt+F3";
"Window Resize" = "Meta+R,,Resize Window";
"Overview" = "Meta+Ctrl+W";
"Grid View" = "Meta+G";
"Edit Tiles" = "Meta+T";
"Activate Window Demanding Attention" = "Meta+Ctrl+A";
"Show Desktop" = "Meta+Ctrl+D";
"Walk Through Windows" = "Alt+Tab";
"Walk Through Windows (Reverse)" = "Alt+Shift+Tab";
"Walk Through Windows of Current Application" = "Alt+`";
"Walk Through Windows of Current Application (Reverse)" = "Alt+~";
"Window Quick Tile Bottom" = "Meta+Down";
"Window Quick Tile Left" = "Meta+Left";
"Window Quick Tile Right" = "Meta+Right";
"Window Quick Tile Top" = "Meta+Up";
"Switch to Desktop 1" = "Meta+1";
"Switch to Desktop 2" = "Meta+2";
"Switch to Desktop 3" = "Meta+3";
"Switch to Desktop 4" = "Meta+4";
"Switch to Desktop 5" = "Meta+5";
"Switch to Desktop 6" = "Meta+6";
"Switch to Desktop 7" = "Meta+7";
"Switch to Desktop 8" = "Meta+8";
"Switch to Desktop 9" = "Meta+9";
"Switch to Desktop 10" = "Meta+0";
"Window to Desktop 1" = "Meta+!"; # Meta+Shift+1
"Window to Desktop 2" = "Meta+@"; # Meta+Shift+2
"Window to Desktop 3" = "Meta+#"; # Meta+Shift+3
"Window to Desktop 4" = "Meta+$"; # Meta+Shift+4
"Window to Desktop 5" = "Meta+%"; # Meta+Shift+5
"Window to Desktop 6" = "Meta+^"; # Meta+Shift+6
"Window to Desktop 7" = "Meta+&"; # Meta+Shift+7
"Window to Desktop 8" = "Meta+*"; # Meta+Shift+8
"Window to Desktop 9" = "Meta+("; # Meta+Shift+9
"Window to Desktop 10" = "Meta+)"; # Meta+Shift+0
"view_actual_size" = "Meta+Ctrl+=";
"view_zoom_in" = ["Meta++" "Meta+=,Meta++" "Meta+=,Zoom In"];
"view_zoom_out" = "Meta+-";
};
"org_kde_powerdevil"."Decrease Keyboard Brightness" = "Keyboard Brightness Down";
"org_kde_powerdevil"."Decrease Screen Brightness" = "Monitor Brightness Down";
"org_kde_powerdevil"."Decrease Screen Brightness Small" = "Shift+Monitor Brightness Down";
"org_kde_powerdevil"."Hibernate" = "Hibernate";
"org_kde_powerdevil"."Increase Keyboard Brightness" = "Keyboard Brightness Up";
"org_kde_powerdevil"."Increase Screen Brightness" = "Monitor Brightness Up";
"org_kde_powerdevil"."Increase Screen Brightness Small" = "Shift+Monitor Brightness Up";
"org_kde_powerdevil"."PowerDown" = "Power Down";
"org_kde_powerdevil"."PowerOff" = "Power Off";
"org_kde_powerdevil"."Sleep" = "Sleep";
"org_kde_powerdevil"."Toggle Keyboard Backlight" = "Keyboard Light On/Off";
"org_kde_powerdevil"."Turn Off Screen" = [ ];
"org_kde_powerdevil"."powerProfile" = ["Battery" "Meta+B,Battery" "Meta+B,Switch Power Profile"];
plasmashell = {
"activate application launcher" = ["Meta" "Alt+F1,Meta" "Alt+F1,Activate Application Launcher"];
"activate task manager entry 1" = "none,,";
"activate task manager entry 2" = "none,,";
"activate task manager entry 3" = "none,,";
"activate task manager entry 4" = "none,,";
"activate task manager entry 5" = "none,,";
"activate task manager entry 6" = "none,,";
"activate task manager entry 7" = "none,,";
"activate task manager entry 8" = "none,,";
"activate task manager entry 9" = "none,,";
"activate task manager entry 10" = "none,,";
"show activity switcher" = "none,,";
};
};
configFile = {
kwinrc.Desktops.Number = {
value = 10;
immutable = true;
};
# Enable KWin tiling features
kwinrc.Tiling = {
# Enable tiling functionality
"padding" = 4;
};
# Enable krohnkite plugin automatically
kwinrc.Plugins = {
krohnkiteEnabled = true;
};
kwinrc.Effect-overview = {
# Configure overview effect for better tiling workflow
BorderActivate = 9; # Top-left corner activation
};
kcminputrc.Libinput = {
AccelerationProfile = "adaptive";
PointerAcceleration = 0.5;
};
kcminputrc.Mouse = {
X11LibInputXAccelProfileFlat = false;
XLbInptAccelProfileFlat = false;
};
kdeglobals.KDE.LookAndFeelPackage = "org.kde.breezedark.desktop";
# Focus follows mouse configuration
kwinrc.Windows = {
FocusPolicy = "FocusFollowsMouse";
AutoRaise = true; # Set to true if you want windows to auto-raise on focus
AutoRaiseInterval = 750; # Delay in ms before auto-raise (if enabled)
DelayFocusInterval = 0; # Delay in ms before focus follows mouse
};
# Desktop wallpaper configuration
plasma-localerc.Formats.LANG = "en_US.UTF-8";
# Set wallpaper for all desktops
plasmarc.Wallpapers.usersWallpapers = "${currentWallpaper.file}";
};
};
};
}

View File

@@ -0,0 +1,72 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.starship;
in
{
options.home.roles.starship = {
enable = mkEnableOption "starship cross-shell prompt";
};
config = mkIf cfg.enable {
programs.starship = {
enable = true;
enableBashIntegration = true;
enableZshIntegration = true;
settings = {
add_newline = true;
character = {
success_symbol = "[>](bold green)";
error_symbol = "[x](bold red)";
vimcmd_symbol = "[<](bold green)";
};
directory = {
truncation_length = 4;
truncate_to_repo = true;
};
git_branch = {
symbol = "";
format = "[$symbol$branch(:$remote_branch)]($style) ";
};
git_status = {
format = "([$all_status$ahead_behind]($style) )";
};
nix_shell = {
symbol = "";
format = "[$symbol$state( \\($name\\))]($style) ";
};
cmd_duration = {
min_time = 2000;
format = "[$duration]($style) ";
};
# Disable modules that are noisy or rarely needed
package.disabled = true;
nodejs.disabled = true;
python.disabled = true;
ruby.disabled = true;
java.disabled = true;
golang.disabled = true;
rust.disabled = true;
php.disabled = true;
lua.disabled = true;
perl.disabled = true;
terraform.disabled = true;
kubernetes.disabled = true;
docker_context.disabled = true;
aws.disabled = true;
gcloud.disabled = true;
azure.disabled = true;
};
};
};
}

View File

@@ -0,0 +1,24 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.sync;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.sync = {
enable = mkEnableOption "Enable file synchronization services";
};
config = mkIf cfg.enable {
# Linux-only: syncthingtray requires system tray support
home.packages = optionals isLinux (with pkgs; [
syncthingtray
]);
services.syncthing = {
enable = true;
};
};
}

View File

@@ -0,0 +1,62 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.roles.tmux;
tokyo-night = pkgs.tmuxPlugins.mkTmuxPlugin {
pluginName = "tokyo-night";
rtpFilePath = "tokyo-night.tmux";
version = "1.6.1";
src = pkgs.fetchFromGitHub {
owner = "janoamaral";
repo = "tokyo-night-tmux";
rev = "d610ced20d5f602a7995854931440e4a1e0ab780";
sha256 = "sha256-17vEgkL7C51p/l5gpT9dkOy0bY9n8l0/LV51mR1k+V8=";
};
};
in
{
options.home.roles.tmux = {
enable = mkEnableOption "tmux terminal multiplexer with Tokyo Night theme";
};
config = mkIf cfg.enable {
programs.tmux.enable = true;
programs.tmux.terminal = "tmux-direct";
programs.tmux.keyMode = "vi";
programs.tmux.escapeTime = 0;
programs.tmux.mouse = true;
programs.tmux.newSession = true;
programs.tmux.historyLimit = 50000;
programs.tmux.clock24 = true;
programs.tmux.baseIndex = 1;
programs.tmux.prefix = "M-\\\\";
programs.tmux.plugins = with pkgs; [
tmuxPlugins.cpu
tmuxPlugins.battery
tmuxPlugins.better-mouse-mode
tmuxPlugins.net-speed
tmuxPlugins.online-status
tmuxPlugins.pain-control
tmuxPlugins.tilish
tmuxPlugins.yank
{
plugin = tmuxPlugins.resurrect;
extraConfig = "set -g @resurrect-strategy-nvim 'session'";
}
{
plugin = tmuxPlugins.continuum;
extraConfig = ''
set -g @continuum-restore 'on'
set -g @continuum-save-interval '15' # minutes
'';
}
tokyo-night
];
};
}

View File

@@ -0,0 +1,45 @@
# Wallpaper rotation system
# The currentIndex is incremented by `nix run .#rotate-wallpaper`
# and gets committed as part of `nix run .#upgrade`
{
currentIndex = 2; # Index into wallpapers list
wallpapers = [
{
name = "metroid-samus-returns";
file = ./metroid-samus-returns-kz-3440x1440.jpg;
sway = "fill";
feh = "--bg-fill";
}
{
name = "metroid3_map";
file = ./metroid3_map.gif;
sway = "fit";
feh = "--bg-max";
}
{
name = "super-metroid-gunship-cavern";
file = ./super-metroid-gunship-cavern.jpg;
sway = "fit";
feh = "--bg-max";
}
{
name = "super-metroid-samus-statue";
file = ./super-metroid-samus-statue.png;
sway = "fit";
feh = "--bg-max";
}
{
name = "metroid-samus-action-4k";
file = ./metroid-samus-action-4k.jpg;
sway = "fit";
feh = "--bg-max";
}
{
name = "metroid-creature-minimalist";
file = ./metroid-creature-minimalist.jpg;
sway = "fit";
feh = "--bg-max";
}
];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 MiB

View File

@@ -17,19 +17,29 @@ with lib;
bluetooth.enable = true;
desktop = {
enable = true;
gaming = true;
gaming.enable = true;
kde = true;
sddm = true;
wayland = true;
};
kodi = {
plasma-bigscreen = {
enable = true;
autologin = false;
wayland = true;
autologin = true;
jellyfinScaleFactor = 1.0;
appLauncherServer.enable = true;
};
nfs-mounts.enable = true;
users.enable = true;
};
# Enable KDE Wallet PAM integration for auto-unlock
security.pam.services.sddm = {
kwallet = {
enable = true;
package = pkgs.kdePackages.kwallet-pam;
};
};
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
@@ -39,12 +49,7 @@ with lib;
services.xserver.videoDrivers = [ "amdgpu" ];
hardware.graphics.enable = true;
hardware.graphics.enable32Bit = true;
hardware.graphics.extraPackages = with pkgs; [
amdvlk
];
hardware.graphics.extraPackages32 = with pkgs; [
driversi686Linux.amdvlk
];
# RADV (AMD's Vulkan driver) is now enabled by default, amdvlk was removed
# This option defines the first version of NixOS you have installed on this particular machine,
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
@@ -66,4 +71,3 @@ with lib;
system.stateVersion = "24.05"; # Did you read the comment?
}

View File

@@ -0,0 +1,74 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{ lib, pkgs, ... }:
with lib;
{
imports = [
# Include the results of the hardware scan.
./hardware-configuration.nix
];
roles = {
audio.enable = true;
bluetooth.enable = true;
desktop = {
enable = true;
gaming.enable = true;
kde = true;
sddm = true;
wayland = true;
};
plasma-bigscreen = {
enable = true;
autologin = true;
jellyfinScaleFactor = 1.0;
appLauncherServer.enable = true;
};
nfs-mounts.enable = true;
users.enable = true;
};
# Enable KDE Wallet PAM integration for auto-unlock
security.pam.services.sddm = {
kwallet = {
enable = true;
package = pkgs.kdePackages.kwallet-pam;
};
};
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
networking.hostName = "gym-box";
networking.networkmanager.enable = true;
services.xserver.videoDrivers = [ "amdgpu" ];
hardware.graphics.enable = true;
hardware.graphics.enable32Bit = true;
# RADV (AMD's Vulkan driver) is now enabled by default, amdvlk was removed
# This option defines the first version of NixOS you have installed on this particular machine,
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
#
# Most users should NEVER change this value after the initial install, for any reason,
# even if you've upgraded your system to a new NixOS release.
#
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
# so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
# to actually do that.
#
# This value being lower than the current NixOS release does NOT mean your system is
# out of date, out of support, or vulnerable.
#
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
# and migrated your data accordingly.
#
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
system.stateVersion = "24.05"; # Did you read the comment?
}

View File

@@ -0,0 +1,31 @@
# 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, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "thunderbolt" "uas" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/59c0df78-c6fa-415d-8592-13547a3fada6";
fsType = "btrfs";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/DC66-D04C";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@@ -0,0 +1,424 @@
# Migration Plan: Arch Linux to NixOS on john-endesktop (ZFS/NFS Server)
## Overview
This document outlines the plan to migrate the john-endesktop server from Arch Linux to NixOS while maintaining the existing ZFS pools and NFS exports that serve your k3s cluster.
## Current System State
### Hardware
- **Boot disk**: nvme0n1
- nvme0n1p3: 1000M EFI partition (UUID: F5C6-D570)
- nvme0n1p4: 120GB ext4 / (current Arch root)
- nvme0n1p5: 810GB - **Target for NixOS** (being removed from media pool)
- **Network**: enp0s31f6 @ 10.0.0.43/24 (DHCP)
### ZFS Pools
- **media**: ~3.5TB JBOD pool (2 drives after nvme0n1p5 removal)
- wwn-0x50014ee2ba653d70-part2
- ata-WDC_WD20EZBX-00AYRA0_WD-WX62D627X7Z8-part2
- Contains: /media/media/nix (bind mounted to /nix on Arch)
- NFS: Shared to 10.0.0.0/24 via ZFS sharenfs property
- **swarmvols**: 928GB mirror pool - **PRODUCTION DATA**
- wwn-0x5002538f52707e2d-part2
- wwn-0x5002538f52707e81-part2
- Contains: iocage jails and k3s persistent volumes
- NFS: Shared to 10.0.0.0/24 via ZFS sharenfs property
- Backed up nightly to remote borg
### Services
- NFS server exporting /media and /swarmvols to k3s cluster
- ZFS managing pools with automatic exports via sharenfs property
## Prerequisites
### Before Starting
1. ✅ Ensure nvme0n1p5 removal from media pool is complete
```bash
ssh 10.0.0.43 "zpool status media"
# Should show no "removing" devices
```
2. ✅ Verify recent backups exist
```bash
# Verify swarmvols backup is recent (< 24 hours)
# Check your borg backup system
```
3. ✅ Notify k3s cluster users of planned maintenance window
- NFS shares will be unavailable during migration
- Estimate: 30-60 minutes downtime
4. ✅ Build NixOS configuration from your workstation
```bash
cd ~/nixos-configs
nix build .#nixosConfigurations.john-endesktop.config.system.build.toplevel
```
## Migration Steps
### Phase 1: Prepare NixOS Installation Media
1. **Download NixOS minimal ISO**
```bash
wget https://channels.nixos.org/nixos-25.11/latest-nixos-minimal-x86_64-linux.iso
```
2. **Create bootable USB**
```bash
# Identify USB device (e.g., /dev/sdb)
lsblk
# Write ISO to USB
sudo dd if=latest-nixos-minimal-x86_64-linux.iso of=/dev/sdX bs=4M status=progress
sudo sync
```
### Phase 2: Backup and Shutdown
1. **On the server, verify ZFS pool status**
```bash
ssh 10.0.0.43 "zpool status"
ssh 10.0.0.43 "zfs list"
```
2. **Export ZFS pools cleanly**
```bash
ssh 10.0.0.43 "sudo zpool export media"
ssh 10.0.0.43 "sudo zpool export swarmvols"
```
3. **Shutdown Arch Linux**
```bash
ssh 10.0.0.43 "sudo shutdown -h now"
```
### Phase 3: Install NixOS
1. **Boot from NixOS USB**
- Insert USB drive
- Power on and select USB in boot menu
2. **Connect to network**
```bash
# If DHCP doesn't work automatically:
sudo systemctl start dhcpcd
ip a # Verify you have 10.0.0.43 or another IP
```
3. **Enable SSH for remote installation (recommended)**
```bash
# Set password for nixos user
sudo passwd nixos
# Start SSH
sudo systemctl start sshd
# From your workstation:
ssh nixos@10.0.0.43
```
4. **Partition nvme0n1p5 with btrfs**
```bash
# Verify the device is clear
lsblk
sudo wipefs -a /dev/nvme0n1p5
# Create btrfs filesystem
sudo mkfs.btrfs -L nixos /dev/nvme0n1p5
# Mount and create subvolumes
sudo mount /dev/nvme0n1p5 /mnt
sudo btrfs subvolume create /mnt/@
sudo btrfs subvolume create /mnt/@home
sudo btrfs subvolume create /mnt/@nix
sudo btrfs subvolume create /mnt/@log
sudo umount /mnt
# Mount root subvolume
sudo mount -o subvol=@,compress=zstd,noatime /dev/nvme0n1p5 /mnt
# Create mount points
sudo mkdir -p /mnt/{boot,home,nix,var/log}
# Mount other subvolumes
sudo mount -o subvol=@home,compress=zstd,noatime /dev/nvme0n1p5 /mnt/home
sudo mount -o subvol=@nix,compress=zstd,noatime /dev/nvme0n1p5 /mnt/nix
sudo mount -o subvol=@log,compress=zstd,noatime /dev/nvme0n1p5 /mnt/var/log
# Mount EFI partition
sudo mount /dev/nvme0n1p3 /mnt/boot
```
5. **Import ZFS pools**
```bash
# Import pools (should be visible)
sudo zpool import
# Import with force if needed due to hostid
sudo zpool import -f media
sudo zpool import -f swarmvols
# Verify pools are mounted
zfs list
ls -la /media /swarmvols
```
6. **Generate initial hardware configuration**
```bash
sudo nixos-generate-config --root /mnt
```
7. **Get the new root filesystem UUID**
```bash
blkid /dev/nvme0n1p5
# Note the UUID for updating hardware-configuration.nix
/dev/nvme0n1p5: LABEL="nixos" UUID="5f4ad025-bfab-4aed-a933-6638348059e5" UUID_SUB="4734d820-7b8a-4b7f-853a-026021c1d204" BLOCK_SIZE="4096" TYPE="btrfs" PARTLABEL="data" PARTUUID="9ea025df-cdb7-48fd-b5d4-37cd5d8588eb"
```
8. **Copy your NixOS configuration to the server**
```bash
# From your workstation:
scp -r ~/nixos-configs/machines/john-endesktop/* nixos@10.0.0.43:/tmp/
# On server:
sudo mkdir -p /mnt/etc/nixos
sudo cp /tmp/configuration.nix /mnt/etc/nixos/
sudo cp /tmp/hardware-configuration.nix /mnt/etc/nixos/
# Edit hardware-configuration.nix to update the root filesystem UUID
sudo nano /mnt/etc/nixos/hardware-configuration.nix
# Change: device = "/dev/disk/by-uuid/CHANGE-THIS-TO-YOUR-UUID";
# To: device = "/dev/disk/by-uuid/[UUID from blkid]";
```
9. **Install NixOS**
```bash
sudo nixos-install
# Set root password when prompted
# Set user password
sudo nixos-install --no-root-passwd
```
10. **Reboot into NixOS**
```bash
sudo reboot
# Remove USB drive
```
### Phase 4: Post-Installation Verification
1. **Boot into NixOS and verify system**
```bash
ssh johno@10.0.0.43
# Check NixOS version
nixos-version
# Verify hostname
hostname # Should be: john-endesktop
```
2. **Verify ZFS pools imported correctly**
```bash
zpool status
zpool list
zfs list
# Check for hostid mismatch warnings (should be gone)
# Verify both pools show ONLINE status
```
3. **Verify NFS exports are active**
```bash
sudo exportfs -v
systemctl status nfs-server
# Should see /media and /swarmvols exported to 10.0.0.0/24
```
4. **Test NFS mount from another machine**
```bash
# From a k3s node or your workstation:
sudo mount -t nfs 10.0.0.43:/swarmvols /mnt
ls -la /mnt
sudo umount /mnt
sudo mount -t nfs 10.0.0.43:/media /mnt
ls -la /mnt
sudo umount /mnt
```
5. **Verify ZFS sharenfs properties preserved**
```bash
zfs get sharenfs media
zfs get sharenfs swarmvols
# Should show: sec=sys,mountpoint,no_subtree_check,no_root_squash,rw=@10.0.0.0/24
```
6. **Check swap device**
```bash
swapon --show
free -h
# Should show /dev/zvol/media/swap
```
### Phase 5: Restore k3s Cluster Access
1. **Restart k3s nodes or remount NFS shares**
```bash
# On each k3s node:
sudo systemctl restart k3s # or k3s-agent
```
2. **Verify k3s pods have access to persistent volumes**
```bash
# On k3s master:
kubectl get pv
kubectl get pvc
# Check that volumes are bound and accessible
```
## Rollback Plan
If something goes wrong during migration, you can roll back to Arch Linux:
### Quick Rollback (If NixOS won't boot)
1. **Boot from NixOS USB (or Arch USB)**
2. **Import ZFS pools**
```bash
sudo zpool import -f media
sudo zpool import -f swarmvols
```
3. **Start NFS manually (temporary)**
```bash
sudo mkdir -p /media /swarmvols
sudo systemctl start nfs-server
sudo exportfs -o rw,sync,no_subtree_check,no_root_squash 10.0.0.0/24:/media
sudo exportfs -o rw,sync,no_subtree_check,no_root_squash 10.0.0.0/24:/swarmvols
sudo exportfs -v
```
This will restore k3s cluster access immediately while you diagnose.
4. **Boot back into Arch Linux**
```bash
# Reboot and select nvme0n1p4 (Arch) in GRUB/boot menu
sudo reboot
```
5. **Verify Arch boots and services start**
```bash
ssh johno@10.0.0.43
zpool status
systemctl status nfs-server
```
### Full Rollback (If needed)
1. **Follow Quick Rollback steps above**
2. **Re-add nvme0n1p5 to media pool (if desired)**
```bash
# Only if you want to restore the original configuration
sudo zpool add media /dev/nvme0n1p5
```
3. **Clean up NixOS partition**
```bash
# If you want to reclaim nvme0n1p5 for other uses
sudo wipefs -a /dev/nvme0n1p5
```
## Risk Mitigation
### Data Safety
- ✅ **swarmvols** (production): Mirrored + nightly borg backups
- ⚠️ **media** (important): JBOD - no redundancy, but not catastrophic
- ✅ **NixOS install**: Separate partition, doesn't touch ZFS pools
- ✅ **Arch Linux**: Remains bootable on nvme0n1p4 until verified
### Service Continuity
- Downtime: 30-60 minutes expected
- k3s cluster: Will reconnect automatically when NFS returns
- Rollback time: < 10 minutes to restore Arch
### Testing Approach
1. Test NFS exports from NixOS live environment before installation
2. Test single NFS mount from k3s node before full cluster restart
3. Keep Arch Linux boot option until 24-48 hours of stable NixOS operation
## Post-Migration Tasks
After successful migration and 24-48 hours of stable operation:
1. **Update k3s NFS mounts (if needed)**
- Verify no hardcoded references to old system
2. **Optional: Repurpose Arch partition**
```bash
# After you're confident NixOS is stable
# You can wipe nvme0n1p4 and repurpose it
```
3. **Update documentation**
- Update infrastructure docs with NixOS configuration
- Document any deviations from this plan
4. **Consider setting up NixOS remote deployment**
```bash
# From your workstation:
nixos-rebuild switch --target-host johno@10.0.0.43 --flake .#john-endesktop
```
## Timeline
- **Preparation**: 1-2 hours (testing config build, downloading ISO)
- **Migration window**: 1-2 hours (installation + verification)
- **Verification period**: 24-48 hours (before removing Arch)
- **Total**: ~3 days from start to declaring success
## Emergency Contacts
- Borg backup location: [Document your borg repo location]
- K3s cluster nodes: [Document your k3s nodes]
- Critical services on k3s: [Document what's running that depends on these NFS shares]
## Checklist
Pre-migration:
- [x] nvme0n1p5 removal from media pool complete
- [x] Recent backup verified (< 24 hours)
- [x] Maintenance window scheduled
- [x] NixOS ISO downloaded
- [x] Bootable USB created
- [x] NixOS config builds successfully
During migration:
- [ ] ZFS pools exported
- [ ] Arch Linux shutdown cleanly
- [ ] Booted from NixOS USB
- [ ] nvme0n1p5 formatted with btrfs
- [ ] Btrfs subvolumes created
- [ ] ZFS pools imported
- [ ] NixOS installed
- [ ] Root password set
Post-migration:
- [ ] NixOS boots successfully
- [ ] ZFS pools mounted automatically
- [ ] NFS server running
- [ ] NFS exports verified
- [ ] Test mount from k3s node successful
- [ ] k3s cluster reconnected
- [ ] Persistent volumes accessible
- [ ] No hostid warnings in zpool status
- [ ] Arch Linux still bootable (for rollback)
Final verification (after 24-48 hours):
- [ ] All services stable
- [ ] No unexpected issues
- [ ] Performance acceptable
- [ ] Ready to remove Arch partition (optional)
- [ ] Ready to remove /swarmvols/media-backup (optional)

View File

@@ -0,0 +1,167 @@
# NixOS configuration for john-endesktop (ZFS/NFS server)
# Migrated from Arch Linux to provide ZFS pools via NFS to k3s cluster
{ config, lib, pkgs, ... }:
with lib;
{
imports = [
./hardware-configuration.nix
];
# Boot configuration
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# ZFS support
boot.supportedFilesystems = [ "zfs" ];
boot.zfs.forceImportRoot = false;
boot.zfs.extraPools = [ "media" "swarmvols" ];
# Set ZFS hostid to match current system (from Arch Linux)
# This resolves the hostid mismatch warnings
networking.hostId = "007f0101";
# Hostname
networking.hostName = "john-endesktop";
# Network configuration - using DHCP on enp0s31f6
networking.useDHCP = false;
networking.interfaces.enp0s31f6.useDHCP = true;
# NFS Server configuration
services.nfs.server = {
enable = true;
# NFS protocol versions
# v3 for broader compatibility, v4 for better performance
exports = ''
# These are managed by ZFS sharenfs properties
# but we enable the NFS server here
'';
};
# Enable NFS4 with proper configuration
services.rpcbind.enable = true;
# Firewall configuration for NFS
networking.firewall = {
enable = true;
allowedTCPPorts = [
111 # rpcbind
2049 # nfs
4000 # nfs callback
4001 # nlockmgr
4002 # mountd
5000 # harmonia binary cache
20048 # mountd
];
allowedUDPPorts = [
111 # rpcbind
2049 # nfs
4000 # nfs callback
4001 # nlockmgr
4002 # mountd
20048 # mountd
];
# Allow NFS from local network
extraCommands = ''
iptables -A nixos-fw -p tcp -s 10.0.0.0/24 -j ACCEPT
iptables -A nixos-fw -p udp -s 10.0.0.0/24 -j ACCEPT
'';
};
# ZFS maintenance
services.zfs = {
autoScrub = {
enable = true;
interval = "monthly";
};
trim = {
enable = true;
interval = "weekly";
};
};
# Basic system packages
environment.systemPackages = with pkgs; [
vim
git
htop
tmux
zfs
rclone
custom.rclone-torbox-setup # Helper script to set up TorBox credentials via rbw
];
# Enable SSH
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "no";
PasswordAuthentication = true;
};
};
# User configuration
roles.users.enable = true;
# Enable as remote builder (similar to zix790prors)
roles.remote-build.enableBuilder = true;
# k3s agent configuration
roles.k3s-node = {
enable = true;
role = "agent";
# serverAddr defaults to https://10.0.0.222:6443
# tokenFile defaults to /etc/k3s/token
extraFlags = [
# Node labels for workload scheduling
# fast-cpu: This node has a faster CPU than other cluster nodes
"--node-label=fast-cpu=true"
# fast-storage: This node is the NFS host with fast local storage access
"--node-label=fast-storage=true"
# k3s-upgrade=disabled: NixOS manages k3s upgrades via Nix, not system-upgrade-controller
"--node-label=k3s-upgrade=disabled"
];
};
roles.virtualisation.enable = true;
# TorBox WebDAV mount for rdt-client and Jellyfin
roles.rclone-mount = {
enable = true;
mounts.torbox = {
webdavUrl = "https://webdav.torbox.app";
username = "john@ogle.fyi"; # TorBox account email
mountPoint = "/media/media/torbox-rclone";
environmentFile = "/etc/rclone/torbox.env";
vfsCacheMode = "full"; # Best for streaming media
dirCacheTime = "5m";
extraArgs = [
"--buffer-size=64M"
"--vfs-read-chunk-size=32M"
"--vfs-read-chunk-size-limit=off"
];
# Wait for ZFS media pool to be mounted before starting
requiresMountsFor = [ "/media" ];
};
};
# Harmonia binary cache server
# Replaces the broken k8s deployment with native NixOS service
services.harmonia = {
enable = true;
signKeyPaths = [ "/etc/harmonia/signing-key.private" ];
settings = {
bind = "[::]:5000";
};
};
# Time zone
time.timeZone = "America/Los_Angeles"; # Adjust as needed
# NixOS version
system.stateVersion = "25.11";
}

View File

@@ -0,0 +1,63 @@
# Hardware configuration for john-endesktop
# This file should be regenerated after NixOS installation using:
# nixos-generate-config --show-hardware-config
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
# Boot configuration
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# File systems - these will need to be updated after installation
# The nvme0n1p5 partition will be formatted as btrfs for NixOS root
fileSystems."/" = {
device = "/dev/disk/by-uuid/5f4ad025-bfab-4aed-a933-6638348059e5";
fsType = "btrfs";
options = [ "subvol=@" "compress=zstd" "noatime" ];
};
fileSystems."/home" = {
device = "/dev/disk/by-uuid/5f4ad025-bfab-4aed-a933-6638348059e5";
fsType = "btrfs";
options = [ "subvol=@home" "compress=zstd" "noatime" ];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/5f4ad025-bfab-4aed-a933-6638348059e5";
fsType = "btrfs";
options = [ "subvol=@nix" "compress=zstd" "noatime" ];
};
fileSystems."/var/log" = {
device = "/dev/disk/by-uuid/5f4ad025-bfab-4aed-a933-6638348059e5";
fsType = "btrfs";
options = [ "subvol=@log" "compress=zstd" "noatime" ];
};
fileSystems."/boot" = {
# This should match your current EFI partition
device = "/dev/disk/by-uuid/F5C6-D570";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
};
# ZFS pools - these are imported by ZFS, not managed by fileSystems
# The pools should be imported automatically via boot.zfs.extraPools
# /media and /swarmvols will be mounted by ZFS
# No swap needed - 23GB RAM is sufficient for this NFS/ZFS server
swapDevices = [ ];
# CPU microcode
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# Networking
networking.useDHCP = lib.mkDefault true;
}

View File

@@ -0,0 +1,23 @@
{ config, lib, pkgs, ... }:
{
# Basic system configuration for macOS work laptop
system.stateVersion = 6;
# Set primary user for nix-darwin
system.primaryUser = "johno";
# System preferences (can be expanded later)
system.defaults = {
dock.autohide = true;
finder.AppleShowAllExtensions = true;
NSGlobalDomain.AppleShowAllExtensions = true;
};
# TODO: Find a way to not duplicate this
launchd.user.envVariables = {
# DOOM Emacs environment variables
DOOMDIR = "/Users/johno/.config/doom";
DOOMLOCALDIR = "/Users/johno/.local/doom";
};
}

View File

@@ -65,6 +65,8 @@
# Enable NetworkManager for easy wifi setup
networking.networkmanager.enable = true;
# Disable wireless networking (conflicts with NetworkManager)
networking.wireless.enable = false;
# Enable SSH daemon for remote access
services.openssh = {

View File

@@ -15,18 +15,47 @@
desktop = {
enable = true;
wayland = true;
gaming = false;
gaming.enable = true;
kde = true;
sddm = true;
};
nfs-mounts.enable = true;
printing.enable = true;
remote-build.builders = [
{
hostName = "zix790prors.oglehome";
maxJobs = 16;
speedFactor = 3;
}
{
hostName = "john-endesktop.oglehome";
maxJobs = 1;
speedFactor = 1;
}
];
spotifyd.enable = true;
users = {
enable = true;
extraGroups = [ "video" ];
};
virtualisation.enable = true;
virtualisation = {
enable = true;
waydroid = true;
};
wireguard = {
enable = true;
autostart = true;
interfaceName = "ogleNet";
address = [ "192.168.4.2/32" ];
privateKeyFile = "/etc/wireguard/oglehome-private-key";
dns = [ "192.168.4.1" ];
peers = [{
publicKey = "AWkmtaz0poyyKJGnRcabO5ecd6ESh1lKu+XRb3ObxBc=";
endpoint = "pi.johnogle.info:6666";
allowedIPs = [ "0.0.0.0/0" ];
persistentKeepalive = 25;
}];
};
};
# Bootloader.
@@ -34,12 +63,15 @@
boot.loader.efi.canTouchEfiVariables = true;
boot.initrd.luks.devices."luks-b614167b-9045-4234-a441-ac6f60a96d81".device = "/dev/disk/by-uuid/b614167b-9045-4234-a441-ac6f60a96d81";
services.logind.settings.Login = {
HandlePowerKey = "hibernate";
HandlePowerKeyLongPress = "poweroff";
};
networking.hostName = "nix-book"; # Define your hostname.
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
boot.kernelPackages = pkgs.linuxPackages_latest;
# Enable networking
networking.networkmanager.enable = true;

View File

@@ -0,0 +1,48 @@
{ pkgs, ... }:
{
imports = [
./hardware-configuration.nix
../../roles/desktop/steamos.nix
];
roles = {
audio.enable = true;
bluetooth.enable = true;
desktop = {
enable = true;
wayland = true;
gaming.enable = true;
kde = true;
steamos = {
enable = true;
autoStart = true;
desktopSession = "plasma";
};
};
remote-build.builders = [
{
hostName = "zix790prors.oglehome";
maxJobs = 16;
speedFactor = 4;
}
{
hostName = "john-endesktop.oglehome";
maxJobs = 1;
speedFactor = 2;
}
];
users = {
enable = true;
extraGroups = [ "video" ];
};
};
# Bootloader
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
networking.hostName = "nix-deck";
networking.networkmanager.enable = true;
system.stateVersion = "25.05";
}

View File

@@ -0,0 +1,51 @@
# Hardware configuration for Steam Deck (nix-deck)
# Generated from nixos-generate-config on 2025-11-17
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
# Steam Deck specific hardware configuration (Jovian)
jovian.devices.steamdeck = {
enable = true;
autoUpdate = false; # Set to true if you want automatic firmware updates
};
# Kernel modules detected by nixos-generate-config
boot.initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"usb_storage"
"uas"
"usbhid"
"sd_mod"
"sdhci_pci"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
# IMPORTANT: Update these filesystem configurations based on your actual partition layout
# The configuration below is a placeholder - adjust according to how you partitioned the disk
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "vfat";
};
swapDevices = [{
device = "/swapfile";
size = 8192; # 8GB swap file
}];
# AMD CPU microcode updates
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@@ -1,62 +0,0 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
# NixOS-WSL specific options are documented on the NixOS-WSL repository:
# https://github.com/nix-community/NixOS-WSL
{ config, lib, pkgs, ... }:
{
imports = [
];
roles = {
audio.enable = true;
desktop = {
enable = true;
wayland = true;
};
users.enable = true;
};
networking.hostName = "wixos";
wsl.enable = true;
wsl.defaultUser = "johno";
wsl.startMenuLaunchers = true;
wsl.useWindowsDriver = true;
wsl.wslConf.network.hostname = "wixos";
wsl.wslConf.user.default = "johno";
services.xserver.videoDrivers = [ "nvidia" ];
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [
mesa
libvdpau-va-gl
vaapiVdpau
];
};
environment.sessionVariables = {
LD_LIBRARY_PATH = [
"/usr/lib/wsl/lib"
"/run/opengl-driver/lib"
];
};
hardware.nvidia = {
modesetting.enable = true;
nvidiaSettings = true;
open = true;
package = config.boot.kernelPackages.nvidiaPackages.latest;
};
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. It's perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "24.05"; # Did you read the comment?
}

View File

@@ -0,0 +1,31 @@
* zix790prors
The re-birthed NixOS install of my 2024/2025 gaming pc / workstation.
** Specs
- **CPU:** Intel Core i7-14700K (20 cores, 28 threads, up to 5.6 GHz)
- **Memory:** 64 GB RAM
- **Storage:**
- 4TB NVMe SSD (main drive with dual-boot partitions)
- Windows 11 partition (NTFS)
- NixOS /nix/store partition (btrfs)
- Shared /games partition (btrfs, accessible from both Windows and NixOS)
- **GPU:** NVIDIA GeForce RTX 4070 Ti
- **Boot:** UEFI with 100MB EFI System Partition
This is a powerful all-purpose workstation optimized for gaming, 3D modeling, and development. It dual-boots Windows 11 with a shared btrfs /games partition accessible from both operating systems.
*** Validation
Given the above specs, I want to run shell commands to validate them for accuracy. Use the run_shell_command tool to get the results required to complete this validation. Do not return to the user until you have exhausted your self-serve options for accomplishing your task.
** BIOS Settings
**2025-09-08**
I underclocked the CPU today. I set the cpu/cache voltage offset to -50mV and lowered the P-Core multiplier from 56x to 50x. I was able to run Intel XTU benchmarks and the CPU stayed around 80C without any throttling kicking in, whereas before it would bounce around various cores at 100C with lots of throttling taking place.
My goals for this change are:
- CPU longevity
- Fan noise
- Addressing various apps that say 100C is a "critical temperature" (though I believe the CPU is rated for up to 110C)
I'm leaving some performance on the table, but it almost feels like the default settings for this CPU were to effectively be overclocked.

View File

@@ -0,0 +1,84 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{ lib, pkgs, ... }:
with lib;
{
imports = [
./hardware-configuration.nix
#./virtual-surround.nix
];
roles = {
audio.enable = true;
bluetooth.enable = true;
desktop = {
enable = true;
gaming = {
enable = true;
};
kde = true;
sddm = true;
wayland = true;
x11 = true;
};
kodi.enable = true;
nfs-mounts.enable = true;
nvidia = {
enable = true;
graphics.enable32Bit = true;
};
printing.enable = true;
remote-build.enableBuilder = true;
users.enable = true;
virtualisation.enable = true;
};
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.systemd-boot.configurationLimit = 20;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.timeout = 10;
networking.hostName = "zix790prors"; # Define your hostname.
# Enable networking
networking.networkmanager.enable = true;
# Fix dual boot clock sync - tell Linux to use local time for hardware clock
time.hardwareClockInLocalTime = true;
# Set DP-0 as primary display with 164.90Hz refresh rate
services.xserver.displayManager.sessionCommands = ''
${pkgs.xorg.xrandr}/bin/xrandr --output DP-0 --mode 3440x1440 --rate 164.90 --primary
'';
services.ollama = {
enable = true;
acceleration = "cuda";
loadModels = [ "gpt-oss" "deepseek-r1" "qwen3:30b" ];
};
# This option defines the first version of NixOS you have installed on this particular machine,
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
#
# Most users should NEVER change this value after the initial install, for any reason,
# even if you've upgraded your system to a new NixOS release.
#
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
# so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
# to actually do that.
#
# This value being lower than the current NixOS release does NOT mean your system is
# out of date, out of support, or vulnerable.
#
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
# and migrated your data accordingly.
#
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
system.stateVersion = "25.11"; # Did you read the comment?
}

View File

@@ -0,0 +1,57 @@
# 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, pkgs, 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 = [ ];
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/11C1-EB58";
fsType = "vfat";
options = [ "fmask=0077" "dmask=0077" ];
};
roles.btrfs = {
enable = true;
filesystems."/dev/disk/by-uuid/ec22734b-d1a3-4c99-8c6f-86f6a8d79007" = {
mountpoints = {
"/" = {
compression = "zstd";
extraOptions = [ "noatime" ];
};
};
scrub.enable = true;
deduplication = {
enable = true;
hashTableSizeMB = 128;
verbosity = "err";
};
};
filesystems."/dev/disk/by-uuid/4f9844ac-c1ad-4426-8eb3-21f2306345fb" = {
mountpoints = {
"/games" = {
extraOptions = [ "noatime" ];
};
};
scrub.enable = true;
deduplication = {
enable = true;
hashTableSizeMB = 256;
verbosity = "err";
};
};
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@@ -0,0 +1,132 @@
# Virtual 4.1 surround sound setup
# Routes FL/FR to AmazonBasics USB speaker, RL/RR to Fosi BT20A PRO Bluetooth speaker
{ pkgs, ... }:
{
services.pipewire.extraConfig.pipewire."10-virtual-surround" = {
"context.objects" = [
{
factory = "adapter";
args = {
"factory.name" = "support.null-audio-sink";
"node.name" = "virtual_surround_sink";
"node.description" = "Virtual 4.1 Surround (AmazonBasics + Fosi)";
"media.class" = "Audio/Sink";
"audio.position" = [ "FL" "FR" "RL" "RR" "LFE" ];
"monitor.channel-volumes" = true;
};
}
];
"context.modules" = [
{
name = "libpipewire-module-loopback";
args = {
"node.description" = "Route Front to AmazonBasics";
"capture.props" = {
"node.name" = "route_front_capture";
"audio.position" = [ "FL" "FR" ];
"stream.dont-remix" = true;
"node.passive" = true;
};
"playback.props" = {
"node.name" = "route_front_playback";
"node.target" = "alsa_output.usb-C-Media_Electronics_Inc._AmazonBasics_Professional_Mic_2-00.analog-stereo";
"audio.position" = [ "FL" "FR" ];
"stream.dont-remix" = true;
};
};
}
{
name = "libpipewire-module-loopback";
args = {
"node.description" = "Route Rear to Fosi Audio";
"capture.props" = {
"node.name" = "route_rear_capture";
"audio.position" = [ "RL" "RR" ];
"stream.dont-remix" = true;
"node.passive" = true;
};
"playback.props" = {
"node.name" = "route_rear_playback";
"node.target" = "bluez_output.F4_4E_FD_FB_58_62.1";
"audio.position" = [ "FL" "FR" ];
"stream.dont-remix" = true;
};
};
}
{
name = "libpipewire-module-loopback";
args = {
"node.description" = "Route Subwoofer to AmazonBasics";
"capture.props" = {
"node.name" = "route_lfe_capture";
"audio.position" = [ "LFE" ];
"stream.dont-remix" = true;
"node.passive" = true;
};
"playback.props" = {
"node.name" = "route_lfe_playback";
"node.target" = "alsa_output.usb-C-Media_Electronics_Inc._AmazonBasics_Professional_Mic_2-00.analog-stereo";
"audio.position" = [ "MONO" ];
"stream.dont-remix" = false;
};
};
}
];
};
# Systemd services to fix PipeWire loopback routing for virtual surround
systemd.user.services.pipewire-surround-link = {
description = "Link virtual surround sink to loopback captures";
after = [ "pipewire.service" "wireplumber.service" ];
requires = [ "pipewire.service" "wireplumber.service" ];
wantedBy = [ "pipewire.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = false;
ExecStart = pkgs.writeShellScript "surround-link" ''
sleep 2
# Disconnect wrong connections
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX0 route_front_capture:input_FL 2>/dev/null || true
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX1 route_front_capture:input_FR 2>/dev/null || true
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX0 route_rear_capture:input_RL 2>/dev/null || true
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX1 route_rear_capture:input_RR 2>/dev/null || true
${pkgs.pipewire}/bin/pw-link -d alsa_input.pci-0000_00_1f.3.pro-input-2:capture_AUX0 route_lfe_capture:input_LFE 2>/dev/null || true
# Create correct connections
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_FL route_front_capture:input_FL 2>/dev/null || true
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_FR route_front_capture:input_FR 2>/dev/null || true
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_RL route_rear_capture:input_RL 2>/dev/null || true
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_RR route_rear_capture:input_RR 2>/dev/null || true
${pkgs.pipewire}/bin/pw-link virtual_surround_sink:monitor_LFE route_lfe_capture:input_LFE 2>/dev/null || true
'';
};
};
systemd.user.services.pipewire-surround-link-check = {
description = "Check and fix surround sink links";
after = [ "pipewire.service" "wireplumber.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = pkgs.writeShellScript "surround-link-check" ''
if ${pkgs.pipewire}/bin/pw-cli ls Node 2>/dev/null | grep -q "bluez_output.F4_4E_FD_FB_58_62"; then
if ${pkgs.pipewire}/bin/pw-link -l 2>/dev/null | grep -q "route_front_capture:input_FL.*alsa_input"; then
${pkgs.systemd}/bin/systemctl --user start pipewire-surround-link.service
fi
if ! ${pkgs.pipewire}/bin/pw-link -l 2>/dev/null | grep -q "virtual_surround_sink:monitor_FL.*route_front_capture"; then
${pkgs.systemd}/bin/systemctl --user start pipewire-surround-link.service
fi
fi
'';
};
};
systemd.user.timers.pipewire-surround-link-check = {
description = "Periodically check surround sink links";
wantedBy = [ "default.target" ];
timerConfig = {
OnStartupSec = "10s";
OnUnitActiveSec = "10s";
Unit = "pipewire-surround-link-check.service";
};
};
}

View File

@@ -0,0 +1,176 @@
#!/usr/bin/env python3
import json
import logging
import os
import subprocess
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse
import psutil
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Allowlisted applications that can be launched
ALLOWED_APPS = {
'firefox': 'firefox',
'kodi': 'kodi'
}
def is_app_running(app_name):
"""Check if an application is already running, returns (is_running, pid)"""
command = ALLOWED_APPS.get(app_name)
if not command:
return False, None
logger.debug(f"Looking for processes related to app '{app_name}' (command: '{command}')")
for proc in psutil.process_iter(['name', 'cmdline', 'pid']):
try:
proc_name = proc.info['name']
cmdline = proc.info['cmdline'] or []
logger.debug(f"Checking process PID {proc.info['pid']}: name='{proc_name}', cmdline={cmdline}")
# Check multiple patterns for the application:
# 1. Process name exactly matches command
# 2. Process name contains the command (e.g., "kodi.bin" contains "kodi")
# 3. Command line starts with the command
# 4. Command line contains the wrapped version (e.g., ".kodi-wrapped")
# 5. Any command line argument ends with the command executable
matches = False
match_reason = ""
if proc_name == command:
matches = True
match_reason = f"exact process name match: '{proc_name}'"
elif command in proc_name:
matches = True
match_reason = f"process name contains command: '{proc_name}' contains '{command}'"
elif cmdline and cmdline[0] == command:
matches = True
match_reason = f"exact cmdline match: '{cmdline[0]}'"
elif cmdline and cmdline[0].endswith('/' + command):
matches = True
match_reason = f"cmdline path ends with command: '{cmdline[0]}'"
elif cmdline and any(f'.{command}-wrapped' in arg for arg in cmdline):
matches = True
match_reason = f"wrapped command in cmdline: {cmdline}"
elif cmdline and any(f'{command}.bin' in arg for arg in cmdline):
matches = True
match_reason = f"binary command in cmdline: {cmdline}"
if matches:
logger.info(f"Found running {app_name} process: PID {proc.info['pid']} ({match_reason})")
return True, proc.info['pid']
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
continue
logger.debug(f"No running process found for {app_name}")
return False, None
class AppLauncherHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
logger.info(format % args)
def do_GET(self):
if self.path == '/':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {
'status': 'running',
'available_apps': list(ALLOWED_APPS.keys()),
'usage': 'POST /launch/<app_name> to launch an application'
}
self.wfile.write(json.dumps(response, indent=2).encode())
else:
self.send_error(404)
def do_POST(self):
parsed_path = urlparse(self.path)
path_parts = parsed_path.path.strip('/').split('/')
if len(path_parts) == 2 and path_parts[0] == 'launch':
app_name = path_parts[1]
self.launch_app(app_name)
else:
self.send_error(404, "Invalid endpoint. Use /launch/<app_name>")
def launch_app(self, app_name):
if app_name not in ALLOWED_APPS:
self.send_error(400, f"Application '{app_name}' not allowed. Available apps: {list(ALLOWED_APPS.keys())}")
return
command = ALLOWED_APPS[app_name]
# Check if app is already running
is_running, existing_pid = is_app_running(app_name)
if is_running:
logger.info(f"Application {app_name} is already running (PID: {existing_pid}), skipping launch")
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {
'status': 'success',
'message': f'{app_name} is already running',
'pid': existing_pid,
'already_running': True
}
self.wfile.write(json.dumps(response).encode())
return
try:
# Launch the application in the background
# Ensure we have the proper environment for GUI apps
env = os.environ.copy()
logger.info(f"Launching application: {command}")
process = subprocess.Popen(
[command],
env=env,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True
)
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {
'status': 'success',
'message': f'Successfully launched {app_name}',
'pid': process.pid,
'already_running': False
}
self.wfile.write(json.dumps(response).encode())
except FileNotFoundError:
logger.error(f"Application not found: {command}")
self.send_error(500, f"Application '{app_name}' not found on system")
except Exception as e:
logger.error(f"Error launching {command}: {e}")
self.send_error(500, f"Failed to launch {app_name}: {str(e)}")
def main():
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8081
server = HTTPServer(('0.0.0.0', port), AppLauncherHandler)
logger.info(f"App launcher server starting on port {port}")
logger.info(f"Available applications: {list(ALLOWED_APPS.keys())}")
try:
server.serve_forever()
except KeyboardInterrupt:
logger.info("Server shutting down...")
server.server_close()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,10 @@
{ pkgs }:
let
python = pkgs.python3.withPackages (ps: with ps; [
psutil
]);
in
pkgs.writeShellScriptBin "app-launcher-server" ''
exec ${python}/bin/python3 ${./app-launcher-server.py} "$@"
''

View File

@@ -0,0 +1,116 @@
# claude-cli
Custom Nix package for Claude Code CLI.
## Why This Package Exists
The official `claude-code` package in nixpkgs tries to fetch from npm registry, which is blocked by Block's corporate security (Cloudflare Teams dependency confusion protection). This custom package fetches directly from Anthropic's Google Cloud Storage distribution, bypassing the npm registry entirely.
## Updating to a New Version
### Automated Update (Recommended)
Run the update script to automatically fetch and update to the latest version:
```bash
cd packages/claude-cli
./update.sh
```
The script will:
- Fetch the latest version from Homebrew cask
- Update version and all SHA256 hashes in default.nix
- Show you what changed
For a dry-run to see what would change:
```bash
./update.sh --dry-run
```
After the script completes, follow the "Test the Build" steps below.
### Manual Update
If you prefer to update manually, or if the automated script fails:
#### 1. Find the Latest Version and Hashes
Check the Homebrew cask formula for the latest version info:
```bash
curl -s "https://raw.githubusercontent.com/Homebrew/homebrew-cask/HEAD/Casks/c/claude-code.rb" | head -50
```
This will show:
- The latest `version` number
- SHA256 hashes for all platforms (`arm64`, `x86_64`, `x86_64_linux`, `arm64_linux`)
#### 2. Update default.nix
Edit `default.nix` and update:
1. The `version` variable (line 9):
```nix
version = "2.0.51"; # Update this
```
2. All four platform sha256 hashes in the `srcs` attribute set (lines 11-27):
```nix
aarch64-darwin = {
sha256 = "..."; # Update from Homebrew cask "arm:" value
};
x86_64-darwin = {
sha256 = "..."; # Update from Homebrew cask "x86_64:" value
};
x86_64-linux = {
sha256 = "..."; # Update from Homebrew cask "x86_64_linux:" value
};
aarch64-linux = {
sha256 = "..."; # Update from Homebrew cask "arm64_linux:" value
};
```
#### 3. Test the Build
Before committing, test that the package builds successfully:
```bash
NIXPKGS_ALLOW_UNFREE=1 nix-build -E 'with import <nixpkgs> { config.allowUnfree = true; }; callPackage ./packages/claude-cli {}'
```
Verify the version:
```bash
./result/bin/claude --version
```
Clean up the test build:
```bash
rm result
```
#### 4. Deploy
Commit your changes and rebuild:
```bash
git add packages/claude-cli/
git commit -m "claude-cli: Update to version X.Y.Z"
darwin-rebuild switch --flake .#blkfv4yf49kt7
```
## Alternative: Automated Hash Fetching
If you prefer to fetch hashes automatically, you can use `nix-prefetch-url`:
```bash
# For macOS ARM64 (your current platform)
nix-prefetch-url "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/VERSION/darwin-arm64/claude"
# For other platforms, replace VERSION and adjust the platform string:
# darwin-x64, linux-x64, linux-arm64
```
This will download the file and output the SHA256 hash.

View File

@@ -0,0 +1,75 @@
{ lib
, stdenv
, fetchurl
, patchelf
, glibc
}:
let
version = "2.1.75";
srcs = {
aarch64-darwin = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-arm64/claude";
sha256 = "8c541a5e924eda2070eaf1702a48047af671c4dff6a11a5e762076614a082675";
};
x86_64-darwin = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-x64/claude";
sha256 = "82c90b91a0a18f60191f817b9b42304d8b17dbed75795b715c41f4fdfe4c782d";
};
x86_64-linux = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-x64/claude";
sha256 = "328b0a429c05a04f911157d886be5123cf1824a19ba8ca1f9d594c004eac32c9";
};
aarch64-linux = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-arm64/claude";
sha256 = "ec8f4f7f7bb50611dae70c109a76ee1da6a3ab45511c65f117df215848ecc905";
};
};
src = srcs.${stdenv.hostPlatform.system} or (throw "Unsupported system: ${stdenv.hostPlatform.system}");
in stdenv.mkDerivation {
pname = "claude-code";
inherit version;
src = fetchurl {
inherit (src) url sha256;
};
dontUnpack = true;
dontBuild = true;
# Bun standalone binaries have JS code appended after the ELF sections
# stripping/patching would remove or corrupt this appended data
dontStrip = true;
dontPatchELF = true;
# Don't use autoPatchelfHook - it rewrites the ELF and strips the appended
# bun bundle (the JS code is appended after the ELF sections)
nativeBuildInputs = lib.optionals stdenv.isLinux [ patchelf ];
installPhase = ''
runHook preInstall
install -Dm755 $src $out/bin/claude
runHook postInstall
'';
# Manually patch the interpreter for bun standalone binaries
# patchelf --set-interpreter modifies in-place without rewriting the entire ELF,
# preserving the appended JS bundle that bun needs at runtime
postFixup = lib.optionalString stdenv.isLinux ''
interpreter="${glibc}/lib/${if stdenv.hostPlatform.system == "aarch64-linux" then "ld-linux-aarch64.so.1" else "ld-linux-x86-64.so.2"}"
patchelf --set-interpreter "$interpreter" $out/bin/claude
'';
meta = with lib; {
description = "Terminal-based AI coding assistant from Anthropic";
homepage = "https://www.anthropic.com/claude-code";
license = licenses.unfree;
maintainers = [ ];
platforms = [ "aarch64-darwin" "x86_64-darwin" "x86_64-linux" "aarch64-linux" ];
mainProgram = "claude";
};
}

View File

@@ -0,0 +1,34 @@
{ lib
, buildNpmPackage
, fetchurl
, nodejs_18
}:
buildNpmPackage {
pname = "claude-cli";
version = "0.2.65";
src = fetchurl {
url = "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.2.65.tgz";
sha256 = "0wwaqq7k9p5aw4vqhfpdgf3da09x64q55wibqaprk6kjvn130i92";
};
npmDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; # Will be updated after first build
nodejs = nodejs_18;
# Don't run npm audit or other network operations during build
npmConfigHook = ''
npm config set audit false
npm config set fund false
'';
meta = with lib; {
description = "Terminal-based AI coding assistant from Anthropic (npm distribution)";
homepage = "https://www.anthropic.com/claude-code";
license = licenses.unfree;
maintainers = [ ];
platforms = platforms.all;
mainProgram = "claude";
};
}

133
packages/claude-code/update.sh Executable file
View File

@@ -0,0 +1,133 @@
#!/usr/bin/env bash
set -euo pipefail
DRY_RUN=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run|-n)
DRY_RUN=true
shift
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --dry-run, -n Show what would be updated without making changes"
echo " --help, -h Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
CASK_URL="https://raw.githubusercontent.com/Homebrew/homebrew-cask/HEAD/Casks/c/claude-code.rb"
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
NIX_FILE="$REPO_ROOT/packages/claude-code/default.nix"
echo "Fetching latest claude-code version from Homebrew cask..."
# Fetch the cask file
CASK_CONTENT=$(curl -fsSL "$CASK_URL")
# Extract version (format: version "X.Y.Z")
NEW_VERSION=$(echo "$CASK_CONTENT" | grep -m1 'version' | sed -E 's/.*version "([^"]+)".*/\1/')
# Extract SHA256 hashes (be specific to match sha256 lines only)
SHA_ARM=$(echo "$CASK_CONTENT" | grep 'sha256 arm:' | sed -E 's/.*"([a-f0-9]{64})".*/\1/')
SHA_X86_64=$(echo "$CASK_CONTENT" | grep 'x86_64:' | sed -E 's/.*"([a-f0-9]{64})".*/\1/')
SHA_X86_64_LINUX=$(echo "$CASK_CONTENT" | grep 'x86_64_linux:' | sed -E 's/.*"([a-f0-9]{64})".*/\1/')
SHA_ARM64_LINUX=$(echo "$CASK_CONTENT" | grep 'arm64_linux:' | sed -E 's/.*"([a-f0-9]{64})".*/\1/')
# Get current version
CURRENT_VERSION=$(grep -m1 'version = ' "$NIX_FILE" | sed -E 's/.*version = "([^"]+)".*/\1/')
# Validate extracted data
if [ -z "$NEW_VERSION" ] || [ -z "$SHA_ARM" ] || [ -z "$SHA_X86_64" ] || [ -z "$SHA_X86_64_LINUX" ] || [ -z "$SHA_ARM64_LINUX" ]; then
echo -e "${RED}Error: Failed to extract all required values from Homebrew cask${NC}"
echo "Version: $NEW_VERSION"
echo "ARM: $SHA_ARM"
echo "x86_64: $SHA_X86_64"
echo "x86_64_linux: $SHA_X86_64_LINUX"
echo "arm64_linux: $SHA_ARM64_LINUX"
exit 1
fi
# Check if update is needed
if [ "$CURRENT_VERSION" = "$NEW_VERSION" ]; then
echo -e "${GREEN}Already up to date: $CURRENT_VERSION${NC}"
exit 0
fi
echo -e "${YELLOW}Updating from $CURRENT_VERSION to $NEW_VERSION${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}DRY RUN - No changes will be made${NC}"
echo ""
echo "Would update:"
echo " Version: $CURRENT_VERSION -> $NEW_VERSION"
echo " aarch64-darwin SHA: $SHA_ARM"
echo " x86_64-darwin SHA: $SHA_X86_64"
echo " x86_64-linux SHA: $SHA_X86_64_LINUX"
echo " aarch64-linux SHA: $SHA_ARM64_LINUX"
exit 0
fi
# Update version
sed -i.tmp "s/version = \".*\";/version = \"$NEW_VERSION\";/" "$NIX_FILE"
# Update SHA256 hashes using awk for more reliable parsing
awk -v sha_arm="$SHA_ARM" -v sha_x86="$SHA_X86_64" -v sha_x86_linux="$SHA_X86_64_LINUX" -v sha_arm_linux="$SHA_ARM64_LINUX" '
/aarch64-darwin = {/ { in_arm = 1 }
/x86_64-darwin = {/ { in_x86 = 1 }
/x86_64-linux = {/ { in_x86_linux = 1 }
/aarch64-linux = {/ { in_arm_linux = 1 }
/};/ {
in_arm = 0
in_x86 = 0
in_x86_linux = 0
in_arm_linux = 0
}
/sha256 = / {
if (in_arm) {
sub(/sha256 = ".*";/, "sha256 = \"" sha_arm "\";")
} else if (in_x86) {
sub(/sha256 = ".*";/, "sha256 = \"" sha_x86 "\";")
} else if (in_x86_linux) {
sub(/sha256 = ".*";/, "sha256 = \"" sha_x86_linux "\";")
} else if (in_arm_linux) {
sub(/sha256 = ".*";/, "sha256 = \"" sha_arm_linux "\";")
}
}
{ print }
' "$NIX_FILE" > "$NIX_FILE.new"
mv "$NIX_FILE.new" "$NIX_FILE"
# Clean up temp files
rm -f "$NIX_FILE.tmp"
echo -e "${GREEN}Successfully updated to version $NEW_VERSION${NC}"
echo ""
echo "Updated SHA256 hashes:"
echo " aarch64-darwin: $SHA_ARM"
echo " x86_64-darwin: $SHA_X86_64"
echo " x86_64-linux: $SHA_X86_64_LINUX"
echo " aarch64-linux: $SHA_ARM64_LINUX"
echo ""
echo "Next steps:"
echo " 1. Review changes: git diff $NIX_FILE"
echo " 2. Test build: NIXPKGS_ALLOW_UNFREE=1 nix-build -E 'with import <nixpkgs> { config.allowUnfree = true; }; callPackage ./packages/claude-code {}'"
echo " 3. Verify version: ./result/bin/claude --version"
echo " 4. Commit: git add $NIX_FILE && git commit -m 'claude-code: Update to version $NEW_VERSION'"

View File

@@ -1,4 +1,11 @@
{ pkgs, ... }:
{
vulkanHDRLayer = pkgs.callPackage ./vulkan-hdr-layer {};
tea-rbw = pkgs.callPackage ./tea-rbw { };
app-launcher-server = pkgs.callPackage ./app-launcher-server { };
claude-code = pkgs.callPackage ./claude-code { };
mcrcon-rbw = pkgs.callPackage ./mcrcon-rbw { };
rclone-torbox-setup = pkgs.callPackage ./rclone-torbox-setup { };
pi-coding-agent = pkgs.callPackage ./pi-coding-agent { };
nextcloud-talk-desktop = pkgs.callPackage ./nextcloud-talk-desktop { };
opencode = pkgs.callPackage ./opencode { };
}

View File

@@ -0,0 +1,40 @@
{ pkgs, ... }:
pkgs.writeShellScriptBin "mcrcon" ''
set -euo pipefail
# Configuration - can be overridden with environment variables
MINECRAFT_RCON_HOST="''${MCRCON_HOST:-10.0.0.165}"
MINECRAFT_RCON_PORT="''${MCRCON_PORT:-25575}"
RBW_ENTRY="minecraft-rcon"
# Check if rbw is available
if ! command -v rbw &> /dev/null; then
echo "Error: rbw is not available. Please ensure rbw is installed and configured."
exit 1
fi
# Retrieve password from Bitwarden
if ! MCRCON_PASS=$(rbw get "$RBW_ENTRY" 2>/dev/null); then
echo "Error: Failed to retrieve RCON password from rbw entry '$RBW_ENTRY'"
echo "Please ensure the entry exists in Bitwarden and rbw is synced."
echo ""
echo "To create the entry:"
echo " 1. Add 'minecraft-rcon' to Bitwarden with the RCON password"
echo " 2. Run 'rbw sync' to refresh the local cache"
exit 1
fi
# Export for mcrcon
export MCRCON_HOST="$MINECRAFT_RCON_HOST"
export MCRCON_PORT="$MINECRAFT_RCON_PORT"
export MCRCON_PASS
# If no arguments provided, start interactive terminal mode
if [[ $# -eq 0 ]]; then
exec ${pkgs.mcrcon}/bin/mcrcon -t
fi
# Execute mcrcon with all provided arguments
exec ${pkgs.mcrcon}/bin/mcrcon "$@"
''

View File

@@ -0,0 +1,60 @@
# Patched Nextcloud Talk Desktop with Wayland screen sharing support
# Applies the core change from upstream draft PR #1022:
# https://github.com/nextcloud/talk-desktop/pull/1022
#
# Patches the webpack bundle in app.asar to add setDisplayMediaRequestHandler
# with useSystemPicker: true, enabling native PipeWire/portal-based
# screen sharing on Wayland (Sway, Hyprland, etc.)
{ lib
, nextcloud-talk-desktop
, nodejs
, asar
}:
nextcloud-talk-desktop.overrideAttrs (old: {
pname = "nextcloud-talk-desktop-patched";
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [ asar nodejs ];
# Patch the asar after the main installPhase creates the output
postFixup = (old.postFixup or "") + ''
echo "Patching app.asar for Wayland screen sharing..."
ASAR_PATH="$out/opt/Nextcloud Talk-linux-x64/resources/app.asar"
WORK=$(mktemp -d)
asar extract "$ASAR_PATH" "$WORK/app"
# In the webpack bundle:
# session = l, desktopCapturer = a, app = n
# We inject setDisplayMediaRequestHandler right after n.whenReady().then((async()=>{
# useSystemPicker: true makes Electron use the native system picker
# (PipeWire/xdg-desktop-portal on Wayland)
node -e "
const fs = require('fs');
const p = '$WORK/app/.webpack/main/index.js';
let c = fs.readFileSync(p, 'utf8');
if (c.includes('setDisplayMediaRequestHandler')) {
console.log('Already patched');
process.exit(0);
}
const marker = 'n.whenReady().then((async()=>{';
const idx = c.indexOf(marker);
if (idx === -1) {
console.error('ERROR: Could not find whenReady marker in webpack bundle');
process.exit(1);
}
// Inject after the marker
const injection = 'l.defaultSession.setDisplayMediaRequestHandler(async(e,t)=>{const s=await a.getSources({types:[\"screen\",\"window\"]});s.length>0?t({video:s[0]}):t({})},{useSystemPicker:!0});';
c = c.slice(0, idx + marker.length) + injection + c.slice(idx + marker.length);
fs.writeFileSync(p, c, 'utf8');
console.log('Successfully patched main bundle for Wayland screen sharing');
"
asar pack "$WORK/app" "$ASAR_PATH"
rm -rf "$WORK"
'';
})

View File

@@ -0,0 +1,82 @@
{
lib,
stdenv,
fetchzip,
patchelf,
glibc,
}:
let
version = "1.4.0";
srcs = {
aarch64-darwin = {
url = "https://github.com/anomalyco/opencode/releases/download/v${version}/opencode-darwin-arm64.zip";
sha256 = "0m97j2vln8yhhvnsjl92phx6dac24y7hgh75csmbkbhawkz9xm4l";
};
x86_64-darwin = {
url = "https://github.com/anomalyco/opencode/releases/download/v${version}/opencode-darwin-x64.zip";
sha256 = "17n04j06pdc2raxjm91y6p87gwpnra0liabpbjwdmyd1iqgqv0q8";
};
x86_64-linux = {
url = "https://github.com/anomalyco/opencode/releases/download/v${version}/opencode-linux-x64.tar.gz";
sha256 = "16117lwfj2lb8wjbq5cyf77vhi52ada5ys3212hjqw3qw3wrcc0r";
};
aarch64-linux = {
url = "https://github.com/anomalyco/opencode/releases/download/v${version}/opencode-linux-arm64.tar.gz";
sha256 = "06lvm1qiji74xdd3psqn6lwxak65gqsbmkib1pjb4n65f9246jwm";
};
};
src =
srcs.${stdenv.hostPlatform.system} or (throw "Unsupported system: ${stdenv.hostPlatform.system}");
in
stdenv.mkDerivation {
pname = "opencode";
inherit version;
src = fetchzip {
inherit (src) url sha256;
};
# Bun standalone binaries have JS code appended after the ELF sections
# stripping/patching would remove or corrupt this appended data
dontStrip = true;
dontPatchELF = true;
nativeBuildInputs = lib.optionals stdenv.isLinux [ patchelf ];
installPhase = ''
runHook preInstall
install -Dm755 $src/opencode $out/bin/opencode
runHook postInstall
'';
# Manually patch the interpreter for bun standalone binaries on Linux
postFixup = lib.optionalString stdenv.isLinux ''
interpreter="${glibc}/lib/${
if stdenv.hostPlatform.system == "aarch64-linux" then
"ld-linux-aarch64.so.1"
else
"ld-linux-x86-64.so.2"
}"
patchelf --set-interpreter "$interpreter" $out/bin/opencode
'';
meta = with lib; {
description = "Terminal-based AI coding assistant";
homepage = "https://opencode.ai";
license = licenses.mit;
maintainers = [ ];
platforms = [
"aarch64-darwin"
"x86_64-darwin"
"x86_64-linux"
"aarch64-linux"
];
mainProgram = "opencode";
};
}

148
packages/opencode/update.sh Executable file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env bash
set -euo pipefail
DRY_RUN=false
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run|-n)
DRY_RUN=true
shift
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --dry-run, -n Show what would be updated without making changes"
echo " --help, -h Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
NIX_FILE="$REPO_ROOT/packages/opencode/default.nix"
echo "Fetching latest opencode version from GitHub API..."
RELEASE_INFO=$(curl -fsSL https://api.github.com/repos/anomalyco/opencode/releases/latest)
NEW_VERSION=$(echo "$RELEASE_INFO" | jq -r '.tag_name' | sed 's/^v//')
if [ -z "$NEW_VERSION" ] || [ "$NEW_VERSION" = "null" ]; then
echo -e "${RED}Error: Failed to fetch version from GitHub API${NC}"
exit 1
fi
CURRENT_VERSION=$(grep -m1 'version = ' "$NIX_FILE" | sed -E 's/.*version = "([^"]+)".*/\1/')
if [ "$CURRENT_VERSION" = "$NEW_VERSION" ]; then
echo -e "${GREEN}Already up to date: $CURRENT_VERSION${NC}"
exit 0
fi
echo -e "${YELLOW}Updating from $CURRENT_VERSION to $NEW_VERSION${NC}"
# Compute SHA256 hashes for each platform
# fetchzip hashes the unpacked directory, so we need to extract and hash
compute_unpacked_hash() {
local url="$1"
local ext="$2"
local tmpdir=$(mktemp -d)
local archive="/tmp/opencode-archive.$ext"
curl -fsSL "$url" -o "$archive"
if [ "$ext" = "zip" ]; then
(cd "$tmpdir" && unzip -q "$archive")
else
(cd "$tmpdir" && tar xzf "$archive")
fi
local sri_hash=$(nix hash path "$tmpdir")
local nix32_hash=$(nix hash convert --hash-algo sha256 --to nix32 "$sri_hash")
rm -rf "$tmpdir" "$archive"
echo "$nix32_hash"
}
echo "Computing SHA256 hashes (this may take a moment)..."
SHA_DARWIN_ARM=$(compute_unpacked_hash "https://github.com/anomalyco/opencode/releases/download/v${NEW_VERSION}/opencode-darwin-arm64.zip" "zip")
echo " aarch64-darwin: $SHA_DARWIN_ARM"
SHA_DARWIN_X64=$(compute_unpacked_hash "https://github.com/anomalyco/opencode/releases/download/v${NEW_VERSION}/opencode-darwin-x64.zip" "zip")
echo " x86_64-darwin: $SHA_DARWIN_X64"
SHA_LINUX_X64=$(compute_unpacked_hash "https://github.com/anomalyco/opencode/releases/download/v${NEW_VERSION}/opencode-linux-x64.tar.gz" "tar.gz")
echo " x86_64-linux: $SHA_LINUX_X64"
SHA_LINUX_ARM64=$(compute_unpacked_hash "https://github.com/anomalyco/opencode/releases/download/v${NEW_VERSION}/opencode-linux-arm64.tar.gz" "tar.gz")
echo " aarch64-linux: $SHA_LINUX_ARM64"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}DRY RUN - No changes will be made${NC}"
echo ""
echo "Would update:"
echo " Version: $CURRENT_VERSION -> $NEW_VERSION"
echo " aarch64-darwin SHA: $SHA_DARWIN_ARM"
echo " x86_64-darwin SHA: $SHA_DARWIN_X64"
echo " x86_64-linux SHA: $SHA_LINUX_X64"
echo " aarch64-linux SHA: $SHA_LINUX_ARM64"
exit 0
fi
# Update version
sed -i.tmp "s/version = \".*\";/version = \"$NEW_VERSION\";/" "$NIX_FILE"
# Update SHA256 hashes using awk
awk -v sha_arm="$SHA_DARWIN_ARM" -v sha_x64="$SHA_DARWIN_X64" -v sha_linux_x64="$SHA_LINUX_X64" -v sha_linux_arm="$SHA_LINUX_ARM64" '
/aarch64-darwin = {/ { in_arm = 1 }
/x86_64-darwin = {/ { in_x64 = 1; in_arm = 0 }
/x86_64-linux = {/ { in_linux_x64 = 1; in_x64 = 0 }
/aarch64-linux = {/ { in_linux_arm = 1; in_linux_x64 = 0 }
/};/ {
in_arm = 0
in_x64 = 0
in_linux_x64 = 0
in_linux_arm = 0
}
/sha256 = / {
if (in_arm) {
sub(/sha256 = ".*";/, "sha256 = \"" sha_arm "\";")
} else if (in_x64) {
sub(/sha256 = ".*";/, "sha256 = \"" sha_x64 "\";")
} else if (in_linux_x64) {
sub(/sha256 = ".*";/, "sha256 = \"" sha_linux_x64 "\";")
} else if (in_linux_arm) {
sub(/sha256 = ".*";/, "sha256 = \"" sha_linux_arm "\";")
}
}
{ print }
' "$NIX_FILE" > "$NIX_FILE.new"
mv "$NIX_FILE.new" "$NIX_FILE"
rm -f "$NIX_FILE.tmp"
echo -e "${GREEN}Successfully updated to version $NEW_VERSION${NC}"
echo ""
echo "Updated SHA256 hashes:"
echo " aarch64-darwin: $SHA_DARWIN_ARM"
echo " x86_64-darwin: $SHA_DARWIN_X64"
echo " x86_64-linux: $SHA_LINUX_X64"
echo " aarch64-linux: $SHA_LINUX_ARM64"
echo ""
echo "Next steps:"
echo " 1. Review changes: git diff $NIX_FILE"
echo " 2. Test build: nix build .#custom-opencode"
echo " 3. Verify version: ./result/bin/opencode --version"
echo " 4. Commit: git add $NIX_FILE && git commit -m 'opencode: Update to version $NEW_VERSION'"

View File

@@ -0,0 +1,79 @@
{ lib
, stdenv
, fetchurl
, patchelf
, glibc
, makeWrapper
}:
let
version = "0.55.4";
srcs = {
aarch64-darwin = {
url = "https://github.com/badlogic/pi-mono/releases/download/v${version}/pi-darwin-arm64.tar.gz";
sha256 = "0vsav9frvnzskk6p6j60i7klrs3m8lphhyi4c39mv2mvhpm8fkl5";
};
x86_64-darwin = {
url = "https://github.com/badlogic/pi-mono/releases/download/v${version}/pi-darwin-x64.tar.gz";
sha256 = "1377rvhsiiww1bbpgv2v46fjm7iz2smmh8g2yhm28kbsq3gwvvr0";
};
x86_64-linux = {
url = "https://github.com/badlogic/pi-mono/releases/download/v${version}/pi-linux-x64.tar.gz";
sha256 = "1wnfwnkfq5ffz6wyqyhciv4lz06bpxims0hv0dlhz0f9vliyc1md";
};
aarch64-linux = {
url = "https://github.com/badlogic/pi-mono/releases/download/v${version}/pi-linux-arm64.tar.gz";
sha256 = "00fp37hgjl40kc59jfpv189i7np53ymm037hvds6k9y2sz818wjy";
};
};
src = srcs.${stdenv.hostPlatform.system} or (throw "Unsupported system: ${stdenv.hostPlatform.system}");
in stdenv.mkDerivation {
pname = "pi-coding-agent";
inherit version;
src = fetchurl {
inherit (src) url sha256;
};
sourceRoot = "pi";
# Bun standalone binaries have JS code appended after the ELF sections
dontStrip = true;
dontPatchELF = true;
nativeBuildInputs = [ makeWrapper ]
++ lib.optionals stdenv.isLinux [ patchelf ];
installPhase = ''
runHook preInstall
# Install the full pi directory structure (binary + supporting files)
mkdir -p $out/lib/pi-coding-agent
cp -r . $out/lib/pi-coding-agent/
# Create bin wrapper that runs the binary from its lib directory
# (pi expects supporting files like themes and wasm relative to itself)
mkdir -p $out/bin
makeWrapper $out/lib/pi-coding-agent/pi $out/bin/pi
runHook postInstall
'';
# Manually patch the interpreter for bun standalone binaries on Linux
postFixup = lib.optionalString stdenv.isLinux ''
interpreter="${glibc}/lib/${if stdenv.hostPlatform.system == "aarch64-linux" then "ld-linux-aarch64.so.1" else "ld-linux-x86-64.so.2"}"
patchelf --set-interpreter "$interpreter" $out/lib/pi-coding-agent/pi
'';
meta = with lib; {
description = "Minimal terminal coding agent with extensible tools and session management";
homepage = "https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent";
license = licenses.mit;
maintainers = [ ];
platforms = [ "aarch64-darwin" "x86_64-darwin" "x86_64-linux" "aarch64-linux" ];
mainProgram = "pi";
};
}

View File

@@ -0,0 +1,98 @@
{ pkgs, ... }:
pkgs.writeShellScriptBin "rclone-torbox-setup" ''
set -euo pipefail
# Default values
RBW_ENTRY="''${1:-torbox}"
ENV_FILE="''${2:-/etc/rclone/torbox.env}"
usage() {
echo "Usage: rclone-torbox-setup [rbw-entry] [env-file]"
echo ""
echo "Sets up rclone credentials for TorBox WebDAV mount."
echo "Retrieves password from rbw (Bitwarden), obscures it for rclone,"
echo "and writes it to the environment file for the systemd service."
echo ""
echo "Arguments:"
echo " rbw-entry Name of the Bitwarden entry containing the password (default: torbox)"
echo " env-file Path to write the environment file (default: /etc/rclone/torbox.env)"
echo ""
echo "The Bitwarden entry should contain your TorBox password as the password field."
echo ""
echo "Example:"
echo " rclone-torbox-setup torbox-password /etc/rclone/torbox.env"
exit 1
}
if [[ "''${1:-}" == "-h" ]] || [[ "''${1:-}" == "--help" ]]; then
usage
fi
echo "rclone TorBox credential setup"
echo "=============================="
echo ""
# Check if rbw is available
if ! command -v rbw &> /dev/null; then
echo "Error: rbw is not available. Please ensure rbw is installed and configured."
exit 1
fi
# Check if rclone is available
if ! command -v rclone &> /dev/null; then
echo "Error: rclone is not available. Please ensure rclone is installed."
exit 1
fi
echo "Retrieving password from rbw entry: $RBW_ENTRY"
# Retrieve password from Bitwarden
if ! TORBOX_PASS=$(rbw get "$RBW_ENTRY" 2>/dev/null); then
echo ""
echo "Error: Failed to retrieve password from rbw entry '$RBW_ENTRY'"
echo ""
echo "Please ensure:"
echo " 1. The entry '$RBW_ENTRY' exists in Bitwarden"
echo " 2. rbw is unlocked: rbw unlock"
echo " 3. rbw is synced: rbw sync"
echo ""
echo "To create the entry in Bitwarden:"
echo " - Name: $RBW_ENTRY"
echo " - Password: Your TorBox password"
exit 1
fi
echo "Password retrieved successfully"
# Obscure the password for rclone
echo "Obscuring password for rclone..."
if ! OBSCURED_PASS=$(echo -n "$TORBOX_PASS" | rclone obscure -); then
echo "Error: Failed to obscure password with rclone"
exit 1
fi
# Create the directory if needed (requires sudo)
ENV_DIR=$(dirname "$ENV_FILE")
if [[ ! -d "$ENV_DIR" ]]; then
echo "Creating directory $ENV_DIR (requires sudo)..."
sudo mkdir -p "$ENV_DIR"
fi
# Write the environment file
echo "Writing environment file to $ENV_FILE (requires sudo)..."
echo "RCLONE_WEBDAV_PASS=$OBSCURED_PASS" | sudo tee "$ENV_FILE" > /dev/null
sudo chmod 600 "$ENV_FILE"
echo ""
echo "Setup complete!"
echo ""
echo "The environment file has been created at: $ENV_FILE"
echo "The rclone-mount-torbox systemd service will use this file."
echo ""
echo "To activate the mount after NixOS rebuild:"
echo " sudo systemctl start rclone-mount-torbox"
echo ""
echo "To check status:"
echo " sudo systemctl status rclone-mount-torbox"
''

View File

@@ -0,0 +1,58 @@
{ pkgs, ... }:
pkgs.writeShellScriptBin "tea" ''
set -euo pipefail
# Check if tea config directory exists and has authentication
TEA_CONFIG_DIR="''${XDG_CONFIG_HOME:-$HOME/.config}/tea"
TEA_CONFIG_FILE="$TEA_CONFIG_DIR/config.yml"
# Function to setup tea authentication with rbw
setup_tea_auth() {
echo "Tea authentication not found. Setting up with rbw..."
# Check if rbw is available
if ! command -v rbw &> /dev/null; then
echo "Error: rbw is not available. Please ensure rbw is installed and configured."
exit 1
fi
# Try to get the token from rbw
echo "Attempting to retrieve Gitea token from rbw..."
echo "Please enter the rbw entry name for your Gitea token:"
read -r rbw_entry
if ! token=$(rbw get "$rbw_entry" 2>/dev/null); then
echo "Error: Failed to retrieve token from rbw entry '$rbw_entry'"
echo "Available rbw entries:"
rbw list 2>/dev/null || echo "Failed to list rbw entries"
exit 1
fi
# Prompt for Gitea URL
echo "Please enter your Gitea URL (e.g., https://git.example.com):"
read -r gitea_url
# Create tea config directory if it doesn't exist
mkdir -p "$TEA_CONFIG_DIR"
# Setup tea login
if ! ${pkgs.tea}/bin/tea login add --name "default" --url "$gitea_url" --token "$token"; then
echo "Error: Failed to setup tea authentication"
exit 1
fi
echo "Tea authentication setup complete!"
}
# Check if tea is already configured
if [[ ! -f "$TEA_CONFIG_FILE" ]]; then
setup_tea_auth
elif ! ${pkgs.tea}/bin/tea whoami &>/dev/null; then
echo "Tea config exists but authentication failed. Re-running setup..."
setup_tea_auth
fi
# Execute tea with all provided arguments
exec ${pkgs.tea}/bin/tea "$@"
''

View File

@@ -1,34 +0,0 @@
{ lib, stdenv, fetchFromGitHub, meson, pkg-config, vulkan-loader, ninja, writeText, vulkan-headers, vulkan-utility-libraries, jq, libX11, libXrandr, libxcb, wayland, wayland-scanner }:
stdenv.mkDerivation rec {
pname = "vulkan-hdr-layer";
version = "63d2eec";
src = (fetchFromGitHub {
owner = "Zamundaaa";
repo = "VK_hdr_layer";
rev = "869199cd2746e7f69cf19955153080842b6dacfc";
fetchSubmodules = true;
hash = "sha256-xfVYI+Aajmnf3BTaY2Ysg5fyDO6SwDFGyU0L+F+E3is=";
}).overrideAttrs (_: {
GIT_CONFIG_COUNT = 1;
GIT_CONFIG_KEY_0 = "url.https://github.com/.insteadOf";
GIT_CONFIG_VALUE_0 = "git@github.com:";
});
nativeBuildInputs = [ vulkan-headers meson ninja pkg-config jq ];
buildInputs = [ vulkan-headers vulkan-loader vulkan-utility-libraries libX11 libXrandr libxcb wayland wayland-scanner ];
# Help vulkan-loader find the validation layers
setupHook = writeText "setup-hook" ''
addToSearchPath XDG_DATA_DIRS @out@/share
'';
meta = with lib; {
description = "Layers providing Vulkan HDR";
homepage = "https://github.com/Zamundaaa/VK_hdr_layer";
platforms = platforms.linux;
license = licenses.mit;
};
}

82
renovate.json Normal file
View File

@@ -0,0 +1,82 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"timezone": "America/Los_Angeles",
"gitAuthor": "Renovate Bot <renovate@ogle.fyi>",
"nix": {
"enabled": true
},
"github-actions": {
"managerFilePatterns": [
"/.gitea/workflows/.+\\.ya?ml$/"
]
},
"lockFileMaintenance": {
"enabled": true,
"schedule": [
"after 5pm and before 7pm on Saturday"
]
},
"dependencyDashboard": true,
"dependencyDashboardAutoclose": false,
"dependencyDashboardTitle": "NixOS Configs Dependency Dashboard",
"packageRules": [
{
"description": "Group all GitHub Actions updates",
"matchManagers": [
"github-actions"
],
"groupName": "github-actions"
},
{
"description": "Group stable NixOS ecosystem inputs",
"matchManagers": [
"nix"
],
"groupName": "nix-stable-ecosystem",
"matchPackageNames": [
"/^nixpkgs$/",
"/^home-manager$/",
"/^nix-darwin$/"
],
"schedule": [
"after 5pm and before 7pm on Saturday"
]
},
{
"description": "Group unstable NixOS ecosystem inputs",
"matchManagers": [
"nix"
],
"groupName": "nix-unstable-ecosystem",
"matchPackageNames": [
"/nixpkgs-unstable/",
"/home-manager-unstable/"
],
"schedule": [
"after 5pm and before 7pm on Saturday"
]
},
{
"description": "nixpkgs-qt updates on Saturday (staggered from main ecosystem)",
"matchManagers": [
"nix"
],
"matchPackageNames": [
"/nixpkgs-qt/"
],
"schedule": [
"after 7pm and before 9pm on Saturday"
]
},
{
"description": "Ignore private Gitea inputs (handle separately)",
"matchManagers": [
"nix"
],
"enabled": false,
"matchPackageNames": [
"/google-cookie-retrieval/"
]
}
]
}

View File

@@ -13,6 +13,7 @@ in
config = mkIf cfg.enable
{
environment.systemPackages = with pkgs; [
easyeffects
paprefs
pavucontrol
pulsemixer
@@ -20,17 +21,11 @@ in
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
services.pulseaudio = {
package = pkgs.pulseaudioFull;
extraConfig = ''
load-module module-combine-sink
load-module module-switch-on-connect
'';
};
services.squeezelite = {
#enable = true;
pulseAudio = true;

View File

@@ -102,6 +102,11 @@ in
};
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [
btrfs-progs
compsize
];
# Generate fileSystems configuration from mountpoints
fileSystems = mkMerge (flatten (mapAttrsToList (device: fsCfg:
mapAttrsToList (mountpoint: mountCfg:
@@ -165,4 +170,4 @@ in
};
}) cfg.filesystems);
};
}
}

43
roles/common.nix Normal file
View File

@@ -0,0 +1,43 @@
# Common configuration shared between NixOS and Darwin
{ lib, pkgs, ... }:
{
config = {
time.timeZone = "America/Los_Angeles";
environment.systemPackages = with pkgs; [
git
glances
pciutils
tree
usbutils
vim
] ++ lib.optionals pkgs.stdenv.isLinux [
ghostty.terminfo # So tmux works when SSH'ing from ghostty
];
nix = {
package = pkgs.nix;
settings = {
experimental-features = [ "nix-command" "flakes" ];
max-jobs = "auto";
trusted-users = [ "johno" ];
substituters = [
"http://john-endesktop.oglehome:5000"
];
trusted-public-keys = [
"harmonia.john-endesktop:1iGr4xZrsR7WtXOlPCgFF3LcODYBpu+B3TS54MyBn4M="
];
fallback = true;
connect-timeout = 5;
};
gc = {
automatic = true;
options = "--delete-older-than 10d";
};
};
nixpkgs.config.allowUnfree = true;
};
}

85
roles/darwin.nix Normal file
View File

@@ -0,0 +1,85 @@
{ config, lib, pkgs, ... }:
with lib;
let
# Extract the set-environment path that nix-darwin generates
setEnvironmentPath = "${config.system.build.setEnvironment}";
in
{
imports = [
./common.nix
];
config = {
# Salt manages /etc/bashrc, /etc/zshrc, /etc/zshenv
# nix-darwin writes to .local variants for nix-specific configuration
# Disable nix-darwin from managing the main shell files
environment.etc."bashrc".enable = false;
environment.etc."zshrc".enable = false;
environment.etc."zshenv".enable = false;
environment.etc."zprofile".enable = false;
# Create .local files with nix environment setup
environment.etc."bash.local".text = ''
# Nix environment setup
if [ -z "$__NIX_DARWIN_SET_ENVIRONMENT_DONE" ]; then
. ${setEnvironmentPath}
fi
'';
environment.etc."zshrc.local".text = ''
# Nix environment setup (already done in zshenv.local)
'';
environment.etc."zshenv.local".text = ''
# Nix environment setup
if [[ -o rcs ]]; then
if [ -z "''${__NIX_DARWIN_SET_ENVIRONMENT_DONE-}" ]; then
. ${setEnvironmentPath}
fi
# Tell zsh how to find installed completions
for p in ''${(z)NIX_PROFILES}; do
fpath=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions $fpath)
done
fi
'';
# System preferences
system.defaults = {
# Custom keyboard shortcuts
CustomUserPreferences = {
"com.apple.symbolichotkeys" = {
AppleSymbolicHotKeys = {
# Screenshot - Capture entire screen (Cmd+Ctrl+3)
"28" = {
enabled = true;
value = {
parameters = [ 51 20 1310720 ];
type = "standard";
};
};
# Screenshot - Capture selected portion (Cmd+Ctrl+4)
"30" = {
enabled = true;
value = {
parameters = [ 52 21 1310720 ];
type = "standard";
};
};
# Screenshot - Show screenshot toolbar (Cmd+Ctrl+5)
"184" = {
enabled = true;
value = {
parameters = [ 53 23 1310720 ];
type = "standard";
};
};
};
};
};
};
};
}

View File

@@ -4,16 +4,23 @@ with lib;
{
imports = [
./common.nix
./audio
./bluetooth
./btrfs
./desktop
./k3s-node
./kodi
./nfs-mounts
./plasma-bigscreen
./nvidia
./printing
./rclone-mount
./remote-build
./spotifyd
./users
./virtualisation
./wireguard
];
config = {
@@ -29,7 +36,6 @@ with lib;
LC_TELEPHONE = "en_US.UTF-8";
LC_TIME = "en_US.UTF-8";
};
time.timeZone = "America/Los_Angeles";
services.xserver.xkb = {
layout = "us";
@@ -47,42 +53,7 @@ with lib;
# Enable the OpenSSH daemon.
services.openssh.enable = true;
environment.systemPackages = with pkgs; [
git
glances
pciutils
tree
usbutils
vim
];
nix = {
package = pkgs.nix;
# distributedBuilds = true;
# buildMachines = [{
# hostName = "z790prors.oglehome";
# system = "x86_64-linux";
# protocol = "ssh-ng";
# sshUser = "johno";
# sshKey = "/root/.ssh/id_ed25519";
# maxJobs = 3;
# speedFactor = 2;
# }];
settings = {
experimental-features = [ "nix-command" "flakes" ];
max-jobs = "auto";
trusted-users = [ "johno" ];
substituters = [
];
};
gc = {
automatic = true;
randomizedDelaySec = "14m";
options = "--delete-older-than 10d";
};
};
nixpkgs.config.allowUnfree = true;
# NixOS-specific gc option (not available on Darwin)
nix.gc.randomizedDelaySec = "14m";
};
}

View File

@@ -9,7 +9,9 @@ with lib;
x11 = mkOption { type = types.bool; default = false; description = "Enable X11 support."; };
wayland = mkOption { type = types.bool; default = false; description = "Enable Wayland support."; };
kde = mkOption { type = types.bool; default = false; description = "Enable KDE."; };
gaming = mkOption { type = types.bool; default = false; description = "Enable gaming support."; };
gaming = {
enable = mkOption { type = types.bool; default = false; description = "Enable gaming support."; };
};
sddm = mkOption { type = types.bool; default = false; description = "Enable SDDM greeter."; };
};

View File

@@ -6,13 +6,32 @@ let
cfg = config.roles.desktop;
in
{
config = mkIf (cfg.enable && cfg.gaming) {
environment.systemPackages = with pkgs; [
steam
lutris
moonlight
];
config = mkMerge [
(mkIf (cfg.enable && cfg.gaming.enable) {
environment.systemPackages = with pkgs; [
lutris
moonlight
# Possibly other gaming specific services or settings
};
# Emulators
dolphin-emu
# Re-enabled in 25.11 after binary build was fixed
dolphin-emu-primehack
# Experimenting with just using the steam version + downloading
# indiviudal cores
#retroarch-full
ryubing
yarg
];
programs.steam = {
enable = true;
remotePlay.openFirewall = true;
dedicatedServer.openFirewall = true;
localNetworkGameTransfers.openFirewall = true;
};
})
];
}

View File

@@ -16,5 +16,23 @@ in
programs.dconf.enable = true;
services.gnome.gnome-keyring.enable = true;
programs.kdeconnect.enable = true;
# XDG Desktop Portal for default application handling
xdg.portal = {
enable = true;
wlr.enable = cfg.wayland; # xdg-desktop-portal-wlr for Sway screen sharing
extraPortals = with pkgs; [
kdePackages.xdg-desktop-portal-kde # For KDE application integration
xdg-desktop-portal-gtk # Fallback for GTK applications
];
config = {
common = {
default = "kde";
};
i3 = {
default = ["kde" "gtk"];
};
};
};
};
}

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