Compare commits

..

4 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
3 changed files with 49 additions and 5 deletions
+42 -2
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.
@@ -225,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
@@ -267,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": 1778869304, "lastModified": 1779536132,
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", "narHash": "sha256-q+fF42iv/geEbHfgSzy3tS0FF/EyD6XTZ98E6yxiBO8=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d233902339c02a9c334e7e593de68855ad26c4cb", "rev": "3d8f0f3f72a6cd4d93d0ad13203f2ea1cb7e1456",
"type": "github" "type": "github"
}, },
"original": { "original": {
+4
View File
@@ -271,6 +271,7 @@
home.file.".claude/settings.json" = { home.file.".claude/settings.json" = {
text = builtins.toJSON { text = builtins.toJSON {
hasCompletedOnboarding = true;
permissions = { permissions = {
allow = [ allow = [
"Edit" "Edit"
@@ -283,6 +284,7 @@
"WebSearch" "WebSearch"
"Agent" "Agent"
"NotebookEdit" "NotebookEdit"
"Monitor"
]; ];
}; };
alwaysThinkingEnabled = true; alwaysThinkingEnabled = true;
@@ -294,6 +296,8 @@
env = { env = {
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1"; CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
OMC_PLUGIN_ROOT = "${oh-my-claudecode}"; 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"; teammateMode = "tmux";
}; };