Skip to content

Use secure direct-socket AirPlay control - #1

Open
minpeter wants to merge 9 commits into
glennbarosen:mainfrom
minpeter:feat/secure-socket-control
Open

minpeter wants to merge 9 commits into
glennbarosen:mainfrom
minpeter:feat/secure-socket-control

Conversation

@minpeter

@minpeter minpeter commented Aug 28, 2026

Copy link
Copy Markdown

Summary

  • replace every doubletake-ctl control subprocess with one-request/one-response JSON over doubletake's Unix socket
  • keep pairing PINs/passwords out of argv, files, and logs; drop request references after flush() and on every terminal path
  • replace reshare.sh and credential-file surgery with the targeted reset-restore-token daemon command
  • retain only the constant binary probe and lazy daemon start subprocess boundaries
  • validate hwaccel and a new portRange setting before constructing daemon argv
  • recreate the Quickshell Socket transport per request so an initial ServerNotFoundError cannot poison retries after lazy daemon startup
  • use only the protected XDG runtime socket; reject the globally claimable /tmp/doubletake.sock and lexical aliases before every write
  • publish status and devices atomically, classify sanitized failures, and clear errors only after a complete verified refresh
  • force plugin-owned labels to Text.PlainText and strip angle brackets before receiver text enters shell-owned hero/tooltips
  • preserve the existing visual/keyboard block except for the Source action handler

Depends on omarroth/doubletake#36.

Security model

The plugin writes control requests only to the protected $XDG_RUNTIME_DIR/doubletake.sock. It rejects the globally claimable /tmp/doubletake.sock plus equivalent ./.. and repeated-separator spellings before every initial or alternate-path write, so pairing credentials cannot cross that boundary. Each accepted action emits exactly one newline-terminated JSON request, accepts one validated response, and disposes that request's transport without duplicating a payload. Unknown commands, malformed targets, missing reset targets, malformed responses, and unknown credential kinds fail closed. Daemon error text is mapped to a fixed sanitized vocabulary. Receiver-controlled text cannot trigger QML rich-text resource loading. JavaScript cleanup drops credential references; it does not claim physical string zeroization.

Verification

  • node --test tests/*.test.js: 99/99 pass
  • deterministic real-Quickshell safe-fallback harness: unavailable primary test socket → policy-checked alternate per-user socket → one successful response
  • deterministic attacker-socket harness: /tmp/./doubletake.sock is rejected and receives no credential payload
  • deterministic real-Quickshell reconnect harness: first request receives ServerNotFoundError, fixture starts on exact READY, second request succeeds through a fresh Socket and settles cleanly
  • pure regression coverage for atomic refresh, omitted idle streams, forward-compatible response parsing, seven failure categories, persistent error recovery, and rich-text guards
  • omarchy plugin validate: pass
  • qmllint airplay/Controller.qml: 0 warnings
  • git diff --check: pass
  • installed PR commit eeef1ba1d1372f1f81a5c4a8bddf6c1c143fd2f5: bar icon remains immediately before omarchy.monitor; lazy daemon argv is exactly doubletake -daemonize -hwaccel vaapi -port-range 60000-60010; cold daemon startup recovers automatically and the panel settles to six receiver rows without stale startup/error state
  • approved meeting-room Apple TV integration completed streamingreset-restore-token → Hyprland source picker → source selection → resumed streaming → targeted disconnect back to idle
  • real installed zero-device replay: omitted streams and devices render the normal no-receivers empty state, not a protocol error
  • fresh installed-shell audit after the final QML layout fix: zero Binding loop warnings

Attribution

The one-request lifecycle, bounded cleanup, fallback, atomic-refresh and recovery approach builds on implementation concepts from Mathias Ringhof's mathiasringhof/omarchy-airplay. The wire contract and targeted restore-token semantics come from Omar Roth's LGPL-3.0-or-later doubletake.

Send control requests, including credentials and source resets, over DoubleTake's Unix socket. Remove CLI subprocesses and the credential-file helper, validate daemon settings, and cover protocol cleanup with Node and Quickshell fixtures.

Co-authored-by: minpeter <minpeterx@gmail.com>

Generated with [OpenAI Codex](https://openai.com/codex/)
Use a fresh Quickshell Socket for every daemon request so an initial ServerNotFoundError cannot poison later retries. Add a deterministic real-Quickshell regression harness that starts the socket after the first failure.

Co-authored-by: minpeter <minpeterx@gmail.com>

Generated with [OpenAI Codex](https://openai.com/codex/)
@minpeter
minpeter marked this pull request as ready for review August 28, 2026 10:53
@minpeter
minpeter marked this pull request as draft August 28, 2026 12:28
minpeter and others added 3 commits August 29, 2026 02:27
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@minpeter
minpeter marked this pull request as ready for review August 28, 2026 18:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant