Compare commits

...

14 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
4 changed files with 93 additions and 22 deletions
+2
View File
@@ -0,0 +1,2 @@
.omc/
wg1.conf
+55 -10
View File
@@ -105,6 +105,10 @@
adwaita-icon-theme adwaita-icon-theme
pkgs-unstable.claude-code # Use unstable for latest version pkgs-unstable.claude-code # Use unstable for latest version
yubikey-manager yubikey-manager
# iOS interop
uxplay
libimobiledevice
ifuse
]; ];
# Some programs need SUID wrappers, can be configured further or are # Some programs need SUID wrappers, can be configured further or are
@@ -116,6 +120,12 @@
pinentryPackage = pkgs.pinentry-gnome3; pinentryPackage = pkgs.pinentry-gnome3;
}; };
# AirDrop-style file transfer (LocalSend)
programs.localsend = {
enable = true;
openFirewall = true;
};
# List services that you want to enable: # List services that you want to enable:
# Enable the OpenSSH daemon. # Enable the OpenSSH daemon.
@@ -125,7 +135,7 @@
# networking.firewall.allowedTCPPorts = [ ... ]; # networking.firewall.allowedTCPPorts = [ ... ];
# networking.firewall.allowedUDPPorts = [ ... ]; # networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether. # Or disable the firewall altogether.
# networking.firewall.enable = false; networking.firewall.enable = false;
# This value determines the NixOS release from which the default # This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions # settings for stateful data, like file locations and database versions
@@ -156,11 +166,6 @@
# Allow /etc/hosts to be modified without system rebuild # Allow /etc/hosts to be modified without system rebuild
environment.etc.hosts.mode = "0644"; environment.etc.hosts.mode = "0644";
# KiCad footprint libraries
environment.sessionVariables = {
KICAD9_FOOTPRINT_DIR = "${pkgs.kicad-libraries.footprints}/share/kicad/footprints";
};
# Enable flakes and extras # Enable flakes and extras
nix.settings.experimental-features = [ "nix-command" "flakes" ]; nix.settings.experimental-features = [ "nix-command" "flakes" ];
@@ -182,12 +187,22 @@
# WireGuard VPN # WireGuard VPN
networking.wg-quick.interfaces.wg0 = { networking.wg-quick.interfaces.wg0 = {
address = [ "10.0.1.67/32" ]; address = [ "10.0.1.67/32" ];
dns = [ "10.0.1.65" ]; dns = [ "1.1.1.1" ];
privateKeyFile = "/etc/wireguard/private.key"; 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 = [{ peers = [{
publicKey = "VEpzr/CeGdS6Wsy0NDDfmlB/bCYxS55A155HWGCIIzc="; publicKey = "VEpzr/CeGdS6Wsy0NDDfmlB/bCYxS55A155HWGCIIzc=";
endpoint = "vpn.leeworks.dev:51820"; endpoint = "69.48.243.22:51820";
# Route all traffic through VPN EXCEPT the local 10.0.0.0/24 network # Route all traffic through VPN EXCEPT the local 10.0.0.0/24 network
allowedIPs = [ allowedIPs = [
"0.0.0.0/5" "0.0.0.0/5"
@@ -220,11 +235,12 @@
}; };
# Open firewall for Tailscale # Open firewall for Tailscale + UxPlay AirPlay receiver
networking.firewall = { networking.firewall = {
checkReversePath = "loose"; checkReversePath = "loose";
trustedInterfaces = [ "tailscale0" "wg0" ]; trustedInterfaces = [ "tailscale0" "wg0" ];
allowedUDPPorts = [ config.services.tailscale.port ]; allowedTCPPorts = [ 7000 7001 7100 ];
allowedUDPPorts = [ config.services.tailscale.port 6000 6001 7011 ];
}; };
# Auto-connect tailscale on boot # Auto-connect tailscale on boot
@@ -262,6 +278,35 @@
services.pcscd.enable = true; 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 = { security.pam.services = {
login.u2fAuth = true; login.u2fAuth = true;
sudo.u2fAuth = true; sudo.u2fAuth = true;
Generated
+3 -3
View File
@@ -92,11 +92,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1776329215, "lastModified": 1779536132,
"narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", "narHash": "sha256-q+fF42iv/geEbHfgSzy3tS0FF/EyD6XTZ98E6yxiBO8=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b86751bc4085f48661017fa226dee99fab6c651b", "rev": "3d8f0f3f72a6cd4d93d0ad13203f2ea1cb7e1456",
"type": "github" "type": "github"
}, },
"original": { "original": {
+33 -9
View File
@@ -20,7 +20,6 @@
bitwarden-cli bitwarden-cli
lunarvim lunarvim
minicom minicom
kicad
ghostty ghostty
gnupg gnupg
pinentry-gnome3 pinentry-gnome3
@@ -29,6 +28,7 @@
gnomeExtensions.arc-menu gnomeExtensions.arc-menu
freecad freecad
gtk3 # Provides org.gtk.Settings.FileChooser schema for FreeCAD gtk3 # Provides org.gtk.Settings.FileChooser schema for FreeCAD
nodejs # Required by claude-code and oh-my-claudecode (HUD, npm plugins)
]; ];
# -------------------------- # --------------------------
@@ -216,14 +216,21 @@
source = "${caveman}/skills/caveman-help"; source = "${caveman}/skills/caveman-help";
recursive = true; recursive = true;
}; };
home.file.".claude/skills/oh-my-claudecode" = { home.file.".claude/skills" = {
source = "${oh-my-claudecode}/skills"; source = "${oh-my-claudecode}/skills";
recursive = true; recursive = true;
}; };
# Claude Code agents (managed declaratively via flake inputs)
home.file.".claude/agents" = {
source = "${oh-my-claudecode}/agents";
recursive = true;
};
# Claude Code configuration # Claude Code configuration
home.file.".claude/CLAUDE.md" = { home.file.".claude/CLAUDE.md" = {
text = '' text = builtins.readFile "${oh-my-claudecode}/CLAUDE.md" + ''
## Commit Behavior ## Commit Behavior
After completing each logical unit of work, use the /commit skill to stage After completing each logical unit of work, use the /commit skill to stage
@@ -264,18 +271,35 @@
home.file.".claude/settings.json" = { home.file.".claude/settings.json" = {
text = builtins.toJSON { text = builtins.toJSON {
hasCompletedOnboarding = true;
permissions = { permissions = {
allow = [ allow = [
"Edit" "Edit"
"Write" "Write"
"Bash(git:*)" "Read"
"Bash(nix-shell:*)" "Glob"
"Bash(nix eval:*)" "Grep"
"Bash(nix flake check:*)" "Bash"
]; "WebFetch"
"WebSearch"
"Agent"
"NotebookEdit"
"Monitor"
];
}; };
alwaysThinkingEnabled = true; alwaysThinkingEnabled = true;
includeCoAuthoredBy = false; 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; force = true;
}; };
@@ -309,7 +333,7 @@
proxyJump = "_JumpHost"; proxyJump = "_JumpHost";
}; };
"git" = { "git" = {
hostname = "10.0.1.10"; hostname = "gitea.leeworks.dev";
port = 22; port = 22;
user = "git"; user = "git";
}; };