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 layout → deck 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: deckd → deskestra
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)
| Old |
New |
Project name deckd |
deskestra |
Python distribution deckd |
deskestra |
Python package daemon/deckd/ |
daemon/deskestra/ |
Executable deckd |
deskestra |
Executable deckd-dev |
deskestra-dev |
Executable deckctl |
deskctl (keep, but see open question below) |
Service unit deckd.service |
deskestra.service |
LaunchAgent com.deckd.daemon |
com.deskestra.daemon |
NixOS module services.deckd-spike |
services.deskestra-spike |
NixOS script deckd-spike-start |
deskestra-spike-start |
udev rules 70-deckd-uinput.rules |
70-deskestra-uinput.rules |
GNOME UUID deckd-focus@local |
deskestra-focus@local |
KWin script ID deckd-focus |
deskestra-focus |
D-Bus bus org.deckd.Focus |
org.deskestra.Focus |
D-Bus path /org/deckd/Focus |
/org/deskestra/Focus |
D-Bus interface org.deckd.Focus |
org.deskestra.Focus |
Env DECKD_PASSWORD |
DESKESTRA_PASSWORD |
Env DECKD_FAKE_MPRIS |
DESKESTRA_FAKE_MPRIS |
Env DECKD_TLS_DIR / DECKD_TLS_HOST |
DESKESTRA_TLS_DIR / DESKESTRA_TLS_HOST |
Env VITE_DECKD_WS |
VITE_DESKESTRA_WS |
Password file ~/.config/deckd/password |
~/.config/deskestra/password |
LaunchAgent log @PROJECT_DIR@/deckd.log |
@PROJECT_DIR@/deskestra.log |
Browser localStorage key deckd.password |
deskestra.password |
HTTP header X-Deckd-Password |
X-Deskestra-Password |
GitHub Pages base /deckd/ |
/deskestra/ |
Pages URL jonocodes.github.io/deckd/ |
jonocodes.github.io/deskestra/ |
Client PWA name / short_name |
deskestra |
HTML <title> |
Deskestra |
macOS PWA apple-mobile-web-app-title |
Deskestra |
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 DeckdFocusDBusService |
DeskestraFocusDBusService |
Class DeckdFocusCache |
DeskestraFocusCache |
Class DeckdFocusExtension |
DeskestraFocusExtension |
Class attr Server._is_deckd_window |
Server._is_deskestra_window |
Setuptools include pattern deckd* |
deskestra* |
Repo URL github.com/jonocodes/deckd |
github.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.
- 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.)
- Lock the new identifiers by editing these in one commit:
pyproject.toml — name, [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).
- Packaging: rename and update these files/paths:
packaging/systemd/deckd.service → packaging/systemd/deskestra.service; rename Description, ExecStart, WorkingDirectory references.
packaging/launchd/com.deckd.daemon.plist → packaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.
packaging/nixos/deckd-spike.nix → packaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.
packaging/udev/70-deckd-uinput.rules → packaging/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.
- 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/...).
- Client (
client/):
package.json: name → deskestra-client (or just deskestra).
index.html: <title> and apple-mobile-web-app-title.
vite.config.ts: VITE_DECKD_WS → VITE_DESKESTRA_WS.
public/manifest.json and public/icon.svg: display name + aria-label.
src/socket.ts: localStorage key deckd.password → deskestra.password; any error text containing deckd.
playwright.config.ts: DECKD_FAKE_MPRIS env var name.
- Scripts (
scripts/): every file that imports deckd.* or references deckd-focus@local / org.deckd.Focus / DECKD_FAKE_MPRIS / ~/.config/deckd/password.
- 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.py → tests/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.
- 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.
- GitHub-side:
- Rename the repo (
github.com/jonocodes/deckd → github.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.
- 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)
just lint — type/format/lint.
just test — Python suite, including the renamed test_deskestra_window.py.
- Build the GNOME extension zip from
packaging/gnome-shell/deskestra-focus@local and confirm the UUID in the metadata matches what the daemon expects.
- 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.
just client-e2e (Playwright) — confirms the daemon spawn command, env var, and auth header.
- Live smoke:
python scripts/smoke_focus_live.py — confirms D-Bus round-trip on the new bus name.
- Live smoke:
python scripts/smoke_mpris_live.py — same.
- 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
deckctl → deskctl (recommended), or something else?
- 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.)
- Repo rename on GitHub happens before, during, or after the code cutover?
- Old-password-file auto-migration on first run, or fresh setup?
- 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.
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
deckdwas a nod to Stream Deck and reads narrowly. We want a name that:The recent
layout→deckcutover (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), butdeskestrais far enough from both (extra syllable, different ending) to stand apart.Service name:
deskestra(no-dsuffix — agreed that<name>dfeels 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(keepingdeckctl— 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) — rejectedB. Generic conducting/control surface — rejected, all taken or weak
conductordugly. Rejected as overused.C.
desk+ fused coined suffix (the vein we liked)D.
controlfamily, no desk — rejected, all crowdedE. Less-literal control roots (Latin / Greek / French) — rejected, all occupied
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:
deckd→deskestraGenerated 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)
deckddeskestradeckddeskestradaemon/deckd/daemon/deskestra/deckddeskestradeckd-devdeskestra-devdeckctldeskctl(keep, but see open question below)deckd.servicedeskestra.servicecom.deckd.daemoncom.deskestra.daemonservices.deckd-spikeservices.deskestra-spikedeckd-spike-startdeskestra-spike-start70-deckd-uinput.rules70-deskestra-uinput.rulesdeckd-focus@localdeskestra-focus@localdeckd-focusdeskestra-focusorg.deckd.Focusorg.deskestra.Focus/org/deckd/Focus/org/deskestra/Focusorg.deckd.Focusorg.deskestra.FocusDECKD_PASSWORDDESKESTRA_PASSWORDDECKD_FAKE_MPRISDESKESTRA_FAKE_MPRISDECKD_TLS_DIR/DECKD_TLS_HOSTDESKESTRA_TLS_DIR/DESKESTRA_TLS_HOSTVITE_DECKD_WSVITE_DESKESTRA_WS~/.config/deckd/password~/.config/deskestra/password@PROJECT_DIR@/deckd.log@PROJECT_DIR@/deskestra.logdeckd.passworddeskestra.passwordX-Deckd-PasswordX-Deskestra-Password/deckd//deskestra/jonocodes.github.io/deckd/jonocodes.github.io/deskestra/name/short_namedeskestra<title>Deskestraapple-mobile-web-app-titleDeskestra"deckd""deskestra"deckd-smoke-*etc.deskestra-smoke-*deckd_(e.g.deckd_actions_total)deskestra_DeckdFocusDBusServiceDeskestraFocusDBusServiceDeckdFocusCacheDeskestraFocusCacheDeckdFocusExtensionDeskestraFocusExtensionServer._is_deckd_windowServer._is_deskestra_windowdeckd*deskestra*github.com/jonocodes/deckdgithub.com/jonocodes/deskestraOpen question on
deckctl: it's the CLI, not the daemon. Options: rename todeskctl(consistent), rename todeskestractl(verbose), or rename to something functional likedeskestra-cli. Leaning towarddeskctlfor 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.
deskestra.com/.app,github.com/deskestra,npmjs.com/package/deskestra, PyPIdeskestra, and that no new project has appeared in the past 24h. (Last check during research came back clean, but registries move fast.)pyproject.toml—name,[project.scripts](deskestra,deskestra-dev),deskctldecision, package include pattern.daemon/deckd/→daemon/deskestra/(rename the package directory itself; every internal import updates accordingly).from deckd…/import deckd…(the directory rename takes care of most; touch up external references).DECKD_*andVITE_DECKD_WSenv var names.~/.config/deckd/password→~/.config/deskestra/passwordinauth.py, systemd unit, launchd plist, README, docs.org.deckd.Focus,/org/deckd/Focus, interface name — inplatform.py, GNOMEextension.js, KWinmain.js, allscripts/*andtests/*.Deckd*→Deskestra*(DBus service, cache, extension class,_is_deckd_window)."deckd"literal →"deskestra"(and case-insensitive match).packaging/systemd/deckd.service→packaging/systemd/deskestra.service; renameDescription,ExecStart,WorkingDirectoryreferences.packaging/launchd/com.deckd.daemon.plist→packaging/launchd/com.deskestra.daemon.plist; label, program, log path, password path.packaging/nixos/deckd-spike.nix→packaging/nixos/deskestra-spike.nix; module name, generated script name, executable path, service name.packaging/udev/70-deckd-uinput.rules→packaging/udev/70-deskestra-uinput.rules.packaging/gnome-shell/deckd-focus@local/→packaging/gnome-shell/deskestra-focus@local/;metadata.jsonuuid + name + description;extension.jsbus/path/interface;wire-shape.jsimport path.packaging/kwin-script/deckd-focus/→packaging/kwin-script/deskestra-focus/;metadata.jsonId + Name;main.jsbus/path/interface + comment header.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/...).client/):package.json:name→deskestra-client(or justdeskestra).index.html:<title>andapple-mobile-web-app-title.vite.config.ts:VITE_DECKD_WS→VITE_DESKESTRA_WS.public/manifest.jsonandpublic/icon.svg: display name + aria-label.src/socket.ts: localStorage keydeckd.password→deskestra.password; any error text containingdeckd.playwright.config.ts:DECKD_FAKE_MPRISenv var name.scripts/): every file that importsdeckd.*or referencesdeckd-focus@local/org.deckd.Focus/DECKD_FAKE_MPRIS/~/.config/deckd/password.tests/):from deckd…imports.org.deckd.Focus).Deckd*class assertions.tests/test_deckd_window.py→tests/test_deskestra_window.pyand update test names + thetests/.pytest_cachenodeids will regenerate.client/e2e/daemon-log.tsreferences if any contain the binary name.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, alldocs/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 identifierdeckd/Deckdchanges.github.com/jonocodes/deckd→github.com/jonocodes/deskestra) — GitHub redirects old URLs but update localgit remoteafter..github/workflows/deploy-pages.yml: Pages base path/deskestra/, build envVITE_BASE_PATH: /deskestra/..github/workflows/ci.yml: any commands invokingdeckd/deckctl/deckd-dev.daemon/deckd.egg-info/,client/dist/,.pytest_cache/,.mypy_cache/,__pycache__/. Rebuild from clean state. Runjust lint test(or the equivalent) and verify.Behavioral / data migration concerns
~/.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.deskestra.passwordand will treat the user as unauthenticated. Acceptable for a personal/OSS project with no users yet.org.deckd.Focusandorg.deskestra.Focusare 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)
just lint— type/format/lint.just test— Python suite, including the renamedtest_deskestra_window.py.packaging/gnome-shell/deskestra-focus@localand confirm the UUID in the metadata matches what the daemon expects.just client-buildor whatever it is) and load it against a running daemon. Confirm:deskestra.password.Deskestra.just client-e2e(Playwright) — confirms the daemon spawn command, env var, and auth header.python scripts/smoke_focus_live.py— confirms D-Bus round-trip on the new bus name.python scripts/smoke_mpris_live.py— same.org.deskestra.Focus.What I deliberately did not put in this plan
git logwill still show the old name. Agit filter-repocould rewrite history but it's not worth it pre-launch.Open questions for the human reviewer
deckctl→deskctl(recommended), or something else?deskestra, or usedeskestrad/deskestra-serviceafter all? (You said you don't like the-dsuffix; I agree.)deckd.egg-infostyle build artifacts are covered by step 10, but double-check the GitHub deploy-pages flow.