Binary, CLI and Desktop App Naming
binaries-and-cli specs/binaries-and-cli/naming.kmd
Nomenclatura canônica para executáveis: binário k<slug>, dir /opt/koder/<slug>/, D-Bus ID dev.koder.<slug>, symlink em /usr/local/bin/, .desktop file, aliases de compatibilidade. Cobre Flutter desktop, CLIs, multi-binary tooling, packaging .deb/.rpm/.kpkg. Android applicationId §11: dev.koder.<short_slug>, source-of-truth em koder.toml [android] application_id, exposto via catálogo da Store — clients NUNCA derivam por heurística.
Quando esta spec se aplica
Triggers primários
- Empacotar ou renomear binário/CLI/desktop app Koder
Todos os triggers
- Empacotar ou renomear binário/CLI/desktop app Koder
- Criar .desktop file, systemd unit ou D-Bus ID para produto Linux
- Decidir o nome de comando CLI que o usuário digita pra abrir um produto
Corpo da especificação
Spec: Binary, CLI and Desktop App Naming
Apply this spec when:
- Creating or renaming a Flutter desktop app binary
- Creating or renaming a Koder CLI tool (single binary, no GUI)
- Building a multi-binary tooling family (e.g.,
dev/koder-tools) - Packaging a product as a
.deb,.rpm,.AppImage, or.kpkg - Setting the D-Bus application ID for a Linux desktop app
- Creating a
.desktopfile or systemd service unit for a Koder product - Deciding the CLI command name a user types to launch a product
1. Core rule: slug = binary = install argument
Every Koder product or CLI has a slug that is simultaneously:
slug = binary name = kpkg install argument
↑ all three are the same string — no transformations
The slug is chosen at product creation and never transformed at packaging or install time. Installers (khub, kpkg) use the slug verbatim as the binary name and install destination — no prefix is prepended, no suffix stripped.
So for kterm: slug kterm, binary kterm, install kpkg install kterm.
For kdedup: slug kdedup, binary kdedup, install kpkg install kdedup.
No exceptions, no transformations.
Slug naming convention
Choose slugs that start with k followed by a short lowercase noun:
kterm, khub, kmail, kjet, kdek. Multi-binary families use
koder-<verb> (§10). These conventions are a guide for new products, not a
transformation applied after the fact.
1.0 Display name parity rule
The slug is also the source of truth for the display name — the human-readable label that appears on the launcher tile, the OS window title, the Settings menu entry, the README H1, and the Hub package listing. Mechanical rule:
DisplayName = TitleCase(slug)
Worked examples:
| Slug | DisplayName |
|---|---|
kruze |
Kruze |
kterm |
KTerm |
kmail |
KMail |
kdek |
KDek |
dek |
Dek |
mosaic |
Mosaic |
hub |
Hub |
kode |
Kode |
koder-tools |
Koder Tools |
The "Koder " prefix that used to live in launcher tiles ("Koder Term",
"Koder Mail") moves to Comment= of the .desktop file and to
marketing/long-form copy. The launcher tile shows the short DisplayName;
GNOME / KDE search still match "koder" via Comment + Keywords (§5).
Rationale:
- One source of truth — rename the folder, rename the slug, the brand follows automatically.
- Removes the brand-family redundancy when the user already sees a cluster of K-prefixed icons in their launcher.
- New products only choose the slug; everything else derives.
Override (rare)
A product whose marketing brand is intentionally not TitleCase(slug)
(B2B partner brand, white-label, legal-mandated naming) sets the override
in its koder.toml:
[package]
slug = "raven"
display_name = "Raven Mail" # default would be "Raven"
kicon validate-metadata reads this field and uses it for Name= in
the generated .desktop file. The override must be documented in the
product's README (one-liner explaining why it diverges) and is the only
place the override may live — never inline-edited in build scripts or
.desktop files.
1.1 Flutter desktop apps (full GUI products)
These follow §2–§7 in full: D-Bus ID, .desktop file, /opt/koder/<slug>/
install layout, optional koder-<slug>.service for background daemons.
| Product | Monorepo path | Slug | Binary / CLI command |
|---|---|---|---|
| Koder Hub | products/dev/hub/ |
khub |
khub |
| Koder Mail | products/horizontal/kmail/ |
kmail |
kmail |
| Koder Jet | infra/jet/ |
kjet |
kjet |
| Koder Drive | products/horizontal/drive/ |
kdrive |
kdrive |
| Koder Dek | products/horizontal/dek/ |
kdek |
kdek |
| Koder Term | products/dev/kterm/ |
kterm |
kterm |
1.2 Pure CLI tools (no GUI)
These follow §9 — single binary in /usr/local/bin/<slug>, no D-Bus, no
.desktop file, no /opt/koder/<slug>/ bundle.
| CLI | Monorepo path | Slug | Binary | Install |
|---|---|---|---|---|
| Koder Icon | products/dev/kicon/ |
kicon |
kicon |
kpkg install kicon |
| Koder Dedup | dev/kdedup/ |
kdedup |
kdedup |
kpkg install kdedup |
| Koder Shell | linux/shell/ |
kosh |
kosh |
kpkg install kosh |
1.3 Multi-binary tooling families
Umbrella modules that ship several related binaries together follow §10 —
prefix koder-<verb> for each binary, single kpkg install for the family.
| Family | Monorepo path | Binaries | Install |
|---|---|---|---|
| Koder Tools | dev/koder-tools/ |
koder-lock, koder-stackdoc, koder-pathspec-commit, koder-backlog-grep, koder-hub-check |
kpkg install koder-tools |
2. Flutter Linux — CMakeLists.txt
# linux/CMakeLists.txt
set(BINARY_NAME "khub") # ← equals the slug, not the Dart package name
This makes the build output build/linux/x64/release/bundle/khub.
The Dart package name in pubspec.yaml (name: koder_hub) is irrelevant to
the binary name; Flutter uses BINARY_NAME for the native executable only.
3. Installation layout
/opt/koder/<slug>/ ← product bundle root
<slug> ← main executable (name equals slug)
lib/ ← shared libraries (.so)
data/ ← Flutter assets, fonts, ICU
/usr/local/bin/<slug> ← symlink → /opt/koder/<slug>/<slug>
The symlink in /usr/local/bin/ is the user-facing CLI command.
Legacy cleanup: if a previous version installed under /opt/koder-<slug>/,
the .deb prerm script must remove that directory on upgrade:
# DEBIAN/prerm
#!/bin/sh
rm -rf /opt/koder-<slug> 2>/dev/null || true
4. D-Bus application ID
dev.koder.<slug>
Examples: dev.koder.khub, dev.koder.kmail, dev.koder.kjet, dev.koder.kterm
Set in the GTK application registration inside linux/my_application.cc:
gtk_application_new("dev.koder.hub", G_APPLICATION_DEFAULT_FLAGS)
This value also becomes the GLib application ID and the desktop file name (see §5). It must match exactly across all three places.
5. Desktop file
Filename: dev.koder.<slug>.desktop
Install path: /usr/share/applications/dev.koder.<slug>.desktop
Minimum required fields:
[Desktop Entry]
Name=KHub
GenericName=App Hub
Comment=Koder Hub — universal app store for the Koder ecosystem
Keywords=koder;hub;apps;store;packages;
Exec=/opt/koder/khub/khub %u
Icon=dev.koder.khub
Terminal=false
Type=Application
Categories=Utility;Network;
MimeType=x-scheme-handler/koderhub;
StartupWMClass=khub
NameisTitleCase(slug)per §1.0 (display-name parity rule). The override inkoder.toml [package].display_namewins when present.GenericNameis the function in 2–3 words ("App Hub", "Web Browser", "Audio Recorder"). Optional but recommended for KDE/GNOME accessibility.Commentcarries the long-form"Koder <DisplayName> — <one-liner>"so launcher search keeps matching "koder" without showing redundant text on the tile.Keywordsalways includeskoder;<slug>;followed by 2–4 functional terms, semicolon-terminated.Iconuses the reverse-domain ID so icon themes can scope icons per product.StartupWMClassmust match the binary name so the taskbar groups windows correctly.%upasses a URL argument (for deep-link / URI-scheme handling).
Icon install path:
/usr/share/icons/hicolor/512x512/apps/dev.koder.<slug>.png
6. Systemd user service (daemons and background agents)
Products that run background agents (e.g., update checker, sync daemon) use:
koder-<slug>.service ← unit file name
koder-<slug> ← ExecStart binary
The koder- prefix disambiguates Koder services from OS system services and
third-party packages that may also use short names.
7. Compatibility aliases (one-version grace period)
When renaming an existing binary (e.g., koder-hub → khub), the .deb
must ship a compatibility symlink for exactly one release cycle:
# DEBIAN/postinst — create backward-compat alias
ln -sf /usr/local/bin/khub /usr/local/bin/koder-hub
Remove the alias in the next release's prerm:
# DEBIAN/prerm — remove old alias
rm -f /usr/local/bin/koder-hub
8. Flutter desktop apps — quick checklist
When packaging a new or renamed Flutter desktop product (covers §2–§7):
-
linux/CMakeLists.txt→BINARY_NAME "<slug>" -
linux/CMakeLists.txt→APPLICATION_ID "dev.koder.<slug>" - Install dir:
/opt/koder/<slug>/ - Symlink:
/usr/local/bin/<slug>→/opt/koder/<slug>/<slug> - Desktop file named
dev.koder.<slug>.desktop - Icon at
/usr/share/icons/hicolor/512x512/apps/dev.koder.<slug>.png -
StartupWMClass=<slug>in desktop file -
kpkg.tomlwith[platforms],[install], and[install.desktop](includingstartup_wm_classandcategories) - Compatibility alias if renaming (
/usr/local/bin/<old-name>for one release) -
prermcleans/opt/koder-<slug>/if upgrading from legacy layout
8.1 Installer responsibility
The installers (khub and kpkg) are responsible for creating all desktop integration artifacts — .desktop file, icon, and /usr/local/bin/ symlink — on behalf of the package.
The source of truth for all installation metadata is the kpkg.toml embedded in the bundle, not the Koder Hub API. This means:
kpkg install <file.kpkg>readskpkg.tomlfrom the ZIP, resolves install paths, creates the.desktopfile, and installs the icon — no API call needed.khub install <slug>downloads the.kpkgfrom the Store, then delegates to the sameinstaller.Install()library path — identical behavior.- Metadata like
StartupWMClassand XDGCategoriescome from[install.desktop]inkpkg.toml, ensuring they are consistent regardless of which tool performs the install.
Apps must not bundle their own installer scripts for desktop integration; the kpkg installer library handles it for all platforms.
9. Pure CLI tools (no GUI)
A pure CLI tool is a single-binary Koder utility with no GUI, no D-Bus
service registration, no .desktop file, and no /opt/koder/<slug>/ install
bundle. Examples: kicon, kdedup, kosh. Sections §2 (Flutter CMakeLists),
§4 (D-Bus ID), §5 (desktop file), and §7 (compatibility aliases) do not
apply to CLI tools.
9.1 Identity
The §1 identity rule: slug = binary name = kpkg install argument — all the
same string, no transformation.
dev/kdedup/ ← module directory
↓
slug = "kdedup" ← in koder.toml
↓
binary = "kdedup" ← in koder.toml + Go's `go build -o`
↓
kpkg install kdedup ← user-facing install command
9.2 koder.toml
A pure CLI's koder.toml declares slug and binary as identical strings:
[app]
slug = "kdedup"
binary = "kdedup"
version = "0.1.0"
Both fields must be present and identical. koder-stackdoc check flags
divergence as KSTORE-TOML-001.
9.3 Installation layout
A single executable in /usr/local/bin/. No /opt/koder/<slug>/ bundle, no
lib/, no data/ — Go-built CLI binaries are statically linked.
/usr/local/bin/<slug> ← the binary itself (not a symlink)
If the CLI ships any auxiliary data (config defaults, completion scripts), it goes under XDG paths:
/usr/share/<slug>/ ← read-only data (templates, completions)
~/.config/<slug>/ ← user config
~/.cache/<slug>/ ← user cache
Never under /opt/.
9.4 Distribution
Three channels, in order of preference:
kpkg install <slug>— universal Koder Hub distribution (preferred once the module ships akpkg.toml)- Pre-built binary —
cp dev/<slug>/<slug> /usr/local/bin/for local builds during development - Build from source —
cd dev/<slug> && GOWORK=off go build -o <slug> ./cmd/<slug>for contributors
The koder.toml and the README must show all three; the install command in
kpkg install is always the slug verbatim — never an alias, never a
shorter form.
9.5 Background daemons
If a CLI tool also exposes a long-running daemon mode (e.g., kicon watch),
the systemd user service follows §6 (koder-<slug>.service with
ExecStart=/usr/local/bin/<slug> daemon).
9.6 Quick checklist
When creating or renaming a pure CLI tool:
- Module directory =
dev/<slug>/(or appropriate Area), where<slug>starts withkif appropriate -
koder.tomlhasslugandbinaryset to the same string - Go build produces single binary named
<slug>(go build -o <slug> ./cmd/<slug>) - Binary lands at
/usr/local/bin/<slug>(no/opt/, no symlink chain) - README install section shows all three channels (
kpkg, pre-built copy, build from source) with the slug verbatim - No
.desktopfile, no D-Bus ID, nogtk_application_new - If the CLI has a daemon mode: §6 systemd unit named
koder-<slug>.service
10. Multi-binary tooling families
A tooling family is an umbrella module that ships several related binaries
together — they share a release cadence, a CHANGELOG, and a single
kpkg install invocation. Example: dev/koder-tools ships koder-lock,
koder-stackdoc, koder-pathspec-commit, koder-backlog-grep,
koder-hub-check as one bundle.
Use a tooling family when:
- The binaries share a domain (e.g., monorepo housekeeping helpers)
- They are versioned and released together
- Splitting them into separate
dev/<slug>/modules would create five near-empty repos with synchronized release cycles
Otherwise, prefer §9 (one CLI = one module).
10.1 Identity
The umbrella module is named koder-tools (or koder-<domain> for other
families). Each binary inside is named koder-<verb> — a verb describing
what the tool does, prefixed with koder- to disambiguate from system tools.
dev/koder-tools/ ← umbrella module directory
cmd/koder-lock/main.go → binary: koder-lock
cmd/koder-stackdoc/main.go → binary: koder-stackdoc
cmd/koder-pathspec-commit/main.go → binary: koder-pathspec-commit
cmd/koder-backlog-grep/main.go → binary: koder-backlog-grep
cmd/koder-hub-check/main.go → binary: koder-hub-check
The umbrella never exposes a binary named just koder-tools — the family
is a delivery vehicle, not a launcher. Each binary stands on its own.
10.2 koder.toml
The umbrella koder.toml declares the family slug and lists all binaries
shipped:
[app]
slug = "koder-tools"
version = "0.2.0"
[[binaries]]
name = "koder-lock"
path = "cmd/koder-lock"
[[binaries]]
name = "koder-stackdoc"
path = "cmd/koder-stackdoc"
# ... one [[binaries]] block per binary
binary = "..." (singular) is omitted at the top level — the family has
no single primary binary.
10.3 Installation layout
Same as §9: each binary lands in /usr/local/bin/<binary-name>, all installed
by a single kpkg install call:
/usr/local/bin/koder-lock
/usr/local/bin/koder-stackdoc
/usr/local/bin/koder-pathspec-commit
/usr/local/bin/koder-backlog-grep
/usr/local/bin/koder-hub-check
10.4 Distribution
kpkg install koder-tools # installs all binaries in the family
Granular install (kpkg install koder-lock for just one binary) is not
supported — families ship as a unit. If a binary needs an independent release
cadence, promote it to its own §9 module.
10.5 Why koder-<verb> and not k<verb>
Tooling-family binaries deliberately use the longer koder- prefix instead of
the short k. Two reasons:
- Disambiguation from products.
klockcould plausibly be a future Koder product (a password manager?);koder-lockclearly signals "internal Koder Stack tooling, not an end-user app." - Verb-shaped names. Tooling-family binaries are verbs (
lock,stackdoc,commit,grep,check); product binaries are nouns (store,mail,dek). Thekoder-<verb>form reads naturally as "koder, do X."
End-user-facing CLI tools (kicon, kdedup, kosh) use §9's short slug form
(kicon, kdedup, kosh) because they are user-facing nouns/products in
their own right.
10.6 Quick checklist
When creating or extending a tooling family:
- Umbrella directory named
dev/koder-<domain>/(e.g.,dev/koder-tools/) - Each binary under
cmd/koder-<verb>/main.go - Each binary named
koder-<verb>— no shortk<verb>aliases - Top-level
koder.tomllists all binaries under[[binaries]] - No top-level
binary = "..."field - Single CHANGELOG covers all binaries; one version applies to the family
-
kpkg install koder-<domain>installs all binaries at once
11. Android applicationId
For Koder products with Android apps (Flutter or native Kotlin/Compose),
the Android applicationId follows a canonical rule rooted in the same
slug used elsewhere in this spec.
11.1 Rule
applicationId = dev.koder.<short_slug>
where short_slug is the catalog slug with the koder- prefix stripped
(when present) and any - replaced by _:
| Catalog slug | short_slug |
applicationId |
|---|---|---|
koder-eye |
eye |
dev.koder.eye |
koder-kruze |
kruze |
dev.koder.kruze |
koder-pass |
pass |
dev.koder.pass |
koder-dek |
dek |
dev.koder.dek |
kmail |
kmail |
dev.koder.kmail |
khub |
khub |
dev.koder.khub |
kterm |
kterm |
dev.koder.kterm |
kode |
kode |
dev.koder.kode |
multi-word |
multi_word |
dev.koder.multi_word |
For slugs that already start with k (the §1 short form), no stripping
happens — they're used as-is. The resulting applicationId is also the
package name in the manifest (<manifest package="…"> and the
namespace in build.gradle.kts).
11.2 Source of truth
The canonical applicationId for each product must be declared in
koder.toml, not derived by heuristic. This is the ground truth that
the Store catalog, build pipeline, and clients all consume:
# koder.toml
[app]
slug = "koder-eye"
[android]
application_id = "dev.koder.eye"
When [android].application_id is absent, the build tooling may
fall back to the §11.1 formula — but only at build time, never at
runtime in clients. Production catalog rows MUST have the explicit
field populated; clients MUST consume it from the catalog API.
11.3 Catalog API contract
The Koder Hub catalog exposes package_name for every Android-
distributing app:
GET https://hub.koder.dev/api/v1/apps/koder-eye
{
"slug": "koder-eye",
"name": "Koder Eye",
"version": "0.2.2",
"platforms": ["android"],
"package_name": "dev.koder.eye"
}
Clients (Koder Hub mobile, third-party update checkers, IDE
integrations) must read package_name from the catalog. The Store
mobile app's _expectedPkg heuristic in app_detail_screen.dart is
an anti-pattern and is being removed in favor of the catalog field
(see commit history of products/dev/hub/app/lib/screens/app_detail_screen.dart).
If the API response omits package_name, the client treats the entry
as not installable until the catalog row is backfilled — never
fall back to a derived guess. A stale install record is preferable
to a destructive operation against the wrong OS package.
11.4 Build-time wiring
In a Flutter Android app:
// android/app/build.gradle.kts
android {
namespace = "dev.koder.eye" // §11.1 applicationId
defaultConfig {
applicationId = "dev.koder.eye"
}
}
In a native Android (Kotlin/Compose) app, the same fields apply.
The AndroidManifest.xml package attribute is implied by namespace
for AGP ≥ 8 and need not be set explicitly.
The Kotlin top-level package directories under app/src/main/kotlin/
mirror the applicationId, e.g.,
app/src/main/kotlin/dev/koder/eye/MainActivity.kt.
11.5 Legacy exceptions (transition state, 2026-04-28)
Four products in production still use non-canonical applicationIds
that predate this spec:
| Product | Slug | Current applicationId | Canonical (§11.1) | Migration |
|---|---|---|---|---|
| Koder Hub | koder-hub |
dev.koder.koder_store |
dev.koder.khub |
TODO |
| Koder Mosaic | koder-mosaic |
dev.koder.koder_mosaic |
dev.koder.kmosaic |
TODO |
| Koder Term | kterm |
dev.koder.ticsign |
dev.koder.kterm |
TODO |
| Koder Mail | koder-mail |
dev.koder.kmail |
dev.koder.mail (or canonicalize slug to kmail) |
TODO |
Each migration requires:
- Update
koder.toml[android].application_id - Update
build.gradle.ktsnamespaceandapplicationId - Move Kotlin source under
app/src/main/kotlin/dev/koder/<new>/ - Backfill the catalog DB row's
package_name - Bump version + ship release with both the old and new
applicationIdfor one cycle (Android does not allow renaming anapplicationIdof an installed app — users must reinstall, so the old package is archived in the catalog and the new one becomes the active product) - Open follow-up ticket per product
Until each migration ships, the catalog row's package_name reflects
the current value (e.g., dev.koder.koder_store), so clients
continue to work against installed users.
11.6 Quick checklist
When creating a new Android Koder app or auditing an existing one:
-
koder.tomlhas[android].application_iddeclared and matches §11.1 -
android/app/build.gradle.ktsnamespaceandapplicationIdmatch - Kotlin/Java source rooted at
app/src/main/<lang>/dev/koder/<short_slug>/ - Catalog API row has
package_namepopulated and matcheskoder.toml - Client consumers read
package_namefrom API — no client-side heuristic onslug → packageName - If product is on the §11.5 legacy list, follow-up ticket exists with concrete migration steps
- App icon resource path:
android/app/src/main/res/mipmap-*/(drawables are per Android resource convention; the spec atspecs/icons/generation-targets.kmdcovers icon generation)
Referências
specs/desktop-apps/title-bar.kmdspecs/releases/packaging.kmdspecs/kpkg/format.kmd