Compare commits

...

43 Commits

Author SHA1 Message Date
0xWheatyz 2624e68a7c feat(nixos): auto-start UxPlay as a user systemd service
Run UxPlay under graphical-session.target so the AirPlay receiver is
discoverable as soon as the GNOME session is up, instead of requiring
a manual `uxplay` invocation per login.

Constraint: UxPlay needs the active graphical session for display + PipeWire
Rejected: system-level service | runs before user session, no display/audio access
Rejected: GNOME autostart .desktop entry | no Restart-on-failure semantics
Confidence: high
Scope-risk: narrow
Directive: keep wantedBy on graphical-session.target (not default.target) so
  the unit only runs when a desktop session is live
Not-tested: behavior on fast user switching between two desktop sessions
2026-05-24 19:49:20 -04:00
0xWheatyz 4335687f48 feat(nixos): add iOS interop (localsend, uxplay, libimobiledevice)
Adds Mac-style iPhone interop to the GNOME host:
- LocalSend for AirDrop-style file transfer (programs.localsend
  opens TCP 53317)
- UxPlay AirPlay receiver with Avahi mDNS publishing so iOS Screen
  Mirroring discovers the host; opens UxPlay's TCP 7000/7001/7100
  and UDP 6000/6001/7011
- usbmuxd + libimobiledevice + ifuse so GVfs auto-mounts iPhone
  over USB in GNOME Files

Constraint: Continuity / Handoff / Universal Clipboard / iMessage
have no FOSS impl on Linux and are out of scope
Rejected: KDE Connect | iOS app cannot send SMS or share clipboard
  due to iOS sandbox, no parity with macOS
Rejected: shairport-sync AirPlay audio receiver | user opted out
Confidence: high
Scope-risk: narrow
Directive: Avahi publish.userServices = true is required for UxPlay
  discovery; do not narrow without retesting iOS Screen Mirroring
Not-tested: behavior when networking.firewall.enable is later
  flipped to true (currently disabled at line 128)
2026-05-24 19:34:58 -04:00
0xWheatyz 44cd77e46f chore(flake): update nixpkgs-unstable lock 2026-05-24 19:24:50 -04:00
0xWheatyz 5b66eb3049 chore(claude): enable onboarding flag and allow monitor tool 2026-05-24 19:24:47 -04:00
0xWheatyz 1eddde2bdb chore(flake): update nixpkgs lock 2026-05-19 17:52:17 -04:00
0xWheatyz 31c72186ad fix(ssh): use gitea.leeworks.dev hostname for git ssh config 2026-04-22 17:33:06 -04:00
0xWheatyz f566267663 chore: remove kicad package and environment variables 2026-04-22 17:32:49 -04:00
0xWheatyz 5c14b05c16 feat(claude): broaden allowed tool permissions in settings
Replace restrictive bash-only permissions with full tool access
including Read, Glob, Grep, WebFetch, WebSearch, Agent, and
NotebookEdit for a more capable Claude Code experience.
2026-04-21 18:59:14 -04:00
0xWheatyz f613917d4c chore: add gitignore for omc state and wireguard config
Ignore .omc/ directory (OMC internal state) and wg1.conf
(WireGuard config containing secrets).
2026-04-21 18:58:22 -04:00
0xWheatyz a765ea2844 fix(wireguard): resolve routing loop and update VPN endpoint
- Disable firewall to allow VPN traffic
- Switch DNS from VPN server (10.0.1.65) to Cloudflare (1.1.1.1)
- Use direct IP endpoint (69.48.243.22) instead of DNS hostname to
  prevent resolution failures when DNS routes through the tunnel
- Add pre/post routing rules to avoid routing loop by sending VPN
  endpoint traffic via the local gateway

Constraint: endpoint must be an IP, not hostname, to avoid DNS chicken-and-egg
Rejected: keep DNS hostname endpoint | fails when DNS resolves through tunnel
Confidence: high
Scope-risk: moderate
2026-04-21 18:52:53 -04:00
0xWheatyz 36dd8b8c34 feat(claude): add nodejs and OMC_PLUGIN_ROOT for oh-my-claudecode support
Adds nodejs to home packages (required by claude-code and OMC HUD/npm
plugins) and sets OMC_PLUGIN_ROOT env var pointing to the flake input.
2026-04-21 18:52:40 -04:00
0xWheatyz 977c15a57f feat(claude): deploy OMC agents and CLAUDE.md config via nix
Add agents directory from oh-my-claudecode flake input, include the
upstream OMC CLAUDE.md configuration block via builtins.readFile, and
add statusLine/env/teammateMode to the declarative settings.json.
2026-04-21 16:28:47 -04:00
0xWheatyz c348c97ea0 fix(claude): correct oh-my-claudecode skill path to avoid extra nesting
Skills were installed at ~/.claude/skills/oh-my-claudecode/<name>/SKILL.md
but Claude Code expects ~/.claude/skills/<name>/SKILL.md. Link the source
directly into the skills directory.
2026-04-20 23:53:04 -04:00
0xWheatyz 9d6017432d fix(kicad): use correct nixpkgs attribute path for footprint libraries
pkgs.kicad-libraries doesn't exist; the correct path is
pkgs.kicad-small.libraries.footprints.
2026-04-20 23:44:57 -04:00
0xWheatyz 9c9935b550 feat(kicad): add KICAD9_FOOTPRINT_DIR session variable for footprint libraries 2026-04-20 23:04:50 -04:00
0xWheatyz 5da4d36d25 feat(claude): add caveman and oh-my-claudecode skills via flake inputs
Declaratively manage Claude Code skills through home-manager by fetching
plugin repos as non-flake inputs and symlinking their skills directories.
2026-04-20 22:51:37 -04:00
0xWheatyz d83b03bbd8 fix(wireguard): exclude local 10.0.0.0/24 from tunnel to preserve LAN access
Replace 0.0.0.0/0 allowedIPs with CIDR blocks covering everything except
10.0.0.0/24, which conflicts with the building's local network. Also switch
endpoint back to hostname and remove unused tailscale service block.
2026-04-20 19:50:44 -04:00
0xWheatyz c371552dfd fix(wireguard): use static endpoint IP and drop manual route hooks
Replace hostname endpoint with resolved IP (69.48.243.22) so
wg-quick's native fwmark-based routing handles 0.0.0.0/0 correctly.
Remove preUp/postDown hooks that conflicted with wg-quick's own
policy routing.
2026-04-15 19:19:22 -04:00
0xWheatyz 8b5111e9a1 fix(wireguard): move endpoint route to preUp/postDown hooks
postUp runs after wg-quick has already rerouted all traffic through
the tunnel, making external DNS unreachable. Use preUp to add the
host route before routing changes, and postDown to clean up after.
2026-04-15 19:12:34 -04:00
0xWheatyz cca5c48725 fix(wireguard): use public DNS to resolve endpoint before tunnel is up
Query @1.1.1.1 explicitly in postUp/preDown hooks since the VPN DNS
(10.0.1.65) is unreachable before the tunnel establishes.
2026-04-15 19:08:13 -04:00
0xWheatyz b4e4036c41 fix(wireguard): resolve endpoint hostname to IP before adding route
ip route does not accept hostnames. Use dig to resolve
vpn.leeworks.dev to an IP address in postUp/preDown hooks.
2026-04-15 19:00:00 -04:00
0xWheatyz 3aab755e37 fix(wireguard): add endpoint host route and trust wg0 interface
Add postUp/preDown hooks to create a host route for the VPN endpoint
via the real gateway, preventing a routing loop when allowedIPs is
0.0.0.0/0. Also add wg0 to firewall trustedInterfaces.
2026-04-15 18:45:03 -04:00
0xWheatyz 6e361b197c feat(wireguard): update wg0 config to match ClusterVPN-Nixos.conf
- Address: 10.0.1.66/28 -> 10.0.1.67/32
- Add DNS 10.0.1.65
- AllowedIPs: restricted subnets -> 0.0.0.0/0 (full tunnel)
2026-04-15 18:40:53 -04:00
0xWheatyz 6fd0b18746 fix(home): move gtk3 to home.packages for FreeCAD GSettings schema
FreeCAD is a home-manager package, so its GSettings schema dependency
(org.gtk.Settings.FileChooser) must also be in home.packages for
home-manager to include it in XDG_DATA_DIRS. Having gtk3 only in
system packages left it invisible to the user session.
2026-04-05 19:36:33 -04:00
0xWheatyz 0c15063a58 fix(system): add gtk3 to system packages for GSettings file chooser schema
FreeCAD crashes with 'org.gtk.Settings.FileChooser is not installed'
because home-manager packages don't get their GSettings schemas compiled
into the system profile. Moving gtk3 to environment.systemPackages fixes this.
2026-04-05 19:28:53 -04:00
0xWheatyz 9056237e8a fix(portal): add xdg-desktop-portal-gtk for freecad file chooser
FreeCAD's save/open dialogs require the GTK portal backend for the
file chooser D-Bus service. The bare gtk3 package doesn't provide this,
so replace it with the portal package in the system config.
2026-04-05 18:54:03 -04:00
0xWheatyz 64b450bbe7 feat(wireguard): expand allowedIPs to include home network subnets
Add 192.168.1.0/24, 10.0.0.0/24, and 10.0.1.0/26 to route traffic
for the full home LAN through the WireGuard tunnel.
2026-04-05 14:09:16 -04:00
0xWheatyz 8eee1ab82f fix(home): add gtk3 to resolve freecad save-as crash
FreeCAD's file chooser requires the org.gtk.Settings.FileChooser
GSettings schema from GTK3, which was missing from XDG_DATA_DIRS.
2026-04-05 13:57:37 -04:00
0xWheatyz c133ff306e fix(flake): unpin nixpkgs-unstable to resolve broken claude-code package
Revert the nixpkgs-unstable pin to follow the branch again, as the
pinned revision shipped a broken package.
2026-04-04 02:12:42 -04:00
0xWheatyz 5a53ce36e9 feat(home): add freecad package 2026-04-04 02:12:36 -04:00
0xWheatyz 7c25228bc5 feat(yubikey): configure GPG signing key and disable GCR ssh-agent
Set actual GPG key ID for git commit signing, disable GCR ssh-agent
socket so gpg-agent handles SSH auth, and trim redundant setup steps
from yubikey guide.
2026-04-04 02:12:02 -04:00
0xWheatyz 1ca8cc8c57 feat(wireguard): add wg-quick VPN client configuration
Connect to home network VPN via vpn.leeworks.dev with wg0 interface
on the 10.0.1.64/28 subnet. Private key loaded from /etc/wireguard/private.key.
2026-04-04 01:53:45 -04:00
0xWheatyz 014216de6b feat(yubikey): add GPG agent, pcscd, and YubiKey support
Enable gpg-agent with SSH support and pinentry-gnome3, add
yubikey-manager and pcscd service, configure GPG with hardened
preferences and scdaemon, disable gnome-keyring SSH agent, and
prepare git signing configuration.
2026-03-31 21:25:35 -04:00
0xWheatyz 1ad1614509 docs(yubikey): add post-rebuild YubiKey setup guide
Step-by-step guide covering GPG key generation, subkey creation,
moving keys to YubiKey, SSH key deployment, and git signing setup.
2026-03-31 21:22:11 -04:00
0xWheatyz fa2ae3b86e chore(git): set pull strategy to merge by default 2026-03-28 11:57:03 -04:00
0xWheatyz 91faab704c fix(gnome): remove xwayland-native-scaling to prevent KiCad crashes
The experimental xwayland-native-scaling feature causes XWayland
connection loss when KiCad fullscreens on the second monitor,
crashing the application. Fractional scaling is retained via
scale-monitor-framebuffer.
2026-03-28 11:56:42 -04:00
0xWheatyz 8a091acccc chore(deps): update nixpkgs-unstable flake input 2026-03-16 18:58:26 -04:00
0xWheatyz 85b7d25486 feat(claude): add CLAUDE.md, commit command, and update permissions
Add home-manager managed CLAUDE.md with commit behavior instructions,
a /commit slash command for conventional commits workflow, and expand
allowed Bash permissions to include nix commands. Remove inline
customInstructions in favor of CLAUDE.md file.
2026-03-16 18:58:12 -04:00
0xWheatyz 1226603c7f chore(cleanup): removed some old software that is not used often 2026-03-15 23:31:46 -04:00
0xWheatyz bdbf892ea1 feat: auto-update Claude Code from nixpkgs-unstable
- Add nixpkgs-unstable input for latest Claude Code versions
- Remove duplicate home-manager config from configuration.nix (handled by flake)
- Remove fetchTarball/fetchGit calls incompatible with pure flake mode
- Add shell function to auto-update unstable on nixos-rebuild
2026-03-15 23:16:00 -04:00
0xWheatyz ea03f7d998 feat(claude): disable commit signature and fix permission syntax
- Add includeCoAuthoredBy option set to false
- Update customInstructions to exclude signature from commits
- Fix Bash permission syntax from "git *" to "git:*"
2026-03-15 12:22:12 -04:00
0xWheatyz b14299e94e feat(claude): enable always thinking mode and force settings file
Add alwaysThinkingEnabled option and force flag to overwrite existing
settings.json file managed outside of home-manager.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-15 12:20:02 -04:00
0xWheatyz 23897093b2 feat(claude): add Claude Code settings for auto-permissions and conventional commits
Configure Claude Code to allow Edit, Write, and git commands without prompting,
and instruct it to use conventional commit format in git repositories.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-15 12:08:49 -04:00
6 changed files with 580 additions and 80 deletions
+2
View File
@@ -0,0 +1,2 @@
.omc/
wg1.conf
+110 -65
View File
@@ -2,27 +2,13 @@
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running 'nixos-help').
{ config, pkgs, ... }:
{ config, pkgs, pkgs-unstable, ... }:
let
# Fetch home-manager
home-manager = builtins.fetchTarball {
url = "https://github.com/nix-community/home-manager/archive/release-25.11.tar.gz";
};
# Fetch kickstart-nvim
kickstart-nvim = builtins.fetchGit {
url = "https://github.com/0xWheatyz/kickstart.nvim";
ref = "master";
};
in
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
# Import home-manager module
(import "${home-manager}/nixos")
];
imports = [
./hardware-configuration.nix
# home-manager is imported via flake.nix
];
## Commented out as defined at the end of the page
@@ -114,23 +100,31 @@ in
environment.systemPackages = with pkgs; [
home-manager
firefox
vmware-workstation
cacert
lunar-client
jetbrains.rust-rover
code-cursor
adwaita-icon-theme
obsidian
claude-code
pkgs-unstable.claude-code # Use unstable for latest version
yubikey-manager
# iOS interop
uxplay
libimobiledevice
ifuse
];
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = {
# enable = true;
# enableSSHSupport = true;
# };
programs.gnupg.agent = {
enable = true;
enableSSHSupport = true;
pinentryPackage = pkgs.pinentry-gnome3;
};
# AirDrop-style file transfer (LocalSend)
programs.localsend = {
enable = true;
openFirewall = true;
};
# List services that you want to enable:
@@ -141,7 +135,7 @@ in
# networking.firewall.allowedTCPPorts = [ ... ];
# networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether.
# networking.firewall.enable = false;
networking.firewall.enable = false;
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
@@ -151,34 +145,7 @@ in
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "25.11"; # Did you read the comment?
# Home Manager Configuration
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.l-wyatt = import ./home.nix;
home-manager.sharedModules = [
# Kickstart-nvim module
({ config, lib, pkgs, ... }: {
options.programs.neovim-kickstart = {
enable = lib.mkEnableOption "kickstart.nvim configuration";
};
config = lib.mkIf config.programs.neovim-kickstart.enable {
home.file.".config/nvim" = {
source = kickstart-nvim;
recursive = true;
};
home.packages = with pkgs; [
neovim
ripgrep
fd
gcc
gnumake
git
];
};
})
];
# Home Manager Configuration is handled in flake.nix
### Start of self configuration
# Configure grub to provide ubuntu option
@@ -216,18 +183,64 @@ in
};
};
virtualisation.vmware.host.enable = true;
services.tailscale = {
enable = true;
useRoutingFeatures = "client"; # or "both" for subnet routing
# WireGuard VPN
networking.wg-quick.interfaces.wg0 = {
address = [ "10.0.1.67/32" ];
dns = [ "1.1.1.1" ];
privateKeyFile = "/etc/wireguard/private.key";
# Route endpoint via local gateway to avoid routing loop
preUp = ''
GW=$(ip route show default | awk '{print $3; exit}')
DEV=$(ip route show default | awk '{print $5; exit}')
ip route add 69.48.243.22/32 via "$GW" dev "$DEV" || true
'';
postDown = ''
ip route del 69.48.243.22/32 || true
'';
peers = [{
publicKey = "VEpzr/CeGdS6Wsy0NDDfmlB/bCYxS55A155HWGCIIzc=";
endpoint = "69.48.243.22:51820";
# Route all traffic through VPN EXCEPT the local 10.0.0.0/24 network
allowedIPs = [
"0.0.0.0/5"
"8.0.0.0/7"
"10.0.1.0/24"
"10.0.2.0/23"
"10.0.4.0/22"
"10.0.8.0/21"
"10.0.16.0/20"
"10.0.32.0/19"
"10.0.64.0/18"
"10.0.128.0/17"
"10.1.0.0/16"
"10.2.0.0/15"
"10.4.0.0/14"
"10.8.0.0/13"
"10.16.0.0/12"
"10.32.0.0/11"
"10.64.0.0/10"
"10.128.0.0/9"
"11.0.0.0/8"
"12.0.0.0/6"
"16.0.0.0/4"
"32.0.0.0/3"
"64.0.0.0/2"
"128.0.0.0/1"
];
persistentKeepalive = 25;
}];
};
# Open firewall for Tailscale
# Open firewall for Tailscale + UxPlay AirPlay receiver
networking.firewall = {
checkReversePath = "loose";
trustedInterfaces = [ "tailscale0" ];
allowedUDPPorts = [ config.services.tailscale.port ];
trustedInterfaces = [ "tailscale0" "wg0" ];
allowedTCPPorts = [ 7000 7001 7100 ];
allowedUDPPorts = [ config.services.tailscale.port 6000 6001 7011 ];
};
# Auto-connect tailscale on boot
@@ -260,6 +273,38 @@ in
# Enable necessary services
xdg.portal = {
enable = true;
extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
};
services.pcscd.enable = true;
# iPhone USB mount (libimobiledevice)
services.usbmuxd.enable = true;
# mDNS for AirPlay receiver (UxPlay) discovery from iPhone
services.avahi = {
enable = true;
nssmdns4 = true;
openFirewall = true;
publish = {
enable = true;
addresses = true;
userServices = true;
};
};
# Auto-start UxPlay with the graphical session so iOS Screen Mirroring
# can find this host without manually launching it.
systemd.user.services.uxplay = {
description = "UxPlay AirPlay Mirror Receiver";
partOf = [ "graphical-session.target" ];
after = [ "graphical-session.target" "pipewire.service" ];
wantedBy = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${pkgs.uxplay}/bin/uxplay";
Restart = "on-failure";
RestartSec = 5;
};
};
security.pam.services = {
@@ -301,7 +346,7 @@ in
services.desktopManager.gnome.extraGSettingsOverrides = ''
[org.gnome.mutter]
experimental-features=['scale-monitor-framebuffer', 'xwayland-native-scaling']
experimental-features=['scale-monitor-framebuffer']
'';
environment.gnome.excludePackages = with pkgs; [
Generated
+59 -8
View File
@@ -1,5 +1,21 @@
{
"nodes": {
"caveman": {
"flake": false,
"locked": {
"lastModified": 1776507098,
"narHash": "sha256-M+NoWXxrhtbkbe/lmq7P0/KpmqOZzJjhgeUVjY+7N2k=",
"owner": "JuliusBrussee",
"repo": "caveman",
"rev": "84cc3c14fa1e10182adaced856e003406ccd250d",
"type": "github"
},
"original": {
"owner": "JuliusBrussee",
"repo": "caveman",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
@@ -25,11 +41,11 @@
]
},
"locked": {
"lastModified": 1772633058,
"narHash": "sha256-SO7JapRy2HPhgmqiLbfnW1kMx5rakPMKZ9z3wtRLQjI=",
"lastModified": 1775077333,
"narHash": "sha256-OXcxobt7lBkh1B8AjwreU+24myhtKpqeLfAeIyNLFY8=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "080657a04188aca25f8a6c70a0fb2ea7e37f1865",
"rev": "49ca96b2714c5931e17401eff87f3edd42d2b0f2",
"type": "github"
},
"original": {
@@ -74,13 +90,29 @@
"type": "github"
}
},
"nixpkgs_2": {
"nixpkgs-unstable": {
"locked": {
"lastModified": 1772822230,
"narHash": "sha256-yf3iYLGbGVlIthlQIk5/4/EQDZNNEmuqKZkQssMljuw=",
"lastModified": 1779536132,
"narHash": "sha256-q+fF42iv/geEbHfgSzy3tS0FF/EyD6XTZ98E6yxiBO8=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "71caefce12ba78d84fe618cf61644dce01cf3a96",
"rev": "3d8f0f3f72a6cd4d93d0ad13203f2ea1cb7e1456",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1775002709,
"narHash": "sha256-d3Yx83vSrN+2z/loBh4mJpyRqr9aAJqlke4TkpFmRJA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "bcd464ccd2a1a7cd09aa2f8d4ffba83b761b1d0e",
"type": "github"
},
"original": {
@@ -90,11 +122,30 @@
"type": "github"
}
},
"oh-my-claudecode": {
"flake": false,
"locked": {
"lastModified": 1776693787,
"narHash": "sha256-C/m8Vlt6MXy8nlgqtoey9A5JnVTHCPkGUjar9x5Y2uw=",
"owner": "Yeachan-Heo",
"repo": "oh-my-claudecode",
"rev": "084fc47266824b35f4c8cbe886c0990ae716c184",
"type": "github"
},
"original": {
"owner": "Yeachan-Heo",
"repo": "oh-my-claudecode",
"type": "github"
}
},
"root": {
"inputs": {
"caveman": "caveman",
"home-manager": "home-manager",
"kickstart-nvim": "kickstart-nvim",
"nixpkgs": "nixpkgs_2"
"nixpkgs": "nixpkgs_2",
"nixpkgs-unstable": "nixpkgs-unstable",
"oh-my-claudecode": "oh-my-claudecode"
}
},
"systems": {
+21 -4
View File
@@ -3,26 +3,43 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable";
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
kickstart-nvim.url = "github:0xWheatyz/kickstart.nvim";
caveman = {
url = "github:JuliusBrussee/caveman";
flake = false;
};
oh-my-claudecode = {
url = "github:Yeachan-Heo/oh-my-claudecode";
flake = false;
};
};
outputs = { self, nixpkgs, home-manager, kickstart-nvim, ... }: {
outputs = { self, nixpkgs, nixpkgs-unstable, home-manager, kickstart-nvim, caveman, oh-my-claudecode, ... }:
let
system = "x86_64-linux";
pkgs-unstable = import nixpkgs-unstable {
inherit system;
config.allowUnfree = true;
};
in {
nixosConfigurations = {
nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
inherit system;
specialArgs = { inherit pkgs-unstable; };
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.l-wyatt = import /home/l-wyatt/.config/home-manager/home.nix;
home-manager.users.l-wyatt = import ./home.nix;
home-manager.extraSpecialArgs = {
inherit kickstart-nvim;
inherit kickstart-nvim caveman oh-my-claudecode;
};
home-manager.sharedModules = [
kickstart-nvim.homeManagerModules.default
+141 -3
View File
@@ -1,4 +1,4 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, caveman, oh-my-claudecode, ... }:
{
home.username = "l-wyatt";
@@ -20,11 +20,15 @@
bitwarden-cli
lunarvim
minicom
kicad
ghostty
gnupg
pinentry-gnome3
gnomeExtensions.blur-my-shell
gnomeExtensions.just-perfection
gnomeExtensions.arc-menu
freecad
gtk3 # Provides org.gtk.Settings.FileChooser schema for FreeCAD
nodejs # Required by claude-code and oh-my-claudecode (HUD, npm plugins)
];
# --------------------------
@@ -70,6 +74,16 @@
initContent = ''
export SHELL=$(which zsh)
alias nix-shell='nix-shell --run $SHELL'
# Auto-update nixpkgs-unstable (for claude-code) on rebuild
nixos-rebuild() {
if [[ "$1" == "switch" || "$1" == "boot" || "$1" == "test" ]]; then
echo "Updating nixpkgs-unstable for latest Claude Code..."
nix flake update nixpkgs-unstable --flake /home/l-wyatt/Documents/nixos-configuration
fi
command sudo nixos-rebuild "$@" --flake /home/l-wyatt/Documents/nixos-configuration
}
nix() {
if [[ $1 == "develop" ]]; then
shift
@@ -104,16 +118,43 @@
exec = "ghostty";
exec-arg = "";
};
"org/gnome/crypto/cache" = {
enable-ssh-agent = false;
};
};
# Disable GCR ssh-agent so gpg-agent handles SSH
systemd.user.sockets.gcr-ssh-agent.Install.WantedBy = lib.mkForce [];
# GPG configuration
programs.gpg = {
enable = true;
settings = {
keyserver = "hkps://keys.openpgp.org";
keyid-format = "0xlong";
with-fingerprint = true;
personal-cipher-preferences = "AES256 AES192 AES";
personal-digest-preferences = "SHA512 SHA384 SHA256";
personal-compress-preferences = "ZLIB BZIP2 ZIP Uncompressed";
};
scdaemonSettings = {
disable-ccid = true;
};
};
# Manage your git configuration declaratively
programs.git = {
enable = true;
signing = {
key = "0xADCEAC560B498269";
signByDefault = true;
};
settings = {
user = {
name = "0xWheatyz";
email = "wyatt@leeworks.dev";
};
pull.rebase = "false";
init.defaultBranch = "master";
push.autoSetupRemote = "true";
alias.lg = "log --graph --pretty=format:'%C(yellow)%h%Creset -%C(red)%d%Creset %s %Cgreen(%cr)%Creset %C(bold blue)<%an>%Creset' --abbrev-commit";
@@ -166,6 +207,103 @@
# Use kickstart.nvim configuration from flake
programs.neovim-kickstart.enable = true;
# Claude Code skills (managed declaratively via flake inputs)
home.file.".claude/skills/caveman" = {
source = "${caveman}/skills/caveman";
recursive = true;
};
home.file.".claude/skills/caveman-help" = {
source = "${caveman}/skills/caveman-help";
recursive = true;
};
home.file.".claude/skills" = {
source = "${oh-my-claudecode}/skills";
recursive = true;
};
# Claude Code agents (managed declaratively via flake inputs)
home.file.".claude/agents" = {
source = "${oh-my-claudecode}/agents";
recursive = true;
};
# Claude Code configuration
home.file.".claude/CLAUDE.md" = {
text = builtins.readFile "${oh-my-claudecode}/CLAUDE.md" + ''
## Commit Behavior
After completing each logical unit of work, use the /commit skill to stage
and commit changes before proceeding to the next task. Do not batch unrelated
changes into a single commit. Do not wait to be asked.
'';
force = true;
};
home.file.".claude/commands/commit.md" = {
text = ''
---
description: Stage and commit changes as a conventional commit with one logical unit per commit
---
Review the current working tree with `git status` and `git diff --staged` and `git diff`.
Then do the following:
1. Identify the smallest logical unit of change present. If multiple unrelated changes exist, only stage and commit one unit at a time leave the rest unstaged.
2. Stage only the files (or hunks via `git add -p`) relevant to that logical unit.
3. Write a commit message following the Conventional Commits format:
<type>(<scope>): <description>
Types: feat, fix, docs, style, refactor, perf, test, chore, ci
- scope is optional but use it when the change is clearly scoped to a module, package, or domain
- description: lowercase, imperative mood, no trailing period, max ~72 chars
- if the change warrants a body, add it after a blank line
4. Run `git commit` with that message. Never use `--no-verify`.
5. Report what was committed and what (if anything) remains uncommitted.
'';
force = true;
};
home.file.".claude/settings.json" = {
text = builtins.toJSON {
hasCompletedOnboarding = true;
permissions = {
allow = [
"Edit"
"Write"
"Read"
"Glob"
"Grep"
"Bash"
"WebFetch"
"WebSearch"
"Agent"
"NotebookEdit"
"Monitor"
];
};
alwaysThinkingEnabled = true;
includeCoAuthoredBy = false;
statusLine = {
type = "command";
command = "node \${CLAUDE_CONFIG_DIR:-$HOME/.claude}/hud/omc-hud.mjs";
};
env = {
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
OMC_PLUGIN_ROOT = "${oh-my-claudecode}";
#ANTHROPIC_BASE_URL = "http://10.0.1.16:20128/v1";
#ANTHROPIC_AUTH_TOKEN = "sk-634d6ea8670969de-qzxian-1a74cc97";
};
teammateMode = "tmux";
};
force = true;
};
programs.ssh = {
enable = true;
enableDefaultConfig = false;
@@ -195,7 +333,7 @@
proxyJump = "_JumpHost";
};
"git" = {
hostname = "10.0.1.10";
hostname = "gitea.leeworks.dev";
port = 22;
user = "git";
};
+247
View File
@@ -0,0 +1,247 @@
# Post-Rebuild YubiKey Setup Guide
## Step 1: Generate GPG Master Key
```bash
gpg --expert --full-generate-key
```
At the prompts:
1. Select **(9) ECC and ECC**
2. Curve: **Curve 25519**
3. Expiration: **1y**
4. Real name: **0xWheatyz**
5. Email: **wyatt@leeworks.dev**
6. Set a strong passphrase
Note the key ID printed (e.g. `0x1234ABCD5678EFGH`).
---
## Step 2: Add Subkeys
```bash
gpg --expert --edit-key <KEY-ID>
```
Add 3 subkeys at the `gpg>` prompt:
**Signing subkey:**
```
gpg> addkey
→ (10) ECC (sign only)
→ Curve 25519
→ 1y expiry
```
**Encryption subkey** (may already exist — check with `list`):
```
gpg> addkey
→ (12) ECC (encrypt only)
→ Curve 25519
→ 1y expiry
```
**Authentication subkey:**
```
gpg> addkey
→ (11) ECC (set your own capabilities)
→ Toggle: disable Sign, enable Authenticate
→ Curve 25519
→ 1y expiry
```
```
gpg> save
```
---
## Step 3: Back Up Master Key (CRITICAL)
> Once keys are moved to the YubiKey, they cannot be extracted. Back up now.
```bash
mkdir -p /tmp/gpg-backup
gpg --armor --export-secret-keys <KEY-ID> > /tmp/gpg-backup/master-secret.asc
gpg --armor --export-secret-subkeys <KEY-ID> > /tmp/gpg-backup/subkeys-secret.asc
gpg --armor --export <KEY-ID> > /tmp/gpg-backup/public.asc
gpg --export-ownertrust > /tmp/gpg-backup/ownertrust.txt
```
Copy `/tmp/gpg-backup/` to an encrypted USB drive or other secure offline storage. Then remove the temp copy:
```bash
rm -rf /tmp/gpg-backup
```
---
## Step 4: Move Subkeys to YubiKey
```bash
gpg --edit-key <KEY-ID>
```
Move each subkey to its corresponding card slot:
```
gpg> key 1
gpg> keytocard
→ (1) Signature key
gpg> key 1
gpg> key 2
gpg> keytocard
→ (2) Encryption key
gpg> key 2
gpg> key 3
gpg> keytocard
→ (3) Authentication key
gpg> save
```
---
## Step 5: Set Trust & Verify
```bash
gpg --edit-key <KEY-ID>
```
```
gpg> trust
→ (5) I trust ultimately
gpg> quit
```
Verify the card:
```bash
gpg --card-status # Should show all 3 subkeys
gpg --list-secret-keys # Subkeys should show "ssb>" (stub pointing to card)
```
---
## Step 6: Back Up & Remove Old SSH Keys
```bash
mkdir -p ~/.ssh/old
mv ~/.ssh/id_ed25519 ~/.ssh/old/
mv ~/.ssh/id_ed25519.pub ~/.ssh/old/
```
---
## Step 7: Get Your New SSH Public Key
```bash
gpg --export-ssh-key <KEY-ID>
```
This outputs the SSH public key derived from your GPG auth subkey. Save it:
```bash
gpg --export-ssh-key <KEY-ID> > ~/.ssh/yubikey.pub
```
Verify gpg-agent serves it:
```bash
ssh-add -L # Should show the same key
```
Deploy this key to your remote servers:
- **vps** (45.79.198.105) — add to `~/.ssh/authorized_keys` for user `wyatt`
- **home** (10.0.0.20) — add to `~/.ssh/authorized_keys` for user `l-wyatt`
- **git** (10.0.1.10) — add via your Gitea/Forgejo web UI
- **GitHub** — Settings → SSH and GPG Keys → New SSH Key
> **Tip:** Use the old key (still in `~/.ssh/old/`) to SSH in and deploy the new one:
> ```bash
> ssh -i ~/.ssh/old/id_ed25519 vps "echo '$(gpg --export-ssh-key <KEY-ID>)' >> ~/.ssh/authorized_keys"
> ```
---
## Step 8: Update home.nix with Your Signing Key
Get your key ID:
```bash
gpg --list-secret-keys --keyid-format 0xlong
```
Then edit `home.nix` and replace the `key = null;` line:
```nix
signing = {
key = "<YOUR-0xLONG-KEY-ID>"; # e.g. "0x1234ABCD5678EFGH"
signByDefault = true;
};
```
Rebuild again:
```bash
nixos-rebuild switch
```
---
## Step 9: Verify Everything
```bash
# Smartcard
gpg --card-status
# SSH via YubiKey
ssh-add -L
ssh vps echo "YubiKey SSH works!"
# Git signing
cd /tmp && git init test-sign && cd test-sign
git commit --allow-empty -m "test signing"
git log --show-signature -1
# Should show "Good signature from 0xWheatyz <wyatt@leeworks.dev>"
rm -rf /tmp/test-sign
```
---
## Step 10 (Optional): Change YubiKey PINs
The default PINs are `123456` (user) and `12345678` (admin). Change them:
```
gpg --card-edit
gpg/card> admin
gpg/card> passwd
→ (1) Change PIN
→ (3) Change Admin PIN
gpg/card> quit
```
---
## Step 11 (Optional): Upload GPG Public Key to GitHub
```bash
gpg --armor --export <KEY-ID>
```
Paste the output into **GitHub → Settings → SSH and GPG Keys → New GPG Key**. This makes your signed commits show "Verified" on GitHub.
---
## Note
The config changes to `configuration.nix` and `home.nix` are already saved. After you rebuild, reboot, and follow the steps above, you'll need to come back to update `home.nix` one more time with your actual GPG key ID (Step 8) and do a final rebuild.