Rename project: deckd → deskestra #145

Description

@jonocodes

Why

The app has outgrown its original framing. It's no longer "an app controller" — it's a full desktop control surface that drives apps, windows, the desktop, and the GNOME/KDE/macOS sessions around them. The name deckd was a nod to Stream Deck and reads narrowly. We want a name that:

  • keeps desk (drives home "you control a screen, not a server"),
  • carries the conducting thread (you conduct your desktop, you don't just press buttons),
  • reads as one natural word with one obvious spelling,
  • is coined enough to be brand-/domain-/product-safe.

The recent layoutdeck cutover (issue #105) already gave us internal vocabulary room. Now we want the project itself to move.

Decision: deskestra

desk + orche·stra → you conduct your desktop. Three syllables, one spelling, easy to say aloud.

Across every candidate we researched, deskestra is the only one that came back genuinely unclaimed — no product, package, org, or app. The desk- sound space is occupied by Deskera (funded cloud ERP) and DeskRoll (a remote-desktop product), but deskestra is far enough from both (extra syllable, different ending) to stand apart.

Service name: deskestra (no -d suffix — agreed that <name>d feels dated).
GNOME extension UUID: deskestra-focus@local.
KWin script ID: deskestra-focus.
Password file: ~/.config/deskestra/password.
Python distribution / entry points: deskestra, deskestra-dev, deskctl (keeping deckctl — it's a CLI for the daemon, not the daemon itself; rename candidate TBD).

Names considered

Clustered by approach. Each entry: name → one-line verdict → why.

A. Keep deckd (reposition instead of rename) — rejected

  • deckd → keep, reframe positioning. Cheapest path. Rejected because we explicitly said the cutover doesn't scare us and we want the broader positioning baked into the name itself.

B. Generic conducting/control surface — rejected, all taken or weak

  • Helm → "you steer the desktop from it." Strong, but collides with Kubernetes Helm.
  • Maestro → conducting metaphor, distinctive. Prior art: Mobile "Maestro" test tool. Rejected as overused.
  • Conductor → conducting + dispatch. conductord ugly. Rejected as overused.
  • Conduit → routing metaphor. Rejected: reads as internal plumbing, not conducting.
  • Podium → fresh, low collision. Rejected: you don't want "pod."
  • Panel / Console / Dispatch / Relay / Pilot → various. Rejected for over-use or vibe mismatch.

C. desk + fused coined suffix (the vein we liked)

  • Deskpit (cockpit) → rejected, you don't like "pit."
  • Deskata → taken-ish (deskata.com exists; risks Deskera echo).
  • Deskra → basically a homophone of Deskera ERP (funded, 80k+ users). Hard no.
  • Deskira / Deskora / Deskana / Deskyra / Deskade → variations. Most share the Deskera phonetic overlap; none meaningfully better than deskestra.
  • Deskomand / Deskommand / Deskrix / Deskrex / Deskura / Deskept → control-rooted variants. Various fatal flaws (skept-adjacent, "command" doesn't fuse, etc.).
  • Deskrol → direct collision with DeskRoll (existing remote-desktop product). Hard no.
  • Deskrola → deskrol + softened; Polish "deskorolki" + Deskera echo. Compromised.
  • Deskestra ✅ → clean, conducting-fused, no collision. Winner.

D. control family, no desk — rejected, all crowded

  • Controlla → near-identical open-source "phone-as-keyboard/mouse-for-desktop" project already uses the name (okekedev/controlla). Plus CAREL Controlla (HVAC controllers). Hard no.
  • Controla → spread thin across active apps (Controla cybersecurity, ControlA university mgmt, Controla.ai org). Diluted.
  • Kontroll / Kontrol → stylized bare spelling. Rejected earlier: dated, un-searchable.
  • Ctrl / Ctrla → literal keyboard modifier, on-theme. Rejected: ambiguous to say aloud.
  • Controlla / Drake collision → also a Drake song. Bonus reason not to.

E. Less-literal control roots (Latin / Greek / French) — rejected, all occupied

  • Dirigo (Latin "I direct") → several active software cos (Dirigo Software accident reconstruction, etc.).
  • Regie (French "control room") → Regie.ai dominates.
  • Kyber (kubernan root) → saturated. KDE has a "Kyber" cross-platform remote-control product, plus post-quantum crypto, plus k8s deploy tool.
  • Imperia (imperare "to command") → Imperia SCM + Imperia Online game.

Pattern: anything that obviously means control/command/steer has been claimed, usually more than once. Coined fusions (deskestra) escape the saturation.


Rename blast-radius plan: deckddeskestra

Generated by an exhaustive inventory of the repo. Hard references are listed explicitly. Soft references (comments, prose, generic "deck" usage as a product concept like "button deck") are not listed and should not be mechanically renamed.

Naming decisions (commit these first)

OldNew
Project name deckddeskestra
Python distribution deckddeskestra
Python package daemon/deckd/daemon/deskestra/
Executable deckddeskestra
Executable deckd-devdeskestra-dev
Executable deckctldeskctl (keep, but see open question below)
Service unit deckd.servicedeskestra.service
LaunchAgent com.deckd.daemoncom.deskestra.daemon
NixOS module services.deckd-spikeservices.deskestra-spike
NixOS script deckd-spike-startdeskestra-spike-start
udev rules 70-deckd-uinput.rules70-deskestra-uinput.rules
GNOME UUID deckd-focus@localdeskestra-focus@local
KWin script ID deckd-focusdeskestra-focus
D-Bus bus org.deckd.Focusorg.deskestra.Focus
D-Bus path /org/deckd/Focus/org/deskestra/Focus
D-Bus interface org.deckd.Focusorg.deskestra.Focus
Env DECKD_PASSWORDDESKESTRA_PASSWORD
Env DECKD_FAKE_MPRISDESKESTRA_FAKE_MPRIS
Env DECKD_TLS_DIR / DECKD_TLS_HOSTDESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST
Env VITE_DECKD_WSVITE_DESKESTRA_WS
Password file ~/.config/deckd/password~/.config/deskestra/password
LaunchAgent log @PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.log
Browser localStorage key deckd.passworddeskestra.password
HTTP header X-Deckd-PasswordX-Deskestra-Password
GitHub Pages base /deckd//deskestra/
Pages URL jonocodes.github.io/deckd/jonocodes.github.io/deskestra/
Client PWA name / short_namedeskestra
HTML <title>Deskestra
macOS PWA apple-mobile-web-app-titleDeskestra
Auto-ignore logic: window titled "deckd"window titled "deskestra"
Test fixture suffix deckd-smoke-* etc.deskestra-smoke-*
CLI metric prefix deckd_ (e.g. deckd_actions_total)deskestra_
Class DeckdFocusDBusServiceDeskestraFocusDBusService
Class DeckdFocusCacheDeskestraFocusCache
Class DeckdFocusExtensionDeskestraFocusExtension
Class attr Server._is_deckd_windowServer._is_deskestra_window
Setuptools include pattern deckd*deskestra*
Repo URL github.com/jonocodes/deckdgithub.com/jonocodes/deskestra

Open question on deckctl: it's the CLI, not the daemon. Options: rename to deskctl (consistent), rename to deskestractl (verbose), or rename to something functional like deskestra-cli. Leaning toward deskctl for symmetry. Worth a quick opinion before the cutover.

Mechanical order of operations

Do these in order so the tree is never in a half-renamed state for more than a commit.

  1. Pre-flight: confirm availability — deskestra.com/.app, github.com/deskestra, npmjs.com/package/deskestra, PyPI deskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)
  2. Lock the new identifiers by editing these in one commit:
    • pyproject.tomlname, [project.scripts] (deskestra, deskestra-dev), deskctl decision, package include pattern.
    • daemon/deckd/daemon/deskestra/ (rename the package directory itself; every internal import updates accordingly).
    • All from deckd… / import deckd… (the directory rename takes care of most; touch up external references).
    • All DECKD_* and VITE_DECKD_WS env var names.
    • ~/.config/deckd/password~/.config/deskestra/password in auth.py, systemd unit, launchd plist, README, docs.
    • D-Bus: org.deckd.Focus, /org/deckd/Focus, interface name — in platform.py, GNOME extension.js, KWin main.js, all scripts/* and tests/*.
    • Class names Deckd*Deskestra* (DBus service, cache, extension class, _is_deckd_window).
    • Auto-ignore logic: "deckd" literal → "deskestra" (and case-insensitive match).
  3. Packaging: rename and update these files/paths:
    • packaging/systemd/deckd.servicepackaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
    • packaging/launchd/com.deckd.daemon.plistpackaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
    • packaging/nixos/deckd-spike.nixpackaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
    • packaging/udev/70-deckd-uinput.rulespackaging/udev/70-deskestra-uinput.rules.
    • packaging/gnome-shell/deckd-focus@local/packaging/gnome-shell/deskestra-focus@local/; metadata.json uuid + name + description; extension.js bus/path/interface; wire-shape.js import path.
    • packaging/kwin-script/deckd-focus/packaging/kwin-script/deskestra-focus/; metadata.json Id + Name; main.js bus/path/interface + comment header.
  4. Justfile: update every recipe that invokes deckd, deckd-dev, deckctl, references the extension UUID, or hardcodes paths. (See "Justfile" in the inventory — this is one of the larger files to edit.) Also update the GNOME pack/install recipe (packaging/gnome-shell/...@local) and the KWin install recipe ($HOME/.local/share/kwin/scripts/deskestra-focus/...).
  5. Client (client/):
    • package.json: namedeskestra-client (or just deskestra).
    • index.html: <title> and apple-mobile-web-app-title.
    • vite.config.ts: VITE_DECKD_WSVITE_DESKESTRA_WS.
    • public/manifest.json and public/icon.svg: display name + aria-label.
    • src/socket.ts: localStorage key deckd.passworddeskestra.password; any error text containing deckd.
    • playwright.config.ts: DECKD_FAKE_MPRIS env var name.
  6. Scripts (scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
  7. Tests (tests/):
    • Update all from deckd… imports.
    • Update all D-Bus name assertions (org.deckd.Focus).
    • Update Deckd* class assertions.
    • Rename tests/test_deckd_window.pytests/test_deskestra_window.py and update test names + the tests/.pytest_cache nodeids will regenerate.
    • Rename e2e client/e2e/daemon-log.ts references if any contain the binary name.
  8. Docs: sweep README.md, CONTEXT.md, AGENTS.md, docs/ONBOARDING.md, docs/REFERENCE.md, docs/TESTING.md, docs/SPIKES.md, docs/ARCHITECTURE.md, docs/research/landscape.md, docs/research/issue-56-macos-nowplaying.md, all docs/adr/*.md. Do not mechanically replace generic "deck" usage — that word is now the product concept (a "deck" is a control surface) and stays. Only the project identifier deckd / Deckd changes.
  9. GitHub-side:
    • Rename the repo (github.com/jonocodes/deckdgithub.com/jonocodes/deskestra) — GitHub redirects old URLs but update local git remote after.
    • .github/workflows/deploy-pages.yml: Pages base path /deskestra/, build env VITE_BASE_PATH: /deskestra/.
    • .github/workflows/ci.yml: any commands invoking deckd / deckctl / deckd-dev.
  10. Cleanup: delete daemon/deckd.egg-info/, client/dist/, .pytest_cache/, .mypy_cache/, __pycache__/. Rebuild from clean state. Run just lint test (or the equivalent) and verify.

Behavioral / data migration concerns

  • Existing user password file: anyone currently running deckd has ~/.config/deckd/password. On first run of deskestra, either (a) auto-migrate by reading the old path and writing the new one, or (b) require re-running the install/setup to generate a new password. Option (a) is nicer and one-line.
  • Browser localStorage key: a returning client will not find deskestra.password and will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.
  • GitHub Pages redirect: GitHub preserves redirects on repo rename, but the Pages site itself is tied to the new repo path — there will be a brief window where old links 404. Acceptable since no users yet.
  • D-Bus name conflict: since org.deckd.Focus and org.deskestra.Focus are different bus names, there is no risk of two instances colliding on the session bus during a transition. But anyone with the old GNOME extension installed will be publishing to the old name — uninstall the old extension before installing the new one.

Verification ladder (run in order)

  1. just lint — type/format/lint.
  2. just test — Python suite, including the renamed test_deskestra_window.py.
  3. Build the GNOME extension zip from packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
  4. Build the client (just client-build or whatever it is) and load it against a running daemon. Confirm:
    • WebSocket connects (uses the new env var).
    • localStorage key is deskestra.password.
    • HTML title is Deskestra.
  5. just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
  6. Live smoke: python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
  7. Live smoke: python scripts/smoke_mpris_live.py — same.
  8. Manual: load a layout that includes raise-window / focus actions, verify the extension is publishing to org.deskestra.Focus.

What I deliberately did not put in this plan

  • Renaming the product concept of a "deck" (a deck of controls, a button deck, deck layout, deck grid) — those stay.
  • Renaming third-party product references (Stream Deck, Macro Deck, WebDeck, Deckboard, OpenDeck, Loupedeck) — those are third parties.
  • Renaming Git history — git log will still show the old name. A git filter-repo could rewrite history but it's not worth it pre-launch.

Open questions for the human reviewer

  1. deckctldeskctl (recommended), or something else?
  2. Do we keep the daemon binary name as deskestra, or use deskestrad/deskestra-service after all? (You said you don't like the -d suffix; I agree.)
  3. Repo rename on GitHub happens before, during, or after the code cutover?
  4. Old-password-file auto-migration on first run, or fresh setup?
  5. Anything else in the inventory I missed — deckd.egg-info style build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      Rename project: deckd → deskestra #145

      Description

      @jonocodes

      Why

      The app has outgrown its original framing. It's no longer "an app controller" — it's a full desktop control surface that drives apps, windows, the desktop, and the GNOME/KDE/macOS sessions around them. The name deckd was a nod to Stream Deck and reads narrowly. We want a name that:

      • keeps desk (drives home "you control a screen, not a server"),
      • carries the conducting thread (you conduct your desktop, you don't just press buttons),
      • reads as one natural word with one obvious spelling,
      • is coined enough to be brand-/domain-/product-safe.

      The recent layoutdeck cutover (issue #105) already gave us internal vocabulary room. Now we want the project itself to move.

      Decision: deskestra

      desk + orche·stra → you conduct your desktop. Three syllables, one spelling, easy to say aloud.

      Across every candidate we researched, deskestra is the only one that came back genuinely unclaimed — no product, package, org, or app. The desk- sound space is occupied by Deskera (funded cloud ERP) and DeskRoll (a remote-desktop product), but deskestra is far enough from both (extra syllable, different ending) to stand apart.

      Service name: deskestra (no -d suffix — agreed that <name>d feels dated).
      GNOME extension UUID: deskestra-focus@local.
      KWin script ID: deskestra-focus.
      Password file: ~/.config/deskestra/password.
      Python distribution / entry points: deskestra, deskestra-dev, deskctl (keeping deckctl — it's a CLI for the daemon, not the daemon itself; rename candidate TBD).

      Names considered

      Clustered by approach. Each entry: name → one-line verdict → why.

      A. Keep deckd (reposition instead of rename) — rejected

      • deckd → keep, reframe positioning. Cheapest path. Rejected because we explicitly said the cutover doesn't scare us and we want the broader positioning baked into the name itself.

      B. Generic conducting/control surface — rejected, all taken or weak

      • Helm → "you steer the desktop from it." Strong, but collides with Kubernetes Helm.
      • Maestro → conducting metaphor, distinctive. Prior art: Mobile "Maestro" test tool. Rejected as overused.
      • Conductor → conducting + dispatch. conductord ugly. Rejected as overused.
      • Conduit → routing metaphor. Rejected: reads as internal plumbing, not conducting.
      • Podium → fresh, low collision. Rejected: you don't want "pod."
      • Panel / Console / Dispatch / Relay / Pilot → various. Rejected for over-use or vibe mismatch.

      C. desk + fused coined suffix (the vein we liked)

      • Deskpit (cockpit) → rejected, you don't like "pit."
      • Deskata → taken-ish (deskata.com exists; risks Deskera echo).
      • Deskra → basically a homophone of Deskera ERP (funded, 80k+ users). Hard no.
      • Deskira / Deskora / Deskana / Deskyra / Deskade → variations. Most share the Deskera phonetic overlap; none meaningfully better than deskestra.
      • Deskomand / Deskommand / Deskrix / Deskrex / Deskura / Deskept → control-rooted variants. Various fatal flaws (skept-adjacent, "command" doesn't fuse, etc.).
      • Deskrol → direct collision with DeskRoll (existing remote-desktop product). Hard no.
      • Deskrola → deskrol + softened; Polish "deskorolki" + Deskera echo. Compromised.
      • Deskestra ✅ → clean, conducting-fused, no collision. Winner.

      D. control family, no desk — rejected, all crowded

      • Controlla → near-identical open-source "phone-as-keyboard/mouse-for-desktop" project already uses the name (okekedev/controlla). Plus CAREL Controlla (HVAC controllers). Hard no.
      • Controla → spread thin across active apps (Controla cybersecurity, ControlA university mgmt, Controla.ai org). Diluted.
      • Kontroll / Kontrol → stylized bare spelling. Rejected earlier: dated, un-searchable.
      • Ctrl / Ctrla → literal keyboard modifier, on-theme. Rejected: ambiguous to say aloud.
      • Controlla / Drake collision → also a Drake song. Bonus reason not to.

      E. Less-literal control roots (Latin / Greek / French) — rejected, all occupied

      • Dirigo (Latin "I direct") → several active software cos (Dirigo Software accident reconstruction, etc.).
      • Regie (French "control room") → Regie.ai dominates.
      • Kyber (kubernan root) → saturated. KDE has a "Kyber" cross-platform remote-control product, plus post-quantum crypto, plus k8s deploy tool.
      • Imperia (imperare "to command") → Imperia SCM + Imperia Online game.

      Pattern: anything that obviously means control/command/steer has been claimed, usually more than once. Coined fusions (deskestra) escape the saturation.


      Rename blast-radius plan: deckddeskestra

      Generated by an exhaustive inventory of the repo. Hard references are listed explicitly. Soft references (comments, prose, generic "deck" usage as a product concept like "button deck") are not listed and should not be mechanically renamed.

      Naming decisions (commit these first)

      OldNew
      Project name deckddeskestra
      Python distribution deckddeskestra
      Python package daemon/deckd/daemon/deskestra/
      Executable deckddeskestra
      Executable deckd-devdeskestra-dev
      Executable deckctldeskctl (keep, but see open question below)
      Service unit deckd.servicedeskestra.service
      LaunchAgent com.deckd.daemoncom.deskestra.daemon
      NixOS module services.deckd-spikeservices.deskestra-spike
      NixOS script deckd-spike-startdeskestra-spike-start
      udev rules 70-deckd-uinput.rules70-deskestra-uinput.rules
      GNOME UUID deckd-focus@localdeskestra-focus@local
      KWin script ID deckd-focusdeskestra-focus
      D-Bus bus org.deckd.Focusorg.deskestra.Focus
      D-Bus path /org/deckd/Focus/org/deskestra/Focus
      D-Bus interface org.deckd.Focusorg.deskestra.Focus
      Env DECKD_PASSWORDDESKESTRA_PASSWORD
      Env DECKD_FAKE_MPRISDESKESTRA_FAKE_MPRIS
      Env DECKD_TLS_DIR / DECKD_TLS_HOSTDESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST
      Env VITE_DECKD_WSVITE_DESKESTRA_WS
      Password file ~/.config/deckd/password~/.config/deskestra/password
      LaunchAgent log @PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.log
      Browser localStorage key deckd.passworddeskestra.password
      HTTP header X-Deckd-PasswordX-Deskestra-Password
      GitHub Pages base /deckd//deskestra/
      Pages URL jonocodes.github.io/deckd/jonocodes.github.io/deskestra/
      Client PWA name / short_namedeskestra
      HTML <title>Deskestra
      macOS PWA apple-mobile-web-app-titleDeskestra
      Auto-ignore logic: window titled "deckd"window titled "deskestra"
      Test fixture suffix deckd-smoke-* etc.deskestra-smoke-*
      CLI metric prefix deckd_ (e.g. deckd_actions_total)deskestra_
      Class DeckdFocusDBusServiceDeskestraFocusDBusService
      Class DeckdFocusCacheDeskestraFocusCache
      Class DeckdFocusExtensionDeskestraFocusExtension
      Class attr Server._is_deckd_windowServer._is_deskestra_window
      Setuptools include pattern deckd*deskestra*
      Repo URL github.com/jonocodes/deckdgithub.com/jonocodes/deskestra

      Open question on deckctl: it's the CLI, not the daemon. Options: rename to deskctl (consistent), rename to deskestractl (verbose), or rename to something functional like deskestra-cli. Leaning toward deskctl for symmetry. Worth a quick opinion before the cutover.

      Mechanical order of operations

      Do these in order so the tree is never in a half-renamed state for more than a commit.

      1. Pre-flight: confirm availability — deskestra.com/.app, github.com/deskestra, npmjs.com/package/deskestra, PyPI deskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)
      2. Lock the new identifiers by editing these in one commit:
        • pyproject.tomlname, [project.scripts] (deskestra, deskestra-dev), deskctl decision, package include pattern.
        • daemon/deckd/daemon/deskestra/ (rename the package directory itself; every internal import updates accordingly).
        • All from deckd… / import deckd… (the directory rename takes care of most; touch up external references).
        • All DECKD_* and VITE_DECKD_WS env var names.
        • ~/.config/deckd/password~/.config/deskestra/password in auth.py, systemd unit, launchd plist, README, docs.
        • D-Bus: org.deckd.Focus, /org/deckd/Focus, interface name — in platform.py, GNOME extension.js, KWin main.js, all scripts/* and tests/*.
        • Class names Deckd*Deskestra* (DBus service, cache, extension class, _is_deckd_window).
        • Auto-ignore logic: "deckd" literal → "deskestra" (and case-insensitive match).
      3. Packaging: rename and update these files/paths:
        • packaging/systemd/deckd.servicepackaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
        • packaging/launchd/com.deckd.daemon.plistpackaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
        • packaging/nixos/deckd-spike.nixpackaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
        • packaging/udev/70-deckd-uinput.rulespackaging/udev/70-deskestra-uinput.rules.
        • packaging/gnome-shell/deckd-focus@local/packaging/gnome-shell/deskestra-focus@local/; metadata.json uuid + name + description; extension.js bus/path/interface; wire-shape.js import path.
        • packaging/kwin-script/deckd-focus/packaging/kwin-script/deskestra-focus/; metadata.json Id + Name; main.js bus/path/interface + comment header.
      4. Justfile: update every recipe that invokes deckd, deckd-dev, deckctl, references the extension UUID, or hardcodes paths. (See "Justfile" in the inventory — this is one of the larger files to edit.) Also update the GNOME pack/install recipe (packaging/gnome-shell/...@local) and the KWin install recipe ($HOME/.local/share/kwin/scripts/deskestra-focus/...).
      5. Client (client/):
        • package.json: namedeskestra-client (or just deskestra).
        • index.html: <title> and apple-mobile-web-app-title.
        • vite.config.ts: VITE_DECKD_WSVITE_DESKESTRA_WS.
        • public/manifest.json and public/icon.svg: display name + aria-label.
        • src/socket.ts: localStorage key deckd.passworddeskestra.password; any error text containing deckd.
        • playwright.config.ts: DECKD_FAKE_MPRIS env var name.
      6. Scripts (scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
      7. Tests (tests/):
        • Update all from deckd… imports.
        • Update all D-Bus name assertions (org.deckd.Focus).
        • Update Deckd* class assertions.
        • Rename tests/test_deckd_window.pytests/test_deskestra_window.py and update test names + the tests/.pytest_cache nodeids will regenerate.
        • Rename e2e client/e2e/daemon-log.ts references if any contain the binary name.
      8. Docs: sweep README.md, CONTEXT.md, AGENTS.md, docs/ONBOARDING.md, docs/REFERENCE.md, docs/TESTING.md, docs/SPIKES.md, docs/ARCHITECTURE.md, docs/research/landscape.md, docs/research/issue-56-macos-nowplaying.md, all docs/adr/*.md. Do not mechanically replace generic "deck" usage — that word is now the product concept (a "deck" is a control surface) and stays. Only the project identifier deckd / Deckd changes.
      9. GitHub-side:
        • Rename the repo (github.com/jonocodes/deckdgithub.com/jonocodes/deskestra) — GitHub redirects old URLs but update local git remote after.
        • .github/workflows/deploy-pages.yml: Pages base path /deskestra/, build env VITE_BASE_PATH: /deskestra/.
        • .github/workflows/ci.yml: any commands invoking deckd / deckctl / deckd-dev.
      10. Cleanup: delete daemon/deckd.egg-info/, client/dist/, .pytest_cache/, .mypy_cache/, __pycache__/. Rebuild from clean state. Run just lint test (or the equivalent) and verify.

      Behavioral / data migration concerns

      • Existing user password file: anyone currently running deckd has ~/.config/deckd/password. On first run of deskestra, either (a) auto-migrate by reading the old path and writing the new one, or (b) require re-running the install/setup to generate a new password. Option (a) is nicer and one-line.
      • Browser localStorage key: a returning client will not find deskestra.password and will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.
      • GitHub Pages redirect: GitHub preserves redirects on repo rename, but the Pages site itself is tied to the new repo path — there will be a brief window where old links 404. Acceptable since no users yet.
      • D-Bus name conflict: since org.deckd.Focus and org.deskestra.Focus are different bus names, there is no risk of two instances colliding on the session bus during a transition. But anyone with the old GNOME extension installed will be publishing to the old name — uninstall the old extension before installing the new one.

      Verification ladder (run in order)

      1. just lint — type/format/lint.
      2. just test — Python suite, including the renamed test_deskestra_window.py.
      3. Build the GNOME extension zip from packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
      4. Build the client (just client-build or whatever it is) and load it against a running daemon. Confirm:
        • WebSocket connects (uses the new env var).
        • localStorage key is deskestra.password.
        • HTML title is Deskestra.
      5. just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
      6. Live smoke: python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
      7. Live smoke: python scripts/smoke_mpris_live.py — same.
      8. Manual: load a layout that includes raise-window / focus actions, verify the extension is publishing to org.deskestra.Focus.

      What I deliberately did not put in this plan

      • Renaming the product concept of a "deck" (a deck of controls, a button deck, deck layout, deck grid) — those stay.
      • Renaming third-party product references (Stream Deck, Macro Deck, WebDeck, Deckboard, OpenDeck, Loupedeck) — those are third parties.
      • Renaming Git history — git log will still show the old name. A git filter-repo could rewrite history but it's not worth it pre-launch.

      Open questions for the human reviewer

      1. deckctldeskctl (recommended), or something else?
      2. Do we keep the daemon binary name as deskestra, or use deskestrad/deskestra-service after all? (You said you don't like the -d suffix; I agree.)
      3. Repo rename on GitHub happens before, during, or after the code cutover?
      4. Old-password-file auto-migration on first run, or fresh setup?
      5. Anything else in the inventory I missed — deckd.egg-info style build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        documentationImprovements or additions to documentationenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          Rename project: deckd → deskestra #145

          Description

          @jonocodes

          Why

          The app has outgrown its original framing. It's no longer "an app controller" — it's a full desktop control surface that drives apps, windows, the desktop, and the GNOME/KDE/macOS sessions around them. The name deckd was a nod to Stream Deck and reads narrowly. We want a name that:

          • keeps desk (drives home "you control a screen, not a server"),
          • carries the conducting thread (you conduct your desktop, you don't just press buttons),
          • reads as one natural word with one obvious spelling,
          • is coined enough to be brand-/domain-/product-safe.

          The recent layoutdeck cutover (issue #105) already gave us internal vocabulary room. Now we want the project itself to move.

          Decision: deskestra

          desk + orche·stra → you conduct your desktop. Three syllables, one spelling, easy to say aloud.

          Across every candidate we researched, deskestra is the only one that came back genuinely unclaimed — no product, package, org, or app. The desk- sound space is occupied by Deskera (funded cloud ERP) and DeskRoll (a remote-desktop product), but deskestra is far enough from both (extra syllable, different ending) to stand apart.

          Service name: deskestra (no -d suffix — agreed that <name>d feels dated).
          GNOME extension UUID: deskestra-focus@local.
          KWin script ID: deskestra-focus.
          Password file: ~/.config/deskestra/password.
          Python distribution / entry points: deskestra, deskestra-dev, deskctl (keeping deckctl — it's a CLI for the daemon, not the daemon itself; rename candidate TBD).

          Names considered

          Clustered by approach. Each entry: name → one-line verdict → why.

          A. Keep deckd (reposition instead of rename) — rejected

          • deckd → keep, reframe positioning. Cheapest path. Rejected because we explicitly said the cutover doesn't scare us and we want the broader positioning baked into the name itself.

          B. Generic conducting/control surface — rejected, all taken or weak

          • Helm → "you steer the desktop from it." Strong, but collides with Kubernetes Helm.
          • Maestro → conducting metaphor, distinctive. Prior art: Mobile "Maestro" test tool. Rejected as overused.
          • Conductor → conducting + dispatch. conductord ugly. Rejected as overused.
          • Conduit → routing metaphor. Rejected: reads as internal plumbing, not conducting.
          • Podium → fresh, low collision. Rejected: you don't want "pod."
          • Panel / Console / Dispatch / Relay / Pilot → various. Rejected for over-use or vibe mismatch.

          C. desk + fused coined suffix (the vein we liked)

          • Deskpit (cockpit) → rejected, you don't like "pit."
          • Deskata → taken-ish (deskata.com exists; risks Deskera echo).
          • Deskra → basically a homophone of Deskera ERP (funded, 80k+ users). Hard no.
          • Deskira / Deskora / Deskana / Deskyra / Deskade → variations. Most share the Deskera phonetic overlap; none meaningfully better than deskestra.
          • Deskomand / Deskommand / Deskrix / Deskrex / Deskura / Deskept → control-rooted variants. Various fatal flaws (skept-adjacent, "command" doesn't fuse, etc.).
          • Deskrol → direct collision with DeskRoll (existing remote-desktop product). Hard no.
          • Deskrola → deskrol + softened; Polish "deskorolki" + Deskera echo. Compromised.
          • Deskestra ✅ → clean, conducting-fused, no collision. Winner.

          D. control family, no desk — rejected, all crowded

          • Controlla → near-identical open-source "phone-as-keyboard/mouse-for-desktop" project already uses the name (okekedev/controlla). Plus CAREL Controlla (HVAC controllers). Hard no.
          • Controla → spread thin across active apps (Controla cybersecurity, ControlA university mgmt, Controla.ai org). Diluted.
          • Kontroll / Kontrol → stylized bare spelling. Rejected earlier: dated, un-searchable.
          • Ctrl / Ctrla → literal keyboard modifier, on-theme. Rejected: ambiguous to say aloud.
          • Controlla / Drake collision → also a Drake song. Bonus reason not to.

          E. Less-literal control roots (Latin / Greek / French) — rejected, all occupied

          • Dirigo (Latin "I direct") → several active software cos (Dirigo Software accident reconstruction, etc.).
          • Regie (French "control room") → Regie.ai dominates.
          • Kyber (kubernan root) → saturated. KDE has a "Kyber" cross-platform remote-control product, plus post-quantum crypto, plus k8s deploy tool.
          • Imperia (imperare "to command") → Imperia SCM + Imperia Online game.

          Pattern: anything that obviously means control/command/steer has been claimed, usually more than once. Coined fusions (deskestra) escape the saturation.


          Rename blast-radius plan: deckddeskestra

          Generated by an exhaustive inventory of the repo. Hard references are listed explicitly. Soft references (comments, prose, generic "deck" usage as a product concept like "button deck") are not listed and should not be mechanically renamed.

          Naming decisions (commit these first)

          OldNew
          Project name deckddeskestra
          Python distribution deckddeskestra
          Python package daemon/deckd/daemon/deskestra/
          Executable deckddeskestra
          Executable deckd-devdeskestra-dev
          Executable deckctldeskctl (keep, but see open question below)
          Service unit deckd.servicedeskestra.service
          LaunchAgent com.deckd.daemoncom.deskestra.daemon
          NixOS module services.deckd-spikeservices.deskestra-spike
          NixOS script deckd-spike-startdeskestra-spike-start
          udev rules 70-deckd-uinput.rules70-deskestra-uinput.rules
          GNOME UUID deckd-focus@localdeskestra-focus@local
          KWin script ID deckd-focusdeskestra-focus
          D-Bus bus org.deckd.Focusorg.deskestra.Focus
          D-Bus path /org/deckd/Focus/org/deskestra/Focus
          D-Bus interface org.deckd.Focusorg.deskestra.Focus
          Env DECKD_PASSWORDDESKESTRA_PASSWORD
          Env DECKD_FAKE_MPRISDESKESTRA_FAKE_MPRIS
          Env DECKD_TLS_DIR / DECKD_TLS_HOSTDESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST
          Env VITE_DECKD_WSVITE_DESKESTRA_WS
          Password file ~/.config/deckd/password~/.config/deskestra/password
          LaunchAgent log @PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.log
          Browser localStorage key deckd.passworddeskestra.password
          HTTP header X-Deckd-PasswordX-Deskestra-Password
          GitHub Pages base /deckd//deskestra/
          Pages URL jonocodes.github.io/deckd/jonocodes.github.io/deskestra/
          Client PWA name / short_namedeskestra
          HTML <title>Deskestra
          macOS PWA apple-mobile-web-app-titleDeskestra
          Auto-ignore logic: window titled "deckd"window titled "deskestra"
          Test fixture suffix deckd-smoke-* etc.deskestra-smoke-*
          CLI metric prefix deckd_ (e.g. deckd_actions_total)deskestra_
          Class DeckdFocusDBusServiceDeskestraFocusDBusService
          Class DeckdFocusCacheDeskestraFocusCache
          Class DeckdFocusExtensionDeskestraFocusExtension
          Class attr Server._is_deckd_windowServer._is_deskestra_window
          Setuptools include pattern deckd*deskestra*
          Repo URL github.com/jonocodes/deckdgithub.com/jonocodes/deskestra

          Open question on deckctl: it's the CLI, not the daemon. Options: rename to deskctl (consistent), rename to deskestractl (verbose), or rename to something functional like deskestra-cli. Leaning toward deskctl for symmetry. Worth a quick opinion before the cutover.

          Mechanical order of operations

          Do these in order so the tree is never in a half-renamed state for more than a commit.

          1. Pre-flight: confirm availability — deskestra.com/.app, github.com/deskestra, npmjs.com/package/deskestra, PyPI deskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)
          2. Lock the new identifiers by editing these in one commit:
            • pyproject.tomlname, [project.scripts] (deskestra, deskestra-dev), deskctl decision, package include pattern.
            • daemon/deckd/daemon/deskestra/ (rename the package directory itself; every internal import updates accordingly).
            • All from deckd… / import deckd… (the directory rename takes care of most; touch up external references).
            • All DECKD_* and VITE_DECKD_WS env var names.
            • ~/.config/deckd/password~/.config/deskestra/password in auth.py, systemd unit, launchd plist, README, docs.
            • D-Bus: org.deckd.Focus, /org/deckd/Focus, interface name — in platform.py, GNOME extension.js, KWin main.js, all scripts/* and tests/*.
            • Class names Deckd*Deskestra* (DBus service, cache, extension class, _is_deckd_window).
            • Auto-ignore logic: "deckd" literal → "deskestra" (and case-insensitive match).
          3. Packaging: rename and update these files/paths:
            • packaging/systemd/deckd.servicepackaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
            • packaging/launchd/com.deckd.daemon.plistpackaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
            • packaging/nixos/deckd-spike.nixpackaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
            • packaging/udev/70-deckd-uinput.rulespackaging/udev/70-deskestra-uinput.rules.
            • packaging/gnome-shell/deckd-focus@local/packaging/gnome-shell/deskestra-focus@local/; metadata.json uuid + name + description; extension.js bus/path/interface; wire-shape.js import path.
            • packaging/kwin-script/deckd-focus/packaging/kwin-script/deskestra-focus/; metadata.json Id + Name; main.js bus/path/interface + comment header.
          4. Justfile: update every recipe that invokes deckd, deckd-dev, deckctl, references the extension UUID, or hardcodes paths. (See "Justfile" in the inventory — this is one of the larger files to edit.) Also update the GNOME pack/install recipe (packaging/gnome-shell/...@local) and the KWin install recipe ($HOME/.local/share/kwin/scripts/deskestra-focus/...).
          5. Client (client/):
            • package.json: namedeskestra-client (or just deskestra).
            • index.html: <title> and apple-mobile-web-app-title.
            • vite.config.ts: VITE_DECKD_WSVITE_DESKESTRA_WS.
            • public/manifest.json and public/icon.svg: display name + aria-label.
            • src/socket.ts: localStorage key deckd.passworddeskestra.password; any error text containing deckd.
            • playwright.config.ts: DECKD_FAKE_MPRIS env var name.
          6. Scripts (scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
          7. Tests (tests/):
            • Update all from deckd… imports.
            • Update all D-Bus name assertions (org.deckd.Focus).
            • Update Deckd* class assertions.
            • Rename tests/test_deckd_window.pytests/test_deskestra_window.py and update test names + the tests/.pytest_cache nodeids will regenerate.
            • Rename e2e client/e2e/daemon-log.ts references if any contain the binary name.
          8. Docs: sweep README.md, CONTEXT.md, AGENTS.md, docs/ONBOARDING.md, docs/REFERENCE.md, docs/TESTING.md, docs/SPIKES.md, docs/ARCHITECTURE.md, docs/research/landscape.md, docs/research/issue-56-macos-nowplaying.md, all docs/adr/*.md. Do not mechanically replace generic "deck" usage — that word is now the product concept (a "deck" is a control surface) and stays. Only the project identifier deckd / Deckd changes.
          9. GitHub-side:
            • Rename the repo (github.com/jonocodes/deckdgithub.com/jonocodes/deskestra) — GitHub redirects old URLs but update local git remote after.
            • .github/workflows/deploy-pages.yml: Pages base path /deskestra/, build env VITE_BASE_PATH: /deskestra/.
            • .github/workflows/ci.yml: any commands invoking deckd / deckctl / deckd-dev.
          10. Cleanup: delete daemon/deckd.egg-info/, client/dist/, .pytest_cache/, .mypy_cache/, __pycache__/. Rebuild from clean state. Run just lint test (or the equivalent) and verify.

          Behavioral / data migration concerns

          • Existing user password file: anyone currently running deckd has ~/.config/deckd/password. On first run of deskestra, either (a) auto-migrate by reading the old path and writing the new one, or (b) require re-running the install/setup to generate a new password. Option (a) is nicer and one-line.
          • Browser localStorage key: a returning client will not find deskestra.password and will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.
          • GitHub Pages redirect: GitHub preserves redirects on repo rename, but the Pages site itself is tied to the new repo path — there will be a brief window where old links 404. Acceptable since no users yet.
          • D-Bus name conflict: since org.deckd.Focus and org.deskestra.Focus are different bus names, there is no risk of two instances colliding on the session bus during a transition. But anyone with the old GNOME extension installed will be publishing to the old name — uninstall the old extension before installing the new one.

          Verification ladder (run in order)

          1. just lint — type/format/lint.
          2. just test — Python suite, including the renamed test_deskestra_window.py.
          3. Build the GNOME extension zip from packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
          4. Build the client (just client-build or whatever it is) and load it against a running daemon. Confirm:
            • WebSocket connects (uses the new env var).
            • localStorage key is deskestra.password.
            • HTML title is Deskestra.
          5. just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
          6. Live smoke: python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
          7. Live smoke: python scripts/smoke_mpris_live.py — same.
          8. Manual: load a layout that includes raise-window / focus actions, verify the extension is publishing to org.deskestra.Focus.

          What I deliberately did not put in this plan

          • Renaming the product concept of a "deck" (a deck of controls, a button deck, deck layout, deck grid) — those stay.
          • Renaming third-party product references (Stream Deck, Macro Deck, WebDeck, Deckboard, OpenDeck, Loupedeck) — those are third parties.
          • Renaming Git history — git log will still show the old name. A git filter-repo could rewrite history but it's not worth it pre-launch.

          Open questions for the human reviewer

          1. deckctldeskctl (recommended), or something else?
          2. Do we keep the daemon binary name as deskestra, or use deskestrad/deskestra-service after all? (You said you don't like the -d suffix; I agree.)
          3. Repo rename on GitHub happens before, during, or after the code cutover?
          4. Old-password-file auto-migration on first run, or fresh setup?
          5. Anything else in the inventory I missed — deckd.egg-info style build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            documentationImprovements or additions to documentationenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              Rename project: deckd → deskestra #145

              Description

              @jonocodes

              Why

              The app has outgrown its original framing. It's no longer "an app controller" — it's a full desktop control surface that drives apps, windows, the desktop, and the GNOME/KDE/macOS sessions around them. The name deckd was a nod to Stream Deck and reads narrowly. We want a name that:

              • keeps desk (drives home "you control a screen, not a server"),
              • carries the conducting thread (you conduct your desktop, you don't just press buttons),
              • reads as one natural word with one obvious spelling,
              • is coined enough to be brand-/domain-/product-safe.

              The recent layoutdeck cutover (issue #105) already gave us internal vocabulary room. Now we want the project itself to move.

              Decision: deskestra

              desk + orche·stra → you conduct your desktop. Three syllables, one spelling, easy to say aloud.

              Across every candidate we researched, deskestra is the only one that came back genuinely unclaimed — no product, package, org, or app. The desk- sound space is occupied by Deskera (funded cloud ERP) and DeskRoll (a remote-desktop product), but deskestra is far enough from both (extra syllable, different ending) to stand apart.

              Service name: deskestra (no -d suffix — agreed that <name>d feels dated).
              GNOME extension UUID: deskestra-focus@local.
              KWin script ID: deskestra-focus.
              Password file: ~/.config/deskestra/password.
              Python distribution / entry points: deskestra, deskestra-dev, deskctl (keeping deckctl — it's a CLI for the daemon, not the daemon itself; rename candidate TBD).

              Names considered

              Clustered by approach. Each entry: name → one-line verdict → why.

              A. Keep deckd (reposition instead of rename) — rejected

              • deckd → keep, reframe positioning. Cheapest path. Rejected because we explicitly said the cutover doesn't scare us and we want the broader positioning baked into the name itself.

              B. Generic conducting/control surface — rejected, all taken or weak

              • Helm → "you steer the desktop from it." Strong, but collides with Kubernetes Helm.
              • Maestro → conducting metaphor, distinctive. Prior art: Mobile "Maestro" test tool. Rejected as overused.
              • Conductor → conducting + dispatch. conductord ugly. Rejected as overused.
              • Conduit → routing metaphor. Rejected: reads as internal plumbing, not conducting.
              • Podium → fresh, low collision. Rejected: you don't want "pod."
              • Panel / Console / Dispatch / Relay / Pilot → various. Rejected for over-use or vibe mismatch.

              C. desk + fused coined suffix (the vein we liked)

              • Deskpit (cockpit) → rejected, you don't like "pit."
              • Deskata → taken-ish (deskata.com exists; risks Deskera echo).
              • Deskra → basically a homophone of Deskera ERP (funded, 80k+ users). Hard no.
              • Deskira / Deskora / Deskana / Deskyra / Deskade → variations. Most share the Deskera phonetic overlap; none meaningfully better than deskestra.
              • Deskomand / Deskommand / Deskrix / Deskrex / Deskura / Deskept → control-rooted variants. Various fatal flaws (skept-adjacent, "command" doesn't fuse, etc.).
              • Deskrol → direct collision with DeskRoll (existing remote-desktop product). Hard no.
              • Deskrola → deskrol + softened; Polish "deskorolki" + Deskera echo. Compromised.
              • Deskestra ✅ → clean, conducting-fused, no collision. Winner.

              D. control family, no desk — rejected, all crowded

              • Controlla → near-identical open-source "phone-as-keyboard/mouse-for-desktop" project already uses the name (okekedev/controlla). Plus CAREL Controlla (HVAC controllers). Hard no.
              • Controla → spread thin across active apps (Controla cybersecurity, ControlA university mgmt, Controla.ai org). Diluted.
              • Kontroll / Kontrol → stylized bare spelling. Rejected earlier: dated, un-searchable.
              • Ctrl / Ctrla → literal keyboard modifier, on-theme. Rejected: ambiguous to say aloud.
              • Controlla / Drake collision → also a Drake song. Bonus reason not to.

              E. Less-literal control roots (Latin / Greek / French) — rejected, all occupied

              • Dirigo (Latin "I direct") → several active software cos (Dirigo Software accident reconstruction, etc.).
              • Regie (French "control room") → Regie.ai dominates.
              • Kyber (kubernan root) → saturated. KDE has a "Kyber" cross-platform remote-control product, plus post-quantum crypto, plus k8s deploy tool.
              • Imperia (imperare "to command") → Imperia SCM + Imperia Online game.

              Pattern: anything that obviously means control/command/steer has been claimed, usually more than once. Coined fusions (deskestra) escape the saturation.


              Rename blast-radius plan: deckddeskestra

              Generated by an exhaustive inventory of the repo. Hard references are listed explicitly. Soft references (comments, prose, generic "deck" usage as a product concept like "button deck") are not listed and should not be mechanically renamed.

              Naming decisions (commit these first)

              OldNew
              Project name deckddeskestra
              Python distribution deckddeskestra
              Python package daemon/deckd/daemon/deskestra/
              Executable deckddeskestra
              Executable deckd-devdeskestra-dev
              Executable deckctldeskctl (keep, but see open question below)
              Service unit deckd.servicedeskestra.service
              LaunchAgent com.deckd.daemoncom.deskestra.daemon
              NixOS module services.deckd-spikeservices.deskestra-spike
              NixOS script deckd-spike-startdeskestra-spike-start
              udev rules 70-deckd-uinput.rules70-deskestra-uinput.rules
              GNOME UUID deckd-focus@localdeskestra-focus@local
              KWin script ID deckd-focusdeskestra-focus
              D-Bus bus org.deckd.Focusorg.deskestra.Focus
              D-Bus path /org/deckd/Focus/org/deskestra/Focus
              D-Bus interface org.deckd.Focusorg.deskestra.Focus
              Env DECKD_PASSWORDDESKESTRA_PASSWORD
              Env DECKD_FAKE_MPRISDESKESTRA_FAKE_MPRIS
              Env DECKD_TLS_DIR / DECKD_TLS_HOSTDESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST
              Env VITE_DECKD_WSVITE_DESKESTRA_WS
              Password file ~/.config/deckd/password~/.config/deskestra/password
              LaunchAgent log @PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.log
              Browser localStorage key deckd.passworddeskestra.password
              HTTP header X-Deckd-PasswordX-Deskestra-Password
              GitHub Pages base /deckd//deskestra/
              Pages URL jonocodes.github.io/deckd/jonocodes.github.io/deskestra/
              Client PWA name / short_namedeskestra
              HTML <title>Deskestra
              macOS PWA apple-mobile-web-app-titleDeskestra
              Auto-ignore logic: window titled "deckd"window titled "deskestra"
              Test fixture suffix deckd-smoke-* etc.deskestra-smoke-*
              CLI metric prefix deckd_ (e.g. deckd_actions_total)deskestra_
              Class DeckdFocusDBusServiceDeskestraFocusDBusService
              Class DeckdFocusCacheDeskestraFocusCache
              Class DeckdFocusExtensionDeskestraFocusExtension
              Class attr Server._is_deckd_windowServer._is_deskestra_window
              Setuptools include pattern deckd*deskestra*
              Repo URL github.com/jonocodes/deckdgithub.com/jonocodes/deskestra

              Open question on deckctl: it's the CLI, not the daemon. Options: rename to deskctl (consistent), rename to deskestractl (verbose), or rename to something functional like deskestra-cli. Leaning toward deskctl for symmetry. Worth a quick opinion before the cutover.

              Mechanical order of operations

              Do these in order so the tree is never in a half-renamed state for more than a commit.

              1. Pre-flight: confirm availability — deskestra.com/.app, github.com/deskestra, npmjs.com/package/deskestra, PyPI deskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)
              2. Lock the new identifiers by editing these in one commit:
                • pyproject.tomlname, [project.scripts] (deskestra, deskestra-dev), deskctl decision, package include pattern.
                • daemon/deckd/daemon/deskestra/ (rename the package directory itself; every internal import updates accordingly).
                • All from deckd… / import deckd… (the directory rename takes care of most; touch up external references).
                • All DECKD_* and VITE_DECKD_WS env var names.
                • ~/.config/deckd/password~/.config/deskestra/password in auth.py, systemd unit, launchd plist, README, docs.
                • D-Bus: org.deckd.Focus, /org/deckd/Focus, interface name — in platform.py, GNOME extension.js, KWin main.js, all scripts/* and tests/*.
                • Class names Deckd*Deskestra* (DBus service, cache, extension class, _is_deckd_window).
                • Auto-ignore logic: "deckd" literal → "deskestra" (and case-insensitive match).
              3. Packaging: rename and update these files/paths:
                • packaging/systemd/deckd.servicepackaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
                • packaging/launchd/com.deckd.daemon.plistpackaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
                • packaging/nixos/deckd-spike.nixpackaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
                • packaging/udev/70-deckd-uinput.rulespackaging/udev/70-deskestra-uinput.rules.
                • packaging/gnome-shell/deckd-focus@local/packaging/gnome-shell/deskestra-focus@local/; metadata.json uuid + name + description; extension.js bus/path/interface; wire-shape.js import path.
                • packaging/kwin-script/deckd-focus/packaging/kwin-script/deskestra-focus/; metadata.json Id + Name; main.js bus/path/interface + comment header.
              4. Justfile: update every recipe that invokes deckd, deckd-dev, deckctl, references the extension UUID, or hardcodes paths. (See "Justfile" in the inventory — this is one of the larger files to edit.) Also update the GNOME pack/install recipe (packaging/gnome-shell/...@local) and the KWin install recipe ($HOME/.local/share/kwin/scripts/deskestra-focus/...).
              5. Client (client/):
                • package.json: namedeskestra-client (or just deskestra).
                • index.html: <title> and apple-mobile-web-app-title.
                • vite.config.ts: VITE_DECKD_WSVITE_DESKESTRA_WS.
                • public/manifest.json and public/icon.svg: display name + aria-label.
                • src/socket.ts: localStorage key deckd.passworddeskestra.password; any error text containing deckd.
                • playwright.config.ts: DECKD_FAKE_MPRIS env var name.
              6. Scripts (scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
              7. Tests (tests/):
                • Update all from deckd… imports.
                • Update all D-Bus name assertions (org.deckd.Focus).
                • Update Deckd* class assertions.
                • Rename tests/test_deckd_window.pytests/test_deskestra_window.py and update test names + the tests/.pytest_cache nodeids will regenerate.
                • Rename e2e client/e2e/daemon-log.ts references if any contain the binary name.
              8. Docs: sweep README.md, CONTEXT.md, AGENTS.md, docs/ONBOARDING.md, docs/REFERENCE.md, docs/TESTING.md, docs/SPIKES.md, docs/ARCHITECTURE.md, docs/research/landscape.md, docs/research/issue-56-macos-nowplaying.md, all docs/adr/*.md. Do not mechanically replace generic "deck" usage — that word is now the product concept (a "deck" is a control surface) and stays. Only the project identifier deckd / Deckd changes.
              9. GitHub-side:
                • Rename the repo (github.com/jonocodes/deckdgithub.com/jonocodes/deskestra) — GitHub redirects old URLs but update local git remote after.
                • .github/workflows/deploy-pages.yml: Pages base path /deskestra/, build env VITE_BASE_PATH: /deskestra/.
                • .github/workflows/ci.yml: any commands invoking deckd / deckctl / deckd-dev.
              10. Cleanup: delete daemon/deckd.egg-info/, client/dist/, .pytest_cache/, .mypy_cache/, __pycache__/. Rebuild from clean state. Run just lint test (or the equivalent) and verify.

              Behavioral / data migration concerns

              • Existing user password file: anyone currently running deckd has ~/.config/deckd/password. On first run of deskestra, either (a) auto-migrate by reading the old path and writing the new one, or (b) require re-running the install/setup to generate a new password. Option (a) is nicer and one-line.
              • Browser localStorage key: a returning client will not find deskestra.password and will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.
              • GitHub Pages redirect: GitHub preserves redirects on repo rename, but the Pages site itself is tied to the new repo path — there will be a brief window where old links 404. Acceptable since no users yet.
              • D-Bus name conflict: since org.deckd.Focus and org.deskestra.Focus are different bus names, there is no risk of two instances colliding on the session bus during a transition. But anyone with the old GNOME extension installed will be publishing to the old name — uninstall the old extension before installing the new one.

              Verification ladder (run in order)

              1. just lint — type/format/lint.
              2. just test — Python suite, including the renamed test_deskestra_window.py.
              3. Build the GNOME extension zip from packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
              4. Build the client (just client-build or whatever it is) and load it against a running daemon. Confirm:
                • WebSocket connects (uses the new env var).
                • localStorage key is deskestra.password.
                • HTML title is Deskestra.
              5. just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
              6. Live smoke: python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
              7. Live smoke: python scripts/smoke_mpris_live.py — same.
              8. Manual: load a layout that includes raise-window / focus actions, verify the extension is publishing to org.deskestra.Focus.

              What I deliberately did not put in this plan

              • Renaming the product concept of a "deck" (a deck of controls, a button deck, deck layout, deck grid) — those stay.
              • Renaming third-party product references (Stream Deck, Macro Deck, WebDeck, Deckboard, OpenDeck, Loupedeck) — those are third parties.
              • Renaming Git history — git log will still show the old name. A git filter-repo could rewrite history but it's not worth it pre-launch.

              Open questions for the human reviewer

              1. deckctldeskctl (recommended), or something else?
              2. Do we keep the daemon binary name as deskestra, or use deskestrad/deskestra-service after all? (You said you don't like the -d suffix; I agree.)
              3. Repo rename on GitHub happens before, during, or after the code cutover?
              4. Old-password-file auto-migration on first run, or fresh setup?
              5. Anything else in the inventory I missed — deckd.egg-info style build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                documentationImprovements or additions to documentationenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  Rename project: deckd → deskestra #145

                  Description

                  @jonocodes

                  Why

                  The app has outgrown its original framing. It's no longer "an app controller" — it's a full desktop control surface that drives apps, windows, the desktop, and the GNOME/KDE/macOS sessions around them. The name deckd was a nod to Stream Deck and reads narrowly. We want a name that:

                  • keeps desk (drives home "you control a screen, not a server"),
                  • carries the conducting thread (you conduct your desktop, you don't just press buttons),
                  • reads as one natural word with one obvious spelling,
                  • is coined enough to be brand-/domain-/product-safe.

                  The recent layoutdeck cutover (issue #105) already gave us internal vocabulary room. Now we want the project itself to move.

                  Decision: deskestra

                  desk + orche·stra → you conduct your desktop. Three syllables, one spelling, easy to say aloud.

                  Across every candidate we researched, deskestra is the only one that came back genuinely unclaimed — no product, package, org, or app. The desk- sound space is occupied by Deskera (funded cloud ERP) and DeskRoll (a remote-desktop product), but deskestra is far enough from both (extra syllable, different ending) to stand apart.

                  Service name: deskestra (no -d suffix — agreed that <name>d feels dated).
                  GNOME extension UUID: deskestra-focus@local.
                  KWin script ID: deskestra-focus.
                  Password file: ~/.config/deskestra/password.
                  Python distribution / entry points: deskestra, deskestra-dev, deskctl (keeping deckctl — it's a CLI for the daemon, not the daemon itself; rename candidate TBD).

                  Names considered

                  Clustered by approach. Each entry: name → one-line verdict → why.

                  A. Keep deckd (reposition instead of rename) — rejected

                  • deckd → keep, reframe positioning. Cheapest path. Rejected because we explicitly said the cutover doesn't scare us and we want the broader positioning baked into the name itself.

                  B. Generic conducting/control surface — rejected, all taken or weak

                  • Helm → "you steer the desktop from it." Strong, but collides with Kubernetes Helm.
                  • Maestro → conducting metaphor, distinctive. Prior art: Mobile "Maestro" test tool. Rejected as overused.
                  • Conductor → conducting + dispatch. conductord ugly. Rejected as overused.
                  • Conduit → routing metaphor. Rejected: reads as internal plumbing, not conducting.
                  • Podium → fresh, low collision. Rejected: you don't want "pod."
                  • Panel / Console / Dispatch / Relay / Pilot → various. Rejected for over-use or vibe mismatch.

                  C. desk + fused coined suffix (the vein we liked)

                  • Deskpit (cockpit) → rejected, you don't like "pit."
                  • Deskata → taken-ish (deskata.com exists; risks Deskera echo).
                  • Deskra → basically a homophone of Deskera ERP (funded, 80k+ users). Hard no.
                  • Deskira / Deskora / Deskana / Deskyra / Deskade → variations. Most share the Deskera phonetic overlap; none meaningfully better than deskestra.
                  • Deskomand / Deskommand / Deskrix / Deskrex / Deskura / Deskept → control-rooted variants. Various fatal flaws (skept-adjacent, "command" doesn't fuse, etc.).
                  • Deskrol → direct collision with DeskRoll (existing remote-desktop product). Hard no.
                  • Deskrola → deskrol + softened; Polish "deskorolki" + Deskera echo. Compromised.
                  • Deskestra ✅ → clean, conducting-fused, no collision. Winner.

                  D. control family, no desk — rejected, all crowded

                  • Controlla → near-identical open-source "phone-as-keyboard/mouse-for-desktop" project already uses the name (okekedev/controlla). Plus CAREL Controlla (HVAC controllers). Hard no.
                  • Controla → spread thin across active apps (Controla cybersecurity, ControlA university mgmt, Controla.ai org). Diluted.
                  • Kontroll / Kontrol → stylized bare spelling. Rejected earlier: dated, un-searchable.
                  • Ctrl / Ctrla → literal keyboard modifier, on-theme. Rejected: ambiguous to say aloud.
                  • Controlla / Drake collision → also a Drake song. Bonus reason not to.

                  E. Less-literal control roots (Latin / Greek / French) — rejected, all occupied

                  • Dirigo (Latin "I direct") → several active software cos (Dirigo Software accident reconstruction, etc.).
                  • Regie (French "control room") → Regie.ai dominates.
                  • Kyber (kubernan root) → saturated. KDE has a "Kyber" cross-platform remote-control product, plus post-quantum crypto, plus k8s deploy tool.
                  • Imperia (imperare "to command") → Imperia SCM + Imperia Online game.

                  Pattern: anything that obviously means control/command/steer has been claimed, usually more than once. Coined fusions (deskestra) escape the saturation.


                  Rename blast-radius plan: deckddeskestra

                  Generated by an exhaustive inventory of the repo. Hard references are listed explicitly. Soft references (comments, prose, generic "deck" usage as a product concept like "button deck") are not listed and should not be mechanically renamed.

                  Naming decisions (commit these first)

                  OldNew
                  Project name deckddeskestra
                  Python distribution deckddeskestra
                  Python package daemon/deckd/daemon/deskestra/
                  Executable deckddeskestra
                  Executable deckd-devdeskestra-dev
                  Executable deckctldeskctl (keep, but see open question below)
                  Service unit deckd.servicedeskestra.service
                  LaunchAgent com.deckd.daemoncom.deskestra.daemon
                  NixOS module services.deckd-spikeservices.deskestra-spike
                  NixOS script deckd-spike-startdeskestra-spike-start
                  udev rules 70-deckd-uinput.rules70-deskestra-uinput.rules
                  GNOME UUID deckd-focus@localdeskestra-focus@local
                  KWin script ID deckd-focusdeskestra-focus
                  D-Bus bus org.deckd.Focusorg.deskestra.Focus
                  D-Bus path /org/deckd/Focus/org/deskestra/Focus
                  D-Bus interface org.deckd.Focusorg.deskestra.Focus
                  Env DECKD_PASSWORDDESKESTRA_PASSWORD
                  Env DECKD_FAKE_MPRISDESKESTRA_FAKE_MPRIS
                  Env DECKD_TLS_DIR / DECKD_TLS_HOSTDESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST
                  Env VITE_DECKD_WSVITE_DESKESTRA_WS
                  Password file ~/.config/deckd/password~/.config/deskestra/password
                  LaunchAgent log @PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.log
                  Browser localStorage key deckd.passworddeskestra.password
                  HTTP header X-Deckd-PasswordX-Deskestra-Password
                  GitHub Pages base /deckd//deskestra/
                  Pages URL jonocodes.github.io/deckd/jonocodes.github.io/deskestra/
                  Client PWA name / short_namedeskestra
                  HTML <title>Deskestra
                  macOS PWA apple-mobile-web-app-titleDeskestra
                  Auto-ignore logic: window titled "deckd"window titled "deskestra"
                  Test fixture suffix deckd-smoke-* etc.deskestra-smoke-*
                  CLI metric prefix deckd_ (e.g. deckd_actions_total)deskestra_
                  Class DeckdFocusDBusServiceDeskestraFocusDBusService
                  Class DeckdFocusCacheDeskestraFocusCache
                  Class DeckdFocusExtensionDeskestraFocusExtension
                  Class attr Server._is_deckd_windowServer._is_deskestra_window
                  Setuptools include pattern deckd*deskestra*
                  Repo URL github.com/jonocodes/deckdgithub.com/jonocodes/deskestra

                  Open question on deckctl: it's the CLI, not the daemon. Options: rename to deskctl (consistent), rename to deskestractl (verbose), or rename to something functional like deskestra-cli. Leaning toward deskctl for symmetry. Worth a quick opinion before the cutover.

                  Mechanical order of operations

                  Do these in order so the tree is never in a half-renamed state for more than a commit.

                  1. Pre-flight: confirm availability — deskestra.com/.app, github.com/deskestra, npmjs.com/package/deskestra, PyPI deskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)
                  2. Lock the new identifiers by editing these in one commit:
                    • pyproject.tomlname, [project.scripts] (deskestra, deskestra-dev), deskctl decision, package include pattern.
                    • daemon/deckd/daemon/deskestra/ (rename the package directory itself; every internal import updates accordingly).
                    • All from deckd… / import deckd… (the directory rename takes care of most; touch up external references).
                    • All DECKD_* and VITE_DECKD_WS env var names.
                    • ~/.config/deckd/password~/.config/deskestra/password in auth.py, systemd unit, launchd plist, README, docs.
                    • D-Bus: org.deckd.Focus, /org/deckd/Focus, interface name — in platform.py, GNOME extension.js, KWin main.js, all scripts/* and tests/*.
                    • Class names Deckd*Deskestra* (DBus service, cache, extension class, _is_deckd_window).
                    • Auto-ignore logic: "deckd" literal → "deskestra" (and case-insensitive match).
                  3. Packaging: rename and update these files/paths:
                    • packaging/systemd/deckd.servicepackaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
                    • packaging/launchd/com.deckd.daemon.plistpackaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
                    • packaging/nixos/deckd-spike.nixpackaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
                    • packaging/udev/70-deckd-uinput.rulespackaging/udev/70-deskestra-uinput.rules.
                    • packaging/gnome-shell/deckd-focus@local/packaging/gnome-shell/deskestra-focus@local/; metadata.json uuid + name + description; extension.js bus/path/interface; wire-shape.js import path.
                    • packaging/kwin-script/deckd-focus/packaging/kwin-script/deskestra-focus/; metadata.json Id + Name; main.js bus/path/interface + comment header.
                  4. Justfile: update every recipe that invokes deckd, deckd-dev, deckctl, references the extension UUID, or hardcodes paths. (See "Justfile" in the inventory — this is one of the larger files to edit.) Also update the GNOME pack/install recipe (packaging/gnome-shell/...@local) and the KWin install recipe ($HOME/.local/share/kwin/scripts/deskestra-focus/...).
                  5. Client (client/):
                    • package.json: namedeskestra-client (or just deskestra).
                    • index.html: <title> and apple-mobile-web-app-title.
                    • vite.config.ts: VITE_DECKD_WSVITE_DESKESTRA_WS.
                    • public/manifest.json and public/icon.svg: display name + aria-label.
                    • src/socket.ts: localStorage key deckd.passworddeskestra.password; any error text containing deckd.
                    • playwright.config.ts: DECKD_FAKE_MPRIS env var name.
                  6. Scripts (scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
                  7. Tests (tests/):
                    • Update all from deckd… imports.
                    • Update all D-Bus name assertions (org.deckd.Focus).
                    • Update Deckd* class assertions.
                    • Rename tests/test_deckd_window.pytests/test_deskestra_window.py and update test names + the tests/.pytest_cache nodeids will regenerate.
                    • Rename e2e client/e2e/daemon-log.ts references if any contain the binary name.
                  8. Docs: sweep README.md, CONTEXT.md, AGENTS.md, docs/ONBOARDING.md, docs/REFERENCE.md, docs/TESTING.md, docs/SPIKES.md, docs/ARCHITECTURE.md, docs/research/landscape.md, docs/research/issue-56-macos-nowplaying.md, all docs/adr/*.md. Do not mechanically replace generic "deck" usage — that word is now the product concept (a "deck" is a control surface) and stays. Only the project identifier deckd / Deckd changes.
                  9. GitHub-side:
                    • Rename the repo (github.com/jonocodes/deckdgithub.com/jonocodes/deskestra) — GitHub redirects old URLs but update local git remote after.
                    • .github/workflows/deploy-pages.yml: Pages base path /deskestra/, build env VITE_BASE_PATH: /deskestra/.
                    • .github/workflows/ci.yml: any commands invoking deckd / deckctl / deckd-dev.
                  10. Cleanup: delete daemon/deckd.egg-info/, client/dist/, .pytest_cache/, .mypy_cache/, __pycache__/. Rebuild from clean state. Run just lint test (or the equivalent) and verify.

                  Behavioral / data migration concerns

                  • Existing user password file: anyone currently running deckd has ~/.config/deckd/password. On first run of deskestra, either (a) auto-migrate by reading the old path and writing the new one, or (b) require re-running the install/setup to generate a new password. Option (a) is nicer and one-line.
                  • Browser localStorage key: a returning client will not find deskestra.password and will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.
                  • GitHub Pages redirect: GitHub preserves redirects on repo rename, but the Pages site itself is tied to the new repo path — there will be a brief window where old links 404. Acceptable since no users yet.
                  • D-Bus name conflict: since org.deckd.Focus and org.deskestra.Focus are different bus names, there is no risk of two instances colliding on the session bus during a transition. But anyone with the old GNOME extension installed will be publishing to the old name — uninstall the old extension before installing the new one.

                  Verification ladder (run in order)

                  1. just lint — type/format/lint.
                  2. just test — Python suite, including the renamed test_deskestra_window.py.
                  3. Build the GNOME extension zip from packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
                  4. Build the client (just client-build or whatever it is) and load it against a running daemon. Confirm:
                    • WebSocket connects (uses the new env var).
                    • localStorage key is deskestra.password.
                    • HTML title is Deskestra.
                  5. just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
                  6. Live smoke: python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
                  7. Live smoke: python scripts/smoke_mpris_live.py — same.
                  8. Manual: load a layout that includes raise-window / focus actions, verify the extension is publishing to org.deskestra.Focus.

                  What I deliberately did not put in this plan

                  • Renaming the product concept of a "deck" (a deck of controls, a button deck, deck layout, deck grid) — those stay.
                  • Renaming third-party product references (Stream Deck, Macro Deck, WebDeck, Deckboard, OpenDeck, Loupedeck) — those are third parties.
                  • Renaming Git history — git log will still show the old name. A git filter-repo could rewrite history but it's not worth it pre-launch.

                  Open questions for the human reviewer

                  1. deckctldeskctl (recommended), or something else?
                  2. Do we keep the daemon binary name as deskestra, or use deskestrad/deskestra-service after all? (You said you don't like the -d suffix; I agree.)
                  3. Repo rename on GitHub happens before, during, or after the code cutover?
                  4. Old-password-file auto-migration on first run, or fresh setup?
                  5. Anything else in the inventory I missed — deckd.egg-info style build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    documentationImprovements or additions to documentationenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      Rename project: deckd → deskestra #145

                      Description

                      @jonocodes

                      Why

                      The app has outgrown its original framing. It's no longer "an app controller" — it's a full desktop control surface that drives apps, windows, the desktop, and the GNOME/KDE/macOS sessions around them. The name deckd was a nod to Stream Deck and reads narrowly. We want a name that:

                      • keeps desk (drives home "you control a screen, not a server"),
                      • carries the conducting thread (you conduct your desktop, you don't just press buttons),
                      • reads as one natural word with one obvious spelling,
                      • is coined enough to be brand-/domain-/product-safe.

                      The recent layoutdeck cutover (issue #105) already gave us internal vocabulary room. Now we want the project itself to move.

                      Decision: deskestra

                      desk + orche·stra → you conduct your desktop. Three syllables, one spelling, easy to say aloud.

                      Across every candidate we researched, deskestra is the only one that came back genuinely unclaimed — no product, package, org, or app. The desk- sound space is occupied by Deskera (funded cloud ERP) and DeskRoll (a remote-desktop product), but deskestra is far enough from both (extra syllable, different ending) to stand apart.

                      Service name: deskestra (no -d suffix — agreed that <name>d feels dated).
                      GNOME extension UUID: deskestra-focus@local.
                      KWin script ID: deskestra-focus.
                      Password file: ~/.config/deskestra/password.
                      Python distribution / entry points: deskestra, deskestra-dev, deskctl (keeping deckctl — it's a CLI for the daemon, not the daemon itself; rename candidate TBD).

                      Names considered

                      Clustered by approach. Each entry: name → one-line verdict → why.

                      A. Keep deckd (reposition instead of rename) — rejected

                      • deckd → keep, reframe positioning. Cheapest path. Rejected because we explicitly said the cutover doesn't scare us and we want the broader positioning baked into the name itself.

                      B. Generic conducting/control surface — rejected, all taken or weak

                      • Helm → "you steer the desktop from it." Strong, but collides with Kubernetes Helm.
                      • Maestro → conducting metaphor, distinctive. Prior art: Mobile "Maestro" test tool. Rejected as overused.
                      • Conductor → conducting + dispatch. conductord ugly. Rejected as overused.
                      • Conduit → routing metaphor. Rejected: reads as internal plumbing, not conducting.
                      • Podium → fresh, low collision. Rejected: you don't want "pod."
                      • Panel / Console / Dispatch / Relay / Pilot → various. Rejected for over-use or vibe mismatch.

                      C. desk + fused coined suffix (the vein we liked)

                      • Deskpit (cockpit) → rejected, you don't like "pit."
                      • Deskata → taken-ish (deskata.com exists; risks Deskera echo).
                      • Deskra → basically a homophone of Deskera ERP (funded, 80k+ users). Hard no.
                      • Deskira / Deskora / Deskana / Deskyra / Deskade → variations. Most share the Deskera phonetic overlap; none meaningfully better than deskestra.
                      • Deskomand / Deskommand / Deskrix / Deskrex / Deskura / Deskept → control-rooted variants. Various fatal flaws (skept-adjacent, "command" doesn't fuse, etc.).
                      • Deskrol → direct collision with DeskRoll (existing remote-desktop product). Hard no.
                      • Deskrola → deskrol + softened; Polish "deskorolki" + Deskera echo. Compromised.
                      • Deskestra ✅ → clean, conducting-fused, no collision. Winner.

                      D. control family, no desk — rejected, all crowded

                      • Controlla → near-identical open-source "phone-as-keyboard/mouse-for-desktop" project already uses the name (okekedev/controlla). Plus CAREL Controlla (HVAC controllers). Hard no.
                      • Controla → spread thin across active apps (Controla cybersecurity, ControlA university mgmt, Controla.ai org). Diluted.
                      • Kontroll / Kontrol → stylized bare spelling. Rejected earlier: dated, un-searchable.
                      • Ctrl / Ctrla → literal keyboard modifier, on-theme. Rejected: ambiguous to say aloud.
                      • Controlla / Drake collision → also a Drake song. Bonus reason not to.

                      E. Less-literal control roots (Latin / Greek / French) — rejected, all occupied

                      • Dirigo (Latin "I direct") → several active software cos (Dirigo Software accident reconstruction, etc.).
                      • Regie (French "control room") → Regie.ai dominates.
                      • Kyber (kubernan root) → saturated. KDE has a "Kyber" cross-platform remote-control product, plus post-quantum crypto, plus k8s deploy tool.
                      • Imperia (imperare "to command") → Imperia SCM + Imperia Online game.

                      Pattern: anything that obviously means control/command/steer has been claimed, usually more than once. Coined fusions (deskestra) escape the saturation.


                      Rename blast-radius plan: deckddeskestra

                      Generated by an exhaustive inventory of the repo. Hard references are listed explicitly. Soft references (comments, prose, generic "deck" usage as a product concept like "button deck") are not listed and should not be mechanically renamed.

                      Naming decisions (commit these first)

                      OldNew
                      Project name deckddeskestra
                      Python distribution deckddeskestra
                      Python package daemon/deckd/daemon/deskestra/
                      Executable deckddeskestra
                      Executable deckd-devdeskestra-dev
                      Executable deckctldeskctl (keep, but see open question below)
                      Service unit deckd.servicedeskestra.service
                      LaunchAgent com.deckd.daemoncom.deskestra.daemon
                      NixOS module services.deckd-spikeservices.deskestra-spike
                      NixOS script deckd-spike-startdeskestra-spike-start
                      udev rules 70-deckd-uinput.rules70-deskestra-uinput.rules
                      GNOME UUID deckd-focus@localdeskestra-focus@local
                      KWin script ID deckd-focusdeskestra-focus
                      D-Bus bus org.deckd.Focusorg.deskestra.Focus
                      D-Bus path /org/deckd/Focus/org/deskestra/Focus
                      D-Bus interface org.deckd.Focusorg.deskestra.Focus
                      Env DECKD_PASSWORDDESKESTRA_PASSWORD
                      Env DECKD_FAKE_MPRISDESKESTRA_FAKE_MPRIS
                      Env DECKD_TLS_DIR / DECKD_TLS_HOSTDESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST
                      Env VITE_DECKD_WSVITE_DESKESTRA_WS
                      Password file ~/.config/deckd/password~/.config/deskestra/password
                      LaunchAgent log @PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.log
                      Browser localStorage key deckd.passworddeskestra.password
                      HTTP header X-Deckd-PasswordX-Deskestra-Password
                      GitHub Pages base /deckd//deskestra/
                      Pages URL jonocodes.github.io/deckd/jonocodes.github.io/deskestra/
                      Client PWA name / short_namedeskestra
                      HTML <title>Deskestra
                      macOS PWA apple-mobile-web-app-titleDeskestra
                      Auto-ignore logic: window titled "deckd"window titled "deskestra"
                      Test fixture suffix deckd-smoke-* etc.deskestra-smoke-*
                      CLI metric prefix deckd_ (e.g. deckd_actions_total)deskestra_
                      Class DeckdFocusDBusServiceDeskestraFocusDBusService
                      Class DeckdFocusCacheDeskestraFocusCache
                      Class DeckdFocusExtensionDeskestraFocusExtension
                      Class attr Server._is_deckd_windowServer._is_deskestra_window
                      Setuptools include pattern deckd*deskestra*
                      Repo URL github.com/jonocodes/deckdgithub.com/jonocodes/deskestra

                      Open question on deckctl: it's the CLI, not the daemon. Options: rename to deskctl (consistent), rename to deskestractl (verbose), or rename to something functional like deskestra-cli. Leaning toward deskctl for symmetry. Worth a quick opinion before the cutover.

                      Mechanical order of operations

                      Do these in order so the tree is never in a half-renamed state for more than a commit.

                      1. Pre-flight: confirm availability — deskestra.com/.app, github.com/deskestra, npmjs.com/package/deskestra, PyPI deskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)
                      2. Lock the new identifiers by editing these in one commit:
                        • pyproject.tomlname, [project.scripts] (deskestra, deskestra-dev), deskctl decision, package include pattern.
                        • daemon/deckd/daemon/deskestra/ (rename the package directory itself; every internal import updates accordingly).
                        • All from deckd… / import deckd… (the directory rename takes care of most; touch up external references).
                        • All DECKD_* and VITE_DECKD_WS env var names.
                        • ~/.config/deckd/password~/.config/deskestra/password in auth.py, systemd unit, launchd plist, README, docs.
                        • D-Bus: org.deckd.Focus, /org/deckd/Focus, interface name — in platform.py, GNOME extension.js, KWin main.js, all scripts/* and tests/*.
                        • Class names Deckd*Deskestra* (DBus service, cache, extension class, _is_deckd_window).
                        • Auto-ignore logic: "deckd" literal → "deskestra" (and case-insensitive match).
                      3. Packaging: rename and update these files/paths:
                        • packaging/systemd/deckd.servicepackaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
                        • packaging/launchd/com.deckd.daemon.plistpackaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
                        • packaging/nixos/deckd-spike.nixpackaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
                        • packaging/udev/70-deckd-uinput.rulespackaging/udev/70-deskestra-uinput.rules.
                        • packaging/gnome-shell/deckd-focus@local/packaging/gnome-shell/deskestra-focus@local/; metadata.json uuid + name + description; extension.js bus/path/interface; wire-shape.js import path.
                        • packaging/kwin-script/deckd-focus/packaging/kwin-script/deskestra-focus/; metadata.json Id + Name; main.js bus/path/interface + comment header.
                      4. Justfile: update every recipe that invokes deckd, deckd-dev, deckctl, references the extension UUID, or hardcodes paths. (See "Justfile" in the inventory — this is one of the larger files to edit.) Also update the GNOME pack/install recipe (packaging/gnome-shell/...@local) and the KWin install recipe ($HOME/.local/share/kwin/scripts/deskestra-focus/...).
                      5. Client (client/):
                        • package.json: namedeskestra-client (or just deskestra).
                        • index.html: <title> and apple-mobile-web-app-title.
                        • vite.config.ts: VITE_DECKD_WSVITE_DESKESTRA_WS.
                        • public/manifest.json and public/icon.svg: display name + aria-label.
                        • src/socket.ts: localStorage key deckd.passworddeskestra.password; any error text containing deckd.
                        • playwright.config.ts: DECKD_FAKE_MPRIS env var name.
                      6. Scripts (scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
                      7. Tests (tests/):
                        • Update all from deckd… imports.
                        • Update all D-Bus name assertions (org.deckd.Focus).
                        • Update Deckd* class assertions.
                        • Rename tests/test_deckd_window.pytests/test_deskestra_window.py and update test names + the tests/.pytest_cache nodeids will regenerate.
                        • Rename e2e client/e2e/daemon-log.ts references if any contain the binary name.
                      8. Docs: sweep README.md, CONTEXT.md, AGENTS.md, docs/ONBOARDING.md, docs/REFERENCE.md, docs/TESTING.md, docs/SPIKES.md, docs/ARCHITECTURE.md, docs/research/landscape.md, docs/research/issue-56-macos-nowplaying.md, all docs/adr/*.md. Do not mechanically replace generic "deck" usage — that word is now the product concept (a "deck" is a control surface) and stays. Only the project identifier deckd / Deckd changes.
                      9. GitHub-side:
                        • Rename the repo (github.com/jonocodes/deckdgithub.com/jonocodes/deskestra) — GitHub redirects old URLs but update local git remote after.
                        • .github/workflows/deploy-pages.yml: Pages base path /deskestra/, build env VITE_BASE_PATH: /deskestra/.
                        • .github/workflows/ci.yml: any commands invoking deckd / deckctl / deckd-dev.
                      10. Cleanup: delete daemon/deckd.egg-info/, client/dist/, .pytest_cache/, .mypy_cache/, __pycache__/. Rebuild from clean state. Run just lint test (or the equivalent) and verify.

                      Behavioral / data migration concerns

                      • Existing user password file: anyone currently running deckd has ~/.config/deckd/password. On first run of deskestra, either (a) auto-migrate by reading the old path and writing the new one, or (b) require re-running the install/setup to generate a new password. Option (a) is nicer and one-line.
                      • Browser localStorage key: a returning client will not find deskestra.password and will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.
                      • GitHub Pages redirect: GitHub preserves redirects on repo rename, but the Pages site itself is tied to the new repo path — there will be a brief window where old links 404. Acceptable since no users yet.
                      • D-Bus name conflict: since org.deckd.Focus and org.deskestra.Focus are different bus names, there is no risk of two instances colliding on the session bus during a transition. But anyone with the old GNOME extension installed will be publishing to the old name — uninstall the old extension before installing the new one.

                      Verification ladder (run in order)

                      1. just lint — type/format/lint.
                      2. just test — Python suite, including the renamed test_deskestra_window.py.
                      3. Build the GNOME extension zip from packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
                      4. Build the client (just client-build or whatever it is) and load it against a running daemon. Confirm:
                        • WebSocket connects (uses the new env var).
                        • localStorage key is deskestra.password.
                        • HTML title is Deskestra.
                      5. just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
                      6. Live smoke: python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
                      7. Live smoke: python scripts/smoke_mpris_live.py — same.
                      8. Manual: load a layout that includes raise-window / focus actions, verify the extension is publishing to org.deskestra.Focus.

                      What I deliberately did not put in this plan

                      • Renaming the product concept of a "deck" (a deck of controls, a button deck, deck layout, deck grid) — those stay.
                      • Renaming third-party product references (Stream Deck, Macro Deck, WebDeck, Deckboard, OpenDeck, Loupedeck) — those are third parties.
                      • Renaming Git history — git log will still show the old name. A git filter-repo could rewrite history but it's not worth it pre-launch.

                      Open questions for the human reviewer

                      1. deckctldeskctl (recommended), or something else?
                      2. Do we keep the daemon binary name as deskestra, or use deskestrad/deskestra-service after all? (You said you don't like the -d suffix; I agree.)
                      3. Repo rename on GitHub happens before, during, or after the code cutover?
                      4. Old-password-file auto-migration on first run, or fresh setup?
                      5. Anything else in the inventory I missed — deckd.egg-info style build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        documentationImprovements or additions to documentationenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          Rename project: deckd → deskestra #145

                          Description

                          @jonocodes

                          Why

                          The app has outgrown its original framing. It's no longer "an app controller" — it's a full desktop control surface that drives apps, windows, the desktop, and the GNOME/KDE/macOS sessions around them. The name deckd was a nod to Stream Deck and reads narrowly. We want a name that:

                          • keeps desk (drives home "you control a screen, not a server"),
                          • carries the conducting thread (you conduct your desktop, you don't just press buttons),
                          • reads as one natural word with one obvious spelling,
                          • is coined enough to be brand-/domain-/product-safe.

                          The recent layoutdeck cutover (issue #105) already gave us internal vocabulary room. Now we want the project itself to move.

                          Decision: deskestra

                          desk + orche·stra → you conduct your desktop. Three syllables, one spelling, easy to say aloud.

                          Across every candidate we researched, deskestra is the only one that came back genuinely unclaimed — no product, package, org, or app. The desk- sound space is occupied by Deskera (funded cloud ERP) and DeskRoll (a remote-desktop product), but deskestra is far enough from both (extra syllable, different ending) to stand apart.

                          Service name: deskestra (no -d suffix — agreed that <name>d feels dated).
                          GNOME extension UUID: deskestra-focus@local.
                          KWin script ID: deskestra-focus.
                          Password file: ~/.config/deskestra/password.
                          Python distribution / entry points: deskestra, deskestra-dev, deskctl (keeping deckctl — it's a CLI for the daemon, not the daemon itself; rename candidate TBD).

                          Names considered

                          Clustered by approach. Each entry: name → one-line verdict → why.

                          A. Keep deckd (reposition instead of rename) — rejected

                          • deckd → keep, reframe positioning. Cheapest path. Rejected because we explicitly said the cutover doesn't scare us and we want the broader positioning baked into the name itself.

                          B. Generic conducting/control surface — rejected, all taken or weak

                          • Helm → "you steer the desktop from it." Strong, but collides with Kubernetes Helm.
                          • Maestro → conducting metaphor, distinctive. Prior art: Mobile "Maestro" test tool. Rejected as overused.
                          • Conductor → conducting + dispatch. conductord ugly. Rejected as overused.
                          • Conduit → routing metaphor. Rejected: reads as internal plumbing, not conducting.
                          • Podium → fresh, low collision. Rejected: you don't want "pod."
                          • Panel / Console / Dispatch / Relay / Pilot → various. Rejected for over-use or vibe mismatch.

                          C. desk + fused coined suffix (the vein we liked)

                          • Deskpit (cockpit) → rejected, you don't like "pit."
                          • Deskata → taken-ish (deskata.com exists; risks Deskera echo).
                          • Deskra → basically a homophone of Deskera ERP (funded, 80k+ users). Hard no.
                          • Deskira / Deskora / Deskana / Deskyra / Deskade → variations. Most share the Deskera phonetic overlap; none meaningfully better than deskestra.
                          • Deskomand / Deskommand / Deskrix / Deskrex / Deskura / Deskept → control-rooted variants. Various fatal flaws (skept-adjacent, "command" doesn't fuse, etc.).
                          • Deskrol → direct collision with DeskRoll (existing remote-desktop product). Hard no.
                          • Deskrola → deskrol + softened; Polish "deskorolki" + Deskera echo. Compromised.
                          • Deskestra ✅ → clean, conducting-fused, no collision. Winner.

                          D. control family, no desk — rejected, all crowded

                          • Controlla → near-identical open-source "phone-as-keyboard/mouse-for-desktop" project already uses the name (okekedev/controlla). Plus CAREL Controlla (HVAC controllers). Hard no.
                          • Controla → spread thin across active apps (Controla cybersecurity, ControlA university mgmt, Controla.ai org). Diluted.
                          • Kontroll / Kontrol → stylized bare spelling. Rejected earlier: dated, un-searchable.
                          • Ctrl / Ctrla → literal keyboard modifier, on-theme. Rejected: ambiguous to say aloud.
                          • Controlla / Drake collision → also a Drake song. Bonus reason not to.

                          E. Less-literal control roots (Latin / Greek / French) — rejected, all occupied

                          • Dirigo (Latin "I direct") → several active software cos (Dirigo Software accident reconstruction, etc.).
                          • Regie (French "control room") → Regie.ai dominates.
                          • Kyber (kubernan root) → saturated. KDE has a "Kyber" cross-platform remote-control product, plus post-quantum crypto, plus k8s deploy tool.
                          • Imperia (imperare "to command") → Imperia SCM + Imperia Online game.

                          Pattern: anything that obviously means control/command/steer has been claimed, usually more than once. Coined fusions (deskestra) escape the saturation.


                          Rename blast-radius plan: deckddeskestra

                          Generated by an exhaustive inventory of the repo. Hard references are listed explicitly. Soft references (comments, prose, generic "deck" usage as a product concept like "button deck") are not listed and should not be mechanically renamed.

                          Naming decisions (commit these first)

                          OldNew
                          Project name deckddeskestra
                          Python distribution deckddeskestra
                          Python package daemon/deckd/daemon/deskestra/
                          Executable deckddeskestra
                          Executable deckd-devdeskestra-dev
                          Executable deckctldeskctl (keep, but see open question below)
                          Service unit deckd.servicedeskestra.service
                          LaunchAgent com.deckd.daemoncom.deskestra.daemon
                          NixOS module services.deckd-spikeservices.deskestra-spike
                          NixOS script deckd-spike-startdeskestra-spike-start
                          udev rules 70-deckd-uinput.rules70-deskestra-uinput.rules
                          GNOME UUID deckd-focus@localdeskestra-focus@local
                          KWin script ID deckd-focusdeskestra-focus
                          D-Bus bus org.deckd.Focusorg.deskestra.Focus
                          D-Bus path /org/deckd/Focus/org/deskestra/Focus
                          D-Bus interface org.deckd.Focusorg.deskestra.Focus
                          Env DECKD_PASSWORDDESKESTRA_PASSWORD
                          Env DECKD_FAKE_MPRISDESKESTRA_FAKE_MPRIS
                          Env DECKD_TLS_DIR / DECKD_TLS_HOSTDESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST
                          Env VITE_DECKD_WSVITE_DESKESTRA_WS
                          Password file ~/.config/deckd/password~/.config/deskestra/password
                          LaunchAgent log @PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.log
                          Browser localStorage key deckd.passworddeskestra.password
                          HTTP header X-Deckd-PasswordX-Deskestra-Password
                          GitHub Pages base /deckd//deskestra/
                          Pages URL jonocodes.github.io/deckd/jonocodes.github.io/deskestra/
                          Client PWA name / short_namedeskestra
                          HTML <title>Deskestra
                          macOS PWA apple-mobile-web-app-titleDeskestra
                          Auto-ignore logic: window titled "deckd"window titled "deskestra"
                          Test fixture suffix deckd-smoke-* etc.deskestra-smoke-*
                          CLI metric prefix deckd_ (e.g. deckd_actions_total)deskestra_
                          Class DeckdFocusDBusServiceDeskestraFocusDBusService
                          Class DeckdFocusCacheDeskestraFocusCache
                          Class DeckdFocusExtensionDeskestraFocusExtension
                          Class attr Server._is_deckd_windowServer._is_deskestra_window
                          Setuptools include pattern deckd*deskestra*
                          Repo URL github.com/jonocodes/deckdgithub.com/jonocodes/deskestra

                          Open question on deckctl: it's the CLI, not the daemon. Options: rename to deskctl (consistent), rename to deskestractl (verbose), or rename to something functional like deskestra-cli. Leaning toward deskctl for symmetry. Worth a quick opinion before the cutover.

                          Mechanical order of operations

                          Do these in order so the tree is never in a half-renamed state for more than a commit.

                          1. Pre-flight: confirm availability — deskestra.com/.app, github.com/deskestra, npmjs.com/package/deskestra, PyPI deskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)
                          2. Lock the new identifiers by editing these in one commit:
                            • pyproject.tomlname, [project.scripts] (deskestra, deskestra-dev), deskctl decision, package include pattern.
                            • daemon/deckd/daemon/deskestra/ (rename the package directory itself; every internal import updates accordingly).
                            • All from deckd… / import deckd… (the directory rename takes care of most; touch up external references).
                            • All DECKD_* and VITE_DECKD_WS env var names.
                            • ~/.config/deckd/password~/.config/deskestra/password in auth.py, systemd unit, launchd plist, README, docs.
                            • D-Bus: org.deckd.Focus, /org/deckd/Focus, interface name — in platform.py, GNOME extension.js, KWin main.js, all scripts/* and tests/*.
                            • Class names Deckd*Deskestra* (DBus service, cache, extension class, _is_deckd_window).
                            • Auto-ignore logic: "deckd" literal → "deskestra" (and case-insensitive match).
                          3. Packaging: rename and update these files/paths:
                            • packaging/systemd/deckd.servicepackaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
                            • packaging/launchd/com.deckd.daemon.plistpackaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
                            • packaging/nixos/deckd-spike.nixpackaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
                            • packaging/udev/70-deckd-uinput.rulespackaging/udev/70-deskestra-uinput.rules.
                            • packaging/gnome-shell/deckd-focus@local/packaging/gnome-shell/deskestra-focus@local/; metadata.json uuid + name + description; extension.js bus/path/interface; wire-shape.js import path.
                            • packaging/kwin-script/deckd-focus/packaging/kwin-script/deskestra-focus/; metadata.json Id + Name; main.js bus/path/interface + comment header.
                          4. Justfile: update every recipe that invokes deckd, deckd-dev, deckctl, references the extension UUID, or hardcodes paths. (See "Justfile" in the inventory — this is one of the larger files to edit.) Also update the GNOME pack/install recipe (packaging/gnome-shell/...@local) and the KWin install recipe ($HOME/.local/share/kwin/scripts/deskestra-focus/...).
                          5. Client (client/):
                            • package.json: namedeskestra-client (or just deskestra).
                            • index.html: <title> and apple-mobile-web-app-title.
                            • vite.config.ts: VITE_DECKD_WSVITE_DESKESTRA_WS.
                            • public/manifest.json and public/icon.svg: display name + aria-label.
                            • src/socket.ts: localStorage key deckd.passworddeskestra.password; any error text containing deckd.
                            • playwright.config.ts: DECKD_FAKE_MPRIS env var name.
                          6. Scripts (scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
                          7. Tests (tests/):
                            • Update all from deckd… imports.
                            • Update all D-Bus name assertions (org.deckd.Focus).
                            • Update Deckd* class assertions.
                            • Rename tests/test_deckd_window.pytests/test_deskestra_window.py and update test names + the tests/.pytest_cache nodeids will regenerate.
                            • Rename e2e client/e2e/daemon-log.ts references if any contain the binary name.
                          8. Docs: sweep README.md, CONTEXT.md, AGENTS.md, docs/ONBOARDING.md, docs/REFERENCE.md, docs/TESTING.md, docs/SPIKES.md, docs/ARCHITECTURE.md, docs/research/landscape.md, docs/research/issue-56-macos-nowplaying.md, all docs/adr/*.md. Do not mechanically replace generic "deck" usage — that word is now the product concept (a "deck" is a control surface) and stays. Only the project identifier deckd / Deckd changes.
                          9. GitHub-side:
                            • Rename the repo (github.com/jonocodes/deckdgithub.com/jonocodes/deskestra) — GitHub redirects old URLs but update local git remote after.
                            • .github/workflows/deploy-pages.yml: Pages base path /deskestra/, build env VITE_BASE_PATH: /deskestra/.
                            • .github/workflows/ci.yml: any commands invoking deckd / deckctl / deckd-dev.
                          10. Cleanup: delete daemon/deckd.egg-info/, client/dist/, .pytest_cache/, .mypy_cache/, __pycache__/. Rebuild from clean state. Run just lint test (or the equivalent) and verify.

                          Behavioral / data migration concerns

                          • Existing user password file: anyone currently running deckd has ~/.config/deckd/password. On first run of deskestra, either (a) auto-migrate by reading the old path and writing the new one, or (b) require re-running the install/setup to generate a new password. Option (a) is nicer and one-line.
                          • Browser localStorage key: a returning client will not find deskestra.password and will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.
                          • GitHub Pages redirect: GitHub preserves redirects on repo rename, but the Pages site itself is tied to the new repo path — there will be a brief window where old links 404. Acceptable since no users yet.
                          • D-Bus name conflict: since org.deckd.Focus and org.deskestra.Focus are different bus names, there is no risk of two instances colliding on the session bus during a transition. But anyone with the old GNOME extension installed will be publishing to the old name — uninstall the old extension before installing the new one.

                          Verification ladder (run in order)

                          1. just lint — type/format/lint.
                          2. just test — Python suite, including the renamed test_deskestra_window.py.
                          3. Build the GNOME extension zip from packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
                          4. Build the client (just client-build or whatever it is) and load it against a running daemon. Confirm:
                            • WebSocket connects (uses the new env var).
                            • localStorage key is deskestra.password.
                            • HTML title is Deskestra.
                          5. just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
                          6. Live smoke: python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
                          7. Live smoke: python scripts/smoke_mpris_live.py — same.
                          8. Manual: load a layout that includes raise-window / focus actions, verify the extension is publishing to org.deskestra.Focus.

                          What I deliberately did not put in this plan

                          • Renaming the product concept of a "deck" (a deck of controls, a button deck, deck layout, deck grid) — those stay.
                          • Renaming third-party product references (Stream Deck, Macro Deck, WebDeck, Deckboard, OpenDeck, Loupedeck) — those are third parties.
                          • Renaming Git history — git log will still show the old name. A git filter-repo could rewrite history but it's not worth it pre-launch.

                          Open questions for the human reviewer

                          1. deckctldeskctl (recommended), or something else?
                          2. Do we keep the daemon binary name as deskestra, or use deskestrad/deskestra-service after all? (You said you don't like the -d suffix; I agree.)
                          3. Repo rename on GitHub happens before, during, or after the code cutover?
                          4. Old-password-file auto-migration on first run, or fresh setup?
                          5. Anything else in the inventory I missed — deckd.egg-info style build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            documentationImprovements or additions to documentationenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              Rename project: deckd → deskestra #145

                              Description

                              @jonocodes

                              Why

                              The app has outgrown its original framing. It's no longer "an app controller" — it's a full desktop control surface that drives apps, windows, the desktop, and the GNOME/KDE/macOS sessions around them. The name deckd was a nod to Stream Deck and reads narrowly. We want a name that:

                              • keeps desk (drives home "you control a screen, not a server"),
                              • carries the conducting thread (you conduct your desktop, you don't just press buttons),
                              • reads as one natural word with one obvious spelling,
                              • is coined enough to be brand-/domain-/product-safe.

                              The recent layoutdeck cutover (issue #105) already gave us internal vocabulary room. Now we want the project itself to move.

                              Decision: deskestra

                              desk + orche·stra → you conduct your desktop. Three syllables, one spelling, easy to say aloud.

                              Across every candidate we researched, deskestra is the only one that came back genuinely unclaimed — no product, package, org, or app. The desk- sound space is occupied by Deskera (funded cloud ERP) and DeskRoll (a remote-desktop product), but deskestra is far enough from both (extra syllable, different ending) to stand apart.

                              Service name: deskestra (no -d suffix — agreed that <name>d feels dated).
                              GNOME extension UUID: deskestra-focus@local.
                              KWin script ID: deskestra-focus.
                              Password file: ~/.config/deskestra/password.
                              Python distribution / entry points: deskestra, deskestra-dev, deskctl (keeping deckctl — it's a CLI for the daemon, not the daemon itself; rename candidate TBD).

                              Names considered

                              Clustered by approach. Each entry: name → one-line verdict → why.

                              A. Keep deckd (reposition instead of rename) — rejected

                              • deckd → keep, reframe positioning. Cheapest path. Rejected because we explicitly said the cutover doesn't scare us and we want the broader positioning baked into the name itself.

                              B. Generic conducting/control surface — rejected, all taken or weak

                              • Helm → "you steer the desktop from it." Strong, but collides with Kubernetes Helm.
                              • Maestro → conducting metaphor, distinctive. Prior art: Mobile "Maestro" test tool. Rejected as overused.
                              • Conductor → conducting + dispatch. conductord ugly. Rejected as overused.
                              • Conduit → routing metaphor. Rejected: reads as internal plumbing, not conducting.
                              • Podium → fresh, low collision. Rejected: you don't want "pod."
                              • Panel / Console / Dispatch / Relay / Pilot → various. Rejected for over-use or vibe mismatch.

                              C. desk + fused coined suffix (the vein we liked)

                              • Deskpit (cockpit) → rejected, you don't like "pit."
                              • Deskata → taken-ish (deskata.com exists; risks Deskera echo).
                              • Deskra → basically a homophone of Deskera ERP (funded, 80k+ users). Hard no.
                              • Deskira / Deskora / Deskana / Deskyra / Deskade → variations. Most share the Deskera phonetic overlap; none meaningfully better than deskestra.
                              • Deskomand / Deskommand / Deskrix / Deskrex / Deskura / Deskept → control-rooted variants. Various fatal flaws (skept-adjacent, "command" doesn't fuse, etc.).
                              • Deskrol → direct collision with DeskRoll (existing remote-desktop product). Hard no.
                              • Deskrola → deskrol + softened; Polish "deskorolki" + Deskera echo. Compromised.
                              • Deskestra ✅ → clean, conducting-fused, no collision. Winner.

                              D. control family, no desk — rejected, all crowded

                              • Controlla → near-identical open-source "phone-as-keyboard/mouse-for-desktop" project already uses the name (okekedev/controlla). Plus CAREL Controlla (HVAC controllers). Hard no.
                              • Controla → spread thin across active apps (Controla cybersecurity, ControlA university mgmt, Controla.ai org). Diluted.
                              • Kontroll / Kontrol → stylized bare spelling. Rejected earlier: dated, un-searchable.
                              • Ctrl / Ctrla → literal keyboard modifier, on-theme. Rejected: ambiguous to say aloud.
                              • Controlla / Drake collision → also a Drake song. Bonus reason not to.

                              E. Less-literal control roots (Latin / Greek / French) — rejected, all occupied

                              • Dirigo (Latin "I direct") → several active software cos (Dirigo Software accident reconstruction, etc.).
                              • Regie (French "control room") → Regie.ai dominates.
                              • Kyber (kubernan root) → saturated. KDE has a "Kyber" cross-platform remote-control product, plus post-quantum crypto, plus k8s deploy tool.
                              • Imperia (imperare "to command") → Imperia SCM + Imperia Online game.

                              Pattern: anything that obviously means control/command/steer has been claimed, usually more than once. Coined fusions (deskestra) escape the saturation.


                              Rename blast-radius plan: deckddeskestra

                              Generated by an exhaustive inventory of the repo. Hard references are listed explicitly. Soft references (comments, prose, generic "deck" usage as a product concept like "button deck") are not listed and should not be mechanically renamed.

                              Naming decisions (commit these first)

                              OldNew
                              Project name deckddeskestra
                              Python distribution deckddeskestra
                              Python package daemon/deckd/daemon/deskestra/
                              Executable deckddeskestra
                              Executable deckd-devdeskestra-dev
                              Executable deckctldeskctl (keep, but see open question below)
                              Service unit deckd.servicedeskestra.service
                              LaunchAgent com.deckd.daemoncom.deskestra.daemon
                              NixOS module services.deckd-spikeservices.deskestra-spike
                              NixOS script deckd-spike-startdeskestra-spike-start
                              udev rules 70-deckd-uinput.rules70-deskestra-uinput.rules
                              GNOME UUID deckd-focus@localdeskestra-focus@local
                              KWin script ID deckd-focusdeskestra-focus
                              D-Bus bus org.deckd.Focusorg.deskestra.Focus
                              D-Bus path /org/deckd/Focus/org/deskestra/Focus
                              D-Bus interface org.deckd.Focusorg.deskestra.Focus
                              Env DECKD_PASSWORDDESKESTRA_PASSWORD
                              Env DECKD_FAKE_MPRISDESKESTRA_FAKE_MPRIS
                              Env DECKD_TLS_DIR / DECKD_TLS_HOSTDESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST
                              Env VITE_DECKD_WSVITE_DESKESTRA_WS
                              Password file ~/.config/deckd/password~/.config/deskestra/password
                              LaunchAgent log @PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.log
                              Browser localStorage key deckd.passworddeskestra.password
                              HTTP header X-Deckd-PasswordX-Deskestra-Password
                              GitHub Pages base /deckd//deskestra/
                              Pages URL jonocodes.github.io/deckd/jonocodes.github.io/deskestra/
                              Client PWA name / short_namedeskestra
                              HTML <title>Deskestra
                              macOS PWA apple-mobile-web-app-titleDeskestra
                              Auto-ignore logic: window titled "deckd"window titled "deskestra"
                              Test fixture suffix deckd-smoke-* etc.deskestra-smoke-*
                              CLI metric prefix deckd_ (e.g. deckd_actions_total)deskestra_
                              Class DeckdFocusDBusServiceDeskestraFocusDBusService
                              Class DeckdFocusCacheDeskestraFocusCache
                              Class DeckdFocusExtensionDeskestraFocusExtension
                              Class attr Server._is_deckd_windowServer._is_deskestra_window
                              Setuptools include pattern deckd*deskestra*
                              Repo URL github.com/jonocodes/deckdgithub.com/jonocodes/deskestra

                              Open question on deckctl: it's the CLI, not the daemon. Options: rename to deskctl (consistent), rename to deskestractl (verbose), or rename to something functional like deskestra-cli. Leaning toward deskctl for symmetry. Worth a quick opinion before the cutover.

                              Mechanical order of operations

                              Do these in order so the tree is never in a half-renamed state for more than a commit.

                              1. Pre-flight: confirm availability — deskestra.com/.app, github.com/deskestra, npmjs.com/package/deskestra, PyPI deskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)
                              2. Lock the new identifiers by editing these in one commit:
                                • pyproject.tomlname, [project.scripts] (deskestra, deskestra-dev), deskctl decision, package include pattern.
                                • daemon/deckd/daemon/deskestra/ (rename the package directory itself; every internal import updates accordingly).
                                • All from deckd… / import deckd… (the directory rename takes care of most; touch up external references).
                                • All DECKD_* and VITE_DECKD_WS env var names.
                                • ~/.config/deckd/password~/.config/deskestra/password in auth.py, systemd unit, launchd plist, README, docs.
                                • D-Bus: org.deckd.Focus, /org/deckd/Focus, interface name — in platform.py, GNOME extension.js, KWin main.js, all scripts/* and tests/*.
                                • Class names Deckd*Deskestra* (DBus service, cache, extension class, _is_deckd_window).
                                • Auto-ignore logic: "deckd" literal → "deskestra" (and case-insensitive match).
                              3. Packaging: rename and update these files/paths:
                                • packaging/systemd/deckd.servicepackaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
                                • packaging/launchd/com.deckd.daemon.plistpackaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
                                • packaging/nixos/deckd-spike.nixpackaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
                                • packaging/udev/70-deckd-uinput.rulespackaging/udev/70-deskestra-uinput.rules.
                                • packaging/gnome-shell/deckd-focus@local/packaging/gnome-shell/deskestra-focus@local/; metadata.json uuid + name + description; extension.js bus/path/interface; wire-shape.js import path.
                                • packaging/kwin-script/deckd-focus/packaging/kwin-script/deskestra-focus/; metadata.json Id + Name; main.js bus/path/interface + comment header.
                              4. Justfile: update every recipe that invokes deckd, deckd-dev, deckctl, references the extension UUID, or hardcodes paths. (See "Justfile" in the inventory — this is one of the larger files to edit.) Also update the GNOME pack/install recipe (packaging/gnome-shell/...@local) and the KWin install recipe ($HOME/.local/share/kwin/scripts/deskestra-focus/...).
                              5. Client (client/):
                                • package.json: namedeskestra-client (or just deskestra).
                                • index.html: <title> and apple-mobile-web-app-title.
                                • vite.config.ts: VITE_DECKD_WSVITE_DESKESTRA_WS.
                                • public/manifest.json and public/icon.svg: display name + aria-label.
                                • src/socket.ts: localStorage key deckd.passworddeskestra.password; any error text containing deckd.
                                • playwright.config.ts: DECKD_FAKE_MPRIS env var name.
                              6. Scripts (scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
                              7. Tests (tests/):
                                • Update all from deckd… imports.
                                • Update all D-Bus name assertions (org.deckd.Focus).
                                • Update Deckd* class assertions.
                                • Rename tests/test_deckd_window.pytests/test_deskestra_window.py and update test names + the tests/.pytest_cache nodeids will regenerate.
                                • Rename e2e client/e2e/daemon-log.ts references if any contain the binary name.
                              8. Docs: sweep README.md, CONTEXT.md, AGENTS.md, docs/ONBOARDING.md, docs/REFERENCE.md, docs/TESTING.md, docs/SPIKES.md, docs/ARCHITECTURE.md, docs/research/landscape.md, docs/research/issue-56-macos-nowplaying.md, all docs/adr/*.md. Do not mechanically replace generic "deck" usage — that word is now the product concept (a "deck" is a control surface) and stays. Only the project identifier deckd / Deckd changes.
                              9. GitHub-side:
                                • Rename the repo (github.com/jonocodes/deckdgithub.com/jonocodes/deskestra) — GitHub redirects old URLs but update local git remote after.
                                • .github/workflows/deploy-pages.yml: Pages base path /deskestra/, build env VITE_BASE_PATH: /deskestra/.
                                • .github/workflows/ci.yml: any commands invoking deckd / deckctl / deckd-dev.
                              10. Cleanup: delete daemon/deckd.egg-info/, client/dist/, .pytest_cache/, .mypy_cache/, __pycache__/. Rebuild from clean state. Run just lint test (or the equivalent) and verify.

                              Behavioral / data migration concerns

                              • Existing user password file: anyone currently running deckd has ~/.config/deckd/password. On first run of deskestra, either (a) auto-migrate by reading the old path and writing the new one, or (b) require re-running the install/setup to generate a new password. Option (a) is nicer and one-line.
                              • Browser localStorage key: a returning client will not find deskestra.password and will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.
                              • GitHub Pages redirect: GitHub preserves redirects on repo rename, but the Pages site itself is tied to the new repo path — there will be a brief window where old links 404. Acceptable since no users yet.
                              • D-Bus name conflict: since org.deckd.Focus and org.deskestra.Focus are different bus names, there is no risk of two instances colliding on the session bus during a transition. But anyone with the old GNOME extension installed will be publishing to the old name — uninstall the old extension before installing the new one.

                              Verification ladder (run in order)

                              1. just lint — type/format/lint.
                              2. just test — Python suite, including the renamed test_deskestra_window.py.
                              3. Build the GNOME extension zip from packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
                              4. Build the client (just client-build or whatever it is) and load it against a running daemon. Confirm:
                                • WebSocket connects (uses the new env var).
                                • localStorage key is deskestra.password.
                                • HTML title is Deskestra.
                              5. just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
                              6. Live smoke: python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
                              7. Live smoke: python scripts/smoke_mpris_live.py — same.
                              8. Manual: load a layout that includes raise-window / focus actions, verify the extension is publishing to org.deskestra.Focus.

                              What I deliberately did not put in this plan

                              • Renaming the product concept of a "deck" (a deck of controls, a button deck, deck layout, deck grid) — those stay.
                              • Renaming third-party product references (Stream Deck, Macro Deck, WebDeck, Deckboard, OpenDeck, Loupedeck) — those are third parties.
                              • Renaming Git history — git log will still show the old name. A git filter-repo could rewrite history but it's not worth it pre-launch.

                              Open questions for the human reviewer

                              1. deckctldeskctl (recommended), or something else?
                              2. Do we keep the daemon binary name as deskestra, or use deskestrad/deskestra-service after all? (You said you don't like the -d suffix; I agree.)
                              3. Repo rename on GitHub happens before, during, or after the code cutover?
                              4. Old-password-file auto-migration on first run, or fresh setup?
                              5. Anything else in the inventory I missed — deckd.egg-info style build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                documentationImprovements or additions to documentationenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions